123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * 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. See the LICENSE file
- * at the top of the source tree.
- */
- /*!
- * \file
- * \brief Bluetooth Mobile Device channel driver
- *
- * \author Dave Bowerman <david.bowerman@gmail.com>
- *
- * \ingroup channel_drivers
- */
- /*! \li \ref chan_mobile.c uses the configuration file \ref chan_mobile.conf
- * \addtogroup configuration_file Configuration Files
- */
- /*!
- * \page chan_mobile.conf chan_mobile.conf
- * \verbinclude chan_mobile.conf.sample
- */
- /*** MODULEINFO
- <depend>bluetooth</depend>
- <defaultenabled>no</defaultenabled>
- <support_level>extended</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <pthread.h>
- #include <signal.h>
- #include <bluetooth/bluetooth.h>
- #include <bluetooth/hci.h>
- #include <bluetooth/hci_lib.h>
- #include <bluetooth/sdp.h>
- #include <bluetooth/sdp_lib.h>
- #include <bluetooth/rfcomm.h>
- #include <bluetooth/sco.h>
- #include <bluetooth/l2cap.h>
- #include "asterisk/compat.h"
- #include "asterisk/lock.h"
- #include "asterisk/channel.h"
- #include "asterisk/config.h"
- #include "asterisk/logger.h"
- #include "asterisk/module.h"
- #include "asterisk/pbx.h"
- #include "asterisk/options.h"
- #include "asterisk/utils.h"
- #include "asterisk/linkedlists.h"
- #include "asterisk/cli.h"
- #include "asterisk/devicestate.h"
- #include "asterisk/causes.h"
- #include "asterisk/dsp.h"
- #include "asterisk/app.h"
- #include "asterisk/manager.h"
- #include "asterisk/io.h"
- #define MBL_CONFIG "chan_mobile.conf"
- #define MBL_CONFIG_OLD "mobile.conf"
- #define DEVICE_FRAME_SIZE 48
- #define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
- #define CHANNEL_FRAME_SIZE 320
- static struct ast_format prefformat;
- static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
- static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */
- static sdp_session_t *sdp_session;
- AST_MUTEX_DEFINE_STATIC(unload_mutex);
- static int unloading_flag = 0;
- static inline int check_unloading(void);
- static inline void set_unloading(void);
- enum mbl_type {
- MBL_TYPE_PHONE,
- MBL_TYPE_HEADSET
- };
- struct adapter_pvt {
- int dev_id; /* device id */
- int hci_socket; /* device descriptor */
- char id[31]; /* the 'name' from mobile.conf */
- bdaddr_t addr; /* adddress of adapter */
- unsigned int inuse:1; /* are we in use ? */
- unsigned int alignment_detection:1; /* do alignment detection on this adpater? */
- struct io_context *io; /*!< io context for audio connections */
- struct io_context *accept_io; /*!< io context for sco listener */
- int *sco_id; /*!< the io context id of the sco listener socket */
- int sco_socket; /*!< sco listener socket */
- pthread_t sco_listener_thread; /*!< sco listener thread */
- AST_LIST_ENTRY(adapter_pvt) entry;
- };
- static AST_RWLIST_HEAD_STATIC(adapters, adapter_pvt);
- struct msg_queue_entry;
- struct hfp_pvt;
- struct mbl_pvt {
- struct ast_channel *owner; /* Channel we belong to, possibly NULL */
- struct ast_frame fr; /* "null" frame */
- ast_mutex_t lock; /*!< pvt lock */
- /*! queue for messages we are expecting */
- AST_LIST_HEAD_NOLOCK(msg_queue, msg_queue_entry) msg_queue;
- enum mbl_type type; /* Phone or Headset */
- char id[31]; /* The id from mobile.conf */
- int group; /* group number for group dialling */
- bdaddr_t addr; /* address of device */
- struct adapter_pvt *adapter; /* the adapter we use */
- char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
- struct hfp_pvt *hfp; /*!< hfp pvt */
- int rfcomm_port; /* rfcomm port number */
- int rfcomm_socket; /* rfcomm socket descriptor */
- char rfcomm_buf[256];
- char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET];
- struct ast_smoother *smoother; /* our smoother, for making 48 byte frames */
- int sco_socket; /* sco socket descriptor */
- pthread_t monitor_thread; /* monitor thread handle */
- int timeout; /*!< used to set the timeout for rfcomm data (may be used in the future) */
- unsigned int no_callsetup:1;
- unsigned int has_sms:1;
- unsigned int do_alignment_detection:1;
- unsigned int alignment_detection_triggered:1;
- unsigned int blackberry:1;
- short alignment_samples[4];
- int alignment_count;
- int ring_sched_id;
- struct ast_dsp *dsp;
- struct ast_sched_context *sched;
- int hangupcause;
- /* flags */
- unsigned int outgoing:1; /*!< outgoing call */
- unsigned int incoming:1; /*!< incoming call */
- unsigned int outgoing_sms:1; /*!< outgoing sms */
- unsigned int incoming_sms:1; /*!< outgoing sms */
- unsigned int needcallerid:1; /*!< we need callerid */
- unsigned int needchup:1; /*!< we need to send a chup */
- unsigned int needring:1; /*!< we need to send a RING */
- unsigned int answered:1; /*!< we sent/received an answer */
- unsigned int connected:1; /*!< do we have an rfcomm connection to a device */
- AST_LIST_ENTRY(mbl_pvt) entry;
- };
- static AST_RWLIST_HEAD_STATIC(devices, mbl_pvt);
- static int handle_response_ok(struct mbl_pvt *pvt, char *buf);
- static int handle_response_error(struct mbl_pvt *pvt, char *buf);
- static int handle_response_ciev(struct mbl_pvt *pvt, char *buf);
- static int handle_response_clip(struct mbl_pvt *pvt, char *buf);
- static int handle_response_ring(struct mbl_pvt *pvt, char *buf);
- static int handle_response_cmti(struct mbl_pvt *pvt, char *buf);
- static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf);
- static int handle_response_cusd(struct mbl_pvt *pvt, char *buf);
- static int handle_response_busy(struct mbl_pvt *pvt);
- static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf);
- static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf);
- static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf);
- /* CLI stuff */
- static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *handle_cli_mobile_cusd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static struct ast_cli_entry mbl_cli[] = {
- AST_CLI_DEFINE(handle_cli_mobile_show_devices, "Show Bluetooth Cell / Mobile devices"),
- AST_CLI_DEFINE(handle_cli_mobile_search, "Search for Bluetooth Cell / Mobile devices"),
- AST_CLI_DEFINE(handle_cli_mobile_rfcomm, "Send commands to the rfcomm port for debugging"),
- AST_CLI_DEFINE(handle_cli_mobile_cusd, "Send CUSD commands to the mobile"),
- };
- /* App stuff */
- static char *app_mblstatus = "MobileStatus";
- static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
- static char *mblstatus_desc =
- "MobileStatus(Device,Variable)\n"
- " Device - Id of mobile device from mobile.conf\n"
- " Variable - Variable to store status in will be 1-3.\n"
- " In order, Disconnected, Connected & Free, Connected & Busy.\n";
- static char *app_mblsendsms = "MobileSendSMS";
- static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
- static char *mblsendsms_desc =
- "MobileSendSms(Device,Dest,Message)\n"
- " Device - Id of device from mobile.conf\n"
- " Dest - destination\n"
- " Message - text of the message\n";
- static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
- const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor);
- static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap,
- const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
- static int mbl_call(struct ast_channel *ast, const char *dest, int timeout);
- static int mbl_hangup(struct ast_channel *ast);
- static int mbl_answer(struct ast_channel *ast);
- static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
- static struct ast_frame *mbl_read(struct ast_channel *ast);
- static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
- static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
- static int mbl_devicestate(const char *data);
- static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen);
- static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control);
- static int mbl_queue_hangup(struct mbl_pvt *pvt);
- static int mbl_ast_hangup(struct mbl_pvt *pvt);
- static int mbl_has_service(struct mbl_pvt *pvt);
- static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel);
- static int rfcomm_write(int rsock, char *buf);
- static int rfcomm_write_full(int rsock, char *buf, size_t count);
- static int rfcomm_wait(int rsock, int *ms);
- static ssize_t rfcomm_read(int rsock, char *buf, size_t count);
- static int sco_connect(bdaddr_t src, bdaddr_t dst);
- static int sco_write(int s, char *buf, int len);
- static int sco_accept(int *id, int fd, short events, void *data);
- static int sco_bind(struct adapter_pvt *adapter);
- static void *do_sco_listen(void *data);
- static int sdp_search(char *addr, int profile);
- static int headset_send_ring(const void *data);
- /*
- * bluetooth handsfree profile helpers
- */
- #define HFP_HF_ECNR (1 << 0)
- #define HFP_HF_CW (1 << 1)
- #define HFP_HF_CID (1 << 2)
- #define HFP_HF_VOICE (1 << 3)
- #define HFP_HF_VOLUME (1 << 4)
- #define HFP_HF_STATUS (1 << 5)
- #define HFP_HF_CONTROL (1 << 6)
- #define HFP_AG_CW (1 << 0)
- #define HFP_AG_ECNR (1 << 1)
- #define HFP_AG_VOICE (1 << 2)
- #define HFP_AG_RING (1 << 3)
- #define HFP_AG_TAG (1 << 4)
- #define HFP_AG_REJECT (1 << 5)
- #define HFP_AG_STATUS (1 << 6)
- #define HFP_AG_CONTROL (1 << 7)
- #define HFP_AG_ERRORS (1 << 8)
- #define HFP_CIND_UNKNOWN -1
- #define HFP_CIND_NONE 0
- #define HFP_CIND_SERVICE 1
- #define HFP_CIND_CALL 2
- #define HFP_CIND_CALLSETUP 3
- #define HFP_CIND_CALLHELD 4
- #define HFP_CIND_SIGNAL 5
- #define HFP_CIND_ROAM 6
- #define HFP_CIND_BATTCHG 7
- /* call indicator values */
- #define HFP_CIND_CALL_NONE 0
- #define HFP_CIND_CALL_ACTIVE 1
- /* callsetup indicator values */
- #define HFP_CIND_CALLSETUP_NONE 0
- #define HFP_CIND_CALLSETUP_INCOMING 1
- #define HFP_CIND_CALLSETUP_OUTGOING 2
- #define HFP_CIND_CALLSETUP_ALERTING 3
- /* service indicator values */
- #define HFP_CIND_SERVICE_NONE 0
- #define HFP_CIND_SERVICE_AVAILABLE 1
- /*!
- * \brief This struct holds HFP features that we support.
- */
- struct hfp_hf {
- int ecnr:1; /*!< echo-cancel/noise reduction */
- int cw:1; /*!< call waiting and three way calling */
- int cid:1; /*!< cli presentation (callier id) */
- int voice:1; /*!< voice recognition activation */
- int volume:1; /*!< remote volume control */
- int status:1; /*!< enhanced call status */
- int control:1; /*!< enhanced call control*/
- };
- /*!
- * \brief This struct holds HFP features the AG supports.
- */
- struct hfp_ag {
- int cw:1; /*!< three way calling */
- int ecnr:1; /*!< echo-cancel/noise reduction */
- int voice:1; /*!< voice recognition */
- int ring:1; /*!< in band ring tone capability */
- int tag:1; /*!< attach a number to a voice tag */
- int reject:1; /*!< ability to reject a call */
- int status:1; /*!< enhanced call status */
- int control:1; /*!< enhanced call control*/
- int errors:1; /*!< extended error result codes*/
- };
- /*!
- * \brief This struct holds mappings for indications.
- */
- struct hfp_cind {
- int service; /*!< whether we have service or not */
- int call; /*!< call state */
- int callsetup; /*!< bluetooth call setup indications */
- int callheld; /*!< bluetooth call hold indications */
- int signal; /*!< signal strength */
- int roam; /*!< roaming indicator */
- int battchg; /*!< battery charge indicator */
- };
- /*!
- * \brief This struct holds state information about the current hfp connection.
- */
- struct hfp_pvt {
- struct mbl_pvt *owner; /*!< the mbl_pvt struct that owns this struct */
- int initialized:1; /*!< whether a service level connection exists or not */
- int nocallsetup:1; /*!< whether we detected a callsetup indicator */
- struct hfp_ag brsf; /*!< the supported feature set of the AG */
- int cind_index[16]; /*!< the cind/ciev index to name mapping for this AG */
- int cind_state[16]; /*!< the cind/ciev state for this AG */
- struct hfp_cind cind_map; /*!< the cind name to index mapping for this AG */
- int rsock; /*!< our rfcomm socket */
- int rport; /*!< our rfcomm port */
- int sent_alerting; /*!< have we sent alerting? */
- };
- /* Our supported features.
- * we only support caller id
- */
- static struct hfp_hf hfp_our_brsf = {
- .ecnr = 0,
- .cw = 0,
- .cid = 1,
- .voice = 0,
- .volume = 0,
- .status = 0,
- .control = 0,
- };
- static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value);
- static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf);
- static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf);
- static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, char **text);
- static int hfp_parse_brsf(struct hfp_pvt *hfp, const char *buf);
- static int hfp_parse_cind(struct hfp_pvt *hfp, char *buf);
- static int hfp_parse_cind_test(struct hfp_pvt *hfp, char *buf);
- static char *hfp_parse_cusd(struct hfp_pvt *hfp, char *buf);
- static int hfp_brsf2int(struct hfp_hf *hf);
- static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag);
- static int hfp_send_brsf(struct hfp_pvt *hfp, struct hfp_hf *brsf);
- static int hfp_send_cind(struct hfp_pvt *hfp);
- static int hfp_send_cind_test(struct hfp_pvt *hfp);
- static int hfp_send_cmer(struct hfp_pvt *hfp, int status);
- static int hfp_send_clip(struct hfp_pvt *hfp, int status);
- static int hfp_send_vgs(struct hfp_pvt *hfp, int value);
- #if 0
- static int hfp_send_vgm(struct hfp_pvt *hfp, int value);
- #endif
- static int hfp_send_dtmf(struct hfp_pvt *hfp, char digit);
- static int hfp_send_cmgf(struct hfp_pvt *hfp, int mode);
- static int hfp_send_cnmi(struct hfp_pvt *hfp);
- static int hfp_send_cmgr(struct hfp_pvt *hfp, int index);
- static int hfp_send_cmgs(struct hfp_pvt *hfp, const char *number);
- static int hfp_send_sms_text(struct hfp_pvt *hfp, const char *message);
- static int hfp_send_chup(struct hfp_pvt *hfp);
- static int hfp_send_atd(struct hfp_pvt *hfp, const char *number);
- static int hfp_send_ata(struct hfp_pvt *hfp);
- static int hfp_send_cusd(struct hfp_pvt *hfp, const char *code);
- /*
- * bluetooth headset profile helpers
- */
- static int hsp_send_ok(int rsock);
- static int hsp_send_error(int rsock);
- static int hsp_send_vgs(int rsock, int gain);
- static int hsp_send_vgm(int rsock, int gain);
- static int hsp_send_ring(int rsock);
- /*
- * Hayes AT command helpers
- */
- typedef enum {
- /* errors */
- AT_PARSE_ERROR = -2,
- AT_READ_ERROR = -1,
- AT_UNKNOWN = 0,
- /* at responses */
- AT_OK,
- AT_ERROR,
- AT_RING,
- AT_BRSF,
- AT_CIND,
- AT_CIEV,
- AT_CLIP,
- AT_CMTI,
- AT_CMGR,
- AT_SMS_PROMPT,
- AT_CMS_ERROR,
- /* at commands */
- AT_A,
- AT_D,
- AT_CHUP,
- AT_CKPD,
- AT_CMGS,
- AT_VGM,
- AT_VGS,
- AT_VTS,
- AT_CMGF,
- AT_CNMI,
- AT_CMER,
- AT_CIND_TEST,
- AT_CUSD,
- AT_BUSY,
- AT_NO_DIALTONE,
- AT_NO_CARRIER,
- AT_ECAM,
- } at_message_t;
- static int at_match_prefix(char *buf, char *prefix);
- static at_message_t at_read_full(int rsock, char *buf, size_t count);
- static inline const char *at_msg2str(at_message_t msg);
- struct msg_queue_entry {
- at_message_t expected;
- at_message_t response_to;
- void *data;
- AST_LIST_ENTRY(msg_queue_entry) entry;
- };
- static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to);
- static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data);
- static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt);
- static void msg_queue_free_and_pop(struct mbl_pvt *pvt);
- static void msg_queue_flush(struct mbl_pvt *pvt);
- static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt);
- /*
- * channel stuff
- */
- static struct ast_channel_tech mbl_tech = {
- .type = "Mobile",
- .description = "Bluetooth Mobile Device Channel Driver",
- .requester = mbl_request,
- .call = mbl_call,
- .hangup = mbl_hangup,
- .answer = mbl_answer,
- .send_digit_end = mbl_digit_end,
- .read = mbl_read,
- .write = mbl_write,
- .fixup = mbl_fixup,
- .devicestate = mbl_devicestate
- };
- /* CLI Commands implementation */
- static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct mbl_pvt *pvt;
- char bdaddr[18];
- char group[6];
- #define FORMAT1 "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-10.10s %-3.3s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "mobile show devices";
- e->usage =
- "Usage: mobile show devices\n"
- " Shows the state of Bluetooth Cell / Mobile devices.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT1, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS");
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- ast_mutex_lock(&pvt->lock);
- ba2str(&pvt->addr, bdaddr);
- snprintf(group, sizeof(group), "%d", pvt->group);
- ast_cli(a->fd, FORMAT1,
- pvt->id,
- bdaddr,
- group,
- pvt->adapter->id,
- pvt->connected ? "Yes" : "No",
- (!pvt->connected) ? "None" : (pvt->owner) ? "Busy" : (pvt->outgoing_sms || pvt->incoming_sms) ? "SMS" : (mbl_has_service(pvt)) ? "Free" : "No Service",
- (pvt->has_sms) ? "Yes" : "No"
- );
- ast_mutex_unlock(&pvt->lock);
- }
- AST_RWLIST_UNLOCK(&devices);
- #undef FORMAT1
- return CLI_SUCCESS;
- }
- static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct adapter_pvt *adapter;
- inquiry_info *ii = NULL;
- int max_rsp, num_rsp;
- int len, flags;
- int i, phport, hsport;
- char addr[19] = {0};
- char name[31] = {0};
- #define FORMAT1 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
- #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "mobile search";
- e->usage =
- "Usage: mobile search\n"
- " Searches for Bluetooth Cell / Mobile devices in range.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 2)
- return CLI_SHOWUSAGE;
- /* find a free adapter */
- AST_RWLIST_RDLOCK(&adapters);
- AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
- if (!adapter->inuse)
- break;
- }
- AST_RWLIST_UNLOCK(&adapters);
- if (!adapter) {
- ast_cli(a->fd, "All Bluetooth adapters are in use at this time.\n");
- return CLI_SUCCESS;
- }
- len = 8;
- max_rsp = 255;
- flags = IREQ_CACHE_FLUSH;
- ii = ast_alloca(max_rsp * sizeof(inquiry_info));
- num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags);
- if (num_rsp > 0) {
- ast_cli(a->fd, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
- for (i = 0; i < num_rsp; i++) {
- ba2str(&(ii + i)->bdaddr, addr);
- name[0] = 0x00;
- if (hci_read_remote_name(adapter->hci_socket, &(ii + i)->bdaddr, sizeof(name) - 1, name, 0) < 0)
- strcpy(name, "[unknown]");
- phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
- if (!phport)
- hsport = sdp_search(addr, HEADSET_PROFILE_ID);
- else
- hsport = 0;
- ast_cli(a->fd, FORMAT2, addr, name, (phport > 0 || hsport > 0) ? "Yes" : "No",
- (phport > 0) ? "Phone" : "Headset", (phport > 0) ? phport : hsport);
- }
- } else
- ast_cli(a->fd, "No Bluetooth Cell / Mobile devices found.\n");
- #undef FORMAT1
- #undef FORMAT2
- return CLI_SUCCESS;
- }
- static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- char buf[128];
- struct mbl_pvt *pvt = NULL;
- switch (cmd) {
- case CLI_INIT:
- e->command = "mobile rfcomm";
- e->usage =
- "Usage: mobile rfcomm <device ID> <command>\n"
- " Send <command> to the rfcomm port on the device\n"
- " with the specified <device ID>.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!strcmp(pvt->id, a->argv[2]))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt) {
- ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
- goto e_return;
- }
- ast_mutex_lock(&pvt->lock);
- if (!pvt->connected) {
- ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
- goto e_unlock_pvt;
- }
- snprintf(buf, sizeof(buf), "%s\r", a->argv[3]);
- rfcomm_write(pvt->rfcomm_socket, buf);
- msg_queue_push(pvt, AT_OK, AT_UNKNOWN);
- e_unlock_pvt:
- ast_mutex_unlock(&pvt->lock);
- e_return:
- return CLI_SUCCESS;
- }
- static char *handle_cli_mobile_cusd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- char buf[128];
- struct mbl_pvt *pvt = NULL;
- switch (cmd) {
- case CLI_INIT:
- e->command = "mobile cusd";
- e->usage =
- "Usage: mobile cusd <device ID> <command>\n"
- " Send cusd <command> to the rfcomm port on the device\n"
- " with the specified <device ID>.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!strcmp(pvt->id, a->argv[2]))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt) {
- ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
- goto e_return;
- }
- ast_mutex_lock(&pvt->lock);
- if (!pvt->connected) {
- ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
- goto e_unlock_pvt;
- }
- snprintf(buf, sizeof(buf), "%s", a->argv[3]);
- if (hfp_send_cusd(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CUSD)) {
- ast_cli(a->fd, "[%s] error sending CUSD\n", pvt->id);
- goto e_unlock_pvt;
- }
- e_unlock_pvt:
- ast_mutex_unlock(&pvt->lock);
- e_return:
- return CLI_SUCCESS;
- }
- /*
- Dialplan applications implementation
- */
- static int mbl_status_exec(struct ast_channel *ast, const char *data)
- {
- struct mbl_pvt *pvt;
- char *parse;
- int stat;
- char status[2];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(device);
- AST_APP_ARG(variable);
- );
- if (ast_strlen_zero(data))
- return -1;
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable))
- return -1;
- stat = 1;
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!strcmp(pvt->id, args.device))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (pvt) {
- ast_mutex_lock(&pvt->lock);
- if (pvt->connected)
- stat = 2;
- if (pvt->owner)
- stat = 3;
- ast_mutex_unlock(&pvt->lock);
- }
- snprintf(status, sizeof(status), "%d", stat);
- pbx_builtin_setvar_helper(ast, args.variable, status);
- return 0;
- }
- static int mbl_sendsms_exec(struct ast_channel *ast, const char *data)
- {
- struct mbl_pvt *pvt;
- char *parse, *message;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(device);
- AST_APP_ARG(dest);
- AST_APP_ARG(message);
- );
- if (ast_strlen_zero(data))
- return -1;
- parse = ast_strdupa(data);
- AST_STANDARD_APP_ARGS(args, parse);
- if (ast_strlen_zero(args.device)) {
- ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
- return -1;
- }
- if (ast_strlen_zero(args.dest)) {
- ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
- return -1;
- }
- if (ast_strlen_zero(args.message)) {
- ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n");
- return -1;
- }
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!strcmp(pvt->id, args.device))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt) {
- ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device);
- goto e_return;
- }
- ast_mutex_lock(&pvt->lock);
- if (!pvt->connected) {
- ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device);
- goto e_unlock_pvt;
- }
- if (!pvt->has_sms) {
- ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device);
- goto e_unlock_pvt;
- }
- message = ast_strdup(args.message);
- if (hfp_send_cmgs(pvt->hfp, args.dest)
- || msg_queue_push_data(pvt, AT_SMS_PROMPT, AT_CMGS, message)) {
- ast_log(LOG_ERROR, "[%s] problem sending SMS message\n", pvt->id);
- goto e_free_message;
- }
- ast_mutex_unlock(&pvt->lock);
- return 0;
- e_free_message:
- ast_free(message);
- e_unlock_pvt:
- ast_mutex_unlock(&pvt->lock);
- e_return:
- return -1;
- }
- /*
- Channel Driver callbacks
- */
- static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
- const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
- {
- struct ast_channel *chn;
- pvt->answered = 0;
- pvt->alignment_count = 0;
- pvt->alignment_detection_triggered = 0;
- if (pvt->adapter->alignment_detection)
- pvt->do_alignment_detection = 1;
- else
- pvt->do_alignment_detection = 0;
- ast_smoother_reset(pvt->smoother, DEVICE_FRAME_SIZE);
- ast_dsp_digitreset(pvt->dsp);
- chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context,
- assignedids, requestor, 0,
- "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
- if (!chn) {
- goto e_return;
- }
- ast_channel_tech_set(chn, &mbl_tech);
- ast_format_cap_add(ast_channel_nativeformats(chn), &prefformat);
- ast_format_copy(ast_channel_rawreadformat(chn), &prefformat);
- ast_format_copy(ast_channel_rawwriteformat(chn), &prefformat);
- ast_format_copy(ast_channel_writeformat(chn), &prefformat);
- ast_format_copy(ast_channel_readformat(chn), &prefformat);
- ast_channel_tech_pvt_set(chn, pvt);
- if (state == AST_STATE_RING)
- ast_channel_rings_set(chn, 1);
- ast_channel_language_set(chn, "en");
- pvt->owner = chn;
- if (pvt->sco_socket != -1) {
- ast_channel_set_fd(chn, 0, pvt->sco_socket);
- }
- ast_channel_unlock(chn);
- return chn;
- e_return:
- return NULL;
- }
- static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap,
- const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
- {
- struct ast_channel *chn = NULL;
- struct mbl_pvt *pvt;
- char *dest_dev = NULL;
- char *dest_num = NULL;
- int group = -1;
- if (!data) {
- ast_log(LOG_WARNING, "Channel requested with no data\n");
- *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
- return NULL;
- }
- if (!(ast_format_cap_iscompatible(cap, &prefformat))) {
- char tmp[256];
- ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), cap));
- *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
- return NULL;
- }
- dest_dev = ast_strdupa(data);
- dest_num = strchr(dest_dev, '/');
- if (dest_num)
- *dest_num++ = 0x00;
- if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
- group = atoi(&dest_dev[1]);
- }
- /* Find requested device and make sure it's connected. */
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (group > -1 && pvt->group == group && pvt->connected && !pvt->owner) {
- if (!mbl_has_service(pvt)) {
- continue;
- }
- break;
- } else if (!strcmp(pvt->id, dest_dev)) {
- break;
- }
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt || !pvt->connected || pvt->owner) {
- ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
- *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
- return NULL;
- }
- if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
- ast_log(LOG_WARNING, "Can't determine destination number.\n");
- *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
- return NULL;
- }
- ast_mutex_lock(&pvt->lock);
- chn = mbl_new(AST_STATE_DOWN, pvt, NULL, assignedids, requestor);
- ast_mutex_unlock(&pvt->lock);
- if (!chn) {
- ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
- *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
- return NULL;
- }
- return chn;
- }
- static int mbl_call(struct ast_channel *ast, const char *dest, int timeout)
- {
- struct mbl_pvt *pvt;
- char *dest_dev;
- char *dest_num = NULL;
- dest_dev = ast_strdupa(dest);
- pvt = ast_channel_tech_pvt(ast);
- if (pvt->type == MBL_TYPE_PHONE) {
- dest_num = strchr(dest_dev, '/');
- if (!dest_num) {
- ast_log(LOG_WARNING, "Cant determine destination number.\n");
- return -1;
- }
- *dest_num++ = 0x00;
- }
- if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
- ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
- return -1;
- }
- ast_debug(1, "Calling %s on %s\n", dest, ast_channel_name(ast));
- ast_mutex_lock(&pvt->lock);
- if (pvt->type == MBL_TYPE_PHONE) {
- if (hfp_send_atd(pvt->hfp, dest_num)) {
- ast_mutex_unlock(&pvt->lock);
- ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id);
- return -1;
- }
- pvt->hangupcause = 0;
- pvt->needchup = 1;
- msg_queue_push(pvt, AT_OK, AT_D);
- } else {
- if (hsp_send_ring(pvt->rfcomm_socket)) {
- ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
- ast_mutex_unlock(&pvt->lock);
- return -1;
- }
- if ((pvt->ring_sched_id = ast_sched_add(pvt->sched, 6000, headset_send_ring, pvt)) == -1) {
- ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
- ast_mutex_unlock(&pvt->lock);
- return -1;
- }
- pvt->outgoing = 1;
- pvt->needring = 1;
- }
- ast_mutex_unlock(&pvt->lock);
- return 0;
- }
- static int mbl_hangup(struct ast_channel *ast)
- {
- struct mbl_pvt *pvt;
- if (!ast_channel_tech_pvt(ast)) {
- ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
- return 0;
- }
- pvt = ast_channel_tech_pvt(ast);
- ast_debug(1, "[%s] hanging up device\n", pvt->id);
- ast_mutex_lock(&pvt->lock);
- ast_channel_set_fd(ast, 0, -1);
- close(pvt->sco_socket);
- pvt->sco_socket = -1;
- if (pvt->needchup) {
- hfp_send_chup(pvt->hfp);
- msg_queue_push(pvt, AT_OK, AT_CHUP);
- pvt->needchup = 0;
- }
- pvt->outgoing = 0;
- pvt->incoming = 0;
- pvt->needring = 0;
- pvt->owner = NULL;
- ast_channel_tech_pvt_set(ast, NULL);
- ast_mutex_unlock(&pvt->lock);
- ast_setstate(ast, AST_STATE_DOWN);
- return 0;
- }
- static int mbl_answer(struct ast_channel *ast)
- {
- struct mbl_pvt *pvt;
- pvt = ast_channel_tech_pvt(ast);
- if (pvt->type == MBL_TYPE_HEADSET)
- return 0;
- ast_mutex_lock(&pvt->lock);
- if (pvt->incoming) {
- hfp_send_ata(pvt->hfp);
- msg_queue_push(pvt, AT_OK, AT_A);
- pvt->answered = 1;
- }
- ast_mutex_unlock(&pvt->lock);
- return 0;
- }
- static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
- {
- struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
- if (pvt->type == MBL_TYPE_HEADSET)
- return 0;
- ast_mutex_lock(&pvt->lock);
- if (hfp_send_dtmf(pvt->hfp, digit)) {
- ast_mutex_unlock(&pvt->lock);
- ast_debug(1, "[%s] error sending digit %c\n", pvt->id, digit);
- return -1;
- }
- msg_queue_push(pvt, AT_OK, AT_VTS);
- ast_mutex_unlock(&pvt->lock);
- ast_debug(1, "[%s] dialed %c\n", pvt->id, digit);
- return 0;
- }
- static struct ast_frame *mbl_read(struct ast_channel *ast)
- {
- struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
- struct ast_frame *fr = &ast_null_frame;
- int r;
- ast_debug(3, "*** mbl_read()\n");
- while (ast_mutex_trylock(&pvt->lock)) {
- CHANNEL_DEADLOCK_AVOIDANCE(ast);
- }
- if (!pvt->owner || pvt->sco_socket == -1) {
- goto e_return;
- }
- memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
- pvt->fr.frametype = AST_FRAME_VOICE;
- ast_format_set(&pvt->fr.subclass.format, DEVICE_FRAME_FORMAT, 0);
- pvt->fr.src = "Mobile";
- pvt->fr.offset = AST_FRIENDLY_OFFSET;
- pvt->fr.mallocd = 0;
- pvt->fr.delivery.tv_sec = 0;
- pvt->fr.delivery.tv_usec = 0;
- pvt->fr.data.ptr = pvt->io_buf + AST_FRIENDLY_OFFSET;
- if ((r = read(pvt->sco_socket, pvt->fr.data.ptr, DEVICE_FRAME_SIZE)) == -1) {
- if (errno != EAGAIN && errno != EINTR) {
- ast_debug(1, "[%s] read error %d, going to wait for new connection\n", pvt->id, errno);
- close(pvt->sco_socket);
- pvt->sco_socket = -1;
- ast_channel_set_fd(ast, 0, -1);
- }
- goto e_return;
- }
- pvt->fr.datalen = r;
- pvt->fr.samples = r / 2;
- if (pvt->do_alignment_detection)
- do_alignment_detection(pvt, pvt->fr.data.ptr, r);
- fr = ast_dsp_process(ast, pvt->dsp, &pvt->fr);
- ast_mutex_unlock(&pvt->lock);
- return fr;
- e_return:
- ast_mutex_unlock(&pvt->lock);
- return fr;
- }
- static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
- {
- struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
- struct ast_frame *f;
- ast_debug(3, "*** mbl_write\n");
- if (frame->frametype != AST_FRAME_VOICE) {
- return 0;
- }
- while (ast_mutex_trylock(&pvt->lock)) {
- CHANNEL_DEADLOCK_AVOIDANCE(ast);
- }
- ast_smoother_feed(pvt->smoother, frame);
- while ((f = ast_smoother_read(pvt->smoother))) {
- sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
- }
- ast_mutex_unlock(&pvt->lock);
- return 0;
- }
- static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
- {
- struct mbl_pvt *pvt = ast_channel_tech_pvt(newchan);
- if (!pvt) {
- ast_debug(1, "fixup failed, no pvt on newchan\n");
- return -1;
- }
- ast_mutex_lock(&pvt->lock);
- if (pvt->owner == oldchan)
- pvt->owner = newchan;
- ast_mutex_unlock(&pvt->lock);
- return 0;
- }
- static int mbl_devicestate(const char *data)
- {
- char *device;
- int res = AST_DEVICE_INVALID;
- struct mbl_pvt *pvt;
- device = ast_strdupa(S_OR(data, ""));
- ast_debug(1, "Checking device state for device %s\n", device);
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!strcmp(pvt->id, device))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt)
- return res;
- ast_mutex_lock(&pvt->lock);
- if (pvt->connected) {
- if (pvt->owner)
- res = AST_DEVICE_INUSE;
- else
- res = AST_DEVICE_NOT_INUSE;
- if (!mbl_has_service(pvt))
- res = AST_DEVICE_UNAVAILABLE;
- }
- ast_mutex_unlock(&pvt->lock);
- return res;
- }
- /*
- Callback helpers
- */
- /*
- do_alignment_detection()
- This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor.
- Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf
- Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
- The result is static or white noise on the inbound (from the adapter) leg of the call.
- This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
- Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
- then average the sum of the averages of frames 1, 2, and 3.
- Frame zero is usually zero.
- If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
- for each subsequent frame during the call.
- If the result is <= 100 then clear the flag so we don't come back in here...
- This seems to work OK....
- */
- static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen)
- {
- int i;
- short a, *s;
- char *p;
- if (pvt->alignment_detection_triggered) {
- for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
- *p = *(p-1);
- *(p+1) = 0;
- return;
- }
- if (pvt->alignment_count < 4) {
- s = (short *)buf;
- for (i=0, a=0; i<buflen/2; i++) {
- a += *s++;
- a /= i+1;
- }
- pvt->alignment_samples[pvt->alignment_count++] = a;
- return;
- }
- ast_debug(1, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
- a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
- a /= 3;
- if (a > 100) {
- pvt->alignment_detection_triggered = 1;
- ast_debug(1, "Alignment Detection Triggered.\n");
- } else
- pvt->do_alignment_detection = 0;
- }
- static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control)
- {
- for (;;) {
- if (pvt->owner) {
- if (ast_channel_trylock(pvt->owner)) {
- DEADLOCK_AVOIDANCE(&pvt->lock);
- } else {
- ast_queue_control(pvt->owner, control);
- ast_channel_unlock(pvt->owner);
- break;
- }
- } else
- break;
- }
- return 0;
- }
- static int mbl_queue_hangup(struct mbl_pvt *pvt)
- {
- for (;;) {
- if (pvt->owner) {
- if (ast_channel_trylock(pvt->owner)) {
- DEADLOCK_AVOIDANCE(&pvt->lock);
- } else {
- if (pvt->hangupcause != 0) {
- ast_channel_hangupcause_set(pvt->owner, pvt->hangupcause);
- }
- ast_queue_hangup(pvt->owner);
- ast_channel_unlock(pvt->owner);
- break;
- }
- } else
- break;
- }
- return 0;
- }
- static int mbl_ast_hangup(struct mbl_pvt *pvt)
- {
- ast_hangup(pvt->owner);
- return 0;
- }
- /*!
- * \brief Check if a mobile device has service.
- * \param pvt a mbl_pvt struct
- * \retval 1 this device has service
- * \retval 0 no service
- *
- * \note This function will always indicate that service is available if the
- * given device does not support service indication.
- */
- static int mbl_has_service(struct mbl_pvt *pvt)
- {
- if (pvt->type != MBL_TYPE_PHONE)
- return 1;
- if (!pvt->hfp->cind_map.service)
- return 1;
- if (pvt->hfp->cind_state[pvt->hfp->cind_map.service] == HFP_CIND_SERVICE_AVAILABLE)
- return 1;
- return 0;
- }
- /*
- rfcomm helpers
- */
- static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel)
- {
- struct sockaddr_rc addr;
- int s;
- if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
- ast_debug(1, "socket() failed (%d).\n", errno);
- return -1;
- }
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &src);
- addr.rc_channel = (uint8_t) 0;
- if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- ast_debug(1, "bind() failed (%d).\n", errno);
- close(s);
- return -1;
- }
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &dst);
- addr.rc_channel = remote_channel;
- if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- ast_debug(1, "connect() failed (%d).\n", errno);
- close(s);
- return -1;
- }
- return s;
- }
- /*!
- * \brief Write to an rfcomm socket.
- * \param rsock the socket to write to
- * \param buf the null terminated buffer to write
- *
- * This function will write characters from buf. The buffer must be null
- * terminated.
- *
- * \retval -1 error
- * \retval 0 success
- */
- static int rfcomm_write(int rsock, char *buf)
- {
- return rfcomm_write_full(rsock, buf, strlen(buf));
- }
- /*!
- * \brief Write to an rfcomm socket.
- * \param rsock the socket to write to
- * \param buf the buffer to write
- * \param count the number of characters from the buffer to write
- *
- * This function will write count characters from buf. It will always write
- * count chars unless it encounters an error.
- *
- * \retval -1 error
- * \retval 0 success
- */
- static int rfcomm_write_full(int rsock, char *buf, size_t count)
- {
- char *p = buf;
- ssize_t out_count;
- ast_debug(1, "rfcomm_write() (%d) [%.*s]\n", rsock, (int) count, buf);
- while (count > 0) {
- if ((out_count = write(rsock, p, count)) == -1) {
- ast_debug(1, "rfcomm_write() error [%d]\n", errno);
- return -1;
- }
- count -= out_count;
- p += out_count;
- }
- return 0;
- }
- /*!
- * \brief Wait for activity on an rfcomm socket.
- * \param rsock the socket to watch
- * \param ms a pointer to an int containing a timeout in ms
- * \return zero on timeout and the socket fd (non-zero) otherwise
- * \retval 0 timeout
- */
- static int rfcomm_wait(int rsock, int *ms)
- {
- int exception, outfd;
- outfd = ast_waitfor_n_fd(&rsock, 1, ms, &exception);
- if (outfd < 0)
- outfd = 0;
- return outfd;
- }
- #ifdef RFCOMM_READ_DEBUG
- #define rfcomm_read_debug(c) __rfcomm_read_debug(c)
- static void __rfcomm_read_debug(char c)
- {
- if (c == '\r')
- ast_debug(2, "rfcomm_read: \\r\n");
- else if (c == '\n')
- ast_debug(2, "rfcomm_read: \\n\n");
- else
- ast_debug(2, "rfcomm_read: %c\n", c);
- }
- #else
- #define rfcomm_read_debug(c)
- #endif
- /*!
- * \brief Append the given character to the given buffer and increase the
- * in_count.
- */
- static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c)
- {
- if (*in_count < count) {
- (*in_count)++;
- *(*buf)++ = c;
- }
- }
- /*!
- * \brief Read a character from the given stream and check if it matches what
- * we expected.
- */
- static int rfcomm_read_and_expect_char(int rsock, char *result, char expected)
- {
- int res;
- char c;
- if (!result)
- result = &c;
- if ((res = read(rsock, result, 1)) < 1) {
- return res;
- }
- rfcomm_read_debug(*result);
- if (*result != expected) {
- return -2;
- }
- return 1;
- }
- /*!
- * \brief Read a character from the given stream and append it to the given
- * buffer if it matches the expected character.
- */
- static int rfcomm_read_and_append_char(int rsock, char **buf, size_t count, size_t *in_count, char *result, char expected)
- {
- int res;
- char c;
- if (!result)
- result = &c;
- if ((res = rfcomm_read_and_expect_char(rsock, result, expected)) < 1) {
- return res;
- }
- rfcomm_append_buf(buf, count, in_count, *result);
- return 1;
- }
- /*!
- * \brief Read until \verbatim '\r\n'. \endverbatim
- * This function consumes the \verbatim'\r\n'\endverbatim but does not add it to buf.
- */
- static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- char c;
- while ((res = read(rsock, &c, 1)) == 1) {
- rfcomm_read_debug(c);
- if (c == '\r') {
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) == 1) {
- break;
- } else if (res == -2) {
- rfcomm_append_buf(buf, count, in_count, '\r');
- } else {
- rfcomm_append_buf(buf, count, in_count, '\r');
- break;
- }
- }
- rfcomm_append_buf(buf, count, in_count, c);
- }
- return res;
- }
- /*!
- * \brief Read the remainder of an AT SMS prompt.
- * \note the entire parsed string is \verbatim '\r\n> ' \endverbatim
- *
- * By the time this function is executed, only a ' ' is left to read.
- */
- static int rfcomm_read_sms_prompt(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, NULL, ' ')) < 1)
- goto e_return;
- return 1;
- e_return:
- ast_log(LOG_ERROR, "error parsing SMS prompt on rfcomm socket\n");
- return res;
- }
- /*!
- * \brief Read until a \verbatim \r\nOK\r\n \endverbatim message.
- */
- static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- char c;
- /* here, we read until finding a \r\n, then we read one character at a
- * time looking for the string '\r\nOK\r\n'. If we only find a partial
- * match, we place that in the buffer and try again. */
- for (;;) {
- if ((res = rfcomm_read_until_crlf(rsock, buf, count, in_count)) != 1) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- if ((res = rfcomm_read_and_expect_char(rsock, &c, 'O')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- if ((res = rfcomm_read_and_expect_char(rsock, &c, 'K')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- rfcomm_append_buf(buf, count, in_count, 'O');
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- rfcomm_append_buf(buf, count, in_count, 'O');
- rfcomm_append_buf(buf, count, in_count, 'K');
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
- if (res != -2) {
- break;
- }
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- rfcomm_append_buf(buf, count, in_count, 'O');
- rfcomm_append_buf(buf, count, in_count, 'K');
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, c);
- continue;
- }
- /* we have successfully parsed a '\r\nOK\r\n' string */
- return 1;
- }
- return res;
- }
- /*!
- * \brief Read the remainder of a +CMGR message.
- * \note the entire parsed string is \verbatim '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' \endverbatim
- */
- static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- /* append the \r\n that was stripped by the calling function */
- rfcomm_append_buf(buf, count, in_count, '\r');
- rfcomm_append_buf(buf, count, in_count, '\n');
- if ((res = rfcomm_read_until_ok(rsock, buf, count, in_count)) != 1) {
- ast_log(LOG_ERROR, "error reading +CMGR message on rfcomm socket\n");
- }
- return res;
- }
- /*!
- * \brief Read and AT result code.
- * \note the entire parsed string is \verbatim '\r\n<result code>\r\n' \endverbatim
- */
- static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- char c;
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) < 1) {
- goto e_return;
- }
- if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, &c, '>')) == 1) {
- return rfcomm_read_sms_prompt(rsock, buf, count, in_count);
- } else if (res != -2) {
- goto e_return;
- }
- rfcomm_append_buf(buf, count, in_count, c);
- res = rfcomm_read_until_crlf(rsock, buf, count, in_count);
- if (res != 1)
- return res;
- /* check for CMGR, which contains an embedded \r\n pairs terminated by
- * an \r\nOK\r\n message */
- if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
- return rfcomm_read_cmgr(rsock, buf, count, in_count);
- }
- return 1;
- e_return:
- ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket\n");
- return res;
- }
- /*!
- * \brief Read the remainder of an AT command.
- * \note the entire parsed string is \verbatim '<at command>\r' \endverbatim
- */
- static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
- {
- int res;
- char c;
- while ((res = read(rsock, &c, 1)) == 1) {
- rfcomm_read_debug(c);
- /* stop when we get to '\r' */
- if (c == '\r')
- break;
- rfcomm_append_buf(buf, count, in_count, c);
- }
- return res;
- }
- /*!
- * \brief Read one Hayes AT message from an rfcomm socket.
- * \param rsock the rfcomm socket to read from
- * \param buf the buffer to store the result in
- * \param count the size of the buffer or the maximum number of characters to read
- *
- * Here we need to read complete Hayes AT messages. The AT message formats we
- * support are listed below.
- *
- * \verbatim
- * \r\n<result code>\r\n
- * <at command>\r
- * \r\n>
- * \endverbatim
- *
- * These formats correspond to AT result codes, AT commands, and the AT SMS
- * prompt respectively. When messages are read the leading and trailing \verbatim '\r' \endverbatim
- * and \verbatim '\n' \endverbatim characters are discarded. If the given buffer is not large enough
- * to hold the response, what does not fit in the buffer will be dropped.
- *
- * \note The rfcomm connection to the device is asynchronous, so there is no
- * guarantee that responses will be returned in a single read() call. We handle
- * this by blocking until we can read an entire response.
- *
- * \retval 0 end of file
- * \retval -1 read error
- * \retval -2 parse error
- * \retval other the number of characters added to buf
- */
- static ssize_t rfcomm_read(int rsock, char *buf, size_t count)
- {
- ssize_t res;
- size_t in_count = 0;
- char c;
- if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) == 1) {
- res = rfcomm_read_result(rsock, &buf, count, &in_count);
- } else if (res == -2) {
- rfcomm_append_buf(&buf, count, &in_count, c);
- res = rfcomm_read_command(rsock, &buf, count, &in_count);
- }
- if (res < 1)
- return res;
- else
- return in_count;
- }
- /*
- sco helpers and callbacks
- */
- static int sco_connect(bdaddr_t src, bdaddr_t dst)
- {
- struct sockaddr_sco addr;
- int s;
- if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
- ast_debug(1, "socket() failed (%d).\n", errno);
- return -1;
- }
- /* XXX this does not work with the do_sco_listen() thread (which also bind()s
- * to this address). Also I am not sure if it is necessary. */
- #if 0
- memset(&addr, 0, sizeof(addr));
- addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, &src);
- if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- ast_debug(1, "bind() failed (%d).\n", errno);
- close(s);
- return -1;
- }
- #endif
- memset(&addr, 0, sizeof(addr));
- addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, &dst);
- if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- ast_debug(1, "sco connect() failed (%d).\n", errno);
- close(s);
- return -1;
- }
- return s;
- }
- static int sco_write(int s, char *buf, int len)
- {
- int r;
- if (s == -1) {
- ast_debug(3, "sco_write() not ready\n");
- return 0;
- }
- ast_debug(3, "sco_write()\n");
- r = write(s, buf, len);
- if (r == -1) {
- ast_debug(3, "sco write error %d\n", errno);
- return 0;
- }
- return 1;
- }
- /*!
- * \brief Accept SCO connections.
- * This function is an ast_io callback function used to accept incoming sco
- * audio connections.
- */
- static int sco_accept(int *id, int fd, short events, void *data)
- {
- struct adapter_pvt *adapter = (struct adapter_pvt *) data;
- struct sockaddr_sco addr;
- socklen_t addrlen;
- struct mbl_pvt *pvt;
- socklen_t len;
- char saddr[18];
- struct sco_options so;
- int sock;
- addrlen = sizeof(struct sockaddr_sco);
- if ((sock = accept(fd, (struct sockaddr *)&addr, &addrlen)) == -1) {
- ast_log(LOG_ERROR, "error accepting audio connection on adapter %s\n", adapter->id);
- return 0;
- }
- len = sizeof(so);
- getsockopt(sock, SOL_SCO, SCO_OPTIONS, &so, &len);
- ba2str(&addr.sco_bdaddr, saddr);
- ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
- /* figure out which device this sco connection belongs to */
- pvt = NULL;
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
- break;
- }
- AST_RWLIST_UNLOCK(&devices);
- if (!pvt) {
- ast_log(LOG_WARNING, "could not find device for incoming audio connection\n");
- close(sock);
- return 1;
- }
- ast_mutex_lock(&pvt->lock);
- if (pvt->sco_socket != -1) {
- close(pvt->sco_socket);
- pvt->sco_socket = -1;
- }
- pvt->sco_socket = sock;
- if (pvt->owner) {
- ast_channel_set_fd(pvt->owner, 0, sock);
- } else {
- ast_debug(1, "incoming audio connection for pvt without owner\n");
- }
- ast_mutex_unlock(&pvt->lock);
- return 1;
- }
- /*!
- * \brief Bind an SCO listener socket for the given adapter.
- * \param adapter an adapter_pvt
- * \return -1 on error, non zero on success
- */
- static int sco_bind(struct adapter_pvt *adapter)
- {
- struct sockaddr_sco addr;
- int opt = 1;
- if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
- ast_log(LOG_ERROR, "Unable to create sco listener socket for adapter %s.\n", adapter->id);
- goto e_return;
- }
- memset(&addr, 0, sizeof(addr));
- addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, &adapter->addr);
- if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
- goto e_close_socket;
- }
- if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
- ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
- goto e_close_socket;
- }
- if (listen(adapter->sco_socket, 5) < 0) {
- ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
- goto e_close_socket;
- }
- return adapter->sco_socket;
- e_close_socket:
- close(adapter->sco_socket);
- adapter->sco_socket = -1;
- e_return:
- return -1;
- }
- /*
- * Hayes AT command helpers.
- */
- /*!
- * \brief Match the given buffer with the given prefix.
- * \param buf the buffer to match
- * \param prefix the prefix to match
- */
- static int at_match_prefix(char *buf, char *prefix)
- {
- return !strncmp(buf, prefix, strlen(prefix));
- }
- /*!
- * \brief Read an AT message and clasify it.
- * \param rsock an rfcomm socket
- * \param buf the buffer to store the result in
- * \param count the size of the buffer or the maximum number of characters to read
- * \return the type of message received, in addition buf will contain the
- * message received and will be null terminated
- * \see at_read()
- */
- static at_message_t at_read_full(int rsock, char *buf, size_t count)
- {
- ssize_t s;
- if ((s = rfcomm_read(rsock, buf, count - 1)) < 1)
- return s;
- buf[s] = '\0';
- if (!strcmp("OK", buf)) {
- return AT_OK;
- } else if (!strcmp("ERROR", buf)) {
- return AT_ERROR;
- } else if (!strcmp("RING", buf)) {
- return AT_RING;
- } else if (!strcmp("AT+CKPD=200", buf)) {
- return AT_CKPD;
- } else if (!strcmp("> ", buf)) {
- return AT_SMS_PROMPT;
- } else if (at_match_prefix(buf, "+CMTI:")) {
- return AT_CMTI;
- } else if (at_match_prefix(buf, "+CIEV:")) {
- return AT_CIEV;
- } else if (at_match_prefix(buf, "+BRSF:")) {
- return AT_BRSF;
- } else if (at_match_prefix(buf, "+CIND:")) {
- return AT_CIND;
- } else if (at_match_prefix(buf, "+CLIP:")) {
- return AT_CLIP;
- } else if (at_match_prefix(buf, "+CMGR:")) {
- return AT_CMGR;
- } else if (at_match_prefix(buf, "+VGM:")) {
- return AT_VGM;
- } else if (at_match_prefix(buf, "+VGS:")) {
- return AT_VGS;
- } else if (at_match_prefix(buf, "+CMS ERROR:")) {
- return AT_CMS_ERROR;
- } else if (at_match_prefix(buf, "AT+VGM=")) {
- return AT_VGM;
- } else if (at_match_prefix(buf, "AT+VGS=")) {
- return AT_VGS;
- } else if (at_match_prefix(buf, "+CUSD:")) {
- return AT_CUSD;
- } else if (at_match_prefix(buf, "BUSY")) {
- return AT_BUSY;
- } else if (at_match_prefix(buf, "NO DIALTONE")) {
- return AT_NO_DIALTONE;
- } else if (at_match_prefix(buf, "NO CARRIER")) {
- return AT_NO_CARRIER;
- } else if (at_match_prefix(buf, "*ECAV:")) {
- return AT_ECAM;
- } else {
- return AT_UNKNOWN;
- }
- }
- /*!
- * \brief Get the string representation of the given AT message.
- * \param msg the message to process
- * \return a string describing the given message
- */
- static inline const char *at_msg2str(at_message_t msg)
- {
- switch (msg) {
- /* errors */
- case AT_PARSE_ERROR:
- return "PARSE ERROR";
- case AT_READ_ERROR:
- return "READ ERROR";
- default:
- case AT_UNKNOWN:
- return "UNKNOWN";
- /* at responses */
- case AT_OK:
- return "OK";
- case AT_ERROR:
- return "ERROR";
- case AT_RING:
- return "RING";
- case AT_BRSF:
- return "AT+BRSF";
- case AT_CIND:
- return "AT+CIND";
- case AT_CIEV:
- return "AT+CIEV";
- case AT_CLIP:
- return "AT+CLIP";
- case AT_CMTI:
- return "AT+CMTI";
- case AT_CMGR:
- return "AT+CMGR";
- case AT_SMS_PROMPT:
- return "SMS PROMPT";
- case AT_CMS_ERROR:
- return "+CMS ERROR";
- case AT_BUSY:
- return "BUSY";
- case AT_NO_DIALTONE:
- return "NO DIALTONE";
- case AT_NO_CARRIER:
- return "NO CARRIER";
- /* at commands */
- case AT_A:
- return "ATA";
- case AT_D:
- return "ATD";
- case AT_CHUP:
- return "AT+CHUP";
- case AT_CKPD:
- return "AT+CKPD";
- case AT_CMGS:
- return "AT+CMGS";
- case AT_VGM:
- return "AT+VGM";
- case AT_VGS:
- return "AT+VGS";
- case AT_VTS:
- return "AT+VTS";
- case AT_CMGF:
- return "AT+CMGF";
- case AT_CNMI:
- return "AT+CNMI";
- case AT_CMER:
- return "AT+CMER";
- case AT_CIND_TEST:
- return "AT+CIND=?";
- case AT_CUSD:
- return "AT+CUSD";
- case AT_ECAM:
- return "AT*ECAM";
- }
- }
- /*
- * bluetooth handsfree profile helpers
- */
- /*!
- * \brief Parse a ECAV event.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \return -1 on error (parse error) or a ECAM value on success
- *
- * Example string: *ECAV: <ccid>,<ccstatus>,<calltype>[,<processid>]
- * [,exitcause][,<number>,<type>]
- *
- * Example indicating busy: *ECAV: 1,7,1
- */
- static int hfp_parse_ecav(struct hfp_pvt *hfp, char *buf)
- {
- int ccid = 0;
- int ccstatus = 0;
- int calltype = 0;
- if (!sscanf(buf, "*ECAV: %2d,%2d,%2d", &ccid, &ccstatus, &calltype)) {
- ast_debug(1, "[%s] error parsing ECAV event '%s'\n", hfp->owner->id, buf);
- return -1;
- }
- return ccstatus;
- }
- /*!
- * \brief Enable Sony Erricson extensions / indications.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_ecam(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "AT*ECAM=1\r");
- }
- /*!
- * \brief Parse a CIEV event.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \param value a pointer to an int to store the event value in (can be NULL)
- * \return 0 on error (parse error, or unknown event) or a HFP_CIND_* value on
- * success
- */
- static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value)
- {
- int i, v;
- if (!value)
- value = &v;
- if (!sscanf(buf, "+CIEV: %d,%d", &i, value)) {
- ast_debug(2, "[%s] error parsing CIEV event '%s'\n", hfp->owner->id, buf);
- return HFP_CIND_NONE;
- }
- if (i >= ARRAY_LEN(hfp->cind_state)) {
- ast_debug(2, "[%s] CIEV event index too high (%s)\n", hfp->owner->id, buf);
- return HFP_CIND_NONE;
- }
- hfp->cind_state[i] = *value;
- return hfp->cind_index[i];
- }
- /*!
- * \brief Parse a CLIP event.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \note buf will be modified when the CID string is parsed
- * \return NULL on error (parse error) or a pointer to the caller id
- * information in buf
- */
- static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf)
- {
- int i, state;
- char *clip = NULL;
- size_t s;
- /* parse clip info in the following format:
- * +CLIP: "123456789",128,...
- */
- state = 0;
- s = strlen(buf);
- for (i = 0; i < s && state != 3; i++) {
- switch (state) {
- case 0: /* search for start of the number (") */
- if (buf[i] == '"') {
- state++;
- }
- break;
- case 1: /* mark the number */
- clip = &buf[i];
- state++;
- /* fall through */
- case 2: /* search for the end of the number (") */
- if (buf[i] == '"') {
- buf[i] = '\0';
- state++;
- }
- break;
- }
- }
- if (state != 3) {
- return NULL;
- }
- return clip;
- }
- /*!
- * \brief Parse a CMTI notification.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \note buf will be modified when the CMTI message is parsed
- * \return -1 on error (parse error) or the index of the new sms message
- */
- static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf)
- {
- int index = -1;
- /* parse cmti info in the following format:
- * +CMTI: <mem>,<index>
- */
- if (!sscanf(buf, "+CMTI: %*[^,],%d", &index)) {
- ast_debug(2, "[%s] error parsing CMTI event '%s'\n", hfp->owner->id, buf);
- return -1;
- }
- return index;
- }
- /*!
- * \brief Parse a CMGR message.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \param from_number a pointer to a char pointer which will store the from
- * number
- * \param text a pointer to a char pointer which will store the message text
- * \note buf will be modified when the CMGR message is parsed
- * \retval -1 parse error
- * \retval 0 success
- */
- static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, char **text)
- {
- int i, state;
- size_t s;
- /* parse cmgr info in the following format:
- * +CMGR: <msg status>,"+123456789",...\r\n
- * <message text>
- */
- state = 0;
- s = strlen(buf);
- for (i = 0; i < s && state != 6; i++) {
- switch (state) {
- case 0: /* search for start of the number section (,) */
- if (buf[i] == ',') {
- state++;
- }
- break;
- case 1: /* find the opening quote (") */
- if (buf[i] == '"') {
- state++;
- }
- break;
- case 2: /* mark the start of the number */
- if (from_number) {
- *from_number = &buf[i];
- state++;
- }
- /* fall through */
- case 3: /* search for the end of the number (") */
- if (buf[i] == '"') {
- buf[i] = '\0';
- state++;
- }
- break;
- case 4: /* search for the start of the message text (\n) */
- if (buf[i] == '\n') {
- state++;
- }
- break;
- case 5: /* mark the start of the message text */
- if (text) {
- *text = &buf[i];
- state++;
- }
- break;
- }
- }
- if (state != 6) {
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Parse a CUSD answer.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \note buf will be modified when the CUSD string is parsed
- * \return NULL on error (parse error) or a pointer to the cusd message
- * information in buf
- */
- static char *hfp_parse_cusd(struct hfp_pvt *hfp, char *buf)
- {
- int i, message_start, message_end;
- char *cusd;
- size_t s;
- /* parse cusd message in the following format:
- * +CUSD: 0,"100,00 EURO, valid till 01.01.2010, you are using tariff "Mega Tariff". More informations *111#."
- */
- message_start = 0;
- message_end = 0;
- s = strlen(buf);
- /* Find the start of the message (") */
- for (i = 0; i < s; i++) {
- if (buf[i] == '"') {
- message_start = i + 1;
- break;
- }
- }
- if (message_start == 0 || message_start >= s) {
- return NULL;
- }
- /* Find the end of the message (") */
- for (i = s; i > 0; i--) {
- if (buf[i] == '"') {
- message_end = i;
- break;
- }
- }
- if (message_end == 0) {
- return NULL;
- }
- if (message_start >= message_end) {
- return NULL;
- }
- cusd = &buf[message_start];
- buf[message_end] = '\0';
- return cusd;
- }
- /*!
- * \brief Convert a hfp_hf struct to a BRSF int.
- * \param hf an hfp_hf brsf object
- * \return an integer representing the given brsf struct
- */
- static int hfp_brsf2int(struct hfp_hf *hf)
- {
- int brsf = 0;
- brsf |= hf->ecnr ? HFP_HF_ECNR : 0;
- brsf |= hf->cw ? HFP_HF_CW : 0;
- brsf |= hf->cid ? HFP_HF_CID : 0;
- brsf |= hf->voice ? HFP_HF_VOICE : 0;
- brsf |= hf->volume ? HFP_HF_VOLUME : 0;
- brsf |= hf->status ? HFP_HF_STATUS : 0;
- brsf |= hf->control ? HFP_HF_CONTROL : 0;
- return brsf;
- }
- /*!
- * \brief Convert a BRSF int to an hfp_ag struct.
- * \param brsf a brsf integer
- * \param ag a AG (hfp_ag) brsf object
- * \return a pointer to the given hfp_ag object populated with the values from
- * the given brsf integer
- */
- static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag)
- {
- ag->cw = brsf & HFP_AG_CW ? 1 : 0;
- ag->ecnr = brsf & HFP_AG_ECNR ? 1 : 0;
- ag->voice = brsf & HFP_AG_VOICE ? 1 : 0;
- ag->ring = brsf & HFP_AG_RING ? 1 : 0;
- ag->tag = brsf & HFP_AG_TAG ? 1 : 0;
- ag->reject = brsf & HFP_AG_REJECT ? 1 : 0;
- ag->status = brsf & HFP_AG_STATUS ? 1 : 0;
- ag->control = brsf & HFP_AG_CONTROL ? 1 : 0;
- ag->errors = brsf & HFP_AG_ERRORS ? 1 : 0;
- return ag;
- }
- /*!
- * \brief Send a BRSF request.
- * \param hfp an hfp_pvt struct
- * \param brsf an hfp_hf brsf struct
- *
- * \retval 0 on success
- * \retval -1 on error
- */
- static int hfp_send_brsf(struct hfp_pvt *hfp, struct hfp_hf *brsf)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+BRSF=%d\r", hfp_brsf2int(brsf));
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send the CIND read command.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_cind(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "AT+CIND?\r");
- }
- /*!
- * \brief Send the CIND test command.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_cind_test(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "AT+CIND=?\r");
- }
- /*!
- * \brief Enable or disable indicator events reporting.
- * \param hfp an hfp_pvt struct
- * \param status enable or disable events reporting (should be 1 or 0)
- */
- static int hfp_send_cmer(struct hfp_pvt *hfp, int status)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+CMER=3,0,0,%d\r", status ? 1 : 0);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send the current speaker gain level.
- * \param hfp an hfp_pvt struct
- * \param value the value to send (must be between 0 and 15)
- */
- static int hfp_send_vgs(struct hfp_pvt *hfp, int value)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+VGS=%d\r", value);
- return rfcomm_write(hfp->rsock, cmd);
- }
- #if 0
- /*!
- * \brief Send the current microphone gain level.
- * \param hfp an hfp_pvt struct
- * \param value the value to send (must be between 0 and 15)
- */
- static int hfp_send_vgm(struct hfp_pvt *hfp, int value)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+VGM=%d\r", value);
- return rfcomm_write(hfp->rsock, cmd);
- }
- #endif
- /*!
- * \brief Enable or disable calling line identification.
- * \param hfp an hfp_pvt struct
- * \param status enable or disable calling line identification (should be 1 or
- * 0)
- */
- static int hfp_send_clip(struct hfp_pvt *hfp, int status)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+CLIP=%d\r", status ? 1 : 0);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send a DTMF command.
- * \param hfp an hfp_pvt struct
- * \param digit the dtmf digit to send
- * \return the result of rfcomm_write() or -1 on an invalid digit being sent
- */
- static int hfp_send_dtmf(struct hfp_pvt *hfp, char digit)
- {
- char cmd[10];
- switch(digit) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '*':
- case '#':
- snprintf(cmd, sizeof(cmd), "AT+VTS=%c\r", digit);
- return rfcomm_write(hfp->rsock, cmd);
- default:
- return -1;
- }
- }
- /*!
- * \brief Set the SMS mode.
- * \param hfp an hfp_pvt struct
- * \param mode the sms mode (0 = PDU, 1 = Text)
- */
- static int hfp_send_cmgf(struct hfp_pvt *hfp, int mode)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+CMGF=%d\r", mode);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Setup SMS new message indication.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_cnmi(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "AT+CNMI=2,1,0,0,0\r");
- }
- /*!
- * \brief Read an SMS message.
- * \param hfp an hfp_pvt struct
- * \param index the location of the requested message
- */
- static int hfp_send_cmgr(struct hfp_pvt *hfp, int index)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "AT+CMGR=%d\r", index);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Start sending an SMS message.
- * \param hfp an hfp_pvt struct
- * \param number the destination of the message
- */
- static int hfp_send_cmgs(struct hfp_pvt *hfp, const char *number)
- {
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"\r", number);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send the text of an SMS message.
- * \param hfp an hfp_pvt struct
- * \param message the text of the message
- */
- static int hfp_send_sms_text(struct hfp_pvt *hfp, const char *message)
- {
- char cmd[162];
- snprintf(cmd, sizeof(cmd), "%.160s\x1a", message);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send AT+CHUP.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_chup(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "AT+CHUP\r");
- }
- /*!
- * \brief Send ATD.
- * \param hfp an hfp_pvt struct
- * \param number the number to send
- */
- static int hfp_send_atd(struct hfp_pvt *hfp, const char *number)
- {
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "ATD%s;\r", number);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Send ATA.
- * \param hfp an hfp_pvt struct
- */
- static int hfp_send_ata(struct hfp_pvt *hfp)
- {
- return rfcomm_write(hfp->rsock, "ATA\r");
- }
- /*!
- * \brief Send CUSD.
- * \param hfp an hfp_pvt struct
- * \param code the CUSD code to send
- */
- static int hfp_send_cusd(struct hfp_pvt *hfp, const char *code)
- {
- char cmd[128];
- snprintf(cmd, sizeof(cmd), "AT+CUSD=1,\"%s\",15\r", code);
- return rfcomm_write(hfp->rsock, cmd);
- }
- /*!
- * \brief Parse BRSF data.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- */
- static int hfp_parse_brsf(struct hfp_pvt *hfp, const char *buf)
- {
- int brsf;
- if (!sscanf(buf, "+BRSF:%d", &brsf))
- return -1;
- hfp_int2brsf(brsf, &hfp->brsf);
- return 0;
- }
- /*!
- * \brief Parse and store the given indicator.
- * \param hfp an hfp_pvt struct
- * \param group the indicator group
- * \param indicator the indicator to parse
- */
- static int hfp_parse_cind_indicator(struct hfp_pvt *hfp, int group, char *indicator)
- {
- int value;
- /* store the current indicator */
- if (group >= ARRAY_LEN(hfp->cind_state)) {
- ast_debug(1, "ignoring CIND state '%s' for group %d, we only support up to %d indicators\n", indicator, group, (int) sizeof(hfp->cind_state));
- return -1;
- }
- if (!sscanf(indicator, "%d", &value)) {
- ast_debug(1, "error parsing CIND state '%s' for group %d\n", indicator, group);
- return -1;
- }
- hfp->cind_state[group] = value;
- return 0;
- }
- /*!
- * \brief Read the result of the AT+CIND? command.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- * \note hfp_send_cind_test() and hfp_parse_cind_test() should be called at
- * least once before this function is called.
- */
- static int hfp_parse_cind(struct hfp_pvt *hfp, char *buf)
- {
- int i, state, group;
- size_t s;
- char *indicator = NULL;
- /* parse current state of all of our indicators. The list is in the
- * following format:
- * +CIND: 1,0,2,0,0,0,0
- */
- group = 0;
- state = 0;
- s = strlen(buf);
- for (i = 0; i < s; i++) {
- switch (state) {
- case 0: /* search for start of the status indicators (a space) */
- if (buf[i] == ' ') {
- group++;
- state++;
- }
- break;
- case 1: /* mark this indicator */
- indicator = &buf[i];
- state++;
- break;
- case 2: /* search for the start of the next indicator (a comma) */
- if (buf[i] == ',') {
- buf[i] = '\0';
- hfp_parse_cind_indicator(hfp, group, indicator);
- group++;
- state = 1;
- }
- break;
- }
- }
- /* store the last indicator */
- if (state == 2)
- hfp_parse_cind_indicator(hfp, group, indicator);
- return 0;
- }
- /*!
- * \brief Parse the result of the AT+CIND=? command.
- * \param hfp an hfp_pvt struct
- * \param buf the buffer to parse (null terminated)
- */
- static int hfp_parse_cind_test(struct hfp_pvt *hfp, char *buf)
- {
- int i, state, group;
- size_t s;
- char *indicator = NULL;
- hfp->nocallsetup = 1;
- /* parse the indications list. It is in the follwing format:
- * +CIND: ("ind1",(0-1)),("ind2",(0-5))
- */
- group = 0;
- state = 0;
- s = strlen(buf);
- for (i = 0; i < s; i++) {
- switch (state) {
- case 0: /* search for start of indicator block */
- if (buf[i] == '(') {
- group++;
- state++;
- }
- break;
- case 1: /* search for '"' in indicator block */
- if (buf[i] == '"') {
- state++;
- }
- break;
- case 2: /* mark the start of the indicator name */
- indicator = &buf[i];
- state++;
- break;
- case 3: /* look for the end of the indicator name */
- if (buf[i] == '"') {
- buf[i] = '\0';
- state++;
- }
- break;
- case 4: /* find the start of the value range */
- if (buf[i] == '(') {
- state++;
- }
- break;
- case 5: /* mark the start of the value range */
- state++;
- break;
- case 6: /* find the end of the value range */
- if (buf[i] == ')') {
- buf[i] = '\0';
- state++;
- }
- break;
- case 7: /* process the values we found */
- if (group < sizeof(hfp->cind_index)) {
- if (!strcmp(indicator, "service")) {
- hfp->cind_map.service = group;
- hfp->cind_index[group] = HFP_CIND_SERVICE;
- } else if (!strcmp(indicator, "call")) {
- hfp->cind_map.call = group;
- hfp->cind_index[group] = HFP_CIND_CALL;
- } else if (!strcmp(indicator, "callsetup")) {
- hfp->nocallsetup = 0;
- hfp->cind_map.callsetup = group;
- hfp->cind_index[group] = HFP_CIND_CALLSETUP;
- } else if (!strcmp(indicator, "call_setup")) { /* non standard call setup identifier */
- hfp->nocallsetup = 0;
- hfp->cind_map.callsetup = group;
- hfp->cind_index[group] = HFP_CIND_CALLSETUP;
- } else if (!strcmp(indicator, "callheld")) {
- hfp->cind_map.callheld = group;
- hfp->cind_index[group] = HFP_CIND_CALLHELD;
- } else if (!strcmp(indicator, "signal")) {
- hfp->cind_map.signal = group;
- hfp->cind_index[group] = HFP_CIND_SIGNAL;
- } else if (!strcmp(indicator, "roam")) {
- hfp->cind_map.roam = group;
- hfp->cind_index[group] = HFP_CIND_ROAM;
- } else if (!strcmp(indicator, "battchg")) {
- hfp->cind_map.battchg = group;
- hfp->cind_index[group] = HFP_CIND_BATTCHG;
- } else {
- hfp->cind_index[group] = HFP_CIND_UNKNOWN;
- ast_debug(2, "ignoring unknown CIND indicator '%s'\n", indicator);
- }
- } else {
- ast_debug(1, "can't store indicator %d (%s), we only support up to %d indicators", group, indicator, (int) sizeof(hfp->cind_index));
- }
- state = 0;
- break;
- }
- }
- hfp->owner->no_callsetup = hfp->nocallsetup;
- return 0;
- }
- /*
- * Bluetooth Headset Profile helpers
- */
- /*!
- * \brief Send an OK AT response.
- * \param rsock the rfcomm socket to use
- */
- static int hsp_send_ok(int rsock)
- {
- return rfcomm_write(rsock, "\r\nOK\r\n");
- }
- /*!
- * \brief Send an ERROR AT response.
- * \param rsock the rfcomm socket to use
- */
- static int hsp_send_error(int rsock)
- {
- return rfcomm_write(rsock, "\r\nERROR\r\n");
- }
- /*!
- * \brief Send a speaker gain unsolicited AT response
- * \param rsock the rfcomm socket to use
- * \param gain the speaker gain value
- */
- static int hsp_send_vgs(int rsock, int gain)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "\r\n+VGS=%d\r\n", gain);
- return rfcomm_write(rsock, cmd);
- }
- /*!
- * \brief Send a microphone gain unsolicited AT response
- * \param rsock the rfcomm socket to use
- * \param gain the microphone gain value
- */
- static int hsp_send_vgm(int rsock, int gain)
- {
- char cmd[32];
- snprintf(cmd, sizeof(cmd), "\r\n+VGM=%d\r\n", gain);
- return rfcomm_write(rsock, cmd);
- }
- /*!
- * \brief Send a RING unsolicited AT response.
- * \param rsock the rfcomm socket to use
- */
- static int hsp_send_ring(int rsock)
- {
- return rfcomm_write(rsock, "\r\nRING\r\n");
- }
- /*
- * message queue functions
- */
- /*!
- * \brief Add an item to the back of the queue.
- * \param pvt a mbl_pvt structure
- * \param expect the msg we expect to receive
- * \param response_to the message that was sent to generate the expected
- * response
- */
- static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to)
- {
- struct msg_queue_entry *msg;
- if (!(msg = ast_calloc(1, sizeof(*msg)))) {
- return -1;
- }
- msg->expected = expect;
- msg->response_to = response_to;
- AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
- return 0;
- }
- /*!
- * \brief Add an item to the back of the queue with data.
- * \param pvt a mbl_pvt structure
- * \param expect the msg we expect to receive
- * \param response_to the message that was sent to generate the expected
- * response
- * \param data data associated with this message, it will be freed when the
- * message is freed
- */
- static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data)
- {
- struct msg_queue_entry *msg;
- if (!(msg = ast_calloc(1, sizeof(*msg)))) {
- return -1;
- }
- msg->expected = expect;
- msg->response_to = response_to;
- msg->data = data;
- AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
- return 0;
- }
- /*!
- * \brief Remove an item from the front of the queue.
- * \param pvt a mbl_pvt structure
- * \return a pointer to the removed item
- */
- static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt)
- {
- return AST_LIST_REMOVE_HEAD(&pvt->msg_queue, entry);
- }
- /*!
- * \brief Remove an item from the front of the queue, and free it.
- * \param pvt a mbl_pvt structure
- */
- static void msg_queue_free_and_pop(struct mbl_pvt *pvt)
- {
- struct msg_queue_entry *msg;
- if ((msg = msg_queue_pop(pvt))) {
- if (msg->data)
- ast_free(msg->data);
- ast_free(msg);
- }
- }
- /*!
- * \brief Remove all itmes from the queue and free them.
- * \param pvt a mbl_pvt structure
- */
- static void msg_queue_flush(struct mbl_pvt *pvt)
- {
- struct msg_queue_entry *msg;
- while ((msg = msg_queue_head(pvt)))
- msg_queue_free_and_pop(pvt);
- }
- /*!
- * \brief Get the head of a queue.
- * \param pvt a mbl_pvt structure
- * \return a pointer to the head of the given msg queue
- */
- static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt)
- {
- return AST_LIST_FIRST(&pvt->msg_queue);
- }
- /*
- sdp helpers
- */
- static int sdp_search(char *addr, int profile)
- {
- sdp_session_t *session = 0;
- bdaddr_t bdaddr;
- uuid_t svc_uuid;
- uint32_t range = 0x0000ffff;
- sdp_list_t *response_list, *search_list, *attrid_list;
- int status, port;
- sdp_list_t *proto_list;
- sdp_record_t *sdprec;
- str2ba(addr, &bdaddr);
- port = 0;
- session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
- if (!session) {
- ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
- return 0;
- }
- sdp_uuid32_create(&svc_uuid, profile);
- search_list = sdp_list_append(0, &svc_uuid);
- attrid_list = sdp_list_append(0, &range);
- response_list = 0x00;
- status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
- if (status == 0) {
- if (response_list) {
- sdprec = (sdp_record_t *) response_list->data;
- proto_list = 0x00;
- if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
- port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
- sdp_list_free(proto_list, 0);
- }
- sdp_record_free(sdprec);
- sdp_list_free(response_list, 0);
- } else
- ast_debug(1, "No responses returned for device %s.\n", addr);
- } else
- ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
- sdp_list_free(search_list, 0);
- sdp_list_free(attrid_list, 0);
- sdp_close(session);
- return port;
- }
- static sdp_session_t *sdp_register(void)
- {
- uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
- uint8_t rfcomm_channel = 1;
- const char *service_name = "Asterisk PABX";
- const char *service_dsc = "Asterisk PABX";
- const char *service_prov = "Asterisk";
- uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
- sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
- sdp_data_t *channel = 0;
- sdp_session_t *session = 0;
- sdp_record_t *record = sdp_record_alloc();
- sdp_uuid128_create(&svc_uuid, &service_uuid_int);
- sdp_set_service_id(record, svc_uuid);
- sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
- sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
- svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
- svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
- sdp_set_service_classes(record, svc_uuid_list);
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root_list = sdp_list_append(0, &root_uuid);
- sdp_set_browse_groups( record, root_list );
- sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
- l2cap_list = sdp_list_append(0, &l2cap_uuid);
- proto_list = sdp_list_append(0, l2cap_list);
- sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
- channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
- rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
- sdp_list_append(rfcomm_list, channel);
- sdp_list_append(proto_list, rfcomm_list);
- access_proto_list = sdp_list_append(0, proto_list);
- sdp_set_access_protos(record, access_proto_list);
- sdp_set_info_attr(record, service_name, service_prov, service_dsc);
- if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
- ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
- else {
- if (sdp_record_register(session, record, 0) < 0) {
- ast_log(LOG_WARNING, "Failed to sdp_record_register error: %d\n", errno);
- return NULL;
- }
- }
- sdp_data_free(channel);
- sdp_list_free(rfcomm_list, 0);
- sdp_list_free(root_list, 0);
- sdp_list_free(access_proto_list, 0);
- sdp_list_free(svc_uuid_list, 0);
- return session;
- }
- /*
- Thread routines
- */
- /*!
- * \brief Handle the BRSF response.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_brsf(struct mbl_pvt *pvt, char *buf)
- {
- struct msg_queue_entry *entry;
- if ((entry = msg_queue_head(pvt)) && entry->expected == AT_BRSF) {
- if (hfp_parse_brsf(pvt->hfp, buf)) {
- ast_debug(1, "[%s] error parsing BRSF\n", pvt->id);
- goto e_return;
- }
- if (msg_queue_push(pvt, AT_OK, AT_BRSF)) {
- ast_debug(1, "[%s] error handling BRSF\n", pvt->id);
- goto e_return;
- }
- msg_queue_free_and_pop(pvt);
- } else if (entry) {
- ast_debug(1, "[%s] received unexpected AT message 'BRSF' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
- } else {
- ast_debug(1, "[%s] received unexpected AT message 'BRSF'\n", pvt->id);
- }
- return 0;
- e_return:
- msg_queue_free_and_pop(pvt);
- return -1;
- }
- /*!
- * \brief Handle the CIND response.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_cind(struct mbl_pvt *pvt, char *buf)
- {
- struct msg_queue_entry *entry;
- if ((entry = msg_queue_head(pvt)) && entry->expected == AT_CIND) {
- switch (entry->response_to) {
- case AT_CIND_TEST:
- if (hfp_parse_cind_test(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND_TEST)) {
- ast_debug(1, "[%s] error performing CIND test\n", pvt->id);
- goto e_return;
- }
- break;
- case AT_CIND:
- if (hfp_parse_cind(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND)) {
- ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
- goto e_return;
- }
- break;
- default:
- ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
- goto e_return;
- }
- msg_queue_free_and_pop(pvt);
- } else if (entry) {
- ast_debug(1, "[%s] received unexpected AT message 'CIND' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
- } else {
- ast_debug(1, "[%s] received unexpected AT message 'CIND'\n", pvt->id);
- }
- return 0;
- e_return:
- msg_queue_free_and_pop(pvt);
- return -1;
- }
- /*!
- * \brief Handle OK AT messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_ok(struct mbl_pvt *pvt, char *buf)
- {
- struct msg_queue_entry *entry;
- if ((entry = msg_queue_head(pvt)) && entry->expected == AT_OK) {
- switch (entry->response_to) {
- /* initialization stuff */
- case AT_BRSF:
- ast_debug(1, "[%s] BSRF sent successfully\n", pvt->id);
- /* If this is a blackberry do CMER now, otherwise
- * continue with CIND as normal. */
- if (pvt->blackberry) {
- if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
- ast_debug(1, "[%s] error sending CMER\n", pvt->id);
- goto e_return;
- }
- } else {
- if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
- ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
- goto e_return;
- }
- }
- break;
- case AT_CIND_TEST:
- ast_debug(1, "[%s] CIND test sent successfully\n", pvt->id);
- ast_debug(2, "[%s] call: %d\n", pvt->id, pvt->hfp->cind_map.call);
- ast_debug(2, "[%s] callsetup: %d\n", pvt->id, pvt->hfp->cind_map.callsetup);
- ast_debug(2, "[%s] service: %d\n", pvt->id, pvt->hfp->cind_map.service);
- if (hfp_send_cind(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND)) {
- ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
- goto e_return;
- }
- break;
- case AT_CIND:
- ast_debug(1, "[%s] CIND sent successfully\n", pvt->id);
- /* check if a call is active */
- if (pvt->hfp->cind_state[pvt->hfp->cind_map.call]) {
- ast_verb(3, "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
- goto e_return;
- }
- /* If this is NOT a blackberry proceed with CMER,
- * otherwise send CLIP. */
- if (!pvt->blackberry) {
- if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
- ast_debug(1, "[%s] error sending CMER\n", pvt->id);
- goto e_return;
- }
- } else {
- if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
- ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
- goto e_return;
- }
- }
- break;
- case AT_CMER:
- ast_debug(1, "[%s] CMER sent successfully\n", pvt->id);
- /* If this is a blackberry proceed with the CIND test,
- * otherwise send CLIP. */
- if (pvt->blackberry) {
- if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
- ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
- goto e_return;
- }
- } else {
- if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
- ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
- goto e_return;
- }
- }
- break;
- case AT_CLIP:
- ast_debug(1, "[%s] caling line indication enabled\n", pvt->id);
- if (hfp_send_ecam(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_ECAM)) {
- ast_debug(1, "[%s] error enabling Sony Ericsson call monitoring extensions\n", pvt->id);
- goto e_return;
- }
- break;
- case AT_ECAM:
- ast_debug(1, "[%s] Sony Ericsson call monitoring is active on device\n", pvt->id);
- if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) {
- ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id);
- goto e_return;
- }
- pvt->timeout = -1;
- pvt->hfp->initialized = 1;
- ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id);
- break;
- case AT_VGS:
- ast_debug(1, "[%s] volume level synchronization successful\n", pvt->id);
- /* set the SMS operating mode to text mode */
- if (pvt->has_sms) {
- if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
- ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
- goto e_return;
- }
- }
- break;
- case AT_CMGF:
- ast_debug(1, "[%s] sms text mode enabled\n", pvt->id);
- /* turn on SMS new message indication */
- if (hfp_send_cnmi(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_CNMI)) {
- ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
- goto e_return;
- }
- break;
- case AT_CNMI:
- ast_debug(1, "[%s] sms new message indication enabled\n", pvt->id);
- pvt->has_sms = 1;
- break;
- /* end initialization stuff */
- case AT_A:
- ast_debug(1, "[%s] answer sent successfully\n", pvt->id);
- pvt->needchup = 1;
- break;
- case AT_D:
- ast_debug(1, "[%s] dial sent successfully\n", pvt->id);
- pvt->needchup = 1;
- pvt->outgoing = 1;
- mbl_queue_control(pvt, AST_CONTROL_PROGRESS);
- break;
- case AT_CHUP:
- ast_debug(1, "[%s] successful hangup\n", pvt->id);
- break;
- case AT_CMGS:
- ast_debug(1, "[%s] successfully sent sms message\n", pvt->id);
- pvt->outgoing_sms = 0;
- break;
- case AT_VTS:
- ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
- break;
- case AT_CUSD:
- ast_debug(1, "[%s] CUSD code sent successfully\n", pvt->id);
- break;
- case AT_UNKNOWN:
- default:
- ast_debug(1, "[%s] received OK for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
- break;
- }
- msg_queue_free_and_pop(pvt);
- } else if (entry) {
- ast_debug(1, "[%s] received AT message 'OK' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
- } else {
- ast_debug(1, "[%s] received unexpected AT message 'OK'\n", pvt->id);
- }
- return 0;
- e_return:
- msg_queue_free_and_pop(pvt);
- return -1;
- }
- /*!
- * \brief Handle ERROR AT messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_error(struct mbl_pvt *pvt, char *buf)
- {
- struct msg_queue_entry *entry;
- if ((entry = msg_queue_head(pvt))
- && (entry->expected == AT_OK
- || entry->expected == AT_ERROR
- || entry->expected == AT_CMS_ERROR
- || entry->expected == AT_CMGR
- || entry->expected == AT_SMS_PROMPT)) {
- switch (entry->response_to) {
- /* initialization stuff */
- case AT_BRSF:
- ast_debug(1, "[%s] error reading BSRF\n", pvt->id);
- goto e_return;
- case AT_CIND_TEST:
- ast_debug(1, "[%s] error during CIND test\n", pvt->id);
- goto e_return;
- case AT_CIND:
- ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
- goto e_return;
- case AT_CMER:
- ast_debug(1, "[%s] error during CMER request\n", pvt->id);
- goto e_return;
- case AT_CLIP:
- ast_debug(1, "[%s] error enabling calling line indication\n", pvt->id);
- goto e_return;
- case AT_VGS:
- ast_debug(1, "[%s] volume level synchronization failed\n", pvt->id);
- /* this is not a fatal error, let's continue with initialization */
- /* set the SMS operating mode to text mode */
- if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
- ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
- goto e_return;
- }
- break;
- case AT_CMGF:
- pvt->has_sms = 0;
- ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
- ast_debug(1, "[%s] no SMS support\n", pvt->id);
- break;
- case AT_CNMI:
- pvt->has_sms = 0;
- ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
- ast_debug(1, "[%s] no SMS support\n", pvt->id);
- break;
- case AT_ECAM:
- ast_debug(1, "[%s] Mobile does not support Sony Ericsson extensions\n", pvt->id);
- /* this is not a fatal error, let's continue with the initialization */
- if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) {
- ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id);
- goto e_return;
- }
- pvt->timeout = -1;
- pvt->hfp->initialized = 1;
- ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id);
- break;
- /* end initialization stuff */
- case AT_A:
- ast_debug(1, "[%s] answer failed\n", pvt->id);
- mbl_queue_hangup(pvt);
- break;
- case AT_D:
- ast_debug(1, "[%s] dial failed\n", pvt->id);
- pvt->needchup = 0;
- mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
- break;
- case AT_CHUP:
- ast_debug(1, "[%s] error sending hangup, disconnecting\n", pvt->id);
- goto e_return;
- case AT_CMGR:
- ast_debug(1, "[%s] error reading sms message\n", pvt->id);
- pvt->incoming_sms = 0;
- break;
- case AT_CMGS:
- ast_debug(1, "[%s] error sending sms message\n", pvt->id);
- pvt->outgoing_sms = 0;
- break;
- case AT_VTS:
- ast_debug(1, "[%s] error sending digit\n", pvt->id);
- break;
- case AT_CUSD:
- ast_verb(0, "[%s] error sending CUSD command\n", pvt->id);
- break;
- case AT_UNKNOWN:
- default:
- ast_debug(1, "[%s] received ERROR for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
- break;
- }
- msg_queue_free_and_pop(pvt);
- } else if (entry) {
- ast_debug(1, "[%s] received AT message 'ERROR' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
- } else {
- ast_debug(1, "[%s] received unexpected AT message 'ERROR'\n", pvt->id);
- }
- return 0;
- e_return:
- msg_queue_free_and_pop(pvt);
- return -1;
- }
- /*!
- * \brief Handle AT+CIEV messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_ciev(struct mbl_pvt *pvt, char *buf)
- {
- int i;
- switch (hfp_parse_ciev(pvt->hfp, buf, &i)) {
- case HFP_CIND_CALL:
- switch (i) {
- case HFP_CIND_CALL_NONE:
- ast_debug(1, "[%s] line disconnected\n", pvt->id);
- if (pvt->owner) {
- ast_debug(1, "[%s] hanging up owner\n", pvt->id);
- if (mbl_queue_hangup(pvt)) {
- ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
- return -1;
- }
- }
- pvt->needchup = 0;
- pvt->needcallerid = 0;
- pvt->incoming = 0;
- pvt->outgoing = 0;
- break;
- case HFP_CIND_CALL_ACTIVE:
- if (pvt->outgoing) {
- ast_debug(1, "[%s] remote end answered\n", pvt->id);
- mbl_queue_control(pvt, AST_CONTROL_ANSWER);
- } else if (pvt->incoming && pvt->answered) {
- ast_setstate(pvt->owner, AST_STATE_UP);
- } else if (pvt->incoming) {
- /* user answered from handset, disconnecting */
- ast_verb(3, "[%s] user answered bluetooth device from handset, disconnecting\n", pvt->id);
- mbl_queue_hangup(pvt);
- return -1;
- }
- break;
- }
- break;
- case HFP_CIND_CALLSETUP:
- switch (i) {
- case HFP_CIND_CALLSETUP_NONE:
- if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) {
- if (pvt->owner) {
- if (pvt->hfp->sent_alerting == 1) {
- handle_response_busy(pvt);
- }
- if (mbl_queue_hangup(pvt)) {
- ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
- return -1;
- }
- }
- pvt->needchup = 0;
- pvt->needcallerid = 0;
- pvt->incoming = 0;
- pvt->outgoing = 0;
- }
- break;
- case HFP_CIND_CALLSETUP_INCOMING:
- ast_debug(1, "[%s] incoming call, waiting for caller id\n", pvt->id);
- pvt->needcallerid = 1;
- pvt->incoming = 1;
- break;
- case HFP_CIND_CALLSETUP_OUTGOING:
- if (pvt->outgoing) {
- pvt->hfp->sent_alerting = 0;
- ast_debug(1, "[%s] outgoing call\n", pvt->id);
- } else {
- ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id);
- return -1;
- }
- break;
- case HFP_CIND_CALLSETUP_ALERTING:
- if (pvt->outgoing) {
- ast_debug(1, "[%s] remote alerting\n", pvt->id);
- mbl_queue_control(pvt, AST_CONTROL_RINGING);
- pvt->hfp->sent_alerting = 1;
- }
- break;
- }
- break;
- case HFP_CIND_NONE:
- ast_debug(1, "[%s] error parsing CIND: %s\n", pvt->id, buf);
- break;
- }
- return 0;
- }
- /*!
- * \brief Handle AT+CLIP messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_clip(struct mbl_pvt *pvt, char *buf)
- {
- char *clip;
- struct msg_queue_entry *msg;
- struct ast_channel *chan;
- if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CLIP) {
- msg_queue_free_and_pop(pvt);
- pvt->needcallerid = 0;
- if (!(clip = hfp_parse_clip(pvt->hfp, buf))) {
- ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
- }
- if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL, NULL))) {
- ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
- hfp_send_chup(pvt->hfp);
- msg_queue_push(pvt, AT_OK, AT_CHUP);
- return -1;
- }
- /* from this point on, we need to send a chup in the event of a
- * hangup */
- pvt->needchup = 1;
- if (ast_pbx_start(chan)) {
- ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
- mbl_ast_hangup(pvt);
- return -1;
- }
- }
- return 0;
- }
- /*!
- * \brief Handle RING messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_ring(struct mbl_pvt *pvt, char *buf)
- {
- if (pvt->needcallerid) {
- ast_debug(1, "[%s] got ring while waiting for caller id\n", pvt->id);
- return msg_queue_push(pvt, AT_CLIP, AT_UNKNOWN);
- } else {
- return 0;
- }
- }
- /*!
- * \brief Handle AT+CMTI messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_cmti(struct mbl_pvt *pvt, char *buf)
- {
- int index = hfp_parse_cmti(pvt->hfp, buf);
- if (index > 0) {
- ast_debug(1, "[%s] incoming sms message\n", pvt->id);
- if (hfp_send_cmgr(pvt->hfp, index)
- || msg_queue_push(pvt, AT_CMGR, AT_CMGR)) {
- ast_debug(1, "[%s] error sending CMGR to retrieve SMS message\n", pvt->id);
- return -1;
- }
- pvt->incoming_sms = 1;
- return 0;
- } else {
- ast_debug(1, "[%s] error parsing incoming sms message alert, disconnecting\n", pvt->id);
- return -1;
- }
- }
- /*!
- * \brief Handle AT+CMGR messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf)
- {
- char *from_number = NULL, *text = NULL;
- struct ast_channel *chan;
- struct msg_queue_entry *msg;
- if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CMGR) {
- msg_queue_free_and_pop(pvt);
- if (hfp_parse_cmgr(pvt->hfp, buf, &from_number, &text)) {
- ast_debug(1, "[%s] error parsing sms message, disconnecting\n", pvt->id);
- return -1;
- }
- ast_debug(1, "[%s] successfully read sms message\n", pvt->id);
- pvt->incoming_sms = 0;
- /* XXX this channel probably does not need to be associated with this pvt */
- if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL, NULL))) {
- ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
- return -1;
- }
- ast_channel_exten_set(chan, "sms");
- pbx_builtin_setvar_helper(chan, "SMSSRC", from_number);
- pbx_builtin_setvar_helper(chan, "SMSTXT", text);
- if (ast_pbx_start(chan)) {
- ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming sms\n", pvt->id);
- mbl_ast_hangup(pvt);
- }
- } else {
- ast_debug(1, "[%s] got unexpected +CMGR message, ignoring\n", pvt->id);
- }
- return 0;
- }
- /*!
- * \brief Send an SMS message from the queue.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf)
- {
- struct msg_queue_entry *msg;
- if (!(msg = msg_queue_head(pvt))) {
- ast_debug(1, "[%s] error, got sms prompt with no pending sms messages\n", pvt->id);
- return 0;
- }
- if (msg->expected != AT_SMS_PROMPT) {
- ast_debug(1, "[%s] error, got sms prompt but no pending sms messages\n", pvt->id);
- return 0;
- }
- if (hfp_send_sms_text(pvt->hfp, msg->data)
- || msg_queue_push(pvt, AT_OK, AT_CMGS)) {
- msg_queue_free_and_pop(pvt);
- ast_debug(1, "[%s] error sending sms message\n", pvt->id);
- return 0;
- }
- msg_queue_free_and_pop(pvt);
- return 0;
- }
- /*!
- * \brief Handle CUSD messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_cusd(struct mbl_pvt *pvt, char *buf)
- {
- char *cusd;
- if (!(cusd = hfp_parse_cusd(pvt->hfp, buf))) {
- ast_verb(0, "[%s] error parsing CUSD: %s\n", pvt->id, buf);
- return 0;
- }
- ast_verb(0, "[%s] CUSD response: %s\n", pvt->id, cusd);
- return 0;
- }
- /*!
- * \brief Handle BUSY messages.
- * \param pvt a mbl_pvt structure
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_busy(struct mbl_pvt *pvt)
- {
- pvt->hangupcause = AST_CAUSE_USER_BUSY;
- pvt->needchup = 1;
- mbl_queue_control(pvt, AST_CONTROL_BUSY);
- return 0;
- }
- /*!
- * \brief Handle NO DIALTONE messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf)
- {
- ast_verb(1, "[%s] mobile reports NO DIALTONE\n", pvt->id);
- pvt->needchup = 1;
- mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
- return 0;
- }
- /*!
- * \brief Handle NO CARRIER messages.
- * \param pvt a mbl_pvt structure
- * \param buf a null terminated buffer containing an AT message
- * \retval 0 success
- * \retval -1 error
- */
- static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf)
- {
- ast_verb(1, "[%s] mobile reports NO CARRIER\n", pvt->id);
- pvt->needchup = 1;
- mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
- return 0;
- }
- static void *do_monitor_phone(void *data)
- {
- struct mbl_pvt *pvt = (struct mbl_pvt *)data;
- struct hfp_pvt *hfp = pvt->hfp;
- char buf[350];
- int t;
- at_message_t at_msg;
- struct msg_queue_entry *entry;
- /* Note: At one point the initialization procedure was neatly contained
- * in the hfp_init() function, but that initialization method did not
- * work with non standard devices. As a result, the initialization
- * procedure is not spread throughout the event handling loop.
- */
- /* start initialization with the BRSF request */
- ast_mutex_lock(&pvt->lock);
- pvt->timeout = 10000;
- if (hfp_send_brsf(hfp, &hfp_our_brsf) || msg_queue_push(pvt, AT_BRSF, AT_BRSF)) {
- ast_debug(1, "[%s] error sending BRSF\n", hfp->owner->id);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- while (!check_unloading()) {
- ast_mutex_lock(&pvt->lock);
- t = pvt->timeout;
- ast_mutex_unlock(&pvt->lock);
- if (!rfcomm_wait(pvt->rfcomm_socket, &t)) {
- ast_debug(1, "[%s] timeout waiting for rfcomm data, disconnecting\n", pvt->id);
- ast_mutex_lock(&pvt->lock);
- if (!hfp->initialized) {
- if ((entry = msg_queue_head(pvt))) {
- switch (entry->response_to) {
- case AT_CIND_TEST:
- if (pvt->blackberry)
- ast_debug(1, "[%s] timeout during CIND test\n", hfp->owner->id);
- else
- ast_debug(1, "[%s] timeout during CIND test, try setting 'blackberry=yes'\n", hfp->owner->id);
- break;
- case AT_CMER:
- if (pvt->blackberry)
- ast_debug(1, "[%s] timeout after sending CMER, try setting 'blackberry=no'\n", hfp->owner->id);
- else
- ast_debug(1, "[%s] timeout after sending CMER\n", hfp->owner->id);
- break;
- default:
- ast_debug(1, "[%s] timeout while waiting for %s in response to %s\n", pvt->id, at_msg2str(entry->expected), at_msg2str(entry->response_to));
- break;
- }
- }
- }
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- if ((at_msg = at_read_full(hfp->rsock, buf, sizeof(buf))) < 0) {
- /* XXX gnu specific strerror_r is assummed here, this
- * is not really safe. See the strerror(3) man page
- * for more info. */
- ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
- break;
- }
- ast_debug(1, "[%s] %s\n", pvt->id, buf);
- switch (at_msg) {
- case AT_BRSF:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_brsf(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CIND:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_cind(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_OK:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_ok(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CMS_ERROR:
- case AT_ERROR:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_error(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_RING:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_ring(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CIEV:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_ciev(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CLIP:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_clip(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CMTI:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_cmti(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CMGR:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_cmgr(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_SMS_PROMPT:
- ast_mutex_lock(&pvt->lock);
- if (handle_sms_prompt(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_CUSD:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_cusd(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_BUSY:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_busy(pvt)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_NO_DIALTONE:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_no_dialtone(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_NO_CARRIER:
- ast_mutex_lock(&pvt->lock);
- if (handle_response_no_carrier(pvt, buf)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_ECAM:
- ast_mutex_lock(&pvt->lock);
- if (hfp_parse_ecav(hfp, buf) == 7) {
- if (handle_response_busy(pvt)) {
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- case AT_UNKNOWN:
- ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf);
- break;
- case AT_PARSE_ERROR:
- ast_debug(1, "[%s] error parsing message\n", pvt->id);
- goto e_cleanup;
- case AT_READ_ERROR:
- ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
- goto e_cleanup;
- default:
- break;
- }
- }
- e_cleanup:
- if (!hfp->initialized)
- ast_verb(3, "Error initializing Bluetooth device %s.\n", pvt->id);
- ast_mutex_lock(&pvt->lock);
- if (pvt->owner) {
- ast_debug(1, "[%s] device disconnected, hanging up owner\n", pvt->id);
- pvt->needchup = 0;
- mbl_queue_hangup(pvt);
- }
- close(pvt->rfcomm_socket);
- close(pvt->sco_socket);
- pvt->sco_socket = -1;
- msg_queue_flush(pvt);
- pvt->connected = 0;
- hfp->initialized = 0;
- pvt->adapter->inuse = 0;
- ast_mutex_unlock(&pvt->lock);
- ast_verb(3, "Bluetooth Device %s has disconnected.\n", pvt->id);
- manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
- return NULL;
- }
- static int headset_send_ring(const void *data)
- {
- struct mbl_pvt *pvt = (struct mbl_pvt *) data;
- ast_mutex_lock(&pvt->lock);
- if (!pvt->needring) {
- ast_mutex_unlock(&pvt->lock);
- return 0;
- }
- ast_mutex_unlock(&pvt->lock);
- if (hsp_send_ring(pvt->rfcomm_socket)) {
- ast_debug(1, "[%s] error sending RING\n", pvt->id);
- return 0;
- }
- return 1;
- }
- static void *do_monitor_headset(void *data)
- {
- struct mbl_pvt *pvt = (struct mbl_pvt *)data;
- char buf[256];
- int t;
- at_message_t at_msg;
- struct ast_channel *chan = NULL;
- ast_verb(3, "Bluetooth Device %s initialised and ready.\n", pvt->id);
- while (!check_unloading()) {
- t = ast_sched_wait(pvt->sched);
- if (t == -1) {
- t = 6000;
- }
- ast_sched_runq(pvt->sched);
- if (rfcomm_wait(pvt->rfcomm_socket, &t) == 0)
- continue;
- if ((at_msg = at_read_full(pvt->rfcomm_socket, buf, sizeof(buf))) < 0) {
- if (strerror_r(errno, buf, sizeof(buf)))
- ast_debug(1, "[%s] error reading from device\n", pvt->id);
- else
- ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, buf, errno);
- goto e_cleanup;
- }
- ast_debug(1, "[%s] %s\n", pvt->id, buf);
- switch (at_msg) {
- case AT_VGS:
- case AT_VGM:
- /* XXX volume change requested, we will just
- * pretend to do something with it */
- if (hsp_send_ok(pvt->rfcomm_socket)) {
- ast_debug(1, "[%s] error sending AT message 'OK'\n", pvt->id);
- goto e_cleanup;
- }
- break;
- case AT_CKPD:
- ast_mutex_lock(&pvt->lock);
- if (pvt->outgoing) {
- pvt->needring = 0;
- hsp_send_ok(pvt->rfcomm_socket);
- if (pvt->answered) {
- /* we have an answered call up to the
- * HS, he wants to hangup */
- mbl_queue_hangup(pvt);
- } else {
- /* we have an outgoing call to the HS,
- * he wants to answer */
- if ((pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr)) == -1) {
- ast_log(LOG_ERROR, "[%s] unable to create audio connection\n", pvt->id);
- mbl_queue_hangup(pvt);
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_channel_set_fd(pvt->owner, 0, pvt->sco_socket);
- mbl_queue_control(pvt, AST_CONTROL_ANSWER);
- pvt->answered = 1;
- if (hsp_send_vgs(pvt->rfcomm_socket, 13) || hsp_send_vgm(pvt->rfcomm_socket, 13)) {
- ast_debug(1, "[%s] error sending VGS/VGM\n", pvt->id);
- mbl_queue_hangup(pvt);
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- }
- } else if (pvt->incoming) {
- /* we have an incoming call from the
- * HS, he wants to hang up */
- mbl_queue_hangup(pvt);
- } else {
- /* no call is up, HS wants to dial */
- hsp_send_ok(pvt->rfcomm_socket);
- if ((pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr)) == -1) {
- ast_log(LOG_ERROR, "[%s] unable to create audio connection\n", pvt->id);
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- pvt->incoming = 1;
- if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL, NULL))) {
- ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- ast_channel_set_fd(chan, 0, pvt->sco_socket);
- ast_channel_exten_set(chan, "s");
- if (ast_pbx_start(chan)) {
- ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
- ast_hangup(chan);
- ast_mutex_unlock(&pvt->lock);
- goto e_cleanup;
- }
- }
- ast_mutex_unlock(&pvt->lock);
- break;
- default:
- ast_debug(1, "[%s] received unknown AT command: %s (%s)\n", pvt->id, buf, at_msg2str(at_msg));
- if (hsp_send_error(pvt->rfcomm_socket)) {
- ast_debug(1, "[%s] error sending AT message 'ERROR'\n", pvt->id);
- goto e_cleanup;
- }
- break;
- }
- }
- e_cleanup:
- ast_mutex_lock(&pvt->lock);
- if (pvt->owner) {
- ast_debug(1, "[%s] device disconnected, hanging up owner\n", pvt->id);
- mbl_queue_hangup(pvt);
- }
- close(pvt->rfcomm_socket);
- close(pvt->sco_socket);
- pvt->sco_socket = -1;
- pvt->connected = 0;
- pvt->needring = 0;
- pvt->outgoing = 0;
- pvt->incoming = 0;
- pvt->adapter->inuse = 0;
- ast_mutex_unlock(&pvt->lock);
- manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
- ast_verb(3, "Bluetooth Device %s has disconnected\n", pvt->id);
- return NULL;
- }
- static int start_monitor(struct mbl_pvt *pvt)
- {
- if (pvt->type == MBL_TYPE_PHONE) {
- pvt->hfp->rsock = pvt->rfcomm_socket;
- if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
- pvt->monitor_thread = AST_PTHREADT_NULL;
- return 0;
- }
- } else {
- if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) {
- pvt->monitor_thread = AST_PTHREADT_NULL;
- return 0;
- }
- }
- return 1;
- }
- static void *do_discovery(void *data)
- {
- struct adapter_pvt *adapter;
- struct mbl_pvt *pvt;
- while (!check_unloading()) {
- AST_RWLIST_RDLOCK(&adapters);
- AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
- if (!adapter->inuse) {
- AST_RWLIST_RDLOCK(&devices);
- AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
- ast_mutex_lock(&pvt->lock);
- if (!adapter->inuse && !pvt->connected && !strcmp(adapter->id, pvt->adapter->id)) {
- if ((pvt->rfcomm_socket = rfcomm_connect(adapter->addr, pvt->addr, pvt->rfcomm_port)) > -1) {
- if (start_monitor(pvt)) {
- pvt->connected = 1;
- adapter->inuse = 1;
- manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id);
- ast_verb(3, "Bluetooth Device %s has connected, initializing...\n", pvt->id);
- }
- }
- }
- ast_mutex_unlock(&pvt->lock);
- }
- AST_RWLIST_UNLOCK(&devices);
- }
- }
- AST_RWLIST_UNLOCK(&adapters);
- /* Go to sleep (only if we are not unloading) */
- if (!check_unloading())
- sleep(discovery_interval);
- }
- return NULL;
- }
- /*!
- * \brief Service new and existing SCO connections.
- * This thread accepts new sco connections and handles audio data. There is
- * one do_sco_listen thread for each adapter.
- */
- static void *do_sco_listen(void *data)
- {
- struct adapter_pvt *adapter = (struct adapter_pvt *) data;
- while (!check_unloading()) {
- /* check for new sco connections */
- if (ast_io_wait(adapter->accept_io, 0) == -1) {
- /* handle errors */
- ast_log(LOG_ERROR, "ast_io_wait() failed for adapter %s\n", adapter->id);
- break;
- }
- /* handle audio data */
- if (ast_io_wait(adapter->io, 1) == -1) {
- ast_log(LOG_ERROR, "ast_io_wait() failed for audio on adapter %s\n", adapter->id);
- break;
- }
- }
- return NULL;
- }
- /*
- Module
- */
- /*!
- * \brief Load an adapter from the configuration file.
- * \param cfg the config to load the adapter from
- * \param cat the adapter to load
- *
- * This function loads the given adapter and starts the sco listener thread for
- * that adapter.
- *
- * \return NULL on error, a pointer to the adapter that was loaded on success
- */
- static struct adapter_pvt *mbl_load_adapter(struct ast_config *cfg, const char *cat)
- {
- const char *id, *address;
- struct adapter_pvt *adapter;
- struct ast_variable *v;
- struct hci_dev_req dr;
- uint16_t vs;
- id = ast_variable_retrieve(cfg, cat, "id");
- address = ast_variable_retrieve(cfg, cat, "address");
- if (ast_strlen_zero(id) || ast_strlen_zero(address)) {
- ast_log(LOG_ERROR, "Skipping adapter. Missing id or address settings.\n");
- goto e_return;
- }
- ast_debug(1, "Reading configuration for adapter %s %s.\n", id, address);
- if (!(adapter = ast_calloc(1, sizeof(*adapter)))) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Error allocating memory.\n", id);
- goto e_return;
- }
- ast_copy_string(adapter->id, id, sizeof(adapter->id));
- str2ba(address, &adapter->addr);
- /* attempt to connect to the adapter */
- adapter->dev_id = hci_devid(address);
- adapter->hci_socket = hci_open_dev(adapter->dev_id);
- if (adapter->dev_id < 0 || adapter->hci_socket < 0) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Unable to communicate with adapter.\n", adapter->id);
- goto e_free_adapter;
- }
- /* check voice setting */
- hci_read_voice_setting(adapter->hci_socket, &vs, 1000);
- vs = htobs(vs);
- if (vs != 0x0060) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Voice setting must be 0x0060 - see 'man hciconfig' for details.\n", adapter->id);
- goto e_hci_close_dev;
- }
- for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
- if (!strcasecmp(v->name, "forcemaster")) {
- if (ast_true(v->value)) {
- dr.dev_id = adapter->dev_id;
- if (hci_strtolm("master", &dr.dev_opt)) {
- if (ioctl(adapter->hci_socket, HCISETLINKMODE, (unsigned long) &dr) < 0) {
- ast_log(LOG_WARNING, "Unable to set adapter %s link mode to MASTER. Ignoring 'forcemaster' option.\n", adapter->id);
- }
- }
- }
- } else if (!strcasecmp(v->name, "alignmentdetection")) {
- adapter->alignment_detection = ast_true(v->value);
- }
- }
- /* create io contexts */
- if (!(adapter->accept_io = io_context_create())) {
- ast_log(LOG_ERROR, "Unable to create I/O context for audio connection listener\n");
- goto e_hci_close_dev;
- }
- if (!(adapter->io = io_context_create())) {
- ast_log(LOG_ERROR, "Unable to create I/O context for audio connections\n");
- goto e_destroy_accept_io;
- }
- /* bind the sco listener socket */
- if (sco_bind(adapter) < 0) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Error binding audio connection listerner socket.\n", adapter->id);
- goto e_destroy_io;
- }
- /* add the socket to the io context */
- if (!(adapter->sco_id = ast_io_add(adapter->accept_io, adapter->sco_socket, sco_accept, AST_IO_IN, adapter))) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Error adding listener socket to I/O context.\n", adapter->id);
- goto e_close_sco;
- }
- /* start the sco listener for this adapter */
- if (ast_pthread_create_background(&adapter->sco_listener_thread, NULL, do_sco_listen, adapter)) {
- ast_log(LOG_ERROR, "Skipping adapter %s. Error creating audio connection listerner thread.\n", adapter->id);
- goto e_remove_sco;
- }
- /* add the adapter to our global list */
- AST_RWLIST_WRLOCK(&adapters);
- AST_RWLIST_INSERT_HEAD(&adapters, adapter, entry);
- AST_RWLIST_UNLOCK(&adapters);
- ast_debug(1, "Loaded adapter %s %s.\n", adapter->id, address);
- return adapter;
- e_remove_sco:
- ast_io_remove(adapter->accept_io, adapter->sco_id);
- e_close_sco:
- close(adapter->sco_socket);
- e_destroy_io:
- io_context_destroy(adapter->io);
- e_destroy_accept_io:
- io_context_destroy(adapter->accept_io);
- e_hci_close_dev:
- hci_close_dev(adapter->hci_socket);
- e_free_adapter:
- ast_free(adapter);
- e_return:
- return NULL;
- }
- /*!
- * \brief Load a device from the configuration file.
- * \param cfg the config to load the device from
- * \param cat the device to load
- * \return NULL on error, a pointer to the device that was loaded on success
- */
- static struct mbl_pvt *mbl_load_device(struct ast_config *cfg, const char *cat)
- {
- struct mbl_pvt *pvt;
- struct adapter_pvt *adapter;
- struct ast_variable *v;
- const char *address, *adapter_str, *port;
- ast_debug(1, "Reading configuration for device %s.\n", cat);
- adapter_str = ast_variable_retrieve(cfg, cat, "adapter");
- if(ast_strlen_zero(adapter_str)) {
- ast_log(LOG_ERROR, "Skipping device %s. No adapter specified.\n", cat);
- goto e_return;
- }
- /* find the adapter */
- AST_RWLIST_RDLOCK(&adapters);
- AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
- if (!strcmp(adapter->id, adapter_str))
- break;
- }
- AST_RWLIST_UNLOCK(&adapters);
- if (!adapter) {
- ast_log(LOG_ERROR, "Skiping device %s. Unknown adapter '%s' specified.\n", cat, adapter_str);
- goto e_return;
- }
- address = ast_variable_retrieve(cfg, cat, "address");
- port = ast_variable_retrieve(cfg, cat, "port");
- if (ast_strlen_zero(port) || ast_strlen_zero(address)) {
- ast_log(LOG_ERROR, "Skipping device %s. Missing required port or address setting.\n", cat);
- goto e_return;
- }
- /* create and initialize our pvt structure */
- if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
- ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", cat);
- goto e_return;
- }
- ast_mutex_init(&pvt->lock);
- AST_LIST_HEAD_INIT_NOLOCK(&pvt->msg_queue);
- /* set some defaults */
- pvt->type = MBL_TYPE_PHONE;
- ast_copy_string(pvt->context, "default", sizeof(pvt->context));
- /* populate the pvt structure */
- pvt->adapter = adapter;
- ast_copy_string(pvt->id, cat, sizeof(pvt->id));
- str2ba(address, &pvt->addr);
- pvt->timeout = -1;
- pvt->rfcomm_socket = -1;
- pvt->rfcomm_port = atoi(port);
- pvt->sco_socket = -1;
- pvt->monitor_thread = AST_PTHREADT_NULL;
- pvt->ring_sched_id = -1;
- pvt->has_sms = 1;
- /* setup the smoother */
- if (!(pvt->smoother = ast_smoother_new(DEVICE_FRAME_SIZE))) {
- ast_log(LOG_ERROR, "Skipping device %s. Error setting up frame smoother.\n", cat);
- goto e_free_pvt;
- }
- /* setup the dsp */
- if (!(pvt->dsp = ast_dsp_new())) {
- ast_log(LOG_ERROR, "Skipping device %s. Error setting up dsp for dtmf detection.\n", cat);
- goto e_free_smoother;
- }
- /* setup the scheduler */
- if (!(pvt->sched = ast_sched_context_create())) {
- ast_log(LOG_ERROR, "Unable to create scheduler context for headset device\n");
- goto e_free_dsp;
- }
- ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DIGIT_DETECT);
- ast_dsp_set_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
- for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
- if (!strcasecmp(v->name, "type")) {
- if (!strcasecmp(v->value, "headset"))
- pvt->type = MBL_TYPE_HEADSET;
- else
- pvt->type = MBL_TYPE_PHONE;
- } else if (!strcasecmp(v->name, "context")) {
- ast_copy_string(pvt->context, v->value, sizeof(pvt->context));
- } else if (!strcasecmp(v->name, "group")) {
- /* group is set to 0 if invalid */
- pvt->group = atoi(v->value);
- } else if (!strcasecmp(v->name, "sms")) {
- pvt->has_sms = ast_true(v->value);
- } else if (!strcasecmp(v->name, "nocallsetup")) {
- pvt->no_callsetup = ast_true(v->value);
- if (pvt->no_callsetup)
- ast_debug(1, "Setting nocallsetup mode for device %s.\n", pvt->id);
- } else if (!strcasecmp(v->name, "blackberry")) {
- pvt->blackberry = ast_true(v->value);
- pvt->has_sms = 0;
- }
- }
- if (pvt->type == MBL_TYPE_PHONE) {
- if (!(pvt->hfp = ast_calloc(1, sizeof(*pvt->hfp)))) {
- ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", pvt->id);
- goto e_free_sched;
- }
- pvt->hfp->owner = pvt;
- pvt->hfp->rport = pvt->rfcomm_port;
- pvt->hfp->nocallsetup = pvt->no_callsetup;
- } else {
- pvt->has_sms = 0;
- }
- AST_RWLIST_WRLOCK(&devices);
- AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
- AST_RWLIST_UNLOCK(&devices);
- ast_debug(1, "Loaded device %s.\n", pvt->id);
- return pvt;
- e_free_sched:
- ast_sched_context_destroy(pvt->sched);
- e_free_dsp:
- ast_dsp_free(pvt->dsp);
- e_free_smoother:
- ast_smoother_free(pvt->smoother);
- e_free_pvt:
- ast_free(pvt);
- e_return:
- return NULL;
- }
- static int mbl_load_config(void)
- {
- struct ast_config *cfg;
- const char *cat;
- struct ast_variable *v;
- struct ast_flags config_flags = { 0 };
- cfg = ast_config_load(MBL_CONFIG, config_flags);
- if (!cfg) {
- cfg = ast_config_load(MBL_CONFIG_OLD, config_flags);
- }
- if (!cfg)
- return -1;
- /* parse [general] section */
- for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
- if (!strcasecmp(v->name, "interval")) {
- if (!sscanf(v->value, "%d", &discovery_interval)) {
- ast_log(LOG_NOTICE, "error parsing 'interval' in general section, using default value\n");
- }
- }
- }
- /* load adapters */
- for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
- if (!strcasecmp(cat, "adapter")) {
- mbl_load_adapter(cfg, cat);
- }
- }
- if (AST_RWLIST_EMPTY(&adapters)) {
- ast_log(LOG_ERROR,
- "***********************************************************************\n"
- "No adapters could be loaded from the configuration file.\n"
- "Please review mobile.conf. See sample for details.\n"
- "***********************************************************************\n"
- );
- ast_config_destroy(cfg);
- return -1;
- }
- /* now load devices */
- for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
- if (strcasecmp(cat, "general") && strcasecmp(cat, "adapter")) {
- mbl_load_device(cfg, cat);
- }
- }
- ast_config_destroy(cfg);
- return 0;
- }
- /*!
- * \brief Check if the module is unloading.
- * \retval 0 not unloading
- * \retval 1 unloading
- */
- static inline int check_unloading()
- {
- int res;
- ast_mutex_lock(&unload_mutex);
- res = unloading_flag;
- ast_mutex_unlock(&unload_mutex);
- return res;
- }
- /*!
- * \brief Set the unloading flag.
- */
- static inline void set_unloading()
- {
- ast_mutex_lock(&unload_mutex);
- unloading_flag = 1;
- ast_mutex_unlock(&unload_mutex);
- }
- static int unload_module(void)
- {
- struct mbl_pvt *pvt;
- struct adapter_pvt *adapter;
- /* First, take us out of the channel loop */
- ast_channel_unregister(&mbl_tech);
- /* Unregister the CLI & APP */
- ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
- ast_unregister_application(app_mblstatus);
- ast_unregister_application(app_mblsendsms);
- /* signal everyone we are unloading */
- set_unloading();
- /* Kill the discovery thread */
- if (discovery_thread != AST_PTHREADT_NULL) {
- pthread_kill(discovery_thread, SIGURG);
- pthread_join(discovery_thread, NULL);
- }
- /* stop the sco listener threads */
- AST_RWLIST_WRLOCK(&adapters);
- AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
- pthread_kill(adapter->sco_listener_thread, SIGURG);
- pthread_join(adapter->sco_listener_thread, NULL);
- }
- AST_RWLIST_UNLOCK(&adapters);
- /* Destroy the device list */
- AST_RWLIST_WRLOCK(&devices);
- while ((pvt = AST_RWLIST_REMOVE_HEAD(&devices, entry))) {
- if (pvt->monitor_thread != AST_PTHREADT_NULL) {
- pthread_kill(pvt->monitor_thread, SIGURG);
- pthread_join(pvt->monitor_thread, NULL);
- }
- close(pvt->sco_socket);
- close(pvt->rfcomm_socket);
- msg_queue_flush(pvt);
- if (pvt->hfp) {
- ast_free(pvt->hfp);
- }
- ast_smoother_free(pvt->smoother);
- ast_dsp_free(pvt->dsp);
- ast_sched_context_destroy(pvt->sched);
- ast_free(pvt);
- }
- AST_RWLIST_UNLOCK(&devices);
- /* Destroy the adapter list */
- AST_RWLIST_WRLOCK(&adapters);
- while ((adapter = AST_RWLIST_REMOVE_HEAD(&adapters, entry))) {
- close(adapter->sco_socket);
- io_context_destroy(adapter->io);
- io_context_destroy(adapter->accept_io);
- hci_close_dev(adapter->hci_socket);
- ast_free(adapter);
- }
- AST_RWLIST_UNLOCK(&adapters);
- if (sdp_session)
- sdp_close(sdp_session);
- mbl_tech.capabilities = ast_format_cap_destroy(mbl_tech.capabilities);
- return 0;
- }
- static int load_module(void)
- {
- int dev_id, s;
- if (!(mbl_tech.capabilities = ast_format_cap_alloc(0))) {
- return AST_MODULE_LOAD_DECLINE;
- }
- ast_format_set(&prefformat, DEVICE_FRAME_FORMAT, 0);
- ast_format_cap_add(mbl_tech.capabilities, &prefformat);
- /* Check if we have Bluetooth, no point loading otherwise... */
- dev_id = hci_get_route(NULL);
- s = hci_open_dev(dev_id);
- if (dev_id < 0 || s < 0) {
- ast_log(LOG_ERROR, "No Bluetooth devices found. Not loading module.\n");
- return AST_MODULE_LOAD_DECLINE;
- }
- hci_close_dev(s);
- if (mbl_load_config()) {
- ast_log(LOG_ERROR, "Errors reading config file %s. Not loading module.\n", MBL_CONFIG);
- return AST_MODULE_LOAD_DECLINE;
- }
- sdp_session = sdp_register();
- /* Spin the discovery thread */
- if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
- ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
- goto e_cleanup;
- }
- /* register our channel type */
- if (ast_channel_register(&mbl_tech)) {
- ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile");
- goto e_cleanup;
- }
- ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
- ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc);
- ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc);
- return AST_MODULE_LOAD_SUCCESS;
- e_cleanup:
- if (sdp_session)
- sdp_close(sdp_session);
- return AST_MODULE_LOAD_FAILURE;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Bluetooth Mobile Device Channel Driver",
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
|