184860c5dSDavid du Colombier /*
284860c5dSDavid du Colombier * USB Enhanced Host Controller Interface (EHCI) driver
384860c5dSDavid du Colombier * High speed USB 2.0.
484860c5dSDavid du Colombier *
584860c5dSDavid du Colombier * Note that all of our unlock routines call coherence.
684860c5dSDavid du Colombier *
784860c5dSDavid du Colombier * BUGS:
884860c5dSDavid du Colombier * - Too many delays and ilocks.
984860c5dSDavid du Colombier * - bandwidth admission control must be done per-frame.
1084860c5dSDavid du Colombier * - requires polling (some controllers miss interrupts).
1184860c5dSDavid du Colombier * - must warn of power overruns.
1284860c5dSDavid du Colombier */
1384860c5dSDavid du Colombier
1484860c5dSDavid du Colombier #include "u.h"
1584860c5dSDavid du Colombier #include "../port/lib.h"
1684860c5dSDavid du Colombier #include "mem.h"
1784860c5dSDavid du Colombier #include "dat.h"
1884860c5dSDavid du Colombier #include "fns.h"
1984860c5dSDavid du Colombier #include "io.h"
2084860c5dSDavid du Colombier #include "../port/error.h"
2184860c5dSDavid du Colombier #include "../port/usb.h"
22*5bdeb9a2SDavid du Colombier #include "../port/portusbehci.h"
2384860c5dSDavid du Colombier #include "usbehci.h"
2484860c5dSDavid du Colombier #include "uncached.h"
2584860c5dSDavid du Colombier
2684860c5dSDavid du Colombier #define diprint if(ehcidebug || iso->debug)print
2784860c5dSDavid du Colombier #define ddiprint if(ehcidebug>1 || iso->debug>1)print
2884860c5dSDavid du Colombier #define dqprint if(ehcidebug || (qh->io && qh->io->debug))print
2984860c5dSDavid du Colombier #define ddqprint if(ehcidebug>1 || (qh->io && qh->io->debug>1))print
3084860c5dSDavid du Colombier
3184860c5dSDavid du Colombier #define TRUNC(x, sz) ((x) & ((sz)-1))
3284860c5dSDavid du Colombier #define LPTR(q) ((ulong*)KADDR((q) & ~0x1F))
3384860c5dSDavid du Colombier
3484860c5dSDavid du Colombier typedef struct Ctlio Ctlio;
3584860c5dSDavid du Colombier typedef union Ed Ed;
3684860c5dSDavid du Colombier typedef struct Edpool Edpool;
3784860c5dSDavid du Colombier typedef struct Itd Itd;
3884860c5dSDavid du Colombier typedef struct Qio Qio;
3984860c5dSDavid du Colombier typedef struct Qtd Qtd;
4084860c5dSDavid du Colombier typedef struct Sitd Sitd;
4184860c5dSDavid du Colombier typedef struct Td Td;
4284860c5dSDavid du Colombier
4384860c5dSDavid du Colombier /*
4484860c5dSDavid du Colombier * EHCI interface registers and bits
4584860c5dSDavid du Colombier */
4684860c5dSDavid du Colombier enum
4784860c5dSDavid du Colombier {
4884860c5dSDavid du Colombier /* Queue states (software) */
4984860c5dSDavid du Colombier Qidle = 0,
5084860c5dSDavid du Colombier Qinstall,
5184860c5dSDavid du Colombier Qrun,
5284860c5dSDavid du Colombier Qdone,
5384860c5dSDavid du Colombier Qclose,
5484860c5dSDavid du Colombier Qfree,
5584860c5dSDavid du Colombier
5684860c5dSDavid du Colombier Enabledelay = 100, /* waiting for a port to enable */
5784860c5dSDavid du Colombier Abortdelay = 5, /* delay after cancelling Tds (ms) */
5884860c5dSDavid du Colombier
5984860c5dSDavid du Colombier Incr = 64, /* for pools of Tds, Qhs, etc. */
6084860c5dSDavid du Colombier Align = 128, /* in bytes for all those descriptors */
6184860c5dSDavid du Colombier
6284860c5dSDavid du Colombier /* Keep them as a power of 2, lower than ctlr->nframes */
6384860c5dSDavid du Colombier /* Also, keep Nisoframes >= Nintrleafs */
6484860c5dSDavid du Colombier Nintrleafs = 32, /* nb. of leaf frames in intr. tree */
6584860c5dSDavid du Colombier Nisoframes = 64, /* nb. of iso frames (in window) */
6684860c5dSDavid du Colombier
6784860c5dSDavid du Colombier /*
6884860c5dSDavid du Colombier * HW constants
6984860c5dSDavid du Colombier */
7084860c5dSDavid du Colombier
7184860c5dSDavid du Colombier /* Itd bits (csw[]) */
7284860c5dSDavid du Colombier Itdactive = 0x80000000, /* execution enabled */
7384860c5dSDavid du Colombier Itddberr = 0x40000000, /* data buffer error */
7484860c5dSDavid du Colombier Itdbabble = 0x20000000, /* babble error */
7584860c5dSDavid du Colombier Itdtrerr = 0x10000000, /* transaction error */
7684860c5dSDavid du Colombier Itdlenshift = 16, /* transaction length */
7784860c5dSDavid du Colombier Itdlenmask = 0xFFF,
7884860c5dSDavid du Colombier Itdioc = 0x00008000, /* interrupt on complete */
7984860c5dSDavid du Colombier Itdpgshift = 12, /* page select field */
8084860c5dSDavid du Colombier Itdoffshift = 0, /* transaction offset */
8184860c5dSDavid du Colombier /* Itd bits, buffer[] */
8284860c5dSDavid du Colombier Itdepshift = 8, /* endpoint address (buffer[0]) */
8384860c5dSDavid du Colombier Itddevshift = 0, /* device address (buffer[0]) */
8484860c5dSDavid du Colombier Itdin = 0x800, /* is input (buffer[1]) */
8584860c5dSDavid du Colombier Itdout = 0,
8684860c5dSDavid du Colombier Itdmaxpktshift = 0, /* max packet (buffer[1]) */
8784860c5dSDavid du Colombier Itdntdsshift = 0, /* nb. of tds per µframe (buffer[2]) */
8884860c5dSDavid du Colombier
8984860c5dSDavid du Colombier Itderrors = Itddberr|Itdbabble|Itdtrerr,
9084860c5dSDavid du Colombier
9184860c5dSDavid du Colombier /* Sitd bits (epc) */
9284860c5dSDavid du Colombier Stdin = 0x80000000, /* input direction */
9384860c5dSDavid du Colombier Stdportshift = 24, /* hub port number */
9484860c5dSDavid du Colombier Stdhubshift = 16, /* hub address */
9584860c5dSDavid du Colombier Stdepshift = 8, /* endpoint address */
9684860c5dSDavid du Colombier Stddevshift = 0, /* device address */
9784860c5dSDavid du Colombier /* Sitd bits (mfs) */
9884860c5dSDavid du Colombier Stdssmshift = 0, /* split start mask */
9984860c5dSDavid du Colombier Stdscmshift = 8, /* split complete mask */
10084860c5dSDavid du Colombier /* Sitd bits (csw) */
10184860c5dSDavid du Colombier Stdioc = 0x80000000, /* interrupt on complete */
10284860c5dSDavid du Colombier Stdpg = 0x40000000, /* page select */
10384860c5dSDavid du Colombier Stdlenshift = 16, /* total bytes to transfer */
10484860c5dSDavid du Colombier Stdlenmask = 0x3FF,
10584860c5dSDavid du Colombier Stdactive = 0x00000080, /* active */
10684860c5dSDavid du Colombier Stderr = 0x00000040, /* tr. translator error */
10784860c5dSDavid du Colombier Stddberr = 0x00000020, /* data buffer error */
10884860c5dSDavid du Colombier Stdbabble = 0x00000010, /* babble error */
10984860c5dSDavid du Colombier Stdtrerr = 0x00000008, /* transaction error */
11084860c5dSDavid du Colombier Stdmmf = 0x00000004, /* missed µframe */
11184860c5dSDavid du Colombier Stddcs = 0x00000002, /* do complete split */
11284860c5dSDavid du Colombier
11384860c5dSDavid du Colombier Stderrors = Stderr|Stddberr|Stdbabble|Stdtrerr|Stdmmf,
11484860c5dSDavid du Colombier
11584860c5dSDavid du Colombier /* Sitd bits buffer[1] */
11684860c5dSDavid du Colombier Stdtpall = 0x00000000, /* all payload here (188 bytes) */
11784860c5dSDavid du Colombier Stdtpbegin = 0x00000008, /* first payload for fs trans. */
11884860c5dSDavid du Colombier Stdtcntmask = 0x00000007, /* T-count */
11984860c5dSDavid du Colombier
12084860c5dSDavid du Colombier /* Td bits (csw) */
12184860c5dSDavid du Colombier Tddata1 = 0x80000000, /* data toggle 1 */
12284860c5dSDavid du Colombier Tddata0 = 0x00000000, /* data toggle 0 */
12384860c5dSDavid du Colombier Tdlenshift = 16, /* total bytes to transfer */
12484860c5dSDavid du Colombier Tdlenmask = 0x7FFF,
12584860c5dSDavid du Colombier Tdmaxpkt = 0x5000, /* max buffer for a Td */
12684860c5dSDavid du Colombier Tdioc = 0x00008000, /* interrupt on complete */
12784860c5dSDavid du Colombier Tdpgshift = 12, /* current page */
12884860c5dSDavid du Colombier Tdpgmask = 7,
12984860c5dSDavid du Colombier Tderr1 = 0x00000400, /* bit 0 of error counter */
13084860c5dSDavid du Colombier Tderr2 = 0x00000800, /* bit 1 of error counter */
13184860c5dSDavid du Colombier Tdtokout = 0x00000000, /* direction out */
13284860c5dSDavid du Colombier Tdtokin = 0x00000100, /* direction in */
13384860c5dSDavid du Colombier Tdtoksetup = 0x00000200, /* setup packet */
13484860c5dSDavid du Colombier Tdtok = 0x00000300, /* token bits */
13584860c5dSDavid du Colombier Tdactive = 0x00000080, /* active */
13684860c5dSDavid du Colombier Tdhalt = 0x00000040, /* halted */
13784860c5dSDavid du Colombier Tddberr = 0x00000020, /* data buffer error */
13884860c5dSDavid du Colombier Tdbabble = 0x00000010, /* babble error */
13984860c5dSDavid du Colombier Tdtrerr = 0x00000008, /* transaction error */
14084860c5dSDavid du Colombier Tdmmf = 0x00000004, /* missed µframe */
14184860c5dSDavid du Colombier Tddcs = 0x00000002, /* do complete split */
14284860c5dSDavid du Colombier Tdping = 0x00000001, /* do ping */
14384860c5dSDavid du Colombier
14484860c5dSDavid du Colombier Tderrors = Tdhalt|Tddberr|Tdbabble|Tdtrerr|Tdmmf,
14584860c5dSDavid du Colombier
14684860c5dSDavid du Colombier /* Qh bits (eps0) */
14784860c5dSDavid du Colombier Qhrlcmask = 0xF, /* nak reload count */
14884860c5dSDavid du Colombier Qhrlcshift = 28, /* nak reload count */
14984860c5dSDavid du Colombier Qhnhctl = 0x08000000, /* not-high speed ctl */
15084860c5dSDavid du Colombier Qhmplmask = 0x7FF, /* max packet */
15184860c5dSDavid du Colombier Qhmplshift = 16,
15284860c5dSDavid du Colombier Qhhrl = 0x00008000, /* head of reclamation list */
15384860c5dSDavid du Colombier Qhdtc = 0x00004000, /* data toggle ctl. */
15484860c5dSDavid du Colombier Qhint = 0x00000080, /* inactivate on next transition */
15584860c5dSDavid du Colombier Qhspeedmask = 0x00003000, /* speed bits */
15684860c5dSDavid du Colombier Qhfull = 0x00000000, /* full speed */
15784860c5dSDavid du Colombier Qhlow = 0x00001000, /* low speed */
15884860c5dSDavid du Colombier Qhhigh = 0x00002000, /* high speed */
15984860c5dSDavid du Colombier
16084860c5dSDavid du Colombier /* Qh bits (eps1) */
16184860c5dSDavid du Colombier Qhmultshift = 30, /* multiple tds per µframe */
16284860c5dSDavid du Colombier Qhmultmask = 3,
16384860c5dSDavid du Colombier Qhportshift = 23, /* hub port number */
16484860c5dSDavid du Colombier Qhhubshift = 16, /* hub address */
16584860c5dSDavid du Colombier Qhscmshift = 8, /* split completion mask bits */
16684860c5dSDavid du Colombier Qhismshift = 0, /* interrupt sched. mask bits */
16784860c5dSDavid du Colombier };
16884860c5dSDavid du Colombier
16984860c5dSDavid du Colombier /*
17084860c5dSDavid du Colombier * Endpoint tree (software)
17184860c5dSDavid du Colombier */
17284860c5dSDavid du Colombier struct Qtree
17384860c5dSDavid du Colombier {
17484860c5dSDavid du Colombier int nel;
17584860c5dSDavid du Colombier int depth;
17684860c5dSDavid du Colombier ulong* bw;
17784860c5dSDavid du Colombier Qh** root;
17884860c5dSDavid du Colombier };
17984860c5dSDavid du Colombier
18084860c5dSDavid du Colombier /*
18184860c5dSDavid du Colombier * One per endpoint per direction, to control I/O.
18284860c5dSDavid du Colombier */
18384860c5dSDavid du Colombier struct Qio
18484860c5dSDavid du Colombier {
18584860c5dSDavid du Colombier QLock; /* for the entire I/O process */
18684860c5dSDavid du Colombier Rendez; /* wait for completion */
18784860c5dSDavid du Colombier Qh* qh; /* Td list (field const after init) */
18884860c5dSDavid du Colombier int usbid; /* usb address for endpoint/device */
18984860c5dSDavid du Colombier int toggle; /* Tddata0/Tddata1 */
19084860c5dSDavid du Colombier int tok; /* Tdtoksetup, Tdtokin, Tdtokout */
19184860c5dSDavid du Colombier ulong iotime; /* last I/O time; to hold interrupt polls */
19284860c5dSDavid du Colombier int debug; /* debug flag from the endpoint */
19384860c5dSDavid du Colombier char* err; /* error string */
19484860c5dSDavid du Colombier char* tag; /* debug (no room in Qh for this) */
19584860c5dSDavid du Colombier ulong bw;
19684860c5dSDavid du Colombier };
19784860c5dSDavid du Colombier
19884860c5dSDavid du Colombier struct Ctlio
19984860c5dSDavid du Colombier {
20084860c5dSDavid du Colombier Qio; /* a single Qio for each RPC */
20184860c5dSDavid du Colombier uchar* data; /* read from last ctl req. */
20284860c5dSDavid du Colombier int ndata; /* number of bytes read */
20384860c5dSDavid du Colombier };
20484860c5dSDavid du Colombier
20584860c5dSDavid du Colombier struct Isoio
20684860c5dSDavid du Colombier {
20784860c5dSDavid du Colombier QLock;
20884860c5dSDavid du Colombier Rendez; /* wait for space/completion/errors */
20984860c5dSDavid du Colombier int usbid; /* address used for device/endpoint */
21084860c5dSDavid du Colombier int tok; /* Tdtokin or Tdtokout */
21184860c5dSDavid du Colombier int state; /* Qrun -> Qdone -> Qrun... -> Qclose */
21284860c5dSDavid du Colombier int nframes; /* number of frames ([S]Itds) used */
21384860c5dSDavid du Colombier uchar* data; /* iso data buffers if not embedded */
21484860c5dSDavid du Colombier char* err; /* error string */
21584860c5dSDavid du Colombier int nerrs; /* nb of consecutive I/O errors */
21684860c5dSDavid du Colombier ulong maxsize; /* ntds * ep->maxpkt */
21784860c5dSDavid du Colombier long nleft; /* number of bytes left from last write */
21884860c5dSDavid du Colombier int debug; /* debug flag from the endpoint */
21984860c5dSDavid du Colombier int hs; /* is high speed? */
22084860c5dSDavid du Colombier Isoio* next; /* in list of active Isoios */
22184860c5dSDavid du Colombier ulong td0frno; /* first frame used in ctlr */
22284860c5dSDavid du Colombier union{
22384860c5dSDavid du Colombier Itd* tdi; /* next td processed by interrupt */
22484860c5dSDavid du Colombier Sitd* stdi;
22584860c5dSDavid du Colombier };
22684860c5dSDavid du Colombier union{
22784860c5dSDavid du Colombier Itd* tdu; /* next td for user I/O in tdps */
22884860c5dSDavid du Colombier Sitd* stdu;
22984860c5dSDavid du Colombier };
23084860c5dSDavid du Colombier union{
23184860c5dSDavid du Colombier Itd** itdps; /* itdps[i]: ptr to Itd for i-th frame or nil */
23284860c5dSDavid du Colombier Sitd** sitdps; /* sitdps[i]: ptr to Sitd for i-th frame or nil */
23384860c5dSDavid du Colombier ulong** tdps; /* same thing, as seen by hw */
23484860c5dSDavid du Colombier };
23584860c5dSDavid du Colombier };
23684860c5dSDavid du Colombier
23784860c5dSDavid du Colombier struct Edpool
23884860c5dSDavid du Colombier {
23984860c5dSDavid du Colombier Lock;
24084860c5dSDavid du Colombier Ed* free;
24184860c5dSDavid du Colombier int nalloc;
24284860c5dSDavid du Colombier int ninuse;
24384860c5dSDavid du Colombier int nfree;
24484860c5dSDavid du Colombier };
24584860c5dSDavid du Colombier
24684860c5dSDavid du Colombier /*
24784860c5dSDavid du Colombier * We use the 64-bit version for Itd, Sitd, Td, and Qh.
24884860c5dSDavid du Colombier * If the ehci is 64-bit capable it assumes we are using those
24984860c5dSDavid du Colombier * structures even when the system is 32 bits.
25084860c5dSDavid du Colombier */
25184860c5dSDavid du Colombier
25284860c5dSDavid du Colombier /*
25384860c5dSDavid du Colombier * Iso transfer descriptor. hw: 92 bytes, 108 bytes total
25484860c5dSDavid du Colombier * aligned to 32.
25584860c5dSDavid du Colombier */
25684860c5dSDavid du Colombier struct Itd
25784860c5dSDavid du Colombier {
25884860c5dSDavid du Colombier ulong link; /* to next hw struct */
25984860c5dSDavid du Colombier ulong csw[8]; /* sts/length/pg/off. updated by hw */
26084860c5dSDavid du Colombier ulong buffer[7]; /* buffer pointers, addrs, maxsz */
26184860c5dSDavid du Colombier ulong xbuffer[7]; /* high 32 bits of buffer for 64-bits */
26284860c5dSDavid du Colombier
26384860c5dSDavid du Colombier ulong _pad0; /* pad to next cache line */
26484860c5dSDavid du Colombier /* cache-line boundary here */
26584860c5dSDavid du Colombier
26684860c5dSDavid du Colombier /* software */
26784860c5dSDavid du Colombier Itd* next;
26884860c5dSDavid du Colombier ulong ndata; /* number of bytes in data */
26984860c5dSDavid du Colombier ulong mdata; /* max number of bytes in data */
27084860c5dSDavid du Colombier uchar* data;
27184860c5dSDavid du Colombier };
27284860c5dSDavid du Colombier
27384860c5dSDavid du Colombier /*
27484860c5dSDavid du Colombier * Split transaction iso transfer descriptor.
27584860c5dSDavid du Colombier * hw: 36 bytes, 52 bytes total. aligned to 32.
27684860c5dSDavid du Colombier */
27784860c5dSDavid du Colombier struct Sitd
27884860c5dSDavid du Colombier {
27984860c5dSDavid du Colombier ulong link; /* to next hw struct */
28084860c5dSDavid du Colombier ulong epc; /* static endpoint state. addrs */
28184860c5dSDavid du Colombier ulong mfs; /* static endpoint state. µ-frame sched. */
28284860c5dSDavid du Colombier ulong csw; /* transfer state. updated by hw */
28384860c5dSDavid du Colombier ulong buffer[2]; /* buf. ptr/offset. offset updated by hw */
28484860c5dSDavid du Colombier /* buf ptr/TP/Tcnt. TP/Tcnt updated by hw */
28584860c5dSDavid du Colombier ulong blink; /* back pointer */
28684860c5dSDavid du Colombier /* cache-line boundary after xbuffer[0] */
28784860c5dSDavid du Colombier ulong xbuffer[2]; /* high 32 bits of buffer for 64-bits */
28884860c5dSDavid du Colombier
28984860c5dSDavid du Colombier /* software */
29084860c5dSDavid du Colombier Sitd* next;
29184860c5dSDavid du Colombier ulong ndata; /* number of bytes in data */
29284860c5dSDavid du Colombier ulong mdata; /* max number of bytes in data */
29384860c5dSDavid du Colombier uchar* data;
29484860c5dSDavid du Colombier };
29584860c5dSDavid du Colombier
29684860c5dSDavid du Colombier /*
29784860c5dSDavid du Colombier * Queue element transfer descriptor.
29884860c5dSDavid du Colombier * hw: first 52 bytes, total 68+sbuff bytes. aligned to 32 bytes.
29984860c5dSDavid du Colombier */
30084860c5dSDavid du Colombier struct Td
30184860c5dSDavid du Colombier {
30284860c5dSDavid du Colombier ulong nlink; /* to next Td */
30384860c5dSDavid du Colombier ulong alink; /* alternate link to next Td */
30484860c5dSDavid du Colombier ulong csw; /* cmd/sts. updated by hw */
30584860c5dSDavid du Colombier ulong buffer[5]; /* buf ptrs. offset updated by hw */
30684860c5dSDavid du Colombier /* cache-line boundary here */
30784860c5dSDavid du Colombier ulong xbuffer[5]; /* high 32 bits of buffer for 64-bits */
30884860c5dSDavid du Colombier
30984860c5dSDavid du Colombier /* software */
31084860c5dSDavid du Colombier Td* next; /* in qh or Isoio or free list */
31184860c5dSDavid du Colombier ulong ndata; /* bytes available/used at data */
31284860c5dSDavid du Colombier uchar* data; /* pointer to actual data */
31384860c5dSDavid du Colombier uchar* buff; /* allocated data buffer or nil */
31484860c5dSDavid du Colombier uchar sbuff[1]; /* first byte of embedded buffer */
31584860c5dSDavid du Colombier };
31684860c5dSDavid du Colombier
31784860c5dSDavid du Colombier /*
31884860c5dSDavid du Colombier * Queue head. Aligned to 32 bytes.
31984860c5dSDavid du Colombier * hw: first 68 bytes, 92 total.
32084860c5dSDavid du Colombier */
32184860c5dSDavid du Colombier struct Qh
32284860c5dSDavid du Colombier {
32384860c5dSDavid du Colombier ulong link; /* to next Qh in round robin */
32484860c5dSDavid du Colombier ulong eps0; /* static endpoint state. addrs */
32584860c5dSDavid du Colombier ulong eps1; /* static endpoint state. µ-frame sched. */
32684860c5dSDavid du Colombier
32784860c5dSDavid du Colombier /* updated by hw */
32884860c5dSDavid du Colombier ulong tclink; /* current Td (No Term bit here!) */
32984860c5dSDavid du Colombier ulong nlink; /* to next Td */
33084860c5dSDavid du Colombier ulong alink; /* alternate link to next Td */
33184860c5dSDavid du Colombier ulong csw; /* cmd/sts. updated by hw */
33284860c5dSDavid du Colombier /* cache-line boundary after buffer[0] */
33384860c5dSDavid du Colombier ulong buffer[5]; /* buf ptrs. offset updated by hw */
33484860c5dSDavid du Colombier ulong xbuffer[5]; /* high 32 bits of buffer for 64-bits */
33584860c5dSDavid du Colombier
33684860c5dSDavid du Colombier /* software */
33784860c5dSDavid du Colombier Qh* next; /* in controller list/tree of Qhs */
33884860c5dSDavid du Colombier int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
33984860c5dSDavid du Colombier Qio* io; /* for this queue */
34084860c5dSDavid du Colombier Td* tds; /* for this queue */
34184860c5dSDavid du Colombier int sched; /* slot for for intr. Qhs */
34284860c5dSDavid du Colombier Qh* inext; /* next in list of intr. qhs */
34384860c5dSDavid du Colombier };
34484860c5dSDavid du Colombier
34584860c5dSDavid du Colombier /*
34684860c5dSDavid du Colombier * We can avoid frame span traversal nodes if we don't span frames.
34784860c5dSDavid du Colombier * Just schedule transfers that can fit on the current frame and
34884860c5dSDavid du Colombier * wait a little bit otherwise.
34984860c5dSDavid du Colombier */
35084860c5dSDavid du Colombier
35184860c5dSDavid du Colombier /*
35284860c5dSDavid du Colombier * Software. Ehci descriptors provided by pool.
35384860c5dSDavid du Colombier * There are soo few because we avoid using Fstn.
35484860c5dSDavid du Colombier */
35584860c5dSDavid du Colombier union Ed
35684860c5dSDavid du Colombier {
35784860c5dSDavid du Colombier Ed* next; /* in free list */
35884860c5dSDavid du Colombier Qh qh;
35984860c5dSDavid du Colombier Td td;
36084860c5dSDavid du Colombier Itd itd;
36184860c5dSDavid du Colombier Sitd sitd;
36284860c5dSDavid du Colombier uchar align[Align];
36384860c5dSDavid du Colombier };
36484860c5dSDavid du Colombier
3655a5ba3fdSDavid du Colombier int ehcidebug = 0;
36684860c5dSDavid du Colombier
36784860c5dSDavid du Colombier static Edpool edpool;
36884860c5dSDavid du Colombier static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };
36984860c5dSDavid du Colombier
37084860c5dSDavid du Colombier Ecapio* ehcidebugcapio;
37184860c5dSDavid du Colombier int ehcidebugport;
37284860c5dSDavid du Colombier
37384860c5dSDavid du Colombier void
ehcirun(Ctlr * ctlr,int on)37484860c5dSDavid du Colombier ehcirun(Ctlr *ctlr, int on)
37584860c5dSDavid du Colombier {
37684860c5dSDavid du Colombier int i;
37784860c5dSDavid du Colombier Eopio *opio;
37884860c5dSDavid du Colombier
37984860c5dSDavid du Colombier ddprint("ehci %#p %s\n", ctlr->capio, on ? "starting" : "halting");
38084860c5dSDavid du Colombier opio = ctlr->opio;
38184860c5dSDavid du Colombier if(on)
38284860c5dSDavid du Colombier opio->cmd |= Crun;
38384860c5dSDavid du Colombier else
38484860c5dSDavid du Colombier opio->cmd = Cstop;
38584860c5dSDavid du Colombier coherence();
38684860c5dSDavid du Colombier for(i = 0; i < 100; i++)
38784860c5dSDavid du Colombier if(on == 0 && (opio->sts & Shalted) != 0)
38884860c5dSDavid du Colombier break;
38984860c5dSDavid du Colombier else if(on != 0 && (opio->sts & Shalted) == 0)
39084860c5dSDavid du Colombier break;
39184860c5dSDavid du Colombier else
39284860c5dSDavid du Colombier delay(1);
39384860c5dSDavid du Colombier if(i == 100)
39484860c5dSDavid du Colombier print("ehci %#p %s cmd timed out\n",
39584860c5dSDavid du Colombier ctlr->capio, on ? "run" : "halt");
39684860c5dSDavid du Colombier ddprint("ehci %#p cmd %#lux sts %#lux\n",
39784860c5dSDavid du Colombier ctlr->capio, opio->cmd, opio->sts);
39884860c5dSDavid du Colombier }
39984860c5dSDavid du Colombier
40084860c5dSDavid du Colombier static void*
edalloc(void)40184860c5dSDavid du Colombier edalloc(void)
40284860c5dSDavid du Colombier {
40384860c5dSDavid du Colombier Ed *ed, *pool;
40484860c5dSDavid du Colombier int i;
40584860c5dSDavid du Colombier
40684860c5dSDavid du Colombier lock(&edpool);
40784860c5dSDavid du Colombier if(edpool.free == nil){
40884860c5dSDavid du Colombier pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
40984860c5dSDavid du Colombier if(pool == nil)
41084860c5dSDavid du Colombier panic("edalloc");
41184860c5dSDavid du Colombier for(i=Incr; --i>=0;){
41284860c5dSDavid du Colombier pool[i].next = edpool.free;
41384860c5dSDavid du Colombier edpool.free = &pool[i];
41484860c5dSDavid du Colombier }
41584860c5dSDavid du Colombier edpool.nalloc += Incr;
41684860c5dSDavid du Colombier edpool.nfree += Incr;
41784860c5dSDavid du Colombier dprint("ehci: edalloc: %d eds\n", edpool.nalloc);
41884860c5dSDavid du Colombier }
41984860c5dSDavid du Colombier ed = edpool.free;
42084860c5dSDavid du Colombier edpool.free = ed->next;
42184860c5dSDavid du Colombier edpool.ninuse++;
42284860c5dSDavid du Colombier edpool.nfree--;
42384860c5dSDavid du Colombier unlock(&edpool);
42484860c5dSDavid du Colombier
42584860c5dSDavid du Colombier memset(ed, 0, sizeof(Ed)); /* safety */
42684860c5dSDavid du Colombier assert(((ulong)ed & 0xF) == 0);
42784860c5dSDavid du Colombier return ed;
42884860c5dSDavid du Colombier }
42984860c5dSDavid du Colombier
43084860c5dSDavid du Colombier static void
edfree(void * a)43184860c5dSDavid du Colombier edfree(void *a)
43284860c5dSDavid du Colombier {
43384860c5dSDavid du Colombier Ed *ed;
43484860c5dSDavid du Colombier
43584860c5dSDavid du Colombier ed = a;
43684860c5dSDavid du Colombier lock(&edpool);
43784860c5dSDavid du Colombier ed->next = edpool.free;
43884860c5dSDavid du Colombier edpool.free = ed;
43984860c5dSDavid du Colombier edpool.ninuse--;
44084860c5dSDavid du Colombier edpool.nfree++;
44184860c5dSDavid du Colombier unlock(&edpool);
44284860c5dSDavid du Colombier }
44384860c5dSDavid du Colombier
44484860c5dSDavid du Colombier /*
44584860c5dSDavid du Colombier * Allocate and do some initialization.
44684860c5dSDavid du Colombier * Free after releasing buffers used.
44784860c5dSDavid du Colombier */
44884860c5dSDavid du Colombier
44984860c5dSDavid du Colombier static Itd*
itdalloc(void)45084860c5dSDavid du Colombier itdalloc(void)
45184860c5dSDavid du Colombier {
45284860c5dSDavid du Colombier Itd *td;
45384860c5dSDavid du Colombier
45484860c5dSDavid du Colombier td = edalloc();
45584860c5dSDavid du Colombier td->link = Lterm;
45684860c5dSDavid du Colombier return td;
45784860c5dSDavid du Colombier }
45884860c5dSDavid du Colombier
45984860c5dSDavid du Colombier static void
itdfree(Itd * td)46084860c5dSDavid du Colombier itdfree(Itd *td)
46184860c5dSDavid du Colombier {
46284860c5dSDavid du Colombier edfree(td);
46384860c5dSDavid du Colombier }
46484860c5dSDavid du Colombier
46584860c5dSDavid du Colombier static Sitd*
sitdalloc(void)46684860c5dSDavid du Colombier sitdalloc(void)
46784860c5dSDavid du Colombier {
46884860c5dSDavid du Colombier Sitd *td;
46984860c5dSDavid du Colombier
47084860c5dSDavid du Colombier td = edalloc();
47184860c5dSDavid du Colombier td->link = td->blink = Lterm;
47284860c5dSDavid du Colombier return td;
47384860c5dSDavid du Colombier }
47484860c5dSDavid du Colombier
47584860c5dSDavid du Colombier static void
sitdfree(Sitd * td)47684860c5dSDavid du Colombier sitdfree(Sitd *td)
47784860c5dSDavid du Colombier {
47884860c5dSDavid du Colombier edfree(td);
47984860c5dSDavid du Colombier }
48084860c5dSDavid du Colombier
48184860c5dSDavid du Colombier static Td*
tdalloc(void)48284860c5dSDavid du Colombier tdalloc(void)
48384860c5dSDavid du Colombier {
48484860c5dSDavid du Colombier Td *td;
48584860c5dSDavid du Colombier
48684860c5dSDavid du Colombier td = edalloc();
48784860c5dSDavid du Colombier td->nlink = td->alink = Lterm;
48884860c5dSDavid du Colombier return td;
48984860c5dSDavid du Colombier }
49084860c5dSDavid du Colombier
49184860c5dSDavid du Colombier static void
tdfree(Td * td)49284860c5dSDavid du Colombier tdfree(Td *td)
49384860c5dSDavid du Colombier {
49484860c5dSDavid du Colombier if(td == nil)
49584860c5dSDavid du Colombier return;
49684860c5dSDavid du Colombier free(td->buff);
49784860c5dSDavid du Colombier edfree(td);
49884860c5dSDavid du Colombier }
49984860c5dSDavid du Colombier
50084860c5dSDavid du Colombier static void
tdlinktd(Td * td,Td * next)50184860c5dSDavid du Colombier tdlinktd(Td *td, Td *next)
50284860c5dSDavid du Colombier {
50384860c5dSDavid du Colombier td->next = next;
50484860c5dSDavid du Colombier td->alink = Lterm;
50584860c5dSDavid du Colombier if(next == nil)
50684860c5dSDavid du Colombier td->nlink = Lterm;
50784860c5dSDavid du Colombier else
50884860c5dSDavid du Colombier td->nlink = PADDR(next);
50984860c5dSDavid du Colombier coherence();
51084860c5dSDavid du Colombier }
51184860c5dSDavid du Colombier
51284860c5dSDavid du Colombier static Qh*
qhlinkqh(Qh * qh,Qh * next)51384860c5dSDavid du Colombier qhlinkqh(Qh *qh, Qh *next)
51484860c5dSDavid du Colombier {
51584860c5dSDavid du Colombier qh->next = next;
5162a782499SDavid du Colombier if(next == nil)
5172a782499SDavid du Colombier qh->link = Lterm;
5182a782499SDavid du Colombier else
51984860c5dSDavid du Colombier qh->link = PADDR(next)|Lqh;
52084860c5dSDavid du Colombier coherence();
52184860c5dSDavid du Colombier return qh;
52284860c5dSDavid du Colombier }
52384860c5dSDavid du Colombier
52484860c5dSDavid du Colombier static void
qhsetaddr(Qh * qh,ulong addr)52584860c5dSDavid du Colombier qhsetaddr(Qh *qh, ulong addr)
52684860c5dSDavid du Colombier {
52784860c5dSDavid du Colombier ulong eps0;
52884860c5dSDavid du Colombier
52984860c5dSDavid du Colombier eps0 = qh->eps0 & ~((Epmax<<8)|Devmax);
53084860c5dSDavid du Colombier qh->eps0 = eps0 | addr & Devmax | ((addr >> 7) & Epmax) << 8;
53184860c5dSDavid du Colombier coherence();
53284860c5dSDavid du Colombier }
53384860c5dSDavid du Colombier
53484860c5dSDavid du Colombier /*
5352a782499SDavid du Colombier * return largest power of 2 <= n
53684860c5dSDavid du Colombier */
53784860c5dSDavid du Colombier static int
flog2lower(int n)53884860c5dSDavid du Colombier flog2lower(int n)
53984860c5dSDavid du Colombier {
54084860c5dSDavid du Colombier int i;
54184860c5dSDavid du Colombier
54284860c5dSDavid du Colombier for(i = 0; (1 << (i + 1)) <= n; i++)
54384860c5dSDavid du Colombier ;
54484860c5dSDavid du Colombier return i;
54584860c5dSDavid du Colombier }
54684860c5dSDavid du Colombier
54784860c5dSDavid du Colombier static int
pickschedq(Qtree * qt,int pollival,ulong bw,ulong limit)54884860c5dSDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
54984860c5dSDavid du Colombier {
55084860c5dSDavid du Colombier int i, j, d, upperb, q;
55184860c5dSDavid du Colombier ulong best, worst, total;
55284860c5dSDavid du Colombier
55384860c5dSDavid du Colombier d = flog2lower(pollival);
55484860c5dSDavid du Colombier if(d > qt->depth)
55584860c5dSDavid du Colombier d = qt->depth;
55684860c5dSDavid du Colombier q = -1;
55784860c5dSDavid du Colombier worst = 0;
55884860c5dSDavid du Colombier best = ~0;
55984860c5dSDavid du Colombier upperb = (1 << (d+1)) - 1;
56084860c5dSDavid du Colombier for(i = (1 << d) - 1; i < upperb; i++){
56184860c5dSDavid du Colombier total = qt->bw[0];
56284860c5dSDavid du Colombier for(j = i; j > 0; j = (j - 1) / 2)
56384860c5dSDavid du Colombier total += qt->bw[j];
56484860c5dSDavid du Colombier if(total < best){
56584860c5dSDavid du Colombier best = total;
56684860c5dSDavid du Colombier q = i;
56784860c5dSDavid du Colombier }
56884860c5dSDavid du Colombier if(total > worst)
56984860c5dSDavid du Colombier worst = total;
57084860c5dSDavid du Colombier }
57184860c5dSDavid du Colombier if(worst + bw >= limit)
57284860c5dSDavid du Colombier return -1;
57384860c5dSDavid du Colombier return q;
57484860c5dSDavid du Colombier }
57584860c5dSDavid du Colombier
57684860c5dSDavid du Colombier static int
schedq(Ctlr * ctlr,Qh * qh,int pollival)57784860c5dSDavid du Colombier schedq(Ctlr *ctlr, Qh *qh, int pollival)
57884860c5dSDavid du Colombier {
57984860c5dSDavid du Colombier int q;
58084860c5dSDavid du Colombier Qh *tqh;
58184860c5dSDavid du Colombier ulong bw;
58284860c5dSDavid du Colombier
58384860c5dSDavid du Colombier bw = qh->io->bw;
58484860c5dSDavid du Colombier q = pickschedq(ctlr->tree, pollival, 0, ~0);
58584860c5dSDavid du Colombier ddqprint("ehci: sched %#p q %d, ival %d, bw %uld\n",
58684860c5dSDavid du Colombier qh->io, q, pollival, bw);
58784860c5dSDavid du Colombier if(q < 0){
58884860c5dSDavid du Colombier print("ehci: no room for ed\n");
58984860c5dSDavid du Colombier return -1;
59084860c5dSDavid du Colombier }
59184860c5dSDavid du Colombier ctlr->tree->bw[q] += bw;
59284860c5dSDavid du Colombier tqh = ctlr->tree->root[q];
59384860c5dSDavid du Colombier qh->sched = q;
59484860c5dSDavid du Colombier qhlinkqh(qh, tqh->next);
59584860c5dSDavid du Colombier qhlinkqh(tqh, qh);
59684860c5dSDavid du Colombier coherence();
59784860c5dSDavid du Colombier qh->inext = ctlr->intrqhs;
59884860c5dSDavid du Colombier ctlr->intrqhs = qh;
59984860c5dSDavid du Colombier coherence();
60084860c5dSDavid du Colombier return 0;
60184860c5dSDavid du Colombier }
60284860c5dSDavid du Colombier
60384860c5dSDavid du Colombier static void
unschedq(Ctlr * ctlr,Qh * qh)60484860c5dSDavid du Colombier unschedq(Ctlr *ctlr, Qh *qh)
60584860c5dSDavid du Colombier {
60684860c5dSDavid du Colombier int q;
60784860c5dSDavid du Colombier Qh *prev, *this, *next;
60884860c5dSDavid du Colombier Qh **l;
60984860c5dSDavid du Colombier ulong bw;
61084860c5dSDavid du Colombier
61184860c5dSDavid du Colombier bw = qh->io->bw;
61284860c5dSDavid du Colombier q = qh->sched;
61384860c5dSDavid du Colombier if(q < 0)
61484860c5dSDavid du Colombier return;
61584860c5dSDavid du Colombier ctlr->tree->bw[q] -= bw;
61684860c5dSDavid du Colombier
61784860c5dSDavid du Colombier prev = ctlr->tree->root[q];
61884860c5dSDavid du Colombier this = prev->next;
61984860c5dSDavid du Colombier while(this != nil && this != qh){
62084860c5dSDavid du Colombier prev = this;
62184860c5dSDavid du Colombier this = this->next;
62284860c5dSDavid du Colombier }
62384860c5dSDavid du Colombier if(this == nil)
62484860c5dSDavid du Colombier print("ehci: unschedq %d: not found\n", q);
62584860c5dSDavid du Colombier else{
62684860c5dSDavid du Colombier next = this->next;
62784860c5dSDavid du Colombier qhlinkqh(prev, next);
62884860c5dSDavid du Colombier }
62984860c5dSDavid du Colombier for(l = &ctlr->intrqhs; *l != nil; l = &(*l)->inext)
63084860c5dSDavid du Colombier if(*l == qh){
63184860c5dSDavid du Colombier *l = (*l)->inext;
63284860c5dSDavid du Colombier return;
63384860c5dSDavid du Colombier }
63484860c5dSDavid du Colombier print("ehci: unschedq: qh %#p not found\n", qh);
63584860c5dSDavid du Colombier }
63684860c5dSDavid du Colombier
63784860c5dSDavid du Colombier static ulong
qhmaxpkt(Qh * qh)63884860c5dSDavid du Colombier qhmaxpkt(Qh *qh)
63984860c5dSDavid du Colombier {
64084860c5dSDavid du Colombier return (qh->eps0 >> Qhmplshift) & Qhmplmask;
64184860c5dSDavid du Colombier }
64284860c5dSDavid du Colombier
64384860c5dSDavid du Colombier static void
qhsetmaxpkt(Qh * qh,int maxpkt)64484860c5dSDavid du Colombier qhsetmaxpkt(Qh *qh, int maxpkt)
64584860c5dSDavid du Colombier {
64684860c5dSDavid du Colombier ulong eps0;
64784860c5dSDavid du Colombier
64884860c5dSDavid du Colombier eps0 = qh->eps0 & ~(Qhmplmask << Qhmplshift);
64984860c5dSDavid du Colombier qh->eps0 = eps0 | (maxpkt & Qhmplmask) << Qhmplshift;
65084860c5dSDavid du Colombier coherence();
65184860c5dSDavid du Colombier }
65284860c5dSDavid du Colombier
65384860c5dSDavid du Colombier /*
65484860c5dSDavid du Colombier * Initialize the round-robin circular list of ctl/bulk Qhs
65584860c5dSDavid du Colombier * if ep is nil. Otherwise, allocate and link a new Qh in the ctlr.
65684860c5dSDavid du Colombier */
65784860c5dSDavid du Colombier static Qh*
qhalloc(Ctlr * ctlr,Ep * ep,Qio * io,char * tag)65884860c5dSDavid du Colombier qhalloc(Ctlr *ctlr, Ep *ep, Qio *io, char* tag)
65984860c5dSDavid du Colombier {
66084860c5dSDavid du Colombier Qh *qh;
66184860c5dSDavid du Colombier int ttype;
66284860c5dSDavid du Colombier
66384860c5dSDavid du Colombier qh = edalloc();
66484860c5dSDavid du Colombier qh->nlink = Lterm;
66584860c5dSDavid du Colombier qh->alink = Lterm;
66684860c5dSDavid du Colombier qh->csw = Tdhalt;
66784860c5dSDavid du Colombier qh->state = Qidle;
66884860c5dSDavid du Colombier qh->sched = -1;
66984860c5dSDavid du Colombier qh->io = io;
67084860c5dSDavid du Colombier if(ep != nil){
67184860c5dSDavid du Colombier qh->eps0 = 0;
67284860c5dSDavid du Colombier qhsetmaxpkt(qh, ep->maxpkt);
67384860c5dSDavid du Colombier if(ep->dev->speed == Lowspeed)
67484860c5dSDavid du Colombier qh->eps0 |= Qhlow;
67584860c5dSDavid du Colombier if(ep->dev->speed == Highspeed)
67684860c5dSDavid du Colombier qh->eps0 |= Qhhigh;
67784860c5dSDavid du Colombier else if(ep->ttype == Tctl)
67884860c5dSDavid du Colombier qh->eps0 |= Qhnhctl;
67984860c5dSDavid du Colombier qh->eps0 |= Qhdtc | 8 << Qhrlcshift; /* 8 naks max */
68084860c5dSDavid du Colombier coherence();
68184860c5dSDavid du Colombier qhsetaddr(qh, io->usbid);
68284860c5dSDavid du Colombier qh->eps1 = (ep->ntds & Qhmultmask) << Qhmultshift;
68384860c5dSDavid du Colombier qh->eps1 |= ep->dev->port << Qhportshift;
68484860c5dSDavid du Colombier qh->eps1 |= ep->dev->hub << Qhhubshift;
68584860c5dSDavid du Colombier qh->eps1 |= 034 << Qhscmshift;
68684860c5dSDavid du Colombier if(ep->ttype == Tintr)
68784860c5dSDavid du Colombier qh->eps1 |= 1 << Qhismshift; /* intr. start µf. */
68884860c5dSDavid du Colombier coherence();
68984860c5dSDavid du Colombier if(io != nil)
69084860c5dSDavid du Colombier io->tag = tag;
69184860c5dSDavid du Colombier }
69284860c5dSDavid du Colombier ilock(ctlr);
69384860c5dSDavid du Colombier ttype = Tctl;
69484860c5dSDavid du Colombier if(ep != nil)
69584860c5dSDavid du Colombier ttype = ep->ttype;
69684860c5dSDavid du Colombier switch(ttype){
69784860c5dSDavid du Colombier case Tctl:
69884860c5dSDavid du Colombier case Tbulk:
69984860c5dSDavid du Colombier if(ctlr->qhs == nil){
70084860c5dSDavid du Colombier ctlr->qhs = qhlinkqh(qh, qh);
70184860c5dSDavid du Colombier qh->eps0 |= Qhhigh | Qhhrl;
70284860c5dSDavid du Colombier coherence();
70384860c5dSDavid du Colombier ctlr->opio->link = PADDR(qh)|Lqh;
70484860c5dSDavid du Colombier coherence();
70584860c5dSDavid du Colombier }else{
70684860c5dSDavid du Colombier qhlinkqh(qh, ctlr->qhs->next);
70784860c5dSDavid du Colombier qhlinkqh(ctlr->qhs, qh);
70884860c5dSDavid du Colombier }
70984860c5dSDavid du Colombier break;
71084860c5dSDavid du Colombier case Tintr:
71184860c5dSDavid du Colombier schedq(ctlr, qh, ep->pollival);
71284860c5dSDavid du Colombier break;
71384860c5dSDavid du Colombier default:
71484860c5dSDavid du Colombier print("ehci: qhalloc called for ttype != ctl/bulk\n");
71584860c5dSDavid du Colombier }
71684860c5dSDavid du Colombier iunlock(ctlr);
71784860c5dSDavid du Colombier return qh;
71884860c5dSDavid du Colombier }
71984860c5dSDavid du Colombier
72084860c5dSDavid du Colombier static int
qhadvanced(void * a)72184860c5dSDavid du Colombier qhadvanced(void *a)
72284860c5dSDavid du Colombier {
72384860c5dSDavid du Colombier Ctlr *ctlr;
72484860c5dSDavid du Colombier
72584860c5dSDavid du Colombier ctlr = a;
72684860c5dSDavid du Colombier return (ctlr->opio->cmd & Ciasync) == 0;
72784860c5dSDavid du Colombier }
72884860c5dSDavid du Colombier
72984860c5dSDavid du Colombier /*
73084860c5dSDavid du Colombier * called when a qh is removed, to be sure the hw is not
73184860c5dSDavid du Colombier * keeping pointers into it.
73284860c5dSDavid du Colombier */
73384860c5dSDavid du Colombier static void
qhcoherency(Ctlr * ctlr)73484860c5dSDavid du Colombier qhcoherency(Ctlr *ctlr)
73584860c5dSDavid du Colombier {
73684860c5dSDavid du Colombier int i;
73784860c5dSDavid du Colombier
73884860c5dSDavid du Colombier qlock(&ctlr->portlck);
73984860c5dSDavid du Colombier ctlr->opio->cmd |= Ciasync; /* ask for intr. on async advance */
74084860c5dSDavid du Colombier coherence();
74184860c5dSDavid du Colombier for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++)
74284860c5dSDavid du Colombier if(!waserror()){
74384860c5dSDavid du Colombier tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
74484860c5dSDavid du Colombier poperror();
74584860c5dSDavid du Colombier }
74684860c5dSDavid du Colombier dprint("ehci: qhcoherency: doorbell %d\n", qhadvanced(ctlr));
74784860c5dSDavid du Colombier if(i == 3)
74884860c5dSDavid du Colombier print("ehci: async advance doorbell did not ring\n");
74984860c5dSDavid du Colombier ctlr->opio->cmd &= ~Ciasync; /* try to clean */
75084860c5dSDavid du Colombier qunlock(&ctlr->portlck);
75184860c5dSDavid du Colombier }
75284860c5dSDavid du Colombier
75384860c5dSDavid du Colombier static void
qhfree(Ctlr * ctlr,Qh * qh)75484860c5dSDavid du Colombier qhfree(Ctlr *ctlr, Qh *qh)
75584860c5dSDavid du Colombier {
75684860c5dSDavid du Colombier Td *td, *ltd;
75784860c5dSDavid du Colombier Qh *q;
75884860c5dSDavid du Colombier
75984860c5dSDavid du Colombier if(qh == nil)
76084860c5dSDavid du Colombier return;
76184860c5dSDavid du Colombier ilock(ctlr);
76284860c5dSDavid du Colombier if(qh->sched < 0){
76384860c5dSDavid du Colombier for(q = ctlr->qhs; q != nil; q = q->next)
76484860c5dSDavid du Colombier if(q->next == qh)
76584860c5dSDavid du Colombier break;
76684860c5dSDavid du Colombier if(q == nil)
76784860c5dSDavid du Colombier panic("qhfree: nil q");
76884860c5dSDavid du Colombier q->next = qh->next;
76984860c5dSDavid du Colombier q->link = qh->link;
77084860c5dSDavid du Colombier coherence();
77184860c5dSDavid du Colombier }else
77284860c5dSDavid du Colombier unschedq(ctlr, qh);
77384860c5dSDavid du Colombier iunlock(ctlr);
77484860c5dSDavid du Colombier
77584860c5dSDavid du Colombier qhcoherency(ctlr);
77684860c5dSDavid du Colombier
77784860c5dSDavid du Colombier for(td = qh->tds; td != nil; td = ltd){
77884860c5dSDavid du Colombier ltd = td->next;
77984860c5dSDavid du Colombier tdfree(td);
78084860c5dSDavid du Colombier }
78184860c5dSDavid du Colombier
78284860c5dSDavid du Colombier edfree(qh);
78384860c5dSDavid du Colombier }
78484860c5dSDavid du Colombier
78584860c5dSDavid du Colombier static void
qhlinktd(Qh * qh,Td * td)78684860c5dSDavid du Colombier qhlinktd(Qh *qh, Td *td)
78784860c5dSDavid du Colombier {
78884860c5dSDavid du Colombier ulong csw;
78984860c5dSDavid du Colombier int i;
79084860c5dSDavid du Colombier
79184860c5dSDavid du Colombier csw = qh->csw;
79284860c5dSDavid du Colombier qh->tds = td;
79384860c5dSDavid du Colombier if(td == nil)
79484860c5dSDavid du Colombier qh->csw = (csw & ~Tdactive) | Tdhalt;
79584860c5dSDavid du Colombier else{
79684860c5dSDavid du Colombier csw &= Tddata1 | Tdping; /* save */
79784860c5dSDavid du Colombier qh->csw = Tdhalt;
79884860c5dSDavid du Colombier coherence();
79984860c5dSDavid du Colombier qh->tclink = 0;
80084860c5dSDavid du Colombier qh->alink = Lterm;
80184860c5dSDavid du Colombier qh->nlink = PADDR(td);
80284860c5dSDavid du Colombier for(i = 0; i < nelem(qh->buffer); i++)
80384860c5dSDavid du Colombier qh->buffer[i] = 0;
80484860c5dSDavid du Colombier coherence();
80584860c5dSDavid du Colombier qh->csw = csw & ~(Tdhalt|Tdactive); /* activate next */
80684860c5dSDavid du Colombier }
80784860c5dSDavid du Colombier coherence();
80884860c5dSDavid du Colombier }
80984860c5dSDavid du Colombier
81084860c5dSDavid du Colombier static char*
seprintlink(char * s,char * se,char * name,ulong l,int typed)81184860c5dSDavid du Colombier seprintlink(char *s, char *se, char *name, ulong l, int typed)
81284860c5dSDavid du Colombier {
81384860c5dSDavid du Colombier s = seprint(s, se, "%s %ulx", name, l);
81484860c5dSDavid du Colombier if((l & Lterm) != 0)
81584860c5dSDavid du Colombier return seprint(s, se, "T");
81684860c5dSDavid du Colombier if(typed == 0)
81784860c5dSDavid du Colombier return s;
81884860c5dSDavid du Colombier switch(l & (3<<1)){
81984860c5dSDavid du Colombier case Litd:
82084860c5dSDavid du Colombier return seprint(s, se, "I");
82184860c5dSDavid du Colombier case Lqh:
82284860c5dSDavid du Colombier return seprint(s, se, "Q");
82384860c5dSDavid du Colombier case Lsitd:
82484860c5dSDavid du Colombier return seprint(s, se, "S");
82584860c5dSDavid du Colombier default:
82684860c5dSDavid du Colombier return seprint(s, se, "F");
82784860c5dSDavid du Colombier }
82884860c5dSDavid du Colombier }
82984860c5dSDavid du Colombier
83084860c5dSDavid du Colombier static char*
seprintitd(char * s,char * se,Itd * td)83184860c5dSDavid du Colombier seprintitd(char *s, char *se, Itd *td)
83284860c5dSDavid du Colombier {
83384860c5dSDavid du Colombier int i;
83484860c5dSDavid du Colombier ulong b0, b1;
83584860c5dSDavid du Colombier char flags[6];
83684860c5dSDavid du Colombier char *rw;
83784860c5dSDavid du Colombier
83884860c5dSDavid du Colombier if(td == nil)
83984860c5dSDavid du Colombier return seprint(s, se, "<nil itd>\n");
84084860c5dSDavid du Colombier b0 = td->buffer[0];
84184860c5dSDavid du Colombier b1 = td->buffer[1];
84284860c5dSDavid du Colombier
84384860c5dSDavid du Colombier s = seprint(s, se, "itd %#p", td);
84484860c5dSDavid du Colombier rw = (b1 & Itdin) ? "in" : "out";
84584860c5dSDavid du Colombier s = seprint(s, se, " %s ep %uld dev %uld max %uld mult %uld",
84684860c5dSDavid du Colombier rw, (b0>>8)&Epmax, (b0&Devmax),
84784860c5dSDavid du Colombier td->buffer[1] & 0x7ff, b1 & 3);
84884860c5dSDavid du Colombier s = seprintlink(s, se, " link", td->link, 1);
84984860c5dSDavid du Colombier s = seprint(s, se, "\n");
85084860c5dSDavid du Colombier for(i = 0; i < nelem(td->csw); i++){
85184860c5dSDavid du Colombier memset(flags, '-', 5);
85284860c5dSDavid du Colombier if((td->csw[i] & Itdactive) != 0)
85384860c5dSDavid du Colombier flags[0] = 'a';
85484860c5dSDavid du Colombier if((td->csw[i] & Itdioc) != 0)
85584860c5dSDavid du Colombier flags[1] = 'i';
85684860c5dSDavid du Colombier if((td->csw[i] & Itddberr) != 0)
85784860c5dSDavid du Colombier flags[2] = 'd';
85884860c5dSDavid du Colombier if((td->csw[i] & Itdbabble) != 0)
85984860c5dSDavid du Colombier flags[3] = 'b';
86084860c5dSDavid du Colombier if((td->csw[i] & Itdtrerr) != 0)
86184860c5dSDavid du Colombier flags[4] = 't';
86284860c5dSDavid du Colombier flags[5] = 0;
86384860c5dSDavid du Colombier s = seprint(s, se, "\ttd%d %s", i, flags);
86484860c5dSDavid du Colombier s = seprint(s, se, " len %uld", (td->csw[i] >> 16) & 0x7ff);
86584860c5dSDavid du Colombier s = seprint(s, se, " pg %uld", (td->csw[i] >> 12) & 0x7);
86684860c5dSDavid du Colombier s = seprint(s, se, " off %uld\n", td->csw[i] & 0xfff);
86784860c5dSDavid du Colombier }
86884860c5dSDavid du Colombier s = seprint(s, se, "\tbuffs:");
86984860c5dSDavid du Colombier for(i = 0; i < nelem(td->buffer); i++)
87084860c5dSDavid du Colombier s = seprint(s, se, " %#lux", td->buffer[i] >> 12);
87184860c5dSDavid du Colombier return seprint(s, se, "\n");
87284860c5dSDavid du Colombier }
87384860c5dSDavid du Colombier
87484860c5dSDavid du Colombier static char*
seprintsitd(char * s,char * se,Sitd * td)87584860c5dSDavid du Colombier seprintsitd(char *s, char *se, Sitd *td)
87684860c5dSDavid du Colombier {
87784860c5dSDavid du Colombier char rw, pg, ss;
87884860c5dSDavid du Colombier char flags[8];
87984860c5dSDavid du Colombier static char pc[4] = { 'a', 'b', 'm', 'e' };
88084860c5dSDavid du Colombier
88184860c5dSDavid du Colombier if(td == nil)
88284860c5dSDavid du Colombier return seprint(s, se, "<nil sitd>\n");
88384860c5dSDavid du Colombier s = seprint(s, se, "sitd %#p", td);
88484860c5dSDavid du Colombier rw = (td->epc & Stdin) ? 'r' : 'w';
88584860c5dSDavid du Colombier s = seprint(s, se, " %c ep %uld dev %uld",
88684860c5dSDavid du Colombier rw, (td->epc>>8)&0xf, td->epc&0x7f);
88784860c5dSDavid du Colombier s = seprint(s, se, " max %uld", (td->csw >> 16) & 0x3ff);
88884860c5dSDavid du Colombier s = seprint(s, se, " hub %uld", (td->epc >> 16) & 0x7f);
88984860c5dSDavid du Colombier s = seprint(s, se, " port %uld\n", (td->epc >> 24) & 0x7f);
89084860c5dSDavid du Colombier memset(flags, '-', 7);
89184860c5dSDavid du Colombier if((td->csw & Stdactive) != 0)
89284860c5dSDavid du Colombier flags[0] = 'a';
89384860c5dSDavid du Colombier if((td->csw & Stdioc) != 0)
89484860c5dSDavid du Colombier flags[1] = 'i';
89584860c5dSDavid du Colombier if((td->csw & Stderr) != 0)
89684860c5dSDavid du Colombier flags[2] = 'e';
89784860c5dSDavid du Colombier if((td->csw & Stddberr) != 0)
89884860c5dSDavid du Colombier flags[3] = 'd';
89984860c5dSDavid du Colombier if((td->csw & Stdbabble) != 0)
90084860c5dSDavid du Colombier flags[4] = 'b';
90184860c5dSDavid du Colombier if((td->csw & Stdtrerr) != 0)
90284860c5dSDavid du Colombier flags[5] = 't';
90384860c5dSDavid du Colombier if((td->csw & Stdmmf) != 0)
90484860c5dSDavid du Colombier flags[6] = 'n';
90584860c5dSDavid du Colombier flags[7] = 0;
90684860c5dSDavid du Colombier ss = (td->csw & Stddcs) ? 'c' : 's';
90784860c5dSDavid du Colombier pg = (td->csw & Stdpg) ? '1' : '0';
90884860c5dSDavid du Colombier s = seprint(s, se, "\t%s %cs pg%c", flags, ss, pg);
90984860c5dSDavid du Colombier s = seprint(s, se, " b0 %#lux b1 %#lux off %uld\n",
91084860c5dSDavid du Colombier td->buffer[0] >> 12, td->buffer[1] >> 12, td->buffer[0] & 0xfff);
91184860c5dSDavid du Colombier s = seprint(s, se, "\ttpos %c tcnt %uld",
91284860c5dSDavid du Colombier pc[(td->buffer[0]>>3)&3], td->buffer[1] & 7);
91384860c5dSDavid du Colombier s = seprint(s, se, " ssm %#lux csm %#lux cspm %#lux",
91484860c5dSDavid du Colombier td->mfs & 0xff, (td->mfs>>8) & 0xff, (td->csw>>8) & 0xff);
91584860c5dSDavid du Colombier s = seprintlink(s, se, " link", td->link, 1);
91684860c5dSDavid du Colombier s = seprintlink(s, se, " blink", td->blink, 0);
91784860c5dSDavid du Colombier return seprint(s, se, "\n");
91884860c5dSDavid du Colombier }
91984860c5dSDavid du Colombier
92084860c5dSDavid du Colombier static long
maxtdlen(Td * td)92184860c5dSDavid du Colombier maxtdlen(Td *td)
92284860c5dSDavid du Colombier {
92384860c5dSDavid du Colombier return (td->csw >> Tdlenshift) & Tdlenmask;
92484860c5dSDavid du Colombier }
92584860c5dSDavid du Colombier
92684860c5dSDavid du Colombier static long
tdlen(Td * td)92784860c5dSDavid du Colombier tdlen(Td *td)
92884860c5dSDavid du Colombier {
92984860c5dSDavid du Colombier if(td->data == nil)
93084860c5dSDavid du Colombier return 0;
93184860c5dSDavid du Colombier return td->ndata - maxtdlen(td);
93284860c5dSDavid du Colombier }
93384860c5dSDavid du Colombier
93484860c5dSDavid du Colombier static char*
seprinttd(char * s,char * se,Td * td,char * tag)93584860c5dSDavid du Colombier seprinttd(char *s, char *se, Td *td, char *tag)
93684860c5dSDavid du Colombier {
93784860c5dSDavid du Colombier int i;
93884860c5dSDavid du Colombier char t, ss;
93984860c5dSDavid du Colombier char flags[9];
94084860c5dSDavid du Colombier static char *tok[4] = { "out", "in", "setup", "BUG" };
94184860c5dSDavid du Colombier
94284860c5dSDavid du Colombier if(td == nil)
94384860c5dSDavid du Colombier return seprint(s, se, "%s <nil td>\n", tag);
94484860c5dSDavid du Colombier s = seprint(s, se, "%s %#p", tag, td);
94584860c5dSDavid du Colombier s = seprintlink(s, se, " nlink", td->nlink, 0);
94684860c5dSDavid du Colombier s = seprintlink(s, se, " alink", td->alink, 0);
94784860c5dSDavid du Colombier s = seprint(s, se, " %s", tok[(td->csw & Tdtok) >> 8]);
94884860c5dSDavid du Colombier if((td->csw & Tdping) != 0)
94984860c5dSDavid du Colombier s = seprint(s, se, " png");
95084860c5dSDavid du Colombier memset(flags, '-', 8);
95184860c5dSDavid du Colombier if((td->csw & Tdactive) != 0)
95284860c5dSDavid du Colombier flags[0] = 'a';
95384860c5dSDavid du Colombier if((td->csw & Tdioc) != 0)
95484860c5dSDavid du Colombier flags[1] = 'i';
95584860c5dSDavid du Colombier if((td->csw & Tdhalt) != 0)
95684860c5dSDavid du Colombier flags[2] = 'h';
95784860c5dSDavid du Colombier if((td->csw & Tddberr) != 0)
95884860c5dSDavid du Colombier flags[3] = 'd';
95984860c5dSDavid du Colombier if((td->csw & Tdbabble) != 0)
96084860c5dSDavid du Colombier flags[4] = 'b';
96184860c5dSDavid du Colombier if((td->csw & Tdtrerr) != 0)
96284860c5dSDavid du Colombier flags[5] = 't';
96384860c5dSDavid du Colombier if((td->csw & Tdmmf) != 0)
96484860c5dSDavid du Colombier flags[6] = 'n';
96584860c5dSDavid du Colombier if((td->csw & (Tderr2|Tderr1)) == 0)
96684860c5dSDavid du Colombier flags[7] = 'z';
96784860c5dSDavid du Colombier flags[8] = 0;
96884860c5dSDavid du Colombier t = (td->csw & Tddata1) ? '1' : '0';
96984860c5dSDavid du Colombier ss = (td->csw & Tddcs) ? 'c' : 's';
97084860c5dSDavid du Colombier s = seprint(s, se, "\n\td%c %s %cs", t, flags, ss);
97184860c5dSDavid du Colombier s = seprint(s, se, " max %uld", maxtdlen(td));
97284860c5dSDavid du Colombier s = seprint(s, se, " pg %uld off %#lux\n",
97384860c5dSDavid du Colombier (td->csw >> Tdpgshift) & Tdpgmask, td->buffer[0] & 0xFFF);
97484860c5dSDavid du Colombier s = seprint(s, se, "\tbuffs:");
97584860c5dSDavid du Colombier for(i = 0; i < nelem(td->buffer); i++)
97684860c5dSDavid du Colombier s = seprint(s, se, " %#lux", td->buffer[i]>>12);
97784860c5dSDavid du Colombier if(td->data != nil)
97884860c5dSDavid du Colombier s = seprintdata(s, se, td->data, td->ndata);
97984860c5dSDavid du Colombier return seprint(s, se, "\n");
98084860c5dSDavid du Colombier }
98184860c5dSDavid du Colombier
98284860c5dSDavid du Colombier static void
dumptd(Td * td,char * pref)98384860c5dSDavid du Colombier dumptd(Td *td, char *pref)
98484860c5dSDavid du Colombier {
98584860c5dSDavid du Colombier char buf[256];
98684860c5dSDavid du Colombier char *se;
98784860c5dSDavid du Colombier int i;
98884860c5dSDavid du Colombier
98984860c5dSDavid du Colombier i = 0;
99084860c5dSDavid du Colombier se = buf+sizeof(buf);
99184860c5dSDavid du Colombier for(; td != nil; td = td->next){
99284860c5dSDavid du Colombier seprinttd(buf, se, td, pref);
99384860c5dSDavid du Colombier print("%s", buf);
99484860c5dSDavid du Colombier if(i++ > 20){
99584860c5dSDavid du Colombier print("...more tds...\n");
99684860c5dSDavid du Colombier break;
99784860c5dSDavid du Colombier }
99884860c5dSDavid du Colombier }
99984860c5dSDavid du Colombier }
100084860c5dSDavid du Colombier
100184860c5dSDavid du Colombier static void
qhdump(Qh * qh)100284860c5dSDavid du Colombier qhdump(Qh *qh)
100384860c5dSDavid du Colombier {
100484860c5dSDavid du Colombier char buf[256];
100584860c5dSDavid du Colombier char *s, *se, *tag;
100684860c5dSDavid du Colombier Td td;
100784860c5dSDavid du Colombier static char *speed[] = {"full", "low", "high", "BUG"};
100884860c5dSDavid du Colombier
100984860c5dSDavid du Colombier if(qh == nil){
101084860c5dSDavid du Colombier print("<nil qh>\n");
101184860c5dSDavid du Colombier return;
101284860c5dSDavid du Colombier }
101384860c5dSDavid du Colombier if(qh->io == nil)
101484860c5dSDavid du Colombier tag = "qh";
101584860c5dSDavid du Colombier else
101684860c5dSDavid du Colombier tag = qh->io->tag;
101784860c5dSDavid du Colombier se = buf+sizeof(buf);
101884860c5dSDavid du Colombier s = seprint(buf, se, "%s %#p", tag, qh);
101984860c5dSDavid du Colombier s = seprint(s, se, " ep %uld dev %uld",
102084860c5dSDavid du Colombier (qh->eps0>>8)&0xf, qh->eps0&0x7f);
102184860c5dSDavid du Colombier s = seprint(s, se, " hub %uld", (qh->eps1 >> 16) & 0x7f);
102284860c5dSDavid du Colombier s = seprint(s, se, " port %uld", (qh->eps1 >> 23) & 0x7f);
102384860c5dSDavid du Colombier s = seprintlink(s, se, " link", qh->link, 1);
102484860c5dSDavid du Colombier seprint(s, se, " clink %#lux", qh->tclink);
102584860c5dSDavid du Colombier print("%s\n", buf);
102684860c5dSDavid du Colombier s = seprint(buf, se, "\tnrld %uld", (qh->eps0 >> Qhrlcshift) & Qhrlcmask);
102784860c5dSDavid du Colombier s = seprint(s, se, " nak %uld", (qh->alink >> 1) & 0xf);
102884860c5dSDavid du Colombier s = seprint(s, se, " max %uld ", qhmaxpkt(qh));
102984860c5dSDavid du Colombier if((qh->eps0 & Qhnhctl) != 0)
103084860c5dSDavid du Colombier s = seprint(s, se, "c");
103184860c5dSDavid du Colombier if((qh->eps0 & Qhhrl) != 0)
103284860c5dSDavid du Colombier s = seprint(s, se, "h");
103384860c5dSDavid du Colombier if((qh->eps0 & Qhdtc) != 0)
103484860c5dSDavid du Colombier s = seprint(s, se, "d");
103584860c5dSDavid du Colombier if((qh->eps0 & Qhint) != 0)
103684860c5dSDavid du Colombier s = seprint(s, se, "i");
103784860c5dSDavid du Colombier s = seprint(s, se, " %s", speed[(qh->eps0 >> 12) & 3]);
103884860c5dSDavid du Colombier s = seprint(s, se, " mult %uld", (qh->eps1 >> Qhmultshift) & Qhmultmask);
103984860c5dSDavid du Colombier seprint(s, se, " scm %#lux ism %#lux\n",
104084860c5dSDavid du Colombier (qh->eps1 >> 8 & 0xff), qh->eps1 & 0xff);
104184860c5dSDavid du Colombier print("%s\n", buf);
104284860c5dSDavid du Colombier memset(&td, 0, sizeof(td));
104384860c5dSDavid du Colombier memmove(&td, &qh->nlink, 32); /* overlay area */
104484860c5dSDavid du Colombier seprinttd(buf, se, &td, "\tovl");
104584860c5dSDavid du Colombier print("%s", buf);
104684860c5dSDavid du Colombier }
104784860c5dSDavid du Colombier
104884860c5dSDavid du Colombier static void
isodump(Isoio * iso,int all)104984860c5dSDavid du Colombier isodump(Isoio* iso, int all)
105084860c5dSDavid du Colombier {
105184860c5dSDavid du Colombier Itd *td, *tdi, *tdu;
105284860c5dSDavid du Colombier Sitd *std, *stdi, *stdu;
105384860c5dSDavid du Colombier char buf[256];
105484860c5dSDavid du Colombier int i;
105584860c5dSDavid du Colombier
105684860c5dSDavid du Colombier if(iso == nil){
105784860c5dSDavid du Colombier print("<nil iso>\n");
105884860c5dSDavid du Colombier return;
105984860c5dSDavid du Colombier }
106084860c5dSDavid du Colombier print("iso %#p %s %s speed state %d nframes %d maxsz %uld",
106184860c5dSDavid du Colombier iso, iso->tok == Tdtokin ? "in" : "out",
106284860c5dSDavid du Colombier iso->hs ? "high" : "full",
106384860c5dSDavid du Colombier iso->state, iso->nframes, iso->maxsize);
106484860c5dSDavid du Colombier print(" td0 %uld tdi %#p tdu %#p data %#p\n",
106584860c5dSDavid du Colombier iso->td0frno, iso->tdi, iso->tdu, iso->data);
106684860c5dSDavid du Colombier if(iso->err != nil)
106784860c5dSDavid du Colombier print("\terr %s\n", iso->err);
106884860c5dSDavid du Colombier if(iso->err != nil)
106984860c5dSDavid du Colombier print("\terr='%s'\n", iso->err);
107084860c5dSDavid du Colombier if(all == 0)
107184860c5dSDavid du Colombier if(iso->hs != 0){
107284860c5dSDavid du Colombier tdi = iso->tdi;
107384860c5dSDavid du Colombier seprintitd(buf, buf+sizeof(buf), tdi);
107484860c5dSDavid du Colombier print("\ttdi %s\n", buf);
107584860c5dSDavid du Colombier tdu = iso->tdu;
107684860c5dSDavid du Colombier seprintitd(buf, buf+sizeof(buf), tdu);
107784860c5dSDavid du Colombier print("\ttdu %s\n", buf);
107884860c5dSDavid du Colombier }else{
107984860c5dSDavid du Colombier stdi = iso->stdi;
108084860c5dSDavid du Colombier seprintsitd(buf, buf+sizeof(buf), stdi);
108184860c5dSDavid du Colombier print("\tstdi %s\n", buf);
108284860c5dSDavid du Colombier stdu = iso->stdu;
108384860c5dSDavid du Colombier seprintsitd(buf, buf+sizeof(buf), stdu);
108484860c5dSDavid du Colombier print("\tstdu %s\n", buf);
108584860c5dSDavid du Colombier }
108684860c5dSDavid du Colombier else
108784860c5dSDavid du Colombier for(i = 0; i < Nisoframes; i++)
108884860c5dSDavid du Colombier if(iso->tdps[i] != nil)
108984860c5dSDavid du Colombier if(iso->hs != 0){
109084860c5dSDavid du Colombier td = iso->itdps[i];
109184860c5dSDavid du Colombier seprintitd(buf, buf+sizeof(buf), td);
109284860c5dSDavid du Colombier if(td == iso->tdi)
109384860c5dSDavid du Colombier print("i->");
109484860c5dSDavid du Colombier if(td == iso->tdu)
109584860c5dSDavid du Colombier print("i->");
109684860c5dSDavid du Colombier print("[%d]\t%s", i, buf);
109784860c5dSDavid du Colombier }else{
109884860c5dSDavid du Colombier std = iso->sitdps[i];
109984860c5dSDavid du Colombier seprintsitd(buf, buf+sizeof(buf), std);
110084860c5dSDavid du Colombier if(std == iso->stdi)
110184860c5dSDavid du Colombier print("i->");
110284860c5dSDavid du Colombier if(std == iso->stdu)
110384860c5dSDavid du Colombier print("u->");
110484860c5dSDavid du Colombier print("[%d]\t%s", i, buf);
110584860c5dSDavid du Colombier }
110684860c5dSDavid du Colombier }
110784860c5dSDavid du Colombier
110884860c5dSDavid du Colombier static void
dump(Hci * hp)110984860c5dSDavid du Colombier dump(Hci *hp)
111084860c5dSDavid du Colombier {
111184860c5dSDavid du Colombier int i;
111284860c5dSDavid du Colombier char *s, *se;
111384860c5dSDavid du Colombier char buf[128];
111484860c5dSDavid du Colombier Ctlr *ctlr;
111584860c5dSDavid du Colombier Eopio *opio;
111684860c5dSDavid du Colombier Isoio *iso;
111784860c5dSDavid du Colombier Qh *qh;
111884860c5dSDavid du Colombier
111984860c5dSDavid du Colombier ctlr = hp->aux;
112084860c5dSDavid du Colombier opio = ctlr->opio;
112184860c5dSDavid du Colombier ilock(ctlr);
112284860c5dSDavid du Colombier print("ehci port %#p frames %#p (%d fr.) nintr %d ntdintr %d",
112384860c5dSDavid du Colombier ctlr->capio, ctlr->frames, ctlr->nframes,
112484860c5dSDavid du Colombier ctlr->nintr, ctlr->ntdintr);
112584860c5dSDavid du Colombier print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr);
112684860c5dSDavid du Colombier print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
112784860c5dSDavid du Colombier opio->cmd, opio->sts, opio->intr, opio->frno);
112884860c5dSDavid du Colombier print(" base %#lux link %#lux fr0 %#lux\n",
112984860c5dSDavid du Colombier opio->frbase, opio->link, ctlr->frames[0]);
113084860c5dSDavid du Colombier se = buf+sizeof(buf);
113184860c5dSDavid du Colombier s = seprint(buf, se, "\t");
113284860c5dSDavid du Colombier for(i = 0; i < hp->nports; i++){
113384860c5dSDavid du Colombier s = seprint(s, se, "p%d %#lux ", i, opio->portsc[i]);
113484860c5dSDavid du Colombier if(hp->nports > 4 && i == hp->nports/2 - 1)
113584860c5dSDavid du Colombier s = seprint(s, se, "\n\t");
113684860c5dSDavid du Colombier }
113784860c5dSDavid du Colombier print("%s\n", buf);
113884860c5dSDavid du Colombier qh = ctlr->qhs;
113984860c5dSDavid du Colombier i = 0;
114084860c5dSDavid du Colombier do{
114184860c5dSDavid du Colombier qhdump(qh);
114284860c5dSDavid du Colombier qh = qh->next;
114384860c5dSDavid du Colombier }while(qh != ctlr->qhs && i++ < 100);
114484860c5dSDavid du Colombier if(i > 100)
114584860c5dSDavid du Colombier print("...too many Qhs...\n");
114684860c5dSDavid du Colombier if(ctlr->intrqhs != nil)
114784860c5dSDavid du Colombier print("intr qhs:\n");
114884860c5dSDavid du Colombier for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
114984860c5dSDavid du Colombier qhdump(qh);
115084860c5dSDavid du Colombier if(ctlr->iso != nil)
115184860c5dSDavid du Colombier print("iso:\n");
115284860c5dSDavid du Colombier for(iso = ctlr->iso; iso != nil; iso = iso->next)
115384860c5dSDavid du Colombier isodump(ctlr->iso, 0);
115484860c5dSDavid du Colombier print("%d eds in tree\n", ctlr->ntree);
115584860c5dSDavid du Colombier iunlock(ctlr);
115684860c5dSDavid du Colombier lock(&edpool);
115784860c5dSDavid du Colombier print("%d eds allocated = %d in use + %d free\n",
115884860c5dSDavid du Colombier edpool.nalloc, edpool.ninuse, edpool.nfree);
115984860c5dSDavid du Colombier unlock(&edpool);
116084860c5dSDavid du Colombier }
116184860c5dSDavid du Colombier
116284860c5dSDavid du Colombier static char*
errmsg(int err)116384860c5dSDavid du Colombier errmsg(int err)
116484860c5dSDavid du Colombier {
116584860c5dSDavid du Colombier if(err == 0)
116684860c5dSDavid du Colombier return "ok";
116784860c5dSDavid du Colombier if(err & Tddberr)
116884860c5dSDavid du Colombier return "data buffer error";
116984860c5dSDavid du Colombier if(err & Tdbabble)
117084860c5dSDavid du Colombier return "babble detected";
117184860c5dSDavid du Colombier if(err & Tdtrerr)
117284860c5dSDavid du Colombier return "transaction error";
117384860c5dSDavid du Colombier if(err & Tdmmf)
117484860c5dSDavid du Colombier return "missed µframe";
117584860c5dSDavid du Colombier if(err & Tdhalt)
117684860c5dSDavid du Colombier return Estalled; /* [uo]hci report this error */
117784860c5dSDavid du Colombier return Eio;
117884860c5dSDavid du Colombier }
117984860c5dSDavid du Colombier
118084860c5dSDavid du Colombier static char*
ierrmsg(int err)118184860c5dSDavid du Colombier ierrmsg(int err)
118284860c5dSDavid du Colombier {
118384860c5dSDavid du Colombier if(err == 0)
118484860c5dSDavid du Colombier return "ok";
118584860c5dSDavid du Colombier if(err & Itddberr)
118684860c5dSDavid du Colombier return "data buffer error";
118784860c5dSDavid du Colombier if(err & Itdbabble)
118884860c5dSDavid du Colombier return "babble detected";
118984860c5dSDavid du Colombier if(err & Itdtrerr)
119084860c5dSDavid du Colombier return "transaction error";
119184860c5dSDavid du Colombier return Eio;
119284860c5dSDavid du Colombier }
119384860c5dSDavid du Colombier
119484860c5dSDavid du Colombier static char*
serrmsg(int err)119584860c5dSDavid du Colombier serrmsg(int err)
119684860c5dSDavid du Colombier {
119784860c5dSDavid du Colombier if(err & Stderr)
119884860c5dSDavid du Colombier return "translation translator error";
119984860c5dSDavid du Colombier /* other errors have same numbers than Td errors */
120084860c5dSDavid du Colombier return errmsg(err);
120184860c5dSDavid du Colombier }
120284860c5dSDavid du Colombier
120384860c5dSDavid du Colombier static int
isocanread(void * a)120484860c5dSDavid du Colombier isocanread(void *a)
120584860c5dSDavid du Colombier {
120684860c5dSDavid du Colombier Isoio *iso;
120784860c5dSDavid du Colombier
120884860c5dSDavid du Colombier iso = a;
120984860c5dSDavid du Colombier if(iso->state == Qclose)
121084860c5dSDavid du Colombier return 1;
121184860c5dSDavid du Colombier if(iso->state == Qrun && iso->tok == Tdtokin){
121284860c5dSDavid du Colombier if(iso->hs != 0 && iso->tdi != iso->tdu)
121384860c5dSDavid du Colombier return 1;
121484860c5dSDavid du Colombier if(iso->hs == 0 && iso->stdi != iso->stdu)
121584860c5dSDavid du Colombier return 1;
121684860c5dSDavid du Colombier }
121784860c5dSDavid du Colombier return 0;
121884860c5dSDavid du Colombier }
121984860c5dSDavid du Colombier
122084860c5dSDavid du Colombier static int
isocanwrite(void * a)122184860c5dSDavid du Colombier isocanwrite(void *a)
122284860c5dSDavid du Colombier {
122384860c5dSDavid du Colombier Isoio *iso;
122484860c5dSDavid du Colombier
122584860c5dSDavid du Colombier iso = a;
122684860c5dSDavid du Colombier if(iso->state == Qclose)
122784860c5dSDavid du Colombier return 1;
122884860c5dSDavid du Colombier if(iso->state == Qrun && iso->tok == Tdtokout){
122984860c5dSDavid du Colombier if(iso->hs != 0 && iso->tdu->next != iso->tdi)
123084860c5dSDavid du Colombier return 1;
123184860c5dSDavid du Colombier if(iso->hs == 0 && iso->stdu->next != iso->stdi)
123284860c5dSDavid du Colombier return 1;
123384860c5dSDavid du Colombier }
123484860c5dSDavid du Colombier return 0;
123584860c5dSDavid du Colombier }
123684860c5dSDavid du Colombier
123784860c5dSDavid du Colombier static void
itdinit(Isoio * iso,Itd * td)123884860c5dSDavid du Colombier itdinit(Isoio *iso, Itd *td)
123984860c5dSDavid du Colombier {
124084860c5dSDavid du Colombier int p, t;
124184860c5dSDavid du Colombier ulong pa, tsize, size;
124284860c5dSDavid du Colombier
124384860c5dSDavid du Colombier /*
124484860c5dSDavid du Colombier * BUG: This does not put an integral number of samples
124584860c5dSDavid du Colombier * on each µframe unless samples per packet % 8 == 0
124684860c5dSDavid du Colombier * Also, all samples are packed early on each frame.
124784860c5dSDavid du Colombier */
124884860c5dSDavid du Colombier p = 0;
124984860c5dSDavid du Colombier size = td->ndata = td->mdata;
125084860c5dSDavid du Colombier pa = PADDR(td->data);
125184860c5dSDavid du Colombier for(t = 0; size > 0 && t < 8; t++){
125284860c5dSDavid du Colombier tsize = size;
125384860c5dSDavid du Colombier if(tsize > iso->maxsize)
125484860c5dSDavid du Colombier tsize = iso->maxsize;
125584860c5dSDavid du Colombier size -= tsize;
125684860c5dSDavid du Colombier assert(p < nelem(td->buffer));
125784860c5dSDavid du Colombier td->csw[t] = tsize << Itdlenshift | p << Itdpgshift |
125884860c5dSDavid du Colombier (pa & 0xFFF) << Itdoffshift | Itdactive | Itdioc;
125984860c5dSDavid du Colombier coherence();
126084860c5dSDavid du Colombier if(((pa+tsize) & ~0xFFF) != (pa & ~0xFFF))
126184860c5dSDavid du Colombier p++;
126284860c5dSDavid du Colombier pa += tsize;
126384860c5dSDavid du Colombier }
126484860c5dSDavid du Colombier }
126584860c5dSDavid du Colombier
126684860c5dSDavid du Colombier static void
sitdinit(Isoio * iso,Sitd * td)126784860c5dSDavid du Colombier sitdinit(Isoio *iso, Sitd *td)
126884860c5dSDavid du Colombier {
126984860c5dSDavid du Colombier td->ndata = td->mdata & Stdlenmask;
127084860c5dSDavid du Colombier td->buffer[0] = PADDR(td->data);
127184860c5dSDavid du Colombier td->buffer[1] = (td->buffer[0] & ~0xFFF) + 0x1000;
127284860c5dSDavid du Colombier if(iso->tok == Tdtokin || td->ndata <= 188)
127384860c5dSDavid du Colombier td->buffer[1] |= Stdtpall;
127484860c5dSDavid du Colombier else
127584860c5dSDavid du Colombier td->buffer[1] |= Stdtpbegin;
127684860c5dSDavid du Colombier if(iso->tok == Tdtokin)
127784860c5dSDavid du Colombier td->buffer[1] |= 1;
127884860c5dSDavid du Colombier else
127984860c5dSDavid du Colombier td->buffer[1] |= ((td->ndata + 187) / 188) & Stdtcntmask;
128084860c5dSDavid du Colombier coherence();
128184860c5dSDavid du Colombier td->csw = td->ndata << Stdlenshift | Stdactive | Stdioc;
128284860c5dSDavid du Colombier coherence();
128384860c5dSDavid du Colombier }
128484860c5dSDavid du Colombier
128584860c5dSDavid du Colombier static int
itdactive(Itd * td)128684860c5dSDavid du Colombier itdactive(Itd *td)
128784860c5dSDavid du Colombier {
128884860c5dSDavid du Colombier int i;
128984860c5dSDavid du Colombier
129084860c5dSDavid du Colombier for(i = 0; i < nelem(td->csw); i++)
129184860c5dSDavid du Colombier if((td->csw[i] & Itdactive) != 0)
129284860c5dSDavid du Colombier return 1;
129384860c5dSDavid du Colombier return 0;
129484860c5dSDavid du Colombier }
129584860c5dSDavid du Colombier
129684860c5dSDavid du Colombier static int
isohsinterrupt(Ctlr * ctlr,Isoio * iso)129784860c5dSDavid du Colombier isohsinterrupt(Ctlr *ctlr, Isoio *iso)
129884860c5dSDavid du Colombier {
129984860c5dSDavid du Colombier int err, i, nframes, t;
130084860c5dSDavid du Colombier Itd *tdi;
130184860c5dSDavid du Colombier
130284860c5dSDavid du Colombier tdi = iso->tdi;
130384860c5dSDavid du Colombier assert(tdi != nil);
130484860c5dSDavid du Colombier if(itdactive(tdi)) /* not all tds are done */
130584860c5dSDavid du Colombier return 0;
130684860c5dSDavid du Colombier ctlr->nisointr++;
130784860c5dSDavid du Colombier ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
130884860c5dSDavid du Colombier if(iso->state != Qrun && iso->state != Qdone)
130984860c5dSDavid du Colombier panic("isofsintr: iso state");
131084860c5dSDavid du Colombier if(ehcidebug > 1 || iso->debug > 1)
131184860c5dSDavid du Colombier isodump(iso, 0);
131284860c5dSDavid du Colombier
131384860c5dSDavid du Colombier nframes = iso->nframes / 2; /* limit how many we look */
131484860c5dSDavid du Colombier if(nframes > Nisoframes)
131584860c5dSDavid du Colombier nframes = Nisoframes;
131684860c5dSDavid du Colombier
131784860c5dSDavid du Colombier if(iso->tok == Tdtokin)
131884860c5dSDavid du Colombier tdi->ndata = 0;
131984860c5dSDavid du Colombier /* else, it has the number of bytes transferred */
132084860c5dSDavid du Colombier
132184860c5dSDavid du Colombier for(i = 0; i < nframes && itdactive(tdi) == 0; i++){
132284860c5dSDavid du Colombier if(iso->tok == Tdtokin)
132384860c5dSDavid du Colombier tdi->ndata += (tdi->csw[i] >> Itdlenshift) & Itdlenmask;
132484860c5dSDavid du Colombier err = 0;
132584860c5dSDavid du Colombier coherence();
132684860c5dSDavid du Colombier for(t = 0; t < nelem(tdi->csw); t++){
132784860c5dSDavid du Colombier tdi->csw[t] &= ~Itdioc;
132884860c5dSDavid du Colombier coherence();
132984860c5dSDavid du Colombier err |= tdi->csw[t] & Itderrors;
133084860c5dSDavid du Colombier }
133184860c5dSDavid du Colombier if(err == 0)
133284860c5dSDavid du Colombier iso->nerrs = 0;
133384860c5dSDavid du Colombier else if(iso->nerrs++ > iso->nframes/2){
133484860c5dSDavid du Colombier if(iso->err == nil){
133584860c5dSDavid du Colombier iso->err = ierrmsg(err);
133684860c5dSDavid du Colombier diprint("isohsintr: tdi %#p error %#ux %s\n",
133784860c5dSDavid du Colombier tdi, err, iso->err);
133884860c5dSDavid du Colombier diprint("ctlr load %uld\n", ctlr->load);
133984860c5dSDavid du Colombier }
134084860c5dSDavid du Colombier tdi->ndata = 0;
134184860c5dSDavid du Colombier }else
134284860c5dSDavid du Colombier tdi->ndata = 0;
134384860c5dSDavid du Colombier if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){
134484860c5dSDavid du Colombier memset(iso->tdu->data, 0, iso->tdu->mdata);
134584860c5dSDavid du Colombier itdinit(iso, iso->tdu);
134684860c5dSDavid du Colombier iso->tdu = iso->tdu->next;
134784860c5dSDavid du Colombier iso->nleft = 0;
134884860c5dSDavid du Colombier }
134984860c5dSDavid du Colombier tdi = tdi->next;
135084860c5dSDavid du Colombier coherence();
135184860c5dSDavid du Colombier }
135284860c5dSDavid du Colombier ddiprint("isohsintr: %d frames processed\n", nframes);
135384860c5dSDavid du Colombier if(i == nframes){
135484860c5dSDavid du Colombier tdi->csw[0] |= Itdioc;
135584860c5dSDavid du Colombier coherence();
135684860c5dSDavid du Colombier }
135784860c5dSDavid du Colombier iso->tdi = tdi;
135884860c5dSDavid du Colombier coherence();
135984860c5dSDavid du Colombier if(isocanwrite(iso) || isocanread(iso)){
136084860c5dSDavid du Colombier diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
136184860c5dSDavid du Colombier iso->tdi, iso->tdu);
136284860c5dSDavid du Colombier wakeup(iso);
136384860c5dSDavid du Colombier }
136484860c5dSDavid du Colombier return 1;
136584860c5dSDavid du Colombier }
136684860c5dSDavid du Colombier
136784860c5dSDavid du Colombier static int
isofsinterrupt(Ctlr * ctlr,Isoio * iso)136884860c5dSDavid du Colombier isofsinterrupt(Ctlr *ctlr, Isoio *iso)
136984860c5dSDavid du Colombier {
137084860c5dSDavid du Colombier int err, i, nframes;
137184860c5dSDavid du Colombier Sitd *stdi;
137284860c5dSDavid du Colombier
137384860c5dSDavid du Colombier stdi = iso->stdi;
137484860c5dSDavid du Colombier assert(stdi != nil);
137584860c5dSDavid du Colombier if((stdi->csw & Stdactive) != 0) /* nothing new done */
137684860c5dSDavid du Colombier return 0;
137784860c5dSDavid du Colombier ctlr->nisointr++;
137884860c5dSDavid du Colombier ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso->stdu);
137984860c5dSDavid du Colombier if(iso->state != Qrun && iso->state != Qdone)
138084860c5dSDavid du Colombier panic("isofsintr: iso state");
138184860c5dSDavid du Colombier if(ehcidebug > 1 || iso->debug > 1)
138284860c5dSDavid du Colombier isodump(iso, 0);
138384860c5dSDavid du Colombier
138484860c5dSDavid du Colombier nframes = iso->nframes / 2; /* limit how many we look */
138584860c5dSDavid du Colombier if(nframes > Nisoframes)
138684860c5dSDavid du Colombier nframes = Nisoframes;
138784860c5dSDavid du Colombier
138884860c5dSDavid du Colombier for(i = 0; i < nframes && (stdi->csw & Stdactive) == 0; i++){
138984860c5dSDavid du Colombier stdi->csw &= ~Stdioc;
139084860c5dSDavid du Colombier /* write back csw and see if it produces errors */
139184860c5dSDavid du Colombier coherence();
139284860c5dSDavid du Colombier err = stdi->csw & Stderrors;
139384860c5dSDavid du Colombier if(err == 0){
139484860c5dSDavid du Colombier iso->nerrs = 0;
139584860c5dSDavid du Colombier if(iso->tok == Tdtokin)
139684860c5dSDavid du Colombier stdi->ndata = (stdi->csw>>Stdlenshift)&Stdlenmask;
139784860c5dSDavid du Colombier /* else len is assumed correct */
139884860c5dSDavid du Colombier }else if(iso->nerrs++ > iso->nframes/2){
139984860c5dSDavid du Colombier if(iso->err == nil){
140084860c5dSDavid du Colombier iso->err = serrmsg(err);
140184860c5dSDavid du Colombier diprint("isofsintr: tdi %#p error %#ux %s\n",
140284860c5dSDavid du Colombier stdi, err, iso->err);
140384860c5dSDavid du Colombier diprint("ctlr load %uld\n", ctlr->load);
140484860c5dSDavid du Colombier }
140584860c5dSDavid du Colombier stdi->ndata = 0;
140684860c5dSDavid du Colombier }else
140784860c5dSDavid du Colombier stdi->ndata = 0;
140884860c5dSDavid du Colombier
140984860c5dSDavid du Colombier if(stdi->next == iso->stdu || stdi->next->next == iso->stdu){
141084860c5dSDavid du Colombier memset(iso->stdu->data, 0, iso->stdu->mdata);
141184860c5dSDavid du Colombier coherence();
141284860c5dSDavid du Colombier sitdinit(iso, iso->stdu);
141384860c5dSDavid du Colombier iso->stdu = iso->stdu->next;
141484860c5dSDavid du Colombier iso->nleft = 0;
141584860c5dSDavid du Colombier }
141684860c5dSDavid du Colombier coherence();
141784860c5dSDavid du Colombier stdi = stdi->next;
141884860c5dSDavid du Colombier }
141984860c5dSDavid du Colombier ddiprint("isofsintr: %d frames processed\n", nframes);
142084860c5dSDavid du Colombier if(i == nframes){
142184860c5dSDavid du Colombier stdi->csw |= Stdioc;
142284860c5dSDavid du Colombier coherence();
142384860c5dSDavid du Colombier }
142484860c5dSDavid du Colombier iso->stdi = stdi;
142584860c5dSDavid du Colombier coherence();
142684860c5dSDavid du Colombier if(isocanwrite(iso) || isocanread(iso)){
142784860c5dSDavid du Colombier diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
142884860c5dSDavid du Colombier iso->stdi, iso->stdu);
142984860c5dSDavid du Colombier wakeup(iso);
143084860c5dSDavid du Colombier }
143184860c5dSDavid du Colombier return 1;
143284860c5dSDavid du Colombier }
143384860c5dSDavid du Colombier
143484860c5dSDavid du Colombier static int
qhinterrupt(Ctlr * ctlr,Qh * qh)143584860c5dSDavid du Colombier qhinterrupt(Ctlr *ctlr, Qh *qh)
143684860c5dSDavid du Colombier {
143784860c5dSDavid du Colombier Td *td;
143884860c5dSDavid du Colombier int err;
143984860c5dSDavid du Colombier
144084860c5dSDavid du Colombier if(qh->state != Qrun)
144184860c5dSDavid du Colombier panic("qhinterrupt: qh state");
144284860c5dSDavid du Colombier td = qh->tds;
144384860c5dSDavid du Colombier if(td == nil)
144484860c5dSDavid du Colombier panic("qhinterrupt: no tds");
144584860c5dSDavid du Colombier if((td->csw & Tdactive) == 0)
144684860c5dSDavid du Colombier ddqprint("qhinterrupt port %#p qh %#p\n", ctlr->capio, qh);
144784860c5dSDavid du Colombier for(; td != nil; td = td->next){
144884860c5dSDavid du Colombier if(td->csw & Tdactive)
144984860c5dSDavid du Colombier return 0;
145084860c5dSDavid du Colombier err = td->csw & Tderrors;
145184860c5dSDavid du Colombier if(err != 0){
145284860c5dSDavid du Colombier if(qh->io->err == nil){
145384860c5dSDavid du Colombier qh->io->err = errmsg(err);
145484860c5dSDavid du Colombier dqprint("qhintr: td %#p csw %#lux error %#ux %s\n",
145584860c5dSDavid du Colombier td, td->csw, err, qh->io->err);
145684860c5dSDavid du Colombier }
145784860c5dSDavid du Colombier break;
145884860c5dSDavid du Colombier }
145984860c5dSDavid du Colombier td->ndata = tdlen(td);
146084860c5dSDavid du Colombier coherence();
146184860c5dSDavid du Colombier if(td->ndata < maxtdlen(td)){ /* EOT */
146284860c5dSDavid du Colombier td = td->next;
146384860c5dSDavid du Colombier break;
146484860c5dSDavid du Colombier }
146584860c5dSDavid du Colombier }
146684860c5dSDavid du Colombier /*
146784860c5dSDavid du Colombier * Done. Make void the Tds not used (errors or EOT) and wakeup epio.
146884860c5dSDavid du Colombier */
146984860c5dSDavid du Colombier for(; td != nil; td = td->next)
147084860c5dSDavid du Colombier td->ndata = 0;
147184860c5dSDavid du Colombier coherence();
147284860c5dSDavid du Colombier qh->state = Qdone;
147384860c5dSDavid du Colombier coherence();
147484860c5dSDavid du Colombier wakeup(qh->io);
147584860c5dSDavid du Colombier return 1;
147684860c5dSDavid du Colombier }
147784860c5dSDavid du Colombier
147884860c5dSDavid du Colombier static int
ehciintr(Hci * hp)147984860c5dSDavid du Colombier ehciintr(Hci *hp)
148084860c5dSDavid du Colombier {
148184860c5dSDavid du Colombier Ctlr *ctlr;
148284860c5dSDavid du Colombier Eopio *opio;
148384860c5dSDavid du Colombier Isoio *iso;
148484860c5dSDavid du Colombier ulong sts;
148584860c5dSDavid du Colombier Qh *qh;
148684860c5dSDavid du Colombier int i, some;
148784860c5dSDavid du Colombier
148884860c5dSDavid du Colombier ctlr = hp->aux;
148984860c5dSDavid du Colombier opio = ctlr->opio;
149084860c5dSDavid du Colombier
149184860c5dSDavid du Colombier /*
149284860c5dSDavid du Colombier * Will we know in USB 3.0 who the interrupt was for?.
149384860c5dSDavid du Colombier * Do they still teach indexing in CS?
149484860c5dSDavid du Colombier * This is Intel's doing.
149584860c5dSDavid du Colombier */
149684860c5dSDavid du Colombier ilock(ctlr);
149784860c5dSDavid du Colombier ctlr->nintr++;
149884860c5dSDavid du Colombier sts = opio->sts & Sintrs;
149984860c5dSDavid du Colombier if(sts == 0){ /* not ours; shared intr. */
150084860c5dSDavid du Colombier iunlock(ctlr);
150184860c5dSDavid du Colombier return 0;
150284860c5dSDavid du Colombier }
150384860c5dSDavid du Colombier opio->sts = sts;
150484860c5dSDavid du Colombier coherence();
150584860c5dSDavid du Colombier if((sts & Sherr) != 0)
150684860c5dSDavid du Colombier print("ehci: port %#p fatal host system error\n", ctlr->capio);
150784860c5dSDavid du Colombier if((sts & Shalted) != 0)
150884860c5dSDavid du Colombier print("ehci: port %#p: halted\n", ctlr->capio);
150984860c5dSDavid du Colombier if((sts & Sasync) != 0){
151084860c5dSDavid du Colombier dprint("ehci: doorbell\n");
151184860c5dSDavid du Colombier wakeup(ctlr);
151284860c5dSDavid du Colombier }
151384860c5dSDavid du Colombier /*
151484860c5dSDavid du Colombier * We enter always this if, even if it seems the
151584860c5dSDavid du Colombier * interrupt does not report anything done/failed.
151684860c5dSDavid du Colombier * Some controllers don't post interrupts right.
151784860c5dSDavid du Colombier */
151884860c5dSDavid du Colombier some = 0;
151984860c5dSDavid du Colombier if((sts & (Serrintr|Sintr)) != 0){
152084860c5dSDavid du Colombier ctlr->ntdintr++;
152184860c5dSDavid du Colombier if(ehcidebug > 1){
152284860c5dSDavid du Colombier print("ehci port %#p frames %#p nintr %d ntdintr %d",
152384860c5dSDavid du Colombier ctlr->capio, ctlr->frames,
152484860c5dSDavid du Colombier ctlr->nintr, ctlr->ntdintr);
152584860c5dSDavid du Colombier print(" nqhintr %d nisointr %d\n",
152684860c5dSDavid du Colombier ctlr->nqhintr, ctlr->nisointr);
152784860c5dSDavid du Colombier print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
152884860c5dSDavid du Colombier opio->cmd, opio->sts, opio->intr, opio->frno);
152984860c5dSDavid du Colombier }
153084860c5dSDavid du Colombier
153184860c5dSDavid du Colombier /* process the Iso transfers */
153284860c5dSDavid du Colombier for(iso = ctlr->iso; iso != nil; iso = iso->next)
153384860c5dSDavid du Colombier if(iso->state == Qrun || iso->state == Qdone)
153484860c5dSDavid du Colombier if(iso->hs != 0)
153584860c5dSDavid du Colombier some += isohsinterrupt(ctlr, iso);
153684860c5dSDavid du Colombier else
153784860c5dSDavid du Colombier some += isofsinterrupt(ctlr, iso);
153884860c5dSDavid du Colombier
153984860c5dSDavid du Colombier /* process the qhs in the periodic tree */
154084860c5dSDavid du Colombier for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
154184860c5dSDavid du Colombier if(qh->state == Qrun)
154284860c5dSDavid du Colombier some += qhinterrupt(ctlr, qh);
154384860c5dSDavid du Colombier
154484860c5dSDavid du Colombier /* process the async Qh circular list */
154584860c5dSDavid du Colombier qh = ctlr->qhs;
154684860c5dSDavid du Colombier i = 0;
154784860c5dSDavid du Colombier do{
154884860c5dSDavid du Colombier if (qh == nil)
154984860c5dSDavid du Colombier panic("ehciintr: nil qh");
155084860c5dSDavid du Colombier if(qh->state == Qrun)
155184860c5dSDavid du Colombier some += qhinterrupt(ctlr, qh);
155284860c5dSDavid du Colombier qh = qh->next;
155384860c5dSDavid du Colombier }while(qh != ctlr->qhs && i++ < 100);
155484860c5dSDavid du Colombier if(i > 100)
155584860c5dSDavid du Colombier print("echi: interrupt: qh loop?\n");
155684860c5dSDavid du Colombier }
155784860c5dSDavid du Colombier // if (some == 0)
155884860c5dSDavid du Colombier // panic("ehciintr: no work");
155984860c5dSDavid du Colombier iunlock(ctlr);
156084860c5dSDavid du Colombier return some;
156184860c5dSDavid du Colombier }
156284860c5dSDavid du Colombier
156384860c5dSDavid du Colombier static void
interrupt(Ureg *,void * a)156484860c5dSDavid du Colombier interrupt(Ureg*, void* a)
156584860c5dSDavid du Colombier {
156684860c5dSDavid du Colombier ehciintr(a);
156784860c5dSDavid du Colombier }
156884860c5dSDavid du Colombier
156984860c5dSDavid du Colombier static int
portenable(Hci * hp,int port,int on)157084860c5dSDavid du Colombier portenable(Hci *hp, int port, int on)
157184860c5dSDavid du Colombier {
157284860c5dSDavid du Colombier Ctlr *ctlr;
157384860c5dSDavid du Colombier Eopio *opio;
157484860c5dSDavid du Colombier int s;
157584860c5dSDavid du Colombier
157684860c5dSDavid du Colombier ctlr = hp->aux;
157784860c5dSDavid du Colombier opio = ctlr->opio;
157884860c5dSDavid du Colombier s = opio->portsc[port-1];
157984860c5dSDavid du Colombier qlock(&ctlr->portlck);
158084860c5dSDavid du Colombier if(waserror()){
158184860c5dSDavid du Colombier qunlock(&ctlr->portlck);
158284860c5dSDavid du Colombier nexterror();
158384860c5dSDavid du Colombier }
158484860c5dSDavid du Colombier dprint("ehci %#p port %d enable=%d; sts %#x\n",
158584860c5dSDavid du Colombier ctlr->capio, port, on, s);
158684860c5dSDavid du Colombier ilock(ctlr);
158784860c5dSDavid du Colombier if(s & (Psstatuschg | Pschange))
158884860c5dSDavid du Colombier opio->portsc[port-1] = s;
158984860c5dSDavid du Colombier if(on)
159084860c5dSDavid du Colombier opio->portsc[port-1] |= Psenable;
159184860c5dSDavid du Colombier else
159284860c5dSDavid du Colombier opio->portsc[port-1] &= ~Psenable;
159384860c5dSDavid du Colombier coherence();
159484860c5dSDavid du Colombier microdelay(64);
159584860c5dSDavid du Colombier iunlock(ctlr);
159684860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
159784860c5dSDavid du Colombier dprint("ehci %#p port %d enable=%d: sts %#lux\n",
159884860c5dSDavid du Colombier ctlr->capio, port, on, opio->portsc[port-1]);
159984860c5dSDavid du Colombier qunlock(&ctlr->portlck);
160084860c5dSDavid du Colombier poperror();
160184860c5dSDavid du Colombier return 0;
160284860c5dSDavid du Colombier }
160384860c5dSDavid du Colombier
160484860c5dSDavid du Colombier /*
160584860c5dSDavid du Colombier * If we detect during status that the port is low-speed or
160684860c5dSDavid du Colombier * during reset that it's full-speed, the device is not for
160784860c5dSDavid du Colombier * ourselves. The companion controller will take care.
160884860c5dSDavid du Colombier * Low-speed devices will not be seen by usbd. Full-speed
160984860c5dSDavid du Colombier * ones are seen because it's only after reset that we know what
161084860c5dSDavid du Colombier * they are (usbd may notice a device not enabled in this case).
161184860c5dSDavid du Colombier */
161284860c5dSDavid du Colombier static void
portlend(Ctlr * ctlr,int port,char * ss)161384860c5dSDavid du Colombier portlend(Ctlr *ctlr, int port, char *ss)
161484860c5dSDavid du Colombier {
161584860c5dSDavid du Colombier Eopio *opio;
161684860c5dSDavid du Colombier ulong s;
161784860c5dSDavid du Colombier
161884860c5dSDavid du Colombier opio = ctlr->opio;
161984860c5dSDavid du Colombier
162084860c5dSDavid du Colombier dprint("ehci %#p port %d: %s speed device: no longer owned\n",
162184860c5dSDavid du Colombier ctlr->capio, port, ss);
162284860c5dSDavid du Colombier s = opio->portsc[port-1] & ~(Pschange|Psstatuschg);
162384860c5dSDavid du Colombier opio->portsc[port-1] = s | Psowner;
162484860c5dSDavid du Colombier coherence();
162584860c5dSDavid du Colombier }
162684860c5dSDavid du Colombier
162784860c5dSDavid du Colombier static int
portreset(Hci * hp,int port,int on)162884860c5dSDavid du Colombier portreset(Hci *hp, int port, int on)
162984860c5dSDavid du Colombier {
16305a5ba3fdSDavid du Colombier ulong *portscp;
163184860c5dSDavid du Colombier Eopio *opio;
163284860c5dSDavid du Colombier Ctlr *ctlr;
163384860c5dSDavid du Colombier int i;
163484860c5dSDavid du Colombier
163584860c5dSDavid du Colombier if(on == 0)
163684860c5dSDavid du Colombier return 0;
163784860c5dSDavid du Colombier
163884860c5dSDavid du Colombier ctlr = hp->aux;
163984860c5dSDavid du Colombier opio = ctlr->opio;
164084860c5dSDavid du Colombier qlock(&ctlr->portlck);
164184860c5dSDavid du Colombier if(waserror()){
164284860c5dSDavid du Colombier iunlock(ctlr);
164384860c5dSDavid du Colombier qunlock(&ctlr->portlck);
164484860c5dSDavid du Colombier nexterror();
164584860c5dSDavid du Colombier }
16465a5ba3fdSDavid du Colombier portscp = &opio->portsc[port-1];
16475a5ba3fdSDavid du Colombier dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
164884860c5dSDavid du Colombier ilock(ctlr);
16495a5ba3fdSDavid du Colombier /* Shalted must be zero, else Psreset will stay set */
16505a5ba3fdSDavid du Colombier if (opio->sts & Shalted)
16515a5ba3fdSDavid du Colombier iprint("ehci %#p: halted yet trying to reset port\n",
16525a5ba3fdSDavid du Colombier ctlr->capio);
16535a5ba3fdSDavid du Colombier *portscp = (*portscp & ~Psenable) | Psreset; /* initiate reset */
165484860c5dSDavid du Colombier coherence();
165584860c5dSDavid du Colombier
16565a5ba3fdSDavid du Colombier /*
16575a5ba3fdSDavid du Colombier * usb 2 spec: reset must finish within 20 ms.
16585a5ba3fdSDavid du Colombier * linux says spec says it can take 50 ms. for hubs.
16595a5ba3fdSDavid du Colombier */
1660afa28516SDavid du Colombier for(i = 0; *portscp & Psreset && i < 50; i++)
166184860c5dSDavid du Colombier delay(10);
16625a5ba3fdSDavid du Colombier if (*portscp & Psreset)
16635a5ba3fdSDavid du Colombier iprint("ehci %#p: port %d didn't reset within %d ms; sts %#lux\n",
16645a5ba3fdSDavid du Colombier ctlr->capio, port, i * 10, *portscp);
16655a5ba3fdSDavid du Colombier *portscp &= ~Psreset; /* force appearance of reset done */
166684860c5dSDavid du Colombier coherence();
16675a5ba3fdSDavid du Colombier delay(10); /* ehci spec: enable within 2 ms. */
166884860c5dSDavid du Colombier
16695a5ba3fdSDavid du Colombier if((*portscp & Psenable) == 0)
167084860c5dSDavid du Colombier portlend(ctlr, port, "full");
167184860c5dSDavid du Colombier
167284860c5dSDavid du Colombier iunlock(ctlr);
167384860c5dSDavid du Colombier dprint("ehci %#p after port %d reset; sts %#lux\n",
16745a5ba3fdSDavid du Colombier ctlr->capio, port, *portscp);
167584860c5dSDavid du Colombier qunlock(&ctlr->portlck);
167684860c5dSDavid du Colombier poperror();
167784860c5dSDavid du Colombier return 0;
167884860c5dSDavid du Colombier }
167984860c5dSDavid du Colombier
168084860c5dSDavid du Colombier static int
portstatus(Hci * hp,int port)168184860c5dSDavid du Colombier portstatus(Hci *hp, int port)
168284860c5dSDavid du Colombier {
168384860c5dSDavid du Colombier int s, r;
168484860c5dSDavid du Colombier Eopio *opio;
168584860c5dSDavid du Colombier Ctlr *ctlr;
168684860c5dSDavid du Colombier
168784860c5dSDavid du Colombier ctlr = hp->aux;
168884860c5dSDavid du Colombier opio = ctlr->opio;
168984860c5dSDavid du Colombier qlock(&ctlr->portlck);
169084860c5dSDavid du Colombier if(waserror()){
169184860c5dSDavid du Colombier iunlock(ctlr);
169284860c5dSDavid du Colombier qunlock(&ctlr->portlck);
169384860c5dSDavid du Colombier nexterror();
169484860c5dSDavid du Colombier }
169584860c5dSDavid du Colombier ilock(ctlr);
169684860c5dSDavid du Colombier s = opio->portsc[port-1];
169784860c5dSDavid du Colombier if(s & (Psstatuschg | Pschange)){
169884860c5dSDavid du Colombier opio->portsc[port-1] = s;
169984860c5dSDavid du Colombier coherence();
170084860c5dSDavid du Colombier ddprint("ehci %#p port %d status %#x\n", ctlr->capio, port, s);
170184860c5dSDavid du Colombier }
170284860c5dSDavid du Colombier /*
170384860c5dSDavid du Colombier * If the port is a low speed port we yield ownership now
170484860c5dSDavid du Colombier * to the [uo]hci companion controller and pretend it's not here.
170584860c5dSDavid du Colombier */
170684860c5dSDavid du Colombier if((s & Pspresent) != 0 && (s & Pslinemask) == Pslow){
170784860c5dSDavid du Colombier portlend(ctlr, port, "low");
170884860c5dSDavid du Colombier s &= ~Pspresent; /* not for us this time */
170984860c5dSDavid du Colombier }
171084860c5dSDavid du Colombier iunlock(ctlr);
171184860c5dSDavid du Colombier qunlock(&ctlr->portlck);
171284860c5dSDavid du Colombier poperror();
171384860c5dSDavid du Colombier
171484860c5dSDavid du Colombier /*
171584860c5dSDavid du Colombier * We must return status bits as a
171684860c5dSDavid du Colombier * get port status hub request would do.
171784860c5dSDavid du Colombier */
171884860c5dSDavid du Colombier r = 0;
171984860c5dSDavid du Colombier if(s & Pspresent)
172084860c5dSDavid du Colombier r |= HPpresent|HPhigh;
172184860c5dSDavid du Colombier if(s & Psenable)
172284860c5dSDavid du Colombier r |= HPenable;
172384860c5dSDavid du Colombier if(s & Pssuspend)
172484860c5dSDavid du Colombier r |= HPsuspend;
172584860c5dSDavid du Colombier if(s & Psreset)
172684860c5dSDavid du Colombier r |= HPreset;
172784860c5dSDavid du Colombier if(s & Psstatuschg)
172884860c5dSDavid du Colombier r |= HPstatuschg;
172984860c5dSDavid du Colombier if(s & Pschange)
173084860c5dSDavid du Colombier r |= HPchange;
173184860c5dSDavid du Colombier return r;
173284860c5dSDavid du Colombier }
173384860c5dSDavid du Colombier
173484860c5dSDavid du Colombier static char*
seprintio(char * s,char * e,Qio * io,char * pref)173584860c5dSDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
173684860c5dSDavid du Colombier {
173784860c5dSDavid du Colombier s = seprint(s,e,"%s io %#p qh %#p id %#x", pref, io, io->qh, io->usbid);
173884860c5dSDavid du Colombier s = seprint(s,e," iot %ld", io->iotime);
173984860c5dSDavid du Colombier s = seprint(s,e," tog %#x tok %#x err %s", io->toggle, io->tok, io->err);
174084860c5dSDavid du Colombier return s;
174184860c5dSDavid du Colombier }
174284860c5dSDavid du Colombier
174384860c5dSDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)174484860c5dSDavid du Colombier seprintep(char *s, char *e, Ep *ep)
174584860c5dSDavid du Colombier {
174684860c5dSDavid du Colombier Qio *io;
174784860c5dSDavid du Colombier Ctlio *cio;
174884860c5dSDavid du Colombier Ctlr *ctlr;
174984860c5dSDavid du Colombier
175084860c5dSDavid du Colombier ctlr = ep->hp->aux;
175184860c5dSDavid du Colombier ilock(ctlr);
175284860c5dSDavid du Colombier if(ep->aux == nil){
175384860c5dSDavid du Colombier *s = 0;
175484860c5dSDavid du Colombier iunlock(ctlr);
175584860c5dSDavid du Colombier return s;
175684860c5dSDavid du Colombier }
175784860c5dSDavid du Colombier switch(ep->ttype){
175884860c5dSDavid du Colombier case Tctl:
175984860c5dSDavid du Colombier cio = ep->aux;
176084860c5dSDavid du Colombier s = seprintio(s, e, cio, "c");
176184860c5dSDavid du Colombier s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
176284860c5dSDavid du Colombier break;
176384860c5dSDavid du Colombier case Tbulk:
176484860c5dSDavid du Colombier case Tintr:
176584860c5dSDavid du Colombier io = ep->aux;
176684860c5dSDavid du Colombier if(ep->mode != OWRITE)
176784860c5dSDavid du Colombier s = seprintio(s, e, &io[OREAD], "r");
176884860c5dSDavid du Colombier if(ep->mode != OREAD)
176984860c5dSDavid du Colombier s = seprintio(s, e, &io[OWRITE], "w");
177084860c5dSDavid du Colombier break;
177184860c5dSDavid du Colombier case Tiso:
177284860c5dSDavid du Colombier *s = 0;
177384860c5dSDavid du Colombier break;
177484860c5dSDavid du Colombier }
177584860c5dSDavid du Colombier iunlock(ctlr);
177684860c5dSDavid du Colombier return s;
177784860c5dSDavid du Colombier }
177884860c5dSDavid du Colombier
177984860c5dSDavid du Colombier /*
178084860c5dSDavid du Colombier * halt condition was cleared on the endpoint. update our toggles.
178184860c5dSDavid du Colombier */
178284860c5dSDavid du Colombier static void
clrhalt(Ep * ep)178384860c5dSDavid du Colombier clrhalt(Ep *ep)
178484860c5dSDavid du Colombier {
178584860c5dSDavid du Colombier Qio *io;
178684860c5dSDavid du Colombier
178784860c5dSDavid du Colombier ep->clrhalt = 0;
178884860c5dSDavid du Colombier coherence();
178984860c5dSDavid du Colombier switch(ep->ttype){
179084860c5dSDavid du Colombier case Tintr:
179184860c5dSDavid du Colombier case Tbulk:
179284860c5dSDavid du Colombier io = ep->aux;
179384860c5dSDavid du Colombier if(ep->mode != OREAD){
179484860c5dSDavid du Colombier qlock(&io[OWRITE]);
179584860c5dSDavid du Colombier io[OWRITE].toggle = Tddata0;
179684860c5dSDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OWRITE);
179784860c5dSDavid du Colombier qunlock(&io[OWRITE]);
179884860c5dSDavid du Colombier }
179984860c5dSDavid du Colombier if(ep->mode != OWRITE){
180084860c5dSDavid du Colombier qlock(&io[OREAD]);
180184860c5dSDavid du Colombier io[OREAD].toggle = Tddata0;
180284860c5dSDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OREAD);
180384860c5dSDavid du Colombier qunlock(&io[OREAD]);
180484860c5dSDavid du Colombier }
180584860c5dSDavid du Colombier break;
180684860c5dSDavid du Colombier }
180784860c5dSDavid du Colombier }
180884860c5dSDavid du Colombier
180984860c5dSDavid du Colombier static void
xdump(char * pref,void * qh)181084860c5dSDavid du Colombier xdump(char* pref, void *qh)
181184860c5dSDavid du Colombier {
181284860c5dSDavid du Colombier int i;
181384860c5dSDavid du Colombier ulong *u;
181484860c5dSDavid du Colombier
181584860c5dSDavid du Colombier u = qh;
181684860c5dSDavid du Colombier print("%s %#p:", pref, u);
181784860c5dSDavid du Colombier for(i = 0; i < 16; i++)
181884860c5dSDavid du Colombier if((i%4) == 0)
181984860c5dSDavid du Colombier print("\n %#8.8ulx", u[i]);
182084860c5dSDavid du Colombier else
182184860c5dSDavid du Colombier print(" %#8.8ulx", u[i]);
182284860c5dSDavid du Colombier print("\n");
182384860c5dSDavid du Colombier }
182484860c5dSDavid du Colombier
182584860c5dSDavid du Colombier static long
episohscpy(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)182684860c5dSDavid du Colombier episohscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
182784860c5dSDavid du Colombier {
182884860c5dSDavid du Colombier int nr;
182984860c5dSDavid du Colombier long tot;
183084860c5dSDavid du Colombier Itd *tdu;
183184860c5dSDavid du Colombier
183284860c5dSDavid du Colombier for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){
183384860c5dSDavid du Colombier tdu = iso->tdu;
183484860c5dSDavid du Colombier if(itdactive(tdu))
183584860c5dSDavid du Colombier break;
183684860c5dSDavid du Colombier nr = tdu->ndata;
183784860c5dSDavid du Colombier if(tot + nr > count)
183884860c5dSDavid du Colombier nr = count - tot;
183984860c5dSDavid du Colombier if(nr == 0)
184084860c5dSDavid du Colombier print("ehci: ep%d.%d: too many polls\n",
184184860c5dSDavid du Colombier ep->dev->nb, ep->nb);
184284860c5dSDavid du Colombier else{
184384860c5dSDavid du Colombier iunlock(ctlr); /* We could page fault here */
184484860c5dSDavid du Colombier memmove(b+tot, tdu->data, nr);
184584860c5dSDavid du Colombier ilock(ctlr);
184684860c5dSDavid du Colombier if(nr < tdu->ndata)
184784860c5dSDavid du Colombier memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
184884860c5dSDavid du Colombier tdu->ndata -= nr;
184984860c5dSDavid du Colombier coherence();
185084860c5dSDavid du Colombier }
185184860c5dSDavid du Colombier if(tdu->ndata == 0){
185284860c5dSDavid du Colombier itdinit(iso, tdu);
185384860c5dSDavid du Colombier iso->tdu = tdu->next;
185484860c5dSDavid du Colombier }
185584860c5dSDavid du Colombier }
185684860c5dSDavid du Colombier return tot;
185784860c5dSDavid du Colombier }
185884860c5dSDavid du Colombier
185984860c5dSDavid du Colombier static long
episofscpy(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)186084860c5dSDavid du Colombier episofscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
186184860c5dSDavid du Colombier {
186284860c5dSDavid du Colombier int nr;
186384860c5dSDavid du Colombier long tot;
186484860c5dSDavid du Colombier Sitd *stdu;
186584860c5dSDavid du Colombier
186684860c5dSDavid du Colombier for(tot = 0; iso->stdi != iso->stdu && tot < count; tot += nr){
186784860c5dSDavid du Colombier stdu = iso->stdu;
186884860c5dSDavid du Colombier if(stdu->csw & Stdactive){
186984860c5dSDavid du Colombier diprint("ehci: episoread: %#p tdu active\n", iso);
187084860c5dSDavid du Colombier break;
187184860c5dSDavid du Colombier }
187284860c5dSDavid du Colombier nr = stdu->ndata;
187384860c5dSDavid du Colombier if(tot + nr > count)
187484860c5dSDavid du Colombier nr = count - tot;
187584860c5dSDavid du Colombier if(nr == 0)
187684860c5dSDavid du Colombier print("ehci: ep%d.%d: too many polls\n",
187784860c5dSDavid du Colombier ep->dev->nb, ep->nb);
187884860c5dSDavid du Colombier else{
187984860c5dSDavid du Colombier iunlock(ctlr); /* We could page fault here */
188084860c5dSDavid du Colombier memmove(b+tot, stdu->data, nr);
188184860c5dSDavid du Colombier ilock(ctlr);
188284860c5dSDavid du Colombier if(nr < stdu->ndata)
188384860c5dSDavid du Colombier memmove(stdu->data, stdu->data+nr,
188484860c5dSDavid du Colombier stdu->ndata - nr);
188584860c5dSDavid du Colombier stdu->ndata -= nr;
188684860c5dSDavid du Colombier coherence();
188784860c5dSDavid du Colombier }
188884860c5dSDavid du Colombier if(stdu->ndata == 0){
188984860c5dSDavid du Colombier sitdinit(iso, stdu);
189084860c5dSDavid du Colombier iso->stdu = stdu->next;
189184860c5dSDavid du Colombier }
189284860c5dSDavid du Colombier }
189384860c5dSDavid du Colombier return tot;
189484860c5dSDavid du Colombier }
189584860c5dSDavid du Colombier
189684860c5dSDavid du Colombier static long
episoread(Ep * ep,Isoio * iso,void * a,long count)189784860c5dSDavid du Colombier episoread(Ep *ep, Isoio *iso, void *a, long count)
189884860c5dSDavid du Colombier {
189984860c5dSDavid du Colombier Ctlr *ctlr;
190084860c5dSDavid du Colombier uchar *b;
190184860c5dSDavid du Colombier long tot;
190284860c5dSDavid du Colombier
190384860c5dSDavid du Colombier iso->debug = ep->debug;
190484860c5dSDavid du Colombier diprint("ehci: episoread: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
190584860c5dSDavid du Colombier
190684860c5dSDavid du Colombier b = a;
190784860c5dSDavid du Colombier ctlr = ep->hp->aux;
190884860c5dSDavid du Colombier qlock(iso);
190984860c5dSDavid du Colombier if(waserror()){
191084860c5dSDavid du Colombier qunlock(iso);
191184860c5dSDavid du Colombier nexterror();
191284860c5dSDavid du Colombier }
191384860c5dSDavid du Colombier iso->err = nil;
191484860c5dSDavid du Colombier iso->nerrs = 0;
191584860c5dSDavid du Colombier ilock(ctlr);
191684860c5dSDavid du Colombier if(iso->state == Qclose){
191784860c5dSDavid du Colombier iunlock(ctlr);
191884860c5dSDavid du Colombier error(iso->err ? iso->err : Eio);
191984860c5dSDavid du Colombier }
192084860c5dSDavid du Colombier iso->state = Qrun;
192184860c5dSDavid du Colombier coherence();
192284860c5dSDavid du Colombier while(isocanread(iso) == 0){
192384860c5dSDavid du Colombier iunlock(ctlr);
192484860c5dSDavid du Colombier diprint("ehci: episoread: %#p sleep\n", iso);
192584860c5dSDavid du Colombier if(waserror()){
192684860c5dSDavid du Colombier if(iso->err == nil)
192784860c5dSDavid du Colombier iso->err = "I/O timed out";
192884860c5dSDavid du Colombier ilock(ctlr);
192984860c5dSDavid du Colombier break;
193084860c5dSDavid du Colombier }
193184860c5dSDavid du Colombier tsleep(iso, isocanread, iso, ep->tmout);
193284860c5dSDavid du Colombier poperror();
193384860c5dSDavid du Colombier ilock(ctlr);
193484860c5dSDavid du Colombier }
193584860c5dSDavid du Colombier if(iso->state == Qclose){
193684860c5dSDavid du Colombier iunlock(ctlr);
193784860c5dSDavid du Colombier error(iso->err ? iso->err : Eio);
193884860c5dSDavid du Colombier }
193984860c5dSDavid du Colombier iso->state = Qdone;
194084860c5dSDavid du Colombier coherence();
194184860c5dSDavid du Colombier assert(iso->tdu != iso->tdi);
194284860c5dSDavid du Colombier
194384860c5dSDavid du Colombier if(iso->hs != 0)
194484860c5dSDavid du Colombier tot = episohscpy(ctlr, ep, iso, b, count);
194584860c5dSDavid du Colombier else
194684860c5dSDavid du Colombier tot = episofscpy(ctlr, ep, iso, b, count);
194784860c5dSDavid du Colombier iunlock(ctlr);
194884860c5dSDavid du Colombier qunlock(iso);
194984860c5dSDavid du Colombier poperror();
195084860c5dSDavid du Colombier diprint("uhci: episoread: %#p %uld bytes err '%s'\n", iso, tot, iso->err);
195184860c5dSDavid du Colombier if(iso->err != nil)
195284860c5dSDavid du Colombier error(iso->err);
195384860c5dSDavid du Colombier return tot;
195484860c5dSDavid du Colombier }
195584860c5dSDavid du Colombier
195684860c5dSDavid du Colombier /*
195784860c5dSDavid du Colombier * iso->tdu is the next place to put data. When it gets full
195884860c5dSDavid du Colombier * it is activated and tdu advanced.
195984860c5dSDavid du Colombier */
196084860c5dSDavid du Colombier static long
putsamples(Isoio * iso,uchar * b,long count)196184860c5dSDavid du Colombier putsamples(Isoio *iso, uchar *b, long count)
196284860c5dSDavid du Colombier {
196384860c5dSDavid du Colombier long tot, n;
196484860c5dSDavid du Colombier
196584860c5dSDavid du Colombier for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
196684860c5dSDavid du Colombier n = count-tot;
196784860c5dSDavid du Colombier if(iso->hs != 0){
196884860c5dSDavid du Colombier if(n > iso->tdu->mdata - iso->nleft)
196984860c5dSDavid du Colombier n = iso->tdu->mdata - iso->nleft;
197084860c5dSDavid du Colombier memmove(iso->tdu->data + iso->nleft, b + tot, n);
197184860c5dSDavid du Colombier coherence();
197284860c5dSDavid du Colombier iso->nleft += n;
197384860c5dSDavid du Colombier if(iso->nleft == iso->tdu->mdata){
197484860c5dSDavid du Colombier itdinit(iso, iso->tdu);
197584860c5dSDavid du Colombier iso->nleft = 0;
197684860c5dSDavid du Colombier iso->tdu = iso->tdu->next;
197784860c5dSDavid du Colombier }
197884860c5dSDavid du Colombier }else{
197984860c5dSDavid du Colombier if(n > iso->stdu->mdata - iso->nleft)
198084860c5dSDavid du Colombier n = iso->stdu->mdata - iso->nleft;
198184860c5dSDavid du Colombier memmove(iso->stdu->data + iso->nleft, b + tot, n);
198284860c5dSDavid du Colombier coherence();
198384860c5dSDavid du Colombier iso->nleft += n;
198484860c5dSDavid du Colombier if(iso->nleft == iso->stdu->mdata){
198584860c5dSDavid du Colombier sitdinit(iso, iso->stdu);
198684860c5dSDavid du Colombier iso->nleft = 0;
198784860c5dSDavid du Colombier iso->stdu = iso->stdu->next;
198884860c5dSDavid du Colombier }
198984860c5dSDavid du Colombier }
199084860c5dSDavid du Colombier }
199184860c5dSDavid du Colombier return tot;
199284860c5dSDavid du Colombier }
199384860c5dSDavid du Colombier
199484860c5dSDavid du Colombier /*
199584860c5dSDavid du Colombier * Queue data for writing and return error status from
199684860c5dSDavid du Colombier * last writes done, to maintain buffered data.
199784860c5dSDavid du Colombier */
199884860c5dSDavid du Colombier static long
episowrite(Ep * ep,Isoio * iso,void * a,long count)199984860c5dSDavid du Colombier episowrite(Ep *ep, Isoio *iso, void *a, long count)
200084860c5dSDavid du Colombier {
200184860c5dSDavid du Colombier Ctlr *ctlr;
200284860c5dSDavid du Colombier uchar *b;
200384860c5dSDavid du Colombier int tot, nw;
200484860c5dSDavid du Colombier char *err;
200584860c5dSDavid du Colombier
200684860c5dSDavid du Colombier iso->debug = ep->debug;
200784860c5dSDavid du Colombier diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
200884860c5dSDavid du Colombier
200984860c5dSDavid du Colombier ctlr = ep->hp->aux;
201084860c5dSDavid du Colombier qlock(iso);
201184860c5dSDavid du Colombier if(waserror()){
201284860c5dSDavid du Colombier qunlock(iso);
201384860c5dSDavid du Colombier nexterror();
201484860c5dSDavid du Colombier }
201584860c5dSDavid du Colombier ilock(ctlr);
201684860c5dSDavid du Colombier if(iso->state == Qclose){
201784860c5dSDavid du Colombier iunlock(ctlr);
201884860c5dSDavid du Colombier error(iso->err ? iso->err : Eio);
201984860c5dSDavid du Colombier }
202084860c5dSDavid du Colombier iso->state = Qrun;
202184860c5dSDavid du Colombier coherence();
202284860c5dSDavid du Colombier b = a;
202384860c5dSDavid du Colombier for(tot = 0; tot < count; tot += nw){
202484860c5dSDavid du Colombier while(isocanwrite(iso) == 0){
202584860c5dSDavid du Colombier iunlock(ctlr);
202684860c5dSDavid du Colombier diprint("ehci: episowrite: %#p sleep\n", iso);
202784860c5dSDavid du Colombier if(waserror()){
202884860c5dSDavid du Colombier if(iso->err == nil)
202984860c5dSDavid du Colombier iso->err = "I/O timed out";
203084860c5dSDavid du Colombier ilock(ctlr);
203184860c5dSDavid du Colombier break;
203284860c5dSDavid du Colombier }
203384860c5dSDavid du Colombier tsleep(iso, isocanwrite, iso, ep->tmout);
203484860c5dSDavid du Colombier poperror();
203584860c5dSDavid du Colombier ilock(ctlr);
203684860c5dSDavid du Colombier }
203784860c5dSDavid du Colombier err = iso->err;
203884860c5dSDavid du Colombier iso->err = nil;
203984860c5dSDavid du Colombier if(iso->state == Qclose || err != nil){
204084860c5dSDavid du Colombier iunlock(ctlr);
204184860c5dSDavid du Colombier error(err ? err : Eio);
204284860c5dSDavid du Colombier }
204384860c5dSDavid du Colombier if(iso->state != Qrun)
204484860c5dSDavid du Colombier panic("episowrite: iso not running");
204584860c5dSDavid du Colombier iunlock(ctlr); /* We could page fault here */
204684860c5dSDavid du Colombier nw = putsamples(iso, b+tot, count-tot);
204784860c5dSDavid du Colombier ilock(ctlr);
204884860c5dSDavid du Colombier }
204984860c5dSDavid du Colombier if(iso->state != Qclose)
205084860c5dSDavid du Colombier iso->state = Qdone;
205184860c5dSDavid du Colombier iunlock(ctlr);
205284860c5dSDavid du Colombier err = iso->err; /* in case it failed early */
205384860c5dSDavid du Colombier iso->err = nil;
205484860c5dSDavid du Colombier qunlock(iso);
205584860c5dSDavid du Colombier poperror();
205684860c5dSDavid du Colombier if(err != nil)
205784860c5dSDavid du Colombier error(err);
205884860c5dSDavid du Colombier diprint("ehci: episowrite: %#p %d bytes\n", iso, tot);
205984860c5dSDavid du Colombier return tot;
206084860c5dSDavid du Colombier }
206184860c5dSDavid du Colombier
206284860c5dSDavid du Colombier static int
nexttoggle(int toggle,int count,int maxpkt)206384860c5dSDavid du Colombier nexttoggle(int toggle, int count, int maxpkt)
206484860c5dSDavid du Colombier {
206584860c5dSDavid du Colombier int np;
206684860c5dSDavid du Colombier
206784860c5dSDavid du Colombier np = count / maxpkt;
206884860c5dSDavid du Colombier if(np == 0)
206984860c5dSDavid du Colombier np = 1;
207084860c5dSDavid du Colombier if((np % 2) == 0)
207184860c5dSDavid du Colombier return toggle;
207284860c5dSDavid du Colombier if(toggle == Tddata1)
207384860c5dSDavid du Colombier return Tddata0;
207484860c5dSDavid du Colombier else
207584860c5dSDavid du Colombier return Tddata1;
207684860c5dSDavid du Colombier }
207784860c5dSDavid du Colombier
207884860c5dSDavid du Colombier static Td*
epgettd(Qio * io,int flags,void * a,int count,int maxpkt)207984860c5dSDavid du Colombier epgettd(Qio *io, int flags, void *a, int count, int maxpkt)
208084860c5dSDavid du Colombier {
208184860c5dSDavid du Colombier Td *td;
208284860c5dSDavid du Colombier ulong pa;
208384860c5dSDavid du Colombier int i;
208484860c5dSDavid du Colombier
208584860c5dSDavid du Colombier if(count > Tdmaxpkt)
208684860c5dSDavid du Colombier panic("ehci: epgettd: too many bytes");
208784860c5dSDavid du Colombier td = tdalloc();
208884860c5dSDavid du Colombier td->csw = flags | io->toggle | io->tok | count << Tdlenshift |
208984860c5dSDavid du Colombier Tderr2 | Tderr1;
209084860c5dSDavid du Colombier
209184860c5dSDavid du Colombier /*
209284860c5dSDavid du Colombier * use the space wasted by alignment as an
209384860c5dSDavid du Colombier * embedded buffer if count bytes fit in there.
209484860c5dSDavid du Colombier */
209584860c5dSDavid du Colombier assert(Align > sizeof(Td));
209684860c5dSDavid du Colombier if(count <= Align - sizeof(Td)){
209784860c5dSDavid du Colombier td->data = td->sbuff;
209884860c5dSDavid du Colombier td->buff = nil;
209984860c5dSDavid du Colombier }else
210084860c5dSDavid du Colombier td->data = td->buff = smalloc(Tdmaxpkt);
210184860c5dSDavid du Colombier
210284860c5dSDavid du Colombier pa = PADDR(td->data);
210384860c5dSDavid du Colombier for(i = 0; i < nelem(td->buffer); i++){
210484860c5dSDavid du Colombier td->buffer[i] = pa;
210584860c5dSDavid du Colombier if(i > 0)
210684860c5dSDavid du Colombier td->buffer[i] &= ~0xFFF;
210784860c5dSDavid du Colombier pa += 0x1000;
210884860c5dSDavid du Colombier }
210984860c5dSDavid du Colombier td->ndata = count;
211084860c5dSDavid du Colombier if(a != nil && count > 0)
211184860c5dSDavid du Colombier memmove(td->data, a, count);
211284860c5dSDavid du Colombier coherence();
211384860c5dSDavid du Colombier io->toggle = nexttoggle(io->toggle, count, maxpkt);
211484860c5dSDavid du Colombier coherence();
211584860c5dSDavid du Colombier return td;
211684860c5dSDavid du Colombier }
211784860c5dSDavid du Colombier
211884860c5dSDavid du Colombier /*
211984860c5dSDavid du Colombier * Try to get them idle
212084860c5dSDavid du Colombier */
212184860c5dSDavid du Colombier static void
aborttds(Qh * qh)212284860c5dSDavid du Colombier aborttds(Qh *qh)
212384860c5dSDavid du Colombier {
212484860c5dSDavid du Colombier Td *td;
212584860c5dSDavid du Colombier
212684860c5dSDavid du Colombier qh->state = Qdone;
212784860c5dSDavid du Colombier coherence();
212884860c5dSDavid du Colombier if(qh->sched >= 0 && (qh->eps0 & Qhspeedmask) != Qhhigh)
212984860c5dSDavid du Colombier qh->eps0 |= Qhint; /* inactivate on next pass */
213084860c5dSDavid du Colombier coherence();
213184860c5dSDavid du Colombier for(td = qh->tds; td != nil; td = td->next){
213284860c5dSDavid du Colombier if(td->csw & Tdactive)
213384860c5dSDavid du Colombier td->ndata = 0;
213484860c5dSDavid du Colombier td->csw |= Tdhalt;
213584860c5dSDavid du Colombier coherence();
213684860c5dSDavid du Colombier }
213784860c5dSDavid du Colombier }
213884860c5dSDavid du Colombier
213984860c5dSDavid du Colombier /*
214084860c5dSDavid du Colombier * Some controllers do not post the usb/error interrupt after
214184860c5dSDavid du Colombier * the work has been done. It seems that we must poll for them.
214284860c5dSDavid du Colombier */
214384860c5dSDavid du Colombier static int
workpending(void * a)214484860c5dSDavid du Colombier workpending(void *a)
214584860c5dSDavid du Colombier {
214684860c5dSDavid du Colombier Ctlr *ctlr;
214784860c5dSDavid du Colombier
214884860c5dSDavid du Colombier ctlr = a;
214984860c5dSDavid du Colombier return ctlr->nreqs > 0;
215084860c5dSDavid du Colombier }
215184860c5dSDavid du Colombier
215284860c5dSDavid du Colombier static void
ehcipoll(void * a)215384860c5dSDavid du Colombier ehcipoll(void* a)
215484860c5dSDavid du Colombier {
215584860c5dSDavid du Colombier Hci *hp;
215684860c5dSDavid du Colombier Ctlr *ctlr;
215784860c5dSDavid du Colombier Poll *poll;
215884860c5dSDavid du Colombier int i;
215984860c5dSDavid du Colombier
216084860c5dSDavid du Colombier hp = a;
216184860c5dSDavid du Colombier ctlr = hp->aux;
216284860c5dSDavid du Colombier poll = &ctlr->poll;
216384860c5dSDavid du Colombier for(;;){
216484860c5dSDavid du Colombier if(ctlr->nreqs == 0){
216584860c5dSDavid du Colombier if(0)ddprint("ehcipoll %#p sleep\n", ctlr->capio);
216684860c5dSDavid du Colombier sleep(poll, workpending, ctlr);
216784860c5dSDavid du Colombier if(0)ddprint("ehcipoll %#p awaken\n", ctlr->capio);
216884860c5dSDavid du Colombier }
216984860c5dSDavid du Colombier for(i = 0; i < 16 && ctlr->nreqs > 0; i++)
217084860c5dSDavid du Colombier if(ehciintr(hp) == 0)
217184860c5dSDavid du Colombier break;
217284860c5dSDavid du Colombier do{
217384860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
217484860c5dSDavid du Colombier ehciintr(hp);
217584860c5dSDavid du Colombier }while(ctlr->nreqs > 0);
217684860c5dSDavid du Colombier }
217784860c5dSDavid du Colombier }
217884860c5dSDavid du Colombier
217984860c5dSDavid du Colombier static void
pollcheck(Hci * hp)218084860c5dSDavid du Colombier pollcheck(Hci *hp)
218184860c5dSDavid du Colombier {
218284860c5dSDavid du Colombier Ctlr *ctlr;
218384860c5dSDavid du Colombier Poll *poll;
218484860c5dSDavid du Colombier
218584860c5dSDavid du Colombier ctlr = hp->aux;
218684860c5dSDavid du Colombier poll = &ctlr->poll;
218784860c5dSDavid du Colombier
218884860c5dSDavid du Colombier if(poll->must != 0 && poll->does == 0){
218984860c5dSDavid du Colombier lock(poll);
219084860c5dSDavid du Colombier if(poll->must != 0 && poll->does == 0){
219184860c5dSDavid du Colombier poll->does++;
219284860c5dSDavid du Colombier print("ehci %#p: polling\n", ctlr->capio);
219384860c5dSDavid du Colombier kproc("ehcipoll", ehcipoll, hp);
219484860c5dSDavid du Colombier }
219584860c5dSDavid du Colombier unlock(poll);
219684860c5dSDavid du Colombier }
219784860c5dSDavid du Colombier }
219884860c5dSDavid du Colombier
219984860c5dSDavid du Colombier static int
epiodone(void * a)220084860c5dSDavid du Colombier epiodone(void *a)
220184860c5dSDavid du Colombier {
220284860c5dSDavid du Colombier Qh *qh;
220384860c5dSDavid du Colombier
220484860c5dSDavid du Colombier qh = a;
220584860c5dSDavid du Colombier return qh->state != Qrun;
220684860c5dSDavid du Colombier }
220784860c5dSDavid du Colombier
220884860c5dSDavid du Colombier static void
epiowait(Hci * hp,Qio * io,int tmout,ulong load)220984860c5dSDavid du Colombier epiowait(Hci *hp, Qio *io, int tmout, ulong load)
221084860c5dSDavid du Colombier {
221184860c5dSDavid du Colombier Qh *qh;
221284860c5dSDavid du Colombier int timedout;
221384860c5dSDavid du Colombier Ctlr *ctlr;
221484860c5dSDavid du Colombier
221584860c5dSDavid du Colombier ctlr = hp->aux;
221684860c5dSDavid du Colombier qh = io->qh;
22175a5ba3fdSDavid du Colombier ddqprint("ehci %#p: io %#p sleep on qh %#p state %s\n",
22185a5ba3fdSDavid du Colombier ctlr->capio, io, qh, qhsname[qh->state]);
221984860c5dSDavid du Colombier timedout = 0;
222084860c5dSDavid du Colombier if(waserror()){
22215a5ba3fdSDavid du Colombier dqprint("ehci %#p: io %#p qh %#p timed out\n",
22225a5ba3fdSDavid du Colombier ctlr->capio, io, qh);
222384860c5dSDavid du Colombier timedout++;
222484860c5dSDavid du Colombier }else{
222584860c5dSDavid du Colombier if(tmout == 0)
222684860c5dSDavid du Colombier sleep(io, epiodone, qh);
222784860c5dSDavid du Colombier else
222884860c5dSDavid du Colombier tsleep(io, epiodone, qh, tmout);
222984860c5dSDavid du Colombier poperror();
223084860c5dSDavid du Colombier }
223184860c5dSDavid du Colombier
223284860c5dSDavid du Colombier ilock(ctlr);
223384860c5dSDavid du Colombier /* Are we missing interrupts? */
223484860c5dSDavid du Colombier if(qh->state == Qrun){
223584860c5dSDavid du Colombier iunlock(ctlr);
223684860c5dSDavid du Colombier ehciintr(hp);
223784860c5dSDavid du Colombier ilock(ctlr);
223884860c5dSDavid du Colombier if(qh->state == Qdone){
223984860c5dSDavid du Colombier dqprint("ehci %#p: polling required\n", ctlr->capio);
224084860c5dSDavid du Colombier ctlr->poll.must = 1;
224184860c5dSDavid du Colombier pollcheck(hp);
224284860c5dSDavid du Colombier }
224384860c5dSDavid du Colombier }
224484860c5dSDavid du Colombier
224584860c5dSDavid du Colombier if(qh->state == Qrun){
22465a5ba3fdSDavid du Colombier // dqprint("ehci %#p: io %#p qh %#p timed out (no intr?)\n",
22475a5ba3fdSDavid du Colombier iprint("ehci %#p: io %#p qh %#p timed out (no intr?)\n",
22485a5ba3fdSDavid du Colombier ctlr->capio, io, qh);
224984860c5dSDavid du Colombier timedout = 1;
225084860c5dSDavid du Colombier }else if(qh->state != Qdone && qh->state != Qclose)
225184860c5dSDavid du Colombier panic("ehci: epio: queue state %d", qh->state);
225284860c5dSDavid du Colombier if(timedout){
225384860c5dSDavid du Colombier aborttds(io->qh);
225484860c5dSDavid du Colombier io->err = "request timed out";
225584860c5dSDavid du Colombier iunlock(ctlr);
225684860c5dSDavid du Colombier if(!waserror()){
225784860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
225884860c5dSDavid du Colombier poperror();
225984860c5dSDavid du Colombier }
226084860c5dSDavid du Colombier ilock(ctlr);
226184860c5dSDavid du Colombier }
226284860c5dSDavid du Colombier if(qh->state != Qclose)
226384860c5dSDavid du Colombier qh->state = Qidle;
226484860c5dSDavid du Colombier coherence();
226584860c5dSDavid du Colombier qhlinktd(qh, nil);
226684860c5dSDavid du Colombier ctlr->load -= load;
226784860c5dSDavid du Colombier ctlr->nreqs--;
226884860c5dSDavid du Colombier iunlock(ctlr);
226984860c5dSDavid du Colombier }
227084860c5dSDavid du Colombier
227184860c5dSDavid du Colombier /*
227284860c5dSDavid du Colombier * Non iso I/O.
227384860c5dSDavid du Colombier * To make it work for control transfers, the caller may
227484860c5dSDavid du Colombier * lock the Qio for the entire control transfer.
227584860c5dSDavid du Colombier */
227684860c5dSDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)227784860c5dSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
227884860c5dSDavid du Colombier {
227984860c5dSDavid du Colombier int saved, ntds, tmout;
228084860c5dSDavid du Colombier long n, tot;
228184860c5dSDavid du Colombier ulong load;
228284860c5dSDavid du Colombier char *err;
228384860c5dSDavid du Colombier char buf[128];
228484860c5dSDavid du Colombier uchar *c;
228584860c5dSDavid du Colombier Ctlr *ctlr;
228684860c5dSDavid du Colombier Qh* qh;
228784860c5dSDavid du Colombier Td *td, *ltd, *td0, *ntd;
228884860c5dSDavid du Colombier
228984860c5dSDavid du Colombier qh = io->qh;
229084860c5dSDavid du Colombier ctlr = ep->hp->aux;
229184860c5dSDavid du Colombier io->debug = ep->debug;
229284860c5dSDavid du Colombier tmout = ep->tmout;
229384860c5dSDavid du Colombier ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n",
229484860c5dSDavid du Colombier io->tok == Tdtokin ? "in" : "out",
229584860c5dSDavid du Colombier ep->dev->nb, ep->nb, io, count, ctlr->load);
229684860c5dSDavid du Colombier if((ehcidebug > 1 || ep->debug > 1) && io->tok != Tdtokin){
229784860c5dSDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
229884860c5dSDavid du Colombier print("echi epio: user data: %s\n", buf);
229984860c5dSDavid du Colombier }
230084860c5dSDavid du Colombier if(mustlock){
230184860c5dSDavid du Colombier qlock(io);
230284860c5dSDavid du Colombier if(waserror()){
230384860c5dSDavid du Colombier qunlock(io);
230484860c5dSDavid du Colombier nexterror();
230584860c5dSDavid du Colombier }
230684860c5dSDavid du Colombier }
230784860c5dSDavid du Colombier io->err = nil;
230884860c5dSDavid du Colombier ilock(ctlr);
230984860c5dSDavid du Colombier if(qh->state == Qclose){ /* Tds released by cancelio */
231084860c5dSDavid du Colombier iunlock(ctlr);
231184860c5dSDavid du Colombier error(io->err ? io->err : Eio);
231284860c5dSDavid du Colombier }
231384860c5dSDavid du Colombier if(qh->state != Qidle)
231484860c5dSDavid du Colombier panic("epio: qh not idle");
231584860c5dSDavid du Colombier qh->state = Qinstall;
231684860c5dSDavid du Colombier iunlock(ctlr);
231784860c5dSDavid du Colombier
231884860c5dSDavid du Colombier c = a;
231984860c5dSDavid du Colombier td0 = ltd = nil;
232084860c5dSDavid du Colombier load = tot = 0;
232184860c5dSDavid du Colombier do{
232284860c5dSDavid du Colombier n = (Tdmaxpkt / ep->maxpkt) * ep->maxpkt;
232384860c5dSDavid du Colombier if(count-tot < n)
232484860c5dSDavid du Colombier n = count-tot;
2325d24fba2aSDavid du Colombier if(c != nil && io->tok != Tdtokin)
232684860c5dSDavid du Colombier td = epgettd(io, Tdactive, c+tot, n, ep->maxpkt);
232784860c5dSDavid du Colombier else
232884860c5dSDavid du Colombier td = epgettd(io, Tdactive, nil, n, ep->maxpkt);
232984860c5dSDavid du Colombier if(td0 == nil)
233084860c5dSDavid du Colombier td0 = td;
233184860c5dSDavid du Colombier else
233284860c5dSDavid du Colombier tdlinktd(ltd, td);
233384860c5dSDavid du Colombier ltd = td;
233484860c5dSDavid du Colombier tot += n;
233584860c5dSDavid du Colombier load += ep->load;
233684860c5dSDavid du Colombier }while(tot < count);
233784860c5dSDavid du Colombier if(td0 == nil || ltd == nil)
233884860c5dSDavid du Colombier panic("epio: no td");
233984860c5dSDavid du Colombier
234084860c5dSDavid du Colombier ltd->csw |= Tdioc; /* the last one interrupts */
234184860c5dSDavid du Colombier coherence();
234284860c5dSDavid du Colombier
234384860c5dSDavid du Colombier ddeprint("ehci: load %uld ctlr load %uld\n", load, ctlr->load);
234484860c5dSDavid du Colombier if(ehcidebug > 1 || ep->debug > 1)
234584860c5dSDavid du Colombier dumptd(td0, "epio: put: ");
234684860c5dSDavid du Colombier
234784860c5dSDavid du Colombier ilock(ctlr);
234884860c5dSDavid du Colombier if(qh->state != Qclose){
234984860c5dSDavid du Colombier io->iotime = TK2MS(MACHP(0)->ticks);
235084860c5dSDavid du Colombier qh->state = Qrun;
235184860c5dSDavid du Colombier coherence();
235284860c5dSDavid du Colombier qhlinktd(qh, td0);
235384860c5dSDavid du Colombier ctlr->nreqs++;
235484860c5dSDavid du Colombier ctlr->load += load;
235584860c5dSDavid du Colombier }
235684860c5dSDavid du Colombier iunlock(ctlr);
235784860c5dSDavid du Colombier
235884860c5dSDavid du Colombier if(ctlr->poll.does)
235984860c5dSDavid du Colombier wakeup(&ctlr->poll);
236084860c5dSDavid du Colombier
236184860c5dSDavid du Colombier epiowait(ep->hp, io, tmout, load);
236284860c5dSDavid du Colombier if(ehcidebug > 1 || ep->debug > 1){
236384860c5dSDavid du Colombier dumptd(td0, "epio: got: ");
236484860c5dSDavid du Colombier qhdump(qh);
236584860c5dSDavid du Colombier }
236684860c5dSDavid du Colombier
236784860c5dSDavid du Colombier tot = 0;
236884860c5dSDavid du Colombier c = a;
236984860c5dSDavid du Colombier saved = 0;
237084860c5dSDavid du Colombier ntds = 0;
237184860c5dSDavid du Colombier for(td = td0; td != nil; td = ntd){
237284860c5dSDavid du Colombier ntds++;
237384860c5dSDavid du Colombier /*
237484860c5dSDavid du Colombier * Use td tok, not io tok, because of setup packets.
23752a782499SDavid du Colombier * Also, we must save the next toggle value from the
23762a782499SDavid du Colombier * last completed Td (in case of a short packet, or
23772a782499SDavid du Colombier * fewer than the requested number of packets in the
23782a782499SDavid du Colombier * Td being transferred).
237984860c5dSDavid du Colombier */
23802a782499SDavid du Colombier if(td->csw & (Tdhalt|Tdactive))
23812a782499SDavid du Colombier saved++;
23822a782499SDavid du Colombier else{
23832a782499SDavid du Colombier if(!saved){
238484860c5dSDavid du Colombier io->toggle = td->csw & Tddata1;
238584860c5dSDavid du Colombier coherence();
238684860c5dSDavid du Colombier }
238784860c5dSDavid du Colombier tot += td->ndata;
2388d24fba2aSDavid du Colombier if(c != nil && (td->csw & Tdtok) == Tdtokin && td->ndata > 0){
238984860c5dSDavid du Colombier memmove(c, td->data, td->ndata);
239084860c5dSDavid du Colombier c += td->ndata;
239184860c5dSDavid du Colombier }
239284860c5dSDavid du Colombier }
239384860c5dSDavid du Colombier ntd = td->next;
239484860c5dSDavid du Colombier tdfree(td);
239584860c5dSDavid du Colombier }
239684860c5dSDavid du Colombier err = io->err;
239784860c5dSDavid du Colombier if(mustlock){
239884860c5dSDavid du Colombier qunlock(io);
239984860c5dSDavid du Colombier poperror();
240084860c5dSDavid du Colombier }
240184860c5dSDavid du Colombier ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n",
240284860c5dSDavid du Colombier io, ntds, tot, err);
240384860c5dSDavid du Colombier if(err == Estalled)
240484860c5dSDavid du Colombier return 0; /* that's our convention */
240584860c5dSDavid du Colombier if(err != nil)
240684860c5dSDavid du Colombier error(err);
240784860c5dSDavid du Colombier if(tot < 0)
240884860c5dSDavid du Colombier error(Eio);
240984860c5dSDavid du Colombier return tot;
241084860c5dSDavid du Colombier }
241184860c5dSDavid du Colombier
241284860c5dSDavid du Colombier static long
epread(Ep * ep,void * a,long count)241384860c5dSDavid du Colombier epread(Ep *ep, void *a, long count)
241484860c5dSDavid du Colombier {
241584860c5dSDavid du Colombier Ctlio *cio;
241684860c5dSDavid du Colombier Qio *io;
241784860c5dSDavid du Colombier Isoio *iso;
241884860c5dSDavid du Colombier char buf[160];
241984860c5dSDavid du Colombier ulong delta;
242084860c5dSDavid du Colombier
242184860c5dSDavid du Colombier ddeprint("ehci: epread\n");
242284860c5dSDavid du Colombier if(ep->aux == nil)
242384860c5dSDavid du Colombier panic("epread: not open");
242484860c5dSDavid du Colombier
242584860c5dSDavid du Colombier pollcheck(ep->hp);
242684860c5dSDavid du Colombier
242784860c5dSDavid du Colombier switch(ep->ttype){
242884860c5dSDavid du Colombier case Tctl:
242984860c5dSDavid du Colombier cio = ep->aux;
243084860c5dSDavid du Colombier qlock(cio);
243184860c5dSDavid du Colombier if(waserror()){
243284860c5dSDavid du Colombier qunlock(cio);
243384860c5dSDavid du Colombier nexterror();
243484860c5dSDavid du Colombier }
243584860c5dSDavid du Colombier ddeprint("epread ctl ndata %d\n", cio->ndata);
243684860c5dSDavid du Colombier if(cio->ndata < 0)
243784860c5dSDavid du Colombier error("request expected");
243884860c5dSDavid du Colombier else if(cio->ndata == 0){
243984860c5dSDavid du Colombier cio->ndata = -1;
244084860c5dSDavid du Colombier count = 0;
244184860c5dSDavid du Colombier }else{
244284860c5dSDavid du Colombier if(count > cio->ndata)
244384860c5dSDavid du Colombier count = cio->ndata;
244484860c5dSDavid du Colombier if(count > 0)
244584860c5dSDavid du Colombier memmove(a, cio->data, count);
244684860c5dSDavid du Colombier /* BUG for big transfers */
244784860c5dSDavid du Colombier free(cio->data);
244884860c5dSDavid du Colombier cio->data = nil;
244984860c5dSDavid du Colombier cio->ndata = 0; /* signal EOF next time */
245084860c5dSDavid du Colombier }
245184860c5dSDavid du Colombier qunlock(cio);
245284860c5dSDavid du Colombier poperror();
245384860c5dSDavid du Colombier if(ehcidebug>1 || ep->debug){
245484860c5dSDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
245584860c5dSDavid du Colombier print("epread: %s\n", buf);
245684860c5dSDavid du Colombier }
245784860c5dSDavid du Colombier return count;
245884860c5dSDavid du Colombier case Tbulk:
245984860c5dSDavid du Colombier io = ep->aux;
246084860c5dSDavid du Colombier if(ep->clrhalt)
246184860c5dSDavid du Colombier clrhalt(ep);
246284860c5dSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
246384860c5dSDavid du Colombier case Tintr:
246484860c5dSDavid du Colombier io = ep->aux;
246584860c5dSDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
246684860c5dSDavid du Colombier if(delta < ep->pollival / 2)
246784860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
246884860c5dSDavid du Colombier if(ep->clrhalt)
246984860c5dSDavid du Colombier clrhalt(ep);
247084860c5dSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
247184860c5dSDavid du Colombier case Tiso:
247284860c5dSDavid du Colombier iso = ep->aux;
247384860c5dSDavid du Colombier return episoread(ep, iso, a, count);
247484860c5dSDavid du Colombier }
247584860c5dSDavid du Colombier return -1;
247684860c5dSDavid du Colombier }
247784860c5dSDavid du Colombier
247884860c5dSDavid du Colombier /*
247984860c5dSDavid du Colombier * Control transfers are one setup write (data0)
248084860c5dSDavid du Colombier * plus zero or more reads/writes (data1, data0, ...)
248184860c5dSDavid du Colombier * plus a final write/read with data1 to ack.
248284860c5dSDavid du Colombier * For both host to device and device to host we perform
248384860c5dSDavid du Colombier * the entire transfer when the user writes the request,
248484860c5dSDavid du Colombier * and keep any data read from the device for a later read.
248584860c5dSDavid du Colombier * We call epio three times instead of placing all Tds at
248684860c5dSDavid du Colombier * the same time because doing so leads to crc/tmout errors
248784860c5dSDavid du Colombier * for some devices.
248884860c5dSDavid du Colombier * Upon errors on the data phase we must still run the status
248984860c5dSDavid du Colombier * phase or the device may cease responding in the future.
249084860c5dSDavid du Colombier */
249184860c5dSDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)249284860c5dSDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
249384860c5dSDavid du Colombier {
249484860c5dSDavid du Colombier uchar *c;
249584860c5dSDavid du Colombier long len;
249684860c5dSDavid du Colombier
249784860c5dSDavid du Colombier ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
249884860c5dSDavid du Colombier cio, ep->dev->nb, ep->nb, count);
249984860c5dSDavid du Colombier if(count < Rsetuplen)
250084860c5dSDavid du Colombier error("short usb comand");
250184860c5dSDavid du Colombier qlock(cio);
250284860c5dSDavid du Colombier free(cio->data);
250384860c5dSDavid du Colombier cio->data = nil;
250484860c5dSDavid du Colombier cio->ndata = 0;
250584860c5dSDavid du Colombier if(waserror()){
250684860c5dSDavid du Colombier free(cio->data);
250784860c5dSDavid du Colombier cio->data = nil;
250884860c5dSDavid du Colombier cio->ndata = 0;
250984860c5dSDavid du Colombier qunlock(cio);
251084860c5dSDavid du Colombier nexterror();
251184860c5dSDavid du Colombier }
251284860c5dSDavid du Colombier
251384860c5dSDavid du Colombier /* set the address if unset and out of configuration state */
251484860c5dSDavid du Colombier if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
251584860c5dSDavid du Colombier if(cio->usbid == 0){
251684860c5dSDavid du Colombier cio->usbid = (ep->nb&Epmax) << 7 | ep->dev->nb&Devmax;
251784860c5dSDavid du Colombier coherence();
251884860c5dSDavid du Colombier qhsetaddr(cio->qh, cio->usbid);
251984860c5dSDavid du Colombier }
252084860c5dSDavid du Colombier /* adjust maxpkt if the user has learned a different one */
252184860c5dSDavid du Colombier if(qhmaxpkt(cio->qh) != ep->maxpkt)
252284860c5dSDavid du Colombier qhsetmaxpkt(cio->qh, ep->maxpkt);
252384860c5dSDavid du Colombier c = a;
252484860c5dSDavid du Colombier cio->tok = Tdtoksetup;
252584860c5dSDavid du Colombier cio->toggle = Tddata0;
252684860c5dSDavid du Colombier coherence();
252784860c5dSDavid du Colombier if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
252884860c5dSDavid du Colombier error(Eio);
252984860c5dSDavid du Colombier a = c + Rsetuplen;
253084860c5dSDavid du Colombier count -= Rsetuplen;
253184860c5dSDavid du Colombier
253284860c5dSDavid du Colombier cio->toggle = Tddata1;
253384860c5dSDavid du Colombier if(c[Rtype] & Rd2h){
253484860c5dSDavid du Colombier cio->tok = Tdtokin;
253584860c5dSDavid du Colombier len = GET2(c+Rcount);
253684860c5dSDavid du Colombier if(len <= 0)
253784860c5dSDavid du Colombier error("bad length in d2h request");
253884860c5dSDavid du Colombier if(len > Maxctllen)
253984860c5dSDavid du Colombier error("d2h data too large to fit in ehci");
254084860c5dSDavid du Colombier a = cio->data = smalloc(len+1);
254184860c5dSDavid du Colombier }else{
254284860c5dSDavid du Colombier cio->tok = Tdtokout;
254384860c5dSDavid du Colombier len = count;
254484860c5dSDavid du Colombier }
254584860c5dSDavid du Colombier coherence();
254684860c5dSDavid du Colombier if(len > 0)
254784860c5dSDavid du Colombier if(waserror())
254884860c5dSDavid du Colombier len = -1;
254984860c5dSDavid du Colombier else{
255084860c5dSDavid du Colombier len = epio(ep, cio, a, len, 0);
255184860c5dSDavid du Colombier poperror();
255284860c5dSDavid du Colombier }
255384860c5dSDavid du Colombier if(c[Rtype] & Rd2h){
255484860c5dSDavid du Colombier count = Rsetuplen;
255584860c5dSDavid du Colombier cio->ndata = len;
255684860c5dSDavid du Colombier cio->tok = Tdtokout;
255784860c5dSDavid du Colombier }else{
255884860c5dSDavid du Colombier if(len < 0)
255984860c5dSDavid du Colombier count = -1;
256084860c5dSDavid du Colombier else
256184860c5dSDavid du Colombier count = Rsetuplen + len;
256284860c5dSDavid du Colombier cio->tok = Tdtokin;
256384860c5dSDavid du Colombier }
256484860c5dSDavid du Colombier cio->toggle = Tddata1;
256584860c5dSDavid du Colombier coherence();
256684860c5dSDavid du Colombier epio(ep, cio, nil, 0, 0);
256784860c5dSDavid du Colombier qunlock(cio);
256884860c5dSDavid du Colombier poperror();
256984860c5dSDavid du Colombier ddeprint("epctlio cio %#p return %ld\n", cio, count);
257084860c5dSDavid du Colombier return count;
257184860c5dSDavid du Colombier }
257284860c5dSDavid du Colombier
257384860c5dSDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)257484860c5dSDavid du Colombier epwrite(Ep *ep, void *a, long count)
257584860c5dSDavid du Colombier {
257684860c5dSDavid du Colombier Qio *io;
257784860c5dSDavid du Colombier Ctlio *cio;
257884860c5dSDavid du Colombier Isoio *iso;
257984860c5dSDavid du Colombier ulong delta;
258084860c5dSDavid du Colombier
258184860c5dSDavid du Colombier pollcheck(ep->hp);
258284860c5dSDavid du Colombier
258384860c5dSDavid du Colombier ddeprint("ehci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
258484860c5dSDavid du Colombier if(ep->aux == nil)
258584860c5dSDavid du Colombier panic("ehci: epwrite: not open");
258684860c5dSDavid du Colombier switch(ep->ttype){
258784860c5dSDavid du Colombier case Tctl:
258884860c5dSDavid du Colombier cio = ep->aux;
258984860c5dSDavid du Colombier return epctlio(ep, cio, a, count);
259084860c5dSDavid du Colombier case Tbulk:
259184860c5dSDavid du Colombier io = ep->aux;
259284860c5dSDavid du Colombier if(ep->clrhalt)
259384860c5dSDavid du Colombier clrhalt(ep);
259484860c5dSDavid du Colombier return epio(ep, &io[OWRITE], a, count, 1);
259584860c5dSDavid du Colombier case Tintr:
259684860c5dSDavid du Colombier io = ep->aux;
259784860c5dSDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
259884860c5dSDavid du Colombier if(delta < ep->pollival)
259984860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - delta);
260084860c5dSDavid du Colombier if(ep->clrhalt)
260184860c5dSDavid du Colombier clrhalt(ep);
260284860c5dSDavid du Colombier return epio(ep, &io[OWRITE], a, count, 1);
260384860c5dSDavid du Colombier case Tiso:
260484860c5dSDavid du Colombier iso = ep->aux;
260584860c5dSDavid du Colombier return episowrite(ep, iso, a, count);
260684860c5dSDavid du Colombier }
260784860c5dSDavid du Colombier return -1;
260884860c5dSDavid du Colombier }
260984860c5dSDavid du Colombier
261084860c5dSDavid du Colombier static void
isofsinit(Ep * ep,Isoio * iso)261184860c5dSDavid du Colombier isofsinit(Ep *ep, Isoio *iso)
261284860c5dSDavid du Colombier {
261384860c5dSDavid du Colombier long left;
261484860c5dSDavid du Colombier Sitd *td, *ltd;
261584860c5dSDavid du Colombier int i;
261684860c5dSDavid du Colombier ulong frno;
261784860c5dSDavid du Colombier
261884860c5dSDavid du Colombier left = 0;
261984860c5dSDavid du Colombier ltd = nil;
262084860c5dSDavid du Colombier frno = iso->td0frno;
262184860c5dSDavid du Colombier for(i = 0; i < iso->nframes; i++){
262284860c5dSDavid du Colombier td = sitdalloc();
262384860c5dSDavid du Colombier td->data = iso->data + i * ep->maxpkt;
262484860c5dSDavid du Colombier td->epc = ep->dev->port << Stdportshift;
262584860c5dSDavid du Colombier td->epc |= ep->dev->hub << Stdhubshift;
262684860c5dSDavid du Colombier td->epc |= ep->nb << Stdepshift;
262784860c5dSDavid du Colombier td->epc |= ep->dev->nb << Stddevshift;
262884860c5dSDavid du Colombier td->mfs = 034 << Stdscmshift | 1 << Stdssmshift;
262984860c5dSDavid du Colombier if(ep->mode == OREAD){
263084860c5dSDavid du Colombier td->epc |= Stdin;
263184860c5dSDavid du Colombier td->mdata = ep->maxpkt;
263284860c5dSDavid du Colombier }else{
263384860c5dSDavid du Colombier td->mdata = (ep->hz+left) * ep->pollival / 1000;
263484860c5dSDavid du Colombier td->mdata *= ep->samplesz;
263584860c5dSDavid du Colombier left = (ep->hz+left) * ep->pollival % 1000;
263684860c5dSDavid du Colombier if(td->mdata > ep->maxpkt){
263784860c5dSDavid du Colombier print("ehci: ep%d.%d: size > maxpkt\n",
263884860c5dSDavid du Colombier ep->dev->nb, ep->nb);
263984860c5dSDavid du Colombier print("size = %ld max = %ld\n",
264084860c5dSDavid du Colombier td->mdata,ep->maxpkt);
264184860c5dSDavid du Colombier td->mdata = ep->maxpkt;
264284860c5dSDavid du Colombier }
264384860c5dSDavid du Colombier }
264484860c5dSDavid du Colombier coherence();
264584860c5dSDavid du Colombier
264684860c5dSDavid du Colombier iso->sitdps[frno] = td;
264784860c5dSDavid du Colombier coherence();
264884860c5dSDavid du Colombier sitdinit(iso, td);
264984860c5dSDavid du Colombier if(ltd != nil)
265084860c5dSDavid du Colombier ltd->next = td;
265184860c5dSDavid du Colombier ltd = td;
265284860c5dSDavid du Colombier frno = TRUNC(frno+ep->pollival, Nisoframes);
265384860c5dSDavid du Colombier }
265484860c5dSDavid du Colombier ltd->next = iso->sitdps[iso->td0frno];
265584860c5dSDavid du Colombier coherence();
265684860c5dSDavid du Colombier }
265784860c5dSDavid du Colombier
265884860c5dSDavid du Colombier static void
isohsinit(Ep * ep,Isoio * iso)265984860c5dSDavid du Colombier isohsinit(Ep *ep, Isoio *iso)
266084860c5dSDavid du Colombier {
266184860c5dSDavid du Colombier int ival, p;
266284860c5dSDavid du Colombier long left;
266384860c5dSDavid du Colombier ulong frno, i, pa;
266484860c5dSDavid du Colombier Itd *ltd, *td;
266584860c5dSDavid du Colombier
266684860c5dSDavid du Colombier iso->hs = 1;
266784860c5dSDavid du Colombier ival = 1;
266884860c5dSDavid du Colombier if(ep->pollival > 8)
266984860c5dSDavid du Colombier ival = ep->pollival/8;
267084860c5dSDavid du Colombier left = 0;
267184860c5dSDavid du Colombier ltd = nil;
267284860c5dSDavid du Colombier frno = iso->td0frno;
267384860c5dSDavid du Colombier for(i = 0; i < iso->nframes; i++){
267484860c5dSDavid du Colombier td = itdalloc();
267584860c5dSDavid du Colombier td->data = iso->data + i * 8 * iso->maxsize;
267684860c5dSDavid du Colombier pa = PADDR(td->data) & ~0xFFF;
267784860c5dSDavid du Colombier for(p = 0; p < 8; p++)
267884860c5dSDavid du Colombier td->buffer[i] = pa + p * 0x1000;
267984860c5dSDavid du Colombier td->buffer[0] = PADDR(iso->data) & ~0xFFF |
268084860c5dSDavid du Colombier ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
268184860c5dSDavid du Colombier if(ep->mode == OREAD)
268284860c5dSDavid du Colombier td->buffer[1] |= Itdin;
268384860c5dSDavid du Colombier else
268484860c5dSDavid du Colombier td->buffer[1] |= Itdout;
268584860c5dSDavid du Colombier td->buffer[1] |= ep->maxpkt << Itdmaxpktshift;
268684860c5dSDavid du Colombier td->buffer[2] |= ep->ntds << Itdntdsshift;
268784860c5dSDavid du Colombier
268884860c5dSDavid du Colombier if(ep->mode == OREAD)
268984860c5dSDavid du Colombier td->mdata = 8 * iso->maxsize;
269084860c5dSDavid du Colombier else{
269184860c5dSDavid du Colombier td->mdata = (ep->hz + left) * ep->pollival / 1000;
269284860c5dSDavid du Colombier td->mdata *= ep->samplesz;
269384860c5dSDavid du Colombier left = (ep->hz + left) * ep->pollival % 1000;
269484860c5dSDavid du Colombier }
269584860c5dSDavid du Colombier coherence();
269684860c5dSDavid du Colombier iso->itdps[frno] = td;
269784860c5dSDavid du Colombier coherence();
269884860c5dSDavid du Colombier itdinit(iso, td);
269984860c5dSDavid du Colombier if(ltd != nil)
270084860c5dSDavid du Colombier ltd->next = td;
270184860c5dSDavid du Colombier ltd = td;
270284860c5dSDavid du Colombier frno = TRUNC(frno + ival, Nisoframes);
270384860c5dSDavid du Colombier }
270484860c5dSDavid du Colombier }
270584860c5dSDavid du Colombier
270684860c5dSDavid du Colombier static void
isoopen(Ctlr * ctlr,Ep * ep)270784860c5dSDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
270884860c5dSDavid du Colombier {
270984860c5dSDavid du Colombier int ival; /* pollival in ms */
271084860c5dSDavid du Colombier int tpf; /* tds per frame */
271184860c5dSDavid du Colombier int i, n, w, woff;
271284860c5dSDavid du Colombier ulong frno;
271384860c5dSDavid du Colombier Isoio *iso;
271484860c5dSDavid du Colombier
271584860c5dSDavid du Colombier iso = ep->aux;
271684860c5dSDavid du Colombier switch(ep->mode){
271784860c5dSDavid du Colombier case OREAD:
271884860c5dSDavid du Colombier iso->tok = Tdtokin;
271984860c5dSDavid du Colombier break;
272084860c5dSDavid du Colombier case OWRITE:
272184860c5dSDavid du Colombier iso->tok = Tdtokout;
272284860c5dSDavid du Colombier break;
272384860c5dSDavid du Colombier default:
272484860c5dSDavid du Colombier error("iso i/o is half-duplex");
272584860c5dSDavid du Colombier }
272684860c5dSDavid du Colombier iso->usbid = ep->nb << 7 | ep->dev->nb & Devmax;
272784860c5dSDavid du Colombier iso->state = Qidle;
272884860c5dSDavid du Colombier coherence();
272984860c5dSDavid du Colombier iso->debug = ep->debug;
273084860c5dSDavid du Colombier ival = ep->pollival;
273184860c5dSDavid du Colombier tpf = 1;
273284860c5dSDavid du Colombier if(ep->dev->speed == Highspeed){
273384860c5dSDavid du Colombier tpf = 8;
273484860c5dSDavid du Colombier if(ival <= 8)
273584860c5dSDavid du Colombier ival = 1;
273684860c5dSDavid du Colombier else
273784860c5dSDavid du Colombier ival /= 8;
273884860c5dSDavid du Colombier }
273984860c5dSDavid du Colombier assert(ival != 0);
274084860c5dSDavid du Colombier iso->nframes = Nisoframes / ival;
274184860c5dSDavid du Colombier if(iso->nframes < 3)
274284860c5dSDavid du Colombier error("uhci isoopen bug"); /* we need at least 3 tds */
274384860c5dSDavid du Colombier iso->maxsize = ep->ntds * ep->maxpkt;
274484860c5dSDavid du Colombier if(ctlr->load + ep->load > 800)
274584860c5dSDavid du Colombier print("usb: ehci: bandwidth may be exceeded\n");
274684860c5dSDavid du Colombier ilock(ctlr);
274784860c5dSDavid du Colombier ctlr->load += ep->load;
274884860c5dSDavid du Colombier ctlr->isoload += ep->load;
274984860c5dSDavid du Colombier ctlr->nreqs++;
275084860c5dSDavid du Colombier dprint("ehci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload);
275184860c5dSDavid du Colombier diprint("iso nframes %d pollival %uld ival %d maxpkt %uld ntds %d\n",
275284860c5dSDavid du Colombier iso->nframes, ep->pollival, ival, ep->maxpkt, ep->ntds);
275384860c5dSDavid du Colombier iunlock(ctlr);
275484860c5dSDavid du Colombier if(ctlr->poll.does)
275584860c5dSDavid du Colombier wakeup(&ctlr->poll);
275684860c5dSDavid du Colombier
275784860c5dSDavid du Colombier /*
275884860c5dSDavid du Colombier * From here on this cannot raise errors
275984860c5dSDavid du Colombier * unless we catch them and release here all memory allocated.
276084860c5dSDavid du Colombier */
276184860c5dSDavid du Colombier assert(ep->maxpkt > 0 && ep->ntds > 0 && ep->ntds < 4);
276284860c5dSDavid du Colombier assert(ep->maxpkt <= 1024);
276384860c5dSDavid du Colombier iso->tdps = smalloc(sizeof(uintptr) * Nisoframes);
276484860c5dSDavid du Colombier iso->data = smalloc(iso->nframes * tpf * ep->ntds * ep->maxpkt);
276584860c5dSDavid du Colombier iso->td0frno = TRUNC(ctlr->opio->frno + 10, Nisoframes);
276684860c5dSDavid du Colombier /* read: now; write: 1s ahead */
276784860c5dSDavid du Colombier
276884860c5dSDavid du Colombier if(ep->dev->speed == Highspeed)
276984860c5dSDavid du Colombier isohsinit(ep, iso);
277084860c5dSDavid du Colombier else
277184860c5dSDavid du Colombier isofsinit(ep, iso);
277284860c5dSDavid du Colombier iso->tdu = iso->tdi = iso->itdps[iso->td0frno];
277384860c5dSDavid du Colombier iso->stdu = iso->stdi = iso->sitdps[iso->td0frno];
277484860c5dSDavid du Colombier coherence();
277584860c5dSDavid du Colombier
277684860c5dSDavid du Colombier ilock(ctlr);
277784860c5dSDavid du Colombier frno = iso->td0frno;
277884860c5dSDavid du Colombier for(i = 0; i < iso->nframes; i++){
277984860c5dSDavid du Colombier *iso->tdps[frno] = ctlr->frames[frno];
278084860c5dSDavid du Colombier frno = TRUNC(frno+ival, Nisoframes);
278184860c5dSDavid du Colombier }
278284860c5dSDavid du Colombier
278384860c5dSDavid du Colombier /*
278484860c5dSDavid du Colombier * Iso uses a virtual frame window of Nisoframes, and we must
278584860c5dSDavid du Colombier * fill the actual ctlr frame array by placing ctlr->nframes/Nisoframes
278684860c5dSDavid du Colombier * copies of the window in the frame array.
278784860c5dSDavid du Colombier */
278884860c5dSDavid du Colombier assert(ctlr->nframes >= Nisoframes && Nisoframes >= iso->nframes);
278984860c5dSDavid du Colombier assert(Nisoframes >= Nintrleafs);
279084860c5dSDavid du Colombier n = ctlr->nframes / Nisoframes;
279184860c5dSDavid du Colombier for(w = 0; w < n; w++){
279284860c5dSDavid du Colombier frno = iso->td0frno;
279384860c5dSDavid du Colombier woff = w * Nisoframes;
279484860c5dSDavid du Colombier for(i = 0; i < iso->nframes ; i++){
279584860c5dSDavid du Colombier assert(woff+frno < ctlr->nframes);
279684860c5dSDavid du Colombier assert(iso->tdps[frno] != nil);
279784860c5dSDavid du Colombier if(ep->dev->speed == Highspeed)
279884860c5dSDavid du Colombier ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])
279984860c5dSDavid du Colombier |Litd;
280084860c5dSDavid du Colombier else
280184860c5dSDavid du Colombier ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])
280284860c5dSDavid du Colombier |Lsitd;
280384860c5dSDavid du Colombier coherence();
280484860c5dSDavid du Colombier frno = TRUNC(frno+ep->pollival, Nisoframes);
280584860c5dSDavid du Colombier }
280684860c5dSDavid du Colombier }
280784860c5dSDavid du Colombier coherence();
280884860c5dSDavid du Colombier iso->next = ctlr->iso;
280984860c5dSDavid du Colombier ctlr->iso = iso;
281084860c5dSDavid du Colombier coherence();
281184860c5dSDavid du Colombier iso->state = Qdone;
281284860c5dSDavid du Colombier iunlock(ctlr);
281384860c5dSDavid du Colombier if(ehcidebug > 1 || iso->debug >1)
281484860c5dSDavid du Colombier isodump(iso, 0);
281584860c5dSDavid du Colombier }
281684860c5dSDavid du Colombier
281784860c5dSDavid du Colombier /*
281884860c5dSDavid du Colombier * Allocate the endpoint and set it up for I/O
281984860c5dSDavid du Colombier * in the controller. This must follow what's said
282084860c5dSDavid du Colombier * in Ep regarding configuration, including perhaps
282184860c5dSDavid du Colombier * the saved toggles (saved on a previous close of
282284860c5dSDavid du Colombier * the endpoint data file by epclose).
282384860c5dSDavid du Colombier */
282484860c5dSDavid du Colombier static void
epopen(Ep * ep)282584860c5dSDavid du Colombier epopen(Ep *ep)
282684860c5dSDavid du Colombier {
282784860c5dSDavid du Colombier Ctlr *ctlr;
282884860c5dSDavid du Colombier Ctlio *cio;
282984860c5dSDavid du Colombier Qio *io;
283084860c5dSDavid du Colombier int usbid;
283184860c5dSDavid du Colombier
283284860c5dSDavid du Colombier ctlr = ep->hp->aux;
283384860c5dSDavid du Colombier deprint("ehci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
283484860c5dSDavid du Colombier if(ep->aux != nil)
283584860c5dSDavid du Colombier panic("ehci: epopen called with open ep");
283684860c5dSDavid du Colombier if(waserror()){
283784860c5dSDavid du Colombier free(ep->aux);
283884860c5dSDavid du Colombier ep->aux = nil;
283984860c5dSDavid du Colombier nexterror();
284084860c5dSDavid du Colombier }
284184860c5dSDavid du Colombier switch(ep->ttype){
284284860c5dSDavid du Colombier case Tnone:
284384860c5dSDavid du Colombier error("endpoint not configured");
284484860c5dSDavid du Colombier case Tiso:
284584860c5dSDavid du Colombier ep->aux = smalloc(sizeof(Isoio));
284684860c5dSDavid du Colombier isoopen(ctlr, ep);
284784860c5dSDavid du Colombier break;
284884860c5dSDavid du Colombier case Tctl:
284984860c5dSDavid du Colombier cio = ep->aux = smalloc(sizeof(Ctlio));
285084860c5dSDavid du Colombier cio->debug = ep->debug;
285184860c5dSDavid du Colombier cio->ndata = -1;
285284860c5dSDavid du Colombier cio->data = nil;
285384860c5dSDavid du Colombier if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
285484860c5dSDavid du Colombier break;
285584860c5dSDavid du Colombier cio->qh = qhalloc(ctlr, ep, cio, "epc");
285684860c5dSDavid du Colombier break;
285784860c5dSDavid du Colombier case Tbulk:
285884860c5dSDavid du Colombier ep->pollival = 1; /* assume this; doesn't really matter */
285984860c5dSDavid du Colombier /* and fall... */
286084860c5dSDavid du Colombier case Tintr:
286184860c5dSDavid du Colombier io = ep->aux = smalloc(sizeof(Qio)*2);
286284860c5dSDavid du Colombier io[OREAD].debug = io[OWRITE].debug = ep->debug;
286384860c5dSDavid du Colombier usbid = (ep->nb&Epmax) << 7 | ep->dev->nb &Devmax;
286484860c5dSDavid du Colombier assert(ep->pollival != 0);
286584860c5dSDavid du Colombier if(ep->mode != OREAD){
286684860c5dSDavid du Colombier if(ep->toggle[OWRITE] != 0)
286784860c5dSDavid du Colombier io[OWRITE].toggle = Tddata1;
286884860c5dSDavid du Colombier else
286984860c5dSDavid du Colombier io[OWRITE].toggle = Tddata0;
287084860c5dSDavid du Colombier io[OWRITE].tok = Tdtokout;
287184860c5dSDavid du Colombier io[OWRITE].usbid = usbid;
287284860c5dSDavid du Colombier io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
287384860c5dSDavid du Colombier io[OWRITE].qh = qhalloc(ctlr, ep, io+OWRITE, "epw");
287484860c5dSDavid du Colombier }
287584860c5dSDavid du Colombier if(ep->mode != OWRITE){
287684860c5dSDavid du Colombier if(ep->toggle[OREAD] != 0)
287784860c5dSDavid du Colombier io[OREAD].toggle = Tddata1;
287884860c5dSDavid du Colombier else
287984860c5dSDavid du Colombier io[OREAD].toggle = Tddata0;
288084860c5dSDavid du Colombier io[OREAD].tok = Tdtokin;
288184860c5dSDavid du Colombier io[OREAD].usbid = usbid;
288284860c5dSDavid du Colombier io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
288384860c5dSDavid du Colombier io[OREAD].qh = qhalloc(ctlr, ep, io+OREAD, "epr");
288484860c5dSDavid du Colombier }
288584860c5dSDavid du Colombier break;
288684860c5dSDavid du Colombier }
288784860c5dSDavid du Colombier coherence();
288884860c5dSDavid du Colombier if(ehcidebug>1 || ep->debug)
288984860c5dSDavid du Colombier dump(ep->hp);
289084860c5dSDavid du Colombier deprint("ehci: epopen done\n");
289184860c5dSDavid du Colombier poperror();
289284860c5dSDavid du Colombier }
289384860c5dSDavid du Colombier
289484860c5dSDavid du Colombier static void
cancelio(Ctlr * ctlr,Qio * io)289584860c5dSDavid du Colombier cancelio(Ctlr *ctlr, Qio *io)
289684860c5dSDavid du Colombier {
289784860c5dSDavid du Colombier Qh *qh;
289884860c5dSDavid du Colombier
289984860c5dSDavid du Colombier ilock(ctlr);
290084860c5dSDavid du Colombier qh = io->qh;
290184860c5dSDavid du Colombier if(io == nil || io->qh == nil || io->qh->state == Qclose){
290284860c5dSDavid du Colombier iunlock(ctlr);
290384860c5dSDavid du Colombier return;
290484860c5dSDavid du Colombier }
290584860c5dSDavid du Colombier dqprint("ehci: cancelio for qh %#p state %s\n",
290684860c5dSDavid du Colombier qh, qhsname[qh->state]);
290784860c5dSDavid du Colombier aborttds(qh);
290884860c5dSDavid du Colombier qh->state = Qclose;
290984860c5dSDavid du Colombier iunlock(ctlr);
291084860c5dSDavid du Colombier if(!waserror()){
291184860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
291284860c5dSDavid du Colombier poperror();
291384860c5dSDavid du Colombier }
291484860c5dSDavid du Colombier wakeup(io);
291584860c5dSDavid du Colombier qlock(io);
291684860c5dSDavid du Colombier /* wait for epio if running */
291784860c5dSDavid du Colombier qunlock(io);
291884860c5dSDavid du Colombier
291984860c5dSDavid du Colombier qhfree(ctlr, qh);
292084860c5dSDavid du Colombier io->qh = nil;
292184860c5dSDavid du Colombier }
292284860c5dSDavid du Colombier
292384860c5dSDavid du Colombier static void
cancelisoio(Ctlr * ctlr,Isoio * iso,int pollival,ulong load)292484860c5dSDavid du Colombier cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
292584860c5dSDavid du Colombier {
292684860c5dSDavid du Colombier int frno, i, n, t, w, woff;
292784860c5dSDavid du Colombier ulong *lp, *tp;
292884860c5dSDavid du Colombier Isoio **il;
292984860c5dSDavid du Colombier Itd *td;
293084860c5dSDavid du Colombier Sitd *std;
293184860c5dSDavid du Colombier
293284860c5dSDavid du Colombier ilock(ctlr);
293384860c5dSDavid du Colombier if(iso->state == Qclose){
293484860c5dSDavid du Colombier iunlock(ctlr);
293584860c5dSDavid du Colombier return;
293684860c5dSDavid du Colombier }
293784860c5dSDavid du Colombier ctlr->nreqs--;
293884860c5dSDavid du Colombier if(iso->state != Qrun && iso->state != Qdone)
293984860c5dSDavid du Colombier panic("bad iso state");
294084860c5dSDavid du Colombier iso->state = Qclose;
294184860c5dSDavid du Colombier coherence();
294284860c5dSDavid du Colombier if(ctlr->isoload < load)
294384860c5dSDavid du Colombier panic("ehci: low isoload");
294484860c5dSDavid du Colombier ctlr->isoload -= load;
294584860c5dSDavid du Colombier ctlr->load -= load;
294684860c5dSDavid du Colombier for(il = &ctlr->iso; *il != nil; il = &(*il)->next)
294784860c5dSDavid du Colombier if(*il == iso)
294884860c5dSDavid du Colombier break;
294984860c5dSDavid du Colombier if(*il == nil)
295084860c5dSDavid du Colombier panic("cancleiso: not found");
295184860c5dSDavid du Colombier *il = iso->next;
295284860c5dSDavid du Colombier
295384860c5dSDavid du Colombier frno = iso->td0frno;
295484860c5dSDavid du Colombier for(i = 0; i < iso->nframes; i++){
295584860c5dSDavid du Colombier tp = iso->tdps[frno];
295684860c5dSDavid du Colombier if(iso->hs != 0){
295784860c5dSDavid du Colombier td = iso->itdps[frno];
295884860c5dSDavid du Colombier for(t = 0; t < nelem(td->csw); t++)
295984860c5dSDavid du Colombier td->csw[t] &= ~(Itdioc|Itdactive);
296084860c5dSDavid du Colombier }else{
296184860c5dSDavid du Colombier std = iso->sitdps[frno];
296284860c5dSDavid du Colombier std->csw &= ~(Stdioc|Stdactive);
296384860c5dSDavid du Colombier }
296484860c5dSDavid du Colombier coherence();
296584860c5dSDavid du Colombier for(lp = &ctlr->frames[frno]; !(*lp & Lterm);
296684860c5dSDavid du Colombier lp = &LPTR(*lp)[0])
296784860c5dSDavid du Colombier if(LPTR(*lp) == tp)
296884860c5dSDavid du Colombier break;
296984860c5dSDavid du Colombier if(*lp & Lterm)
297084860c5dSDavid du Colombier panic("cancelisoio: td not found");
297184860c5dSDavid du Colombier *lp = tp[0];
297284860c5dSDavid du Colombier /*
297384860c5dSDavid du Colombier * Iso uses a virtual frame window of Nisoframes, and we must
297484860c5dSDavid du Colombier * restore pointers in copies of the window kept at ctlr->frames.
297584860c5dSDavid du Colombier */
297684860c5dSDavid du Colombier if(lp == &ctlr->frames[frno]){
297784860c5dSDavid du Colombier n = ctlr->nframes / Nisoframes;
297884860c5dSDavid du Colombier for(w = 1; w < n; w++){
297984860c5dSDavid du Colombier woff = w * Nisoframes;
298084860c5dSDavid du Colombier ctlr->frames[woff+frno] = *lp;
298184860c5dSDavid du Colombier }
298284860c5dSDavid du Colombier }
298384860c5dSDavid du Colombier coherence();
298484860c5dSDavid du Colombier frno = TRUNC(frno+pollival, Nisoframes);
298584860c5dSDavid du Colombier }
298684860c5dSDavid du Colombier iunlock(ctlr);
298784860c5dSDavid du Colombier
298884860c5dSDavid du Colombier /*
298984860c5dSDavid du Colombier * wakeup anyone waiting for I/O and
299084860c5dSDavid du Colombier * wait to be sure no I/O is in progress in the controller.
299184860c5dSDavid du Colombier * and then wait to be sure episo* is no longer running.
299284860c5dSDavid du Colombier */
299384860c5dSDavid du Colombier wakeup(iso);
299484860c5dSDavid du Colombier diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso);
299584860c5dSDavid du Colombier tsleep(&up->sleep, return0, 0, 5);
299684860c5dSDavid du Colombier qlock(iso);
299784860c5dSDavid du Colombier qunlock(iso);
299884860c5dSDavid du Colombier diprint("cancelisoio iso %#p releasing iso\n", iso);
299984860c5dSDavid du Colombier
300084860c5dSDavid du Colombier frno = iso->td0frno;
300184860c5dSDavid du Colombier for(i = 0; i < iso->nframes; i++){
300284860c5dSDavid du Colombier if(iso->hs != 0)
300384860c5dSDavid du Colombier itdfree(iso->itdps[frno]);
300484860c5dSDavid du Colombier else
300584860c5dSDavid du Colombier sitdfree(iso->sitdps[frno]);
300684860c5dSDavid du Colombier iso->tdps[frno] = nil;
300784860c5dSDavid du Colombier frno = TRUNC(frno+pollival, Nisoframes);
300884860c5dSDavid du Colombier }
300984860c5dSDavid du Colombier free(iso->tdps);
301084860c5dSDavid du Colombier iso->tdps = nil;
301184860c5dSDavid du Colombier free(iso->data);
301284860c5dSDavid du Colombier iso->data = nil;
301384860c5dSDavid du Colombier coherence();
301484860c5dSDavid du Colombier }
301584860c5dSDavid du Colombier
301684860c5dSDavid du Colombier static void
epclose(Ep * ep)301784860c5dSDavid du Colombier epclose(Ep *ep)
301884860c5dSDavid du Colombier {
301984860c5dSDavid du Colombier Qio *io;
302084860c5dSDavid du Colombier Ctlio *cio;
302184860c5dSDavid du Colombier Isoio *iso;
302284860c5dSDavid du Colombier Ctlr *ctlr;
302384860c5dSDavid du Colombier
302484860c5dSDavid du Colombier ctlr = ep->hp->aux;
302584860c5dSDavid du Colombier deprint("ehci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
302684860c5dSDavid du Colombier
302784860c5dSDavid du Colombier if(ep->aux == nil)
302884860c5dSDavid du Colombier panic("ehci: epclose called with closed ep");
302984860c5dSDavid du Colombier switch(ep->ttype){
303084860c5dSDavid du Colombier case Tctl:
303184860c5dSDavid du Colombier cio = ep->aux;
303284860c5dSDavid du Colombier cancelio(ctlr, cio);
303384860c5dSDavid du Colombier free(cio->data);
303484860c5dSDavid du Colombier cio->data = nil;
303584860c5dSDavid du Colombier break;
303684860c5dSDavid du Colombier case Tintr:
303784860c5dSDavid du Colombier case Tbulk:
303884860c5dSDavid du Colombier io = ep->aux;
303984860c5dSDavid du Colombier ep->toggle[OREAD] = ep->toggle[OWRITE] = 0;
304084860c5dSDavid du Colombier if(ep->mode != OWRITE){
304184860c5dSDavid du Colombier cancelio(ctlr, &io[OREAD]);
304284860c5dSDavid du Colombier if(io[OREAD].toggle == Tddata1)
304384860c5dSDavid du Colombier ep->toggle[OREAD] = 1;
304484860c5dSDavid du Colombier }
304584860c5dSDavid du Colombier if(ep->mode != OREAD){
304684860c5dSDavid du Colombier cancelio(ctlr, &io[OWRITE]);
304784860c5dSDavid du Colombier if(io[OWRITE].toggle == Tddata1)
304884860c5dSDavid du Colombier ep->toggle[OWRITE] = 1;
304984860c5dSDavid du Colombier }
305084860c5dSDavid du Colombier coherence();
305184860c5dSDavid du Colombier break;
305284860c5dSDavid du Colombier case Tiso:
305384860c5dSDavid du Colombier iso = ep->aux;
305484860c5dSDavid du Colombier cancelisoio(ctlr, iso, ep->pollival, ep->load);
305584860c5dSDavid du Colombier break;
305684860c5dSDavid du Colombier default:
305784860c5dSDavid du Colombier panic("epclose: bad ttype");
305884860c5dSDavid du Colombier }
305984860c5dSDavid du Colombier free(ep->aux);
306084860c5dSDavid du Colombier ep->aux = nil;
306184860c5dSDavid du Colombier }
306284860c5dSDavid du Colombier
306384860c5dSDavid du Colombier /*
306484860c5dSDavid du Colombier * return smallest power of 2 >= n
306584860c5dSDavid du Colombier */
306684860c5dSDavid du Colombier static int
flog2(int n)306784860c5dSDavid du Colombier flog2(int n)
306884860c5dSDavid du Colombier {
306984860c5dSDavid du Colombier int i;
307084860c5dSDavid du Colombier
307184860c5dSDavid du Colombier for(i = 0; (1 << i) < n; i++)
307284860c5dSDavid du Colombier ;
307384860c5dSDavid du Colombier return i;
307484860c5dSDavid du Colombier }
307584860c5dSDavid du Colombier
307684860c5dSDavid du Colombier /*
307784860c5dSDavid du Colombier * build the periodic scheduling tree:
307884860c5dSDavid du Colombier * framesize must be a multiple of the tree size
307984860c5dSDavid du Colombier */
308084860c5dSDavid du Colombier static void
mkqhtree(Ctlr * ctlr)308184860c5dSDavid du Colombier mkqhtree(Ctlr *ctlr)
308284860c5dSDavid du Colombier {
308384860c5dSDavid du Colombier int i, n, d, o, leaf0, depth;
308484860c5dSDavid du Colombier ulong leafs[Nintrleafs];
308584860c5dSDavid du Colombier Qh *qh;
308684860c5dSDavid du Colombier Qh **tree;
308784860c5dSDavid du Colombier Qtree *qt;
308884860c5dSDavid du Colombier
308984860c5dSDavid du Colombier depth = flog2(Nintrleafs);
309084860c5dSDavid du Colombier n = (1 << (depth+1)) - 1;
309184860c5dSDavid du Colombier qt = mallocz(sizeof(*qt), 1);
309284860c5dSDavid du Colombier if(qt == nil)
309384860c5dSDavid du Colombier panic("ehci: mkqhtree: no memory");
309484860c5dSDavid du Colombier qt->nel = n;
309584860c5dSDavid du Colombier qt->depth = depth;
309684860c5dSDavid du Colombier qt->bw = mallocz(n * sizeof(qt->bw), 1);
309784860c5dSDavid du Colombier qt->root = tree = mallocz(n * sizeof(Qh *), 1);
309884860c5dSDavid du Colombier if(qt->bw == nil || tree == nil)
309984860c5dSDavid du Colombier panic("ehci: mkqhtree: no memory");
310084860c5dSDavid du Colombier for(i = 0; i < n; i++){
310184860c5dSDavid du Colombier tree[i] = qh = edalloc();
310284860c5dSDavid du Colombier if(qh == nil)
310384860c5dSDavid du Colombier panic("ehci: mkqhtree: no memory");
310484860c5dSDavid du Colombier qh->nlink = qh->alink = qh->link = Lterm;
310584860c5dSDavid du Colombier qh->csw = Tdhalt;
310684860c5dSDavid du Colombier qh->state = Qidle;
310784860c5dSDavid du Colombier coherence();
310884860c5dSDavid du Colombier if(i > 0)
310984860c5dSDavid du Colombier qhlinkqh(tree[i], tree[(i-1)/2]);
311084860c5dSDavid du Colombier }
311184860c5dSDavid du Colombier ctlr->ntree = i;
311284860c5dSDavid du Colombier dprint("ehci: tree: %d endpoints allocated\n", i);
311384860c5dSDavid du Colombier
311484860c5dSDavid du Colombier /* distribute leaves evenly round the frame list */
311584860c5dSDavid du Colombier leaf0 = n / 2;
311684860c5dSDavid du Colombier for(i = 0; i < Nintrleafs; i++){
311784860c5dSDavid du Colombier o = 0;
311884860c5dSDavid du Colombier for(d = 0; d < depth; d++){
311984860c5dSDavid du Colombier o <<= 1;
312084860c5dSDavid du Colombier if(i & (1 << d))
312184860c5dSDavid du Colombier o |= 1;
312284860c5dSDavid du Colombier }
312384860c5dSDavid du Colombier if(leaf0 + o >= n){
312484860c5dSDavid du Colombier print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
312584860c5dSDavid du Colombier break;
312684860c5dSDavid du Colombier }
312784860c5dSDavid du Colombier leafs[i] = PADDR(tree[leaf0 + o]) | Lqh;
312884860c5dSDavid du Colombier }
312984860c5dSDavid du Colombier assert((ctlr->nframes % Nintrleafs) == 0);
313084860c5dSDavid du Colombier for(i = 0; i < ctlr->nframes; i += Nintrleafs){
313184860c5dSDavid du Colombier memmove(ctlr->frames + i, leafs, sizeof leafs);
313284860c5dSDavid du Colombier coherence();
313384860c5dSDavid du Colombier }
313484860c5dSDavid du Colombier ctlr->tree = qt;
313584860c5dSDavid du Colombier coherence();
313684860c5dSDavid du Colombier }
313784860c5dSDavid du Colombier
313884860c5dSDavid du Colombier void
ehcimeminit(Ctlr * ctlr)313984860c5dSDavid du Colombier ehcimeminit(Ctlr *ctlr)
314084860c5dSDavid du Colombier {
314184860c5dSDavid du Colombier int i, frsize;
314284860c5dSDavid du Colombier Eopio *opio;
314384860c5dSDavid du Colombier
314484860c5dSDavid du Colombier opio = ctlr->opio;
314584860c5dSDavid du Colombier frsize = ctlr->nframes * sizeof(ulong);
314684860c5dSDavid du Colombier assert((frsize & 0xFFF) == 0); /* must be 4k aligned */
314784860c5dSDavid du Colombier ctlr->frames = xspanalloc(frsize, frsize, 0);
314884860c5dSDavid du Colombier if(ctlr->frames == nil)
314984860c5dSDavid du Colombier panic("ehci reset: no memory");
315084860c5dSDavid du Colombier
315184860c5dSDavid du Colombier for (i = 0; i < ctlr->nframes; i++)
315284860c5dSDavid du Colombier ctlr->frames[i] = Lterm;
315384860c5dSDavid du Colombier opio->frbase = PADDR(ctlr->frames);
315484860c5dSDavid du Colombier opio->frno = 0;
315584860c5dSDavid du Colombier coherence();
315684860c5dSDavid du Colombier
315784860c5dSDavid du Colombier qhalloc(ctlr, nil, nil, nil); /* init async list */
315884860c5dSDavid du Colombier mkqhtree(ctlr); /* init sync list */
315984860c5dSDavid du Colombier edfree(edalloc()); /* try to get some ones pre-allocated */
316084860c5dSDavid du Colombier
316184860c5dSDavid du Colombier dprint("ehci %#p flb %#lux frno %#lux\n",
316284860c5dSDavid du Colombier ctlr->capio, opio->frbase, opio->frno);
316384860c5dSDavid du Colombier }
316484860c5dSDavid du Colombier
316584860c5dSDavid du Colombier static void
init(Hci * hp)316684860c5dSDavid du Colombier init(Hci *hp)
316784860c5dSDavid du Colombier {
316884860c5dSDavid du Colombier Ctlr *ctlr;
316984860c5dSDavid du Colombier Eopio *opio;
317084860c5dSDavid du Colombier int i;
31712a782499SDavid du Colombier static int ctlrno;
317284860c5dSDavid du Colombier
317384860c5dSDavid du Colombier hp->highspeed = 1;
317484860c5dSDavid du Colombier ctlr = hp->aux;
317584860c5dSDavid du Colombier opio = ctlr->opio;
317684860c5dSDavid du Colombier dprint("ehci %#p init\n", ctlr->capio);
317784860c5dSDavid du Colombier
317884860c5dSDavid du Colombier ilock(ctlr);
317984860c5dSDavid du Colombier /*
318084860c5dSDavid du Colombier * Unless we activate frroll interrupt
318184860c5dSDavid du Colombier * some machines won't post other interrupts.
318284860c5dSDavid du Colombier */
318384860c5dSDavid du Colombier opio->intr = Iusb|Ierr|Iportchg|Ihcerr|Iasync;
318484860c5dSDavid du Colombier coherence();
318584860c5dSDavid du Colombier opio->cmd |= Cpse;
318684860c5dSDavid du Colombier coherence();
318784860c5dSDavid du Colombier opio->cmd |= Case;
318884860c5dSDavid du Colombier coherence();
318984860c5dSDavid du Colombier ehcirun(ctlr, 1);
31902a782499SDavid du Colombier /*
31912a782499SDavid du Colombier * route all ports by default to only one ehci (the first).
31922a782499SDavid du Colombier * it's not obvious how multiple ehcis could work and on some
31932a782499SDavid du Colombier * machines, setting Callmine on all ehcis makes the machine seize up.
31942a782499SDavid du Colombier */
31952a782499SDavid du Colombier opio->config = (ctlrno == 0? Callmine: 0);
319684860c5dSDavid du Colombier coherence();
319784860c5dSDavid du Colombier
319884860c5dSDavid du Colombier for (i = 0; i < hp->nports; i++)
319984860c5dSDavid du Colombier opio->portsc[i] = Pspower;
320084860c5dSDavid du Colombier iunlock(ctlr);
320184860c5dSDavid du Colombier if(ehcidebug > 1)
320284860c5dSDavid du Colombier dump(hp);
32032a782499SDavid du Colombier ctlrno++;
320484860c5dSDavid du Colombier }
320584860c5dSDavid du Colombier
320684860c5dSDavid du Colombier void
ehcilinkage(Hci * hp)320784860c5dSDavid du Colombier ehcilinkage(Hci *hp)
320884860c5dSDavid du Colombier {
320984860c5dSDavid du Colombier hp->init = init;
321084860c5dSDavid du Colombier hp->dump = dump;
321184860c5dSDavid du Colombier hp->interrupt = interrupt;
321284860c5dSDavid du Colombier hp->epopen = epopen;
321384860c5dSDavid du Colombier hp->epclose = epclose;
321484860c5dSDavid du Colombier hp->epread = epread;
321584860c5dSDavid du Colombier hp->epwrite = epwrite;
321684860c5dSDavid du Colombier hp->seprintep = seprintep;
321784860c5dSDavid du Colombier hp->portenable = portenable;
321884860c5dSDavid du Colombier hp->portreset = portreset;
321984860c5dSDavid du Colombier hp->portstatus = portstatus;
322084860c5dSDavid du Colombier // hp->shutdown = shutdown;
322184860c5dSDavid du Colombier // hp->debug = setdebug;
322284860c5dSDavid du Colombier hp->type = "ehci";
322384860c5dSDavid du Colombier }
3224