123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614 |
- /*
- * DAHDI Telephony Interface Driver
- *
- * Written by Mark Spencer <markster@digium.com>
- * Based on previous works, designs, and architectures conceived and
- * written by Jim Dixon <jim@lambdatel.com>.
- *
- * Special thanks to Steve Underwood <steve@coppice.org>
- * for substantial contributions to signal processing functions
- * in DAHDI and the Zapata library.
- *
- * Yury Bokhoncovich <byg@cf1.ru>
- * Adaptation for 2.4.20+ kernels (HDLC API was changed)
- * The work has been performed as a part of our move
- * from Cisco 3620 to IBM x305 here in F1 Group
- *
- * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
- * Copyright (C) 2001 - 2012 Digium, Inc.
- *
- * All rights reserved.
- *
- */
- /*
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2 as published by the
- * Free Software Foundation. See the LICENSE file included with
- * this program for more details.
- */
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/module.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/version.h>
- #include <linux/ctype.h>
- #include <linux/kmod.h>
- #include <linux/moduleparam.h>
- #include <linux/sched.h>
- #include <linux/list.h>
- #include <linux/delay.h>
- #include <linux/mutex.h>
- #include <linux/ktime.h>
- #include <linux/slab.h>
- #if defined(HAVE_UNLOCKED_IOCTL) && defined(CONFIG_BKL)
- #include <linux/smp_lock.h>
- #endif
- #include <linux/ppp_defs.h>
- #include <asm/atomic.h>
- #define DAHDI_PRINK_MACROS_USE_debug
- /* Grab fasthdlc with tables */
- #define FAST_HDLC_NEED_TABLES
- #include <dahdi/kernel.h>
- #include "ecdis.h"
- #include "dahdi.h"
- #ifdef CONFIG_DAHDI_PPP
- #include <linux/netdevice.h>
- #include <linux/if.h>
- #include <linux/if_ppp.h>
- #endif
- #ifdef CONFIG_DAHDI_NET
- #include <linux/netdevice.h>
- #endif
- #include "hpec/hpec_user.h"
- #include <stdbool.h>
- #if defined(EMPULSE) && defined(EMFLASH)
- #error "You cannot define both EMPULSE and EMFLASH"
- #endif
- /* Get helper arithmetic */
- #include "arith.h"
- #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
- #include <asm/i387.h>
- #endif
- #define hdlc_to_chan(h) (((struct dahdi_hdlc *)(h))->chan)
- #define netdev_to_chan(h) (((struct dahdi_hdlc *)(dev_to_hdlc(h)->priv))->chan)
- #define chan_to_netdev(h) ((h)->hdlcnetdev->netdev)
- /* macro-oni for determining a unit (channel) number */
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
- #define UNIT(file) MINOR(file->f_dentry->d_inode->i_rdev)
- #else
- #define UNIT(file) MINOR(file->f_path.dentry->d_inode->i_rdev)
- #endif
- EXPORT_SYMBOL(dahdi_transcode_fops);
- EXPORT_SYMBOL(dahdi_init_tone_state);
- EXPORT_SYMBOL(dahdi_mf_tone);
- EXPORT_SYMBOL(__dahdi_mulaw);
- EXPORT_SYMBOL(__dahdi_alaw);
- #ifdef CONFIG_CALC_XLAW
- EXPORT_SYMBOL(__dahdi_lineartoulaw);
- EXPORT_SYMBOL(__dahdi_lineartoalaw);
- #else
- EXPORT_SYMBOL(__dahdi_lin2mu);
- EXPORT_SYMBOL(__dahdi_lin2a);
- #endif
- EXPORT_SYMBOL(dahdi_rbsbits);
- EXPORT_SYMBOL(dahdi_qevent_nolock);
- EXPORT_SYMBOL(dahdi_qevent_lock);
- EXPORT_SYMBOL(dahdi_hooksig);
- EXPORT_SYMBOL(dahdi_alarm_notify);
- EXPORT_SYMBOL(dahdi_hdlc_abort);
- EXPORT_SYMBOL(dahdi_hdlc_finish);
- EXPORT_SYMBOL(dahdi_hdlc_getbuf);
- EXPORT_SYMBOL(dahdi_hdlc_putbuf);
- EXPORT_SYMBOL(dahdi_alarm_channel);
- EXPORT_SYMBOL(dahdi_register_echocan_factory);
- EXPORT_SYMBOL(dahdi_unregister_echocan_factory);
- EXPORT_SYMBOL(dahdi_set_hpec_ioctl);
- #ifdef CONFIG_PROC_FS
- static struct proc_dir_entry *root_proc_entry;
- #endif
- static int deftaps = 64;
- int debug;
- #define DEBUG_MAIN (1 << 0)
- #define DEBUG_RBS (1 << 5)
- static int hwec_overrides_swec = 1;
- /*!
- * \brief states for transmit signalling
- */
- enum dahdi_txstate {
- DAHDI_TXSTATE_ONHOOK,
- DAHDI_TXSTATE_OFFHOOK,
- DAHDI_TXSTATE_START,
- DAHDI_TXSTATE_PREWINK,
- DAHDI_TXSTATE_WINK,
- DAHDI_TXSTATE_PREFLASH,
- DAHDI_TXSTATE_FLASH,
- DAHDI_TXSTATE_DEBOUNCE,
- DAHDI_TXSTATE_AFTERSTART,
- DAHDI_TXSTATE_RINGON,
- DAHDI_TXSTATE_RINGOFF,
- DAHDI_TXSTATE_KEWL,
- DAHDI_TXSTATE_AFTERKEWL,
- DAHDI_TXSTATE_PULSEBREAK,
- DAHDI_TXSTATE_PULSEMAKE,
- DAHDI_TXSTATE_PULSEAFTER,
- };
- typedef short sumtype[DAHDI_MAX_CHUNKSIZE];
- static sumtype sums[(DAHDI_MAX_CONF + 1) * 3];
- /* Translate conference aliases into actual conferences
- and vice-versa */
- static short confalias[DAHDI_MAX_CONF + 1];
- static short confrev[DAHDI_MAX_CONF + 1];
- static sumtype *conf_sums_next;
- static sumtype *conf_sums;
- static sumtype *conf_sums_prev;
- static struct dahdi_span *master_span;
- struct file_operations *dahdi_transcode_fops = NULL;
- #ifdef CONFIG_DAHDI_CONFLINK
- static struct {
- int src; /* source conf number */
- int dst; /* dst conf number */
- } conf_links[DAHDI_MAX_CONF + 1];
- static int maxlinks;
- #endif
- #ifdef CONFIG_DAHDI_CORE_TIMER
- static struct core_timer {
- struct timer_list timer;
- struct timespec start_interval;
- unsigned long interval;
- int dahdi_receive_used;
- atomic_t count;
- atomic_t shutdown;
- atomic_t last_count;
- } core_timer;
- #endif /* CONFIG_DAHDI_CORE_TIMER */
- enum dahdi_digit_mode {
- DIGIT_MODE_DTMF,
- DIGIT_MODE_MFR1,
- DIGIT_MODE_PULSE,
- DIGIT_MODE_MFR2_FWD,
- DIGIT_MODE_MFR2_REV,
- };
- /* At the end of silence, the tone stops */
- static struct dahdi_tone dtmf_silence = {
- .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_DTMF_LENGTH),
- };
- /* At the end of silence, the tone stops */
- static struct dahdi_tone mfr1_silence = {
- .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR1_LENGTH),
- };
- /* At the end of silence, the tone stops */
- static struct dahdi_tone mfr2_silence = {
- .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR2_LENGTH),
- };
- /* A pause in the dialing */
- static struct dahdi_tone tone_pause = {
- .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_PAUSE_LENGTH),
- };
- static struct dahdi_dialparams global_dialparams = {
- .dtmf_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_DTMF_LENGTH),
- .mfv1_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR1_LENGTH),
- .mfr2_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR2_LENGTH),
- };
- static DEFINE_MUTEX(global_dialparamslock);
- static int dahdi_chan_ioctl(struct file *file, unsigned int cmd, unsigned long data);
- #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
- #if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_I386)
- struct fpu_save_buf {
- unsigned long cr0;
- unsigned long fpu_buf[128];
- };
- static DEFINE_PER_CPU(struct fpu_save_buf, fpu_buf);
- /** dahdi_kernel_fpu_begin() - Save floating point registers
- *
- * This function is similar to kernel_fpu_begin() . However it is
- * designed to work in an interrupt context. Restoring must be done with
- * dahdi_kernel_fpu_end().
- *
- * Furthermore, the whole code between the call to
- * dahdi_kernel_fpu_begin() and dahdi_kernel_fpu_end() must reside
- * inside a spinlock. Otherwise the context might be restored to the
- * wrong process.
- *
- * Current implementation is x86/ia32-specific and will not even build on
- * x86_64)
- * */
- static inline void dahdi_kernel_fpu_begin(void)
- {
- struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
- __asm__ __volatile__ ("movl %%cr0,%0; clts" : "=r" (buf->cr0));
- __asm__ __volatile__ ("fnsave %0" : "=m" (buf->fpu_buf));
- }
- /** dahdi_kernel_fpu_end() - restore floating point context
- *
- * Must be used with context saved by dahdi_kernel_fpu_begin(). See its
- * documentation for further information.
- */
- static inline void dahdi_kernel_fpu_end(void)
- {
- struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
- __asm__ __volatile__ ("frstor %0" : "=m" (buf->fpu_buf));
- __asm__ __volatile__ ("movl %0,%%cr0" : : "r" (buf->cr0));
- }
- #else /* We haven't fixed FP context saving/restoring yet */
- /* Very strange things can happen when the context is not properly
- * restored. OTOH, some people do report success with this. Hence we
- * so far just issue a warning */
- #warning CONFIG_DAHDI_MMX may behave randomly on this platform
- #define dahdi_kernel_fpu_begin kernel_fpu_begin
- #define dahdi_kernel_fpu_end kernel_fpu_end
- #endif
- #endif
- struct dahdi_timer {
- spinlock_t lock;
- int ms; /* Countdown */
- int pos; /* Position */
- int ping; /* Whether we've been ping'd */
- int tripped; /* Whether we're tripped */
- struct list_head list;
- wait_queue_head_t sel;
- };
- static LIST_HEAD(dahdi_timers);
- static DEFINE_SPINLOCK(dahdi_timer_lock);
- #define DEFAULT_TONE_ZONE (-1)
- struct dahdi_zone {
- int ringcadence[DAHDI_MAX_CADENCE];
- struct dahdi_tone *tones[DAHDI_TONE_MAX];
- /* Each of these is a circular list
- of dahdi_tones to generate what we
- want. Use NULL if the tone is
- unavailable */
- struct dahdi_tone dtmf[16]; /* DTMF tones for this zone, with desired length */
- struct dahdi_tone dtmf_continuous[16]; /* DTMF tones for this zone, continuous play */
- struct dahdi_tone mfr1[15]; /* MFR1 tones for this zone, with desired length */
- struct dahdi_tone mfr2_fwd[15]; /* MFR2 FWD tones for this zone, with desired length */
- struct dahdi_tone mfr2_rev[15]; /* MFR2 REV tones for this zone, with desired length */
- struct dahdi_tone mfr2_fwd_continuous[16]; /* MFR2 FWD tones for this zone, continuous play */
- struct dahdi_tone mfr2_rev_continuous[16]; /* MFR2 REV tones for this zone, continuous play */
- struct list_head node;
- struct kref refcount;
- const char *name; /* Informational, only */
- u8 num;
- };
- static void tone_zone_release(struct kref *kref)
- {
- struct dahdi_zone *z = container_of(kref, struct dahdi_zone, refcount);
- kfree(z->name);
- kfree(z);
- }
- /**
- * tone_zone_put() - Release the reference on the tone_zone.
- *
- * On old kernels, since kref_put does not have a return value, we'll just
- * always report that we released the memory.
- *
- */
- static inline int tone_zone_put(struct dahdi_zone *z)
- {
- return kref_put(&z->refcount, tone_zone_release);
- }
- static inline void tone_zone_get(struct dahdi_zone *z)
- {
- kref_get(&z->refcount);
- }
- static DEFINE_SPINLOCK(zone_lock);
- /* The first zone on the list is the default zone. */
- static LIST_HEAD(tone_zones);
- static inline struct device *span_device(struct dahdi_span *span)
- {
- return &span->parent->dev;
- }
- /* Protects the span_list and pseudo_chans lists from concurrent access in
- * process context. The spin_lock is needed to synchronize with the interrupt
- * handler. */
- static DEFINE_SPINLOCK(chan_lock);
- struct pseudo_chan {
- struct dahdi_chan chan;
- struct list_head node;
- };
- static inline struct pseudo_chan *chan_to_pseudo(struct dahdi_chan *chan)
- {
- return container_of(chan, struct pseudo_chan, chan);
- }
- enum { FIRST_PSEUDO_CHANNEL = 0x8000, };
- /* This list is protected by the chan_lock. */
- static LIST_HEAD(pseudo_chans);
- /**
- * is_pseudo_chan() - By definition pseudo channels are not on a span.
- */
- static inline bool is_pseudo_chan(const struct dahdi_chan *chan)
- {
- return (NULL == chan->span);
- }
- static DEFINE_MUTEX(registration_mutex);
- static LIST_HEAD(span_list);
- static unsigned long
- __for_each_channel(unsigned long (*func)(struct dahdi_chan *chan,
- unsigned long data),
- unsigned long data)
- {
- int res;
- struct dahdi_span *s;
- struct pseudo_chan *pseudo;
- list_for_each_entry(s, &span_list, spans_node) {
- unsigned long x;
- for (x = 0; x < s->channels; x++) {
- struct dahdi_chan *const chan = s->chans[x];
- res = func(chan, data);
- if (res)
- return res;
- }
- }
- list_for_each_entry(pseudo, &pseudo_chans, node) {
- res = func(&pseudo->chan, data);
- if (res)
- return res;
- }
- return 0;
- }
- /**
- * _chan_from_num - Lookup a channel
- *
- * Must be called with the registration_mutex held.
- *
- */
- static struct dahdi_chan *_chan_from_num(unsigned int channo)
- {
- struct dahdi_span *s;
- struct pseudo_chan *pseudo;
- if (channo >= FIRST_PSEUDO_CHANNEL) {
- list_for_each_entry(pseudo, &pseudo_chans, node) {
- if (pseudo->chan.channo == channo)
- return &pseudo->chan;
- }
- return NULL;
- }
- /* When searching for the channel amongst the spans, we can use the
- * fact that channels on a span must be numbered consecutively to skip
- * checking each individual channel. */
- list_for_each_entry(s, &span_list, spans_node) {
- unsigned int basechan;
- struct dahdi_chan *chan;
- if (unlikely(!s->channels))
- continue;
- basechan = s->chans[0]->channo;
- if (channo >= (basechan + s->channels))
- continue;
- /* Since all the spans should be on the list in sorted order,
- * if channo is less than base chan, the caller must be
- * looking for a channel that has already been removed. */
- if (unlikely(channo < basechan))
- return NULL;
- chan = s->chans[channo - basechan];
- WARN_ON(chan->channo != channo);
- return chan;
- }
- return NULL;
- }
- static struct dahdi_chan *chan_from_num(unsigned int channo)
- {
- struct dahdi_chan *chan;
- mutex_lock(®istration_mutex);
- chan = _chan_from_num(channo);
- mutex_unlock(®istration_mutex);
- return chan;
- }
- static inline struct dahdi_chan *chan_from_file(struct file *file)
- {
- return (file->private_data) ?
- file->private_data : chan_from_num(UNIT(file));
- }
- /**
- * _find_span() - Find a span by span number.
- *
- * Must be called with registration_mutex held.
- *
- */
- static struct dahdi_span *_find_span(int spanno)
- {
- struct dahdi_span *s;
- list_for_each_entry(s, &span_list, spans_node) {
- if (s->spanno == spanno) {
- return s;
- }
- }
- return NULL;
- }
- /**
- * span_find_and_get() - Search for the span by number, and if found take out
- * a reference on it.
- *
- * When you are no longer using the returned pointer, you must release it with
- * a put_span call.
- *
- */
- static struct dahdi_span *span_find_and_get(int spanno)
- {
- struct dahdi_span *found;
- mutex_lock(®istration_mutex);
- found = _find_span(spanno);
- if (found && !get_span(found))
- found = NULL;
- mutex_unlock(®istration_mutex);
- return found;
- }
- static unsigned int span_count(void)
- {
- unsigned int count = 0;
- struct dahdi_span *s;
- unsigned long flags;
- spin_lock_irqsave(&chan_lock, flags);
- list_for_each_entry(s, &span_list, spans_node)
- ++count;
- spin_unlock_irqrestore(&chan_lock, flags);
- return count;
- }
- static inline bool can_provide_timing(const struct dahdi_span *const s)
- {
- return !s->cannot_provide_timing;
- }
- static int maxconfs;
- short __dahdi_mulaw[256];
- short __dahdi_alaw[256];
- #ifndef CONFIG_CALC_XLAW
- u_char __dahdi_lin2mu[16384];
- u_char __dahdi_lin2a[16384];
- #endif
- static u_char defgain[256];
- #define NUM_SIGS 10
- static DEFINE_SPINLOCK(ecfactory_list_lock);
- static LIST_HEAD(ecfactory_list);
- struct ecfactory {
- const struct dahdi_echocan_factory *ec;
- struct list_head list;
- };
- int dahdi_register_echocan_factory(const struct dahdi_echocan_factory *ec)
- {
- struct ecfactory *cur;
- struct ecfactory *new;
- WARN_ON(!ec->owner);
- new = kzalloc(sizeof(*new), GFP_KERNEL);
- if (!new)
- return -ENOMEM;
- INIT_LIST_HEAD(&new->list);
- spin_lock(&ecfactory_list_lock);
- /* make sure it isn't already registered */
- list_for_each_entry(cur, &ecfactory_list, list) {
- if (cur->ec == ec) {
- spin_unlock(&ecfactory_list_lock);
- kfree(new);
- return -EPERM;
- }
- }
- new->ec = ec;
- list_add_tail(&new->list, &ecfactory_list);
- spin_unlock(&ecfactory_list_lock);
- return 0;
- }
- void dahdi_unregister_echocan_factory(const struct dahdi_echocan_factory *ec)
- {
- struct ecfactory *cur, *next;
- spin_lock(&ecfactory_list_lock);
- list_for_each_entry_safe(cur, next, &ecfactory_list, list) {
- if (cur->ec == ec) {
- list_del(&cur->list);
- kfree(cur);
- break;
- }
- }
- spin_unlock(&ecfactory_list_lock);
- }
- /* Is this span our syncronization master? */
- int dahdi_is_sync_master(const struct dahdi_span *span)
- {
- return span == master_span;
- }
- static inline void rotate_sums(void)
- {
- /* Rotate where we sum and so forth */
- static int pos = 0;
- conf_sums_prev = sums + (DAHDI_MAX_CONF + 1) * pos;
- conf_sums = sums + (DAHDI_MAX_CONF + 1) * ((pos + 1) % 3);
- conf_sums_next = sums + (DAHDI_MAX_CONF + 1) * ((pos + 2) % 3);
- pos = (pos + 1) % 3;
- memset(conf_sums_next, 0, maxconfs * sizeof(sumtype));
- }
- /**
- * is_chan_dacsed() - True if chan is sourcing it's data from another channel.
- *
- */
- static inline bool is_chan_dacsed(const struct dahdi_chan *const chan)
- {
- return (NULL != chan->dacs_chan);
- }
- /**
- * can_dacs_chans() - Returns true if it may be possible to dacs two channels.
- *
- */
- static bool can_dacs_chans(struct dahdi_chan *dst, struct dahdi_chan *src)
- {
- if (src && dst && src->span && dst->span && src->span->ops &&
- dst->span->ops && src->span->ops->dacs &&
- (src->span->ops->dacs == dst->span->ops->dacs))
- return true;
- else
- return false;
- }
- /**
- * dahdi_chan_dacs() - Cross (or uncross) connect two channels.
- * @dst: Channel on which to transmit the src data.
- * @src: NULL to disable cross connect, otherwise the source of the
- * data.
- *
- * This allows those boards that support it to cross connect one channel to
- * another in hardware. If the cards cannot be crossed, uncross the
- * destination channel by default..
- *
- */
- static int dahdi_chan_dacs(struct dahdi_chan *dst, struct dahdi_chan *src)
- {
- int ret = 0;
- if (can_dacs_chans(dst, src))
- ret = dst->span->ops->dacs(dst, src);
- else if (dst->span && dst->span->ops->dacs)
- ret = dst->span->ops->dacs(dst, NULL);
- return ret;
- }
- static void dahdi_disable_dacs(struct dahdi_chan *chan)
- {
- dahdi_chan_dacs(chan, NULL);
- }
- /*!
- * \return quiescent (idle) signalling states, for the various signalling types
- */
- static int dahdi_q_sig(struct dahdi_chan *chan)
- {
- int x;
- static const unsigned int in_sig[NUM_SIGS][2] = {
- { DAHDI_SIG_NONE, 0 },
- { DAHDI_SIG_EM, (DAHDI_ABIT << 8) },
- { DAHDI_SIG_FXSLS, DAHDI_BBIT | (DAHDI_BBIT << 8) },
- { DAHDI_SIG_FXSGS, DAHDI_ABIT | DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) },
- { DAHDI_SIG_FXSKS, DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) },
- { DAHDI_SIG_FXOLS, (DAHDI_ABIT << 8) },
- { DAHDI_SIG_FXOGS, DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) },
- { DAHDI_SIG_FXOKS, (DAHDI_ABIT << 8) },
- { DAHDI_SIG_SF, 0 },
- { DAHDI_SIG_EM_E1, DAHDI_DBIT | ((DAHDI_ABIT | DAHDI_DBIT) << 8) },
- };
- /* must have span to begin with */
- if (!chan->span)
- return -1;
- /* if RBS does not apply, return error */
- if (!(chan->span->flags & DAHDI_FLAG_RBS) || !chan->span->ops->rbsbits)
- return -1;
- if (chan->sig == DAHDI_SIG_CAS)
- return chan->idlebits;
- for (x = 0; x < NUM_SIGS; x++) {
- if (in_sig[x][0] == chan->sig)
- return in_sig[x][1];
- }
- return -1; /* not found -- error */
- }
- enum spantypes dahdi_str2spantype(const char *name)
- {
- if (strcasecmp("FXS", name) == 0)
- return SPANTYPE_ANALOG_FXS;
- else if (strcasecmp("FXO", name) == 0)
- return SPANTYPE_ANALOG_FXO;
- else if (strcasecmp("ANALOG_MIXED", name) == 0)
- return SPANTYPE_ANALOG_MIXED;
- else if (strcasecmp("E1", name) == 0)
- return SPANTYPE_DIGITAL_E1;
- else if (strcasecmp("T1", name) == 0)
- return SPANTYPE_DIGITAL_T1;
- else if (strcasecmp("J1", name) == 0)
- return SPANTYPE_DIGITAL_J1;
- else if (strcasecmp("BRI_NT", name) == 0)
- return SPANTYPE_DIGITAL_BRI_NT;
- else if (strcasecmp("BRI_TE", name) == 0)
- return SPANTYPE_DIGITAL_BRI_TE;
- else if (strcasecmp("BRI_SOFT", name) == 0)
- return SPANTYPE_DIGITAL_BRI_SOFT;
- else if (strcasecmp("DYNAMIC", name) == 0)
- return SPANTYPE_DIGITAL_DYNAMIC;
- else
- return SPANTYPE_INVALID;
- }
- EXPORT_SYMBOL(dahdi_str2spantype);
- const char *dahdi_spantype2str(enum spantypes st)
- {
- switch (st) {
- case SPANTYPE_ANALOG_FXS: return "FXS";
- case SPANTYPE_ANALOG_FXO: return "FXO";
- case SPANTYPE_ANALOG_MIXED: return "ANALOG_MIXED";
- case SPANTYPE_DIGITAL_E1: return "E1";
- case SPANTYPE_DIGITAL_T1: return "T1";
- case SPANTYPE_DIGITAL_J1: return "J1";
- case SPANTYPE_DIGITAL_BRI_NT: return "BRI_NT";
- case SPANTYPE_DIGITAL_BRI_TE: return "BRI_TE";
- case SPANTYPE_DIGITAL_BRI_SOFT: return "BRI_SOFT";
- case SPANTYPE_DIGITAL_DYNAMIC: return "DYNAMIC";
- default:
- case SPANTYPE_INVALID: return "INVALID";
- };
- }
- EXPORT_SYMBOL(dahdi_spantype2str);
- const char *dahdi_lineconfig_bit_name(int lineconfig_bit)
- {
- static const char * const table[] = {
- /* These apply to T1 */
- [4] = "D4",
- [5] = "ESF",
- [6] = "AMI",
- [7] = "B8ZS",
- /* These apply to E1 */
- [8] = "CCS",
- [9] = "HDB3",
- [10] = "CRC4",
- /* These apply to BRI */
- [11] = "NTTE",
- [12] = "TERM",
- /* Finish */
- [16] = "NOTOPEN",
- };
- if (lineconfig_bit < 0 || lineconfig_bit >= ARRAY_SIZE(table))
- return NULL;
- return table[lineconfig_bit];
- }
- EXPORT_SYMBOL(dahdi_lineconfig_bit_name);
- ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
- {
- int framing_bit = 0;
- int coding_bit = 0;
- int crc4_bit = 0;
- int len = 0;
- int bit;
- bool written = false;
- for (bit = 4; bit <= 12; bit++) {
- int mask = (1 << bit);
- if (!(lineconfig & mask))
- continue;
- if (!framing_bit) {
- switch (mask) {
- case DAHDI_CONFIG_B8ZS:
- case DAHDI_CONFIG_AMI:
- case DAHDI_CONFIG_HDB3:
- framing_bit = bit;
- len += snprintf(buf + len, size, "%s%s",
- (written) ? "/" : "",
- dahdi_lineconfig_bit_name(bit));
- written = true;
- }
- }
- if (!coding_bit) {
- switch (mask) {
- case DAHDI_CONFIG_ESF:
- case DAHDI_CONFIG_D4:
- case DAHDI_CONFIG_CCS:
- coding_bit = bit;
- len += snprintf(buf + len, size, "%s%s",
- (written) ? "/" : "",
- dahdi_lineconfig_bit_name(bit));
- written = true;
- }
- }
- if (!crc4_bit && mask == DAHDI_CONFIG_CRC4) {
- crc4_bit = bit;
- len += snprintf(buf + len, size, "%s%s",
- (written) ? "/" : "",
- dahdi_lineconfig_bit_name(bit));
- written = true;
- }
- }
- return len;
- }
- EXPORT_SYMBOL(lineconfig_str);
- #ifdef CONFIG_PROC_FS
- const char *sigstr(int sig)
- {
- switch (sig) {
- case DAHDI_SIG_FXSLS:
- return "FXSLS";
- case DAHDI_SIG_FXSKS:
- return "FXSKS";
- case DAHDI_SIG_FXSGS:
- return "FXSGS";
- case DAHDI_SIG_FXOLS:
- return "FXOLS";
- case DAHDI_SIG_FXOKS:
- return "FXOKS";
- case DAHDI_SIG_FXOGS:
- return "FXOGS";
- case DAHDI_SIG_EM:
- return "E&M";
- case DAHDI_SIG_EM_E1:
- return "E&M-E1";
- case DAHDI_SIG_CLEAR:
- return "Clear";
- case DAHDI_SIG_HDLCRAW:
- return "HDLCRAW";
- case DAHDI_SIG_HDLCFCS:
- return "HDLCFCS";
- case DAHDI_SIG_HDLCNET:
- return "HDLCNET";
- case DAHDI_SIG_HARDHDLC:
- return "Hardware-assisted HDLC";
- case DAHDI_SIG_MTP2:
- return "MTP2";
- case DAHDI_SIG_SLAVE:
- return "Slave";
- case DAHDI_SIG_CAS:
- return "CAS";
- case DAHDI_SIG_DACS:
- return "DACS";
- case DAHDI_SIG_DACS_RBS:
- return "DACS+RBS";
- case DAHDI_SIG_SF:
- return "SF (ToneOnly)";
- case DAHDI_SIG_NONE:
- default:
- return "Unconfigured";
- }
- }
- int fill_alarm_string(char *buf, int count, int alarms)
- {
- int len;
- if (alarms <= 0)
- return 0;
- len = snprintf(buf, count, "%s%s%s%s%s%s",
- (alarms & DAHDI_ALARM_BLUE) ? "BLUE " : "",
- (alarms & DAHDI_ALARM_YELLOW) ? "YELLOW " : "",
- (alarms & DAHDI_ALARM_RED) ? "RED " : "",
- (alarms & DAHDI_ALARM_LOOPBACK) ? "LOOP " : "",
- (alarms & DAHDI_ALARM_RECOVER) ? "RECOVERING " : "",
- (alarms & DAHDI_ALARM_NOTOPEN) ? "NOTOPEN " : "");
- return len;
- }
- /*
- * Sequential proc interface
- */
- static void seq_fill_alarm_string(struct seq_file *sfile, int alarms)
- {
- char tmp[70];
- if (fill_alarm_string(tmp, sizeof(tmp), alarms))
- seq_printf(sfile, "%s", tmp);
- }
- static int dahdi_seq_show(struct seq_file *sfile, void *data)
- {
- long spanno = (long)sfile->private;
- int x;
- struct dahdi_span *s;
- s = span_find_and_get(spanno);
- if (!s)
- return -ENODEV;
- if (s->name)
- seq_printf(sfile, "Span %d: %s ", s->spanno, s->name);
- if (s->desc)
- seq_printf(sfile, "\"%s\"", s->desc);
- else
- seq_printf(sfile, "\"\"");
- if (dahdi_is_sync_master(s))
- seq_printf(sfile, " (MASTER)");
- if (s->lineconfig) {
- char tmpbuf[20];
- lineconfig_str(s->lineconfig, tmpbuf, sizeof(tmpbuf));
- seq_printf(sfile, " %s", tmpbuf);
- }
- seq_printf(sfile, " ");
- /* list alarms */
- seq_fill_alarm_string(sfile, s->alarms);
- if (s->syncsrc &&
- (s->syncsrc == s->spanno))
- seq_printf(sfile, "ClockSource ");
- seq_printf(sfile, "\n");
- if (s->count.bpv)
- seq_printf(sfile, "\tBPV count: %d\n", s->count.bpv);
- if (s->count.crc4)
- seq_printf(sfile, "\tCRC4 error count: %d\n", s->count.crc4);
- if (s->count.ebit)
- seq_printf(sfile, "\tE-bit error count: %d\n", s->count.ebit);
- if (s->count.fas)
- seq_printf(sfile, "\tFAS error count: %d\n", s->count.fas);
- if (s->parent->irqmisses)
- seq_printf(sfile, "\tIRQ misses: %d\n", s->parent->irqmisses);
- if (s->count.timingslips)
- seq_printf(sfile, "\tTiming slips: %d\n", s->count.timingslips);
- seq_printf(sfile, "\n");
- for (x = 0; x < s->channels; x++) {
- struct dahdi_chan *chan = s->chans[x];
- if (chan->name)
- seq_printf(sfile, "\t%4d %s ", chan->channo,
- chan->name);
- if (chan->sig) {
- if (chan->sig == DAHDI_SIG_SLAVE)
- seq_printf(sfile, "%s ",
- sigstr(chan->master->sig));
- else {
- seq_printf(sfile, "%s ", sigstr(chan->sig));
- if (chan->nextslave &&
- (chan->master->channo == chan->channo))
- seq_printf(sfile, "Master ");
- }
- } else if (!chan->sigcap) {
- seq_printf(sfile, "Reserved ");
- }
- if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags))
- seq_printf(sfile, "(In use) ");
- #ifdef OPTIMIZE_CHANMUTE
- if (chan->chanmute)
- seq_printf(sfile, "(no pcm) ");
- #endif
- seq_fill_alarm_string(sfile, chan->chan_alarms);
- mutex_lock(&chan->mutex);
- if (chan->ec_factory) {
- seq_printf(sfile, "(EC: %s - %s) ",
- chan->ec_factory->get_name(chan),
- chan->ec_state ? "ACTIVE" : "INACTIVE");
- }
- mutex_unlock(&chan->mutex);
- seq_printf(sfile, "\n");
- }
- put_span(s);
- return 0;
- }
- static int dahdi_proc_open(struct inode *inode, struct file *file)
- {
- return single_open(file, dahdi_seq_show, PDE_DATA(inode));
- }
- static const struct file_operations dahdi_proc_ops = {
- .owner = THIS_MODULE,
- .open = dahdi_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- #endif
- static int dahdi_first_empty_alias(void)
- {
- /* Find the first conference which has no alias pointing to it */
- int x;
- for (x=1;x<DAHDI_MAX_CONF;x++) {
- if (!confrev[x])
- return x;
- }
- return -1;
- }
- static void recalc_maxconfs(void)
- {
- int x;
- for (x = DAHDI_MAX_CONF - 1; x > 0; x--) {
- if (confrev[x]) {
- maxconfs = x + 1;
- return;
- }
- }
- maxconfs = 0;
- }
- static int dahdi_first_empty_conference(void)
- {
- /* Find the first conference which has no alias */
- int x;
- for (x = DAHDI_MAX_CONF - 1; x > 0; x--) {
- if (!confalias[x])
- return x;
- }
- return -1;
- }
- static int dahdi_get_conf_alias(int x)
- {
- int a;
- if (confalias[x])
- return confalias[x];
- /* Allocate an alias */
- a = dahdi_first_empty_alias();
- confalias[x] = a;
- confrev[a] = x;
- /* Highest conference may have changed */
- recalc_maxconfs();
- return a;
- }
- static unsigned long _chan_in_conf(struct dahdi_chan *chan, unsigned long x)
- {
- const int confmode = chan->confmode & DAHDI_CONF_MODE_MASK;
- return (chan && (chan->confna == x) &&
- (confmode == DAHDI_CONF_CONF ||
- confmode == DAHDI_CONF_CONFANN ||
- confmode == DAHDI_CONF_CONFMON ||
- confmode == DAHDI_CONF_CONFANNMON ||
- confmode == DAHDI_CONF_REALANDPSEUDO)) ? 1 : 0;
- }
- #ifdef CONFIG_DAHDI_CONFLINK
- static void recalc_maxlinks(void)
- {
- int x;
- for (x = DAHDI_MAX_CONF - 1; x > 0; x--) {
- if (conf_links[x].src || conf_links[x].dst) {
- maxlinks = x + 1;
- return;
- }
- }
- maxlinks = 0;
- }
- #endif
- static void dahdi_check_conf(int x)
- {
- unsigned long res;
- unsigned long flags;
- #ifdef CONFIG_DAHDI_CONFLINK
- int i;
- #endif
- /* return if no valid conf number */
- if (x <= 0)
- return;
- /* Return if there is no alias */
- if (!confalias[x])
- return;
- spin_lock_irqsave(&chan_lock, flags);
- res = __for_each_channel(_chan_in_conf, x);
- spin_unlock_irqrestore(&chan_lock, flags);
- if (res)
- return;
- /* If we get here, nobody is in the conference anymore. Clear it out
- both forward and reverse */
- confrev[confalias[x]] = 0;
- confalias[x] = 0;
- /* Highest conference may have changed */
- recalc_maxconfs();
- #ifdef CONFIG_DAHDI_CONFLINK
- /* And unlink it from any conflinks */
- for (i = DAHDI_MAX_CONF - 1; i > 0; i--) {
- if (conf_links[i].src == x)
- conf_links[i].src = 0;
- if (conf_links[i].dst == x)
- conf_links[i].dst = 0;
- }
- recalc_maxlinks();
- #endif
- }
- /* enqueue an event on a channel */
- static void __qevent(struct dahdi_chan *chan, int event)
- {
- /* if full, ignore */
- if ((chan->eventoutidx == 0) && (chan->eventinidx == (DAHDI_MAX_EVENTSIZE - 1)))
- return;
- /* if full, ignore */
- if (chan->eventinidx == (chan->eventoutidx - 1))
- return;
- /* save the event */
- chan->eventbuf[chan->eventinidx++] = event;
- /* wrap the index, if necessary */
- if (chan->eventinidx >= DAHDI_MAX_EVENTSIZE)
- chan->eventinidx = 0;
- /* wake em all up */
- wake_up_interruptible(&chan->waitq);
- return;
- }
- void dahdi_qevent_nolock(struct dahdi_chan *chan, int event)
- {
- __qevent(chan, event);
- }
- void dahdi_qevent_lock(struct dahdi_chan *chan, int event)
- {
- unsigned long flags;
- spin_lock_irqsave(&chan->lock, flags);
- __qevent(chan, event);
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- static inline void calc_fcs(struct dahdi_chan *ss, int inwritebuf)
- {
- int x;
- unsigned int fcs = PPP_INITFCS;
- unsigned char *data = ss->writebuf[inwritebuf];
- int len = ss->writen[inwritebuf];
- /* Not enough space to do FCS calculation */
- if (len < 2)
- return;
- for (x = 0; x < len - 2; x++)
- fcs = PPP_FCS(fcs, data[x]);
- fcs ^= 0xffff;
- /* Send out the FCS */
- data[len - 2] = (fcs & 0xff);
- data[len - 1] = (fcs >> 8) & 0xff;
- }
- static int dahdi_reallocbufs(struct dahdi_chan *ss, int blocksize, int numbufs)
- {
- unsigned char *newtxbuf = NULL;
- unsigned char *newrxbuf = NULL;
- unsigned char *oldtxbuf = NULL;
- unsigned char *oldrxbuf = NULL;
- unsigned long flags;
- int x;
- /* Check numbufs */
- if (numbufs < 2)
- numbufs = 2;
- if (numbufs > DAHDI_MAX_NUM_BUFS)
- numbufs = DAHDI_MAX_NUM_BUFS;
- /* We need to allocate our buffers now */
- if (blocksize) {
- newtxbuf = kzalloc(blocksize * numbufs, GFP_KERNEL);
- if (NULL == newtxbuf)
- return -ENOMEM;
- newrxbuf = kzalloc(blocksize * numbufs, GFP_KERNEL);
- if (NULL == newrxbuf) {
- kfree(newtxbuf);
- return -ENOMEM;
- }
- }
- /* Now that we've allocated our new buffers, we can safely
- move things around... */
- spin_lock_irqsave(&ss->lock, flags);
- ss->blocksize = blocksize; /* set the blocksize */
- oldrxbuf = ss->readbuf[0]; /* Keep track of the old buffer */
- oldtxbuf = ss->writebuf[0];
- ss->readbuf[0] = NULL;
- if (newrxbuf) {
- BUG_ON(NULL == newtxbuf);
- for (x = 0; x < numbufs; x++) {
- ss->readbuf[x] = newrxbuf + x * blocksize;
- ss->writebuf[x] = newtxbuf + x * blocksize;
- }
- } else {
- for (x = 0; x < numbufs; x++) {
- ss->readbuf[x] = NULL;
- ss->writebuf[x] = NULL;
- }
- }
- /* Mark all buffers as empty */
- for (x = 0; x < numbufs; x++) {
- ss->writen[x] =
- ss->writeidx[x]=
- ss->readn[x]=
- ss->readidx[x] = 0;
- }
- /* Keep track of where our data goes (if it goes
- anywhere at all) */
- if (newrxbuf) {
- ss->inreadbuf = 0;
- ss->inwritebuf = 0;
- } else {
- ss->inreadbuf = -1;
- ss->inwritebuf = -1;
- }
- ss->outreadbuf = -1;
- ss->outwritebuf = -1;
- ss->numbufs = numbufs;
- if ((ss->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || (ss->txbufpolicy == DAHDI_POLICY_HALF_FULL))
- ss->txdisable = 1;
- else
- ss->txdisable = 0;
- spin_unlock_irqrestore(&ss->lock, flags);
- kfree(oldtxbuf);
- kfree(oldrxbuf);
- return 0;
- }
- static int dahdi_hangup(struct dahdi_chan *chan);
- static void dahdi_set_law(struct dahdi_chan *chan, int law);
- /* Pull a DAHDI_CHUNKSIZE piece off the queue. Returns
- 0 on success or -1 on failure. If failed, provides
- silence */
- static int __buf_pull(struct confq *q, u_char *data, struct dahdi_chan *c)
- {
- int oldoutbuf = q->outbuf;
- /* Ain't nuffin to read */
- if (q->outbuf < 0) {
- if (data)
- memset(data, DAHDI_LIN2X(0,c), DAHDI_CHUNKSIZE);
- return -1;
- }
- if (data)
- memcpy(data, q->buf[q->outbuf], DAHDI_CHUNKSIZE);
- q->outbuf = (q->outbuf + 1) % DAHDI_CB_SIZE;
- /* Won't be nuffin next time */
- if (q->outbuf == q->inbuf) {
- q->outbuf = -1;
- }
- /* If they thought there was no space then
- there is now where we just read */
- if (q->inbuf < 0)
- q->inbuf = oldoutbuf;
- return 0;
- }
- /* Returns a place to put stuff, or NULL if there is
- no room */
- static u_char *__buf_pushpeek(struct confq *q)
- {
- if (q->inbuf < 0)
- return NULL;
- return q->buf[q->inbuf];
- }
- static u_char *__buf_peek(struct confq *q)
- {
- if (q->outbuf < 0)
- return NULL;
- return q->buf[q->outbuf];
- }
- /* Push something onto the queue, or assume what
- is there is valid if data is NULL */
- static int __buf_push(struct confq *q, const u_char *data)
- {
- int oldinbuf = q->inbuf;
- if (q->inbuf < 0) {
- return -1;
- }
- if (data)
- /* Copy in the data */
- memcpy(q->buf[q->inbuf], data, DAHDI_CHUNKSIZE);
- /* Advance the inbuf pointer */
- q->inbuf = (q->inbuf + 1) % DAHDI_CB_SIZE;
- if (q->inbuf == q->outbuf) {
- /* No space anymore... */
- q->inbuf = -1;
- }
- /* If they don't think data is ready, let
- them know it is now */
- if (q->outbuf < 0) {
- q->outbuf = oldinbuf;
- }
- return 0;
- }
- static void reset_conf(struct dahdi_chan *chan)
- {
- int x;
- /* Empty out buffers and reset to initialization */
- for (x = 0; x < DAHDI_CB_SIZE; x++)
- chan->confin.buf[x] = chan->confin.buffer + DAHDI_CHUNKSIZE * x;
- chan->confin.inbuf = 0;
- chan->confin.outbuf = -1;
- for (x = 0; x < DAHDI_CB_SIZE; x++)
- chan->confout.buf[x] = chan->confout.buffer + DAHDI_CHUNKSIZE * x;
- chan->confout.inbuf = 0;
- chan->confout.outbuf = -1;
- }
- static const struct dahdi_echocan_factory *find_echocan(const char *name)
- {
- struct ecfactory *cur;
- char *name_upper;
- char *c;
- const char *d;
- char modname_buf[128] = "dahdi_echocan_";
- unsigned int tried_once = 0;
- name_upper = kmalloc(strlen(name) + 1, GFP_KERNEL);
- if (!name_upper)
- return NULL;
- for (c = name_upper, d = name; *d; c++, d++) {
- *c = toupper(*d);
- }
- *c = '\0';
- retry:
- spin_lock(&ecfactory_list_lock);
- list_for_each_entry(cur, &ecfactory_list, list) {
- if (!strcmp(name_upper, cur->ec->get_name(NULL))) {
- if (try_module_get(cur->ec->owner)) {
- spin_unlock(&ecfactory_list_lock);
- kfree(name_upper);
- return cur->ec;
- } else {
- spin_unlock(&ecfactory_list_lock);
- kfree(name_upper);
- return NULL;
- }
- }
- }
- spin_unlock(&ecfactory_list_lock);
- if (tried_once) {
- kfree(name_upper);
- return NULL;
- }
- /* couldn't find it, let's try to load it */
- for (c = &modname_buf[strlen(modname_buf)], d = name; *d; c++, d++) {
- *c = tolower(*d);
- }
- request_module("%s", modname_buf);
- tried_once = 1;
- /* and try one more time */
- goto retry;
- }
- static void release_echocan(const struct dahdi_echocan_factory *ec)
- {
- if (ec)
- module_put(ec->owner);
- }
- /**
- * is_gain_allocated() - True if gain tables were dynamically allocated.
- * @chan: The channel to check.
- */
- static inline bool is_gain_allocated(const struct dahdi_chan *chan)
- {
- return (chan->rxgain && (chan->rxgain != defgain));
- }
- static const char *hwec_def_name = "HWEC";
- static const char *hwec_get_name(const struct dahdi_chan *chan)
- {
- if (chan && chan->span && chan->span->ops->echocan_name)
- return chan->span->ops->echocan_name(chan);
- else
- return hwec_def_name;
- }
- static int hwec_echocan_create(struct dahdi_chan *chan,
- struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct dahdi_echocan_state **ec)
- {
- if (chan->span && chan->span->ops->echocan_create)
- return chan->span->ops->echocan_create(chan, ecp, p, ec);
- else
- return -ENODEV;
- }
- static const struct dahdi_echocan_factory hwec_factory = {
- .get_name = hwec_get_name,
- .owner = THIS_MODULE,
- .echocan_create = hwec_echocan_create,
- };
- /**
- * dahdi_enable_hw_preechocan - Let the board driver enable hwpreec if possible.
- * @chan: The channel to monitor.
- *
- * Returns 0 on success, if there is a software echocanceler attached on
- * the channel, or the span does not have an enable_hw_preechocan callback.
- * Otherwise an error code.
- *
- */
- static int dahdi_enable_hw_preechocan(struct dahdi_chan *chan)
- {
- int res;
- mutex_lock(&chan->mutex);
- if (chan->ec_factory != &hwec_factory)
- res = -ENODEV;
- else
- res = 0;
- mutex_unlock(&chan->mutex);
- if (-ENODEV == res)
- return 0;
- if (chan->span->ops->enable_hw_preechocan)
- return chan->span->ops->enable_hw_preechocan(chan);
- else
- return 0;
- }
- /**
- * dahdi_disable_hw_preechocan - Disable any hardware pre echocan monitoring.
- * @chan: The channel to stop monitoring.
- *
- * Give the board driver the option to free any resources needed to monitor
- * the preechocan stream.
- *
- */
- static void dahdi_disable_hw_preechocan(struct dahdi_chan *chan)
- {
- if (chan->span->ops->disable_hw_preechocan)
- chan->span->ops->disable_hw_preechocan(chan);
- }
- /*
- * close_channel - close the channel, resetting any channel variables
- * @chan: the dahdi_chan to close
- *
- * This function is called before either the parent span is linked into the
- * span list, or for pseudos, place on the psuedo_list. Therefore, this
- * function nor it's callers should depend on the channel being findable
- * via those methods.
- */
- static void close_channel(struct dahdi_chan *chan)
- {
- unsigned long flags;
- const void *rxgain = NULL;
- struct dahdi_echocan_state *ec_state;
- const struct dahdi_echocan_factory *ec_current;
- int oldconf;
- short *readchunkpreec;
- #ifdef CONFIG_DAHDI_PPP
- struct ppp_channel *ppp;
- #endif
- might_sleep();
- if (chan->conf_chan &&
- ((DAHDI_CONF_MONITOR_RX_PREECHO == chan->confmode) ||
- (DAHDI_CONF_MONITOR_TX_PREECHO == chan->confmode) ||
- (DAHDI_CONF_MONITORBOTH_PREECHO == chan->confmode))) {
- void *readchunkpreec;
- spin_lock_irqsave(&chan->conf_chan->lock, flags);
- readchunkpreec = chan->conf_chan->readchunkpreec;
- chan->conf_chan->readchunkpreec = NULL;
- spin_unlock_irqrestore(&chan->conf_chan->lock, flags);
- if (readchunkpreec) {
- dahdi_disable_hw_preechocan(chan->conf_chan);
- kfree(readchunkpreec);
- }
- }
- /* XXX Buffers should be send out before reallocation!!! XXX */
- if (!(chan->flags & DAHDI_FLAG_NOSTDTXRX))
- dahdi_reallocbufs(chan, 0, 0);
- spin_lock_irqsave(&chan->lock, flags);
- #ifdef CONFIG_DAHDI_PPP
- ppp = chan->ppp;
- chan->ppp = NULL;
- #endif
- ec_state = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- readchunkpreec = chan->readchunkpreec;
- chan->readchunkpreec = NULL;
- chan->curtone = NULL;
- if (chan->curzone) {
- struct dahdi_zone *zone = chan->curzone;
- chan->curzone = NULL;
- tone_zone_put(zone);
- }
- chan->cadencepos = 0;
- chan->pdialcount = 0;
- dahdi_hangup(chan);
- chan->itimerset = chan->itimer = 0;
- chan->pulsecount = 0;
- chan->pulsetimer = 0;
- chan->ringdebtimer = 0;
- chan->txdialbuf[0] = '\0';
- chan->digitmode = DIGIT_MODE_DTMF;
- chan->dialing = 0;
- chan->afterdialingtimer = 0;
- /* initialize IO MUX mask */
- chan->iomask = 0;
- /* save old conf number, if any */
- oldconf = chan->confna;
- /* initialize conference variables */
- chan->_confn = 0;
- chan->confna = 0;
- chan->confmode = 0;
- if ((chan->sig & __DAHDI_SIG_DACS) != __DAHDI_SIG_DACS)
- chan->dacs_chan = NULL;
- chan->confmute = 0;
- chan->gotgs = 0;
- reset_conf(chan);
- chan->dacs_chan = NULL;
- if (is_gain_allocated(chan))
- rxgain = chan->rxgain;
- chan->rxgain = defgain;
- chan->txgain = defgain;
- chan->eventinidx = chan->eventoutidx = 0;
- chan->flags &= ~(DAHDI_FLAG_LOOPED | DAHDI_FLAG_LINEAR | DAHDI_FLAG_PPP | DAHDI_FLAG_SIGFREEZE);
- dahdi_set_law(chan, DAHDI_LAW_DEFAULT);
- memset(chan->conflast, 0, sizeof(chan->conflast));
- memset(chan->conflast1, 0, sizeof(chan->conflast1));
- memset(chan->conflast2, 0, sizeof(chan->conflast2));
- if (chan->span && oldconf)
- dahdi_disable_dacs(chan);
- spin_unlock_irqrestore(&chan->lock, flags);
- if (ec_state) {
- ec_state->ops->echocan_free(chan, ec_state);
- release_echocan(ec_current);
- }
- /* release conference resource, if any to release */
- if (oldconf)
- dahdi_check_conf(oldconf);
- if (rxgain)
- kfree(rxgain);
- if (readchunkpreec) {
- dahdi_disable_hw_preechocan(chan);
- kfree(readchunkpreec);
- }
- #ifdef CONFIG_DAHDI_PPP
- if (ppp) {
- tasklet_kill(&chan->ppp_calls);
- skb_queue_purge(&chan->ppp_rq);
- ppp_unregister_channel(ppp);
- kfree(ppp);
- }
- #endif
- }
- static int dahdi_ioctl_freezone(unsigned long data)
- {
- struct dahdi_zone *z;
- struct dahdi_zone *found = NULL;
- int num;
- if (get_user(num, (int __user *) data))
- return -EFAULT;
- spin_lock(&zone_lock);
- list_for_each_entry(z, &tone_zones, node) {
- if (z->num == num) {
- found = z;
- break;
- }
- }
- if (found) {
- list_del(&found->node);
- }
- spin_unlock(&zone_lock);
- if (found) {
- if (debug) {
- module_printk(KERN_INFO,
- "Unregistering tone zone %d (%s)\n",
- found->num, found->name);
- }
- tone_zone_put(found);
- }
- return 0;
- }
- static int dahdi_register_tone_zone(struct dahdi_zone *zone)
- {
- struct dahdi_zone *cur;
- int res = 0;
- kref_init(&zone->refcount);
- spin_lock(&zone_lock);
- list_for_each_entry(cur, &tone_zones, node) {
- if (cur->num == zone->num) {
- res = -EINVAL;
- break;
- }
- }
- if (!res) {
- list_add_tail(&zone->node, &tone_zones);
- if (debug) {
- module_printk(KERN_INFO,
- "Registered tone zone %d (%s)\n",
- zone->num, zone->name);
- }
- }
- spin_unlock(&zone_lock);
- return res;
- }
- static int start_tone_digit(struct dahdi_chan *chan, int tone)
- {
- struct dahdi_tone *playtone = NULL;
- int base, max;
- if (!chan->curzone)
- return -ENODATA;
- switch (chan->digitmode) {
- case DIGIT_MODE_DTMF:
- /* Set dialing so that a dial operation doesn't interrupt this tone */
- chan->dialing = 1;
- base = DAHDI_TONE_DTMF_BASE;
- max = DAHDI_TONE_DTMF_MAX;
- break;
- case DIGIT_MODE_MFR2_FWD:
- base = DAHDI_TONE_MFR2_FWD_BASE;
- max = DAHDI_TONE_MFR2_FWD_MAX;
- break;
- case DIGIT_MODE_MFR2_REV:
- base = DAHDI_TONE_MFR2_REV_BASE;
- max = DAHDI_TONE_MFR2_REV_MAX;
- break;
- default:
- return -EINVAL;
- }
- if ((tone < base) || (tone > max))
- return -EINVAL;
- switch (chan->digitmode) {
- case DIGIT_MODE_DTMF:
- playtone = &chan->curzone->dtmf_continuous[tone - base];
- break;
- case DIGIT_MODE_MFR2_FWD:
- playtone = &chan->curzone->mfr2_fwd_continuous[tone - base];
- break;
- case DIGIT_MODE_MFR2_REV:
- playtone = &chan->curzone->mfr2_rev_continuous[tone - base];
- break;
- }
- if (!playtone || !playtone->tonesamples)
- return -ENOSYS;
- chan->curtone = playtone;
- return 0;
- }
- static int start_tone(struct dahdi_chan *chan, int tone)
- {
- int res = -EINVAL;
- /* Stop the current tone, no matter what */
- chan->tonep = 0;
- chan->curtone = NULL;
- chan->pdialcount = 0;
- chan->txdialbuf[0] = '\0';
- chan->dialing = 0;
- if (tone == -1) {
- /* Just stop the current tone */
- res = 0;
- } else if (!chan->curzone) {
- static int __warnonce = 1;
- if (__warnonce) {
- __warnonce = 0;
- /* The tonezones are loaded by dahdi_cfg based on /etc/dahdi/system.conf. */
- module_printk(KERN_WARNING, "DAHDI: Cannot start tones until tone zone is loaded.\n");
- }
- /* Note that no tone zone exists at the moment */
- res = -ENODATA;
- } else if ((tone >= 0 && tone <= DAHDI_TONE_MAX)) {
- /* Have a tone zone */
- if (chan->curzone->tones[tone]) {
- chan->curtone = chan->curzone->tones[tone];
- res = 0;
- } else { /* Indicate that zone is loaded but no such tone exists */
- res = -ENOSYS;
- }
- } else if (chan->digitmode == DIGIT_MODE_DTMF ||
- chan->digitmode == DIGIT_MODE_MFR2_FWD ||
- chan->digitmode == DIGIT_MODE_MFR2_REV) {
- res = start_tone_digit(chan, tone);
- } else {
- chan->dialing = 0;
- res = -EINVAL;
- }
- if (chan->curtone)
- dahdi_init_tone_state(&chan->ts, chan->curtone);
- return res;
- }
- /**
- * stop_tone - Stops any tones on a channel.
- *
- * Must be called with chan->lock held.
- *
- */
- static inline int stop_tone(struct dahdi_chan *chan)
- {
- return start_tone(chan, -1);
- }
- static int set_tone_zone(struct dahdi_chan *chan, int zone)
- {
- int res = 0;
- struct dahdi_zone *cur;
- struct dahdi_zone *z;
- unsigned long flags;
- z = NULL;
- spin_lock(&zone_lock);
- if ((DEFAULT_TONE_ZONE == zone) && !list_empty(&tone_zones)) {
- z = list_entry(tone_zones.next, struct dahdi_zone, node);
- tone_zone_get(z);
- } else {
- list_for_each_entry(cur, &tone_zones, node) {
- if (cur->num != (u8)zone)
- continue;
- z = cur;
- tone_zone_get(z);
- break;
- }
- }
- spin_unlock(&zone_lock);
- if (unlikely(!z))
- return -ENODATA;
- spin_lock_irqsave(&chan->lock, flags);
- stop_tone(chan);
- if (chan->curzone) {
- struct dahdi_zone *zone = chan->curzone;
- chan->curzone = NULL;
- tone_zone_put(zone);
- }
- chan->curzone = z;
- memcpy(chan->ringcadence, z->ringcadence, sizeof(chan->ringcadence));
- spin_unlock_irqrestore(&chan->lock, flags);
- return res;
- }
- static void dahdi_set_law(struct dahdi_chan *chan, int law)
- {
- if (DAHDI_LAW_DEFAULT == law) {
- if (chan->deflaw)
- law = chan->deflaw;
- else
- if (chan->span) law = chan->span->deflaw;
- else law = DAHDI_LAW_MULAW;
- }
- if (law == DAHDI_LAW_ALAW) {
- chan->xlaw = __dahdi_alaw;
- #ifdef CONFIG_CALC_XLAW
- chan->lineartoxlaw = __dahdi_lineartoalaw;
- #else
- chan->lin2x = __dahdi_lin2a;
- #endif
- } else {
- chan->xlaw = __dahdi_mulaw;
- #ifdef CONFIG_CALC_XLAW
- chan->lineartoxlaw = __dahdi_lineartoulaw;
- #else
- chan->lin2x = __dahdi_lin2mu;
- #endif
- }
- }
- /**
- * __dahdi_init_chan - Initialize the channel data structures.
- * @chan: The channel to initialize
- *
- */
- static void __dahdi_init_chan(struct dahdi_chan *chan)
- {
- might_sleep();
- spin_lock_init(&chan->lock);
- mutex_init(&chan->mutex);
- init_waitqueue_head(&chan->waitq);
- if (!chan->master)
- chan->master = chan;
- if (!chan->readchunk)
- chan->readchunk = chan->sreadchunk;
- if (!chan->writechunk)
- chan->writechunk = chan->swritechunk;
- chan->rxgain = NULL;
- chan->txgain = NULL;
- close_channel(chan);
- }
- /**
- * dahdi_chan_reg - Mark the channel registered.
- *
- * This must be called after close channel during registration, normally
- * covered by the call to __dahdi_init_chan, to avoid "HDLC hangage"
- */
- static inline void dahdi_chan_reg(struct dahdi_chan *chan)
- {
- set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
- }
- /**
- * dahdi_lboname() - Convert line build out number to string.
- *
- */
- const char *dahdi_lboname(int lbo)
- {
- /* names of tx level settings */
- static const char *const dahdi_txlevelnames[] = {
- "0 db (CSU)/0-133 feet (DSX-1)",
- "133-266 feet (DSX-1)",
- "266-399 feet (DSX-1)",
- "399-533 feet (DSX-1)",
- "533-655 feet (DSX-1)",
- "-7.5db (CSU)",
- "-15db (CSU)",
- "-22.5db (CSU)"
- };
- if ((lbo < 0) || (lbo > 7))
- return "Unknown";
- return dahdi_txlevelnames[lbo];
- }
- EXPORT_SYMBOL(dahdi_lboname);
- #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP)
- static inline void print_debug_writebuf(struct dahdi_chan* ss, struct sk_buff *skb, int oldbuf)
- {
- #ifdef CONFIG_DAHDI_DEBUG
- int x;
- module_printk(KERN_NOTICE, "Buffered %d bytes to go out in buffer %d\n", ss->writen[oldbuf], oldbuf);
- module_printk(KERN_DEBUG "");
- for (x=0;x<ss->writen[oldbuf];x++)
- printk("%02x ", ss->writebuf[oldbuf][x]);
- printk("\n");
- #endif
- }
- #endif
- #ifdef CONFIG_DAHDI_NET
- #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26)
- static inline struct net_device_stats *hdlc_stats(struct net_device *dev)
- {
- return &dev->stats;
- }
- #endif
- static int dahdi_net_open(struct net_device *dev)
- {
- int res = hdlc_open(dev);
- struct dahdi_chan *ms = netdev_to_chan(dev);
- /* if (!dev->hard_start_xmit) return res; is this really necessary? --byg */
- if (res) /* this is necessary to avoid kernel panic when UNSPEC link encap, proven --byg */
- return res;
- if (!ms) {
- module_printk(KERN_NOTICE, "dahdi_net_open: nothing??\n");
- return -EINVAL;
- }
- if (test_bit(DAHDI_FLAGBIT_OPEN, &ms->flags)) {
- module_printk(KERN_NOTICE, "%s is already open!\n", ms->name);
- return -EBUSY;
- }
- if (!dahdi_have_netdev(ms)) {
- module_printk(KERN_NOTICE, "%s is not a net device!\n", ms->name);
- return -EINVAL;
- }
- ms->txbufpolicy = DAHDI_POLICY_IMMEDIATE;
- res = dahdi_reallocbufs(ms, DAHDI_DEFAULT_MTU_MRU, DAHDI_DEFAULT_NUM_BUFS);
- if (res)
- return res;
- fasthdlc_init(&ms->rxhdlc, (ms->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- fasthdlc_init(&ms->txhdlc, (ms->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- ms->infcs = PPP_INITFCS;
- netif_start_queue(chan_to_netdev(ms));
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "DAHDINET: Opened channel %d name %s\n", ms->channo, ms->name);
- #endif
- return 0;
- }
- static int dahdi_register_hdlc_device(struct net_device *dev, const char *dev_name)
- {
- int result;
- if (dev_name && *dev_name) {
- if ((result = dev_alloc_name(dev, dev_name)) < 0)
- return result;
- }
- result = register_netdev(dev);
- if (result != 0)
- return -EIO;
- return 0;
- }
- static int dahdi_net_stop(struct net_device *dev)
- {
- hdlc_device *h = dev_to_hdlc(dev);
- struct dahdi_hdlc *hdlc = h->priv;
- struct dahdi_chan *ms = hdlc_to_chan(hdlc);
- if (!ms) {
- module_printk(KERN_NOTICE, "dahdi_net_stop: nothing??\n");
- return 0;
- }
- if (!dahdi_have_netdev(ms)) {
- module_printk(KERN_NOTICE, "dahdi_net_stop: %s is not a net device!\n", ms->name);
- return 0;
- }
- /* Not much to do here. Just deallocate the buffers */
- netif_stop_queue(chan_to_netdev(ms));
- dahdi_reallocbufs(ms, 0, 0);
- hdlc_close(dev);
- return 0;
- }
- /* kernel 2.4.20+ has introduced attach function, dunno what to do,
- just copy sources from dscc4 to be sure and ready for further mastering,
- NOOP right now (i.e. really a stub) --byg */
- static int dahdi_net_attach(struct net_device *dev, unsigned short encoding,
- unsigned short parity)
- {
- /* struct net_device *dev = hdlc_to_dev(hdlc);
- struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
- if (encoding != ENCODING_NRZ &&
- encoding != ENCODING_NRZI &&
- encoding != ENCODING_FM_MARK &&
- encoding != ENCODING_FM_SPACE &&
- encoding != ENCODING_MANCHESTER)
- return -EINVAL;
- if (parity != PARITY_NONE &&
- parity != PARITY_CRC16_PR0_CCITT &&
- parity != PARITY_CRC16_PR1_CCITT &&
- parity != PARITY_CRC32_PR0_CCITT &&
- parity != PARITY_CRC32_PR1_CCITT)
- return -EINVAL;
- dpriv->encoding = encoding;
- dpriv->parity = parity;*/
- return 0;
- }
- static struct dahdi_hdlc *dahdi_hdlc_alloc(void)
- {
- return kzalloc(sizeof(struct dahdi_hdlc), GFP_KERNEL);
- }
- static int dahdi_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- /* FIXME: this construction seems to be not very optimal for me but I
- * could find nothing better at the moment (Friday, 10PM :( ) --byg
- * */
- struct dahdi_chan *ss = netdev_to_chan(dev);
- struct net_device_stats *stats = hdlc_stats(dev);
- int retval = 1;
- int x,oldbuf;
- unsigned int fcs;
- unsigned char *data;
- unsigned long flags;
- /* See if we have any buffers */
- spin_lock_irqsave(&ss->lock, flags);
- if (skb->len > ss->blocksize - 2) {
- module_printk(KERN_ERR, "dahdi_xmit(%s): skb is too large (%d > %d)\n", dev->name, skb->len, ss->blocksize -2);
- stats->tx_dropped++;
- retval = 0;
- } else if (ss->inwritebuf >= 0) {
- /* We have a place to put this packet */
- /* XXX We should keep the SKB and avoid the memcpy XXX */
- data = ss->writebuf[ss->inwritebuf];
- memcpy(data, skb->data, skb->len);
- ss->writen[ss->inwritebuf] = skb->len;
- ss->writeidx[ss->inwritebuf] = 0;
- /* Calculate the FCS */
- fcs = PPP_INITFCS;
- for (x=0;x<skb->len;x++)
- fcs = PPP_FCS(fcs, data[x]);
- /* Invert it */
- fcs ^= 0xffff;
- /* Send it out LSB first */
- data[ss->writen[ss->inwritebuf]++] = (fcs & 0xff);
- data[ss->writen[ss->inwritebuf]++] = (fcs >> 8) & 0xff;
- /* Advance to next window */
- oldbuf = ss->inwritebuf;
- ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs;
- if (ss->inwritebuf == ss->outwritebuf) {
- /* Whoops, no more space. */
- ss->inwritebuf = -1;
- netif_stop_queue(chan_to_netdev(ss));
- }
- if (ss->outwritebuf < 0) {
- /* Let the interrupt handler know there's
- some space for us */
- ss->outwritebuf = oldbuf;
- }
- dev->trans_start = jiffies;
- stats->tx_packets++;
- stats->tx_bytes += ss->writen[oldbuf];
- print_debug_writebuf(ss, skb, oldbuf);
- retval = 0;
- /* Free the SKB */
- dev_kfree_skb_any(skb);
- }
- spin_unlock_irqrestore(&ss->lock, flags);
- return retval;
- }
- static int dahdi_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
- {
- return hdlc_ioctl(dev, ifr, cmd);
- }
- #endif
- #ifdef CONFIG_DAHDI_PPP
- static int dahdi_ppp_xmit(struct ppp_channel *ppp, struct sk_buff *skb)
- {
- /*
- * If we can't handle the packet right now, return 0. If we
- * we handle or drop it, return 1. Always free if we return
- * 1 and never if we return 0
- */
- struct dahdi_chan *ss = ppp->private;
- int x,oldbuf;
- unsigned int fcs;
- unsigned char *data;
- unsigned long flags;
- int retval = 0;
- /* See if we have any buffers */
- spin_lock_irqsave(&ss->lock, flags);
- if (!(test_bit(DAHDI_FLAGBIT_OPEN, &ss->flags))) {
- module_printk(KERN_ERR, "Can't transmit on closed channel\n");
- retval = 1;
- } else if (skb->len > ss->blocksize - 4) {
- module_printk(KERN_ERR, "dahdi_ppp_xmit(%s): skb is too large (%d > %d)\n", ss->name, skb->len, ss->blocksize -2);
- retval = 1;
- } else if (ss->inwritebuf >= 0) {
- /* We have a place to put this packet */
- /* XXX We should keep the SKB and avoid the memcpy XXX */
- data = ss->writebuf[ss->inwritebuf];
- /* Start with header of two bytes */
- /* Add "ALL STATIONS" and "UNNUMBERED" */
- data[0] = 0xff;
- data[1] = 0x03;
- ss->writen[ss->inwritebuf] = 2;
- /* Copy real data and increment amount written */
- memcpy(data + 2, skb->data, skb->len);
- ss->writen[ss->inwritebuf] += skb->len;
- /* Re-set index back to zero */
- ss->writeidx[ss->inwritebuf] = 0;
- /* Calculate the FCS */
- fcs = PPP_INITFCS;
- for (x=0;x<skb->len + 2;x++)
- fcs = PPP_FCS(fcs, data[x]);
- /* Invert it */
- fcs ^= 0xffff;
- /* Point past the real data now */
- data += (skb->len + 2);
- /* Send FCS out LSB first */
- data[0] = (fcs & 0xff);
- data[1] = (fcs >> 8) & 0xff;
- /* Account for FCS length */
- ss->writen[ss->inwritebuf]+=2;
- /* Advance to next window */
- oldbuf = ss->inwritebuf;
- ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs;
- if (ss->inwritebuf == ss->outwritebuf) {
- /* Whoops, no more space. */
- ss->inwritebuf = -1;
- }
- if (ss->outwritebuf < 0) {
- /* Let the interrupt handler know there's
- some space for us */
- ss->outwritebuf = oldbuf;
- }
- print_debug_writebuf(ss, skb, oldbuf);
- retval = 1;
- }
- spin_unlock_irqrestore(&ss->lock, flags);
- if (retval) {
- /* Get rid of the SKB if we're returning non-zero */
- /* N.B. this is called in process or BH context so
- dev_kfree_skb is OK. */
- dev_kfree_skb(skb);
- }
- return retval;
- }
- static int dahdi_ppp_ioctl(struct ppp_channel *ppp, unsigned int cmd, unsigned long flags)
- {
- return -EIO;
- }
- static struct ppp_channel_ops ztppp_ops =
- {
- .start_xmit = dahdi_ppp_xmit,
- .ioctl = dahdi_ppp_ioctl,
- };
- #endif
- /**
- * is_monitor_mode() - True if the confmode indicates that one channel is monitoring another.
- *
- */
- static bool is_monitor_mode(int confmode)
- {
- confmode &= DAHDI_CONF_MODE_MASK;
- if ((confmode == DAHDI_CONF_MONITOR) ||
- (confmode == DAHDI_CONF_MONITORTX) ||
- (confmode == DAHDI_CONF_MONITORBOTH) ||
- (confmode == DAHDI_CONF_MONITOR_RX_PREECHO) ||
- (confmode == DAHDI_CONF_MONITOR_TX_PREECHO) ||
- (confmode == DAHDI_CONF_MONITORBOTH_PREECHO)) {
- return true;
- } else {
- return false;
- }
- }
- static unsigned long _chan_cleanup(struct dahdi_chan *pos, unsigned long data)
- {
- unsigned long flags;
- struct dahdi_chan *const chan = (struct dahdi_chan *)data;
- /* Remove anyone pointing to us as master
- and make them their own thing */
- if (pos->master == chan)
- pos->master = pos;
- if (((pos->confna == chan->channo) &&
- is_monitor_mode(pos->confmode)) ||
- (pos->dacs_chan == chan) ||
- (pos->conf_chan == chan)) {
- /* Take them out of conference with us */
- /* release conference resource if any */
- if (pos->confna)
- dahdi_check_conf(pos->confna);
- dahdi_disable_dacs(pos);
- spin_lock_irqsave(&pos->lock, flags);
- pos->confna = 0;
- pos->_confn = 0;
- pos->confmode = 0;
- pos->conf_chan = NULL;
- pos->dacs_chan = NULL;
- spin_unlock_irqrestore(&pos->lock, flags);
- }
- return 0;
- }
- static const struct file_operations nodev_fops;
- static void dahdi_chan_unreg(struct dahdi_chan *chan)
- {
- unsigned long flags;
- might_sleep();
- /* In the case of surprise removal of hardware, make sure any open
- * file handles to this channel are disassociated with the actual
- * dahdi_chan. */
- if (chan->file) {
- module_printk(KERN_NOTICE,
- "%s: surprise removal: chan %d\n",
- __func__, chan->channo);
- chan->file->private_data = NULL;
- chan->file->f_op = &nodev_fops;
- /*
- * From now on, any file_operations for this device
- * would call the nodev_fops methods.
- */
- }
- mutex_lock(&chan->mutex);
- release_echocan(chan->ec_factory);
- chan->ec_factory = NULL;
- mutex_unlock(&chan->mutex);
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(chan)) {
- unregister_hdlc_device(chan->hdlcnetdev->netdev);
- free_netdev(chan->hdlcnetdev->netdev);
- kfree(chan->hdlcnetdev);
- chan->hdlcnetdev = NULL;
- }
- #endif
- clear_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
- #ifdef CONFIG_DAHDI_PPP
- if (chan->ppp) {
- module_printk(KERN_NOTICE, "HUH??? PPP still attached??\n");
- }
- #endif
- spin_lock_irqsave(&chan_lock, flags);
- __for_each_channel(_chan_cleanup, (unsigned long)chan);
- spin_unlock_irqrestore(&chan_lock, flags);
- chan->channo = -1;
- /* Let processeses out of their poll_wait() */
- wake_up_interruptible(&chan->waitq);
- /* release tone_zone */
- close_channel(chan);
- if (chan->file) {
- if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags)) {
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- if (chan->span) {
- if (chan->span->ops->close) {
- int res;
- res = chan->span->ops->close(chan);
- if (res)
- module_printk(KERN_NOTICE,
- "%s: close() failed: %d\n",
- __func__, res);
- }
- }
- }
- msleep(20);
- /*
- * FIXME: THE BIG SLEEP above, is hiding a terrible
- * race condition:
- * - the module_put() ahead, would allow the low-level driver
- * to free the channel.
- * - We should make sure no-one reference this channel
- * from now on.
- */
- if (chan->span)
- put_span(chan->span);
- }
- }
- static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- struct dahdi_chan *chan = file->private_data;
- int amnt;
- int res, rv;
- int oldbuf,x;
- unsigned long flags;
- /* Make sure count never exceeds 65k, and make sure it's unsigned */
- count &= 0xffff;
- if (unlikely(!chan)) {
- /*
- * This should never happen. Surprise device removal
- * should lead us to the nodev_* file_operations
- */
- msleep(5);
- module_printk(KERN_ERR, "%s: NODEV\n", __func__);
- return -ENODEV;
- }
- if (unlikely(count < 1))
- return -EINVAL;
- for (;;) {
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->eventinidx != chan->eventoutidx) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -ELAST /* - chan->eventbuf[chan->eventoutidx]*/;
- }
- res = chan->outreadbuf;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (res >= 0)
- break;
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
- /* Wake up when data is available or when the board driver
- * unregistered the channel. */
- rv = wait_event_interruptible(chan->waitq,
- (!chan->file->private_data || chan->outreadbuf > -1));
- if (rv)
- return rv;
- if (unlikely(!chan->file->private_data))
- return -ENODEV;
- }
- amnt = count;
- if (chan->flags & DAHDI_FLAG_LINEAR) {
- if (amnt > (chan->readn[res] << 1))
- amnt = chan->readn[res] << 1;
- if (amnt) {
- /* There seems to be a max stack size, so we have
- to do this in smaller pieces */
- short lindata[128];
- int left = amnt >> 1; /* amnt is in bytes */
- int pos = 0;
- int pass;
- while (left) {
- pass = left;
- if (pass > 128)
- pass = 128;
- for (x = 0; x < pass; x++)
- lindata[x] = DAHDI_XLAW(chan->readbuf[res][x + pos], chan);
- if (copy_to_user(usrbuf + (pos << 1), lindata, pass << 1))
- return -EFAULT;
- left -= pass;
- pos += pass;
- }
- }
- } else {
- if (amnt > chan->readn[res])
- amnt = chan->readn[res];
- if (amnt) {
- if (copy_to_user(usrbuf, chan->readbuf[res], amnt))
- return -EFAULT;
- }
- }
- spin_lock_irqsave(&chan->lock, flags);
- chan->readidx[res] = 0;
- chan->readn[res] = 0;
- oldbuf = res;
- chan->outreadbuf = (res + 1) % chan->numbufs;
- if (chan->outreadbuf == chan->inreadbuf) {
- /* Out of stuff */
- chan->outreadbuf = -1;
- }
- if (chan->inreadbuf < 0) {
- /* Notify interrupt handler that we have some space now */
- chan->inreadbuf = oldbuf;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- return amnt;
- }
- static int num_filled_bufs(struct dahdi_chan *chan)
- {
- int range1, range2;
- if (chan->inwritebuf < 0) {
- return chan->numbufs;
- }
- if (chan->outwritebuf < 0) {
- return 0;
- }
- if (chan->outwritebuf <= chan->inwritebuf) {
- return chan->inwritebuf - chan->outwritebuf;
- }
- /* This means (in > out) and we have wrap around */
- range1 = chan->numbufs - chan->outwritebuf;
- range2 = chan->inwritebuf;
- return range1 + range2;
- }
- static ssize_t dahdi_chan_write(struct file *file, const char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- unsigned long flags;
- struct dahdi_chan *chan = file->private_data;
- int res, amnt, oldbuf, rv, x;
- /* Make sure count never exceeds 65k, and make sure it's unsigned */
- count &= 0xffff;
- if (unlikely(!chan)) {
- /*
- * This should never happen. Surprise device removal
- * should lead us to the nodev_* file_operations
- */
- msleep(5);
- module_printk(KERN_ERR, "%s: NODEV\n", __func__);
- return -ENODEV;
- }
- if (unlikely(count < 1))
- return -EINVAL;
- for (;;) {
- spin_lock_irqsave(&chan->lock, flags);
- if ((chan->curtone || chan->pdialcount) && !is_pseudo_chan(chan)) {
- chan->curtone = NULL;
- chan->tonep = 0;
- chan->dialing = 0;
- chan->txdialbuf[0] = '\0';
- chan->pdialcount = 0;
- }
- if (chan->eventinidx != chan->eventoutidx) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -ELAST;
- }
- res = chan->inwritebuf;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (res >= 0)
- break;
- if (file->f_flags & O_NONBLOCK) {
- #ifdef BUFFER_DEBUG
- printk("Error: Nonblock\n");
- #endif
- return -EAGAIN;
- }
- /* Wake up when room in the write queue is available or when
- * the board driver unregistered the channel. */
- rv = wait_event_interruptible(chan->waitq,
- (!chan->file->private_data || chan->inwritebuf > -1));
- if (rv)
- return rv;
- if (unlikely(!chan->file->private_data))
- return -ENODEV;
- }
- amnt = count;
- if (chan->flags & DAHDI_FLAG_LINEAR) {
- if (amnt > (chan->blocksize << 1))
- amnt = chan->blocksize << 1;
- } else {
- if (amnt > chan->blocksize)
- amnt = chan->blocksize;
- }
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "dahdi_chan_write(chan: %d, res: %d, outwritebuf: %d amnt: %d\n",
- chan->channo, res, chan->outwritebuf, amnt);
- #endif
- if (amnt) {
- if (chan->flags & DAHDI_FLAG_LINEAR) {
- /* There seems to be a max stack size, so we have
- to do this in smaller pieces */
- short lindata[128];
- int left = amnt >> 1; /* amnt is in bytes */
- int pos = 0;
- int pass;
- while (left) {
- pass = left;
- if (pass > 128)
- pass = 128;
- if (copy_from_user(lindata, usrbuf + (pos << 1), pass << 1)) {
- return -EFAULT;
- }
- left -= pass;
- for (x = 0; x < pass; x++)
- chan->writebuf[res][x + pos] = DAHDI_LIN2X(lindata[x], chan);
- pos += pass;
- }
- chan->writen[res] = amnt >> 1;
- } else {
- if (copy_from_user(chan->writebuf[res], usrbuf, amnt)) {
- return -EFAULT;
- }
- chan->writen[res] = amnt;
- }
- #ifdef CONFIG_DAHDI_ECHOCAN_PROCESS_TX
- if ((chan->ec_state) &&
- (ECHO_MODE_ACTIVE == chan->ec_state->status.mode) &&
- (chan->ec_state->ops->echocan_process_tx)) {
- struct dahdi_echocan_state *const ec = chan->ec_state;
- for (x = 0; x < chan->writen[res]; ++x) {
- short tx;
- tx = DAHDI_XLAW(chan->writebuf[res][x], chan);
- ec->ops->echocan_process_tx(ec, &tx, 1);
- chan->writebuf[res][x] = DAHDI_LIN2X((int) tx,
- chan);
- }
- }
- #endif
- chan->writeidx[res] = 0;
- if (chan->flags & DAHDI_FLAG_FCS)
- calc_fcs(chan, res);
- oldbuf = res;
- spin_lock_irqsave(&chan->lock, flags);
- chan->inwritebuf = (res + 1) % chan->numbufs;
- if (chan->inwritebuf == chan->outwritebuf) {
- /* Don't stomp on the transmitter, just wait for them to
- wake us up */
- chan->inwritebuf = -1;
- /* Make sure the transmitter is transmitting in case of POLICY_WHEN_FULL */
- chan->txdisable = 0;
- }
- if (chan->outwritebuf < 0) {
- /* Okay, the interrupt handler has been waiting for us. Give them a buffer */
- chan->outwritebuf = oldbuf;
- }
- if ((chan->txbufpolicy == DAHDI_POLICY_HALF_FULL) && (chan->txdisable)) {
- if (num_filled_bufs(chan) >= (chan->numbufs >> 1)) {
- #ifdef BUFFER_DEBUG
- printk("Reached buffer fill mark of %d\n", num_filled_bufs(chan));
- #endif
- chan->txdisable = 0;
- }
- }
- #ifdef BUFFER_DEBUG
- if ((chan->statcount <= 0) || (amnt != 128) || (num_filled_bufs(chan) != chan->lastnumbufs)) {
- printk("amnt: %d Number of filled buffers: %d\n", amnt, num_filled_bufs(chan));
- chan->statcount = 32000;
- chan->lastnumbufs = num_filled_bufs(chan);
- }
- #endif
- spin_unlock_irqrestore(&chan->lock, flags);
- if (chan->flags & DAHDI_FLAG_NOSTDTXRX && chan->span->ops->hdlc_hard_xmit)
- chan->span->ops->hdlc_hard_xmit(chan);
- }
- return amnt;
- }
- static int dahdi_ctl_open(struct file *file)
- {
- /* Nothing to do, really */
- return 0;
- }
- static int dahdi_chan_open(struct file *file)
- {
- /* Nothing to do here for now either */
- return 0;
- }
- static int dahdi_ctl_release(struct file *file)
- {
- /* Nothing to do */
- return 0;
- }
- static int dahdi_chan_release(struct file *file)
- {
- /* Nothing to do for now */
- return 0;
- }
- static void set_txtone(struct dahdi_chan *ss, int fac, int init_v2, int init_v3)
- {
- if (fac == 0) {
- ss->v2_1 = 0;
- ss->v3_1 = 0;
- return;
- }
- ss->txtone = fac;
- ss->v1_1 = 0;
- ss->v2_1 = init_v2;
- ss->v3_1 = init_v3;
- return;
- }
- static void dahdi_rbs_sethook(struct dahdi_chan *chan, int txsig, int txstate,
- int timeout)
- {
- static const struct {
- unsigned int sig_type;
- /* Index is dahdi_txsig enum */
- unsigned int bits[DAHDI_TXSIG_TOTAL];
- } outs[NUM_SIGS] = {
- {
- /*
- * We set the idle case of the DAHDI_SIG_NONE to this pattern to make idle E1 CAS
- * channels happy. Should not matter with T1, since on an un-configured channel,
- * who cares what the sig bits are as long as they are stable
- */
- .sig_type = DAHDI_SIG_NONE,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_ACD,
- }, {
- .sig_type = DAHDI_SIG_EM,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD,
- }, {
- .sig_type = DAHDI_SIG_FXSLS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD,
- }, {
- .sig_type = DAHDI_SIG_FXSGS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD,
- #ifndef CONFIG_CAC_GROUNDSTART
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_AC,
- #endif
- }, {
- .sig_type = DAHDI_SIG_FXSKS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD,
- }, {
- .sig_type = DAHDI_SIG_FXOLS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD,
- }, {
- .sig_type = DAHDI_SIG_FXOGS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD,
- }, {
- .sig_type = DAHDI_SIG_FXOKS,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD,
- .bits[DAHDI_TXSIG_KEWL] = DAHDI_BITS_ABCD,
- }, {
- .sig_type = DAHDI_SIG_SF,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BCD,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD,
- .bits[DAHDI_TXSIG_KEWL] = DAHDI_BITS_BCD,
- }, {
- .sig_type = DAHDI_SIG_EM_E1,
- .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_DBIT,
- .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABD,
- .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABD,
- .bits[DAHDI_TXSIG_KEWL] = DAHDI_DBIT,
- }
- };
- int x;
- /* if no span, return doing nothing */
- if (!chan->span)
- return;
- if (!(chan->span->flags & DAHDI_FLAG_RBS)) {
- module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state on non-RBS channel %s\n", chan->name);
- return;
- }
- if ((txsig > 3) || (txsig < 0)) {
- module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state %d (> 3) on channel %s\n", txsig, chan->name);
- return;
- }
- if (!chan->span->ops->rbsbits && !chan->span->ops->hooksig) {
- module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state %d on channel %s while span %s lacks rbsbits or hooksig function\n",
- txsig, chan->name, chan->span->name);
- return;
- }
- /* Don't do anything for RBS */
- if (chan->sig == DAHDI_SIG_DACS_RBS)
- return;
- chan->txstate = txstate;
- /* if tone signalling */
- if (chan->sig == DAHDI_SIG_SF) {
- chan->txhooksig = txsig;
- if (chan->txtone) { /* if set to make tone for tx */
- if ((txsig && !(chan->toneflags & DAHDI_REVERSE_TXTONE)) ||
- ((!txsig) && (chan->toneflags & DAHDI_REVERSE_TXTONE))) {
- set_txtone(chan,chan->txtone,chan->tx_v2,chan->tx_v3);
- } else {
- set_txtone(chan,0,0,0);
- }
- }
- chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */
- return;
- }
- if (chan->span->ops->hooksig) {
- if (chan->txhooksig != txsig) {
- chan->txhooksig = txsig;
- chan->span->ops->hooksig(chan, txsig);
- }
- chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */
- return;
- } else {
- for (x = 0; x < NUM_SIGS; x++) {
- if (outs[x].sig_type == chan->sig) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Setting bits to %d for channel %s state %d in %d signalling\n", outs[x].bits[txsig], chan->name, txsig, chan->sig);
- #endif
- chan->txhooksig = txsig;
- chan->txsig = outs[x].bits[txsig];
- chan->span->ops->rbsbits(chan, chan->txsig);
- chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */
- return;
- }
- }
- }
- module_printk(KERN_NOTICE, "dahdi_rbs: Don't know RBS signalling type %d on channel %s\n", chan->sig, chan->name);
- }
- static int dahdi_cas_setbits(struct dahdi_chan *chan, int bits)
- {
- /* if no span, return as error */
- if (!chan->span)
- return -1;
- if (chan->span->ops->rbsbits) {
- chan->txsig = bits;
- chan->span->ops->rbsbits(chan, bits);
- } else {
- module_printk(KERN_NOTICE, "Huh? CAS setbits, but no RBS bits function\n");
- }
- return 0;
- }
- static int dahdi_hangup(struct dahdi_chan *chan)
- {
- int x, res = 0;
- /* Can't hangup pseudo channels */
- if (!chan->span)
- return 0;
- /* Can't hang up a clear channel */
- if (chan->flags & (DAHDI_FLAG_CLEAR | DAHDI_FLAG_NOSTDTXRX))
- return -EINVAL;
- chan->kewlonhook = 0;
- if ((chan->sig == DAHDI_SIG_FXSLS) || (chan->sig == DAHDI_SIG_FXSKS) ||
- (chan->sig == DAHDI_SIG_FXSGS)) {
- chan->ringdebtimer = RING_DEBOUNCE_TIME;
- }
- if (chan->span->flags & DAHDI_FLAG_RBS) {
- if (chan->sig == DAHDI_SIG_CAS) {
- dahdi_cas_setbits(chan, chan->idlebits);
- } else if ((chan->sig == DAHDI_SIG_FXOKS) && (chan->txstate != DAHDI_TXSTATE_ONHOOK)
- /* if other party is already on-hook we shouldn't do any battery drop */
- && !((chan->rxhooksig == DAHDI_RXSIG_ONHOOK) && (chan->itimer <= 0))) {
- /* Do RBS signalling on the channel's behalf */
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_KEWL, DAHDI_TXSTATE_KEWL, DAHDI_KEWLTIME);
- } else
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0);
- } else {
- /* Let the driver hang up the line if it wants to */
- if (chan->span->ops->sethook) {
- if (chan->txhooksig != DAHDI_ONHOOK) {
- chan->txhooksig = DAHDI_ONHOOK;
- res = chan->span->ops->sethook(chan, DAHDI_ONHOOK);
- } else
- res = 0;
- }
- }
- /* if not registered yet, just return here */
- if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags))
- return res;
- /* Mark all buffers as empty */
- for (x = 0; x < chan->numbufs; x++) {
- chan->writen[x] =
- chan->writeidx[x]=
- chan->readn[x]=
- chan->readidx[x] = 0;
- }
- if (chan->readbuf[0]) {
- chan->inreadbuf = 0;
- chan->inwritebuf = 0;
- } else {
- chan->inreadbuf = -1;
- chan->inwritebuf = -1;
- }
- chan->outreadbuf = -1;
- chan->outwritebuf = -1;
- chan->dialing = 0;
- chan->afterdialingtimer = 0;
- chan->curtone = NULL;
- chan->pdialcount = 0;
- chan->cadencepos = 0;
- chan->txdialbuf[0] = 0;
- return res;
- }
- static int initialize_channel(struct dahdi_chan *chan)
- {
- int res;
- unsigned long flags;
- const void *rxgain = NULL;
- struct dahdi_echocan_state *ec_state;
- const struct dahdi_echocan_factory *ec_current;
- if ((res = dahdi_reallocbufs(chan, DAHDI_DEFAULT_BLOCKSIZE, DAHDI_DEFAULT_NUM_BUFS)))
- return res;
- spin_lock_irqsave(&chan->lock, flags);
- chan->txbufpolicy = DAHDI_POLICY_IMMEDIATE;
- ec_state = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- chan->txdisable = 0;
- chan->digitmode = DIGIT_MODE_DTMF;
- chan->dialing = 0;
- chan->afterdialingtimer = 0;
- chan->cadencepos = 0;
- chan->firstcadencepos = 0; /* By default loop back to first cadence position */
- /* HDLC & FCS stuff */
- fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- chan->infcs = PPP_INITFCS;
- /* Timings for RBS */
- chan->prewinktime = DAHDI_DEFAULT_PREWINKTIME;
- chan->preflashtime = DAHDI_DEFAULT_PREFLASHTIME;
- chan->winktime = DAHDI_DEFAULT_WINKTIME;
- chan->flashtime = DAHDI_DEFAULT_FLASHTIME;
- if (chan->sig & __DAHDI_SIG_FXO)
- chan->starttime = DAHDI_DEFAULT_RINGTIME;
- else
- chan->starttime = DAHDI_DEFAULT_STARTTIME;
- chan->rxwinktime = DAHDI_DEFAULT_RXWINKTIME;
- chan->rxflashtime = DAHDI_DEFAULT_RXFLASHTIME;
- chan->debouncetime = DAHDI_DEFAULT_DEBOUNCETIME;
- chan->pulsemaketime = DAHDI_DEFAULT_PULSEMAKETIME;
- chan->pulsebreaktime = DAHDI_DEFAULT_PULSEBREAKTIME;
- chan->pulseaftertime = DAHDI_DEFAULT_PULSEAFTERTIME;
- /* Initialize RBS timers */
- chan->itimerset = chan->itimer = chan->otimer = 0;
- chan->ringdebtimer = 0;
- /* Reset conferences */
- reset_conf(chan);
- chan->dacs_chan = NULL;
- /* I/O Mask, etc */
- chan->iomask = 0;
- /* release conference resource if any */
- if (chan->confna)
- dahdi_check_conf(chan->confna);
- if ((chan->sig & __DAHDI_SIG_DACS) != __DAHDI_SIG_DACS) {
- chan->confna = 0;
- chan->confmode = 0;
- chan->conf_chan = NULL;
- dahdi_disable_dacs(chan);
- }
- chan->_confn = 0;
- memset(chan->conflast, 0, sizeof(chan->conflast));
- memset(chan->conflast1, 0, sizeof(chan->conflast1));
- memset(chan->conflast2, 0, sizeof(chan->conflast2));
- chan->confmute = 0;
- chan->gotgs = 0;
- chan->curtone = NULL;
- chan->tonep = 0;
- chan->pdialcount = 0;
- if (is_gain_allocated(chan))
- rxgain = chan->rxgain;
- chan->rxgain = defgain;
- chan->txgain = defgain;
- chan->eventinidx = chan->eventoutidx = 0;
- dahdi_set_law(chan, DAHDI_LAW_DEFAULT);
- dahdi_hangup(chan);
- /* Make sure that the audio flag is cleared on a clear channel */
- if ((chan->sig & DAHDI_SIG_CLEAR) || (chan->sig & DAHDI_SIG_HARDHDLC))
- chan->flags &= ~DAHDI_FLAG_AUDIO;
- if ((chan->sig == DAHDI_SIG_CLEAR) || (chan->sig == DAHDI_SIG_HARDHDLC))
- chan->flags &= ~(DAHDI_FLAG_PPP | DAHDI_FLAG_FCS | DAHDI_FLAG_HDLC);
- chan->flags &= ~DAHDI_FLAG_LINEAR;
- if (chan->curzone) {
- /* Take cadence from tone zone */
- memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence));
- } else {
- /* Do a default */
- memset(chan->ringcadence, 0, sizeof(chan->ringcadence));
- chan->ringcadence[0] = chan->starttime;
- chan->ringcadence[1] = DAHDI_RINGOFFTIME;
- }
- if (ec_state) {
- ec_state->ops->echocan_free(chan, ec_state);
- release_echocan(ec_current);
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- set_tone_zone(chan, DEFAULT_TONE_ZONE);
- if (rxgain)
- kfree(rxgain);
- return 0;
- }
- static const struct file_operations dahdi_timer_fops;
- static int dahdi_timer_open(struct file *file)
- {
- struct dahdi_timer *t = kzalloc(sizeof(*t), GFP_KERNEL);
- if (!t)
- return -ENOMEM;
- INIT_LIST_HEAD(&t->list);
- init_waitqueue_head(&t->sel);
- file->private_data = t;
- spin_lock_init(&t->lock);
- file->f_op = &dahdi_timer_fops;
- return 0;
- }
- static int dahdi_timer_release(struct inode *inode, struct file *file)
- {
- struct dahdi_timer *timer = file->private_data;
- unsigned long flags;
- if (!timer)
- return 0;
- spin_lock_irqsave(&timer->lock, flags);
- if (!list_empty(&timer->list)) {
- spin_unlock(&timer->lock);
- spin_lock(&dahdi_timer_lock);
- spin_lock(&timer->lock);
- list_del_init(&timer->list);
- spin_unlock(&dahdi_timer_lock);
- }
- file->private_data = NULL;
- spin_unlock_irqrestore(&timer->lock, flags);
- kfree(timer);
- return 0;
- }
- static const struct file_operations dahdi_chan_fops;
- static int dahdi_specchan_open(struct file *file)
- {
- int res = -ENXIO;
- struct dahdi_chan *const chan = chan_from_file(file);
- if (!chan || !chan->sig)
- return -ENXIO;
- /* Make sure we're not already open, a net device, or a slave
- * device */
- if (dahdi_have_netdev(chan))
- res = -EBUSY;
- else if (chan->master != chan)
- res = -EBUSY;
- else if ((chan->sig & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS)
- res = -EBUSY;
- else if (!test_and_set_bit(DAHDI_FLAGBIT_OPEN, &chan->flags)) {
- unsigned long flags;
- const struct dahdi_span_ops *const ops =
- (!is_pseudo_chan(chan)) ? chan->span->ops : NULL;
- if (ops && !try_module_get(ops->owner)) {
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- return -ENXIO;
- }
- res = initialize_channel(chan);
- if (res) {
- /* Reallocbufs must have failed */
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- return res;
- }
- spin_lock_irqsave(&chan->lock, flags);
- if (is_pseudo_chan(chan))
- chan->flags |= DAHDI_FLAG_AUDIO;
- chan->file = file;
- file->private_data = chan;
- /* Since we know we're a channel now, we can
- * update the f_op pointer and bypass a few of
- * the checks on the minor number. */
- file->f_op = &dahdi_chan_fops;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (ops && ops->open) {
- res = ops->open(chan);
- if (res) {
- spin_lock_irqsave(&chan->lock, flags);
- chan->file = NULL;
- file->private_data = NULL;
- spin_unlock_irqrestore(&chan->lock, flags);
- module_put(ops->owner);
- close_channel(chan);
- clear_bit(DAHDI_FLAGBIT_OPEN,
- &chan->flags);
- }
- }
- } else {
- res = -EBUSY;
- }
- return res;
- }
- static int dahdi_specchan_release(struct file *file)
- {
- int res=0;
- unsigned long flags;
- struct dahdi_chan *chan = chan_from_file(file);
- if (chan) {
- /* Chan lock protects contents against potentially non atomic accesses.
- * So if the pointer setting is not atomic, we should protect */
- #ifdef CONFIG_DAHDI_MIRROR
- if (chan->srcmirror) {
- struct dahdi_chan *const srcmirror = chan->srcmirror;
- spin_lock_irqsave(&srcmirror->lock, flags);
- if (chan == srcmirror->txmirror) {
- module_printk(KERN_INFO, "Chan %d tx mirror " \
- "to %d stopped\n",
- srcmirror->txmirror->channo,
- srcmirror->channo);
- srcmirror->txmirror = NULL;
- }
- if (chan == srcmirror->rxmirror) {
- module_printk(KERN_INFO, "Chan %d rx mirror " \
- "to %d stopped\n",
- srcmirror->rxmirror->channo,
- srcmirror->channo);
- chan->srcmirror->rxmirror = NULL;
- }
- spin_unlock_irqrestore(&chan->srcmirror->lock, flags);
- }
- #endif /* CONFIG_DAHDI_MIRROR */
- spin_lock_irqsave(&chan->lock, flags);
- chan->file = NULL;
- file->private_data = NULL;
- #ifdef CONFIG_DAHDI_MIRROR
- chan->srcmirror = NULL;
- #endif /* CONFIG_DAHDI_MIRROR */
- spin_unlock_irqrestore(&chan->lock, flags);
- close_channel(chan);
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- if (chan->span) {
- struct module *owner = chan->span->ops->owner;
- if (chan->span->ops->close)
- res = chan->span->ops->close(chan);
- module_put(owner);
- }
- } else
- res = -ENXIO;
- return res;
- }
- static int can_open_timer(void)
- {
- #ifdef CONFIG_DAHDI_CORE_TIMER
- return 1;
- #else
- return (list_empty(&span_list)) ? 0 : 1;
- #endif
- }
- static unsigned int max_pseudo_channels = 512;
- static unsigned int num_pseudo_channels;
- /**
- * dahdi_alloc_pseudo() - Returns a new pseudo channel.
- *
- * Call with the registration_mutex held since this function will determine a
- * channel number, and must be protected from additional registrations while
- * that is happening.
- *
- */
- static struct dahdi_chan *dahdi_alloc_pseudo(struct file *file)
- {
- struct pseudo_chan *pseudo;
- unsigned long flags;
- unsigned int channo;
- struct pseudo_chan *p;
- struct list_head *pos = &pseudo_chans;
- /* Don't allow /dev/dahdi/pseudo to open if there is not a timing
- * source. */
- if (!can_open_timer())
- return NULL;
- if (unlikely(num_pseudo_channels >= max_pseudo_channels))
- return NULL;
- pseudo = kzalloc(sizeof(*pseudo), GFP_KERNEL);
- if (NULL == pseudo)
- return NULL;
- pseudo->chan.sig = DAHDI_SIG_CLEAR;
- pseudo->chan.sigcap = DAHDI_SIG_CLEAR;
- pseudo->chan.flags = DAHDI_FLAG_AUDIO;
- pseudo->chan.span = NULL; /* No span == psuedo channel */
- channo = FIRST_PSEUDO_CHANNEL;
- list_for_each_entry(p, &pseudo_chans, node) {
- if (channo != p->chan.channo)
- break;
- pos = &p->node;
- ++channo;
- }
- pseudo->chan.channo = channo;
- pseudo->chan.chanpos = channo - FIRST_PSEUDO_CHANNEL + 1;
- __dahdi_init_chan(&pseudo->chan);
- dahdi_chan_reg(&pseudo->chan);
- snprintf(pseudo->chan.name, sizeof(pseudo->chan.name)-1,
- "Pseudo/%d", pseudo->chan.chanpos);
- file->private_data = &pseudo->chan;
- /* Once we place the pseudo chan on the list...it's registered and
- * live. */
- spin_lock_irqsave(&chan_lock, flags);
- ++num_pseudo_channels;
- list_add(&pseudo->node, pos);
- spin_unlock_irqrestore(&chan_lock, flags);
- return &pseudo->chan;
- }
- static void dahdi_free_pseudo(struct dahdi_chan *chan)
- {
- struct pseudo_chan *pseudo;
- unsigned long flags;
- if (!chan)
- return;
- mutex_lock(®istration_mutex);
- pseudo = chan_to_pseudo(chan);
- spin_lock_irqsave(&chan_lock, flags);
- list_del(&pseudo->node);
- --num_pseudo_channels;
- spin_unlock_irqrestore(&chan_lock, flags);
- dahdi_chan_unreg(chan);
- mutex_unlock(®istration_mutex);
- kfree(pseudo);
- }
- static int dahdi_open(struct inode *inode, struct file *file)
- {
- int unit = UNIT(file);
- struct dahdi_chan *chan;
- /* Minor 0: Special "control" descriptor */
- if (unit == DAHDI_CTL)
- return dahdi_ctl_open(file);
- if (unit == DAHDI_TRANSCODE) {
- if (!dahdi_transcode_fops) {
- if (request_module("dahdi_transcode")) {
- return -ENXIO;
- }
- }
- if (!try_module_get(dahdi_transcode_fops->owner)) {
- return -ENXIO;
- }
- if (dahdi_transcode_fops && dahdi_transcode_fops->open) {
- return dahdi_transcode_fops->open(inode, file);
- } else {
- /* dahdi_transcode module should have exported a
- * file_operations table. */
- WARN_ON(1);
- }
- return -ENXIO;
- }
- if (unit == DAHDI_TIMER) {
- if (can_open_timer()) {
- return dahdi_timer_open(file);
- } else {
- return -ENXIO;
- }
- }
- if (unit == DAHDI_CHANNEL)
- return dahdi_chan_open(file);
- if (unit == DAHDI_PSEUDO) {
- mutex_lock(®istration_mutex);
- chan = dahdi_alloc_pseudo(file);
- mutex_unlock(®istration_mutex);
- if (unlikely(!chan))
- return -ENOMEM;
- return dahdi_specchan_open(file);
- }
- return dahdi_specchan_open(file);
- }
- /**
- * dahdi_ioctl_defaultzone() - Set defzone to the default.
- * @defzone: The number of the default zone.
- *
- * The default zone is the zone that will be used if the channels request the
- * default zone in dahdi_ioctl_chanconfig. The first entry on the tone_zones
- * list is the default zone. This function searches the list for the zone,
- * and if found, moves it to the head of the list.
- */
- static int dahdi_ioctl_defaultzone(unsigned long data)
- {
- int defzone;
- struct dahdi_zone *cur;
- struct dahdi_zone *dz = NULL;
- if (get_user(defzone, (int __user *)data))
- return -EFAULT;
- spin_lock(&zone_lock);
- list_for_each_entry(cur, &tone_zones, node) {
- if (cur->num != defzone)
- continue;
- dz = cur;
- break;
- }
- if (dz)
- list_move(&dz->node, &tone_zones);
- spin_unlock(&zone_lock);
- return (dz) ? 0 : -EINVAL;
- }
- /* No bigger than 32k for everything per tone zone */
- #define MAX_SIZE 32768
- /* No more than 128 subtones */
- #define MAX_TONES 128
- /* The tones to be loaded can (will) be a mix of regular tones,
- DTMF tones and MF tones. We need to load DTMF and MF tones
- a bit differently than regular tones because their storage
- format is much simpler (an array structure field of the zone
- structure, rather an array of pointers).
- */
- static int dahdi_ioctl_loadzone(unsigned long data)
- {
- struct load_zone_workarea {
- struct dahdi_tone *samples[MAX_TONES];
- short next[MAX_TONES];
- struct dahdi_tone_def_header th;
- struct dahdi_tone_def td;
- } *work;
- size_t space;
- size_t size;
- int res;
- int x;
- void *ptr;
- struct dahdi_zone *z = NULL;
- struct dahdi_tone *t = NULL;
- void __user * user_data = (void __user *)data;
- const unsigned char MAX_ZONE = -1;
- work = kzalloc(sizeof(*work), GFP_KERNEL);
- if (!work)
- return -ENOMEM;
- if (copy_from_user(&work->th, user_data, sizeof(work->th))) {
- res = -EFAULT;
- goto error_exit;
- }
- if ((work->th.zone < 0) || (work->th.zone > MAX_ZONE)) {
- res = -EINVAL;
- goto error_exit;
- }
- user_data += sizeof(work->th);
- if ((work->th.count < 0) || (work->th.count > MAX_TONES)) {
- module_printk(KERN_NOTICE, "Too many tones included\n");
- res = -EINVAL;
- goto error_exit;
- }
- space = size = sizeof(*z) + work->th.count * sizeof(*t);
- if (size > MAX_SIZE) {
- res = -E2BIG;
- goto error_exit;
- }
- z = ptr = kzalloc(size, GFP_KERNEL);
- if (!z) {
- res = -ENOMEM;
- goto error_exit;
- }
- ptr = (char *) ptr + sizeof(*z);
- space -= sizeof(*z);
- z->name = kasprintf(GFP_KERNEL, work->th.name);
- if (!z->name) {
- res = -ENOMEM;
- goto error_exit;
- }
- for (x = 0; x < DAHDI_MAX_CADENCE; x++)
- z->ringcadence[x] = work->th.ringcadence[x];
- mutex_lock(&global_dialparamslock);
- for (x = 0; x < work->th.count; x++) {
- enum {
- REGULAR_TONE,
- DTMF_TONE,
- MFR1_TONE,
- MFR2_FWD_TONE,
- MFR2_REV_TONE,
- } tone_type;
- if (space < sizeof(*t)) {
- module_printk(KERN_NOTICE, "Insufficient tone zone space\n");
- res = -EINVAL;
- goto unlock_error_exit;
- }
- res = copy_from_user(&work->td, user_data,
- sizeof(work->td));
- if (res) {
- res = -EFAULT;
- goto unlock_error_exit;
- }
- user_data += sizeof(work->td);
- if ((work->td.tone >= 0) && (work->td.tone < DAHDI_TONE_MAX)) {
- tone_type = REGULAR_TONE;
- t = work->samples[x] = ptr;
- space -= sizeof(*t);
- ptr = (char *) ptr + sizeof(*t);
- /* Remember which sample is work->next */
- work->next[x] = work->td.next;
- /* Make sure the "next" one is sane */
- if ((work->next[x] >= work->th.count) ||
- (work->next[x] < 0)) {
- module_printk(KERN_NOTICE,
- "Invalid 'next' pointer: %d\n",
- work->next[x]);
- res = -EINVAL;
- goto unlock_error_exit;
- }
- } else if ((work->td.tone >= DAHDI_TONE_DTMF_BASE) &&
- (work->td.tone <= DAHDI_TONE_DTMF_MAX)) {
- tone_type = DTMF_TONE;
- work->td.tone -= DAHDI_TONE_DTMF_BASE;
- t = &z->dtmf[work->td.tone];
- } else if ((work->td.tone >= DAHDI_TONE_MFR1_BASE) &&
- (work->td.tone <= DAHDI_TONE_MFR1_MAX)) {
- tone_type = MFR1_TONE;
- work->td.tone -= DAHDI_TONE_MFR1_BASE;
- t = &z->mfr1[work->td.tone];
- } else if ((work->td.tone >= DAHDI_TONE_MFR2_FWD_BASE) &&
- (work->td.tone <= DAHDI_TONE_MFR2_FWD_MAX)) {
- tone_type = MFR2_FWD_TONE;
- work->td.tone -= DAHDI_TONE_MFR2_FWD_BASE;
- t = &z->mfr2_fwd[work->td.tone];
- } else if ((work->td.tone >= DAHDI_TONE_MFR2_REV_BASE) &&
- (work->td.tone <= DAHDI_TONE_MFR2_REV_MAX)) {
- tone_type = MFR2_REV_TONE;
- work->td.tone -= DAHDI_TONE_MFR2_REV_BASE;
- t = &z->mfr2_rev[work->td.tone];
- } else {
- module_printk(KERN_NOTICE,
- "Invalid tone (%d) defined\n",
- work->td.tone);
- res = -EINVAL;
- goto unlock_error_exit;
- }
- t->fac1 = work->td.fac1;
- t->init_v2_1 = work->td.init_v2_1;
- t->init_v3_1 = work->td.init_v3_1;
- t->fac2 = work->td.fac2;
- t->init_v2_2 = work->td.init_v2_2;
- t->init_v3_2 = work->td.init_v3_2;
- t->modulate = work->td.modulate;
- switch (tone_type) {
- case REGULAR_TONE:
- t->tonesamples = work->td.samples;
- if (!z->tones[work->td.tone])
- z->tones[work->td.tone] = t;
- break;
- case DTMF_TONE:
- t->tonesamples = global_dialparams.dtmf_tonelen;
- t->next = &dtmf_silence;
- z->dtmf_continuous[work->td.tone] = *t;
- z->dtmf_continuous[work->td.tone].next =
- &z->dtmf_continuous[work->td.tone];
- break;
- case MFR1_TONE:
- switch (work->td.tone + DAHDI_TONE_MFR1_BASE) {
- case DAHDI_TONE_MFR1_KP:
- case DAHDI_TONE_MFR1_ST:
- case DAHDI_TONE_MFR1_STP:
- case DAHDI_TONE_MFR1_ST2P:
- case DAHDI_TONE_MFR1_ST3P:
- /* signaling control tones are always 100ms */
- t->tonesamples = 100 * DAHDI_CHUNKSIZE;
- break;
- default:
- t->tonesamples = global_dialparams.mfv1_tonelen;
- break;
- }
- t->next = &mfr1_silence;
- break;
- case MFR2_FWD_TONE:
- t->tonesamples = global_dialparams.mfr2_tonelen;
- t->next = &dtmf_silence;
- z->mfr2_fwd_continuous[work->td.tone] = *t;
- z->mfr2_fwd_continuous[work->td.tone].next =
- &z->mfr2_fwd_continuous[work->td.tone];
- break;
- case MFR2_REV_TONE:
- t->tonesamples = global_dialparams.mfr2_tonelen;
- t->next = &dtmf_silence;
- z->mfr2_rev_continuous[work->td.tone] = *t;
- z->mfr2_rev_continuous[work->td.tone].next =
- &z->mfr2_rev_continuous[work->td.tone];
- break;
- }
- }
- mutex_unlock(&global_dialparamslock);
- for (x = 0; x < work->th.count; x++) {
- if (work->samples[x])
- work->samples[x]->next = work->samples[work->next[x]];
- }
- z->num = work->th.zone;
- /* After we call dahdi_register_tone_zone, the only safe way to free
- * the zone is with a tone_zone_put call. */
- res = dahdi_register_tone_zone(z);
- if (res)
- tone_zone_put(z);
- kfree(work);
- return res;
- unlock_error_exit:
- mutex_unlock(&global_dialparamslock);
- error_exit:
- if (z)
- kfree(z->name);
- kfree(z);
- kfree(work);
- return res;
- }
- void dahdi_init_tone_state(struct dahdi_tone_state *ts, struct dahdi_tone *zt)
- {
- ts->v1_1 = 0;
- ts->v2_1 = zt->init_v2_1;
- ts->v3_1 = zt->init_v3_1;
- ts->v1_2 = 0;
- ts->v2_2 = zt->init_v2_2;
- ts->v3_2 = zt->init_v3_2;
- ts->modulate = zt->modulate;
- }
- struct dahdi_tone *dahdi_mf_tone(const struct dahdi_chan *chan, char digit, int digitmode)
- {
- unsigned int tone_index;
- if (!chan->curzone) {
- static int __warnonce = 1;
- if (__warnonce) {
- __warnonce = 0;
- /* The tonezones are loaded by dahdi_cfg based on /etc/dahdi/system.conf. */
- module_printk(KERN_WARNING, "Cannot get dtmf tone until tone zone is loaded.\n");
- }
- return NULL;
- }
- switch (digitmode) {
- case DIGIT_MODE_PULSE:
- /* We should only get here with a pulse digit if we need
- * to "dial" 'W' (wait 0.5 second)
- */
- if (digit == 'W')
- return &tone_pause;
- return NULL;
- case DIGIT_MODE_DTMF:
- switch (digit) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- tone_index = DAHDI_TONE_DTMF_0 + (digit - '0');
- break;
- case '*':
- tone_index = DAHDI_TONE_DTMF_s;
- break;
- case '#':
- tone_index = DAHDI_TONE_DTMF_p;
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- tone_index = DAHDI_TONE_DTMF_A + (digit - 'A');
- break;
- case 'W':
- return &tone_pause;
- default:
- return NULL;
- }
- return &chan->curzone->dtmf[tone_index - DAHDI_TONE_DTMF_BASE];
- case DIGIT_MODE_MFR1:
- switch (digit) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- tone_index = DAHDI_TONE_MFR1_0 + (digit - '0');
- break;
- case '*':
- tone_index = DAHDI_TONE_MFR1_KP;
- break;
- case '#':
- tone_index = DAHDI_TONE_MFR1_ST;
- break;
- case 'A':
- tone_index = DAHDI_TONE_MFR1_STP;
- break;
- case 'B':
- tone_index = DAHDI_TONE_MFR1_ST2P;
- break;
- case 'C':
- tone_index = DAHDI_TONE_MFR1_ST3P;
- break;
- case 'W':
- return &tone_pause;
- default:
- return NULL;
- }
- return &chan->curzone->mfr1[tone_index - DAHDI_TONE_MFR1_BASE];
- case DIGIT_MODE_MFR2_FWD:
- switch (digit) {
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- tone_index = DAHDI_TONE_MFR2_FWD_1 + (digit - '1');
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- tone_index = DAHDI_TONE_MFR2_FWD_10 + (digit - 'A');
- break;
- case 'W':
- return &tone_pause;
- default:
- return NULL;
- }
- return &chan->curzone->mfr2_fwd[tone_index - DAHDI_TONE_MFR2_FWD_BASE];
- case DIGIT_MODE_MFR2_REV:
- switch (digit) {
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- tone_index = DAHDI_TONE_MFR2_REV_1 + (digit - '1');
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- tone_index = DAHDI_TONE_MFR2_REV_10 + (digit - 'A');
- break;
- case 'W':
- return &tone_pause;
- default:
- return NULL;
- }
- return &chan->curzone->mfr2_rev[tone_index - DAHDI_TONE_MFR2_REV_BASE];
- default:
- return NULL;
- }
- }
- static void __do_dtmf(struct dahdi_chan *chan)
- {
- char c;
- /* Called with chan->lock held */
- while ((c = chan->txdialbuf[0])) {
- memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1);
- switch (c) {
- case 'T':
- chan->digitmode = DIGIT_MODE_DTMF;
- chan->tonep = 0;
- break;
- case 'M':
- chan->digitmode = DIGIT_MODE_MFR1;
- chan->tonep = 0;
- break;
- case 'O':
- chan->digitmode = DIGIT_MODE_MFR2_FWD;
- chan->tonep = 0;
- break;
- case 'R':
- chan->digitmode = DIGIT_MODE_MFR2_REV;
- chan->tonep = 0;
- break;
- case 'P':
- chan->digitmode = DIGIT_MODE_PULSE;
- chan->tonep = 0;
- break;
- default:
- if ((c != 'W') && (chan->digitmode == DIGIT_MODE_PULSE)) {
- if ((c >= '0') && (c <= '9') && (chan->txhooksig == DAHDI_TXSIG_OFFHOOK)) {
- chan->pdialcount = (c == '0') ? 10 : c - '0';
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_PULSEBREAK,
- chan->pulsebreaktime);
- return;
- }
- } else {
- chan->curtone = dahdi_mf_tone(chan, c, chan->digitmode);
- chan->tonep = 0;
- if (chan->curtone) {
- dahdi_init_tone_state(&chan->ts, chan->curtone);
- return;
- }
- }
- }
- }
- /* Notify userspace process if there is nothing left */
- chan->dialing = 0;
- __qevent(chan, DAHDI_EVENT_DIALCOMPLETE);
- }
- static int dahdi_release(struct inode *inode, struct file *file)
- {
- int unit = UNIT(file);
- int res;
- struct dahdi_chan *chan;
- if (unit == DAHDI_CTL)
- return dahdi_ctl_release(file);
- if (unit == DAHDI_TIMER) {
- return dahdi_timer_release(inode, file);
- }
- if (unit == DAHDI_TRANSCODE) {
- /* We should not be here because the dahdi_transcode.ko module
- * should have updated the file_operations for this file
- * handle when the file was opened. */
- WARN_ON(1);
- return -EFAULT;
- }
- if (unit == DAHDI_CHANNEL) {
- chan = file->private_data;
- if (!chan)
- return dahdi_chan_release(file);
- else
- return dahdi_specchan_release(file);
- }
- if (unit == DAHDI_PSEUDO) {
- chan = file->private_data;
- if (chan) {
- res = dahdi_specchan_release(file);
- dahdi_free_pseudo(chan);
- } else {
- module_printk(KERN_NOTICE, "Pseudo release and no private data??\n");
- res = 0;
- }
- return res;
- }
- return dahdi_specchan_release(file);
- }
- /**
- * dahdi_alarm_channel() - notify userspace channel is (not) in alarm
- * @chan: the DAHDI channel
- * @alarms: alarm bits set
- *
- * Notify userspace about a change in alarm status of this channel.
- *
- * Note that channel drivers should only use this function directly if
- * they have a single port per channel. Whole-span alarms should be sent
- * using dahdi_alarm_notify() .
- *
- * Does nothing if alarms on the channel have not changed. If they have,
- * triggers sending either DAHDI_EVENT_NOALARM (if they were cleared) or
- * DAHDI_EVENT_ALARM (otherwise).
- *
- * Currently it is only used by drivers of FXO ports to notify (with a
- * red alarm) they have no battery current.
- */
- void dahdi_alarm_channel(struct dahdi_chan *chan, int alarms)
- {
- unsigned long flags;
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->chan_alarms != alarms) {
- chan->chan_alarms = alarms;
- dahdi_qevent_nolock(chan, alarms ? DAHDI_EVENT_ALARM : DAHDI_EVENT_NOALARM);
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- struct dahdi_span *get_master_span(void)
- {
- return master_span;
- }
- void set_master_span(int spanno)
- {
- struct dahdi_span *s;
- unsigned long flags;
- struct dahdi_span *old_master;
- spin_lock_irqsave(&chan_lock, flags);
- old_master = master_span;
- list_for_each_entry(s, &span_list, spans_node) {
- if (spanno == s->spanno) {
- master_span = s;
- break;
- }
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if ((debug & DEBUG_MAIN) && (old_master != master_span))
- module_printk(KERN_NOTICE, "Master span is set to %d (%s)\n",
- master_span->spanno, master_span->name);
- }
- static void __dahdi_find_master_span(void)
- {
- struct dahdi_span *s;
- unsigned long flags;
- struct dahdi_span *old_master;
- spin_lock_irqsave(&chan_lock, flags);
- old_master = master_span;
- list_for_each_entry(s, &span_list, spans_node) {
- if (s->alarms && old_master)
- continue;
- if (dahdi_is_digital_span(s) &&
- !test_bit(DAHDI_FLAGBIT_RUNNING, &s->flags) &&
- old_master)
- continue;
- if (!can_provide_timing(s))
- continue;
- if (master_span == s)
- continue;
- master_span = s;
- break;
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if ((debug & DEBUG_MAIN) && (old_master != master_span))
- module_printk(KERN_NOTICE, "Master changed to %s\n", s->name);
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
- static void _dahdi_find_master_span(void *work)
- {
- __dahdi_find_master_span();
- }
- static DECLARE_WORK(find_master_work, _dahdi_find_master_span, NULL);
- #else
- static void _dahdi_find_master_span(struct work_struct *work)
- {
- __dahdi_find_master_span();
- }
- static DECLARE_WORK(find_master_work, _dahdi_find_master_span);
- #endif
- static void dahdi_find_master_span(void)
- {
- schedule_work(&find_master_work);
- }
- void dahdi_alarm_notify(struct dahdi_span *span)
- {
- int x;
- span->alarms &= ~DAHDI_ALARM_LOOPBACK;
- /* Determine maint status */
- if (span->maintstat || span->mainttimer)
- span->alarms |= DAHDI_ALARM_LOOPBACK;
- /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON.
- The expression (a != b) does *NOT* do the same thing
- as ((!a) != (!b)) */
- /* if change in general state */
- if ((!span->alarms) != (!span->lastalarms)) {
- span->lastalarms = span->alarms;
- for (x = 0; x < span->channels; x++)
- dahdi_alarm_channel(span->chans[x], span->alarms);
- /* If we're going into or out of alarm we should try to find a
- * new master_span that may be a better fit. */
- dahdi_find_master_span();
- /* Report more detailed alarms */
- if (debug & DEBUG_MAIN) {
- if (span->alarms & DAHDI_ALARM_LOS) {
- module_printk(KERN_NOTICE,
- "Span %d: Loss of signal\n",
- span->spanno);
- }
- if (span->alarms & DAHDI_ALARM_LFA) {
- module_printk(KERN_NOTICE,
- "Span %d: Loss of Frame Alignment\n",
- span->spanno);
- }
- if (span->alarms & DAHDI_ALARM_LMFA) {
- module_printk(KERN_NOTICE,
- "Span %d: Loss of Multi-Frame "\
- "Alignment\n", span->spanno);
- }
- }
- }
- }
- static long
- dahdi_timer_unlocked_ioctl(struct file *file, unsigned int cmd,
- unsigned long data)
- {
- int j;
- unsigned long flags;
- struct dahdi_timer *const timer = file->private_data;
- switch(cmd) {
- case DAHDI_TIMERCONFIG:
- get_user(j, (int __user *)data);
- if (j < 0)
- j = 0;
- spin_lock_irqsave(&timer->lock, flags);
- if (timer->ms != j) {
- if (j && list_empty(&timer->list)) {
- /* The timer is being activated so add to the
- * global timer list. */
- spin_unlock(&timer->lock);
- spin_lock(&dahdi_timer_lock);
- spin_lock(&timer->lock);
- timer->ms = timer->pos = j;
- list_add(&timer->list, &dahdi_timers);
- spin_unlock(&dahdi_timer_lock);
- } else if (!j && !list_empty(&timer->list)) {
- /* The timer is being disabled so we can remove
- * from the global timer list. */
- spin_unlock(&timer->lock);
- spin_lock(&dahdi_timer_lock);
- spin_lock(&timer->lock);
- list_del_init(&timer->list);
- timer->ms = timer->pos = j;
- spin_unlock(&dahdi_timer_lock);
- } else {
- timer->ms = timer->pos = j;
- }
- }
- spin_unlock_irqrestore(&timer->lock, flags);
- break;
- case DAHDI_TIMERACK:
- get_user(j, (int __user *)data);
- spin_lock_irqsave(&timer->lock, flags);
- if ((j < 1) || (j > timer->tripped))
- j = timer->tripped;
- timer->tripped -= j;
- spin_unlock_irqrestore(&timer->lock, flags);
- break;
- case DAHDI_GETEVENT: /* Get event on queue */
- j = DAHDI_EVENT_NONE;
- spin_lock_irqsave(&timer->lock, flags);
- /* set up for no event */
- if (timer->tripped)
- j = DAHDI_EVENT_TIMER_EXPIRED;
- if (timer->ping)
- j = DAHDI_EVENT_TIMER_PING;
- spin_unlock_irqrestore(&timer->lock, flags);
- put_user(j, (int __user *)data);
- break;
- case DAHDI_TIMERPING:
- spin_lock_irqsave(&timer->lock, flags);
- timer->ping = 1;
- wake_up_interruptible(&timer->sel);
- spin_unlock_irqrestore(&timer->lock, flags);
- break;
- case DAHDI_TIMERPONG:
- spin_lock_irqsave(&timer->lock, flags);
- timer->ping = 0;
- spin_unlock_irqrestore(&timer->lock, flags);
- break;
- default:
- return -ENOTTY;
- }
- return 0;
- }
- #ifndef HAVE_UNLOCKED_IOCTL
- static int dahdi_timer_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long data)
- {
- return dahdi_timer_unlocked_ioctl(file, cmd, data);
- }
- #endif
- static int dahdi_ioctl_getgains(struct file *file, unsigned long data)
- {
- int res = 0;
- struct dahdi_gains *gain;
- int j;
- void __user * const user_data = (void __user *)data;
- struct dahdi_chan *chan;
- gain = kzalloc(sizeof(*gain), GFP_KERNEL);
- if (!gain)
- return -ENOMEM;
- if (copy_from_user(gain, user_data, sizeof(*gain))) {
- res = -EFAULT;
- goto cleanup;
- }
- chan = (!gain->chan) ? chan_from_file(file) :
- chan_from_num(gain->chan);
- if (!chan) {
- res = -EINVAL;
- goto cleanup;
- }
- if (!(chan->flags & DAHDI_FLAG_AUDIO)) {
- res = -EINVAL;
- goto cleanup;
- }
- gain->chan = chan->channo;
- for (j = 0; j < 256; ++j) {
- gain->txgain[j] = chan->txgain[j];
- gain->rxgain[j] = chan->rxgain[j];
- }
- if (copy_to_user(user_data, gain, sizeof(*gain))) {
- res = -EFAULT;
- goto cleanup;
- }
- cleanup:
- kfree(gain);
- return res;
- }
- static int dahdi_ioctl_setgains(struct file *file, unsigned long data)
- {
- int res = 0;
- struct dahdi_gains *gain;
- unsigned char *txgain, *rxgain;
- int j;
- unsigned long flags;
- const int GAIN_TABLE_SIZE = sizeof(defgain);
- void __user * const user_data = (void __user *)data;
- struct dahdi_chan *chan;
- gain = kzalloc(sizeof(*gain), GFP_KERNEL);
- if (!gain)
- return -ENOMEM;
- if (copy_from_user(gain, user_data, sizeof(*gain))) {
- res = -EFAULT;
- goto cleanup;
- }
- chan = (!gain->chan) ? chan_from_file(file) :
- chan_from_num(gain->chan);
- if (!chan) {
- res = -EINVAL;
- goto cleanup;
- }
- if (!(chan->flags & DAHDI_FLAG_AUDIO)) {
- res = -EINVAL;
- goto cleanup;
- }
- rxgain = kzalloc(GAIN_TABLE_SIZE*2, GFP_KERNEL);
- if (!rxgain) {
- res = -ENOMEM;
- goto cleanup;
- }
- gain->chan = chan->channo;
- txgain = rxgain + GAIN_TABLE_SIZE;
- for (j = 0; j < GAIN_TABLE_SIZE; ++j) {
- rxgain[j] = gain->rxgain[j];
- txgain[j] = gain->txgain[j];
- }
- if (!memcmp(rxgain, defgain, GAIN_TABLE_SIZE) &&
- !memcmp(txgain, defgain, GAIN_TABLE_SIZE)) {
- kfree(rxgain);
- spin_lock_irqsave(&chan->lock, flags);
- if (is_gain_allocated(chan))
- kfree(chan->rxgain);
- chan->rxgain = defgain;
- chan->txgain = defgain;
- spin_unlock_irqrestore(&chan->lock, flags);
- } else {
- /* This is a custom gain setting */
- spin_lock_irqsave(&chan->lock, flags);
- if (is_gain_allocated(chan))
- kfree(chan->rxgain);
- chan->rxgain = rxgain;
- chan->txgain = txgain;
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- if (copy_to_user(user_data, gain, sizeof(*gain))) {
- res = -EFAULT;
- goto cleanup;
- }
- cleanup:
- kfree(gain);
- return res;
- }
- static int dahdi_ioctl_chandiag(struct file *file, unsigned long data)
- {
- unsigned long flags;
- int channo;
- /* there really is no need to initialize this structure because when it is used it has
- * already been completely overwritten, but apparently the compiler cannot figure that
- * out and warns about uninitialized usage... so initialize it.
- */
- struct dahdi_echocan_state ec_state = { .ops = NULL, };
- struct dahdi_chan *chan;
- struct dahdi_chan *temp;
- /* get channel number from user */
- get_user(channo, (int __user *)data);
- chan = chan_from_num(channo);
- if (!chan)
- return -EINVAL;
- temp = kmalloc(sizeof(*chan), GFP_KERNEL);
- if (!temp)
- return -ENOMEM;
- spin_lock_irqsave(&chan->lock, flags);
- *temp = *chan;
- if (temp->ec_state)
- ec_state = *temp->ec_state;
- if (temp->curzone)
- tone_zone_get(temp->curzone);
- spin_unlock_irqrestore(&chan->lock, flags);
- module_printk(KERN_INFO, "Dump of DAHDI Channel %d (%s,%d,%d):\n\n",
- channo, temp->name, temp->channo, temp->chanpos);
- module_printk(KERN_INFO, "flags: %x hex, writechunk: %p, readchunk: %p\n",
- (unsigned int) temp->flags, temp->writechunk, temp->readchunk);
- module_printk(KERN_INFO, "rxgain: %p, txgain: %p, gainalloc: %d\n",
- temp->rxgain, temp->txgain, is_gain_allocated(temp));
- module_printk(KERN_INFO, "span: %p, sig: %x hex, sigcap: %x hex\n",
- temp->span, temp->sig, temp->sigcap);
- module_printk(KERN_INFO, "inreadbuf: %d, outreadbuf: %d, inwritebuf: %d, outwritebuf: %d\n",
- temp->inreadbuf, temp->outreadbuf, temp->inwritebuf, temp->outwritebuf);
- module_printk(KERN_INFO, "blocksize: %d, numbufs: %d, txbufpolicy: %d, txbufpolicy: %d\n",
- temp->blocksize, temp->numbufs, temp->txbufpolicy,
- DAHDI_POLICY_IMMEDIATE);
- module_printk(KERN_INFO, "txdisable: %d, rxdisable: %d, iomask: %d\n",
- temp->txdisable, 0, temp->iomask);
- module_printk(KERN_INFO, "curzone: %p, tonezone: %d, curtone: %p, tonep: %d\n",
- temp->curzone,
- ((temp->curzone) ? temp->curzone->num : -1),
- temp->curtone, temp->tonep);
- module_printk(KERN_INFO, "digitmode: %d, txdialbuf: %s, dialing: %d, aftdialtimer: %d, cadpos. %d\n",
- temp->digitmode, temp->txdialbuf, temp->dialing,
- temp->afterdialingtimer, temp->cadencepos);
- module_printk(KERN_INFO, "confna: %d, confn: %d, confmode: %d, confmute: %d\n",
- temp->confna, temp->_confn, temp->confmode, temp->confmute);
- module_printk(KERN_INFO, "ec: %p, deflaw: %d, xlaw: %p\n",
- temp->ec_state, temp->deflaw, temp->xlaw);
- if (temp->ec_state) {
- module_printk(KERN_INFO, "echostate: %02x, echotimer: %d, echolastupdate: %d\n",
- ec_state.status.mode, ec_state.status.pretrain_timer, ec_state.status.last_train_tap);
- }
- module_printk(KERN_INFO, "itimer: %d, otimer: %d, ringdebtimer: %d\n\n",
- temp->itimer, temp->otimer, temp->ringdebtimer);
- if (temp->curzone)
- tone_zone_put(temp->curzone);
- kfree(temp);
- return 0;
- }
- /**
- * dahdi_ioctl_getparams() - Get channel parameters.
- *
- */
- static int dahdi_ioctl_getparams(struct file *file, unsigned long data)
- {
- size_t size_to_copy;
- struct dahdi_params param;
- bool return_master = false;
- struct dahdi_chan *chan;
- int j;
- size_to_copy = sizeof(struct dahdi_params);
- if (copy_from_user(¶m, (void __user *)data, size_to_copy))
- return -EFAULT;
- /* check to see if the caller wants to receive our master channel
- * number */
- if (param.channo & DAHDI_GET_PARAMS_RETURN_MASTER) {
- return_master = true;
- param.channo &= ~DAHDI_GET_PARAMS_RETURN_MASTER;
- }
- /* Pick the right channo's */
- chan = chan_from_file(file);
- if (!chan)
- chan = chan_from_num(param.channo);
- if (!chan)
- return -EINVAL;
- /* point to relevant structure */
- param.sigtype = chan->sig; /* get signalling type */
- /* return non-zero if rx not in idle state */
- if (chan->span) {
- j = dahdi_q_sig(chan);
- if (j >= 0) { /* if returned with success */
- param.rxisoffhook = ((chan->rxsig & (j >> 8)) !=
- (j & 0xff));
- } else {
- const int sig = chan->rxhooksig;
- param.rxisoffhook = ((sig != DAHDI_RXSIG_ONHOOK) &&
- (sig != DAHDI_RXSIG_INITIAL));
- }
- } else if ((chan->txstate == DAHDI_TXSTATE_KEWL) ||
- (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) {
- param.rxisoffhook = 1;
- } else {
- param.rxisoffhook = 0;
- }
- if (chan->span &&
- chan->span->ops->rbsbits && !(chan->sig & DAHDI_SIG_CLEAR)) {
- param.rxbits = chan->rxsig;
- param.txbits = chan->txsig;
- param.idlebits = chan->idlebits;
- } else {
- param.rxbits = -1;
- param.txbits = -1;
- param.idlebits = 0;
- }
- if (chan->span &&
- (chan->span->ops->rbsbits || chan->span->ops->hooksig) &&
- !(chan->sig & DAHDI_SIG_CLEAR)) {
- param.rxhooksig = chan->rxhooksig;
- param.txhooksig = chan->txhooksig;
- } else {
- param.rxhooksig = -1;
- param.txhooksig = -1;
- }
- param.prewinktime = chan->prewinktime;
- param.preflashtime = chan->preflashtime;
- param.winktime = chan->winktime;
- param.flashtime = chan->flashtime;
- param.starttime = chan->starttime;
- param.rxwinktime = chan->rxwinktime;
- param.rxflashtime = chan->rxflashtime;
- param.debouncetime = chan->debouncetime;
- param.channo = chan->channo;
- param.chan_alarms = chan->chan_alarms;
- /* if requested, put the master channel number in the top 16 bits of
- * the result */
- if (return_master)
- param.channo |= chan->master->channo << 16;
- param.pulsemaketime = chan->pulsemaketime;
- param.pulsebreaktime = chan->pulsebreaktime;
- param.pulseaftertime = chan->pulseaftertime;
- param.spanno = (chan->span) ? chan->span->spanno : 0;
- strlcpy(param.name, chan->name, sizeof(param.name));
- param.chanpos = chan->chanpos;
- param.sigcap = chan->sigcap;
- /* Return current law */
- if (chan->xlaw == __dahdi_alaw)
- param.curlaw = DAHDI_LAW_ALAW;
- else
- param.curlaw = DAHDI_LAW_MULAW;
- if (copy_to_user((void __user *)data, ¶m, size_to_copy))
- return -EFAULT;
- return 0;
- }
- /**
- * dahdi_ioctl_setparams() - Set channel parameters.
- *
- */
- static int dahdi_ioctl_setparams(struct file *file, unsigned long data)
- {
- struct dahdi_params param;
- struct dahdi_chan *chan;
- if (copy_from_user(¶m, (void __user *)data, sizeof(param)))
- return -EFAULT;
- param.chan_alarms = 0; /* be explicit about the above */
- /* Pick the right channo's */
- chan = chan_from_file(file);
- if (!chan)
- chan = chan_from_num(param.channo);
- if (!chan)
- return -EINVAL;
- /* point to relevant structure */
- /* NOTE: sigtype is *not* included in this */
- /* get timing paramters */
- chan->prewinktime = param.prewinktime;
- chan->preflashtime = param.preflashtime;
- chan->winktime = param.winktime;
- chan->flashtime = param.flashtime;
- chan->starttime = param.starttime;
- /* Update ringtime if not using a tone zone */
- if (!chan->curzone)
- chan->ringcadence[0] = chan->starttime;
- chan->rxwinktime = param.rxwinktime;
- chan->rxflashtime = param.rxflashtime;
- chan->debouncetime = param.debouncetime;
- chan->pulsemaketime = param.pulsemaketime;
- chan->pulsebreaktime = param.pulsebreaktime;
- chan->pulseaftertime = param.pulseaftertime;
- return 0;
- }
- /**
- * dahdi_ioctl_spanstat() - Return statistics for a span.
- *
- */
- static int dahdi_ioctl_spanstat(struct file *file, unsigned long data)
- {
- int ret = 0;
- struct dahdi_spaninfo spaninfo;
- struct dahdi_span *s;
- int j;
- size_t size_to_copy;
- bool via_chan = false;
- size_to_copy = sizeof(struct dahdi_spaninfo);
- if (copy_from_user(&spaninfo, (void __user *)data, size_to_copy))
- return -EFAULT;
- if (!spaninfo.spanno) {
- struct dahdi_chan *const chan = chan_from_file(file);
- if (!chan)
- return -EINVAL;
- s = chan->span;
- via_chan = true;
- } else {
- s = span_find_and_get(spaninfo.spanno);
- }
- if (!s)
- return -EINVAL;
- spaninfo.spanno = s->spanno; /* put the span # in here */
- spaninfo.totalspans = span_count();
- strlcpy(spaninfo.desc, s->desc, sizeof(spaninfo.desc));
- strlcpy(spaninfo.name, s->name, sizeof(spaninfo.name));
- spaninfo.alarms = s->alarms; /* get alarm status */
- spaninfo.rxlevel = s->rxlevel; /* get rx level */
- spaninfo.txlevel = s->txlevel; /* get tx level */
- spaninfo.bpvcount = s->count.bpv;
- spaninfo.crc4count = s->count.crc4;
- spaninfo.ebitcount = s->count.ebit;
- spaninfo.fascount = s->count.fas;
- spaninfo.fecount = s->count.fe;
- spaninfo.cvcount = s->count.cv;
- spaninfo.becount = s->count.be;
- spaninfo.prbs = s->count.prbs;
- spaninfo.errsec = s->count.errsec;
- spaninfo.irqmisses = s->parent->irqmisses; /* get IRQ miss count */
- spaninfo.syncsrc = s->syncsrc; /* get active sync source */
- spaninfo.totalchans = s->channels;
- spaninfo.numchans = 0;
- for (j = 0; j < s->channels; j++) {
- if (s->chans[j]->sig)
- spaninfo.numchans++;
- }
- spaninfo.lbo = s->lbo;
- spaninfo.lineconfig = s->lineconfig;
- spaninfo.irq = 0;
- spaninfo.linecompat = s->linecompat;
- strlcpy(spaninfo.lboname, dahdi_lboname(s->lbo),
- sizeof(spaninfo.lboname));
- if (s->parent->manufacturer) {
- strlcpy(spaninfo.manufacturer, s->parent->manufacturer,
- sizeof(spaninfo.manufacturer));
- }
- if (s->parent->devicetype) {
- strlcpy(spaninfo.devicetype, s->parent->devicetype,
- sizeof(spaninfo.devicetype));
- }
- if (s->parent->location) {
- strlcpy(spaninfo.location, s->parent->location,
- sizeof(spaninfo.location));
- }
- if (s->spantype) {
- /*
- * The API is brain-damaged, returning fixed length
- * null terminated strings via ioctl() is...
- *
- * This field contain only 6 characters
- * (including null termination, 5 effective characters).
- *
- * For backward compatibility, massage this info for dahdi-scan
- * and friends, until:
- * - They either learn to read the info from sysfs
- * - Or this API is broken to return the enum value
- */
- const char *st = dahdi_spantype2str(s->spantype);
- switch (s->spantype) {
- case SPANTYPE_DIGITAL_BRI_NT:
- strlcpy(spaninfo.spantype, "NT",
- sizeof(spaninfo.spantype));
- break;
- case SPANTYPE_DIGITAL_BRI_TE:
- strlcpy(spaninfo.spantype, "TE",
- sizeof(spaninfo.spantype));
- break;
- default:
- /*
- * The rest are either short (FXS, FXO, E1, T1, J1)
- * Or new (BRI_SOFT, ANALOG_MIXED, INVALID),
- * so no backward compatibility for this
- * broken interface.
- */
- strlcpy(spaninfo.spantype, st,
- sizeof(spaninfo.spantype));
- break;
- }
- }
- if (copy_to_user((void __user *)data, &spaninfo, size_to_copy))
- ret = -EFAULT;
- if (!via_chan)
- put_span(s);
- return ret;
- }
- /**
- * dahdi_ioctl_spanstat_v1() - Return statistics for a span in a legacy format.
- *
- */
- static int dahdi_ioctl_spanstat_v1(struct file *file, unsigned long data)
- {
- int ret = 0;
- struct dahdi_spaninfo_v1 spaninfo_v1;
- struct dahdi_span *s;
- int j;
- bool via_chan = false;
- if (copy_from_user(&spaninfo_v1, (void __user *)data,
- sizeof(spaninfo_v1))) {
- return -EFAULT;
- }
- if (!spaninfo_v1.spanno) {
- struct dahdi_chan *const chan = chan_from_file(file);
- if (!chan)
- return -EINVAL;
- s = chan->span;
- via_chan = true;
- } else {
- s = span_find_and_get(spaninfo_v1.spanno);
- }
- if (!s)
- return -EINVAL;
- spaninfo_v1.spanno = s->spanno; /* put the span # in here */
- spaninfo_v1.totalspans = 0;
- spaninfo_v1.totalspans = span_count();
- strlcpy(spaninfo_v1.desc,
- s->desc,
- sizeof(spaninfo_v1.desc));
- strlcpy(spaninfo_v1.name,
- s->name,
- sizeof(spaninfo_v1.name));
- spaninfo_v1.alarms = s->alarms;
- spaninfo_v1.bpvcount = s->count.bpv;
- spaninfo_v1.rxlevel = s->rxlevel;
- spaninfo_v1.txlevel = s->txlevel;
- spaninfo_v1.crc4count = s->count.crc4;
- spaninfo_v1.ebitcount = s->count.ebit;
- spaninfo_v1.fascount = s->count.fas;
- spaninfo_v1.irqmisses = s->parent->irqmisses;
- spaninfo_v1.syncsrc = s->syncsrc;
- spaninfo_v1.totalchans = s->channels;
- spaninfo_v1.numchans = 0;
- for (j = 0; j < s->channels; j++) {
- if (s->chans[j]->sig)
- spaninfo_v1.numchans++;
- }
- spaninfo_v1.lbo = s->lbo;
- spaninfo_v1.lineconfig = s->lineconfig;
- spaninfo_v1.irq = 0;
- spaninfo_v1.linecompat = s->linecompat;
- strlcpy(spaninfo_v1.lboname,
- dahdi_lboname(s->lbo),
- sizeof(spaninfo_v1.lboname));
- if (s->parent->manufacturer) {
- strlcpy(spaninfo_v1.manufacturer, s->parent->manufacturer,
- sizeof(spaninfo_v1.manufacturer));
- }
- if (s->parent->devicetype) {
- strlcpy(spaninfo_v1.devicetype, s->parent->devicetype,
- sizeof(spaninfo_v1.devicetype));
- }
- strlcpy(spaninfo_v1.location, s->parent->location,
- sizeof(spaninfo_v1.location));
- if (s->spantype) {
- strlcpy(spaninfo_v1.spantype,
- dahdi_spantype2str(s->spantype),
- sizeof(spaninfo_v1.spantype));
- }
- if (copy_to_user((void __user *)data, &spaninfo_v1,
- sizeof(spaninfo_v1))) {
- ret = -EFAULT;
- }
- if (!via_chan)
- put_span(s);
- return ret;
- }
- #ifdef CONFIG_DAHDI_CONFLINK
- static int dahdi_ioctl_conflink(struct file *file, unsigned long data)
- {
- struct dahdi_chan *chan;
- struct dahdi_confinfo conf;
- unsigned long flags;
- int res = 0;
- int i;
- chan = chan_from_file(file);
- if (!chan)
- return -EINVAL;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- if (copy_from_user(&conf, (void __user *)data, sizeof(conf)))
- return -EFAULT;
- /* check sanity of arguments */
- if ((conf.chan < 0) || (conf.chan > DAHDI_MAX_CONF))
- return -EINVAL;
- if ((conf.confno < 0) || (conf.confno > DAHDI_MAX_CONF))
- return -EINVAL;
- /* cant listen to self!! */
- if (conf.chan && (conf.chan == conf.confno))
- return -EINVAL;
- spin_lock_irqsave(&chan_lock, flags);
- spin_lock(&chan->lock);
- /* if to clear all links */
- if ((!conf.chan) && (!conf.confno)) {
- /* clear all the links */
- memset(conf_links, 0, sizeof(conf_links));
- recalc_maxlinks();
- spin_unlock(&chan->lock);
- spin_unlock_irqrestore(&chan_lock, flags);
- return 0;
- }
- /* look for already existant specified combination */
- for (i = 1; i <= DAHDI_MAX_CONF; i++) {
- /* if found, exit */
- if ((conf_links[i].src == conf.chan) &&
- (conf_links[i].dst == conf.confno))
- break;
- }
- if (i <= DAHDI_MAX_CONF) { /* if found */
- if (!conf.confmode) { /* if to remove link */
- conf_links[i].src = 0;
- conf_links[i].dst = 0;
- } else { /* if to add and already there, error */
- res = -EEXIST;
- }
- } else { /* if not found */
- if (conf.confmode) { /* if to add link */
- /* look for empty location */
- for (i = 1; i <= DAHDI_MAX_CONF; i++) {
- /* if empty, exit loop */
- if ((!conf_links[i].src) &&
- (!conf_links[i].dst))
- break;
- }
- /* if empty spot found */
- if (i <= DAHDI_MAX_CONF) {
- conf_links[i].src = conf.chan;
- conf_links[i].dst = conf.confno;
- } else { /* if no empties -- error */
- res = -ENOSPC;
- }
- } else { /* if to remove, and not found -- error */
- res = -ENOENT;
- }
- }
- recalc_maxlinks();
- spin_unlock(&chan->lock);
- spin_unlock_irqrestore(&chan_lock, flags);
- return res;
- }
- #else
- static int dahdi_ioctl_conflink(struct file *file, unsigned long data)
- {
- return -ENOSYS;
- }
- #endif
- static int dahdi_common_ioctl(struct file *file, unsigned int cmd,
- unsigned long data)
- {
- switch (cmd) {
- /* get channel parameters */
- case DAHDI_GET_PARAMS_V1: /* Intentional drop through. */
- case DAHDI_GET_PARAMS:
- return dahdi_ioctl_getparams(file, data);
- case DAHDI_SET_PARAMS:
- return dahdi_ioctl_setparams(file, data);
- case DAHDI_GETGAINS_V1: /* Intentional drop through. */
- case DAHDI_GETGAINS: /* get gain stuff */
- return dahdi_ioctl_getgains(file, data);
- case DAHDI_SETGAINS: /* set gain stuff */
- return dahdi_ioctl_setgains(file, data);
- case DAHDI_SPANSTAT:
- return dahdi_ioctl_spanstat(file, data);
- case DAHDI_SPANSTAT_V1:
- return dahdi_ioctl_spanstat_v1(file, data);
- case DAHDI_CHANDIAG_V1: /* Intentional drop through. */
- case DAHDI_CHANDIAG:
- return dahdi_ioctl_chandiag(file, data);
- case DAHDI_CONFLINK:
- return dahdi_ioctl_conflink(file, data);
- default:
- return -ENOTTY;
- }
- return 0;
- }
- static const struct dahdi_dynamic_ops *dahdi_dynamic_ops;
- void dahdi_set_dynamic_ops(const struct dahdi_dynamic_ops *ops)
- {
- mutex_lock(®istration_mutex);
- dahdi_dynamic_ops = ops;
- mutex_unlock(®istration_mutex);
- }
- EXPORT_SYMBOL(dahdi_set_dynamic_ops);
- static int (*dahdi_hpec_ioctl)(unsigned int cmd, unsigned long data);
- void dahdi_set_hpec_ioctl(int (*func)(unsigned int cmd, unsigned long data))
- {
- dahdi_hpec_ioctl = func;
- }
- static void recalc_slaves(struct dahdi_chan *chan)
- {
- int x;
- struct dahdi_chan *last = chan;
- /* Makes no sense if you don't have a span */
- if (!chan->span)
- return;
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Recalculating slaves on %s\n", chan->name);
- #endif
- /* Link all slaves appropriately */
- for (x=chan->chanpos;x<chan->span->channels;x++)
- if (chan->span->chans[x]->master == chan) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Channel %s, slave to %s, last is %s, its next will be %d\n",
- chan->span->chans[x]->name, chan->name, last->name, x);
- #endif
- last->nextslave = chan->span->chans[x];
- last = last->nextslave;
- }
- /* Terminate list */
- last->nextslave = NULL;
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Done Recalculating slaves on %s (last is %s)\n", chan->name, last->name);
- #endif
- }
- #if defined(CONFIG_DAHDI_NET) && defined(HAVE_NET_DEVICE_OPS)
- static const struct net_device_ops dahdi_netdev_ops = {
- .ndo_open = dahdi_net_open,
- .ndo_stop = dahdi_net_stop,
- .ndo_do_ioctl = dahdi_net_ioctl,
- .ndo_start_xmit = dahdi_xmit,
- };
- #endif
- static int dahdi_ioctl_chanconfig(struct file *file, unsigned long data)
- {
- int res = 0;
- int y;
- struct dahdi_chanconfig ch;
- struct dahdi_chan *newmaster;
- struct dahdi_chan *chan;
- struct dahdi_chan *dacs_chan = NULL;
- unsigned long flags;
- int sigcap;
- if (copy_from_user(&ch, (void __user *)data, sizeof(ch)))
- return -EFAULT;
- chan = chan_from_num(ch.chan);
- if (!chan) {
- printk(KERN_NOTICE "%s: No channel for number %d\n",
- __func__, ch.chan);
- return -EINVAL;
- }
- if (ch.sigtype == DAHDI_SIG_SLAVE) {
- newmaster = chan_from_num(ch.master);
- if (!newmaster) {
- chan_notice(chan, "%s: slave channel without master.\n",
- __func__);
- return -EINVAL;
- }
- ch.sigtype = newmaster->sig;
- } else if ((ch.sigtype & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS) {
- newmaster = chan;
- dacs_chan = chan_from_num(ch.idlebits);
- if (!dacs_chan) {
- chan_notice(chan, "%s: dacs channel not found: %d.\n",
- __func__, ch.idlebits);
- return -EINVAL;
- }
- } else {
- newmaster = chan;
- }
- spin_lock_irqsave(&chan->lock, flags);
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(chan)) {
- if (chan_to_netdev(chan)->flags & IFF_UP) {
- spin_unlock_irqrestore(&chan->lock, flags);
- module_printk(KERN_WARNING, "Can't switch HDLC net mode on channel %s, since current interface is up\n", chan->name);
- return -EBUSY;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- unregister_hdlc_device(chan->hdlcnetdev->netdev);
- spin_lock_irqsave(&chan->lock, flags);
- free_netdev(chan->hdlcnetdev->netdev);
- kfree(chan->hdlcnetdev);
- chan->hdlcnetdev = NULL;
- clear_bit(DAHDI_FLAGBIT_NETDEV, &chan->flags);
- }
- #else
- if (ch.sigtype == DAHDI_SIG_HDLCNET) {
- spin_unlock_irqrestore(&chan->lock, flags);
- module_printk(KERN_WARNING, "DAHDI networking not supported by this build.\n");
- return -ENOSYS;
- }
- #endif
- sigcap = chan->sigcap;
- /* If they support clear channel, then they support the HDLC and such through
- us. */
- if (sigcap & DAHDI_SIG_CLEAR)
- sigcap |= (DAHDI_SIG_HDLCRAW | DAHDI_SIG_HDLCFCS | DAHDI_SIG_HDLCNET | DAHDI_SIG_DACS);
- if ((sigcap & ch.sigtype) != ch.sigtype) {
- if (debug) {
- chan_notice(chan, "%s: bad sigtype. sigcap: %x, sigtype: %x.\n",
- __func__, sigcap, ch.sigtype);
- }
- res = -EINVAL;
- }
- if (chan->master != chan) {
- struct dahdi_chan *oldmaster = chan->master;
- /* Clear the master channel */
- chan->master = chan;
- chan->nextslave = NULL;
- /* Unlink this channel from the master's channel list */
- recalc_slaves(oldmaster);
- }
- if (!res) {
- chan->sig = ch.sigtype;
- if (chan->sig == DAHDI_SIG_CAS)
- chan->idlebits = ch.idlebits;
- else
- chan->idlebits = 0;
- if ((ch.sigtype & DAHDI_SIG_CLEAR) == DAHDI_SIG_CLEAR) {
- /* Set clear channel flag if appropriate */
- chan->flags &= ~DAHDI_FLAG_AUDIO;
- chan->flags |= DAHDI_FLAG_CLEAR;
- } else {
- /* Set audio flag and not clear channel otherwise */
- chan->flags |= DAHDI_FLAG_AUDIO;
- chan->flags &= ~DAHDI_FLAG_CLEAR;
- }
- if ((ch.sigtype & DAHDI_SIG_HDLCRAW) == DAHDI_SIG_HDLCRAW) {
- /* Set the HDLC flag */
- chan->flags |= DAHDI_FLAG_HDLC;
- } else {
- /* Clear the HDLC flag */
- chan->flags &= ~DAHDI_FLAG_HDLC;
- }
- if ((ch.sigtype & DAHDI_SIG_HDLCFCS) == DAHDI_SIG_HDLCFCS) {
- /* Set FCS to be calculated if appropriate */
- chan->flags |= DAHDI_FLAG_FCS;
- } else {
- /* Clear FCS flag */
- chan->flags &= ~DAHDI_FLAG_FCS;
- }
- if ((ch.sigtype & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS) {
- if (unlikely(!dacs_chan)) {
- spin_unlock_irqrestore(&chan->lock, flags);
- chan_notice(chan, "%s: dacs but no dacs_chan\n",
- __func__);
- return -EINVAL;
- }
- /* Setup conference properly */
- chan->confmode = DAHDI_CONF_DIGITALMON;
- chan->confna = ch.idlebits;
- chan->dacs_chan = dacs_chan;
- res = dahdi_chan_dacs(chan, dacs_chan);
- } else {
- dahdi_disable_dacs(chan);
- }
- chan->master = newmaster;
- /* Note new slave if we are not our own master */
- if (newmaster != chan) {
- recalc_slaves(chan->master);
- }
- if ((ch.sigtype & DAHDI_SIG_HARDHDLC) == DAHDI_SIG_HARDHDLC) {
- chan->flags &= ~DAHDI_FLAG_FCS;
- chan->flags &= ~DAHDI_FLAG_HDLC;
- chan->flags |= DAHDI_FLAG_NOSTDTXRX;
- } else {
- chan->flags &= ~DAHDI_FLAG_NOSTDTXRX;
- }
- if ((ch.sigtype & DAHDI_SIG_MTP2) == DAHDI_SIG_MTP2)
- chan->flags |= DAHDI_FLAG_MTP2;
- else
- chan->flags &= ~DAHDI_FLAG_MTP2;
- }
- /* Chanconfig can block, do not call through the function pointer with
- * the channel lock held. */
- spin_unlock_irqrestore(&chan->lock, flags);
- if (!res && chan->span->ops->chanconfig)
- res = chan->span->ops->chanconfig(file, chan, ch.sigtype);
- spin_lock_irqsave(&chan->lock, flags);
- #ifdef CONFIG_DAHDI_NET
- if (!res &&
- (newmaster == chan) &&
- (chan->sig == DAHDI_SIG_HDLCNET)) {
- chan->hdlcnetdev = dahdi_hdlc_alloc();
- if (chan->hdlcnetdev) {
- /* struct hdlc_device *hdlc = chan->hdlcnetdev;
- struct net_device *d = hdlc_to_dev(hdlc); mmm...get it right later --byg */
- chan->hdlcnetdev->netdev = alloc_hdlcdev(chan->hdlcnetdev);
- if (chan->hdlcnetdev->netdev) {
- chan->hdlcnetdev->chan = chan;
- #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23)
- SET_MODULE_OWNER(chan->hdlcnetdev->netdev);
- #endif
- chan->hdlcnetdev->netdev->tx_queue_len = 50;
- #ifdef HAVE_NET_DEVICE_OPS
- chan->hdlcnetdev->netdev->netdev_ops = &dahdi_netdev_ops;
- #else
- chan->hdlcnetdev->netdev->do_ioctl = dahdi_net_ioctl;
- chan->hdlcnetdev->netdev->open = dahdi_net_open;
- chan->hdlcnetdev->netdev->stop = dahdi_net_stop;
- #endif
- dev_to_hdlc(chan->hdlcnetdev->netdev)->attach = dahdi_net_attach;
- dev_to_hdlc(chan->hdlcnetdev->netdev)->xmit = dahdi_xmit;
- spin_unlock_irqrestore(&chan->lock, flags);
- /* Briefly restore interrupts while we register the device */
- res = dahdi_register_hdlc_device(chan->hdlcnetdev->netdev, ch.netdev_name);
- spin_lock_irqsave(&chan->lock, flags);
- } else {
- module_printk(KERN_NOTICE, "Unable to allocate hdlc: *shrug*\n");
- res = -1;
- }
- if (!res)
- set_bit(DAHDI_FLAGBIT_NETDEV, &chan->flags);
- } else {
- module_printk(KERN_NOTICE, "Unable to allocate netdev: out of memory\n");
- res = -1;
- }
- }
- #endif
- if ((chan->sig == DAHDI_SIG_HDLCNET) &&
- (chan == newmaster) &&
- !dahdi_have_netdev(chan))
- module_printk(KERN_NOTICE, "Unable to register HDLC device for channel %s\n", chan->name);
- if (!res) {
- /* Setup default law */
- chan->deflaw = ch.deflaw;
- /* Copy back any modified settings */
- spin_unlock_irqrestore(&chan->lock, flags);
- if (copy_to_user((void __user *)data, &ch, sizeof(ch)))
- return -EFAULT;
- spin_lock_irqsave(&chan->lock, flags);
- /* And hangup */
- dahdi_hangup(chan);
- y = dahdi_q_sig(chan) & 0xff;
- if (y >= 0)
- chan->rxsig = (unsigned char) y;
- chan->rxhooksig = DAHDI_RXSIG_INITIAL;
- }
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Configured channel %s, flags %04lx, sig %04x\n", chan->name, chan->flags, chan->sig);
- #endif
- spin_unlock_irqrestore(&chan->lock, flags);
- return res;
- }
- /**
- * dahdi_ioctl_set_dialparms - Set the global dial parameters.
- * @data: Pointer to user space that contains dahdi_dialparams.
- */
- static int dahdi_ioctl_set_dialparams(unsigned long data)
- {
- unsigned int i;
- struct dahdi_dialparams tdp;
- struct dahdi_zone *z;
- struct dahdi_dialparams *const gdp = &global_dialparams;
- if (copy_from_user(&tdp, (void __user *)data, sizeof(tdp)))
- return -EFAULT;
- mutex_lock(&global_dialparamslock);
- if ((tdp.dtmf_tonelen >= 10) && (tdp.dtmf_tonelen <= 4000))
- gdp->dtmf_tonelen = tdp.dtmf_tonelen;
- if ((tdp.mfv1_tonelen >= 10) && (tdp.mfv1_tonelen <= 4000))
- gdp->mfv1_tonelen = tdp.mfv1_tonelen;
- if ((tdp.mfr2_tonelen >= 10) && (tdp.mfr2_tonelen <= 4000))
- gdp->mfr2_tonelen = tdp.mfr2_tonelen;
- /* update the lengths in all currently loaded zones */
- spin_lock(&zone_lock);
- list_for_each_entry(z, &tone_zones, node) {
- for (i = 0; i < ARRAY_SIZE(z->dtmf); i++) {
- z->dtmf[i].tonesamples =
- gdp->dtmf_tonelen * DAHDI_CHUNKSIZE;
- }
- /* for MFR1, we only adjust the length of the digits */
- for (i = DAHDI_TONE_MFR1_0; i <= DAHDI_TONE_MFR1_9; i++) {
- z->mfr1[i - DAHDI_TONE_MFR1_BASE].tonesamples =
- gdp->mfv1_tonelen * DAHDI_CHUNKSIZE;
- }
- for (i = 0; i < ARRAY_SIZE(z->mfr2_fwd); i++) {
- z->mfr2_fwd[i].tonesamples =
- gdp->mfr2_tonelen * DAHDI_CHUNKSIZE;
- }
- for (i = 0; i < ARRAY_SIZE(z->mfr2_rev); i++) {
- z->mfr2_rev[i].tonesamples =
- gdp->mfr2_tonelen * DAHDI_CHUNKSIZE;
- }
- }
- spin_unlock(&zone_lock);
- dtmf_silence.tonesamples = gdp->dtmf_tonelen * DAHDI_CHUNKSIZE;
- mfr1_silence.tonesamples = gdp->mfv1_tonelen * DAHDI_CHUNKSIZE;
- mfr2_silence.tonesamples = gdp->mfr2_tonelen * DAHDI_CHUNKSIZE;
- mutex_unlock(&global_dialparamslock);
- return 0;
- }
- static int dahdi_ioctl_get_dialparams(unsigned long data)
- {
- struct dahdi_dialparams tdp;
- mutex_lock(&global_dialparamslock);
- tdp = global_dialparams;
- mutex_unlock(&global_dialparamslock);
- if (copy_to_user((void __user *)data, &tdp, sizeof(tdp)))
- return -EFAULT;
- return 0;
- }
- static int dahdi_ioctl_indirect(struct file *file, unsigned long data)
- {
- int res;
- struct dahdi_indirect_data ind;
- void *old;
- static bool warned;
- struct dahdi_chan *chan;
- if (copy_from_user(&ind, (void __user *)data, sizeof(ind)))
- return -EFAULT;
- chan = chan_from_num(ind.chan);
- if (!chan)
- return -EINVAL;
- if (!warned) {
- warned = true;
- module_printk(KERN_WARNING, "Using deprecated " \
- "DAHDI_INDIRECT. Please update " \
- "dahdi-tools.\n");
- }
- /* Since dahdi_chan_ioctl expects to be called on file handles
- * associated with channels, we'll temporarily set the
- * private_data pointer on the ctl file handle just for this
- * call. */
- old = file->private_data;
- file->private_data = chan;
- res = dahdi_chan_ioctl(file, ind.op, (unsigned long) ind.data);
- file->private_data = old;
- return res;
- }
- static int dahdi_ioctl_spanconfig(struct file *file, unsigned long data)
- {
- int res = 0;
- struct dahdi_lineconfig lc;
- struct dahdi_span *s;
- if (copy_from_user(&lc, (void __user *)data, sizeof(lc)))
- return -EFAULT;
- s = span_find_and_get(lc.span);
- if (!s)
- return -ENXIO;
- if ((lc.lineconfig & 0x1ff0 & s->linecompat) !=
- (lc.lineconfig & 0x1ff0)) {
- put_span(s);
- return -EINVAL;
- }
- if (s->ops->spanconfig) {
- s->lineconfig = lc.lineconfig;
- s->lbo = lc.lbo;
- s->txlevel = lc.lbo;
- s->rxlevel = 0;
- res = s->ops->spanconfig(file, s, &lc);
- }
- put_span(s);
- return res;
- }
- static int dahdi_ioctl_startup(struct file *file, unsigned long data)
- {
- /* I/O CTL's for control interface */
- int j;
- int res = 0;
- int x, y;
- unsigned long flags;
- struct dahdi_span *s;
- if (get_user(j, (int __user *)data))
- return -EFAULT;
- s = span_find_and_get(j);
- if (!s)
- return -ENXIO;
- if (s->flags & DAHDI_FLAG_RUNNING) {
- put_span(s);
- return 0;
- }
- if (s->ops->startup)
- res = s->ops->startup(file, s);
- if (!res) {
- /* Mark as running and hangup any channels */
- s->flags |= DAHDI_FLAG_RUNNING;
- for (x = 0; x < s->channels; x++) {
- y = dahdi_q_sig(s->chans[x]) & 0xff;
- if (y >= 0)
- s->chans[x]->rxsig = (unsigned char)y;
- spin_lock_irqsave(&s->chans[x]->lock, flags);
- dahdi_hangup(s->chans[x]);
- spin_unlock_irqrestore(&s->chans[x]->lock, flags);
- /*
- * Set the rxhooksig back to
- * DAHDI_RXSIG_INITIAL so that new events are
- * queued on the channel with the actual
- * received hook state.
- *
- */
- s->chans[x]->rxhooksig = DAHDI_RXSIG_INITIAL;
- }
- /* Now that this span is running, it might be selected as the
- * master_span */
- __dahdi_find_master_span();
- }
- put_span(s);
- return res;
- }
- static int dahdi_shutdown_span(struct dahdi_span *s)
- {
- int res = 0;
- int x;
- /* Unconfigure channels */
- for (x = 0; x < s->channels; x++)
- s->chans[x]->sig = 0;
- if (s->ops->shutdown)
- res = s->ops->shutdown(s);
- clear_bit(DAHDI_FLAGBIT_RUNNING, &s->flags);
- return res;
- }
- static int dahdi_ioctl_shutdown(unsigned long data)
- {
- int res;
- /* I/O CTL's for control interface */
- int j;
- struct dahdi_span *s;
- if (get_user(j, (int __user *)data))
- return -EFAULT;
- s = span_find_and_get(j);
- if (!s)
- return -ENXIO;
- res = dahdi_shutdown_span(s);
- put_span(s);
- return res;
- }
- /**
- * dahdi_is_hwec_available - Is hardware echocan available on a channel?
- * @chan: The channel to check
- *
- * Returns true if there is a hardware echocan available for the attached
- * channel, or false otherwise.
- *
- */
- static bool dahdi_is_hwec_available(const struct dahdi_chan *chan)
- {
- if (!chan->span || !chan->span->ops->echocan_name ||
- !hwec_factory.get_name(chan))
- return false;
- return true;
- }
- static int dahdi_ioctl_attach_echocan(unsigned long data)
- {
- struct dahdi_chan *chan;
- struct dahdi_attach_echocan ae;
- const struct dahdi_echocan_factory *new = NULL, *old;
- if (copy_from_user(&ae, (void __user *)data, sizeof(ae)))
- return -EFAULT;
- chan = chan_from_num(ae.chan);
- if (!chan)
- return -EINVAL;
- ae.echocan[sizeof(ae.echocan) - 1] = '\0';
- if (dahdi_is_hwec_available(chan)) {
- if (hwec_overrides_swec) {
- chan_dbg(GENERAL, chan,
- "Using echocan '%s' instead of requested " \
- "'%s'.\n", hwec_def_name, ae.echocan);
- /* If there is a hardware echocan available we'll
- * always use it instead of any configured software
- * echocan. This matches the behavior in dahdi 2.4.1.2
- * and earlier releases. */
- strlcpy(ae.echocan, hwec_def_name, sizeof(ae.echocan));
- } else if (strcasecmp(ae.echocan, hwec_def_name) != 0) {
- chan_dbg(GENERAL, chan,
- "Using '%s' on channel even though '%s' is " \
- "available.\n", ae.echocan, hwec_def_name);
- }
- }
- if (ae.echocan[0]) {
- new = find_echocan(ae.echocan);
- if (!new)
- return -EINVAL;
- if (!new->get_name(chan)) {
- release_echocan(new);
- return -EINVAL;
- }
- }
- mutex_lock(&chan->mutex);
- old = chan->ec_factory;
- chan->ec_factory = new;
- mutex_unlock(&chan->mutex);
- if (old)
- release_echocan(old);
- return 0;
- }
- static int dahdi_ioctl_sfconfig(unsigned long data)
- {
- int res = 0;
- unsigned long flags;
- struct dahdi_chan *chan;
- struct dahdi_sfconfig sf;
- if (copy_from_user(&sf, (void __user *)data, sizeof(sf)))
- return -EFAULT;
- chan = chan_from_num(sf.chan);
- if (!chan)
- return -EINVAL;
- if (chan->sig != DAHDI_SIG_SF)
- return -EINVAL;
- spin_lock_irqsave(&chan->lock, flags);
- chan->rxp1 = sf.rxp1;
- chan->rxp2 = sf.rxp2;
- chan->rxp3 = sf.rxp3;
- chan->txtone = sf.txtone;
- chan->tx_v2 = sf.tx_v2;
- chan->tx_v3 = sf.tx_v3;
- chan->toneflags = sf.toneflag;
- if (sf.txtone) { /* if set to make tone for tx */
- if ((chan->txhooksig &&
- !(sf.toneflag & DAHDI_REVERSE_TXTONE)) ||
- ((!chan->txhooksig) &&
- (sf.toneflag & DAHDI_REVERSE_TXTONE))) {
- set_txtone(chan, sf.txtone, sf.tx_v2, sf.tx_v3);
- } else {
- set_txtone(chan, 0, 0, 0);
- }
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- return res;
- }
- /* Returns true if there are any hardware echocan on any span. */
- static bool dahdi_any_hwec_available(void)
- {
- int i;
- bool hwec_available = false;
- struct dahdi_span *s;
- mutex_lock(®istration_mutex);
- list_for_each_entry(s, &span_list, spans_node) {
- for (i = 0; i < s->channels; ++i) {
- struct dahdi_chan *const chan = s->chans[i];
- if (dahdi_is_hwec_available(chan)) {
- hwec_available = true;
- break;
- }
- }
- }
- mutex_unlock(®istration_mutex);
- return hwec_available;
- }
- static int dahdi_ioctl_get_version(unsigned long data)
- {
- struct dahdi_versioninfo vi;
- struct ecfactory *cur;
- size_t space = sizeof(vi.echo_canceller) - 1;
- bool have_hwec = dahdi_any_hwec_available();
- memset(&vi, 0, sizeof(vi));
- strlcpy(vi.version, dahdi_version, sizeof(vi.version));
- spin_lock(&ecfactory_list_lock);
- list_for_each_entry(cur, &ecfactory_list, list) {
- const char * const ec_name = cur->ec->get_name(NULL);
- if ((ec_name == hwec_def_name) && !have_hwec) {
- /*
- * The hardware echocan factory is always registered so
- * that hwec can be configured on the channels as if it
- * was a software echocan. However, it can be confusing
- * to list it as one of the available options in the
- * output of dahdi_cfg if there isn't a REAL hardware
- * echocanceler attached to any of the spans. In that
- * case, do not report the presence of the hardware
- * echocan factory to userspace.
- *
- */
- continue;
- }
- strncat(vi.echo_canceller + strlen(vi.echo_canceller),
- ec_name, space);
- space -= strlen(ec_name);
- if (space < 1)
- break;
- if (cur->list.next && (cur->list.next != &ecfactory_list)) {
- strncat(vi.echo_canceller + strlen(vi.echo_canceller),
- ", ", space);
- space -= 2;
- if (space < 1)
- break;
- }
- }
- spin_unlock(&ecfactory_list_lock);
- if (copy_to_user((void __user *)data, &vi, sizeof(vi)))
- return -EFAULT;
- return 0;
- }
- static int dahdi_ioctl_maint(unsigned long data)
- {
- int i;
- unsigned long flags;
- int rv;
- struct dahdi_span *s;
- struct dahdi_maintinfo maint;
- /* get struct from user */
- if (copy_from_user(&maint, (void __user *)data, sizeof(maint)))
- return -EFAULT;
- s = span_find_and_get(maint.spanno);
- if (!s)
- return -EINVAL;
- if (!s->ops->maint) {
- put_span(s);
- return -ENOSYS;
- }
- spin_lock_irqsave(&s->lock, flags);
- /* save current maint state */
- i = s->maintstat;
- /* set maint mode */
- s->maintstat = maint.command;
- switch (maint.command) {
- case DAHDI_MAINT_NONE:
- case DAHDI_MAINT_LOCALLOOP:
- case DAHDI_MAINT_NETWORKLINELOOP:
- case DAHDI_MAINT_NETWORKPAYLOADLOOP:
- /* if same, ignore it */
- if (i == maint.command)
- break;
- rv = s->ops->maint(s, maint.command);
- spin_unlock_irqrestore(&s->lock, flags);
- if (rv) {
- put_span(s);
- /* Restore the state on error */
- s->maintstat = i;
- return rv;
- }
- spin_lock_irqsave(&s->lock, flags);
- break;
- case DAHDI_MAINT_LOOPUP:
- case DAHDI_MAINT_LOOPDOWN:
- s->mainttimer = DAHDI_LOOPCODE_TIME * DAHDI_CHUNKSIZE;
- rv = s->ops->maint(s, maint.command);
- spin_unlock_irqrestore(&s->lock, flags);
- if (rv) {
- put_span(s);
- /* Restore the state on error */
- s->maintstat = i;
- return rv;
- }
- spin_lock_irqsave(&s->lock, flags);
- break;
- case DAHDI_MAINT_FAS_DEFECT:
- case DAHDI_MAINT_MULTI_DEFECT:
- case DAHDI_MAINT_CRC_DEFECT:
- case DAHDI_MAINT_CAS_DEFECT:
- case DAHDI_MAINT_PRBS_DEFECT:
- case DAHDI_MAINT_BIPOLAR_DEFECT:
- case DAHDI_MAINT_PRBS:
- case DAHDI_RESET_COUNTERS:
- case DAHDI_MAINT_ALARM_SIM:
- /* Prevent notifying an alarm state for generic
- maintenance functions, unless the driver is
- already in a maint state */
- if (!i)
- s->maintstat = 0;
- rv = s->ops->maint(s, maint.command);
- spin_unlock_irqrestore(&s->lock, flags);
- if (rv) {
- put_span(s);
- /* Restore the state on error */
- s->maintstat = i;
- return rv;
- }
- spin_lock_irqsave(&s->lock, flags);
- break;
- default:
- spin_unlock_irqrestore(&s->lock, flags);
- module_printk(KERN_NOTICE,
- "Unknown maintenance event: %d\n",
- maint.command);
- put_span(s);
- /* Restore the state on error */
- s->maintstat = i;
- return -ENOSYS;
- }
- dahdi_alarm_notify(s); /* process alarm-related events */
- spin_unlock_irqrestore(&s->lock, flags);
- put_span(s);
- return 0;
- }
- static int dahdi_ioctl_dynamic(unsigned int cmd, unsigned long data)
- {
- bool tried_load = false;
- int res;
- retry_check:
- mutex_lock(®istration_mutex);
- if (!dahdi_dynamic_ops) {
- mutex_unlock(®istration_mutex);
- if (tried_load)
- return -ENOSYS;
- request_module("dahdi_dynamic");
- tried_load = true;
- goto retry_check;
- }
- if (!try_module_get(dahdi_dynamic_ops->owner)) {
- mutex_unlock(®istration_mutex);
- return -ENOSYS;
- }
- mutex_unlock(®istration_mutex);
- res = dahdi_dynamic_ops->ioctl(cmd, data);
- module_put(dahdi_dynamic_ops->owner);
- return res;
- }
- static int
- dahdi_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long data)
- {
- switch (cmd) {
- case DAHDI_INDIRECT:
- return dahdi_ioctl_indirect(file, data);
- case DAHDI_SPANCONFIG:
- return dahdi_ioctl_spanconfig(file, data);
- case DAHDI_STARTUP:
- return dahdi_ioctl_startup(file, data);
- case DAHDI_SHUTDOWN:
- return dahdi_ioctl_shutdown(data);
- case DAHDI_ATTACH_ECHOCAN:
- return dahdi_ioctl_attach_echocan(data);
- case DAHDI_CHANCONFIG:
- return dahdi_ioctl_chanconfig(file, data);
- case DAHDI_SFCONFIG:
- return dahdi_ioctl_sfconfig(data);
- case DAHDI_DEFAULTZONE:
- return dahdi_ioctl_defaultzone(data);
- case DAHDI_LOADZONE:
- return dahdi_ioctl_loadzone(data);
- case DAHDI_FREEZONE:
- return dahdi_ioctl_freezone(data);
- case DAHDI_SET_DIALPARAMS:
- return dahdi_ioctl_set_dialparams(data);
- case DAHDI_GET_DIALPARAMS:
- return dahdi_ioctl_get_dialparams(data);
- case DAHDI_GETVERSION:
- return dahdi_ioctl_get_version(data);
- case DAHDI_MAINT:
- return dahdi_ioctl_maint(data);
- case DAHDI_DYNAMIC_CREATE:
- case DAHDI_DYNAMIC_DESTROY:
- return dahdi_ioctl_dynamic(cmd, data);
- case DAHDI_EC_LICENSE_CHALLENGE:
- case DAHDI_EC_LICENSE_RESPONSE:
- if (dahdi_hpec_ioctl) {
- return dahdi_hpec_ioctl(cmd, data);
- } else {
- request_module("dahdi_echocan_hpec");
- if (dahdi_hpec_ioctl)
- return dahdi_hpec_ioctl(cmd, data);
- }
- return -ENOSYS;
- }
- return dahdi_common_ioctl(file, cmd, data);
- }
- static int ioctl_dahdi_dial(struct dahdi_chan *chan, unsigned long data)
- {
- struct dahdi_dialoperation *tdo;
- unsigned long flags;
- char *s;
- int rv;
- void __user * const user_data = (void __user *)data;
- tdo = kmalloc(sizeof(*tdo), GFP_KERNEL);
- if (!tdo)
- return -ENOMEM;
- if (copy_from_user(tdo, user_data, sizeof(*tdo)))
- return -EFAULT;
- rv = 0;
- /* Force proper NULL termination and uppercase entry */
- tdo->dialstr[DAHDI_MAX_DTMF_BUF - 1] = '\0';
- for (s = tdo->dialstr; *s; s++)
- *s = toupper(*s);
- spin_lock_irqsave(&chan->lock, flags);
- if (!chan->curzone) {
- spin_unlock_irqrestore(&chan->lock, flags);
- /* The tone zones are loaded by dahdi_cfg from /etc/dahdi/system.conf */
- module_printk(KERN_WARNING, "Cannot dial until a tone zone is loaded.\n");
- return -ENODATA;
- }
- switch (tdo->op) {
- case DAHDI_DIAL_OP_CANCEL:
- chan->curtone = NULL;
- chan->dialing = 0;
- chan->txdialbuf[0] = '\0';
- chan->tonep = 0;
- chan->pdialcount = 0;
- break;
- case DAHDI_DIAL_OP_REPLACE:
- strcpy(chan->txdialbuf, tdo->dialstr);
- chan->dialing = 1;
- __do_dtmf(chan);
- break;
- case DAHDI_DIAL_OP_APPEND:
- if (strlen(tdo->dialstr) + strlen(chan->txdialbuf) >= (DAHDI_MAX_DTMF_BUF - 1)) {
- rv = -EBUSY;
- break;
- }
- strlcpy(chan->txdialbuf + strlen(chan->txdialbuf), tdo->dialstr,
- DAHDI_MAX_DTMF_BUF - strlen(chan->txdialbuf));
- if (!chan->dialing) {
- chan->dialing = 1;
- __do_dtmf(chan);
- }
- break;
- default:
- rv = -EINVAL;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- return rv;
- }
- static int dahdi_ioctl_setconf(struct file *file, unsigned long data)
- {
- struct dahdi_confinfo conf;
- struct dahdi_chan *chan;
- struct dahdi_chan *conf_chan = NULL;
- unsigned long flags;
- unsigned int confmode;
- int oldconf;
- enum {NONE, ENABLE_HWPREEC, DISABLE_HWPREEC} preec = NONE;
- if (copy_from_user(&conf, (void __user *)data, sizeof(conf)))
- return -EFAULT;
- confmode = conf.confmode & DAHDI_CONF_MODE_MASK;
- chan = (conf.chan) ? chan_from_num(conf.chan) :
- chan_from_file(file);
- if (!chan)
- return -EINVAL;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- if ((DAHDI_CONF_DIGITALMON == confmode) ||
- is_monitor_mode(conf.confmode)) {
- conf_chan = chan_from_num(conf.confno);
- if (!conf_chan)
- return -EINVAL;
- } else {
- /* make sure conf number makes sense, too */
- if ((conf.confno < -1) || (conf.confno > DAHDI_MAX_CONF))
- return -EINVAL;
- }
- /* if taking off of any conf, must have 0 mode */
- if ((!conf.confno) && conf.confmode)
- return -EINVAL;
- /* likewise if 0 mode must have no conf */
- if ((!conf.confmode) && conf.confno)
- return -EINVAL;
- dahdi_check_conf(conf.confno);
- conf.chan = chan->channo; /* return with real channel # */
- spin_lock_irqsave(&chan_lock, flags);
- spin_lock(&chan->lock);
- if (conf.confno == -1)
- conf.confno = dahdi_first_empty_conference();
- if ((conf.confno < 1) && (conf.confmode)) {
- /* No more empty conferences */
- spin_unlock(&chan->lock);
- spin_unlock_irqrestore(&chan_lock, flags);
- return -EBUSY;
- }
- /* if changing confs, clear last added info */
- if (conf.confno != chan->confna) {
- memset(chan->conflast, 0, DAHDI_MAX_CHUNKSIZE);
- memset(chan->conflast1, 0, DAHDI_MAX_CHUNKSIZE);
- memset(chan->conflast2, 0, DAHDI_MAX_CHUNKSIZE);
- }
- oldconf = chan->confna; /* save old conference number */
- chan->confna = conf.confno; /* set conference number */
- chan->conf_chan = conf_chan;
- chan->confmode = conf.confmode; /* set conference mode */
- chan->_confn = 0; /* Clear confn */
- if (chan->span && chan->span->ops->dacs) {
- if ((confmode == DAHDI_CONF_DIGITALMON) &&
- (chan->txgain == defgain) &&
- (chan->rxgain == defgain) &&
- (conf_chan->txgain == defgain) &&
- (conf_chan->rxgain == defgain)) {
- dahdi_chan_dacs(chan, conf_chan);
- } else {
- dahdi_disable_dacs(chan);
- }
- }
- /* if we are going onto a conf */
- if (conf.confno &&
- (confmode == DAHDI_CONF_CONF ||
- confmode == DAHDI_CONF_CONFANN ||
- confmode == DAHDI_CONF_CONFMON ||
- confmode == DAHDI_CONF_CONFANNMON ||
- confmode == DAHDI_CONF_REALANDPSEUDO)) {
- /* Get alias */
- chan->_confn = dahdi_get_conf_alias(conf.confno);
- }
- spin_unlock(&chan->lock);
- if (conf_chan) {
- if ((confmode == DAHDI_CONF_MONITOR_RX_PREECHO) ||
- (confmode == DAHDI_CONF_MONITOR_TX_PREECHO) ||
- (confmode == DAHDI_CONF_MONITORBOTH_PREECHO)) {
- if (!conf_chan->readchunkpreec) {
- void *temp = kmalloc(sizeof(short) *
- DAHDI_CHUNKSIZE, GFP_ATOMIC);
- if (temp) {
- preec = ENABLE_HWPREEC;
- spin_lock(&conf_chan->lock);
- conf_chan->readchunkpreec = temp;
- spin_unlock(&conf_chan->lock);
- }
- }
- } else {
- preec = DISABLE_HWPREEC;
- spin_lock(&conf_chan->lock);
- kfree(conf_chan->readchunkpreec);
- conf_chan->readchunkpreec = NULL;
- spin_unlock(&conf_chan->lock);
- }
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if (ENABLE_HWPREEC == preec) {
- int res = dahdi_enable_hw_preechocan(conf_chan);
- if (res) {
- spin_lock_irqsave(&chan_lock, flags);
- spin_lock(&conf_chan->lock);
- kfree(conf_chan->readchunkpreec);
- conf_chan->readchunkpreec = NULL;
- spin_unlock(&conf_chan->lock);
- spin_unlock_irqrestore(&chan_lock, flags);
- }
- return res;
- } else if (DISABLE_HWPREEC == preec) {
- dahdi_disable_hw_preechocan(conf_chan);
- }
- dahdi_check_conf(oldconf);
- if (copy_to_user((void __user *)data, &conf, sizeof(conf)))
- return -EFAULT;
- return 0;
- }
- /**
- * dahdi_ioctl_confdiag() - Output debug info about conferences to console.
- *
- * This is a pure debugging aide since the only result is to the console.
- *
- * TODO: Does anyone use this anymore? Should it be hidden behind a debug
- * compile time option?
- */
- static int dahdi_ioctl_confdiag(struct file *file, unsigned long data)
- {
- struct dahdi_chan *chan;
- unsigned long flags;
- int i;
- int j;
- int c;
- chan = chan_from_file(file);
- if (!chan)
- return -EINVAL;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- get_user(j, (int __user *)data); /* get conf # */
- /* loop thru the interesting ones */
- for (i = ((j) ? j : 1); i <= ((j) ? j : DAHDI_MAX_CONF); i++) {
- struct dahdi_span *s;
- struct pseudo_chan *pseudo;
- int k;
- c = 0;
- spin_lock_irqsave(&chan_lock, flags);
- list_for_each_entry(s, &span_list, spans_node) {
- for (k = 0; k < s->channels; k++) {
- chan = s->chans[k];
- if (chan->confna != i)
- continue;
- if (!c)
- module_printk(KERN_NOTICE, "Conf #%d:\n", i);
- c = 1;
- module_printk(KERN_NOTICE, "chan %d, mode %x\n",
- chan->channo, chan->confmode);
- }
- }
- list_for_each_entry(pseudo, &pseudo_chans, node) {
- /* skip if not in this conf */
- if (pseudo->chan.confna != i)
- continue;
- if (!c)
- module_printk(KERN_NOTICE, "Conf #%d:\n", i);
- c = 1;
- module_printk(KERN_NOTICE, "chan %d, mode %x\n",
- pseudo->chan.channo, pseudo->chan.confmode);
- }
- #ifdef CONFIG_DAHDI_CONFLINK
- {
- int rv;
- rv = 0;
- for (k = 1; k <= DAHDI_MAX_CONF; k++) {
- if (conf_links[k].dst == i) {
- if (!c) {
- c = 1;
- module_printk(KERN_NOTICE,
- "Conf #%d:\n", i);
- }
- if (!rv) {
- rv = 1;
- module_printk(KERN_NOTICE,
- "Snooping on:\n");
- }
- module_printk(KERN_NOTICE, "conf %d\n",
- conf_links[k].src);
- }
- }
- }
- #endif
- spin_unlock_irqrestore(&chan_lock, flags);
- if (c)
- module_printk(KERN_NOTICE, "\n");
- }
- return 0;
- }
- static int dahdi_ioctl_getconf(struct file *file, unsigned long data)
- {
- struct dahdi_confinfo conf;
- struct dahdi_chan *chan;
- if (copy_from_user(&conf, (void __user *)data, sizeof(conf)))
- return -EFAULT;
- chan = (!conf.chan) ? chan_from_file(file) :
- chan_from_num(conf.chan);
- if (!chan)
- return -EINVAL;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- conf.chan = chan->channo; /* get channel number */
- conf.confno = chan->confna; /* get conference number */
- conf.confmode = chan->confmode; /* get conference mode */
- if (copy_to_user((void __user *)data, &conf, sizeof(conf)))
- return -EFAULT;
- return 0;
- }
- /**
- * dahdi_ioctl_iomux() - Wait for *something* to happen.
- *
- * This is now basically like the wait_event_interruptible function, but with
- * a much more involved wait condition.
- */
- static int dahdi_ioctl_iomux(struct file *file, unsigned long data)
- {
- struct dahdi_chan *const chan = chan_from_file(file);
- unsigned long flags;
- unsigned int iomask;
- int ret = 0;
- DEFINE_WAIT(wait);
- if (get_user(iomask, (int __user *)data))
- return -EFAULT;
- if (unlikely(!iomask || !chan))
- return -EINVAL;
- while (1) {
- unsigned int wait_result;
- wait_result = 0;
- prepare_to_wait(&chan->waitq, &wait, TASK_INTERRUPTIBLE);
- if (unlikely(!chan->file->private_data)) {
- /*
- * This should never happen. Surprise device removal
- * should lead us to the nodev_* file_operations
- */
- msleep(5);
- module_printk(KERN_ERR, "%s: NODEV\n", __func__);
- ret = -ENODEV;
- break;
- }
- spin_lock_irqsave(&chan->lock, flags);
- chan->iomask = iomask;
- if (iomask & DAHDI_IOMUX_READ) {
- if (chan->outreadbuf > -1)
- wait_result |= DAHDI_IOMUX_READ;
- }
- if (iomask & DAHDI_IOMUX_WRITE) {
- if (chan->inwritebuf > -1)
- wait_result |= DAHDI_IOMUX_WRITE;
- }
- if (iomask & DAHDI_IOMUX_WRITEEMPTY) {
- /* if everything empty -- be sure the transmitter is
- * enabled */
- chan->txdisable = 0;
- if (chan->outwritebuf < 0)
- wait_result |= DAHDI_IOMUX_WRITEEMPTY;
- }
- if (iomask & DAHDI_IOMUX_SIGEVENT) {
- if (chan->eventinidx != chan->eventoutidx)
- wait_result |= DAHDI_IOMUX_SIGEVENT;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- if (wait_result || (iomask & DAHDI_IOMUX_NOWAIT)) {
- put_user(wait_result, (int __user *)data);
- break;
- }
- if (signal_pending(current)) {
- finish_wait(&chan->waitq, &wait);
- return -ERESTARTSYS;
- }
- schedule();
- }
- finish_wait(&chan->waitq, &wait);
- spin_lock_irqsave(&chan->lock, flags);
- chan->iomask = 0;
- spin_unlock_irqrestore(&chan->lock, flags);
- return ret;
- }
- #ifdef CONFIG_DAHDI_MIRROR
- static int dahdi_ioctl_rxmirror(struct file *file, unsigned long data)
- {
- int res;
- int i;
- unsigned long flags;
- struct dahdi_chan *const chan = chan_from_file(file);
- struct dahdi_chan *srcmirror;
- if (!chan || chan->srcmirror)
- return -ENODEV;
- res = get_user(i, (int __user *)data);
- if (res)
- return res;
- srcmirror = chan_from_num(i);
- if (!srcmirror)
- return -EINVAL;
- module_printk(KERN_INFO, "Chan %d rx mirrored to %d\n",
- srcmirror->channo, chan->channo);
- spin_lock_irqsave(&srcmirror->lock, flags);
- if (srcmirror->rxmirror == NULL)
- srcmirror->rxmirror = chan;
- spin_unlock_irqrestore(&srcmirror->lock, flags);
- if (srcmirror->rxmirror != chan) {
- module_printk(KERN_INFO, "Chan %d cannot be rxmirrored, " \
- "already in use\n", srcmirror->channo);
- return -EFAULT;
- }
- spin_lock_irqsave(&chan->lock, flags);
- chan->srcmirror = srcmirror;
- chan->flags = srcmirror->flags;
- chan->sig = srcmirror->sig;
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- spin_unlock_irqrestore(&chan->lock, flags);
- return 0;
- }
- static int dahdi_ioctl_txmirror(struct file *file, unsigned long data)
- {
- int res;
- int i;
- unsigned long flags;
- struct dahdi_chan *const chan = chan_from_file(file);
- struct dahdi_chan *srcmirror;
- if (!chan || chan->srcmirror)
- return -ENODEV;
- res = get_user(i, (int __user *)data);
- if (res)
- return res;
- srcmirror = chan_from_num(i);
- if (!srcmirror)
- return -EINVAL;
- module_printk(KERN_INFO, "Chan %d tx mirrored to %d\n",
- srcmirror->channo, chan->channo);
- spin_lock_irqsave(&srcmirror->lock, flags);
- srcmirror->txmirror = chan;
- if (srcmirror->txmirror == NULL)
- srcmirror->txmirror = chan;
- spin_unlock_irqrestore(&srcmirror->lock, flags);
- if (srcmirror->txmirror != chan) {
- module_printk(KERN_INFO, "Chan %d cannot be txmirrored, " \
- "already in use\n", i);
- return -EFAULT;
- }
- spin_lock_irqsave(&chan->lock, flags);
- chan->srcmirror = srcmirror;
- chan->flags = srcmirror->flags;
- chan->sig = srcmirror->sig;
- clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
- spin_unlock_irqrestore(&chan->lock, flags);
- return 0;
- }
- #endif /* CONFIG_DAHDI_MIRROR */
- static int
- dahdi_chanandpseudo_ioctl(struct file *file, unsigned int cmd,
- unsigned long data)
- {
- struct dahdi_chan *chan = chan_from_file(file);
- union {
- struct dahdi_bufferinfo bi;
- struct dahdi_ring_cadence cad;
- } stack;
- unsigned long flags;
- int i, j, rv;
- void __user * const user_data = (void __user *)data;
- if (!chan)
- return -EINVAL;
- switch(cmd) {
- #ifdef CONFIG_DAHDI_MIRROR
- case DAHDI_RXMIRROR:
- return dahdi_ioctl_rxmirror(file, data);
- case DAHDI_TXMIRROR:
- return dahdi_ioctl_txmirror(file, data);
- #endif /* CONFIG_DAHDI_MIRROR */
- case DAHDI_DIALING:
- spin_lock_irqsave(&chan->lock, flags);
- j = chan->dialing;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (copy_to_user(user_data, &j, sizeof(int)))
- return -EFAULT;
- return 0;
- case DAHDI_DIAL:
- return ioctl_dahdi_dial(chan, data);
- case DAHDI_GET_BUFINFO:
- memset(&stack.bi, 0, sizeof(stack.bi));
- stack.bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
- stack.bi.txbufpolicy = chan->txbufpolicy;
- stack.bi.numbufs = chan->numbufs;
- stack.bi.bufsize = chan->blocksize;
- /* XXX FIXME! XXX */
- stack.bi.readbufs = -1;
- stack.bi.writebufs = -1;
- if (copy_to_user(user_data, &stack.bi, sizeof(stack.bi)))
- return -EFAULT;
- break;
- case DAHDI_SET_BUFINFO:
- if (copy_from_user(&stack.bi, user_data, sizeof(stack.bi)))
- return -EFAULT;
- if (stack.bi.bufsize > DAHDI_MAX_BLOCKSIZE)
- return -EINVAL;
- if (stack.bi.bufsize < 16)
- return -EINVAL;
- if (stack.bi.bufsize * stack.bi.numbufs > DAHDI_MAX_BUF_SPACE)
- return -EINVAL;
- /* It does not make sense to allow user mode to change the
- * receive buffering policy. DAHDI always provides received
- * buffers to upper layers immediately. Transmission is
- * different since we might want to allow the kernel to build
- * up a buffer in order to prevent underruns from the
- * interrupt context. */
- chan->txbufpolicy = stack.bi.txbufpolicy & 0x3;
- if ((rv = dahdi_reallocbufs(chan, stack.bi.bufsize, stack.bi.numbufs)))
- return (rv);
- break;
- case DAHDI_GET_BLOCKSIZE: /* get blocksize */
- /* return block size */
- put_user(chan->blocksize, (int __user *)data);
- break;
- case DAHDI_SET_BLOCKSIZE: /* set blocksize */
- get_user(j, (int __user *)data);
- /* cannot be larger than max amount */
- if (j > DAHDI_MAX_BLOCKSIZE) return(-EINVAL);
- /* cannot be less then 16 */
- if (j < 16) return(-EINVAL);
- /* allocate a single kernel buffer which we then
- sub divide into four pieces */
- if ((rv = dahdi_reallocbufs(chan, j, chan->numbufs)))
- return (rv);
- break;
- case DAHDI_FLUSH: /* flush input buffer, output buffer, and/or event queue */
- get_user(i, (int __user *)data); /* get param */
- spin_lock_irqsave(&chan->lock, flags);
- if (i & DAHDI_FLUSH_READ) /* if for read (input) */
- {
- /* initialize read buffers and pointers */
- chan->inreadbuf = 0;
- chan->outreadbuf = -1;
- for (j=0;j<chan->numbufs;j++) {
- /* Do we need this? */
- chan->readn[j] = 0;
- chan->readidx[j] = 0;
- }
- wake_up_interruptible(&chan->waitq); /* wake_up_interruptible waiting on read */
- }
- if (i & DAHDI_FLUSH_WRITE) /* if for write (output) */
- {
- /* initialize write buffers and pointers */
- chan->outwritebuf = -1;
- chan->inwritebuf = 0;
- for (j=0;j<chan->numbufs;j++) {
- /* Do we need this? */
- chan->writen[j] = 0;
- chan->writeidx[j] = 0;
- }
- wake_up_interruptible(&chan->waitq); /* wake_up_interruptible waiting on write */
- }
- if (i & DAHDI_FLUSH_EVENT) /* if for events */
- {
- /* initialize the event pointers */
- chan->eventinidx = chan->eventoutidx = 0;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- case DAHDI_SYNC: /* wait for no tx */
- for(;;) /* loop forever */
- {
- spin_lock_irqsave(&chan->lock, flags);
- /* Know if there is a write pending */
- i = (chan->outwritebuf > -1);
- spin_unlock_irqrestore(&chan->lock, flags);
- if (!i)
- break; /* skip if none */
- rv = wait_event_interruptible(chan->waitq,
- (!chan->file->private_data || chan->outwritebuf > -1));
- if (rv)
- return rv;
- if (unlikely(!chan->file->private_data))
- return -ENODEV;
- }
- break;
- case DAHDI_IOMUX: /* wait for something to happen */
- return dahdi_ioctl_iomux(file, data);
- case DAHDI_GETEVENT: /* Get event on queue */
- /* set up for no event */
- j = DAHDI_EVENT_NONE;
- spin_lock_irqsave(&chan->lock, flags);
- /* if some event in queue */
- if (chan->eventinidx != chan->eventoutidx)
- {
- j = chan->eventbuf[chan->eventoutidx++];
- /* get the data, bump index */
- /* if index overflow, set to beginning */
- if (chan->eventoutidx >= DAHDI_MAX_EVENTSIZE)
- chan->eventoutidx = 0;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- put_user(j, (int __user *)data);
- break;
- case DAHDI_CONFMUTE: /* set confmute flag */
- get_user(j, (int __user *)data); /* get conf # */
- if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL);
- spin_lock_irqsave(&chan_lock, flags);
- chan->confmute = j;
- spin_unlock_irqrestore(&chan_lock, flags);
- break;
- case DAHDI_GETCONFMUTE: /* get confmute flag */
- if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL);
- j = chan->confmute;
- put_user(j, (int __user *)data); /* get conf # */
- rv = 0;
- break;
- case DAHDI_SETTONEZONE:
- get_user(j, (int __user *) data);
- rv = set_tone_zone(chan, j);
- return rv;
- case DAHDI_GETTONEZONE:
- spin_lock_irqsave(&chan->lock, flags);
- j = (chan->curzone) ? chan->curzone->num : 0;
- spin_unlock_irqrestore(&chan->lock, flags);
- put_user(j, (int __user *) data);
- break;
- case DAHDI_SENDTONE:
- get_user(j, (int __user *)data);
- spin_lock_irqsave(&chan->lock, flags);
- rv = start_tone(chan, j);
- spin_unlock_irqrestore(&chan->lock, flags);
- return rv;
- case DAHDI_GETCONF_V1: /* intentional drop through */
- case DAHDI_GETCONF: /* get conf stuff */
- return dahdi_ioctl_getconf(file, data);
- case DAHDI_SETCONF_V1: /* Intentional fall through. */
- case DAHDI_SETCONF: /* set conf stuff */
- return dahdi_ioctl_setconf(file, data);
- case DAHDI_CONFDIAG_V1: /* Intentional fall-through */
- case DAHDI_CONFDIAG: /* output diagnostic info to console */
- return dahdi_ioctl_confdiag(file, data);
- case DAHDI_CHANNO: /* get channel number of stream */
- /* return channel number */
- put_user(chan->channo, (int __user *)data);
- break;
- case DAHDI_SETLAW:
- get_user(j, (int __user *)data);
- if ((j < 0) || (j > DAHDI_LAW_ALAW))
- return -EINVAL;
- dahdi_set_law(chan, j);
- break;
- case DAHDI_SETLINEAR:
- get_user(j, (int __user *)data);
- /* Makes no sense on non-audio channels */
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- if (j)
- chan->flags |= DAHDI_FLAG_LINEAR;
- else
- chan->flags &= ~DAHDI_FLAG_LINEAR;
- break;
- case DAHDI_SETCADENCE:
- if (data) {
- /* Use specific ring cadence */
- if (copy_from_user(&stack.cad, user_data,
- sizeof(stack.cad))) {
- return -EFAULT;
- }
- memcpy(chan->ringcadence, &stack.cad, sizeof(chan->ringcadence));
- chan->firstcadencepos = 0;
- /* Looking for negative ringing time indicating where to loop back into ringcadence */
- for (i=0; i<DAHDI_MAX_CADENCE; i+=2 ) {
- if (chan->ringcadence[i]<0) {
- chan->ringcadence[i] *= -1;
- chan->firstcadencepos = i;
- break;
- }
- }
- } else {
- /* Reset to default */
- chan->firstcadencepos = 0;
- if (chan->curzone) {
- memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence));
- /* Looking for negative ringing time indicating where to loop back into ringcadence */
- for (i=0; i<DAHDI_MAX_CADENCE; i+=2 ) {
- if (chan->ringcadence[i]<0) {
- chan->ringcadence[i] *= -1;
- chan->firstcadencepos = i;
- break;
- }
- }
- } else {
- memset(chan->ringcadence, 0, sizeof(chan->ringcadence));
- chan->ringcadence[0] = chan->starttime;
- chan->ringcadence[1] = DAHDI_RINGOFFTIME;
- }
- }
- break;
- default:
- /* Check for common ioctl's and private ones */
- rv = dahdi_common_ioctl(file, cmd, data);
- /* if no span, just return with value */
- if (!chan->span) return rv;
- if ((rv == -ENOTTY) && chan->span->ops->ioctl)
- rv = chan->span->ops->ioctl(chan, cmd, data);
- return rv;
- }
- return 0;
- }
- #ifdef CONFIG_DAHDI_PPP
- /*
- * This is called at softirq (BH) level when there are calls
- * we need to make to the ppp_generic layer. We do it this
- * way because the ppp_generic layer functions may not be called
- * at interrupt level.
- */
- static void do_ppp_calls(unsigned long data)
- {
- struct dahdi_chan *chan = (struct dahdi_chan *) data;
- struct sk_buff *skb;
- if (!chan->ppp)
- return;
- if (chan->do_ppp_wakeup) {
- chan->do_ppp_wakeup = 0;
- ppp_output_wakeup(chan->ppp);
- }
- while ((skb = skb_dequeue(&chan->ppp_rq)) != NULL)
- ppp_input(chan->ppp, skb);
- if (chan->do_ppp_error) {
- chan->do_ppp_error = 0;
- ppp_input_error(chan->ppp, 0);
- }
- }
- #endif
- static int
- ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
- const void __user *data)
- {
- struct dahdi_echocan_state *ec = NULL, *ec_state;
- const struct dahdi_echocan_factory *ec_current;
- struct dahdi_echocanparam *params;
- int ret = 0;
- unsigned long flags;
- if (ecp->param_count > DAHDI_MAX_ECHOCANPARAMS)
- return -E2BIG;
- if (ecp->tap_length == 0) {
- /* disable mode, don't need to inspect params */
- mutex_lock(&chan->mutex);
- spin_lock_irqsave(&chan->lock, flags);
- ec_state = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (ec_state) {
- ec_state->ops->echocan_free(chan, ec_state);
- release_echocan(ec_current);
- }
- mutex_unlock(&chan->mutex);
- return 0;
- }
- params = kmalloc(sizeof(params[0]) * DAHDI_MAX_ECHOCANPARAMS, GFP_KERNEL);
- if (!params)
- return -ENOMEM;
- /* enable mode, need the params */
- if (copy_from_user(params, data,
- sizeof(params[0]) * ecp->param_count)) {
- ret = -EFAULT;
- goto exit_with_free;
- }
- mutex_lock(&chan->mutex);
- /* free any echocan that may be on the channel already */
- spin_lock_irqsave(&chan->lock, flags);
- ec_state = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (ec_state) {
- ec_state->ops->echocan_free(chan, ec_state);
- release_echocan(ec_current);
- }
- switch (ecp->tap_length) {
- case 32:
- case 64:
- case 128:
- case 256:
- case 512:
- case 1024:
- break;
- default:
- ecp->tap_length = deftaps;
- }
- ec_current = NULL;
- if (chan->ec_factory) {
- /* try to get another reference to the module providing
- this channel's echo canceler */
- if (!try_module_get(chan->ec_factory->owner)) {
- module_printk(KERN_ERR, "Cannot get a reference to the"
- " '%s' echo canceler\n",
- chan->ec_factory->get_name(chan));
- goto exit_with_free;
- }
- /* got the reference, copy the pointer and use it for making
- an echo canceler instance if possible */
- ec_current = chan->ec_factory;
- ret = ec_current->echocan_create(chan, ecp, params, &ec);
- if (ret) {
- release_echocan(ec_current);
- goto exit_with_free;
- }
- if (!ec) {
- module_printk(KERN_ERR, "%s failed to allocate an " \
- "dahdi_echocan_state instance.\n",
- ec_current->get_name(chan));
- ret = -EFAULT;
- goto exit_with_free;
- }
- }
- if (ec) {
- spin_lock_irqsave(&chan->lock, flags);
- chan->ec_current = ec_current;
- chan->ec_state = ec;
- ec->status.mode = ECHO_MODE_ACTIVE;
- if (!ec->features.CED_tx_detect) {
- echo_can_disable_detector_init(&chan->ec_state->txecdis);
- }
- if (!ec->features.CED_rx_detect) {
- echo_can_disable_detector_init(&chan->ec_state->rxecdis);
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- exit_with_free:
- mutex_unlock(&chan->mutex);
- kfree(params);
- return ret;
- }
- static void set_echocan_fax_mode(struct dahdi_chan *chan, unsigned int channo, const char *reason, unsigned int enable)
- {
- if (enable) {
- if (!chan->ec_state)
- module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for channel %d with no echo canceller\n", reason, channo);
- else if (chan->ec_state->status.mode == ECHO_MODE_FAX)
- module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller already in FAX mode on channel %d\n", reason, channo);
- else if (chan->ec_state->status.mode != ECHO_MODE_ACTIVE)
- module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller not in active mode on channel %d\n", reason, channo);
- else if (chan->ec_state->features.NLP_automatic) {
- /* for echocans that automatically do the right thing, just
- * mark it as being in FAX mode without making any
- * changes, as none are necessary.
- */
- chan->ec_state->status.mode = ECHO_MODE_FAX;
- } else if (chan->ec_state->features.NLP_toggle) {
- module_printk(KERN_NOTICE, "Disabled echo canceller NLP because of %s on channel %d\n", reason, channo);
- dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED);
- chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 0);
- chan->ec_state->status.mode = ECHO_MODE_FAX;
- } else {
- module_printk(KERN_NOTICE, "Idled echo canceller because of %s on channel %d\n", reason, channo);
- chan->ec_state->status.mode = ECHO_MODE_IDLE;
- }
- } else {
- if (!chan->ec_state)
- module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for channel %d with no echo canceller\n", reason, channo);
- else if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
- module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller already in voice mode on channel %d\n", reason, channo);
- else if ((chan->ec_state->status.mode != ECHO_MODE_FAX) &&
- (chan->ec_state->status.mode != ECHO_MODE_IDLE))
- module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller not in FAX or idle mode on channel %d\n", reason, channo);
- else if (chan->ec_state->features.NLP_automatic) {
- /* for echocans that automatically do the right thing, just
- * mark it as being in active mode without making any
- * changes, as none are necessary.
- */
- chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
- } else if (chan->ec_state->features.NLP_toggle) {
- module_printk(KERN_NOTICE, "Enabled echo canceller NLP because of %s on channel %d\n", reason, channo);
- dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED);
- chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 1);
- chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
- } else {
- module_printk(KERN_NOTICE, "Activated echo canceller because of %s on channel %d\n", reason, channo);
- chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
- }
- }
- }
- static inline bool
- is_txstate(struct dahdi_chan *const chan, const int txstate)
- {
- bool ret;
- unsigned long flags;
- spin_lock_irqsave(&chan->lock, flags);
- ret = (txstate == chan->txstate);
- spin_unlock_irqrestore(&chan->lock, flags);
- return ret;
- }
- static int dahdi_chan_ioctl(struct file *file, unsigned int cmd, unsigned long data)
- {
- struct dahdi_chan *const chan = chan_from_file(file);
- unsigned long flags;
- int j;
- int ret;
- int oldconf;
- const void *rxgain = NULL;
- if (!chan)
- return -ENOSYS;
- WARN_ON(!chan->master);
- switch(cmd) {
- case DAHDI_SETSIGFREEZE:
- get_user(j, (int __user *)data);
- spin_lock_irqsave(&chan->lock, flags);
- if (j) {
- chan->flags |= DAHDI_FLAG_SIGFREEZE;
- } else {
- chan->flags &= ~DAHDI_FLAG_SIGFREEZE;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- case DAHDI_GETSIGFREEZE:
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->flags & DAHDI_FLAG_SIGFREEZE)
- j = 1;
- else
- j = 0;
- spin_unlock_irqrestore(&chan->lock, flags);
- put_user(j, (int __user *)data);
- break;
- case DAHDI_AUDIOMODE:
- /* Only literal clear channels can be put in */
- if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL);
- get_user(j, (int __user *)data);
- if (j) {
- spin_lock_irqsave(&chan->lock, flags);
- chan->flags |= DAHDI_FLAG_AUDIO;
- chan->flags &= ~(DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- spin_unlock_irqrestore(&chan->lock, flags);
- } else {
- /* Coming out of audio mode, also clear all
- conferencing and gain related info as well
- as echo canceller */
- struct dahdi_echocan_state *ec_state;
- const struct dahdi_echocan_factory *ec_current;
- spin_lock_irqsave(&chan->lock, flags);
- chan->flags &= ~DAHDI_FLAG_AUDIO;
- /* save old conf number, if any */
- oldconf = chan->confna;
- /* initialize conference variables */
- chan->_confn = 0;
- chan->confna = 0;
- chan->conf_chan = NULL;
- dahdi_disable_dacs(chan);
- chan->confmode = 0;
- chan->confmute = 0;
- memset(chan->conflast, 0, sizeof(chan->conflast));
- memset(chan->conflast1, 0, sizeof(chan->conflast1));
- memset(chan->conflast2, 0, sizeof(chan->conflast2));
- ec_state = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- /* release conference resource, if any to release */
- reset_conf(chan);
- if (is_gain_allocated(chan))
- rxgain = chan->rxgain;
- else
- rxgain = NULL;
- chan->rxgain = defgain;
- chan->txgain = defgain;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (ec_state) {
- ec_state->ops->echocan_free(chan, ec_state);
- release_echocan(ec_current);
- }
- if (rxgain)
- kfree(rxgain);
- if (oldconf) dahdi_check_conf(oldconf);
- }
- #ifdef DAHDI_AUDIO_NOTIFY
- if (chan->span->ops->audio_notify)
- chan->span->ops->audio_notify(chan, j);
- #endif
- break;
- case DAHDI_HDLCPPP:
- #ifdef CONFIG_DAHDI_PPP
- if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL);
- get_user(j, (int __user *)data);
- if (j) {
- if (!chan->ppp) {
- chan->ppp = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL);
- if (chan->ppp) {
- struct dahdi_echocan_state *tec;
- const struct dahdi_echocan_factory *ec_current;
- chan->ppp->private = chan;
- chan->ppp->ops = &ztppp_ops;
- chan->ppp->mtu = DAHDI_DEFAULT_MTU_MRU;
- chan->ppp->hdrlen = 0;
- skb_queue_head_init(&chan->ppp_rq);
- chan->do_ppp_wakeup = 0;
- tasklet_init(&chan->ppp_calls, do_ppp_calls,
- (unsigned long)chan);
- if ((ret = dahdi_reallocbufs(chan, DAHDI_DEFAULT_MTU_MRU, DAHDI_DEFAULT_NUM_BUFS))) {
- kfree(chan->ppp);
- chan->ppp = NULL;
- return ret;
- }
- if ((ret = ppp_register_channel(chan->ppp))) {
- kfree(chan->ppp);
- chan->ppp = NULL;
- return ret;
- }
- tec = chan->ec_state;
- chan->ec_state = NULL;
- ec_current = chan->ec_current;
- chan->ec_current = NULL;
- /* Make sure there's no gain */
- if (is_gain_allocated(chan))
- kfree(chan->rxgain);
- chan->rxgain = defgain;
- chan->txgain = defgain;
- chan->flags &= ~DAHDI_FLAG_AUDIO;
- chan->flags |= (DAHDI_FLAG_PPP | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- if (tec) {
- tec->ops->echocan_free(chan, tec);
- release_echocan(ec_current);
- }
- } else
- return -ENOMEM;
- }
- } else {
- chan->flags &= ~(DAHDI_FLAG_PPP | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- if (chan->ppp) {
- struct ppp_channel *ppp = chan->ppp;
- chan->ppp = NULL;
- tasklet_kill(&chan->ppp_calls);
- skb_queue_purge(&chan->ppp_rq);
- ppp_unregister_channel(ppp);
- kfree(ppp);
- }
- }
- #else
- module_printk(KERN_NOTICE, "PPP support not compiled in\n");
- return -ENOSYS;
- #endif
- break;
- case DAHDI_HDLCRAWMODE:
- if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL);
- get_user(j, (int __user *)data);
- chan->flags &= ~(DAHDI_FLAG_AUDIO | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- if (j) {
- chan->flags |= DAHDI_FLAG_HDLC;
- fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- }
- break;
- case DAHDI_HDLCFCSMODE:
- if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL);
- get_user(j, (int __user *)data);
- chan->flags &= ~(DAHDI_FLAG_AUDIO | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- if (j) {
- chan->flags |= DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS;
- fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- }
- break;
- case DAHDI_HDLC_RATE:
- get_user(j, (int __user *)data);
- if (j == 56) {
- chan->flags |= DAHDI_FLAG_HDLC56;
- } else {
- chan->flags &= ~DAHDI_FLAG_HDLC56;
- }
- fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64);
- break;
- case DAHDI_ECHOCANCEL_PARAMS:
- {
- struct dahdi_echocanparams ecp;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- ret = copy_from_user(&ecp,
- (struct dahdi_echocanparams __user *)data,
- sizeof(ecp));
- if (ret)
- return -EFAULT;
- data += sizeof(ecp);
- ret = ioctl_echocancel(chan, &ecp, (void __user *)data);
- if (ret)
- return ret;
- break;
- }
- case DAHDI_ECHOCANCEL:
- {
- struct dahdi_echocanparams ecp;
- if (!(chan->flags & DAHDI_FLAG_AUDIO))
- return -EINVAL;
- get_user(j, (int __user *) data);
- ecp.tap_length = j;
- ecp.param_count = 0;
- if ((ret = ioctl_echocancel(chan, &ecp, NULL)))
- return ret;
- break;
- }
- case DAHDI_ECHOTRAIN:
- /* get pre-training time from user */
- get_user(j, (int __user *)data);
- if ((j < 0) || (j >= DAHDI_MAX_PRETRAINING))
- return -EINVAL;
- j <<= 3;
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->ec_state) {
- /* Start pretraining stage */
- if (chan->ec_state->ops->echocan_traintap) {
- chan->ec_state->status.mode = ECHO_MODE_PRETRAINING;
- chan->ec_state->status.pretrain_timer = j;
- }
- spin_unlock_irqrestore(&chan->lock, flags);
- } else {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -EINVAL;
- }
- break;
- case DAHDI_ECHOCANCEL_FAX_MODE:
- if (!chan->ec_state) {
- return -EINVAL;
- } else {
- get_user(j, (int __user *) data);
- spin_lock_irqsave(&chan->lock, flags);
- set_echocan_fax_mode(chan, chan->channo, "ioctl", j ? 1 : 0);
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- break;
- case DAHDI_SETTXBITS:
- if (chan->sig != DAHDI_SIG_CAS)
- return -EINVAL;
- get_user(j, (int __user *)data);
- dahdi_cas_setbits(chan, j);
- break;
- case DAHDI_GETRXBITS:
- put_user(chan->rxsig, (int __user *)data);
- break;
- case DAHDI_LOOPBACK:
- get_user(j, (int __user *)data);
- spin_lock_irqsave(&chan->lock, flags);
- if (j)
- chan->flags |= DAHDI_FLAG_LOOPED;
- else
- chan->flags &= ~DAHDI_FLAG_LOOPED;
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- case DAHDI_HOOK:
- get_user(j, (int __user *)data);
- if (chan->flags & DAHDI_FLAG_CLEAR)
- return -EINVAL;
- if (chan->sig == DAHDI_SIG_CAS)
- return -EINVAL;
- /* if no span, just do nothing */
- if (!chan->span) return(0);
- spin_lock_irqsave(&chan->lock, flags);
- /* if dialing, stop it */
- chan->curtone = NULL;
- chan->dialing = 0;
- chan->txdialbuf[0] = '\0';
- chan->tonep = 0;
- chan->pdialcount = 0;
- spin_unlock_irqrestore(&chan->lock, flags);
- if (chan->span->flags & DAHDI_FLAG_RBS) {
- switch (j) {
- case DAHDI_ONHOOK:
- spin_lock_irqsave(&chan->lock, flags);
- dahdi_hangup(chan);
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- case DAHDI_OFFHOOK:
- spin_lock_irqsave(&chan->lock, flags);
- if ((chan->txstate == DAHDI_TXSTATE_KEWL) ||
- (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -EBUSY;
- }
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_DEBOUNCE, chan->debouncetime);
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- case DAHDI_RING:
- case DAHDI_START:
- spin_lock_irqsave(&chan->lock, flags);
- if (!chan->curzone) {
- spin_unlock_irqrestore(&chan->lock, flags);
- module_printk(KERN_WARNING, "Cannot start tone until a tone zone is loaded.\n");
- return -ENODATA;
- }
- if (chan->txstate != DAHDI_TXSTATE_ONHOOK) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -EBUSY;
- }
- if (chan->sig & __DAHDI_SIG_FXO) {
- ret = 0;
- chan->cadencepos = 0;
- ret = chan->ringcadence[0];
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_RINGON, ret);
- } else
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_START, chan->starttime);
- spin_unlock_irqrestore(&chan->lock, flags);
- if (file->f_flags & O_NONBLOCK)
- return -EINPROGRESS;
- break;
- case DAHDI_WINK:
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->txstate != DAHDI_TXSTATE_ONHOOK) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -EBUSY;
- }
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_PREWINK, chan->prewinktime);
- spin_unlock_irqrestore(&chan->lock, flags);
- if (file->f_flags & O_NONBLOCK)
- return -EINPROGRESS;
- wait_event_interruptible(chan->waitq,
- !chan->file->private_data || is_txstate(chan, DAHDI_TXSIG_ONHOOK));
- if (unlikely(!chan->file->private_data))
- return -ENODEV;
- if (signal_pending(current))
- return -ERESTARTSYS;
- break;
- case DAHDI_FLASH:
- spin_lock_irqsave(&chan->lock, flags);
- if (chan->txstate != DAHDI_TXSTATE_OFFHOOK) {
- spin_unlock_irqrestore(&chan->lock, flags);
- return -EBUSY;
- }
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_PREFLASH, chan->preflashtime);
- spin_unlock_irqrestore(&chan->lock, flags);
- if (file->f_flags & O_NONBLOCK)
- return -EINPROGRESS;
- wait_event_interruptible(chan->waitq,
- !chan->file->private_data || is_txstate(chan, DAHDI_TXSIG_OFFHOOK));
- if (unlikely(!chan->file->private_data))
- return -ENODEV;
- if (signal_pending(current))
- return -ERESTARTSYS;
- break;
- case DAHDI_RINGOFF:
- spin_lock_irqsave(&chan->lock, flags);
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0);
- spin_unlock_irqrestore(&chan->lock, flags);
- break;
- default:
- return -EINVAL;
- }
- } else if (chan->span->ops->sethook) {
- if (chan->txhooksig != j) {
- chan->txhooksig = j;
- chan->span->ops->sethook(chan, j);
- }
- } else
- return -ENOSYS;
- break;
- #ifdef CONFIG_DAHDI_PPP
- case PPPIOCGCHAN:
- if (chan->flags & DAHDI_FLAG_PPP) {
- return put_user(ppp_channel_index(chan->ppp),
- (int __user *)data) ? -EFAULT : 0;
- } else {
- return -EINVAL;
- }
- break;
- case PPPIOCGUNIT:
- if (chan->flags & DAHDI_FLAG_PPP) {
- return put_user(ppp_unit_number(chan->ppp),
- (int __user *)data) ? -EFAULT : 0;
- } else {
- return -EINVAL;
- }
- break;
- #endif
- case DAHDI_BUFFER_EVENTS:
- if (get_user(j, (int __user *)data))
- return -EFAULT;
- if (j)
- set_bit(DAHDI_FLAGBIT_BUFEVENTS, &chan->flags);
- else
- clear_bit(DAHDI_FLAGBIT_BUFEVENTS, &chan->flags);
- break;
- default:
- return dahdi_chanandpseudo_ioctl(file, cmd, data);
- }
- return 0;
- }
- static int dahdi_prechan_ioctl(struct file *file, unsigned int cmd, unsigned long data)
- {
- int channo;
- int res;
- if (file->private_data) {
- module_printk(KERN_NOTICE, "Huh? Prechan already has private data??\n");
- }
- switch(cmd) {
- case DAHDI_SPECIFY:
- get_user(channo, (int __user *)data);
- file->private_data = chan_from_num(channo);
- if (!file->private_data)
- return -EINVAL;
- res = dahdi_specchan_open(file);
- if (res)
- file->private_data = NULL;
- return res;
- default:
- return -ENOSYS;
- }
- return 0;
- }
- static long
- dahdi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long data)
- {
- int unit = UNIT(file);
- int ret;
- if (unit == DAHDI_CTL) {
- ret = dahdi_ctl_ioctl(file, cmd, data);
- goto exit;
- }
- if (unit == DAHDI_TRANSCODE) {
- /* dahdi_transcode should have updated the file_operations on
- * this file object on open, so we shouldn't be here. */
- WARN_ON(1);
- ret = -EFAULT;
- goto exit;
- }
- if (unit == DAHDI_TIMER) {
- /* The file operations for a timer device should have been
- * updated. */
- WARN_ON(1);
- ret = -EFAULT;
- goto exit;
- }
- if (unit == DAHDI_CHANNEL) {
- if (file->private_data)
- ret = dahdi_chan_ioctl(file, cmd, data);
- else
- ret = dahdi_prechan_ioctl(file, cmd, data);
- goto exit;
- }
- if (unit == DAHDI_PSEUDO) {
- if (!file->private_data) {
- module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n");
- ret = -EINVAL;
- goto exit;
- }
- ret = dahdi_chanandpseudo_ioctl(file, cmd, data);
- goto exit;
- }
- if (!file->private_data) {
- ret = -ENXIO;
- goto exit;
- }
- ret = dahdi_chan_ioctl(file, cmd, data);
- exit:
- return ret;
- }
- #ifndef HAVE_UNLOCKED_IOCTL
- static int dahdi_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long data)
- {
- return dahdi_unlocked_ioctl(file, cmd, data);
- }
- #endif
- #ifdef HAVE_COMPAT_IOCTL
- static long dahdi_ioctl_compat(struct file *file, unsigned int cmd,
- unsigned long data)
- {
- if (cmd == DAHDI_SFCONFIG)
- return -ENOTTY; /* Not supported yet */
- return dahdi_unlocked_ioctl(file, cmd, data);
- }
- #endif
- /**
- * _get_next_channo - Return the next taken channel number from the span list.
- * @span: The span with which to start the search.
- *
- * Returns -1 if there aren't any channels on span or any of the following
- * spans, otherwise, returns the channel number of the first channel.
- *
- * Must be callled with registration_mutex held.
- *
- */
- static unsigned int _get_next_channo(const struct dahdi_span *span)
- {
- const struct list_head *pos = &span->spans_node;
- while (pos != &span_list) {
- span = list_entry(pos, struct dahdi_span, spans_node);
- if (span->channels)
- return span->chans[0]->channo;
- pos = pos->next;
- }
- return -1;
- }
- static void
- set_spanno_and_basechan(struct dahdi_span *span, u32 spanno, u32 basechan)
- {
- int i;
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "set: spanno=%d, basechan=%d (span->channels=%d)\n",
- spanno, basechan, span->channels);
- span->spanno = spanno;
- for (i = 0; i < span->channels; ++i)
- span->chans[i]->channo = basechan + i;
- }
- /**
- * _assign_spanno_and_basechan - Assign next available span and channel numbers.
- *
- * This function will set span->spanno and channo for all the member channels.
- * It will assign the first available location.
- *
- * Must be called with registration_mutex held.
- *
- */
- static int _assign_spanno_and_basechan(struct dahdi_span *span)
- {
- struct dahdi_span *pos;
- unsigned int next_channo;
- unsigned int spanno = 1;
- unsigned int basechan = 1;
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "assign: channels=%d\n", span->channels);
- list_for_each_entry(pos, &span_list, spans_node) {
- if (pos->spanno <= spanno) {
- spanno = pos->spanno + 1;
- basechan = pos->chans[0]->channo + pos->channels;
- continue;
- }
- next_channo = _get_next_channo(pos);
- if ((basechan + span->channels) <= next_channo)
- break;
- /* We can't fit here, let's look at the next location. */
- spanno = pos->spanno + 1;
- if (pos->channels)
- basechan = pos->chans[0]->channo + pos->channels;
- }
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "good: spanno=%d, basechan=%d (span->channels=%d)\n",
- spanno, basechan, span->channels);
- set_spanno_and_basechan(span, spanno, basechan);
- return 0;
- }
- static inline struct dahdi_span *span_from_node(struct list_head *node)
- {
- return container_of(node, struct dahdi_span, spans_node);
- }
- /*
- * Call with registration_mutex held. Make sure all the spans are on the list
- * ordered by span.
- *
- */
- static void _dahdi_add_span_to_span_list(struct dahdi_span *span)
- {
- unsigned long flags;
- struct dahdi_span *pos;
- if (list_empty(&span_list)) {
- list_add_tail(&span->spans_node, &span_list);
- return;
- }
- list_for_each_entry(pos, &span_list, spans_node) {
- WARN_ON(0 == pos->spanno);
- if (pos->spanno > span->spanno)
- break;
- }
- spin_lock_irqsave(&chan_lock, flags);
- list_add(&span->spans_node, pos->spans_node.prev);
- spin_unlock_irqrestore(&chan_lock, flags);
- }
- /**
- * _check_spanno_and_basechan - Check if we can fit the new span in the requested location.
- *
- * Must be called with registration_mutex held.
- *
- */
- static int
- _check_spanno_and_basechan(struct dahdi_span *span, u32 spanno, u32 basechan)
- {
- struct dahdi_span *pos;
- unsigned int next_channo;
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "check: spanno=%d, basechan=%d (span->channels=%d)\n",
- spanno, basechan, span->channels);
- list_for_each_entry(pos, &span_list, spans_node) {
- next_channo = _get_next_channo(pos);
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "pos: spanno=%d channels=%d (next_channo=%d)\n",
- pos->spanno, pos->channels, next_channo);
- if (pos->spanno <= spanno) {
- if (basechan < next_channo + pos->channels) {
- /* Requested basechan breaks channel sorting */
- dev_notice(span_device(span),
- "[%d] basechan (%d) is too low for wanted span %d\n",
- local_spanno(span), basechan, spanno);
- return -EINVAL;
- }
- continue;
- }
- if (next_channo == -1)
- break;
- if ((basechan + span->channels) <= next_channo)
- break;
- /* Cannot fit the span into the requested location. Abort. */
- dev_notice(span_device(span),
- "cannot fit span %d (basechan=%d) into requested location\n",
- spanno, basechan);
- return -EINVAL;
- }
- dahdi_dev_dbg(ASSIGN, span_device(span),
- "good: spanno=%d, basechan=%d (span->channels=%d)\n",
- spanno, basechan, span->channels);
- set_spanno_and_basechan(span, spanno, basechan);
- return 0;
- }
- struct dahdi_device *dahdi_create_device(void)
- {
- struct dahdi_device *ddev;
- ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
- if (!ddev)
- return NULL;
- INIT_LIST_HEAD(&ddev->spans);
- dahdi_sysfs_init_device(ddev);
- return ddev;
- }
- EXPORT_SYMBOL(dahdi_create_device);
- void dahdi_free_device(struct dahdi_device *ddev)
- {
- put_device(&ddev->dev);
- }
- EXPORT_SYMBOL(dahdi_free_device);
- /**
- * __dahdi_init_span - Setup all the data structures for the span.
- * @span: The span of interest.
- *
- */
- static void __dahdi_init_span(struct dahdi_span *span)
- {
- int x;
- INIT_LIST_HEAD(&span->spans_node);
- spin_lock_init(&span->lock);
- clear_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags);
- if (!span->deflaw) {
- module_printk(KERN_NOTICE, "Span %s didn't specify default "
- "law. Assuming mulaw, please fix driver!\n",
- span->name);
- span->deflaw = DAHDI_LAW_MULAW;
- }
- if (span->spantype == SPANTYPE_INVALID) {
- module_printk(KERN_NOTICE,
- "Warning: Span %s didn't specify a spantype. "
- "Please fix driver!\n", span->name);
- }
- for (x = 0; x < span->channels; ++x) {
- span->chans[x]->span = span;
- __dahdi_init_chan(span->chans[x]);
- }
- }
- /**
- * dahdi_init_span - (Re)Initializes a dahdi span.
- * @span: The span to initialize.
- *
- * Reinitializing a device span might be necessary if a span has been changed
- * (channels added / removed) between when the dahdi_device it is on was first
- * registered and when the spans are actually assigned.
- *
- */
- void dahdi_init_span(struct dahdi_span *span)
- {
- mutex_lock(®istration_mutex);
- __dahdi_init_span(span);
- mutex_unlock(®istration_mutex);
- }
- EXPORT_SYMBOL(dahdi_init_span);
- /**
- * _dahdi_assign_span() - Assign a new DAHDI span
- * @span: the DAHDI span
- * @spanno: The span number we would like assigned. If 0, the first
- * available spanno/basechan will be used.
- * @basechan: The base channel number we would like. Ignored if spanno is 0.
- * @prefmaster: will the new span be preferred as a master_span?
- *
- * Assigns a span for usage with DAHDI. All the channel numbers in it will
- * have their numbering started at basechan.
- *
- * If prefmaster is set to anything > 0, span will attempt to become the
- * master DAHDI span at registration time. If 0: it will only become
- * master if no other span is currently the master (i.e.: it is the
- * first one).
- *
- * Must be called with registration_mutex held, and the span must have already
- * been initialized ith the __dahdi_init_span call.
- *
- */
- static int _dahdi_assign_span(struct dahdi_span *span, unsigned int spanno,
- unsigned int basechan, int prefmaster)
- {
- int res = 0;
- unsigned int x;
- unsigned long flags;
- if (!span || !span->ops || !span->ops->owner)
- return -EFAULT;
- if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) {
- dev_notice(span_device(span),
- "local span %d is already assigned span %d\n",
- local_spanno(span), span->spanno);
- return -EINVAL;
- }
- /* DAHDI_ALARM_NOTOPEN can be set when a span is disabled, i.e. via
- * sysfs, so when the span is being reassigned we should make sure it's
- * cleared. This eliminates the need for board drivers to re-report
- * their alarm states on span reassignment. */
- spin_lock_irqsave(&span->lock, flags);
- span->alarms &= ~DAHDI_ALARM_NOTOPEN;
- dahdi_alarm_notify(span);
- spin_unlock_irqrestore(&span->lock, flags);
- if (span->ops->enable_hw_preechocan ||
- span->ops->disable_hw_preechocan) {
- if ((NULL == span->ops->enable_hw_preechocan) ||
- (NULL == span->ops->disable_hw_preechocan)) {
- dev_notice(span_device(span),
- "span with inconsistent enable/disable hw_preechocan");
- return -EFAULT;
- }
- }
- if (!span->deflaw) {
- module_printk(KERN_NOTICE, "Span %s didn't specify default law. "
- "Assuming mulaw, please fix driver!\n", span->name);
- span->deflaw = DAHDI_LAW_MULAW;
- }
- /* Look through the span list to find the first available span number.
- * The spans are kept on this list in sorted order. We'll also save
- * off the next available channel number to use. */
- if (0 == spanno)
- res = _assign_spanno_and_basechan(span);
- else
- res = _check_spanno_and_basechan(span, spanno, basechan);
- if (res)
- return res;
- for (x = 0; x < span->channels; x++)
- dahdi_chan_reg(span->chans[x]);
- #ifdef CONFIG_PROC_FS
- {
- char tempfile[17];
- snprintf(tempfile, sizeof(tempfile), "%d", span->spanno);
- span->proc_entry = proc_create_data(tempfile, 0444,
- root_proc_entry, &dahdi_proc_ops,
- (void *)((unsigned long)span->spanno));
- if (!span->proc_entry) {
- res = -EFAULT;
- span_err(span, "Error creating procfs entry\n");
- goto cleanup;
- }
- }
- #endif
- res = span_sysfs_create(span);
- if (res)
- goto cleanup;
- if (debug & DEBUG_MAIN) {
- module_printk(KERN_NOTICE, "Registered Span %d ('%s') with "
- "%d channels\n", span->spanno, span->name, span->channels);
- }
- _dahdi_add_span_to_span_list(span);
- set_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags);
- if (span->ops->assigned)
- span->ops->assigned(span);
- __dahdi_find_master_span();
- return 0;
- cleanup:
- #ifdef CONFIG_PROC_FS
- if (span->proc_entry) {
- char tempfile[17];
- snprintf(tempfile, sizeof(tempfile), "dahdi/%d", span->spanno);
- remove_proc_entry(tempfile, NULL);
- span->proc_entry = NULL;
- }
- #endif
- for (x = 0; x < span->channels; x++) {
- struct dahdi_chan *chan = span->chans[x];
- if (test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags))
- dahdi_chan_unreg(chan);
- }
- return res;
- }
- int dahdi_assign_span(struct dahdi_span *span, unsigned int spanno,
- unsigned int basechan, int prefmaster)
- {
- int ret;
- mutex_lock(®istration_mutex);
- ret = _dahdi_assign_span(span, spanno, basechan, prefmaster);
- mutex_unlock(®istration_mutex);
- return ret;
- }
- int dahdi_assign_device_spans(struct dahdi_device *ddev)
- {
- struct dahdi_span *span;
- mutex_lock(®istration_mutex);
- list_for_each_entry(span, &ddev->spans, device_node)
- _dahdi_assign_span(span, 0, 0, 1);
- mutex_unlock(®istration_mutex);
- return 0;
- }
- static int auto_assign_spans = 1;
- static const char *UNKNOWN = "";
- /**
- * dahdi_auto_assign_spans - is the parameter auto_assign_spans set?
- */
- int dahdi_get_auto_assign_spans(void)
- {
- return auto_assign_spans;
- }
- EXPORT_SYMBOL(dahdi_get_auto_assign_spans);
- /**
- * _dahdi_register_device - Registers a DAHDI device and assign its spans.
- * @ddev: the DAHDI device
- *
- * If auto_assign_spans is 0, add the device to the device list and wait for
- * userspace to finish registration. Otherwise, go ahead and register the
- * spans in order as was done historically.
- *
- * Must hold registration_mutex when this function is called.
- *
- */
- static int _dahdi_register_device(struct dahdi_device *ddev,
- struct device *parent)
- {
- struct dahdi_span *s;
- int ret;
- ddev->manufacturer = (ddev->manufacturer) ?: UNKNOWN;
- ddev->location = (ddev->location) ?: UNKNOWN;
- ddev->devicetype = (ddev->devicetype) ?: UNKNOWN;
- list_for_each_entry(s, &ddev->spans, device_node) {
- s->parent = ddev;
- s->spanno = 0;
- __dahdi_init_span(s);
- }
- ktime_get_ts(&ddev->registration_time);
- ret = dahdi_sysfs_add_device(ddev, parent);
- if (ret)
- return ret;
- if (!auto_assign_spans)
- return 0;
- list_for_each_entry(s, &ddev->spans, device_node)
- ret = _dahdi_assign_span(s, 0, 0, 1);
- if (ret)
- dahdi_sysfs_unregister_device(ddev);
- return ret;
- }
- /**
- * dahdi_register_device() - unregister a new DAHDI device
- * @ddev: the DAHDI device
- *
- * Registers a device for usage with DAHDI.
- *
- */
- int dahdi_register_device(struct dahdi_device *ddev, struct device *parent)
- {
- int ret;
- if (!ddev)
- return -EINVAL;
- mutex_lock(®istration_mutex);
- ret = _dahdi_register_device(ddev, parent);
- mutex_unlock(®istration_mutex);
- return ret;
- }
- EXPORT_SYMBOL(dahdi_register_device);
- static void disable_span(struct dahdi_span *span)
- {
- int x;
- unsigned long flags;
- spin_lock_irqsave(&span->lock, flags);
- span->alarms = DAHDI_ALARM_NOTOPEN;
- for (x = 0; x < span->channels; x++) {
- /*
- * This event may not make it to user space before the channel
- * is gone, but let's try.
- */
- dahdi_qevent_lock(span->chans[x], DAHDI_EVENT_REMOVED);
- }
- dahdi_alarm_notify(span);
- spin_unlock_irqrestore(&span->lock, flags);
- module_printk(KERN_INFO, "%s: span %d\n", __func__, span->spanno);
- }
- #ifdef CONFIG_PROC_FS
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
- static inline void proc_remove(struct proc_dir_entry *proc_entry)
- {
- remove_proc_entry(proc_entry->name, root_proc_entry);
- }
- #endif
- #endif
- /**
- * _dahdi_unassign_span() - unassign a DAHDI span
- * @span: the DAHDI span
- *
- * Unassigns a span that has been previously assigned with
- * dahdi_assign_span().
- *
- * Must be called with the registration_mutex held.
- *
- */
- static int _dahdi_unassign_span(struct dahdi_span *span)
- {
- int res;
- int x;
- struct dahdi_span *new_master, *s;
- unsigned long flags;
- if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) {
- dev_info(span_device(span),
- "local span %d is already unassigned\n",
- local_spanno(span));
- return -EINVAL;
- }
- spin_lock_irqsave(&chan_lock, flags);
- list_del_init(&span->spans_node);
- spin_unlock_irqrestore(&chan_lock, flags);
- span->spanno = 0;
- clear_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags);
- res = dahdi_shutdown_span(span);
- if (res) {
- dev_err(span_device(span),
- "Failed to shutdown when unassigning.\n");
- }
- if (debug & DEBUG_MAIN)
- module_printk(KERN_NOTICE, "Unassigning Span '%s' with %d channels\n", span->name, span->channels);
- #ifdef CONFIG_PROC_FS
- if (span->proc_entry) {
- proc_remove(span->proc_entry);
- span->proc_entry = NULL;
- }
- #endif /* CONFIG_PROC_FS */
- span_sysfs_remove(span);
- for (x=0;x<span->channels;x++)
- dahdi_chan_unreg(span->chans[x]);
- new_master = master_span; /* FIXME: locking */
- if (master_span == span)
- new_master = NULL;
- spin_lock_irqsave(&chan_lock, flags);
- list_for_each_entry(s, &span_list, spans_node) {
- if ((s == new_master) || !can_provide_timing(s))
- continue;
- new_master = s;
- break;
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if (master_span != new_master) {
- if (debug & DEBUG_MAIN) {
- module_printk(KERN_NOTICE, "%s: Span ('%s') is new master\n", __FUNCTION__,
- (new_master)? new_master->name: "no master");
- }
- }
- master_span = new_master;
- return 0;
- }
- static int open_channel_count(const struct dahdi_span *span)
- {
- int i;
- int open_channels = 0;
- struct dahdi_chan *chan;
- for (i = 0; i < span->channels; ++i) {
- chan = span->chans[i];
- if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags))
- ++open_channels;
- }
- return open_channels;
- }
- int dahdi_unassign_span(struct dahdi_span *span)
- {
- int ret;
- module_printk(KERN_NOTICE, "%s: %s\n", __func__, span->name);
- disable_span(span);
- if (open_channel_count(span) > 0)
- msleep(1000); /* Give user space a chance to read this */
- mutex_lock(®istration_mutex);
- ret = _dahdi_unassign_span(span);
- mutex_unlock(®istration_mutex);
- return ret;
- }
- /**
- * dahdi_unregister_device() - unregister a DAHDI device
- * @span: the DAHDI span
- *
- * Unregisters a device that has been previously registered with
- * dahdi_register_device().
- *
- */
- void dahdi_unregister_device(struct dahdi_device *ddev)
- {
- struct dahdi_span *s;
- struct dahdi_span *next;
- unsigned int spans_with_open_channels = 0;
- WARN_ON(!ddev);
- might_sleep();
- if (unlikely(!ddev))
- return;
- list_for_each_entry_safe(s, next, &ddev->spans, device_node) {
- disable_span(s);
- if (open_channel_count(s) > 0)
- ++spans_with_open_channels;
- }
- if (spans_with_open_channels > 0)
- msleep(1000); /* give user space a chance to read this */
- mutex_lock(®istration_mutex);
- list_for_each_entry_safe(s, next, &ddev->spans, device_node) {
- _dahdi_unassign_span(s);
- list_del_init(&s->device_node);
- }
- mutex_unlock(®istration_mutex);
- dahdi_sysfs_unregister_device(ddev);
- if (UNKNOWN == ddev->location)
- ddev->location = NULL;
- if (UNKNOWN == ddev->manufacturer)
- ddev->manufacturer = NULL;
- if (UNKNOWN == ddev->devicetype)
- ddev->devicetype = NULL;
- }
- EXPORT_SYMBOL(dahdi_unregister_device);
- /*
- ** This routine converts from linear to ulaw
- **
- ** Craig Reese: IDA/Supercomputing Research Center
- ** Joe Campbell: Department of Defense
- ** 29 September 1989
- **
- ** References:
- ** 1) CCITT Recommendation G.711 (very difficult to follow)
- ** 2) "A New Digital Technique for Implementation of Any
- ** Continuous PCM Companding Law," Villeret, Michel,
- ** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
- ** 1973, pg. 11.12-11.17
- ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
- ** for Analog-to_Digital Conversion Techniques,"
- ** 17 February 1987
- **
- ** Input: Signed 16 bit linear sample
- ** Output: 8 bit ulaw sample
- */
- #define ZEROTRAP /* turn on the trap as per the MIL-STD */
- #define BIAS 0x84 /* define the add-in bias for 16 bit samples */
- #define CLIP 32635
- #ifdef CONFIG_CALC_XLAW
- unsigned char
- #else
- static unsigned char __init
- #endif
- __dahdi_lineartoulaw(short sample)
- {
- static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
- 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
- 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
- 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
- int sign, exponent, mantissa;
- unsigned char ulawbyte;
- /* Get the sample into sign-magnitude. */
- sign = (sample >> 8) & 0x80; /* set aside the sign */
- if (sign != 0) sample = -sample; /* get magnitude */
- if (sample > CLIP) sample = CLIP; /* clip the magnitude */
- /* Convert from 16 bit linear to ulaw. */
- sample = sample + BIAS;
- exponent = exp_lut[(sample >> 7) & 0xFF];
- mantissa = (sample >> (exponent + 3)) & 0x0F;
- ulawbyte = ~(sign | (exponent << 4) | mantissa);
- #ifdef ZEROTRAP
- if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */
- #endif
- if (ulawbyte == 0xff) ulawbyte = 0x7f; /* never return 0xff */
- return(ulawbyte);
- }
- #define AMI_MASK 0x55
- #ifdef CONFIG_CALC_XLAW
- unsigned char
- #else
- static inline unsigned char __init
- #endif
- __dahdi_lineartoalaw (short linear)
- {
- int mask;
- int seg;
- int pcm_val;
- static int seg_end[8] =
- {
- 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
- };
- pcm_val = linear;
- if (pcm_val >= 0)
- {
- /* Sign (7th) bit = 1 */
- mask = AMI_MASK | 0x80;
- }
- else
- {
- /* Sign bit = 0 */
- mask = AMI_MASK;
- pcm_val = -pcm_val;
- }
- /* Convert the scaled magnitude to segment number. */
- for (seg = 0; seg < 8; seg++)
- {
- if (pcm_val <= seg_end[seg])
- break;
- }
- /* Combine the sign, segment, and quantization bits. */
- return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
- }
- /*- End of function --------------------------------------------------------*/
- static inline short int __init alaw2linear (uint8_t alaw)
- {
- int i;
- int seg;
- alaw ^= AMI_MASK;
- i = ((alaw & 0x0F) << 4);
- seg = (((int) alaw & 0x70) >> 4);
- if (seg)
- i = (i + 0x100) << (seg - 1);
- return (short int) ((alaw & 0x80) ? i : -i);
- }
- /*- End of function --------------------------------------------------------*/
- static void __init dahdi_conv_init(void)
- {
- int i;
- /*
- * Set up mu-law conversion table
- */
- for(i = 0;i < 256;i++)
- {
- short mu,e,f,y;
- static short etab[]={0,132,396,924,1980,4092,8316,16764};
- mu = 255-i;
- e = (mu & 0x70)/16;
- f = mu & 0x0f;
- y = f * (1 << (e + 3));
- y += etab[e];
- if (mu & 0x80) y = -y;
- __dahdi_mulaw[i] = y;
- __dahdi_alaw[i] = alaw2linear(i);
- /* Default (0.0 db) gain table */
- defgain[i] = i;
- }
- #ifndef CONFIG_CALC_XLAW
- /* set up the reverse (mu-law) conversion table */
- for(i = -32768; i < 32768; i += 4)
- {
- __dahdi_lin2mu[((unsigned short)(short)i) >> 2] = __dahdi_lineartoulaw(i);
- __dahdi_lin2a[((unsigned short)(short)i) >> 2] = __dahdi_lineartoalaw(i);
- }
- #endif
- }
- static inline void __dahdi_process_getaudio_chunk(struct dahdi_chan *ss, unsigned char *txb)
- {
- /* We transmit data from our master channel */
- /* Called with ss->lock held */
- struct dahdi_chan *ms = ss->master;
- /* Linear representation */
- short getlin[DAHDI_CHUNKSIZE], k[DAHDI_CHUNKSIZE];
- int x;
- /* Okay, now we've got something to transmit */
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- getlin[x] = DAHDI_XLAW(txb[x], ms);
- #ifndef CONFIG_DAHDI_NO_ECHOCAN_DISABLE
- if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_tx_detect) {
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- if (echo_can_disable_detector_update(&ms->ec_state->txecdis, getlin[x])) {
- set_echocan_fax_mode(ms, ss->channo, "CED tx detected", 1);
- dahdi_qevent_nolock(ms, DAHDI_EVENT_TX_CED_DETECTED);
- break;
- }
- }
- }
- #endif
- if ((!ms->confmute && !ms->dialing) || (is_pseudo_chan(ms))) {
- struct dahdi_chan *const conf_chan = ms->conf_chan;
- /* Handle conferencing on non-clear channel and non-HDLC channels */
- switch(ms->confmode & DAHDI_CONF_MODE_MASK) {
- case DAHDI_CONF_NORMAL:
- /* Do nuffin */
- break;
- case DAHDI_CONF_MONITOR: /* Monitor a channel's rx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- /* Add monitored channel */
- if (is_pseudo_chan(conf_chan))
- ACSS(getlin, conf_chan->getlin);
- else
- ACSS(getlin, conf_chan->putlin);
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_MONITORTX: /* Monitor a channel's tx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- /* Add monitored channel */
- if (is_pseudo_chan(conf_chan))
- ACSS(getlin, conf_chan->putlin);
- else
- ACSS(getlin, conf_chan->getlin);
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_MONITORBOTH: /* monitor a channel's rx and tx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- ACSS(getlin, conf_chan->putlin);
- ACSS(getlin, conf_chan->getlin);
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_MONITOR_RX_PREECHO: /* Monitor a channel's rx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- /* Add monitored channel */
- ACSS(getlin, is_pseudo_chan(conf_chan) ?
- conf_chan->readchunkpreec : conf_chan->putlin);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- /* Add monitored channel */
- ACSS(getlin, is_pseudo_chan(conf_chan) ?
- conf_chan->putlin : conf_chan->readchunkpreec);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_MONITORBOTH_PREECHO: /* monitor a channel's rx and tx mode */
- /* if a pseudo-channel, ignore */
- if (is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- ACSS(getlin, conf_chan->putlin);
- ACSS(getlin, conf_chan->readchunkpreec);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_REALANDPSEUDO:
- /* This strange mode takes the transmit buffer and
- puts it on the conference, minus its last sample,
- then outputs from the conference minus the
- real channel's last sample. */
- /* if to talk on conf */
- if (ms->confmode & DAHDI_CONF_PSEUDO_TALKER) {
- /* Store temp value */
- memcpy(k, getlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* Add conf value */
- ACSS(k, conf_sums_next[ms->_confn]);
- /* save last one */
- memcpy(ms->conflast2, ms->conflast1, DAHDI_CHUNKSIZE * sizeof(short));
- memcpy(ms->conflast1, k, DAHDI_CHUNKSIZE * sizeof(short));
- /* get amount actually added */
- SCSS(ms->conflast1, conf_sums_next[ms->_confn]);
- /* Really add in new value */
- ACSS(conf_sums_next[ms->_confn], ms->conflast1);
- } else {
- memset(ms->conflast1, 0, DAHDI_CHUNKSIZE * sizeof(short));
- memset(ms->conflast2, 0, DAHDI_CHUNKSIZE * sizeof(short));
- }
- memset(getlin, 0, DAHDI_CHUNKSIZE * sizeof(short));
- txb[0] = DAHDI_LIN2X(0, ms);
- memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1);
- /* fall through to normal conf mode */
- case DAHDI_CONF_CONF: /* Normal conference mode */
- if (is_pseudo_chan(ms)) /* if pseudo-channel */
- {
- /* if to talk on conf */
- if (ms->confmode & DAHDI_CONF_TALKER) {
- /* Store temp value */
- memcpy(k, getlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* Add conf value */
- ACSS(k, conf_sums[ms->_confn]);
- /* get amount actually added */
- memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short));
- SCSS(ms->conflast, conf_sums[ms->_confn]);
- /* Really add in new value */
- ACSS(conf_sums[ms->_confn], ms->conflast);
- memcpy(ms->getlin, getlin, DAHDI_CHUNKSIZE * sizeof(short));
- } else {
- memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short));
- memcpy(getlin, ms->getlin, DAHDI_CHUNKSIZE * sizeof(short));
- }
- txb[0] = DAHDI_LIN2X(0, ms);
- memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1);
- break;
- }
- /* fall through */
- case DAHDI_CONF_CONFMON: /* Conference monitor mode */
- if (ms->confmode & DAHDI_CONF_LISTENER) {
- /* Subtract out last sample written to conf */
- SCSS(getlin, ms->conflast);
- /* Add in conference */
- ACSS(getlin, conf_sums[ms->_confn]);
- }
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_CONFANN:
- case DAHDI_CONF_CONFANNMON:
- /* First, add tx buffer to conf */
- ACSS(conf_sums_next[ms->_confn], getlin);
- /* Start with silence */
- memset(getlin, 0, DAHDI_CHUNKSIZE * sizeof(short));
- /* If a listener on the conf... */
- if (ms->confmode & DAHDI_CONF_LISTENER) {
- /* Subtract last value written */
- SCSS(getlin, ms->conflast);
- /* Add in conf */
- ACSS(getlin, conf_sums[ms->_confn]);
- }
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- break;
- case DAHDI_CONF_DIGITALMON:
- /* Real digital monitoring, but still echo cancel if
- * desired */
- if (!conf_chan)
- break;
- if (is_pseudo_chan(conf_chan)) {
- if (ms->ec_state) {
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- txb[x] = DAHDI_LIN2X(conf_chan->getlin[x], ms);
- } else {
- memcpy(txb, conf_chan->getraw, DAHDI_CHUNKSIZE);
- }
- } else {
- if (ms->ec_state) {
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- txb[x] = DAHDI_LIN2X(conf_chan->putlin[x], ms);
- } else {
- memcpy(txb, conf_chan->putraw,
- DAHDI_CHUNKSIZE);
- }
- }
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- getlin[x] = DAHDI_XLAW(txb[x], ms);
- break;
- }
- }
- if (ms->confmute || (ms->ec_state && (ms->ec_state->status.mode) & __ECHO_MODE_MUTE)) {
- txb[0] = DAHDI_LIN2X(0, ms);
- memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1);
- if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_STARTTRAINING)) {
- /* Transmit impulse now */
- txb[0] = DAHDI_LIN2X(16384, ms);
- ms->ec_state->status.mode = ECHO_MODE_AWAITINGECHO;
- }
- }
- /* save value from current */
- memcpy(ms->getlin, getlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* save value from current */
- memcpy(ms->getraw, txb, DAHDI_CHUNKSIZE);
- /* if to make tx tone */
- if (ms->v1_1 || ms->v2_1 || ms->v3_1)
- {
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- {
- getlin[x] += dahdi_txtone_nextsample(ms);
- txb[x] = DAHDI_LIN2X(getlin[x], ms);
- }
- }
- /* This is what to send (after having applied gain) */
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- txb[x] = ms->txgain[txb[x]];
- }
- static void __putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb,
- int bytes);
- static inline void __dahdi_getbuf_chunk(struct dahdi_chan *ss, unsigned char *txb)
- {
- #ifdef CONFIG_DAHDI_MIRROR
- unsigned char *orig_txb = txb;
- #endif /* CONFIG_DAHDI_MIRROR */
- /* Called with ss->lock held */
- /* We transmit data from our master channel */
- struct dahdi_chan *ms = ss->master;
- /* Buffer we're using */
- unsigned char *buf;
- /* Old buffer number */
- int oldbuf;
- /* Linear representation */
- int getlin;
- /* How many bytes we need to process */
- int bytes = DAHDI_CHUNKSIZE, left;
- bool needtxunderrun = false;
- int x;
- /* Let's pick something to transmit. First source to
- try is our write-out buffer. Always check it first because
- its our 'fast path' for whatever that's worth. */
- while(bytes) {
- if ((ms->outwritebuf > -1) && !ms->txdisable) {
- buf= ms->writebuf[ms->outwritebuf];
- left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf];
- if (left > bytes)
- left = bytes;
- if (ms->flags & DAHDI_FLAG_HDLC) {
- /* If this is an HDLC channel we only send a byte of
- HDLC. */
- for(x=0;x<left;x++) {
- if (fasthdlc_tx_need_data(&ms->txhdlc))
- /* Load a byte of data only if needed */
- fasthdlc_tx_load_nocheck(&ms->txhdlc, buf[ms->writeidx[ms->outwritebuf]++]);
- *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc);
- }
- bytes -= left;
- } else {
- memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left);
- ms->writeidx[ms->outwritebuf]+=left;
- txb += left;
- bytes -= left;
- }
- /* Check buffer status */
- if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) {
- /* We've reached the end of our buffer. Go to the next. */
- oldbuf = ms->outwritebuf;
- /* Clear out write index and such */
- ms->writeidx[oldbuf] = 0;
- ms->outwritebuf = (ms->outwritebuf + 1) % ms->numbufs;
- if (!(ms->flags & DAHDI_FLAG_MTP2)) {
- ms->writen[oldbuf] = 0;
- if (ms->outwritebuf == ms->inwritebuf) {
- /* Whoopsies, we're run out of buffers. Mark ours
- as -1 and wait for the filler to notify us that
- there is something to write */
- ms->outwritebuf = -1;
- if (ms->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY))
- wake_up_interruptible(&ms->waitq);
- /* If we're only supposed to start when full, disable the transmitter */
- if ((ms->txbufpolicy == DAHDI_POLICY_WHEN_FULL) ||
- (ms->txbufpolicy == DAHDI_POLICY_HALF_FULL))
- ms->txdisable = 1;
- }
- } else {
- if (ms->outwritebuf == ms->inwritebuf) {
- ms->outwritebuf = oldbuf;
- if (ms->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY))
- wake_up_interruptible(&ms->waitq);
- /* If we're only supposed to start when full, disable the transmitter */
- if ((ms->txbufpolicy == DAHDI_POLICY_WHEN_FULL) ||
- (ms->txbufpolicy == DAHDI_POLICY_HALF_FULL))
- ms->txdisable = 1;
- }
- }
- if (ms->inwritebuf < 0) {
- /* The filler doesn't have a place to put data. Now
- that we're done with this buffer, notify them. */
- ms->inwritebuf = oldbuf;
- }
- /* In the very orignal driver, it was quite well known to me (Jim) that there
- was a possibility that a channel sleeping on a write block needed to
- be potentially woken up EVERY time a buffer was emptied, not just on the first
- one, because if only done on the first one there is a slight timing potential
- of missing the wakeup (between where it senses the (lack of) active condition
- (with interrupts disabled) and where it does the sleep (interrupts enabled)
- in the read or iomux call, etc). That is why the write and iomux calls start
- with an infinite loop that gets broken out of upon an active condition,
- otherwise keeps sleeping and looking. The part in this code got "optimized"
- out in the later versions, and is put back now. */
- if (!(ms->flags & DAHDI_FLAG_PPP) ||
- !dahdi_have_netdev(ms)) {
- wake_up_interruptible(&ms->waitq);
- }
- /* Transmit a flag if this is an HDLC channel */
- if (ms->flags & DAHDI_FLAG_HDLC)
- fasthdlc_tx_frame_nocheck(&ms->txhdlc);
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(ms))
- netif_wake_queue(chan_to_netdev(ms));
- #endif
- #ifdef CONFIG_DAHDI_PPP
- if (ms->flags & DAHDI_FLAG_PPP) {
- ms->do_ppp_wakeup = 1;
- tasklet_schedule(&ms->ppp_calls);
- }
- #endif
- }
- } else if (ms->curtone && !is_pseudo_chan(ms)) {
- left = ms->curtone->tonesamples - ms->tonep;
- if (left > bytes)
- left = bytes;
- for (x=0;x<left;x++) {
- /* Pick our default value from the next sample of the current tone */
- getlin = dahdi_tone_nextsample(&ms->ts, ms->curtone);
- *(txb++) = DAHDI_LIN2X(getlin, ms);
- }
- ms->tonep+=left;
- bytes -= left;
- if (ms->tonep >= ms->curtone->tonesamples) {
- struct dahdi_tone *last;
- /* Go to the next sample of the tone */
- ms->tonep = 0;
- last = ms->curtone;
- ms->curtone = ms->curtone->next;
- if (!ms->curtone) {
- /* No more tones... Is this dtmf or mf? If so, go to the next digit */
- if (ms->dialing)
- __do_dtmf(ms);
- } else {
- if (last != ms->curtone)
- dahdi_init_tone_state(&ms->ts, ms->curtone);
- }
- }
- } else if (ms->flags & DAHDI_FLAG_LOOPED) {
- for (x = 0; x < bytes; x++)
- txb[x] = ms->readchunk[x];
- bytes = 0;
- } else if (ms->flags & DAHDI_FLAG_HDLC) {
- for (x=0;x<bytes;x++) {
- /* Okay, if we're HDLC, then transmit a flag by default */
- if (fasthdlc_tx_need_data(&ms->txhdlc))
- fasthdlc_tx_frame_nocheck(&ms->txhdlc);
- *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc);
- }
- bytes = 0;
- } else if (ms->flags & DAHDI_FLAG_CLEAR) {
- /* Clear channels that are idle in audio mode need
- to send silence; in non-audio mode, always send 0xff
- so stupid switches won't consider the channel active
- */
- if (ms->flags & DAHDI_FLAG_AUDIO) {
- memset(txb, DAHDI_LIN2X(0, ms), bytes);
- } else {
- memset(txb, 0xFF, bytes);
- }
- needtxunderrun += bytes;
- bytes = 0;
- } else {
- memset(txb, DAHDI_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */
- needtxunderrun += bytes;
- bytes = 0;
- }
- }
- if (needtxunderrun) {
- if (!test_bit(DAHDI_FLAGBIT_TXUNDERRUN, &ms->flags)) {
- if (test_bit(DAHDI_FLAGBIT_BUFEVENTS, &ms->flags))
- __qevent(ms, DAHDI_EVENT_WRITE_UNDERRUN);
- set_bit(DAHDI_FLAGBIT_TXUNDERRUN, &ms->flags);
- }
- } else {
- clear_bit(DAHDI_FLAGBIT_TXUNDERRUN, &ms->flags);
- }
- #ifdef CONFIG_DAHDI_MIRROR
- if (ss->txmirror) {
- spin_lock(&ss->txmirror->lock);
- __putbuf_chunk(ss->txmirror, orig_txb, DAHDI_CHUNKSIZE);
- spin_unlock(&ss->txmirror->lock);
- }
- #endif /* CONFIG_DAHDI_MIRROR */
- }
- static inline void rbs_itimer_expire(struct dahdi_chan *chan)
- {
- /* the only way this could have gotten here, is if a channel
- went onf hook longer then the wink or flash detect timeout */
- /* Called with chan->lock held */
- switch(chan->sig)
- {
- case DAHDI_SIG_FXOLS: /* if FXO, its definitely on hook */
- case DAHDI_SIG_FXOGS:
- case DAHDI_SIG_FXOKS:
- __qevent(chan,DAHDI_EVENT_ONHOOK);
- chan->gotgs = 0;
- break;
- #if defined(EMFLASH) || defined(EMPULSE)
- case DAHDI_SIG_EM:
- case DAHDI_SIG_EM_E1:
- if (chan->rxhooksig == DAHDI_RXSIG_ONHOOK) {
- __qevent(chan,DAHDI_EVENT_ONHOOK);
- break;
- }
- __qevent(chan,DAHDI_EVENT_RINGOFFHOOK);
- break;
- #endif
- #ifdef FXSFLASH
- case DAHDI_SIG_FXSKS:
- if (chan->rxhooksig == DAHDI_RXSIG_ONHOOK) {
- __qevent(chan, DAHDI_EVENT_ONHOOK);
- break;
- }
- #endif
- /* fall thru intentionally */
- default: /* otherwise, its definitely off hook */
- __qevent(chan,DAHDI_EVENT_RINGOFFHOOK);
- break;
- }
- }
- static inline void __rbs_otimer_expire(struct dahdi_chan *chan)
- {
- int len = 0;
- /* Called with chan->lock held */
- chan->otimer = 0;
- /* Move to the next timer state */
- switch(chan->txstate) {
- case DAHDI_TXSTATE_RINGOFF:
- /* Turn on the ringer now that the silent time has passed */
- ++chan->cadencepos;
- if (chan->cadencepos >= DAHDI_MAX_CADENCE)
- chan->cadencepos = chan->firstcadencepos;
- len = chan->ringcadence[chan->cadencepos];
- if (!len) {
- chan->cadencepos = chan->firstcadencepos;
- len = chan->ringcadence[chan->cadencepos];
- }
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_RINGON, len);
- __qevent(chan, DAHDI_EVENT_RINGERON);
- break;
- case DAHDI_TXSTATE_RINGON:
- /* Turn off the ringer now that the loud time has passed */
- ++chan->cadencepos;
- if (chan->cadencepos >= DAHDI_MAX_CADENCE)
- chan->cadencepos = 0;
- len = chan->ringcadence[chan->cadencepos];
- if (!len) {
- chan->cadencepos = 0;
- len = chan->curzone->ringcadence[chan->cadencepos];
- }
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_RINGOFF, len);
- __qevent(chan, DAHDI_EVENT_RINGEROFF);
- break;
- case DAHDI_TXSTATE_START:
- /* If we were starting, go off hook now ready to debounce */
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_AFTERSTART, DAHDI_AFTERSTART_TIME);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_PREWINK:
- /* Actually wink */
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_WINK, chan->winktime);
- break;
- case DAHDI_TXSTATE_WINK:
- /* Wink complete, go on hook and stabalize */
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0);
- if (chan->file && (chan->file->f_flags & O_NONBLOCK))
- __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_PREFLASH:
- /* Actually flash */
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_FLASH, chan->flashtime);
- break;
- case DAHDI_TXSTATE_FLASH:
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0);
- if (chan->file && (chan->file->f_flags & O_NONBLOCK))
- __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_DEBOUNCE:
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0);
- /* See if we've gone back on hook */
- if ((chan->rxhooksig == DAHDI_RXSIG_ONHOOK) && (chan->rxflashtime > 2))
- chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE;
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_AFTERSTART:
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0);
- if (chan->file && (chan->file->f_flags & O_NONBLOCK))
- __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_KEWL:
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_AFTERKEWL, DAHDI_AFTERKEWLTIME);
- if (chan->file && (chan->file->f_flags & O_NONBLOCK))
- __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_AFTERKEWL:
- if (chan->kewlonhook) {
- __qevent(chan,DAHDI_EVENT_ONHOOK);
- }
- chan->txstate = DAHDI_TXSTATE_ONHOOK;
- chan->gotgs = 0;
- break;
- case DAHDI_TXSTATE_PULSEBREAK:
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_PULSEMAKE,
- chan->pulsemaketime);
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_PULSEMAKE:
- if (chan->pdialcount)
- chan->pdialcount--;
- if (chan->pdialcount)
- {
- dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK,
- DAHDI_TXSTATE_PULSEBREAK, chan->pulsebreaktime);
- break;
- }
- chan->txstate = DAHDI_TXSTATE_PULSEAFTER;
- chan->otimer = chan->pulseaftertime * DAHDI_CHUNKSIZE;
- wake_up_interruptible(&chan->waitq);
- break;
- case DAHDI_TXSTATE_PULSEAFTER:
- chan->txstate = DAHDI_TXSTATE_OFFHOOK;
- __do_dtmf(chan);
- wake_up_interruptible(&chan->waitq);
- break;
- default:
- break;
- }
- }
- static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig)
- {
- /* State machines for receive hookstate transitions
- called with chan->lock held */
- if ((chan->rxhooksig) == rxsig) return;
- if ((chan->flags & DAHDI_FLAG_SIGFREEZE)) return;
- chan->rxhooksig = rxsig;
- #ifdef RINGBEGIN
- if ((chan->sig & __DAHDI_SIG_FXS) && (rxsig == DAHDI_RXSIG_RING) &&
- (!chan->ringdebtimer))
- __qevent(chan,DAHDI_EVENT_RINGBEGIN);
- #endif
- switch(chan->sig) {
- case DAHDI_SIG_EM: /* E and M */
- case DAHDI_SIG_EM_E1:
- switch(rxsig) {
- case DAHDI_RXSIG_OFFHOOK: /* went off hook */
- /* The interface is going off hook */
- #ifdef EMFLASH
- if (chan->itimer)
- {
- __qevent(chan,DAHDI_EVENT_WINKFLASH);
- chan->itimerset = chan->itimer = 0;
- break;
- }
- #endif
- #ifdef EMPULSE
- if (chan->itimer) /* if timer still running */
- {
- int plen = chan->itimerset - chan->itimer;
- if (plen <= DAHDI_MAXPULSETIME)
- {
- if (plen >= DAHDI_MINPULSETIME)
- {
- chan->pulsecount++;
- chan->pulsetimer = DAHDI_PULSETIMEOUT;
- chan->itimerset = chan->itimer = 0;
- if (chan->pulsecount == 1)
- __qevent(chan,DAHDI_EVENT_PULSE_START);
- }
- }
- break;
- }
- #endif
- /* set wink timer */
- chan->itimerset = chan->itimer = chan->rxwinktime * DAHDI_CHUNKSIZE;
- break;
- case DAHDI_RXSIG_ONHOOK: /* went on hook */
- /* This interface is now going on hook.
- Check for WINK, etc */
- if (chan->itimer)
- __qevent(chan,DAHDI_EVENT_WINKFLASH);
- #if defined(EMFLASH) || defined(EMPULSE)
- else {
- #ifdef EMFLASH
- chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE;
- #else /* EMFLASH */
- chan->itimerset = chan->itimer = chan->rxwinktime * DAHDI_CHUNKSIZE;
- #endif /* EMFLASH */
- chan->gotgs = 0;
- break;
- }
- #else /* EMFLASH || EMPULSE */
- else {
- __qevent(chan,DAHDI_EVENT_ONHOOK);
- chan->gotgs = 0;
- }
- #endif
- chan->itimerset = chan->itimer = 0;
- break;
- default:
- break;
- }
- break;
- case DAHDI_SIG_FXSKS: /* FXS Kewlstart */
- /* ignore a bit error if loop not closed and stable */
- if (chan->txstate != DAHDI_TXSTATE_OFFHOOK) break;
- #ifdef FXSFLASH
- if (rxsig == DAHDI_RXSIG_ONHOOK) {
- chan->itimer = DAHDI_FXSFLASHMAXTIME * DAHDI_CHUNKSIZE;
- break;
- } else if (rxsig == DAHDI_RXSIG_OFFHOOK) {
- if (chan->itimer) {
- /* did the offhook occur in the window? if not, ignore both events */
- if (chan->itimer <= ((DAHDI_FXSFLASHMAXTIME - DAHDI_FXSFLASHMINTIME) * DAHDI_CHUNKSIZE))
- __qevent(chan, DAHDI_EVENT_WINKFLASH);
- }
- chan->itimer = 0;
- break;
- }
- #endif
- /* fall through intentionally */
- case DAHDI_SIG_FXSGS: /* FXS Groundstart */
- if (rxsig == DAHDI_RXSIG_ONHOOK) {
- chan->ringdebtimer = RING_DEBOUNCE_TIME;
- chan->ringtrailer = 0;
- if (chan->txstate != DAHDI_TXSTATE_DEBOUNCE) {
- chan->gotgs = 0;
- __qevent(chan,DAHDI_EVENT_ONHOOK);
- }
- }
- break;
- case DAHDI_SIG_FXOGS: /* FXO Groundstart */
- if (rxsig == DAHDI_RXSIG_START) {
- /* if havent got gs, report it */
- if (!chan->gotgs) {
- __qevent(chan,DAHDI_EVENT_RINGOFFHOOK);
- chan->gotgs = 1;
- }
- }
- /* fall through intentionally */
- case DAHDI_SIG_FXOLS: /* FXO Loopstart */
- case DAHDI_SIG_FXOKS: /* FXO Kewlstart */
- switch(rxsig) {
- case DAHDI_RXSIG_OFFHOOK: /* went off hook */
- /* if asserti ng ring, stop it */
- if (chan->txstate == DAHDI_TXSTATE_START) {
- dahdi_rbs_sethook(chan,DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_AFTERSTART, DAHDI_AFTERSTART_TIME);
- }
- chan->kewlonhook = 0;
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Off hook on channel %d, itimer = %d, gotgs = %d\n", chan->channo, chan->itimer, chan->gotgs);
- #endif
- if (chan->itimer) /* if timer still running */
- {
- int plen = chan->itimerset - chan->itimer;
- if (plen <= DAHDI_MAXPULSETIME)
- {
- if (plen >= DAHDI_MINPULSETIME)
- {
- chan->pulsecount++;
- chan->pulsetimer = DAHDI_PULSETIMEOUT;
- chan->itimer = chan->itimerset;
- if (chan->pulsecount == 1)
- __qevent(chan,DAHDI_EVENT_PULSE_START);
- }
- } else
- __qevent(chan,DAHDI_EVENT_WINKFLASH);
- } else {
- /* if havent got GS detect */
- if (!chan->gotgs) {
- __qevent(chan,DAHDI_EVENT_RINGOFFHOOK);
- chan->gotgs = 1;
- chan->itimerset = chan->itimer = 0;
- }
- }
- chan->itimerset = chan->itimer = 0;
- break;
- case DAHDI_RXSIG_ONHOOK: /* went on hook */
- /* if not during offhook debounce time */
- if ((chan->txstate != DAHDI_TXSTATE_DEBOUNCE) &&
- (chan->txstate != DAHDI_TXSTATE_KEWL) &&
- (chan->txstate != DAHDI_TXSTATE_AFTERKEWL)) {
- chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE;
- }
- if (chan->txstate == DAHDI_TXSTATE_KEWL)
- chan->kewlonhook = 1;
- break;
- default:
- break;
- }
- default:
- break;
- }
- }
- /**
- * dahdi_hooksig() - send a signal on a channel to userspace
- * @chan: the DAHDI channel
- * @rxsig: signal (number) to send
- *
- * Called from a channel driver to send a DAHDI signal to userspace.
- * The signal will be queued for delivery to userspace.
- *
- * If the signal is the same as previous one sent, it won't be re-sent.
- */
- void dahdi_hooksig(struct dahdi_chan *chan, enum dahdi_rxsig rxsig)
- {
- /* skip if no change */
- unsigned long flags;
- spin_lock_irqsave(&chan->lock, flags);
- __dahdi_hooksig_pvt(chan,rxsig);
- spin_unlock_irqrestore(&chan->lock, flags);
- }
- /**
- * dahdi_rbsbits() - set Rx RBS bits on the channel
- * @chan: the DAHDI channel
- * @cursig: the bits to set
- *
- * Set the channel's rxsig (received: from device to userspace) and act
- * accordingly.
- */
- void dahdi_rbsbits(struct dahdi_chan *chan, int cursig)
- {
- unsigned long flags;
- if (cursig == chan->rxsig)
- return;
- if ((chan->flags & DAHDI_FLAG_SIGFREEZE)) return;
- spin_lock_irqsave(&chan->lock, flags);
- switch(chan->sig) {
- case DAHDI_SIG_FXOGS: /* FXO Groundstart */
- /* B-bit only matters for FXO GS */
- if (!(cursig & DAHDI_BBIT)) {
- __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_START);
- break;
- }
- /* Fall through */
- case DAHDI_SIG_EM_E1:
- case DAHDI_SIG_FXOLS: /* FXO Loopstart */
- case DAHDI_SIG_FXOKS: /* FXO Kewlstart */
- if (cursig & DAHDI_ABIT) /* off hook */
- __dahdi_hooksig_pvt(chan,DAHDI_RXSIG_OFFHOOK);
- else /* on hook */
- __dahdi_hooksig_pvt(chan,DAHDI_RXSIG_ONHOOK);
- break;
- case DAHDI_SIG_EM: /* E and M */
- /* Watch only the ABIT for changes. */
- if ((cursig & DAHDI_ABIT) == (chan->rxsig & DAHDI_ABIT))
- break;
- __dahdi_hooksig_pvt(chan, (cursig & DAHDI_ABIT) ?
- DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK);
- break;
- case DAHDI_SIG_FXSKS: /* FXS Kewlstart */
- case DAHDI_SIG_FXSGS: /* FXS Groundstart */
- /* Fall through */
- case DAHDI_SIG_FXSLS:
- if (!(cursig & DAHDI_BBIT)) {
- /* Check for ringing first */
- __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_RING);
- break;
- }
- if ((chan->sig != DAHDI_SIG_FXSLS) && (cursig & DAHDI_ABIT)) {
- /* if went on hook */
- __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_ONHOOK);
- } else {
- __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_OFFHOOK);
- }
- break;
- case DAHDI_SIG_CAS:
- /* send event that something changed */
- __qevent(chan, DAHDI_EVENT_BITSCHANGED);
- break;
- default:
- break;
- }
- /* Keep track of signalling for next time */
- chan->rxsig = cursig;
- spin_unlock_irqrestore(&chan->lock, flags);
- if ((debug & DEBUG_RBS) && printk_ratelimit()) {
- chan_notice(chan, "Detected sigbits change to %04x\n", cursig);
- }
- }
- static void process_echocan_events(struct dahdi_chan *chan)
- {
- union dahdi_echocan_events events = chan->ec_state->events;
- if (events.bit.CED_tx_detected) {
- dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CED_DETECTED);
- if (chan->ec_state) {
- if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
- set_echocan_fax_mode(chan, chan->channo, "CED tx detected", 1);
- else
- module_printk(KERN_NOTICE, "Detected CED tone (tx) on channel %d\n", chan->channo);
- }
- }
- if (events.bit.CED_rx_detected) {
- dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CED_DETECTED);
- if (chan->ec_state) {
- if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
- set_echocan_fax_mode(chan, chan->channo, "CED rx detected", 1);
- else
- module_printk(KERN_NOTICE, "Detected CED tone (rx) on channel %d\n", chan->channo);
- }
- }
- if (events.bit.CNG_tx_detected)
- dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CNG_DETECTED);
- if (events.bit.CNG_rx_detected)
- dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CNG_DETECTED);
- if (events.bit.NLP_auto_disabled) {
- dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED);
- chan->ec_state->status.mode = ECHO_MODE_FAX;
- }
- if (events.bit.NLP_auto_enabled) {
- dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED);
- chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
- }
- }
- /**
- * __dahdi_ec_chunk() - process echo for a single channel
- * @ss: DAHDI channel
- * @rxchunk: buffer to store audio with cancelled audio
- * @preecchunk: chunk of audio on which to cancel echo
- * @txchunk: reference chunk from the other direction
- *
- * The echo canceller function fixes received (from device to userspace)
- * audio. In order to fix it it uses the transmitted audio as a
- * reference. This call updates the echo canceller for a single chunk (8
- * bytes).
- *
- * Call with local interrupts disabled.
- */
- void __dahdi_ec_chunk(struct dahdi_chan *ss, u8 *rxchunk,
- const u8 *preecchunk, const u8 *txchunk)
- {
- short rxlin;
- int x;
- spin_lock(&ss->lock);
- if (ss->readchunkpreec) {
- /* Save a copy of the audio before the echo can has its way with it */
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- /* We only ever really need to deal with signed linear - let's just convert it now */
- ss->readchunkpreec[x] = DAHDI_XLAW(preecchunk[x], ss);
- }
- /* Perform echo cancellation on a chunk if necessary */
- if (ss->ec_state) {
- #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
- dahdi_kernel_fpu_begin();
- #endif
- if (ss->ec_state->status.mode & __ECHO_MODE_MUTE) {
- /* Special stuff for training the echo can */
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
- rxlin = DAHDI_XLAW(preecchunk[x], ss);
- if (ss->ec_state->status.mode == ECHO_MODE_PRETRAINING) {
- if (--ss->ec_state->status.pretrain_timer <= 0) {
- ss->ec_state->status.pretrain_timer = 0;
- ss->ec_state->status.mode = ECHO_MODE_STARTTRAINING;
- }
- }
- if (ss->ec_state->status.mode == ECHO_MODE_AWAITINGECHO) {
- ss->ec_state->status.last_train_tap = 0;
- ss->ec_state->status.mode = ECHO_MODE_TRAINING;
- }
- if ((ss->ec_state->status.mode == ECHO_MODE_TRAINING) &&
- (ss->ec_state->ops->echocan_traintap)) {
- if (ss->ec_state->ops->echocan_traintap(ss->ec_state, ss->ec_state->status.last_train_tap++, rxlin)) {
- ss->ec_state->status.mode = ECHO_MODE_ACTIVE;
- }
- }
- rxlin = 0;
- rxchunk[x] = DAHDI_LIN2X((int)rxlin, ss);
- }
- } else if (ss->ec_state->status.mode != ECHO_MODE_IDLE) {
- ss->ec_state->events.all = 0;
- if (ss->ec_state->ops->echocan_process) {
- short rxlins[DAHDI_CHUNKSIZE], txlins[DAHDI_CHUNKSIZE];
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- rxlins[x] = DAHDI_XLAW(preecchunk[x],
- ss);
- txlins[x] = DAHDI_XLAW(txchunk[x], ss);
- }
- ss->ec_state->ops->echocan_process(ss->ec_state, rxlins, txlins, DAHDI_CHUNKSIZE);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- rxchunk[x] = DAHDI_LIN2X((int) rxlins[x], ss);
- } else if (ss->ec_state->ops->echocan_events)
- ss->ec_state->ops->echocan_events(ss->ec_state);
- if (ss->ec_state->events.all)
- process_echocan_events(ss);
- }
- #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
- dahdi_kernel_fpu_end();
- #endif
- }
- spin_unlock(&ss->lock);
- }
- EXPORT_SYMBOL(__dahdi_ec_chunk);
- /**
- * dahdi_ec_span() - process echo for all channels in a span.
- * @span: DAHDI span
- *
- * Similar to calling dahdi_ec_chunk() for each of the channels in the
- * span. Uses dahdi_chunk.write_chunk for the rxchunk (the chunk to fix)
- * and dahdi_chan.readchunk as the txchunk (the reference chunk).
- */
- void _dahdi_ec_span(struct dahdi_span *span)
- {
- int x;
- for (x = 0; x < span->channels; x++) {
- struct dahdi_chan *const chan = span->chans[x];
- if (!chan->ec_current)
- continue;
- _dahdi_ec_chunk(chan, chan->readchunk, chan->writechunk);
- }
- }
- EXPORT_SYMBOL(_dahdi_ec_span);
- /* return 0 if nothing detected, 1 if lack of tone, 2 if presence of tone */
- /* modifies buffer pointed to by 'amp' with notched-out values */
- static inline int sf_detect(struct sf_detect_state *s,
- short *amp,
- int samples,long p1, long p2, long p3)
- {
- int i,rv = 0;
- long x,y;
- #define SF_DETECT_SAMPLES (DAHDI_CHUNKSIZE * 5)
- #define SF_DETECT_MIN_ENERGY 500
- #define NB 14 /* number of bits to shift left */
- /* determine energy level before filtering */
- for(i = 0; i < samples; i++)
- {
- if (amp[i] < 0) s->e1 -= amp[i];
- else s->e1 += amp[i];
- }
- /* do 2nd order IIR notch filter at given freq. and calculate
- energy */
- for(i = 0; i < samples; i++)
- {
- x = amp[i] << NB;
- y = s->x2 + (p1 * (s->x1 >> NB)) + x;
- y += (p2 * (s->y2 >> NB)) +
- (p3 * (s->y1 >> NB));
- s->x2 = s->x1;
- s->x1 = x;
- s->y2 = s->y1;
- s->y1 = y;
- amp[i] = y >> NB;
- if (amp[i] < 0) s->e2 -= amp[i];
- else s->e2 += amp[i];
- }
- s->samps += i;
- /* if time to do determination */
- if ((s->samps) >= SF_DETECT_SAMPLES)
- {
- rv = 1; /* default to no tone */
- /* if enough energy, it is determined to be a tone */
- if (((s->e1 - s->e2) / s->samps) > SF_DETECT_MIN_ENERGY) rv = 2;
- /* reset energy processing variables */
- s->samps = 0;
- s->e1 = s->e2 = 0;
- }
- return(rv);
- }
- static inline void __dahdi_process_putaudio_chunk(struct dahdi_chan *ss, unsigned char *rxb)
- {
- /* We transmit data from our master channel */
- /* Called with ss->lock held */
- struct dahdi_chan *ms = ss->master;
- /* Linear version of received data */
- short putlin[DAHDI_CHUNKSIZE],k[DAHDI_CHUNKSIZE];
- int x,r;
- if (ms->dialing) ms->afterdialingtimer = 50;
- else if (ms->afterdialingtimer) ms->afterdialingtimer--;
- if (ms->afterdialingtimer && !is_pseudo_chan(ms)) {
- /* Be careful since memset is likely a macro */
- rxb[0] = DAHDI_LIN2X(0, ms);
- memset(&rxb[1], rxb[0], DAHDI_CHUNKSIZE - 1); /* receive as silence if dialing */
- }
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
- rxb[x] = ms->rxgain[rxb[x]];
- putlin[x] = DAHDI_XLAW(rxb[x], ms);
- }
- #ifndef CONFIG_DAHDI_NO_ECHOCAN_DISABLE
- if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_rx_detect) {
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- if (echo_can_disable_detector_update(&ms->ec_state->rxecdis, putlin[x])) {
- set_echocan_fax_mode(ms, ss->channo, "CED rx detected", 1);
- dahdi_qevent_nolock(ms, DAHDI_EVENT_RX_CED_DETECTED);
- break;
- }
- }
- }
- #endif
- /* if doing rx tone decoding */
- if (ms->rxp1 && ms->rxp2 && ms->rxp3)
- {
- r = sf_detect(&ms->rd,putlin,DAHDI_CHUNKSIZE,ms->rxp1,
- ms->rxp2,ms->rxp3);
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- if (r) /* if something happened */
- {
- if (r != ms->rd.lastdetect)
- {
- if (((r == 2) && !(ms->toneflags & DAHDI_REVERSE_RXTONE)) ||
- ((r == 1) && (ms->toneflags & DAHDI_REVERSE_RXTONE)))
- {
- __qevent(ms,DAHDI_EVENT_RINGOFFHOOK);
- }
- else
- {
- __qevent(ms,DAHDI_EVENT_ONHOOK);
- }
- ms->rd.lastdetect = r;
- }
- }
- }
- if (!is_pseudo_chan(ms)) {
- memcpy(ms->putlin, putlin, DAHDI_CHUNKSIZE * sizeof(short));
- memcpy(ms->putraw, rxb, DAHDI_CHUNKSIZE);
- }
- /* Take the rxc, twiddle it for conferencing if appropriate and put it
- back */
- if ((!ms->confmute && !ms->afterdialingtimer) || is_pseudo_chan(ms)) {
- struct dahdi_chan *const conf_chan = ms->conf_chan;
- switch(ms->confmode & DAHDI_CONF_MODE_MASK) {
- case DAHDI_CONF_NORMAL: /* Normal mode */
- /* Do nothing. rx goes output */
- break;
- case DAHDI_CONF_MONITOR: /* Monitor a channel's rx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- /* Add monitored channel */
- if (is_pseudo_chan(conf_chan))
- ACSS(putlin, conf_chan->getlin);
- else
- ACSS(putlin, conf_chan->putlin);
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_MONITORTX: /* Monitor a channel's tx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- /* Add monitored channel */
- if (is_pseudo_chan(conf_chan))
- ACSS(putlin, conf_chan->putlin);
- else
- ACSS(putlin, conf_chan->getlin);
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_MONITORBOTH: /* Monitor a channel's tx and rx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- /* Note: Technically, saturation should be done at
- the end of the whole addition, but for performance
- reasons, we don't do that. Besides, it only matters
- when you're so loud you're clipping anyway */
- ACSS(putlin, conf_chan->getlin);
- ACSS(putlin, conf_chan->putlin);
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_MONITOR_RX_PREECHO: /* Monitor a channel's rx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- /* Add monitored channel */
- ACSS(putlin, is_pseudo_chan(conf_chan) ?
- conf_chan->getlin : conf_chan->readchunkpreec);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- /* Add monitored channel */
- ACSS(putlin, is_pseudo_chan(conf_chan) ?
- conf_chan->readchunkpreec : conf_chan->getlin);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_MONITORBOTH_PREECHO: /* Monitor a channel's tx and rx mode */
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- if (!conf_chan->readchunkpreec)
- break;
- /* Note: Technically, saturation should be done at
- the end of the whole addition, but for performance
- reasons, we don't do that. Besides, it only matters
- when you're so loud you're clipping anyway */
- ACSS(putlin, conf_chan->getlin);
- ACSS(putlin, conf_chan->readchunkpreec);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_REALANDPSEUDO:
- /* do normal conf mode processing */
- if (ms->confmode & DAHDI_CONF_TALKER) {
- /* Store temp value */
- memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* Add conf value */
- ACSS(k, conf_sums_next[ms->_confn]);
- /* get amount actually added */
- memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short));
- SCSS(ms->conflast, conf_sums_next[ms->_confn]);
- /* Really add in new value */
- ACSS(conf_sums_next[ms->_confn], ms->conflast);
- } else memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short));
- /* do the pseudo-channel part processing */
- memset(putlin, 0, DAHDI_CHUNKSIZE * sizeof(short));
- if (ms->confmode & DAHDI_CONF_PSEUDO_LISTENER) {
- /* Subtract out previous last sample written to conf */
- SCSS(putlin, ms->conflast2);
- /* Add in conference */
- ACSS(putlin, conf_sums[ms->_confn]);
- }
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- break;
- case DAHDI_CONF_CONF: /* Normal conference mode */
- if (is_pseudo_chan(ms)) /* if a pseudo-channel */
- {
- if (ms->confmode & DAHDI_CONF_LISTENER) {
- /* Subtract out last sample written to conf */
- SCSS(putlin, ms->conflast);
- /* Add in conference */
- ACSS(putlin, conf_sums[ms->_confn]);
- }
- /* Convert back */
- for(x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X(putlin[x], ms);
- memcpy(ss->putlin, putlin, DAHDI_CHUNKSIZE * sizeof(short));
- break;
- }
- /* fall through */
- case DAHDI_CONF_CONFANN: /* Conference with announce */
- if (ms->confmode & DAHDI_CONF_TALKER) {
- /* Store temp value */
- memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* Add conf value */
- ACSS(k, conf_sums_next[ms->_confn]);
- /* get amount actually added */
- memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short));
- SCSS(ms->conflast, conf_sums_next[ms->_confn]);
- /* Really add in new value */
- ACSS(conf_sums_next[ms->_confn], ms->conflast);
- } else
- memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short));
- /* rxc unmodified */
- break;
- case DAHDI_CONF_CONFMON:
- case DAHDI_CONF_CONFANNMON:
- if (ms->confmode & DAHDI_CONF_TALKER) {
- /* Store temp value */
- memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short));
- /* Subtract last value */
- SCSS(conf_sums[ms->_confn], ms->conflast);
- /* Add conf value */
- ACSS(k, conf_sums[ms->_confn]);
- /* get amount actually added */
- memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short));
- SCSS(ms->conflast, conf_sums[ms->_confn]);
- /* Really add in new value */
- ACSS(conf_sums[ms->_confn], ms->conflast);
- } else
- memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short));
- for (x=0;x<DAHDI_CHUNKSIZE;x++)
- rxb[x] = DAHDI_LIN2X((int)conf_sums_prev[ms->_confn][x], ms);
- break;
- case DAHDI_CONF_DIGITALMON:
- /* if not a pseudo-channel, ignore */
- if (!is_pseudo_chan(ms))
- break;
- /* Add monitored channel */
- if (is_pseudo_chan(conf_chan))
- memcpy(rxb, conf_chan->getraw, DAHDI_CHUNKSIZE);
- else
- memcpy(rxb, conf_chan->putraw, DAHDI_CHUNKSIZE);
- break;
- }
- }
- }
- /* HDLC (or other) receiver buffer functions for read side */
- static void __putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb, int bytes)
- {
- /* We transmit data from our master channel */
- /* Called with ss->lock held */
- struct dahdi_chan *ms = ss->master;
- /* Our receive buffer */
- unsigned char *buf;
- #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP)
- /* SKB for receiving network stuff */
- struct sk_buff *skb=NULL;
- #endif
- int oldbuf;
- int eof=0;
- int abort=0;
- int res;
- int left, x;
- while(bytes) {
- #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP)
- skb = NULL;
- #endif
- abort = 0;
- eof = 0;
- /* Next, figure out if we've got a buffer to receive into */
- if (ms->inreadbuf > -1) {
- /* Read into the current buffer */
- buf = ms->readbuf[ms->inreadbuf];
- left = ms->blocksize - ms->readidx[ms->inreadbuf];
- if (left > bytes)
- left = bytes;
- if (ms->flags & DAHDI_FLAG_HDLC) {
- for (x=0;x<left;x++) {
- /* Handle HDLC deframing */
- fasthdlc_rx_load_nocheck(&ms->rxhdlc, *(rxb++));
- bytes--;
- res = fasthdlc_rx_run(&ms->rxhdlc);
- /* If there is nothing there, continue */
- if (res & RETURN_EMPTY_FLAG)
- continue;
- else if (res & RETURN_COMPLETE_FLAG) {
- /* Only count this if it's a non-empty frame */
- if (ms->readidx[ms->inreadbuf]) {
- if ((ms->flags & DAHDI_FLAG_FCS) && (ms->infcs != PPP_GOODFCS)) {
- abort = DAHDI_EVENT_BADFCS;
- } else
- eof=1;
- break;
- }
- continue;
- } else if (res & RETURN_DISCARD_FLAG) {
- /* This could be someone idling with
- "idle" instead of "flag" */
- if (!ms->readidx[ms->inreadbuf])
- continue;
- abort = DAHDI_EVENT_ABORT;
- break;
- } else {
- unsigned char rxc;
- rxc = res;
- ms->infcs = PPP_FCS(ms->infcs, rxc);
- buf[ms->readidx[ms->inreadbuf]++] = rxc;
- /* Pay attention to the possibility of an overrun */
- if (ms->readidx[ms->inreadbuf] >= ms->blocksize) {
- if (!ss->span->alarms)
- module_printk(KERN_WARNING, "HDLC Receiver overrun on channel %s (master=%s)\n", ss->name, ss->master->name);
- abort=DAHDI_EVENT_OVERRUN;
- /* Force the HDLC state back to frame-search mode */
- ms->rxhdlc.state = 0;
- ms->rxhdlc.bits = 0;
- ms->readidx[ms->inreadbuf]=0;
- break;
- }
- }
- }
- } else {
- /* Not HDLC */
- memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left);
- rxb += left;
- ms->readidx[ms->inreadbuf] += left;
- bytes -= left;
- /* End of frame is decided by block size of 'N' */
- eof = (ms->readidx[ms->inreadbuf] >= ms->blocksize);
- if (eof && (ss->flags & DAHDI_FLAG_NOSTDTXRX)) {
- eof = 0;
- abort = DAHDI_EVENT_OVERRUN;
- }
- }
- if (eof) {
- /* Finished with this buffer, try another. */
- oldbuf = ms->inreadbuf;
- ms->infcs = PPP_INITFCS;
- ms->readn[ms->inreadbuf] = ms->readidx[ms->inreadbuf];
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "EOF, len is %d\n", ms->readn[ms->inreadbuf]);
- #endif
- #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP)
- if ((ms->flags & DAHDI_FLAG_PPP) ||
- dahdi_have_netdev(ms)) {
- #ifdef CONFIG_DAHDI_NET
- #endif /* CONFIG_DAHDI_NET */
- /* Our network receiver logic is MUCH
- different. We actually only use a single
- buffer */
- if (ms->readn[ms->inreadbuf] > 1) {
- /* Drop the FCS */
- ms->readn[ms->inreadbuf] -= 2;
- /* Allocate an SKB */
- #ifdef CONFIG_DAHDI_PPP
- if (!ms->do_ppp_error)
- #endif
- skb = dev_alloc_skb(ms->readn[ms->inreadbuf] + 2);
- if (skb) {
- unsigned char cisco_addr = *(ms->readbuf[ms->inreadbuf]);
- if (cisco_addr != 0x0f && cisco_addr != 0x8f)
- skb_reserve(skb, 2);
- /* XXX Get rid of this memcpy XXX */
- memcpy(skb->data, ms->readbuf[ms->inreadbuf], ms->readn[ms->inreadbuf]);
- skb_put(skb, ms->readn[ms->inreadbuf]);
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(ms)) {
- struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
- stats->rx_packets++;
- stats->rx_bytes += ms->readn[ms->inreadbuf];
- }
- #endif
- } else {
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(ms)) {
- struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
- stats->rx_dropped++;
- }
- #endif
- #ifdef CONFIG_DAHDI_PPP
- if (ms->flags & DAHDI_FLAG_PPP) {
- abort = DAHDI_EVENT_OVERRUN;
- }
- #endif
- #if 1
- #ifdef CONFIG_DAHDI_PPP
- if (!ms->do_ppp_error)
- #endif
- module_printk(KERN_NOTICE, "Memory squeeze, dropped one\n");
- #endif
- }
- }
- /* We don't cycle through buffers, just
- reuse the same one */
- ms->readn[ms->inreadbuf] = 0;
- ms->readidx[ms->inreadbuf] = 0;
- } else
- #endif
- {
- /* This logic might confuse and astound. Basically we need to find
- * the previous buffer index. It should be safe because, regardless
- * of whether or not it has been copied to user space, nothing should
- * have messed around with it since then */
- int comparemessage;
- /* Shut compiler up */
- int myres = 0;
- if (ms->flags & DAHDI_FLAG_MTP2) {
- comparemessage = (ms->inreadbuf - 1) & (ms->numbufs - 1);
- myres = memcmp(ms->readbuf[comparemessage], ms->readbuf[ms->inreadbuf], ms->readn[ms->inreadbuf]);
- }
- if ((ms->flags & DAHDI_FLAG_MTP2) && !myres) {
- /* Our messages are the same, so discard -
- * Don't advance buffers, reset indexes and buffer sizes. */
- ms->readn[ms->inreadbuf] = 0;
- ms->readidx[ms->inreadbuf] = 0;
- } else {
- ms->inreadbuf = (ms->inreadbuf + 1) % ms->numbufs;
- if (ms->inreadbuf == ms->outreadbuf) {
- /* Whoops, we're full, and have no where else
- to store into at the moment. We'll drop it
- until there's a buffer available */
- #ifdef BUFFER_DEBUG
- module_printk(KERN_NOTICE, "Out of storage space\n");
- #endif
- ms->inreadbuf = -1;
- }
- if (ms->outreadbuf < 0) { /* start out buffer if not already */
- ms->outreadbuf = oldbuf;
- /* if there are processes waiting in poll() on this channel,
- wake them up */
- wake_up_interruptible(&ms->waitq);
- }
- /* In the very orignal driver, it was quite well known to me (Jim) that there
- was a possibility that a channel sleeping on a receive block needed to
- be potentially woken up EVERY time a buffer was filled, not just on the first
- one, because if only done on the first one there is a slight timing potential
- of missing the wakeup (between where it senses the (lack of) active condition
- (with interrupts disabled) and where it does the sleep (interrupts enabled)
- in the read or iomux call, etc). That is why the read and iomux calls start
- with an infinite loop that gets broken out of upon an active condition,
- otherwise keeps sleeping and looking. The part in this code got "optimized"
- out in the later versions, and is put back now. Note that this is *NOT*
- needed for poll() waiters, because the poll_wait() function that is used there
- is atomic enough for this purpose; it will not go to sleep before ensuring
- that the waitqueue is empty. */
- /* Notify a blocked reader that there is data available
- to be read, unless we're waiting for it to be full */
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Notifying reader data in block %d\n", oldbuf);
- #endif
- wake_up_interruptible(&ms->waitq);
- }
- }
- }
- if (abort) {
- /* Start over reading frame */
- ms->readidx[ms->inreadbuf] = 0;
- ms->infcs = PPP_INITFCS;
- #ifdef CONFIG_DAHDI_NET
- if (dahdi_have_netdev(ms)) {
- struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
- stats->rx_errors++;
- if (abort == DAHDI_EVENT_OVERRUN)
- stats->rx_over_errors++;
- if (abort == DAHDI_EVENT_BADFCS)
- stats->rx_crc_errors++;
- if (abort == DAHDI_EVENT_ABORT)
- stats->rx_frame_errors++;
- } else
- #endif
- #ifdef CONFIG_DAHDI_PPP
- if (ms->flags & DAHDI_FLAG_PPP) {
- ms->do_ppp_error = 1;
- tasklet_schedule(&ms->ppp_calls);
- } else
- #endif
- if (test_bit(DAHDI_FLAGBIT_OPEN, &ms->flags) && !ss->span->alarms) {
- /* Notify the receiver... */
- __qevent(ss->master, abort);
- }
- }
- } else /* No place to receive -- drop on the floor */
- break;
- #ifdef CONFIG_DAHDI_NET
- if (skb && dahdi_have_netdev(ms))
- {
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
- skb->mac.raw = skb->data;
- #else
- skb_reset_mac_header(skb);
- #endif
- skb->dev = chan_to_netdev(ms);
- #ifdef DAHDI_HDLC_TYPE_TRANS
- skb->protocol = hdlc_type_trans(skb,
- chan_to_netdev(ms));
- #else
- skb->protocol = htons (ETH_P_HDLC);
- #endif
- netif_rx(skb);
- }
- #endif
- #ifdef CONFIG_DAHDI_PPP
- if (skb && (ms->flags & DAHDI_FLAG_PPP)) {
- unsigned char *tmp;
- tmp = skb->data;
- skb_pull(skb, 2);
- /* Make sure that it's addressed to ALL STATIONS and UNNUMBERED */
- if (!tmp || (tmp[0] != 0xff) || (tmp[1] != 0x03)) {
- /* Invalid SKB -- drop */
- if (tmp)
- module_printk(KERN_NOTICE, "Received invalid SKB (%02x, %02x)\n", tmp[0], tmp[1]);
- dev_kfree_skb_irq(skb);
- } else {
- skb_queue_tail(&ms->ppp_rq, skb);
- tasklet_schedule(&ms->ppp_calls);
- }
- }
- #endif
- }
- if (bytes) {
- if (!test_bit(DAHDI_FLAGBIT_RXOVERRUN, &ms->flags)) {
- if (test_bit(DAHDI_FLAGBIT_BUFEVENTS, &ms->flags))
- __qevent(ms, DAHDI_EVENT_READ_OVERRUN);
- set_bit(DAHDI_FLAGBIT_RXOVERRUN, &ms->flags);
- }
- } else {
- clear_bit(DAHDI_FLAGBIT_RXOVERRUN, &ms->flags);
- }
- }
- static inline void __dahdi_putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb)
- {
- __putbuf_chunk(ss, rxb, DAHDI_CHUNKSIZE);
- #ifdef CONFIG_DAHDI_MIRROR
- if (ss->rxmirror) {
- spin_lock(&ss->rxmirror->lock);
- __putbuf_chunk(ss->rxmirror, rxb, DAHDI_CHUNKSIZE);
- spin_unlock(&ss->rxmirror->lock);
- }
- #endif /* CONFIG_DAHDI_MIRROR */
- }
- static void __dahdi_hdlc_abort(struct dahdi_chan *ss, int event)
- {
- if (ss->inreadbuf >= 0)
- ss->readidx[ss->inreadbuf] = 0;
- if (test_bit(DAHDI_FLAGBIT_OPEN, &ss->flags) && !ss->span->alarms)
- __qevent(ss->master, event);
- }
- void dahdi_hdlc_abort(struct dahdi_chan *ss, int event)
- {
- unsigned long flags;
- spin_lock_irqsave(&ss->lock, flags);
- __dahdi_hdlc_abort(ss, event);
- spin_unlock_irqrestore(&ss->lock, flags);
- }
- void dahdi_hdlc_putbuf(struct dahdi_chan *ss, unsigned char *rxb, int bytes)
- {
- unsigned long flags;
- int left;
- spin_lock_irqsave(&ss->lock, flags);
- if (ss->inreadbuf < 0) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "No place to receive HDLC frame\n");
- #endif
- spin_unlock_irqrestore(&ss->lock, flags);
- return;
- }
- /* Read into the current buffer */
- left = ss->blocksize - ss->readidx[ss->inreadbuf];
- if (left > bytes)
- left = bytes;
- if (left > 0) {
- memcpy(ss->readbuf[ss->inreadbuf] + ss->readidx[ss->inreadbuf], rxb, left);
- rxb += left;
- ss->readidx[ss->inreadbuf] += left;
- bytes -= left;
- }
- /* Something isn't fit into buffer */
- if (bytes) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "HDLC frame isn't fit into buffer space\n");
- #endif
- __dahdi_hdlc_abort(ss, DAHDI_EVENT_OVERRUN);
- }
- spin_unlock_irqrestore(&ss->lock, flags);
- }
- void dahdi_hdlc_finish(struct dahdi_chan *ss)
- {
- int oldreadbuf;
- unsigned long flags;
- spin_lock_irqsave(&ss->lock, flags);
- if ((oldreadbuf = ss->inreadbuf) < 0) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "No buffers to finish\n");
- #endif
- spin_unlock_irqrestore(&ss->lock, flags);
- return;
- }
- if (!ss->readidx[ss->inreadbuf]) {
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Empty HDLC frame received\n");
- #endif
- spin_unlock_irqrestore(&ss->lock, flags);
- return;
- }
- ss->readn[ss->inreadbuf] = ss->readidx[ss->inreadbuf];
- ss->inreadbuf = (ss->inreadbuf + 1) % ss->numbufs;
- if (ss->inreadbuf == ss->outreadbuf) {
- ss->inreadbuf = -1;
- #ifdef CONFIG_DAHDI_DEBUG
- module_printk(KERN_NOTICE, "Notifying reader data in block %d\n", oldreadbuf);
- #endif
- }
- if (ss->outreadbuf < 0) {
- ss->outreadbuf = oldreadbuf;
- }
- wake_up_interruptible(&ss->waitq);
- spin_unlock_irqrestore(&ss->lock, flags);
- }
- /* Returns 1 if EOF, 0 if data is still in frame, -1 if EOF and no buffers left */
- int dahdi_hdlc_getbuf(struct dahdi_chan *ss, unsigned char *bufptr, unsigned int *size)
- {
- unsigned char *buf;
- unsigned long flags;
- int left = 0;
- int res;
- int oldbuf;
- spin_lock_irqsave(&ss->lock, flags);
- if (ss->outwritebuf > -1) {
- buf = ss->writebuf[ss->outwritebuf];
- left = ss->writen[ss->outwritebuf] - ss->writeidx[ss->outwritebuf];
- /* Strip off the empty HDLC CRC end */
- left -= 2;
- if (left <= *size) {
- *size = left;
- res = 1;
- } else
- res = 0;
- memcpy(bufptr, &buf[ss->writeidx[ss->outwritebuf]], *size);
- ss->writeidx[ss->outwritebuf] += *size;
- if (res) {
- /* Rotate buffers */
- oldbuf = ss->outwritebuf;
- ss->writeidx[oldbuf] = 0;
- ss->writen[oldbuf] = 0;
- ss->outwritebuf = (ss->outwritebuf + 1) % ss->numbufs;
- if (ss->outwritebuf == ss->inwritebuf) {
- ss->outwritebuf = -1;
- if (ss->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY))
- wake_up_interruptible(&ss->waitq);
- /* If we're only supposed to start when full, disable the transmitter */
- if ((ss->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || (ss->txbufpolicy == DAHDI_POLICY_HALF_FULL))
- ss->txdisable = 1;
- res = -1;
- }
- if (ss->inwritebuf < 0)
- ss->inwritebuf = oldbuf;
- if (!(ss->flags & DAHDI_FLAG_PPP) ||
- !dahdi_have_netdev(ss)) {
- wake_up_interruptible(&ss->waitq);
- }
- }
- } else {
- res = -1;
- *size = 0;
- }
- spin_unlock_irqrestore(&ss->lock, flags);
- return res;
- }
- static void process_timers(void)
- {
- struct dahdi_timer *cur;
- if (list_empty(&dahdi_timers))
- return;
- spin_lock(&dahdi_timer_lock);
- list_for_each_entry(cur, &dahdi_timers, list) {
- spin_lock(&cur->lock);
- cur->pos -= DAHDI_CHUNKSIZE;
- if (cur->pos <= 0) {
- cur->tripped++;
- cur->pos = cur->ms;
- wake_up_interruptible(&cur->sel);
- }
- spin_unlock(&cur->lock);
- }
- spin_unlock(&dahdi_timer_lock);
- }
- static unsigned int dahdi_timer_poll(struct file *file, struct poll_table_struct *wait_table)
- {
- struct dahdi_timer *timer = file->private_data;
- unsigned long flags;
- int ret = 0;
- if (timer) {
- poll_wait(file, &timer->sel, wait_table);
- spin_lock_irqsave(&timer->lock, flags);
- if (timer->tripped || timer->ping)
- ret |= POLLPRI;
- spin_unlock_irqrestore(&timer->lock, flags);
- } else {
- /*
- * This should never happen. Surprise device removal
- * should lead us to the nodev_* file_operations
- */
- msleep(5);
- module_printk(KERN_ERR, "%s: NODEV\n", __func__);
- return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
- }
- return ret;
- }
- /* device poll routine */
- static unsigned int
- dahdi_chan_poll(struct file *file, struct poll_table_struct *wait_table)
- {
- struct dahdi_chan *const c = file->private_data;
- int ret = 0;
- unsigned long flags;
- if (unlikely(!c)) {
- /*
- * This should never happen. Surprise device removal
- * should lead us to the nodev_* file_operations
- */
- msleep(5);
- module_printk(KERN_ERR, "%s: NODEV\n", __func__);
- return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
- }
- poll_wait(file, &c->waitq, wait_table);
- spin_lock_irqsave(&c->lock, flags);
- ret |= (c->inwritebuf > -1) ? POLLOUT|POLLWRNORM : 0;
- ret |= (c->outreadbuf > -1) ? POLLIN|POLLRDNORM : 0;
- ret |= (c->eventoutidx != c->eventinidx) ? POLLPRI : 0;
- spin_unlock_irqrestore(&c->lock, flags);
- return ret;
- }
- static unsigned int dahdi_poll(struct file *file, struct poll_table_struct *wait_table)
- {
- const int unit = UNIT(file);
- if (likely(unit == DAHDI_TIMER))
- return dahdi_timer_poll(file, wait_table);
- /* transcoders and channels should have updated their file_operations
- * before poll is ever called. */
- return -EINVAL;
- }
- static void __dahdi_transmit_chunk(struct dahdi_chan *chan, unsigned char *buf)
- {
- unsigned char silly[DAHDI_CHUNKSIZE];
- /* Called with chan->lock locked */
- #ifdef OPTIMIZE_CHANMUTE
- if(likely(chan->chanmute))
- return;
- #endif
- if (!buf)
- buf = silly;
- __dahdi_getbuf_chunk(chan, buf);
- if ((chan->flags & DAHDI_FLAG_AUDIO) || (chan->confmode)) {
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_begin();
- #endif
- __dahdi_process_getaudio_chunk(chan, buf);
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_end();
- #endif
- }
- }
- static inline void __dahdi_real_transmit(struct dahdi_chan *chan)
- {
- /* Called with chan->lock held */
- #ifdef OPTIMIZE_CHANMUTE
- if(likely(chan->chanmute))
- return;
- #endif
- if (chan->confmode) {
- /* Pull queued data off the conference */
- __buf_pull(&chan->confout, chan->writechunk, chan);
- } else {
- __dahdi_transmit_chunk(chan, chan->writechunk);
- }
- }
- static void __dahdi_getempty(struct dahdi_chan *ms, unsigned char *buf)
- {
- int bytes = DAHDI_CHUNKSIZE;
- int left;
- unsigned char *txb = buf;
- int x;
- short getlin;
- /* Called with ms->lock held */
- while(bytes) {
- /* Receive silence, or tone */
- if (ms->curtone) {
- left = ms->curtone->tonesamples - ms->tonep;
- if (left > bytes)
- left = bytes;
- for (x=0;x<left;x++) {
- /* Pick our default value from the next sample of the current tone */
- getlin = dahdi_tone_nextsample(&ms->ts, ms->curtone);
- *(txb++) = DAHDI_LIN2X(getlin, ms);
- }
- ms->tonep+=left;
- bytes -= left;
- if (ms->tonep >= ms->curtone->tonesamples) {
- struct dahdi_tone *last;
- /* Go to the next sample of the tone */
- ms->tonep = 0;
- last = ms->curtone;
- ms->curtone = ms->curtone->next;
- if (!ms->curtone) {
- /* No more tones... Is this dtmf or mf? If so, go to the next digit */
- if (ms->dialing)
- __do_dtmf(ms);
- } else {
- if (last != ms->curtone)
- dahdi_init_tone_state(&ms->ts, ms->curtone);
- }
- }
- } else {
- /* Use silence */
- memset(txb, DAHDI_LIN2X(0, ms), bytes);
- bytes = 0;
- }
- }
- }
- static void __dahdi_receive_chunk(struct dahdi_chan *chan, unsigned char *buf)
- {
- /* Receive chunk of audio -- called with chan->lock held */
- unsigned char waste[DAHDI_CHUNKSIZE];
- #ifdef OPTIMIZE_CHANMUTE
- if(likely(chan->chanmute))
- return;
- #endif
- if (!buf) {
- memset(waste, DAHDI_LIN2X(0, chan), sizeof(waste));
- buf = waste;
- }
- if ((chan->flags & DAHDI_FLAG_AUDIO) || (chan->confmode)) {
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_begin();
- #endif
- __dahdi_process_putaudio_chunk(chan, buf);
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_end();
- #endif
- }
- __dahdi_putbuf_chunk(chan, buf);
- }
- static inline void __dahdi_real_receive(struct dahdi_chan *chan)
- {
- /* Called with chan->lock held */
- #ifdef OPTIMIZE_CHANMUTE
- if(likely(chan->chanmute))
- return;
- #endif
- if (chan->confmode) {
- /* Load into queue if we have space */
- __buf_push(&chan->confin, chan->readchunk);
- } else {
- __dahdi_receive_chunk(chan, chan->readchunk);
- }
- }
- /**
- * __transmit_to_slaves() - Distribute the tx data to all the slave channels.
- *
- */
- static void __transmit_to_slaves(struct dahdi_chan *const chan)
- {
- u_char data[DAHDI_CHUNKSIZE];
- int i;
- int pos = DAHDI_CHUNKSIZE;
- struct dahdi_chan *slave;
- for (i = 0; i < DAHDI_CHUNKSIZE; i++) {
- for (slave = chan; (NULL != slave); slave = slave->nextslave) {
- if (pos == DAHDI_CHUNKSIZE) {
- __dahdi_transmit_chunk(chan, data);
- pos = 0;
- }
- slave->writechunk[i] = data[pos++];
- }
- }
- }
- int _dahdi_transmit(struct dahdi_span *span)
- {
- unsigned int x;
- for (x=0;x<span->channels;x++) {
- struct dahdi_chan *const chan = span->chans[x];
- spin_lock(&chan->lock);
- if (unlikely(chan->flags & DAHDI_FLAG_NOSTDTXRX)) {
- spin_unlock(&chan->lock);
- continue;
- }
- if (chan == chan->master) {
- if (is_chan_dacsed(chan)) {
- struct dahdi_chan *const src = chan->dacs_chan;
- memcpy(chan->writechunk, src->readchunk,
- DAHDI_CHUNKSIZE);
- if (chan->sig == DAHDI_SIG_DACS_RBS) {
- /* Just set bits for our destination */
- if (chan->txsig != src->rxsig) {
- chan->txsig = src->rxsig;
- span->ops->rbsbits(chan, src->rxsig);
- }
- }
- /* there is no further processing to do for
- * DACS channels, so jump to the next channel
- * in the span */
- spin_unlock(&chan->lock);
- continue;
- } else if (chan->nextslave) {
- __transmit_to_slaves(chan);
- } else {
- /* Process a normal channel */
- __dahdi_real_transmit(chan);
- }
- if (chan->otimer) {
- chan->otimer -= DAHDI_CHUNKSIZE;
- if (chan->otimer <= 0)
- __rbs_otimer_expire(chan);
- }
- }
- spin_unlock(&chan->lock);
- }
- if (span->mainttimer) {
- span->mainttimer -= DAHDI_CHUNKSIZE;
- if (span->mainttimer <= 0) {
- span->mainttimer = 0;
- span->maintstat = 0;
- }
- }
- return 0;
- }
- EXPORT_SYMBOL(_dahdi_transmit);
- static inline void __pseudo_rx_audio(struct dahdi_chan *chan)
- {
- unsigned char tmp[DAHDI_CHUNKSIZE];
- spin_lock(&chan->lock);
- __dahdi_getempty(chan, tmp);
- __dahdi_receive_chunk(chan, tmp);
- spin_unlock(&chan->lock);
- }
- #ifdef CONFIG_DAHDI_MIRROR
- static inline void pseudo_rx_audio(struct dahdi_chan *chan)
- {
- if (!chan->srcmirror)
- __pseudo_rx_audio(chan);
- }
- #else
- static inline void pseudo_rx_audio(struct dahdi_chan *chan)
- {
- __pseudo_rx_audio(chan);
- }
- #endif /* CONFIG_DAHDI_MIRROR */
- #ifdef DAHDI_SYNC_TICK
- static inline void dahdi_sync_tick(struct dahdi_span *const s)
- {
- if (s->ops->sync_tick)
- s->ops->sync_tick(s, dahdi_is_sync_master(s));
- }
- #else
- #define dahdi_sync_tick(x) do { ; } while (0)
- #endif
- /**
- * _process_masterspan - Handle conferencing and timers.
- *
- * There are three sets of conference sum accumulators. One for the current
- * sample chunk (conf_sums), one for the next sample chunk (conf_sums_next), and
- * one for the previous sample chunk (conf_sums_prev). The following routine
- * (rotate_sums) "rotates" the pointers to these accululator arrays as part
- * of the events of sample chink processing as follows:
- *
- * 1. All (real span) receive chunks are processed (with putbuf). The last one
- * to be processed is the master span. The data received is loaded into the
- * accumulators for the next chunk (conf_sums_next), to be in alignment with
- * current data after rotate_sums() is called (which immediately follows).
- * Keep in mind that putbuf is *also* a transmit routine for the pseudo parts
- * of channels that are in the REALANDPSEUDO conference mode. These channels
- * are processed from data in the current sample chunk (conf_sums), being
- * that this is a "transmit" function (for the pseudo part).
- *
- * 2. rotate_sums() is called.
- *
- * 3. All pseudo channel receive chunks are processed. This data is loaded into
- * the current sample chunk accumulators (conf_sums).
- *
- * 4. All conference links are processed (being that all receive data for this
- * chunk has already been processed by now).
- *
- * 5. All pseudo channel transmit chunks are processed. This data is loaded from
- * the current sample chunk accumulators (conf_sums).
- *
- * 6. All (real span) transmit chunks are processed (with getbuf). This data is
- * loaded from the current sample chunk accumulators (conf_sums). Keep in mind
- * that getbuf is *also* a receive routine for the pseudo part of channels that
- * are in the REALANDPSEUDO conference mode. These samples are loaded into
- * the next sample chunk accumulators (conf_sums_next) to be processed as part
- * of the next sample chunk's data (next time around the world).
- *
- */
- static void _process_masterspan(void)
- {
- int x;
- struct pseudo_chan *pseudo;
- struct dahdi_span *s;
- u_char *data;
- #ifdef CONFIG_DAHDI_CORE_TIMER
- /* We increment the calls since start here, so that if we switch over
- * to the core timer, we know how many times we need to call
- * process_masterspan in order to catch up since this function needs
- * to be called (1000 / (DAHDI_CHUNKSIZE / 8)) times per second. */
- atomic_inc(&core_timer.count);
- #endif
- /* Hold the chan_lock for the duration of major
- activities which touch all sorts of channels */
- spin_lock(&chan_lock);
- /* Process any timers */
- process_timers();
- list_for_each_entry(s, &span_list, spans_node) {
- for (x = 0; x < s->channels; ++x) {
- struct dahdi_chan *const chan = s->chans[x];
- if (!chan->confmode)
- continue;
- spin_lock(&chan->lock);
- data = __buf_peek(&chan->confin);
- __dahdi_receive_chunk(chan, data);
- if (data)
- __buf_pull(&chan->confin, NULL, chan);
- spin_unlock(&chan->lock);
- }
- }
- /* This is the master channel, so make things switch over */
- rotate_sums();
- /* do all the pseudo and/or conferenced channel receives (getbuf's) */
- list_for_each_entry(pseudo, &pseudo_chans, node) {
- spin_lock(&pseudo->chan.lock);
- __dahdi_transmit_chunk(&pseudo->chan, NULL);
- spin_unlock(&pseudo->chan.lock);
- }
- #ifdef CONFIG_DAHDI_CONFLINK
- if (maxlinks) {
- int z;
- int y;
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_begin();
- #endif
- /* process all the conf links */
- for (x = 1; x <= maxlinks; x++) {
- /* if we have a destination conf */
- z = confalias[conf_links[x].dst];
- if (z) {
- y = confalias[conf_links[x].src];
- if (y)
- ACSS(conf_sums[z], conf_sums[y]);
- }
- }
- #ifdef CONFIG_DAHDI_MMX
- dahdi_kernel_fpu_end();
- #endif
- }
- #endif /* CONFIG_DAHDI_CONFLINK */
- /* do all the pseudo/conferenced channel transmits (putbuf's) */
- list_for_each_entry(pseudo, &pseudo_chans, node) {
- pseudo_rx_audio(&pseudo->chan);
- }
- list_for_each_entry(s, &span_list, spans_node) {
- for (x = 0; x < s->channels; x++) {
- struct dahdi_chan *const chan = s->chans[x];
- if (!chan->confmode)
- continue;
- spin_lock(&chan->lock);
- data = __buf_pushpeek(&chan->confout);
- __dahdi_transmit_chunk(chan, data);
- if (data)
- __buf_push(&chan->confout, NULL);
- spin_unlock(&chan->lock);
- }
- dahdi_sync_tick(s);
- }
- spin_unlock(&chan_lock);
- }
- #ifndef CONFIG_DAHDI_CORE_TIMER
- static void coretimer_init(void)
- {
- return;
- }
- static void coretimer_cleanup(void)
- {
- return;
- }
- #else
- static unsigned long core_diff_ms(struct timespec *t0, struct timespec *t1)
- {
- long nanosec, sec;
- unsigned long ms;
- sec = (t1->tv_sec - t0->tv_sec);
- nanosec = (t1->tv_nsec - t0->tv_nsec);
- while (nanosec >= NSEC_PER_SEC) {
- nanosec -= NSEC_PER_SEC;
- ++sec;
- }
- while (nanosec < 0) {
- nanosec += NSEC_PER_SEC;
- --sec;
- }
- ms = (sec * 1000) + (nanosec / 1000000L);
- return ms;
- }
- static inline unsigned long msecs_processed(const struct core_timer *const ct)
- {
- return atomic_read(&ct->count) * DAHDI_MSECS_PER_CHUNK;
- }
- static void coretimer_func(unsigned long param)
- {
- unsigned long flags;
- unsigned long ms_since_start;
- struct timespec now;
- const unsigned long MAX_INTERVAL = 100000L;
- const unsigned long ONESEC_INTERVAL = HZ;
- const long MS_LIMIT = 3000;
- long difference;
- ktime_get_ts(&now);
- if (atomic_read(&core_timer.count) ==
- atomic_read(&core_timer.last_count)) {
- /* This is the code path if a board driver is not calling
- * dahdi_receive, and therefore the core of dahdi needs to
- * perform the master span processing itself. */
- if (core_timer.dahdi_receive_used) {
- core_timer.dahdi_receive_used = 0;
- dahdi_dbg(GENERAL, "Master changed to core_timer\n");
- }
- if (!atomic_read(&core_timer.shutdown)) {
- mod_timer(&core_timer.timer, jiffies +
- core_timer.interval);
- }
- ms_since_start = core_diff_ms(&core_timer.start_interval, &now);
- /*
- * If the system time has changed, it is possible for us to be
- * far behind. If we are more than MS_LIMIT milliseconds
- * behind (or ahead in time), just reset our time base and
- * continue so that we do not hang the system here.
- *
- */
- difference = ms_since_start - msecs_processed(&core_timer);
- if (unlikely((difference > MS_LIMIT) || (difference < 0))) {
- if (printk_ratelimit()) {
- module_printk(KERN_INFO,
- "Detected time shift.\n");
- }
- atomic_set(&core_timer.count, 0);
- atomic_set(&core_timer.last_count, 0);
- core_timer.start_interval = now;
- return;
- }
- local_irq_save(flags);
- while (ms_since_start > msecs_processed(&core_timer))
- _process_masterspan();
- local_irq_restore(flags);
- if (ms_since_start > MAX_INTERVAL) {
- atomic_set(&core_timer.count, 0);
- atomic_set(&core_timer.last_count, 0);
- core_timer.start_interval = now;
- } else {
- atomic_set(&core_timer.last_count,
- atomic_read(&core_timer.count));
- }
- } else {
- /* It looks like a board driver is calling dahdi_receive. We
- * will just check again in a second. */
- if (!core_timer.dahdi_receive_used) {
- core_timer.dahdi_receive_used = 1;
- dahdi_dbg(GENERAL, "Master is no longer core_timer\n");
- }
- atomic_set(&core_timer.count, 0);
- atomic_set(&core_timer.last_count, 0);
- core_timer.start_interval = now;
- if (!atomic_read(&core_timer.shutdown))
- mod_timer(&core_timer.timer, jiffies + ONESEC_INTERVAL);
- }
- }
- static void coretimer_init(void)
- {
- init_timer(&core_timer.timer);
- core_timer.timer.function = coretimer_func;
- ktime_get_ts(&core_timer.start_interval);
- atomic_set(&core_timer.count, 0);
- atomic_set(&core_timer.shutdown, 0);
- core_timer.interval = max(msecs_to_jiffies(DAHDI_MSECS_PER_CHUNK), 1UL);
- if (core_timer.interval < (HZ/250))
- core_timer.interval = (HZ/250);
- core_timer.timer.expires = jiffies + core_timer.interval;
- add_timer(&core_timer.timer);
- }
- static void coretimer_cleanup(void)
- {
- atomic_set(&core_timer.shutdown, 1);
- del_timer_sync(&core_timer.timer);
- }
- #endif /* CONFIG_DAHDI_CORE_TIMER */
- /**
- * __receive_from_slaves() - Collect the rx data from all the slave channels.
- *
- */
- static void __receive_from_slaves(struct dahdi_chan *const chan)
- {
- u_char data[DAHDI_CHUNKSIZE];
- int i;
- int pos = 0;
- struct dahdi_chan *slave;
- for (i = 0; i < DAHDI_CHUNKSIZE; ++i) {
- for (slave = chan; (NULL != slave); slave = slave->nextslave) {
- data[pos++] = slave->readchunk[i];
- if (pos == DAHDI_CHUNKSIZE) {
- __dahdi_receive_chunk(chan, data);
- pos = 0;
- }
- }
- }
- }
- static inline bool should_skip_receive(const struct dahdi_chan *const chan)
- {
- return (unlikely(chan->flags & DAHDI_FLAG_NOSTDTXRX) ||
- (chan->master != chan) ||
- is_chan_dacsed(chan));
- }
- int _dahdi_receive(struct dahdi_span *span)
- {
- unsigned int x;
- #ifdef CONFIG_DAHDI_WATCHDOG
- span->watchcounter--;
- #endif
- for (x = 0; x < span->channels; x++) {
- struct dahdi_chan *const chan = span->chans[x];
- spin_lock(&chan->lock);
- if (should_skip_receive(chan)) {
- spin_unlock(&chan->lock);
- continue;
- }
- if (chan->nextslave) {
- __receive_from_slaves(chan);
- } else {
- /* Process a normal channel */
- __dahdi_real_receive(chan);
- }
- if (chan->itimer) {
- chan->itimer -= DAHDI_CHUNKSIZE;
- if (chan->itimer <= 0)
- rbs_itimer_expire(chan);
- }
- if (chan->ringdebtimer)
- chan->ringdebtimer--;
- if (chan->sig & __DAHDI_SIG_FXS) {
- if (chan->rxhooksig == DAHDI_RXSIG_RING)
- chan->ringtrailer = DAHDI_RINGTRAILER;
- else if (chan->ringtrailer) {
- chan->ringtrailer -= DAHDI_CHUNKSIZE;
- /* See if RING trailer is expired */
- if (!chan->ringtrailer && !chan->ringdebtimer)
- __qevent(chan, DAHDI_EVENT_RINGOFFHOOK);
- }
- }
- if (chan->pulsetimer) {
- chan->pulsetimer--;
- if (chan->pulsetimer <= 0) {
- if (chan->pulsecount) {
- if (chan->pulsecount > 12) {
- module_printk(KERN_NOTICE, "Got pulse digit %d on %s???\n",
- chan->pulsecount,
- chan->name);
- } else if (chan->pulsecount > 11) {
- __qevent(chan, DAHDI_EVENT_PULSEDIGIT | '#');
- } else if (chan->pulsecount > 10) {
- __qevent(chan, DAHDI_EVENT_PULSEDIGIT | '*');
- } else if (chan->pulsecount > 9) {
- __qevent(chan, DAHDI_EVENT_PULSEDIGIT | '0');
- } else {
- __qevent(chan, DAHDI_EVENT_PULSEDIGIT | ('0' +
- chan->pulsecount));
- }
- chan->pulsecount = 0;
- }
- }
- }
- #ifdef BUFFER_DEBUG
- chan->statcount -= DAHDI_CHUNKSIZE;
- #endif
- spin_unlock(&chan->lock);
- }
- if (dahdi_is_sync_master(span))
- _process_masterspan();
- return 0;
- }
- EXPORT_SYMBOL(_dahdi_receive);
- MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
- MODULE_DESCRIPTION("DAHDI Telephony Interface");
- MODULE_LICENSE("GPL v2");
- /* DAHDI now provides timing. If anybody wants dahdi_dummy it's probably
- * for that. So make dahdi provide it for now. This alias may be removed
- * in the future, and users are encouraged not to rely on it. */
- MODULE_ALIAS("dahdi_dummy");
- module_param(debug, int, 0644);
- MODULE_PARM_DESC(debug, "Sets debugging verbosity as a bitfield, to see"\
- " general debugging set this to 1. To see RBS debugging set"\
- " this to 32");
- module_param(deftaps, int, 0644);
- module_param(max_pseudo_channels, int, 0644);
- MODULE_PARM_DESC(max_pseudo_channels, "Maximum number of pseudo channels.");
- module_param(hwec_overrides_swec, int, 0644);
- MODULE_PARM_DESC(hwec_overrides_swec, "When true, a hardware echo canceller is used instead of configured SWEC.");
- module_param(auto_assign_spans, int, 0644);
- MODULE_PARM_DESC(auto_assign_spans,
- "If 1 spans will automatically have their children span and "
- "channel numbers assigned by the driver. If 0, user space "
- "will need to assign them via /sys/bus/dahdi_devices.");
- static ssize_t dahdi_no_read(struct file *file, char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- return -ENOSYS;
- }
- static ssize_t dahdi_no_write(struct file *file, const char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- return -ENOSYS;
- }
- static const struct file_operations dahdi_fops = {
- .owner = THIS_MODULE,
- .open = dahdi_open,
- .release = dahdi_release,
- #ifdef HAVE_UNLOCKED_IOCTL
- .unlocked_ioctl = dahdi_unlocked_ioctl,
- #ifdef HAVE_COMPAT_IOCTL
- .compat_ioctl = dahdi_ioctl_compat,
- #endif
- #else
- .ioctl = dahdi_ioctl,
- #endif
- .poll = dahdi_poll,
- .read = dahdi_no_read,
- .write = dahdi_no_write,
- };
- static const struct file_operations dahdi_timer_fops = {
- .owner = THIS_MODULE,
- .release = dahdi_timer_release,
- #ifdef HAVE_UNLOCKED_IOCTL
- .unlocked_ioctl = dahdi_timer_unlocked_ioctl,
- #ifdef HAVE_COMPAT_IOCTL
- .compat_ioctl = dahdi_timer_unlocked_ioctl,
- #endif
- #else
- .ioctl = dahdi_timer_ioctl,
- #endif
- .poll = dahdi_timer_poll,
- .read = dahdi_no_read,
- .write = dahdi_no_write,
- };
- /*
- * DAHDI stability should not depend on the calling process behaviour.
- * In case of suprise device removal, we should be able to return
- * sane results (-ENODEV) even after the underlying device was released.
- *
- * This should be OK even if the calling process (hint, hint Asterisk)
- * ignores the system calls return value.
- *
- * We simply use dummy file_operations to implement this.
- */
- /*
- * Common behaviour called from all other nodev_*() file_operations
- */
- static int nodev_common(const char msg[])
- {
- if (printk_ratelimit()) {
- module_printk(KERN_NOTICE,
- "nodev: %s: process %d still calling\n",
- msg, current->tgid);
- }
- msleep(5);
- return -ENODEV;
- }
- static ssize_t nodev_chan_read(struct file *file, char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- return nodev_common("read");
- }
- static ssize_t nodev_chan_write(struct file *file, const char __user *usrbuf,
- size_t count, loff_t *ppos)
- {
- return nodev_common("write");
- }
- static unsigned int
- nodev_chan_poll(struct file *file, struct poll_table_struct *wait_table)
- {
- return nodev_common("poll");
- }
- static long
- nodev_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long data)
- {
- switch (cmd) {
- case DAHDI_GETEVENT: /* Get event on queue */
- /*
- * Hint the bugger that the channel is gone for good
- */
- put_user(DAHDI_EVENT_REMOVED, (int __user *)data);
- break;
- }
- return nodev_common("ioctl");
- }
- #ifndef HAVE_UNLOCKED_IOCTL
- static int nodev_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long data)
- {
- return nodev_unlocked_ioctl(file, cmd, data);
- }
- #endif
- #ifdef HAVE_COMPAT_IOCTL
- static long nodev_ioctl_compat(struct file *file, unsigned int cmd,
- unsigned long data)
- {
- if (cmd == DAHDI_SFCONFIG)
- return -ENOTTY; /* Not supported yet */
- return nodev_unlocked_ioctl(file, cmd, data);
- }
- #endif
- static const struct file_operations nodev_fops = {
- .owner = THIS_MODULE,
- #ifdef HAVE_UNLOCKED_IOCTL
- .unlocked_ioctl = nodev_unlocked_ioctl,
- #ifdef HAVE_COMPAT_IOCTL
- .compat_ioctl = nodev_ioctl_compat,
- #endif
- #else
- .ioctl = nodev_ioctl,
- #endif
- .read = nodev_chan_read,
- .write = nodev_chan_write,
- .poll = nodev_chan_poll,
- };
- static const struct file_operations dahdi_chan_fops = {
- .owner = THIS_MODULE,
- .open = dahdi_open,
- .release = dahdi_release,
- #ifdef HAVE_UNLOCKED_IOCTL
- .unlocked_ioctl = dahdi_unlocked_ioctl,
- #ifdef HAVE_COMPAT_IOCTL
- .compat_ioctl = dahdi_ioctl_compat,
- #endif
- #else
- .ioctl = dahdi_ioctl,
- #endif
- .read = dahdi_chan_read,
- .write = dahdi_chan_write,
- .poll = dahdi_chan_poll,
- };
- #ifdef CONFIG_DAHDI_WATCHDOG
- static struct timer_list watchdogtimer;
- static void watchdog_check(unsigned long ignored)
- {
- unsigned long flags;
- static int wdcheck=0;
- struct dahdi_span *s;
- spin_lock_irqsave(&chan_lock, flags);
- list_for_each_entry(s, &span_list, spans_node) {
- if (s->flags & DAHDI_FLAG_RUNNING) {
- if (s->watchcounter == DAHDI_WATCHDOG_INIT) {
- /* Whoops, dead card */
- if ((s->watchstate == DAHDI_WATCHSTATE_OK) ||
- (s->watchstate == DAHDI_WATCHSTATE_UNKNOWN)) {
- s->watchstate = DAHDI_WATCHSTATE_RECOVERING;
- if (s->ops->watchdog) {
- module_printk(KERN_NOTICE, "Kicking span %s\n", s->name);
- s->ops->watchdog(s, DAHDI_WATCHDOG_NOINTS);
- } else {
- module_printk(KERN_NOTICE, "Span %s is dead with no revival\n", s->name);
- s->watchstate = DAHDI_WATCHSTATE_FAILED;
- }
- }
- } else {
- if ((s->watchstate != DAHDI_WATCHSTATE_OK) &&
- (s->watchstate != DAHDI_WATCHSTATE_UNKNOWN))
- module_printk(KERN_NOTICE, "Span %s is alive!\n", s->name);
- s->watchstate = DAHDI_WATCHSTATE_OK;
- }
- s->watchcounter = DAHDI_WATCHDOG_INIT;
- }
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if (!wdcheck) {
- module_printk(KERN_NOTICE, "watchdog on duty!\n");
- wdcheck=1;
- }
- mod_timer(&watchdogtimer, jiffies + 2);
- }
- static int __init watchdog_init(void)
- {
- init_timer(&watchdogtimer);
- watchdogtimer.expires = 0;
- watchdogtimer.data =0;
- watchdogtimer.function = watchdog_check;
- /* Run every couple of jiffy or so */
- mod_timer(&watchdogtimer, jiffies + 2);
- return 0;
- }
- static void __exit watchdog_cleanup(void)
- {
- del_timer(&watchdogtimer);
- }
- #endif
- static int __init dahdi_init(void)
- {
- int res = 0;
- module_printk(KERN_INFO, "Version: %s\n", dahdi_version);
- #ifdef CONFIG_PROC_FS
- root_proc_entry = proc_mkdir("dahdi", NULL);
- if (!root_proc_entry) {
- dahdi_err("dahdi init: Failed creating /proc/dahdi\n");
- return -EEXIST;
- }
- #endif
- res = dahdi_sysfs_init(&dahdi_fops);
- if (res)
- goto failed_driver_init;
- dahdi_conv_init();
- fasthdlc_precalc();
- rotate_sums();
- #ifdef CONFIG_DAHDI_WATCHDOG
- watchdog_init();
- #endif
- coretimer_init();
- res = dahdi_register_echocan_factory(&hwec_factory);
- if (res) {
- WARN_ON(1);
- goto failed_register_ec_factory;
- }
- return 0;
- failed_register_ec_factory:
- coretimer_cleanup();
- dahdi_sysfs_exit();
- failed_driver_init:
- if (root_proc_entry) {
- remove_proc_entry("dahdi", NULL);
- root_proc_entry = NULL;
- }
- return res;
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
- #ifdef CONFIG_PCI
- void dahdi_pci_disable_link_state(struct pci_dev *pdev, int state)
- {
- u16 reg16;
- int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- state &= (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
- if (!pos)
- return;
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16);
- reg16 &= ~(state);
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
- }
- EXPORT_SYMBOL(dahdi_pci_disable_link_state);
- #endif /* CONFIG_PCI */
- #endif /* 2.6.25 */
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
- static inline void flush_find_master_work(void)
- {
- flush_scheduled_work();
- }
- #else
- static inline void flush_find_master_work(void)
- {
- cancel_work_sync(&find_master_work);
- }
- #endif
- static void __exit dahdi_cleanup(void)
- {
- struct dahdi_zone *z;
- dahdi_unregister_echocan_factory(&hwec_factory);
- coretimer_cleanup();
- dahdi_sysfs_exit();
- #ifdef CONFIG_PROC_FS
- if (root_proc_entry) {
- remove_proc_entry("dahdi", NULL);
- root_proc_entry = NULL;
- }
- #endif
- module_printk(KERN_INFO, "Telephony Interface Unloaded\n");
- spin_lock(&zone_lock);
- while (!list_empty(&tone_zones)) {
- z = list_entry(tone_zones.next, struct dahdi_zone, node);
- list_del(&z->node);
- if (!tone_zone_put(z)) {
- module_printk(KERN_WARNING,
- "Potential memory leak detected in %s\n",
- __func__);
- }
- }
- spin_unlock(&zone_lock);
- #ifdef CONFIG_DAHDI_WATCHDOG
- watchdog_cleanup();
- #endif
- flush_find_master_work();
- }
- module_init(dahdi_init);
- module_exit(dahdi_cleanup);
|