├── .gitignore ├── subprojects ├── libdrm.wrap └── json-c.wrap ├── modifiers.h ├── drm_info.h ├── meson_options.txt ├── tables.h ├── .build.yml ├── drm_info.1.scd ├── main.c ├── LICENSE ├── fourcc.py ├── README.md ├── meson.build ├── modifiers.c ├── json.c └── pretty.c /.gitignore: -------------------------------------------------------------------------------- 1 | subprojects/packagecache 2 | subprojects/json-c-0.13.1 3 | subprojects/libdrm 4 | -------------------------------------------------------------------------------- /subprojects/libdrm.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://gitlab.freedesktop.org/mesa/drm.git 3 | revision = libdrm-2.4.113 4 | depth = 1 5 | -------------------------------------------------------------------------------- /modifiers.h: -------------------------------------------------------------------------------- 1 | #ifndef MODIFIERS_H 2 | #define MODIFIERS_H 3 | 4 | #include 5 | 6 | void print_modifier(uint64_t modifier); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /drm_info.h: -------------------------------------------------------------------------------- 1 | #ifndef DRM_INFO_H 2 | #define DRM_INFO_H 3 | 4 | struct json_object; 5 | 6 | struct json_object *drm_info(char *paths[]); 7 | void print_drm(struct json_object *obj); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('libpci', 2 | type: 'feature', 3 | value: 'auto', 4 | description: 'Print PCI device names via libpci' 5 | ) 6 | option('man-pages', 7 | type: 'feature', 8 | value: 'auto', 9 | description: 'Generate and install man pages' 10 | ) 11 | -------------------------------------------------------------------------------- /tables.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLES_H 2 | #define TABLES_H 3 | 4 | #include 5 | 6 | /* The implementation of these functions are generated by fourcc.py */ 7 | 8 | const char *format_str(uint32_t format); 9 | const char *basic_modifier_str(uint64_t modifier); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /.build.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | packages: 3 | - libdrm-dev 4 | - meson 5 | - pkgconf 6 | - python3-setuptools 7 | - scdoc 8 | sources: 9 | - https://github.com/ascent12/drm_info 10 | tasks: 11 | - setup: | 12 | cd drm_info 13 | meson subprojects download 14 | meson build 15 | - build: | 16 | cd drm_info 17 | ninja -C build 18 | -------------------------------------------------------------------------------- /subprojects/json-c.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = json-c-0.16 3 | source_url = https://s3.amazonaws.com/json-c_releases/releases/json-c-0.16.tar.gz 4 | source_filename = json-c-0.16.tar.gz 5 | source_hash = 8e45ac8f96ec7791eaf3bb7ee50e9c2100bbbc87b8d0f1d030c5ba8a0288d96b 6 | patch_filename = json-c_0.16-3_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/json-c_0.16-3/get_patch 8 | patch_hash = 6e712826ba1e6e1b6d21c6157a764984fedc6b594c9f92443498b972b6377a94 9 | wrapdb_version = 0.16-3 10 | 11 | [provide] 12 | json-c = json_c_dep 13 | -------------------------------------------------------------------------------- /drm_info.1.scd: -------------------------------------------------------------------------------- 1 | drm_info(1) 2 | 3 | # NAME 4 | 5 | drm_info - dump information about DRM devices 6 | 7 | # SYNOPSIS 8 | 9 | *drm_info* [-j] [device]... 10 | 11 | # DESCRIPTION 12 | 13 | *drm_info* is a small utility to dump information about DRM devices. 14 | 15 | _device_ must be a path to a DRM device, e.g. "/dev/dri/card0". When omitted, 16 | all available devices are printed. 17 | 18 | # OPTIONS 19 | 20 | *-j* 21 | Print information in JSON format. By default, the output will be 22 | pretty-printed in a human-readable format. 23 | 24 | # AUTHORS 25 | 26 | Maintained by Scott Anderson . For more information about 27 | drm_info development, see . 28 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "drm_info.h" 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | bool json = false; 15 | 16 | int opt; 17 | while ((opt = getopt(argc, argv, "j")) != -1) { 18 | switch (opt) { 19 | case 'j': 20 | json = true; 21 | break; 22 | default: 23 | fprintf(stderr, "usage: drm_info [-j] [--] [path]...\n"); 24 | exit(opt == '?' ? EXIT_SUCCESS : EXIT_FAILURE); 25 | } 26 | } 27 | 28 | struct json_object *obj = drm_info(&argv[optind]); 29 | if (!obj) { 30 | exit(EXIT_FAILURE); 31 | } 32 | if (json) { 33 | json_object_to_fd(STDOUT_FILENO, obj, 34 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED); 35 | } else { 36 | print_drm(obj); 37 | } 38 | json_object_put(obj); 39 | return EXIT_SUCCESS; 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Scott Anderson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /fourcc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import re 5 | 6 | def case_print(f, c, s): 7 | f.write('\tcase {}:\n'.format(c)) 8 | f.write('\t\treturn "{}";\n'.format(s)) 9 | 10 | info = { 11 | 'fmt': r'^#define (\w+)\s*(?:\\$\s*)?fourcc_code', 12 | 'basic_pre': r'^#define (I915_FORMAT_MOD_\w+)\b', 13 | 'basic_post': r'^#define (DRM_FORMAT_MOD_(?:INVALID|LINEAR|SAMSUNG|QCOM|VIVANTE|NVIDIA|BROADCOM|ALLWINNER)\w*)\s', 14 | } 15 | 16 | with open(sys.argv[1], 'r') as f: 17 | data = f.read() 18 | for k, v in info.items(): 19 | info[k] = re.findall(v, data, flags=re.M) 20 | 21 | with open(sys.argv[2], 'w') as f: 22 | f.write('''\ 23 | #include 24 | #include 25 | 26 | #include "tables.h" 27 | 28 | const char *format_str(uint32_t format) 29 | { 30 | switch (format) { 31 | case DRM_FORMAT_INVALID: 32 | return "INVALID"; 33 | ''') 34 | 35 | for ident in info['fmt']: 36 | case_print(f, ident, ident[len('DRM_FORMAT_'):]) 37 | 38 | f.write('''\ 39 | default: 40 | return "Unknown"; 41 | } 42 | } 43 | 44 | const char *basic_modifier_str(uint64_t modifier) 45 | { 46 | switch (modifier) { 47 | ''') 48 | 49 | for ident in info['basic_pre'] + info['basic_post']: 50 | case_print(f, ident, ident) 51 | 52 | f.write('''\ 53 | default: 54 | return "Unknown"; 55 | } 56 | } 57 | ''') 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drm_info 2 | 3 | [![builds.sr.ht status](https://builds.sr.ht/~ascent/drm_info/commits.svg)](https://builds.sr.ht/~ascent/drm_info/commits?) 4 | 5 | > ### ⚠️ Migrated to gitlab.freedesktop.org 6 | > 7 | > This project has [migrated to gitlab.freedesktop.org](https://gitlab.freedesktop.org/emersion/drm_info). 8 | 9 | Small utility to dump info about DRM devices. 10 | 11 | Requires libdrm and json-c. 12 | 13 | ## Building 14 | 15 | Build with 16 | ``` 17 | meson build 18 | cd build 19 | ninja 20 | sudo ninja install 21 | ``` 22 | 23 | If you don't have the minimum json-c version (0.13.0), meson will automatically 24 | download and compile it for you. If you don't want this, run the first meson 25 | command with 26 | ``` 27 | meson build --wrap-mode nofallback 28 | ``` 29 | 30 | ## Usage 31 | 32 | ``` 33 | drm_info [-j] [--] [path]... 34 | ``` 35 | - `-j` - Output info in JSON. Otherwise the output is pretty-printed. 36 | - `path` - Zero or more paths to a DRM device to print info about, e.g. 37 | `/dev/dri/card0`. If no paths are given, all devices found in 38 | `/dev/dri/card*` are printed. 39 | 40 | ## DRM database 41 | 42 | [drmdb](https://drmdb.emersion.fr) is a database of Direct Rendering Manager 43 | dumps. This database is used to keep track of GPUs and DRM driver features 44 | support. 45 | 46 | Please help us gather more data! You can do so by uploading DRM information 47 | from your GPU. 48 | 49 | ``` 50 | drm_info -j | curl -X POST -d @- https://drmdb.emersion.fr/submit 51 | ``` 52 | 53 | This will upload information about your GPUs, your GPU drivers and your 54 | screens. 55 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('drm_info', 'c', 2 | version: '2.3.0', 3 | license: 'MIT', 4 | meson_version: '>=0.49.0', 5 | default_options: [ 6 | 'c_std=c11', 7 | 'warning_level=2', 8 | ], 9 | ) 10 | 11 | warning('Heads up! This project has migrated to gitlab.freedesktop.org:') 12 | warning('https://gitlab.freedesktop.org/emersion/drm_info') 13 | 14 | cc = meson.get_compiler('c') 15 | 16 | add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') 17 | 18 | jsonc = dependency('json-c', version: '>=0.14', fallback: ['json-c', 'json_c_dep']) 19 | libpci = dependency('libpci', required: get_option('libpci')) 20 | libdrm = dependency('libdrm', 21 | fallback: ['libdrm', 'ext_libdrm'], 22 | default_options: [ 23 | 'intel=disabled', 24 | 'radeon=disabled', 25 | 'amdgpu=disabled', 26 | 'nouveau=disabled', 27 | 'vmwgfx=disabled', 28 | 'omap=disabled', 29 | 'exynos=disabled', 30 | 'freedreno=disabled', 31 | 'tegra=disabled', 32 | 'vc4=disabled', 33 | 'etnaviv=disabled', 34 | 'cairo-tests=disabled', 35 | 'man-pages=disabled', 36 | 'valgrind=disabled', 37 | ], 38 | ) 39 | 40 | inc = [] 41 | # libdrm pretty consistently pulls in the linux userspace API headers. 42 | # We want a new libdrm to get all of the #defines in those headers, but 43 | # we don't actually need to link against a new version of libdrm itself. 44 | # 45 | # We need to make sure we don't use any new libdrm functions, but those 46 | # are added very infrequently, so this is unlikely to be an issue. 47 | if libdrm.version().version_compare('<2.4.113') 48 | if libdrm.type_name() == 'internal' 49 | error('libdrm subproject out of date. Run `meson subprojects update`.') 50 | endif 51 | 52 | msg = [ 53 | 'System libdrm version does not have latest Linux DRM headers.', 54 | 'Attempting to use headers from meson subproject if present.', 55 | 'If this fails, update your system libdrm or run `meson subprojects download`.', 56 | ] 57 | foreach s : msg 58 | warning(s) 59 | endforeach 60 | 61 | fourcc_h = meson.current_source_dir() / 'subprojects/libdrm/include/drm/drm_fourcc.h' 62 | inc += include_directories('subprojects/libdrm/include/drm') 63 | libdrm = libdrm.partial_dependency(link_args: true) 64 | elif libdrm.type_name() == 'internal' 65 | fourcc_h = meson.current_source_dir() / 'subprojects/libdrm/include/drm/drm_fourcc.h' 66 | else 67 | fourcc_h = libdrm.get_pkgconfig_variable('includedir') / 'libdrm/drm_fourcc.h' 68 | endif 69 | 70 | if libpci.found() 71 | add_project_arguments('-DHAVE_LIBPCI', language: 'c') 72 | endif 73 | 74 | if libdrm.type_name() == 'internal' or cc.has_function('drmModeGetFB2', dependencies: [libdrm]) 75 | add_project_arguments('-DHAVE_GETFB2', language: 'c') 76 | endif 77 | 78 | python3 = import('python').find_installation() 79 | 80 | tables_c = custom_target('tables_c', 81 | output : 'tables.c', 82 | command : [python3, files('fourcc.py'), fourcc_h, '@OUTPUT@']) 83 | 84 | executable('drm_info', 85 | ['main.c', 'modifiers.c', 'json.c', 'pretty.c', tables_c], 86 | include_directories: inc, 87 | dependencies: [libdrm, libpci, jsonc], 88 | install: true, 89 | ) 90 | 91 | scdoc = dependency('scdoc', native: true, required: get_option('man-pages')) 92 | if scdoc.found() 93 | man_pages = ['drm_info.1.scd'] 94 | sh = find_program('sh', native: true) 95 | 96 | foreach src : man_pages 97 | topic = src.split('.')[0] 98 | section = src.split('.')[1] 99 | output = topic + '.' + section 100 | 101 | custom_target( 102 | output, 103 | input: src, 104 | output: output, 105 | command: [ 106 | sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.get_pkgconfig_variable('scdoc'), output) 107 | ], 108 | install: true, 109 | install_dir: get_option('mandir') / 'man' + section 110 | ) 111 | endforeach 112 | endif 113 | -------------------------------------------------------------------------------- /modifiers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "tables.h" 8 | 9 | static void print_nvidia_modifier(uint64_t mod) { 10 | if (!(mod & 0x10)) { 11 | printf("NVIDIA(unknown)"); 12 | return; 13 | } 14 | 15 | uint64_t h = mod & 0xF; 16 | uint64_t k = (mod >> 12) & 0xFF; 17 | uint64_t g = (mod >> 20) & 0x3; 18 | uint64_t s = (mod >> 22) & 0x1; 19 | uint64_t c = (mod >> 23) & 0x7; 20 | 21 | printf("NVIDIA_BLOCK_LINEAR_2D(h=%"PRIu64", k=%"PRIu64", g=%"PRIu64", " 22 | "s=%"PRIu64", c=%"PRIu64")", h, k, g, s, c); 23 | } 24 | 25 | static const char *amd_tile_version_str(uint64_t tile_version) { 26 | switch (tile_version) { 27 | case AMD_FMT_MOD_TILE_VER_GFX9: 28 | return "GFX9"; 29 | case AMD_FMT_MOD_TILE_VER_GFX10: 30 | return "GFX10"; 31 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: 32 | return "GFX10_RBPLUS"; 33 | } 34 | return "Unknown"; 35 | } 36 | 37 | static const char *amd_tile_str(uint64_t tile, uint64_t tile_version) { 38 | switch (tile_version) { 39 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: 40 | /* fallthrough */ 41 | case AMD_FMT_MOD_TILE_VER_GFX10: 42 | /* fallthrough */ 43 | case AMD_FMT_MOD_TILE_VER_GFX9: 44 | switch (tile) { 45 | case AMD_FMT_MOD_TILE_GFX9_64K_S: 46 | return "GFX9_64K_S"; 47 | case AMD_FMT_MOD_TILE_GFX9_64K_D: 48 | return "GFX9_64K_D"; 49 | case AMD_FMT_MOD_TILE_GFX9_64K_S_X: 50 | return "GFX9_64K_S_X"; 51 | case AMD_FMT_MOD_TILE_GFX9_64K_D_X: 52 | return "GFX9_64K_D_X"; 53 | case AMD_FMT_MOD_TILE_GFX9_64K_R_X: 54 | return "GFX9_64K_R_X"; 55 | } 56 | } 57 | return "Unknown"; 58 | } 59 | 60 | static const char *amd_dcc_block_size_str(uint64_t size) { 61 | switch (size) { 62 | case AMD_FMT_MOD_DCC_BLOCK_64B: 63 | return "64B"; 64 | case AMD_FMT_MOD_DCC_BLOCK_128B: 65 | return "128B"; 66 | case AMD_FMT_MOD_DCC_BLOCK_256B: 67 | return "256B"; 68 | } 69 | return "Unknown"; 70 | } 71 | 72 | static bool amd_gfx9_tile_is_x_t(uint64_t tile) { 73 | switch (tile) { 74 | case AMD_FMT_MOD_TILE_GFX9_64K_S_X: 75 | case AMD_FMT_MOD_TILE_GFX9_64K_D_X: 76 | case AMD_FMT_MOD_TILE_GFX9_64K_R_X: 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | static void print_amd_modifier(uint64_t mod) { 83 | uint64_t tile_version = AMD_FMT_MOD_GET(TILE_VERSION, mod); 84 | uint64_t tile = AMD_FMT_MOD_GET(TILE, mod); 85 | uint64_t dcc = AMD_FMT_MOD_GET(DCC, mod); 86 | uint64_t dcc_retile = AMD_FMT_MOD_GET(DCC_RETILE, mod); 87 | 88 | printf("AMD(TILE_VERSION = %s, TILE = %s", 89 | amd_tile_version_str(tile_version), amd_tile_str(tile, tile_version)); 90 | 91 | if (dcc) { 92 | printf(", DCC"); 93 | if (dcc_retile) { 94 | printf(", DCC_RETILE"); 95 | } 96 | if (!dcc_retile && AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, mod)) { 97 | printf(", DCC_PIPE_ALIGN"); 98 | } 99 | if (AMD_FMT_MOD_GET(DCC_INDEPENDENT_64B, mod)) { 100 | printf(", DCC_INDEPENDENT_64B"); 101 | } 102 | if (AMD_FMT_MOD_GET(DCC_INDEPENDENT_128B, mod)) { 103 | printf(", DCC_INDEPENDENT_128B"); 104 | } 105 | uint64_t dcc_max_compressed_block = 106 | AMD_FMT_MOD_GET(DCC_MAX_COMPRESSED_BLOCK, mod); 107 | printf(", DCC_MAX_COMPRESSED_BLOCK = %s", 108 | amd_dcc_block_size_str(dcc_max_compressed_block)); 109 | if (AMD_FMT_MOD_GET(DCC_CONSTANT_ENCODE, mod)) { 110 | printf(", DCC_CONSTANT_ENCODE"); 111 | } 112 | } 113 | 114 | if (tile_version >= AMD_FMT_MOD_TILE_VER_GFX9 && amd_gfx9_tile_is_x_t(tile)) { 115 | printf(", PIPE_XOR_BITS = %"PRIu64, AMD_FMT_MOD_GET(PIPE_XOR_BITS, mod)); 116 | if (tile_version == AMD_FMT_MOD_TILE_VER_GFX9) { 117 | printf(", BANK_XOR_BITS = %"PRIu64, AMD_FMT_MOD_GET(BANK_XOR_BITS, mod)); 118 | } 119 | if (tile_version == AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) { 120 | printf(", PACKERS = %"PRIu64, AMD_FMT_MOD_GET(PACKERS, mod)); 121 | } 122 | if (tile_version == AMD_FMT_MOD_TILE_VER_GFX9 && dcc) { 123 | printf(", RB = %"PRIu64, AMD_FMT_MOD_GET(RB, mod)); 124 | } 125 | if (tile_version == AMD_FMT_MOD_TILE_VER_GFX9 && dcc && 126 | (dcc_retile || AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, mod))) { 127 | printf(", PIPE = %"PRIu64, AMD_FMT_MOD_GET(PIPE, mod)); 128 | } 129 | } 130 | 131 | printf(")"); 132 | } 133 | 134 | static const char *arm_afbc_block_size_str(uint64_t block_size) { 135 | switch (block_size) { 136 | case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: 137 | return "16x16"; 138 | case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8: 139 | return "32x8"; 140 | case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4: 141 | return "64x4"; 142 | case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4: 143 | return "32x8_64x4"; 144 | } 145 | return "Unknown"; 146 | } 147 | 148 | static const char *arm_afrc_cu_size_str(uint64_t cu_size) { 149 | switch (cu_size) { 150 | case AFRC_FORMAT_MOD_CU_SIZE_16: 151 | return "16"; 152 | case AFRC_FORMAT_MOD_CU_SIZE_24: 153 | return "24"; 154 | case AFRC_FORMAT_MOD_CU_SIZE_32: 155 | return "32"; 156 | } 157 | return "Unknown"; 158 | } 159 | 160 | static void print_arm_modifier(uint64_t mod) { 161 | uint64_t type = (mod >> 52) & 0xF; 162 | uint64_t value = mod & 0x000FFFFFFFFFFFFFULL; 163 | 164 | switch (type) { 165 | case DRM_FORMAT_MOD_ARM_TYPE_AFBC:; 166 | uint64_t block_size = value & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK; 167 | printf("ARM_AFBC(BLOCK_SIZE = %s", arm_afbc_block_size_str(block_size)); 168 | if (value & AFBC_FORMAT_MOD_YTR) { 169 | printf(", YTR"); 170 | } 171 | if (value & AFBC_FORMAT_MOD_SPLIT) { 172 | printf(", SPLIT"); 173 | } 174 | if (value & AFBC_FORMAT_MOD_SPARSE) { 175 | printf(", SPARSE"); 176 | } 177 | if (value & AFBC_FORMAT_MOD_CBR) { 178 | printf(", CBR"); 179 | } 180 | if (value & AFBC_FORMAT_MOD_TILED) { 181 | printf(", TILED"); 182 | } 183 | if (value & AFBC_FORMAT_MOD_SC) { 184 | printf(", SC"); 185 | } 186 | if (value & AFBC_FORMAT_MOD_DB) { 187 | printf(", DB"); 188 | } 189 | if (value & AFBC_FORMAT_MOD_BCH) { 190 | printf(", BCH"); 191 | } 192 | if (value & AFBC_FORMAT_MOD_USM) { 193 | printf(", USM"); 194 | } 195 | printf(")"); 196 | break; 197 | case DRM_FORMAT_MOD_ARM_TYPE_MISC: 198 | switch (mod) { 199 | case DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED: 200 | printf("ARM_16X16_BLOCK_U_INTERLEAVED"); 201 | break; 202 | default: 203 | printf("ARM_MISC(unknown)"); 204 | } 205 | break; 206 | case DRM_FORMAT_MOD_ARM_TYPE_AFRC: 207 | uint64_t cu_size_p0 = value & AFRC_FORMAT_MOD_CU_SIZE_MASK; 208 | uint64_t cu_size_p12 = (value >> 4) & AFRC_FORMAT_MOD_CU_SIZE_MASK; 209 | printf("ARM_AFRC("); 210 | printf("CU_SIZE_P0 = %s", arm_afrc_cu_size_str(cu_size_p0)); 211 | printf(", CU_SIZE_P12 = %s", arm_afrc_cu_size_str(cu_size_p12)); 212 | if (value & AFRC_FORMAT_MOD_LAYOUT_SCAN) 213 | printf(", SCAN"); 214 | else 215 | printf(", ROT"); 216 | printf(")"); 217 | break; 218 | default: 219 | printf("ARM(unknown)"); 220 | } 221 | } 222 | 223 | static const char *amlogic_layout_str(uint64_t layout) { 224 | switch (layout) { 225 | case AMLOGIC_FBC_LAYOUT_BASIC: 226 | return "BASIC"; 227 | case AMLOGIC_FBC_LAYOUT_SCATTER: 228 | return "SCATTER"; 229 | } 230 | return "Unknown"; 231 | } 232 | 233 | static void print_amlogic_modifier(uint64_t mod) { 234 | uint64_t layout = mod & 0xFF; 235 | uint64_t options = (mod >> 8) & 0xFF; 236 | 237 | printf("AMLOGIC_FBC(layout = %s, options = %s)", 238 | amlogic_layout_str(layout), 239 | (options & AMLOGIC_FBC_OPTION_MEM_SAVING) ? "MEM_SAVING" : "0"); 240 | } 241 | 242 | static uint8_t mod_vendor(uint64_t mod) { 243 | return (uint8_t)(mod >> 56); 244 | } 245 | 246 | void print_modifier(uint64_t mod) { 247 | switch (mod_vendor(mod)) { 248 | case DRM_FORMAT_MOD_VENDOR_NVIDIA: 249 | print_nvidia_modifier(mod); 250 | break; 251 | case DRM_FORMAT_MOD_VENDOR_AMD: 252 | print_amd_modifier(mod); 253 | break; 254 | case DRM_FORMAT_MOD_VENDOR_ARM: 255 | print_arm_modifier(mod); 256 | break; 257 | case DRM_FORMAT_MOD_VENDOR_AMLOGIC: 258 | print_amlogic_modifier(mod); 259 | break; 260 | default: 261 | printf("%s", basic_modifier_str(mod)); 262 | } 263 | printf(" (0x%"PRIx64")", mod); 264 | } 265 | -------------------------------------------------------------------------------- /json.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "drm_info.h" 18 | 19 | static const struct { 20 | const char *name; 21 | uint64_t cap; 22 | } client_caps[] = { 23 | { "STEREO_3D", DRM_CLIENT_CAP_STEREO_3D }, 24 | { "UNIVERSAL_PLANES", DRM_CLIENT_CAP_UNIVERSAL_PLANES }, 25 | { "ATOMIC", DRM_CLIENT_CAP_ATOMIC }, 26 | { "ASPECT_RATIO", DRM_CLIENT_CAP_ASPECT_RATIO }, 27 | { "WRITEBACK_CONNECTORS", DRM_CLIENT_CAP_WRITEBACK_CONNECTORS }, 28 | }; 29 | 30 | static const struct { 31 | const char *name; 32 | uint64_t cap; 33 | } caps[] = { 34 | { "DUMB_BUFFER", DRM_CAP_DUMB_BUFFER }, 35 | { "VBLANK_HIGH_CRTC", DRM_CAP_VBLANK_HIGH_CRTC }, 36 | { "DUMB_PREFERRED_DEPTH", DRM_CAP_DUMB_PREFERRED_DEPTH }, 37 | { "DUMB_PREFER_SHADOW", DRM_CAP_DUMB_PREFER_SHADOW }, 38 | { "PRIME", DRM_CAP_PRIME }, 39 | { "TIMESTAMP_MONOTONIC", DRM_CAP_TIMESTAMP_MONOTONIC }, 40 | { "ASYNC_PAGE_FLIP", DRM_CAP_ASYNC_PAGE_FLIP }, 41 | { "CURSOR_WIDTH", DRM_CAP_CURSOR_WIDTH }, 42 | { "CURSOR_HEIGHT", DRM_CAP_CURSOR_HEIGHT }, 43 | { "ADDFB2_MODIFIERS", DRM_CAP_ADDFB2_MODIFIERS }, 44 | { "PAGE_FLIP_TARGET", DRM_CAP_PAGE_FLIP_TARGET }, 45 | { "CRTC_IN_VBLANK_EVENT", DRM_CAP_CRTC_IN_VBLANK_EVENT }, 46 | { "SYNCOBJ", DRM_CAP_SYNCOBJ }, 47 | { "SYNCOBJ_TIMELINE", DRM_CAP_SYNCOBJ_TIMELINE }, 48 | }; 49 | 50 | static struct json_object *kernel_info(void) 51 | { 52 | struct utsname utsname; 53 | if (uname(&utsname) != 0) { 54 | perror("uname"); 55 | return NULL; 56 | } 57 | 58 | struct json_object *obj = json_object_new_object(); 59 | json_object_object_add(obj, "sysname", 60 | json_object_new_string(utsname.sysname)); 61 | json_object_object_add(obj, "release", 62 | json_object_new_string(utsname.release)); 63 | json_object_object_add(obj, "version", 64 | json_object_new_string(utsname.version)); 65 | return obj; 66 | } 67 | 68 | static struct json_object *driver_info(int fd) 69 | { 70 | drmVersion *ver = drmGetVersion(fd); 71 | if (!ver) { 72 | perror("drmGetVersion"); 73 | return NULL; 74 | } 75 | 76 | struct json_object *obj = json_object_new_object(); 77 | 78 | json_object_object_add(obj, "name", json_object_new_string(ver->name)); 79 | json_object_object_add(obj, "desc", json_object_new_string(ver->desc)); 80 | 81 | struct json_object *ver_obj = json_object_new_object(); 82 | json_object_object_add(ver_obj, "major", 83 | json_object_new_int(ver->version_major)); 84 | json_object_object_add(ver_obj, "minor", 85 | json_object_new_int(ver->version_minor)); 86 | json_object_object_add(ver_obj, "patch", 87 | json_object_new_int(ver->version_patchlevel)); 88 | json_object_object_add(ver_obj, "date", 89 | json_object_new_string(ver->date)); 90 | json_object_object_add(obj, "version", ver_obj); 91 | 92 | drmFreeVersion(ver); 93 | 94 | json_object_object_add(obj, "kernel", kernel_info()); 95 | 96 | struct json_object *client_caps_obj = json_object_new_object(); 97 | for (size_t i = 0; i < sizeof(client_caps) / sizeof(client_caps[0]); ++i) { 98 | bool supported = drmSetClientCap(fd, client_caps[i].cap, 1) == 0; 99 | json_object_object_add(client_caps_obj, client_caps[i].name, 100 | json_object_new_boolean(supported)); 101 | } 102 | json_object_object_add(obj, "client_caps", client_caps_obj); 103 | 104 | struct json_object *caps_obj = json_object_new_object(); 105 | for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) { 106 | struct json_object *cap_obj = NULL; 107 | uint64_t cap; 108 | if (drmGetCap(fd, caps[i].cap, &cap) == 0) { 109 | cap_obj = json_object_new_uint64(cap); 110 | } 111 | json_object_object_add(caps_obj, caps[i].name, cap_obj); 112 | } 113 | json_object_object_add(obj, "caps", caps_obj); 114 | 115 | return obj; 116 | } 117 | 118 | static struct json_object *device_info(int fd) 119 | { 120 | drmDevice *dev; 121 | if (drmGetDevice(fd, &dev) != 0) { 122 | perror("drmGetDevice"); 123 | return NULL; 124 | } 125 | 126 | struct json_object *obj = json_object_new_object(); 127 | json_object_object_add(obj, "available_nodes", 128 | json_object_new_uint64(dev->available_nodes)); 129 | json_object_object_add(obj, "bus_type", 130 | json_object_new_uint64(dev->bustype)); 131 | 132 | struct json_object *device_data_obj = NULL; 133 | switch (dev->bustype) { 134 | case DRM_BUS_PCI:; 135 | drmPciDeviceInfo *pci = dev->deviceinfo.pci; 136 | device_data_obj = json_object_new_object(); 137 | json_object_object_add(device_data_obj, "vendor", 138 | json_object_new_uint64(pci->vendor_id)); 139 | json_object_object_add(device_data_obj, "device", 140 | json_object_new_uint64(pci->device_id)); 141 | json_object_object_add(device_data_obj, "subsystem_vendor", 142 | json_object_new_uint64(pci->subvendor_id)); 143 | json_object_object_add(device_data_obj, "subsystem_device", 144 | json_object_new_uint64(pci->subdevice_id)); 145 | break; 146 | case DRM_BUS_USB:; 147 | drmUsbDeviceInfo *usb = dev->deviceinfo.usb; 148 | device_data_obj = json_object_new_object(); 149 | json_object_object_add(device_data_obj, "vendor", 150 | json_object_new_uint64(usb->vendor)); 151 | json_object_object_add(device_data_obj, "product", 152 | json_object_new_uint64(usb->product)); 153 | break; 154 | case DRM_BUS_PLATFORM:; 155 | drmPlatformDeviceInfo *platform = dev->deviceinfo.platform; 156 | device_data_obj = json_object_new_object(); 157 | struct json_object *compatible_arr = json_object_new_array(); 158 | for (size_t i = 0; platform->compatible[i]; ++i) 159 | json_object_array_add(compatible_arr, 160 | json_object_new_string(platform->compatible[i])); 161 | json_object_object_add(device_data_obj, "compatible", compatible_arr); 162 | break; 163 | } 164 | json_object_object_add(obj, "device_data", device_data_obj); 165 | 166 | drmFreeDevice(&dev); 167 | 168 | return obj; 169 | } 170 | 171 | static struct json_object *in_formats_info(int fd, uint32_t blob_id) 172 | { 173 | struct json_object *arr = json_object_new_array(); 174 | 175 | drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); 176 | if (!blob) { 177 | perror("drmModeGetPropertyBlob"); 178 | return NULL; 179 | } 180 | 181 | struct drm_format_modifier_blob *data = blob->data; 182 | 183 | uint32_t *fmts = (uint32_t *) 184 | ((char *)data + data->formats_offset); 185 | 186 | struct drm_format_modifier *mods = (struct drm_format_modifier *) 187 | ((char *)data + data->modifiers_offset); 188 | 189 | for (uint32_t i = 0; i < data->count_modifiers; ++i) { 190 | struct json_object *mod_obj = json_object_new_object(); 191 | json_object_object_add(mod_obj, "modifier", 192 | json_object_new_uint64(mods[i].modifier)); 193 | 194 | struct json_object *fmts_arr = json_object_new_array(); 195 | for (uint64_t j = 0; j < 64; ++j) { 196 | if (mods[i].formats & (1ull << j)) { 197 | uint32_t fmt = fmts[j + mods[i].offset]; 198 | json_object_array_add(fmts_arr, json_object_new_uint64(fmt)); 199 | } 200 | } 201 | json_object_object_add(mod_obj, "formats", fmts_arr); 202 | 203 | json_object_array_add(arr, mod_obj); 204 | } 205 | 206 | drmModeFreePropertyBlob(blob); 207 | 208 | return arr; 209 | } 210 | 211 | static struct json_object *mode_info(const drmModeModeInfo *mode) 212 | { 213 | struct json_object *obj = json_object_new_object(); 214 | 215 | json_object_object_add(obj, "clock", json_object_new_uint64(mode->clock)); 216 | 217 | json_object_object_add(obj, "hdisplay", json_object_new_uint64(mode->hdisplay)); 218 | json_object_object_add(obj, "hsync_start", json_object_new_uint64(mode->hsync_start)); 219 | json_object_object_add(obj, "hsync_end", json_object_new_uint64(mode->hsync_end)); 220 | json_object_object_add(obj, "htotal", json_object_new_uint64(mode->htotal)); 221 | json_object_object_add(obj, "hskew", json_object_new_uint64(mode->hskew)); 222 | 223 | json_object_object_add(obj, "vdisplay", json_object_new_uint64(mode->vdisplay)); 224 | json_object_object_add(obj, "vsync_start", json_object_new_uint64(mode->vsync_start)); 225 | json_object_object_add(obj, "vsync_end", json_object_new_uint64(mode->vsync_end)); 226 | json_object_object_add(obj, "vtotal", json_object_new_uint64(mode->vtotal)); 227 | json_object_object_add(obj, "vscan", json_object_new_uint64(mode->vscan)); 228 | 229 | json_object_object_add(obj, "vrefresh", json_object_new_uint64(mode->vrefresh)); 230 | 231 | json_object_object_add(obj, "flags", json_object_new_uint64(mode->flags)); 232 | json_object_object_add(obj, "type", json_object_new_uint64(mode->type)); 233 | json_object_object_add(obj, "name", json_object_new_string(mode->name)); 234 | 235 | return obj; 236 | } 237 | 238 | static struct json_object *mode_id_info(int fd, uint32_t blob_id) 239 | { 240 | drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); 241 | if (!blob) { 242 | perror("drmModeGetPropertyBlob"); 243 | return NULL; 244 | } 245 | 246 | drmModeModeInfo *mode = blob->data; 247 | 248 | struct json_object *obj = mode_info(mode); 249 | 250 | drmModeFreePropertyBlob(blob); 251 | 252 | return obj; 253 | } 254 | 255 | static struct json_object *writeback_pixel_formats_info(int fd, uint32_t blob_id) 256 | { 257 | struct json_object *arr = json_object_new_array(); 258 | 259 | drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); 260 | if (!blob) { 261 | perror("drmModeGetPropertyBlob"); 262 | return NULL; 263 | } 264 | 265 | uint32_t *fmts = blob->data; 266 | uint32_t fmts_len = blob->length / sizeof(uint32_t); 267 | for (uint32_t i = 0; i < fmts_len; ++i) { 268 | json_object_array_add(arr, json_object_new_uint64(fmts[i])); 269 | } 270 | 271 | drmModeFreePropertyBlob(blob); 272 | 273 | return arr; 274 | } 275 | 276 | static struct json_object *path_info(int fd, uint32_t blob_id) 277 | { 278 | drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); 279 | if (!blob) { 280 | perror("drmModeGetPropertyBlob"); 281 | return NULL; 282 | } 283 | 284 | struct json_object *obj = json_object_new_string_len(blob->data, blob->length); 285 | 286 | drmModeFreePropertyBlob(blob); 287 | 288 | return obj; 289 | } 290 | 291 | static struct json_object *fb_info(int fd, uint32_t id) 292 | { 293 | #ifdef HAVE_GETFB2 294 | drmModeFB2 *fb2 = drmModeGetFB2(fd, id); 295 | if (!fb2 && errno != EINVAL) { 296 | perror("drmModeGetFB2"); 297 | return NULL; 298 | } 299 | if (fb2) { 300 | struct json_object *obj = json_object_new_object(); 301 | json_object_object_add(obj, "id", json_object_new_uint64(fb2->fb_id)); 302 | json_object_object_add(obj, "width", json_object_new_uint64(fb2->width)); 303 | json_object_object_add(obj, "height", json_object_new_uint64(fb2->height)); 304 | 305 | json_object_object_add(obj, "format", json_object_new_uint64(fb2->pixel_format)); 306 | if (fb2->flags & DRM_MODE_FB_MODIFIERS) { 307 | json_object_object_add(obj, "modifier", json_object_new_uint64(fb2->modifier)); 308 | } 309 | 310 | struct json_object *planes_arr = json_object_new_array(); 311 | json_object_object_add(obj, "planes", planes_arr); 312 | 313 | for (size_t i = 0; i < sizeof(fb2->pitches) / sizeof(fb2->pitches[0]); i++) { 314 | if (!fb2->pitches[i]) 315 | continue; 316 | 317 | struct json_object *plane_obj = json_object_new_object(); 318 | json_object_array_add(planes_arr, plane_obj); 319 | 320 | json_object_object_add(plane_obj, "offset", 321 | json_object_new_uint64(fb2->offsets[i])); 322 | json_object_object_add(plane_obj, "pitch", 323 | json_object_new_uint64(fb2->pitches[i])); 324 | } 325 | 326 | drmModeFreeFB2(fb2); 327 | 328 | return obj; 329 | } 330 | #endif 331 | 332 | // Fallback to drmModeGetFB is drmModeGetFB2 isn't available 333 | drmModeFB *fb = drmModeGetFB(fd, id); 334 | if (!fb) { 335 | perror("drmModeGetFB"); 336 | return NULL; 337 | } 338 | 339 | struct json_object *obj = json_object_new_object(); 340 | json_object_object_add(obj, "id", json_object_new_uint64(fb->fb_id)); 341 | json_object_object_add(obj, "width", json_object_new_uint64(fb->width)); 342 | json_object_object_add(obj, "height", json_object_new_uint64(fb->height)); 343 | 344 | // Legacy properties 345 | json_object_object_add(obj, "pitch", json_object_new_uint64(fb->pitch)); 346 | json_object_object_add(obj, "bpp", json_object_new_uint64(fb->bpp)); 347 | json_object_object_add(obj, "depth", json_object_new_uint64(fb->depth)); 348 | 349 | drmModeFreeFB(fb); 350 | 351 | return obj; 352 | } 353 | 354 | 355 | static struct json_object *properties_info(int fd, uint32_t id, uint32_t type) 356 | { 357 | drmModeObjectProperties *props = drmModeObjectGetProperties(fd, id, type); 358 | if (!props) { 359 | perror("drmModeObjectGetProperties"); 360 | return NULL; 361 | } 362 | 363 | struct json_object *obj = json_object_new_object(); 364 | 365 | for (uint32_t i = 0; i < props->count_props; ++i) { 366 | drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]); 367 | if (!prop) { 368 | perror("drmModeGetProperty"); 369 | continue; 370 | } 371 | 372 | uint32_t flags = prop->flags; 373 | uint32_t type = flags & 374 | (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE); 375 | bool atomic = flags & DRM_MODE_PROP_ATOMIC; 376 | bool immutable = flags & DRM_MODE_PROP_IMMUTABLE; 377 | uint64_t value = props->prop_values[i]; 378 | 379 | struct json_object *prop_obj = json_object_new_object(); 380 | json_object_object_add(prop_obj, "id", 381 | json_object_new_uint64(prop->prop_id)); 382 | json_object_object_add(prop_obj, "flags", 383 | json_object_new_uint64(flags)); 384 | json_object_object_add(prop_obj, "type", json_object_new_uint64(type)); 385 | json_object_object_add(prop_obj, "atomic", 386 | json_object_new_boolean(atomic)); 387 | json_object_object_add(prop_obj, "immutable", 388 | json_object_new_boolean(immutable)); 389 | 390 | json_object_object_add(prop_obj, "raw_value", 391 | json_object_new_uint64(value)); 392 | 393 | struct json_object *spec_obj = NULL; 394 | switch (type) { 395 | case DRM_MODE_PROP_RANGE: 396 | spec_obj = json_object_new_object(); 397 | json_object_object_add(spec_obj, "min", 398 | json_object_new_uint64(prop->values[0])); 399 | json_object_object_add(spec_obj, "max", 400 | json_object_new_uint64(prop->values[1])); 401 | break; 402 | case DRM_MODE_PROP_ENUM: 403 | case DRM_MODE_PROP_BITMASK: 404 | spec_obj = json_object_new_array(); 405 | for (int j = 0; j < prop->count_enums; ++j) { 406 | struct json_object *item_obj = json_object_new_object(); 407 | json_object_object_add(item_obj, "name", 408 | json_object_new_string(prop->enums[j].name)); 409 | json_object_object_add(item_obj, "value", 410 | json_object_new_uint64(prop->enums[j].value)); 411 | json_object_array_add(spec_obj, item_obj); 412 | } 413 | break; 414 | case DRM_MODE_PROP_OBJECT: 415 | spec_obj = json_object_new_uint64(prop->values[0]); 416 | break; 417 | case DRM_MODE_PROP_SIGNED_RANGE: 418 | spec_obj = json_object_new_object(); 419 | json_object_object_add(spec_obj, "min", 420 | json_object_new_int64((int64_t)prop->values[0])); 421 | json_object_object_add(spec_obj, "max", 422 | json_object_new_int64((int64_t)prop->values[1])); 423 | break; 424 | } 425 | json_object_object_add(prop_obj, "spec", spec_obj); 426 | 427 | struct json_object *value_obj = NULL; 428 | switch (type) { 429 | case DRM_MODE_PROP_RANGE: 430 | case DRM_MODE_PROP_ENUM: 431 | case DRM_MODE_PROP_BITMASK: 432 | case DRM_MODE_PROP_OBJECT: 433 | value_obj = json_object_new_uint64(value); 434 | break; 435 | case DRM_MODE_PROP_BLOB: 436 | // TODO: base64-encode blob contents 437 | value_obj = NULL; 438 | break; 439 | case DRM_MODE_PROP_SIGNED_RANGE: 440 | value_obj = json_object_new_int64((int64_t)value); 441 | break; 442 | } 443 | json_object_object_add(prop_obj, "value", value_obj); 444 | 445 | struct json_object *data_obj = NULL; 446 | switch (type) { 447 | case DRM_MODE_PROP_BLOB: 448 | if (!value) { 449 | break; 450 | } 451 | if (strcmp(prop->name, "IN_FORMATS") == 0) { 452 | data_obj = in_formats_info(fd, value); 453 | } else if (strcmp(prop->name, "MODE_ID") == 0) { 454 | data_obj = mode_id_info(fd, value); 455 | } else if (strcmp(prop->name, "WRITEBACK_PIXEL_FORMATS") == 0) { 456 | data_obj = writeback_pixel_formats_info(fd, value); 457 | } else if (strcmp(prop->name, "PATH") == 0) { 458 | data_obj = path_info(fd, value); 459 | } 460 | break; 461 | case DRM_MODE_PROP_RANGE: 462 | // This is a special case, as the SRC_* properties are 463 | // in 16.16 fixed point 464 | if (strncmp(prop->name, "SRC_", 4) == 0) { 465 | data_obj = json_object_new_uint64(value >> 16); 466 | } 467 | break; 468 | case DRM_MODE_PROP_OBJECT: 469 | if (!value) { 470 | break; 471 | } 472 | if (strcmp(prop->name, "FB_ID") == 0) { 473 | data_obj = fb_info(fd, value); 474 | } 475 | break; 476 | } 477 | json_object_object_add(prop_obj, "data", data_obj); 478 | 479 | json_object_object_add(obj, prop->name, prop_obj); 480 | 481 | drmModeFreeProperty(prop); 482 | } 483 | 484 | drmModeFreeObjectProperties(props); 485 | 486 | return obj; 487 | } 488 | 489 | static struct json_object *connectors_info(int fd, drmModeRes *res) 490 | { 491 | struct json_object *arr = json_object_new_array(); 492 | 493 | for (int i = 0; i < res->count_connectors; ++i) { 494 | drmModeConnector *conn = drmModeGetConnectorCurrent(fd, res->connectors[i]); 495 | if (!conn) { 496 | perror("drmModeGetConnectorCurrent"); 497 | continue; 498 | } 499 | 500 | struct json_object *conn_obj = json_object_new_object(); 501 | 502 | json_object_object_add(conn_obj, "id", 503 | json_object_new_uint64(conn->connector_id)); 504 | json_object_object_add(conn_obj, "type", 505 | json_object_new_uint64(conn->connector_type)); 506 | json_object_object_add(conn_obj, "status", 507 | json_object_new_uint64(conn->connection)); 508 | json_object_object_add(conn_obj, "phy_width", 509 | json_object_new_uint64(conn->mmWidth)); 510 | json_object_object_add(conn_obj, "phy_height", 511 | json_object_new_uint64(conn->mmHeight)); 512 | json_object_object_add(conn_obj, "subpixel", 513 | json_object_new_uint64(conn->subpixel)); 514 | json_object_object_add(conn_obj, "encoder_id", 515 | json_object_new_uint64(conn->encoder_id)); 516 | 517 | struct json_object *encoders_arr = json_object_new_array(); 518 | for (int j = 0; j < conn->count_encoders; ++j) { 519 | json_object_array_add(encoders_arr, 520 | json_object_new_uint64(conn->encoders[j])); 521 | } 522 | json_object_object_add(conn_obj, "encoders", encoders_arr); 523 | 524 | struct json_object *modes_arr = json_object_new_array(); 525 | for (int j = 0; j < conn->count_modes; ++j) { 526 | const drmModeModeInfo *mode = &conn->modes[j]; 527 | json_object_array_add(modes_arr, mode_info(mode)); 528 | } 529 | json_object_object_add(conn_obj, "modes", modes_arr); 530 | 531 | struct json_object *props_obj = properties_info(fd, 532 | conn->connector_id, DRM_MODE_OBJECT_CONNECTOR); 533 | json_object_object_add(conn_obj, "properties", props_obj); 534 | 535 | drmModeFreeConnector(conn); 536 | 537 | json_object_array_add(arr, conn_obj); 538 | } 539 | 540 | return arr; 541 | } 542 | 543 | static struct json_object *encoders_info(int fd, drmModeRes *res) 544 | { 545 | struct json_object *arr = json_object_new_array(); 546 | 547 | for (int i = 0; i < res->count_encoders; ++i) { 548 | drmModeEncoder *enc = drmModeGetEncoder(fd, res->encoders[i]); 549 | if (!enc) { 550 | perror("drmModeGetEncoder"); 551 | continue; 552 | } 553 | 554 | struct json_object *enc_obj = json_object_new_object(); 555 | 556 | json_object_object_add(enc_obj, "id", 557 | json_object_new_uint64(enc->encoder_id)); 558 | json_object_object_add(enc_obj, "type", 559 | json_object_new_uint64(enc->encoder_type)); 560 | json_object_object_add(enc_obj, "crtc_id", 561 | json_object_new_uint64(enc->crtc_id)); 562 | json_object_object_add(enc_obj, "possible_crtcs", 563 | json_object_new_uint64(enc->possible_crtcs)); 564 | json_object_object_add(enc_obj, "possible_clones", 565 | json_object_new_uint64(enc->possible_clones)); 566 | 567 | drmModeFreeEncoder(enc); 568 | 569 | json_object_array_add(arr, enc_obj); 570 | } 571 | 572 | return arr; 573 | } 574 | 575 | static struct json_object *crtcs_info(int fd, drmModeRes *res) 576 | { 577 | struct json_object *arr = json_object_new_array(); 578 | 579 | for (int i = 0; i < res->count_crtcs; ++i) { 580 | drmModeCrtc *crtc = drmModeGetCrtc(fd, res->crtcs[i]); 581 | if (!crtc) { 582 | perror("drmModeGetCrtc"); 583 | continue; 584 | } 585 | 586 | struct json_object *crtc_obj = json_object_new_object(); 587 | 588 | json_object_object_add(crtc_obj, "id", 589 | json_object_new_uint64(crtc->crtc_id)); 590 | json_object_object_add(crtc_obj, "fb_id", 591 | json_object_new_uint64(crtc->buffer_id)); 592 | json_object_object_add(crtc_obj, "x", 593 | json_object_new_uint64(crtc->x)); 594 | json_object_object_add(crtc_obj, "y", 595 | json_object_new_uint64(crtc->y)); 596 | if (crtc->mode_valid) { 597 | json_object_object_add(crtc_obj, "mode", mode_info(&crtc->mode)); 598 | } else { 599 | json_object_object_add(crtc_obj, "mode", NULL); 600 | } 601 | json_object_object_add(crtc_obj, "gamma_size", 602 | json_object_new_int(crtc->gamma_size)); 603 | 604 | struct json_object *props_obj = properties_info(fd, 605 | crtc->crtc_id, DRM_MODE_OBJECT_CRTC); 606 | json_object_object_add(crtc_obj, "properties", props_obj); 607 | 608 | drmModeFreeCrtc(crtc); 609 | 610 | json_object_array_add(arr, crtc_obj); 611 | } 612 | 613 | return arr; 614 | } 615 | 616 | static struct json_object *planes_info(int fd) 617 | { 618 | drmModePlaneRes *res = drmModeGetPlaneResources(fd); 619 | if (!res) { 620 | perror("drmModeGetPlaneResources"); 621 | return NULL; 622 | } 623 | 624 | struct json_object *arr = json_object_new_array(); 625 | 626 | for (uint32_t i = 0; i < res->count_planes; ++i) { 627 | drmModePlane *plane = drmModeGetPlane(fd, res->planes[i]); 628 | if (!plane) { 629 | perror("drmModeGetPlane"); 630 | continue; 631 | } 632 | 633 | struct json_object *plane_obj = json_object_new_object(); 634 | 635 | json_object_object_add(plane_obj, "id", 636 | json_object_new_uint64(plane->plane_id)); 637 | json_object_object_add(plane_obj, "possible_crtcs", 638 | json_object_new_uint64(plane->possible_crtcs)); 639 | json_object_object_add(plane_obj, "crtc_id", 640 | json_object_new_uint64(plane->crtc_id)); 641 | json_object_object_add(plane_obj, "fb_id", 642 | json_object_new_uint64(plane->fb_id)); 643 | json_object_object_add(plane_obj, "crtc_x", 644 | json_object_new_uint64(plane->crtc_x)); 645 | json_object_object_add(plane_obj, "crtc_y", 646 | json_object_new_uint64(plane->crtc_y)); 647 | json_object_object_add(plane_obj, "x", 648 | json_object_new_uint64(plane->x)); 649 | json_object_object_add(plane_obj, "y", 650 | json_object_new_uint64(plane->y)); 651 | json_object_object_add(plane_obj, "gamma_size", 652 | json_object_new_uint64(plane->gamma_size)); 653 | 654 | json_object_object_add(plane_obj, "fb", 655 | plane->fb_id ? fb_info(fd, plane->fb_id) : NULL); 656 | 657 | struct json_object *formats_arr = json_object_new_array(); 658 | for (uint32_t j = 0; j < plane->count_formats; ++j) { 659 | json_object_array_add(formats_arr, 660 | json_object_new_uint64(plane->formats[j])); 661 | } 662 | json_object_object_add(plane_obj, "formats", formats_arr); 663 | 664 | struct json_object *props_obj = properties_info(fd, 665 | plane->plane_id, DRM_MODE_OBJECT_PLANE); 666 | json_object_object_add(plane_obj, "properties", props_obj); 667 | 668 | drmModeFreePlane(plane); 669 | 670 | json_object_array_add(arr, plane_obj); 671 | } 672 | 673 | drmModeFreePlaneResources(res); 674 | 675 | return arr; 676 | } 677 | 678 | static struct json_object *node_info(const char *path) 679 | { 680 | int fd = open(path, O_RDONLY); 681 | if (fd < 0) { 682 | perror(path); 683 | return NULL; 684 | } 685 | 686 | struct json_object *obj = json_object_new_object(); 687 | 688 | // Get driver info before getting resources, as it'll try to enable some 689 | // DRM client capabilities 690 | json_object_object_add(obj, "driver", driver_info(fd)); 691 | 692 | json_object_object_add(obj, "device", device_info(fd)); 693 | 694 | drmModeRes *res = drmModeGetResources(fd); 695 | if (!res) { 696 | perror("drmModeGetResources"); 697 | close(fd); 698 | json_object_put(obj); 699 | return NULL; 700 | } 701 | 702 | struct json_object *fb_size_obj = json_object_new_object(); 703 | json_object_object_add(fb_size_obj, "min_width", 704 | json_object_new_uint64(res->min_width)); 705 | json_object_object_add(fb_size_obj, "max_width", 706 | json_object_new_uint64(res->max_width)); 707 | json_object_object_add(fb_size_obj, "min_height", 708 | json_object_new_uint64(res->min_height)); 709 | json_object_object_add(fb_size_obj, "max_height", 710 | json_object_new_uint64(res->max_height)); 711 | json_object_object_add(obj, "fb_size", fb_size_obj); 712 | 713 | json_object_object_add(obj, "connectors", connectors_info(fd, res)); 714 | json_object_object_add(obj, "encoders", encoders_info(fd, res)); 715 | json_object_object_add(obj, "crtcs", crtcs_info(fd, res)); 716 | json_object_object_add(obj, "planes", planes_info(fd)); 717 | 718 | drmModeFreeResources(res); 719 | 720 | close(fd); 721 | 722 | return obj; 723 | } 724 | 725 | /* paths is a NULL terminated argv array */ 726 | struct json_object *drm_info(char *paths[]) 727 | { 728 | struct json_object *obj = json_object_new_object(); 729 | 730 | /* Print everything by default */ 731 | if (!paths[0]) { 732 | drmDevice *devices[64]; 733 | int n = drmGetDevices(devices, sizeof(devices) / sizeof(devices[0])); 734 | if (n < 0) { 735 | perror("drmGetDevices"); 736 | json_object_put(obj); 737 | return NULL; 738 | } 739 | 740 | for (int i = 0; i < n; ++i) { 741 | drmDevice *dev = devices[i]; 742 | if (!(dev->available_nodes & (1 << DRM_NODE_PRIMARY))) 743 | continue; 744 | 745 | const char *path = dev->nodes[DRM_NODE_PRIMARY]; 746 | struct json_object *dev_obj = node_info(path); 747 | if (!dev_obj) { 748 | fprintf(stderr, "Failed to retrieve information from %s\n", path); 749 | continue; 750 | } 751 | 752 | json_object_object_add(obj, path, dev_obj); 753 | } 754 | 755 | drmFreeDevices(devices, n); 756 | } else { 757 | for (char **path = paths; *path; ++path) { 758 | struct json_object *dev = node_info(*path); 759 | if (!dev) 760 | continue; 761 | 762 | json_object_object_add(obj, *path, dev); 763 | } 764 | } 765 | 766 | return obj; 767 | } 768 | -------------------------------------------------------------------------------- /pretty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef HAVE_LIBPCI 11 | #include 12 | #endif 13 | 14 | #include "drm_info.h" 15 | #include "modifiers.h" 16 | #include "tables.h" 17 | 18 | #define L_LINE "│ " 19 | #define L_VAL "├───" 20 | #define L_LAST "└───" 21 | #define L_GAP " " 22 | 23 | static const char *get_object_object_string(struct json_object *obj, 24 | const char *key) 25 | { 26 | struct json_object *str_obj = json_object_object_get(obj, key); 27 | if (!str_obj) { 28 | return NULL; 29 | } 30 | return json_object_get_string(str_obj); 31 | } 32 | 33 | static uint64_t get_object_object_uint64(struct json_object *obj, 34 | const char *key) 35 | { 36 | struct json_object *uint64_obj = json_object_object_get(obj, key); 37 | if (!uint64_obj) { 38 | return 0; 39 | } 40 | return json_object_get_uint64(uint64_obj); 41 | } 42 | 43 | static void print_driver(struct json_object *obj) 44 | { 45 | const char *name = get_object_object_string(obj, "name"); 46 | const char *desc = get_object_object_string(obj, "desc"); 47 | struct json_object *version_obj = json_object_object_get(obj, "version"); 48 | int version_major = get_object_object_uint64(version_obj, "major"); 49 | int version_minor = get_object_object_uint64(version_obj, "minor"); 50 | int version_patch = get_object_object_uint64(version_obj, "patch"); 51 | const char *version_date = get_object_object_string(version_obj, "date"); 52 | 53 | printf(L_VAL "Driver: %s (%s) version %d.%d.%d (%s)\n", name, desc, 54 | version_major, version_minor, version_patch, version_date); 55 | 56 | struct json_object_iter iter; 57 | struct json_object *client_caps_obj = 58 | json_object_object_get(obj, "client_caps"); 59 | json_object_object_foreachC(client_caps_obj, iter) { 60 | json_bool supported = json_object_get_boolean(iter.val); 61 | printf(L_LINE L_VAL "DRM_CLIENT_CAP_%s %s\n", iter.key, 62 | supported ? "supported" : "not supported"); 63 | } 64 | 65 | struct json_object *caps_obj = json_object_object_get(obj, "caps"); 66 | json_object_object_foreachC(caps_obj, iter) { 67 | printf(!iter.entry->next ? L_LINE L_LAST : L_LINE L_VAL); 68 | if (iter.val == NULL) { 69 | printf("DRM_CAP_%s not supported\n", iter.key); 70 | } else { 71 | printf("DRM_CAP_%s = %"PRIu64"\n", iter.key, 72 | json_object_get_uint64(iter.val)); 73 | } 74 | } 75 | } 76 | 77 | static const char *bustype_str(int type) 78 | { 79 | switch (type) { 80 | case DRM_BUS_PCI: return "PCI"; 81 | case DRM_BUS_USB: return "USB"; 82 | case DRM_BUS_PLATFORM: return "platform"; 83 | case DRM_BUS_HOST1X: return "host1x"; 84 | default: return "unknown"; 85 | } 86 | } 87 | 88 | static const char *node_type_str(int type) 89 | { 90 | switch (type) { 91 | case DRM_NODE_PRIMARY: 92 | return "primary"; 93 | case DRM_NODE_CONTROL: 94 | return "control"; 95 | case DRM_NODE_RENDER: 96 | return "render"; 97 | default: 98 | return "unknown"; 99 | } 100 | } 101 | 102 | static void print_available_nodes(int available_nodes) 103 | { 104 | bool first = true; 105 | for (int i = 0; i < DRM_NODE_MAX; i++) { 106 | if (!(available_nodes & (1 << i))) 107 | continue; 108 | printf("%s%s", first ? "" : ", ", node_type_str(i)); 109 | first = false; 110 | } 111 | } 112 | 113 | static void print_device(struct json_object *obj) 114 | { 115 | if (!obj) 116 | return; 117 | 118 | int available_nodes = get_object_object_uint64(obj, "available_nodes"); 119 | int bus_type = get_object_object_uint64(obj, "bus_type"); 120 | struct json_object *data_obj = json_object_object_get(obj, "device_data"); 121 | 122 | printf(L_VAL "Device: %s", bustype_str(bus_type)); 123 | switch (bus_type) { 124 | case DRM_BUS_PCI:; 125 | uint16_t pci_vendor = get_object_object_uint64(data_obj, "vendor"); 126 | uint16_t pci_device = get_object_object_uint64(data_obj, "device"); 127 | printf(" %04x:%04x", pci_vendor, pci_device); 128 | #ifdef HAVE_LIBPCI 129 | struct pci_access *pci = pci_alloc(); 130 | pci_init(pci); 131 | char name[512]; 132 | if (pci_lookup_name(pci, name, sizeof(name), 133 | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, 134 | pci_vendor, pci_device)) { 135 | printf(" %s", name); 136 | } 137 | pci_cleanup(pci); 138 | #endif 139 | break; 140 | case DRM_BUS_USB:; 141 | uint16_t usb_vendor = get_object_object_uint64(data_obj, "vendor"); 142 | uint16_t usb_product = get_object_object_uint64(data_obj, "product"); 143 | printf(" %04x:%04x", usb_vendor, usb_product); 144 | break; 145 | case DRM_BUS_PLATFORM:; 146 | struct json_object *compatible_arr = 147 | json_object_object_get(data_obj, "compatible"); 148 | for (size_t i = 0; i < json_object_array_length(compatible_arr); i++) { 149 | printf(" %s", json_object_get_string( 150 | json_object_array_get_idx(compatible_arr, i))); 151 | } 152 | break; 153 | } 154 | printf("\n"); 155 | 156 | printf(L_LINE L_LAST "Available nodes: "); 157 | print_available_nodes(available_nodes); 158 | printf("\n"); 159 | } 160 | 161 | // The refresh rate provided by the mode itself is innacurate, 162 | // so we calculate it ourself. 163 | static int32_t refresh_rate(struct json_object *obj) { 164 | int clock = get_object_object_uint64(obj, "clock"); 165 | int htotal = get_object_object_uint64(obj, "htotal"); 166 | int vtotal = get_object_object_uint64(obj, "vtotal"); 167 | int vscan = get_object_object_uint64(obj, "vscan"); 168 | int flags = get_object_object_uint64(obj, "flags"); 169 | 170 | int32_t refresh = (clock * 1000000LL / htotal + 171 | vtotal / 2) / vtotal; 172 | 173 | if (flags & DRM_MODE_FLAG_INTERLACE) 174 | refresh *= 2; 175 | 176 | if (flags & DRM_MODE_FLAG_DBLSCAN) 177 | refresh /= 2; 178 | 179 | if (vscan > 1) 180 | refresh /= vscan; 181 | 182 | return refresh; 183 | } 184 | 185 | static void print_mode(struct json_object *obj) 186 | { 187 | int hdisplay = get_object_object_uint64(obj, "hdisplay"); 188 | int vdisplay = get_object_object_uint64(obj, "vdisplay"); 189 | int type = get_object_object_uint64(obj, "type"); 190 | int flags = get_object_object_uint64(obj, "flags"); 191 | 192 | printf("%"PRIu16"x%"PRIu16"@%.02f ", hdisplay, vdisplay, 193 | refresh_rate(obj) / 1000.0); 194 | 195 | if (type & DRM_MODE_TYPE_PREFERRED) 196 | printf("preferred "); 197 | if (type & DRM_MODE_TYPE_USERDEF) 198 | printf("userdef "); 199 | if (type & DRM_MODE_TYPE_DRIVER) 200 | printf("driver "); 201 | 202 | if (flags & DRM_MODE_FLAG_PHSYNC) 203 | printf("phsync "); 204 | if (flags & DRM_MODE_FLAG_NHSYNC) 205 | printf("nhsync "); 206 | if (flags & DRM_MODE_FLAG_PVSYNC) 207 | printf("pvsync "); 208 | if (flags & DRM_MODE_FLAG_NVSYNC) 209 | printf("nvsync "); 210 | if (flags & DRM_MODE_FLAG_INTERLACE) 211 | printf("interlace "); 212 | if (flags & DRM_MODE_FLAG_DBLSCAN) 213 | printf("dblscan "); 214 | if (flags & DRM_MODE_FLAG_CSYNC) 215 | printf("csync "); 216 | if (flags & DRM_MODE_FLAG_PCSYNC) 217 | printf("pcsync "); 218 | if (flags & DRM_MODE_FLAG_NCSYNC) 219 | printf("nvsync "); 220 | if (flags & DRM_MODE_FLAG_HSKEW) 221 | printf("hskew "); 222 | if (flags & DRM_MODE_FLAG_DBLCLK) 223 | printf("dblclk "); 224 | if (flags & DRM_MODE_FLAG_CLKDIV2) 225 | printf("clkdiv2 "); 226 | 227 | switch (flags & DRM_MODE_FLAG_PIC_AR_MASK) { 228 | case DRM_MODE_FLAG_PIC_AR_NONE: 229 | break; 230 | case DRM_MODE_FLAG_PIC_AR_4_3: 231 | printf("4:3 "); 232 | break; 233 | case DRM_MODE_FLAG_PIC_AR_16_9: 234 | printf("16:9 "); 235 | break; 236 | case DRM_MODE_FLAG_PIC_AR_64_27: 237 | printf("64:27 "); 238 | break; 239 | case DRM_MODE_FLAG_PIC_AR_256_135: 240 | printf("256:135 "); 241 | break; 242 | } 243 | 244 | switch (flags & DRM_MODE_FLAG_3D_MASK) { 245 | case DRM_MODE_FLAG_3D_NONE: 246 | break; 247 | case DRM_MODE_FLAG_3D_FRAME_PACKING: 248 | printf("3d-frame-packing "); 249 | break; 250 | case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: 251 | printf("3d-field-alternative "); 252 | break; 253 | case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: 254 | printf("3d-line-alternative "); 255 | break; 256 | case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: 257 | printf("3d-side-by-side-full "); 258 | break; 259 | case DRM_MODE_FLAG_3D_L_DEPTH: 260 | printf("3d-l-depth "); 261 | break; 262 | case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: 263 | printf("3d-l-depth-gfx-gfx-depth "); 264 | break; 265 | case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: 266 | printf("3d-top-and-bottom "); 267 | break; 268 | case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: 269 | printf("3d-side-by-side-half "); 270 | break; 271 | } 272 | } 273 | 274 | /* Replace well-known constants with strings */ 275 | static const char *u64_str(uint64_t val) 276 | { 277 | switch (val) { 278 | case INT8_MAX: return "INT8_MAX"; 279 | case UINT8_MAX: return "UINT8_MAX"; 280 | case INT16_MAX: return "INT16_MAX"; 281 | case UINT16_MAX: return "UINT16_MAX"; 282 | case INT32_MAX: return "INT32_MAX"; 283 | case UINT32_MAX: return "UINT32_MAX"; 284 | case INT64_MAX: return "INT64_MAX"; 285 | case UINT64_MAX: return "UINT64_MAX"; 286 | default: return NULL; 287 | } 288 | } 289 | 290 | /* Replace well-known constants with strings */ 291 | static const char *i64_str(int64_t val) 292 | { 293 | switch (val) { 294 | case INT8_MIN: return "INT8_MIN"; 295 | case INT16_MIN: return "INT16_MIN"; 296 | case INT32_MIN: return "INT32_MIN"; 297 | case INT64_MIN: return "INT64_MIN"; 298 | case INT8_MAX: return "INT8_MAX"; 299 | case UINT8_MAX: return "UINT8_MAX"; 300 | case INT16_MAX: return "INT16_MAX"; 301 | case UINT16_MAX: return "UINT16_MAX"; 302 | case INT32_MAX: return "INT32_MAX"; 303 | case UINT32_MAX: return "UINT32_MAX"; 304 | case INT64_MAX: return "INT64_MAX"; 305 | default: return NULL; 306 | } 307 | } 308 | 309 | static const char *obj_str(uint32_t type) 310 | { 311 | switch (type) { 312 | case DRM_MODE_OBJECT_CRTC: return "CRTC"; 313 | case DRM_MODE_OBJECT_CONNECTOR: return "connector"; 314 | case DRM_MODE_OBJECT_ENCODER: return "encoder"; 315 | case DRM_MODE_OBJECT_MODE: return "mode"; 316 | case DRM_MODE_OBJECT_PROPERTY: return "property"; 317 | case DRM_MODE_OBJECT_FB: return "framebuffer"; 318 | case DRM_MODE_OBJECT_BLOB: return "blob"; 319 | case DRM_MODE_OBJECT_PLANE: return "plane"; 320 | case DRM_MODE_OBJECT_ANY: return "any"; 321 | default: return "unknown"; 322 | } 323 | } 324 | 325 | static void print_in_formats(struct json_object *arr, const char *prefix) 326 | { 327 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 328 | bool last = i == json_object_array_length(arr) - 1; 329 | struct json_object *mod_obj = json_object_array_get_idx(arr, i); 330 | uint64_t mod = get_object_object_uint64(mod_obj, "modifier"); 331 | struct json_object *formats_arr = 332 | json_object_object_get(mod_obj, "formats"); 333 | 334 | printf("%s%s", prefix, last ? L_LAST : L_VAL); 335 | print_modifier(mod); 336 | printf("\n"); 337 | for (size_t j = 0; j < json_object_array_length(formats_arr); ++j) { 338 | bool fmt_last = j == json_object_array_length(formats_arr) - 1; 339 | uint32_t fmt = json_object_get_uint64( 340 | json_object_array_get_idx(formats_arr, j)); 341 | printf("%s%s%s%s (0x%08"PRIx32")\n", prefix, last ? L_GAP : L_LINE, 342 | fmt_last ? L_LAST : L_VAL, format_str(fmt), fmt); 343 | } 344 | } 345 | } 346 | 347 | static void print_mode_id(struct json_object *obj, const char *prefix) 348 | { 349 | printf("%s" L_LAST, prefix); 350 | print_mode(obj); 351 | printf("\n"); 352 | } 353 | 354 | static void print_writeback_pixel_formats(struct json_object *arr, 355 | const char *prefix) 356 | { 357 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 358 | bool last = i == json_object_array_length(arr) - 1; 359 | uint32_t fmt = json_object_get_uint64(json_object_array_get_idx(arr, i)); 360 | printf("%s%s%s (0x%08"PRIx32")\n", prefix, last ? L_LAST : L_VAL, 361 | format_str(fmt), fmt); 362 | } 363 | } 364 | 365 | static void print_path(struct json_object *obj, const char *prefix) 366 | { 367 | printf("%s" L_LAST "%s\n", prefix, json_object_get_string(obj)); 368 | } 369 | 370 | static void print_fb(struct json_object *obj, const char *prefix) 371 | { 372 | uint32_t id = get_object_object_uint64(obj, "id"); 373 | uint32_t width = get_object_object_uint64(obj, "width"); 374 | uint32_t height = get_object_object_uint64(obj, "height"); 375 | 376 | struct json_object *pitch_obj = json_object_object_get(obj, "pitch"); 377 | struct json_object *bpp_obj = json_object_object_get(obj, "bpp"); 378 | struct json_object *depth_obj = json_object_object_get(obj, "depth"); 379 | struct json_object *format_obj = json_object_object_get(obj, "format"); 380 | struct json_object *modifier_obj = json_object_object_get(obj, "modifier"); 381 | struct json_object *planes_arr = json_object_object_get(obj, "planes"); 382 | bool has_legacy = pitch_obj && bpp_obj && depth_obj; 383 | 384 | printf("%s" L_VAL "Object ID: %"PRIu32"\n", prefix, id); 385 | printf("%s%sSize: %"PRIu32"x%"PRIu32"\n", prefix, 386 | (has_legacy || format_obj) ? L_VAL : L_LAST, 387 | width, height); 388 | 389 | if (has_legacy) { 390 | printf("%s" L_VAL "Pitch: %"PRIu32" bytes\n", prefix, 391 | (uint32_t)json_object_get_uint64(pitch_obj)); 392 | printf("%s" L_VAL "Bits per pixel: %"PRIu32"\n", prefix, 393 | (uint32_t)json_object_get_uint64(bpp_obj)); 394 | printf("%s%sDepth: %"PRIu32"\n", prefix, format_obj ? L_VAL : L_LAST, 395 | (uint32_t)json_object_get_uint64(depth_obj)); 396 | } 397 | if (format_obj) { 398 | bool last = !modifier_obj && !planes_arr; 399 | uint32_t fmt = (uint32_t)json_object_get_uint64(format_obj); 400 | printf("%s%sFormat: %s (0x%08"PRIx32")\n", prefix, 401 | last ? L_LAST : L_VAL, format_str(fmt), fmt); 402 | } 403 | if (modifier_obj) { 404 | uint64_t mod = json_object_get_uint64(modifier_obj); 405 | printf("%s%sModifier: ", prefix, planes_arr ? L_VAL : L_LAST); 406 | print_modifier(mod); 407 | printf("\n"); 408 | } 409 | if (planes_arr) { 410 | printf("%s" L_LAST "Planes:\n", prefix); 411 | for (size_t i = 0; i < json_object_array_length(planes_arr); ++i) { 412 | bool last = i == json_object_array_length(planes_arr) - 1; 413 | struct json_object *plane_obj = 414 | json_object_array_get_idx(planes_arr, i); 415 | uint64_t offset = get_object_object_uint64(plane_obj, "offset"); 416 | uint64_t pitch = get_object_object_uint64(plane_obj, "pitch"); 417 | printf("%s" L_GAP "%sPlane %zu: " 418 | "offset = %"PRIu64", pitch = %"PRIu64" bytes\n", 419 | prefix, last ? L_LAST : L_VAL, i, offset, pitch); 420 | } 421 | } 422 | } 423 | 424 | static void print_properties(struct json_object *obj, const char *prefix) 425 | { 426 | printf("%s" L_LAST "Properties\n", prefix); 427 | 428 | struct json_object_iter iter; 429 | json_object_object_foreachC(obj, iter) { 430 | bool last = !iter.entry->next; 431 | const char *prop_name = iter.key; 432 | struct json_object *prop_obj = iter.val; 433 | 434 | char sub_prefix[strlen(prefix) + 2 * strlen(L_VAL) + 1]; 435 | snprintf(sub_prefix, sizeof(sub_prefix), "%s" L_GAP "%s", 436 | prefix, last ? L_GAP : L_LINE); 437 | 438 | uint32_t flags = get_object_object_uint64(prop_obj, "flags"); 439 | uint32_t type = flags & (DRM_MODE_PROP_LEGACY_TYPE | 440 | DRM_MODE_PROP_EXTENDED_TYPE); 441 | bool atomic = flags & DRM_MODE_PROP_ATOMIC; 442 | bool immutable = flags & DRM_MODE_PROP_IMMUTABLE; 443 | 444 | printf("%s" L_GAP "%s\"%s\"", prefix, last ? L_LAST : L_VAL, prop_name); 445 | if (atomic && immutable) 446 | printf(" (atomic, immutable)"); 447 | else if (atomic) 448 | printf(" (atomic)"); 449 | else if (immutable) 450 | printf(" (immutable)"); 451 | 452 | printf(": "); 453 | 454 | uint64_t raw_val = get_object_object_uint64(prop_obj, "raw_value"); 455 | struct json_object *spec_obj = json_object_object_get(prop_obj, "spec"); 456 | struct json_object *val_obj = json_object_object_get(prop_obj, "value"); 457 | struct json_object *data_obj = json_object_object_get(prop_obj, "data"); 458 | bool first; 459 | switch (type) { 460 | case DRM_MODE_PROP_RANGE:; 461 | uint64_t min = get_object_object_uint64(spec_obj, "min"); 462 | uint64_t max = get_object_object_uint64(spec_obj, "max"); 463 | const char *min_str = u64_str(min); 464 | const char *max_str = u64_str(max); 465 | 466 | if (min_str) 467 | printf("range [%s, ", min_str); 468 | else 469 | printf("range [%"PRIu64", ", min); 470 | 471 | if (max_str) 472 | printf("%s]", max_str); 473 | else 474 | printf("%"PRIu64"]", max); 475 | 476 | if (data_obj != NULL) 477 | printf(" = %"PRIu64"\n", json_object_get_uint64(data_obj)); 478 | else 479 | printf(" = %"PRIu64"\n", json_object_get_uint64(val_obj)); 480 | break; 481 | case DRM_MODE_PROP_ENUM: 482 | printf("enum {"); 483 | const char *val_name = NULL; 484 | first = true; 485 | for (size_t j = 0; j < json_object_array_length(spec_obj); ++j) { 486 | struct json_object *item_obj = 487 | json_object_array_get_idx(spec_obj, j); 488 | const char *item_name = 489 | get_object_object_string(item_obj, "name"); 490 | uint64_t item_value = 491 | get_object_object_uint64(item_obj, "value"); 492 | 493 | if (raw_val == item_value) { 494 | val_name = item_name; 495 | } 496 | 497 | printf("%s%s", first ? "" : ", ", item_name); 498 | first = false; 499 | } 500 | printf("} = "); 501 | 502 | if (val_name) { 503 | printf("%s\n", val_name); 504 | } else { 505 | printf("invalid (%"PRIu64")\n", raw_val); 506 | } 507 | break; 508 | case DRM_MODE_PROP_BLOB:; 509 | printf("blob = %" PRIu64 "\n", raw_val); 510 | if (!data_obj) 511 | break; 512 | if (strcmp(prop_name, "IN_FORMATS") == 0) 513 | print_in_formats(data_obj, sub_prefix); 514 | else if (strcmp(prop_name, "MODE_ID") == 0) 515 | print_mode_id(data_obj, sub_prefix); 516 | else if (strcmp(prop_name, "WRITEBACK_PIXEL_FORMATS") == 0) 517 | print_writeback_pixel_formats(data_obj, sub_prefix); 518 | else if (strcmp(prop_name, "PATH") == 0) 519 | print_path(data_obj, sub_prefix); 520 | break; 521 | case DRM_MODE_PROP_BITMASK: 522 | printf("bitmask {"); 523 | first = true; 524 | for (size_t j = 0; j < json_object_array_length(spec_obj); ++j) { 525 | struct json_object *item_obj = 526 | json_object_array_get_idx(spec_obj, j); 527 | const char *item_name = 528 | get_object_object_string(item_obj, "name"); 529 | 530 | printf("%s%s", first ? "" : ", ", item_name); 531 | first = false; 532 | } 533 | printf("} = ("); 534 | 535 | first = true; 536 | for (size_t j = 0; j < json_object_array_length(spec_obj); ++j) { 537 | struct json_object *item_obj = 538 | json_object_array_get_idx(spec_obj, j); 539 | const char *item_name = 540 | get_object_object_string(item_obj, "name"); 541 | uint64_t item_value = 542 | get_object_object_uint64(item_obj, "value"); 543 | uint64_t item_bit = 1 << item_value; 544 | if ((item_bit & raw_val) != item_bit) { 545 | continue; 546 | } 547 | 548 | printf("%s%s", first ? "" : " | ", item_name); 549 | first = false; 550 | } 551 | 552 | printf(")\n"); 553 | break; 554 | case DRM_MODE_PROP_OBJECT:; 555 | uint32_t obj_type = json_object_get_uint64(spec_obj); 556 | printf("object %s = %"PRIu64"\n", obj_str(obj_type), raw_val); 557 | if (!data_obj) 558 | break; 559 | if (strcmp(prop_name, "FB_ID") == 0) 560 | print_fb(data_obj, sub_prefix); 561 | break; 562 | case DRM_MODE_PROP_SIGNED_RANGE:; 563 | int64_t smin = 564 | json_object_get_int64(json_object_object_get(spec_obj, "min")); 565 | int64_t smax = 566 | json_object_get_int64(json_object_object_get(spec_obj, "max")); 567 | const char *smin_str = i64_str(smin); 568 | const char *smax_str = i64_str(smax); 569 | 570 | if (smin_str) 571 | printf("srange [%s, ", smin_str); 572 | else 573 | printf("srange [%"PRIi64", ", smin); 574 | 575 | if (smax_str) 576 | printf("%s]", smax_str); 577 | else 578 | printf("%"PRIi64"]", smax); 579 | 580 | if (data_obj != NULL) 581 | printf(" = %"PRIi64"\n", json_object_get_int64(data_obj)); 582 | else 583 | printf(" = %"PRIi64"\n", json_object_get_int64(val_obj)); 584 | break; 585 | default: 586 | printf("unknown type (%"PRIu32") = %"PRIu64"\n", type, raw_val); 587 | } 588 | } 589 | } 590 | 591 | static void print_modes(struct json_object *arr, const char *prefix) 592 | { 593 | if (json_object_array_length(arr) == 0) { 594 | return; 595 | } 596 | 597 | printf("%s" L_VAL "Modes\n", prefix); 598 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 599 | bool last = i == json_object_array_length(arr) - 1; 600 | printf("%s" L_LINE "%s", prefix, last ? L_LAST : L_VAL); 601 | print_mode(json_object_array_get_idx(arr, i)); 602 | printf("\n"); 603 | } 604 | } 605 | 606 | static const char *conn_name(uint32_t type) 607 | { 608 | switch (type) { 609 | case DRM_MODE_CONNECTOR_Unknown: return "unknown"; 610 | case DRM_MODE_CONNECTOR_VGA: return "VGA"; 611 | case DRM_MODE_CONNECTOR_DVII: return "DVI-I"; 612 | case DRM_MODE_CONNECTOR_DVID: return "DVI-D"; 613 | case DRM_MODE_CONNECTOR_DVIA: return "DVI-A"; 614 | case DRM_MODE_CONNECTOR_Composite: return "composite"; 615 | case DRM_MODE_CONNECTOR_SVIDEO: return "S-VIDEO"; 616 | case DRM_MODE_CONNECTOR_LVDS: return "LVDS"; 617 | case DRM_MODE_CONNECTOR_Component: return "component"; 618 | case DRM_MODE_CONNECTOR_9PinDIN: return "DIN"; 619 | case DRM_MODE_CONNECTOR_DisplayPort: return "DisplayPort"; 620 | case DRM_MODE_CONNECTOR_HDMIA: return "HDMI-A"; 621 | case DRM_MODE_CONNECTOR_HDMIB: return "HDMI-B"; 622 | case DRM_MODE_CONNECTOR_TV: return "TV"; 623 | case DRM_MODE_CONNECTOR_eDP: return "eDP"; 624 | case DRM_MODE_CONNECTOR_VIRTUAL: return "virtual"; 625 | case DRM_MODE_CONNECTOR_DSI: return "DSI"; 626 | case DRM_MODE_CONNECTOR_DPI: return "DPI"; 627 | case DRM_MODE_CONNECTOR_WRITEBACK: return "writeback"; 628 | default: return "unknown"; 629 | } 630 | } 631 | 632 | static const char *conn_status(drmModeConnection conn) 633 | { 634 | switch (conn) { 635 | case DRM_MODE_CONNECTED: return "connected"; 636 | case DRM_MODE_DISCONNECTED: return "disconnected"; 637 | case DRM_MODE_UNKNOWNCONNECTION: return "unknown"; 638 | default: return "unknown"; 639 | } 640 | } 641 | 642 | static const char *conn_subpixel(drmModeSubPixel subpixel) 643 | { 644 | switch (subpixel) { 645 | case DRM_MODE_SUBPIXEL_UNKNOWN: return "unknown"; 646 | case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: return "horizontal RGB"; 647 | case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: return "horizontal BGR"; 648 | case DRM_MODE_SUBPIXEL_VERTICAL_RGB: return "vertical RGB"; 649 | case DRM_MODE_SUBPIXEL_VERTICAL_BGR: return "vertical BGR"; 650 | case DRM_MODE_SUBPIXEL_NONE: return "none"; 651 | default: return "unknown"; 652 | } 653 | } 654 | 655 | static ssize_t find_encoder_index(struct json_object *encoders_arr, 656 | uint32_t enc_id) 657 | { 658 | for (size_t i = 0; i < json_object_array_length(encoders_arr); ++i) { 659 | struct json_object *obj = json_object_array_get_idx(encoders_arr, i); 660 | uint32_t id = get_object_object_uint64(obj, "id"); 661 | if (enc_id == id) { 662 | return i; 663 | } 664 | } 665 | return -1; 666 | } 667 | 668 | static void print_connectors(struct json_object *arr, 669 | struct json_object *encoders_arr) 670 | { 671 | printf(L_VAL "Connectors\n"); 672 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 673 | struct json_object *obj = json_object_array_get_idx(arr, i); 674 | bool last = i == json_object_array_length(arr) - 1; 675 | 676 | uint32_t id = get_object_object_uint64(obj, "id"); 677 | uint32_t type = get_object_object_uint64(obj, "type"); 678 | drmModeConnection status = get_object_object_uint64(obj, "status"); 679 | uint32_t phy_width = get_object_object_uint64(obj, "phy_width"); 680 | uint32_t phy_height = get_object_object_uint64(obj, "phy_height"); 681 | drmModeSubPixel subpixel = get_object_object_uint64(obj, "subpixel"); 682 | struct json_object *encs_arr = json_object_object_get(obj, "encoders"); 683 | struct json_object *modes_arr = json_object_object_get(obj, "modes"); 684 | struct json_object *props_obj = json_object_object_get(obj, "properties"); 685 | 686 | printf(L_LINE "%sConnector %zu\n", last ? L_LAST : L_VAL, i); 687 | 688 | printf(L_LINE "%s" L_VAL "Object ID: %"PRIu32"\n", 689 | last ? L_GAP : L_LINE, id); 690 | printf(L_LINE "%s" L_VAL "Type: %s\n", last ? L_GAP : L_LINE, 691 | conn_name(type)); 692 | printf(L_LINE "%s" L_VAL "Status: %s\n", last ? L_GAP : L_LINE, 693 | conn_status(status)); 694 | if (status != DRM_MODE_DISCONNECTED) { 695 | printf(L_LINE "%s" L_VAL "Physical size: %"PRIu32"x%"PRIu32" mm\n", 696 | last ? L_GAP : L_LINE, phy_width, phy_height); 697 | printf(L_LINE "%s" L_VAL "Subpixel: %s\n", last ? L_GAP : L_LINE, 698 | conn_subpixel(subpixel)); 699 | } 700 | 701 | bool first = true; 702 | printf(L_LINE "%s" L_VAL "Encoders: {", last ? L_GAP : L_LINE); 703 | for (size_t j = 0; j < json_object_array_length(encs_arr); ++j) { 704 | uint32_t enc_id = json_object_get_uint64( 705 | json_object_array_get_idx(encs_arr, j)); 706 | printf("%s%zi", first ? "" : ", ", 707 | find_encoder_index(encoders_arr, enc_id)); 708 | first = false; 709 | } 710 | printf("}\n"); 711 | 712 | print_modes(modes_arr, last ? L_LINE L_GAP : L_LINE L_LINE); 713 | print_properties(props_obj, last ? L_LINE L_GAP : L_LINE L_LINE); 714 | } 715 | } 716 | 717 | static const char *encoder_str(uint32_t type) 718 | { 719 | switch (type) { 720 | case DRM_MODE_ENCODER_NONE: return "none"; 721 | case DRM_MODE_ENCODER_DAC: return "DAC"; 722 | case DRM_MODE_ENCODER_TMDS: return "TMDS"; 723 | case DRM_MODE_ENCODER_LVDS: return "LVDS"; 724 | case DRM_MODE_ENCODER_TVDAC: return "TV DAC"; 725 | case DRM_MODE_ENCODER_VIRTUAL: return "virtual"; 726 | case DRM_MODE_ENCODER_DSI: return "DSI"; 727 | case DRM_MODE_ENCODER_DPMST: return "DP MST"; 728 | case DRM_MODE_ENCODER_DPI: return "DPI"; 729 | default: return "unknown"; 730 | } 731 | } 732 | 733 | static void print_bitmask(uint32_t mask) 734 | { 735 | bool first = true; 736 | printf("{"); 737 | for (uint32_t i = 0; i < 31; ++i) { 738 | if (!(mask & (1 << i))) 739 | continue; 740 | 741 | printf("%s%"PRIu32, first ? "" : ", ", i); 742 | first = false; 743 | } 744 | printf("}"); 745 | } 746 | 747 | static void print_encoders(struct json_object *arr) 748 | { 749 | printf(L_VAL "Encoders\n"); 750 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 751 | struct json_object *obj = json_object_array_get_idx(arr, i); 752 | bool last = i == json_object_array_length(arr) - 1; 753 | 754 | uint32_t id = get_object_object_uint64(obj, "id"); 755 | uint32_t type = get_object_object_uint64(obj, "type"); 756 | uint32_t crtcs = get_object_object_uint64(obj, "possible_crtcs"); 757 | uint32_t clones = get_object_object_uint64(obj, "possible_clones"); 758 | 759 | printf(L_LINE "%sEncoder %zu\n", last ? L_LAST : L_VAL, i); 760 | 761 | printf(L_LINE "%s" L_VAL "Object ID: %"PRIu32"\n", 762 | last ? L_GAP : L_LINE, id); 763 | printf(L_LINE "%s" L_VAL "Type: %s\n", last ? L_GAP : L_LINE, 764 | encoder_str(type)); 765 | 766 | printf(L_LINE "%s" L_VAL "CRTCS: ", last ? L_GAP : L_LINE); 767 | print_bitmask(crtcs); 768 | printf("\n"); 769 | 770 | printf(L_LINE "%s" L_LAST "Clones: ", last ? L_GAP : L_LINE); 771 | print_bitmask(clones); 772 | printf("\n"); 773 | } 774 | } 775 | 776 | static void print_crtcs(struct json_object *arr) 777 | { 778 | printf(L_VAL "CRTCs\n"); 779 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 780 | struct json_object *obj = json_object_array_get_idx(arr, i); 781 | bool last = i == json_object_array_length(arr) - 1; 782 | const char *prefix = last ? L_LINE L_GAP : L_LINE L_LINE; 783 | 784 | uint32_t id = get_object_object_uint64(obj, "id"); 785 | struct json_object *props_obj = json_object_object_get(obj, "properties"); 786 | 787 | printf(L_LINE "%sCRTC %zu\n", last ? L_LAST : L_VAL, i); 788 | 789 | printf("%s" L_VAL "Object ID: %"PRIu32"\n", prefix, id); 790 | 791 | printf("%s" L_VAL "Legacy info\n", prefix); 792 | 793 | struct json_object *mode_obj = json_object_object_get(obj, "mode"); 794 | if (mode_obj) { 795 | printf("%s" L_LINE L_VAL "Mode: ", prefix); 796 | print_mode(mode_obj); 797 | printf("\n"); 798 | } 799 | 800 | struct json_object *gamma_size_obj = 801 | json_object_object_get(obj, "gamma_size"); 802 | printf("%s" L_LINE L_LAST "Gamma size: %d\n", prefix, 803 | json_object_get_int(gamma_size_obj)); 804 | 805 | print_properties(props_obj, prefix); 806 | } 807 | } 808 | 809 | static void print_planes(struct json_object *arr) 810 | { 811 | printf(L_LAST "Planes\n"); 812 | for (size_t i = 0; i < json_object_array_length(arr); ++i) { 813 | struct json_object *obj = json_object_array_get_idx(arr, i); 814 | bool last = i == json_object_array_length(arr) - 1; 815 | const char *prefix = last ? L_GAP L_GAP : L_GAP L_LINE; 816 | 817 | uint32_t id = get_object_object_uint64(obj, "id"); 818 | uint32_t crtcs = get_object_object_uint64(obj, "possible_crtcs"); 819 | uint32_t fb_id = get_object_object_uint64(obj, "fb_id"); 820 | struct json_object *fb_obj = json_object_object_get(obj, "fb"); 821 | struct json_object *formats_arr = json_object_object_get(obj, "formats"); 822 | struct json_object *props_obj = json_object_object_get(obj, "properties"); 823 | 824 | printf(L_GAP "%sPlane %zu\n", last ? L_LAST : L_VAL, i); 825 | 826 | printf("%s" L_VAL "Object ID: %"PRIu32"\n", prefix, id); 827 | printf("%s" L_VAL "CRTCs: ", prefix); 828 | print_bitmask(crtcs); 829 | printf("\n"); 830 | 831 | printf("%s" L_VAL "Legacy info\n", prefix); 832 | 833 | char sub_prefix[strlen(prefix) + 2 * strlen(L_LINE) + 1]; 834 | snprintf(sub_prefix, sizeof(sub_prefix), "%s" L_LINE L_LINE, prefix); 835 | 836 | printf("%s" L_LINE L_VAL "FB ID: %"PRIu32"\n", prefix, fb_id); 837 | if (fb_obj) { 838 | print_fb(fb_obj, sub_prefix); 839 | } 840 | 841 | printf("%s" L_LINE L_LAST "Formats:\n", prefix); 842 | for (size_t j = 0; j < json_object_array_length(formats_arr); ++j) { 843 | uint32_t fmt = json_object_get_uint64( 844 | json_object_array_get_idx(formats_arr, j)); 845 | bool fmt_last = j == json_object_array_length(formats_arr) - 1; 846 | 847 | printf("%s" L_LINE L_GAP "%s%s (0x%08"PRIx32")\n", prefix, 848 | fmt_last ? L_LAST : L_VAL, 849 | format_str(fmt), fmt); 850 | } 851 | 852 | print_properties(props_obj, prefix); 853 | } 854 | } 855 | 856 | static void print_node(const char *path, struct json_object *obj) 857 | { 858 | printf("Node: %s\n", path); 859 | print_driver(json_object_object_get(obj, "driver")); 860 | print_device(json_object_object_get(obj, "device")); 861 | 862 | printf(L_VAL "Framebuffer size\n"); 863 | struct json_object *fb_size_obj = json_object_object_get(obj, "fb_size"); 864 | printf(L_LINE L_VAL "Width: [%"PRIu64", %"PRIu64"]\n", 865 | get_object_object_uint64(fb_size_obj, "min_width"), 866 | get_object_object_uint64(fb_size_obj, "max_width")); 867 | printf(L_LINE L_LAST "Height: [%"PRIu64", %"PRIu64"]\n", 868 | get_object_object_uint64(fb_size_obj, "min_height"), 869 | get_object_object_uint64(fb_size_obj, "max_height")); 870 | 871 | struct json_object *encs_arr = json_object_object_get(obj, "encoders"); 872 | print_connectors(json_object_object_get(obj, "connectors"), encs_arr); 873 | print_encoders(encs_arr); 874 | print_crtcs(json_object_object_get(obj, "crtcs")); 875 | print_planes(json_object_object_get(obj, "planes")); 876 | } 877 | 878 | void print_drm(struct json_object *obj) 879 | { 880 | json_object_object_foreach(obj, path, node_obj) { 881 | print_node(path, node_obj); 882 | } 883 | } 884 | --------------------------------------------------------------------------------