123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601 |
- /*
- * 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 Open Host Controller Interface (Ohci) driver
- *
- * BUGS:
- * - Missing isochronous input streams.
- * - Too many delays and ilocks.
- * - bandwidth admission control must be done per-frame.
- * - Buffering could be handled like in uhci, to avoid
- * needed block allocation and avoid allocs for small Tds.
- * - 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 Ed Ed;
- typedef struct Edpool Edpool;
- typedef struct Epx Epx;
- typedef struct Hcca Hcca;
- typedef struct Isoio Isoio;
- typedef struct Ohci Ohci;
- typedef struct Qio Qio;
- typedef struct Qtree Qtree;
- typedef struct Td Td;
- typedef struct Tdpool Tdpool;
- enum
- {
- Incr = 64, /* for Td and Ed pools */
- Align = 0x20, /* OHCI only requires 0x10 */
- /* use always a power of 2 */
- Abortdelay = 1, /* delay after cancelling Tds (ms) */
- Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */
- Enabledelay = 100, /* waiting for a port to enable */
- /* Queue states (software) */
- Qidle = 0,
- Qinstall,
- Qrun,
- Qdone,
- Qclose,
- Qfree,
- /* Ed control bits */
- Edmpsmask = 0x7ff, /* max packet size */
- Edmpsshift = 16,
- Edlow = 1 << 13, /* low speed */
- Edskip = 1 << 14, /* skip this ed */
- Ediso = 1 << 15, /* iso Tds used */
- Edtddir = 0, /* get dir from td */
- Edin = 2 << 11, /* direction in */
- Edout = 1 << 11, /* direction out */
- Eddirmask = 3 << 11, /* direction bits */
- Edhalt = 1, /* halted (in head ptr) */
- Edtoggle = 2, /* toggle (in head ptr) 1 == data1 */
- /* Td control bits */
- Tdround = 1<<18, /* (rounding) short packets ok */
- Tdtoksetup = 0<<19, /* setup packet */
- Tdtokin = 2<<19, /* in packet */
- Tdtokout = 1<<19, /* out packet */
- Tdtokmask = 3<<19, /* in/out/setup bits */
- Tdnoioc = 7<<21, /* intr. cnt. value for no interrupt */
- Tdusetog = 1<<25, /* use toggle from Td (1) or Ed (0) */
- Tddata1 = 1<<24, /* data toggle (1 == data1) */
- Tddata0 = 0<<24,
- Tdfcmask = 7, /* frame count (iso) */
- Tdfcshift = 24,
- Tdsfmask = 0xFFFF, /* starting frame (iso) */
- Tderrmask = 3, /* error counter */
- Tderrshift = 26,
- Tdccmask = 0xf, /* condition code (status) */
- Tdccshift = 28,
- Tdiccmask = 0xf, /* condition code (iso, offsets) */
- Tdiccshift = 12,
- Ntdframes = 0x10000, /* # of different iso frame numbers */
- /* Td errors (condition code) */
- Tdok = 0,
- Tdcrc = 1,
- Tdbitstuff = 2,
- Tdbadtog = 3,
- Tdstalled = 4,
- Tdtmout = 5,
- Tdpidchk = 6,
- Tdbadpid = 7,
- Tddataovr = 8,
- Tddataund = 9,
- Tdbufovr = 0xC,
- Tdbufund = 0xD,
- Tdnotacc = 0xE,
- /* control register */
- Cple = 0x04, /* periodic list enable */
- Cie = 0x08, /* iso. list enable */
- Ccle = 0x10, /* ctl list enable */
- Cble = 0x20, /* bulk list enable */
- Cfsmask = 3 << 6, /* functional state... */
- Cfsreset = 0 << 6,
- Cfsresume = 1 << 6,
- Cfsoper = 2 << 6,
- Cfssuspend = 3 << 6,
- /* command status */
- Sblf = 1 << 2, /* bulk list (load) flag */
- Sclf = 1 << 1, /* control list (load) flag */
- Shcr = 1 << 0, /* host controller reset */
- /* intr enable */
- Mie = 1 << 31,
- Oc = 1 << 30,
- Rhsc = 1 << 6,
- Fno = 1 << 5,
- Ue = 1 << 4,
- Rd = 1 << 3,
- Sf = 1 << 2,
- Wdh = 1 << 1,
- So = 1 << 0,
- Fmaxpktmask = 0x7fff,
- Fmaxpktshift = 16,
- HcRhDescA_POTPGT_MASK = 0xff << 24,
- HcRhDescA_POTPGT_SHIFT = 24,
- /* Rh status */
- Lps = 1 << 0,
- Cgp = 1 << 0,
- Oci = 1 << 1,
- Psm = 1 << 8,
- Nps = 1 << 9,
- Drwe = 1 << 15,
- Srwe = 1 << 15,
- Lpsc = 1 << 16,
- Ccic = 1 << 17,
- Crwe = 1 << 31,
- /* port status */
- Ccs = 0x00001, /* current connect status */
- Pes = 0x00002, /* port enable status */
- Pss = 0x00004, /* port suspend status */
- Poci = 0x00008, /* over current indicator */
- Prs = 0x00010, /* port reset status */
- Pps = 0x00100, /* port power status */
- Lsda = 0x00200, /* low speed device attached */
- Csc = 0x10000, /* connect status change */
- Pesc = 0x20000, /* enable status change */
- Pssc = 0x40000, /* suspend status change */
- Ocic = 0x80000, /* over current ind. change */
- Prsc = 0x100000, /* reset status change */
- /* port status write bits */
- Cpe = 0x001, /* clear port enable */
- Spe = 0x002, /* set port enable */
- Spr = 0x010, /* set port reset */
- Spp = 0x100, /* set port power */
- Cpp = 0x200, /* clear port power */
- };
- /*
- * Endpoint descriptor. (first 4 words used by hardware)
- */
- struct Ed {
- uint32_t ctrl;
- uint32_t tail; /* transfer descriptor */
- uint32_t head;
- uint32_t nexted;
- Ed* next; /* sw; in free list or next in list */
- Td* tds; /* in use by current xfer; all for iso */
- Ep* ep; /* debug/align */
- Ed* inext; /* debug/align (dump interrupt eds). */
- };
- /*
- * Endpoint I/O state (software), per direction.
- */
- struct Qio
- {
- QLock ql; /* for the entire I/O process */
- Rendez Rendez; /* wait for completion */
- Ed* ed; /* to place Tds on it */
- int sched; /* queue number (intr/iso) */
- int toggle; /* Tddata0/Tddata1 */
- uint32_t usbid; /* device/endpoint address */
- int tok; /* Tdsetup, Tdtokin, Tdtokout */
- int32_t iotime; /* last I/O time; to hold interrupt polls */
- int debug; /* for the endpoint */
- char* err; /* error status */
- int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
- int32_t bw; /* load (intr/iso) */
- };
- struct Ctlio
- {
- Qio Qio; /* single Ed for all transfers */
- unsigned char* data; /* read from last ctl req. */
- int ndata; /* number of bytes read */
- };
- struct Isoio
- {
- Qio Qio;
- int nframes; /* number of frames for a full second */
- Td* atds; /* Tds avail for further I/O */
- int navail; /* number of avail Tds */
- uint32_t frno; /* next frame number avail for I/O */
- uint32_t left; /* remainder after rounding Hz to samples/ms */
- int nerrs; /* consecutive errors on iso I/O */
- };
- /*
- * Transfer descriptor. Size must be multiple of 32
- * First block is used by hardware (aligned to 32).
- */
- struct Td
- {
- uint32_t ctrl;
- uint32_t cbp; /* current buffer pointer */
- uint32_t nexttd;
- uint32_t be;
- uint16_t offsets[8]; /* used by Iso Tds only */
- uint32_t nbytes; /* bytes in this Td */
- uint32_t cbp0; /* initial value for cbp */
- uint32_t last; /* true for last Td in Qio */
- uint32_t _160[5];
- Td* next; /* in free or Ed tds list */
- Td* anext; /* in avail td list (iso) */
- Ep* ep; /* using this Td for I/O */
- Qio* io; /* using this Td for I/O */
- Block* bp; /* data for this Td */
- uint64_t _64[3];
- };
- /*
- * Host controller communication area (hardware)
- */
- struct Hcca
- {
- uint32_t intrtable[32];
- uint16_t framenumber;
- uint16_t _16;
- uint32_t donehead;
- unsigned char reserved[116];
- };
- /*
- * I/O registers
- */
- struct Ohci
- {
- /* control and status group */
- uint32_t revision; /*00*/
- uint32_t control; /*04*/
- uint32_t cmdsts; /*08*/
- uint32_t intrsts; /*0c*/
- uint32_t intrenable; /*10*/
- uint32_t intrdisable; /*14*/
- /* memory pointer group */
- uint32_t hcca; /*18*/
- uint32_t periodcurred; /*1c*/
- uint32_t ctlheaded; /*20*/
- uint32_t ctlcurred; /*24*/
- uint32_t bulkheaded; /*28*/
- uint32_t bulkcurred; /*2c*/
- uint32_t donehead; /*30*/
- /* frame counter group */
- uint32_t fminterval; /*34*/
- uint32_t fmremaining; /*38*/
- uint32_t fmnumber; /*3c*/
- uint32_t periodicstart; /*40*/
- uint32_t lsthreshold; /*44*/
- /* root hub group */
- uint32_t rhdesca; /*48*/
- uint32_t rhdescb; /*4c*/
- uint32_t rhsts; /*50*/
- uint32_t rhportsts[15]; /*54*/
- uint32_t _640[20]; /*90*/
- /* unknown */
- uint32_t hostueaddr; /*e0*/
- uint32_t hostuests; /*e4*/
- uint32_t hosttimeoutctrl; /*e8*/
- uint32_t _32_1; /*ec*/
- uint32_t _32_2; /*f0*/
- uint32_t hostrevision; /*f4*/
- uint32_t _64[2];
- /*100*/
- };
- /*
- * Endpoint tree (software)
- */
- struct Qtree
- {
- int nel;
- int depth;
- uint32_t* bw;
- Ed** root;
- };
- struct Tdpool
- {
- Lock l;
- Td* free;
- int nalloc;
- int ninuse;
- int nfree;
- };
- struct Edpool
- {
- Lock l;
- Ed* free;
- int nalloc;
- int ninuse;
- int nfree;
- };
- struct Ctlr
- {
- Lock l; /* for ilock; lists and basic ctlr I/O */
- QLock resetl; /* lock controller during USB reset */
- int active;
- Ctlr* next;
- int nports;
- Ohci* ohci; /* base I/O address */
- Hcca* hcca; /* intr/done Td lists (used by hardware) */
- int overrun; /* sched. overrun */
- Ed* intrhd; /* list of intr. eds in tree */
- Qtree* tree; /* tree for t Ep i/o */
- int ntree; /* number of dummy Eds in tree */
- Pcidev* pcidev;
- };
- #define dqprint if(debug || (io && io->debug))print
- #define ddqprint if(debug>1 || (io && io->debug>1))print
- #define diprint if(debug || (iso && iso->Qio.debug))print
- #define ddiprint if(debug>1 || (iso && iso->Qio.debug>1))print
- #define TRUNC(x, sz) ((x) & ((sz)-1))
- static int ohciinterrupts[Nttypes];
- static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" };
- static int debug;
- static Edpool edpool;
- static Tdpool tdpool;
- static Ctlr* ctlrs[Nhcis];
- /* Never used
- static char EnotWritten[] = "usb write unfinished";
- static char EnotRead[] = "usb read unfinished";
- static char Eunderrun[] = "usb endpoint underrun";
- static QLock usbhstate; / * protects name space state * /
- static int schedendpt(Ctlr *ub, Ep *ep);
- static void unschedendpt(Ctlr *ub, Ep *ep);
- static int32_t qtd(Ctlr*, Ep*, int, Block*, unsigned char*, unsigned char*, int, uint32_t);
- */
- static char* errmsgs[] =
- {
- [Tdcrc] = "crc error",
- [Tdbitstuff] = "bit stuffing error",
- [Tdbadtog] = "bad toggle",
- [Tdstalled] = Estalled,
- [Tdtmout] = "timeout error",
- [Tdpidchk] = "pid check error",
- [Tdbadpid] = "bad pid",
- [Tddataovr] = "data overrun",
- [Tddataund] = "data underrun",
- [Tdbufovr] = "buffer overrun",
- [Tdbufund] = "buffer underrun",
- [Tdnotacc] = "not accessed"
- };
- static void*
- pa2ptr(uint32_t pa)
- {
- if(pa == 0)
- return nil;
- else
- return KADDR(pa);
- }
- static uint32_t
- ptr2pa(void *p)
- {
- if(p == nil)
- return 0;
- else
- return PADDR(p);
- }
- static void
- waitSOF(Ctlr *ub)
- {
- int frame = ub->hcca->framenumber & 0x3f;
- do {
- delay(2);
- } while(frame == (ub->hcca->framenumber & 0x3f));
- }
- static char*
- errmsg(int err)
- {
- if(err < nelem(errmsgs))
- return errmsgs[err];
- return nil;
- }
- static Ed*
- ctlhd(Ctlr *ctlr)
- {
- return pa2ptr(ctlr->ohci->ctlheaded);
- }
- static Ed*
- bulkhd(Ctlr *ctlr)
- {
- return pa2ptr(ctlr->ohci->bulkheaded);
- }
- static void
- edlinked(Ed *ed, Ed *next)
- {
- if(ed == nil)
- print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed));
- ed->nexted = ptr2pa(next);
- ed->next = next;
- }
- static void
- setctlhd(Ctlr *ctlr, Ed *ed)
- {
- ctlr->ohci->ctlheaded = ptr2pa(ed);
- if(ed != nil)
- ctlr->ohci->cmdsts |= Sclf; /* reload it on next pass */
- }
- static void
- setbulkhd(Ctlr *ctlr, Ed *ed)
- {
- ctlr->ohci->bulkheaded = ptr2pa(ed);
- if(ed != nil)
- ctlr->ohci->cmdsts |= Sblf; /* reload it on next pass */
- }
- static void
- unlinkctl(Ctlr *ctlr, Ed *ed)
- {
- Ed *this, *prev, *next;
- ctlr->ohci->control &= ~Ccle;
- waitSOF(ctlr);
- this = ctlhd(ctlr);
- ctlr->ohci->ctlcurred = 0;
- prev = nil;
- while(this != nil && this != ed){
- prev = this;
- this = this->next;
- }
- if(this == nil){
- print("unlinkctl: not found\n");
- return;
- }
- next = this->next;
- if(prev == nil)
- setctlhd(ctlr, next);
- else
- edlinked(prev, next);
- ctlr->ohci->control |= Ccle;
- edlinked(ed, nil); /* wipe out next field */
- }
- static void
- unlinkbulk(Ctlr *ctlr, Ed *ed)
- {
- Ed *this, *prev, *next;
- ctlr->ohci->control &= ~Cble;
- waitSOF(ctlr);
- this = bulkhd(ctlr);
- ctlr->ohci->bulkcurred = 0;
- prev = nil;
- while(this != nil && this != ed){
- prev = this;
- this = this->next;
- }
- if(this == nil){
- print("unlinkbulk: not found\n");
- return;
- }
- next = this->next;
- if(prev == nil)
- setbulkhd(ctlr, next);
- else
- edlinked(prev, next);
- ctlr->ohci->control |= Cble;
- edlinked(ed, nil); /* wipe out next field */
- }
- static void
- edsetaddr(Ed *ed, uint32_t addr)
- {
- uint32_t ctrl;
- ctrl = ed->ctrl & ~((Epmax<<7)|Devmax);
- ctrl |= (addr & ((Epmax<<7)|Devmax));
- ed->ctrl = ctrl;
- }
- /*
- static void
- edsettog(Ed *ed, int c)
- {
- if(c != 0)
- ed->head |= Edtoggle;
- else
- ed->head &= ~Edtoggle;
- }
- */
- static int
- edtoggle(Ed *ed)
- {
- return ed->head & Edtoggle;
- }
- static int
- edhalted(Ed *ed)
- {
- return ed->head & Edhalt;
- }
- static int
- edmaxpkt(Ed *ed)
- {
- return (ed->ctrl >> Edmpsshift) & Edmpsmask;
- }
- static void
- edsetmaxpkt(Ed *ed, int m)
- {
- uint32_t c;
- c = ed->ctrl & ~(Edmpsmask << Edmpsshift);
- ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift);
- }
- static int
- tderrs(Td *td)
- {
- return (td->ctrl >> Tdccshift) & Tdccmask;
- }
- static int
- tdtok(Td *td)
- {
- return (td->ctrl & Tdtokmask);
- }
- static Td*
- tdalloc(void)
- {
- Td *td;
- Td *pool;
- int i, sz;
- sz = ROUNDUP(sizeof *td, 16);
- lock(&tdpool.l);
- if(tdpool.free == nil){
- ddprint("ohci: tdalloc %d Tds\n", Incr);
- pool = mallocalign(Incr*sz, Align, 0, 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;
- }
- tdpool.ninuse++;
- tdpool.nfree--;
- td = tdpool.free;
- tdpool.free = td->next;
- memset(td, 0, sizeof(Td));
- unlock(&tdpool.l);
- if(((uint64_t)td & 0xF) != 0)
- panic("usbohci: tdalloc td 0x%p (not 16-aligned)", td);
- return td;
- }
- static void
- tdfree(Td *td)
- {
- if(td == 0)
- return;
- freeb(td->bp);
- td->bp = nil;
- lock(&tdpool.l);
- if(td->nexttd == 0x77777777)
- panic("ohci: tdfree: double free");
- memset(td, 7, sizeof(Td)); /* poison */
- td->next = tdpool.free;
- tdpool.free = td;
- tdpool.ninuse--;
- tdpool.nfree++;
- unlock(&tdpool.l);
- }
- static Ed*
- edalloc(void)
- {
- Ed *ed, *pool;
- int i, sz;
- sz = ROUNDUP(sizeof *ed, 16);
- lock(&edpool.l);
- if(edpool.free == nil){
- ddprint("ohci: edalloc %d Eds\n", Incr);
- pool = mallocalign(Incr*sz, Align, 0, 0);
- if(pool == nil)
- panic("edalloc");
- for(i=Incr; --i>=0;){
- pool[i].next = edpool.free;
- edpool.free = &pool[i];
- }
- edpool.nalloc += Incr;
- edpool.nfree += Incr;
- }
- edpool.ninuse++;
- edpool.nfree--;
- ed = edpool.free;
- edpool.free = ed->next;
- memset(ed, 0, sizeof(Ed));
- unlock(&edpool.l);
- return ed;
- }
- static void
- edfree(Ed *ed)
- {
- Td *td, *next;
- int i;
- if(ed == 0)
- return;
- i = 0;
- for(td = ed->tds; td != nil; td = next){
- next = td->next;
- tdfree(td);
- if(i++ > 2000){
- print("ohci: bug: ed with more than 2000 tds\n");
- break;
- }
- }
- lock(&edpool.l);
- if(ed->nexted == 0x99999999)
- panic("ohci: edfree: double free");
- memset(ed, 9, sizeof(Ed)); /* poison */
- ed->next = edpool.free;
- edpool.free = ed;
- edpool.ninuse--;
- edpool.nfree++;
- unlock(&edpool.l);
- ddprint("edfree: ed %#p\n", ed);
- }
- /*
- * return smallest power of 2 >= n
- */
- static int
- flog2(int n)
- {
- int i;
- for(i = 0; (1 << i) < n; i++)
- ;
- return i;
- }
- /*
- * return smallest power of 2 <= n
- */
- static int
- flog2lower(int n)
- {
- int i;
- for(i = 0; (1 << (i + 1)) <= n; i++)
- ;
- return i;
- }
- static int
- pickschedq(Qtree *qt, int pollival, uint32_t bw, uint32_t limit)
- {
- int i, j, d, upperb, q;
- uint32_t best, worst, total;
- d = flog2lower(pollival);
- if(d > qt->depth)
- d = qt->depth;
- q = -1;
- worst = 0;
- best = ~0;
- upperb = (1 << (d+1)) - 1;
- for(i = (1 << d) - 1; i < upperb; i++){
- total = qt->bw[0];
- for(j = i; j > 0; j = (j - 1) / 2)
- total += qt->bw[j];
- if(total < best){
- best = total;
- q = i;
- }
- if(total > worst)
- worst = total;
- }
- if(worst + bw >= limit)
- return -1;
- return q;
- }
- static int
- schedq(Ctlr *ctlr, Qio *io, int pollival)
- {
- int q;
- Ed *ted;
- q = pickschedq(ctlr->tree, pollival, io->bw, ~0);
- ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw);
- if(q < 0){
- print("ohci: no room for ed\n");
- return -1;
- }
- ctlr->tree->bw[q] += io->bw;
- ted = ctlr->tree->root[q];
- io->sched = q;
- edlinked(io->ed, ted->next);
- edlinked(ted, io->ed);
- io->ed->inext = ctlr->intrhd;
- ctlr->intrhd = io->ed;
- return 0;
- }
- static void
- unschedq(Ctlr *ctlr, Qio *qio)
- {
- int q;
- Ed *prev, *this, *next;
- Ed **l;
- q = qio->sched;
- if(q < 0)
- return;
- ctlr->tree->bw[q] -= qio->bw;
- prev = ctlr->tree->root[q];
- this = prev->next;
- while(this != nil && this != qio->ed){
- prev = this;
- this = this->next;
- }
- if(this == nil)
- print("ohci: unschedq %d: not found\n", q);
- else{
- next = this->next;
- edlinked(prev, next);
- }
- waitSOF(ctlr);
- for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext)
- if(*l == qio->ed){
- *l = (*l)->inext;
- return;
- }
- print("ohci: unschedq: ed %#p not found\n", qio->ed);
- }
- static char*
- seprinttdtok(char *s, char *e, int tok)
- {
- switch(tok){
- case Tdtoksetup:
- s = seprint(s, e, " setup");
- break;
- case Tdtokin:
- s = seprint(s, e, " in");
- break;
- case Tdtokout:
- s = seprint(s, e, " out");
- break;
- }
- return s;
- }
- static char*
- seprinttd(char *s, char *e, Td *td, int iso)
- {
- int i;
- Block *bp;
- if(td == nil)
- return seprint(s, e, "<nil td>\n");
- s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl);
- s = seprint(s, e, " cc=%#lx", (td->ctrl >> Tdccshift) & Tdccmask);
- if(iso == 0){
- if((td->ctrl & Tdround) != 0)
- s = seprint(s, e, " rnd");
- s = seprinttdtok(s, e, td->ctrl & Tdtokmask);
- if((td->ctrl & Tdusetog) != 0)
- s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0);
- else
- s = seprint(s, e, " d-");
- s = seprint(s, e, " ec=%lu", (td->ctrl >> Tderrshift) & Tderrmask);
- }else{
- s = seprint(s, e, " fc=%lu", (td->ctrl >> Tdfcshift) & Tdfcmask);
- s = seprint(s, e, " sf=%lu", td->ctrl & Tdsfmask);
- }
- s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s",
- td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : "");
- s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes);
- if((bp = td->bp) != nil){
- s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp);
- if(BLEN(bp) > 0)
- s = seprintdata(s, e, bp->rp, bp->wp - bp->rp);
- }
- if(iso == 0)
- return seprint(s, e, "\n");
- s = seprint(s, e, "\n\t\t");
- /* we use only offsets[0] */
- i = 0;
- s = seprint(s, e, "[%d] %#x cc=%#x sz=%u\n", i, td->offsets[i],
- (td->offsets[i] >> Tdiccshift) & Tdiccmask,
- td->offsets[i] & 0x7FF);
- return s;
- }
- static void
- dumptd(Td *td, char *p, int iso)
- {
- static char buf[512]; /* Too much */
- char *s;
- s = seprint(buf, buf+sizeof(buf), "%s: ", p);
- s = seprinttd(s, buf+sizeof(buf), td, iso);
- if(s > buf && s[-1] != '\n')
- s[-1] = '\n';
- print("\t%s", buf);
- }
- static void
- dumptds(Td *td, char *p, int iso)
- {
- int i;
- for(i = 0; td != nil; td = td->next){
- dumptd(td, p, iso);
- if(td->last)
- break;
- if(tdtok(td) == Tdtokin && ++i > 2){
- print("\t\t...\n");
- break;
- }
- }
- }
- static void
- dumped(Ed *ed)
- {
- char *buf, *s, *e;
- if(ed == nil){
- print("<null ed>\n");
- return;
- }
- buf = malloc(512);
- /* no waserror; may want to use from interrupt context */
- if(buf == nil)
- return;
- e = buf+512;
- s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl);
- if((ed->ctrl & Edskip) != 0)
- s = seprint(s, e, " skip");
- if((ed->ctrl & Ediso) != 0)
- s = seprint(s, e, " iso");
- if((ed->ctrl & Edlow) != 0)
- s = seprint(s, e, " low");
- s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0);
- if((ed->ctrl & Eddirmask) == Edin)
- s = seprint(s, e, " in");
- if((ed->ctrl & Eddirmask) == Edout)
- s = seprint(s, e, " out");
- if(edhalted(ed))
- s = seprint(s, e, " hlt");
- s = seprint(s, e, " ep%lu.%lu", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f);
- s = seprint(s, e, " maxpkt %lu", (ed->ctrl>>Edmpsshift)&Edmpsmask);
- seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted);
- print("%s", buf);
- free(buf);
- if(ed->tds != nil && (ed->ctrl & Ediso) == 0)
- dumptds(ed->tds, "td", 0);
- }
- static char*
- seprintio(char *s, char *e, Qio *io, char *pref)
- {
- s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed);
- s = seprint(s, e, " tog %d iot %ld err %s id %#lx",
- io->toggle, io->iotime, io->err, io->usbid);
- s = seprinttdtok(s, e, io->tok);
- s = seprint(s, e, " %s\n", iosname[io->state]);
- return s;
- }
- static char*
- seprintep(char* s, char* e, Ep *ep)
- {
- Isoio *iso;
- Qio *io;
- Ctlio *cio;
- if(ep == nil)
- return seprint(s, e, "<nil ep>\n");
- if(ep->aux == nil)
- return seprint(s, e, "no mdep\n");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- s = seprintio(s, e, &cio->Qio, "c");
- s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
- break;
- case Tbulk:
- case Tintr:
- io = ep->aux;
- if(ep->mode != OWRITE)
- s = seprintio(s, e, &io[OREAD], "r");
- if(ep->mode != OREAD)
- s = seprintio(s, e, &io[OWRITE], "w");
- break;
- case Tiso:
- iso = ep->aux;
- s = seprintio(s, e, &iso->Qio, "w");
- s = seprint(s, e, "\tntds %d avail %d frno %lu left %lu next avail %#p\n",
- iso->nframes, iso->navail, iso->frno, iso->left, iso->atds);
- break;
- }
- return s;
- }
- static char*
- seprintctl(char *s, char *se, uint32_t ctl)
- {
- s = seprint(s, se, "en=");
- if((ctl&Cple) != 0)
- s = seprint(s, se, "p");
- if((ctl&Cie) != 0)
- s = seprint(s, se, "i");
- if((ctl&Ccle) != 0)
- s = seprint(s, se, "c");
- if((ctl&Cble) != 0)
- s = seprint(s, se, "b");
- switch(ctl & Cfsmask){
- case Cfsreset:
- return seprint(s, se, " reset");
- case Cfsresume:
- return seprint(s, se, " resume");
- case Cfsoper:
- return seprint(s, se, " run");
- case Cfssuspend:
- return seprint(s, se, " suspend");
- default:
- return seprint(s, se, " ???");
- }
- }
- static void
- dump(Hci *hp)
- {
- Ctlr *ctlr;
- Ed *ed;
- char cs[20];
- ctlr = hp->Hciimpl.aux;
- ilock(&ctlr->l);
- seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control);
- print("ohci ctlr %#p: frno %#x ctl %#lx %s sts %#lx intr %#lx\n",
- ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs,
- ctlr->ohci->cmdsts, ctlr->ohci->intrsts);
- print("ctlhd %#lx cur %#lx bulkhd %#lx cur %#lx done %#lx\n",
- ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred,
- ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred,
- ctlr->ohci->donehead);
- if(ctlhd(ctlr) != nil)
- print("[ctl]\n");
- for(ed = ctlhd(ctlr); ed != nil; ed = ed->next)
- dumped(ed);
- if(bulkhd(ctlr) != nil)
- print("[bulk]\n");
- for(ed = bulkhd(ctlr); ed != nil; ed = ed->next)
- dumped(ed);
- if(ctlr->intrhd != nil)
- print("[intr]\n");
- for(ed = ctlr->intrhd; ed != nil; ed = ed->inext)
- dumped(ed);
- if(ctlr->tree->root[0]->next != nil)
- print("[iso]");
- for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next)
- dumped(ed);
- print("%d eds in tree\n", ctlr->ntree);
- iunlock(&ctlr->l);
- lock(&tdpool.l);
- print("%d tds allocated = %d in use + %d free\n",
- tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
- unlock(&tdpool.l);
- lock(&edpool.l);
- print("%d eds allocated = %d in use + %d free\n",
- edpool.nalloc, edpool.ninuse, edpool.nfree);
- unlock(&edpool.l);
- }
- /*
- * Compute size for the next iso Td and setup its
- * descriptor for I/O according to the buffer size.
- */
- static void
- isodtdinit(Ep *ep, Isoio *iso, Td *td)
- {
- Block *bp;
- int32_t size;
- int i;
- bp = td->bp;
- assert(bp != nil && BLEN(bp) == 0);
- size = (ep->hz+iso->left) * ep->pollival / 1000;
- iso->left = (ep->hz+iso->left) * ep->pollival % 1000;
- size *= ep->samplesz;
- if(size > ep->maxpkt){
- print("ohci: ep%d.%d: size > maxpkt\n",
- ep->dev->nb, ep->nb);
- print("size = %lu max = %ld\n", size, ep->maxpkt);
- size = ep->maxpkt;
- }
- td->nbytes = size;
- memset(bp->wp, 0, size); /* in case we don't fill it on time */
- td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF;
- td->ctrl = TRUNC(iso->frno, Ntdframes);
- td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF);
- td->offsets[0] |= (Tdnotacc << Tdiccshift);
- /* in case the controller checks out the offests... */
- for(i = 1; i < nelem(td->offsets); i++)
- td->offsets[i] = td->offsets[0];
- td->be = ptr2pa(bp->rp + size - 1);
- td->ctrl |= (0 << Tdfcshift); /* frame count is 1 */
- iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes);
- }
- /*
- * start I/O on the dummy td and setup a new dummy to fill up.
- */
- static void
- isoadvance(Ep *ep, Isoio *iso, Td *td)
- {
- Td *dtd;
- dtd = iso->atds;
- iso->atds = dtd->anext;
- iso->navail--;
- dtd->anext = nil;
- dtd->bp->wp = dtd->bp->rp;
- dtd->nexttd = 0;
- td->nexttd = ptr2pa(dtd);
- isodtdinit(ep, iso, dtd);
- iso->Qio.ed->tail = ptr2pa(dtd);
- }
- static int
- isocanwrite(void *a)
- {
- Isoio *iso;
- iso = a;
- return iso->Qio.state == Qclose || iso->Qio.err != nil ||
- iso->navail > iso->nframes / 2;
- }
- /*
- * Service a completed/failed Td from the done queue.
- * It may be of any transfer type.
- * The queue is not in completion order.
- * (It's actually in reverse completion order).
- *
- * When an error, a short packet, or a last Td is found
- * we awake the process waiting for the transfer.
- * Although later we will process other Tds completed
- * before, epio won't be able to touch the current Td
- * until interrupt returns and releases the lock on the
- * controller.
- */
- static void
- qhinterrupt(Ctlr *ctrl, Ep *ep, Qio *io, Td *td, int n)
- {
- Block *bp;
- int mode, err;
- Ed *ed;
- ed = io->ed;
- if(io->state != Qrun)
- return;
- if(tdtok(td) == Tdtokin)
- mode = OREAD;
- else
- mode = OWRITE;
- bp = td->bp;
- err = tderrs(td);
- switch(err){
- case Tddataovr: /* Overrun is not an error */
- break;
- case Tdok:
- /* virtualbox doesn't always report underflow on short packets */
- if(td->cbp == 0)
- break;
- /* fall through */
- case Tddataund:
- /* short input packets are ok */
- if(mode == OREAD){
- if(td->cbp == 0)
- panic("ohci: short packet but cbp == 0");
- /*
- * td->cbp and td->cbp0 are the real addresses
- * corresponding to virtual addresses bp->wp and
- * bp->rp respectively.
- */
- bp->wp = bp->rp + (td->cbp - td->cbp0);
- if(bp->wp < bp->rp)
- panic("ohci: wp < rp");
- /*
- * It's ok. clear error and flag as last in xfer.
- * epio must ignore following Tds.
- */
- td->last = 1;
- td->ctrl &= ~(Tdccmask << Tdccshift);
- break;
- }
- /* else fall; it's an error */
- case Tdcrc:
- case Tdbitstuff:
- case Tdbadtog:
- case Tdstalled:
- case Tdtmout:
- case Tdpidchk:
- case Tdbadpid:
- bp->wp = bp->rp; /* no bytes in xfer. */
- io->err = errmsg(err);
- if(debug || ep->debug){
- print("tdinterrupt: failed err %d (%s)\n", err, io->err);
- dumptd(td, "failed", ed->ctrl & Ediso);
- }
- td->last = 1;
- break;
- default:
- panic("ohci: td cc %u unknown", err);
- }
- if(td->last != 0){
- /*
- * clear td list and halt flag.
- */
- ed->head = (ed->head & Edtoggle) | ed->tail;
- ed->tds = pa2ptr(ed->tail);
- io->state = Qdone;
- wakeup(&io->Rendez);
- }
- }
- /*
- * BUG: Iso input streams are not implemented.
- */
- static void
- isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int n)
- {
- Isoio *iso;
- Block *bp;
- Ed *ed;
- int err, isoerr;
- iso = ep->aux;
- ed = io->ed;
- if(io->state == Qclose)
- return;
- bp = td->bp;
- /*
- * When we get more than half the frames consecutive errors
- * we signal an actual error. Errors in the entire Td are
- * more serious and are always singaled.
- * Errors like overrun are not really errors. In fact, for
- * output, errors cannot be really detected. The driver will
- * hopefully notice I/O errors on input endpoints and detach the device.
- */
- err = tderrs(td);
- isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask;
- if(isoerr == Tdok || isoerr == Tdnotacc)
- iso->nerrs = 0;
- else if(iso->nerrs++ > iso->nframes/2)
- err = Tdstalled;
- if(err != Tdok && err != Tddataovr){
- bp->wp = bp->rp;
- io->err = errmsg(err);
- if(debug || ep->debug){
- print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lx\n",
- ep->dev->nb, ep->nb,
- err, errmsg(err), ctlr->ohci->fmnumber);
- dumptd(td, "failed", ed->ctrl & Ediso);
- }
- }
- td->bp->wp = td->bp->rp;
- td->nbytes = 0;
- td->anext = iso->atds;
- iso->atds = td;
- iso->navail++;
- /*
- * If almost all Tds are avail the user is not doing I/O at the
- * required rate. We put another Td in place to keep the polling rate.
- */
- if(iso->Qio.err == nil && iso->navail > iso->nframes - 10)
- isoadvance(ep, iso, pa2ptr(iso->Qio.ed->tail));
- /*
- * If there's enough buffering futher I/O can be done.
- */
- if(isocanwrite(iso))
- wakeup(&iso->Qio.Rendez);
- }
- static void
- interrupt(Ureg *ureg, void *arg)
- {
- Td *td, *ntd;
- Hci *hp;
- Ctlr *ctlr;
- uint32_t status, curred;
- int i, frno;
- hp = arg;
- ctlr = hp->Hciimpl.aux;
- ilock(&ctlr->l);
- ctlr->ohci->intrdisable = Mie;
- coherence();
- status = ctlr->ohci->intrsts & ctlr->ohci->intrenable;
- status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
- frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
- if(status & Wdh){
- /* lsb of donehead has bit to flag other intrs. */
- td = pa2ptr(ctlr->hcca->donehead & ~0xF);
- for(i = 0; td != nil && i < 1024; i++){
- if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
- ntd = pa2ptr(td->nexttd & ~0xF);
- td->nexttd = 0;
- if(td->ep == nil || td->io == nil)
- panic("ohci: interrupt: ep %#p io %#p",
- td->ep, td->io);
- ohciinterrupts[td->ep->ttype]++;
- if(td->ep->ttype == Tiso)
- isointerrupt(ctlr, td->ep, td->io, td, frno);
- else
- qhinterrupt(ctlr, td->ep, td->io, td, frno);
- td = ntd;
- }
- if(i >= 1024)
- print("ohci: bug: more than 1024 done Tds?\n");
- ctlr->hcca->donehead = 0;
- }
- ctlr->ohci->intrsts = status;
- status &= ~Wdh;
- status &= ~Sf;
- if(status & So){
- print("ohci: sched overrun: too much load\n");
- ctlr->overrun++;
- status &= ~So;
- }
- if((status & Ue) != 0){
- curred = ctlr->ohci->periodcurred;
- print("ohci: unrecoverable error frame 0x%.8lx ed 0x%.8lx, "
- "ints %d %d %d %d\n",
- ctlr->ohci->fmnumber, curred,
- ohciinterrupts[Tctl], ohciinterrupts[Tintr],
- ohciinterrupts[Tbulk], ohciinterrupts[Tiso]);
- if(curred != 0)
- dumped(pa2ptr(curred));
- status &= ~Ue;
- }
- if(status != 0)
- print("ohci interrupt: unhandled sts 0x%.8lx\n", status);
- ctlr->ohci->intrenable = Mie | Wdh | Ue;
- iunlock(&ctlr->l);
- }
- /*
- * The old dummy Td is used to implement the new Td.
- * A new dummy is linked at the end of the old one and
- * returned, to link further Tds if needed.
- */
- static Td*
- epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count)
- {
- Td *td, *dtd;
- Block *bp;
- if(count <= BIGPGSZ)
- bp = allocb(count);
- else{
- if(count > 2*BIGPGSZ)
- panic("ohci: transfer > two pages");
- /* maximum of one physical page crossing allowed */
- bp = allocb(count+BIGPGSZ);
- bp->rp = (unsigned char*)BIGPGROUND((uintptr)bp->rp);
- bp->wp = bp->rp;
- }
- dtd = *dtdp;
- td = dtd;
- td->bp = bp;
- if(count > 0){
- td->cbp0 = td->cbp = ptr2pa(bp->wp);
- td->be = ptr2pa(bp->wp + count - 1);
- if(a != nil){
- /* validaddr((uintptr)a, count, 0); DEBUG */
- memmove(bp->wp, a, count);
- }
- bp->wp += count;
- }
- td->nbytes = count;
- td->ctrl = io->tok|Tdusetog|io->toggle|flags;
- if(io->toggle == Tddata0)
- io->toggle = Tddata1;
- else
- io->toggle = Tddata0;
- assert(td->ep == ep);
- td->io = io;
- dtd = tdalloc(); /* new dummy */
- dtd->ep = ep;
- td->nexttd = ptr2pa(dtd);
- td->next = dtd;
- *dtdp = dtd;
- return td;
- }
- /*
- * Try to get them idle
- */
- static void
- aborttds(Qio *io)
- {
- Ed *ed;
- Td *td;
- ed = io->ed;
- if(ed == nil)
- return;
- ed->ctrl |= Edskip;
- for(td = ed->tds; td != nil; td = td->next)
- if(td->bp != nil)
- td->bp->wp = td->bp->rp;
- ed->head = (ed->head&0xF) | ed->tail;
- if((ed->ctrl & Ediso) == 0)
- ed->tds = pa2ptr(ed->tail);
- }
- static int
- epiodone(void *a)
- {
- Qio *io;
- io = a;
- return io->state != Qrun;
- }
- static void
- epiowait(Ctlr *ctlr, Qio *io, int tmout, uint32_t n)
- {
- Proc *up = externup();
- Ed *ed;
- int timedout;
- ed = io->ed;
- if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n",
- io, ed, iosname[io->state]);
- timedout = 0;
- if(waserror()){
- dqprint("ohci io %#p ed %#p timed out\n", io, ed);
- timedout++;
- }else{
- if(tmout == 0)
- sleep(&io->Rendez, epiodone, io);
- else
- tsleep(&io->Rendez, epiodone, io, tmout);
- poperror();
- }
- ilock(&ctlr->l);
- if(io->state == Qrun)
- timedout = 1;
- else if(io->state != Qdone && io->state != Qclose)
- panic("epio: ed not done and not closed");
- if(timedout){
- aborttds(io);
- io->err = "request timed out";
- iunlock(&ctlr->l);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
- ilock(&ctlr->l);
- }
- if(io->state != Qclose)
- io->state = Qidle;
- iunlock(&ctlr->l);
- }
- /*
- * 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();
- Ed *ed;
- Ctlr *ctlr;
- char buf[80];
- char *err;
- unsigned char *c;
- Td *td, *ltd, *ntd, *td0;
- int last, ntds, tmout;
- int32_t tot, n;
- uint32_t load;
- ed = io->ed;
- ctlr = ep->hp->Hciimpl.aux;
- io->debug = ep->debug;
- tmout = ep->tmout;
- ddeprint("ohci: %s ep%d.%d io %#p count %ld\n",
- io->tok == Tdtokin ? "in" : "out",
- ep->dev->nb, ep->nb, io, count);
- if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
- seprintdata(buf, buf+sizeof(buf), a, count);
- print("\t%s\n", buf);
- }
- if(mustlock){
- qlock(&io->ql);
- if(waserror()){
- qunlock(&io->ql);
- nexterror();
- }
- }
- io->err = nil;
- ilock(&ctlr->l);
- if(io->state == Qclose){ /* Tds released by cancelio */
- iunlock(&ctlr->l);
- error(io->err ? io->err : Eio);
- }
- if(io->state != Qidle)
- panic("epio: qio not idle");
- io->state = Qinstall;
- c = a;
- ltd = td0 = ed->tds;
- load = tot = 0;
- do{
- n = 2*BIGPGSZ;
- if(count-tot < n)
- n = count-tot;
- if(c != nil && io->tok != Tdtokin)
- td = epgettd(ep, io, <d, 0, c+tot, n);
- else
- td = epgettd(ep, io, <d, 0, nil, n);
- tot += n;
- load += ep->load;
- }while(tot < count);
- if(td0 == nil || ltd == nil || td0 == ltd)
- panic("epio: no td");
- td->last = 1;
- if(debug > 2 || ep->debug > 2)
- dumptds(td0, "put td", ep->ttype == Tiso);
- iunlock(&ctlr->l);
- ilock(&ctlr->l);
- if(io->state != Qclose){
- io->iotime = TK2MS(machp()->ticks);
- io->state = Qrun;
- ed->tail = ptr2pa(ltd);
- if(ep->ttype == Tctl)
- ctlr->ohci->cmdsts |= Sclf;
- else if(ep->ttype == Tbulk)
- ctlr->ohci->cmdsts |= Sblf;
- }
- iunlock(&ctlr->l);
- epiowait(ctlr, io, tmout, load);
- ilock(&ctlr->l);
- if(debug > 1 || ep->debug > 1)
- dumptds(td0, "got td", 0);
- iunlock(&ctlr->l);
- tot = 0;
- c = a;
- ntds = last = 0;
- for(td = td0; td != ltd; td = ntd){
- ntds++;
- /*
- * If the Td is flagged as last we must
- * ignore any following Td. The block may
- * seem to have bytes but interrupt has not seen
- * those Tds through the done queue, and they are void.
- */
- if(last == 0 && tderrs(td) == Tdok){
- n = BLEN(td->bp);
- tot += n;
- if(c != nil && tdtok(td) == Tdtokin && n > 0){
- memmove(c, td->bp->rp, n);
- c += n;
- }
- }
- last |= td->last;
- ntd = td->next;
- tdfree(td);
- }
- if(edtoggle(ed) == 0)
- io->toggle = Tddata0;
- else
- io->toggle = Tddata1;
- err = io->err;
- if(mustlock){
- qunlock(&io->ql);
- poperror();
- }
- ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\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].ql);
- io[OWRITE].toggle = Tddata0;
- deprint("ep clrhalt for io %#p\n", io+OWRITE);
- qunlock(&io[OWRITE].ql);
- }
- if(ep->mode != OWRITE){
- qlock(&io[OREAD].ql);
- io[OREAD].toggle = Tddata0;
- deprint("ep clrhalt for io %#p\n", io+OREAD);
- qunlock(&io[OREAD].ql);
- }
- break;
- }
- }
- static int32_t
- epread(Ep *ep, void *a, int32_t count)
- {
- Proc *up = externup();
- Ctlio *cio;
- Qio *io;
- char buf[80];
- uint32_t delta;
- if(ep->aux == nil)
- panic("epread: not open");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- qlock(&cio->Qio.ql);
- if(waserror()){
- qunlock(&cio->Qio.ql);
- 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->Qio.ql);
- 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:
- panic("ohci: iso read not implemented");
- break;
- 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 command");
- qlock(&cio->Qio.ql);
- free(cio->data);
- cio->data = nil;
- cio->ndata = 0;
- if(waserror()){
- qunlock(&cio->Qio.ql);
- 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->Qio.usbid == 0){
- cio->Qio.usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
- edsetaddr(cio->Qio.ed, cio->Qio.usbid);
- }
- /* adjust maxpkt if the user has learned a different one */
- if(edmaxpkt(cio->Qio.ed) != ep->maxpkt)
- edsetmaxpkt(cio->Qio.ed, ep->maxpkt);
- c = a;
- cio->Qio.tok = Tdtoksetup;
- cio->Qio.toggle = Tddata0;
- if(epio(ep, &cio->Qio, a, Rsetuplen, 0) < Rsetuplen)
- error(Eio);
- a = c + Rsetuplen;
- count -= Rsetuplen;
- cio->Qio.toggle = Tddata1;
- if(c[Rtype] & Rd2h){
- cio->Qio.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 ohci");
- a = cio->data = smalloc(len+1);
- }else{
- cio->Qio.tok = Tdtokout;
- len = count;
- }
- if(len > 0){
- if(waserror())
- len = -1;
- else{
- len = epio(ep, &cio->Qio, a, len, 0);
- poperror();
- }
- }
- if(c[Rtype] & Rd2h){
- count = Rsetuplen;
- cio->ndata = len;
- cio->Qio.tok = Tdtokout;
- }else{
- if(len < 0)
- count = -1;
- else
- count = Rsetuplen + len;
- cio->Qio.tok = Tdtokin;
- }
- cio->Qio.toggle = Tddata1;
- epio(ep, &cio->Qio, nil, 0, 0);
- qunlock(&cio->Qio.ql);
- poperror();
- ddeprint("epctlio cio %#p return %ld\n", cio, count);
- return count;
- }
- /*
- * Put new samples in the dummy Td.
- * BUG: This does only a transfer per Td. We could do up to 8.
- */
- static int32_t
- putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, unsigned char *b, int32_t count)
- {
- Td *td;
- uint32_t n;
- td = pa2ptr(iso->Qio.ed->tail);
- n = count;
- if(n > td->nbytes - BLEN(td->bp))
- n = td->nbytes - BLEN(td->bp);
- assert(td->bp->wp + n <= td->bp->lim);
- memmove(td->bp->wp, b, n);
- td->bp->wp += n;
- if(BLEN(td->bp) == td->nbytes){ /* full Td: activate it */
- ilock(&ctlr->l);
- isoadvance(ep, iso, td);
- iunlock(&ctlr->l);
- }
- return n;
- }
- static int32_t
- episowrite(Ep *ep, void *a, int32_t count)
- {
- Proc *up = externup();
- int32_t tot, nw;
- char *err;
- unsigned char *b;
- Ctlr *ctlr;
- Isoio *iso;
- ctlr = ep->hp->Hciimpl.aux;
- iso = ep->aux;
- iso->Qio.debug = ep->debug;
- qlock(&iso->Qio.ql);
- if(waserror()){
- qunlock(&iso->Qio.ql);
- nexterror();
- }
- diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
- ilock(&ctlr->l);
- if(iso->Qio.state == Qclose){
- iunlock(&ctlr->l);
- error(iso->Qio.err ? iso->Qio.err : Eio);
- }
- iso->Qio.state = Qrun;
- b = a;
- for(tot = 0; tot < count; tot += nw){
- while(isocanwrite(iso) == 0){
- iunlock(&ctlr->l);
- diprint("ohci: episowrite: %#p sleep\n", iso);
- if(waserror()){
- if(iso->Qio.err == nil)
- iso->Qio.err = "I/O timed out";
- ilock(&ctlr->l);
- break;
- }
- tsleep(&iso->Qio.Rendez, isocanwrite, iso, ep->tmout);
- poperror();
- ilock(&ctlr->l);
- }
- err = iso->Qio.err;
- iso->Qio.err = nil;
- if(iso->Qio.state == Qclose || err != nil){
- iunlock(&ctlr->l);
- error(err ? err : Eio);
- }
- if(iso->Qio.state != Qrun)
- panic("episowrite: iso not running");
- iunlock(&ctlr->l); /* We could page fault here */
- nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
- ilock(&ctlr->l);
- }
- if(iso->Qio.state != Qclose)
- iso->Qio.state = Qdone;
- iunlock(&ctlr->l);
- err = iso->Qio.err; /* in case it failed early */
- iso->Qio.err = nil;
- qunlock(&iso->Qio.ql);
- poperror();
- if(err != nil)
- error(err);
- diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot);
- return tot;
- }
- static int32_t
- epwrite(Ep *ep, void *a, int32_t count)
- {
- Proc *up = externup();
- Qio *io;
- Ctlio *cio;
- uint32_t delta;
- unsigned char *b;
- int32_t tot, nw;
- if(ep->aux == nil)
- panic("ohci: 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;
- assert(a != nil);
- 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:
- return episowrite(ep, a, count);
- default:
- panic("ohci: epwrite: bad ep ttype %d", ep->ttype);
- }
- return -1;
- }
- static Ed*
- newed(Ctlr *ctlr, Ep *ep, Qio *io, char *c)
- {
- Proc *up = externup();
- Ed *ed;
- Td *td;
- ed = io->ed = edalloc(); /* no errors raised here, really */
- td = tdalloc();
- td->ep = ep;
- td->io = io;
- ed->tail = ptr2pa(td);
- ed->head = ptr2pa(td);
- ed->tds = td;
- ed->ep = ep;
- ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift;
- if(ep->ttype == Tiso)
- ed->ctrl |= Ediso;
- if(waserror()){
- edfree(ed);
- io->ed = nil;
- nexterror();
- }
- /* For setup endpoints we start with the config address */
- if(ep->ttype != Tctl)
- edsetaddr(io->ed, io->usbid);
- if(ep->dev->speed == Lowspeed)
- ed->ctrl |= Edlow;
- switch(io->tok){
- case Tdtokin:
- ed->ctrl |= Edin;
- break;
- case Tdtokout:
- ed->ctrl |= Edout;
- break;
- default:
- ed->ctrl |= Edtddir; /* Td will say */
- break;
- }
- switch(ep->ttype){
- case Tctl:
- ilock(&ctlr->l);
- edlinked(ed, ctlhd(ctlr));
- setctlhd(ctlr, ed);
- iunlock(&ctlr->l);
- break;
- case Tbulk:
- ilock(&ctlr->l);
- edlinked(ed, bulkhd(ctlr));
- setbulkhd(ctlr, ed);
- iunlock(&ctlr->l);
- break;
- case Tintr:
- case Tiso:
- ilock(&ctlr->l);
- schedq(ctlr, io, ep->pollival);
- iunlock(&ctlr->l);
- break;
- default:
- panic("ohci: newed: bad ttype");
- }
- poperror();
- return ed;
- }
- static void
- isoopen(Ctlr *ctlr, Ep *ep)
- {
- Td *td, *edtds;
- Isoio *iso;
- int i;
- iso = ep->aux;
- iso->Qio.usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
- iso->Qio.bw = ep->hz * ep->samplesz; /* bytes/sec */
- if(ep->mode != OWRITE){
- print("ohci: bug: iso input streams not implemented\n");
- error("ohci iso input streams not implemented");
- }else
- iso->Qio.tok = Tdtokout;
- iso->left = 0;
- iso->nerrs = 0;
- iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes);
- iso->nframes = 1000 / ep->pollival;
- if(iso->nframes < 10){
- print("ohci: isoopen: less than 10 frames; using 10.\n");
- iso->nframes = 10;
- }
- iso->navail = iso->nframes;
- iso->atds = edtds = nil;
- for(i = 0; i < iso->nframes-1; i++){ /* -1 for dummy */
- td = tdalloc();
- td->ep = ep;
- td->io = &iso->Qio;
- td->bp = allocb(ep->maxpkt);
- td->anext = iso->atds; /* link as avail */
- iso->atds = td;
- td->next = edtds;
- edtds = td;
- }
- newed(ctlr, ep, &iso->Qio, "iso"); /* allocates a dummy td */
- iso->Qio.ed->tds->bp = allocb(ep->maxpkt); /* but not its block */
- iso->Qio.ed->tds->next = edtds;
- isodtdinit(ep, iso, iso->Qio.ed->tds);
- }
- /*
- * 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;
- Qio *io;
- Ctlio *cio;
- uint32_t usbid;
- ctlr = ep->hp->Hciimpl.aux;
- deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux != nil)
- panic("ohci: epopen called with open ep");
- if(waserror()){
- free(ep->aux);
- ep->aux = nil;
- nexterror();
- }
- switch(ep->ttype){
- case Tnone:
- error("endpoint not configured");
- case Tiso:
- ep->aux = smalloc(sizeof(Isoio));
- isoopen(ctlr, ep);
- break;
- case Tctl:
- cio = ep->aux = smalloc(sizeof(Ctlio));
- cio->Qio.debug = ep->debug;
- cio->ndata = -1;
- cio->data = nil;
- cio->Qio.tok = -1; /* invalid; Tds will say */
- if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
- break;
- newed(ctlr, ep, &cio->Qio, "epc");
- break;
- case Tbulk:
- ep->pollival = 1; /* assume this; doesn't really matter */
- /* and fall... */
- case Tintr:
- io = ep->aux = smalloc(sizeof(Qio)*2);
- io[OREAD].debug = io[OWRITE].debug = ep->debug;
- usbid = (ep->nb<<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].usbid = usbid;
- io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
- newed(ctlr, ep, io+OWRITE, "epw");
- }
- if(ep->mode != OWRITE){
- if(ep->toggle[OREAD] != 0)
- io[OREAD].toggle = Tddata1;
- else
- io[OREAD].toggle = Tddata0;
- io[OREAD].tok = Tdtokin;
- io[OREAD].usbid = usbid;
- io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
- newed(ctlr, ep, io+OREAD, "epr");
- }
- break;
- }
- deprint("ohci: epopen done:\n");
- if(debug || ep->debug)
- dump(ep->hp);
- poperror();
- }
- static void
- cancelio(Ep *ep, Qio *io)
- {
- Proc *up = externup();
- Ed *ed;
- Ctlr *ctlr;
- ctlr = ep->hp->Hciimpl.aux;
- ilock(&ctlr->l);
- if(io == nil || io->state == Qclose){
- assert(io == nil || io->ed == nil);
- iunlock(&ctlr->l);
- return;
- }
- ed = io->ed;
- io->state = Qclose;
- io->err = Eio;
- aborttds(io);
- iunlock(&ctlr->l);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, Abortdelay);
- poperror();
- }
- wakeup(&io->Rendez);
- qlock(&io->ql);
- /* wait for epio if running */
- qunlock(&io->ql);
- ilock(&ctlr->l);
- switch(ep->ttype){
- case Tctl:
- unlinkctl(ctlr, ed);
- break;
- case Tbulk:
- unlinkbulk(ctlr, ed);
- break;
- case Tintr:
- case Tiso:
- unschedq(ctlr, io);
- break;
- default:
- panic("ohci cancelio: bad ttype");
- }
- iunlock(&ctlr->l);
- edfree(io->ed);
- io->ed = nil;
- }
- static void
- epclose(Ep *ep)
- {
- Ctlio *cio;
- Isoio *iso;
- Qio *io;
- deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux == nil)
- panic("ohci: epclose called with closed ep");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- cancelio(ep, &cio->Qio);
- free(cio->data);
- cio->data = nil;
- break;
- case Tbulk:
- case Tintr:
- io = ep->aux;
- if(ep->mode != OWRITE){
- cancelio(ep, &io[OREAD]);
- if(io[OREAD].toggle == Tddata1)
- ep->toggle[OREAD] = 1;
- }
- if(ep->mode != OREAD){
- cancelio(ep, &io[OWRITE]);
- if(io[OWRITE].toggle == Tddata1)
- ep->toggle[OWRITE] = 1;
- }
- break;
- case Tiso:
- iso = ep->aux;
- cancelio(ep, &iso->Qio);
- break;
- default:
- panic("epclose: bad ttype %d", ep->ttype);
- }
- deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb);
- free(ep->aux);
- ep->aux = nil;
- }
- static int
- portreset(Hci *hp, int port, int on)
- {
- Proc *up = externup();
- Ctlr *ctlr;
- Ohci *ohci;
- if(on == 0)
- return 0;
- ctlr = hp->Hciimpl.aux;
- qlock(&ctlr->resetl);
- if(waserror()){
- qunlock(&ctlr->resetl);
- nexterror();
- }
- ilock(&ctlr->l);
- ohci = ctlr->ohci;
- ohci->rhportsts[port - 1] = Spp;
- if((ohci->rhportsts[port - 1] & Ccs) == 0){
- iunlock(&ctlr->l);
- error("port not connected");
- }
- ohci->rhportsts[port - 1] = Spr;
- while((ohci->rhportsts[port - 1] & Prsc) == 0){
- iunlock(&ctlr->l);
- dprint("ohci: portreset, wait for reset complete\n");
- ilock(&ctlr->l);
- }
- ohci->rhportsts[port - 1] = Prsc;
- iunlock(&ctlr->l);
- poperror();
- qunlock(&ctlr->resetl);
- return 0;
- }
- static int
- portenable(Hci *hp, int port, int on)
- {
- Proc *up = externup();
- Ctlr *ctlr;
- ctlr = hp->Hciimpl.aux;
- dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
- qlock(&ctlr->resetl);
- if(waserror()){
- qunlock(&ctlr->resetl);
- nexterror();
- }
- ilock(&ctlr->l);
- if(on)
- ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
- else
- ctlr->ohci->rhportsts[port - 1] = Cpe;
- iunlock(&ctlr->l);
- tsleep(&up->sleep, return0, 0, Enabledelay);
- poperror();
- qunlock(&ctlr->resetl);
- return 0;
- }
- static int
- portstatus(Hci *hp, int port)
- {
- int v;
- Ctlr *ub;
- uint32_t ohcistatus;
- /*
- * We must return status bits as a
- * get port status hub request would do.
- */
- ub = hp->Hciimpl.aux;
- ohcistatus = ub->ohci->rhportsts[port - 1];
- v = 0;
- if(ohcistatus & Ccs)
- v |= HPpresent;
- if(ohcistatus & Pes)
- v |= HPenable;
- if(ohcistatus & Pss)
- v |= HPsuspend;
- if(ohcistatus & Prs)
- v |= HPreset;
- else {
- /* port is not in reset; these potential writes are ok */
- if(ohcistatus & Csc){
- v |= HPstatuschg;
- ub->ohci->rhportsts[port - 1] = Csc;
- }
- if(ohcistatus & Pesc){
- v |= HPchange;
- ub->ohci->rhportsts[port - 1] = Pesc;
- }
- }
- if(ohcistatus & Lsda)
- v |= HPslow;
- if(v & (HPstatuschg|HPchange))
- ddprint("ohci port %d sts %#lx hub sts %#x\n", port, ohcistatus, v);
- return v;
- }
- static void
- dumpohci(Ctlr *ctlr)
- {
- int i;
- uint32_t *ohci;
- ohci = &ctlr->ohci->revision;
- print("ohci registers: \n");
- for(i = 0; i < sizeof(Ohci)/sizeof(uint32_t); i++)
- if(i < 3 || ohci[i] != 0)
- print("\t[%#2.2x]\t%#8.8lx\n", i * 4, ohci[i]);
- print("\n");
- }
- static void
- init(Hci *hp)
- {
- Ctlr *ctlr;
- Ohci *ohci;
- int i;
- uint32_t ival, ctrl, fmi;
- ctlr = hp->Hciimpl.aux;
- dprint("ohci %#p init\n", ctlr->ohci);
- ohci = ctlr->ohci;
- fmi = ctlr->ohci->fminterval;
- ctlr->ohci->cmdsts = Shcr; /* reset the block */
- while(ctlr->ohci->cmdsts & Shcr)
- delay(1); /* wait till reset complete, Ohci says 10us max. */
- ctlr->ohci->fminterval = fmi;
- /*
- * now that soft reset is done we are in suspend state.
- * Setup registers which take in suspend state
- * (will only be here for 2ms).
- */
- ctlr->ohci->hcca = ptr2pa(ctlr->hcca);
- setctlhd(ctlr, nil);
- ctlr->ohci->ctlcurred = 0;
- setbulkhd(ctlr, nil);
- ctlr->ohci->bulkcurred = 0;
- ohci->intrenable = Mie | Wdh | Ue;
- ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper;
- /* set frame after operational */
- ohci->rhdesca = Nps; /* no power switching */
- if(ohci->rhdesca & Nps){
- dprint("ohci: ports are not power switched\n");
- }else{
- dprint("ohci: ports are power switched\n");
- ohci->rhdesca &= ~Psm;
- ohci->rhsts &= ~Lpsc;
- }
- for(i = 0; i < ctlr->nports; i++) /* paranoia */
- ohci->rhportsts[i] = 0; /* this has no effect */
- delay(50);
- for(i = 0; i < ctlr->nports; i++){
- ohci->rhportsts[i] = Spp;
- if((ohci->rhportsts[i] & Ccs) != 0)
- ohci->rhportsts[i] |= Spr;
- }
- delay(100);
- ctrl = ohci->control;
- if((ctrl & Cfsmask) != Cfsoper){
- ctrl = (ctrl & ~Cfsmask) | Cfsoper;
- ohci->control = ctrl;
- ohci->rhsts = Lpsc;
- }
- ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift);
- ohci->fminterval = ival | (5120 << Fmaxpktshift);
- if(debug > 1)
- dumpohci(ctlr);
- }
- static void
- scanpci(void)
- {
- uint32_t mem;
- Ctlr *ctlr;
- Pcidev *p;
- int i;
- static int already = 0;
- if(already)
- return;
- already = 1;
- p = nil;
- while((p = pcimatch(p, 0, 0)) != nil){
- /*
- * Find Ohci controllers (Programming Interface = 0x10).
- */
- if(p->ccrb != Pcibcserial || p->ccru != Pciscusb ||
- p->ccrp != 0x10)
- continue;
- mem = p->mem[0].bar & ~0x0F;
- dprint("ohci: %x/%x port 0x%lx size 0x%x irq %d\n",
- p->vid, p->did, mem, p->mem[0].size, p->intl);
- if(mem == 0){
- print("ohci: failed to map registers\n");
- continue;
- }
- if(p->intl == 0xFF || p->intl == 0) {
- print("ohci: no irq assigned for port %#lx\n", mem);
- continue;
- }
- ctlr = malloc(sizeof(Ctlr));
- if (ctlr == nil)
- panic("ohci: out of memory");
- ctlr->pcidev = p;
- ctlr->ohci = vmap(mem, p->mem[0].size);
- dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci);
- pcisetbme(p);
- pcisetpms(p, 0);
- for(i = 0; i < Nhcis; i++)
- if(ctlrs[i] == nil){
- ctlrs[i] = ctlr;
- break;
- }
- if(i == Nhcis)
- print("ohci: bug: no more controllers\n");
- }
- }
- static void
- usbdebug(Hci *hci, int d)
- {
- debug = d;
- }
- /*
- * build the periodic scheduling tree:
- * framesize must be a multiple of the tree size
- */
- static void
- mkqhtree(Ctlr *ctlr)
- {
- int i, n, d, o, leaf0, depth;
- Ed **tree;
- Qtree *qt;
- depth = flog2(32);
- n = (1 << (depth+1)) - 1;
- qt = mallocz(sizeof(*qt), 1);
- if(qt == nil)
- panic("usb: can't allocate scheduling tree");
- qt->nel = n;
- qt->depth = depth;
- qt->bw = mallocz(n * sizeof(qt->bw), 1);
- qt->root = tree = mallocz(n * sizeof(Ed *), 1);
- if(qt->bw == nil || qt->root == nil)
- panic("usb: can't allocate scheduling tree");
- for(i = 0; i < n; i++){
- if((tree[i] = edalloc()) == nil)
- panic("mkqhtree");
- tree[i]->ctrl = (8 << Edmpsshift); /* not needed */
- tree[i]->ctrl |= Edskip;
- if(i > 0)
- edlinked(tree[i], tree[(i-1)/2]);
- else
- edlinked(tree[i], nil);
- }
- ctlr->ntree = i;
- dprint("ohci: tree: %d endpoints allocated\n", i);
- /* distribute leaves evenly round the frame list */
- leaf0 = n / 2;
- for(i = 0; i < 32; i++){
- o = 0;
- for(d = 0; d < depth; d++){
- o <<= 1;
- if(i & (1 << d))
- o |= 1;
- }
- if(leaf0 + o >= n){
- print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
- break;
- }
- ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]);
- }
- ctlr->tree = qt;
- }
- static void
- ohcimeminit(Ctlr *ctlr)
- {
- Hcca *hcca;
- edfree(edalloc()); /* allocate pools now */
- tdfree(tdalloc());
- hcca = mallocalign(sizeof(Hcca), 256, 0, 0);
- if(hcca == nil)
- panic("usbhreset: no memory for Hcca");
- memset(hcca, 0, sizeof(*hcca));
- ctlr->hcca = hcca;
- mkqhtree(ctlr);
- }
- static void
- ohcireset(Ctlr *ctlr)
- {
- ilock(&ctlr->l);
- dprint("ohci %#p reset\n", ctlr->ohci);
- /*
- * usually enter here in reset, wait till its through,
- * then do our own so we are on known timing conditions.
- * Is this needed?
- */
- delay(100);
- ctlr->ohci->control = 0;
- delay(100);
- /* legacy support register: turn off lunacy mode */
- pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
- iunlock(&ctlr->l);
- }
- static void
- shutdown(Hci *hp)
- {
- Ctlr *ctlr;
- ctlr = hp->Hciimpl.aux;
- ilock(&ctlr->l);
- ctlr->ohci->intrdisable = Mie;
- ctlr->ohci->control = 0;
- coherence();
- delay(100);
- iunlock(&ctlr->l);
- }
- static int
- reset(Hci *hp)
- {
- int i;
- Ctlr *ctlr;
- Pcidev *p;
- static Lock resetlck;
- 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->ISAConf.port == 0 || hp->ISAConf.port == (uintptr)ctlr->ohci){
- ctlr->active = 1;
- break;
- }
- }
- iunlock(&resetlck);
- if(ctlrs[i] == nil || i == Nhcis)
- return -1;
- if(ctlr->ohci->control == ~0)
- return -1;
- p = ctlr->pcidev;
- hp->Hciimpl.aux = ctlr;
- hp->ISAConf.port = (uintptr)ctlr->ohci;
- hp->ISAConf.irq = p->intl;
- hp->tbdf = p->tbdf;
- ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff;
- ohcireset(ctlr);
- ohcimeminit(ctlr);
- /*
- * Linkage to the generic HCI driver.
- */
- hp->Hciimpl.init = init;
- hp->Hciimpl.dump = dump;
- hp->Hciimpl.interrupt = interrupt;
- hp->Hciimpl.epopen = epopen;
- hp->Hciimpl.epclose = epclose;
- hp->Hciimpl.epread = epread;
- hp->Hciimpl.epwrite = epwrite;
- hp->Hciimpl.seprintep = seprintep;
- hp->Hciimpl.portenable = portenable;
- hp->Hciimpl.portreset = portreset;
- hp->Hciimpl.portstatus = portstatus;
- hp->Hciimpl.shutdown = shutdown;
- hp->Hciimpl.debug = usbdebug;
- hp->ISAConf.type = "ohci";
- return 0;
- }
- void
- usbohcilink(void)
- {
- addhcitype("ohci", reset);
- }
|