1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466 |
- /*
- * USB Enhanced Host Controller Interface (EHCI) driver
- * High speed USB 2.0.
- *
- * BUGS:
- * - Too many delays and ilocks.
- * - bandwidth admission control must be done per-frame.
- * - requires polling (some controllers miss interrupts).
- * - 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 "usb.h"
- #include "usbehci.h"
- typedef struct Ctlio Ctlio;
- typedef struct Ctlr Ctlr;
- typedef struct Itd Itd;
- typedef struct Sitd Sitd;
- typedef struct Qtd Qtd;
- typedef struct Td Td;
- typedef struct Qh Qh;
- typedef struct Fstn Fstn;
- typedef union Ed Ed;
- typedef struct Edpool Edpool;
- typedef struct Qio Qio;
- typedef struct Qtree Qtree;
- typedef struct Isoio Isoio;
- typedef struct Poll Poll;
- /*
- * EHCI interface registers and bits
- */
- enum
- {
- /* Queue states (software) */
- Qidle = 0,
- Qinstall,
- Qrun,
- Qdone,
- Qclose,
- Qfree,
- Enabledelay = 100, /* waiting for a port to enable */
- Abortdelay = 5, /* delay after cancelling Tds (ms) */
- Incr = 64, /* for pools of Tds, Qhs, etc. */
- Align = 128, /* in bytes for all those descriptors */
- /* Keep them as a power of 2, lower than ctlr->nframes */
- /* Also, keep Nisoframes >= Nintrleafs */
- Nintrleafs = 32, /* nb. of leaf frames in intr. tree */
- Nisoframes = 64, /* nb. of iso frames (in window) */
- /*
- * HW constants
- */
- /* Itd bits (csw[]) */
- Itdactive = 0x80000000, /* execution enabled */
- Itddberr = 0x40000000, /* data buffer error */
- Itdbabble = 0x20000000, /* babble error */
- Itdtrerr = 0x10000000, /* transaction error */
- Itdlenshift = 16, /* transaction length */
- Itdlenmask = 0xFFF,
- Itdioc = 0x00008000, /* interrupt on complete */
- Itdpgshift = 12, /* page select field */
- Itdoffshift = 0, /* transaction offset */
- /* Itd bits, buffer[] */
- Itdepshift = 8, /* endpoint address (buffer[0]) */
- Itddevshift = 0, /* device address (buffer[0]) */
- Itdin = 0x800, /* is input (buffer[1]) */
- Itdout = 0,
- Itdmaxpktshift = 0, /* max packet (buffer[1]) */
- Itdntdsshift = 0, /* nb. of tds per µframe (buffer[2]) */
- Itderrors = Itddberr|Itdbabble|Itdtrerr,
- /* Sitd bits (epc) */
- Stdin = 0x80000000, /* input direction */
- Stdportshift = 24, /* hub port number */
- Stdhubshift = 16, /* hub address */
- Stdepshift = 8, /* endpoint address */
- Stddevshift = 0, /* device address */
- /* Sitd bits (mfs) */
- Stdssmshift = 0, /* split start mask */
- Stdscmshift = 8, /* split complete mask */
- /* Sitd bits (csw) */
- Stdioc = 0x80000000, /* interrupt on complete */
- Stdpg = 0x40000000, /* page select */
- Stdlenshift = 16, /* total bytes to transfer */
- Stdlenmask = 0x3FF,
- Stdactive = 0x00000080, /* active */
- Stderr = 0x00000040, /* tr. translator error */
- Stddberr = 0x00000020, /* data buffer error */
- Stdbabble = 0x00000010, /* babble error */
- Stdtrerr = 0x00000008, /* transanction error */
- Stdmmf = 0x00000004, /* missed µframe */
- Stddcs = 0x00000002, /* do complete split */
- Stderrors = Stderr|Stddberr|Stdbabble|Stdtrerr|Stdmmf,
- /* Sitd bits buffer[1] */
- Stdtpall = 0x00000000, /* all payload here (188 bytes) */
- Stdtpbegin = 0x00000008, /* first payload for fs trans. */
- Stdtcntmask = 0x00000007, /* T-count */
- /* Td bits (csw) */
- Tddata1 = 0x80000000, /* data toggle 1 */
- Tddata0 = 0x00000000, /* data toggle 0 */
- Tdlenshift = 16, /* total bytes to transfer */
- Tdlenmask = 0x7FFF,
- Tdmaxpkt = 0x5000, /* max buffer for a Td */
- Tdioc = 0x00008000, /* interrupt on complete */
- Tdpgshift = 12, /* current page */
- Tdpgmask = 7,
- Tderr1 = 0x00000400, /* bit 0 of error counter */
- Tderr2 = 0x00000800, /* bit 1 of error counter */
- Tdtokout = 0x00000000, /* direction out */
- Tdtokin = 0x00000100, /* direction in */
- Tdtoksetup = 0x00000200, /* setup packet */
- Tdtok = 0x00000300, /* token bits */
- Tdactive = 0x00000080, /* active */
- Tdhalt = 0x00000040, /* halted */
- Tddberr = 0x00000020, /* data buffer error */
- Tdbabble = 0x00000010, /* babble error */
- Tdtrerr = 0x00000008, /* transanction error */
- Tdmmf = 0x00000004, /* missed µframe */
- Tddcs = 0x00000002, /* do complete split */
- Tdping = 0x00000001, /* do ping */
- Tderrors = Tdhalt|Tddberr|Tdbabble|Tdtrerr|Tdmmf,
- /* Qh bits (eps0) */
- Qhrlcmask = 0xF, /* nak reload count */
- Qhrlcshift = 28, /* nak reload count */
- Qhnhctl = 0x08000000, /* not-high speed ctl */
- Qhmplmask = 0x7FF, /* max packet */
- Qhmplshift = 16,
- Qhhrl = 0x00008000, /* head of reclamation list */
- Qhdtc = 0x00004000, /* data toggle ctl. */
- Qhint = 0x00000080, /* inactivate on next transition */
- Qhspeedmask = 0x00003000, /* speed bits */
- Qhfull = 0x00000000, /* full speed */
- Qhlow = 0x00001000, /* low speed */
- Qhhigh = 0x00002000, /* high speed */
- /* Qh bits (eps1) */
- Qhmultshift = 30, /* multiple tds per µframe */
- Qhmultmask = 3,
- Qhportshift = 23, /* hub port number */
- Qhhubshift = 16, /* hub address */
- Qhscmshift = 8, /* split completion mask bits */
- Qhismshift = 0, /* interrupt sched. mask bits */
- };
- /*
- * Endpoint tree (software)
- */
- struct Qtree
- {
- int nel;
- int depth;
- ulong* bw;
- Qh** root;
- };
- /*
- * One per endpoint per direction, to control I/O.
- */
- 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 */
- ulong iotime; /* last I/O time; to hold interrupt polls */
- int debug; /* debug flag from the endpoint */
- char* err; /* error string */
- char* tag; /* debug (no room in Qh for this) */
- ulong bw;
- };
- struct Ctlio
- {
- Qio; /* a single Qio for each RPC */
- uchar* 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; /* number of frames ([S]Itds) used */
- uchar* data; /* iso data buffers if not embedded */
- char* err; /* error string */
- int nerrs; /* nb of consecutive I/O errors */
- ulong maxsize; /* ntds * ep->maxpkt */
- long nleft; /* number of bytes left from last write */
- int debug; /* debug flag from the endpoint */
- int hs; /* is high speed? */
- Isoio* next; /* in list of active Isoios */
- ulong td0frno; /* first frame used in ctlr */
- union{
- Itd* tdi; /* next td processed by interrupt */
- Sitd* stdi;
- };
- union{
- Itd* tdu; /* next td for user I/O in tdps */
- Sitd* stdu;
- };
- union{
- Itd** itdps; /* itdps[i]: ptr to Itd for i-th frame or nil */
- Sitd** sitdps; /* sitdps[i]: ptr to Sitd for i-th frame or nil */
- ulong** tdps; /* same thing, as seen by hw */
- };
- };
- struct Poll
- {
- Lock;
- Rendez;
- int must;
- int does;
- };
- struct Ctlr
- {
- Rendez; /* for waiting to async advance doorbell */
- Lock; /* for ilock. qh lists and basic ctlr I/O */
- QLock portlck; /* for port resets/enable... (and doorbell) */
- int active; /* in use or not */
- Ecapio* capio; /* Capability i/o regs */
- Eopio* opio; /* Operational i/o regs */
- int nframes; /* 1024, 512, or 256 frames in the list */
- ulong* frames; /* periodic frame list (hw) */
- Qh* qhs; /* async Qh circular list for bulk/ctl */
- Qtree* tree; /* tree of Qhs for the periodic list */
- int ntree; /* number of dummy qhs in tree */
- Qh* intrqhs; /* list of (not dummy) qhs in tree */
- Isoio* iso; /* list of active Iso I/O */
- ulong load;
- ulong isoload;
- int nintr; /* number of interrupts attended */
- int ntdintr; /* number of intrs. with something to do */
- int nqhintr; /* number of async td intrs. */
- int nisointr; /* number of periodic td intrs. */
- int nreqs;
- Poll poll;
- };
- struct Edpool
- {
- Lock;
- Ed* free;
- int nalloc;
- int ninuse;
- int nfree;
- };
- /*
- * We use the 64-bit version for Itd, Sitd, Td, and Qh.
- * If the ehci is 64-bit capable it assumes we are using those
- * structures even when the system is 32 bits.
- */
- /*
- * Iso transfer descriptor. hw. 92 bytes, 104 bytes total
- * aligned to 32.
- */
- struct Itd
- {
- ulong link; /* to next hw struct */
- ulong csw[8]; /* sts/length/pg/off. updated by hw */
- ulong buffer[7]; /* buffer pointers, addrs, maxsz */
- ulong xbuffer[7]; /* high 32 bits of buffer for 64-bits */
- /* software */
- Itd* next;
- ulong ndata; /* number of bytes in data */
- ulong mdata; /* max number of bytes in data */
- uchar* data;
- };
- /*
- * Split transaction iso transfer descriptor.
- * hw: 36 bytes, 52 bytes total. aligned to 32.
- */
- struct Sitd
- {
- ulong link; /* to next hw struct */
- ulong epc; /* static endpoint state. addrs */
- ulong mfs; /* static endpoint state. µ-frame sched. */
- ulong csw; /* transfer state. updated by hw */
- ulong buffer[2]; /* buf. ptr/offset. offset updated by hw */
- /* buf ptr/TP/Tcnt. TP/Tcnt updated by hw */
- ulong blink; /* back pointer */
- ulong xbuffer[2]; /* high 32 bits of buffer for 64-bits */
- /* software */
- Sitd* next;
- ulong ndata; /* number of bytes in data */
- ulong mdata; /* max number of bytes in data */
- uchar* data;
- };
- /*
- * Queue element transfer descriptor.
- * hw: first 52 bytes; total 68+sbuff bytes aligned to 32 bytes.
- */
- struct Td
- {
- ulong nlink; /* to next Td */
- ulong alink; /* alternate link to next Td */
- ulong csw; /* cmd/sts. updated by hw */
- ulong buffer[5]; /* buf ptrs. offset updated by hw */
- ulong xbuffer[5]; /* high 32 bits of buffer for 64-bits */
- Td* next; /* in qh or Isoio or free list */
- ulong ndata; /* bytes available/used at data */
- uchar* data; /* pointer to actual data */
- uchar* buff; /* allocated data buffer or nil */
- uchar sbuff[1]; /* first byte of embedded buffer */
- };
- /*
- * Queue head. Aligned to 32 bytes.
- * hw uses the first 68 bytes, 92 total.
- */
- struct Qh
- {
- ulong link; /* to next Qh in round robin */
- ulong eps0; /* static endpoint state. addrs */
- ulong eps1; /* static endpoint state. µ-frame sched. */
- /* updated by hw */
- ulong clink; /* current Td (No Term bit here!) */
- ulong nlink; /* to next Td */
- ulong alink; /* alternate link to next Td */
- ulong csw; /* cmd/sts. updated by hw */
- ulong buffer[5]; /* buf ptrs. offset updated by hw */
- ulong xbuffer[5]; /* high 32 bits of buffer for 64-bits */
- /* software */
- Qh* next; /* in controller list/tree of Qhs */
- int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
- Qio* io; /* for this queue */
- Td* tds; /* for this queue */
- int sched; /* slot for for intr. Qhs */
- Qh* inext; /* next in list of intr. qhs */
- };
- /*
- * We can avoid frame span traversal nodes if we don't span frames.
- * Just schedule transfer that can fit on the current frame and
- * wait a little bit otherwise.
- */
- /*
- * Software. Ehci descriptors provided by pool.
- * There are soo few because we avoid using Fstn.
- */
- union Ed
- {
- Ed* next; /* in free list */
- Qh qh;
- Td td;
- Itd itd;
- Sitd sitd;
- uchar align[Align];
- };
- typedef struct Kwusb Kwusb;
- typedef struct Usbwin Usbwin;
- struct Kwusb { /* at offset 0x300 from Addrusb */
- ulong bcs; /* bridge ctl & sts */
- uchar _pad0[0x310-0x304];
- ulong bic; /* bridge intr. cause */
- ulong bim; /* bridge intr. mask */
- ulong _pad1;
- ulong bea; /* bridge error addr. */
- struct Usbwin {
- ulong ctl;
- ulong base;
- ulong _pad2[2];
- } win[4];
- ulong phycfg; /* phy config. */
- uchar _pad3[0x400-0x364];
- ulong pwrctl; /* power control */
- uchar _pad4[0x410-0x404];
- ulong phypll; /* phy pll control */
- uchar _pad5[0x420-0x414];
- ulong phytxctl; /* phy transmit control */
- uchar _pad6[0x430-0x424];
- ulong phyrxctl; /* phy receive control */
- uchar _pad7[0x440-0x434];
- ulong phyivref; /* phy ivref control */
- };
- enum {
- /* Kwusb->win[i].ctl bits */
- Winenable = 1<<0,
- };
- #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
- #define TRUNC(x, sz) ((x) & ((sz)-1))
- #define LPTR(q) ((ulong*)KADDR((q) & ~0x1F))
- static int debug;
- static Edpool edpool;
- static Ctlr* ctlrs[Nhcis];
- static char Ebug[] = "not yet implemented";
- static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };
- static void
- ehcirun(Ctlr *ctlr, int on)
- {
- int i;
- Eopio *opio;
- ddprint("ehci %#p %s\n", ctlr->capio, on ? "starting" : "halting");
- opio = ctlr->opio;
- if(on)
- opio->cmd |= Crun;
- else
- opio->cmd = Cstop;
- for(i = 0; i < 100; i++)
- if(on == 0 && (opio->sts & Shalted) != 0)
- break;
- else if(on != 0 && (opio->sts & Shalted) == 0)
- break;
- else
- delay(1);
- if(i == 100)
- print("ehci %#p %s cmd timed out\n",
- ctlr->capio, on ? "run" : "halt");
- ddprint("ehci %#p cmd %#ulx sts %#ulx\n", ctlr->capio, opio->cmd, opio->sts);
- }
- static void*
- edalloc(void)
- {
- Ed *ed, *pool;
- int i;
- lock(&edpool);
- if(edpool.free == nil){
- pool = xspanalloc(Incr*sizeof(Ed), Align, 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;
- dprint("ehci: edalloc: %d eds\n", edpool.nalloc);
- }
- ed = edpool.free;
- edpool.free = ed->next;
- edpool.ninuse++;
- edpool.nfree--;
- unlock(&edpool);
- memset(ed, 0, sizeof(Ed)); /* safety */
- assert(((ulong)ed & 0xF) == 0);
- return ed;
- }
- static void
- edfree(void *a)
- {
- Ed *ed;
- ed = a;
- lock(&edpool);
- ed->next = edpool.free;
- edpool.free = ed;
- edpool.ninuse--;
- edpool.nfree++;
- unlock(&edpool);
- }
- /*
- * Allocate and so same initialization.
- * Free after releasing buffers used.
- */
- static Itd*
- itdalloc(void)
- {
- Itd *td;
- td = edalloc();
- td->link = Lterm;
- return td;
- }
- static void
- itdfree(Itd *td)
- {
- edfree(td);
- }
- static Sitd*
- sitdalloc(void)
- {
- Sitd *td;
- td = edalloc();
- td->link = td->blink = Lterm;
- return td;
- }
- static void
- sitdfree(Sitd *td)
- {
- edfree(td);
- }
- static Td*
- tdalloc(void)
- {
- Td *td;
- td = edalloc();
- td->nlink = td->alink = Lterm;
- return td;
- }
- static void
- tdfree(Td *td)
- {
- if(td == nil)
- return;
- free(td->buff);
- edfree(td);
- }
- static void
- tdlinktd(Td *td, Td *next)
- {
- td->next = next;
- td->alink = Lterm;
- if(next == nil)
- td->nlink = Lterm;
- else
- td->nlink = PADDR(next);
- }
- static Qh*
- qhlinkqh(Qh *qh, Qh *next)
- {
- qh->next = next;
- qh->link = PADDR(next)|Lqh;
- return qh;
- }
- static void
- qhsetaddr(Qh *qh, ulong addr)
- {
- ulong eps0;
- ulong ep;
- ulong dev;
- eps0 = qh->eps0 & ~((Epmax<<8)|Devmax);
- ep = (addr >> 7) & Epmax;
- dev = addr & Devmax;
- eps0 |= ep << 8;
- eps0 |= dev;
- qh->eps0 = eps0;
- }
- /*
- * 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, ulong bw, ulong limit)
- {
- int i, j, d, upperb, q;
- ulong 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, Qh *qh, int pollival)
- {
- int q;
- Qh *tqh;
- ulong bw;
- bw = qh->io->bw;
- q = pickschedq(ctlr->tree, pollival, 0, ~0);
- ddqprint("ehci: sched %#p q %d, ival %d, bw %uld\n",
- qh->io, q, pollival, bw);
- if(q < 0){
- print("ehci: no room for ed\n");
- return -1;
- }
- ctlr->tree->bw[q] += bw;
- tqh = ctlr->tree->root[q];
- qh->sched = q;
- qhlinkqh(qh, tqh->next);
- qhlinkqh(tqh, qh);
- qh->inext = ctlr->intrqhs;
- ctlr->intrqhs = qh;
- return 0;
- }
- static void
- unschedq(Ctlr *ctlr, Qh *qh)
- {
- int q;
- Qh *prev, *this, *next;
- Qh **l;
- ulong bw;
- bw = qh->io->bw;
- q = qh->sched;
- if(q < 0)
- return;
- ctlr->tree->bw[q] -= bw;
- prev = ctlr->tree->root[q];
- this = prev->next;
- while(this != nil && this != qh){
- prev = this;
- this = this->next;
- }
- if(this == nil)
- print("ehci: unschedq %d: not found\n", q);
- else{
- next = this->next;
- qhlinkqh(prev, next);
- }
- for(l = &ctlr->intrqhs; *l != nil; l = &(*l)->inext)
- if(*l == qh){
- *l = (*l)->inext;
- return;
- }
- print("ehci: unschedq: qh %#p not found\n", qh);
- }
- static ulong
- qhmaxpkt(Qh *qh)
- {
- return (qh->eps0 >> Qhmplshift) & Qhmplmask;
- }
- static void
- qhsetmaxpkt(Qh *qh, int maxpkt)
- {
- ulong eps0;
- eps0 = qh->eps0 & ~(Qhmplmask << Qhmplshift);
- eps0 |= (maxpkt & Qhmplmask) << Qhmplshift;
- qh->eps0 = eps0;
- }
- /*
- * Initialize the round-robin circular list of ctl/bulk Qhs
- * if ep is nil. Otherwise, allocate and link a new Qh in the ctlr.
- */
- static Qh*
- qhalloc(Ctlr *ctlr, Ep *ep, Qio *io, char* tag)
- {
- Qh *qh;
- int ttype;
- qh = edalloc();
- qh->nlink = Lterm;
- qh->alink = Lterm;
- qh->csw = Tdhalt;
- qh->state = Qidle;
- qh->sched = -1;
- qh->io = io;
- if(ep != nil){
- qh->eps0 = 0;
- qhsetmaxpkt(qh, ep->maxpkt);
- if(ep->dev->speed == Lowspeed)
- qh->eps0 |= Qhlow;
- if(ep->dev->speed == Highspeed)
- qh->eps0 |= Qhhigh;
- else if(ep->ttype == Tctl)
- qh->eps0 |= Qhnhctl;
- qh->eps0 |= Qhdtc;
- qh->eps0 |= (8 << Qhrlcshift); /* 8 naks max */
- qhsetaddr(qh, io->usbid);
- qh->eps1 = (ep->ntds & Qhmultmask) << Qhmultshift;
- qh->eps1 |= ep->dev->port << Qhportshift;
- qh->eps1 |= ep->dev->hub << Qhhubshift;
- qh->eps1 |= 034 << Qhscmshift;
- if(ep->ttype == Tintr)
- qh->eps1 |= (1 << Qhismshift); /* intr. start µf. */
- if(io != nil)
- io->tag = tag;
- }
- ilock(ctlr);
- ttype = Tctl;
- if(ep != nil)
- ttype = ep->ttype;
- switch(ttype){
- case Tctl:
- case Tbulk:
- if(ctlr->qhs == nil){
- ctlr->qhs = qhlinkqh(qh, qh);
- ctlr->opio->link = PADDR(qh)|Lqh;
- qh->eps0 |= Qhhigh | Qhhrl;
- }else{
- qhlinkqh(qh, ctlr->qhs->next);
- qhlinkqh(ctlr->qhs, qh);
- }
- break;
- case Tintr:
- schedq(ctlr, qh, ep->pollival);
- break;
- default:
- print("ehci: qhalloc called for ttype != ctl/bulk\n");
- }
- iunlock(ctlr);
- return qh;
- }
- static int
- qhadvanced(void *a)
- {
- Ctlr *ctlr;
- ctlr = a;
- return (ctlr->opio->cmd & Ciasync) == 0;
- }
- /*
- * called when a qh is removed, to be sure the hw is not
- * keeping pointers into it.
- */
- static void
- qhcoherency(Ctlr *ctlr)
- {
- int i;
- qlock(&ctlr->portlck);
- ctlr->opio->cmd |= Ciasync; /* ask for intr. on async advance */
- for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++)
- if(!waserror()){
- tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
- poperror();
- }
- dprint("ehci: qhcoherency: doorbell %d\n", qhadvanced(ctlr));
- if(i == 3)
- print("ehci: async advance doorbell did not ring\n");
- ctlr->opio->cmd &= ~Ciasync; /* try to clean */
- qunlock(&ctlr->portlck);
- }
- static void
- qhfree(Ctlr *ctlr, Qh *qh)
- {
- Td *td;
- Td *ltd;
- Qh *q;
- if(qh == nil)
- return;
- ilock(ctlr);
- if(qh->sched < 0){
- 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;
- }else
- unschedq(ctlr, qh);
- iunlock(ctlr);
- qhcoherency(ctlr);
- for(td = qh->tds; td != nil; td = ltd){
- ltd = td->next;
- tdfree(td);
- }
- edfree(qh);
- }
- static void
- qhlinktd(Qh *qh, Td *td)
- {
- ulong csw;
- int i;
- if(td == nil){
- qh->tds = nil;
- qh->csw |= Tdhalt;
- qh->csw &= ~Tdactive;
- }else{
- qh->tds = td;
- csw = qh->csw & (Tddata1|Tdping); /* save */
- qh->csw = Tdhalt;
- qh->clink = 0;
- qh->alink = Lterm;
- qh->nlink = PADDR(td);
- for(i = 0; i < nelem(qh->buffer); i++)
- qh->buffer[i] = 0;
- qh->csw = csw & ~(Tdhalt|Tdactive); /* activate next */
- }
- }
- static char*
- seprintlink(char *s, char *se, char *name, ulong l, int typed)
- {
- s = seprint(s, se, "%s %ulx", name, l);
- if((l & Lterm) != 0)
- return seprint(s, se, "T");
- if(typed == 0)
- return s;
- switch(l & (3<<1)){
- case Litd:
- return seprint(s, se, "I");
- case Lqh:
- return seprint(s, se, "Q");
- case Lsitd:
- return seprint(s, se, "S");
- default:
- return seprint(s, se, "F");
- }
- }
- static char*
- seprintitd(char *s, char *se, Itd *td)
- {
- int i;
- char flags[6];
- ulong b0;
- ulong b1;
- char *rw;
- if(td == nil)
- return seprint(s, se, "<nil itd>\n");
- b0 = td->buffer[0];
- b1 = td->buffer[1];
- s = seprint(s, se, "itd %#p", td);
- rw = (b1 & Itdin) ? "in" : "out";
- s = seprint(s, se, " %s ep %uld dev %uld max %uld mult %uld",
- rw, (b0>>8)&Epmax, (b0&Devmax),
- td->buffer[1] & 0x7ff, b1 & 3);
- s = seprintlink(s, se, " link", td->link, 1);
- s = seprint(s, se, "\n");
- for(i = 0; i < nelem(td->csw); i++){
- memset(flags, '-', 5);
- if((td->csw[i] & Itdactive) != 0)
- flags[0] = 'a';
- if((td->csw[i] & Itdioc) != 0)
- flags[1] = 'i';
- if((td->csw[i] & Itddberr) != 0)
- flags[2] = 'd';
- if((td->csw[i] & Itdbabble) != 0)
- flags[3] = 'b';
- if((td->csw[i] & Itdtrerr) != 0)
- flags[4] = 't';
- flags[5] = 0;
- s = seprint(s, se, "\ttd%d %s", i, flags);
- s = seprint(s, se, " len %uld", (td->csw[i] >> 16) & 0x7ff);
- s = seprint(s, se, " pg %uld", (td->csw[i] >> 12) & 0x7);
- s = seprint(s, se, " off %uld\n", td->csw[i] & 0xfff);
- }
- s = seprint(s, se, "\tbuffs:");
- for(i = 0; i < nelem(td->buffer); i++)
- s = seprint(s, se, " %#ulx", td->buffer[i] >> 12);
- return seprint(s, se, "\n");
- }
- static char*
- seprintsitd(char *s, char *se, Sitd *td)
- {
- static char pc[4] = { 'a', 'b', 'm', 'e' };
- char rw;
- char pg;
- char ss;
- char flags[8];
- if(td == nil)
- return seprint(s, se, "<nil sitd>\n");
- s = seprint(s, se, "sitd %#p", td);
- rw = (td->epc & Stdin) ? 'r' : 'w';
- s = seprint(s, se, " %c ep %uld dev %uld",
- rw, (td->epc>>8)&0xf, td->epc&0x7f);
- s = seprint(s, se, " max %uld", (td->csw >> 16) & 0x3ff);
- s = seprint(s, se, " hub %uld", (td->epc >> 16) & 0x7f);
- s = seprint(s, se, " port %uld\n", (td->epc >> 24) & 0x7f);
- memset(flags, '-', 7);
- if((td->csw & Stdactive) != 0)
- flags[0] = 'a';
- if((td->csw & Stdioc) != 0)
- flags[1] = 'i';
- if((td->csw & Stderr) != 0)
- flags[2] = 'e';
- if((td->csw & Stddberr) != 0)
- flags[3] = 'd';
- if((td->csw & Stdbabble) != 0)
- flags[4] = 'b';
- if((td->csw & Stdtrerr) != 0)
- flags[5] = 't';
- if((td->csw & Stdmmf) != 0)
- flags[6] = 'n';
- flags[7] = 0;
- ss = (td->csw & Stddcs) ? 'c' : 's';
- pg = (td->csw & Stdpg) ? '1' : '0';
- s = seprint(s, se, "\t%s %cs pg%c", flags, ss, pg);
- s = seprint(s, se, " b0 %#ulx b1 %#ulx off %uld\n",
- td->buffer[0] >> 12, td->buffer[1] >> 12, td->buffer[0] & 0xfff);
- s = seprint(s, se, "\ttpos %c tcnt %uld",
- pc[(td->buffer[0]>>3)&3], td->buffer[1] & 7);
- s = seprint(s, se, " ssm %#ulx csm %#ulx cspm %#ulx",
- td->mfs & 0xff, (td->mfs>>8) & 0xff, (td->csw>>8) & 0xff);
- s = seprintlink(s, se, " link", td->link, 1);
- s = seprintlink(s, se, " blink", td->blink, 0);
- return seprint(s, se, "\n");
- }
- static long
- maxtdlen(Td *td)
- {
- return (td->csw >> Tdlenshift) & Tdlenmask;
- }
- static long
- tdlen(Td *td)
- {
- if(td->data == nil)
- return 0;
- return td->ndata - maxtdlen(td);
- }
- static char*
- seprinttd(char *s, char *se, Td *td, char *tag)
- {
- static char *tok[4] = { "out", "in", "setup", "BUG" };
- char flags[9];
- char t;
- char ss;
- int i;
- s = seprint(s, se, "%s %#p", tag, td);
- s = seprintlink(s, se, " nlink", td->nlink, 0);
- s = seprintlink(s, se, " alink", td->alink, 0);
- s = seprint(s, se, " %s", tok[(td->csw & Tdtok) >> 8]);
- if((td->csw & Tdping) != 0)
- s = seprint(s, se, " png");
- memset(flags, '-', 8);
- if((td->csw & Tdactive) != 0)
- flags[0] = 'a';
- if((td->csw & Tdioc) != 0)
- flags[1] = 'i';
- if((td->csw & Tdhalt) != 0)
- flags[2] = 'h';
- if((td->csw & Tddberr) != 0)
- flags[3] = 'd';
- if((td->csw & Tdbabble) != 0)
- flags[4] = 'b';
- if((td->csw & Tdtrerr) != 0)
- flags[5] = 't';
- if((td->csw & Tdmmf) != 0)
- flags[6] = 'n';
- if((td->csw & (Tderr2|Tderr1)) == 0)
- flags[7] = 'z';
- flags[8] = 0;
- t = (td->csw & Tddata1) ? '1' : '0';
- ss = (td->csw & Tddcs) ? 'c' : 's';
- s = seprint(s, se, "\n\td%c %s %cs", t, flags, ss);
- s = seprint(s, se, " max %uld", maxtdlen(td));
- s = seprint(s, se, " pg %uld off %#ulx\n",
- (td->csw >> Tdpgshift) & Tdpgmask, td->buffer[0] & 0xFFF);
- s = seprint(s, se, "\tbuffs:");
- for(i = 0; i < nelem(td->buffer); i++)
- s = seprint(s, se, " %#ulx", td->buffer[i]>>12);
- if(td->data != nil)
- s = seprintdata(s, se, td->data, td->ndata);
- return seprint(s, se, "\n");
- }
- static void
- dumptd(Td *td, char *pref)
- {
- char buf[256];
- char *se;
- int i;
- i = 0;
- se = buf+sizeof(buf);
- for(; td != nil; td = td->next){
- seprinttd(buf, se, td, pref);
- print("%s", buf);
- if(i++ > 20){
- print("...more tds...\n");
- break;
- }
- }
- }
- static void
- qhdump(Qh *qh)
- {
- static char *speed[] = {"full", "low", "high", "BUG"};
- char buf[256];
- char *s;
- char *se;
- char *tag;
- Td td;
- if(qh == nil){
- print("<nil qh>\n");
- return;
- }
- if(qh->io == nil)
- tag = "qh";
- else
- tag = qh->io->tag;
- se = buf+sizeof(buf);
- s = seprint(buf, se, "%s %#p", tag, qh);
- s = seprint(s, se, " ep %uld dev %uld",
- (qh->eps0>>8)&0xf, qh->eps0&0x7f);
- s = seprint(s, se, " hub %uld", (qh->eps1 >> 16) & 0x7f);
- s = seprint(s, se, " port %uld", (qh->eps1 >> 23) & 0x7f);
- s = seprintlink(s, se, " link", qh->link, 1);
- seprint(s, se, " clink %#ulx", qh->clink);
- print("%s\n", buf);
- s = seprint(buf, se, "\tnrld %uld", (qh->eps0 >> Qhrlcshift) & Qhrlcmask);
- s = seprint(s, se, " nak %uld", (qh->alink >> 1) & 0xf);
- s = seprint(s, se, " max %uld ", qhmaxpkt(qh));
- if((qh->eps0 & Qhnhctl) != 0)
- s = seprint(s, se, "c");
- if((qh->eps0 & Qhhrl) != 0)
- s = seprint(s, se, "h");
- if((qh->eps0 & Qhdtc) != 0)
- s = seprint(s, se, "d");
- if((qh->eps0 & Qhint) != 0)
- s = seprint(s, se, "i");
- s = seprint(s, se, " %s", speed[(qh->eps0 >> 12) & 3]);
- s = seprint(s, se, " mult %uld", (qh->eps1 >> Qhmultshift) & Qhmultmask);
- seprint(s, se, " scm %#ulx ism %#ulx\n",
- (qh->eps1 >> 8 & 0xff), qh->eps1 & 0xff);
- print("%s\n", buf);
- memset(&td, 0, sizeof(td));
- memmove(&td, &qh->nlink, 32); /* overlay area */
- seprinttd(buf, se, &td, "\tovl");
- print("%s", buf);
- }
- static void
- isodump(Isoio* iso, int all)
- {
- Itd *td, *tdi, *tdu;
- Sitd *std, *stdi, *stdu;
- char buf[256];
- int i;
- if(iso == nil){
- print("<nil iso>\n");
- return;
- }
- print("iso %#p %s %s speed state %d nframes %d maxsz %uld",
- iso, iso->tok == Tdtokin ? "in" : "out",
- iso->hs ? "high" : "full",
- iso->state, iso->nframes, iso->maxsize);
- print(" td0 %uld tdi %#p tdu %#p data %#p\n",
- iso->td0frno, iso->tdi, iso->tdu, iso->data);
- if(iso->err != nil)
- print("\terr %s\n", iso->err);
- if(iso->err != nil)
- print("\terr='%s'\n", iso->err);
- if(all == 0)
- if(iso->hs != 0){
- tdi = iso->tdi;
- seprintitd(buf, buf+sizeof(buf), tdi);
- print("\ttdi %s\n", buf);
- tdu = iso->tdu;
- seprintitd(buf, buf+sizeof(buf), tdu);
- print("\ttdu %s\n", buf);
- }else{
- stdi = iso->stdi;
- seprintsitd(buf, buf+sizeof(buf), stdi);
- print("\tstdi %s\n", buf);
- stdu = iso->stdu;
- seprintsitd(buf, buf+sizeof(buf), stdu);
- print("\tstdu %s\n", buf);
- }
- else{
- for(i = 0; i < Nisoframes; i++)
- if(iso->tdps[i] != nil)
- if(iso->hs != 0){
- td = iso->itdps[i];
- seprintitd(buf, buf+sizeof(buf), td);
- if(td == iso->tdi)
- print("i->");
- if(td == iso->tdu)
- print("i->");
- print("[%d]\t%s", i, buf);
- }else{
- std = iso->sitdps[i];
- seprintsitd(buf, buf+sizeof(buf), std);
- if(std == iso->stdi)
- print("i->");
- if(std == iso->stdu)
- print("u->");
- print("[%d]\t%s", i, buf);
- }
- }
- }
- static void
- dump(Hci *hp)
- {
- Ctlr *ctlr;
- Isoio *iso;
- Eopio *opio;
- int i;
- char buf[128];
- char *s;
- char *se;
- Qh *qh;
- ctlr = hp->aux;
- opio = ctlr->opio;
- ilock(ctlr);
- print("ehci port %#p frames %#p (%d fr.) nintr %d ntdintr %d",
- ctlr->capio, ctlr->frames, ctlr->nframes,
- ctlr->nintr, ctlr->ntdintr);
- print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr);
- print("\tcmd %#ulx sts %#ulx intr %#ulx frno %uld",
- opio->cmd, opio->sts, opio->intr, opio->frno);
- print(" base %#ulx link %#ulx fr0 %#ulx\n",
- opio->frbase, opio->link, ctlr->frames[0]);
- se = buf+sizeof(buf);
- s = seprint(buf, se, "\t");
- for(i = 0; i < hp->nports; i++){
- s = seprint(s, se, "p%d %#ulx ", i, opio->portsc[i]);
- if(hp->nports > 4 && i == hp->nports/2 - 1)
- s = seprint(s, se, "\n\t");
- }
- print("%s\n", buf);
- qh = ctlr->qhs;
- i = 0;
- do{
- qhdump(qh);
- qh = qh->next;
- }while(qh != ctlr->qhs && i++ < 100);
- if(i > 100)
- print("...too many Qhs...\n");
- if(ctlr->intrqhs != nil)
- print("intr qhs:\n");
- for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
- qhdump(qh);
- if(ctlr->iso != nil)
- print("iso:\n");
- for(iso = ctlr->iso; iso != nil; iso = iso->next)
- isodump(ctlr->iso, 0);
- print("%d eds in tree\n", ctlr->ntree);
- iunlock(ctlr);
- lock(&edpool);
- print("%d eds allocated = %d in use + %d free\n",
- edpool.nalloc, edpool.ninuse, edpool.nfree);
- unlock(&edpool);
- }
- static char*
- errmsg(int err)
- {
- if(err == 0)
- return "ok";
- if(err & Tddberr)
- return "data buffer error";
- if(err & Tdbabble)
- return "babble detected";
- if(err & Tdtrerr)
- return "transaction error";
- if(err & Tdmmf)
- return "missed µframe";
- if(err & Tdhalt)
- return Estalled; /* [uo]hci report this error */
- return Eio;
- }
- static char*
- ierrmsg(int err)
- {
- if(err == 0)
- return "ok";
- if(err & Itddberr)
- return "data buffer error";
- if(err & Itdbabble)
- return "babble detected";
- if(err & Itdtrerr)
- return "transaction error";
- return Eio;
- }
- static char*
- serrmsg(int err)
- {
- if(err & Stderr)
- return "translation translator error";
- /* other errors have same numbers than Td errors */
- return errmsg(err);
- }
- static int
- isocanread(void *a)
- {
- Isoio *iso;
- iso = a;
- if(iso->state == Qclose)
- return 1;
- if(iso->state == Qrun && iso->tok == Tdtokin){
- if(iso->hs != 0 && iso->tdi != iso->tdu)
- return 1;
- if(iso->hs == 0 && iso->stdi != iso->stdu)
- return 1;
- }
- return 0;
- }
- static int
- isocanwrite(void *a)
- {
- Isoio *iso;
- iso = a;
- if(iso->state == Qclose)
- return 1;
- if(iso->state == Qrun && iso->tok == Tdtokout){
- if(iso->hs != 0 && iso->tdu->next != iso->tdi)
- return 1;
- if(iso->hs == 0 && iso->stdu->next != iso->stdi)
- return 1;
- }
- return 0;
- }
- static void
- itdinit(Isoio *iso, Itd *td)
- {
- ulong pa;
- int p;
- int t;
- ulong tsize;
- ulong size;
- /*
- * BUG: This does not put an integral number of samples
- * on each µframe unless samples per packet % 8 == 0
- * Also, all samples are packed early on each frame.
- */
- p = 0;
- size = td->ndata = td->mdata;
- pa = PADDR(td->data);
- for(t = 0; size > 0 && t < 8; t++){
- tsize = size;
- if(tsize > iso->maxsize)
- tsize = iso->maxsize;
- size -= tsize;
- td->csw[t] = tsize << Itdlenshift;
- assert(p < nelem(td->buffer));
- td->csw[t] |= p << Itdpgshift;
- td->csw[t] |= (pa & 0xFFF) << Itdoffshift;
- td->csw[t] |= Itdactive|Itdioc;
- if(((pa+tsize) & ~0xFFF) != (pa & ~0xFFF))
- p++;
- pa += tsize;
- }
- }
- static void
- sitdinit(Isoio *iso, Sitd *td)
- {
- td->ndata = td->mdata & Stdlenmask;
- td->csw = (td->ndata << Stdlenshift) | Stdactive | Stdioc;
- td->buffer[0] = PADDR(td->data);
- td->buffer[1] = (td->buffer[0] & ~0xFFF) + 0x1000;
- if(iso->tok == Tdtokin || td->ndata <= 188)
- td->buffer[1] |= Stdtpall;
- else
- td->buffer[1] |= Stdtpbegin;
- if(iso->tok == Tdtokin)
- td->buffer[1] |= 1;
- else
- td->buffer[1] |= ((td->ndata + 187 ) / 188) & Stdtcntmask;
- }
- static int
- itdactive(Itd *td)
- {
- int i;
- for(i = 0; i < nelem(td->csw); i++)
- if((td->csw[i] & Itdactive) != 0)
- return 1;
- return 0;
- }
- static int
- isohsinterrupt(Ctlr *ctlr, Isoio *iso)
- {
- Itd *tdi;
- int err;
- int i;
- int t;
- int nframes;
- tdi = iso->tdi;
- assert(tdi != nil);
- if(itdactive(tdi)) /* not all tds are done */
- return 0;
- ctlr->nisointr++;
- ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
- if(iso->state != Qrun && iso->state != Qdone)
- panic("isofsintr: iso state");
- if(debug > 1 || iso->debug > 1)
- isodump(iso, 0);
- nframes = iso->nframes / 2; /* limit how many we look */
- if(nframes > Nisoframes)
- nframes = Nisoframes;
- if(iso->tok == Tdtokin)
- tdi->ndata = 0;
- /* else, it has the number of bytes transferred */
- for(i = 0; i < nframes && itdactive(tdi) == 0; i++){
- err = 0;
- if(iso->tok == Tdtokin)
- tdi->ndata += (tdi->csw[i] >> Itdlenshift)&Itdlenmask;
- for(t = 0; t < nelem(tdi->csw); t++){
- tdi->csw[i] &= ~Itdioc;
- err |= tdi->csw[i] & Itderrors;
- }
- if(err == 0)
- iso->nerrs = 0;
- else if(iso->nerrs++ > iso->nframes/2){
- if(iso->err == nil){
- iso->err = ierrmsg(err);
- diprint("isohsintr: tdi %#p error %#ux %s\n",
- tdi, err, iso->err);
- diprint("ctlr load %uld\n", ctlr->load);
- }
- tdi->ndata = 0;
- }else
- tdi->ndata = 0;
- if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){
- memset(iso->tdu->data, 0, iso->tdu->mdata);
- itdinit(iso, iso->tdu);
- iso->tdu = iso->tdu->next;
- iso->nleft = 0;
- }
- tdi = tdi->next;
- }
- ddiprint("isohsintr: %d frames processed\n", nframes);
- if(i == nframes)
- tdi->csw[0] |= Itdioc;
- iso->tdi = tdi;
- if(isocanwrite(iso) || isocanread(iso)){
- diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
- iso->tdi, iso->tdu);
- wakeup(iso);
- }
- return 1;
- }
- static int
- isofsinterrupt(Ctlr *ctlr, Isoio *iso)
- {
- Sitd *stdi;
- int err;
- int i;
- int nframes;
- stdi = iso->stdi;
- assert(stdi != nil);
- if((stdi->csw & Stdactive) != 0) /* nothing new done */
- return 0;
- ctlr->nisointr++;
- ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso->stdu);
- if(iso->state != Qrun && iso->state != Qdone)
- panic("isofsintr: iso state");
- if(debug > 1 || iso->debug > 1)
- isodump(iso, 0);
- nframes = iso->nframes / 2; /* limit how many we look */
- if(nframes > Nisoframes)
- nframes = Nisoframes;
- for(i = 0; i < nframes && (stdi->csw & Stdactive) == 0; i++){
- stdi->csw &= ~Stdioc;
- err = stdi->csw & Stderrors;
- if(err == 0){
- iso->nerrs = 0;
- if(iso->tok == Tdtokin)
- stdi->ndata = (stdi->csw>>Stdlenshift)&Stdlenmask;
- /* else len is assumed correct */
- }else if(iso->nerrs++ > iso->nframes/2){
- if(iso->err == nil){
- iso->err = serrmsg(err);
- diprint("isofsintr: tdi %#p error %#ux %s\n",
- stdi, err, iso->err);
- diprint("ctlr load %uld\n", ctlr->load);
- }
- stdi->ndata = 0;
- }else
- stdi->ndata = 0;
- if(stdi->next == iso->stdu || stdi->next->next == iso->stdu){
- memset(iso->stdu->data, 0, iso->stdu->mdata);
- sitdinit(iso, iso->stdu);
- iso->stdu = iso->stdu->next;
- iso->nleft = 0;
- }
- stdi = stdi->next;
- }
- ddiprint("isofsintr: %d frames processed\n", nframes);
- if(i == nframes)
- stdi->csw |= Stdioc;
- iso->stdi = stdi;
- if(isocanwrite(iso) || isocanread(iso)){
- diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
- iso->stdi, iso->stdu);
- wakeup(iso);
- }
- return 1;
- }
- static int
- qhinterrupt(Ctlr *ctlr, Qh *qh)
- {
- Td *td;
- int err;
- 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 %#p qh %#p\n",ctlr->capio, qh);
- for(td = qh->tds; td != nil; td = td->next){
- if(td->csw & Tdactive)
- return 0;
- if((td->csw & Tderrors) != 0){
- err = td->csw & Tderrors;
- if(qh->io->err == nil){
- qh->io->err = errmsg(td->csw & Tderrors);
- dqprint("qhintr: td %#p csw %#ulx error %#ux %s\n",
- td, td->csw, err, qh->io->err);
- }
- break;
- }
- 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.
- */
- for(; td != nil; td = td->next)
- td->ndata = 0;
- qh->state = Qdone;
- wakeup(qh->io);
- return 1;
- }
- static int
- ehciintr(Hci *hp)
- {
- Ctlr *ctlr;
- Eopio *opio;
- Isoio *iso;
- ulong sts;
- Qh *qh;
- int i;
- int some;
- ctlr = hp->aux;
- opio = ctlr->opio;
- /*
- * Will we know in USB 3.0 who the interrupt was for?.
- * Do they still teach indexing in CS?
- * This is Intel's doing.
- */
- ilock(ctlr);
- ctlr->nintr++;
- sts = opio->sts & Sintrs;
- if(sts == 0){ /* not ours; shared intr. */
- iunlock(ctlr);
- return 0;
- }
- opio->sts = sts;
- if((sts & Sherr) != 0)
- print("ehci: port %#p fatal host system error\n", ctlr->capio);
- if((sts & Shalted) != 0)
- print("ehci: port %#p: halted\n", ctlr->capio);
- if((sts & Sasync) != 0){
- dprint("ehci: doorbell\n");
- wakeup(ctlr);
- }
- /*
- * We enter always this if, even if it seems the
- * interrupt does not report anything done/failed.
- * Some controllers don't post interrupts right.
- */
- some = 0;
- if((sts & (Serrintr|Sintr)) != 0){
- ctlr->ntdintr++;
- if(debug > 1){
- print("ehci port %#p frames %#p nintr %d ntdintr %d",
- ctlr->capio, ctlr->frames,
- ctlr->nintr, ctlr->ntdintr);
- print(" nqhintr %d nisointr %d\n",
- ctlr->nqhintr, ctlr->nisointr);
- print("\tcmd %#ulx sts %#ulx intr %#ulx frno %uld",
- opio->cmd, opio->sts, opio->intr, opio->frno);
- }
- /* process the Iso transfers */
- for(iso = ctlr->iso; iso != nil; iso = iso->next)
- if(iso->state == Qrun || iso->state == Qdone)
- if(iso->hs != 0)
- some += isohsinterrupt(ctlr, iso);
- else
- some += isofsinterrupt(ctlr, iso);
- /* process the qhs in the periodic tree */
- for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
- if(qh->state == Qrun)
- some += qhinterrupt(ctlr, qh);
- /* process the async Qh circular list */
- qh = ctlr->qhs;
- i = 0;
- do{
- if(qh->state == Qrun)
- some += qhinterrupt(ctlr, qh);
- qh = qh->next;
- }while(qh != ctlr->qhs && i++ < 100);
- if(i > 100)
- print("echi: interrupt: qh loop?\n");
- }
- iunlock(ctlr);
- return some;
- }
- static void
- interrupt(Ureg*, void* a)
- {
- ehciintr(a);
- }
- static int
- portenable(Hci *hp, int port, int on)
- {
- Ctlr *ctlr;
- Eopio *opio;
- int s;
- ctlr = hp->aux;
- opio = ctlr->opio;
- s = opio->portsc[port-1];
- qlock(&ctlr->portlck);
- if(waserror()){
- qunlock(&ctlr->portlck);
- nexterror();
- }
- dprint("ehci %#p port %d enable=%d; sts %#x\n",
- ctlr->capio, port, on, s);
- ilock(ctlr);
- if(s & (Psstatuschg | Pschange))
- opio->portsc[port-1] = s;
- if(on)
- opio->portsc[port-1] |= Psenable;
- else
- opio->portsc[port-1] &= ~Psenable;
- microdelay(64);
- iunlock(ctlr);
- tsleep(&up->sleep, return0, 0, Enabledelay);
- dprint("ehci %#p port %d enable=%d: sts %#ulx\n",
- ctlr->capio, port, on, opio->portsc[port-1]);
- qunlock(&ctlr->portlck);
- poperror();
- return 0;
- }
- /*
- * If we detect during status that the port is low-speed or
- * during reset that it's full-speed, the device is not for
- * ourselves. The companion controller will take care.
- * Low-speed devices will not be seen by usbd. Full-speed
- * ones are seen because it's only after reset that we know what
- * they are (usbd may notice a device not enabled in this case).
- */
- static void
- portlend(Ctlr *ctlr, int port, char *ss)
- {
- Eopio *opio;
- ulong s;
- opio = ctlr->opio;
- dprint("ehci %#p port %d: %s speed device: no longer owned\n",
- ctlr->capio, port, ss);
- s = opio->portsc[port-1];
- s &= ~(Pschange|Psstatuschg);
- s |= Psowner;
- opio->portsc[port-1] = s;
- }
- static int
- portreset(Hci *hp, int port, int on)
- {
- ulong s;
- Eopio *opio;
- Ctlr *ctlr;
- int i;
- if(on == 0)
- return 0;
- ctlr = hp->aux;
- opio = ctlr->opio;
- qlock(&ctlr->portlck);
- if(waserror()){
- iunlock(ctlr);
- qunlock(&ctlr->portlck);
- nexterror();
- }
- s = opio->portsc[port-1];
- dprint("ehci %#p port %d reset; sts %#ulx\n", ctlr->capio, port, s);
- ilock(ctlr);
- s &= ~(Psenable|Psreset);
- opio->portsc[port-1] = s|Psreset;
- for(i = 0; i < 10; i++){
- delay(10);
- if((opio->portsc[port-1] & Psreset) == 0)
- break;
- }
- opio->portsc[port-1] &= ~Psreset;
- delay(10);
- if((opio->portsc[port-1] & Psenable) == 0)
- portlend(ctlr, port, "full");
- iunlock(ctlr);
- dprint("ehci %#p after port %d reset; sts %#ulx\n",
- ctlr->capio, port, opio->portsc[port-1]);
- qunlock(&ctlr->portlck);
- poperror();
- return 0;
- }
- static int
- portstatus(Hci *hp, int port)
- {
- int s;
- int r;
- Eopio *opio;
- Ctlr *ctlr;
- ctlr = hp->aux;
- opio = ctlr->opio;
- qlock(&ctlr->portlck);
- if(waserror()){
- iunlock(ctlr);
- qunlock(&ctlr->portlck);
- nexterror();
- }
- ilock(ctlr);
- s = opio->portsc[port-1];
- if(s & (Psstatuschg | Pschange)){
- opio->portsc[port-1] = s;
- ddprint("ehci %#p port %d status %#x\n", ctlr->capio, port, s);
- }
- /*
- * If the port is a low speed port we yield ownership now
- * to the [uo]hci companion controller and pretend it's not here.
- */
- if((s & Pspresent) != 0 && (s & Pslinemask) == Pslow){
- portlend(ctlr, port, "low");
- s &= ~Pspresent; /* not for us this time */
- }
- 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|HPhigh;
- if(s & Psenable)
- r |= HPenable;
- if(s & Pssuspend)
- r |= HPsuspend;
- if(s & Psreset)
- r |= HPreset;
- if(s & Psstatuschg)
- r |= HPstatuschg;
- if(s & Pschange)
- r |= HPchange;
- return r;
- }
- static char*
- seprintio(char *s, char *e, Qio *io, char *pref)
- {
- s = seprint(s,e,"%s io %#p qh %#p id %#x", pref, io, io->qh, io->usbid);
- s = seprint(s,e," iot %ld", io->iotime);
- s = seprint(s,e," tog %#x tok %#x err %s", io->toggle, io->tok, io->err);
- return s;
- }
- static char*
- seprintep(char *s, char *e, Ep *ep)
- {
- Qio *io;
- Ctlio *cio;
- 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 = seprintio(s, e, cio, "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:
- *s = 0;
- break;
- }
- iunlock(ctlr);
- return s;
- }
- /*
- * halt condition was cleared on the endpoint. update our toggles.
- */
- static void
- clrhalt(Ep *ep)
- {
- Qio *io;
- ep->clrhalt = 0;
- switch(ep->ttype){
- case Tintr:
- case Tbulk:
- 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 void
- xdump(char* pref, void *qh)
- {
- int i;
- ulong *u;
- u = qh;
- print("%s %#p:", pref, u);
- for(i = 0; i < 16; i++)
- if((i%4) == 0)
- print("\n %#8.8ulx", u[i]);
- else
- print(" %#8.8ulx", u[i]);
- print("\n");
- }
- static long
- episohscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
- {
- int nr;
- long tot;
- Itd *tdu;
- for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){
- tdu = iso->tdu;
- if(itdactive(tdu))
- break;
- nr = tdu->ndata;
- if(tot + nr > count)
- nr = count - tot;
- if(nr == 0)
- print("ehci: 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){
- itdinit(iso, tdu);
- iso->tdu = tdu->next;
- }
- }
- return tot;
- }
- static long
- episofscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
- {
- int nr;
- long tot;
- Sitd *stdu;
- for(tot = 0; iso->stdi != iso->stdu && tot < count; tot += nr){
- stdu = iso->stdu;
- if(stdu->csw & Stdactive){
- diprint("ehci: episoread: %#p tdu active\n", iso);
- break;
- }
- nr = stdu->ndata;
- if(tot + nr > count)
- nr = count - tot;
- if(nr == 0)
- print("ehci: ep%d.%d: too many polls\n",
- ep->dev->nb, ep->nb);
- else{
- iunlock(ctlr); /* We could page fault here */
- memmove(b+tot, stdu->data, nr);
- ilock(ctlr);
- if(nr < stdu->ndata)
- memmove(stdu->data,stdu->data+nr,stdu->ndata - nr);
- stdu->ndata -= nr;
- }
- if(stdu->ndata == 0){
- sitdinit(iso, stdu);
- iso->stdu = stdu->next;
- }
- }
- return tot;
- }
- static long
- episoread(Ep *ep, Isoio *iso, void *a, long count)
- {
- Ctlr *ctlr;
- uchar *b;
- long tot;
- iso->debug = ep->debug;
- diprint("ehci: 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("ehci: 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);
- if(iso->hs != 0)
- tot = episohscpy(ctlr, ep, iso, b, count);
- else
- tot = episofscpy(ctlr, ep, iso, b, count);
- iunlock(ctlr);
- qunlock(iso);
- poperror();
- diprint("uhci: episoread: %#p %uld bytes err '%s'\n", iso, tot, iso->err);
- if(iso->err != nil)
- error(iso->err);
- return tot;
- }
- /*
- * iso->tdu is the next place to put data. When it gets full
- * it is activated and tdu advanced.
- */
- static long
- putsamples(Isoio *iso, uchar *b, long count)
- {
- long tot;
- long n;
- for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
- n = count-tot;
- if(iso->hs != 0){
- if(n > iso->tdu->mdata - iso->nleft)
- n = iso->tdu->mdata - iso->nleft;
- memmove(iso->tdu->data+iso->nleft, b+tot, n);
- iso->nleft += n;
- if(iso->nleft == iso->tdu->mdata){
- itdinit(iso, iso->tdu);
- iso->nleft = 0;
- iso->tdu = iso->tdu->next;
- }
- }else{
- if(n > iso->stdu->mdata - iso->nleft)
- n = iso->stdu->mdata - iso->nleft;
- memmove(iso->stdu->data+iso->nleft, b+tot, n);
- iso->nleft += n;
- if(iso->nleft == iso->stdu->mdata){
- sitdinit(iso, iso->stdu);
- iso->nleft = 0;
- iso->stdu = iso->stdu->next;
- }
- }
- }
- return tot;
- }
- /*
- * Queue data for writing and return error status from
- * last writes done, to maintain buffered data.
- */
- static long
- episowrite(Ep *ep, Isoio *iso, void *a, long count)
- {
- Ctlr *ctlr;
- uchar *b;
- int tot;
- int nw;
- char *err;
- iso->debug = ep->debug;
- diprint("ehci: 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("ehci: 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("ehci: episowrite: %#p %d bytes\n", iso, tot);
- return tot;
- }
- static int
- nexttoggle(int toggle, int count, int maxpkt)
- {
- int np;
- np = count / maxpkt;
- if(np == 0)
- np = 1;
- if((np % 2) == 0)
- return toggle;
- if(toggle == Tddata1)
- return Tddata0;
- else
- return Tddata1;
- }
- static Td*
- epgettd(Qio *io, int flags, void *a, int count, int maxpkt)
- {
- Td *td;
- ulong pa;
- int i;
- if(count > Tdmaxpkt)
- panic("ehci: epgettd: too many bytes");
- td = tdalloc();
- td->csw = flags;
- td->csw |= io->toggle | io->tok | (count << Tdlenshift);
- td->csw |= Tderr2|Tderr1;
- /*
- * use the space wasted by alignment as an
- * embedded buffer if count bytes fit in there.
- */
- assert(Align > sizeof(Td));
- if(count <= Align - sizeof(Td))
- td->data = td->sbuff;
- else
- td->data = td->buff = smalloc(Tdmaxpkt);
- pa = PADDR(td->data);
- for(i = 0; i < nelem(td->buffer); i++){
- td->buffer[i] = pa;
- if(i > 0)
- td->buffer[i] &= ~0xFFF;
- pa += 0x1000;
- }
- td->ndata = count;
- if(a != nil && count > 0)
- memmove(td->data, a, count);
- io->toggle = nexttoggle(io->toggle, count, maxpkt);
- return td;
- }
- /*
- * Try to get them idle
- */
- static void
- aborttds(Qh *qh)
- {
- Td *td;
- qh->state = Qdone;
- if(qh->sched >= 0 && (qh->eps0&Qhspeedmask) != Qhhigh)
- qh->eps0 |= Qhint; /* inactivate on next pass */
- for(td = qh->tds; td != nil; td = td->next){
- if(td->csw & Tdactive)
- td->ndata = 0;
- td->csw |= Tdhalt;
- }
- }
- /*
- * Some controllers do not post the usb/error interrupt after
- * the work has been done. It seems that we must poll for them.
- */
- static int
- workpending(void *a)
- {
- Ctlr *ctlr;
- ctlr = a;
- return ctlr->nreqs > 0;
- }
- static void
- ehcipoll(void* a)
- {
- Hci *hp;
- Ctlr *ctlr;
- Poll *poll;
- int i;
- hp = a;
- ctlr = hp->aux;
- poll = &ctlr->poll;
- for(;;){
- if(ctlr->nreqs == 0){
- if(0)ddprint("ehcipoll %#p sleep\n", ctlr->capio);
- sleep(poll, workpending, ctlr);
- if(0)ddprint("ehcipoll %#p awaken\n", ctlr->capio);
- }
- for(i = 0; i < 16 && ctlr->nreqs > 0; i++)
- if(ehciintr(hp) == 0)
- break;
- do{
- tsleep(&up->sleep, return0, 0, 1);
- ehciintr(hp);
- }while(ctlr->nreqs > 0);
- }
- }
- static void
- pollcheck(Hci *hp)
- {
- Ctlr *ctlr;
- Poll *poll;
- ctlr = hp->aux;
- poll = &ctlr->poll;
- if(poll->must != 0 && poll->does == 0){
- lock(poll);
- if(poll->must != 0 && poll->does == 0){
- poll->does++;
- print("ehci %#p: polling\n", ctlr->capio);
- kproc("ehcipoll", ehcipoll, hp);
- }
- unlock(poll);
- }
- }
- static int
- epiodone(void *a)
- {
- Qh *qh;
- qh = a;
- return qh->state != Qrun;
- }
- static void
- epiowait(Hci *hp, Qio *io, int tmout, ulong load)
- {
- Qh *qh;
- int timedout;
- Ctlr *ctlr;
- ctlr = hp->aux;
- qh = io->qh;
- ddqprint("ehci io %#p sleep on qh %#p state %s\n",
- io, qh, qhsname[qh->state]);
- timedout = 0;
- if(waserror()){
- dqprint("ehci 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);
- /* Are we missing interrupts? */
- if(qh->state == Qrun){
- iunlock(ctlr);
- ehciintr(hp);
- ilock(ctlr);
- if(qh->state == Qdone){
- dqprint("ehci %#p: polling required\n", ctlr->capio);
- ctlr->poll.must = 1;
- pollcheck(hp);
- }
- }
- if(qh->state == Qrun){
- dqprint("ehci io %#p qh %#p timed out (no intr?)\n", io, qh);
- timedout = 1;
- }else if(qh->state != Qdone && qh->state != Qclose)
- panic("ehci: epio: queue state %d", qh->state);
- 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;
- ctlr->nreqs--;
- 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 long
- epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
- {
- Td *td, *ltd, *td0, *ntd;
- Ctlr *ctlr;
- Qh* qh;
- long n, tot;
- char buf[128];
- uchar *c;
- int saved, ntds, tmout;
- ulong 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("echi 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 = (Tdmaxpkt / ep->maxpkt) * ep->maxpkt;
- if(count-tot < n)
- n = count-tot;
- if(io->tok != Tdtokin)
- td = epgettd(io, Tdactive, c+tot, n, ep->maxpkt);
- else
- td = epgettd(io, Tdactive, nil, n, ep->maxpkt);
- 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("ehci: load %uld ctlr load %uld\n", load, ctlr->load);
- if(debug > 1 || ep->debug > 1)
- dumptd(td0, "epio: put: ");
- ilock(ctlr);
- if(qh->state != Qclose){
- io->iotime = TK2MS(MACHP(0)->ticks);
- qh->state = Qrun;
- qhlinktd(qh, td0);
- ctlr->nreqs++;
- ctlr->load += load;
- }
- iunlock(ctlr);
- if(ctlr->poll.does)
- wakeup(&ctlr->poll);
- epiowait(ep->hp, io, tmout, load);
- if(debug > 1 || ep->debug > 1){
- dumptd(td0, "epio: got: ");
- qhdump(qh);
- }
- 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 & (Tdhalt|Tdactive)){
- if(saved++ == 0)
- io->toggle = td->csw & Tddata1;
- }else{
- tot += td->ndata;
- if((td->csw & Tdtok) == 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 == Estalled)
- return 0; /* that's our convention */
- if(err != nil)
- error(err);
- if(tot < 0)
- error(Eio);
- return tot;
- }
- static long
- epread(Ep *ep, void *a, long count)
- {
- Ctlio *cio;
- Qio *io;
- Isoio *iso;
- char buf[160];
- ulong delta;
- ddeprint("ehci: epread\n");
- if(ep->aux == nil)
- panic("epread: not open");
- pollcheck(ep->hp);
- 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(0)->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);
- }
- 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 long
- epctlio(Ep *ep, Ctlio *cio, void *a, long count)
- {
- uchar *c;
- long 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);
- qhsetaddr(cio->qh, cio->usbid);
- }
- /* adjust maxpkt if the user has learned a different one */
- if(qhmaxpkt(cio->qh) != ep->maxpkt)
- qhsetmaxpkt(cio->qh, ep->maxpkt);
- 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 ehci");
- 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 long
- epwrite(Ep *ep, void *a, long count)
- {
- Qio *io;
- Ctlio *cio;
- Isoio *iso;
- ulong delta;
- pollcheck(ep->hp);
- ddeprint("ehci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux == nil)
- panic("ehci: 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);
- return epio(ep, &io[OWRITE], a, count, 1);
- case Tintr:
- io = ep->aux;
- delta = TK2MS(MACHP(0)->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);
- }
- return -1;
- }
- static void
- isofsinit(Ep *ep, Isoio *iso)
- {
- long left;
- Sitd *td;
- Sitd *ltd;
- int i;
- ulong frno;
- left = 0;
- ltd = nil;
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- td = iso->sitdps[frno] = sitdalloc();
- td->data = iso->data + i * ep->maxpkt;
- td->epc = ep->dev->port << Stdportshift;
- td->epc |= ep->dev->hub << Stdhubshift;
- td->epc |= ep->nb << Stdepshift;
- td->epc |= ep->dev->nb << Stddevshift;
- td->mfs = (034 << Stdscmshift) | (1 << Stdssmshift);
- if(ep->mode == OREAD){
- td->epc |= Stdin;
- td->mdata = ep->maxpkt;
- }else{
- td->mdata = (ep->hz+left) * ep->pollival / 1000;
- td->mdata *= ep->samplesz;
- left = (ep->hz+left) * ep->pollival % 1000;
- if(td->mdata > ep->maxpkt){
- print("ehci: ep%d.%d: size > maxpkt\n",
- ep->dev->nb, ep->nb);
- print("size = %ld max = %ld\n",
- td->mdata,ep->maxpkt);
- td->mdata = ep->maxpkt;
- }
- }
- sitdinit(iso, td);
- if(ltd != nil)
- ltd->next = td;
- ltd = td;
- frno = TRUNC(frno+ep->pollival, Nisoframes);
- }
- ltd->next = iso->sitdps[iso->td0frno];
- }
- static void
- isohsinit(Ep *ep, Isoio *iso)
- {
- long left;
- Itd *td;
- Itd *ltd;
- ulong i;
- ulong pa;
- int p;
- ulong frno;
- int ival;
- iso->hs = 1;
- ival = 1;
- if(ep->pollival > 8)
- ival = ep->pollival/8;
- left = 0;
- ltd = nil;
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- td = iso->itdps[frno] = itdalloc();
- td->data = iso->data + i * 8 * iso->maxsize;
- pa = PADDR(td->data) & ~0xFFF;
- for(p = 0; p < 8; p++)
- td->buffer[i] = pa + p * 0x1000;
- td->buffer[0] = PADDR(iso->data) & ~0xFFF;
- td->buffer[0] |= ep->nb << Itdepshift;
- td->buffer[0] |= ep->dev->nb << Itddevshift;
- if(ep->mode == OREAD)
- td->buffer[1] |= Itdin;
- else
- td->buffer[1] |= Itdout;
- td->buffer[1] |= ep->maxpkt << Itdmaxpktshift;
- td->buffer[2] |= ep->ntds << Itdntdsshift;
- if(ep->mode == OREAD)
- td->mdata = 8 * iso->maxsize;
- else{
- td->mdata = (ep->hz + left) * ep->pollival / 1000;
- td->mdata *= ep->samplesz;
- left = (ep->hz + left) * ep->pollival % 1000;
- }
- itdinit(iso, td);
- if(ltd != nil)
- ltd->next = td;
- ltd = td;
- frno = TRUNC(frno + ival, Nisoframes);
- }
- }
- static void
- isoopen(Ctlr *ctlr, Ep *ep)
- {
- Isoio *iso;
- int ival; /* pollival in ms */
- int n;
- ulong frno;
- int i;
- int w;
- int woff;
- int tpf; /* tds per frame */
- iso = ep->aux;
- switch(ep->mode){
- case OREAD:
- iso->tok = Tdtokin;
- break;
- case OWRITE:
- iso->tok = Tdtokout;
- break;
- default:
- error("iso i/o is half-duplex");
- }
- iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
- iso->state = Qidle;
- iso->debug = ep->debug;
- ival = ep->pollival;
- tpf = 1;
- if(ep->dev->speed == Highspeed){
- tpf = 8;
- if(ival <= 8)
- ival = 1;
- else
- ival /= 8;
- }
- assert(ival != 0);
- iso->nframes = Nisoframes / ival;
- if(iso->nframes < 3)
- error("uhci isoopen bug"); /* we need at least 3 tds */
- iso->maxsize = ep->ntds * ep->maxpkt;
- if(ctlr->load + ep->load > 800)
- print("usb: ehci: bandwidth may be exceeded\n");
- ilock(ctlr);
- ctlr->load += ep->load;
- ctlr->isoload += ep->load;
- ctlr->nreqs++;
- dprint("ehci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload);
- diprint("iso nframes %d pollival %uld ival %d maxpkt %uld ntds %d\n",
- iso->nframes, ep->pollival, ival, ep->maxpkt, ep->ntds);
- iunlock(ctlr);
- if(ctlr->poll.does)
- wakeup(&ctlr->poll);
- /*
- * From here on this cannot raise errors
- * unless we catch them and release here all memory allocated.
- */
- assert(ep->maxpkt > 0 && ep->ntds > 0 && ep->ntds < 4);
- assert(ep->maxpkt <= 1024);
- iso->tdps = smalloc(sizeof(uintptr) * Nisoframes);
- iso->data = smalloc(iso->nframes * tpf * ep->ntds * ep->maxpkt);
- iso->td0frno = TRUNC(ctlr->opio->frno + 10, Nisoframes);
- /* read: now; write: 1s ahead */
- if(ep->dev->speed == Highspeed)
- isohsinit(ep, iso);
- else
- isofsinit(ep, iso);
- iso->tdu = iso->tdi = iso->itdps[iso->td0frno];
- iso->stdu = iso->stdi = iso->sitdps[iso->td0frno];
- ilock(ctlr);
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- *iso->tdps[frno] = ctlr->frames[frno];
- frno = TRUNC(frno+ival, Nisoframes);
- }
- /*
- * Iso uses a virtual frame window of Nisoframes, and we must
- * fill the actual ctlr frame array by placing ctlr->nframes/Nisoframes
- * copies of the window in the frame array.
- */
- assert(ctlr->nframes >= Nisoframes && Nisoframes >= iso->nframes);
- assert(Nisoframes >= Nintrleafs);
- n = ctlr->nframes / Nisoframes;
- for(w = 0; w < n; w++){
- frno = iso->td0frno;
- woff = w * Nisoframes;
- for(i = 0; i < iso->nframes ; i++){
- assert(woff+frno < ctlr->nframes);
- assert(iso->tdps[frno] != nil);
- if(ep->dev->speed == Highspeed)
- ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])|Litd;
- else
- ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])|Lsitd;
- frno = TRUNC(frno+ep->pollival, Nisoframes);
- }
- }
- 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)
- {
- Ctlr *ctlr;
- Ctlio *cio;
- Qio *io;
- int usbid;
- ctlr = ep->hp->aux;
- deprint("ehci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux != nil)
- panic("ehci: 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->debug = ep->debug;
- cio->ndata = -1;
- cio->data = nil;
- if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
- break;
- cio->qh = qhalloc(ctlr, ep, cio, "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&Epmax)<<7)|(ep->dev->nb &Devmax);
- assert(ep->pollival != 0);
- 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 */
- io[OWRITE].qh = qhalloc(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 */
- io[OREAD].qh = qhalloc(ctlr, ep, io+OREAD, "epr");
- }
- break;
- }
- if(debug>1 || ep->debug)
- dump(ep->hp);
- deprint("ehci: epopen done\n");
- poperror();
- }
- static void
- cancelio(Ctlr *ctlr, Qio *io)
- {
- Qh *qh;
- ilock(ctlr);
- qh = io->qh;
- if(io == nil || io->qh == nil || io->qh->state == Qclose){
- iunlock(ctlr);
- return;
- }
- dqprint("ehci: 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, ulong load)
- {
- Isoio **il;
- ulong *lp;
- int i;
- int frno;
- int w;
- int n;
- int woff;
- ulong *tp;
- Itd *td;
- Sitd *std;
- int t;
- ilock(ctlr);
- if(iso->state == Qclose){
- iunlock(ctlr);
- return;
- }
- ctlr->nreqs--;
- if(iso->state != Qrun && iso->state != Qdone)
- panic("bad iso state");
- iso->state = Qclose;
- if(ctlr->isoload < load)
- panic("ehci: 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("cancleiso: not found");
- *il = iso->next;
- frno = iso->td0frno;
- for(i = 0; i < iso->nframes; i++){
- tp = iso->tdps[frno];
- if(iso->hs != 0){
- td = iso->itdps[frno];
- for(t = 0; t < nelem(td->csw); t++)
- td->csw[1] &= ~(Itdioc|Itdactive);
- }else{
- std = iso->sitdps[frno];
- std->csw &= ~(Stdioc|Stdactive);
- }
- for(lp=&ctlr->frames[frno]; !(*lp & Lterm); lp = &LPTR(*lp)[0])
- if(LPTR(*lp) == tp)
- break;
- if(*lp & Lterm)
- panic("cancelisoio: td not found");
- *lp = tp[0];
- /*
- * Iso uses a virtual frame window of Nisoframes, and we must
- * restore pointers in copies of the window kept at ctlr->frames.
- */
- if(lp == &ctlr->frames[frno]){
- n = ctlr->nframes / Nisoframes;
- for(w = 1; w < n; w++){
- woff = w * Nisoframes;
- ctlr->frames[woff+frno] = *lp;
- }
- }
- frno = TRUNC(frno+pollival, Nisoframes);
- }
- 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* is no longer 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++){
- if(iso->hs != 0)
- itdfree(iso->itdps[frno]);
- else
- sitdfree(iso->sitdps[frno]);
- iso->tdps[frno] = nil;
- frno = TRUNC(frno+pollival, Nisoframes);
- }
- free(iso->tdps);
- iso->tdps = nil;
- free(iso->data);
- iso->data = nil;
- }
- static void
- epclose(Ep *ep)
- {
- Qio *io;
- Ctlio *cio;
- Isoio *iso;
- Ctlr *ctlr;
- ctlr = ep->hp->aux;
- deprint("ehci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
- if(ep->aux == nil)
- panic("ehci: epclose called with closed ep");
- switch(ep->ttype){
- case Tctl:
- cio = ep->aux;
- cancelio(ctlr, cio);
- free(cio->data);
- cio->data = nil;
- break;
- case Tintr:
- case Tbulk:
- 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;
- break;
- default:
- panic("epclose: bad ttype");
- }
- free(ep->aux);
- ep->aux = nil;
- }
- static void
- scanpci(void) /* actually just use fixed addresses on sheeva */
- {
- Ctlr *ctlr;
- static int already = 0;
- if(already)
- return;
- already = 1;
- ctlr = mallocz(sizeof(Ctlr), 1);
- /* the sheeva's usb 2.0 otg uses a superset of the ehci registers */
- ctlr->capio = (Ecapio *)(Addrusb + 0x100);
- ctlr->opio = (Eopio *) (Addrusb + 0x140);
- dprint("usbehci: port %#p\n", ctlr->capio);
- ctlrs[0] = ctlr;
- }
- /*
- * return smallest power of 2 >= n
- */
- static int
- flog2(int n)
- {
- int i;
- for(i = 0; (1 << i) < n; i++)
- ;
- return i;
- }
- /*
- * 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;
- Qh **tree;
- Qtree *qt;
- Qh *qh;
- ulong leafs[Nintrleafs];
- depth = flog2(Nintrleafs);
- n = (1 << (depth+1)) - 1;
- qt = mallocz(sizeof(*qt), 1);
- if(qt == nil)
- panic("ehci: mkqhtree: no memory");
- qt->nel = n;
- qt->depth = depth;
- qt->bw = mallocz(n * sizeof(qt->bw), 1);
- qt->root = tree = mallocz(n * sizeof(Qh *), 1);
- if(qt->bw == nil || tree == nil)
- panic("ehci: mkqhtree: no memory");
- for(i = 0; i < n; i++){
- qh = tree[i] = edalloc();
- if(qh == nil)
- panic("ehci: mkqhtree: no memory");
- qh->nlink = qh->alink = qh->link = Lterm;
- qh->csw = Tdhalt;
- qh->state = Qidle;
- if(i > 0)
- qhlinkqh(tree[i], tree[(i-1)/2]);
- }
- ctlr->ntree = i;
- dprint("ehci: tree: %d endpoints allocated\n", i);
- /* distribute leaves evenly round the frame list */
- leaf0 = n / 2;
- for(i = 0; i < Nintrleafs; 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;
- }
- leafs[i] = PADDR(tree[leaf0 + o]) | Lqh;
- }
- assert((ctlr->nframes % Nintrleafs) == 0);
- for(i = 0; i < ctlr->nframes; i += Nintrleafs)
- memmove(ctlr->frames + i, leafs, sizeof(leafs));
- ctlr->tree = qt;
- }
- static void
- ehcimeminit(Ctlr *ctlr)
- {
- int frsize;
- Eopio *opio;
- int i;
- opio = ctlr->opio;
- frsize = ctlr->nframes*sizeof(ulong);
- assert((frsize & 0xFFF) == 0); /* must be 4k aligned */
- ctlr->frames = xspanalloc(frsize, frsize, 0);
- if(ctlr->frames == nil)
- panic("ehci reset: no memory");
- for (i = 0; i < ctlr->nframes; i++)
- ctlr->frames[i] = Lterm;
- opio->frbase = PADDR(ctlr->frames);
- opio->frno = 0;
- qhalloc(ctlr, nil, nil, nil); /* init async list */
- mkqhtree(ctlr); /* init sync list */
- edfree(edalloc()); /* try to get some ones pre-allocated */
- dprint("ehci %#p flb %#ulx frno %#ulx\n",
- ctlr->capio, opio->frbase, opio->frno);
- }
- static void
- init(Hci *hp)
- {
- Ctlr *ctlr;
- Eopio *opio;
- int i;
- hp->highspeed = 1;
- ctlr = hp->aux;
- opio = ctlr->opio;
- dprint("ehci %#p init\n", ctlr->capio);
- ilock(ctlr);
- /*
- * Unless we activate frroll interrupt
- * some machines won't post other interrupts.
- */
- opio->intr = Iusb|Ierr|Iportchg|Ihcerr|Iasync;
- opio->cmd |= Cpse;
- opio->cmd |= Case;
- ehcirun(ctlr, 1);
- opio->config = Callmine; /* reclaim all ports */
- for (i = 0; i < hp->nports; i++)
- opio->portsc[i] = Pspower;
- iunlock(ctlr);
- if(debug > 1)
- dump(hp);
- }
- #define WINTARG(ctl) (((ctl) >> 4) & 017)
- #define WINATTR(ctl) (((ctl) >> 8) & 0377)
- #define WIN64KSIZE(ctl) (((ctl) >> 16) + 1)
- #define SIZETO64KSIZE(size) ((size) / (64*1024) - 1)
- static void
- addrmapdump(void)
- {
- int i;
- ulong ctl, targ, attr, size64k;
- Kwusb *map;
- Usbwin *win;
- map = (Kwusb *)(Addrusb + 0x300);
- for (i = 0; i < nelem(map->win); i++) {
- win = &map->win[i];
- ctl = win->ctl;
- if (ctl & Winenable) {
- targ = WINTARG(ctl);
- attr = WINATTR(ctl);
- size64k = WIN64KSIZE(ctl);
- print("usb address map window %d: "
- "targ %ld attr %#lux size %,ld addr %#lux\n",
- i, targ, attr, size64k * 64*1024, win->base);
- }
- }
- }
- /* assumes ctlr is ilocked */
- static void
- ctlrreset(Ctlr *ctlr)
- {
- int i;
- Eopio *opio;
- opio = ctlr->opio;
- opio->cmd |= Chcreset;
- /* wait for it to come out of reset */
- for(i = 0; i < 100 && opio->cmd & Chcreset; i++)
- delay(1);
- if(i >= 100)
- print("ehci %#p controller reset timed out\n", ctlr->capio);
- /*
- * Marvell errata FE-USB-340 workaround: 1 << 4 magic:
- * disable streaming. Magic 3 (usb host mode) from the linux driver
- * makes it work. Ick.
- */
- opio->usbmode |= 1 << 4 | 3;
- coherence();
- }
- static void
- setaddrwin(Kwusb *kw, int win, int attr, ulong base)
- {
- /* target 0, size 256MB */
- kw->win[win].ctl = Winenable | 0 << 4 | attr << 8 |
- SIZETO64KSIZE(256*MB) << 16;
- kw->win[win].base = base;
- }
- static void
- ehcireset(Ctlr *ctlr)
- {
- int i;
- ulong v;
- Eopio *opio;
- Kwusb *kw;
- ilock(ctlr);
- dprint("ehci %#p reset\n", ctlr->capio);
- opio = ctlr->opio;
- kw = (Kwusb *)(Addrusb + 0x300);
- kw->bic = kw->bim = 0;
- ctlrreset(ctlr);
- /*
- * clear high 32 bits of address signals if it's 64 bits capable.
- * This is probably not needed but it does not hurt and others do it.
- */
- if((ctlr->capio->capparms & C64) != 0){
- dprint("ehci: 64 bits\n");
- opio->seg = 0;
- }
- /* requesting more interrupts per µframe may miss interrupts */
- opio->cmd |= Citc8; /* 1 intr. per ms */
- switch(opio->cmd & Cflsmask){
- case Cfls1024:
- ctlr->nframes = 1024;
- break;
- case Cfls512:
- ctlr->nframes = 512;
- break;
- case Cfls256:
- ctlr->nframes = 256;
- break;
- default:
- panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
- }
- dprint("ehci: %d frames\n", ctlr->nframes);
- /*
- * set up the USB address map (bridge address decoding)
- */
- for (i = 0; i < nelem(kw->win); i++)
- kw->win[i].ctl = kw->win[i].base = 0;
- coherence();
- setaddrwin(kw, 0, 0xe, 0); /* attr 0xe: sdram cs 0 */
- setaddrwin(kw, 1, 0xd, 256*MB); /* attr 0xd: sdram cs 1 */
- setaddrwin(kw, 2, 0xe, (ulong)KADDR(0)); /* attr 0xe: sdram cs 0 */
- setaddrwin(kw, 3, 0xd, (ulong)KADDR(256*MB)); /* attr 0xd: sdram cs 1 */
- coherence();
- if (kw->bcs & (1 << 4))
- print("usb BS bit is one, thus no byte swapping\n");
- else
- print("usb BS bit is zero, thus byte swapping\n");
- addrmapdump(); /* verify sanity */
- kw->pwrctl |= 1 << 0 | 1 << 1; /* Pu | PuPll */
- coherence();
- /*
- * Marvell guideline GL-USB-160.
- */
- kw->phypll |= 1 << 21; /* VCOCAL_START: PLL calibration */
- coherence();
- microdelay(100);
- kw->phypll &= ~(1 << 21);
- v = kw->phytxctl & ~(017 << 27 | 7); /* REG_EXT_FS_RCALL & AMP_2_0 */
- /*
- * AMP_2_0 = 4 for 6281-A0 (but 3 for A1).
- * also set REG_EXT_FS_RCALL_EN | REG_RCAL_START.
- */
- kw->phytxctl = v | 1 << 26 | 1 << 12 | 4;
- coherence();
- microdelay(100);
- kw->phytxctl &= ~(1 << 12);
- v = kw->phyrxctl & ~(3 << 2 | 017 << 4); /* LPF_COEF_1_0 & SQ_THRESH_3_0 */
- kw->phyrxctl = v | 1 << 2 | 8 << 4;
- v = kw->phyivref & ~(3 << 8); /* TXVDD12 */
- kw->phyivref = v | 1 << 8; /* 1 for 6281-A0; 3 for A1 */
- coherence();
- /*
- * Turn off legacy mode. Some controllers won't
- * interrupt us as expected otherwise.
- */
- ehcirun(ctlr, 0);
- ctlrreset(ctlr);
- iunlock(ctlr);
- }
- static void
- setdebug(Hci*, int d)
- {
- debug = d;
- }
- static void
- shutdown(Hci *hp)
- {
- Ctlr *ctlr;
- Eopio *opio;
- ctlr = hp->aux;
- ilock(ctlr);
- ctlrreset(ctlr);
- delay(100);
- ehcirun(ctlr, 0);
- opio = ctlr->opio;
- opio->frbase = 0;
- iunlock(ctlr);
- }
- static int
- reset(Hci *hp)
- {
- static Lock resetlck;
- int i;
- Ctlr *ctlr;
- Ecapio *capio;
- 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 == (uintptr)ctlr->capio){
- ctlr->active = 1;
- break;
- }
- }
- iunlock(&resetlck);
- if(ctlrs[i] == nil || i == Nhcis)
- return -1;
- hp->aux = ctlr;
- hp->port = (uintptr)ctlr->capio;
- hp->irq = IRQ0usb0;
- hp->tbdf = 0;
- capio = ctlr->capio;
- hp->nports = capio->parms & Cnports;
- ddprint("echi: %s, ncc %lud npcc %lud\n",
- capio->parms & 0x10000 ? "leds" : "no leds",
- (capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
- ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
- capio->parms & 0x40 ? "explicit" : "automatic",
- capio->parms & 0x10 ? "" : "no ", hp->nports);
- ehcireset(ctlr);
- ehcimeminit(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 = "ehci";
- return 0;
- }
- void
- usbehcilink(void)
- {
- addhcitype("ehci", reset);
- }
|