1 Star 0 Fork 41

梁鹏辉/third_party_libdrm

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
xf86drm.c 127.98 KB
一键复制 编辑 原始数据 按行查看 历史
朱思远 提交于 2023-04-12 16:34 . upgrade version 2.4.111
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108
/**
* \file xf86drm.c
* User-level interface to DRM device
*
* \author Rickard E. (Rik) Faith <[email protected]>
* \author Kevin E. Martin <[email protected]>
*/
/*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <dirent.h>
#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#define stat_t struct stat
#include <sys/ioctl.h>
#include <sys/time.h>
#include <stdarg.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#include <math.h>
#include <inttypes.h>
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/pciio.h>
#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/* Not all systems have MAP_FAILED defined */
#ifndef MAP_FAILED
#define MAP_FAILED ((void *)-1)
#endif
#include "xf86drm.h"
#include "libdrm_macros.h"
#include "drm_fourcc.h"
#include "util_math.h"
#ifdef __DragonFly__
#define DRM_MAJOR 145
#endif
#ifdef __NetBSD__
#define DRM_MAJOR 34
#endif
#ifdef __OpenBSD__
#ifdef __i386__
#define DRM_MAJOR 88
#else
#define DRM_MAJOR 87
#endif
#endif /* __OpenBSD__ */
#ifndef DRM_MAJOR
#define DRM_MAJOR 226 /* Linux */
#endif
#if defined(__OpenBSD__) || defined(__DragonFly__)
struct drm_pciinfo {
uint16_t domain;
uint8_t bus;
uint8_t dev;
uint8_t func;
uint16_t vendor_id;
uint16_t device_id;
uint16_t subvendor_id;
uint16_t subdevice_id;
uint8_t revision_id;
};
#define DRM_IOCTL_GET_PCIINFO DRM_IOR(0x15, struct drm_pciinfo)
#endif
#define DRM_MSG_VERBOSITY 3
#define memclear(s) memset(&s, 0, sizeof(s))
static drmServerInfoPtr drm_server_info;
static bool drmNodeIsDRM(int maj, int min);
static char *drmGetMinorNameForFD(int fd, int type);
#define DRM_MODIFIER(v, f, f_name) \
.modifier = DRM_FORMAT_MOD_##v ## _ ##f, \
.modifier_name = #f_name
#define DRM_MODIFIER_INVALID(v, f_name) \
.modifier = DRM_FORMAT_MOD_INVALID, .modifier_name = #f_name
#define DRM_MODIFIER_LINEAR(v, f_name) \
.modifier = DRM_FORMAT_MOD_LINEAR, .modifier_name = #f_name
/* Intel is abit special as the format doesn't follow other vendors naming
* scheme */
#define DRM_MODIFIER_INTEL(f, f_name) \
.modifier = I915_FORMAT_MOD_##f, .modifier_name = #f_name
struct drmFormatModifierInfo {
uint64_t modifier;
const char *modifier_name;
};
struct drmFormatModifierVendorInfo {
uint8_t vendor;
const char *vendor_name;
};
#include "generated_static_table_fourcc.h"
struct drmVendorInfo {
uint8_t vendor;
char *(*vendor_cb)(uint64_t modifier);
};
struct drmFormatVendorModifierInfo {
uint64_t modifier;
const char *modifier_name;
};
static char *
drmGetFormatModifierNameFromArm(uint64_t modifier);
static char *
drmGetFormatModifierNameFromNvidia(uint64_t modifier);
static char *
drmGetFormatModifierNameFromAmd(uint64_t modifier);
static char *
drmGetFormatModifierNameFromAmlogic(uint64_t modifier);
static const struct drmVendorInfo modifier_format_vendor_table[] = {
{ DRM_FORMAT_MOD_VENDOR_ARM, drmGetFormatModifierNameFromArm },
{ DRM_FORMAT_MOD_VENDOR_NVIDIA, drmGetFormatModifierNameFromNvidia },
{ DRM_FORMAT_MOD_VENDOR_AMD, drmGetFormatModifierNameFromAmd },
{ DRM_FORMAT_MOD_VENDOR_AMLOGIC, drmGetFormatModifierNameFromAmlogic },
};
#ifndef AFBC_FORMAT_MOD_MODE_VALUE_MASK
#define AFBC_FORMAT_MOD_MODE_VALUE_MASK 0x000fffffffffffffULL
#endif
static const struct drmFormatVendorModifierInfo arm_mode_value_table[] = {
{ AFBC_FORMAT_MOD_YTR, "YTR" },
{ AFBC_FORMAT_MOD_SPLIT, "SPLIT" },
{ AFBC_FORMAT_MOD_SPARSE, "SPARSE" },
{ AFBC_FORMAT_MOD_CBR, "CBR" },
{ AFBC_FORMAT_MOD_TILED, "TILED" },
{ AFBC_FORMAT_MOD_SC, "SC" },
{ AFBC_FORMAT_MOD_DB, "DB" },
{ AFBC_FORMAT_MOD_BCH, "BCH" },
{ AFBC_FORMAT_MOD_USM, "USM" },
};
static bool is_x_t_amd_gfx9_tile(uint64_t tile)
{
switch (tile) {
case AMD_FMT_MOD_TILE_GFX9_64K_S_X:
case AMD_FMT_MOD_TILE_GFX9_64K_D_X:
case AMD_FMT_MOD_TILE_GFX9_64K_R_X:
return true;
}
return false;
}
static bool
drmGetAfbcFormatModifierNameFromArm(uint64_t modifier, FILE *fp)
{
uint64_t mode_value = modifier & AFBC_FORMAT_MOD_MODE_VALUE_MASK;
uint64_t block_size = mode_value & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK;
const char *block = NULL;
const char *mode = NULL;
bool did_print_mode = false;
/* add block, can only have a (single) block */
switch (block_size) {
case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
block = "16x16";
break;
case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
block = "32x8";
break;
case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
block = "64x4";
break;
case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4:
block = "32x8_64x4";
break;
}
if (!block) {
return false;
}
fprintf(fp, "BLOCK_SIZE=%s,", block);
/* add mode */
for (unsigned int i = 0; i < ARRAY_SIZE(arm_mode_value_table); i++) {
if (arm_mode_value_table[i].modifier & mode_value) {
mode = arm_mode_value_table[i].modifier_name;
if (!did_print_mode) {
fprintf(fp, "MODE=%s", mode);
did_print_mode = true;
} else {
fprintf(fp, "|%s", mode);
}
}
}
return true;
}
static bool
drmGetAfrcFormatModifierNameFromArm(uint64_t modifier, FILE *fp)
{
bool scan_layout;
for (unsigned int i = 0; i < 2; ++i) {
uint64_t coding_unit_block =
(modifier >> (i * 4)) & AFRC_FORMAT_MOD_CU_SIZE_MASK;
const char *coding_unit_size = NULL;
switch (coding_unit_block) {
case AFRC_FORMAT_MOD_CU_SIZE_16:
coding_unit_size = "CU_16";
break;
case AFRC_FORMAT_MOD_CU_SIZE_24:
coding_unit_size = "CU_24";
break;
case AFRC_FORMAT_MOD_CU_SIZE_32:
coding_unit_size = "CU_32";
break;
}
if (!coding_unit_size) {
if (i == 0) {
return false;
}
break;
}
if (i == 0) {
fprintf(fp, "P0=%s,", coding_unit_size);
} else {
fprintf(fp, "P12=%s,", coding_unit_size);
}
}
scan_layout =
(modifier & AFRC_FORMAT_MOD_LAYOUT_SCAN) == AFRC_FORMAT_MOD_LAYOUT_SCAN;
if (scan_layout) {
fprintf(fp, "SCAN");
} else {
fprintf(fp, "ROT");
}
return true;
}
static char *
drmGetFormatModifierNameFromArm(uint64_t modifier)
{
uint64_t type = (modifier >> 52) & 0xf;
FILE *fp;
size_t size = 0;
char *modifier_name = NULL;
bool result = false;
fp = open_memstream(&modifier_name, &size);
if (!fp)
return NULL;
switch (type) {
case DRM_FORMAT_MOD_ARM_TYPE_AFBC:
result = drmGetAfbcFormatModifierNameFromArm(modifier, fp);
break;
case DRM_FORMAT_MOD_ARM_TYPE_AFRC:
result = drmGetAfrcFormatModifierNameFromArm(modifier, fp);
break;
/* misc type is already handled by the static table */
case DRM_FORMAT_MOD_ARM_TYPE_MISC:
default:
result = false;
break;
}
fclose(fp);
if (!result) {
free(modifier_name);
return NULL;
}
return modifier_name;
}
static char *
drmGetFormatModifierNameFromNvidia(uint64_t modifier)
{
uint64_t height, kind, gen, sector, compression;
height = modifier & 0xf;
kind = (modifier >> 12) & 0xff;
gen = (modifier >> 20) & 0x3;
sector = (modifier >> 22) & 0x1;
compression = (modifier >> 23) & 0x7;
/* just in case there could other simpler modifiers, not yet added, avoid
* testing against TEGRA_TILE */
if ((modifier & 0x10) == 0x10) {
char *mod_nvidia;
asprintf(&mod_nvidia, "BLOCK_LINEAR_2D,HEIGHT=%"PRIu64",KIND=%"PRIu64","
"GEN=%"PRIu64",SECTOR=%"PRIu64",COMPRESSION=%"PRIu64"", height,
kind, gen, sector, compression);
return mod_nvidia;
}
return NULL;
}
static void
drmGetFormatModifierNameFromAmdDcc(uint64_t modifier, FILE *fp)
{
uint64_t dcc_max_compressed_block =
AMD_FMT_MOD_GET(DCC_MAX_COMPRESSED_BLOCK, modifier);
uint64_t dcc_retile = AMD_FMT_MOD_GET(DCC_RETILE, modifier);
const char *dcc_max_compressed_block_str = NULL;
fprintf(fp, ",DCC");
if (dcc_retile)
fprintf(fp, ",DCC_RETILE");
if (!dcc_retile && AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, modifier))
fprintf(fp, ",DCC_PIPE_ALIGN");
if (AMD_FMT_MOD_GET(DCC_INDEPENDENT_64B, modifier))
fprintf(fp, ",DCC_INDEPENDENT_64B");
if (AMD_FMT_MOD_GET(DCC_INDEPENDENT_128B, modifier))
fprintf(fp, ",DCC_INDEPENDENT_128B");
switch (dcc_max_compressed_block) {
case AMD_FMT_MOD_DCC_BLOCK_64B:
dcc_max_compressed_block_str = "64B";
break;
case AMD_FMT_MOD_DCC_BLOCK_128B:
dcc_max_compressed_block_str = "128B";
break;
case AMD_FMT_MOD_DCC_BLOCK_256B:
dcc_max_compressed_block_str = "256B";
break;
}
if (dcc_max_compressed_block_str)
fprintf(fp, ",DCC_MAX_COMPRESSED_BLOCK=%s",
dcc_max_compressed_block_str);
if (AMD_FMT_MOD_GET(DCC_CONSTANT_ENCODE, modifier))
fprintf(fp, ",DCC_CONSTANT_ENCODE");
}
static void
drmGetFormatModifierNameFromAmdTile(uint64_t modifier, FILE *fp)
{
uint64_t pipe_xor_bits, bank_xor_bits, packers, rb;
uint64_t pipe, pipe_align, dcc, dcc_retile, tile_version;
pipe_align = AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, modifier);
pipe_xor_bits = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier);
dcc = AMD_FMT_MOD_GET(DCC, modifier);
dcc_retile = AMD_FMT_MOD_GET(DCC_RETILE, modifier);
tile_version = AMD_FMT_MOD_GET(TILE_VERSION, modifier);
fprintf(fp, ",PIPE_XOR_BITS=%"PRIu64, pipe_xor_bits);
if (tile_version == AMD_FMT_MOD_TILE_VER_GFX9) {
bank_xor_bits = AMD_FMT_MOD_GET(BANK_XOR_BITS, modifier);
fprintf(fp, ",BANK_XOR_BITS=%"PRIu64, bank_xor_bits);
}
if (tile_version == AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) {
packers = AMD_FMT_MOD_GET(PACKERS, modifier);
fprintf(fp, ",PACKERS=%"PRIu64, packers);
}
if (dcc && tile_version == AMD_FMT_MOD_TILE_VER_GFX9) {
rb = AMD_FMT_MOD_GET(RB, modifier);
fprintf(fp, ",RB=%"PRIu64, rb);
}
if (dcc && tile_version == AMD_FMT_MOD_TILE_VER_GFX9 &&
(dcc_retile || pipe_align)) {
pipe = AMD_FMT_MOD_GET(PIPE, modifier);
fprintf(fp, ",PIPE_%"PRIu64, pipe);
}
}
static char *
drmGetFormatModifierNameFromAmd(uint64_t modifier)
{
uint64_t tile, tile_version, dcc;
FILE *fp;
char *mod_amd = NULL;
size_t size = 0;
const char *str_tile = NULL;
const char *str_tile_version = NULL;
tile = AMD_FMT_MOD_GET(TILE, modifier);
tile_version = AMD_FMT_MOD_GET(TILE_VERSION, modifier);
dcc = AMD_FMT_MOD_GET(DCC, modifier);
fp = open_memstream(&mod_amd, &size);
if (!fp)
return NULL;
/* add tile */
switch (tile_version) {
case AMD_FMT_MOD_TILE_VER_GFX9:
str_tile_version = "GFX9";
break;
case AMD_FMT_MOD_TILE_VER_GFX10:
str_tile_version = "GFX10";
break;
case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS:
str_tile_version = "GFX10_RBPLUS";
break;
}
if (str_tile_version) {
fprintf(fp, "%s", str_tile_version);
} else {
fclose(fp);
free(mod_amd);
return NULL;
}
/* add tile str */
switch (tile) {
case AMD_FMT_MOD_TILE_GFX9_64K_S:
str_tile = "GFX9_64K_S";
break;
case AMD_FMT_MOD_TILE_GFX9_64K_D:
str_tile = "GFX9_64K_D";
break;
case AMD_FMT_MOD_TILE_GFX9_64K_S_X:
str_tile = "GFX9_64K_S_X";
break;
case AMD_FMT_MOD_TILE_GFX9_64K_D_X:
str_tile = "GFX9_64K_D_X";
break;
case AMD_FMT_MOD_TILE_GFX9_64K_R_X:
str_tile = "GFX9_64K_R_X";
break;
}
if (str_tile)
fprintf(fp, ",%s", str_tile);
if (dcc)
drmGetFormatModifierNameFromAmdDcc(modifier, fp);
if (tile_version >= AMD_FMT_MOD_TILE_VER_GFX9 && is_x_t_amd_gfx9_tile(tile))
drmGetFormatModifierNameFromAmdTile(modifier, fp);
fclose(fp);
return mod_amd;
}
static char *
drmGetFormatModifierNameFromAmlogic(uint64_t modifier)
{
uint64_t layout = modifier & 0xff;
uint64_t options = (modifier >> 8) & 0xff;
char *mod_amlogic = NULL;
const char *layout_str;
const char *opts_str;
switch (layout) {
case AMLOGIC_FBC_LAYOUT_BASIC:
layout_str = "BASIC";
break;
case AMLOGIC_FBC_LAYOUT_SCATTER:
layout_str = "SCATTER";
break;
default:
layout_str = "INVALID_LAYOUT";
break;
}
if (options & AMLOGIC_FBC_OPTION_MEM_SAVING)
opts_str = "MEM_SAVING";
else
opts_str = "0";
asprintf(&mod_amlogic, "FBC,LAYOUT=%s,OPTIONS=%s", layout_str, opts_str);
return mod_amlogic;
}
static unsigned log2_int(unsigned x)
{
unsigned l;
if (x < 2) {
return 0;
}
for (l = 2; ; l++) {
if ((unsigned)(1 << l) > x) {
return l - 1;
}
}
return 0;
}
drm_public void drmSetServerInfo(drmServerInfoPtr info)
{
drm_server_info = info;
}
/**
* Output a message to stderr.
*
* \param format printf() like format string.
*
* \internal
* This function is a wrapper around vfprintf().
*/
static int DRM_PRINTFLIKE(1, 0)
drmDebugPrint(const char *format, va_list ap)
{
return vfprintf(stderr, format, ap);
}
drm_public void
drmMsg(const char *format, ...)
{
va_list ap;
const char *env;
if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) ||
(drm_server_info && drm_server_info->debug_print))
{
va_start(ap, format);
if (drm_server_info) {
drm_server_info->debug_print(format,ap);
} else {
drmDebugPrint(format, ap);
}
va_end(ap);
}
}
static void *drmHashTable = NULL; /* Context switch callbacks */
drm_public void *drmGetHashTable(void)
{
return drmHashTable;
}
drm_public void *drmMalloc(int size)
{
return calloc(1, size);
}
drm_public void drmFree(void *pt)
{
free(pt);
}
/**
* Call ioctl, restarting if it is interrupted
*/
drm_public int
drmIoctl(int fd, unsigned long request, void *arg)
{
int ret;
do {
ret = ioctl(fd, request, arg);
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
return ret;
}
static unsigned long drmGetKeyFromFd(int fd)
{
stat_t st;
st.st_rdev = 0;
fstat(fd, &st);
return st.st_rdev;
}
drm_public drmHashEntry *drmGetEntry(int fd)
{
unsigned long key = drmGetKeyFromFd(fd);
void *value;
drmHashEntry *entry;
if (!drmHashTable)
drmHashTable = drmHashCreate();
if (drmHashLookup(drmHashTable, key, &value)) {
entry = drmMalloc(sizeof(*entry));
entry->fd = fd;
entry->f = NULL;
entry->tagTable = drmHashCreate();
drmHashInsert(drmHashTable, key, entry);
} else {
entry = value;
}
return entry;
}
/**
* Compare two busid strings
*
* \param first
* \param second
*
* \return 1 if matched.
*
* \internal
* This function compares two bus ID strings. It understands the older
* PCI:b:d:f format and the newer pci:oooo:bb:dd.f format. In the format, o is
* domain, b is bus, d is device, f is function.
*/
static int drmMatchBusID(const char *id1, const char *id2, int pci_domain_ok)
{
/* First, check if the IDs are exactly the same */
if (strcasecmp(id1, id2) == 0)
return 1;
/* Try to match old/new-style PCI bus IDs. */
if (strncasecmp(id1, "pci", 3) == 0) {
unsigned int o1, b1, d1, f1;
unsigned int o2, b2, d2, f2;
int ret;
ret = sscanf(id1, "pci:%04x:%02x:%02x.%u", &o1, &b1, &d1, &f1);
if (ret != 4) {
o1 = 0;
ret = sscanf(id1, "PCI:%u:%u:%u", &b1, &d1, &f1);
if (ret != 3)
return 0;
}
ret = sscanf(id2, "pci:%04x:%02x:%02x.%u", &o2, &b2, &d2, &f2);
if (ret != 4) {
o2 = 0;
ret = sscanf(id2, "PCI:%u:%u:%u", &b2, &d2, &f2);
if (ret != 3)
return 0;
}
/* If domains aren't properly supported by the kernel interface,
* just ignore them, which sucks less than picking a totally random
* card with "open by name"
*/
if (!pci_domain_ok)
o1 = o2 = 0;
if ((o1 != o2) || (b1 != b2) || (d1 != d2) || (f1 != f2))
return 0;
else
return 1;
}
return 0;
}
/**
* Handles error checking for chown call.
*
* \param path to file.
* \param id of the new owner.
* \param id of the new group.
*
* \return zero if success or -1 if failure.
*
* \internal
* Checks for failure. If failure was caused by signal call chown again.
* If any other failure happened then it will output error message using
* drmMsg() call.
*/
#if !UDEV
static int chown_check_return(const char *path, uid_t owner, gid_t group)
{
int rv;
do {
rv = chown(path, owner, group);
} while (rv != 0 && errno == EINTR);
if (rv == 0)
return 0;
drmMsg("Failed to change owner or group for file %s! %d: %s\n",
path, errno, strerror(errno));
return -1;
}
#endif
static const char *drmGetDeviceName(int type)
{
switch (type) {
case DRM_NODE_PRIMARY:
return DRM_DEV_NAME;
case DRM_NODE_CONTROL:
return DRM_CONTROL_DEV_NAME;
case DRM_NODE_RENDER:
return DRM_RENDER_DEV_NAME;
}
return NULL;
}
/**
* Open the DRM device, creating it if necessary.
*
* \param dev major and minor numbers of the device.
* \param minor minor number of the device.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* Assembles the device name from \p minor and opens it, creating the device
* special file node with the major and minor numbers specified by \p dev and
* parent directory if necessary and was called by root.
*/
static int drmOpenDevice(dev_t dev, int minor, int type)
{
stat_t st;
const char *dev_name = drmGetDeviceName(type);
char buf[DRM_NODE_NAME_MAX];
int fd;
mode_t devmode = DRM_DEV_MODE, serv_mode;
gid_t serv_group;
#if !UDEV
int isroot = !geteuid();
uid_t user = DRM_DEV_UID;
gid_t group = DRM_DEV_GID;
#endif
if (!dev_name)
return -EINVAL;
sprintf(buf, dev_name, DRM_DIR_NAME, minor);
drmMsg("drmOpenDevice: node name is %s\n", buf);
if (drm_server_info && drm_server_info->get_perms) {
drm_server_info->get_perms(&serv_group, &serv_mode);
devmode = serv_mode ? serv_mode : DRM_DEV_MODE;
devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
}
#if !UDEV
if (stat(DRM_DIR_NAME, &st)) {
if (!isroot)
return DRM_ERR_NOT_ROOT;
mkdir(DRM_DIR_NAME, DRM_DEV_DIRMODE);
chown_check_return(DRM_DIR_NAME, 0, 0); /* root:root */
chmod(DRM_DIR_NAME, DRM_DEV_DIRMODE);
}
/* Check if the device node exists and create it if necessary. */
if (stat(buf, &st)) {
if (!isroot)
return DRM_ERR_NOT_ROOT;
remove(buf);
mknod(buf, S_IFCHR | devmode, dev);
}
if (drm_server_info && drm_server_info->get_perms) {
group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID;
chown_check_return(buf, user, group);
chmod(buf, devmode);
}
#else
/* if we modprobed then wait for udev */
{
int udev_count = 0;
wait_for_udev:
if (stat(DRM_DIR_NAME, &st)) {
usleep(20);
udev_count++;
if (udev_count == 50)
return -1;
goto wait_for_udev;
}
if (stat(buf, &st)) {
usleep(20);
udev_count++;
if (udev_count == 50)
return -1;
goto wait_for_udev;
}
}
#endif
fd = open(buf, O_RDWR | O_CLOEXEC, 0);
drmMsg("drmOpenDevice: open result is %d, (%s)\n",
fd, fd < 0 ? strerror(errno) : "OK");
if (fd >= 0)
return fd;
#if !UDEV
/* Check if the device node is not what we expect it to be, and recreate it
* and try again if so.
*/
if (st.st_rdev != dev) {
if (!isroot)
return DRM_ERR_NOT_ROOT;
remove(buf);
mknod(buf, S_IFCHR | devmode, dev);
if (drm_server_info && drm_server_info->get_perms) {
chown_check_return(buf, user, group);
chmod(buf, devmode);
}
}
fd = open(buf, O_RDWR | O_CLOEXEC, 0);
drmMsg("drmOpenDevice: open result is %d, (%s)\n",
fd, fd < 0 ? strerror(errno) : "OK");
if (fd >= 0)
return fd;
drmMsg("drmOpenDevice: Open failed\n");
remove(buf);
#endif
return -errno;
}
/**
* Open the DRM device
*
* \param minor device minor number.
* \param create allow to create the device if set.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* Calls drmOpenDevice() if \p create is set, otherwise assembles the device
* name from \p minor and opens it.
*/
static int drmOpenMinor(int minor, int create, int type)
{
int fd;
char buf[DRM_NODE_NAME_MAX];
const char *dev_name = drmGetDeviceName(type);
if (create)
return drmOpenDevice(makedev(DRM_MAJOR, minor), minor, type);
if (!dev_name)
return -EINVAL;
sprintf(buf, dev_name, DRM_DIR_NAME, minor);
if ((fd = open(buf, O_RDWR | O_CLOEXEC, 0)) >= 0)
return fd;
return -errno;
}
/**
* Determine whether the DRM kernel driver has been loaded.
*
* \return 1 if the DRM driver is loaded, 0 otherwise.
*
* \internal
* Determine the presence of the kernel driver by attempting to open the 0
* minor and get version information. For backward compatibility with older
* Linux implementations, /proc/dri is also checked.
*/
drm_public int drmAvailable(void)
{
drmVersionPtr version;
int retval = 0;
int fd;
if ((fd = drmOpenMinor(0, 1, DRM_NODE_PRIMARY)) < 0) {
#ifdef __linux__
/* Try proc for backward Linux compatibility */
if (!access("/proc/dri/0", R_OK))
return 1;
#endif
return 0;
}
if ((version = drmGetVersion(fd))) {
retval = 1;
drmFreeVersion(version);
}
close(fd);
return retval;
}
static int drmGetMinorBase(int type)
{
switch (type) {
case DRM_NODE_PRIMARY:
return 0;
case DRM_NODE_CONTROL:
return 64;
case DRM_NODE_RENDER:
return 128;
default:
return -1;
};
}
static int drmGetMinorType(int major, int minor)
{
#ifdef __FreeBSD__
char name[SPECNAMELEN];
int id;
if (!devname_r(makedev(major, minor), S_IFCHR, name, sizeof(name)))
return -1;
if (sscanf(name, "drm/%d", &id) != 1) {
// If not in /dev/drm/ we have the type in the name
if (sscanf(name, "dri/card%d\n", &id) >= 1)
return DRM_NODE_PRIMARY;
else if (sscanf(name, "dri/control%d\n", &id) >= 1)
return DRM_NODE_CONTROL;
else if (sscanf(name, "dri/renderD%d\n", &id) >= 1)
return DRM_NODE_RENDER;
return -1;
}
minor = id;
#endif
int type = minor >> 6;
if (minor < 0)
return -1;
switch (type) {
case DRM_NODE_PRIMARY:
case DRM_NODE_CONTROL:
case DRM_NODE_RENDER:
return type;
default:
return -1;
}
}
static const char *drmGetMinorName(int type)
{
switch (type) {
case DRM_NODE_PRIMARY:
return DRM_PRIMARY_MINOR_NAME;
case DRM_NODE_CONTROL:
return DRM_CONTROL_MINOR_NAME;
case DRM_NODE_RENDER:
return DRM_RENDER_MINOR_NAME;
default:
return NULL;
}
}
/**
* Open the device by bus ID.
*
* \param busid bus ID.
* \param type device node type.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* This function attempts to open every possible minor (up to DRM_MAX_MINOR),
* comparing the device bus ID with the one supplied.
*
* \sa drmOpenMinor() and drmGetBusid().
*/
static int drmOpenByBusid(const char *busid, int type)
{
int i, pci_domain_ok = 1;
int fd;
const char *buf;
drmSetVersion sv;
int base = drmGetMinorBase(type);
if (base < 0)
return -1;
drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid);
for (i = base; i < base + DRM_MAX_MINOR; i++) {
fd = drmOpenMinor(i, 1, type);
drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd);
if (fd >= 0) {
/* We need to try for 1.4 first for proper PCI domain support
* and if that fails, we know the kernel is busted
*/
sv.drm_di_major = 1;
sv.drm_di_minor = 4;
sv.drm_dd_major = -1; /* Don't care */
sv.drm_dd_minor = -1; /* Don't care */
if (drmSetInterfaceVersion(fd, &sv)) {
#ifndef __alpha__
pci_domain_ok = 0;
#endif
sv.drm_di_major = 1;
sv.drm_di_minor = 1;
sv.drm_dd_major = -1; /* Don't care */
sv.drm_dd_minor = -1; /* Don't care */
drmMsg("drmOpenByBusid: Interface 1.4 failed, trying 1.1\n");
drmSetInterfaceVersion(fd, &sv);
}
buf = drmGetBusid(fd);
drmMsg("drmOpenByBusid: drmGetBusid reports %s\n", buf);
if (buf && drmMatchBusID(buf, busid, pci_domain_ok)) {
drmFreeBusid(buf);
return fd;
}
if (buf)
drmFreeBusid(buf);
close(fd);
}
}
return -1;
}
/**
* Open the device by name.
*
* \param name driver name.
* \param type the device node type.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* This function opens the first minor number that matches the driver name and
* isn't already in use. If it's in use it then it will already have a bus ID
* assigned.
*
* \sa drmOpenMinor(), drmGetVersion() and drmGetBusid().
*/
static int drmOpenByName(const char *name, int type)
{
int i;
int fd;
drmVersionPtr version;
char * id;
int base = drmGetMinorBase(type);
if (base < 0)
return -1;
/*
* Open the first minor number that matches the driver name and isn't
* already in use. If it's in use it will have a busid assigned already.
*/
for (i = base; i < base + DRM_MAX_MINOR; i++) {
if ((fd = drmOpenMinor(i, 1, type)) >= 0) {
if ((version = drmGetVersion(fd))) {
if (!strcmp(version->name, name)) {
drmFreeVersion(version);
id = drmGetBusid(fd);
drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL");
if (!id || !*id) {
if (id)
drmFreeBusid(id);
return fd;
} else {
drmFreeBusid(id);
}
} else {
drmFreeVersion(version);
}
}
close(fd);
}
}
#ifdef __linux__
/* Backward-compatibility /proc support */
for (i = 0; i < 8; i++) {
char proc_name[64], buf[512];
char *driver, *pt, *devstring;
int retcode;
sprintf(proc_name, "/proc/dri/%d/name", i);
if ((fd = open(proc_name, O_RDONLY, 0)) >= 0) {
retcode = read(fd, buf, sizeof(buf)-1);
close(fd);
if (retcode) {
buf[retcode-1] = '\0';
for (driver = pt = buf; *pt && *pt != ' '; ++pt)
;
if (*pt) { /* Device is next */
*pt = '\0';
if (!strcmp(driver, name)) { /* Match */
for (devstring = ++pt; *pt && *pt != ' '; ++pt)
;
if (*pt) { /* Found busid */
return drmOpenByBusid(++pt, type);
} else { /* No busid */
return drmOpenDevice(strtol(devstring, NULL, 0),i, type);
}
}
}
}
}
}
#endif
return -1;
}
/**
* Open the DRM device.
*
* Looks up the specified name and bus ID, and opens the device found. The
* entry in /dev/dri is created if necessary and if called by root.
*
* \param name driver name. Not referenced if bus ID is supplied.
* \param busid bus ID. Zero if not known.
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
* otherwise.
*/
drm_public int drmOpen(const char *name, const char *busid)
{
return drmOpenWithType(name, busid, DRM_NODE_PRIMARY);
}
/**
* Open the DRM device with specified type.
*
* Looks up the specified name and bus ID, and opens the device found. The
* entry in /dev/dri is created if necessary and if called by root.
*
* \param name driver name. Not referenced if bus ID is supplied.
* \param busid bus ID. Zero if not known.
* \param type the device node type to open, PRIMARY, CONTROL or RENDER
*
* \return a file descriptor on success, or a negative value on error.
*
* \internal
* It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
* otherwise.
*/
drm_public int drmOpenWithType(const char *name, const char *busid, int type)
{
if (name != NULL && drm_server_info &&
drm_server_info->load_module && !drmAvailable()) {
/* try to load the kernel module */
if (!drm_server_info->load_module(name)) {
drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
return -1;
}
}
if (busid) {
int fd = drmOpenByBusid(busid, type);
if (fd >= 0)
return fd;
}
if (name)
return drmOpenByName(name, type);
return -1;
}
drm_public int drmOpenControl(int minor)
{
return drmOpenMinor(minor, 0, DRM_NODE_CONTROL);
}
drm_public int drmOpenRender(int minor)
{
return drmOpenMinor(minor, 0, DRM_NODE_RENDER);
}
/**
* Free the version information returned by drmGetVersion().
*
* \param v pointer to the version information.
*
* \internal
* It frees the memory pointed by \p %v as well as all the non-null strings
* pointers in it.
*/
drm_public void drmFreeVersion(drmVersionPtr v)
{
if (!v)
return;
drmFree(v->name);
drmFree(v->date);
drmFree(v->desc);
drmFree(v);
}
/**
* Free the non-public version information returned by the kernel.
*
* \param v pointer to the version information.
*
* \internal
* Used by drmGetVersion() to free the memory pointed by \p %v as well as all
* the non-null strings pointers in it.
*/
static void drmFreeKernelVersion(drm_version_t *v)
{
if (!v)
return;
drmFree(v->name);
drmFree(v->date);
drmFree(v->desc);
drmFree(v);
}
/**
* Copy version information.
*
* \param d destination pointer.
* \param s source pointer.
*
* \internal
* Used by drmGetVersion() to translate the information returned by the ioctl
* interface in a private structure into the public structure counterpart.
*/
static void drmCopyVersion(drmVersionPtr d, const drm_version_t *s)
{
d->version_major = s->version_major;
d->version_minor = s->version_minor;
d->version_patchlevel = s->version_patchlevel;
d->name_len = s->name_len;
d->name = strdup(s->name);
d->date_len = s->date_len;
d->date = strdup(s->date);
d->desc_len = s->desc_len;
d->desc = strdup(s->desc);
}
/**
* Query the driver version information.
*
* \param fd file descriptor.
*
* \return pointer to a drmVersion structure which should be freed with
* drmFreeVersion().
*
* \note Similar information is available via /proc/dri.
*
* \internal
* It gets the version information via successive DRM_IOCTL_VERSION ioctls,
* first with zeros to get the string lengths, and then the actually strings.
* It also null-terminates them since they might not be already.
*/
drm_public drmVersionPtr drmGetVersion(int fd)
{
drmVersionPtr retval;
drm_version_t *version = drmMalloc(sizeof(*version));
if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
drmFreeKernelVersion(version);
return NULL;
}
if (version->name_len)
version->name = drmMalloc(version->name_len + 1);
if (version->date_len)
version->date = drmMalloc(version->date_len + 1);
if (version->desc_len)
version->desc = drmMalloc(version->desc_len + 1);
if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
drmMsg("DRM_IOCTL_VERSION: %s\n", strerror(errno));
drmFreeKernelVersion(version);
return NULL;
}
/* The results might not be null-terminated strings, so terminate them. */
if (version->name_len) version->name[version->name_len] = '\0';
if (version->date_len) version->date[version->date_len] = '\0';
if (version->desc_len) version->desc[version->desc_len] = '\0';
retval = drmMalloc(sizeof(*retval));
drmCopyVersion(retval, version);
drmFreeKernelVersion(version);
return retval;
}
/**
* Get version information for the DRM user space library.
*
* This version number is driver independent.
*
* \param fd file descriptor.
*
* \return version information.
*
* \internal
* This function allocates and fills a drm_version structure with a hard coded
* version number.
*/
drm_public drmVersionPtr drmGetLibVersion(int fd)
{
drm_version_t *version = drmMalloc(sizeof(*version));
/* Version history:
* NOTE THIS MUST NOT GO ABOVE VERSION 1.X due to drivers needing it
* revision 1.0.x = original DRM interface with no drmGetLibVersion
* entry point and many drm<Device> extensions
* revision 1.1.x = added drmCommand entry points for device extensions
* added drmGetLibVersion to identify libdrm.a version
* revision 1.2.x = added drmSetInterfaceVersion
* modified drmOpen to handle both busid and name
* revision 1.3.x = added server + memory manager
*/
version->version_major = 1;
version->version_minor = 3;
version->version_patchlevel = 0;
return (drmVersionPtr)version;
}
drm_public int drmGetCap(int fd, uint64_t capability, uint64_t *value)
{
struct drm_get_cap cap;
int ret;
memclear(cap);
cap.capability = capability;
ret = drmIoctl(fd, DRM_IOCTL_GET_CAP, &cap);
if (ret)
return ret;
*value = cap.value;
return 0;
}
drm_public int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
{
struct drm_set_client_cap cap;
memclear(cap);
cap.capability = capability;
cap.value = value;
return drmIoctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &cap);
}
/**
* Free the bus ID information.
*
* \param busid bus ID information string as given by drmGetBusid().
*
* \internal
* This function is just frees the memory pointed by \p busid.
*/
drm_public void drmFreeBusid(const char *busid)
{
drmFree((void *)busid);
}
/**
* Get the bus ID of the device.
*
* \param fd file descriptor.
*
* \return bus ID string.
*
* \internal
* This function gets the bus ID via successive DRM_IOCTL_GET_UNIQUE ioctls to
* get the string length and data, passing the arguments in a drm_unique
* structure.
*/
drm_public char *drmGetBusid(int fd)
{
drm_unique_t u;
memclear(u);
if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u))
return NULL;
u.unique = drmMalloc(u.unique_len + 1);
if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) {
drmFree(u.unique);
return NULL;
}
u.unique[u.unique_len] = '\0';
return u.unique;
}
/**
* Set the bus ID of the device.
*
* \param fd file descriptor.
* \param busid bus ID string.
*
* \return zero on success, negative on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_SET_UNIQUE ioctl, passing
* the arguments in a drm_unique structure.
*/
drm_public int drmSetBusid(int fd, const char *busid)
{
drm_unique_t u;
memclear(u);
u.unique = (char *)busid;
u.unique_len = strlen(busid);
if (drmIoctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) {
return -errno;
}
return 0;
}
drm_public int drmGetMagic(int fd, drm_magic_t * magic)
{
drm_auth_t auth;
memclear(auth);
*magic = 0;
if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
return -errno;
*magic = auth.magic;
return 0;
}
drm_public int drmAuthMagic(int fd, drm_magic_t magic)
{
drm_auth_t auth;
memclear(auth);
auth.magic = magic;
if (drmIoctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth))
return -errno;
return 0;
}
/**
* Specifies a range of memory that is available for mapping by a
* non-root process.
*
* \param fd file descriptor.
* \param offset usually the physical address. The actual meaning depends of
* the \p type parameter. See below.
* \param size of the memory in bytes.
* \param type type of the memory to be mapped.
* \param flags combination of several flags to modify the function actions.
* \param handle will be set to a value that may be used as the offset
* parameter for mmap().
*
* \return zero on success or a negative value on error.
*
* \par Mapping the frame buffer
* For the frame buffer
* - \p offset will be the physical address of the start of the frame buffer,
* - \p size will be the size of the frame buffer in bytes, and
* - \p type will be DRM_FRAME_BUFFER.
*
* \par
* The area mapped will be uncached. If MTRR support is available in the
* kernel, the frame buffer area will be set to write combining.
*
* \par Mapping the MMIO register area
* For the MMIO register area,
* - \p offset will be the physical address of the start of the register area,
* - \p size will be the size of the register area bytes, and
* - \p type will be DRM_REGISTERS.
* \par
* The area mapped will be uncached.
*
* \par Mapping the SAREA
* For the SAREA,
* - \p offset will be ignored and should be set to zero,
* - \p size will be the desired size of the SAREA in bytes,
* - \p type will be DRM_SHM.
*
* \par
* A shared memory area of the requested size will be created and locked in
* kernel memory. This area may be mapped into client-space by using the handle
* returned.
*
* \note May only be called by root.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_ADD_MAP ioctl, passing
* the arguments in a drm_map structure.
*/
drm_public int drmAddMap(int fd, drm_handle_t offset, drmSize size, drmMapType type,
drmMapFlags flags, drm_handle_t *handle)
{
drm_map_t map;
memclear(map);
map.offset = offset;
map.size = size;
map.type = (enum drm_map_type)type;
map.flags = (enum drm_map_flags)flags;
if (drmIoctl(fd, DRM_IOCTL_ADD_MAP, &map))
return -errno;
if (handle)
*handle = (drm_handle_t)(uintptr_t)map.handle;
return 0;
}
drm_public int drmRmMap(int fd, drm_handle_t handle)
{
drm_map_t map;
memclear(map);
map.handle = (void *)(uintptr_t)handle;
if(drmIoctl(fd, DRM_IOCTL_RM_MAP, &map))
return -errno;
return 0;
}
/**
* Make buffers available for DMA transfers.
*
* \param fd file descriptor.
* \param count number of buffers.
* \param size size of each buffer.
* \param flags buffer allocation flags.
* \param agp_offset offset in the AGP aperture
*
* \return number of buffers allocated, negative on error.
*
* \internal
* This function is a wrapper around DRM_IOCTL_ADD_BUFS ioctl.
*
* \sa drm_buf_desc.
*/
drm_public int drmAddBufs(int fd, int count, int size, drmBufDescFlags flags,
int agp_offset)
{
drm_buf_desc_t request;
memclear(request);
request.count = count;
request.size = size;
request.flags = (int)flags;
request.agp_start = agp_offset;
if (drmIoctl(fd, DRM_IOCTL_ADD_BUFS, &request))
return -errno;
return request.count;
}
drm_public int drmMarkBufs(int fd, double low, double high)
{
drm_buf_info_t info;
int i;
memclear(info);
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
return -EINVAL;
if (!info.count)
return -EINVAL;
if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
return -ENOMEM;
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
int retval = -errno;
drmFree(info.list);
return retval;
}
for (i = 0; i < info.count; i++) {
info.list[i].low_mark = low * info.list[i].count;
info.list[i].high_mark = high * info.list[i].count;
if (drmIoctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) {
int retval = -errno;
drmFree(info.list);
return retval;
}
}
drmFree(info.list);
return 0;
}
/**
* Free buffers.
*
* \param fd file descriptor.
* \param count number of buffers to free.
* \param list list of buffers to be freed.
*
* \return zero on success, or a negative value on failure.
*
* \note This function is primarily used for debugging.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_FREE_BUFS ioctl, passing
* the arguments in a drm_buf_free structure.
*/
drm_public int drmFreeBufs(int fd, int count, int *list)
{
drm_buf_free_t request;
memclear(request);
request.count = count;
request.list = list;
if (drmIoctl(fd, DRM_IOCTL_FREE_BUFS, &request))
return -errno;
return 0;
}
/**
* Close the device.
*
* \param fd file descriptor.
*
* \internal
* This function closes the file descriptor.
*/
drm_public int drmClose(int fd)
{
unsigned long key = drmGetKeyFromFd(fd);
drmHashEntry *entry = drmGetEntry(fd);
drmHashDestroy(entry->tagTable);
entry->fd = 0;
entry->f = NULL;
entry->tagTable = NULL;
drmHashDelete(drmHashTable, key);
drmFree(entry);
return close(fd);
}
/**
* Map a region of memory.
*
* \param fd file descriptor.
* \param handle handle returned by drmAddMap().
* \param size size in bytes. Must match the size used by drmAddMap().
* \param address will contain the user-space virtual address where the mapping
* begins.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper for mmap().
*/
drm_public int drmMap(int fd, drm_handle_t handle, drmSize size,
drmAddressPtr address)
{
static unsigned long pagesize_mask = 0;
if (fd < 0)
return -EINVAL;
if (!pagesize_mask)
pagesize_mask = getpagesize() - 1;
size = (size + pagesize_mask) & ~pagesize_mask;
*address = drm_mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle);
if (*address == MAP_FAILED)
return -errno;
return 0;
}
/**
* Unmap mappings obtained with drmMap().
*
* \param address address as given by drmMap().
* \param size size in bytes. Must match the size used by drmMap().
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper for munmap().
*/
drm_public int drmUnmap(drmAddress address, drmSize size)
{
return drm_munmap(address, size);
}
drm_public drmBufInfoPtr drmGetBufInfo(int fd)
{
drm_buf_info_t info;
drmBufInfoPtr retval;
int i;
memclear(info);
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
return NULL;
if (info.count) {
if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
return NULL;
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
drmFree(info.list);
return NULL;
}
retval = drmMalloc(sizeof(*retval));
retval->count = info.count;
if (!(retval->list = drmMalloc(info.count * sizeof(*retval->list)))) {
drmFree(retval);
drmFree(info.list);
return NULL;
}
for (i = 0; i < info.count; i++) {
retval->list[i].count = info.list[i].count;
retval->list[i].size = info.list[i].size;
retval->list[i].low_mark = info.list[i].low_mark;
retval->list[i].high_mark = info.list[i].high_mark;
}
drmFree(info.list);
return retval;
}
return NULL;
}
/**
* Map all DMA buffers into client-virtual space.
*
* \param fd file descriptor.
*
* \return a pointer to a ::drmBufMap structure.
*
* \note The client may not use these buffers until obtaining buffer indices
* with drmDMA().
*
* \internal
* This function calls the DRM_IOCTL_MAP_BUFS ioctl and copies the returned
* information about the buffers in a drm_buf_map structure into the
* client-visible data structures.
*/
drm_public drmBufMapPtr drmMapBufs(int fd)
{
drm_buf_map_t bufs;
drmBufMapPtr retval;
int i;
memclear(bufs);
if (drmIoctl(fd, DRM_IOCTL_MAP_BUFS, &bufs))
return NULL;
if (!bufs.count)
return NULL;
if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list))))
return NULL;
if (drmIoctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) {
drmFree(bufs.list);
return NULL;
}
retval = drmMalloc(sizeof(*retval));
retval->count = bufs.count;
retval->list = drmMalloc(bufs.count * sizeof(*retval->list));
for (i = 0; i < bufs.count; i++) {
retval->list[i].idx = bufs.list[i].idx;
retval->list[i].total = bufs.list[i].total;
retval->list[i].used = 0;
retval->list[i].address = bufs.list[i].address;
}
drmFree(bufs.list);
return retval;
}
/**
* Unmap buffers allocated with drmMapBufs().
*
* \return zero on success, or negative value on failure.
*
* \internal
* Calls munmap() for every buffer stored in \p bufs and frees the
* memory allocated by drmMapBufs().
*/
drm_public int drmUnmapBufs(drmBufMapPtr bufs)
{
int i;
for (i = 0; i < bufs->count; i++) {
drm_munmap(bufs->list[i].address, bufs->list[i].total);
}
drmFree(bufs->list);
drmFree(bufs);
return 0;
}
#define DRM_DMA_RETRY 16
/**
* Reserve DMA buffers.
*
* \param fd file descriptor.
* \param request
*
* \return zero on success, or a negative value on failure.
*
* \internal
* Assemble the arguments into a drm_dma structure and keeps issuing the
* DRM_IOCTL_DMA ioctl until success or until maximum number of retries.
*/
drm_public int drmDMA(int fd, drmDMAReqPtr request)
{
drm_dma_t dma;
int ret, i = 0;
dma.context = request->context;
dma.send_count = request->send_count;
dma.send_indices = request->send_list;
dma.send_sizes = request->send_sizes;
dma.flags = (enum drm_dma_flags)request->flags;
dma.request_count = request->request_count;
dma.request_size = request->request_size;
dma.request_indices = request->request_list;
dma.request_sizes = request->request_sizes;
dma.granted_count = 0;
do {
ret = ioctl( fd, DRM_IOCTL_DMA, &dma );
} while ( ret && errno == EAGAIN && i++ < DRM_DMA_RETRY );
if ( ret == 0 ) {
request->granted_count = dma.granted_count;
return 0;
} else {
return -errno;
}
}
/**
* Obtain heavyweight hardware lock.
*
* \param fd file descriptor.
* \param context context.
* \param flags flags that determine the state of the hardware when the function
* returns.
*
* \return always zero.
*
* \internal
* This function translates the arguments into a drm_lock structure and issue
* the DRM_IOCTL_LOCK ioctl until the lock is successfully acquired.
*/
drm_public int drmGetLock(int fd, drm_context_t context, drmLockFlags flags)
{
drm_lock_t lock;
memclear(lock);
lock.context = context;
lock.flags = 0;
if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY;
if (flags & DRM_LOCK_QUIESCENT) lock.flags |= _DRM_LOCK_QUIESCENT;
if (flags & DRM_LOCK_FLUSH) lock.flags |= _DRM_LOCK_FLUSH;
if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL;
if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
while (drmIoctl(fd, DRM_IOCTL_LOCK, &lock))
;
return 0;
}
/**
* Release the hardware lock.
*
* \param fd file descriptor.
* \param context context.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_UNLOCK ioctl, passing the
* argument in a drm_lock structure.
*/
drm_public int drmUnlock(int fd, drm_context_t context)
{
drm_lock_t lock;
memclear(lock);
lock.context = context;
return drmIoctl(fd, DRM_IOCTL_UNLOCK, &lock);
}
drm_public drm_context_t *drmGetReservedContextList(int fd, int *count)
{
drm_ctx_res_t res;
drm_ctx_t *list;
drm_context_t * retval;
int i;
memclear(res);
if (drmIoctl(fd, DRM_IOCTL_RES_CTX, &res))
return NULL;
if (!res.count)
return NULL;
if (!(list = drmMalloc(res.count * sizeof(*list))))
return NULL;
if (!(retval = drmMalloc(res.count * sizeof(*retval))))
goto err_free_list;
res.contexts = list;
if (drmIoctl(fd, DRM_IOCTL_RES_CTX, &res))
goto err_free_context;
for (i = 0; i < res.count; i++)
retval[i] = list[i].handle;
drmFree(list);
*count = res.count;
return retval;
err_free_list:
drmFree(list);
err_free_context:
drmFree(retval);
return NULL;
}
drm_public void drmFreeReservedContextList(drm_context_t *pt)
{
drmFree(pt);
}
/**
* Create context.
*
* Used by the X server during GLXContext initialization. This causes
* per-context kernel-level resources to be allocated.
*
* \param fd file descriptor.
* \param handle is set on success. To be used by the client when requesting DMA
* dispatch with drmDMA().
*
* \return zero on success, or a negative value on failure.
*
* \note May only be called by root.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_ADD_CTX ioctl, passing the
* argument in a drm_ctx structure.
*/
drm_public int drmCreateContext(int fd, drm_context_t *handle)
{
drm_ctx_t ctx;
memclear(ctx);
if (drmIoctl(fd, DRM_IOCTL_ADD_CTX, &ctx))
return -errno;
*handle = ctx.handle;
return 0;
}
drm_public int drmSwitchToContext(int fd, drm_context_t context)
{
drm_ctx_t ctx;
memclear(ctx);
ctx.handle = context;
if (drmIoctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx))
return -errno;
return 0;
}
drm_public int drmSetContextFlags(int fd, drm_context_t context,
drm_context_tFlags flags)
{
drm_ctx_t ctx;
/*
* Context preserving means that no context switches are done between DMA
* buffers from one context and the next. This is suitable for use in the
* X server (which promises to maintain hardware context), or in the
* client-side library when buffers are swapped on behalf of two threads.
*/
memclear(ctx);
ctx.handle = context;
if (flags & DRM_CONTEXT_PRESERVED)
ctx.flags |= _DRM_CONTEXT_PRESERVED;
if (flags & DRM_CONTEXT_2DONLY)
ctx.flags |= _DRM_CONTEXT_2DONLY;
if (drmIoctl(fd, DRM_IOCTL_MOD_CTX, &ctx))
return -errno;
return 0;
}
drm_public int drmGetContextFlags(int fd, drm_context_t context,
drm_context_tFlagsPtr flags)
{
drm_ctx_t ctx;
memclear(ctx);
ctx.handle = context;
if (drmIoctl(fd, DRM_IOCTL_GET_CTX, &ctx))
return -errno;
*flags = 0;
if (ctx.flags & _DRM_CONTEXT_PRESERVED)
*flags |= DRM_CONTEXT_PRESERVED;
if (ctx.flags & _DRM_CONTEXT_2DONLY)
*flags |= DRM_CONTEXT_2DONLY;
return 0;
}
/**
* Destroy context.
*
* Free any kernel-level resources allocated with drmCreateContext() associated
* with the context.
*
* \param fd file descriptor.
* \param handle handle given by drmCreateContext().
*
* \return zero on success, or a negative value on failure.
*
* \note May only be called by root.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_RM_CTX ioctl, passing the
* argument in a drm_ctx structure.
*/
drm_public int drmDestroyContext(int fd, drm_context_t handle)
{
drm_ctx_t ctx;
memclear(ctx);
ctx.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_RM_CTX, &ctx))
return -errno;
return 0;
}
drm_public int drmCreateDrawable(int fd, drm_drawable_t *handle)
{
drm_draw_t draw;
memclear(draw);
if (drmIoctl(fd, DRM_IOCTL_ADD_DRAW, &draw))
return -errno;
*handle = draw.handle;
return 0;
}
drm_public int drmDestroyDrawable(int fd, drm_drawable_t handle)
{
drm_draw_t draw;
memclear(draw);
draw.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_RM_DRAW, &draw))
return -errno;
return 0;
}
drm_public int drmUpdateDrawableInfo(int fd, drm_drawable_t handle,
drm_drawable_info_type_t type,
unsigned int num, void *data)
{
drm_update_draw_t update;
memclear(update);
update.handle = handle;
update.type = type;
update.num = num;
update.data = (unsigned long long)(unsigned long)data;
if (drmIoctl(fd, DRM_IOCTL_UPDATE_DRAW, &update))
return -errno;
return 0;
}
drm_public int drmCrtcGetSequence(int fd, uint32_t crtcId, uint64_t *sequence,
uint64_t *ns)
{
struct drm_crtc_get_sequence get_seq;
int ret;
memclear(get_seq);
get_seq.crtc_id = crtcId;
ret = drmIoctl(fd, DRM_IOCTL_CRTC_GET_SEQUENCE, &get_seq);
if (ret)
return ret;
if (sequence)
*sequence = get_seq.sequence;
if (ns)
*ns = get_seq.sequence_ns;
return 0;
}
drm_public int drmCrtcQueueSequence(int fd, uint32_t crtcId, uint32_t flags,
uint64_t sequence,
uint64_t *sequence_queued,
uint64_t user_data)
{
struct drm_crtc_queue_sequence queue_seq;
int ret;
memclear(queue_seq);
queue_seq.crtc_id = crtcId;
queue_seq.flags = flags;
queue_seq.sequence = sequence;
queue_seq.user_data = user_data;
ret = drmIoctl(fd, DRM_IOCTL_CRTC_QUEUE_SEQUENCE, &queue_seq);
if (ret == 0 && sequence_queued)
*sequence_queued = queue_seq.sequence;
return ret;
}
/**
* Acquire the AGP device.
*
* Must be called before any of the other AGP related calls.
*
* \param fd file descriptor.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_ACQUIRE ioctl.
*/
drm_public int drmAgpAcquire(int fd)
{
if (drmIoctl(fd, DRM_IOCTL_AGP_ACQUIRE, NULL))
return -errno;
return 0;
}
/**
* Release the AGP device.
*
* \param fd file descriptor.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_RELEASE ioctl.
*/
drm_public int drmAgpRelease(int fd)
{
if (drmIoctl(fd, DRM_IOCTL_AGP_RELEASE, NULL))
return -errno;
return 0;
}
/**
* Set the AGP mode.
*
* \param fd file descriptor.
* \param mode AGP mode.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_ENABLE ioctl, passing the
* argument in a drm_agp_mode structure.
*/
drm_public int drmAgpEnable(int fd, unsigned long mode)
{
drm_agp_mode_t m;
memclear(m);
m.mode = mode;
if (drmIoctl(fd, DRM_IOCTL_AGP_ENABLE, &m))
return -errno;
return 0;
}
/**
* Allocate a chunk of AGP memory.
*
* \param fd file descriptor.
* \param size requested memory size in bytes. Will be rounded to page boundary.
* \param type type of memory to allocate.
* \param address if not zero, will be set to the physical address of the
* allocated memory.
* \param handle on success will be set to a handle of the allocated memory.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_ALLOC ioctl, passing the
* arguments in a drm_agp_buffer structure.
*/
drm_public int drmAgpAlloc(int fd, unsigned long size, unsigned long type,
unsigned long *address, drm_handle_t *handle)
{
drm_agp_buffer_t b;
memclear(b);
*handle = DRM_AGP_NO_HANDLE;
b.size = size;
b.type = type;
if (drmIoctl(fd, DRM_IOCTL_AGP_ALLOC, &b))
return -errno;
if (address != 0UL)
*address = b.physical;
*handle = b.handle;
return 0;
}
/**
* Free a chunk of AGP memory.
*
* \param fd file descriptor.
* \param handle handle to the allocated memory, as given by drmAgpAllocate().
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_FREE ioctl, passing the
* argument in a drm_agp_buffer structure.
*/
drm_public int drmAgpFree(int fd, drm_handle_t handle)
{
drm_agp_buffer_t b;
memclear(b);
b.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_AGP_FREE, &b))
return -errno;
return 0;
}
/**
* Bind a chunk of AGP memory.
*
* \param fd file descriptor.
* \param handle handle to the allocated memory, as given by drmAgpAllocate().
* \param offset offset in bytes. It will round to page boundary.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_BIND ioctl, passing the
* argument in a drm_agp_binding structure.
*/
drm_public int drmAgpBind(int fd, drm_handle_t handle, unsigned long offset)
{
drm_agp_binding_t b;
memclear(b);
b.handle = handle;
b.offset = offset;
if (drmIoctl(fd, DRM_IOCTL_AGP_BIND, &b))
return -errno;
return 0;
}
/**
* Unbind a chunk of AGP memory.
*
* \param fd file descriptor.
* \param handle handle to the allocated memory, as given by drmAgpAllocate().
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_UNBIND ioctl, passing
* the argument in a drm_agp_binding structure.
*/
drm_public int drmAgpUnbind(int fd, drm_handle_t handle)
{
drm_agp_binding_t b;
memclear(b);
b.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_AGP_UNBIND, &b))
return -errno;
return 0;
}
/**
* Get AGP driver major version number.
*
* \param fd file descriptor.
*
* \return major version number on success, or a negative value on failure..
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public int drmAgpVersionMajor(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return -errno;
return i.agp_version_major;
}
/**
* Get AGP driver minor version number.
*
* \param fd file descriptor.
*
* \return minor version number on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public int drmAgpVersionMinor(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return -errno;
return i.agp_version_minor;
}
/**
* Get AGP mode.
*
* \param fd file descriptor.
*
* \return mode on success, or zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned long drmAgpGetMode(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.mode;
}
/**
* Get AGP aperture base.
*
* \param fd file descriptor.
*
* \return aperture base on success, zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned long drmAgpBase(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.aperture_base;
}
/**
* Get AGP aperture size.
*
* \param fd file descriptor.
*
* \return aperture size on success, zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned long drmAgpSize(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.aperture_size;
}
/**
* Get used AGP memory.
*
* \param fd file descriptor.
*
* \return memory used on success, or zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned long drmAgpMemoryUsed(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.memory_used;
}
/**
* Get available AGP memory.
*
* \param fd file descriptor.
*
* \return memory available on success, or zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned long drmAgpMemoryAvail(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.memory_allowed;
}
/**
* Get hardware vendor ID.
*
* \param fd file descriptor.
*
* \return vendor ID on success, or zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned int drmAgpVendorId(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.id_vendor;
}
/**
* Get hardware device ID.
*
* \param fd file descriptor.
*
* \return zero on success, or zero on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
* necessary information in a drm_agp_info structure.
*/
drm_public unsigned int drmAgpDeviceId(int fd)
{
drm_agp_info_t i;
memclear(i);
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.id_device;
}
drm_public int drmScatterGatherAlloc(int fd, unsigned long size,
drm_handle_t *handle)
{
drm_scatter_gather_t sg;
memclear(sg);
*handle = 0;
sg.size = size;
if (drmIoctl(fd, DRM_IOCTL_SG_ALLOC, &sg))
return -errno;
*handle = sg.handle;
return 0;
}
drm_public int drmScatterGatherFree(int fd, drm_handle_t handle)
{
drm_scatter_gather_t sg;
memclear(sg);
sg.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_SG_FREE, &sg))
return -errno;
return 0;
}
/**
* Wait for VBLANK.
*
* \param fd file descriptor.
* \param vbl pointer to a drmVBlank structure.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_WAIT_VBLANK ioctl.
*/
drm_public int drmWaitVBlank(int fd, drmVBlankPtr vbl)
{
struct timespec timeout, cur;
int ret;
ret = clock_gettime(CLOCK_MONOTONIC, &timeout);
if (ret < 0) {
fprintf(stderr, "clock_gettime failed: %s\n", strerror(errno));
goto out;
}
timeout.tv_sec++;
do {
ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
vbl->request.type &= ~DRM_VBLANK_RELATIVE;
if (ret && errno == EINTR) {
clock_gettime(CLOCK_MONOTONIC, &cur);
/* Timeout after 1s */
if (cur.tv_sec > timeout.tv_sec + 1 ||
(cur.tv_sec == timeout.tv_sec && cur.tv_nsec >=
timeout.tv_nsec)) {
errno = EBUSY;
ret = -1;
break;
}
}
} while (ret && errno == EINTR);
out:
return ret;
}
drm_public int drmError(int err, const char *label)
{
switch (err) {
case DRM_ERR_NO_DEVICE:
fprintf(stderr, "%s: no device\n", label);
break;
case DRM_ERR_NO_ACCESS:
fprintf(stderr, "%s: no access\n", label);
break;
case DRM_ERR_NOT_ROOT:
fprintf(stderr, "%s: not root\n", label);
break;
case DRM_ERR_INVALID:
fprintf(stderr, "%s: invalid args\n", label);
break;
default:
if (err < 0)
err = -err;
fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) );
break;
}
return 1;
}
/**
* Install IRQ handler.
*
* \param fd file descriptor.
* \param irq IRQ number.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the
* argument in a drm_control structure.
*/
drm_public int drmCtlInstHandler(int fd, int irq)
{
drm_control_t ctl;
memclear(ctl);
ctl.func = DRM_INST_HANDLER;
ctl.irq = irq;
if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
return -errno;
return 0;
}
/**
* Uninstall IRQ handler.
*
* \param fd file descriptor.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the
* argument in a drm_control structure.
*/
drm_public int drmCtlUninstHandler(int fd)
{
drm_control_t ctl;
memclear(ctl);
ctl.func = DRM_UNINST_HANDLER;
ctl.irq = 0;
if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
return -errno;
return 0;
}
drm_public int drmFinish(int fd, int context, drmLockFlags flags)
{
drm_lock_t lock;
memclear(lock);
lock.context = context;
if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY;
if (flags & DRM_LOCK_QUIESCENT) lock.flags |= _DRM_LOCK_QUIESCENT;
if (flags & DRM_LOCK_FLUSH) lock.flags |= _DRM_LOCK_FLUSH;
if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL;
if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
if (drmIoctl(fd, DRM_IOCTL_FINISH, &lock))
return -errno;
return 0;
}
/**
* Get IRQ from bus ID.
*
* \param fd file descriptor.
* \param busnum bus number.
* \param devnum device number.
* \param funcnum function number.
*
* \return IRQ number on success, or a negative value on failure.
*
* \internal
* This function is a wrapper around the DRM_IOCTL_IRQ_BUSID ioctl, passing the
* arguments in a drm_irq_busid structure.
*/
drm_public int drmGetInterruptFromBusID(int fd, int busnum, int devnum,
int funcnum)
{
drm_irq_busid_t p;
memclear(p);
p.busnum = busnum;
p.devnum = devnum;
p.funcnum = funcnum;
if (drmIoctl(fd, DRM_IOCTL_IRQ_BUSID, &p))
return -errno;
return p.irq;
}
drm_public int drmAddContextTag(int fd, drm_context_t context, void *tag)
{
drmHashEntry *entry = drmGetEntry(fd);
if (drmHashInsert(entry->tagTable, context, tag)) {
drmHashDelete(entry->tagTable, context);
drmHashInsert(entry->tagTable, context, tag);
}
return 0;
}
drm_public int drmDelContextTag(int fd, drm_context_t context)
{
drmHashEntry *entry = drmGetEntry(fd);
return drmHashDelete(entry->tagTable, context);
}
drm_public void *drmGetContextTag(int fd, drm_context_t context)
{
drmHashEntry *entry = drmGetEntry(fd);
void *value;
if (drmHashLookup(entry->tagTable, context, &value))
return NULL;
return value;
}
drm_public int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id,
drm_handle_t handle)
{
drm_ctx_priv_map_t map;
memclear(map);
map.ctx_id = ctx_id;
map.handle = (void *)(uintptr_t)handle;
if (drmIoctl(fd, DRM_IOCTL_SET_SAREA_CTX, &map))
return -errno;
return 0;
}
drm_public int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id,
drm_handle_t *handle)
{
drm_ctx_priv_map_t map;
memclear(map);
map.ctx_id = ctx_id;
if (drmIoctl(fd, DRM_IOCTL_GET_SAREA_CTX, &map))
return -errno;
if (handle)
*handle = (drm_handle_t)(uintptr_t)map.handle;
return 0;
}
drm_public int drmGetMap(int fd, int idx, drm_handle_t *offset, drmSize *size,
drmMapType *type, drmMapFlags *flags,
drm_handle_t *handle, int *mtrr)
{
drm_map_t map;
memclear(map);
map.offset = idx;
if (drmIoctl(fd, DRM_IOCTL_GET_MAP, &map))
return -errno;
*offset = map.offset;
*size = map.size;
*type = (drmMapType)map.type;
*flags = (drmMapFlags)map.flags;
*handle = (unsigned long)map.handle;
*mtrr = map.mtrr;
return 0;
}
drm_public int drmGetClient(int fd, int idx, int *auth, int *pid, int *uid,
unsigned long *magic, unsigned long *iocs)
{
drm_client_t client;
memclear(client);
client.idx = idx;
if (drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client))
return -errno;
*auth = client.auth;
*pid = client.pid;
*uid = client.uid;
*magic = client.magic;
*iocs = client.iocs;
return 0;
}
drm_public int drmGetStats(int fd, drmStatsT *stats)
{
drm_stats_t s;
unsigned i;
memclear(s);
if (drmIoctl(fd, DRM_IOCTL_GET_STATS, &s))
return -errno;
stats->count = 0;
memset(stats, 0, sizeof(*stats));
if (s.count > sizeof(stats->data)/sizeof(stats->data[0]))
return -1;
#define SET_VALUE \
stats->data[i].long_format = "%-20.20s"; \
stats->data[i].rate_format = "%8.8s"; \
stats->data[i].isvalue = 1; \
stats->data[i].verbose = 0
#define SET_COUNT \
stats->data[i].long_format = "%-20.20s"; \
stats->data[i].rate_format = "%5.5s"; \
stats->data[i].isvalue = 0; \
stats->data[i].mult_names = "kgm"; \
stats->data[i].mult = 1000; \
stats->data[i].verbose = 0
#define SET_BYTE \
stats->data[i].long_format = "%-20.20s"; \
stats->data[i].rate_format = "%5.5s"; \
stats->data[i].isvalue = 0; \
stats->data[i].mult_names = "KGM"; \
stats->data[i].mult = 1024; \
stats->data[i].verbose = 0
stats->count = s.count;
for (i = 0; i < s.count; i++) {
stats->data[i].value = s.data[i].value;
switch (s.data[i].type) {
case _DRM_STAT_LOCK:
stats->data[i].long_name = "Lock";
stats->data[i].rate_name = "Lock";
SET_VALUE;
break;
case _DRM_STAT_OPENS:
stats->data[i].long_name = "Opens";
stats->data[i].rate_name = "O";
SET_COUNT;
stats->data[i].verbose = 1;
break;
case _DRM_STAT_CLOSES:
stats->data[i].long_name = "Closes";
stats->data[i].rate_name = "Lock";
SET_COUNT;
stats->data[i].verbose = 1;
break;
case _DRM_STAT_IOCTLS:
stats->data[i].long_name = "Ioctls";
stats->data[i].rate_name = "Ioc/s";
SET_COUNT;
break;
case _DRM_STAT_LOCKS:
stats->data[i].long_name = "Locks";
stats->data[i].rate_name = "Lck/s";
SET_COUNT;
break;
case _DRM_STAT_UNLOCKS:
stats->data[i].long_name = "Unlocks";
stats->data[i].rate_name = "Unl/s";
SET_COUNT;
break;
case _DRM_STAT_IRQ:
stats->data[i].long_name = "IRQs";
stats->data[i].rate_name = "IRQ/s";
SET_COUNT;
break;
case _DRM_STAT_PRIMARY:
stats->data[i].long_name = "Primary Bytes";
stats->data[i].rate_name = "PB/s";
SET_BYTE;
break;
case _DRM_STAT_SECONDARY:
stats->data[i].long_name = "Secondary Bytes";
stats->data[i].rate_name = "SB/s";
SET_BYTE;
break;
case _DRM_STAT_DMA:
stats->data[i].long_name = "DMA";
stats->data[i].rate_name = "DMA/s";
SET_COUNT;
break;
case _DRM_STAT_SPECIAL:
stats->data[i].long_name = "Special DMA";
stats->data[i].rate_name = "dma/s";
SET_COUNT;
break;
case _DRM_STAT_MISSED:
stats->data[i].long_name = "Miss";
stats->data[i].rate_name = "Ms/s";
SET_COUNT;
break;
case _DRM_STAT_VALUE:
stats->data[i].long_name = "Value";
stats->data[i].rate_name = "Value";
SET_VALUE;
break;
case _DRM_STAT_BYTE:
stats->data[i].long_name = "Bytes";
stats->data[i].rate_name = "B/s";
SET_BYTE;
break;
case _DRM_STAT_COUNT:
default:
stats->data[i].long_name = "Count";
stats->data[i].rate_name = "Cnt/s";
SET_COUNT;
break;
}
}
return 0;
}
/**
* Issue a set-version ioctl.
*
* \param fd file descriptor.
* \param drmCommandIndex command index
* \param data source pointer of the data to be read and written.
* \param size size of the data to be read and written.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* It issues a read-write ioctl given by
* \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
*/
drm_public int drmSetInterfaceVersion(int fd, drmSetVersion *version)
{
int retcode = 0;
drm_set_version_t sv;
memclear(sv);
sv.drm_di_major = version->drm_di_major;
sv.drm_di_minor = version->drm_di_minor;
sv.drm_dd_major = version->drm_dd_major;
sv.drm_dd_minor = version->drm_dd_minor;
if (drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv)) {
retcode = -errno;
}
version->drm_di_major = sv.drm_di_major;
version->drm_di_minor = sv.drm_di_minor;
version->drm_dd_major = sv.drm_dd_major;
version->drm_dd_minor = sv.drm_dd_minor;
return retcode;
}
/**
* Send a device-specific command.
*
* \param fd file descriptor.
* \param drmCommandIndex command index
*
* \return zero on success, or a negative value on failure.
*
* \internal
* It issues a ioctl given by
* \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
*/
drm_public int drmCommandNone(int fd, unsigned long drmCommandIndex)
{
unsigned long request;
request = DRM_IO( DRM_COMMAND_BASE + drmCommandIndex);
if (drmIoctl(fd, request, NULL)) {
return -errno;
}
return 0;
}
/**
* Send a device-specific read command.
*
* \param fd file descriptor.
* \param drmCommandIndex command index
* \param data destination pointer of the data to be read.
* \param size size of the data to be read.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* It issues a read ioctl given by
* \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
*/
drm_public int drmCommandRead(int fd, unsigned long drmCommandIndex,
void *data, unsigned long size)
{
unsigned long request;
request = DRM_IOC( DRM_IOC_READ, DRM_IOCTL_BASE,
DRM_COMMAND_BASE + drmCommandIndex, size);
if (drmIoctl(fd, request, data)) {
return -errno;
}
return 0;
}
/**
* Send a device-specific write command.
*
* \param fd file descriptor.
* \param drmCommandIndex command index
* \param data source pointer of the data to be written.
* \param size size of the data to be written.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* It issues a write ioctl given by
* \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
*/
drm_public int drmCommandWrite(int fd, unsigned long drmCommandIndex,
void *data, unsigned long size)
{
unsigned long request;
request = DRM_IOC( DRM_IOC_WRITE, DRM_IOCTL_BASE,
DRM_COMMAND_BASE + drmCommandIndex, size);
if (drmIoctl(fd, request, data)) {
return -errno;
}
return 0;
}
/**
* Send a device-specific read-write command.
*
* \param fd file descriptor.
* \param drmCommandIndex command index
* \param data source pointer of the data to be read and written.
* \param size size of the data to be read and written.
*
* \return zero on success, or a negative value on failure.
*
* \internal
* It issues a read-write ioctl given by
* \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
*/
drm_public int drmCommandWriteRead(int fd, unsigned long drmCommandIndex,
void *data, unsigned long size)
{
unsigned long request;
request = DRM_IOC( DRM_IOC_READ|DRM_IOC_WRITE, DRM_IOCTL_BASE,
DRM_COMMAND_BASE + drmCommandIndex, size);
if (drmIoctl(fd, request, data))
return -errno;
return 0;
}
#define DRM_MAX_FDS 16
static struct {
char *BusID;
int fd;
int refcount;
int type;
} connection[DRM_MAX_FDS];
static int nr_fds = 0;
drm_public int drmOpenOnce(void *unused, const char *BusID, int *newlyopened)
{
return drmOpenOnceWithType(BusID, newlyopened, DRM_NODE_PRIMARY);
}
drm_public int drmOpenOnceWithType(const char *BusID, int *newlyopened,
int type)
{
int i;
int fd;
for (i = 0; i < nr_fds; i++)
if ((strcmp(BusID, connection[i].BusID) == 0) &&
(connection[i].type == type)) {
connection[i].refcount++;
*newlyopened = 0;
return connection[i].fd;
}
fd = drmOpenWithType(NULL, BusID, type);
if (fd < 0 || nr_fds == DRM_MAX_FDS)
return fd;
connection[nr_fds].BusID = strdup(BusID);
connection[nr_fds].fd = fd;
connection[nr_fds].refcount = 1;
connection[nr_fds].type = type;
*newlyopened = 1;
if (0)
fprintf(stderr, "saved connection %d for %s %d\n",
nr_fds, connection[nr_fds].BusID,
strcmp(BusID, connection[nr_fds].BusID));
nr_fds++;
return fd;
}
drm_public void drmCloseOnce(int fd)
{
int i;
for (i = 0; i < nr_fds; i++) {
if (fd == connection[i].fd) {
if (--connection[i].refcount == 0) {
drmClose(connection[i].fd);
free(connection[i].BusID);
if (i < --nr_fds)
connection[i] = connection[nr_fds];
return;
}
}
}
}
drm_public int drmSetMaster(int fd)
{
return drmIoctl(fd, DRM_IOCTL_SET_MASTER, NULL);
}
drm_public int drmDropMaster(int fd)
{
return drmIoctl(fd, DRM_IOCTL_DROP_MASTER, NULL);
}
drm_public int drmIsMaster(int fd)
{
/* Detect master by attempting something that requires master.
*
* Authenticating magic tokens requires master and 0 is an
* internal kernel detail which we could use. Attempting this on
* a master fd would fail therefore fail with EINVAL because 0
* is invalid.
*
* A non-master fd will fail with EACCES, as the kernel checks
* for master before attempting to do anything else.
*
* Since we don't want to leak implementation details, use
* EACCES.
*/
return drmAuthMagic(fd, 0) != -EACCES;
}
drm_public char *drmGetDeviceNameFromFd(int fd)
{
#ifdef __FreeBSD__
struct stat sbuf;
int maj, min;
int nodetype;
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
nodetype = drmGetMinorType(maj, min);
return drmGetMinorNameForFD(fd, nodetype);
#else
char name[128];
struct stat sbuf;
dev_t d;
int i;
/* The whole drmOpen thing is a fiasco and we need to find a way
* back to just using open(2). For now, however, lets just make
* things worse with even more ad hoc directory walking code to
* discover the device file name. */
fstat(fd, &sbuf);
d = sbuf.st_rdev;
for (i = 0; i < DRM_MAX_MINOR; i++) {
snprintf(name, sizeof name, DRM_DEV_NAME, DRM_DIR_NAME, i);
if (stat(name, &sbuf) == 0 && sbuf.st_rdev == d)
break;
}
if (i == DRM_MAX_MINOR)
return NULL;
return strdup(name);
#endif
}
static bool drmNodeIsDRM(int maj, int min)
{
#ifdef __linux__
char path[64];
struct stat sbuf;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/drm",
maj, min);
return stat(path, &sbuf) == 0;
#elif defined(__FreeBSD__)
char name[SPECNAMELEN];
if (!devname_r(makedev(maj, min), S_IFCHR, name, sizeof(name)))
return 0;
/* Handle drm/ and dri/ as both are present in different FreeBSD version
* FreeBSD on amd64/i386/powerpc external kernel modules create node in
* in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
* only device nodes in /dev/dri/ */
return (!strncmp(name, "drm/", 4) || !strncmp(name, "dri/", 4));
#else
return maj == DRM_MAJOR;
#endif
}
drm_public int drmGetNodeTypeFromFd(int fd)
{
struct stat sbuf;
int maj, min, type;
if (fstat(fd, &sbuf))
return -1;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode)) {
errno = EINVAL;
return -1;
}
type = drmGetMinorType(maj, min);
if (type == -1)
errno = ENODEV;
return type;
}
drm_public int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags,
int *prime_fd)
{
struct drm_prime_handle args;
int ret;
memclear(args);
args.fd = -1;
args.handle = handle;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
if (ret)
return ret;
*prime_fd = args.fd;
return 0;
}
drm_public int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle)
{
struct drm_prime_handle args;
int ret;
memclear(args);
args.fd = prime_fd;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
if (ret)
return ret;
*handle = args.handle;
return 0;
}
drm_public int drmCloseBufferHandle(int fd, uint32_t handle)
{
struct drm_gem_close args;
memclear(args);
args.handle = handle;
return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &args);
}
static char *drmGetMinorNameForFD(int fd, int type)
{
#ifdef __linux__
DIR *sysdir;
struct dirent *ent;
struct stat sbuf;
const char *name = drmGetMinorName(type);
int len;
char dev_name[64], buf[64];
int maj, min;
if (!name)
return NULL;
len = strlen(name);
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/drm", maj, min);
sysdir = opendir(buf);
if (!sysdir)
return NULL;
while ((ent = readdir(sysdir))) {
if (strncmp(ent->d_name, name, len) == 0) {
if (snprintf(dev_name, sizeof(dev_name), DRM_DIR_NAME "/%s",
ent->d_name) < 0)
return NULL;
closedir(sysdir);
return strdup(dev_name);
}
}
closedir(sysdir);
return NULL;
#elif defined(__FreeBSD__)
struct stat sbuf;
char dname[SPECNAMELEN];
const char *mname;
char name[SPECNAMELEN];
int id, maj, min, nodetype, i;
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
if (!devname_r(sbuf.st_rdev, S_IFCHR, dname, sizeof(dname)))
return NULL;
/* Handle both /dev/drm and /dev/dri
* FreeBSD on amd64/i386/powerpc external kernel modules create node in
* in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
* only device nodes in /dev/dri/ */
/* Get the node type represented by fd so we can deduce the target name */
nodetype = drmGetMinorType(maj, min);
if (nodetype == -1)
return (NULL);
mname = drmGetMinorName(type);
for (i = 0; i < SPECNAMELEN; i++) {
if (isalpha(dname[i]) == 0 && dname[i] != '/')
break;
}
if (dname[i] == '\0')
return (NULL);
id = (int)strtol(&dname[i], NULL, 10);
id -= drmGetMinorBase(nodetype);
snprintf(name, sizeof(name), DRM_DIR_NAME "/%s%d", mname,
id + drmGetMinorBase(type));
return strdup(name);
#else
struct stat sbuf;
char buf[PATH_MAX + 1];
const char *dev_name = drmGetDeviceName(type);
unsigned int maj, min;
int n;
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
if (!dev_name)
return NULL;
n = snprintf(buf, sizeof(buf), dev_name, DRM_DIR_NAME, min);
if (n == -1 || n >= sizeof(buf))
return NULL;
return strdup(buf);
#endif
}
drm_public char *drmGetPrimaryDeviceNameFromFd(int fd)
{
return drmGetMinorNameForFD(fd, DRM_NODE_PRIMARY);
}
drm_public char *drmGetRenderDeviceNameFromFd(int fd)
{
return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
}
#ifdef __linux__
static char * DRM_PRINTFLIKE(2, 3)
sysfs_uevent_get(const char *path, const char *fmt, ...)
{
char filename[PATH_MAX + 1], *key, *line = NULL, *value = NULL;
size_t size = 0, len;
ssize_t num;
va_list ap;
FILE *fp;
va_start(ap, fmt);
num = vasprintf(&key, fmt, ap);
va_end(ap);
len = num;
snprintf(filename, sizeof(filename), "%s/uevent", path);
fp = fopen(filename, "r");
if (!fp) {
free(key);
return NULL;
}
while ((num = getline(&line, &size, fp)) >= 0) {
if ((strncmp(line, key, len) == 0) && (line[len] == '=')) {
char *start = line + len + 1, *end = line + num - 1;
if (*end != '\n')
end++;
value = strndup(start, end - start);
break;
}
}
free(line);
fclose(fp);
free(key);
return value;
}
#endif
/* Little white lie to avoid major rework of the existing code */
#define DRM_BUS_VIRTIO 0x10
#ifdef __linux__
static int get_subsystem_type(const char *device_path)
{
char path[PATH_MAX + 1] = "";
char link[PATH_MAX + 1] = "";
char *name;
struct {
const char *name;
int bus_type;
} bus_types[] = {
{ "/pci", DRM_BUS_PCI },
{ "/usb", DRM_BUS_USB },
{ "/platform", DRM_BUS_PLATFORM },
{ "/spi", DRM_BUS_PLATFORM },
{ "/host1x", DRM_BUS_HOST1X },
{ "/virtio", DRM_BUS_VIRTIO },
};
strncpy(path, device_path, PATH_MAX);
strncat(path, "/subsystem", PATH_MAX);
if (readlink(path, link, PATH_MAX) < 0)
return -errno;
name = strrchr(link, '/');
if (!name)
return -EINVAL;
for (unsigned i = 0; i < ARRAY_SIZE(bus_types); i++) {
if (strncmp(name, bus_types[i].name, strlen(bus_types[i].name)) == 0)
return bus_types[i].bus_type;
}
return -EINVAL;
}
#endif
static int drmParseSubsystemType(int maj, int min)
{
#ifdef __linux__
char path[PATH_MAX + 1] = "";
char real_path[PATH_MAX + 1] = "";
int subsystem_type;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
subsystem_type = get_subsystem_type(path);
/* Try to get the parent (underlying) device type */
if (subsystem_type == DRM_BUS_VIRTIO) {
/* Assume virtio-pci on error */
if (!realpath(path, real_path))
return DRM_BUS_VIRTIO;
strncat(path, "/..", PATH_MAX);
subsystem_type = get_subsystem_type(path);
if (subsystem_type < 0)
return DRM_BUS_VIRTIO;
}
return subsystem_type;
#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
return DRM_BUS_PCI;
#else
#warning "Missing implementation of drmParseSubsystemType"
return -EINVAL;
#endif
}
#ifdef __linux__
static void
get_pci_path(int maj, int min, char *pci_path)
{
char path[PATH_MAX + 1], *term;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
if (!realpath(path, pci_path)) {
strcpy(pci_path, path);
return;
}
term = strrchr(pci_path, '/');
if (term && strncmp(term, "/virtio", 7) == 0)
*term = 0;
}
#endif
#ifdef __FreeBSD__
static int get_sysctl_pci_bus_info(int maj, int min, drmPciBusInfoPtr info)
{
char dname[SPECNAMELEN];
char sysctl_name[16];
char sysctl_val[256];
size_t sysctl_len;
int id, type, nelem;
unsigned int rdev, majmin, domain, bus, dev, func;
rdev = makedev(maj, min);
if (!devname_r(rdev, S_IFCHR, dname, sizeof(dname)))
return -EINVAL;
if (sscanf(dname, "drm/%d\n", &id) != 1)
return -EINVAL;
type = drmGetMinorType(maj, min);
if (type == -1)
return -EINVAL;
/* BUG: This above section is iffy, since it mandates that a driver will
* create both card and render node.
* If it does not, the next DRM device will create card#X and
* renderD#(128+X)-1.
* This is a possibility in FreeBSD but for now there is no good way for
* obtaining the info.
*/
switch (type) {
case DRM_NODE_PRIMARY:
break;
case DRM_NODE_CONTROL:
id -= 64;
break;
case DRM_NODE_RENDER:
id -= 128;
break;
}
if (id < 0)
return -EINVAL;
if (snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", id) <= 0)
return -EINVAL;
sysctl_len = sizeof(sysctl_val);
if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0))
return -EINVAL;
#define bus_fmt "pci:%04x:%02x:%02x.%u"
nelem = sscanf(sysctl_val, bus_fmt, &domain, &bus, &dev, &func);
if (nelem != 4)
return -EINVAL;
info->domain = domain;
info->bus = bus;
info->dev = dev;
info->func = func;
return 0;
}
#endif
static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
{
#ifdef __linux__
unsigned int domain, bus, dev, func;
char pci_path[PATH_MAX + 1], *value;
int num;
get_pci_path(maj, min, pci_path);
value = sysfs_uevent_get(pci_path, "PCI_SLOT_NAME");
if (!value)
return -ENOENT;
num = sscanf(value, "%04x:%02x:%02x.%1u", &domain, &bus, &dev, &func);
free(value);
if (num != 4)
return -EINVAL;
info->domain = domain;
info->bus = bus;
info->dev = dev;
info->func = func;
return 0;
#elif defined(__OpenBSD__) || defined(__DragonFly__)
struct drm_pciinfo pinfo;
int fd, type;
type = drmGetMinorType(maj, min);
if (type == -1)
return -ENODEV;
fd = drmOpenMinor(min, 0, type);
if (fd < 0)
return -errno;
if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
close(fd);
return -errno;
}
close(fd);
info->domain = pinfo.domain;
info->bus = pinfo.bus;
info->dev = pinfo.dev;
info->func = pinfo.func;
return 0;
#elif defined(__FreeBSD__)
return get_sysctl_pci_bus_info(maj, min, info);
#else
#warning "Missing implementation of drmParsePciBusInfo"
return -EINVAL;
#endif
}
drm_public int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b)
{
if (a == NULL || b == NULL)
return 0;
if (a->bustype != b->bustype)
return 0;
switch (a->bustype) {
case DRM_BUS_PCI:
return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0;
case DRM_BUS_USB:
return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)) == 0;
case DRM_BUS_PLATFORM:
return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo)) == 0;
case DRM_BUS_HOST1X:
return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo)) == 0;
default:
break;
}
return 0;
}
static int drmGetNodeType(const char *name)
{
if (strncmp(name, DRM_CONTROL_MINOR_NAME,
sizeof(DRM_CONTROL_MINOR_NAME ) - 1) == 0)
return DRM_NODE_CONTROL;
if (strncmp(name, DRM_RENDER_MINOR_NAME,
sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
return DRM_NODE_RENDER;
if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
return DRM_NODE_PRIMARY;
return -EINVAL;
}
static int drmGetMaxNodeName(void)
{
return sizeof(DRM_DIR_NAME) +
MAX3(sizeof(DRM_PRIMARY_MINOR_NAME),
sizeof(DRM_CONTROL_MINOR_NAME),
sizeof(DRM_RENDER_MINOR_NAME)) +
3 /* length of the node number */;
}
#ifdef __linux__
static int parse_separate_sysfs_files(int maj, int min,
drmPciDeviceInfoPtr device,
bool ignore_revision)
{
static const char *attrs[] = {
"revision", /* Older kernels are missing the file, so check for it first */
"vendor",
"device",
"subsystem_vendor",
"subsystem_device",
};
char path[PATH_MAX + 1], pci_path[PATH_MAX + 1];
unsigned int data[ARRAY_SIZE(attrs)];
FILE *fp;
int ret;
get_pci_path(maj, min, pci_path);
for (unsigned i = ignore_revision ? 1 : 0; i < ARRAY_SIZE(attrs); i++) {
if (snprintf(path, PATH_MAX, "%s/%s", pci_path, attrs[i]) < 0)
return -errno;
fp = fopen(path, "r");
if (!fp)
return -errno;
ret = fscanf(fp, "%x", &data[i]);
fclose(fp);
if (ret != 1)
return -errno;
}
device->revision_id = ignore_revision ? 0xff : data[0] & 0xff;
device->vendor_id = data[1] & 0xffff;
device->device_id = data[2] & 0xffff;
device->subvendor_id = data[3] & 0xffff;
device->subdevice_id = data[4] & 0xffff;
return 0;
}
static int parse_config_sysfs_file(int maj, int min,
drmPciDeviceInfoPtr device)
{
char path[PATH_MAX + 1], pci_path[PATH_MAX + 1];
unsigned char config[64];
int fd, ret;
get_pci_path(maj, min, pci_path);
if (snprintf(path, PATH_MAX, "%s/config", pci_path) < 0)
return -errno;
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
ret = read(fd, config, sizeof(config));
close(fd);
if (ret < 0)
return -errno;
device->vendor_id = config[0] | (config[1] << 8);
device->device_id = config[2] | (config[3] << 8);
device->revision_id = config[8];
device->subvendor_id = config[44] | (config[45] << 8);
device->subdevice_id = config[46] | (config[47] << 8);
return 0;
}
#endif
static int drmParsePciDeviceInfo(int maj, int min,
drmPciDeviceInfoPtr device,
uint32_t flags)
{
#ifdef __linux__
if (!(flags & DRM_DEVICE_GET_PCI_REVISION))
return parse_separate_sysfs_files(maj, min, device, true);
if (parse_separate_sysfs_files(maj, min, device, false))
return parse_config_sysfs_file(maj, min, device);
return 0;
#elif defined(__OpenBSD__) || defined(__DragonFly__)
struct drm_pciinfo pinfo;
int fd, type;
type = drmGetMinorType(maj, min);
if (type == -1)
return -ENODEV;
fd = drmOpenMinor(min, 0, type);
if (fd < 0)
return -errno;
if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
close(fd);
return -errno;
}
close(fd);
device->vendor_id = pinfo.vendor_id;
device->device_id = pinfo.device_id;
device->revision_id = pinfo.revision_id;
device->subvendor_id = pinfo.subvendor_id;
device->subdevice_id = pinfo.subdevice_id;
return 0;
#elif defined(__FreeBSD__)
drmPciBusInfo info;
struct pci_conf_io pc;
struct pci_match_conf patterns[1];
struct pci_conf results[1];
int fd, error;
if (get_sysctl_pci_bus_info(maj, min, &info) != 0)
return -EINVAL;
fd = open("/dev/pci", O_RDONLY, 0);
if (fd < 0)
return -errno;
bzero(&patterns, sizeof(patterns));
patterns[0].pc_sel.pc_domain = info.domain;
patterns[0].pc_sel.pc_bus = info.bus;
patterns[0].pc_sel.pc_dev = info.dev;
patterns[0].pc_sel.pc_func = info.func;
patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS
| PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC;
bzero(&pc, sizeof(struct pci_conf_io));
pc.num_patterns = 1;
pc.pat_buf_len = sizeof(patterns);
pc.patterns = patterns;
pc.match_buf_len = sizeof(results);
pc.matches = results;
if (ioctl(fd, PCIOCGETCONF, &pc) || pc.status == PCI_GETCONF_ERROR) {
error = errno;
close(fd);
return -error;
}
close(fd);
device->vendor_id = results[0].pc_vendor;
device->device_id = results[0].pc_device;
device->subvendor_id = results[0].pc_subvendor;
device->subdevice_id = results[0].pc_subdevice;
device->revision_id = results[0].pc_revid;
return 0;
#else
#warning "Missing implementation of drmParsePciDeviceInfo"
return -EINVAL;
#endif
}
static void drmFreePlatformDevice(drmDevicePtr device)
{
if (device->deviceinfo.platform) {
if (device->deviceinfo.platform->compatible) {
char **compatible = device->deviceinfo.platform->compatible;
while (*compatible) {
free(*compatible);
compatible++;
}
free(device->deviceinfo.platform->compatible);
}
}
}
static void drmFreeHost1xDevice(drmDevicePtr device)
{
if (device->deviceinfo.host1x) {
if (device->deviceinfo.host1x->compatible) {
char **compatible = device->deviceinfo.host1x->compatible;
while (*compatible) {
free(*compatible);
compatible++;
}
free(device->deviceinfo.host1x->compatible);
}
}
}
drm_public void drmFreeDevice(drmDevicePtr *device)
{
if (device == NULL)
return;
if (*device) {
switch ((*device)->bustype) {
case DRM_BUS_PLATFORM:
drmFreePlatformDevice(*device);
break;
case DRM_BUS_HOST1X:
drmFreeHost1xDevice(*device);
break;
}
}
free(*device);
*device = NULL;
}
drm_public void drmFreeDevices(drmDevicePtr devices[], int count)
{
int i;
if (devices == NULL)
return;
for (i = 0; i < count; i++)
if (devices[i])
drmFreeDevice(&devices[i]);
}
static drmDevicePtr drmDeviceAlloc(unsigned int type, const char *node,
size_t bus_size, size_t device_size,
char **ptrp)
{
size_t max_node_length, extra, size;
drmDevicePtr device;
unsigned int i;
char *ptr;
max_node_length = ALIGN(drmGetMaxNodeName(), sizeof(void *));
extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
size = sizeof(*device) + extra + bus_size + device_size;
device = calloc(1, size);
if (!device)
return NULL;
device->available_nodes = 1 << type;
ptr = (char *)device + sizeof(*device);
device->nodes = (char **)ptr;
ptr += DRM_NODE_MAX * sizeof(void *);
for (i = 0; i < DRM_NODE_MAX; i++) {
device->nodes[i] = ptr;
ptr += max_node_length;
}
memcpy(device->nodes[type], node, max_node_length);
*ptrp = ptr;
return device;
}
static int drmProcessPciDevice(drmDevicePtr *device,
const char *node, int node_type,
int maj, int min, bool fetch_deviceinfo,
uint32_t flags)
{
drmDevicePtr dev;
char *addr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmPciBusInfo),
sizeof(drmPciDeviceInfo), &addr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_PCI;
dev->businfo.pci = (drmPciBusInfoPtr)addr;
ret = drmParsePciBusInfo(maj, min, dev->businfo.pci);
if (ret)
goto free_device;
// Fetch the device info if the user has requested it
if (fetch_deviceinfo) {
addr += sizeof(drmPciBusInfo);
dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags);
if (ret)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
#ifdef __linux__
static int drm_usb_dev_path(int maj, int min, char *path, size_t len)
{
char *value, *tmp_path, *slash;
bool usb_device, usb_interface;
snprintf(path, len, "/sys/dev/char/%d:%d/device", maj, min);
value = sysfs_uevent_get(path, "DEVTYPE");
if (!value)
return -ENOENT;
usb_device = strcmp(value, "usb_device") == 0;
usb_interface = strcmp(value, "usb_interface") == 0;
free(value);
if (usb_device)
return 0;
if (!usb_interface)
return -ENOTSUP;
/* The parent of a usb_interface is a usb_device */
tmp_path = realpath(path, NULL);
if (!tmp_path)
return -errno;
slash = strrchr(tmp_path, '/');
if (!slash) {
free(tmp_path);
return -EINVAL;
}
*slash = '\0';
if (snprintf(path, len, "%s", tmp_path) >= (int)len) {
free(tmp_path);
return -EINVAL;
}
free(tmp_path);
return 0;
}
#endif
static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *value;
unsigned int bus, dev;
int ret;
ret = drm_usb_dev_path(maj, min, path, sizeof(path));
if (ret < 0)
return ret;
value = sysfs_uevent_get(path, "BUSNUM");
if (!value)
return -ENOENT;
ret = sscanf(value, "%03u", &bus);
free(value);
if (ret <= 0)
return -errno;
value = sysfs_uevent_get(path, "DEVNUM");
if (!value)
return -ENOENT;
ret = sscanf(value, "%03u", &dev);
free(value);
if (ret <= 0)
return -errno;
info->bus = bus;
info->dev = dev;
return 0;
#else
#warning "Missing implementation of drmParseUsbBusInfo"
return -EINVAL;
#endif
}
static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info)
{
#ifdef __linux__
char path[PATH_MAX + 1], *value;
unsigned int vendor, product;
int ret;
ret = drm_usb_dev_path(maj, min, path, sizeof(path));
if (ret < 0)
return ret;
value = sysfs_uevent_get(path, "PRODUCT");
if (!value)
return -ENOENT;
ret = sscanf(value, "%x/%x", &vendor, &product);
free(value);
if (ret <= 0)
return -errno;
info->vendor = vendor;
info->product = product;
return 0;
#else
#warning "Missing implementation of drmParseUsbDeviceInfo"
return -EINVAL;
#endif
}
static int drmProcessUsbDevice(drmDevicePtr *device, const char *node,
int node_type, int maj, int min,
bool fetch_deviceinfo, uint32_t flags)
{
drmDevicePtr dev;
char *ptr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmUsbBusInfo),
sizeof(drmUsbDeviceInfo), &ptr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_USB;
dev->businfo.usb = (drmUsbBusInfoPtr)ptr;
ret = drmParseUsbBusInfo(maj, min, dev->businfo.usb);
if (ret < 0)
goto free_device;
if (fetch_deviceinfo) {
ptr += sizeof(drmUsbBusInfo);
dev->deviceinfo.usb = (drmUsbDeviceInfoPtr)ptr;
ret = drmParseUsbDeviceInfo(maj, min, dev->deviceinfo.usb);
if (ret < 0)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
static int drmParseOFBusInfo(int maj, int min, char *fullname)
{
#ifdef __linux__
char path[PATH_MAX + 1], *name, *tmp_name;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
name = sysfs_uevent_get(path, "OF_FULLNAME");
tmp_name = name;
if (!name) {
/* If the device lacks OF data, pick the MODALIAS info */
name = sysfs_uevent_get(path, "MODALIAS");
if (!name)
return -ENOENT;
/* .. and strip the MODALIAS=[platform,usb...]: part. */
tmp_name = strrchr(name, ':');
if (!tmp_name) {
free(name);
return -ENOENT;
}
tmp_name++;
}
strncpy(fullname, tmp_name, DRM_PLATFORM_DEVICE_NAME_LEN);
fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
free(name);
return 0;
#else
#warning "Missing implementation of drmParseOFBusInfo"
return -EINVAL;
#endif
}
static int drmParseOFDeviceInfo(int maj, int min, char ***compatible)
{
#ifdef __linux__
char path[PATH_MAX + 1], *value, *tmp_name;
unsigned int count, i;
int err;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
if (value) {
sscanf(value, "%u", &count);
free(value);
} else {
/* Assume one entry if the device lack OF data */
count = 1;
}
*compatible = calloc(count + 1, sizeof(char *));
if (!*compatible)
return -ENOMEM;
for (i = 0; i < count; i++) {
value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
tmp_name = value;
if (!value) {
/* If the device lacks OF data, pick the MODALIAS info */
value = sysfs_uevent_get(path, "MODALIAS");
if (!value) {
err = -ENOENT;
goto free;
}
/* .. and strip the MODALIAS=[platform,usb...]: part. */
tmp_name = strrchr(value, ':');
if (!tmp_name) {
free(value);
return -ENOENT;
}
tmp_name = strdup(tmp_name + 1);
free(value);
}
(*compatible)[i] = tmp_name;
}
return 0;
free:
while (i--)
free((*compatible)[i]);
free(*compatible);
return err;
#else
#warning "Missing implementation of drmParseOFDeviceInfo"
return -EINVAL;
#endif
}
static int drmProcessPlatformDevice(drmDevicePtr *device,
const char *node, int node_type,
int maj, int min, bool fetch_deviceinfo,
uint32_t flags)
{
drmDevicePtr dev;
char *ptr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmPlatformBusInfo),
sizeof(drmPlatformDeviceInfo), &ptr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_PLATFORM;
dev->businfo.platform = (drmPlatformBusInfoPtr)ptr;
ret = drmParseOFBusInfo(maj, min, dev->businfo.platform->fullname);
if (ret < 0)
goto free_device;
if (fetch_deviceinfo) {
ptr += sizeof(drmPlatformBusInfo);
dev->deviceinfo.platform = (drmPlatformDeviceInfoPtr)ptr;
ret = drmParseOFDeviceInfo(maj, min, &dev->deviceinfo.platform->compatible);
if (ret < 0)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
static int drmProcessHost1xDevice(drmDevicePtr *device,
const char *node, int node_type,
int maj, int min, bool fetch_deviceinfo,
uint32_t flags)
{
drmDevicePtr dev;
char *ptr;
int ret;
dev = drmDeviceAlloc(node_type, node, sizeof(drmHost1xBusInfo),
sizeof(drmHost1xDeviceInfo), &ptr);
if (!dev)
return -ENOMEM;
dev->bustype = DRM_BUS_HOST1X;
dev->businfo.host1x = (drmHost1xBusInfoPtr)ptr;
ret = drmParseOFBusInfo(maj, min, dev->businfo.host1x->fullname);
if (ret < 0)
goto free_device;
if (fetch_deviceinfo) {
ptr += sizeof(drmHost1xBusInfo);
dev->deviceinfo.host1x = (drmHost1xDeviceInfoPtr)ptr;
ret = drmParseOFDeviceInfo(maj, min, &dev->deviceinfo.host1x->compatible);
if (ret < 0)
goto free_device;
}
*device = dev;
return 0;
free_device:
free(dev);
return ret;
}
static int
process_device(drmDevicePtr *device, const char *d_name,
int req_subsystem_type,
bool fetch_deviceinfo, uint32_t flags)
{
struct stat sbuf;
char node[PATH_MAX + 1];
int node_type, subsystem_type;
unsigned int maj, min;
node_type = drmGetNodeType(d_name);
if (node_type < 0)
return -1;
snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, d_name);
if (stat(node, &sbuf))
return -1;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return -1;
subsystem_type = drmParseSubsystemType(maj, min);
if (req_subsystem_type != -1 && req_subsystem_type != subsystem_type)
return -1;
switch (subsystem_type) {
case DRM_BUS_PCI:
case DRM_BUS_VIRTIO:
return drmProcessPciDevice(device, node, node_type, maj, min,
fetch_deviceinfo, flags);
case DRM_BUS_USB:
return drmProcessUsbDevice(device, node, node_type, maj, min,
fetch_deviceinfo, flags);
case DRM_BUS_PLATFORM:
return drmProcessPlatformDevice(device, node, node_type, maj, min,
fetch_deviceinfo, flags);
case DRM_BUS_HOST1X:
return drmProcessHost1xDevice(device, node, node_type, maj, min,
fetch_deviceinfo, flags);
default:
return -1;
}
}
/* Consider devices located on the same bus as duplicate and fold the respective
* entries into a single one.
*
* Note: this leaves "gaps" in the array, while preserving the length.
*/
static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
{
int node_type, i, j;
for (i = 0; i < count; i++) {
for (j = i + 1; j < count; j++) {
if (drmDevicesEqual(local_devices[i], local_devices[j])) {
local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
node_type = log2_int(local_devices[j]->available_nodes);
memcpy(local_devices[i]->nodes[node_type],
local_devices[j]->nodes[node_type], drmGetMaxNodeName());
drmFreeDevice(&local_devices[j]);
}
}
}
}
/* Check that the given flags are valid returning 0 on success */
static int
drm_device_validate_flags(uint32_t flags)
{
return (flags & ~DRM_DEVICE_GET_PCI_REVISION);
}
static bool
drm_device_has_rdev(drmDevicePtr device, dev_t find_rdev)
{
struct stat sbuf;
for (int i = 0; i < DRM_NODE_MAX; i++) {
if (device->available_nodes & 1 << i) {
if (stat(device->nodes[i], &sbuf) == 0 &&
sbuf.st_rdev == find_rdev)
return true;
}
}
return false;
}
/*
* The kernel drm core has a number of places that assume maximum of
* 3x64 devices nodes. That's 64 for each of primary, control and
* render nodes. Rounded it up to 256 for simplicity.
*/
#define MAX_DRM_NODES 256
/**
* Get information about a device from its dev_t identifier
*
* \param find_rdev dev_t identifier of the device
* \param flags feature/behaviour bitmask
* \param device the address of a drmDevicePtr where the information
* will be allocated in stored
*
* \return zero on success, negative error code otherwise.
*/
drm_public int drmGetDeviceFromDevId(dev_t find_rdev, uint32_t flags, drmDevicePtr *device)
{
#ifdef __OpenBSD__
/*
* DRI device nodes on OpenBSD are not in their own directory, they reside
* in /dev along with a large number of statically generated /dev nodes.
* Avoid stat'ing all of /dev needlessly by implementing this custom path.
*/
drmDevicePtr d;
char node[PATH_MAX + 1];
const char *dev_name;
int node_type, subsystem_type;
int maj, min, n, ret;
if (device == NULL)
return -EINVAL;
maj = major(find_rdev);
min = minor(find_rdev);
if (!drmNodeIsDRM(maj, min))
return -EINVAL;
node_type = drmGetMinorType(maj, min);
if (node_type == -1)
return -ENODEV;
dev_name = drmGetDeviceName(node_type);
if (!dev_name)
return -EINVAL;
n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min);
if (n == -1 || n >= PATH_MAX)
return -errno;
if (stat(node, &sbuf))
return -EINVAL;
subsystem_type = drmParseSubsystemType(maj, min);
if (subsystem_type != DRM_BUS_PCI)
return -ENODEV;
ret = drmProcessPciDevice(&d, node, node_type, maj, min, true, flags);
if (ret)
return ret;
*device = d;
return 0;
#else
drmDevicePtr local_devices[MAX_DRM_NODES];
drmDevicePtr d;
DIR *sysdir;
struct dirent *dent;
int subsystem_type;
int maj, min;
int ret, i, node_count;
if (drm_device_validate_flags(flags))
return -EINVAL;
if (device == NULL)
return -EINVAL;
maj = major(find_rdev);
min = minor(find_rdev);
if (!drmNodeIsDRM(maj, min))
return -EINVAL;
subsystem_type = drmParseSubsystemType(maj, min);
if (subsystem_type < 0)
return subsystem_type;
sysdir = opendir(DRM_DIR_NAME);
if (!sysdir)
return -errno;
i = 0;
while ((dent = readdir(sysdir))) {
ret = process_device(&d, dent->d_name, subsystem_type, true, flags);
if (ret)
continue;
if (i >= MAX_DRM_NODES) {
fprintf(stderr, "More than %d drm nodes detected. "
"Please report a bug - that should not happen.\n"
"Skipping extra nodes\n", MAX_DRM_NODES);
break;
}
local_devices[i] = d;
i++;
}
node_count = i;
drmFoldDuplicatedDevices(local_devices, node_count);
*device = NULL;
for (i = 0; i < node_count; i++) {
if (!local_devices[i])
continue;
if (drm_device_has_rdev(local_devices[i], find_rdev))
*device = local_devices[i];
else
drmFreeDevice(&local_devices[i]);
}
closedir(sysdir);
if (*device == NULL)
return -ENODEV;
return 0;
#endif
}
/**
* Get information about the opened drm device
*
* \param fd file descriptor of the drm device
* \param flags feature/behaviour bitmask
* \param device the address of a drmDevicePtr where the information
* will be allocated in stored
*
* \return zero on success, negative error code otherwise.
*
* \note Unlike drmGetDevice it does not retrieve the pci device revision field
* unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
*/
drm_public int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
{
struct stat sbuf;
if (fd == -1)
return -EINVAL;
if (fstat(fd, &sbuf))
return -errno;
if (!S_ISCHR(sbuf.st_mode))
return -EINVAL;
return drmGetDeviceFromDevId(sbuf.st_rdev, flags, device);
}
/**
* Get information about the opened drm device
*
* \param fd file descriptor of the drm device
* \param device the address of a drmDevicePtr where the information
* will be allocated in stored
*
* \return zero on success, negative error code otherwise.
*/
drm_public int drmGetDevice(int fd, drmDevicePtr *device)
{
return drmGetDevice2(fd, DRM_DEVICE_GET_PCI_REVISION, device);
}
/**
* Get drm devices on the system
*
* \param flags feature/behaviour bitmask
* \param devices the array of devices with drmDevicePtr elements
* can be NULL to get the device number first
* \param max_devices the maximum number of devices for the array
*
* \return on error - negative error code,
* if devices is NULL - total number of devices available on the system,
* alternatively the number of devices stored in devices[], which is
* capped by the max_devices.
*
* \note Unlike drmGetDevices it does not retrieve the pci device revision field
* unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
*/
drm_public int drmGetDevices2(uint32_t flags, drmDevicePtr devices[],
int max_devices)
{
drmDevicePtr local_devices[MAX_DRM_NODES];
drmDevicePtr device;
DIR *sysdir;
struct dirent *dent;
int ret, i, node_count, device_count;
if (drm_device_validate_flags(flags))
return -EINVAL;
sysdir = opendir(DRM_DIR_NAME);
if (!sysdir)
return -errno;
i = 0;
while ((dent = readdir(sysdir))) {
ret = process_device(&device, dent->d_name, -1, devices != NULL, flags);
if (ret)
continue;
if (i >= MAX_DRM_NODES) {
fprintf(stderr, "More than %d drm nodes detected. "
"Please report a bug - that should not happen.\n"
"Skipping extra nodes\n", MAX_DRM_NODES);
break;
}
local_devices[i] = device;
i++;
}
node_count = i;
drmFoldDuplicatedDevices(local_devices, node_count);
device_count = 0;
for (i = 0; i < node_count; i++) {
if (!local_devices[i])
continue;
if ((devices != NULL) && (device_count < max_devices))
devices[device_count] = local_devices[i];
else
drmFreeDevice(&local_devices[i]);
device_count++;
}
closedir(sysdir);
if (devices != NULL)
return MIN2(device_count, max_devices);
return device_count;
}
/**
* Get drm devices on the system
*
* \param devices the array of devices with drmDevicePtr elements
* can be NULL to get the device number first
* \param max_devices the maximum number of devices for the array
*
* \return on error - negative error code,
* if devices is NULL - total number of devices available on the system,
* alternatively the number of devices stored in devices[], which is
* capped by the max_devices.
*/
drm_public int drmGetDevices(drmDevicePtr devices[], int max_devices)
{
return drmGetDevices2(DRM_DEVICE_GET_PCI_REVISION, devices, max_devices);
}
drm_public char *drmGetDeviceNameFromFd2(int fd)
{
#ifdef __linux__
struct stat sbuf;
char path[PATH_MAX + 1], *value;
unsigned int maj, min;
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d", maj, min);
value = sysfs_uevent_get(path, "DEVNAME");
if (!value)
return NULL;
snprintf(path, sizeof(path), "/dev/%s", value);
free(value);
return strdup(path);
#elif defined(__FreeBSD__)
return drmGetDeviceNameFromFd(fd);
#else
struct stat sbuf;
char node[PATH_MAX + 1];
const char *dev_name;
int node_type;
int maj, min, n;
if (fstat(fd, &sbuf))
return NULL;
maj = major(sbuf.st_rdev);
min = minor(sbuf.st_rdev);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
node_type = drmGetMinorType(maj, min);
if (node_type == -1)
return NULL;
dev_name = drmGetDeviceName(node_type);
if (!dev_name)
return NULL;
n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min);
if (n == -1 || n >= PATH_MAX)
return NULL;
return strdup(node);
#endif
}
drm_public int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle)
{
struct drm_syncobj_create args;
int ret;
memclear(args);
args.flags = flags;
args.handle = 0;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);
if (ret)
return ret;
*handle = args.handle;
return 0;
}
drm_public int drmSyncobjDestroy(int fd, uint32_t handle)
{
struct drm_syncobj_destroy args;
memclear(args);
args.handle = handle;
return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
}
drm_public int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd)
{
struct drm_syncobj_handle args;
int ret;
memclear(args);
args.fd = -1;
args.handle = handle;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
if (ret)
return ret;
*obj_fd = args.fd;
return 0;
}
drm_public int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle)
{
struct drm_syncobj_handle args;
int ret;
memclear(args);
args.fd = obj_fd;
args.handle = 0;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
if (ret)
return ret;
*handle = args.handle;
return 0;
}
drm_public int drmSyncobjImportSyncFile(int fd, uint32_t handle,
int sync_file_fd)
{
struct drm_syncobj_handle args;
memclear(args);
args.fd = sync_file_fd;
args.handle = handle;
args.flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE;
return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
}
drm_public int drmSyncobjExportSyncFile(int fd, uint32_t handle,
int *sync_file_fd)
{
struct drm_syncobj_handle args;
int ret;
memclear(args);
args.fd = -1;
args.handle = handle;
args.flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
if (ret)
return ret;
*sync_file_fd = args.fd;
return 0;
}
drm_public int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles,
int64_t timeout_nsec, unsigned flags,
uint32_t *first_signaled)
{
struct drm_syncobj_wait args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.timeout_nsec = timeout_nsec;
args.count_handles = num_handles;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &args);
if (ret < 0)
return -errno;
if (first_signaled)
*first_signaled = args.first_signaled;
return ret;
}
drm_public int drmSyncobjReset(int fd, const uint32_t *handles,
uint32_t handle_count)
{
struct drm_syncobj_array args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.count_handles = handle_count;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &args);
return ret;
}
drm_public int drmSyncobjSignal(int fd, const uint32_t *handles,
uint32_t handle_count)
{
struct drm_syncobj_array args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.count_handles = handle_count;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &args);
return ret;
}
drm_public int drmSyncobjTimelineSignal(int fd, const uint32_t *handles,
uint64_t *points, uint32_t handle_count)
{
struct drm_syncobj_timeline_array args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.points = (uintptr_t)points;
args.count_handles = handle_count;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
return ret;
}
drm_public int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points,
unsigned num_handles,
int64_t timeout_nsec, unsigned flags,
uint32_t *first_signaled)
{
struct drm_syncobj_timeline_wait args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.points = (uintptr_t)points;
args.timeout_nsec = timeout_nsec;
args.count_handles = num_handles;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
if (ret < 0)
return -errno;
if (first_signaled)
*first_signaled = args.first_signaled;
return ret;
}
drm_public int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points,
uint32_t handle_count)
{
struct drm_syncobj_timeline_array args;
int ret;
memclear(args);
args.handles = (uintptr_t)handles;
args.points = (uintptr_t)points;
args.count_handles = handle_count;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
if (ret)
return ret;
return 0;
}
drm_public int drmSyncobjQuery2(int fd, uint32_t *handles, uint64_t *points,
uint32_t handle_count, uint32_t flags)
{
struct drm_syncobj_timeline_array args;
memclear(args);
args.handles = (uintptr_t)handles;
args.points = (uintptr_t)points;
args.count_handles = handle_count;
args.flags = flags;
return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
}
drm_public int drmSyncobjTransfer(int fd,
uint32_t dst_handle, uint64_t dst_point,
uint32_t src_handle, uint64_t src_point,
uint32_t flags)
{
struct drm_syncobj_transfer args;
int ret;
memclear(args);
args.src_handle = src_handle;
args.dst_handle = dst_handle;
args.src_point = src_point;
args.dst_point = dst_point;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
return ret;
}
static char *
drmGetFormatModifierFromSimpleTokens(uint64_t modifier)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(drm_format_modifier_table); i++) {
if (drm_format_modifier_table[i].modifier == modifier)
return strdup(drm_format_modifier_table[i].modifier_name);
}
return NULL;
}
/** Retrieves a human-readable representation of a vendor (as a string) from
* the format token modifier
*
* \param modifier the format modifier token
* \return a char pointer to the human-readable form of the vendor. Caller is
* responsible for freeing it.
*/
drm_public char *
drmGetFormatModifierVendor(uint64_t modifier)
{
unsigned int i;
uint8_t vendor = fourcc_mod_get_vendor(modifier);
for (i = 0; i < ARRAY_SIZE(drm_format_modifier_vendor_table); i++) {
if (drm_format_modifier_vendor_table[i].vendor == vendor)
return strdup(drm_format_modifier_vendor_table[i].vendor_name);
}
return NULL;
}
/** Retrieves a human-readable representation string from a format token
* modifier
*
* If the dedicated function was not able to extract a valid name or searching
* the format modifier was not in the table, this function would return NULL.
*
* \param modifier the token format
* \return a malloc'ed string representation of the modifier. Caller is
* responsible for freeing the string returned.
*
*/
drm_public char *
drmGetFormatModifierName(uint64_t modifier)
{
uint8_t vendorid = fourcc_mod_get_vendor(modifier);
char *modifier_found = NULL;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(modifier_format_vendor_table); i++) {
if (modifier_format_vendor_table[i].vendor == vendorid)
modifier_found = modifier_format_vendor_table[i].vendor_cb(modifier);
}
if (!modifier_found)
return drmGetFormatModifierFromSimpleTokens(modifier);
return modifier_found;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mutouooz/third_party_libdrm.git
[email protected]:mutouooz/third_party_libdrm.git
mutouooz
third_party_libdrm
third_party_libdrm
master

搜索帮助