123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * USB Universal Host Controller Interface (sic) driver.
- *
- * BUGS:
- * - Too many delays and ilocks.
- * - bandwidth admission control must be done per-frame.
- * - interrupt endpoints should go on a tree like [oe]hci.
- * - must warn of power overruns.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- #include "../port/usb.h"
- typedef struct Ctlio Ctlio;
- typedef struct Ctlr Ctlr;
- typedef struct Isoio Isoio;
- typedef struct Qh Qh;
- typedef struct Qhpool Qhpool;
- typedef struct Qio Qio;
- typedef struct Td Td;
- typedef struct Tdpool Tdpool;
- enum
- {
- Resetdelay = 100, /* delay after a controller reset (ms) */
- Enabledelay = 100, /* waiting for a port to enable */
- Abortdelay = 5, /* delay after cancelling Tds (ms) */
- Incr = 64, /* for Td and Qh pools */
- Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */
- /* Queue states (software) */
- Qidle = 0,
- Qinstall,
- Qrun,
- Qdone,
- Qclose,
- Qfree,
- /*
- * HW constants
- */
- Nframes = 1024, /* 2ⁿ for xspanalloc; max 1024 */
- Align = 16, /* for data structures */
- /* Size of small buffer kept within Tds. (software) */
- /* Keep as a multiple of Align to maintain alignment of Tds in pool */
- Tdndata = 1*Align,
- /* i/o space
- * Some ports are short, some are int32_t, some are byte.
- * We use ins[bsl] and not vmap.
- */
- Cmd = 0,
- Crun = 0x01,
- Chcreset = 0x02, /* host controller reset */
- Cgreset = 0x04, /* global reset */
- Cegsm = 0x08, /* enter global suspend */
- Cfgr = 0x10, /* forge global resume */
- Cdbg = 0x20, /* single step, debug */
- Cmaxp = 0x80, /* max packet */
- Status = 2,
- Susbintr = 0x01, /* interrupt */
- Seintr = 0x02, /* error interrupt */
- Sresume = 0x04, /* resume detect */
- Shserr = 0x08, /* host system error */
- Shcerr = 0x10, /* host controller error */
- Shalted = 0x20, /* controller halted */
- Sall = 0x3F,
- Usbintr = 4,
- Itmout = 0x01, /* timeout or crc */
- Iresume = 0x02, /* resume interrupt enable */
- Ioc = 0x04, /* interrupt on complete */
- Ishort = 0x08, /* short packet interrupt */
- Iall = 0x0F,
- Frnum = 6,
- Flbaseadd = 8,
- SOFmod = 0xC, /* start of frame modifier register */
- Portsc0 = 0x10,
- PSpresent = 0x0001, /* device present */
- PSstatuschg = 0x0002, /* PSpresent changed */
- PSenable = 0x0004, /* device enabled */
- PSchange = 0x0008, /* PSenable changed */
- PSresume = 0x0040, /* resume detected */
- PSreserved1 = 0x0080, /* always read as 1; reserved */
- PSslow = 0x0100, /* device has low speed */
- PSreset = 0x0200, /* port reset */
- PSsuspend = 0x1000, /* port suspended */
- /* Transfer descriptor link */
- Tdterm = 0x1, /* nil (terminate) */
- Tdlinkqh = 0x2, /* link refers to a QH */
- Tdvf = 0x4, /* run linked Tds first (depth-first)*/
- /* Transfer status bits */
- Tdbitstuff = 0x00020000, /* bit stuffing error */
- Tdcrcto = 0x00040000, /* crc or timeout error */
- Tdnak = 0x00080000, /* nak packet received */
- Tdbabble = 0x00100000, /* babble detected */
- Tddberr = 0x00200000, /* data buf. error */
- Tdstalled = 0x00400000, /* serious error to ep. */
- Tdactive = 0x00800000, /* enabled/in use by hw */
- /* Transfer control bits */
- Tdioc = 0x01000000, /* interrupt on complete */
- Tdiso = 0x02000000, /* isochronous select */
- Tdlow = 0x04000000, /* low speed device */
- Tderr1 = 0x08000000, /* bit 0 of error counter */
- Tderr2 = 0x10000000, /* bit 1 of error counter */
- Tdspd = 0x20000000, /* short packet detect */
- Tdlen = 0x000003FF, /* actual length field */
- Tdfatalerr = Tdnak|Tdbabble|Tdstalled, /* hw retries others */
- Tderrors = Tdfatalerr|Tdbitstuff|Tdcrcto|Tddberr,
- /* Transfer descriptor token bits */
- Tddata0 = 0,
- Tddata1 = 0x80000, /* data toggle (1==DATA1) */
- Tdtokin = 0x69,
- Tdtokout = 0xE1,
- Tdtoksetup = 0x2D,
- Tdmaxpkt = 0x800, /* max packet size */
- /* Queue head bits */
- QHterm = 1<<0, /* nil (terminate) */
- QHlinkqh = 1<<1, /* link refers to a QH */
- QHvf = 1<<2, /* vertical first (depth first) */
- };
- struct Ctlr
- {
- Lock; /* for ilock. qh lists and basic ctlr I/O */
- QLock portlck; /* for port resets/enable... */
- Pcidev* pcidev;
- int active;
- int port; /* I/O address */
- Qh* qhs; /* list of Qhs for this controller */
- Qh* qh[Tmax]; /* Dummy Qhs to insert Qhs after */
- Isoio* iso; /* list of active iso I/O */
- uint32_t* frames; /* frame list (used by hw) */
- uint32_t load; /* max load for a single frame */
- uint32_t isoload; /* max iso load for a single frame */
- int nintr; /* number of interrupts attended */
- int ntdintr; /* number of intrs. with something to do */
- int nqhintr; /* number of intrs. for Qhs */
- int nisointr; /* number of intrs. for iso transfers */
- };
- struct Qio
- {
- QLock; /* for the entire I/O process */
- Rendez; /* wait for completion */
- Qh* qh; /* Td list (field const after init) */
- int usbid; /* usb address for endpoint/device */
- int toggle; /* Tddata0/Tddata1 */
- int tok; /* Tdtoksetup, Tdtokin, Tdtokout */
- uint32_t iotime; /* time of last I/O */
- int debug; /* debug flag from the endpoint */
- char* err; /* error string */
- };
- struct Ctlio
- {
- Qio; /* a single Qio for each RPC */
- unsigned char* data; /* read from last ctl req. */
- int ndata; /* number of bytes read */
- };
- struct Isoio
- {
- QLock;
- Rendez; /* wait for space/completion/errors */
- int usbid; /* address used for device/endpoint */
- int tok; /* Tdtokin or Tdtokout */
- int state; /* Qrun -> Qdone -> Qrun... -> Qclose */
- int nframes; /* Nframes/ep->pollival */
- unsigned char* data; /* iso data buffers if not embedded */
- int td0frno; /* frame number for first Td */
- Td* tdu; /* next td for user I/O in tdps */
- Td* tdi; /* next td processed by interrupt */
- char* err; /* error string */
- int nerrs; /* nb of consecutive I/O errors */
- int32_t nleft; /* number of bytes left from last write */
- int debug; /* debug flag from the endpoint */
- Isoio* next; /* in list of active Isoios */
- Td* tdps[Nframes]; /* pointer to Td used for i-th frame or nil */
- };
- struct Tdpool
- {
- Lock;
- Td* free;
- int nalloc;
- int ninuse;
- int nfree;
- };
- struct Qhpool
- {
- Lock;
- Qh* free;
- int nalloc;
- int ninuse;
- int nfree;
- };
- /*
- * HW data structures
- */
- /*
- * Queue header (known by hw).
- * 16-byte aligned. first two words used by hw.
- * They are taken from the pool upon endpoint opening and
- * queued after the dummy queue header for the endpoint type
- * in the controller. Actual I/O happens as Tds are linked into it.
- * The driver does I/O in lock-step.
- * The user builds a list of Tds and links it into the Qh,
- * then the Qh goes from Qidle to Qrun and nobody touches it until
- * it becomes Qdone at interrupt time.
- * At that point the user collects the Tds and it goes Qidle.
- * A premature cancel may set the state to Qclose and abort I/O.
- * The Ctlr lock protects change of state for Qhs in use.
- */
- struct Qh
- {
- uint32_t link; /* link to next horiz. item (eg. Qh) */
- uint32_t elink; /* link to element (eg. Td; updated by hw) */
- uint32_t state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
- Qio* io; /* for this queue */
- Qh* next; /* in active or free list */
- Td* tds; /* Td list in this Qh (initially, elink) */
- char* tag; /* debug and align, mostly */
- uint32_t align;
- };
- /*
- * Transfer descriptor.
- * 16-byte aligned. first two words used by hw. Next 4 by sw.
- * We keep an embedded buffer for small I/O transfers.
- * They are taken from the pool when buffers are needed for I/O
- * and linked at the Qh/Isoio for the endpoint and direction requiring it.
- * The block keeps actual data. They are protected from races by
- * the queue or the pool keeping it. The owner of the link to the Td
- * is free to use it and can be the only one using it.
- */
- struct Td
- {
- uint32_t link; /* Link to next Td or Qh */
- uint32_t csw; /* control and status word (updated by hw) */
- uint32_t token; /* endpt, device, pid */
- uint32_t buffer; /* buffer pointer */
- Td* next; /* in qh or Isoio or free list */
- uint32_t ndata; /* bytes available/used at data */
- unsigned char* data; /* pointer to actual data */
- void* buff; /* allocated data, for large transfers */
- unsigned char sbuff[Tdndata]; /* embedded buffer, for small transfers */
- };
- #define INB(x) inb(ctlr->port+(x))
- #define INS(x) ins(ctlr->port+(x))
- #define INL(x) inl(ctlr->port+(x))
- #define OUTB(x, v) outb(ctlr->port+(x), (v))
- #define OUTS(x, v) outs(ctlr->port+(x), (v))
- #define OUTL(x, v) outl(ctlr->port+(x), (v))
- #define TRUNC(x, sz) ((x) & ((sz)-1))
- #define PTR(q) ((void*)KADDR((uint32_t)(q) & ~ (0xF|PCIWINDOW)))
- #define QPTR(q) ((Qh*)PTR(q))
- #define TPTR(q) ((Td*)PTR(q))
- #define PORT(p) (Portsc0 + 2*(p))
- #define diprint if(debug || iso->debug)print
- #define ddiprint if(debug>1 || iso->debug>1)print
- #define dqprint if(debug || (qh->io && qh->io->debug))print
- #define ddqprint if(debug>1 || (qh->io && qh->io->debug>1))print
- static Ctlr* ctlrs[Nhcis];
- static Tdpool tdpool;
- static Qhpool qhpool;
- static int debug;
- static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };
- static void
- uhcicmd(Ctlr *ctlr, int c)
- {
- OUTS(Cmd, c);
- }
- static void
- uhcirun(Ctlr *ctlr, int on)
- {
- int i;
- ddprint("uhci %#ux setting run to %d\n", ctlr->port, on);
- if(on)
- uhcicmd(ctlr, INS(Cmd)|Crun);
- else
- uhcicmd(ctlr, INS(Cmd) & ~Crun);
- for(i = 0; i < 100; i++)
- if(on == 0 && (INS(Status) & Shalted) != 0)
- break;
- else if(on != 0 && (INS(Status) & Shalted) == 0)
- break;
- else
- delay(1);
- if(i == 100)
- dprint("uhci %#x run cmd timed out\n", ctlr->port);
- ddprint("uhci %#ux cmd %#ux sts %#ux\n",
- ctlr->port, INS(Cmd), INS(Status));
- }
- static int
- tdlen(Td *td)
- {
- return (td->csw+1) & Tdlen;
- }
- static int
- maxtdlen(Td *td)
- {
- return ((td->token>>21)+1) & (Tdmaxpkt-1);
- }
- static int
- tdtok(Td *td)
- {
- return td->token & 0xFF;
- }
- static char*
- seprinttd(char *s, char *se, Td *td)
- {
- s = seprint(s, se, "%#p link %#ulx", td, td->link);
- if((td->link & Tdvf) != 0)
- s = seprint(s, se, "V");
- if((td->link & Tdterm) != 0)
- s = seprint(s, se, "T");
- if((td->link & Tdlinkqh) != 0)
- s = seprint(s, se, "Q");
- s = seprint(s, se, " csw %#ulx ", td->csw);
- if(td->csw & Tdactive)
- s = seprint(s, se, "a");
- if(td->csw & Tdiso)
- s = seprint(s, se, "I");
- if(td->csw & Tdioc)
- s = seprint(s, se, "i");
- if(td->csw & Tdlow)
- s = seprint(s, se, "l");
- if((td->csw & (Tderr1|Tderr2)) == 0)
- s = seprint(s, se, "z");
- if(td->csw & Tderrors)
- s = seprint(s, se, " err %#ulx", td->csw & Tderrors);
- if(td->csw & Tdstalled)
- s = seprint(s, se, "s");
- if(td->csw & Tddberr)
- s = seprint(s, se, "d");
- if(td->csw & Tdbabble)
- s = seprint(s, se, "b");
- if(td->csw & Tdnak)
- s = seprint(s, se, "n");
- if(td->csw & Tdcrcto)
- s = seprint(s, se, "c");
- if(td->csw & Tdbitstuff)
- s = seprint(s, se, "B");
- s = seprint(s, se, " stslen %d", tdlen(td));
- s = seprint(s, se, " token %#ulx", td->token);
- if(td->token == 0) /* the BWS loopback Td, ignore rest */
- return s;
- s = seprint(s, se, " maxlen %d", maxtdlen(td));
- if(td->token & Tddata1)
- s = seprint(s, se, " d1");
- else
- s = seprint(s, se, " d0");
- s = seprint(s, se, " id %#ulx:", (td->token>>15) & Epmax);
- s = seprint(s, se, "%#ulx", (td->token>>8) & Devmax);
- switch(tdtok(td)){
- case Tdtokin:
- s = seprint(s, se, " in");
- break;
- case Tdtokout:
- s = seprint(s, se, " out");
- break;
- case Tdtoksetup:
- s = seprint(s, se, " setup");
- break;
- default:
- s = seprint(s, se, " BADPID");
- }
- s = seprint(s, se, "\n\t buffer %#ulx data %#p", td->buffer, td->data);
- s = seprint(s, se, " ndata %uld sbuff %#p buff %#p",
- td->ndata, td->sbuff, td->buff);
- if(td->ndata > 0)
- s = seprintdata(s, se, td->data, td->ndata);
- return s;
- }
- static void
- isodump(Isoio *iso, int all)
- {
- char buf[256];
- Td *td;
- int i;
- print("iso %#p %s state %d nframes %d"
- " td0 %#p tdu %#p tdi %#p data %#p\n",
- iso, iso->tok == Tdtokin ? "in" : "out",
- iso->state, iso->nframes, iso->tdps[iso->td0frno],
- iso->tdu, iso->tdi, iso->data);
- if(iso->err != nil)
- print("\terr='%s'\n", iso->err);
- if(all == 0){
- seprinttd(buf, buf+sizeof(buf), iso->tdu);
- print("\ttdu %s\n", buf);
- seprinttd(buf, buf+sizeof(buf), iso->tdi);
- print("\ttdi %s\n", buf);
- }else{
- td = iso->tdps[iso->td0frno];
- for(i = 0; i < iso->nframes; i++){
- seprinttd(buf, buf+sizeof(buf), td);
- if(td == iso->tdi)
- print("i->");
- if(td == iso->tdu)
- print("u->");
- print("\t%s\n", buf);
- td = td->next;
- }
- }
- }
- static int
- sameptr(void *p, uint32_t l)
- {
- if(l & QHterm)
- return p == nil;
- return PTR(l) == p;
- }
- static void
- dumptd(Td *td, char *pref)
- {
- char buf[256];
- char *s;
- char *se;
- int i;
- i = 0;
- se = buf+sizeof(buf);
- for(; td != nil; td = td->next){
- s = seprinttd(buf, se, td);
- if(!sameptr(td->next, td->link))
- seprint(s, se, " next %#p != link %#ulx %#p",
- td->next, td->link, TPTR(td->link));
- print("%std %s\n", pref, buf);
- if(i++ > 20){
- print("...more tds...\n");
- break;
- }
- }
- }
- static void
- qhdump(Qh *qh, char *pref)
- {
- char buf[256];
- char *s;
- char *se;
- uint32_t td;
- int i;
- s = buf;
- se = buf+sizeof(buf);
- s = seprint(s, se, "%sqh %s %#p state %s link %#ulx", pref,
- qh->tag, qh, qhsname[qh->state], qh->link);
- if(!sameptr(qh->tds, qh->elink))
- s = seprint(s, se, " [tds %#p != elink %#ulx %#p]",
- qh->tds, qh->elink, TPTR(qh->elink));
- if(!sameptr(qh->next, qh->link))
- s = seprint(s, se, " [next %#p != link %#ulx %#p]",
- qh->next, qh->link, QPTR(qh->link));
- if((qh->link & Tdterm) != 0)
- s = seprint(s, se, "T");
- if((qh->link & Tdlinkqh) != 0)
- s = seprint(s, se, "Q");
- s = seprint(s, se, " elink %#ulx", qh->elink);
- if((qh->elink & Tdterm) != 0)
- s = seprint(s, se, "T");
- if((qh->elink & Tdlinkqh) != 0)
- s = seprint(s, se, "Q");
- s = seprint(s, se, " io %#p", qh->io);
- if(qh->io != nil && qh->io->err != nil)
- seprint(s, se, " err='%s'", qh->io->err);
- print("%s\n", buf);
- dumptd(qh->tds, "\t");
- if((qh->elink & QHterm) == 0){
- print("\thw tds:");
- i = 0;
- for(td = qh->elink; (td & Tdterm) == 0; td = TPTR(td)->link){
- print(" %#ulx", td);
- if(td == TPTR(td)->link) /* BWS Td */
- break;
- if(i++ > 40){
- print("...");
- break;
- }
- }
- print("\n");
- }
- }
- static void
- xdump(Ctlr *ctlr, int doilock)
- {
- Isoio *iso;
- Qh *qh;
- int i;
- if(doilock){
- if(ctlr == ctlrs[0]){
- lock(&tdpool);
- print("tds: alloc %d = inuse %d + free %d\n",
- tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
- unlock(&tdpool);
- lock(&qhpool);
- print("qhs: alloc %d = inuse %d + free %d\n",
- qhpool.nalloc, qhpool.ninuse, qhpool.nfree);
- unlock(&qhpool);
- }
- ilock(ctlr);
- }
- print("uhci port %#x frames %#p nintr %d ntdintr %d",
- ctlr->port, ctlr->frames, ctlr->nintr, ctlr->ntdintr);
- print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr);
- print("cmd %#ux sts %#ux fl %#ulx ps1 %#ux ps2 %#ux frames[0] %#ulx\n",
- INS(Cmd), INS(Status),
- INL(Flbaseadd), INS(PORT(0)), INS(PORT(1)),
- ctlr->frames[0]);
- for(iso = ctlr->iso; iso != nil; iso = iso->next)
- isodump(iso, 1);
- i = 0;
- for(qh = ctlr->qhs; qh != nil; qh = qh->next){
- qhdump(qh, "");
- if(i++ > 20){
- print("qhloop\n");
- break;
- }
- }
- print("\n");
- if(doilock)
- iunlock(ctlr);
- }
- static void
- dump(Hci *hp)
- {
- xdump(hp->aux, 1);
- }
- static Td*
- tdalloc(void)
- {
- int i;
- Td *td;
- Td *pool;
- lock(&tdpool);
- if(tdpool.free == nil){
- ddprint("uhci: tdalloc %d Tds\n", Incr);
- pool = xspanalloc(Incr*sizeof(Td), Align, 0);
- if(pool == nil)
- panic("tdalloc");
- for(i=Incr; --i>=0;){
- pool[i].next = tdpool.free;
- tdpool.free = &pool[i];
- }
- tdpool.nalloc += Incr;
- tdpool.nfree += Incr;
- }
- td = tdpool.free;
- tdpool.free = td->next;
- tdpool.ninuse++;
- tdpool.nfree--;
- unlock(&tdpool);
- memset(td, 0, sizeof(Td));
- td->link = Tdterm;
- assert(((uint64_t)td & 0xF) == 0);
- return td;
- }
- static void
- tdfree(Td *td)
- {
- if(td == nil)
- return;
- free(td->buff);
- td->buff = nil;
- lock(&tdpool);
- td->next = tdpool.free;
- tdpool.free = td;
- tdpool.ninuse--;
- tdpool.nfree++;
- unlock(&tdpool);
- }
- static void
- qhlinkqh(Qh* qh, Qh* next)
- {
- if(next == nil)
- qh->link = QHterm;
- else{
- next->link = qh->link;
- next->next = qh->next;
- qh->link = PCIWADDR(next)|QHlinkqh;
- }
- qh->next = next;
- }
- static void
- qhlinktd(Qh *qh, Td *td)
- {
- qh->tds = td;
- if(td == nil)
- qh->elink = QHvf|QHterm;
- else
- qh->elink = PCIWADDR(td);
- }
- static void
- tdlinktd(Td *td, Td *next)
- {
- td->next = next;
- if(next == nil)
- td->link = Tdterm;
- else
- td->link = PCIWADDR(next)|Tdvf;
- }
- static Qh*
- qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag)
- {
- int i;
- Qh *qh;
- Qh *pool;
- lock(&qhpool);
- if(qhpool.free == nil){
- ddprint("uhci: qhalloc %d Qhs\n", Incr);
- pool = xspanalloc(Incr*sizeof(Qh), Align, 0);
- if(pool == nil)
- panic("qhalloc");
- for(i=Incr; --i>=0;){
- pool[i].next = qhpool.free;
- qhpool.free = &pool[i];
- }
- qhpool.nalloc += Incr;
- qhpool.nfree += Incr;
- }
- qh = qhpool.free;
- qhpool.free = qh->next;
- qh->next = nil;
- qh->link = QHterm;
- qhpool.ninuse++;
- qhpool.nfree--;
- unlock(&qhpool);
- qh->tds = nil;
- qh->elink = QHterm;
- qh->state = Qidle;
- qh->io = io;
- qh->tag = nil;
- kstrdup(&qh->tag, tag);
- if(prev != nil){
- coherence();
- ilock(ctlr);
- qhlinkqh(prev, qh);
- iunlock(ctlr);
- }
- assert(((uint64_t)qh & 0xF) == 0);
- return qh;
- }
- static void
- qhfree(Ctlr *ctlr, Qh *qh)
- {
- Td *td;
- Td *ltd;
- Qh *q;
- if(qh == nil)
- return;
- ilock(ctlr);
- for(q = ctlr->qhs; q != nil; q = q->next)
- if(q->next == qh)
- break;
- if(q == nil)
- panic("qhfree: nil q");
- q->next = qh->next;
- q->link = qh->link;
- iunlock(ctlr);
- for(td = qh->tds; td != nil; td = ltd){
- ltd = td->next;
- tdfree(td);
- }
- lock(&qhpool);
- qh->state = Qfree; /* paranoia */
- qh->next = qhpool.free;
- qh->tag = nil;
- qh->io = nil;
- qhpool.free = qh;
- qhpool.ninuse--;
- qhpool.nfree++;
- unlock(&qhpool);
- ddprint("qhfree: qh %#p\n", qh);
- }
- static char*
- errmsg(int err)
- {
- if(err == 0)
- return "ok";
- if(err & Tdcrcto)
- return "crc/timeout error";
- if(err & Tdbabble)
- return "babble detected";
- if(err & Tddberr)
- return "db error";
- if(err & Tdbitstuff)
- return "bit stuffing error";
- if(err & Tdstalled)
- return Estalled;
- return Eio;
- }
- static int
- isocanread(void *a)
- {
- Isoio *iso;
- iso = a;
- return iso->state == Qclose ||
- (iso->state == Qrun &&
- iso->tok == Tdtokin && iso->tdi != iso->tdu);
- }
- static int
- isocanwrite(void *a)
- {
- Isoio *iso;
- iso = a;
- return iso->state == Qclose ||
- (iso->state == Qrun &&
- iso->tok == Tdtokout && iso->tdu->next != iso->tdi);
- }
- static void
- tdisoinit(Isoio *iso, Td *td, int32_t count)
- {
- td->ndata = count;
- td->token = ((count-1)<<21)| ((iso->usbid & 0x7FF)<<8) | iso->tok;
- td->csw = Tderr1|Tdiso|Tdactive|Tdioc;
- }
- /*
- * Process Iso i/o on interrupt. For writes update just error status.
- * For reads update tds to reflect data and also error status.
- * When tdi aproaches tdu, advance tdu; data may be lost.
- * (If nframes is << Nframes tdu might be far away but this avoids
- * races regarding frno.)
- * If we suffer errors for more than half the frames we stall.
- */
- static void
- isointerrupt(Ctlr *ctlr, Isoio* iso)
- {
- Td *tdi;
- int err;
- int i;
- int nframes;
- tdi = iso->tdi;
- if((tdi->csw & Tdactive) != 0) /* nothing new done */
- return;
- ctlr->nisointr++;
- ddiprint("isointr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
- if(iso->state != Qrun && iso->state != Qdone)
- panic("isointr: iso state");
- if(debug > 1 || iso->debug > 1)
- isodump(iso, 0);
- nframes = iso->nframes / 2; /* limit how many we look */
- if(nframes > 64)
- nframes = 64;
- for(i = 0; i < nframes && (tdi->csw & Tdactive) == 0; i++){
- tdi->csw &= ~Tdioc;
- err = tdi->csw & Tderrors;
- if(err == 0)
- iso->nerrs = 0;
- else if(iso->nerrs++ > iso->nframes/2)
- tdi->csw |= Tdstalled;
- if((tdi->csw & Tdstalled) != 0){
- if(iso->err == nil){
- iso->err = errmsg(err);
- diprint("isointerrupt: tdi %#p error %#ux %s\n",
- tdi, err, iso->err);
- diprint("ctlr load %uld\n", ctlr->load);
- }
- tdi->ndata = 0;
- }else
- tdi->ndata = tdlen(tdi);
- if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){
- memset(iso->tdu->data, 0, maxtdlen(iso->tdu));
- tdisoinit(iso, iso->tdu, maxtdlen(iso->tdu));
- iso->tdu = iso->tdu->next;
- iso->nleft = 0;
- }
- tdi = tdi->next;
- }
- ddiprint("isointr: %d frames processed\n", nframes);
- if(i == nframes)
- tdi->csw |= Tdioc;
- iso->tdi = tdi;
- if(isocanwrite(iso) || isocanread(iso)){
- diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
- iso->tdi, iso->tdu);
- wakeup(iso);
- }
- }
- /*
- * Process a Qh upon interrupt. There's one per ongoing user I/O.
- * User process releases resources later, that is not done here.
- * We may find in this order one or more Tds:
- * - none/many non active and completed Tds
- * - none/one (usually(!) not active) and failed Td
- * - none/many active Tds.
- * Upon errors the entire transfer is aborted and error reported.
- * Otherwise, the transfer is complete only when all Tds are done or
- * when a read with less than maxpkt is found.
- * Use the software list and not qh->elink to avoid races.
- * We could use qh->elink to see if there's something new or not.
- */
- static void
- qhinterrupt(Ctlr *ctlr, Qh *qh)
- {
- Td *td;
- int err;
- ctlr->nqhintr++;
- if(qh->state != Qrun)
- panic("qhinterrupt: qh state");
- if(qh->tds == nil)
- panic("qhinterrupt: no tds");
- if((qh->tds->csw & Tdactive) == 0)
- ddqprint("qhinterrupt port %#ux qh %#p p0 %#x p1 %#x\n",
- ctlr->port, qh, INS(PORT(0)), INS(PORT(1)));
- for(td = qh->tds; td != nil; td = td->next){
- if(td->csw & Tdactive)
- return;
- td->csw &= ~Tdioc;
- if((td->csw & Tdstalled) != 0){
- err = td->csw & Tderrors;
- /* just stalled is end of xfer but not an error */
- if(err != Tdstalled && qh->io->err == nil){
- qh->io->err = errmsg(td->csw & Tderrors);
- dqprint("qhinterrupt: td %#p error %#ux %s\n",
- td, err, qh->io->err);
- dqprint("ctlr load %uld\n", ctlr->load);
- }
- break;
- }
- if((td->csw & Tdnak) != 0){ /* retransmit; not serious */
- td->csw &= ~Tdnak;
- if(td->next == nil)
- td->csw |= Tdioc;
- }
- td->ndata = tdlen(td);
- if(td->ndata < maxtdlen(td)){ /* EOT */
- td = td->next;
- break;
- }
- }
- /*
- * Done. Make void the Tds not used (errors or EOT) and wakeup epio.
- */
- qh->elink = QHterm;
- for(; td != nil; td = td->next)
- td->ndata = 0;
- qh->state = Qdone;
- wakeup(qh->io);
- }
- static void
- interrupt(Ureg *ureg, void *a)
- {
- Hci *hp;
- Ctlr *ctlr;
- int frptr;
- int frno;
- Qh *qh;
- Isoio *iso;
- int sts;
- int cmd;
- hp = a;
- ctlr = hp->aux;
- ilock(ctlr);
- ctlr->nintr++;
- sts = INS(Status);
- if((sts & Sall) == 0){ /* not for us; sharing irq */
- iunlock(ctlr);
- return;
- }
- OUTS(Status, sts & Sall);
- cmd = INS(Cmd);
- if(cmd & Crun == 0){
- print("uhci %#ux: not running: uhci bug?\n", ctlr->port);
- /* BUG: should abort everything in this case */
- }
- if(debug > 1){
- frptr = INL(Flbaseadd);
- frno = INL(Frnum);
- frno = TRUNC(frno, Nframes);
- print("cmd %#ux sts %#ux frptr %#ux frno %d\n",
- cmd, sts, frptr, frno);
- }
- ctlr->ntdintr++;
- /*
- * Will we know in USB 3.0 who the interrupt was for?.
- * Do they still teach indexing in CS?
- * This is Intel's doing.
- */
- for(iso = ctlr->iso; iso != nil; iso = iso->next)
- if(iso->state == Qrun || iso->state == Qdone)
- isointerrupt(ctlr, iso);
- for(qh = ctlr->qhs; qh != nil; qh = qh->next)
- if(qh->state == Qrun)
- qhinterrupt(ctlr, qh);
- else if(qh->state == Qclose)
- qhlinktd(qh, nil);
- iunlock(ctlr);
- }
- /*
- * iso->tdu is the next place to put data. When it gets full
- * it is activated and tdu advanced.
- */
- static int32_t
- putsamples(Isoio *iso, unsigned char *b, int32_t count)
- {
- int32_t tot;
- int32_t n;
- for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
- n = count-tot;
- if(n > maxtdlen(iso->tdu) - iso->nleft)
- n = maxtdlen(iso->tdu) - iso->nleft;
- memmove(iso->tdu->data+iso->nleft, b+tot, n);
- iso->nleft += n;
- if(iso->nleft == maxtdlen(iso->tdu)){
- tdisoinit(iso, iso->tdu, iso->nleft);
- iso->nleft = 0;
- iso->tdu = iso->tdu->next;
- }
- }
- return tot;
- }
- /*
- * Queue data for writing and return error status from
- * last writes done, to maintain buffered data.
- */
- static int32_t
- episowrite(Ep *ep, Isoio *iso, void *a, int32_t count)
- {
- Proc *up = externup();
- Ctlr *ctlr;
- unsigned char *b;
- int tot;
- int nw;
- char *err;
- iso->debug = ep->debug;
- diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
- ctlr = ep->hp->aux;
- qlock(iso);
- if(waserror()){
- qunlock(iso);
- nexterror();
- }
- ilock(ctlr);
- if(iso->state == Qclose){
- iunlock(ctlr);
- error(iso->err ? iso->err : Eio);
- }
- iso->state = Qrun;
- b = a;
- for(tot = 0; tot < count; tot += nw){
- while(isocanwrite(iso) == 0){
- iunlock(ctlr);
- diprint("uhci: episowrite: %#p sleep\n", iso);
- if(waserror()){
- if(iso->err == nil)
- iso->err = "I/O timed out";
- ilock(ctlr);
- break;
- }
- tsleep(iso, isocanwrite, iso, ep->tmout);
- poperror();
- ilock(ctlr);
- }
- err = iso->err;
- iso->err = nil;
- if(iso->state == Qclose || err != nil){
- iunlock(ctlr);
- error(err ? err : Eio);
- }
- if(iso->state != Qrun)
- panic("episowrite: iso not running");
- iunlock(ctlr); /* We could page fault here */
- nw = putsamples(iso, b+tot, count-tot);
- ilock(ctlr);
- }
- if(iso->state != Qclose)
- iso->state = Qdone;
- iunlock(ctlr);
- err = iso->err; /* in case it failed early */
- iso->err = nil;
- qunlock(iso);
- poperror();
- if(err != nil)
- error(err);
- diprint("uhci: episowrite: %#p %d bytes\n", iso, tot);
- return tot;
- }
- /*
- * Available data is kept at tdu and following tds, up to tdi (excluded).
- */
- static int32_t
- episoread(Ep *ep, Isoio *iso, void *a, int count)
- {
- Proc *up = externup();
- Ctlr *ctlr;
- unsigned char *b;
- int nr;
- int tot;
- Td *tdu;
- iso->debug = ep->debug;
- diprint("uhci: episoread: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
- b = a;
- ctlr = ep->hp->aux;
- qlock(iso);
- if(waserror()){
- qunlock(iso);
- nexterror();
- }
- iso->err = nil;
- iso->nerrs = 0;
- ilock(ctlr);
- if(iso->state == Qclose){
- iunlock(ctlr);
- error(iso->err ? iso->err : Eio);
- }
- iso->state = Qrun;
- while(isocanread(iso) == 0){
- iunlock(ctlr);
- diprint("uhci: episoread: %#p sleep\n", iso);
- if(waserror()){
- if(iso->err == nil)
- iso->err = "I/O timed out";
- ilock(ctlr);
- break;
- }
- tsleep(iso, isocanread, iso, ep->tmout);
- poperror();
- ilock(ctlr);
- }
- if(iso->state == Qclose){
- iunlock(ctlr);
- error(iso->err ? iso->err : Eio);
- }
- iso->state = Qdone;
- assert(iso->tdu != iso->tdi);
- for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){
- tdu = iso->tdu;
- if(tdu->csw & Tdactive){
- diprint("uhci: episoread: %#p tdu active\n", iso);
- break;
- }
- nr = tdu->ndata;
- if(tot + nr > count)
- nr = count - tot;
- if(nr == 0)
- print("uhci: ep%d.%d: too many polls\n",
- ep->dev->nb, ep->nb);
- else{
- iunlock(ctlr); /* We could page fault here */
- memmove(b+tot, tdu->data, nr);
- ilock(ctlr);
- if(nr < tdu->ndata)
- memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
- tdu->ndata -= nr;
- }
- if(tdu->ndata == 0){
- tdisoinit(iso, tdu, ep->maxpkt);
- iso->tdu = tdu->next;
- }
- }
- iunlock(ctlr);
- qunlock(iso);
- poperror();
- diprint("uhci: episoread: %#p %d bytes err '%s'\n", iso, tot, iso->err);
- if(iso->err != nil)
- error(iso->err);
- return tot;
- }
- static int
- nexttoggle(int tog)
- {
- if(tog == Tddata0)
- return Tddata1;
- else
- return Tddata0;
- }
- static Td*
- epgettd(Ep *ep, Qio *io, int flags, void *a, int count)
- {
- Td *td;
- int tok;
- if(ep->maxpkt < count)
- error("maxpkt too short");
- td = tdalloc();
- if(count <= Tdndata)
- td->data = td->sbuff;
- else
- td->data = td->buff = smalloc(ep->maxpkt);
- td->buffer = PCIWADDR(td->data);
- td->ndata = count;
- if(a != nil && count > 0)
- memmove(td->data, a, count);
- td->csw = Tderr2|Tderr1|flags;
- if(ep->dev->speed == Lowspeed)
- td->csw |= Tdlow;
- tok = io->tok | io->toggle;
- io->toggle = nexttoggle(io->toggle);
- td->token = ((count-1)<<21) | ((io->usbid&0x7FF)<<8) | tok;
- return td;
- }
- /*
- * Try to get them idle
- */
- static void
- aborttds(Qh *qh)
- {
- Td *td;
- qh->state = Qdone;
- qh->elink = QHterm;
- for(td = qh->tds; td != nil; td = td->next){
- if(td->csw & Tdactive)
- td->ndata = 0;
- td->csw &= ~(Tdactive|Tdioc);
- }
- }
- static int
- epiodone(void *a)
- {
- Qh *qh;
- qh = a;
- return qh->state != Qrun;
- }
- static void
- epiowait(Ctlr *ctlr, Qio *io, int tmout, uint32_t load)
- {
- Proc *up = externup();
- Qh *qh;
- int timedout;
- qh = io->qh;
- ddqprint("uhci io %#p sleep on qh %#p state %uld\n", io, qh, qh->state);
- timedout = 0;
- if(waserror()){
- dqprint("uhci io %#p qh %#p timed out\n", io, qh);
- timedout++;
- }else{
- if(tmout == 0)
- sleep(io, epiodone, qh);
- else
- tsleep(io, epiodone, qh, tmout);
- poperror();
- }
- ilock(ctlr);
- if(qh->state == Qrun)
- timedout = 1;
- else if(qh->state != Qdone && qh->state != Qclose)
- panic("epio: queue not done and not closed");
- if(timedout){
- aborttds(io->qh);
- io->err = "request timed out";
- iunlock(ctlr);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
- ilock(ctlr);
- }
- if(qh->state != Qclose)
- qh->state = Qidle;
- qhlinktd(qh, nil);
- ctlr->load -= load;
- iunlock(ctlr);
- }
- /*
- * Non iso I/O.
- * To make it work for control transfers, the caller may
- * lock the Qio for the entire control transfer.
- */
- static int32_t
- epio(Ep *ep, Qio *io, void *a, int32_t count, int mustlock)
- {
- Proc *up = externup();
- Td *td, *ltd, *td0, *ntd;
- Ctlr *ctlr;
- Qh* qh;
- int32_t n, tot;
- char buf[128];
- unsigned char *c;
- int saved, ntds, tmout;
- uint32_t load;
- char *err;
- qh = io->qh;
- ctlr = ep->hp->aux;
- io->debug = ep->debug;
- tmout = ep->tmout;
- ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n",
- io->tok == Tdtokin ? "in" : "out",
- ep->dev->nb, ep->nb, io, count, ctlr->load);
- if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
- seprintdata(buf, buf+sizeof(buf), a, count);
- print("uchi epio: user data: %s\n", buf);
- }
- if(mustlock){
- qlock(io);
- if(waserror()){
- qunlock(io);
- nexterror();
- }
- }
- io->err = nil;
- ilock(ctlr);
- if(qh->state == Qclose){ /* Tds released by cancelio */
- iunlock(ctlr);
- error(io->err ? io->err : Eio);
- }
- if(qh->state != Qidle)
- panic("epio: qh not idle");
- qh->state = Qinstall;
- iunlock(ctlr);
- c = a;
- td0 = ltd = nil;
- load = tot = 0;
- do{
- n = ep->maxpkt;
- if(count-tot < n)
- n = count-tot;
- if(c != nil && io->tok != Tdtokin)
- td = epgettd(ep, io, Tdactive, c+tot, n);
- else
- td = epgettd(ep, io, Tdactive|Tdspd, nil, n);
- if(td0 == nil)
- td0 = td;
- else
- tdlinktd(ltd, td);
- ltd = td;
- tot += n;
- load += ep->load;
- }while(tot < count);
- if(td0 == nil || ltd == nil)
- panic("epio: no td");
- ltd->csw |= Tdioc; /* the last one interrupts */
- ddeprint("uhci: load %uld ctlr load %uld\n", load, ctlr->load);
- ilock(ctlr);
- if(qh->state != Qclose){
- io->iotime = TK2MS(machp()->ticks);
- qh->state = Qrun;
- coherence();
- qhlinktd(qh, td0);
- ctlr->load += load;
- }
- iunlock(ctlr);
- epiowait(ctlr, io, tmout, load);
- if(debug > 1 || ep->debug > 1)
- dumptd(td0, "epio: got tds: ");
- tot = 0;
- c = a;
- saved = 0;
- ntds = 0;
- for(td = td0; td != nil; td = ntd){
- ntds++;
- /*
- * Use td tok, not io tok, because of setup packets.
- * Also, if the Td was stalled or active (previous Td
- * was a short packet), we must save the toggle as it is.
- */
- if(td->csw & (Tdstalled|Tdactive)){
- if(saved++ == 0)
- io->toggle = td->token & Tddata1;
- }else{
- tot += td->ndata;
- if(c != nil && tdtok(td) == Tdtokin && td->ndata > 0){
- memmove(c, td->data, td->ndata);
- c += td->ndata;
- }
- }
- ntd = td->next;
- tdfree(td);
- }
- err = io->err;
- if(mustlock){
- qunlock(io);
- poperror();
- }
- ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n",
- io, ntds, tot, err);
- if(err != nil)
- error(err);
- if(tot < 0)
- error(Eio);
- return tot;
- }
- /*
- * halt condition was cleared on the endpoint. update our toggles.
- */
- static void
- clrhalt(Ep *ep)
- {
- Qio *io;
- ep->clrhalt = 0;
- switch(ep->ttype){
- case Tbulk:
- case Tintr:
- io = ep->aux;
- if(ep->mode != OREAD){
- qlock(&io[OWRITE]);
- io[OWRITE].toggle = Tddata0;
- deprint("ep clrhalt for io %#p\n", io+OWRITE);
- qunlock(&io[OWRITE]);
- }
- if(ep->mode != OWRITE){
- qlock(&io[OREAD]);
- io[OREAD].toggle = Tddata0;
- deprint("ep clrhalt for io %#p\n", io+OREAD);
- qunlock(&io[OREAD]);
- }
- break;
- }
- }
- static int32_t
- epread(Ep *ep, void *a, int32_t count)
- {
- Proc *up = externup();
- Ctlio *cio;
- Qio *io;
- Isoio *iso;
- char buf[160];
- uint32_t delta;
- ddeprint("uhci: epread\n");
- if(ep->aux == nil)
- panic("epread: not open");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- qlock(cio);
- if(waserror()){
- qunlock(cio);
- nexterror();
- }
- ddeprint("epread ctl ndata %d\n", cio->ndata);
- if(cio->ndata < 0)
- error("request expected");
- else if(cio->ndata == 0){
- cio->ndata = -1;
- count = 0;
- }else{
- if(count > cio->ndata)
- count = cio->ndata;
- if(count > 0)
- memmove(a, cio->data, count);
- /* BUG for big transfers */
- free(cio->data);
- cio->data = nil;
- cio->ndata = 0; /* signal EOF next time */
- }
- qunlock(cio);
- poperror();
- if(debug>1 || ep->debug){
- seprintdata(buf, buf+sizeof(buf), a, count);
- print("epread: %s\n", buf);
- }
- return count;
- case Tbulk:
- io = ep->aux;
- if(ep->clrhalt)
- clrhalt(ep);
- return epio(ep, &io[OREAD], a, count, 1);
- case Tintr:
- io = ep->aux;
- delta = TK2MS(machp()->ticks) - io[OREAD].iotime + 1;
- if(delta < ep->pollival / 2)
- tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
- if(ep->clrhalt)
- clrhalt(ep);
- return epio(ep, &io[OREAD], a, count, 1);
- case Tiso:
- iso = ep->aux;
- return episoread(ep, iso, a, count);
- default:
- panic("epread: bad ep ttype %d", ep->ttype);
- }
- return -1;
- }
- /*
- * Control transfers are one setup write (data0)
- * plus zero or more reads/writes (data1, data0, ...)
- * plus a final write/read with data1 to ack.
- * For both host to device and device to host we perform
- * the entire transfer when the user writes the request,
- * and keep any data read from the device for a later read.
- * We call epio three times instead of placing all Tds at
- * the same time because doing so leads to crc/tmout errors
- * for some devices.
- * Upon errors on the data phase we must still run the status
- * phase or the device may cease responding in the future.
- */
- static int32_t
- epctlio(Ep *ep, Ctlio *cio, void *a, int32_t count)
- {
- Proc *up = externup();
- unsigned char *c;
- int32_t len;
- ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
- cio, ep->dev->nb, ep->nb, count);
- if(count < Rsetuplen)
- error("short usb comand");
- qlock(cio);
- free(cio->data);
- cio->data = nil;
- cio->ndata = 0;
- if(waserror()){
- qunlock(cio);
- free(cio->data);
- cio->data = nil;
- cio->ndata = 0;
- nexterror();
- }
- /* set the address if unset and out of configuration state */
- if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
- if(cio->usbid == 0)
- cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
- c = a;
- cio->tok = Tdtoksetup;
- cio->toggle = Tddata0;
- if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
- error(Eio);
- a = c + Rsetuplen;
- count -= Rsetuplen;
- cio->toggle = Tddata1;
- if(c[Rtype] & Rd2h){
- cio->tok = Tdtokin;
- len = GET2(c+Rcount);
- if(len <= 0)
- error("bad length in d2h request");
- if(len > Maxctllen)
- error("d2h data too large to fit in uhci");
- a = cio->data = smalloc(len+1);
- }else{
- cio->tok = Tdtokout;
- len = count;
- }
- if(len > 0)
- if(waserror())
- len = -1;
- else{
- len = epio(ep, cio, a, len, 0);
- poperror();
- }
- if(c[Rtype] & Rd2h){
- count = Rsetuplen;
- cio->ndata = len;
- cio->tok = Tdtokout;
- }else{
- if(len < 0)
- count = -1;
- else
- count = Rsetuplen + len;
- cio->tok = Tdtokin;
- }
- cio->toggle = Tddata1;
- epio(ep, cio, nil, 0, 0);
- qunlock(cio);
- poperror();
- ddeprint("epctlio cio %#p return %ld\n", cio, count);
- return count;
- }
- static int32_t
- epwrite(Ep *ep, void *a, int32_t count)
- {
- Proc *up = externup();
- Ctlio *cio;
- Isoio *iso;
- Qio *io;
- uint32_t delta;
- char *b;
- int tot;
- int nw;
- ddeprint("uhci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux == nil)
- panic("uhci: epwrite: not open");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- return epctlio(ep, cio, a, count);
- case Tbulk:
- io = ep->aux;
- if(ep->clrhalt)
- clrhalt(ep);
- /*
- * Put at most Tdatomic Tds (512 bytes) at a time.
- * Otherwise some devices produce babble errors.
- */
- b = a;
- for(tot = 0; tot < count ; tot += nw){
- nw = count - tot;
- if(nw > Tdatomic * ep->maxpkt)
- nw = Tdatomic * ep->maxpkt;
- nw = epio(ep, &io[OWRITE], b+tot, nw, 1);
- }
- return tot;
- case Tintr:
- io = ep->aux;
- delta = TK2MS(machp()->ticks) - io[OWRITE].iotime + 1;
- if(delta < ep->pollival)
- tsleep(&up->sleep, return0, 0, ep->pollival - delta);
- if(ep->clrhalt)
- clrhalt(ep);
- return epio(ep, &io[OWRITE], a, count, 1);
- case Tiso:
- iso = ep->aux;
- return episowrite(ep, iso, a, count);
- default:
- panic("uhci: epwrite: bad ep ttype %d", ep->ttype);
- }
- return -1;
- }
- static void
- isoopen(Ep *ep)
- {
- Ctlr *ctlr;
- Isoio *iso;
- int frno;
- int i;
- Td* td;
- Td* ltd;
- int size;
- int left;
- if(ep->mode == ORDWR)
- error("iso i/o is half-duplex");
- ctlr = ep->hp->aux;
- iso = ep->aux;
- iso->debug = ep->debug;
- iso->next = nil; /* paranoia */
- if(ep->mode == OREAD)
- iso->tok = Tdtokin;
- else
- iso->tok = Tdtokout;
- iso->usbid = ((ep->nb & Epmax)<<7)|(ep->dev->nb & Devmax);
- iso->state = Qidle;
- iso->nframes = Nframes/ep->pollival;
- if(iso->nframes < 3)
- error("uhci isoopen bug"); /* we need at least 3 tds */
- ilock(ctlr);
- if(ctlr->load + ep->load > 800)
- print("usb: uhci: bandwidth may be exceeded\n");
- ctlr->load += ep->load;
- ctlr->isoload += ep->load;
- dprint("uhci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload);
- iunlock(ctlr);
- /*
- * From here on this cannot raise errors
- * unless we catch them and release here all memory allocated.
- */
- if(ep->maxpkt > Tdndata)
- iso->data = smalloc(iso->nframes*ep->maxpkt);
- ilock(ctlr);
- frno = INS(Frnum) + 10; /* start 10ms ahead */
- frno = TRUNC(frno, Nframes);
- iunlock(ctlr);
- iso->td0frno = frno;
- ltd = nil;
- left = 0;
- for(i = 0; i < iso->nframes; i++){
- td = iso->tdps[frno] = tdalloc();
- if(ep->mode == OREAD)
- size = ep->maxpkt;
- else{
- size = (ep->hz+left) * ep->pollival / 1000;
- size *= ep->samplesz;
- left = (ep->hz+left) * ep->pollival % 1000;
- if(size > ep->maxpkt){
- print("uhci: ep%d.%d: size > maxpkt\n",
- ep->dev->nb, ep->nb);
- print("size = %d max = %ld\n", size, ep->maxpkt);
- size = ep->maxpkt;
- }
- }
- if(size > Tdndata)
- td->data = iso->data + i * ep->maxpkt;
- else
- td->data = td->sbuff;
- td->buffer = PCIWADDR(td->data);
- tdisoinit(iso, td, size);
- if(ltd != nil)
- ltd->next = td;
- ltd = td;
- frno = TRUNC(frno+ep->pollival, Nframes);
- }
- ltd->next = iso->tdps[iso->td0frno];
- iso->tdi = iso->tdps[iso->td0frno];
- iso->tdu = iso->tdi; /* read: right now; write: 1s ahead */
- ilock(ctlr);
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- iso->tdps[frno]->link = ctlr->frames[frno];
- frno = TRUNC(frno+ep->pollival, Nframes);
- }
- coherence();
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- ctlr->frames[frno] = PCIWADDR(iso->tdps[frno]);
- frno = TRUNC(frno+ep->pollival, Nframes);
- }
- iso->next = ctlr->iso;
- ctlr->iso = iso;
- iso->state = Qdone;
- iunlock(ctlr);
- if(debug > 1 || iso->debug >1)
- isodump(iso, 0);
- }
- /*
- * Allocate the endpoint and set it up for I/O
- * in the controller. This must follow what's said
- * in Ep regarding configuration, including perhaps
- * the saved toggles (saved on a previous close of
- * the endpoint data file by epclose).
- */
- static void
- epopen(Ep *ep)
- {
- Proc *up = externup();
- Ctlr *ctlr;
- Qh *cqh;
- Qio *io;
- Ctlio *cio;
- int usbid;
- ctlr = ep->hp->aux;
- deprint("uhci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux != nil)
- panic("uhci: epopen called with open ep");
- if(waserror()){
- free(ep->aux);
- ep->aux = nil;
- nexterror();
- }
- if(ep->maxpkt > Tdmaxpkt){
- print("uhci: maxkpkt too large: using %d\n", Tdmaxpkt);
- ep->maxpkt = Tdmaxpkt;
- }
- cqh = ctlr->qh[ep->ttype];
- switch(ep->ttype){
- case Tnone:
- error("endpoint not configured");
- case Tiso:
- ep->aux = smalloc(sizeof(Isoio));
- isoopen(ep);
- break;
- case Tctl:
- cio = ep->aux = smalloc(sizeof(Ctlio));
- cio->debug = ep->debug;
- cio->ndata = -1;
- cio->data = nil;
- if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
- break;
- cio->qh = qhalloc(ctlr, cqh, cio, "epc");
- break;
- case Tbulk:
- case Tintr:
- io = ep->aux = smalloc(sizeof(Qio)*2);
- io[OREAD].debug = io[OWRITE].debug = ep->debug;
- usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb &Devmax);
- if(ep->mode != OREAD){
- if(ep->toggle[OWRITE] != 0)
- io[OWRITE].toggle = Tddata1;
- else
- io[OWRITE].toggle = Tddata0;
- io[OWRITE].tok = Tdtokout;
- io[OWRITE].qh = qhalloc(ctlr, cqh, io+OWRITE, "epw");
- io[OWRITE].usbid = usbid;
- }
- if(ep->mode != OWRITE){
- if(ep->toggle[OREAD] != 0)
- io[OREAD].toggle = Tddata1;
- else
- io[OREAD].toggle = Tddata0;
- io[OREAD].tok = Tdtokin;
- io[OREAD].qh = qhalloc(ctlr, cqh, io+OREAD, "epr");
- io[OREAD].usbid = usbid;
- }
- break;
- }
- if(debug>1 || ep->debug)
- dump(ep->hp);
- deprint("uhci: epopen done\n");
- poperror();
- }
- static void
- cancelio(Ctlr *ctlr, Qio *io)
- {
- Proc *up = externup();
- Qh *qh;
- ilock(ctlr);
- qh = io->qh;
- if(io == nil || io->qh == nil || io->qh->state == Qclose){
- iunlock(ctlr);
- return;
- }
- dqprint("uhci: cancelio for qh %#p state %s\n",
- qh, qhsname[qh->state]);
- aborttds(qh);
- qh->state = Qclose;
- iunlock(ctlr);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
- wakeup(io);
- qlock(io);
- /* wait for epio if running */
- qunlock(io);
- qhfree(ctlr, qh);
- io->qh = nil;
- }
- static void
- cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, uint32_t load)
- {
- Proc *up = externup();
- Isoio **il;
- uint32_t *lp;
- int i;
- int frno;
- Td *td;
- ilock(ctlr);
- if(iso->state == Qclose){
- iunlock(ctlr);
- return;
- }
- if(iso->state != Qrun && iso->state != Qdone)
- panic("bad iso state");
- iso->state = Qclose;
- if(ctlr->isoload < load)
- panic("uhci: low isoload");
- ctlr->isoload -= load;
- ctlr->load -= load;
- for(il = &ctlr->iso; *il != nil; il = &(*il)->next)
- if(*il == iso)
- break;
- if(*il == nil)
- panic("isocancel: not found");
- *il = iso->next;
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- td = iso->tdps[frno];
- td->csw &= ~(Tdioc|Tdactive);
- for(lp=&ctlr->frames[frno]; !(*lp & Tdterm);
- lp = &TPTR(*lp)->link)
- if(TPTR(*lp) == td)
- break;
- if(*lp & Tdterm)
- panic("cancelisoio: td not found");
- *lp = td->link;
- frno = TRUNC(frno+pollival, Nframes);
- }
- iunlock(ctlr);
- /*
- * wakeup anyone waiting for I/O and
- * wait to be sure no I/O is in progress in the controller.
- * and then wait to be sure episo-io is no int32_ter running.
- */
- wakeup(iso);
- diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso);
- tsleep(&up->sleep, return0, 0, 5);
- qlock(iso);
- qunlock(iso);
- diprint("cancelisoio iso %#p releasing iso\n", iso);
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- tdfree(iso->tdps[frno]);
- iso->tdps[frno] = nil;
- frno = TRUNC(frno+pollival, Nframes);
- }
- free(iso->data);
- iso->data = nil;
- }
- static void
- epclose(Ep *ep)
- {
- Ctlr *ctlr;
- Ctlio *cio;
- Isoio *iso;
- Qio *io;
- ctlr = ep->hp->aux;
- deprint("uhci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux == nil)
- panic("uhci: epclose called with closed ep");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- cancelio(ctlr, cio);
- free(cio->data);
- cio->data = nil;
- break;
- case Tbulk:
- case Tintr:
- io = ep->aux;
- ep->toggle[OREAD] = ep->toggle[OWRITE] = 0;
- if(ep->mode != OWRITE){
- cancelio(ctlr, &io[OREAD]);
- if(io[OREAD].toggle == Tddata1)
- ep->toggle[OREAD] = 1;
- }
- if(ep->mode != OREAD){
- cancelio(ctlr, &io[OWRITE]);
- if(io[OWRITE].toggle == Tddata1)
- ep->toggle[OWRITE] = 1;
- }
- break;
- case Tiso:
- iso = ep->aux;
- cancelisoio(ctlr, iso, ep->pollival, ep->load);
- break;
- default:
- panic("epclose: bad ttype %d", ep->ttype);
- }
- free(ep->aux);
- ep->aux = nil;
- }
- static char*
- seprintep(char *s, char *e, Ep *ep)
- {
- Ctlio *cio;
- Qio *io;
- Isoio *iso;
- Ctlr *ctlr;
- ctlr = ep->hp->aux;
- ilock(ctlr);
- if(ep->aux == nil){
- *s = 0;
- iunlock(ctlr);
- return s;
- }
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- s = seprint(s,e,"cio %#p qh %#p"
- " id %#x tog %#x tok %#x err %s\n",
- cio, cio->qh, cio->usbid, cio->toggle,
- cio->tok, cio->err);
- break;
- case Tbulk:
- case Tintr:
- io = ep->aux;
- if(ep->mode != OWRITE)
- s = seprint(s,e,"r: qh %#p id %#x tog %#x tok %#x err %s\n",
- io[OREAD].qh, io[OREAD].usbid, io[OREAD].toggle,
- io[OREAD].tok, io[OREAD].err);
- if(ep->mode != OREAD)
- s = seprint(s,e,"w: qh %#p id %#x tog %#x tok %#x err %s\n",
- io[OWRITE].qh, io[OWRITE].usbid, io[OWRITE].toggle,
- io[OWRITE].tok, io[OWRITE].err);
- break;
- case Tiso:
- iso = ep->aux;
- s = seprint(s,e,"iso %#p id %#x tok %#x tdu %#p tdi %#p err %s\n",
- iso, iso->usbid, iso->tok, iso->tdu, iso->tdi, iso->err);
- break;
- }
- iunlock(ctlr);
- return s;
- }
- static int
- portenable(Hci *hp, int port, int on)
- {
- Proc *up = externup();
- int s;
- int ioport;
- Ctlr *ctlr;
- ctlr = hp->aux;
- dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
- ioport = PORT(port-1);
- qlock(&ctlr->portlck);
- if(waserror()){
- qunlock(&ctlr->portlck);
- nexterror();
- }
- ilock(ctlr);
- s = INS(ioport);
- if(on)
- OUTS(ioport, s | PSenable);
- else
- OUTS(ioport, s & ~PSenable);
- microdelay(64);
- iunlock(ctlr);
- tsleep(&up->sleep, return0, 0, Enabledelay);
- dprint("uhci %#ux port %d enable=%d: sts %#x\n",
- ctlr->port, port, on, INS(ioport));
- qunlock(&ctlr->portlck);
- poperror();
- return 0;
- }
- static int
- portreset(Hci *hp, int port, int on)
- {
- int i, p;
- Ctlr *ctlr;
- if(on == 0)
- return 0;
- ctlr = hp->aux;
- dprint("uhci: %#ux port %d reset\n", ctlr->port, port);
- p = PORT(port-1);
- ilock(ctlr);
- OUTS(p, PSreset);
- delay(50);
- OUTS(p, INS(p) & ~PSreset);
- OUTS(p, INS(p) | PSenable);
- microdelay(64);
- for(i=0; i<1000 && (INS(p) & PSenable) == 0; i++)
- ;
- OUTS(p, (INS(p) & ~PSreset)|PSenable);
- iunlock(ctlr);
- dprint("uhci %#ux after port %d reset: sts %#x\n",
- ctlr->port, port, INS(p));
- return 0;
- }
- static int
- portstatus(Hci *hp, int port)
- {
- Proc *up = externup();
- int s;
- int r;
- int ioport;
- Ctlr *ctlr;
- ctlr = hp->aux;
- ioport = PORT(port-1);
- qlock(&ctlr->portlck);
- if(waserror()){
- iunlock(ctlr);
- qunlock(&ctlr->portlck);
- nexterror();
- }
- ilock(ctlr);
- s = INS(ioport);
- if(s & (PSstatuschg | PSchange)){
- OUTS(ioport, s);
- ddprint("uhci %#ux port %d status %#x\n", ctlr->port, port, s);
- }
- iunlock(ctlr);
- qunlock(&ctlr->portlck);
- poperror();
- /*
- * We must return status bits as a
- * get port status hub request would do.
- */
- r = 0;
- if(s & PSpresent)
- r |= HPpresent;
- if(s & PSenable)
- r |= HPenable;
- if(s & PSsuspend)
- r |= HPsuspend;
- if(s & PSreset)
- r |= HPreset;
- if(s & PSslow)
- r |= HPslow;
- if(s & PSstatuschg)
- r |= HPstatuschg;
- if(s & PSchange)
- r |= HPchange;
- return r;
- }
- static void
- scanpci(void)
- {
- static int already = 0;
- int io;
- int i;
- Ctlr *ctlr;
- Pcidev *p;
- if(already)
- return;
- already = 1;
- p = nil;
- while(p = pcimatch(p, 0, 0)){
- /*
- * Find UHCI controllers (Programming Interface = 0).
- */
- if(p->ccrb != Pcibcserial || p->ccru != Pciscusb)
- continue;
- switch(p->ccrp){
- case 0:
- io = p->mem[4].bar & ~0x0F;
- break;
- default:
- continue;
- }
- if(io == 0){
- print("usbuhci: %#x %#x: failed to map registers\n",
- p->vid, p->did);
- continue;
- }
- if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
- print("usbuhci: port %#ux in use\n", io);
- continue;
- }
- if(p->intl == 0xFF || p->intl == 0){
- print("usbuhci: no irq assigned for port %#ux\n", io);
- continue;
- }
- dprint("uhci: %#x %#x: port %#ux size %#x irq %d\n",
- p->vid, p->did, io, p->mem[4].size, p->intl);
- ctlr = malloc(sizeof(Ctlr));
- if (ctlr == nil)
- panic("uhci: out of memory");
- ctlr->pcidev = p;
- ctlr->port = io;
- for(i = 0; i < Nhcis; i++)
- if(ctlrs[i] == nil){
- ctlrs[i] = ctlr;
- break;
- }
- if(i == Nhcis)
- print("uhci: bug: no more controllers\n");
- }
- }
- static void
- uhcimeminit(Ctlr *ctlr)
- {
- Td* td;
- Qh *qh;
- int frsize;
- int i;
- ctlr->qhs = ctlr->qh[Tctl] = qhalloc(ctlr, nil, nil, "CTL");
- ctlr->qh[Tintr] = qhalloc(ctlr, ctlr->qh[Tctl], nil, "INT");
- ctlr->qh[Tbulk] = qhalloc(ctlr, ctlr->qh[Tintr], nil, "BLK");
- /* idle Td from dummy Qh at the end. looped back to itself */
- /* This is a workaround for PIIX4 errata 29773804.pdf */
- qh = qhalloc(ctlr, ctlr->qh[Tbulk], nil, "BWS");
- td = tdalloc();
- td->link = PCIWADDR(td);
- qhlinktd(qh, td);
- /* loop (hw only) from the last qh back to control xfers.
- * this may be done only for some of them. Disable until ehci comes.
- */
- if(0)
- qh->link = PCIWADDR(ctlr->qhs);
- frsize = Nframes*sizeof(uint32_t);
- ctlr->frames = xspanalloc(frsize, frsize, 0);
- if(ctlr->frames == nil)
- panic("uhci reset: no memory");
- ctlr->iso = nil;
- for(i = 0; i < Nframes; i++)
- ctlr->frames[i] = PCIWADDR(ctlr->qhs)|QHlinkqh;
- OUTL(Flbaseadd, PCIWADDR(ctlr->frames));
- OUTS(Frnum, 0);
- dprint("uhci %#ux flb %#ulx frno %#ux\n", ctlr->port,
- INL(Flbaseadd), INS(Frnum));
- }
- static void
- init(Hci *hp)
- {
- Ctlr *ctlr;
- int sts;
- int i;
- ctlr = hp->aux;
- dprint("uhci %#ux init\n", ctlr->port);
- coherence();
- ilock(ctlr);
- OUTS(Usbintr, Itmout|Iresume|Ioc|Ishort);
- uhcirun(ctlr, 1);
- dprint("uhci: init: cmd %#ux sts %#ux sof %#ux",
- INS(Cmd), INS(Status), INS(SOFmod));
- dprint(" flb %#ulx frno %#ux psc0 %#ux psc1 %#ux",
- INL(Flbaseadd), INS(Frnum), INS(PORT(0)), INS(PORT(1)));
- /* guess other ports */
- for(i = 2; i < 6; i++){
- sts = INS(PORT(i));
- if(sts != 0xFFFF && (sts & PSreserved1) == 1){
- dprint(" psc%d %#ux", i, sts);
- hp->nports++;
- }else
- break;
- }
- for(i = 0; i < hp->nports; i++)
- OUTS(PORT(i), 0);
- iunlock(ctlr);
- }
- static void
- uhcireset(Ctlr *ctlr)
- {
- int i;
- int sof;
- ilock(ctlr);
- dprint("uhci %#ux reset\n", ctlr->port);
- /*
- * Turn off legacy mode. Some controllers won't
- * interrupt us as expected otherwise.
- */
- uhcirun(ctlr, 0);
- pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
- OUTS(Usbintr, 0);
- sof = INB(SOFmod);
- uhcicmd(ctlr, Cgreset); /* global reset */
- delay(Resetdelay);
- uhcicmd(ctlr, 0); /* all halt */
- uhcicmd(ctlr, Chcreset); /* controller reset */
- for(i = 0; i < 100; i++){
- if((INS(Cmd) & Chcreset) == 0)
- break;
- delay(1);
- }
- if(i == 100)
- print("uhci %#x controller reset timed out\n", ctlr->port);
- OUTB(SOFmod, sof);
- iunlock(ctlr);
- }
- static void
- setdebug(Hci *hci, int d)
- {
- debug = d;
- }
- static void
- shutdown(Hci *hp)
- {
- Ctlr *ctlr;
- ctlr = hp->aux;
- ilock(ctlr);
- uhcirun(ctlr, 0);
- delay(100);
- iunlock(ctlr);
- }
- static int
- reset(Hci *hp)
- {
- static Lock resetlck;
- int i;
- Ctlr *ctlr;
- Pcidev *p;
- /*
- if(getconf("*nousbuhci"))
- return -1;
- */
- ilock(&resetlck);
- scanpci();
- /*
- * Any adapter matches if no hp->port is supplied,
- * otherwise the ports must match.
- */
- ctlr = nil;
- for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
- ctlr = ctlrs[i];
- if(ctlr->active == 0)
- if(hp->port == 0 || hp->port == ctlr->port){
- ctlr->active = 1;
- break;
- }
- }
- iunlock(&resetlck);
- if(ctlrs[i] == nil || i == Nhcis)
- return -1;
- p = ctlr->pcidev;
- hp->aux = ctlr;
- hp->port = ctlr->port;
- hp->irq = p->intl;
- hp->tbdf = p->tbdf;
- hp->nports = 2; /* default */
- uhcireset(ctlr);
- uhcimeminit(ctlr);
- /*
- * Linkage to the generic HCI driver.
- */
- hp->init = init;
- hp->dump = dump;
- hp->interrupt = interrupt;
- hp->epopen = epopen;
- hp->epclose = epclose;
- hp->epread = epread;
- hp->epwrite = epwrite;
- hp->seprintep = seprintep;
- hp->portenable = portenable;
- hp->portreset = portreset;
- hp->portstatus = portstatus;
- hp->shutdown = shutdown;
- hp->debug = setdebug;
- hp->type = "uhci";
- return 0;
- }
- void
- usbuhcilink(void)
- {
- addhcitype("uhci", reset);
- }
|