16a5dc222SDavid du Colombier /*
2906943f9SDavid du Colombier * USB Open Host Controller Interface (Ohci) driver
3906943f9SDavid du Colombier *
4906943f9SDavid du Colombier * BUGS:
5906943f9SDavid du Colombier * - Missing isochronous input streams.
6906943f9SDavid du Colombier * - Too many delays and ilocks.
7906943f9SDavid du Colombier * - bandwidth admission control must be done per-frame.
8906943f9SDavid du Colombier * - Buffering could be handled like in uhci, to avoid
9906943f9SDavid du Colombier * needed block allocation and avoid allocs for small Tds.
10906943f9SDavid du Colombier * - must warn of power overruns.
116a5dc222SDavid du Colombier */
12906943f9SDavid du Colombier
136a5dc222SDavid du Colombier #include "u.h"
146a5dc222SDavid du Colombier #include "../port/lib.h"
156a5dc222SDavid du Colombier #include "mem.h"
166a5dc222SDavid du Colombier #include "dat.h"
176a5dc222SDavid du Colombier #include "fns.h"
186a5dc222SDavid du Colombier #include "io.h"
196a5dc222SDavid du Colombier #include "../port/error.h"
206a5dc222SDavid du Colombier
2184860c5dSDavid du Colombier #include "../port/usb.h"
226a5dc222SDavid du Colombier
2339dc1420SDavid du Colombier typedef struct Ctlio Ctlio;
246a5dc222SDavid du Colombier typedef struct Ctlr Ctlr;
2539dc1420SDavid du Colombier typedef struct Ed Ed;
2639dc1420SDavid du Colombier typedef struct Edpool Edpool;
27906943f9SDavid du Colombier typedef struct Epx Epx;
28906943f9SDavid du Colombier typedef struct Hcca Hcca;
2939dc1420SDavid du Colombier typedef struct Isoio Isoio;
30906943f9SDavid du Colombier typedef struct Ohci Ohci;
31906943f9SDavid du Colombier typedef struct Qio Qio;
3239dc1420SDavid du Colombier typedef struct Qtree Qtree;
3339dc1420SDavid du Colombier typedef struct Td Td;
34906943f9SDavid du Colombier typedef struct Tdpool Tdpool;
356a5dc222SDavid du Colombier
36906943f9SDavid du Colombier enum
376a5dc222SDavid du Colombier {
38906943f9SDavid du Colombier Incr = 64, /* for Td and Ed pools */
396a5dc222SDavid du Colombier
40906943f9SDavid du Colombier Align = 0x20, /* OHCI only requires 0x10 */
41906943f9SDavid du Colombier /* use always a power of 2 */
426a5dc222SDavid du Colombier
43906943f9SDavid du Colombier Abortdelay = 1, /* delay after cancelling Tds (ms) */
44906943f9SDavid du Colombier Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */
45906943f9SDavid du Colombier Enabledelay = 100, /* waiting for a port to enable */
466a5dc222SDavid du Colombier
476a5dc222SDavid du Colombier
48906943f9SDavid du Colombier /* Queue states (software) */
49906943f9SDavid du Colombier Qidle = 0,
50906943f9SDavid du Colombier Qinstall,
51906943f9SDavid du Colombier Qrun,
52906943f9SDavid du Colombier Qdone,
53906943f9SDavid du Colombier Qclose,
54906943f9SDavid du Colombier Qfree,
556a5dc222SDavid du Colombier
56906943f9SDavid du Colombier /* Ed control bits */
57906943f9SDavid du Colombier Edmpsmask = 0x7ff, /* max packet size */
58906943f9SDavid du Colombier Edmpsshift = 16,
59906943f9SDavid du Colombier Edlow = 1 << 13, /* low speed */
60906943f9SDavid du Colombier Edskip = 1 << 14, /* skip this ed */
61906943f9SDavid du Colombier Ediso = 1 << 15, /* iso Tds used */
62906943f9SDavid du Colombier Edtddir = 0, /* get dir from td */
63906943f9SDavid du Colombier Edin = 2 << 11, /* direction in */
64906943f9SDavid du Colombier Edout = 1 << 11, /* direction out */
65906943f9SDavid du Colombier Eddirmask = 3 << 11, /* direction bits */
66906943f9SDavid du Colombier Edhalt = 1, /* halted (in head ptr) */
67906943f9SDavid du Colombier Edtoggle = 2, /* toggle (in head ptr) 1 == data1 */
686a5dc222SDavid du Colombier
69906943f9SDavid du Colombier /* Td control bits */
70906943f9SDavid du Colombier Tdround = 1<<18, /* (rounding) short packets ok */
71906943f9SDavid du Colombier Tdtoksetup = 0<<19, /* setup packet */
72906943f9SDavid du Colombier Tdtokin = 2<<19, /* in packet */
73906943f9SDavid du Colombier Tdtokout = 1<<19, /* out packet */
74906943f9SDavid du Colombier Tdtokmask = 3<<19, /* in/out/setup bits */
75906943f9SDavid du Colombier Tdnoioc = 7<<21, /* intr. cnt. value for no interrupt */
76906943f9SDavid du Colombier Tdusetog = 1<<25, /* use toggle from Td (1) or Ed (0) */
77906943f9SDavid du Colombier Tddata1 = 1<<24, /* data toggle (1 == data1) */
78906943f9SDavid du Colombier Tddata0 = 0<<24,
79906943f9SDavid du Colombier Tdfcmask = 7, /* frame count (iso) */
80906943f9SDavid du Colombier Tdfcshift = 24,
81906943f9SDavid du Colombier Tdsfmask = 0xFFFF, /* starting frame (iso) */
82906943f9SDavid du Colombier Tderrmask = 3, /* error counter */
83906943f9SDavid du Colombier Tderrshift = 26,
84906943f9SDavid du Colombier Tdccmask = 0xf, /* condition code (status) */
85906943f9SDavid du Colombier Tdccshift = 28,
86906943f9SDavid du Colombier Tdiccmask = 0xf, /* condition code (iso, offsets) */
87906943f9SDavid du Colombier Tdiccshift = 12,
886a5dc222SDavid du Colombier
89906943f9SDavid du Colombier Ntdframes = 0x10000, /* # of different iso frame numbers */
906a5dc222SDavid du Colombier
91906943f9SDavid du Colombier /* Td errors (condition code) */
92906943f9SDavid du Colombier Tdok = 0,
93906943f9SDavid du Colombier Tdcrc = 1,
94906943f9SDavid du Colombier Tdbitstuff = 2,
95906943f9SDavid du Colombier Tdbadtog = 3,
96906943f9SDavid du Colombier Tdstalled = 4,
97906943f9SDavid du Colombier Tdtmout = 5,
98906943f9SDavid du Colombier Tdpidchk = 6,
99906943f9SDavid du Colombier Tdbadpid = 7,
100906943f9SDavid du Colombier Tddataovr = 8,
101906943f9SDavid du Colombier Tddataund = 9,
102906943f9SDavid du Colombier Tdbufovr = 0xC,
103906943f9SDavid du Colombier Tdbufund = 0xD,
104906943f9SDavid du Colombier Tdnotacc = 0xE,
1056a5dc222SDavid du Colombier
106906943f9SDavid du Colombier /* control register */
107906943f9SDavid du Colombier Cple = 0x04, /* periodic list enable */
108906943f9SDavid du Colombier Cie = 0x08, /* iso. list enable */
109906943f9SDavid du Colombier Ccle = 0x10, /* ctl list enable */
110906943f9SDavid du Colombier Cble = 0x20, /* bulk list enable */
111906943f9SDavid du Colombier Cfsmask = 3 << 6, /* functional state... */
112906943f9SDavid du Colombier Cfsreset = 0 << 6,
113906943f9SDavid du Colombier Cfsresume = 1 << 6,
114906943f9SDavid du Colombier Cfsoper = 2 << 6,
115906943f9SDavid du Colombier Cfssuspend = 3 << 6,
1166a5dc222SDavid du Colombier
117906943f9SDavid du Colombier /* command status */
118906943f9SDavid du Colombier Sblf = 1 << 2, /* bulk list (load) flag */
119906943f9SDavid du Colombier Sclf = 1 << 1, /* control list (load) flag */
120906943f9SDavid du Colombier Shcr = 1 << 0, /* host controller reset */
1216a5dc222SDavid du Colombier
122906943f9SDavid du Colombier /* intr enable */
1236a5dc222SDavid du Colombier Mie = 1 << 31,
1246a5dc222SDavid du Colombier Oc = 1 << 30,
1256a5dc222SDavid du Colombier Rhsc = 1 << 6,
1266a5dc222SDavid du Colombier Fno = 1 << 5,
1276a5dc222SDavid du Colombier Ue = 1 << 4,
1286a5dc222SDavid du Colombier Rd = 1 << 3,
1296a5dc222SDavid du Colombier Sf = 1 << 2,
1306a5dc222SDavid du Colombier Wdh = 1 << 1,
1316a5dc222SDavid du Colombier So = 1 << 0,
132906943f9SDavid du Colombier
133906943f9SDavid du Colombier Fmaxpktmask = 0x7fff,
134906943f9SDavid du Colombier Fmaxpktshift = 16,
1356a5dc222SDavid du Colombier HcRhDescA_POTPGT_MASK = 0xff << 24,
1366a5dc222SDavid du Colombier HcRhDescA_POTPGT_SHIFT = 24,
137906943f9SDavid du Colombier
138906943f9SDavid du Colombier /* Rh status */
1396a5dc222SDavid du Colombier Lps = 1 << 0,
1406a5dc222SDavid du Colombier Cgp = 1 << 0,
1416a5dc222SDavid du Colombier Oci = 1 << 1,
142906943f9SDavid du Colombier Psm = 1 << 8,
143906943f9SDavid du Colombier Nps = 1 << 9,
1446a5dc222SDavid du Colombier Drwe = 1 << 15,
1456a5dc222SDavid du Colombier Srwe = 1 << 15,
146906943f9SDavid du Colombier Lpsc = 1 << 16,
1476a5dc222SDavid du Colombier Ccic = 1 << 17,
1486a5dc222SDavid du Colombier Crwe = 1 << 31,
1496a5dc222SDavid du Colombier
150906943f9SDavid du Colombier /* port status */
151906943f9SDavid du Colombier Ccs = 0x00001, /* current connect status */
152906943f9SDavid du Colombier Pes = 0x00002, /* port enable status */
153906943f9SDavid du Colombier Pss = 0x00004, /* port suspend status */
154906943f9SDavid du Colombier Poci = 0x00008, /* over current indicator */
155906943f9SDavid du Colombier Prs = 0x00010, /* port reset status */
156906943f9SDavid du Colombier Pps = 0x00100, /* port power status */
157906943f9SDavid du Colombier Lsda = 0x00200, /* low speed device attached */
158906943f9SDavid du Colombier Csc = 0x10000, /* connect status change */
159906943f9SDavid du Colombier Pesc = 0x20000, /* enable status change */
160906943f9SDavid du Colombier Pssc = 0x40000, /* suspend status change */
161906943f9SDavid du Colombier Ocic = 0x80000, /* over current ind. change */
162906943f9SDavid du Colombier Prsc = 0x100000, /* reset status change */
163906943f9SDavid du Colombier
164906943f9SDavid du Colombier /* port status write bits */
165906943f9SDavid du Colombier Cpe = 0x001, /* clear port enable */
166906943f9SDavid du Colombier Spe = 0x002, /* set port enable */
167906943f9SDavid du Colombier Spr = 0x010, /* set port reset */
168906943f9SDavid du Colombier Spp = 0x100, /* set port power */
169906943f9SDavid du Colombier Cpp = 0x200, /* clear port power */
170906943f9SDavid du Colombier
1716a5dc222SDavid du Colombier };
1726a5dc222SDavid du Colombier
173906943f9SDavid du Colombier /*
174906943f9SDavid du Colombier * Endpoint descriptor. (first 4 words used by hardware)
175906943f9SDavid du Colombier */
176906943f9SDavid du Colombier struct Ed {
177906943f9SDavid du Colombier ulong ctrl;
178906943f9SDavid du Colombier ulong tail; /* transfer descriptor */
179906943f9SDavid du Colombier ulong head;
180906943f9SDavid du Colombier ulong nexted;
181906943f9SDavid du Colombier
182906943f9SDavid du Colombier Ed* next; /* sw; in free list or next in list */
183906943f9SDavid du Colombier Td* tds; /* in use by current xfer; all for iso */
184906943f9SDavid du Colombier Ep* ep; /* debug/align */
185906943f9SDavid du Colombier Ed* inext; /* debug/align (dump interrupt eds). */
1866a5dc222SDavid du Colombier };
1876a5dc222SDavid du Colombier
188906943f9SDavid du Colombier /*
189906943f9SDavid du Colombier * Endpoint I/O state (software), per direction.
190906943f9SDavid du Colombier */
191906943f9SDavid du Colombier struct Qio
192906943f9SDavid du Colombier {
193906943f9SDavid du Colombier QLock; /* for the entire I/O process */
194906943f9SDavid du Colombier Rendez; /* wait for completion */
195906943f9SDavid du Colombier Ed* ed; /* to place Tds on it */
196906943f9SDavid du Colombier int sched; /* queue number (intr/iso) */
197906943f9SDavid du Colombier int toggle; /* Tddata0/Tddata1 */
198906943f9SDavid du Colombier ulong usbid; /* device/endpoint address */
199906943f9SDavid du Colombier int tok; /* Tdsetup, Tdtokin, Tdtokout */
200906943f9SDavid du Colombier long iotime; /* last I/O time; to hold interrupt polls */
201906943f9SDavid du Colombier int debug; /* for the endpoint */
202906943f9SDavid du Colombier char* err; /* error status */
203906943f9SDavid du Colombier int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
204906943f9SDavid du Colombier long bw; /* load (intr/iso) */
2056a5dc222SDavid du Colombier };
2066a5dc222SDavid du Colombier
207906943f9SDavid du Colombier struct Ctlio
208906943f9SDavid du Colombier {
209906943f9SDavid du Colombier Qio; /* single Ed for all transfers */
210906943f9SDavid du Colombier uchar* data; /* read from last ctl req. */
211906943f9SDavid du Colombier int ndata; /* number of bytes read */
212906943f9SDavid du Colombier };
2136a5dc222SDavid du Colombier
214906943f9SDavid du Colombier struct Isoio
215906943f9SDavid du Colombier {
216906943f9SDavid du Colombier Qio;
217906943f9SDavid du Colombier int nframes; /* number of frames for a full second */
218906943f9SDavid du Colombier Td* atds; /* Tds avail for further I/O */
219906943f9SDavid du Colombier int navail; /* number of avail Tds */
220906943f9SDavid du Colombier ulong frno; /* next frame number avail for I/O */
221906943f9SDavid du Colombier ulong left; /* remainder after rounding Hz to samples/ms */
222906943f9SDavid du Colombier int nerrs; /* consecutive errors on iso I/O */
223906943f9SDavid du Colombier };
224906943f9SDavid du Colombier
225906943f9SDavid du Colombier /*
226906943f9SDavid du Colombier * Transfer descriptor. Size must be multiple of 32
227906943f9SDavid du Colombier * First block is used by hardware (aligned to 32).
228906943f9SDavid du Colombier */
229906943f9SDavid du Colombier struct Td
230906943f9SDavid du Colombier {
231906943f9SDavid du Colombier ulong ctrl;
232add6b5c5SDavid du Colombier ulong cbp; /* current buffer pointer */
233906943f9SDavid du Colombier ulong nexttd;
234906943f9SDavid du Colombier ulong be;
235906943f9SDavid du Colombier ushort offsets[8]; /* used by Iso Tds only */
236906943f9SDavid du Colombier
237906943f9SDavid du Colombier Td* next; /* in free or Ed tds list */
238906943f9SDavid du Colombier Td* anext; /* in avail td list (iso) */
239906943f9SDavid du Colombier Ep* ep; /* using this Td for I/O */
240906943f9SDavid du Colombier Qio* io; /* using this Td for I/O */
241906943f9SDavid du Colombier Block* bp; /* data for this Td */
242906943f9SDavid du Colombier ulong nbytes; /* bytes in this Td */
243906943f9SDavid du Colombier ulong cbp0; /* initial value for cbp */
244906943f9SDavid du Colombier ulong last; /* true for last Td in Qio */
245906943f9SDavid du Colombier };
246906943f9SDavid du Colombier
247906943f9SDavid du Colombier /*
248906943f9SDavid du Colombier * Host controller communication area (hardware)
249906943f9SDavid du Colombier */
250906943f9SDavid du Colombier struct Hcca
251906943f9SDavid du Colombier {
252906943f9SDavid du Colombier ulong intrtable[32];
253906943f9SDavid du Colombier ushort framenumber;
254906943f9SDavid du Colombier ushort pad1;
255906943f9SDavid du Colombier ulong donehead;
256906943f9SDavid du Colombier uchar reserved[116];
257906943f9SDavid du Colombier };
258906943f9SDavid du Colombier
259906943f9SDavid du Colombier /*
260906943f9SDavid du Colombier * I/O registers
261906943f9SDavid du Colombier */
262906943f9SDavid du Colombier struct Ohci
263906943f9SDavid du Colombier {
264906943f9SDavid du Colombier /* control and status group */
265906943f9SDavid du Colombier ulong revision; /*00*/
266906943f9SDavid du Colombier ulong control; /*04*/
267906943f9SDavid du Colombier ulong cmdsts; /*08*/
268906943f9SDavid du Colombier ulong intrsts; /*0c*/
269906943f9SDavid du Colombier ulong intrenable; /*10*/
270906943f9SDavid du Colombier ulong intrdisable; /*14*/
271906943f9SDavid du Colombier
272906943f9SDavid du Colombier /* memory pointer group */
273906943f9SDavid du Colombier ulong hcca; /*18*/
274906943f9SDavid du Colombier ulong periodcurred; /*1c*/
275906943f9SDavid du Colombier ulong ctlheaded; /*20*/
276906943f9SDavid du Colombier ulong ctlcurred; /*24*/
277906943f9SDavid du Colombier ulong bulkheaded; /*28*/
278906943f9SDavid du Colombier ulong bulkcurred; /*2c*/
279906943f9SDavid du Colombier ulong donehead; /*30*/
280906943f9SDavid du Colombier
281906943f9SDavid du Colombier /* frame counter group */
282906943f9SDavid du Colombier ulong fminterval; /*34*/
283906943f9SDavid du Colombier ulong fmremaining; /*38*/
284906943f9SDavid du Colombier ulong fmnumber; /*3c*/
285906943f9SDavid du Colombier ulong periodicstart; /*40*/
286906943f9SDavid du Colombier ulong lsthreshold; /*44*/
287906943f9SDavid du Colombier
288906943f9SDavid du Colombier /* root hub group */
289906943f9SDavid du Colombier ulong rhdesca; /*48*/
290906943f9SDavid du Colombier ulong rhdescb; /*4c*/
291906943f9SDavid du Colombier ulong rhsts; /*50*/
292906943f9SDavid du Colombier ulong rhportsts[15]; /*54*/
293906943f9SDavid du Colombier ulong pad25[20]; /*90*/
294906943f9SDavid du Colombier
295906943f9SDavid du Colombier /* unknown */
296906943f9SDavid du Colombier ulong hostueaddr; /*e0*/
297906943f9SDavid du Colombier ulong hostuests; /*e4*/
298906943f9SDavid du Colombier ulong hosttimeoutctrl; /*e8*/
299906943f9SDavid du Colombier ulong pad59; /*ec*/
300906943f9SDavid du Colombier ulong pad60; /*f0*/
301906943f9SDavid du Colombier ulong hostrevision; /*f4*/
302906943f9SDavid du Colombier ulong pad62[2];
303906943f9SDavid du Colombier /*100*/
304906943f9SDavid du Colombier };
305906943f9SDavid du Colombier
306906943f9SDavid du Colombier /*
307906943f9SDavid du Colombier * Endpoint tree (software)
308906943f9SDavid du Colombier */
309906943f9SDavid du Colombier struct Qtree
310906943f9SDavid du Colombier {
311906943f9SDavid du Colombier int nel;
312906943f9SDavid du Colombier int depth;
313906943f9SDavid du Colombier ulong* bw;
314906943f9SDavid du Colombier Ed** root;
315906943f9SDavid du Colombier };
316906943f9SDavid du Colombier
317906943f9SDavid du Colombier struct Tdpool
318906943f9SDavid du Colombier {
319906943f9SDavid du Colombier Lock;
320906943f9SDavid du Colombier Td* free;
321906943f9SDavid du Colombier int nalloc;
322906943f9SDavid du Colombier int ninuse;
323906943f9SDavid du Colombier int nfree;
324906943f9SDavid du Colombier };
325906943f9SDavid du Colombier
326906943f9SDavid du Colombier struct Edpool
327906943f9SDavid du Colombier {
328906943f9SDavid du Colombier Lock;
329906943f9SDavid du Colombier Ed* free;
330906943f9SDavid du Colombier int nalloc;
331906943f9SDavid du Colombier int ninuse;
332906943f9SDavid du Colombier int nfree;
333906943f9SDavid du Colombier };
334906943f9SDavid du Colombier
335906943f9SDavid du Colombier struct Ctlr
336906943f9SDavid du Colombier {
337906943f9SDavid du Colombier Lock; /* for ilock; lists and basic ctlr I/O */
338906943f9SDavid du Colombier QLock resetl; /* lock controller during USB reset */
339906943f9SDavid du Colombier int active;
340906943f9SDavid du Colombier Ctlr* next;
341906943f9SDavid du Colombier int nports;
342906943f9SDavid du Colombier
343906943f9SDavid du Colombier Ohci* ohci; /* base I/O address */
344906943f9SDavid du Colombier Hcca* hcca; /* intr/done Td lists (used by hardware) */
345906943f9SDavid du Colombier int overrun; /* sched. overrun */
346906943f9SDavid du Colombier Ed* intrhd; /* list of intr. eds in tree */
347906943f9SDavid du Colombier Qtree* tree; /* tree for t Ep i/o */
348906943f9SDavid du Colombier int ntree; /* number of dummy Eds in tree */
349c8cbc0e9SDavid du Colombier Pcidev* pcidev;
350906943f9SDavid du Colombier };
351906943f9SDavid du Colombier
352906943f9SDavid du Colombier #define dqprint if(debug || io && io->debug)print
353906943f9SDavid du Colombier #define ddqprint if(debug>1 || (io && io->debug>1))print
354906943f9SDavid du Colombier #define diprint if(debug || iso && iso->debug)print
355906943f9SDavid du Colombier #define ddiprint if(debug>1 || (iso && iso->debug>1))print
356906943f9SDavid du Colombier #define TRUNC(x, sz) ((x) & ((sz)-1))
357906943f9SDavid du Colombier
358906943f9SDavid du Colombier static int ohciinterrupts[Nttypes];
359906943f9SDavid du Colombier static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" };
360906943f9SDavid du Colombier
361906943f9SDavid du Colombier static int debug;
362906943f9SDavid du Colombier static Edpool edpool;
363906943f9SDavid du Colombier static Tdpool tdpool;
364906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis];
365906943f9SDavid du Colombier
3666a5dc222SDavid du Colombier static QLock usbhstate; /* protects name space state */
3676a5dc222SDavid du Colombier
368906943f9SDavid du Colombier static int schedendpt(Ctlr *ub, Ep *ep);
369906943f9SDavid du Colombier static void unschedendpt(Ctlr *ub, Ep *ep);
370906943f9SDavid du Colombier static long qtd(Ctlr*, Ep*, int, Block*, uchar*, uchar*, int, ulong);
3716a5dc222SDavid du Colombier
372906943f9SDavid du Colombier static char* errmsgs[] =
3736a5dc222SDavid du Colombier {
374906943f9SDavid du Colombier [Tdcrc] "crc error",
375906943f9SDavid du Colombier [Tdbitstuff] "bit stuffing error",
376906943f9SDavid du Colombier [Tdbadtog] "bad toggle",
377906943f9SDavid du Colombier [Tdstalled] Estalled,
378906943f9SDavid du Colombier [Tdtmout] "timeout error",
379906943f9SDavid du Colombier [Tdpidchk] "pid check error",
380906943f9SDavid du Colombier [Tdbadpid] "bad pid",
381906943f9SDavid du Colombier [Tddataovr] "data overrun",
382906943f9SDavid du Colombier [Tddataund] "data underrun",
383906943f9SDavid du Colombier [Tdbufovr] "buffer overrun",
384906943f9SDavid du Colombier [Tdbufund] "buffer underrun",
385906943f9SDavid du Colombier [Tdnotacc] "not accessed"
386906943f9SDavid du Colombier };
3876a5dc222SDavid du Colombier
388906943f9SDavid du Colombier static void*
pa2ptr(ulong pa)389906943f9SDavid du Colombier pa2ptr(ulong pa)
3906a5dc222SDavid du Colombier {
391906943f9SDavid du Colombier if(pa == 0)
3926a5dc222SDavid du Colombier return nil;
3936a5dc222SDavid du Colombier else
394906943f9SDavid du Colombier return KADDR(pa);
3956a5dc222SDavid du Colombier }
3966a5dc222SDavid du Colombier
397906943f9SDavid du Colombier static ulong
ptr2pa(void * p)398906943f9SDavid du Colombier ptr2pa(void *p)
3996a5dc222SDavid du Colombier {
400906943f9SDavid du Colombier if(p == nil)
401906943f9SDavid du Colombier return 0;
402906943f9SDavid du Colombier else
403906943f9SDavid du Colombier return PADDR(p);
4046a5dc222SDavid du Colombier }
4056a5dc222SDavid du Colombier
4066a5dc222SDavid du Colombier static void
waitSOF(Ctlr * ub)4076a5dc222SDavid du Colombier waitSOF(Ctlr *ub)
4086a5dc222SDavid du Colombier {
409906943f9SDavid du Colombier int frame = ub->hcca->framenumber & 0x3f;
4106a5dc222SDavid du Colombier
4116a5dc222SDavid du Colombier do {
4126a5dc222SDavid du Colombier delay(2);
413906943f9SDavid du Colombier } while(frame == (ub->hcca->framenumber & 0x3f));
414906943f9SDavid du Colombier }
415906943f9SDavid du Colombier
416906943f9SDavid du Colombier static char*
errmsg(int err)417906943f9SDavid du Colombier errmsg(int err)
418906943f9SDavid du Colombier {
419906943f9SDavid du Colombier
420906943f9SDavid du Colombier if(err < nelem(errmsgs))
421906943f9SDavid du Colombier return errmsgs[err];
422906943f9SDavid du Colombier return nil;
423906943f9SDavid du Colombier }
424906943f9SDavid du Colombier
425906943f9SDavid du Colombier static Ed*
ctlhd(Ctlr * ctlr)426906943f9SDavid du Colombier ctlhd(Ctlr *ctlr)
427906943f9SDavid du Colombier {
428906943f9SDavid du Colombier return pa2ptr(ctlr->ohci->ctlheaded);
429906943f9SDavid du Colombier }
430906943f9SDavid du Colombier
431906943f9SDavid du Colombier static Ed*
bulkhd(Ctlr * ctlr)432906943f9SDavid du Colombier bulkhd(Ctlr *ctlr)
433906943f9SDavid du Colombier {
434906943f9SDavid du Colombier return pa2ptr(ctlr->ohci->bulkheaded);
4356a5dc222SDavid du Colombier }
4366a5dc222SDavid du Colombier
4376a5dc222SDavid du Colombier static void
edlinked(Ed * ed,Ed * next)438906943f9SDavid du Colombier edlinked(Ed *ed, Ed *next)
4396a5dc222SDavid du Colombier {
4406a5dc222SDavid du Colombier if(ed == nil)
441906943f9SDavid du Colombier print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed));
442906943f9SDavid du Colombier ed->nexted = ptr2pa(next);
443906943f9SDavid du Colombier ed->next = next;
4446a5dc222SDavid du Colombier }
4456a5dc222SDavid du Colombier
4466a5dc222SDavid du Colombier static void
setctlhd(Ctlr * ctlr,Ed * ed)447906943f9SDavid du Colombier setctlhd(Ctlr *ctlr, Ed *ed)
4486a5dc222SDavid du Colombier {
449906943f9SDavid du Colombier ctlr->ohci->ctlheaded = ptr2pa(ed);
450906943f9SDavid du Colombier if(ed != nil)
451906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sclf; /* reload it on next pass */
4526a5dc222SDavid du Colombier }
4536a5dc222SDavid du Colombier
4546a5dc222SDavid du Colombier static void
setbulkhd(Ctlr * ctlr,Ed * ed)455906943f9SDavid du Colombier setbulkhd(Ctlr *ctlr, Ed *ed)
4566a5dc222SDavid du Colombier {
457906943f9SDavid du Colombier ctlr->ohci->bulkheaded = ptr2pa(ed);
458906943f9SDavid du Colombier if(ed != nil)
459906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sblf; /* reload it on next pass */
460906943f9SDavid du Colombier }
4616a5dc222SDavid du Colombier
462906943f9SDavid du Colombier static void
unlinkctl(Ctlr * ctlr,Ed * ed)463906943f9SDavid du Colombier unlinkctl(Ctlr *ctlr, Ed *ed)
464906943f9SDavid du Colombier {
465906943f9SDavid du Colombier Ed *this, *prev, *next;
466906943f9SDavid du Colombier
467906943f9SDavid du Colombier ctlr->ohci->control &= ~Ccle;
468906943f9SDavid du Colombier waitSOF(ctlr);
469906943f9SDavid du Colombier this = ctlhd(ctlr);
470906943f9SDavid du Colombier ctlr->ohci->ctlcurred = 0;
4716a5dc222SDavid du Colombier prev = nil;
4726a5dc222SDavid du Colombier while(this != nil && this != ed){
4736a5dc222SDavid du Colombier prev = this;
474906943f9SDavid du Colombier this = this->next;
4756a5dc222SDavid du Colombier }
4766a5dc222SDavid du Colombier if(this == nil){
477906943f9SDavid du Colombier print("unlinkctl: not found\n");
4786a5dc222SDavid du Colombier return;
4796a5dc222SDavid du Colombier }
480906943f9SDavid du Colombier next = this->next;
4816a5dc222SDavid du Colombier if(prev == nil)
482906943f9SDavid du Colombier setctlhd(ctlr, next);
4836a5dc222SDavid du Colombier else
484906943f9SDavid du Colombier edlinked(prev, next);
485906943f9SDavid du Colombier ctlr->ohci->control |= Ccle;
486906943f9SDavid du Colombier edlinked(ed, nil); /* wipe out next field */
4876a5dc222SDavid du Colombier }
4886a5dc222SDavid du Colombier
4896a5dc222SDavid du Colombier static void
unlinkbulk(Ctlr * ctlr,Ed * ed)490906943f9SDavid du Colombier unlinkbulk(Ctlr *ctlr, Ed *ed)
4916a5dc222SDavid du Colombier {
492906943f9SDavid du Colombier Ed *this, *prev, *next;
4936a5dc222SDavid du Colombier
494906943f9SDavid du Colombier ctlr->ohci->control &= ~Cble;
495906943f9SDavid du Colombier waitSOF(ctlr);
496906943f9SDavid du Colombier this = bulkhd(ctlr);
497906943f9SDavid du Colombier ctlr->ohci->bulkcurred = 0;
4986a5dc222SDavid du Colombier prev = nil;
4996a5dc222SDavid du Colombier while(this != nil && this != ed){
5006a5dc222SDavid du Colombier prev = this;
501906943f9SDavid du Colombier this = this->next;
5026a5dc222SDavid du Colombier }
503906943f9SDavid du Colombier if(this == nil){
504906943f9SDavid du Colombier print("unlinkbulk: not found\n");
505906943f9SDavid du Colombier return;
506906943f9SDavid du Colombier }
507906943f9SDavid du Colombier next = this->next;
5086a5dc222SDavid du Colombier if(prev == nil)
509906943f9SDavid du Colombier setbulkhd(ctlr, next);
5106a5dc222SDavid du Colombier else
511906943f9SDavid du Colombier edlinked(prev, next);
512906943f9SDavid du Colombier ctlr->ohci->control |= Cble;
513906943f9SDavid du Colombier edlinked(ed, nil); /* wipe out next field */
5146a5dc222SDavid du Colombier }
5156a5dc222SDavid du Colombier
5166a5dc222SDavid du Colombier static void
edsetaddr(Ed * ed,ulong addr)517906943f9SDavid du Colombier edsetaddr(Ed *ed, ulong addr)
5186a5dc222SDavid du Colombier {
5196a5dc222SDavid du Colombier ulong ctrl;
5206a5dc222SDavid du Colombier
521906943f9SDavid du Colombier ctrl = ed->ctrl & ~((Epmax<<7)|Devmax);
522906943f9SDavid du Colombier ctrl |= (addr & ((Epmax<<7)|Devmax));
523906943f9SDavid du Colombier ed->ctrl = ctrl;
5246a5dc222SDavid du Colombier }
5256a5dc222SDavid du Colombier
5266a5dc222SDavid du Colombier static void
edsettog(Ed * ed,int c)527906943f9SDavid du Colombier edsettog(Ed *ed, int c)
5286a5dc222SDavid du Colombier {
529906943f9SDavid du Colombier if(c != 0)
530906943f9SDavid du Colombier ed->head |= Edtoggle;
531906943f9SDavid du Colombier else
532906943f9SDavid du Colombier ed->head &= ~Edtoggle;
5336a5dc222SDavid du Colombier }
534906943f9SDavid du Colombier
535906943f9SDavid du Colombier static int
edtoggle(Ed * ed)536906943f9SDavid du Colombier edtoggle(Ed *ed)
537906943f9SDavid du Colombier {
538906943f9SDavid du Colombier return ed->head & Edtoggle;
539906943f9SDavid du Colombier }
540906943f9SDavid du Colombier
541906943f9SDavid du Colombier static int
edhalted(Ed * ed)542906943f9SDavid du Colombier edhalted(Ed *ed)
543906943f9SDavid du Colombier {
544906943f9SDavid du Colombier return ed->head & Edhalt;
545906943f9SDavid du Colombier }
546906943f9SDavid du Colombier
547906943f9SDavid du Colombier static int
edmaxpkt(Ed * ed)548906943f9SDavid du Colombier edmaxpkt(Ed *ed)
549906943f9SDavid du Colombier {
550906943f9SDavid du Colombier return (ed->ctrl >> Edmpsshift) & Edmpsmask;
5516a5dc222SDavid du Colombier }
5526a5dc222SDavid du Colombier
5536a5dc222SDavid du Colombier static void
edsetmaxpkt(Ed * ed,int m)554906943f9SDavid du Colombier edsetmaxpkt(Ed *ed, int m)
5556a5dc222SDavid du Colombier {
556906943f9SDavid du Colombier ulong c;
5576a5dc222SDavid du Colombier
558906943f9SDavid du Colombier c = ed->ctrl & ~(Edmpsmask << Edmpsshift);
559906943f9SDavid du Colombier ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift);
5606a5dc222SDavid du Colombier }
561906943f9SDavid du Colombier
562906943f9SDavid du Colombier static int
tderrs(Td * td)563906943f9SDavid du Colombier tderrs(Td *td)
564906943f9SDavid du Colombier {
565906943f9SDavid du Colombier return (td->ctrl >> Tdccshift) & Tdccmask;
566906943f9SDavid du Colombier }
567906943f9SDavid du Colombier
568906943f9SDavid du Colombier static int
tdtok(Td * td)569906943f9SDavid du Colombier tdtok(Td *td)
570906943f9SDavid du Colombier {
571906943f9SDavid du Colombier return (td->ctrl & Tdtokmask);
572906943f9SDavid du Colombier }
573906943f9SDavid du Colombier
574906943f9SDavid du Colombier static Td*
tdalloc(void)575906943f9SDavid du Colombier tdalloc(void)
576906943f9SDavid du Colombier {
577906943f9SDavid du Colombier Td *td;
578906943f9SDavid du Colombier Td *pool;
579906943f9SDavid du Colombier int i;
580906943f9SDavid du Colombier
581906943f9SDavid du Colombier lock(&tdpool);
582906943f9SDavid du Colombier if(tdpool.free == nil){
583906943f9SDavid du Colombier ddprint("ohci: tdalloc %d Tds\n", Incr);
584906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Td), Align, 0);
585906943f9SDavid du Colombier if(pool == nil)
586906943f9SDavid du Colombier panic("tdalloc");
587906943f9SDavid du Colombier for(i=Incr; --i>=0;){
588906943f9SDavid du Colombier pool[i].next = tdpool.free;
589906943f9SDavid du Colombier tdpool.free = &pool[i];
590906943f9SDavid du Colombier }
591906943f9SDavid du Colombier tdpool.nalloc += Incr;
592906943f9SDavid du Colombier tdpool.nfree += Incr;
593906943f9SDavid du Colombier }
594906943f9SDavid du Colombier tdpool.ninuse++;
595906943f9SDavid du Colombier tdpool.nfree--;
596906943f9SDavid du Colombier td = tdpool.free;
597906943f9SDavid du Colombier tdpool.free = td->next;
598906943f9SDavid du Colombier memset(td, 0, sizeof(Td));
599906943f9SDavid du Colombier unlock(&tdpool);
600906943f9SDavid du Colombier
601906943f9SDavid du Colombier assert(((uintptr)td & 0xF) == 0);
602906943f9SDavid du Colombier return td;
603906943f9SDavid du Colombier }
604906943f9SDavid du Colombier
605906943f9SDavid du Colombier static void
tdfree(Td * td)606906943f9SDavid du Colombier tdfree(Td *td)
607906943f9SDavid du Colombier {
608906943f9SDavid du Colombier if(td == 0)
609906943f9SDavid du Colombier return;
610906943f9SDavid du Colombier freeb(td->bp);
611906943f9SDavid du Colombier td->bp = nil;
612906943f9SDavid du Colombier lock(&tdpool);
613906943f9SDavid du Colombier if(td->nexttd == 0x77777777)
614906943f9SDavid du Colombier panic("ohci: tdfree: double free");
615906943f9SDavid du Colombier memset(td, 7, sizeof(Td)); /* poison */
616906943f9SDavid du Colombier td->next = tdpool.free;
617906943f9SDavid du Colombier tdpool.free = td;
618906943f9SDavid du Colombier tdpool.ninuse--;
619906943f9SDavid du Colombier tdpool.nfree++;
620906943f9SDavid du Colombier unlock(&tdpool);
621906943f9SDavid du Colombier }
622906943f9SDavid du Colombier
623906943f9SDavid du Colombier static Ed*
edalloc(void)624906943f9SDavid du Colombier edalloc(void)
625906943f9SDavid du Colombier {
62639dc1420SDavid du Colombier Ed *ed, *pool;
627906943f9SDavid du Colombier int i;
628906943f9SDavid du Colombier
629906943f9SDavid du Colombier lock(&edpool);
630906943f9SDavid du Colombier if(edpool.free == nil){
631906943f9SDavid du Colombier ddprint("ohci: edalloc %d Eds\n", Incr);
632906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
633906943f9SDavid du Colombier if(pool == nil)
634906943f9SDavid du Colombier panic("edalloc");
635906943f9SDavid du Colombier for(i=Incr; --i>=0;){
636906943f9SDavid du Colombier pool[i].next = edpool.free;
637906943f9SDavid du Colombier edpool.free = &pool[i];
638906943f9SDavid du Colombier }
639906943f9SDavid du Colombier edpool.nalloc += Incr;
640906943f9SDavid du Colombier edpool.nfree += Incr;
641906943f9SDavid du Colombier }
642906943f9SDavid du Colombier edpool.ninuse++;
643906943f9SDavid du Colombier edpool.nfree--;
644906943f9SDavid du Colombier ed = edpool.free;
645906943f9SDavid du Colombier edpool.free = ed->next;
646906943f9SDavid du Colombier memset(ed, 0, sizeof(Ed));
647906943f9SDavid du Colombier unlock(&edpool);
648906943f9SDavid du Colombier
649906943f9SDavid du Colombier return ed;
650906943f9SDavid du Colombier }
651906943f9SDavid du Colombier
652906943f9SDavid du Colombier static void
edfree(Ed * ed)653906943f9SDavid du Colombier edfree(Ed *ed)
654906943f9SDavid du Colombier {
655906943f9SDavid du Colombier Td *td, *next;
656906943f9SDavid du Colombier int i;
657906943f9SDavid du Colombier
658906943f9SDavid du Colombier if(ed == 0)
659906943f9SDavid du Colombier return;
660906943f9SDavid du Colombier i = 0;
661906943f9SDavid du Colombier for(td = ed->tds; td != nil; td = next){
662906943f9SDavid du Colombier next = td->next;
663906943f9SDavid du Colombier tdfree(td);
664906943f9SDavid du Colombier if(i++ > 2000){
665906943f9SDavid du Colombier print("ohci: bug: ed with more than 2000 tds\n");
666906943f9SDavid du Colombier break;
667906943f9SDavid du Colombier }
668906943f9SDavid du Colombier }
669906943f9SDavid du Colombier lock(&edpool);
670906943f9SDavid du Colombier if(ed->nexted == 0x99999999)
671906943f9SDavid du Colombier panic("ohci: edfree: double free");
672906943f9SDavid du Colombier memset(ed, 9, sizeof(Ed)); /* poison */
673906943f9SDavid du Colombier ed->next = edpool.free;
674906943f9SDavid du Colombier edpool.free = ed;
675906943f9SDavid du Colombier edpool.ninuse--;
676906943f9SDavid du Colombier edpool.nfree++;
677906943f9SDavid du Colombier unlock(&edpool);
678906943f9SDavid du Colombier ddprint("edfree: ed %#p\n", ed);
6796a5dc222SDavid du Colombier }
6806a5dc222SDavid du Colombier
6816a5dc222SDavid du Colombier /*
6826a5dc222SDavid du Colombier * return smallest power of 2 >= n
6836a5dc222SDavid du Colombier */
6846a5dc222SDavid du Colombier static int
flog2(int n)6856a5dc222SDavid du Colombier flog2(int n)
6866a5dc222SDavid du Colombier {
6876a5dc222SDavid du Colombier int i;
6886a5dc222SDavid du Colombier
6896a5dc222SDavid du Colombier for(i = 0; (1 << i) < n; i++)
6906a5dc222SDavid du Colombier ;
6916a5dc222SDavid du Colombier return i;
6926a5dc222SDavid du Colombier }
6936a5dc222SDavid du Colombier
6946a5dc222SDavid du Colombier /*
6956a5dc222SDavid du Colombier * return smallest power of 2 <= n
6966a5dc222SDavid du Colombier */
6976a5dc222SDavid du Colombier static int
flog2lower(int n)6986a5dc222SDavid du Colombier flog2lower(int n)
6996a5dc222SDavid du Colombier {
7006a5dc222SDavid du Colombier int i;
7016a5dc222SDavid du Colombier
7026a5dc222SDavid du Colombier for(i = 0; (1 << (i + 1)) <= n; i++)
7036a5dc222SDavid du Colombier ;
7046a5dc222SDavid du Colombier return i;
7056a5dc222SDavid du Colombier }
7066a5dc222SDavid du Colombier
7076a5dc222SDavid du Colombier static int
pickschedq(Qtree * qt,int pollival,ulong bw,ulong limit)708906943f9SDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
7096a5dc222SDavid du Colombier {
7106a5dc222SDavid du Colombier int i, j, d, upperb, q;
7116a5dc222SDavid du Colombier ulong best, worst, total;
7126a5dc222SDavid du Colombier
713906943f9SDavid du Colombier d = flog2lower(pollival);
7146a5dc222SDavid du Colombier if(d > qt->depth)
7156a5dc222SDavid du Colombier d = qt->depth;
7166a5dc222SDavid du Colombier q = -1;
7176a5dc222SDavid du Colombier worst = 0;
7186a5dc222SDavid du Colombier best = ~0;
7196a5dc222SDavid du Colombier upperb = (1 << (d+1)) - 1;
7206a5dc222SDavid du Colombier for(i = (1 << d) - 1; i < upperb; i++){
7216a5dc222SDavid du Colombier total = qt->bw[0];
7226a5dc222SDavid du Colombier for(j = i; j > 0; j = (j - 1) / 2)
7236a5dc222SDavid du Colombier total += qt->bw[j];
7246a5dc222SDavid du Colombier if(total < best){
7256a5dc222SDavid du Colombier best = total;
7266a5dc222SDavid du Colombier q = i;
7276a5dc222SDavid du Colombier }
7286a5dc222SDavid du Colombier if(total > worst)
7296a5dc222SDavid du Colombier worst = total;
7306a5dc222SDavid du Colombier }
7316a5dc222SDavid du Colombier if(worst + bw >= limit)
7326a5dc222SDavid du Colombier return -1;
7336a5dc222SDavid du Colombier return q;
7346a5dc222SDavid du Colombier }
7356a5dc222SDavid du Colombier
7366a5dc222SDavid du Colombier static int
schedq(Ctlr * ctlr,Qio * io,int pollival)737906943f9SDavid du Colombier schedq(Ctlr *ctlr, Qio *io, int pollival)
7386a5dc222SDavid du Colombier {
7396a5dc222SDavid du Colombier int q;
740906943f9SDavid du Colombier Ed *ted;
7416a5dc222SDavid du Colombier
742906943f9SDavid du Colombier q = pickschedq(ctlr->tree, pollival, io->bw, ~0);
743906943f9SDavid du Colombier ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw);
7446a5dc222SDavid du Colombier if(q < 0){
745906943f9SDavid du Colombier print("ohci: no room for ed\n");
7466a5dc222SDavid du Colombier return -1;
7476a5dc222SDavid du Colombier }
748906943f9SDavid du Colombier ctlr->tree->bw[q] += io->bw;
749906943f9SDavid du Colombier ted = ctlr->tree->root[q];
750906943f9SDavid du Colombier io->sched = q;
751906943f9SDavid du Colombier edlinked(io->ed, ted->next);
752906943f9SDavid du Colombier edlinked(ted, io->ed);
753906943f9SDavid du Colombier io->ed->inext = ctlr->intrhd;
754906943f9SDavid du Colombier ctlr->intrhd = io->ed;
7556a5dc222SDavid du Colombier return 0;
7566a5dc222SDavid du Colombier }
7576a5dc222SDavid du Colombier
7586a5dc222SDavid du Colombier static void
unschedq(Ctlr * ctlr,Qio * qio)759906943f9SDavid du Colombier unschedq(Ctlr *ctlr, Qio *qio)
7606a5dc222SDavid du Colombier {
7616a5dc222SDavid du Colombier int q;
762906943f9SDavid du Colombier Ed *prev, *this, *next;
763906943f9SDavid du Colombier Ed **l;
7646a5dc222SDavid du Colombier
765906943f9SDavid du Colombier q = qio->sched;
766906943f9SDavid du Colombier if(q < 0)
7676a5dc222SDavid du Colombier return;
768906943f9SDavid du Colombier ctlr->tree->bw[q] -= qio->bw;
7696a5dc222SDavid du Colombier
770906943f9SDavid du Colombier prev = ctlr->tree->root[q];
771906943f9SDavid du Colombier this = prev->next;
772906943f9SDavid du Colombier while(this != nil && this != qio->ed){
7736a5dc222SDavid du Colombier prev = this;
774906943f9SDavid du Colombier this = this->next;
7756a5dc222SDavid du Colombier }
7766a5dc222SDavid du Colombier if(this == nil)
777906943f9SDavid du Colombier print("ohci: unschedq %d: not found\n", q);
7786a5dc222SDavid du Colombier else{
779906943f9SDavid du Colombier next = this->next;
780906943f9SDavid du Colombier edlinked(prev, next);
7816a5dc222SDavid du Colombier }
782906943f9SDavid du Colombier waitSOF(ctlr);
783906943f9SDavid du Colombier for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext)
784906943f9SDavid du Colombier if(*l == qio->ed){
785906943f9SDavid du Colombier *l = (*l)->inext;
786906943f9SDavid du Colombier return;
787906943f9SDavid du Colombier }
788906943f9SDavid du Colombier print("ohci: unschedq: ed %#p not found\n", qio->ed);
7896a5dc222SDavid du Colombier }
7906a5dc222SDavid du Colombier
791906943f9SDavid du Colombier static char*
seprinttdtok(char * s,char * e,int tok)792906943f9SDavid du Colombier seprinttdtok(char *s, char *e, int tok)
7936a5dc222SDavid du Colombier {
794906943f9SDavid du Colombier switch(tok){
795906943f9SDavid du Colombier case Tdtoksetup:
796906943f9SDavid du Colombier s = seprint(s, e, " setup");
797906943f9SDavid du Colombier break;
798906943f9SDavid du Colombier case Tdtokin:
799906943f9SDavid du Colombier s = seprint(s, e, " in");
800906943f9SDavid du Colombier break;
801906943f9SDavid du Colombier case Tdtokout:
802906943f9SDavid du Colombier s = seprint(s, e, " out");
803906943f9SDavid du Colombier break;
8046a5dc222SDavid du Colombier }
805906943f9SDavid du Colombier return s;
806906943f9SDavid du Colombier }
807906943f9SDavid du Colombier
808906943f9SDavid du Colombier
809906943f9SDavid du Colombier static char*
seprinttd(char * s,char * e,Td * td,int iso)810906943f9SDavid du Colombier seprinttd(char *s, char *e, Td *td, int iso)
811906943f9SDavid du Colombier {
812906943f9SDavid du Colombier int i;
813906943f9SDavid du Colombier Block *bp;
814906943f9SDavid du Colombier
815906943f9SDavid du Colombier if(td == nil)
816906943f9SDavid du Colombier return seprint(s, e, "<nil td>\n");
817906943f9SDavid du Colombier s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl);
818906943f9SDavid du Colombier s = seprint(s, e, " cc=%#ulx", (td->ctrl >> Tdccshift) & Tdccmask);
819906943f9SDavid du Colombier if(iso == 0){
820906943f9SDavid du Colombier if((td->ctrl & Tdround) != 0)
821906943f9SDavid du Colombier s = seprint(s, e, " rnd");
822906943f9SDavid du Colombier s = seprinttdtok(s, e, td->ctrl & Tdtokmask);
823906943f9SDavid du Colombier if((td->ctrl & Tdusetog) != 0)
824906943f9SDavid du Colombier s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0);
825906943f9SDavid du Colombier else
826906943f9SDavid du Colombier s = seprint(s, e, " d-");
827906943f9SDavid du Colombier s = seprint(s, e, " ec=%uld", (td->ctrl >> Tderrshift) & Tderrmask);
828906943f9SDavid du Colombier }else{
829906943f9SDavid du Colombier s = seprint(s, e, " fc=%uld", (td->ctrl >> Tdfcshift) & Tdfcmask);
830906943f9SDavid du Colombier s = seprint(s, e, " sf=%uld", td->ctrl & Tdsfmask);
831906943f9SDavid du Colombier }
832906943f9SDavid du Colombier s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s",
833906943f9SDavid du Colombier td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : "");
834906943f9SDavid du Colombier s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes);
835906943f9SDavid du Colombier if((bp = td->bp) != nil){
836906943f9SDavid du Colombier s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp);
837906943f9SDavid du Colombier if(BLEN(bp) > 0)
838906943f9SDavid du Colombier s = seprintdata(s, e, bp->rp, bp->wp - bp->rp);
839906943f9SDavid du Colombier }
840906943f9SDavid du Colombier if(iso == 0)
841906943f9SDavid du Colombier return seprint(s, e, "\n");
842906943f9SDavid du Colombier s = seprint(s, e, "\n\t\t");
843906943f9SDavid du Colombier /* we use only offsets[0] */
844906943f9SDavid du Colombier i = 0;
845906943f9SDavid du Colombier s = seprint(s, e, "[%d] %#ux cc=%#ux sz=%ud\n", i, td->offsets[i],
846906943f9SDavid du Colombier (td->offsets[i] >> Tdiccshift) & Tdiccmask,
847906943f9SDavid du Colombier td->offsets[i] & 0x7FF);
848906943f9SDavid du Colombier return s;
849906943f9SDavid du Colombier }
850906943f9SDavid du Colombier
851906943f9SDavid du Colombier static void
dumptd(Td * td,char * p,int iso)852906943f9SDavid du Colombier dumptd(Td *td, char *p, int iso)
853906943f9SDavid du Colombier {
854906943f9SDavid du Colombier static char buf[512]; /* Too much */
855906943f9SDavid du Colombier char *s;
856906943f9SDavid du Colombier
857906943f9SDavid du Colombier s = seprint(buf, buf+sizeof(buf), "%s: ", p);
858906943f9SDavid du Colombier s = seprinttd(s, buf+sizeof(buf), td, iso);
859906943f9SDavid du Colombier if(s > buf && s[-1] != '\n')
860906943f9SDavid du Colombier s[-1] = '\n';
861906943f9SDavid du Colombier print("\t%s", buf);
862906943f9SDavid du Colombier }
863906943f9SDavid du Colombier
864906943f9SDavid du Colombier static void
dumptds(Td * td,char * p,int iso)865906943f9SDavid du Colombier dumptds(Td *td, char *p, int iso)
866906943f9SDavid du Colombier {
867906943f9SDavid du Colombier int i;
868906943f9SDavid du Colombier
869906943f9SDavid du Colombier for(i = 0; td != nil; td = td->next){
870906943f9SDavid du Colombier dumptd(td, p, iso);
871906943f9SDavid du Colombier if(td->last)
872906943f9SDavid du Colombier break;
873906943f9SDavid du Colombier if(tdtok(td) == Tdtokin && ++i > 2){
874906943f9SDavid du Colombier print("\t\t...\n");
875906943f9SDavid du Colombier break;
876906943f9SDavid du Colombier }
877906943f9SDavid du Colombier }
878906943f9SDavid du Colombier }
879906943f9SDavid du Colombier
880906943f9SDavid du Colombier static void
dumped(Ed * ed)881906943f9SDavid du Colombier dumped(Ed *ed)
882906943f9SDavid du Colombier {
88339dc1420SDavid du Colombier char *buf, *s, *e;
884906943f9SDavid du Colombier
885906943f9SDavid du Colombier if(ed == nil){
886906943f9SDavid du Colombier print("<null ed>\n");
887906943f9SDavid du Colombier return;
888906943f9SDavid du Colombier }
889906943f9SDavid du Colombier buf = malloc(512);
890906943f9SDavid du Colombier /* no waserror; may want to use from interrupt context */
891906943f9SDavid du Colombier if(buf == nil)
892906943f9SDavid du Colombier return;
893906943f9SDavid du Colombier e = buf+512;
894906943f9SDavid du Colombier s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl);
895906943f9SDavid du Colombier if((ed->ctrl & Edskip) != 0)
896906943f9SDavid du Colombier s = seprint(s, e, " skip");
897906943f9SDavid du Colombier if((ed->ctrl & Ediso) != 0)
898906943f9SDavid du Colombier s = seprint(s, e, " iso");
899906943f9SDavid du Colombier if((ed->ctrl & Edlow) != 0)
900906943f9SDavid du Colombier s = seprint(s, e, " low");
901906943f9SDavid du Colombier s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0);
902906943f9SDavid du Colombier if((ed->ctrl & Eddirmask) == Edin)
903906943f9SDavid du Colombier s = seprint(s, e, " in");
904906943f9SDavid du Colombier if((ed->ctrl & Eddirmask) == Edout)
905906943f9SDavid du Colombier s = seprint(s, e, " out");
906906943f9SDavid du Colombier if(edhalted(ed))
907906943f9SDavid du Colombier s = seprint(s, e, " hlt");
908906943f9SDavid du Colombier s = seprint(s, e, " ep%uld.%uld", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f);
909906943f9SDavid du Colombier s = seprint(s, e, " maxpkt %uld", (ed->ctrl>>Edmpsshift)&Edmpsmask);
910906943f9SDavid du Colombier seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted);
911906943f9SDavid du Colombier print("%s", buf);
912906943f9SDavid du Colombier free(buf);
913906943f9SDavid du Colombier if(ed->tds != nil && (ed->ctrl & Ediso) == 0)
914906943f9SDavid du Colombier dumptds(ed->tds, "td", 0);
915906943f9SDavid du Colombier }
916906943f9SDavid du Colombier
917906943f9SDavid du Colombier static char*
seprintio(char * s,char * e,Qio * io,char * pref)918906943f9SDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
919906943f9SDavid du Colombier {
920906943f9SDavid du Colombier s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed);
921906943f9SDavid du Colombier s = seprint(s, e, " tog %d iot %ld err %s id %#ulx",
922906943f9SDavid du Colombier io->toggle, io->iotime, io->err, io->usbid);
923906943f9SDavid du Colombier s = seprinttdtok(s, e, io->tok);
924906943f9SDavid du Colombier s = seprint(s, e, " %s\n", iosname[io->state]);
925906943f9SDavid du Colombier return s;
926906943f9SDavid du Colombier }
927906943f9SDavid du Colombier
928906943f9SDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)929906943f9SDavid du Colombier seprintep(char* s, char* e, Ep *ep)
930906943f9SDavid du Colombier {
931906943f9SDavid du Colombier Isoio *iso;
932906943f9SDavid du Colombier Qio *io;
933906943f9SDavid du Colombier Ctlio *cio;
934906943f9SDavid du Colombier
935906943f9SDavid du Colombier if(ep == nil)
936906943f9SDavid du Colombier return seprint(s, e, "<nil ep>\n");
937906943f9SDavid du Colombier if(ep->aux == nil)
938906943f9SDavid du Colombier return seprint(s, e, "no mdep\n");
939906943f9SDavid du Colombier switch(ep->ttype){
940906943f9SDavid du Colombier case Tctl:
941906943f9SDavid du Colombier cio = ep->aux;
942906943f9SDavid du Colombier s = seprintio(s, e, cio, "c");
943906943f9SDavid du Colombier s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
944906943f9SDavid du Colombier break;
945906943f9SDavid du Colombier case Tbulk:
946906943f9SDavid du Colombier case Tintr:
947906943f9SDavid du Colombier io = ep->aux;
948906943f9SDavid du Colombier if(ep->mode != OWRITE)
949906943f9SDavid du Colombier s = seprintio(s, e, &io[OREAD], "r");
950906943f9SDavid du Colombier if(ep->mode != OREAD)
951906943f9SDavid du Colombier s = seprintio(s, e, &io[OWRITE], "w");
952906943f9SDavid du Colombier break;
953906943f9SDavid du Colombier case Tiso:
954906943f9SDavid du Colombier iso = ep->aux;
955906943f9SDavid du Colombier s = seprintio(s, e, iso, "w");
956906943f9SDavid du Colombier s = seprint(s, e, "\tntds %d avail %d frno %uld left %uld next avail %#p\n",
957906943f9SDavid du Colombier iso->nframes, iso->navail, iso->frno, iso->left, iso->atds);
958906943f9SDavid du Colombier break;
959906943f9SDavid du Colombier }
960906943f9SDavid du Colombier return s;
961906943f9SDavid du Colombier }
962906943f9SDavid du Colombier
963906943f9SDavid du Colombier static char*
seprintctl(char * s,char * se,ulong ctl)964906943f9SDavid du Colombier seprintctl(char *s, char *se, ulong ctl)
965906943f9SDavid du Colombier {
966906943f9SDavid du Colombier s = seprint(s, se, "en=");
967906943f9SDavid du Colombier if((ctl&Cple) != 0)
968906943f9SDavid du Colombier s = seprint(s, se, "p");
969906943f9SDavid du Colombier if((ctl&Cie) != 0)
970906943f9SDavid du Colombier s = seprint(s, se, "i");
971906943f9SDavid du Colombier if((ctl&Ccle) != 0)
972906943f9SDavid du Colombier s = seprint(s, se, "c");
973906943f9SDavid du Colombier if((ctl&Cble) != 0)
974906943f9SDavid du Colombier s = seprint(s, se, "b");
975906943f9SDavid du Colombier switch(ctl & Cfsmask){
976906943f9SDavid du Colombier case Cfsreset:
977906943f9SDavid du Colombier return seprint(s, se, " reset");
978906943f9SDavid du Colombier case Cfsresume:
979906943f9SDavid du Colombier return seprint(s, se, " resume");
980906943f9SDavid du Colombier case Cfsoper:
981906943f9SDavid du Colombier return seprint(s, se, " run");
982906943f9SDavid du Colombier case Cfssuspend:
983906943f9SDavid du Colombier return seprint(s, se, " suspend");
984906943f9SDavid du Colombier default:
985906943f9SDavid du Colombier return seprint(s, se, " ???");
986906943f9SDavid du Colombier }
987906943f9SDavid du Colombier }
988906943f9SDavid du Colombier
989906943f9SDavid du Colombier static void
dump(Hci * hp)990906943f9SDavid du Colombier dump(Hci *hp)
991906943f9SDavid du Colombier {
992906943f9SDavid du Colombier Ctlr *ctlr;
993906943f9SDavid du Colombier Ed *ed;
994906943f9SDavid du Colombier char cs[20];
995906943f9SDavid du Colombier
996906943f9SDavid du Colombier ctlr = hp->aux;
997906943f9SDavid du Colombier ilock(ctlr);
998906943f9SDavid du Colombier seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control);
999906943f9SDavid du Colombier print("ohci ctlr %#p: frno %#ux ctl %#lux %s sts %#lux intr %#lux\n",
1000906943f9SDavid du Colombier ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs,
1001906943f9SDavid du Colombier ctlr->ohci->cmdsts, ctlr->ohci->intrsts);
1002906943f9SDavid du Colombier print("ctlhd %#ulx cur %#ulx bulkhd %#ulx cur %#ulx done %#ulx\n",
1003906943f9SDavid du Colombier ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred,
1004906943f9SDavid du Colombier ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred,
1005906943f9SDavid du Colombier ctlr->ohci->donehead);
1006906943f9SDavid du Colombier if(ctlhd(ctlr) != nil)
1007906943f9SDavid du Colombier print("[ctl]\n");
1008906943f9SDavid du Colombier for(ed = ctlhd(ctlr); ed != nil; ed = ed->next)
1009906943f9SDavid du Colombier dumped(ed);
1010906943f9SDavid du Colombier if(bulkhd(ctlr) != nil)
1011906943f9SDavid du Colombier print("[bulk]\n");
1012906943f9SDavid du Colombier for(ed = bulkhd(ctlr); ed != nil; ed = ed->next)
1013906943f9SDavid du Colombier dumped(ed);
1014906943f9SDavid du Colombier if(ctlr->intrhd != nil)
1015906943f9SDavid du Colombier print("[intr]\n");
1016906943f9SDavid du Colombier for(ed = ctlr->intrhd; ed != nil; ed = ed->inext)
1017906943f9SDavid du Colombier dumped(ed);
1018906943f9SDavid du Colombier if(ctlr->tree->root[0]->next != nil)
1019906943f9SDavid du Colombier print("[iso]");
1020906943f9SDavid du Colombier for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next)
1021906943f9SDavid du Colombier dumped(ed);
1022906943f9SDavid du Colombier print("%d eds in tree\n", ctlr->ntree);
1023906943f9SDavid du Colombier iunlock(ctlr);
1024906943f9SDavid du Colombier lock(&tdpool);
1025906943f9SDavid du Colombier print("%d tds allocated = %d in use + %d free\n",
1026906943f9SDavid du Colombier tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
1027906943f9SDavid du Colombier unlock(&tdpool);
1028906943f9SDavid du Colombier lock(&edpool);
1029906943f9SDavid du Colombier print("%d eds allocated = %d in use + %d free\n",
1030906943f9SDavid du Colombier edpool.nalloc, edpool.ninuse, edpool.nfree);
1031906943f9SDavid du Colombier unlock(&edpool);
1032906943f9SDavid du Colombier }
1033906943f9SDavid du Colombier
1034906943f9SDavid du Colombier /*
1035906943f9SDavid du Colombier * Compute size for the next iso Td and setup its
1036906943f9SDavid du Colombier * descriptor for I/O according to the buffer size.
1037906943f9SDavid du Colombier */
1038906943f9SDavid du Colombier static void
isodtdinit(Ep * ep,Isoio * iso,Td * td)1039906943f9SDavid du Colombier isodtdinit(Ep *ep, Isoio *iso, Td *td)
1040906943f9SDavid du Colombier {
1041906943f9SDavid du Colombier Block *bp;
1042906943f9SDavid du Colombier long size;
1043906943f9SDavid du Colombier int i;
1044906943f9SDavid du Colombier
1045906943f9SDavid du Colombier bp = td->bp;
1046906943f9SDavid du Colombier assert(bp != nil && BLEN(bp) == 0);
1047906943f9SDavid du Colombier size = (ep->hz+iso->left) * ep->pollival / 1000;
1048906943f9SDavid du Colombier iso->left = (ep->hz+iso->left) * ep->pollival % 1000;
1049906943f9SDavid du Colombier size *= ep->samplesz;
1050906943f9SDavid du Colombier if(size > ep->maxpkt){
1051906943f9SDavid du Colombier print("ohci: ep%d.%d: size > maxpkt\n",
1052906943f9SDavid du Colombier ep->dev->nb, ep->nb);
1053906943f9SDavid du Colombier print("size = %uld max = %ld\n", size, ep->maxpkt);
1054906943f9SDavid du Colombier size = ep->maxpkt;
1055906943f9SDavid du Colombier }
1056906943f9SDavid du Colombier td->nbytes = size;
1057906943f9SDavid du Colombier memset(bp->wp, 0, size); /* in case we don't fill it on time */
1058906943f9SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF;
1059906943f9SDavid du Colombier td->ctrl = TRUNC(iso->frno, Ntdframes);
1060906943f9SDavid du Colombier td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF);
1061906943f9SDavid du Colombier td->offsets[0] |= (Tdnotacc << Tdiccshift);
1062906943f9SDavid du Colombier /* in case the controller checks out the offests... */
1063906943f9SDavid du Colombier for(i = 1; i < nelem(td->offsets); i++)
1064906943f9SDavid du Colombier td->offsets[i] = td->offsets[0];
1065906943f9SDavid du Colombier td->be = ptr2pa(bp->rp + size - 1);
1066906943f9SDavid du Colombier td->ctrl |= (0 << Tdfcshift); /* frame count is 1 */
1067906943f9SDavid du Colombier
1068906943f9SDavid du Colombier iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes);
1069906943f9SDavid du Colombier }
1070906943f9SDavid du Colombier
1071906943f9SDavid du Colombier /*
1072906943f9SDavid du Colombier * start I/O on the dummy td and setup a new dummy to fill up.
1073906943f9SDavid du Colombier */
1074906943f9SDavid du Colombier static void
isoadvance(Ep * ep,Isoio * iso,Td * td)1075906943f9SDavid du Colombier isoadvance(Ep *ep, Isoio *iso, Td *td)
1076906943f9SDavid du Colombier {
1077906943f9SDavid du Colombier Td *dtd;
1078906943f9SDavid du Colombier
1079906943f9SDavid du Colombier dtd = iso->atds;
1080906943f9SDavid du Colombier iso->atds = dtd->anext;
1081906943f9SDavid du Colombier iso->navail--;
1082906943f9SDavid du Colombier dtd->anext = nil;
1083906943f9SDavid du Colombier dtd->bp->wp = dtd->bp->rp;
1084906943f9SDavid du Colombier dtd->nexttd = 0;
1085906943f9SDavid du Colombier td->nexttd = ptr2pa(dtd);
1086906943f9SDavid du Colombier isodtdinit(ep, iso, dtd);
1087906943f9SDavid du Colombier iso->ed->tail = ptr2pa(dtd);
1088906943f9SDavid du Colombier }
1089906943f9SDavid du Colombier
1090906943f9SDavid du Colombier static int
isocanwrite(void * a)1091906943f9SDavid du Colombier isocanwrite(void *a)
1092906943f9SDavid du Colombier {
1093906943f9SDavid du Colombier Isoio *iso;
1094906943f9SDavid du Colombier
1095906943f9SDavid du Colombier iso = a;
1096906943f9SDavid du Colombier return iso->state == Qclose || iso->err != nil ||
1097906943f9SDavid du Colombier iso->navail > iso->nframes / 2;
1098906943f9SDavid du Colombier }
1099906943f9SDavid du Colombier
1100906943f9SDavid du Colombier /*
1101906943f9SDavid du Colombier * Service a completed/failed Td from the done queue.
1102906943f9SDavid du Colombier * It may be of any transfer type.
1103906943f9SDavid du Colombier * The queue is not in completion order.
1104906943f9SDavid du Colombier * (It's actually in reverse completion order).
1105906943f9SDavid du Colombier *
1106906943f9SDavid du Colombier * When an error, a short packet, or a last Td is found
1107906943f9SDavid du Colombier * we awake the process waiting for the transfer.
1108906943f9SDavid du Colombier * Although later we will process other Tds completed
1109906943f9SDavid du Colombier * before, epio won't be able to touch the current Td
1110906943f9SDavid du Colombier * until interrupt returns and releases the lock on the
1111906943f9SDavid du Colombier * controller.
1112906943f9SDavid du Colombier */
1113906943f9SDavid du Colombier static void
qhinterrupt(Ctlr *,Ep * ep,Qio * io,Td * td,int)1114906943f9SDavid du Colombier qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int)
1115906943f9SDavid du Colombier {
1116906943f9SDavid du Colombier Block *bp;
111739dc1420SDavid du Colombier int mode, err;
1118906943f9SDavid du Colombier Ed *ed;
1119906943f9SDavid du Colombier
1120906943f9SDavid du Colombier ed = io->ed;
1121906943f9SDavid du Colombier if(io->state != Qrun)
1122906943f9SDavid du Colombier return;
1123906943f9SDavid du Colombier if(tdtok(td) == Tdtokin)
1124906943f9SDavid du Colombier mode = OREAD;
1125906943f9SDavid du Colombier else
1126906943f9SDavid du Colombier mode = OWRITE;
1127906943f9SDavid du Colombier bp = td->bp;
1128906943f9SDavid du Colombier err = tderrs(td);
1129906943f9SDavid du Colombier
1130906943f9SDavid du Colombier switch(err){
1131906943f9SDavid du Colombier case Tddataovr: /* Overrun is not an error */
1132906943f9SDavid du Colombier break;
11333be656c8SDavid du Colombier case Tdok:
11343be656c8SDavid du Colombier /* virtualbox doesn't always report underflow on short packets */
11353be656c8SDavid du Colombier if(td->cbp == 0)
11363be656c8SDavid du Colombier break;
11373be656c8SDavid du Colombier /* fall through */
1138906943f9SDavid du Colombier case Tddataund:
1139906943f9SDavid du Colombier /* short input packets are ok */
1140906943f9SDavid du Colombier if(mode == OREAD){
1141906943f9SDavid du Colombier if(td->cbp == 0)
1142906943f9SDavid du Colombier panic("ohci: short packet but cbp == 0");
1143db0946c0SDavid du Colombier /*
1144db0946c0SDavid du Colombier * td->cbp and td->cbp0 are the real addresses
1145db0946c0SDavid du Colombier * corresponding to virtual addresses bp->wp and
1146db0946c0SDavid du Colombier * bp->rp respectively.
1147db0946c0SDavid du Colombier */
1148db0946c0SDavid du Colombier bp->wp = bp->rp + (td->cbp - td->cbp0);
1149906943f9SDavid du Colombier if(bp->wp < bp->rp)
1150906943f9SDavid du Colombier panic("ohci: wp < rp");
1151906943f9SDavid du Colombier /*
1152906943f9SDavid du Colombier * It's ok. clear error and flag as last in xfer.
1153906943f9SDavid du Colombier * epio must ignore following Tds.
1154906943f9SDavid du Colombier */
1155906943f9SDavid du Colombier td->last = 1;
1156906943f9SDavid du Colombier td->ctrl &= ~(Tdccmask << Tdccshift);
1157906943f9SDavid du Colombier break;
1158906943f9SDavid du Colombier }
1159906943f9SDavid du Colombier /* else fall; it's an error */
1160906943f9SDavid du Colombier case Tdcrc:
1161906943f9SDavid du Colombier case Tdbitstuff:
1162906943f9SDavid du Colombier case Tdbadtog:
1163906943f9SDavid du Colombier case Tdstalled:
1164906943f9SDavid du Colombier case Tdtmout:
1165906943f9SDavid du Colombier case Tdpidchk:
1166906943f9SDavid du Colombier case Tdbadpid:
1167906943f9SDavid du Colombier bp->wp = bp->rp; /* no bytes in xfer. */
1168906943f9SDavid du Colombier io->err = errmsg(err);
1169906943f9SDavid du Colombier if(debug || ep->debug){
1170906943f9SDavid du Colombier print("tdinterrupt: failed err %d (%s)\n", err, io->err);
1171906943f9SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso);
1172906943f9SDavid du Colombier }
1173906943f9SDavid du Colombier td->last = 1;
1174906943f9SDavid du Colombier break;
1175906943f9SDavid du Colombier default:
1176c9b6d007SDavid du Colombier panic("ohci: td cc %ud unknown", err);
1177906943f9SDavid du Colombier }
1178906943f9SDavid du Colombier
1179906943f9SDavid du Colombier if(td->last != 0){
1180906943f9SDavid du Colombier /*
1181906943f9SDavid du Colombier * clear td list and halt flag.
1182906943f9SDavid du Colombier */
1183906943f9SDavid du Colombier ed->head = (ed->head & Edtoggle) | ed->tail;
1184906943f9SDavid du Colombier ed->tds = pa2ptr(ed->tail);
1185906943f9SDavid du Colombier io->state = Qdone;
1186906943f9SDavid du Colombier wakeup(io);
1187906943f9SDavid du Colombier }
1188906943f9SDavid du Colombier }
1189906943f9SDavid du Colombier
1190906943f9SDavid du Colombier /*
1191906943f9SDavid du Colombier * BUG: Iso input streams are not implemented.
1192906943f9SDavid du Colombier */
1193906943f9SDavid du Colombier static void
isointerrupt(Ctlr * ctlr,Ep * ep,Qio * io,Td * td,int)1194906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int)
1195906943f9SDavid du Colombier {
1196906943f9SDavid du Colombier Isoio *iso;
1197906943f9SDavid du Colombier Block *bp;
1198906943f9SDavid du Colombier Ed *ed;
1199d37e33ffSDavid du Colombier int err, isoerr;
1200906943f9SDavid du Colombier
1201906943f9SDavid du Colombier iso = ep->aux;
1202906943f9SDavid du Colombier ed = io->ed;
1203906943f9SDavid du Colombier if(io->state == Qclose)
1204906943f9SDavid du Colombier return;
1205906943f9SDavid du Colombier bp = td->bp;
1206906943f9SDavid du Colombier /*
1207906943f9SDavid du Colombier * When we get more than half the frames consecutive errors
1208906943f9SDavid du Colombier * we signal an actual error. Errors in the entire Td are
1209906943f9SDavid du Colombier * more serious and are always singaled.
1210906943f9SDavid du Colombier * Errors like overrun are not really errors. In fact, for
1211906943f9SDavid du Colombier * output, errors cannot be really detected. The driver will
1212906943f9SDavid du Colombier * hopefully notice I/O errors on input endpoints and detach the device.
1213906943f9SDavid du Colombier */
1214906943f9SDavid du Colombier err = tderrs(td);
1215906943f9SDavid du Colombier isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask;
1216906943f9SDavid du Colombier if(isoerr == Tdok || isoerr == Tdnotacc)
1217906943f9SDavid du Colombier iso->nerrs = 0;
1218906943f9SDavid du Colombier else if(iso->nerrs++ > iso->nframes/2)
1219906943f9SDavid du Colombier err = Tdstalled;
1220906943f9SDavid du Colombier if(err != Tdok && err != Tddataovr){
1221906943f9SDavid du Colombier bp->wp = bp->rp;
1222906943f9SDavid du Colombier io->err = errmsg(err);
1223906943f9SDavid du Colombier if(debug || ep->debug){
1224906943f9SDavid du Colombier print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lux\n",
1225906943f9SDavid du Colombier ep->dev->nb, ep->nb,
1226906943f9SDavid du Colombier err, errmsg(err), ctlr->ohci->fmnumber);
1227906943f9SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso);
1228906943f9SDavid du Colombier }
1229906943f9SDavid du Colombier }
1230906943f9SDavid du Colombier td->bp->wp = td->bp->rp;
1231906943f9SDavid du Colombier td->nbytes = 0;
1232906943f9SDavid du Colombier td->anext = iso->atds;
1233906943f9SDavid du Colombier iso->atds = td;
1234906943f9SDavid du Colombier iso->navail++;
1235906943f9SDavid du Colombier /*
1236906943f9SDavid du Colombier * If almost all Tds are avail the user is not doing I/O at the
1237906943f9SDavid du Colombier * required rate. We put another Td in place to keep the polling rate.
1238906943f9SDavid du Colombier */
1239906943f9SDavid du Colombier if(iso->err == nil && iso->navail > iso->nframes - 10)
1240906943f9SDavid du Colombier isoadvance(ep, iso, pa2ptr(iso->ed->tail));
1241906943f9SDavid du Colombier /*
1242906943f9SDavid du Colombier * If there's enough buffering futher I/O can be done.
1243906943f9SDavid du Colombier */
1244906943f9SDavid du Colombier if(isocanwrite(iso))
1245906943f9SDavid du Colombier wakeup(iso);
1246906943f9SDavid du Colombier }
1247906943f9SDavid du Colombier
1248906943f9SDavid du Colombier static void
interrupt(Ureg *,void * arg)1249906943f9SDavid du Colombier interrupt(Ureg *, void *arg)
1250906943f9SDavid du Colombier {
1251*9d244d11SDavid du Colombier Td *td, *ntd;
1252906943f9SDavid du Colombier Hci *hp;
1253906943f9SDavid du Colombier Ctlr *ctlr;
125439dc1420SDavid du Colombier ulong status, curred;
125539dc1420SDavid du Colombier int i, frno;
1256906943f9SDavid du Colombier
1257906943f9SDavid du Colombier hp = arg;
1258906943f9SDavid du Colombier ctlr = hp->aux;
1259906943f9SDavid du Colombier ilock(ctlr);
1260*9d244d11SDavid du Colombier ctlr->ohci->intrdisable = Mie;
1261*9d244d11SDavid du Colombier coherence();
1262*9d244d11SDavid du Colombier status = ctlr->ohci->intrsts & ctlr->ohci->intrenable;
1263906943f9SDavid du Colombier status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
1264906943f9SDavid du Colombier frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
1265*9d244d11SDavid du Colombier if(status & Wdh){
1266906943f9SDavid du Colombier /* lsb of donehead has bit to flag other intrs. */
1267906943f9SDavid du Colombier td = pa2ptr(ctlr->hcca->donehead & ~0xF);
1268906943f9SDavid du Colombier
1269906943f9SDavid du Colombier for(i = 0; td != nil && i < 1024; i++){
1270906943f9SDavid du Colombier if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
1271906943f9SDavid du Colombier ntd = pa2ptr(td->nexttd & ~0xF);
1272906943f9SDavid du Colombier td->nexttd = 0;
1273906943f9SDavid du Colombier if(td->ep == nil || td->io == nil)
1274*9d244d11SDavid du Colombier panic("ohci: interrupt: ep %#p io %#p",
1275*9d244d11SDavid du Colombier td->ep, td->io);
1276906943f9SDavid du Colombier ohciinterrupts[td->ep->ttype]++;
1277906943f9SDavid du Colombier if(td->ep->ttype == Tiso)
1278906943f9SDavid du Colombier isointerrupt(ctlr, td->ep, td->io, td, frno);
1279906943f9SDavid du Colombier else
1280906943f9SDavid du Colombier qhinterrupt(ctlr, td->ep, td->io, td, frno);
1281906943f9SDavid du Colombier td = ntd;
1282906943f9SDavid du Colombier }
1283*9d244d11SDavid du Colombier if(i >= 1024)
1284906943f9SDavid du Colombier print("ohci: bug: more than 1024 done Tds?\n");
1285906943f9SDavid du Colombier ctlr->hcca->donehead = 0;
1286*9d244d11SDavid du Colombier }
1287906943f9SDavid du Colombier
1288906943f9SDavid du Colombier ctlr->ohci->intrsts = status;
1289906943f9SDavid du Colombier status &= ~Wdh;
1290906943f9SDavid du Colombier status &= ~Sf;
1291906943f9SDavid du Colombier if(status & So){
1292906943f9SDavid du Colombier print("ohci: sched overrun: too much load\n");
1293906943f9SDavid du Colombier ctlr->overrun++;
1294906943f9SDavid du Colombier status &= ~So;
1295906943f9SDavid du Colombier }
1296906943f9SDavid du Colombier if((status & Ue) != 0){
1297906943f9SDavid du Colombier curred = ctlr->ohci->periodcurred;
1298906943f9SDavid du Colombier print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
1299906943f9SDavid du Colombier "ints %d %d %d %d\n",
1300906943f9SDavid du Colombier ctlr->ohci->fmnumber, curred,
1301906943f9SDavid du Colombier ohciinterrupts[Tctl], ohciinterrupts[Tintr],
1302906943f9SDavid du Colombier ohciinterrupts[Tbulk], ohciinterrupts[Tiso]);
1303906943f9SDavid du Colombier if(curred != 0)
1304906943f9SDavid du Colombier dumped(pa2ptr(curred));
1305906943f9SDavid du Colombier status &= ~Ue;
1306906943f9SDavid du Colombier }
1307906943f9SDavid du Colombier if(status != 0)
1308906943f9SDavid du Colombier print("ohci interrupt: unhandled sts 0x%.8lux\n", status);
1309*9d244d11SDavid du Colombier ctlr->ohci->intrenable = Mie | Wdh | Ue;
1310906943f9SDavid du Colombier iunlock(ctlr);
1311906943f9SDavid du Colombier }
1312906943f9SDavid du Colombier
1313906943f9SDavid du Colombier /*
1314906943f9SDavid du Colombier * The old dummy Td is used to implement the new Td.
1315906943f9SDavid du Colombier * A new dummy is linked at the end of the old one and
1316906943f9SDavid du Colombier * returned, to link further Tds if needed.
1317906943f9SDavid du Colombier */
1318906943f9SDavid du Colombier static Td*
epgettd(Ep * ep,Qio * io,Td ** dtdp,int flags,void * a,int count)1319906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count)
1320906943f9SDavid du Colombier {
132139dc1420SDavid du Colombier Td *td, *dtd;
1322906943f9SDavid du Colombier Block *bp;
1323906943f9SDavid du Colombier
13243be656c8SDavid du Colombier if(count <= BY2PG)
13253be656c8SDavid du Colombier bp = allocb(count);
13263be656c8SDavid du Colombier else{
13273be656c8SDavid du Colombier if(count > 2*BY2PG)
13283be656c8SDavid du Colombier panic("ohci: transfer > two pages");
13293be656c8SDavid du Colombier /* maximum of one physical page crossing allowed */
13303be656c8SDavid du Colombier bp = allocb(count+BY2PG);
13313be656c8SDavid du Colombier bp->rp = (uchar*)PGROUND((uintptr)bp->rp);
13323be656c8SDavid du Colombier bp->wp = bp->rp;
13333be656c8SDavid du Colombier }
1334906943f9SDavid du Colombier dtd = *dtdp;
1335906943f9SDavid du Colombier td = dtd;
1336906943f9SDavid du Colombier td->bp = bp;
1337906943f9SDavid du Colombier if(count > 0){
1338906943f9SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->wp);
1339906943f9SDavid du Colombier td->be = ptr2pa(bp->wp + count - 1);
1340906943f9SDavid du Colombier if(a != nil){
1341c8cbc0e9SDavid du Colombier /* validaddr((uintptr)a, count, 0); DEBUG */
1342906943f9SDavid du Colombier memmove(bp->wp, a, count);
1343906943f9SDavid du Colombier }
1344906943f9SDavid du Colombier bp->wp += count;
1345906943f9SDavid du Colombier }
1346906943f9SDavid du Colombier td->nbytes = count;
1347906943f9SDavid du Colombier td->ctrl = io->tok|Tdusetog|io->toggle|flags;
1348906943f9SDavid du Colombier if(io->toggle == Tddata0)
1349906943f9SDavid du Colombier io->toggle = Tddata1;
1350906943f9SDavid du Colombier else
1351906943f9SDavid du Colombier io->toggle = Tddata0;
1352906943f9SDavid du Colombier assert(td->ep == ep);
1353906943f9SDavid du Colombier td->io = io;
1354906943f9SDavid du Colombier dtd = tdalloc(); /* new dummy */
1355906943f9SDavid du Colombier dtd->ep = ep;
1356906943f9SDavid du Colombier td->nexttd = ptr2pa(dtd);
1357906943f9SDavid du Colombier td->next = dtd;
1358906943f9SDavid du Colombier *dtdp = dtd;
1359906943f9SDavid du Colombier return td;
1360906943f9SDavid du Colombier }
1361906943f9SDavid du Colombier
1362906943f9SDavid du Colombier /*
1363906943f9SDavid du Colombier * Try to get them idle
1364906943f9SDavid du Colombier */
1365906943f9SDavid du Colombier static void
aborttds(Qio * io)1366906943f9SDavid du Colombier aborttds(Qio *io)
1367906943f9SDavid du Colombier {
1368906943f9SDavid du Colombier Ed *ed;
1369906943f9SDavid du Colombier Td *td;
1370906943f9SDavid du Colombier
1371906943f9SDavid du Colombier ed = io->ed;
1372db0946c0SDavid du Colombier if(ed == nil)
1373db0946c0SDavid du Colombier return;
1374906943f9SDavid du Colombier ed->ctrl |= Edskip;
1375906943f9SDavid du Colombier for(td = ed->tds; td != nil; td = td->next)
1376906943f9SDavid du Colombier if(td->bp != nil)
1377906943f9SDavid du Colombier td->bp->wp = td->bp->rp;
1378906943f9SDavid du Colombier ed->head = (ed->head&0xF) | ed->tail;
1379906943f9SDavid du Colombier if((ed->ctrl & Ediso) == 0)
1380906943f9SDavid du Colombier ed->tds = pa2ptr(ed->tail);
1381906943f9SDavid du Colombier }
1382906943f9SDavid du Colombier
1383906943f9SDavid du Colombier static int
epiodone(void * a)1384906943f9SDavid du Colombier epiodone(void *a)
1385906943f9SDavid du Colombier {
1386906943f9SDavid du Colombier Qio *io;
1387906943f9SDavid du Colombier
1388906943f9SDavid du Colombier io = a;
1389906943f9SDavid du Colombier return io->state != Qrun;
1390906943f9SDavid du Colombier }
1391906943f9SDavid du Colombier
1392906943f9SDavid du Colombier static void
epiowait(Ctlr * ctlr,Qio * io,int tmout,ulong)1393906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong)
1394906943f9SDavid du Colombier {
1395906943f9SDavid du Colombier Ed *ed;
1396906943f9SDavid du Colombier int timedout;
1397906943f9SDavid du Colombier
1398906943f9SDavid du Colombier ed = io->ed;
1399906943f9SDavid du Colombier if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n",
1400906943f9SDavid du Colombier io, ed, iosname[io->state]);
1401906943f9SDavid du Colombier timedout = 0;
1402906943f9SDavid du Colombier if(waserror()){
1403906943f9SDavid du Colombier dqprint("ohci io %#p ed %#p timed out\n", io, ed);
1404906943f9SDavid du Colombier timedout++;
1405906943f9SDavid du Colombier }else{
1406906943f9SDavid du Colombier if(tmout == 0)
1407906943f9SDavid du Colombier sleep(io, epiodone, io);
1408906943f9SDavid du Colombier else
1409906943f9SDavid du Colombier tsleep(io, epiodone, io, tmout);
14106a5dc222SDavid du Colombier poperror();
14116a5dc222SDavid du Colombier }
1412906943f9SDavid du Colombier ilock(ctlr);
1413906943f9SDavid du Colombier if(io->state == Qrun)
1414906943f9SDavid du Colombier timedout = 1;
1415906943f9SDavid du Colombier else if(io->state != Qdone && io->state != Qclose)
1416906943f9SDavid du Colombier panic("epio: ed not done and not closed");
1417906943f9SDavid du Colombier if(timedout){
1418906943f9SDavid du Colombier aborttds(io);
1419906943f9SDavid du Colombier io->err = "request timed out";
1420906943f9SDavid du Colombier iunlock(ctlr);
1421906943f9SDavid du Colombier if(!waserror()){
1422906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
1423906943f9SDavid du Colombier poperror();
1424906943f9SDavid du Colombier }
1425906943f9SDavid du Colombier ilock(ctlr);
1426906943f9SDavid du Colombier }
1427906943f9SDavid du Colombier if(io->state != Qclose)
1428906943f9SDavid du Colombier io->state = Qidle;
1429906943f9SDavid du Colombier iunlock(ctlr);
14306a5dc222SDavid du Colombier }
14316a5dc222SDavid du Colombier
14326a5dc222SDavid du Colombier /*
1433906943f9SDavid du Colombier * Non iso I/O.
1434906943f9SDavid du Colombier * To make it work for control transfers, the caller may
1435906943f9SDavid du Colombier * lock the Qio for the entire control transfer.
14366a5dc222SDavid du Colombier */
1437906943f9SDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)1438d37e33ffSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
1439906943f9SDavid du Colombier {
1440906943f9SDavid du Colombier Ed *ed;
1441906943f9SDavid du Colombier Ctlr *ctlr;
1442906943f9SDavid du Colombier char buf[80];
1443906943f9SDavid du Colombier char *err;
144439dc1420SDavid du Colombier uchar *c;
144539dc1420SDavid du Colombier Td *td, *ltd, *ntd, *td0;
1446c8cbc0e9SDavid du Colombier int last, ntds, tmout;
144739dc1420SDavid du Colombier long tot, n;
144839dc1420SDavid du Colombier ulong load;
1449906943f9SDavid du Colombier
1450906943f9SDavid du Colombier ed = io->ed;
1451906943f9SDavid du Colombier ctlr = ep->hp->aux;
1452906943f9SDavid du Colombier io->debug = ep->debug;
1453d37e33ffSDavid du Colombier tmout = ep->tmout;
1454906943f9SDavid du Colombier ddeprint("ohci: %s ep%d.%d io %#p count %ld\n",
1455906943f9SDavid du Colombier io->tok == Tdtokin ? "in" : "out",
1456906943f9SDavid du Colombier ep->dev->nb, ep->nb, io, count);
1457906943f9SDavid du Colombier if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
1458906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
1459906943f9SDavid du Colombier print("\t%s\n", buf);
14606a5dc222SDavid du Colombier }
1461906943f9SDavid du Colombier if(mustlock){
1462906943f9SDavid du Colombier qlock(io);
1463906943f9SDavid du Colombier if(waserror()){
1464906943f9SDavid du Colombier qunlock(io);
1465906943f9SDavid du Colombier nexterror();
1466906943f9SDavid du Colombier }
1467906943f9SDavid du Colombier }
1468906943f9SDavid du Colombier io->err = nil;
1469906943f9SDavid du Colombier ilock(ctlr);
1470906943f9SDavid du Colombier if(io->state == Qclose){ /* Tds released by cancelio */
1471906943f9SDavid du Colombier iunlock(ctlr);
1472906943f9SDavid du Colombier error(io->err ? io->err : Eio);
1473906943f9SDavid du Colombier }
1474906943f9SDavid du Colombier if(io->state != Qidle)
1475906943f9SDavid du Colombier panic("epio: qio not idle");
1476906943f9SDavid du Colombier io->state = Qinstall;
1477906943f9SDavid du Colombier
1478906943f9SDavid du Colombier c = a;
1479906943f9SDavid du Colombier ltd = td0 = ed->tds;
1480906943f9SDavid du Colombier load = tot = 0;
1481906943f9SDavid du Colombier do{
14823be656c8SDavid du Colombier n = 2*BY2PG;
1483906943f9SDavid du Colombier if(count-tot < n)
1484906943f9SDavid du Colombier n = count-tot;
1485d24fba2aSDavid du Colombier if(c != nil && io->tok != Tdtokin)
1486906943f9SDavid du Colombier td = epgettd(ep, io, <d, 0, c+tot, n);
1487d24fba2aSDavid du Colombier else
1488906943f9SDavid du Colombier td = epgettd(ep, io, <d, 0, nil, n);
1489906943f9SDavid du Colombier tot += n;
1490906943f9SDavid du Colombier load += ep->load;
1491906943f9SDavid du Colombier }while(tot < count);
1492906943f9SDavid du Colombier if(td0 == nil || ltd == nil || td0 == ltd)
1493906943f9SDavid du Colombier panic("epio: no td");
1494906943f9SDavid du Colombier td->last = 1;
1495906943f9SDavid du Colombier if(debug > 2 || ep->debug > 2)
1496906943f9SDavid du Colombier dumptds(td0, "put td", ep->ttype == Tiso);
1497906943f9SDavid du Colombier iunlock(ctlr);
1498906943f9SDavid du Colombier
1499906943f9SDavid du Colombier ilock(ctlr);
1500906943f9SDavid du Colombier if(io->state != Qclose){
1501906943f9SDavid du Colombier io->iotime = TK2MS(MACHP(0)->ticks);
1502906943f9SDavid du Colombier io->state = Qrun;
1503906943f9SDavid du Colombier ed->tail = ptr2pa(ltd);
1504906943f9SDavid du Colombier if(ep->ttype == Tctl)
1505906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sclf;
1506906943f9SDavid du Colombier else if(ep->ttype == Tbulk)
1507906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sblf;
1508906943f9SDavid du Colombier }
1509906943f9SDavid du Colombier iunlock(ctlr);
1510906943f9SDavid du Colombier
1511906943f9SDavid du Colombier epiowait(ctlr, io, tmout, load);
1512906943f9SDavid du Colombier ilock(ctlr);
1513906943f9SDavid du Colombier if(debug > 1 || ep->debug > 1)
1514906943f9SDavid du Colombier dumptds(td0, "got td", 0);
1515906943f9SDavid du Colombier iunlock(ctlr);
1516906943f9SDavid du Colombier
1517906943f9SDavid du Colombier tot = 0;
1518906943f9SDavid du Colombier c = a;
1519906943f9SDavid du Colombier ntds = last = 0;
1520906943f9SDavid du Colombier for(td = td0; td != ltd; td = ntd){
1521906943f9SDavid du Colombier ntds++;
1522906943f9SDavid du Colombier /*
1523906943f9SDavid du Colombier * If the Td is flagged as last we must
1524906943f9SDavid du Colombier * ignore any following Td. The block may
1525906943f9SDavid du Colombier * seem to have bytes but interrupt has not seen
1526906943f9SDavid du Colombier * those Tds through the done queue, and they are void.
1527906943f9SDavid du Colombier */
1528906943f9SDavid du Colombier if(last == 0 && tderrs(td) == Tdok){
1529906943f9SDavid du Colombier n = BLEN(td->bp);
1530906943f9SDavid du Colombier tot += n;
1531d24fba2aSDavid du Colombier if(c != nil && tdtok(td) == Tdtokin && n > 0){
1532906943f9SDavid du Colombier memmove(c, td->bp->rp, n);
1533906943f9SDavid du Colombier c += n;
1534906943f9SDavid du Colombier }
1535906943f9SDavid du Colombier }
1536906943f9SDavid du Colombier last |= td->last;
1537906943f9SDavid du Colombier ntd = td->next;
1538906943f9SDavid du Colombier tdfree(td);
1539906943f9SDavid du Colombier }
1540906943f9SDavid du Colombier if(edtoggle(ed) == 0)
1541906943f9SDavid du Colombier io->toggle = Tddata0;
1542906943f9SDavid du Colombier else
1543906943f9SDavid du Colombier io->toggle = Tddata1;
1544906943f9SDavid du Colombier
1545906943f9SDavid du Colombier err = io->err;
1546906943f9SDavid du Colombier if(mustlock){
1547906943f9SDavid du Colombier qunlock(io);
1548906943f9SDavid du Colombier poperror();
1549906943f9SDavid du Colombier }
1550906943f9SDavid du Colombier ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\n",
1551906943f9SDavid du Colombier io, ntds, tot, err);
1552906943f9SDavid du Colombier if(err != nil)
1553906943f9SDavid du Colombier error(err);
1554906943f9SDavid du Colombier if(tot < 0)
1555906943f9SDavid du Colombier error(Eio);
1556906943f9SDavid du Colombier return tot;
1557906943f9SDavid du Colombier }
1558906943f9SDavid du Colombier
1559906943f9SDavid du Colombier /*
1560906943f9SDavid du Colombier * halt condition was cleared on the endpoint. update our toggles.
1561906943f9SDavid du Colombier */
1562906943f9SDavid du Colombier static void
clrhalt(Ep * ep)1563906943f9SDavid du Colombier clrhalt(Ep *ep)
1564906943f9SDavid du Colombier {
1565906943f9SDavid du Colombier Qio *io;
1566906943f9SDavid du Colombier
1567906943f9SDavid du Colombier ep->clrhalt = 0;
1568906943f9SDavid du Colombier switch(ep->ttype){
1569906943f9SDavid du Colombier case Tbulk:
1570906943f9SDavid du Colombier case Tintr:
1571906943f9SDavid du Colombier io = ep->aux;
1572906943f9SDavid du Colombier if(ep->mode != OREAD){
1573906943f9SDavid du Colombier qlock(&io[OWRITE]);
1574906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0;
1575906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OWRITE);
1576906943f9SDavid du Colombier qunlock(&io[OWRITE]);
1577906943f9SDavid du Colombier }
1578906943f9SDavid du Colombier if(ep->mode != OWRITE){
1579906943f9SDavid du Colombier qlock(&io[OREAD]);
1580906943f9SDavid du Colombier io[OREAD].toggle = Tddata0;
1581906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OREAD);
1582906943f9SDavid du Colombier qunlock(&io[OREAD]);
1583906943f9SDavid du Colombier }
1584906943f9SDavid du Colombier break;
15856a5dc222SDavid du Colombier }
15866a5dc222SDavid du Colombier }
15876a5dc222SDavid du Colombier
15886a5dc222SDavid du Colombier static long
epread(Ep * ep,void * a,long count)1589906943f9SDavid du Colombier epread(Ep *ep, void *a, long count)
15906a5dc222SDavid du Colombier {
1591906943f9SDavid du Colombier Ctlio *cio;
1592906943f9SDavid du Colombier Qio *io;
1593906943f9SDavid du Colombier char buf[80];
1594906943f9SDavid du Colombier ulong delta;
15956a5dc222SDavid du Colombier
1596906943f9SDavid du Colombier if(ep->aux == nil)
1597906943f9SDavid du Colombier panic("epread: not open");
1598906943f9SDavid du Colombier
1599906943f9SDavid du Colombier switch(ep->ttype){
1600906943f9SDavid du Colombier case Tctl:
1601906943f9SDavid du Colombier cio = ep->aux;
1602906943f9SDavid du Colombier qlock(cio);
1603906943f9SDavid du Colombier if(waserror()){
1604906943f9SDavid du Colombier qunlock(cio);
1605906943f9SDavid du Colombier nexterror();
16066a5dc222SDavid du Colombier }
1607906943f9SDavid du Colombier ddeprint("epread ctl ndata %d\n", cio->ndata);
1608906943f9SDavid du Colombier if(cio->ndata < 0)
1609906943f9SDavid du Colombier error("request expected");
1610906943f9SDavid du Colombier else if(cio->ndata == 0){
1611906943f9SDavid du Colombier cio->ndata = -1;
1612906943f9SDavid du Colombier count = 0;
16136a5dc222SDavid du Colombier }else{
1614906943f9SDavid du Colombier if(count > cio->ndata)
1615906943f9SDavid du Colombier count = cio->ndata;
1616906943f9SDavid du Colombier if(count > 0)
1617906943f9SDavid du Colombier memmove(a, cio->data, count);
1618906943f9SDavid du Colombier /* BUG for big transfers */
1619906943f9SDavid du Colombier free(cio->data);
1620906943f9SDavid du Colombier cio->data = nil;
1621906943f9SDavid du Colombier cio->ndata = 0; /* signal EOF next time */
16226a5dc222SDavid du Colombier }
1623906943f9SDavid du Colombier qunlock(cio);
16246a5dc222SDavid du Colombier poperror();
1625906943f9SDavid du Colombier if(debug>1 || ep->debug){
1626906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
1627906943f9SDavid du Colombier print("epread: %s\n", buf);
1628906943f9SDavid du Colombier }
1629906943f9SDavid du Colombier return count;
1630906943f9SDavid du Colombier case Tbulk:
1631906943f9SDavid du Colombier io = ep->aux;
1632906943f9SDavid du Colombier if(ep->clrhalt)
1633906943f9SDavid du Colombier clrhalt(ep);
1634d37e33ffSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
1635906943f9SDavid du Colombier case Tintr:
1636906943f9SDavid du Colombier io = ep->aux;
1637906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
1638906943f9SDavid du Colombier if(delta < ep->pollival / 2)
1639906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
1640906943f9SDavid du Colombier if(ep->clrhalt)
1641906943f9SDavid du Colombier clrhalt(ep);
1642d37e33ffSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
1643906943f9SDavid du Colombier case Tiso:
1644906943f9SDavid du Colombier panic("ohci: iso read not implemented");
1645906943f9SDavid du Colombier break;
1646906943f9SDavid du Colombier default:
1647906943f9SDavid du Colombier panic("epread: bad ep ttype %d", ep->ttype);
1648906943f9SDavid du Colombier }
1649906943f9SDavid du Colombier return -1;
1650906943f9SDavid du Colombier }
16516a5dc222SDavid du Colombier
1652906943f9SDavid du Colombier /*
1653906943f9SDavid du Colombier * Control transfers are one setup write (data0)
1654906943f9SDavid du Colombier * plus zero or more reads/writes (data1, data0, ...)
1655906943f9SDavid du Colombier * plus a final write/read with data1 to ack.
1656906943f9SDavid du Colombier * For both host to device and device to host we perform
1657906943f9SDavid du Colombier * the entire transfer when the user writes the request,
1658906943f9SDavid du Colombier * and keep any data read from the device for a later read.
1659906943f9SDavid du Colombier * We call epio three times instead of placing all Tds at
1660906943f9SDavid du Colombier * the same time because doing so leads to crc/tmout errors
1661906943f9SDavid du Colombier * for some devices.
1662906943f9SDavid du Colombier * Upon errors on the data phase we must still run the status
1663906943f9SDavid du Colombier * phase or the device may cease responding in the future.
1664906943f9SDavid du Colombier */
1665906943f9SDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)1666906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
1667906943f9SDavid du Colombier {
1668906943f9SDavid du Colombier uchar *c;
1669906943f9SDavid du Colombier long len;
1670906943f9SDavid du Colombier
1671906943f9SDavid du Colombier ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
1672906943f9SDavid du Colombier cio, ep->dev->nb, ep->nb, count);
1673906943f9SDavid du Colombier if(count < Rsetuplen)
1674906943f9SDavid du Colombier error("short usb command");
1675906943f9SDavid du Colombier qlock(cio);
1676906943f9SDavid du Colombier free(cio->data);
1677906943f9SDavid du Colombier cio->data = nil;
1678906943f9SDavid du Colombier cio->ndata = 0;
1679906943f9SDavid du Colombier if(waserror()){
1680906943f9SDavid du Colombier qunlock(cio);
1681906943f9SDavid du Colombier free(cio->data);
1682906943f9SDavid du Colombier cio->data = nil;
1683906943f9SDavid du Colombier cio->ndata = 0;
1684906943f9SDavid du Colombier nexterror();
1685906943f9SDavid du Colombier }
1686906943f9SDavid du Colombier
1687906943f9SDavid du Colombier /* set the address if unset and out of configuration state */
1688a23bc242SDavid du Colombier if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
1689a23bc242SDavid du Colombier if(cio->usbid == 0){
1690906943f9SDavid du Colombier cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1691906943f9SDavid du Colombier edsetaddr(cio->ed, cio->usbid);
1692906943f9SDavid du Colombier }
1693906943f9SDavid du Colombier /* adjust maxpkt if the user has learned a different one */
1694906943f9SDavid du Colombier if(edmaxpkt(cio->ed) != ep->maxpkt)
1695906943f9SDavid du Colombier edsetmaxpkt(cio->ed, ep->maxpkt);
1696906943f9SDavid du Colombier c = a;
1697906943f9SDavid du Colombier cio->tok = Tdtoksetup;
1698906943f9SDavid du Colombier cio->toggle = Tddata0;
1699d37e33ffSDavid du Colombier if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
1700906943f9SDavid du Colombier error(Eio);
1701906943f9SDavid du Colombier
1702906943f9SDavid du Colombier a = c + Rsetuplen;
1703906943f9SDavid du Colombier count -= Rsetuplen;
1704906943f9SDavid du Colombier
1705906943f9SDavid du Colombier cio->toggle = Tddata1;
1706906943f9SDavid du Colombier if(c[Rtype] & Rd2h){
1707906943f9SDavid du Colombier cio->tok = Tdtokin;
1708906943f9SDavid du Colombier len = GET2(c+Rcount);
1709906943f9SDavid du Colombier if(len <= 0)
1710906943f9SDavid du Colombier error("bad length in d2h request");
1711906943f9SDavid du Colombier if(len > Maxctllen)
1712906943f9SDavid du Colombier error("d2h data too large to fit in ohci");
1713906943f9SDavid du Colombier a = cio->data = smalloc(len+1);
1714906943f9SDavid du Colombier }else{
1715906943f9SDavid du Colombier cio->tok = Tdtokout;
1716906943f9SDavid du Colombier len = count;
1717906943f9SDavid du Colombier }
1718906943f9SDavid du Colombier if(len > 0)
1719906943f9SDavid du Colombier if(waserror())
1720906943f9SDavid du Colombier len = -1;
1721906943f9SDavid du Colombier else{
1722d37e33ffSDavid du Colombier len = epio(ep, cio, a, len, 0);
1723906943f9SDavid du Colombier poperror();
1724906943f9SDavid du Colombier }
1725906943f9SDavid du Colombier if(c[Rtype] & Rd2h){
1726906943f9SDavid du Colombier count = Rsetuplen;
1727906943f9SDavid du Colombier cio->ndata = len;
1728906943f9SDavid du Colombier cio->tok = Tdtokout;
1729906943f9SDavid du Colombier }else{
1730906943f9SDavid du Colombier if(len < 0)
1731906943f9SDavid du Colombier count = -1;
1732906943f9SDavid du Colombier else
1733906943f9SDavid du Colombier count = Rsetuplen + len;
1734906943f9SDavid du Colombier cio->tok = Tdtokin;
1735906943f9SDavid du Colombier }
1736906943f9SDavid du Colombier cio->toggle = Tddata1;
1737d24fba2aSDavid du Colombier epio(ep, cio, nil, 0, 0);
1738906943f9SDavid du Colombier qunlock(cio);
1739906943f9SDavid du Colombier poperror();
1740906943f9SDavid du Colombier ddeprint("epctlio cio %#p return %ld\n", cio, count);
1741906943f9SDavid du Colombier return count;
1742906943f9SDavid du Colombier }
1743906943f9SDavid du Colombier
1744906943f9SDavid du Colombier /*
1745906943f9SDavid du Colombier * Put new samples in the dummy Td.
1746906943f9SDavid du Colombier * BUG: This does only a transfer per Td. We could do up to 8.
1747906943f9SDavid du Colombier */
1748906943f9SDavid du Colombier static long
putsamples(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)1749906943f9SDavid du Colombier putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, uchar *b, long count)
1750906943f9SDavid du Colombier {
1751906943f9SDavid du Colombier Td *td;
1752906943f9SDavid du Colombier ulong n;
1753906943f9SDavid du Colombier
1754906943f9SDavid du Colombier td = pa2ptr(iso->ed->tail);
1755906943f9SDavid du Colombier n = count;
1756906943f9SDavid du Colombier if(n > td->nbytes - BLEN(td->bp))
1757906943f9SDavid du Colombier n = td->nbytes - BLEN(td->bp);
1758906943f9SDavid du Colombier assert(td->bp->wp + n <= td->bp->lim);
1759906943f9SDavid du Colombier memmove(td->bp->wp, b, n);
1760906943f9SDavid du Colombier td->bp->wp += n;
1761906943f9SDavid du Colombier if(BLEN(td->bp) == td->nbytes){ /* full Td: activate it */
1762906943f9SDavid du Colombier ilock(ctlr);
1763906943f9SDavid du Colombier isoadvance(ep, iso, td);
1764906943f9SDavid du Colombier iunlock(ctlr);
1765906943f9SDavid du Colombier }
1766906943f9SDavid du Colombier return n;
1767906943f9SDavid du Colombier }
1768906943f9SDavid du Colombier
1769906943f9SDavid du Colombier static long
episowrite(Ep * ep,void * a,long count)1770906943f9SDavid du Colombier episowrite(Ep *ep, void *a, long count)
1771906943f9SDavid du Colombier {
1772d37e33ffSDavid du Colombier long tot, nw;
1773d37e33ffSDavid du Colombier char *err;
1774906943f9SDavid du Colombier uchar *b;
1775906943f9SDavid du Colombier Ctlr *ctlr;
1776d37e33ffSDavid du Colombier Isoio *iso;
1777906943f9SDavid du Colombier
1778906943f9SDavid du Colombier ctlr = ep->hp->aux;
1779906943f9SDavid du Colombier iso = ep->aux;
1780906943f9SDavid du Colombier iso->debug = ep->debug;
1781906943f9SDavid du Colombier
1782906943f9SDavid du Colombier qlock(iso);
1783906943f9SDavid du Colombier if(waserror()){
1784906943f9SDavid du Colombier qunlock(iso);
1785906943f9SDavid du Colombier nexterror();
1786906943f9SDavid du Colombier }
1787906943f9SDavid du Colombier diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1788906943f9SDavid du Colombier ilock(ctlr);
1789906943f9SDavid du Colombier if(iso->state == Qclose){
1790906943f9SDavid du Colombier iunlock(ctlr);
1791906943f9SDavid du Colombier error(iso->err ? iso->err : Eio);
1792906943f9SDavid du Colombier }
1793906943f9SDavid du Colombier iso->state = Qrun;
1794906943f9SDavid du Colombier b = a;
1795906943f9SDavid du Colombier for(tot = 0; tot < count; tot += nw){
1796906943f9SDavid du Colombier while(isocanwrite(iso) == 0){
1797906943f9SDavid du Colombier iunlock(ctlr);
1798906943f9SDavid du Colombier diprint("ohci: episowrite: %#p sleep\n", iso);
1799906943f9SDavid du Colombier if(waserror()){
1800906943f9SDavid du Colombier if(iso->err == nil)
1801906943f9SDavid du Colombier iso->err = "I/O timed out";
1802906943f9SDavid du Colombier ilock(ctlr);
1803906943f9SDavid du Colombier break;
1804906943f9SDavid du Colombier }
1805d37e33ffSDavid du Colombier tsleep(iso, isocanwrite, iso, ep->tmout);
1806906943f9SDavid du Colombier poperror();
1807906943f9SDavid du Colombier ilock(ctlr);
1808906943f9SDavid du Colombier }
1809906943f9SDavid du Colombier err = iso->err;
1810906943f9SDavid du Colombier iso->err = nil;
1811906943f9SDavid du Colombier if(iso->state == Qclose || err != nil){
1812906943f9SDavid du Colombier iunlock(ctlr);
1813906943f9SDavid du Colombier error(err ? err : Eio);
1814906943f9SDavid du Colombier }
1815906943f9SDavid du Colombier if(iso->state != Qrun)
1816906943f9SDavid du Colombier panic("episowrite: iso not running");
1817906943f9SDavid du Colombier iunlock(ctlr); /* We could page fault here */
1818906943f9SDavid du Colombier nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
1819906943f9SDavid du Colombier ilock(ctlr);
1820906943f9SDavid du Colombier }
1821906943f9SDavid du Colombier if(iso->state != Qclose)
1822906943f9SDavid du Colombier iso->state = Qdone;
1823906943f9SDavid du Colombier iunlock(ctlr);
1824906943f9SDavid du Colombier err = iso->err; /* in case it failed early */
1825906943f9SDavid du Colombier iso->err = nil;
1826906943f9SDavid du Colombier qunlock(iso);
1827906943f9SDavid du Colombier poperror();
1828906943f9SDavid du Colombier if(err != nil)
1829906943f9SDavid du Colombier error(err);
1830906943f9SDavid du Colombier diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot);
1831906943f9SDavid du Colombier return tot;
1832906943f9SDavid du Colombier }
1833906943f9SDavid du Colombier
1834906943f9SDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)1835906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count)
1836906943f9SDavid du Colombier {
1837906943f9SDavid du Colombier Qio *io;
1838906943f9SDavid du Colombier Ctlio *cio;
1839906943f9SDavid du Colombier ulong delta;
1840906943f9SDavid du Colombier uchar *b;
184139dc1420SDavid du Colombier long tot, nw;
1842906943f9SDavid du Colombier
1843906943f9SDavid du Colombier if(ep->aux == nil)
1844906943f9SDavid du Colombier panic("ohci: epwrite: not open");
1845906943f9SDavid du Colombier switch(ep->ttype){
1846906943f9SDavid du Colombier case Tctl:
1847906943f9SDavid du Colombier cio = ep->aux;
1848906943f9SDavid du Colombier return epctlio(ep, cio, a, count);
1849906943f9SDavid du Colombier case Tbulk:
1850906943f9SDavid du Colombier io = ep->aux;
1851906943f9SDavid du Colombier if(ep->clrhalt)
1852906943f9SDavid du Colombier clrhalt(ep);
1853906943f9SDavid du Colombier /*
1854906943f9SDavid du Colombier * Put at most Tdatomic Tds (512 bytes) at a time.
1855906943f9SDavid du Colombier * Otherwise some devices produce babble errors.
1856906943f9SDavid du Colombier */
1857906943f9SDavid du Colombier b = a;
1858add6b5c5SDavid du Colombier assert(a != nil);
1859906943f9SDavid du Colombier for(tot = 0; tot < count ; tot += nw){
1860906943f9SDavid du Colombier nw = count - tot;
1861906943f9SDavid du Colombier if(nw > Tdatomic * ep->maxpkt)
1862906943f9SDavid du Colombier nw = Tdatomic * ep->maxpkt;
1863d37e33ffSDavid du Colombier nw = epio(ep, &io[OWRITE], b+tot, nw, 1);
1864906943f9SDavid du Colombier }
1865906943f9SDavid du Colombier return tot;
1866906943f9SDavid du Colombier case Tintr:
1867906943f9SDavid du Colombier io = ep->aux;
1868906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
1869906943f9SDavid du Colombier if(delta < ep->pollival)
1870906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - delta);
1871906943f9SDavid du Colombier if(ep->clrhalt)
1872906943f9SDavid du Colombier clrhalt(ep);
1873d37e33ffSDavid du Colombier return epio(ep, &io[OWRITE], a, count, 1);
1874906943f9SDavid du Colombier case Tiso:
1875906943f9SDavid du Colombier return episowrite(ep, a, count);
1876906943f9SDavid du Colombier default:
1877906943f9SDavid du Colombier panic("ohci: epwrite: bad ep ttype %d", ep->ttype);
1878906943f9SDavid du Colombier }
1879906943f9SDavid du Colombier return -1;
1880906943f9SDavid du Colombier }
1881906943f9SDavid du Colombier
1882906943f9SDavid du Colombier static Ed*
newed(Ctlr * ctlr,Ep * ep,Qio * io,char *)1883906943f9SDavid du Colombier newed(Ctlr *ctlr, Ep *ep, Qio *io, char *)
1884906943f9SDavid du Colombier {
1885906943f9SDavid du Colombier Ed *ed;
1886906943f9SDavid du Colombier Td *td;
1887906943f9SDavid du Colombier
1888906943f9SDavid du Colombier ed = io->ed = edalloc(); /* no errors raised here, really */
1889906943f9SDavid du Colombier td = tdalloc();
1890906943f9SDavid du Colombier td->ep = ep;
1891906943f9SDavid du Colombier td->io = io;
1892906943f9SDavid du Colombier ed->tail = ptr2pa(td);
1893906943f9SDavid du Colombier ed->head = ptr2pa(td);
1894906943f9SDavid du Colombier ed->tds = td;
1895906943f9SDavid du Colombier ed->ep = ep;
1896906943f9SDavid du Colombier ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift;
1897906943f9SDavid du Colombier if(ep->ttype == Tiso)
1898906943f9SDavid du Colombier ed->ctrl |= Ediso;
1899906943f9SDavid du Colombier if(waserror()){
1900906943f9SDavid du Colombier edfree(ed);
1901906943f9SDavid du Colombier io->ed = nil;
1902906943f9SDavid du Colombier nexterror();
1903906943f9SDavid du Colombier }
1904906943f9SDavid du Colombier /* For setup endpoints we start with the config address */
1905906943f9SDavid du Colombier if(ep->ttype != Tctl)
1906906943f9SDavid du Colombier edsetaddr(io->ed, io->usbid);
1907906943f9SDavid du Colombier if(ep->dev->speed == Lowspeed)
1908906943f9SDavid du Colombier ed->ctrl |= Edlow;
1909906943f9SDavid du Colombier switch(io->tok){
1910906943f9SDavid du Colombier case Tdtokin:
1911906943f9SDavid du Colombier ed->ctrl |= Edin;
1912906943f9SDavid du Colombier break;
1913906943f9SDavid du Colombier case Tdtokout:
1914906943f9SDavid du Colombier ed->ctrl |= Edout;
1915906943f9SDavid du Colombier break;
1916906943f9SDavid du Colombier default:
1917906943f9SDavid du Colombier ed->ctrl |= Edtddir; /* Td will say */
1918906943f9SDavid du Colombier break;
1919906943f9SDavid du Colombier }
1920906943f9SDavid du Colombier
1921906943f9SDavid du Colombier switch(ep->ttype){
1922906943f9SDavid du Colombier case Tctl:
1923906943f9SDavid du Colombier ilock(ctlr);
1924906943f9SDavid du Colombier edlinked(ed, ctlhd(ctlr));
1925906943f9SDavid du Colombier setctlhd(ctlr, ed);
1926906943f9SDavid du Colombier iunlock(ctlr);
1927906943f9SDavid du Colombier break;
1928906943f9SDavid du Colombier case Tbulk:
1929906943f9SDavid du Colombier ilock(ctlr);
1930906943f9SDavid du Colombier edlinked(ed, bulkhd(ctlr));
1931906943f9SDavid du Colombier setbulkhd(ctlr, ed);
1932906943f9SDavid du Colombier iunlock(ctlr);
1933906943f9SDavid du Colombier break;
1934906943f9SDavid du Colombier case Tintr:
1935906943f9SDavid du Colombier case Tiso:
1936906943f9SDavid du Colombier ilock(ctlr);
1937906943f9SDavid du Colombier schedq(ctlr, io, ep->pollival);
1938906943f9SDavid du Colombier iunlock(ctlr);
1939906943f9SDavid du Colombier break;
1940906943f9SDavid du Colombier default:
1941906943f9SDavid du Colombier panic("ohci: newed: bad ttype");
1942906943f9SDavid du Colombier }
1943906943f9SDavid du Colombier poperror();
1944906943f9SDavid du Colombier return ed;
1945906943f9SDavid du Colombier }
1946906943f9SDavid du Colombier
1947906943f9SDavid du Colombier static void
isoopen(Ctlr * ctlr,Ep * ep)1948906943f9SDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
1949906943f9SDavid du Colombier {
195039dc1420SDavid du Colombier Td *td, *edtds;
1951906943f9SDavid du Colombier Isoio *iso;
1952906943f9SDavid du Colombier int i;
1953906943f9SDavid du Colombier
1954906943f9SDavid du Colombier iso = ep->aux;
1955906943f9SDavid du Colombier iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1956906943f9SDavid du Colombier iso->bw = ep->hz * ep->samplesz; /* bytes/sec */
1957906943f9SDavid du Colombier if(ep->mode != OWRITE){
1958906943f9SDavid du Colombier print("ohci: bug: iso input streams not implemented\n");
1959906943f9SDavid du Colombier error("ohci iso input streams not implemented");
1960906943f9SDavid du Colombier }else
1961906943f9SDavid du Colombier iso->tok = Tdtokout;
1962906943f9SDavid du Colombier
1963906943f9SDavid du Colombier iso->left = 0;
1964906943f9SDavid du Colombier iso->nerrs = 0;
1965906943f9SDavid du Colombier iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes);
1966906943f9SDavid du Colombier iso->nframes = 1000 / ep->pollival;
1967906943f9SDavid du Colombier if(iso->nframes < 10){
1968906943f9SDavid du Colombier print("ohci: isoopen: less than 10 frames; using 10.\n");
1969906943f9SDavid du Colombier iso->nframes = 10;
1970906943f9SDavid du Colombier }
1971906943f9SDavid du Colombier iso->navail = iso->nframes;
1972906943f9SDavid du Colombier iso->atds = edtds = nil;
1973906943f9SDavid du Colombier for(i = 0; i < iso->nframes-1; i++){ /* -1 for dummy */
1974906943f9SDavid du Colombier td = tdalloc();
1975906943f9SDavid du Colombier td->ep = ep;
1976906943f9SDavid du Colombier td->io = iso;
1977906943f9SDavid du Colombier td->bp = allocb(ep->maxpkt);
1978906943f9SDavid du Colombier td->anext = iso->atds; /* link as avail */
1979906943f9SDavid du Colombier iso->atds = td;
1980906943f9SDavid du Colombier td->next = edtds;
1981906943f9SDavid du Colombier edtds = td;
1982906943f9SDavid du Colombier }
1983906943f9SDavid du Colombier newed(ctlr, ep, iso, "iso"); /* allocates a dummy td */
1984906943f9SDavid du Colombier iso->ed->tds->bp = allocb(ep->maxpkt); /* but not its block */
1985906943f9SDavid du Colombier iso->ed->tds->next = edtds;
1986906943f9SDavid du Colombier isodtdinit(ep, iso, iso->ed->tds);
1987906943f9SDavid du Colombier }
1988906943f9SDavid du Colombier
1989906943f9SDavid du Colombier /*
1990906943f9SDavid du Colombier * Allocate the endpoint and set it up for I/O
1991906943f9SDavid du Colombier * in the controller. This must follow what's said
1992906943f9SDavid du Colombier * in Ep regarding configuration, including perhaps
1993906943f9SDavid du Colombier * the saved toggles (saved on a previous close of
1994906943f9SDavid du Colombier * the endpoint data file by epclose).
1995906943f9SDavid du Colombier */
1996906943f9SDavid du Colombier static void
epopen(Ep * ep)1997906943f9SDavid du Colombier epopen(Ep *ep)
1998906943f9SDavid du Colombier {
1999906943f9SDavid du Colombier Ctlr *ctlr;
2000906943f9SDavid du Colombier Qio *io;
2001906943f9SDavid du Colombier Ctlio *cio;
2002906943f9SDavid du Colombier ulong usbid;
2003906943f9SDavid du Colombier
2004906943f9SDavid du Colombier ctlr = ep->hp->aux;
2005906943f9SDavid du Colombier deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
2006906943f9SDavid du Colombier if(ep->aux != nil)
2007906943f9SDavid du Colombier panic("ohci: epopen called with open ep");
2008906943f9SDavid du Colombier if(waserror()){
2009906943f9SDavid du Colombier free(ep->aux);
2010906943f9SDavid du Colombier ep->aux = nil;
2011906943f9SDavid du Colombier nexterror();
2012906943f9SDavid du Colombier }
2013906943f9SDavid du Colombier switch(ep->ttype){
2014906943f9SDavid du Colombier case Tnone:
2015906943f9SDavid du Colombier error("endpoint not configured");
2016906943f9SDavid du Colombier case Tiso:
2017906943f9SDavid du Colombier ep->aux = smalloc(sizeof(Isoio));
2018906943f9SDavid du Colombier isoopen(ctlr, ep);
2019906943f9SDavid du Colombier break;
2020906943f9SDavid du Colombier case Tctl:
2021906943f9SDavid du Colombier cio = ep->aux = smalloc(sizeof(Ctlio));
2022906943f9SDavid du Colombier cio->debug = ep->debug;
2023906943f9SDavid du Colombier cio->ndata = -1;
2024906943f9SDavid du Colombier cio->data = nil;
2025906943f9SDavid du Colombier cio->tok = -1; /* invalid; Tds will say */
2026906943f9SDavid du Colombier if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
2027906943f9SDavid du Colombier break;
2028906943f9SDavid du Colombier newed(ctlr, ep, cio, "epc");
2029906943f9SDavid du Colombier break;
2030906943f9SDavid du Colombier case Tbulk:
2031906943f9SDavid du Colombier ep->pollival = 1; /* assume this; doesn't really matter */
2032906943f9SDavid du Colombier /* and fall... */
2033906943f9SDavid du Colombier case Tintr:
2034906943f9SDavid du Colombier io = ep->aux = smalloc(sizeof(Qio)*2);
2035906943f9SDavid du Colombier io[OREAD].debug = io[OWRITE].debug = ep->debug;
2036906943f9SDavid du Colombier usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
2037906943f9SDavid du Colombier if(ep->mode != OREAD){
2038906943f9SDavid du Colombier if(ep->toggle[OWRITE] != 0)
2039906943f9SDavid du Colombier io[OWRITE].toggle = Tddata1;
2040906943f9SDavid du Colombier else
2041906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0;
2042906943f9SDavid du Colombier io[OWRITE].tok = Tdtokout;
2043906943f9SDavid du Colombier io[OWRITE].usbid = usbid;
2044906943f9SDavid du Colombier io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2045906943f9SDavid du Colombier newed(ctlr, ep, io+OWRITE, "epw");
2046906943f9SDavid du Colombier }
2047906943f9SDavid du Colombier if(ep->mode != OWRITE){
2048906943f9SDavid du Colombier if(ep->toggle[OREAD] != 0)
2049906943f9SDavid du Colombier io[OREAD].toggle = Tddata1;
2050906943f9SDavid du Colombier else
2051906943f9SDavid du Colombier io[OREAD].toggle = Tddata0;
2052906943f9SDavid du Colombier io[OREAD].tok = Tdtokin;
2053906943f9SDavid du Colombier io[OREAD].usbid = usbid;
2054906943f9SDavid du Colombier io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2055906943f9SDavid du Colombier newed(ctlr, ep, io+OREAD, "epr");
2056906943f9SDavid du Colombier }
2057906943f9SDavid du Colombier break;
2058906943f9SDavid du Colombier }
2059906943f9SDavid du Colombier deprint("ohci: epopen done:\n");
2060906943f9SDavid du Colombier if(debug || ep->debug)
2061906943f9SDavid du Colombier dump(ep->hp);
2062906943f9SDavid du Colombier poperror();
2063906943f9SDavid du Colombier }
2064906943f9SDavid du Colombier
2065906943f9SDavid du Colombier static void
cancelio(Ep * ep,Qio * io)2066906943f9SDavid du Colombier cancelio(Ep *ep, Qio *io)
2067906943f9SDavid du Colombier {
2068906943f9SDavid du Colombier Ed *ed;
2069906943f9SDavid du Colombier Ctlr *ctlr;
2070906943f9SDavid du Colombier
2071906943f9SDavid du Colombier ctlr = ep->hp->aux;
2072906943f9SDavid du Colombier
2073906943f9SDavid du Colombier ilock(ctlr);
2074906943f9SDavid du Colombier if(io == nil || io->state == Qclose){
2075767ad8bbSDavid du Colombier assert(io == nil || io->ed == nil);
2076906943f9SDavid du Colombier iunlock(ctlr);
2077906943f9SDavid du Colombier return;
2078906943f9SDavid du Colombier }
2079767ad8bbSDavid du Colombier ed = io->ed;
2080906943f9SDavid du Colombier io->state = Qclose;
2081906943f9SDavid du Colombier io->err = Eio;
2082906943f9SDavid du Colombier aborttds(io);
2083906943f9SDavid du Colombier iunlock(ctlr);
2084906943f9SDavid du Colombier if(!waserror()){
2085906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
2086906943f9SDavid du Colombier poperror();
2087906943f9SDavid du Colombier }
2088906943f9SDavid du Colombier
2089906943f9SDavid du Colombier wakeup(io);
2090906943f9SDavid du Colombier qlock(io);
2091906943f9SDavid du Colombier /* wait for epio if running */
2092906943f9SDavid du Colombier qunlock(io);
2093906943f9SDavid du Colombier
2094906943f9SDavid du Colombier ilock(ctlr);
2095906943f9SDavid du Colombier switch(ep->ttype){
2096906943f9SDavid du Colombier case Tctl:
2097906943f9SDavid du Colombier unlinkctl(ctlr, ed);
2098906943f9SDavid du Colombier break;
2099906943f9SDavid du Colombier case Tbulk:
2100906943f9SDavid du Colombier unlinkbulk(ctlr, ed);
2101906943f9SDavid du Colombier break;
2102906943f9SDavid du Colombier case Tintr:
2103906943f9SDavid du Colombier case Tiso:
2104906943f9SDavid du Colombier unschedq(ctlr, io);
2105906943f9SDavid du Colombier break;
2106906943f9SDavid du Colombier default:
2107906943f9SDavid du Colombier panic("ohci cancelio: bad ttype");
2108906943f9SDavid du Colombier }
2109906943f9SDavid du Colombier iunlock(ctlr);
2110906943f9SDavid du Colombier edfree(io->ed);
2111906943f9SDavid du Colombier io->ed = nil;
2112906943f9SDavid du Colombier }
2113906943f9SDavid du Colombier
2114906943f9SDavid du Colombier static void
epclose(Ep * ep)2115906943f9SDavid du Colombier epclose(Ep *ep)
2116906943f9SDavid du Colombier {
2117906943f9SDavid du Colombier Ctlio *cio;
2118906943f9SDavid du Colombier Isoio *iso;
2119906943f9SDavid du Colombier Qio *io;
2120906943f9SDavid du Colombier
2121906943f9SDavid du Colombier deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
2122906943f9SDavid du Colombier if(ep->aux == nil)
2123906943f9SDavid du Colombier panic("ohci: epclose called with closed ep");
2124906943f9SDavid du Colombier switch(ep->ttype){
2125906943f9SDavid du Colombier case Tctl:
2126906943f9SDavid du Colombier cio = ep->aux;
2127906943f9SDavid du Colombier cancelio(ep, cio);
2128906943f9SDavid du Colombier free(cio->data);
2129906943f9SDavid du Colombier cio->data = nil;
2130906943f9SDavid du Colombier break;
2131906943f9SDavid du Colombier case Tbulk:
2132906943f9SDavid du Colombier case Tintr:
2133906943f9SDavid du Colombier io = ep->aux;
2134906943f9SDavid du Colombier if(ep->mode != OWRITE){
2135906943f9SDavid du Colombier cancelio(ep, &io[OREAD]);
2136906943f9SDavid du Colombier if(io[OREAD].toggle == Tddata1)
2137906943f9SDavid du Colombier ep->toggle[OREAD] = 1;
2138906943f9SDavid du Colombier }
2139906943f9SDavid du Colombier if(ep->mode != OREAD){
2140906943f9SDavid du Colombier cancelio(ep, &io[OWRITE]);
2141906943f9SDavid du Colombier if(io[OWRITE].toggle == Tddata1)
2142906943f9SDavid du Colombier ep->toggle[OWRITE] = 1;
2143906943f9SDavid du Colombier }
2144906943f9SDavid du Colombier break;
2145906943f9SDavid du Colombier case Tiso:
2146906943f9SDavid du Colombier iso = ep->aux;
2147906943f9SDavid du Colombier cancelio(ep, iso);
2148906943f9SDavid du Colombier break;
2149906943f9SDavid du Colombier default:
2150906943f9SDavid du Colombier panic("epclose: bad ttype %d", ep->ttype);
2151906943f9SDavid du Colombier }
2152906943f9SDavid du Colombier
2153906943f9SDavid du Colombier deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb);
2154906943f9SDavid du Colombier free(ep->aux);
2155906943f9SDavid du Colombier ep->aux = nil;
2156906943f9SDavid du Colombier }
2157906943f9SDavid du Colombier
2158906943f9SDavid du Colombier static int
portreset(Hci * hp,int port,int on)2159906943f9SDavid du Colombier portreset(Hci *hp, int port, int on)
2160906943f9SDavid du Colombier {
2161906943f9SDavid du Colombier Ctlr *ctlr;
216239dc1420SDavid du Colombier Ohci *ohci;
2163906943f9SDavid du Colombier
2164906943f9SDavid du Colombier if(on == 0)
2165906943f9SDavid du Colombier return 0;
2166906943f9SDavid du Colombier
2167906943f9SDavid du Colombier ctlr = hp->aux;
2168906943f9SDavid du Colombier qlock(&ctlr->resetl);
2169906943f9SDavid du Colombier if(waserror()){
2170906943f9SDavid du Colombier qunlock(&ctlr->resetl);
2171906943f9SDavid du Colombier nexterror();
2172906943f9SDavid du Colombier }
2173906943f9SDavid du Colombier ilock(ctlr);
217439dc1420SDavid du Colombier ohci = ctlr->ohci;
217539dc1420SDavid du Colombier ohci->rhportsts[port - 1] = Spp;
217639dc1420SDavid du Colombier if((ohci->rhportsts[port - 1] & Ccs) == 0){
217739dc1420SDavid du Colombier iunlock(ctlr);
217839dc1420SDavid du Colombier error("port not connected");
217939dc1420SDavid du Colombier }
218039dc1420SDavid du Colombier ohci->rhportsts[port - 1] = Spr;
218139dc1420SDavid du Colombier while((ohci->rhportsts[port - 1] & Prsc) == 0){
2182906943f9SDavid du Colombier iunlock(ctlr);
2183906943f9SDavid du Colombier dprint("ohci: portreset, wait for reset complete\n");
2184906943f9SDavid du Colombier ilock(ctlr);
2185906943f9SDavid du Colombier }
218639dc1420SDavid du Colombier ohci->rhportsts[port - 1] = Prsc;
2187906943f9SDavid du Colombier iunlock(ctlr);
2188906943f9SDavid du Colombier poperror();
2189906943f9SDavid du Colombier qunlock(&ctlr->resetl);
2190906943f9SDavid du Colombier return 0;
2191906943f9SDavid du Colombier }
2192906943f9SDavid du Colombier
2193906943f9SDavid du Colombier static int
portenable(Hci * hp,int port,int on)2194906943f9SDavid du Colombier portenable(Hci *hp, int port, int on)
2195906943f9SDavid du Colombier {
2196906943f9SDavid du Colombier Ctlr *ctlr;
2197906943f9SDavid du Colombier
2198906943f9SDavid du Colombier ctlr = hp->aux;
2199906943f9SDavid du Colombier dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
2200906943f9SDavid du Colombier qlock(&ctlr->resetl);
2201906943f9SDavid du Colombier if(waserror()){
2202906943f9SDavid du Colombier qunlock(&ctlr->resetl);
2203906943f9SDavid du Colombier nexterror();
2204906943f9SDavid du Colombier }
2205906943f9SDavid du Colombier ilock(ctlr);
2206906943f9SDavid du Colombier if(on)
2207906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
2208906943f9SDavid du Colombier else
2209906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Cpe;
2210906943f9SDavid du Colombier iunlock(ctlr);
2211906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
2212906943f9SDavid du Colombier poperror();
2213906943f9SDavid du Colombier qunlock(&ctlr->resetl);
2214906943f9SDavid du Colombier return 0;
2215906943f9SDavid du Colombier }
2216906943f9SDavid du Colombier
2217906943f9SDavid du Colombier static int
portstatus(Hci * hp,int port)2218906943f9SDavid du Colombier portstatus(Hci *hp, int port)
2219906943f9SDavid du Colombier {
2220906943f9SDavid du Colombier int v;
2221906943f9SDavid du Colombier Ctlr *ub;
2222906943f9SDavid du Colombier ulong ohcistatus;
2223906943f9SDavid du Colombier
2224906943f9SDavid du Colombier /*
2225906943f9SDavid du Colombier * We must return status bits as a
2226906943f9SDavid du Colombier * get port status hub request would do.
2227906943f9SDavid du Colombier */
2228906943f9SDavid du Colombier ub = hp->aux;
2229906943f9SDavid du Colombier ohcistatus = ub->ohci->rhportsts[port - 1];
2230906943f9SDavid du Colombier v = 0;
2231906943f9SDavid du Colombier if(ohcistatus & Ccs)
2232906943f9SDavid du Colombier v |= HPpresent;
2233906943f9SDavid du Colombier if(ohcistatus & Pes)
2234906943f9SDavid du Colombier v |= HPenable;
2235906943f9SDavid du Colombier if(ohcistatus & Pss)
2236906943f9SDavid du Colombier v |= HPsuspend;
2237906943f9SDavid du Colombier if(ohcistatus & Prs)
2238906943f9SDavid du Colombier v |= HPreset;
2239906943f9SDavid du Colombier else {
2240906943f9SDavid du Colombier /* port is not in reset; these potential writes are ok */
2241906943f9SDavid du Colombier if(ohcistatus & Csc){
2242906943f9SDavid du Colombier v |= HPstatuschg;
2243906943f9SDavid du Colombier ub->ohci->rhportsts[port - 1] = Csc;
2244906943f9SDavid du Colombier }
2245906943f9SDavid du Colombier if(ohcistatus & Pesc){
2246906943f9SDavid du Colombier v |= HPchange;
2247906943f9SDavid du Colombier ub->ohci->rhportsts[port - 1] = Pesc;
22486a5dc222SDavid du Colombier }
22496a5dc222SDavid du Colombier }
2250906943f9SDavid du Colombier if(ohcistatus & Lsda)
2251906943f9SDavid du Colombier v |= HPslow;
2252906943f9SDavid du Colombier if(v & (HPstatuschg|HPchange))
2253906943f9SDavid du Colombier ddprint("ohci port %d sts %#ulx hub sts %#x\n", port, ohcistatus, v);
2254906943f9SDavid du Colombier return v;
2255906943f9SDavid du Colombier }
2256906943f9SDavid du Colombier
2257906943f9SDavid du Colombier static void
dumpohci(Ctlr * ctlr)2258906943f9SDavid du Colombier dumpohci(Ctlr *ctlr)
2259906943f9SDavid du Colombier {
2260906943f9SDavid du Colombier int i;
2261906943f9SDavid du Colombier ulong *ohci;
2262906943f9SDavid du Colombier
2263906943f9SDavid du Colombier ohci = &ctlr->ohci->revision;
2264906943f9SDavid du Colombier print("ohci registers: \n");
2265906943f9SDavid du Colombier for(i = 0; i < sizeof(Ohci)/sizeof(ulong); i++)
2266906943f9SDavid du Colombier if(i < 3 || ohci[i] != 0)
2267906943f9SDavid du Colombier print("\t[%#2.2x]\t%#8.8ulx\n", i * 4, ohci[i]);
2268906943f9SDavid du Colombier print("\n");
2269906943f9SDavid du Colombier }
2270906943f9SDavid du Colombier
2271906943f9SDavid du Colombier static void
init(Hci * hp)2272906943f9SDavid du Colombier init(Hci *hp)
2273906943f9SDavid du Colombier {
2274906943f9SDavid du Colombier Ctlr *ctlr;
2275906943f9SDavid du Colombier Ohci *ohci;
2276906943f9SDavid du Colombier int i;
227739dc1420SDavid du Colombier ulong ival, ctrl, fmi;
2278906943f9SDavid du Colombier
2279906943f9SDavid du Colombier ctlr = hp->aux;
2280906943f9SDavid du Colombier dprint("ohci %#p init\n", ctlr->ohci);
2281906943f9SDavid du Colombier ohci = ctlr->ohci;
2282906943f9SDavid du Colombier
2283906943f9SDavid du Colombier fmi = ctlr->ohci->fminterval;
2284906943f9SDavid du Colombier ctlr->ohci->cmdsts = Shcr; /* reset the block */
2285906943f9SDavid du Colombier while(ctlr->ohci->cmdsts & Shcr)
2286906943f9SDavid du Colombier delay(1); /* wait till reset complete, Ohci says 10us max. */
2287906943f9SDavid du Colombier ctlr->ohci->fminterval = fmi;
2288906943f9SDavid du Colombier
2289906943f9SDavid du Colombier /*
2290906943f9SDavid du Colombier * now that soft reset is done we are in suspend state.
2291906943f9SDavid du Colombier * Setup registers which take in suspend state
2292906943f9SDavid du Colombier * (will only be here for 2ms).
2293906943f9SDavid du Colombier */
2294906943f9SDavid du Colombier
2295906943f9SDavid du Colombier ctlr->ohci->hcca = ptr2pa(ctlr->hcca);
2296906943f9SDavid du Colombier setctlhd(ctlr, nil);
2297906943f9SDavid du Colombier ctlr->ohci->ctlcurred = 0;
2298906943f9SDavid du Colombier setbulkhd(ctlr, nil);
2299906943f9SDavid du Colombier ctlr->ohci->bulkcurred = 0;
2300906943f9SDavid du Colombier
2301906943f9SDavid du Colombier ohci->intrenable = Mie | Wdh | Ue;
2302906943f9SDavid du Colombier ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper;
2303906943f9SDavid du Colombier
2304906943f9SDavid du Colombier /* set frame after operational */
2305906943f9SDavid du Colombier ohci->rhdesca = Nps; /* no power switching */
2306906943f9SDavid du Colombier if(ohci->rhdesca & Nps){
2307906943f9SDavid du Colombier dprint("ohci: ports are not power switched\n");
2308906943f9SDavid du Colombier }else{
2309906943f9SDavid du Colombier dprint("ohci: ports are power switched\n");
2310906943f9SDavid du Colombier ohci->rhdesca &= ~Psm;
2311906943f9SDavid du Colombier ohci->rhsts &= ~Lpsc;
2312906943f9SDavid du Colombier }
2313906943f9SDavid du Colombier for(i = 0; i < ctlr->nports; i++) /* paranoia */
2314906943f9SDavid du Colombier ohci->rhportsts[i] = 0; /* this has no effect */
2315906943f9SDavid du Colombier delay(50);
2316906943f9SDavid du Colombier
231739dc1420SDavid du Colombier for(i = 0; i < ctlr->nports; i++){
231839dc1420SDavid du Colombier ohci->rhportsts[i] = Spp;
231939dc1420SDavid du Colombier if((ohci->rhportsts[i] & Ccs) != 0)
232039dc1420SDavid du Colombier ohci->rhportsts[i] |= Spr;
232139dc1420SDavid du Colombier }
2322906943f9SDavid du Colombier delay(100);
2323906943f9SDavid du Colombier
2324906943f9SDavid du Colombier ctrl = ohci->control;
2325906943f9SDavid du Colombier if((ctrl & Cfsmask) != Cfsoper){
2326906943f9SDavid du Colombier ctrl = (ctrl & ~Cfsmask) | Cfsoper;
2327906943f9SDavid du Colombier ohci->control = ctrl;
2328c8cbc0e9SDavid du Colombier ohci->rhsts = Lpsc;
2329906943f9SDavid du Colombier }
2330c8cbc0e9SDavid du Colombier ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift);
2331c8cbc0e9SDavid du Colombier ohci->fminterval = ival | (5120 << Fmaxpktshift);
2332906943f9SDavid du Colombier
2333906943f9SDavid du Colombier if(debug > 1)
2334906943f9SDavid du Colombier dumpohci(ctlr);
2335906943f9SDavid du Colombier }
2336906943f9SDavid du Colombier
2337906943f9SDavid du Colombier static void
scanpci(void)2338906943f9SDavid du Colombier scanpci(void)
2339906943f9SDavid du Colombier {
2340906943f9SDavid du Colombier ulong mem;
2341906943f9SDavid du Colombier Ctlr *ctlr;
2342906943f9SDavid du Colombier Pcidev *p;
2343906943f9SDavid du Colombier int i;
2344906943f9SDavid du Colombier static int already = 0;
2345906943f9SDavid du Colombier
2346906943f9SDavid du Colombier if(already)
2347906943f9SDavid du Colombier return;
2348906943f9SDavid du Colombier already = 1;
2349906943f9SDavid du Colombier p = nil;
2350906943f9SDavid du Colombier while(p = pcimatch(p, 0, 0)) {
2351906943f9SDavid du Colombier /*
2352906943f9SDavid du Colombier * Find Ohci controllers (Programming Interface = 0x10).
2353906943f9SDavid du Colombier */
2354906943f9SDavid du Colombier if(p->ccrb != Pcibcserial || p->ccru != Pciscusb ||
2355906943f9SDavid du Colombier p->ccrp != 0x10)
2356906943f9SDavid du Colombier continue;
2357906943f9SDavid du Colombier mem = p->mem[0].bar & ~0x0F;
2358906943f9SDavid du Colombier dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n",
2359906943f9SDavid du Colombier p->vid, p->did, mem, p->mem[0].size, p->intl);
2360906943f9SDavid du Colombier if(mem == 0){
2361906943f9SDavid du Colombier print("ohci: failed to map registers\n");
2362906943f9SDavid du Colombier continue;
2363906943f9SDavid du Colombier }
2364906943f9SDavid du Colombier if(p->intl == 0xFF || p->intl == 0) {
2365906943f9SDavid du Colombier print("ohci: no irq assigned for port %#lux\n", mem);
2366906943f9SDavid du Colombier continue;
2367906943f9SDavid du Colombier }
2368906943f9SDavid du Colombier
236906d03f7fSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
237006d03f7fSDavid du Colombier if (ctlr == nil)
237106d03f7fSDavid du Colombier panic("ohci: out of memory");
2372906943f9SDavid du Colombier ctlr->pcidev = p;
2373906943f9SDavid du Colombier ctlr->ohci = vmap(mem, p->mem[0].size);
2374906943f9SDavid du Colombier dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci);
2375906943f9SDavid du Colombier pcisetbme(p);
2376906943f9SDavid du Colombier pcisetpms(p, 0);
2377906943f9SDavid du Colombier for(i = 0; i < Nhcis; i++)
2378906943f9SDavid du Colombier if(ctlrs[i] == nil){
2379906943f9SDavid du Colombier ctlrs[i] = ctlr;
2380906943f9SDavid du Colombier break;
2381906943f9SDavid du Colombier }
2382906943f9SDavid du Colombier if(i == Nhcis)
2383906943f9SDavid du Colombier print("ohci: bug: no more controllers\n");
2384906943f9SDavid du Colombier }
2385906943f9SDavid du Colombier }
2386906943f9SDavid du Colombier
2387906943f9SDavid du Colombier static void
usbdebug(Hci *,int d)2388906943f9SDavid du Colombier usbdebug(Hci*, int d)
2389906943f9SDavid du Colombier {
2390906943f9SDavid du Colombier debug = d;
23916a5dc222SDavid du Colombier }
23926a5dc222SDavid du Colombier
23936a5dc222SDavid du Colombier /*
23946a5dc222SDavid du Colombier * build the periodic scheduling tree:
23956a5dc222SDavid du Colombier * framesize must be a multiple of the tree size
23966a5dc222SDavid du Colombier */
2397906943f9SDavid du Colombier static void
mkqhtree(Ctlr * ctlr)2398906943f9SDavid du Colombier mkqhtree(Ctlr *ctlr)
23996a5dc222SDavid du Colombier {
24006a5dc222SDavid du Colombier int i, n, d, o, leaf0, depth;
2401906943f9SDavid du Colombier Ed **tree;
2402906943f9SDavid du Colombier Qtree *qt;
24036a5dc222SDavid du Colombier
24046a5dc222SDavid du Colombier depth = flog2(32);
24056a5dc222SDavid du Colombier n = (1 << (depth+1)) - 1;
24066a5dc222SDavid du Colombier qt = mallocz(sizeof(*qt), 1);
24076a5dc222SDavid du Colombier if(qt == nil)
2408906943f9SDavid du Colombier panic("usb: can't allocate scheduling tree");
24096a5dc222SDavid du Colombier qt->nel = n;
24106a5dc222SDavid du Colombier qt->depth = depth;
24116a5dc222SDavid du Colombier qt->bw = mallocz(n * sizeof(qt->bw), 1);
2412906943f9SDavid du Colombier qt->root = tree = mallocz(n * sizeof(Ed *), 1);
2413906943f9SDavid du Colombier if(qt->bw == nil || qt->root == nil)
2414906943f9SDavid du Colombier panic("usb: can't allocate scheduling tree");
2415906943f9SDavid du Colombier for(i = 0; i < n; i++){
2416906943f9SDavid du Colombier if((tree[i] = edalloc()) == nil)
2417906943f9SDavid du Colombier panic("mkqhtree");
2418906943f9SDavid du Colombier tree[i]->ctrl = (8 << Edmpsshift); /* not needed */
2419906943f9SDavid du Colombier tree[i]->ctrl |= Edskip;
2420906943f9SDavid du Colombier
2421906943f9SDavid du Colombier if(i > 0)
2422906943f9SDavid du Colombier edlinked(tree[i], tree[(i-1)/2]);
2423906943f9SDavid du Colombier else
2424906943f9SDavid du Colombier edlinked(tree[i], nil);
24256a5dc222SDavid du Colombier }
2426906943f9SDavid du Colombier ctlr->ntree = i;
2427906943f9SDavid du Colombier dprint("ohci: tree: %d endpoints allocated\n", i);
24286a5dc222SDavid du Colombier
24296a5dc222SDavid du Colombier /* distribute leaves evenly round the frame list */
24306a5dc222SDavid du Colombier leaf0 = n / 2;
24316a5dc222SDavid du Colombier for(i = 0; i < 32; i++){
24326a5dc222SDavid du Colombier o = 0;
24336a5dc222SDavid du Colombier for(d = 0; d < depth; d++){
24346a5dc222SDavid du Colombier o <<= 1;
24356a5dc222SDavid du Colombier if(i & (1 << d))
24366a5dc222SDavid du Colombier o |= 1;
24376a5dc222SDavid du Colombier }
24386a5dc222SDavid du Colombier if(leaf0 + o >= n){
24396a5dc222SDavid du Colombier print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
24406a5dc222SDavid du Colombier break;
24416a5dc222SDavid du Colombier }
2442906943f9SDavid du Colombier ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]);
24436a5dc222SDavid du Colombier }
2444906943f9SDavid du Colombier ctlr->tree = qt;
24456a5dc222SDavid du Colombier }
24466a5dc222SDavid du Colombier
24476a5dc222SDavid du Colombier static void
ohcimeminit(Ctlr * ctlr)2448906943f9SDavid du Colombier ohcimeminit(Ctlr *ctlr)
24496a5dc222SDavid du Colombier {
2450906943f9SDavid du Colombier Hcca *hcca;
24516a5dc222SDavid du Colombier
2452906943f9SDavid du Colombier edfree(edalloc()); /* allocate pools now */
2453906943f9SDavid du Colombier tdfree(tdalloc());
2454906943f9SDavid du Colombier
2455906943f9SDavid du Colombier hcca = xspanalloc(sizeof(Hcca), 256, 0);
2456906943f9SDavid du Colombier if(hcca == nil)
2457906943f9SDavid du Colombier panic("usbhreset: no memory for Hcca");
2458906943f9SDavid du Colombier memset(hcca, 0, sizeof(*hcca));
2459906943f9SDavid du Colombier ctlr->hcca = hcca;
2460906943f9SDavid du Colombier
2461906943f9SDavid du Colombier mkqhtree(ctlr);
24626a5dc222SDavid du Colombier }
2463906943f9SDavid du Colombier
2464906943f9SDavid du Colombier static void
ohcireset(Ctlr * ctlr)2465906943f9SDavid du Colombier ohcireset(Ctlr *ctlr)
2466906943f9SDavid du Colombier {
24676a5dc222SDavid du Colombier ilock(ctlr);
2468906943f9SDavid du Colombier dprint("ohci %#p reset\n", ctlr->ohci);
24696a5dc222SDavid du Colombier
24706a5dc222SDavid du Colombier /*
24716a5dc222SDavid du Colombier * usually enter here in reset, wait till its through,
24726a5dc222SDavid du Colombier * then do our own so we are on known timing conditions.
2473906943f9SDavid du Colombier * Is this needed?
24746a5dc222SDavid du Colombier */
24756a5dc222SDavid du Colombier delay(100);
2476906943f9SDavid du Colombier ctlr->ohci->control = 0;
2477906943f9SDavid du Colombier delay(100);
24786a5dc222SDavid du Colombier
24796a5dc222SDavid du Colombier /* legacy support register: turn off lunacy mode */
2480906943f9SDavid du Colombier pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
24816a5dc222SDavid du Colombier
2482906943f9SDavid du Colombier iunlock(ctlr);
24836a5dc222SDavid du Colombier }
24846a5dc222SDavid du Colombier
2485c8cbc0e9SDavid du Colombier static void
shutdown(Hci * hp)2486c8cbc0e9SDavid du Colombier shutdown(Hci *hp)
2487c8cbc0e9SDavid du Colombier {
2488c8cbc0e9SDavid du Colombier Ctlr *ctlr;
2489c8cbc0e9SDavid du Colombier
2490c8cbc0e9SDavid du Colombier ctlr = hp->aux;
2491c8cbc0e9SDavid du Colombier
2492c8cbc0e9SDavid du Colombier ilock(ctlr);
2493*9d244d11SDavid du Colombier ctlr->ohci->intrdisable = Mie;
2494c8cbc0e9SDavid du Colombier ctlr->ohci->control = 0;
2495*9d244d11SDavid du Colombier coherence();
2496c8cbc0e9SDavid du Colombier delay(100);
2497c8cbc0e9SDavid du Colombier iunlock(ctlr);
2498c8cbc0e9SDavid du Colombier }
2499c8cbc0e9SDavid du Colombier
2500906943f9SDavid du Colombier static int
reset(Hci * hp)2501906943f9SDavid du Colombier reset(Hci *hp)
2502906943f9SDavid du Colombier {
2503906943f9SDavid du Colombier int i;
2504906943f9SDavid du Colombier Ctlr *ctlr;
2505906943f9SDavid du Colombier Pcidev *p;
250639dc1420SDavid du Colombier static Lock resetlck;
25076a5dc222SDavid du Colombier
2508906943f9SDavid du Colombier if(getconf("*nousbohci"))
25096a5dc222SDavid du Colombier return -1;
2510906943f9SDavid du Colombier ilock(&resetlck);
2511906943f9SDavid du Colombier scanpci();
25126a5dc222SDavid du Colombier
25136a5dc222SDavid du Colombier /*
2514906943f9SDavid du Colombier * Any adapter matches if no hp->port is supplied,
2515906943f9SDavid du Colombier * otherwise the ports must match.
25166a5dc222SDavid du Colombier */
2517906943f9SDavid du Colombier ctlr = nil;
2518906943f9SDavid du Colombier for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
2519906943f9SDavid du Colombier ctlr = ctlrs[i];
2520906943f9SDavid du Colombier if(ctlr->active == 0)
2521906943f9SDavid du Colombier if(hp->port == 0 || hp->port == (uintptr)ctlr->ohci){
2522906943f9SDavid du Colombier ctlr->active = 1;
2523906943f9SDavid du Colombier break;
25246a5dc222SDavid du Colombier }
2525906943f9SDavid du Colombier }
2526906943f9SDavid du Colombier iunlock(&resetlck);
2527906943f9SDavid du Colombier if(ctlrs[i] == nil || i == Nhcis)
2528906943f9SDavid du Colombier return -1;
2529906943f9SDavid du Colombier if(ctlr->ohci->control == ~0)
2530906943f9SDavid du Colombier return -1;
2531906943f9SDavid du Colombier
25326a5dc222SDavid du Colombier
25336a5dc222SDavid du Colombier p = ctlr->pcidev;
2534906943f9SDavid du Colombier hp->aux = ctlr;
2535906943f9SDavid du Colombier hp->port = (uintptr)ctlr->ohci;
2536906943f9SDavid du Colombier hp->irq = p->intl;
2537906943f9SDavid du Colombier hp->tbdf = p->tbdf;
2538906943f9SDavid du Colombier ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff;
2539906943f9SDavid du Colombier
2540906943f9SDavid du Colombier ohcireset(ctlr);
2541906943f9SDavid du Colombier ohcimeminit(ctlr);
25426a5dc222SDavid du Colombier
25436a5dc222SDavid du Colombier /*
2544906943f9SDavid du Colombier * Linkage to the generic HCI driver.
25456a5dc222SDavid du Colombier */
2546906943f9SDavid du Colombier hp->init = init;
2547906943f9SDavid du Colombier hp->dump = dump;
2548906943f9SDavid du Colombier hp->interrupt = interrupt;
2549906943f9SDavid du Colombier hp->epopen = epopen;
2550906943f9SDavid du Colombier hp->epclose = epclose;
2551906943f9SDavid du Colombier hp->epread = epread;
2552906943f9SDavid du Colombier hp->epwrite = epwrite;
2553906943f9SDavid du Colombier hp->seprintep = seprintep;
2554906943f9SDavid du Colombier hp->portenable = portenable;
2555906943f9SDavid du Colombier hp->portreset = portreset;
2556906943f9SDavid du Colombier hp->portstatus = portstatus;
2557c8cbc0e9SDavid du Colombier hp->shutdown = shutdown;
2558906943f9SDavid du Colombier hp->debug = usbdebug;
2559906943f9SDavid du Colombier hp->type = "ohci";
25606a5dc222SDavid du Colombier return 0;
25616a5dc222SDavid du Colombier }
25626a5dc222SDavid du Colombier
25636a5dc222SDavid du Colombier void
usbohcilink(void)25646a5dc222SDavid du Colombier usbohcilink(void)
25656a5dc222SDavid du Colombier {
2566906943f9SDavid du Colombier addhcitype("ohci", reset);
25676a5dc222SDavid du Colombier }
2568