| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2006-2010 Nokia Corporation
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2011 Texas Instruments, Inc.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <stdlib.h>
- #include <stdint.h>
- #include <inttypes.h>
- #include <stdbool.h>
- #include <errno.h>
- #include <unistd.h>
- #include <assert.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <glib.h>
- #include <dbus/dbus.h>
- #include "bluetooth/bluetooth.h"
- #include "bluetooth/sdp.h"
- #include "bluetooth/sdp_lib.h"
- #include "lib/uuid.h"
- #include "gdbus/gdbus.h"
- #include "src/plugin.h"
- #include "src/adapter.h"
- #include "src/device.h"
- #include "src/profile.h"
- #include "src/service.h"
- #include "src/log.h"
- #include "src/error.h"
- #include "src/sdpd.h"
- #include "src/dbus-common.h"
- #include "src/shared/timeout.h"
- #include "src/shared/util.h"
- #include "avctp.h"
- #include "avrcp.h"
- #include "control.h"
- #include "media.h"
- #include "player.h"
- #include "transport.h"
- /* Company IDs for vendor dependent commands */
- #define IEEEID_BTSIG 0x001958
- /* Status codes */
- #define AVRCP_STATUS_INVALID_COMMAND 0x00
- #define AVRCP_STATUS_INVALID_PARAM 0x01
- #define AVRCP_STATUS_PARAM_NOT_FOUND 0x02
- #define AVRCP_STATUS_INTERNAL_ERROR 0x03
- #define AVRCP_STATUS_SUCCESS 0x04
- #define AVRCP_STATUS_UID_CHANGED 0x05
- #define AVRCP_STATUS_DOES_NOT_EXIST 0x09
- #define AVRCP_STATUS_INVALID_SCOPE 0x0a
- #define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b
- #define AVRCP_STATUS_INVALID_PLAYER_ID 0x11
- #define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12
- #define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15
- #define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16
- /* Packet types */
- #define AVRCP_PACKET_TYPE_SINGLE 0x00
- #define AVRCP_PACKET_TYPE_START 0x01
- #define AVRCP_PACKET_TYPE_CONTINUING 0x02
- #define AVRCP_PACKET_TYPE_END 0x03
- /* PDU types for metadata transfer */
- #define AVRCP_GET_CAPABILITIES 0x10
- #define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11
- #define AVRCP_LIST_PLAYER_VALUES 0x12
- #define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
- #define AVRCP_SET_PLAYER_VALUE 0x14
- #define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15
- #define AVRCP_GET_PLAYER_VALUE_TEXT 0x16
- #define AVRCP_DISPLAYABLE_CHARSET 0x17
- #define AVRCP_CT_BATTERY_STATUS 0x18
- #define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20
- #define AVRCP_GET_PLAY_STATUS 0x30
- #define AVRCP_REGISTER_NOTIFICATION 0x31
- #define AVRCP_REQUEST_CONTINUING 0x40
- #define AVRCP_ABORT_CONTINUING 0x41
- #define AVRCP_SET_ABSOLUTE_VOLUME 0x50
- #define AVRCP_SET_ADDRESSED_PLAYER 0x60
- #define AVRCP_SET_BROWSED_PLAYER 0x70
- #define AVRCP_GET_FOLDER_ITEMS 0x71
- #define AVRCP_CHANGE_PATH 0x72
- #define AVRCP_GET_ITEM_ATTRIBUTES 0x73
- #define AVRCP_PLAY_ITEM 0x74
- #define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75
- #define AVRCP_SEARCH 0x80
- #define AVRCP_ADD_TO_NOW_PLAYING 0x90
- #define AVRCP_GENERAL_REJECT 0xA0
- /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
- #define CAP_COMPANY_ID 0x02
- #define CAP_EVENTS_SUPPORTED 0x03
- #define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
- #define AVRCP_GET_CAPABILITIES_PARAM_LENGTH 1
- #define AVRCP_FEATURE_CATEGORY_1 0x0001
- #define AVRCP_FEATURE_CATEGORY_2 0x0002
- #define AVRCP_FEATURE_CATEGORY_3 0x0004
- #define AVRCP_FEATURE_CATEGORY_4 0x0008
- #define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010
- #define AVRCP_FEATURE_BROWSING 0x0040
- #define AVRCP_BATTERY_STATUS_NORMAL 0
- #define AVRCP_BATTERY_STATUS_WARNING 1
- #define AVRCP_BATTERY_STATUS_CRITICAL 2
- #define AVRCP_BATTERY_STATUS_EXTERNAL 3
- #define AVRCP_BATTERY_STATUS_FULL_CHARGE 4
- #define AVRCP_CHARSET_UTF8 106
- #define AVRCP_BROWSING_TIMEOUT 1
- #define AVRCP_CT_VERSION 0x0106
- #define AVRCP_TG_VERSION 0x0105
- #define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00
- #define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01
- #define AVRCP_SCOPE_SEARCH 0x02
- #define AVRCP_SCOPE_NOW_PLAYING 0x03
- #if __BYTE_ORDER == __LITTLE_ENDIAN
- struct avrcp_header {
- uint8_t company_id[3];
- uint8_t pdu_id;
- uint8_t packet_type:2;
- uint8_t rsvd:6;
- uint16_t params_len;
- uint8_t params[0];
- } __attribute__ ((packed));
- #define AVRCP_HEADER_LENGTH 7
- #elif __BYTE_ORDER == __BIG_ENDIAN
- struct avrcp_header {
- uint8_t company_id[3];
- uint8_t pdu_id;
- uint8_t rsvd:6;
- uint8_t packet_type:2;
- uint16_t params_len;
- uint8_t params[0];
- } __attribute__ ((packed));
- #define AVRCP_HEADER_LENGTH 7
- #else
- #error "Unknown byte order"
- #endif
- #define AVRCP_MTU (AVC_MTU - AVC_HEADER_LENGTH)
- #define AVRCP_PDU_MTU (AVRCP_MTU - AVRCP_HEADER_LENGTH)
- struct avrcp_browsing_header {
- uint8_t pdu_id;
- uint16_t param_len;
- uint8_t params[0];
- } __attribute__ ((packed));
- #define AVRCP_BROWSING_HEADER_LENGTH 3
- struct get_folder_items_rsp {
- uint8_t status;
- uint16_t uid_counter;
- uint16_t num_items;
- uint8_t data[0];
- } __attribute__ ((packed));
- struct folder_item {
- uint8_t type;
- uint16_t len;
- uint8_t data[0];
- } __attribute__ ((packed));
- struct player_item {
- uint16_t player_id;
- uint8_t type;
- uint32_t subtype;
- uint8_t status;
- uint8_t features[16];
- uint16_t charset;
- uint16_t namelen;
- char name[0];
- } __attribute__ ((packed));
- struct get_total_number_of_items_rsp {
- uint8_t status;
- uint16_t uid_counter;
- uint32_t num_items;
- } __attribute__ ((packed));
- struct avrcp_server {
- struct btd_adapter *adapter;
- bool browsing;
- uint32_t tg_record_id;
- uint32_t ct_record_id;
- GSList *players;
- GSList *sessions;
- };
- struct pending_pdu {
- uint8_t pdu_id;
- GList *attr_ids;
- uint16_t offset;
- };
- struct pending_list_items {
- GSList *items;
- uint32_t start;
- uint32_t end;
- uint64_t total;
- };
- struct avrcp_player {
- struct avrcp_server *server;
- GSList *sessions;
- uint16_t id;
- uint8_t scope;
- uint64_t uid;
- uint16_t uid_counter;
- bool browsed;
- bool addressed;
- uint8_t *features;
- char *path;
- guint changed_id;
- struct pending_list_items *p;
- char *change_path;
- uint64_t change_uid;
- struct avrcp_player_cb *cb;
- void *user_data;
- GDestroyNotify destroy;
- };
- struct avrcp_data {
- struct avrcp_player *player;
- uint16_t version;
- int features;
- GSList *players;
- };
- struct avrcp {
- struct avrcp_server *server;
- struct avctp *conn;
- struct btd_device *dev;
- struct avrcp_data *target;
- struct avrcp_data *controller;
- const struct passthrough_handler *passthrough_handlers;
- const struct control_pdu_handler *control_handlers;
- unsigned int passthrough_id;
- unsigned int control_id;
- unsigned int browsing_id;
- unsigned int browsing_timer;
- uint16_t supported_events;
- uint16_t registered_events;
- uint8_t transaction;
- uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
- struct pending_pdu *pending_pdu;
- };
- struct passthrough_handler {
- uint8_t op;
- bool (*func) (struct avrcp *session);
- };
- struct control_pdu_handler {
- uint8_t pdu_id;
- uint8_t code;
- uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
- uint8_t transaction);
- };
- static struct {
- uint8_t feature_bit;
- uint8_t avc;
- } passthrough_map[] = {
- { 0, AVC_SELECT },
- { 1, AVC_UP },
- { 2, AVC_DOWN },
- { 3, AVC_LEFT },
- { 4, AVC_RIGHT },
- { 5, AVC_RIGHT_UP },
- { 6, AVC_RIGHT_DOWN },
- { 7, AVC_LEFT_UP },
- { 8, AVC_LEFT_DOWN },
- { 9, AVC_ROOT_MENU },
- { 10, AVC_SETUP_MENU },
- { 11, AVC_CONTENTS_MENU },
- { 12, AVC_FAVORITE_MENU },
- { 13, AVC_EXIT },
- { 14, AVC_0 },
- { 15, AVC_1 },
- { 16, AVC_2 },
- { 17, AVC_3 },
- { 18, AVC_4 },
- { 19, AVC_5 },
- { 20, AVC_6 },
- { 21, AVC_7 },
- { 22, AVC_8 },
- { 23, AVC_9 },
- { 24, AVC_DOT },
- { 25, AVC_ENTER },
- { 26, AVC_CLEAR },
- { 27, AVC_CHANNEL_UP },
- { 28, AVC_CHANNEL_DOWN },
- { 29, AVC_CHANNEL_PREVIOUS },
- { 30, AVC_SOUND_SELECT },
- { 31, AVC_INPUT_SELECT },
- { 32, AVC_INFO },
- { 33, AVC_HELP },
- { 34, AVC_PAGE_UP },
- { 35, AVC_PAGE_DOWN },
- { 36, AVC_POWER },
- { 37, AVC_VOLUME_UP },
- { 38, AVC_VOLUME_DOWN },
- { 39, AVC_MUTE },
- { 40, AVC_PLAY },
- { 41, AVC_STOP },
- { 42, AVC_PAUSE },
- { 43, AVC_RECORD },
- { 44, AVC_REWIND },
- { 45, AVC_FAST_FORWARD },
- { 46, AVC_EJECT },
- { 47, AVC_FORWARD },
- { 48, AVC_BACKWARD },
- { 49, AVC_ANGLE },
- { 50, AVC_SUBPICTURE },
- { 51, AVC_F1 },
- { 52, AVC_F2 },
- { 53, AVC_F3 },
- { 54, AVC_F4 },
- { 55, AVC_F5 },
- { 56, AVC_VENDOR_UNIQUE },
- { 0xff, 0xff }
- };
- static GSList *servers = NULL;
- static unsigned int avctp_id = 0;
- /* Default feature bit mask for media player */
- static uint8_t default_features[16];
- /* Company IDs supported by this device */
- static uint32_t company_ids[] = {
- IEEEID_BTSIG,
- };
- static void avrcp_register_notification(struct avrcp *session, uint8_t event);
- static GList *player_list_settings(struct avrcp_player *player);
- static void avrcp_browsing_record(sdp_record_t *record, sdp_data_t *version)
- {
- sdp_list_t *apseq;
- uuid_t l2cap, avctp;
- sdp_list_t *aproto, *proto[2];
- sdp_data_t *psm;
- uint16_t ap = AVCTP_BROWSING_PSM;
- sdp_uuid16_create(&l2cap, L2CAP_UUID);
- proto[0] = sdp_list_append(NULL, &l2cap);
- psm = sdp_data_alloc(SDP_UINT16, &ap);
- proto[0] = sdp_list_append(proto[0], psm);
- apseq = sdp_list_append(NULL, proto[0]);
- sdp_uuid16_create(&avctp, AVCTP_UUID);
- proto[1] = sdp_list_append(NULL, &avctp);
- proto[1] = sdp_list_append(proto[1], version);
- apseq = sdp_list_append(apseq, proto[1]);
- aproto = sdp_list_append(NULL, apseq);
- sdp_set_add_access_protos(record, aproto);
- free(psm);
- sdp_list_free(proto[0], NULL);
- sdp_list_free(proto[1], NULL);
- sdp_list_free(apseq, NULL);
- sdp_list_free(aproto, NULL);
- }
- static sdp_record_t *avrcp_ct_record(bool browsing)
- {
- sdp_list_t *svclass_id, *pfseq, *apseq, *root;
- uuid_t root_uuid, l2cap, avctp, avrct, avrctr;
- sdp_profile_desc_t profile[1];
- sdp_list_t *aproto, *proto[2];
- sdp_record_t *record;
- sdp_data_t *psm[2], *version, *features;
- uint16_t lp = AVCTP_CONTROL_PSM;
- uint16_t avctp_ver = 0x0103;
- uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
- AVRCP_FEATURE_CATEGORY_2 |
- AVRCP_FEATURE_CATEGORY_3 |
- AVRCP_FEATURE_CATEGORY_4);
- record = sdp_record_alloc();
- if (!record)
- return NULL;
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root = sdp_list_append(NULL, &root_uuid);
- sdp_set_browse_groups(record, root);
- /* Service Class ID List */
- sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
- svclass_id = sdp_list_append(NULL, &avrct);
- sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
- svclass_id = sdp_list_append(svclass_id, &avrctr);
- sdp_set_service_classes(record, svclass_id);
- /* Protocol Descriptor List */
- sdp_uuid16_create(&l2cap, L2CAP_UUID);
- proto[0] = sdp_list_append(NULL, &l2cap);
- psm[0] = sdp_data_alloc(SDP_UINT16, &lp);
- proto[0] = sdp_list_append(proto[0], psm[0]);
- apseq = sdp_list_append(NULL, proto[0]);
- sdp_uuid16_create(&avctp, AVCTP_UUID);
- proto[1] = sdp_list_append(NULL, &avctp);
- version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
- proto[1] = sdp_list_append(proto[1], version);
- apseq = sdp_list_append(apseq, proto[1]);
- aproto = sdp_list_append(NULL, apseq);
- sdp_set_access_protos(record, aproto);
- /* Additional Protocol Descriptor List */
- if (browsing) {
- feat |= AVRCP_FEATURE_BROWSING;
- avrcp_browsing_record(record, version);
- }
- /* Bluetooth Profile Descriptor List */
- sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_CT_VERSION;
- pfseq = sdp_list_append(NULL, &profile[0]);
- sdp_set_profile_descs(record, pfseq);
- features = sdp_data_alloc(SDP_UINT16, &feat);
- sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
- sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
- free(psm[0]);
- free(version);
- sdp_list_free(proto[0], NULL);
- sdp_list_free(proto[1], NULL);
- sdp_list_free(apseq, NULL);
- sdp_list_free(pfseq, NULL);
- sdp_list_free(aproto, NULL);
- sdp_list_free(root, NULL);
- sdp_list_free(svclass_id, NULL);
- return record;
- }
- static sdp_record_t *avrcp_tg_record(bool browsing)
- {
- sdp_list_t *svclass_id, *pfseq, *apseq, *root;
- uuid_t root_uuid, l2cap, avctp, avrtg;
- sdp_profile_desc_t profile[1];
- sdp_list_t *aproto_control, *proto_control[2];
- sdp_record_t *record;
- sdp_data_t *psm_control, *version, *features;
- uint16_t lp = AVCTP_CONTROL_PSM;
- uint16_t avctp_ver = 0x0103;
- uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
- AVRCP_FEATURE_CATEGORY_2 |
- AVRCP_FEATURE_CATEGORY_3 |
- AVRCP_FEATURE_CATEGORY_4 |
- AVRCP_FEATURE_PLAYER_SETTINGS );
- record = sdp_record_alloc();
- if (!record)
- return NULL;
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root = sdp_list_append(NULL, &root_uuid);
- sdp_set_browse_groups(record, root);
- /* Service Class ID List */
- sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
- svclass_id = sdp_list_append(NULL, &avrtg);
- sdp_set_service_classes(record, svclass_id);
- /* Protocol Descriptor List */
- sdp_uuid16_create(&l2cap, L2CAP_UUID);
- proto_control[0] = sdp_list_append(NULL, &l2cap);
- psm_control = sdp_data_alloc(SDP_UINT16, &lp);
- proto_control[0] = sdp_list_append(proto_control[0], psm_control);
- apseq = sdp_list_append(NULL, proto_control[0]);
- sdp_uuid16_create(&avctp, AVCTP_UUID);
- proto_control[1] = sdp_list_append(NULL, &avctp);
- version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
- proto_control[1] = sdp_list_append(proto_control[1], version);
- apseq = sdp_list_append(apseq, proto_control[1]);
- aproto_control = sdp_list_append(NULL, apseq);
- sdp_set_access_protos(record, aproto_control);
- /* Additional Protocol Descriptor List */
- if (browsing) {
- feat |= AVRCP_FEATURE_BROWSING;
- avrcp_browsing_record(record, version);
- }
- /* Bluetooth Profile Descriptor List */
- sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_TG_VERSION;
- pfseq = sdp_list_append(NULL, &profile[0]);
- sdp_set_profile_descs(record, pfseq);
- features = sdp_data_alloc(SDP_UINT16, &feat);
- sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
- sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
- free(psm_control);
- free(version);
- sdp_list_free(proto_control[0], NULL);
- sdp_list_free(proto_control[1], NULL);
- sdp_list_free(apseq, NULL);
- sdp_list_free(aproto_control, NULL);
- sdp_list_free(pfseq, NULL);
- sdp_list_free(root, NULL);
- sdp_list_free(svclass_id, NULL);
- return record;
- }
- static void populate_default_features(void)
- {
- int i;
- for (i = 0; passthrough_map[i].feature_bit != 0xff; i++) {
- if (avctp_supports_avc(passthrough_map[i].avc)) {
- uint8_t bit = passthrough_map[i].feature_bit;
- default_features[bit >> 3] |= (1 << (bit & 7));
- }
- }
- /* supports at least AVRCP 1.4 */
- default_features[7] |= (1 << 2);
- /* supports GetTotalNumberOfItems browsing command */
- default_features[8] |= (1 << 3);
- }
- static unsigned int attr_get_max_val(uint8_t attr)
- {
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return AVRCP_EQUALIZER_ON;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return AVRCP_REPEAT_MODE_GROUP;
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return AVRCP_SHUFFLE_GROUP;
- case AVRCP_ATTRIBUTE_SCAN:
- return AVRCP_SCAN_GROUP;
- }
- return 0;
- }
- static const char *battery_status_to_str(uint8_t status)
- {
- switch (status) {
- case AVRCP_BATTERY_STATUS_NORMAL:
- return "normal";
- case AVRCP_BATTERY_STATUS_WARNING:
- return "warning";
- case AVRCP_BATTERY_STATUS_CRITICAL:
- return "critical";
- case AVRCP_BATTERY_STATUS_EXTERNAL:
- return "external";
- case AVRCP_BATTERY_STATUS_FULL_CHARGE:
- return "fullcharge";
- }
- return NULL;
- }
- /*
- * get_company_id:
- *
- * Get three-byte Company_ID from incoming AVRCP message
- */
- static uint32_t get_company_id(const uint8_t cid[3])
- {
- return cid[0] << 16 | cid[1] << 8 | cid[2];
- }
- /*
- * set_company_id:
- *
- * Set three-byte Company_ID into outgoing AVRCP message
- */
- static void set_company_id(uint8_t cid[3], uint32_t cid_in)
- {
- cid[0] = (cid_in & 0xff0000) >> 16;
- cid[1] = (cid_in & 0x00ff00) >> 8;
- cid[2] = (cid_in & 0x0000ff);
- }
- static const char *attr_to_str(uint8_t attr)
- {
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return "Equalizer";
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return "Repeat";
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return "Shuffle";
- case AVRCP_ATTRIBUTE_SCAN:
- return "Scan";
- }
- return NULL;
- }
- static int attrval_to_val(uint8_t attr, const char *value)
- {
- int ret;
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- if (!strcmp(value, "off"))
- ret = AVRCP_EQUALIZER_OFF;
- else if (!strcmp(value, "on"))
- ret = AVRCP_EQUALIZER_ON;
- else
- ret = -EINVAL;
- return ret;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- if (!strcmp(value, "off"))
- ret = AVRCP_REPEAT_MODE_OFF;
- else if (!strcmp(value, "singletrack"))
- ret = AVRCP_REPEAT_MODE_SINGLE;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_REPEAT_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_REPEAT_MODE_GROUP;
- else
- ret = -EINVAL;
- return ret;
- case AVRCP_ATTRIBUTE_SHUFFLE:
- if (!strcmp(value, "off"))
- ret = AVRCP_SHUFFLE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SHUFFLE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SHUFFLE_GROUP;
- else
- ret = -EINVAL;
- return ret;
- case AVRCP_ATTRIBUTE_SCAN:
- if (!strcmp(value, "off"))
- ret = AVRCP_SCAN_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SCAN_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SCAN_GROUP;
- else
- ret = -EINVAL;
- return ret;
- }
- return -EINVAL;
- }
- static int attr_to_val(const char *str)
- {
- if (!strcasecmp(str, "Equalizer"))
- return AVRCP_ATTRIBUTE_EQUALIZER;
- else if (!strcasecmp(str, "Repeat"))
- return AVRCP_ATTRIBUTE_REPEAT_MODE;
- else if (!strcasecmp(str, "Shuffle"))
- return AVRCP_ATTRIBUTE_SHUFFLE;
- else if (!strcasecmp(str, "Scan"))
- return AVRCP_ATTRIBUTE_SCAN;
- return -EINVAL;
- }
- static int player_get_setting(struct avrcp_player *player, uint8_t id)
- {
- const char *key;
- const char *value;
- if (player == NULL)
- return -ENOENT;
- key = attr_to_str(id);
- if (key == NULL)
- return -EINVAL;
- value = player->cb->get_setting(key, player->user_data);
- if (value == NULL)
- return -EINVAL;
- return attrval_to_val(id, value);
- }
- static int play_status_to_val(const char *status)
- {
- if (!strcasecmp(status, "stopped"))
- return AVRCP_PLAY_STATUS_STOPPED;
- else if (!strcasecmp(status, "playing"))
- return AVRCP_PLAY_STATUS_PLAYING;
- else if (!strcasecmp(status, "paused"))
- return AVRCP_PLAY_STATUS_PAUSED;
- else if (!strcasecmp(status, "forward-seek"))
- return AVRCP_PLAY_STATUS_FWD_SEEK;
- else if (!strcasecmp(status, "reverse-seek"))
- return AVRCP_PLAY_STATUS_REV_SEEK;
- else if (!strcasecmp(status, "error"))
- return AVRCP_PLAY_STATUS_ERROR;
- return -EINVAL;
- }
- static uint16_t player_settings_changed(struct avrcp_player *player,
- struct avrcp_header *pdu)
- {
- GList *settings = player_list_settings(player);
- int size = 2;
- for (; settings; settings = settings->next) {
- const char *key = settings->data;
- int attr;
- int val;
- attr = attr_to_val(key);
- if (attr < 0)
- continue;
- val = player_get_setting(player, attr);
- if (val < 0)
- continue;
- pdu->params[size++] = attr;
- pdu->params[size++] = val;
- }
- g_list_free(settings);
- pdu->params[1] = (size - 2) >> 1;
- return size;
- }
- void avrcp_player_event(struct avrcp_player *player, uint8_t id,
- const void *data)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 9];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
- uint16_t size;
- GSList *l;
- if (player->sessions == NULL)
- return;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- DBG("id=%u", id);
- if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
- code = AVC_CTYPE_REJECTED;
- size = 1;
- pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
- goto done;
- }
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
- switch (id) {
- case AVRCP_EVENT_STATUS_CHANGED:
- size = 2;
- pdu->params[1] = play_status_to_val(data);
- break;
- case AVRCP_EVENT_TRACK_CHANGED:
- size = 9;
- memcpy(&pdu->params[1], data, sizeof(uint64_t));
- break;
- case AVRCP_EVENT_TRACK_REACHED_END:
- case AVRCP_EVENT_TRACK_REACHED_START:
- size = 1;
- break;
- case AVRCP_EVENT_SETTINGS_CHANGED:
- size = player_settings_changed(player, pdu);
- break;
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- size = 5;
- memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
- memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- size = 1;
- break;
- default:
- error("Unknown event %u", id);
- return;
- }
- done:
- pdu->params_len = htons(size);
- for (l = player->sessions; l; l = l->next) {
- struct avrcp *session = l->data;
- int err;
- if (!(session->registered_events & (1 << id)))
- continue;
- err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
- buf, size + AVRCP_HEADER_LENGTH);
- if (err < 0)
- continue;
- /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
- }
- return;
- }
- static const char *metadata_to_str(uint32_t id)
- {
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
- return "Title";
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- return "Artist";
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- return "Album";
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- return "Genre";
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- return "TrackNumber";
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- return "NumberOfTracks";
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- return "Duration";
- }
- return NULL;
- }
- static const char *player_get_metadata(struct avrcp_player *player,
- uint32_t id)
- {
- const char *key;
- key = metadata_to_str(id);
- if (key == NULL)
- return NULL;
- if (player != NULL)
- return player->cb->get_metadata(key, player->user_data);
- if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
- return "";
- return NULL;
- }
- static uint16_t player_write_media_attribute(struct avrcp_player *player,
- uint32_t id, uint8_t *buf,
- uint16_t *pos,
- uint16_t *offset)
- {
- uint16_t len;
- uint16_t attr_len;
- const char *value = NULL;
- DBG("%u", id);
- value = player_get_metadata(player, id);
- if (value == NULL) {
- *offset = 0;
- return 0;
- }
- attr_len = strlen(value);
- value = ((char *) value) + *offset;
- len = attr_len - *offset;
- if (len > AVRCP_PDU_MTU - *pos) {
- len = AVRCP_PDU_MTU - *pos;
- *offset += len;
- } else {
- *offset = 0;
- }
- memcpy(&buf[*pos], value, len);
- *pos += len;
- return attr_len;
- }
- static GList *player_fill_media_attribute(struct avrcp_player *player,
- GList *attr_ids, uint8_t *buf,
- uint16_t *pos, uint16_t *offset)
- {
- struct media_attribute_header {
- uint32_t id;
- uint16_t charset;
- uint16_t len;
- } *hdr = NULL;
- GList *l;
- for (l = attr_ids; l != NULL; l = g_list_delete_link(l, l)) {
- uint32_t attr = GPOINTER_TO_UINT(l->data);
- uint16_t attr_len;
- if (*offset == 0) {
- if (*pos + sizeof(*hdr) >= AVRCP_PDU_MTU)
- break;
- hdr = (void *) &buf[*pos];
- hdr->id = htonl(attr);
- /* Always use UTF-8 */
- hdr->charset = htons(AVRCP_CHARSET_UTF8);
- *pos += sizeof(*hdr);
- }
- attr_len = player_write_media_attribute(player, attr, buf,
- pos, offset);
- if (hdr != NULL)
- hdr->len = htons(attr_len);
- if (*offset > 0)
- break;
- }
- return l;
- }
- static struct pending_pdu *pending_pdu_new(uint8_t pdu_id, GList *attr_ids,
- unsigned int offset)
- {
- struct pending_pdu *pending = g_new(struct pending_pdu, 1);
- pending->pdu_id = pdu_id;
- pending->attr_ids = attr_ids;
- pending->offset = offset;
- return pending;
- }
- static gboolean session_abort_pending_pdu(struct avrcp *session)
- {
- if (session->pending_pdu == NULL)
- return FALSE;
- g_list_free(session->pending_pdu->attr_ids);
- g_free(session->pending_pdu);
- session->pending_pdu = NULL;
- return TRUE;
- }
- static const char *attrval_to_str(uint8_t attr, uint8_t value)
- {
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- switch (value) {
- case AVRCP_EQUALIZER_ON:
- return "on";
- case AVRCP_EQUALIZER_OFF:
- return "off";
- }
- break;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- switch (value) {
- case AVRCP_REPEAT_MODE_OFF:
- return "off";
- case AVRCP_REPEAT_MODE_SINGLE:
- return "singletrack";
- case AVRCP_REPEAT_MODE_ALL:
- return "alltracks";
- case AVRCP_REPEAT_MODE_GROUP:
- return "group";
- }
- break;
- /* Shuffle and scan have the same values */
- case AVRCP_ATTRIBUTE_SHUFFLE:
- case AVRCP_ATTRIBUTE_SCAN:
- switch (value) {
- case AVRCP_SCAN_OFF:
- return "off";
- case AVRCP_SCAN_ALL:
- return "alltracks";
- case AVRCP_SCAN_GROUP:
- return "group";
- }
- break;
- }
- return NULL;
- }
- static int player_set_setting(struct avrcp_player *player, uint8_t id,
- uint8_t val)
- {
- const char *key, *value;
- key = attr_to_str(id);
- if (key == NULL)
- return -EINVAL;
- value = attrval_to_str(id, val);
- if (value == NULL)
- return -EINVAL;
- if (player == NULL)
- return -ENOENT;
- return player->cb->set_setting(key, value, player->user_data);
- }
- static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- uint16_t len = ntohs(pdu->params_len);
- unsigned int i;
- if (len != 1)
- goto err;
- DBG("id=%u", pdu->params[0]);
- switch (pdu->params[0]) {
- case CAP_COMPANY_ID:
- for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
- set_company_id(&pdu->params[2 + i * 3],
- company_ids[i]);
- }
- pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
- pdu->params[1] = G_N_ELEMENTS(company_ids);
- return AVC_CTYPE_STABLE;
- case CAP_EVENTS_SUPPORTED:
- pdu->params[1] = 0;
- for (i = 1; i <= AVRCP_EVENT_LAST; i++) {
- if (session->supported_events & (1 << i)) {
- pdu->params[1]++;
- pdu->params[pdu->params[1] + 1] = i;
- }
- }
- pdu->params_len = htons(2 + pdu->params[1]);
- return AVC_CTYPE_STABLE;
- }
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static struct avrcp_player *target_get_player(struct avrcp *session)
- {
- if (!session->target)
- return NULL;
- return session->target->player;
- }
- static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- unsigned int i;
- if (len != 0) {
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- if (!player)
- goto done;
- for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
- if (player_get_setting(player, i) < 0)
- continue;
- len++;
- pdu->params[len] = i;
- }
- done:
- pdu->params[0] = len;
- pdu->params_len = htons(len + 1);
- return AVC_CTYPE_STABLE;
- }
- static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- unsigned int i;
- if (len != 1)
- goto err;
- if (player_get_setting(player, pdu->params[0]) < 0)
- goto err;
- len = attr_get_max_val(pdu->params[0]);
- for (i = 1; i <= len; i++)
- pdu->params[i] = i;
- pdu->params[0] = len;
- pdu->params_len = htons(len + 1);
- return AVC_CTYPE_STABLE;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint32_t str_to_metadata(const char *str)
- {
- if (strcasecmp(str, "Title") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_TITLE;
- else if (strcasecmp(str, "Artist") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
- else if (strcasecmp(str, "Album") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
- else if (strcasecmp(str, "Genre") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_GENRE;
- else if (strcasecmp(str, "TrackNumber") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_TRACK;
- else if (strcasecmp(str, "NumberOfTracks") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
- else if (strcasecmp(str, "Duration") == 0)
- return AVRCP_MEDIA_ATTRIBUTE_DURATION;
- return 0;
- }
- static GList *player_list_metadata(struct avrcp_player *player)
- {
- GList *l, *attrs = NULL;
- if (player == NULL)
- return g_list_prepend(NULL,
- GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE));
- l = player->cb->list_metadata(player->user_data);
- for (; l; l = l->next) {
- const char *key = l->data;
- attrs = g_list_append(attrs,
- GUINT_TO_POINTER(str_to_metadata(key)));
- }
- return attrs;
- }
- static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- uint64_t identifier = get_le64(&pdu->params[0]);
- uint16_t pos;
- uint8_t nattr;
- GList *attr_ids;
- uint16_t offset;
- if (len < 9 || identifier != 0)
- goto err;
- nattr = pdu->params[8];
- if (len < nattr * sizeof(uint32_t) + 1)
- goto err;
- if (!nattr) {
- /*
- * Return all available information, at least
- * title must be returned if there's a track selected.
- */
- attr_ids = player_list_metadata(player);
- len = g_list_length(attr_ids);
- } else {
- unsigned int i;
- for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++) {
- uint32_t id;
- id = get_be32(&pdu->params[9] + (i * sizeof(id)));
- /* Don't add invalid attributes */
- if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
- id > AVRCP_MEDIA_ATTRIBUTE_LAST)
- continue;
- len++;
- attr_ids = g_list_prepend(attr_ids,
- GUINT_TO_POINTER(id));
- }
- attr_ids = g_list_reverse(attr_ids);
- }
- if (!len)
- goto err;
- session_abort_pending_pdu(session);
- pos = 1;
- offset = 0;
- attr_ids = player_fill_media_attribute(player, attr_ids, pdu->params,
- &pos, &offset);
- if (attr_ids != NULL) {
- session->pending_pdu = pending_pdu_new(pdu->pdu_id, attr_ids,
- offset);
- pdu->packet_type = AVRCP_PACKET_TYPE_START;
- }
- pdu->params[0] = len;
- pdu->params_len = htons(pos);
- return AVC_CTYPE_STABLE;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- uint8_t *settings;
- unsigned int i;
- if (len <= 1 || pdu->params[0] != len - 1)
- goto err;
- /*
- * Save a copy of requested settings because we can override them
- * while responding
- */
- settings = g_memdup(&pdu->params[1], pdu->params[0]);
- len = 0;
- /*
- * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
- * and send a response with the existent ones. Only if all IDs are
- * non-existent we should send an error.
- */
- for (i = 0; i < pdu->params[0]; i++) {
- int val;
- if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER ||
- settings[i] > AVRCP_ATTRIBUTE_SCAN) {
- DBG("Ignoring %u", settings[i]);
- continue;
- }
- val = player_get_setting(player, settings[i]);
- if (val < 0)
- continue;
- pdu->params[++len] = settings[i];
- pdu->params[++len] = val;
- }
- g_free(settings);
- if (len) {
- pdu->params[0] = len / 2;
- pdu->params_len = htons(len + 1);
- return AVC_CTYPE_STABLE;
- }
- error("No valid attributes in request");
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_set_player_value(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- unsigned int i;
- uint8_t *param;
- if (len < 3 || len > 2 * pdu->params[0] + 1U || player == NULL)
- goto err;
- /*
- * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
- * and set the existent ones. Sec. 5.2.4 is not clear however how to
- * indicate that a certain ID was not accepted. If at least one
- * attribute is valid, we respond with no parameters. Otherwise an
- * AVRCP_STATUS_INVALID_PARAM is sent.
- */
- for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0];
- i++, param += 2) {
- if (player_set_setting(player, param[0], param[1]) < 0)
- continue;
- len++;
- }
- if (len) {
- pdu->params_len = 0;
- return AVC_CTYPE_ACCEPTED;
- }
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_displayable_charset(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- uint16_t len = ntohs(pdu->params_len);
- if (len < 3) {
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- /*
- * We acknowledge the commands, but we always use UTF-8 for
- * encoding since CT is obliged to support it.
- */
- pdu->params_len = 0;
- return AVC_CTYPE_STABLE;
- }
- static uint8_t avrcp_handle_ct_battery_status(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- uint16_t len = ntohs(pdu->params_len);
- const char *valstr;
- if (len != 1)
- goto err;
- valstr = battery_status_to_str(pdu->params[0]);
- if (valstr == NULL)
- goto err;
- pdu->params_len = 0;
- return AVC_CTYPE_STABLE;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint32_t player_get_position(struct avrcp_player *player)
- {
- if (player == NULL)
- return 0;
- return player->cb->get_position(player->user_data);
- }
- static uint32_t player_get_duration(struct avrcp_player *player)
- {
- uint32_t num;
- if (player == NULL)
- return UINT32_MAX;
- num = player->cb->get_duration(player->user_data);
- if (num == 0)
- return UINT32_MAX;
- return num;
- }
- static uint8_t player_get_status(struct avrcp_player *player)
- {
- const char *value;
- if (player == NULL)
- return AVRCP_PLAY_STATUS_STOPPED;
- value = player->cb->get_status(player->user_data);
- if (value == NULL)
- return AVRCP_PLAY_STATUS_STOPPED;
- return play_status_to_val(value);
- }
- static uint16_t player_get_id(struct avrcp_player *player)
- {
- if (player == NULL)
- return 0x0000;
- return player->id;
- }
- static uint16_t player_get_uid_counter(struct avrcp_player *player)
- {
- if (player == NULL)
- return 0x0000;
- return player->uid_counter;
- }
- static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- uint32_t position;
- uint32_t duration;
- if (len != 0) {
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- position = player_get_position(player);
- duration = player_get_duration(player);
- position = htonl(position);
- duration = htonl(duration);
- memcpy(&pdu->params[0], &duration, 4);
- memcpy(&pdu->params[4], &position, 4);
- pdu->params[8] = player_get_status(player);
- pdu->params_len = htons(9);
- return AVC_CTYPE_STABLE;
- }
- static uint64_t player_get_uid(struct avrcp_player *player)
- {
- if (player == NULL)
- return UINT64_MAX;
- return player->cb->get_uid(player->user_data);
- }
- static GList *player_list_settings(struct avrcp_player *player)
- {
- if (player == NULL)
- return NULL;
- return player->cb->list_settings(player->user_data);
- }
- static bool avrcp_handle_play(struct avrcp *session)
- {
- struct avrcp_player *player = target_get_player(session);
- if (player == NULL)
- return false;
- return player->cb->play(player->user_data);
- }
- static bool avrcp_handle_stop(struct avrcp *session)
- {
- struct avrcp_player *player = target_get_player(session);
- if (player == NULL)
- return false;
- return player->cb->stop(player->user_data);
- }
- static bool avrcp_handle_pause(struct avrcp *session)
- {
- struct avrcp_player *player = target_get_player(session);
- if (player == NULL)
- return false;
- return player->cb->pause(player->user_data);
- }
- static bool avrcp_handle_next(struct avrcp *session)
- {
- struct avrcp_player *player = target_get_player(session);
- if (player == NULL)
- return false;
- return player->cb->next(player->user_data);
- }
- static bool avrcp_handle_previous(struct avrcp *session)
- {
- struct avrcp_player *player = target_get_player(session);
- if (player == NULL)
- return false;
- return player->cb->previous(player->user_data);
- }
- static const struct passthrough_handler passthrough_handlers[] = {
- { AVC_PLAY, avrcp_handle_play },
- { AVC_STOP, avrcp_handle_stop },
- { AVC_PAUSE, avrcp_handle_pause },
- { AVC_FORWARD, avrcp_handle_next },
- { AVC_BACKWARD, avrcp_handle_previous },
- { },
- };
- static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
- void *user_data)
- {
- struct avrcp *session = user_data;
- const struct passthrough_handler *handler;
- for (handler = session->passthrough_handlers; handler->func;
- handler++) {
- if (handler->op == op)
- break;
- }
- if (handler->func == NULL)
- return false;
- /* Do not trigger handler on release */
- if (!pressed)
- return true;
- return handler->func(session);
- }
- static uint8_t avrcp_handle_register_notification(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- struct btd_device *dev = session->dev;
- uint16_t len = ntohs(pdu->params_len);
- uint64_t uid;
- int8_t volume;
- /*
- * 1 byte for EventID, 4 bytes for Playback interval but the latest
- * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
- * 1.3 spec, section 5.4.2.
- */
- if (len != 5)
- goto err;
- /* Check if event is supported otherwise reject */
- if (!(session->supported_events & (1 << pdu->params[0])))
- goto err;
- switch (pdu->params[0]) {
- case AVRCP_EVENT_STATUS_CHANGED:
- len = 2;
- pdu->params[1] = player_get_status(player);
- break;
- case AVRCP_EVENT_TRACK_CHANGED:
- len = 9;
- uid = player_get_uid(player);
- memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
- break;
- case AVRCP_EVENT_TRACK_REACHED_END:
- case AVRCP_EVENT_TRACK_REACHED_START:
- len = 1;
- break;
- case AVRCP_EVENT_SETTINGS_CHANGED:
- len = player_settings_changed(player, pdu);
- break;
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- len = 5;
- bt_put_be16(player_get_id(player), &pdu->params[1]);
- bt_put_be16(player_get_uid_counter(player), &pdu->params[3]);
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- len = 1;
- break;
- case AVRCP_EVENT_VOLUME_CHANGED:
- volume = media_transport_get_device_volume(dev);
- if (volume < 0)
- goto err;
- pdu->params[1] = volume;
- len = 2;
- break;
- default:
- /* All other events are not supported yet */
- goto err;
- }
- /* Register event and save the transaction used */
- session->registered_events |= (1 << pdu->params[0]);
- session->transaction_events[pdu->params[0]] = transaction;
- pdu->params_len = htons(len);
- return AVC_CTYPE_INTERIM;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_request_continuing(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player = target_get_player(session);
- uint16_t len = ntohs(pdu->params_len);
- struct pending_pdu *pending;
- if (len != 1 || session->pending_pdu == NULL)
- goto err;
- pending = session->pending_pdu;
- if (pending->pdu_id != pdu->params[0])
- goto err;
- len = 0;
- pending->attr_ids = player_fill_media_attribute(player,
- pending->attr_ids,
- pdu->params, &len,
- &pending->offset);
- pdu->pdu_id = pending->pdu_id;
- if (pending->attr_ids == NULL) {
- g_free(session->pending_pdu);
- session->pending_pdu = NULL;
- pdu->packet_type = AVRCP_PACKET_TYPE_END;
- } else {
- pdu->packet_type = AVRCP_PACKET_TYPE_CONTINUING;
- }
- pdu->params_len = htons(len);
- return AVC_CTYPE_STABLE;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_abort_continuing(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- uint16_t len = ntohs(pdu->params_len);
- struct pending_pdu *pending;
- if (len != 1 || session->pending_pdu == NULL)
- goto err;
- pending = session->pending_pdu;
- if (pending->pdu_id != pdu->params[0])
- goto err;
- session_abort_pending_pdu(session);
- pdu->params_len = 0;
- return AVC_CTYPE_ACCEPTED;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- uint16_t len = ntohs(pdu->params_len);
- int8_t volume;
- if (len != 1)
- goto err;
- volume = pdu->params[0] & 0x7F;
- media_transport_update_device_volume(session->dev, volume);
- return AVC_CTYPE_ACCEPTED;
- err:
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- return AVC_CTYPE_REJECTED;
- }
- static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
- {
- struct avrcp_server *server = session->server;
- GSList *l;
- for (l = server->players; l; l = l->next) {
- struct avrcp_player *player = l->data;
- if (player->id == id)
- return player;
- }
- return NULL;
- }
- static gboolean notify_addressed_player_changed(gpointer user_data)
- {
- struct avrcp_player *player = user_data;
- uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
- AVRCP_EVENT_TRACK_CHANGED,
- AVRCP_EVENT_TRACK_REACHED_START,
- AVRCP_EVENT_TRACK_REACHED_END,
- AVRCP_EVENT_SETTINGS_CHANGED,
- AVRCP_EVENT_PLAYBACK_POS_CHANGED
- };
- uint8_t i;
- /*
- * Set changed_id to an non-zero value to indicate addreddsed player
- * changed.
- */
- player->changed_id = 1;
- avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
- /*
- * TG shall complete all player specific
- * notifications with AV/C C-Type REJECTED
- * with error code as Addressed Player Changed.
- */
- for (i = 0; i < sizeof(events); i++)
- avrcp_player_event(player, events[i], NULL);
- player->changed_id = 0;
- return FALSE;
- }
- static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
- {
- struct avrcp_player *player;
- uint16_t len = ntohs(pdu->params_len);
- uint16_t player_id = 0;
- uint8_t status;
- if (len < 1) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto err;
- }
- player_id = bt_get_be16(&pdu->params[0]);
- player = find_tg_player(session, player_id);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- if (player) {
- player->addressed = true;
- status = AVRCP_STATUS_SUCCESS;
- pdu->params_len = htons(len);
- pdu->params[0] = status;
- } else {
- status = AVRCP_STATUS_INVALID_PLAYER_ID;
- goto err;
- }
- /* Don't emit player changed immediately since PTS expect the
- * response of SetAddressedPlayer before the event.
- */
- player->changed_id = g_idle_add(notify_addressed_player_changed,
- player);
- return AVC_CTYPE_ACCEPTED;
- err:
- pdu->params_len = htons(sizeof(status));
- pdu->params[0] = status;
- return AVC_CTYPE_REJECTED;
- }
- static const struct control_pdu_handler control_handlers[] = {
- { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
- avrcp_handle_get_capabilities },
- { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
- avrcp_handle_list_player_attributes },
- { AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
- avrcp_handle_list_player_values },
- { AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
- avrcp_handle_get_element_attributes },
- { AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
- avrcp_handle_get_current_player_value },
- { AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
- avrcp_handle_set_player_value },
- { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
- NULL },
- { AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
- NULL },
- { AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
- avrcp_handle_displayable_charset },
- { AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
- avrcp_handle_ct_battery_status },
- { AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
- avrcp_handle_get_play_status },
- { AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
- avrcp_handle_register_notification },
- { AVRCP_SET_ABSOLUTE_VOLUME, AVC_CTYPE_CONTROL,
- avrcp_handle_set_absolute_volume },
- { AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL,
- avrcp_handle_request_continuing },
- { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
- avrcp_handle_abort_continuing },
- { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
- avrcp_handle_set_addressed_player },
- { },
- };
- /* handle vendordep pdu inside an avctp packet */
- static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
- uint8_t *code, uint8_t *subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- const struct control_pdu_handler *handler;
- struct avrcp_header *pdu = (void *) operands;
- uint32_t company_id = get_company_id(pdu->company_id);
- if (company_id != IEEEID_BTSIG) {
- *code = AVC_CTYPE_NOT_IMPLEMENTED;
- return 0;
- }
- DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
- pdu->pdu_id, company_id, ntohs(pdu->params_len));
- pdu->packet_type = 0;
- pdu->rsvd = 0;
- if (operand_count < AVRCP_HEADER_LENGTH) {
- pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
- goto err_metadata;
- }
- operands += sizeof(*pdu);
- operand_count -= sizeof(*pdu);
- if (ntohs(pdu->params_len) != operand_count) {
- DBG("AVRCP PDU parameters length don't match");
- pdu->params_len = htons(operand_count);
- }
- for (handler = session->control_handlers; handler->pdu_id; handler++) {
- if (handler->pdu_id == pdu->pdu_id)
- break;
- }
- if (handler->pdu_id != pdu->pdu_id || handler->code != *code) {
- pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
- goto err_metadata;
- }
- if (!handler->func) {
- pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
- goto err_metadata;
- }
- *code = handler->func(session, pdu, transaction);
- if (*code != AVC_CTYPE_REJECTED &&
- pdu->pdu_id != AVRCP_GET_ELEMENT_ATTRIBUTES &&
- pdu->pdu_id != AVRCP_REQUEST_CONTINUING &&
- pdu->pdu_id != AVRCP_ABORT_CONTINUING)
- session_abort_pending_pdu(session);
- return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- err_metadata:
- pdu->params_len = htons(1);
- *code = AVC_CTYPE_REJECTED;
- return AVRCP_HEADER_LENGTH + 1;
- }
- static void avrcp_handle_media_player_list(struct avrcp *session,
- struct avrcp_browsing_header *pdu,
- uint32_t start_item, uint32_t end_item)
- {
- struct avrcp_player *player = session->target->player;
- struct get_folder_items_rsp *rsp;
- const char *name = NULL;
- GSList *l;
- rsp = (void *)pdu->params;
- rsp->status = AVRCP_STATUS_SUCCESS;
- rsp->uid_counter = htons(player_get_uid_counter(player));
- rsp->num_items = 0;
- pdu->param_len = sizeof(*rsp);
- for (l = g_slist_nth(session->server->players, start_item);
- l; l = g_slist_next(l)) {
- struct avrcp_player *player = l->data;
- struct folder_item *folder;
- struct player_item *item;
- uint16_t namelen;
- if (rsp->num_items == (end_item - start_item) + 1)
- break;
- folder = (void *)&pdu->params[pdu->param_len];
- folder->type = 0x01; /* Media Player */
- pdu->param_len += sizeof(*folder);
- item = (void *)folder->data;
- item->player_id = htons(player->id);
- item->type = 0x01; /* Audio */
- item->subtype = htonl(0x01); /* Audio Book */
- item->status = player_get_status(player);
- /* Assign Default Feature Bit Mask */
- memcpy(&item->features, &default_features,
- sizeof(default_features));
- item->charset = htons(AVRCP_CHARSET_UTF8);
- name = player->cb->get_name(player->user_data);
- namelen = strlen(name);
- item->namelen = htons(namelen);
- memcpy(item->name, name, namelen);
- folder->len = htons(sizeof(*item) + namelen);
- pdu->param_len += sizeof(*item) + namelen;
- rsp->num_items++;
- }
- /* If no player could be found respond with an error */
- if (!rsp->num_items)
- goto failed;
- rsp->num_items = htons(rsp->num_items);
- pdu->param_len = htons(pdu->param_len);
- return;
- failed:
- pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS;
- pdu->param_len = htons(1);
- }
- static void avrcp_handle_get_folder_items(struct avrcp *session,
- struct avrcp_browsing_header *pdu,
- uint8_t transaction)
- {
- uint32_t start_item = 0;
- uint32_t end_item = 0;
- uint8_t scope;
- uint8_t status = AVRCP_STATUS_SUCCESS;
- if (ntohs(pdu->param_len) < 10) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto failed;
- }
- scope = pdu->params[0];
- start_item = bt_get_be32(&pdu->params[1]);
- end_item = bt_get_be32(&pdu->params[5]);
- DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope,
- start_item, end_item);
- if (end_item < start_item) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto failed;
- }
- switch (scope) {
- case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
- avrcp_handle_media_player_list(session, pdu,
- start_item, end_item);
- break;
- case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
- case AVRCP_SCOPE_SEARCH:
- case AVRCP_SCOPE_NOW_PLAYING:
- default:
- status = AVRCP_STATUS_INVALID_SCOPE;
- goto failed;
- }
- return;
- failed:
- pdu->params[0] = status;
- pdu->param_len = htons(1);
- }
- static void avrcp_handle_media_player_list_num_items(struct avrcp *session,
- struct avrcp_browsing_header *pdu)
- {
- struct avrcp_player *player = target_get_player(session);
- struct get_total_number_of_items_rsp *rsp;
- rsp = (void *)pdu->params;
- rsp->status = AVRCP_STATUS_SUCCESS;
- rsp->uid_counter = htons(player_get_uid_counter(player));
- rsp->num_items = htonl(g_slist_length(session->server->players));
- pdu->param_len = htons(sizeof(*rsp));
- }
- static void avrcp_handle_get_total_number_of_items(struct avrcp *session,
- struct avrcp_browsing_header *pdu,
- uint8_t transaction)
- {
- uint8_t scope;
- uint8_t status = AVRCP_STATUS_SUCCESS;
- if (ntohs(pdu->param_len) != 1) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto failed;
- }
- scope = pdu->params[0];
- switch (scope) {
- case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
- avrcp_handle_media_player_list_num_items(session, pdu);
- break;
- case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
- case AVRCP_SCOPE_SEARCH:
- case AVRCP_SCOPE_NOW_PLAYING:
- default:
- status = AVRCP_STATUS_INVALID_SCOPE;
- goto failed;
- }
- return;
- failed:
- pdu->params[0] = status;
- pdu->param_len = htons(1);
- }
- static struct browsing_pdu_handler {
- uint8_t pdu_id;
- void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
- uint8_t transaction);
- } browsing_handlers[] = {
- { AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items },
- { AVRCP_GET_TOTAL_NUMBER_OF_ITEMS,
- avrcp_handle_get_total_number_of_items },
- { },
- };
- size_t avrcp_browsing_general_reject(uint8_t *operands)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- uint8_t status;
- pdu->pdu_id = AVRCP_GENERAL_REJECT;
- status = AVRCP_STATUS_INVALID_COMMAND;
- pdu->param_len = htons(sizeof(status));
- memcpy(pdu->params, &status, (sizeof(status)));
- return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
- }
- static size_t handle_browsing_pdu(struct avctp *conn,
- uint8_t transaction, uint8_t *operands,
- size_t operand_count, void *user_data)
- {
- struct avrcp *session = user_data;
- struct browsing_pdu_handler *handler;
- struct avrcp_browsing_header *pdu = (void *) operands;
- DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
- ntohs(pdu->param_len));
- for (handler = browsing_handlers; handler->pdu_id; handler++) {
- if (handler->pdu_id == pdu->pdu_id)
- goto done;
- }
- return avrcp_browsing_general_reject(operands);
- done:
- session->transaction = transaction;
- handler->func(session, pdu, transaction);
- return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len);
- }
- size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
- {
- struct avrcp_header *pdu = (void *) operands;
- uint32_t company_id = get_company_id(pdu->company_id);
- *code = AVC_CTYPE_REJECTED;
- pdu->params_len = htons(1);
- pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
- DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
- pdu->pdu_id, company_id, ntohs(pdu->params_len));
- return AVRCP_HEADER_LENGTH + 1;
- }
- static struct avrcp_server *find_server(GSList *list, struct btd_adapter *a)
- {
- for (; list; list = list->next) {
- struct avrcp_server *server = list->data;
- if (server->adapter == a)
- return server;
- }
- return NULL;
- }
- static const char *status_to_string(uint8_t status)
- {
- switch (status) {
- case AVRCP_PLAY_STATUS_STOPPED:
- return "stopped";
- case AVRCP_PLAY_STATUS_PLAYING:
- return "playing";
- case AVRCP_PLAY_STATUS_PAUSED:
- return "paused";
- case AVRCP_PLAY_STATUS_FWD_SEEK:
- return "forward-seek";
- case AVRCP_PLAY_STATUS_REV_SEEK:
- return "reverse-seek";
- case AVRCP_PLAY_STATUS_ERROR:
- return "error";
- default:
- return NULL;
- }
- }
- static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- struct avrcp_header *pdu = (void *) operands;
- uint32_t duration;
- uint32_t position;
- uint8_t status;
- if (pdu == NULL || code == AVC_CTYPE_REJECTED ||
- ntohs(pdu->params_len) != 9)
- return FALSE;
- memcpy(&duration, pdu->params, sizeof(uint32_t));
- duration = ntohl(duration);
- media_player_set_duration(mp, duration);
- memcpy(&position, pdu->params + 4, sizeof(uint32_t));
- position = ntohl(position);
- media_player_set_position(mp, position);
- memcpy(&status, pdu->params + 8, sizeof(uint8_t));
- media_player_set_status(mp, status_to_string(status));
- return FALSE;
- }
- static void avrcp_get_play_status(struct avrcp *session)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_get_play_status_rsp,
- session);
- }
- static const char *status_to_str(uint8_t status)
- {
- switch (status) {
- case AVRCP_STATUS_INVALID_COMMAND:
- return "Invalid Command";
- case AVRCP_STATUS_INVALID_PARAM:
- return "Invalid Parameter";
- case AVRCP_STATUS_INTERNAL_ERROR:
- return "Internal Error";
- case AVRCP_STATUS_SUCCESS:
- return "Success";
- default:
- return "Unknown";
- }
- }
- static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- struct avrcp_header *pdu = (void *) operands;
- uint8_t count;
- int i;
- if (pdu == NULL) {
- media_player_set_setting(mp, "Error", "Timeout");
- return FALSE;
- }
- if (code == AVC_CTYPE_REJECTED) {
- media_player_set_setting(mp, "Error",
- status_to_str(pdu->params[0]));
- return FALSE;
- }
- count = pdu->params[0];
- if (pdu->params_len < count * 2)
- return FALSE;
- for (i = 1; count > 0; count--, i += 2) {
- const char *key;
- const char *value;
- key = attr_to_str(pdu->params[i]);
- if (key == NULL)
- continue;
- value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
- if (value == NULL)
- continue;
- media_player_set_setting(mp, key, value);
- }
- return FALSE;
- }
- static void avrcp_get_current_player_value(struct avrcp *session,
- uint8_t *attrs, uint8_t count)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_ATTRIBUTE_LAST + 1];
- struct avrcp_header *pdu = (void *) buf;
- uint16_t length = AVRCP_HEADER_LENGTH + count + 1;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params_len = htons(count + 1);
- pdu->params[0] = count;
- memcpy(pdu->params + 1, attrs, count);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_player_value_rsp, session);
- }
- static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
- uint8_t transaction, uint8_t *operands,
- size_t operand_count, void *user_data)
- {
- uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
- struct avrcp *session = user_data;
- struct avrcp_header *pdu = (void *) operands;
- uint8_t len, count = 0;
- int i;
- if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED)
- return FALSE;
- len = pdu->params[0];
- if (ntohs(pdu->params_len) < count) {
- error("Invalid parameters");
- return FALSE;
- }
- for (i = 0; len > 0; len--, i++) {
- /* Don't query invalid attributes */
- if (pdu->params[i + 1] == AVRCP_ATTRIBUTE_ILEGAL ||
- pdu->params[i + 1] > AVRCP_ATTRIBUTE_LAST)
- continue;
- attrs[count++] = pdu->params[i + 1];
- }
- avrcp_get_current_player_value(session, attrs, count);
- return FALSE;
- }
- static void avrcp_list_player_attributes(struct avrcp *session)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_list_player_attributes_rsp,
- session);
- }
- static void avrcp_parse_attribute_list(struct avrcp_player *player,
- uint8_t *operands, uint8_t count)
- {
- struct media_player *mp = player->user_data;
- struct media_item *item;
- int i;
- item = media_player_set_playlist_item(mp, player->uid);
- for (i = 0; count > 0; count--) {
- uint32_t id;
- uint16_t charset, len;
- id = get_be32(&operands[i]);
- i += sizeof(uint32_t);
- charset = get_be16(&operands[i]);
- i += sizeof(uint16_t);
- len = get_be16(&operands[i]);
- i += sizeof(uint16_t);
- if (charset == 106) {
- const char *key = metadata_to_str(id);
- if (key != NULL)
- media_player_set_metadata(mp, item,
- metadata_to_str(id),
- &operands[i], len);
- }
- i += len;
- }
- }
- static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
- uint8_t transaction,
- uint8_t *operands,
- size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct avrcp_header *pdu = (void *) operands;
- uint8_t count;
- if (code == AVC_CTYPE_REJECTED)
- return FALSE;
- count = pdu->params[0];
- if (ntohs(pdu->params_len) - 1 < count * 8) {
- error("Invalid parameters");
- return FALSE;
- }
- avrcp_parse_attribute_list(player, &pdu->params[1], count);
- avrcp_get_play_status(session);
- return FALSE;
- }
- static void avrcp_get_element_attributes(struct avrcp *session)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 9];
- struct avrcp_header *pdu = (void *) buf;
- uint16_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
- pdu->params_len = htons(9);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_get_element_attributes_rsp,
- session);
- }
- static const char *type_to_string(uint8_t type)
- {
- switch (type & 0x0F) {
- case 0x01:
- return "Audio";
- case 0x02:
- return "Video";
- case 0x03:
- return "Audio, Video";
- case 0x04:
- return "Audio Broadcasting";
- case 0x05:
- return "Audio, Audio Broadcasting";
- case 0x06:
- return "Video, Audio Broadcasting";
- case 0x07:
- return "Audio, Video, Audio Broadcasting";
- case 0x08:
- return "Video Broadcasting";
- case 0x09:
- return "Audio, Video Broadcasting";
- case 0x0A:
- return "Video, Video Broadcasting";
- case 0x0B:
- return "Audio, Video, Video Broadcasting";
- case 0x0C:
- return "Audio Broadcasting, Video Broadcasting";
- case 0x0D:
- return "Audio, Audio Broadcasting, Video Broadcasting";
- case 0x0E:
- return "Video, Audio Broadcasting, Video Broadcasting";
- case 0x0F:
- return "Audio, Video, Audio Broadcasting, Video Broadcasting";
- }
- return "None";
- }
- static const char *subtype_to_string(uint32_t subtype)
- {
- switch (subtype & 0x03) {
- case 0x01:
- return "Audio Book";
- case 0x02:
- return "Podcast";
- case 0x03:
- return "Audio Book, Podcast";
- }
- return "None";
- }
- static struct media_item *parse_media_element(struct avrcp *session,
- uint8_t *operands, uint16_t len)
- {
- struct avrcp_player *player;
- struct media_player *mp;
- struct media_item *item;
- uint16_t namelen;
- char name[255];
- uint64_t uid;
- if (len < 13)
- return NULL;
- uid = get_be64(&operands[0]);
- namelen = MIN(get_be16(&operands[11]), sizeof(name) - 1);
- if (namelen > 0) {
- memcpy(name, &operands[13], namelen);
- name[namelen] = '\0';
- }
- player = session->controller->player;
- mp = player->user_data;
- item = media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
- if (item == NULL)
- return NULL;
- media_item_set_playable(item, true);
- return item;
- }
- static struct media_item *parse_media_folder(struct avrcp *session,
- uint8_t *operands, uint16_t len)
- {
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- struct media_item *item;
- uint16_t namelen;
- char name[255];
- uint64_t uid;
- uint8_t type;
- uint8_t playable;
- if (len < 12)
- return NULL;
- uid = get_be64(&operands[0]);
- type = operands[8];
- playable = operands[9];
- namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1);
- if (namelen > 0) {
- memcpy(name, &operands[14], namelen);
- name[namelen] = '\0';
- }
- item = media_player_create_folder(mp, name, type, uid);
- if (!item)
- return NULL;
- media_item_set_playable(item, playable & 0x01);
- return item;
- }
- static void avrcp_list_items(struct avrcp *session, uint32_t start,
- uint32_t end);
- static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
- size_t operand_count, void *user_data)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct pending_list_items *p = player->p;
- uint16_t count;
- uint64_t items;
- size_t i;
- int err = 0;
- if (pdu == NULL) {
- err = -ETIMEDOUT;
- goto done;
- }
- /* AVRCP 1.5 - Page 76:
- * If the TG receives a GetFolderItems command for an empty folder then
- * the TG shall return the error (= Range Out of Bounds) in the status
- * field of the GetFolderItems response.
- */
- if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
- goto done;
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5) {
- err = -EINVAL;
- goto done;
- }
- count = get_be16(&operands[6]);
- if (count == 0)
- goto done;
- for (i = 8; count && i + 3 < operand_count; count--) {
- struct media_item *item;
- uint8_t type;
- uint16_t len;
- type = operands[i++];
- len = get_be16(&operands[i]);
- i += 2;
- if (type != 0x03 && type != 0x02) {
- i += len;
- continue;
- }
- if (i + len > operand_count) {
- error("Invalid item length");
- break;
- }
- if (type == 0x03)
- item = parse_media_element(session, &operands[i], len);
- else
- item = parse_media_folder(session, &operands[i], len);
- if (item)
- p->items = g_slist_append(p->items, item);
- i += len;
- }
- items = g_slist_length(p->items);
- DBG("start %u end %u items %" PRIu64 " total %" PRIu64 "", p->start,
- p->end, items, p->total);
- if (items < p->total) {
- avrcp_list_items(session, p->start + items, p->end);
- return FALSE;
- }
- done:
- media_player_list_complete(player->user_data, p->items, err);
- g_slist_free(p->items);
- g_free(p);
- player->p = NULL;
- return FALSE;
- }
- static void avrcp_list_items(struct avrcp *session, uint32_t start,
- uint32_t end)
- {
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10 +
- AVRCP_MEDIA_ATTRIBUTE_LAST * sizeof(uint32_t)];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_browsing_header *pdu = (void *) buf;
- uint16_t length = AVRCP_BROWSING_HEADER_LENGTH + 10;
- uint32_t attribute;
- memset(buf, 0, sizeof(buf));
- pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
- pdu->param_len = htons(10 + sizeof(uint32_t));
- pdu->params[0] = player->scope;
- put_be32(start, &pdu->params[1]);
- put_be32(end, &pdu->params[5]);
- pdu->params[9] = 1;
- /* Only the title (0x01) is mandatory. This can be extended to
- * support AVRCP_MEDIA_ATTRIBUTE_* attributes */
- attribute = htonl(AVRCP_MEDIA_ATTRIBUTE_TITLE);
- memcpy(&pdu->params[10], &attribute, sizeof(uint32_t));
- length += sizeof(uint32_t);
- avctp_send_browsing_req(session->conn, buf, length,
- avrcp_list_items_rsp, session);
- }
- static gboolean avrcp_change_path_rsp(struct avctp *conn,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- int ret;
- if (pdu == NULL) {
- ret = -ETIMEDOUT;
- goto done;
- }
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS) {
- ret = -EINVAL;
- goto done;
- }
- ret = get_be32(&pdu->params[1]);
- done:
- if (ret < 0) {
- g_free(player->change_path);
- player->change_path = NULL;
- } else {
- g_free(player->path);
- player->path = player->change_path;
- player->change_path = NULL;
- }
- media_player_change_folder_complete(mp, player->path,
- player->change_uid, ret);
- player->change_uid = 0;
- return FALSE;
- }
- static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
- uint8_t *operands,
- size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- struct avrcp_browsing_header *pdu = (void *) operands;
- uint32_t items;
- char **folders;
- uint8_t depth, count;
- size_t i;
- if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
- operand_count < 13)
- return FALSE;
- player->uid_counter = get_be16(&pdu->params[1]);
- player->browsed = true;
- items = get_be32(&pdu->params[3]);
- depth = pdu->params[9];
- folders = g_new0(char *, depth + 2);
- folders[0] = g_strdup("/Filesystem");
- for (i = 10, count = 1; count - 1 < depth && i < operand_count;
- count++) {
- uint8_t len;
- len = pdu->params[i++];
- if (!len)
- continue;
- if (i + len > operand_count) {
- error("Invalid folder length");
- break;
- }
- folders[count] = g_memdup(&pdu->params[i], len);
- i += len;
- }
- player->path = g_build_pathv("/", folders);
- g_strfreev(folders);
- media_player_set_folder(mp, player->path, items);
- return FALSE;
- }
- static void avrcp_set_browsed_player(struct avrcp *session,
- struct avrcp_player *player)
- {
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 2];
- struct avrcp_browsing_header *pdu = (void *) buf;
- uint16_t id;
- memset(buf, 0, sizeof(buf));
- pdu->pdu_id = AVRCP_SET_BROWSED_PLAYER;
- id = htons(player->id);
- memcpy(pdu->params, &id, 2);
- pdu->param_len = htons(2);
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_set_browsed_player_rsp, session);
- }
- static gboolean avrcp_get_item_attributes_rsp(struct avctp *conn,
- uint8_t *operands,
- size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct avrcp_browsing_header *pdu = (void *) operands;
- uint8_t count;
- if (pdu == NULL) {
- avrcp_get_element_attributes(session);
- return FALSE;
- }
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 4) {
- avrcp_get_element_attributes(session);
- return FALSE;
- }
- count = pdu->params[1];
- if (ntohs(pdu->param_len) - 1 < count * 8) {
- error("Invalid parameters");
- return FALSE;
- }
- avrcp_parse_attribute_list(player, &pdu->params[2], count);
- avrcp_get_play_status(session);
- return FALSE;
- }
- static void avrcp_get_item_attributes(struct avrcp *session, uint64_t uid)
- {
- struct avrcp_player *player = session->controller->player;
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 12];
- struct avrcp_browsing_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- pdu->pdu_id = AVRCP_GET_ITEM_ATTRIBUTES;
- pdu->params[0] = 0x03;
- put_be64(uid, &pdu->params[1]);
- put_be16(player->uid_counter, &pdu->params[9]);
- pdu->param_len = htons(12);
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_get_item_attributes_rsp, session);
- }
- static void avrcp_player_parse_features(struct avrcp_player *player,
- uint8_t *features)
- {
- struct media_player *mp = player->user_data;
- player->features = g_memdup(features, 16);
- if (features[7] & 0x08) {
- media_player_set_browsable(mp, true);
- media_player_create_folder(mp, "/Filesystem",
- PLAYER_FOLDER_TYPE_MIXED, 0);
- }
- if (features[7] & 0x10)
- media_player_set_searchable(mp, true);
- if (features[8] & 0x02) {
- media_player_create_folder(mp, "/NowPlaying",
- PLAYER_FOLDER_TYPE_MIXED, 0);
- media_player_set_playlist(mp, "/NowPlaying");
- }
- }
- static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
- uint8_t val)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 3];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = 1;
- pdu->params[1] = attr;
- pdu->params[2] = val;
- pdu->params_len = htons(3);
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_player_value_rsp, session);
- }
- static gboolean avrcp_set_addressed_player_rsp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct avrcp_header *pdu = (void *) operands;
- if (!pdu || code != AVC_CTYPE_ACCEPTED)
- return FALSE;
- player->addressed = true;
- return FALSE;
- }
- static void avrcp_set_addressed_player(struct avrcp *session,
- struct avrcp_player *player)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 2];
- struct avrcp_header *pdu = (void *) buf;
- uint16_t id;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_SET_ADDRESSED_PLAYER;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- id = htons(player->id);
- memcpy(pdu->params, &id, 2);
- pdu->params_len = htons(2);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_set_addressed_player_rsp,
- session);
- }
- static void set_addressed_player(struct avrcp *session,
- struct avrcp_player *player)
- {
- if (!player || !player->id || player->addressed ||
- session->controller->version < 0x0104)
- return;
- /* Set player as addressed */
- avrcp_set_addressed_player(session, player);
- }
- static void set_browsed_player(struct avrcp *session,
- struct avrcp_player *player)
- {
- if (!player || !player->id || player->browsed)
- return;
- if (media_player_get_browsable(player->user_data))
- avrcp_set_browsed_player(session, player);
- }
- static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
- {
- struct btd_service *service;
- if (session->controller->player == player)
- goto done;
- session->controller->player = player;
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- control_set_player(service, player ?
- media_player_get_path(player->user_data) : NULL);
- done:
- set_addressed_player(session, player);
- set_browsed_player(session, player);
- }
- static bool ct_set_setting(struct media_player *mp, const char *key,
- const char *value, void *user_data)
- {
- struct avrcp_player *player = user_data;
- int attr;
- int val;
- struct avrcp *session;
- session = player->sessions->data;
- if (session == NULL)
- return false;
- if (session->controller->version < 0x0103)
- return false;
- set_ct_player(session, player);
- attr = attr_to_val(key);
- if (attr < 0)
- return false;
- val = attrval_to_val(attr, value);
- if (val < 0)
- return false;
- avrcp_set_player_value(session, attr, val);
- return true;
- }
- static int ct_press(struct avrcp_player *player, uint8_t op)
- {
- struct avrcp *session = player->sessions->data;
- if (session == NULL)
- return -ENOTCONN;
- set_ct_player(session, player);
- return avctp_send_passthrough(session->conn, op, false);
- }
- static int ct_hold(struct avrcp_player *player, uint8_t op)
- {
- struct avrcp *session = player->sessions->data;
- if (session == NULL)
- return -ENOTCONN;
- set_ct_player(session, player);
- return avctp_send_passthrough(session->conn, op, true);
- }
- static int ct_release(struct avrcp_player *player)
- {
- struct avrcp *session = player->sessions->data;
- if (session == NULL)
- return -ENOTCONN;
- set_ct_player(session, player);
- return avctp_send_release_passthrough(session->conn);
- }
- static int ct_play(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, AVC_PLAY);
- }
- static int ct_pause(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, AVC_PAUSE);
- }
- static int ct_stop(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, AVC_STOP);
- }
- static int ct_next(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, AVC_FORWARD);
- }
- static int ct_previous(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, AVC_BACKWARD);
- }
- static int ct_fast_forward(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_hold(player, AVC_FAST_FORWARD);
- }
- static int ct_rewind(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_hold(player, AVC_REWIND);
- }
- static int ct_press_key(struct media_player *mp, uint8_t avc_key,
- void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_press(player, avc_key);
- }
- static int ct_hold_key(struct media_player *mp, uint8_t avc_key,
- void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_hold(player, avc_key);
- }
- static int ct_release_key(struct media_player *mp, void *user_data)
- {
- struct avrcp_player *player = user_data;
- return ct_release(player);
- }
- static int ct_list_items(struct media_player *mp, const char *name,
- uint32_t start, uint32_t end, void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- struct pending_list_items *p;
- if (player->p != NULL)
- return -EBUSY;
- session = player->sessions->data;
- set_ct_player(session, player);
- if (g_str_has_prefix(name, "/NowPlaying"))
- player->scope = 0x03;
- else if (g_str_has_suffix(name, "/search"))
- player->scope = 0x02;
- else
- player->scope = 0x01;
- avrcp_list_items(session, start, end);
- p = g_new0(struct pending_list_items, 1);
- p->start = start;
- p->end = end;
- p->total = (uint64_t) (p->end - p->start) + 1;
- player->p = p;
- return 0;
- }
- static void avrcp_change_path(struct avrcp *session, uint8_t direction,
- uint64_t uid)
- {
- struct avrcp_player *player = session->controller->player;
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 11];
- struct avrcp_browsing_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- put_be16(player->uid_counter, &pdu->params[0]);
- pdu->params[2] = direction;
- put_be64(uid, &pdu->params[3]);
- pdu->pdu_id = AVRCP_CHANGE_PATH;
- pdu->param_len = htons(11);
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_change_path_rsp, session);
- }
- static int ct_change_folder(struct media_player *mp, const char *path,
- uint64_t uid, void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- uint8_t direction;
- session = player->sessions->data;
- set_ct_player(session, player);
- player->change_path = g_strdup(path);
- player->change_uid = uid;
- direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
- avrcp_change_path(session, direction, uid);
- return 0;
- }
- static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands,
- size_t operand_count, void *user_data)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = (void *) user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- int ret;
- if (pdu == NULL) {
- ret = -ETIMEDOUT;
- goto done;
- }
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7) {
- ret = -EINVAL;
- goto done;
- }
- player->uid_counter = get_be16(&pdu->params[1]);
- ret = get_be32(&pdu->params[3]);
- done:
- media_player_search_complete(mp, ret);
- return FALSE;
- }
- static void avrcp_search(struct avrcp *session, const char *string)
- {
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 255];
- struct avrcp_browsing_header *pdu = (void *) buf;
- uint16_t len, stringlen;
- memset(buf, 0, sizeof(buf));
- len = AVRCP_BROWSING_HEADER_LENGTH + 4;
- stringlen = strnlen(string, sizeof(buf) - len);
- len += stringlen;
- put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]);
- put_be16(stringlen, &pdu->params[2]);
- memcpy(&pdu->params[4], string, stringlen);
- pdu->pdu_id = AVRCP_SEARCH;
- pdu->param_len = htons(len - AVRCP_BROWSING_HEADER_LENGTH);
- avctp_send_browsing_req(session->conn, buf, len, avrcp_search_rsp,
- session);
- }
- static int ct_search(struct media_player *mp, const char *string,
- void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- session = player->sessions->data;
- set_ct_player(session, player);
- avrcp_search(session, string);
- return 0;
- }
- static gboolean avrcp_play_item_rsp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp_header *pdu = (void *) operands;
- struct avrcp *session = (void *) user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- int ret = 0;
- if (pdu == NULL) {
- ret = -ETIMEDOUT;
- goto done;
- }
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS) {
- switch (pdu->params[0]) {
- case AVRCP_STATUS_UID_CHANGED:
- case AVRCP_STATUS_DOES_NOT_EXIST:
- ret = -ENOENT;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- goto done;
- }
- done:
- media_player_play_item_complete(mp, ret);
- return FALSE;
- }
- static void avrcp_play_item(struct avrcp *session, uint64_t uid)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 11];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_header *pdu = (void *) buf;
- uint16_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_PLAY_ITEM;
- pdu->params_len = htons(11);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = player->scope;
- put_be64(uid, &pdu->params[1]);
- put_be16(player->uid_counter, &pdu->params[9]);
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_play_item_rsp, session);
- }
- static int ct_play_item(struct media_player *mp, const char *name,
- uint64_t uid, void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- if (player->p != NULL)
- return -EBUSY;
- session = player->sessions->data;
- set_ct_player(session, player);
- if (g_strrstr(name, "/NowPlaying"))
- player->scope = 0x03;
- else if (g_strrstr(name, "/Search"))
- player->scope = 0x02;
- else
- player->scope = 0x01;
- avrcp_play_item(session, uid);
- return 0;
- }
- static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 11];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_header *pdu = (void *) buf;
- uint16_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_ADD_TO_NOW_PLAYING;
- pdu->params_len = htons(11);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = player->scope;
- put_be64(uid, &pdu->params[1]);
- put_be16(player->uid_counter, &pdu->params[9]);
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, buf, length,
- NULL, session);
- }
- static int ct_add_to_nowplaying(struct media_player *mp, const char *name,
- uint64_t uid, void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- if (player->p != NULL)
- return -EBUSY;
- session = player->sessions->data;
- if (g_strrstr(name, "/NowPlaying"))
- player->scope = 0x03;
- else
- player->scope = 0x01;
- set_ct_player(session, player);
- avrcp_add_to_nowplaying(session, uid);
- return 0;
- }
- static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint32_t num_of_items = 0;
- if (pdu == NULL)
- return -ETIMEDOUT;
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7)
- return -EINVAL;
- if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
- goto done;
- player->uid_counter = get_be16(&pdu->params[1]);
- num_of_items = get_be32(&pdu->params[3]);
- if (!num_of_items)
- return -EINVAL;
- done:
- media_player_total_items_complete(mp, num_of_items);
- return FALSE;
- }
- static void avrcp_get_total_numberofitems(struct avrcp *session)
- {
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_browsing_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS;
- pdu->param_len = htons(7 + sizeof(uint32_t));
- pdu->params[0] = player->scope;
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_get_total_numberofitems_rsp, session);
- }
- static int ct_get_total_numberofitems(struct media_player *mp, const char *name,
- void *user_data)
- {
- struct avrcp_player *player = user_data;
- struct avrcp *session;
- session = player->sessions->data;
- set_ct_player(session, player);
- if (session->controller->version != 0x0106) {
- error("version not supported");
- return -1;
- }
- if (g_str_has_prefix(name, "/NowPlaying"))
- player->scope = 0x03;
- else if (g_str_has_suffix(name, "/search"))
- player->scope = 0x02;
- else
- player->scope = 0x01;
- avrcp_get_total_numberofitems(session);
- return 0;
- }
- static const struct media_player_callback ct_cbs = {
- .set_setting = ct_set_setting,
- .play = ct_play,
- .pause = ct_pause,
- .stop = ct_stop,
- .next = ct_next,
- .previous = ct_previous,
- .fast_forward = ct_fast_forward,
- .rewind = ct_rewind,
- .press = ct_press_key,
- .hold = ct_hold_key,
- .release = ct_release_key,
- .list_items = ct_list_items,
- .change_folder = ct_change_folder,
- .search = ct_search,
- .play_item = ct_play_item,
- .add_to_nowplaying = ct_add_to_nowplaying,
- .total_items = ct_get_total_numberofitems,
- };
- static struct avrcp_player *create_ct_player(struct avrcp *session,
- uint16_t id)
- {
- struct avrcp_player *player;
- struct media_player *mp;
- const char *path;
- player = g_new0(struct avrcp_player, 1);
- player->id = id;
- player->sessions = g_slist_prepend(player->sessions, session);
- path = device_get_path(session->dev);
- mp = media_player_controller_create(path, id);
- if (mp == NULL) {
- g_free(player);
- return NULL;
- }
- media_player_set_callbacks(mp, &ct_cbs, player);
- player->user_data = mp;
- player->destroy = (GDestroyNotify) media_player_destroy;
- if (session->controller->player == NULL)
- set_ct_player(session, player);
- session->controller->players = g_slist_prepend(
- session->controller->players,
- player);
- return player;
- }
- static struct avrcp_player *find_ct_player(struct avrcp *session, uint16_t id)
- {
- GSList *l;
- for (l = session->controller->players; l; l = l->next) {
- struct avrcp_player *player = l->data;
- if (player->id == 0) {
- player->id = id;
- return player;
- }
- if (player->id == id)
- return player;
- }
- return NULL;
- }
- static struct avrcp_player *
- avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
- uint16_t len)
- {
- struct avrcp_player *player;
- struct media_player *mp;
- uint16_t id, namelen;
- uint32_t subtype;
- const char *curval, *strval;
- char name[255];
- if (len < 28)
- return NULL;
- id = get_be16(&operands[0]);
- player = find_ct_player(session, id);
- if (player == NULL) {
- player = create_ct_player(session, id);
- if (player == NULL)
- return NULL;
- } else if (player->features != NULL)
- return player;
- mp = player->user_data;
- media_player_set_type(mp, type_to_string(operands[2]));
- subtype = get_be32(&operands[3]);
- media_player_set_subtype(mp, subtype_to_string(subtype));
- curval = media_player_get_status(mp);
- strval = status_to_string(operands[7]);
- if (g_strcmp0(curval, strval) != 0) {
- media_player_set_status(mp, strval);
- avrcp_get_play_status(session);
- }
- avrcp_player_parse_features(player, &operands[8]);
- namelen = get_be16(&operands[26]);
- if (namelen > 0 && namelen + 28 == len) {
- namelen = MIN(namelen, sizeof(name) - 1);
- memcpy(name, &operands[28], namelen);
- name[namelen] = '\0';
- media_player_set_name(mp, name);
- }
- if (player->addressed)
- set_browsed_player(session, player);
- return player;
- }
- static void player_destroy(gpointer data)
- {
- struct avrcp_player *player = data;
- if (player->destroy)
- player->destroy(player->user_data);
- if (player->changed_id > 0)
- g_source_remove(player->changed_id);
- g_slist_free(player->sessions);
- g_free(player->path);
- g_free(player->change_path);
- g_free(player->features);
- g_free(player);
- }
- static void player_remove(gpointer data)
- {
- struct avrcp_player *player = data;
- GSList *l;
- /* Don't remove reserved player */
- if (!player->id)
- return;
- for (l = player->sessions; l; l = l->next) {
- struct avrcp *session = l->data;
- struct avrcp_data *controller = session->controller;
- controller->players = g_slist_remove(controller->players,
- player);
- /* Check if current player is being removed */
- if (controller->player == player)
- set_ct_player(session, g_slist_nth_data(
- controller->players, 0));
- }
- player_destroy(player);
- }
- static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
- uint8_t *operands,
- size_t operand_count,
- void *user_data)
- {
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- uint16_t count;
- size_t i;
- GSList *removed;
- if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
- operand_count < 5)
- return FALSE;
- removed = g_slist_copy(session->controller->players);
- count = get_be16(&operands[6]);
- for (i = 8; count && i < operand_count; count--) {
- struct avrcp_player *player;
- uint8_t type;
- uint16_t len;
- type = operands[i++];
- len = get_be16(&operands[i]);
- i += 2;
- if (type != 0x01) {
- i += len;
- continue;
- }
- if (i + len > operand_count) {
- error("Invalid player item length");
- return FALSE;
- }
- player = avrcp_parse_media_player_item(session, &operands[i],
- len);
- if (player)
- removed = g_slist_remove(removed, player);
- i += len;
- }
- g_slist_free_full(removed, player_remove);
- /* There should always be an active player */
- if (!session->controller->player)
- create_ct_player(session, 0);
- return FALSE;
- }
- static void avrcp_get_media_player_list(struct avrcp *session)
- {
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10];
- struct avrcp_browsing_header *pdu = (void *) buf;
- memset(buf, 0, sizeof(buf));
- pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
- put_be32(0, &pdu->params[1]);
- put_be32(UINT32_MAX, &pdu->params[5]);
- pdu->param_len = htons(10);
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_get_media_player_list_rsp, session);
- }
- static void avrcp_volume_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- struct avrcp_player *player = target_get_player(session);
- int8_t volume;
- volume = pdu->params[1] & 0x7F;
- /* Always attempt to update the transport volume */
- media_transport_update_device_volume(session->dev, volume);
- if (player)
- player->cb->set_volume(volume, session->dev, player->user_data);
- }
- static void avrcp_status_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint8_t value;
- const char *curval, *strval;
- value = pdu->params[1];
- curval = media_player_get_status(mp);
- strval = status_to_string(value);
- if (g_strcmp0(curval, strval) == 0)
- return;
- media_player_set_status(mp, strval);
- avrcp_get_play_status(session);
- }
- static void avrcp_track_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- if (session->browsing_id) {
- struct avrcp_player *player = session->controller->player;
- player->uid = get_be64(&pdu->params[1]);
- avrcp_get_item_attributes(session, player->uid);
- } else
- avrcp_get_element_attributes(session);
- }
- static void avrcp_playback_pos_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint32_t position;
- position = get_be32(&pdu->params[1]);
- media_player_set_position(mp, position);
- }
- static void avrcp_setting_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint8_t count = pdu->params[1];
- int i;
- for (i = 2; count > 0; count--, i += 2) {
- const char *key;
- const char *value;
- key = attr_to_str(pdu->params[i]);
- if (key == NULL)
- continue;
- value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
- if (value == NULL)
- continue;
- media_player_set_setting(mp, key, value);
- }
- }
- static void avrcp_available_players_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- avrcp_get_media_player_list(session);
- }
- static void avrcp_addressed_player_changed(struct avrcp *session,
- struct avrcp_header *pdu)
- {
- struct avrcp_player *player = session->controller->player;
- uint16_t id = get_be16(&pdu->params[1]);
- if (player != NULL && player->id == id)
- return;
- player = find_ct_player(session, id);
- if (player == NULL) {
- player = create_ct_player(session, id);
- if (player == NULL)
- return;
- }
- player->addressed = true;
- player->uid_counter = get_be16(&pdu->params[3]);
- set_ct_player(session, player);
- if (player->features != NULL)
- return;
- avrcp_get_media_player_list(session);
- }
- static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu)
- {
- struct avrcp_player *player = session->controller->player;
- player->uid_counter = get_be16(&pdu->params[1]);
- }
- static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_header *pdu = (void *) operands;
- uint8_t event;
- if (!pdu)
- return FALSE;
- if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
- if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
- code == AVC_CTYPE_REJECTED) {
- int i;
- /* Lookup event by transaction */
- for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
- if (session->transaction_events[i] ==
- transaction) {
- event = i;
- goto changed;
- }
- }
- }
- return FALSE;
- }
- event = pdu->params[0];
- if (code == AVC_CTYPE_CHANGED) {
- goto changed;
- }
- switch (event) {
- case AVRCP_EVENT_VOLUME_CHANGED:
- avrcp_volume_changed(session, pdu);
- break;
- case AVRCP_EVENT_STATUS_CHANGED:
- avrcp_status_changed(session, pdu);
- break;
- case AVRCP_EVENT_TRACK_CHANGED:
- avrcp_track_changed(session, pdu);
- break;
- case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
- avrcp_playback_pos_changed(session, pdu);
- break;
- case AVRCP_EVENT_SETTINGS_CHANGED:
- avrcp_setting_changed(session, pdu);
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- avrcp_available_players_changed(session, pdu);
- break;
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- avrcp_addressed_player_changed(session, pdu);
- break;
- case AVRCP_EVENT_UIDS_CHANGED:
- avrcp_uids_changed(session, pdu);
- break;
- }
- session->registered_events |= (1 << event);
- session->transaction_events[event] = transaction;
- return TRUE;
- changed:
- session->registered_events ^= (1 << event);
- session->transaction_events[event] = 0;
- avrcp_register_notification(session, event);
- return FALSE;
- }
- static void avrcp_register_notification(struct avrcp *session, uint8_t event)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = event;
- /*
- * Set maximum interval possible for position changed as we only
- * use it to resync.
- */
- if (event == AVRCP_EVENT_PLAYBACK_POS_CHANGED)
- bt_put_be32(UINT32_MAX / 1000, &pdu->params[1]);
- pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_handle_event, session);
- }
- static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_header *pdu = (void *) operands;
- uint16_t events = 0;
- uint8_t count;
- if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED ||
- pdu == NULL || pdu->params[0] != CAP_EVENTS_SUPPORTED)
- return FALSE;
- /* Connect browsing if pending */
- if (session->browsing_timer > 0) {
- timeout_remove(session->browsing_timer);
- session->browsing_timer = 0;
- avctp_connect_browsing(session->conn);
- }
- count = pdu->params[1];
- for (; count > 0; count--) {
- uint8_t event = pdu->params[1 + count];
- events |= (1 << event);
- switch (event) {
- case AVRCP_EVENT_STATUS_CHANGED:
- case AVRCP_EVENT_TRACK_CHANGED:
- case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
- case AVRCP_EVENT_SETTINGS_CHANGED:
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- case AVRCP_EVENT_UIDS_CHANGED:
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- /* These events above requires a player */
- if (!session->controller ||
- !session->controller->player)
- break;
- /* fall through */
- case AVRCP_EVENT_VOLUME_CHANGED:
- avrcp_register_notification(session, event);
- break;
- }
- }
- if (!session->controller || !session->controller->player)
- return FALSE;
- /* Skip if player status/metadata if only volume changes is supported */
- if (events == (1 << AVRCP_EVENT_VOLUME_CHANGED))
- return FALSE;
- if ((session->controller->features & AVRCP_FEATURE_PLAYER_SETTINGS) &&
- !(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
- avrcp_list_player_attributes(session);
- if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
- avrcp_get_play_status(session);
- if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
- avrcp_get_element_attributes(session);
- return FALSE;
- }
- static void avrcp_get_capabilities(struct avrcp *session)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_GET_CAPABILITIES_PARAM_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t length;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_CAPABILITIES;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = CAP_EVENTS_SUPPORTED;
- pdu->params_len = htons(AVRCP_GET_CAPABILITIES_PARAM_LENGTH);
- length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, length,
- avrcp_get_capabilities_resp,
- session);
- }
- static struct avrcp *find_session(GSList *list, struct btd_device *dev)
- {
- for (; list; list = list->next) {
- struct avrcp *session = list->data;
- if (session->dev == dev)
- return session;
- }
- return NULL;
- }
- static void destroy_browsing(void *data)
- {
- struct avrcp *session = data;
- session->browsing_id = 0;
- }
- static void session_init_browsing(struct avrcp *session)
- {
- if (session->browsing_timer > 0) {
- timeout_remove(session->browsing_timer);
- session->browsing_timer = 0;
- }
- session->browsing_id = avctp_register_browsing_pdu_handler(
- session->conn,
- handle_browsing_pdu,
- session,
- destroy_browsing);
- }
- static struct avrcp_data *data_init(struct avrcp *session, const char *uuid)
- {
- struct avrcp_data *data;
- const sdp_record_t *rec;
- sdp_list_t *list;
- sdp_profile_desc_t *desc;
- data = g_new0(struct avrcp_data, 1);
- rec = btd_device_get_record(session->dev, uuid);
- if (rec == NULL)
- return data;
- if (sdp_get_profile_descs(rec, &list) == 0) {
- desc = list->data;
- data->version = desc->version;
- }
- sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &data->features);
- sdp_list_free(list, free);
- return data;
- }
- static bool connect_browsing(gpointer user_data)
- {
- struct avrcp *session = user_data;
- session->browsing_timer = 0;
- avctp_connect_browsing(session->conn);
- return FALSE;
- }
- static void avrcp_connect_browsing(struct avrcp *session)
- {
- /* Immediately connect browsing channel if initiator otherwise delay
- * it to avoid possible collisions
- */
- if (avctp_is_initiator(session->conn)) {
- avctp_connect_browsing(session->conn);
- return;
- }
- if (session->browsing_timer > 0)
- return;
- session->browsing_timer = timeout_add_seconds(AVRCP_BROWSING_TIMEOUT,
- connect_browsing,
- session, NULL);
- }
- static void target_init(struct avrcp *session)
- {
- struct avrcp_server *server = session->server;
- struct avrcp_data *target;
- struct avrcp_player *player;
- struct btd_service *service;
- if (session->target != NULL)
- return;
- target = data_init(session, AVRCP_REMOTE_UUID);
- session->target = target;
- DBG("%p version 0x%04x", target, target->version);
- service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
- btd_service_connecting_complete(service, 0);
- player = g_slist_nth_data(server->players, 0);
- if (player != NULL) {
- int8_t init_volume;
- target->player = player;
- player->sessions = g_slist_prepend(player->sessions, session);
- init_volume = media_player_get_device_volume(session->dev);
- media_transport_update_device_volume(session->dev, init_volume);
- }
- session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) |
- (1 << AVRCP_EVENT_TRACK_CHANGED) |
- (1 << AVRCP_EVENT_TRACK_REACHED_START) |
- (1 << AVRCP_EVENT_TRACK_REACHED_END) |
- (1 << AVRCP_EVENT_SETTINGS_CHANGED);
- if (target->version < 0x0104)
- return;
- session->supported_events |=
- (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
- (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
- (1 << AVRCP_EVENT_VOLUME_CHANGED);
- /* Only check capabilities if controller is not supported */
- if (session->controller == NULL)
- avrcp_get_capabilities(session);
- if (!(target->features & AVRCP_FEATURE_BROWSING))
- return;
- avrcp_connect_browsing(session);
- }
- static void controller_init(struct avrcp *session)
- {
- struct avrcp_player *player;
- struct btd_service *service;
- struct avrcp_data *controller;
- if (session->controller != NULL)
- return;
- controller = data_init(session, AVRCP_TARGET_UUID);
- session->controller = controller;
- DBG("%p version 0x%04x", controller, controller->version);
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- btd_service_connecting_complete(service, 0);
- /* Only create player if category 1 is supported */
- if (controller->features & AVRCP_FEATURE_CATEGORY_1) {
- player = create_ct_player(session, 0);
- if (player == NULL)
- return;
- }
- if (controller->version < 0x0103)
- return;
- avrcp_get_capabilities(session);
- if (controller->version < 0x0104)
- return;
- if (!(controller->features & AVRCP_FEATURE_BROWSING))
- return;
- avrcp_connect_browsing(session);
- }
- static void session_init_control(struct avrcp *session)
- {
- session->passthrough_id = avctp_register_passthrough_handler(
- session->conn,
- handle_passthrough,
- session);
- session->passthrough_handlers = passthrough_handlers;
- session->control_id = avctp_register_pdu_handler(session->conn,
- AVC_OP_VENDORDEP,
- handle_vendordep_pdu,
- session);
- session->control_handlers = control_handlers;
- if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
- controller_init(session);
- if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
- target_init(session);
- }
- static void controller_destroy(struct avrcp *session)
- {
- struct avrcp_data *controller = session->controller;
- DBG("%p", controller);
- g_slist_free_full(controller->players, player_destroy);
- g_free(controller);
- }
- static void target_destroy(struct avrcp *session)
- {
- struct avrcp_data *target = session->target;
- struct avrcp_player *player = target->player;
- DBG("%p", target);
- if (player != NULL)
- player->sessions = g_slist_remove(player->sessions, session);
- g_free(target);
- }
- static void session_destroy(struct avrcp *session, int err)
- {
- struct avrcp_server *server = session->server;
- struct btd_service *service;
- server->sessions = g_slist_remove(server->sessions, session);
- session_abort_pending_pdu(session);
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- if (service != NULL) {
- if (session->control_id == 0)
- btd_service_connecting_complete(service, err);
- else
- btd_service_disconnecting_complete(service, 0);
- }
- service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
- if (service != NULL) {
- if (session->control_id == 0)
- btd_service_connecting_complete(service, err);
- else
- btd_service_disconnecting_complete(service, 0);
- }
- if (session->browsing_timer > 0)
- timeout_remove(session->browsing_timer);
- if (session->controller != NULL)
- controller_destroy(session);
- if (session->target != NULL)
- target_destroy(session);
- if (session->passthrough_id > 0)
- avctp_unregister_passthrough_handler(session->passthrough_id);
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(session->browsing_id);
- g_free(session);
- }
- static struct avrcp *session_create(struct avrcp_server *server,
- struct btd_device *device)
- {
- struct avrcp *session;
- session = g_new0(struct avrcp, 1);
- session->server = server;
- session->conn = avctp_connect(device);
- session->dev = device;
- server->sessions = g_slist_append(server->sessions, session);
- return session;
- }
- static void state_changed(struct btd_device *device, avctp_state_t old_state,
- avctp_state_t new_state, int err,
- void *user_data)
- {
- struct avrcp_server *server;
- struct avrcp *session;
- server = find_server(servers, device_get_adapter(device));
- if (!server)
- return;
- session = find_session(server->sessions, device);
- switch (new_state) {
- case AVCTP_STATE_DISCONNECTED:
- if (session == NULL)
- break;
- session_destroy(session, err);
- break;
- case AVCTP_STATE_CONNECTING:
- if (session != NULL)
- break;
- session_create(server, device);
- break;
- case AVCTP_STATE_CONNECTED:
- if (session == NULL || session->control_id > 0)
- break;
- session_init_control(session);
- break;
- case AVCTP_STATE_BROWSING_CONNECTED:
- if (session == NULL || session->browsing_id > 0)
- break;
- session_init_browsing(session);
- break;
- case AVCTP_STATE_BROWSING_CONNECTING:
- default:
- return;
- }
- }
- static struct avrcp_server *avrcp_server_register(struct btd_adapter *adapter)
- {
- struct avrcp_server *server;
- bool browsing;
- if (avctp_register(adapter, TRUE, &browsing) < 0)
- return NULL;
- server = g_new0(struct avrcp_server, 1);
- server->adapter = btd_adapter_ref(adapter);
- server->browsing = browsing;
- servers = g_slist_append(servers, server);
- if (!avctp_id)
- avctp_id = avctp_add_state_cb(NULL, state_changed, NULL);
- return server;
- }
- static void avrcp_server_unregister(struct avrcp_server *server)
- {
- g_slist_free_full(server->sessions, g_free);
- g_slist_free_full(server->players, player_destroy);
- servers = g_slist_remove(servers, server);
- avctp_unregister(server->adapter);
- btd_adapter_unref(server->adapter);
- g_free(server);
- if (servers)
- return;
- if (avctp_id) {
- avctp_remove_state_cb(avctp_id);
- avctp_id = 0;
- }
- }
- struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
- struct avrcp_player_cb *cb,
- void *user_data,
- GDestroyNotify destroy)
- {
- struct avrcp_server *server;
- struct avrcp_player *player;
- GSList *l;
- static uint16_t id = 0;
- server = find_server(servers, adapter);
- if (!server)
- return NULL;
- player = g_new0(struct avrcp_player, 1);
- player->id = ++id;
- player->server = server;
- player->cb = cb;
- player->user_data = user_data;
- player->destroy = destroy;
- server->players = g_slist_append(server->players, player);
- /* Assign player to session without current player */
- for (l = server->sessions; l; l = l->next) {
- struct avrcp *session = l->data;
- struct avrcp_data *target = session->target;
- if (target == NULL)
- continue;
- if (target->player == NULL) {
- target->player = player;
- player->sessions = g_slist_append(player->sessions,
- session);
- }
- }
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
- return player;
- }
- void avrcp_unregister_player(struct avrcp_player *player)
- {
- struct avrcp_server *server = player->server;
- GSList *l;
- server->players = g_slist_remove(server->players, player);
- /* Remove player from sessions using it */
- for (l = player->sessions; l; l = l->next) {
- struct avrcp *session = l->data;
- struct avrcp_data *target = session->target;
- if (target == NULL)
- continue;
- if (target->player != player)
- continue;
- target->player = g_slist_nth_data(server->players, 0);
- if (target->player) {
- target->player->sessions = g_slist_append(
- target->player->sessions,
- session);
- notify_addressed_player_changed(player);
- }
- }
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
- player_destroy(player);
- }
- static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
- uint8_t *operands, size_t operand_count,
- void *user_data)
- {
- struct avrcp *session = user_data;
- struct avrcp_player *player = target_get_player(session);
- struct avrcp_header *pdu = (void *) operands;
- int8_t volume;
- if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED ||
- pdu == NULL)
- return FALSE;
- volume = pdu->params[0] & 0x7F;
- /* Always attempt to update the transport volume */
- media_transport_update_device_volume(session->dev, volume);
- if (player != NULL)
- player->cb->set_volume(volume, session->dev, player->user_data);
- return FALSE;
- }
- static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
- {
- uint8_t buf[AVRCP_HEADER_LENGTH + 2];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
- uint16_t size;
- int err;
- /* Verify that the event is registered */
- if (!(session->registered_events & (1 << id)))
- return -ENOENT;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
- DBG("id=%u", id);
- switch (id) {
- case AVRCP_EVENT_VOLUME_CHANGED:
- size = 2;
- memcpy(&pdu->params[1], data, sizeof(uint8_t));
- break;
- default:
- error("Unknown event %u", id);
- return -EINVAL;
- }
- pdu->params_len = htons(size);
- err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
- buf, size + AVRCP_HEADER_LENGTH);
- if (err < 0)
- return err;
- /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
- return err;
- }
- static bool avrcp_event_registered(struct avrcp *session, uint8_t event)
- {
- return session->registered_events & (1 << event);
- }
- int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
- {
- struct avrcp_server *server;
- struct avrcp *session;
- uint8_t buf[AVRCP_HEADER_LENGTH + 1];
- struct avrcp_header *pdu = (void *) buf;
- if (volume < 0)
- return -EINVAL;
- server = find_server(servers, device_get_adapter(dev));
- if (server == NULL)
- return -EINVAL;
- session = find_session(server->sessions, dev);
- if (session == NULL)
- return -ENOTCONN;
- if (notify) {
- if (!session->target)
- return -ENOTSUP;
- return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
- &volume);
- }
- if (!session->controller && !avrcp_event_registered(session,
- AVRCP_EVENT_VOLUME_CHANGED))
- return -ENOTSUP;
- memset(buf, 0, sizeof(buf));
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
- pdu->params[0] = volume;
- pdu->params_len = htons(1);
- return avctp_send_vendordep_req(session->conn,
- AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
- buf, sizeof(buf),
- avrcp_handle_set_volume, session);
- }
- struct avrcp_player *avrcp_get_target_player_by_device(struct btd_device *dev)
- {
- struct avrcp_server *server;
- struct avrcp *session;
- struct avrcp_data *target;
- server = find_server(servers, device_get_adapter(dev));
- if (server == NULL)
- return NULL;
- session = find_session(server->sessions, dev);
- if (session == NULL)
- return NULL;
- target = session->target;
- if (target == NULL)
- return NULL;
- return target->player;
- }
- static int avrcp_connect(struct btd_service *service)
- {
- struct btd_device *dev = btd_service_get_device(service);
- const char *path = device_get_path(dev);
- DBG("path %s", path);
- return control_connect(service);
- }
- static int avrcp_disconnect(struct btd_service *service)
- {
- struct btd_device *dev = btd_service_get_device(service);
- const char *path = device_get_path(dev);
- DBG("path %s", path);
- return control_disconnect(service);
- }
- static int avrcp_target_probe(struct btd_service *service)
- {
- struct btd_device *dev = btd_service_get_device(service);
- DBG("path %s", device_get_path(dev));
- return control_init_target(service);
- }
- static void avrcp_target_remove(struct btd_service *service)
- {
- control_unregister(service);
- }
- static void avrcp_target_server_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
- {
- struct avrcp_server *server;
- DBG("path %s", adapter_get_path(adapter));
- server = find_server(servers, adapter);
- if (!server)
- return;
- if (server->tg_record_id != 0) {
- adapter_service_remove(adapter, server->tg_record_id);
- server->tg_record_id = 0;
- }
- if (server->ct_record_id == 0)
- avrcp_server_unregister(server);
- }
- static int avrcp_target_server_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
- {
- sdp_record_t *record;
- struct avrcp_server *server;
- DBG("path %s", adapter_get_path(adapter));
- server = find_server(servers, adapter);
- if (server != NULL)
- goto done;
- server = avrcp_server_register(adapter);
- if (server == NULL)
- return -EPROTONOSUPPORT;
- done:
- record = avrcp_tg_record(server->browsing);
- if (!record) {
- error("Unable to allocate new service record");
- avrcp_target_server_remove(p, adapter);
- return -1;
- }
- if (adapter_service_add(adapter, record) < 0) {
- error("Unable to register AVRCP target service record");
- avrcp_target_server_remove(p, adapter);
- sdp_record_free(record);
- return -1;
- }
- server->tg_record_id = record->handle;
- return 0;
- }
- static struct btd_profile avrcp_target_profile = {
- .name = "audio-avrcp-target",
- .remote_uuid = AVRCP_TARGET_UUID,
- .device_probe = avrcp_target_probe,
- .device_remove = avrcp_target_remove,
- .connect = avrcp_connect,
- .disconnect = avrcp_disconnect,
- .adapter_probe = avrcp_target_server_probe,
- .adapter_remove = avrcp_target_server_remove,
- };
- static int avrcp_controller_probe(struct btd_service *service)
- {
- struct btd_device *dev = btd_service_get_device(service);
- DBG("path %s", device_get_path(dev));
- return control_init_remote(service);
- }
- static void avrcp_controller_remove(struct btd_service *service)
- {
- control_unregister(service);
- }
- static void avrcp_controller_server_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
- {
- struct avrcp_server *server;
- DBG("path %s", adapter_get_path(adapter));
- server = find_server(servers, adapter);
- if (!server)
- return;
- if (server->ct_record_id != 0) {
- adapter_service_remove(adapter, server->ct_record_id);
- server->ct_record_id = 0;
- }
- if (server->tg_record_id == 0)
- avrcp_server_unregister(server);
- }
- static int avrcp_controller_server_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
- {
- sdp_record_t *record;
- struct avrcp_server *server;
- DBG("path %s", adapter_get_path(adapter));
- server = find_server(servers, adapter);
- if (server != NULL)
- goto done;
- server = avrcp_server_register(adapter);
- if (server == NULL)
- return -EPROTONOSUPPORT;
- done:
- record = avrcp_ct_record(server->browsing);
- if (!record) {
- error("Unable to allocate new service record");
- avrcp_controller_server_remove(p, adapter);
- return -1;
- }
- if (adapter_service_add(adapter, record) < 0) {
- error("Unable to register AVRCP service record");
- avrcp_controller_server_remove(p, adapter);
- sdp_record_free(record);
- return -1;
- }
- server->ct_record_id = record->handle;
- return 0;
- }
- static struct btd_profile avrcp_controller_profile = {
- .name = "avrcp-controller",
- .remote_uuid = AVRCP_REMOTE_UUID,
- .device_probe = avrcp_controller_probe,
- .device_remove = avrcp_controller_remove,
- .connect = avrcp_connect,
- .disconnect = avrcp_disconnect,
- .adapter_probe = avrcp_controller_server_probe,
- .adapter_remove = avrcp_controller_server_remove,
- };
- static int avrcp_init(void)
- {
- btd_profile_register(&avrcp_controller_profile);
- btd_profile_register(&avrcp_target_profile);
- populate_default_features();
- return 0;
- }
- static void avrcp_exit(void)
- {
- btd_profile_unregister(&avrcp_controller_profile);
- btd_profile_unregister(&avrcp_target_profile);
- }
- BLUETOOTH_PLUGIN_DEFINE(avrcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- avrcp_init, avrcp_exit)
|