• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <algorithm>
2 #include <cinttypes>
3 #include <cstdio>
4 #include <iostream>
5 #include <string>
6 #include <unistd.h>
7 
8 #include <kms++/kms++.h>
9 #include <kms++util/kms++util.h>
10 
11 using namespace std;
12 using namespace kms;
13 
14 static struct {
15 	bool print_props;
16 	bool print_modes;
17 	bool print_list;
18 	bool x_modeline;
19 } s_opts;
20 
format_mode(const Videomode & m,unsigned idx)21 static string format_mode(const Videomode& m, unsigned idx)
22 {
23 	string str;
24 
25 	str = sformat("  %2u ", idx);
26 
27 	if (s_opts.x_modeline) {
28 		str += sformat("%12s %6u %4u %4u %4u %4u %4u %4u %4u %4u  %2u %#x %#x",
29 			       m.name.c_str(),
30 			       m.clock,
31 			       m.hdisplay, m.hsync_start, m.hsync_end, m.htotal,
32 			       m.vdisplay, m.vsync_start, m.vsync_end, m.vtotal,
33 			       m.vrefresh,
34 			       m.flags,
35 			       m.type);
36 	} else {
37 		string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
38 		string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
39 
40 		str += sformat("%-12s %7.3f %-16s %-16s %2u (%.2f) %#10x %#6x",
41 			       m.name.c_str(),
42 			       m.clock / 1000.0,
43 			       h.c_str(), v.c_str(),
44 			       m.vrefresh, m.calculated_vrefresh(),
45 			       m.flags,
46 			       m.type);
47 	}
48 
49 	return str;
50 }
51 
format_mode_short(const Videomode & m)52 static string format_mode_short(const Videomode& m)
53 {
54 	string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
55 	string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
56 
57 	return sformat("%s %.3f %s %s %u (%.2f)",
58 		       m.name.c_str(),
59 		       m.clock / 1000.0,
60 		       h.c_str(), v.c_str(),
61 		       m.vrefresh, m.calculated_vrefresh());
62 }
63 
format_connector(Connector & c)64 static string format_connector(Connector& c)
65 {
66 	string str;
67 
68 	str = sformat("Connector %u (%u) %s",
69 		      c.idx(), c.id(), c.fullname().c_str());
70 
71 	if (c.connected())
72 		str += " (connected)";
73 	else
74 		str += " (disconnected)";
75 
76 	return str;
77 }
78 
format_encoder(Encoder & e)79 static string format_encoder(Encoder& e)
80 {
81 	return sformat("Encoder %u (%u) %s",
82 		       e.idx(), e.id(), e.get_encoder_type().c_str());
83 }
84 
format_crtc(Crtc & c)85 static string format_crtc(Crtc& c)
86 {
87 	string str;
88 
89 	str = sformat("Crtc %u (%u)", c.idx(), c.id());
90 
91 	if (c.mode_valid())
92 		str += " " + format_mode_short(c.mode());
93 
94 	return str;
95 }
96 
format_plane(Plane & p)97 static string format_plane(Plane& p)
98 {
99 	string str;
100 
101 	str = sformat("Plane %u (%u)", p.idx(), p.id());
102 
103 	if (p.fb_id())
104 		str += sformat(" fb-id: %u", p.fb_id());
105 
106 	string crtcs = join<Crtc*>(p.get_possible_crtcs(), " ", [](Crtc* crtc) { return to_string(crtc->idx()); });
107 
108 	str += sformat(" (crtcs: %s)", crtcs.c_str());
109 
110 	if (p.card().has_atomic()) {
111 		str += sformat(" %u,%u %ux%u -> %u,%u %ux%u",
112 			       (uint32_t)p.get_prop_value("SRC_X") >> 16,
113 			       (uint32_t)p.get_prop_value("SRC_Y") >> 16,
114 			       (uint32_t)p.get_prop_value("SRC_W") >> 16,
115 			       (uint32_t)p.get_prop_value("SRC_H") >> 16,
116 			       (uint32_t)p.get_prop_value("CRTC_X"),
117 			       (uint32_t)p.get_prop_value("CRTC_Y"),
118 			       (uint32_t)p.get_prop_value("CRTC_W"),
119 			       (uint32_t)p.get_prop_value("CRTC_H"));
120 	}
121 
122 	string fmts = join<PixelFormat>(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); });
123 
124 	str += sformat(" (%s)", fmts.c_str());
125 
126 	return str;
127 }
128 
format_fb(Framebuffer & fb)129 static string format_fb(Framebuffer& fb)
130 {
131 	return sformat("FB %u %ux%u",
132 		       fb.id(), fb.width(), fb.height());
133 }
134 
format_property(const Property * prop,uint64_t val)135 static string format_property(const Property* prop, uint64_t val)
136 {
137 	string ret = sformat("%s = ", prop->name().c_str());
138 
139 	switch (prop->type()) {
140 	case PropertyType::Bitmask:
141 	{
142 		vector<string> v, vall;
143 
144 		for (auto kvp : prop->get_enums()) {
145 			if (val & (1 << kvp.first))
146 				v.push_back(kvp.second);
147 			vall.push_back(sformat("%s=0x%x", kvp.second.c_str(), 1 << kvp.first));
148 		}
149 
150 		ret += sformat("0x%" PRIx64 " (%s) [%s]", val, join(v, "|").c_str(), join(vall, "|").c_str());
151 
152 		break;
153 	}
154 
155 	case PropertyType::Blob:
156 	{
157 		uint32_t blob_id = (uint32_t)val;
158 
159 		if (blob_id) {
160 			Blob blob(prop->card(), blob_id);
161 			auto data = blob.data();
162 
163 			ret += sformat("blob-id %u len %zu", blob_id, data.size());
164 		} else {
165 			ret += sformat("blob-id %u", blob_id);
166 		}
167 
168 		break;
169 	}
170 
171 	case PropertyType::Enum:
172 	{
173 		string cur;
174 		vector<string> vall;
175 
176 		for (auto kvp : prop->get_enums()) {
177 			if (val == kvp.first)
178 				cur = kvp.second;
179 			vall.push_back(sformat("%s=%" PRIu64, kvp.second.c_str(), kvp.first));
180 		}
181 
182 		ret += sformat("%" PRIu64 " (%s) [%s]", val, cur.c_str(), join(vall, "|").c_str());
183 
184 		break;
185 	}
186 
187 	case PropertyType::Object:
188 	{
189 		ret += sformat("object id %u", (uint32_t)val);
190 		break;
191 	}
192 
193 	case PropertyType::Range:
194 	{
195 		auto values = prop->get_values();
196 
197 		ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]",
198 			       val, values[0], values[1]);
199 
200 		break;
201 	}
202 
203 	case PropertyType::SignedRange:
204 	{
205 		auto values = prop->get_values();
206 
207 		ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]",
208 			       (int64_t)val, (int64_t)values[0], (int64_t)values[1]);
209 
210 		break;
211 	}
212 
213 	}
214 
215 	if (prop->is_pending())
216 		ret += " (pending)";
217 	if (prop->is_immutable())
218 		ret += " (immutable)";
219 
220 	return ret;
221 }
222 
format_props(DrmPropObject * o)223 static vector<string> format_props(DrmPropObject* o)
224 {
225 	vector<string> lines;
226 
227 	auto pmap = o->get_prop_map();
228 	for (auto pp : pmap) {
229 		const Property* p = o->card().get_prop(pp.first);
230 		lines.push_back(format_property(p, pp.second));
231 	}
232 
233 	return lines;
234 }
235 
format_ob(DrmObject * ob)236 static string format_ob(DrmObject* ob)
237 {
238 	if (auto o = dynamic_cast<Connector*>(ob))
239 		return format_connector(*o);
240 	else if (auto o = dynamic_cast<Encoder*>(ob))
241 		return format_encoder(*o);
242 	else if (auto o = dynamic_cast<Crtc*>(ob))
243 		return format_crtc(*o);
244 	else if (auto o = dynamic_cast<Plane*>(ob))
245 		return format_plane(*o);
246 	else if (auto o = dynamic_cast<Framebuffer*>(ob))
247 		return format_fb(*o);
248 	else
249 		EXIT("Unkown DRM Object type\n");
250 }
251 
252 template<class T>
filter(const vector<T> & sequence,function<bool (T)> predicate)253 vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate)
254 {
255 	vector<T> result;
256 
257 	for(auto it = sequence.begin(); it != sequence.end(); ++it)
258 		if(predicate(*it))
259 			result.push_back(*it);
260 
261 	return result;
262 }
263 
264 struct Entry
265 {
266 	string title;
267 	vector<string> lines;
268 	vector<Entry> children;
269 };
270 
add_entry(vector<Entry> & entries)271 static Entry& add_entry(vector<Entry>& entries)
272 {
273 	entries.emplace_back();
274 	return entries.back();
275 }
276 /*
277 static bool on_tty()
278 {
279 	return isatty(STDOUT_FILENO) > 0;
280 }
281 */
282 enum class TreeGlyphMode {
283 	None,
284 	ASCII,
285 	UTF8,
286 };
287 
288 static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None;
289 
290 enum class TreeGlyph {
291 	Vertical,
292 	Branch,
293 	Right,
294 	Space,
295 };
296 
297 static const map<TreeGlyph, string> glyphs_utf8 = {
298 	{ TreeGlyph::Vertical, "│ " },
299 	{ TreeGlyph::Branch, "├─" },
300 	{ TreeGlyph::Right, "└─" },
301 	{ TreeGlyph::Space, "  " },
302 
303 };
304 
305 static const map<TreeGlyph, string> glyphs_ascii = {
306 	{ TreeGlyph::Vertical, "| " },
307 	{ TreeGlyph::Branch, "|-" },
308 	{ TreeGlyph::Right, "`-" },
309 	{ TreeGlyph::Space, "  " },
310 
311 };
312 
get_glyph(TreeGlyph glyph)313 const char* get_glyph(TreeGlyph glyph)
314 {
315 	if (s_glyph_mode == TreeGlyphMode::None)
316 		return "  ";
317 
318 	const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii;
319 
320 	return glyphs.at(glyph).c_str();
321 }
322 
print_entry(const Entry & e,const string & prefix,bool is_child,bool is_last)323 static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last)
324 {
325 	string prefix1;
326 	string prefix2;
327 
328 	if (is_child) {
329 		prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch));
330 		prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical));
331 	}
332 
333 	printf("%s%s\n", prefix1.c_str(), e.title.c_str());
334 
335 	bool has_children = e.children.size() > 0;
336 
337 	string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space));
338 
339 	for (const string& str : e.lines) {
340 		string p = data_prefix + get_glyph(TreeGlyph::Space);
341 		printf("%s%s\n", p.c_str(), str.c_str());
342 	}
343 
344 	for (const Entry& child : e.children) {
345 		bool is_last = &child == &e.children.back();
346 
347 		print_entry(child, prefix2, true, is_last);
348 	}
349 }
350 
print_entries(const vector<Entry> & entries,const string & prefix)351 static void print_entries(const vector<Entry>& entries, const string& prefix)
352 {
353 	for (const Entry& e: entries) {
354 		print_entry(e, "", false, false);
355 	}
356 }
357 
358 template<class T>
append(vector<DrmObject * > & dst,const vector<T * > & src)359 static void append(vector<DrmObject*>& dst, const vector<T*>& src)
360 {
361 	dst.insert(dst.end(), src.begin(), src.end());
362 }
363 
364 
print_as_list(Card & card)365 static void print_as_list(Card& card)
366 {
367 	vector<DrmPropObject*> obs;
368 	vector<Framebuffer*> fbs;
369 
370 	for (Connector* conn : card.get_connectors()) {
371 		obs.push_back(conn);
372 	}
373 
374 	for (Encoder* enc : card.get_encoders()) {
375 		obs.push_back(enc);
376 	}
377 
378 	for (Crtc* crtc : card.get_crtcs()) {
379 		obs.push_back(crtc);
380 		if (crtc->buffer_id() && !card.has_has_universal_planes()) {
381 			Framebuffer* fb = new Framebuffer(card, crtc->buffer_id());
382 			fbs.push_back(fb);
383 		}
384 	}
385 
386 	for (Plane* plane : card.get_planes()) {
387 		obs.push_back(plane);
388 		if (plane->fb_id()) {
389 			Framebuffer* fb = new Framebuffer(card, plane->fb_id());
390 			fbs.push_back(fb);
391 		}
392 	}
393 
394 	for (DrmPropObject* ob: obs) {
395 		printf("%s\n", format_ob(ob).c_str());
396 
397 		if (s_opts.print_props) {
398 			for (string str : format_props(ob))
399 				printf("    %s\n", str.c_str());
400 		}
401 	}
402 
403 	for (Framebuffer* fb: fbs) {
404 		printf("%s\n", format_ob(fb).c_str());
405 	}
406 }
407 
print_as_tree(Card & card)408 static void print_as_tree(Card& card)
409 {
410 	vector<Entry> entries;
411 
412 	for (Connector* conn : card.get_connectors()) {
413 		Entry& e1 = add_entry(entries);
414 		e1.title = format_ob(conn);
415 		if (s_opts.print_props)
416 			e1.lines = format_props(conn);
417 
418 		for (Encoder* enc : conn->get_encoders()) {
419 
420 			Entry& e2 = add_entry(e1.children);
421 			e2.title = format_ob(enc);
422 			if (s_opts.print_props)
423 				e2.lines = format_props(enc);
424 
425 			if (Crtc* crtc = enc->get_crtc()) {
426 				Entry& e3 = add_entry(e2.children);
427 				e3.title = format_ob(crtc);
428 				if (s_opts.print_props)
429 					e3.lines = format_props(crtc);
430 
431 				if (crtc->buffer_id() && !card.has_has_universal_planes()) {
432 					Framebuffer fb(card, crtc->buffer_id());
433 					Entry& e5 = add_entry(e3.children);
434 
435 					e5.title = format_ob(&fb);
436 				}
437 
438 				for (Plane* plane : card.get_planes()) {
439 					if (plane->crtc_id() != crtc->id())
440 						continue;
441 
442 					Entry& e4 = add_entry(e3.children);
443 					e4.title = format_ob(plane);
444 					if (s_opts.print_props)
445 						e4.lines = format_props(plane);
446 
447 					uint32_t fb_id = plane->fb_id();
448 					if (fb_id) {
449 						Framebuffer fb(card, fb_id);
450 
451 						Entry& e5 = add_entry(e4.children);
452 
453 						e5.title = format_ob(&fb);
454 					}
455 				}
456 			}
457 		}
458 	}
459 
460 	print_entries(entries, "");
461 }
462 
print_modes(Card & card)463 static void print_modes(Card& card)
464 {
465 	for (Connector* conn : card.get_connectors()) {
466 		if (!conn->connected())
467 			continue;
468 
469 		printf("%s\n", format_ob(conn).c_str());
470 
471 		auto modes = conn->get_modes();
472 		for (unsigned i = 0; i < modes.size(); ++i)
473 			printf("%s\n", format_mode(modes[i], i).c_str());
474 	}
475 }
476 
477 static const char* usage_str =
478 		"Usage: kmsprint [OPTIONS]\n\n"
479 		"Options:\n"
480 		"  -l, --list        Print list instead of tree\n"
481 		"  -m, --modes       Print modes\n"
482 		"      --xmode       Print modes using X modeline\n"
483 		"  -p, --props       Print properties\n"
484 		;
485 
usage()486 static void usage()
487 {
488 	puts(usage_str);
489 }
490 
main(int argc,char ** argv)491 int main(int argc, char **argv)
492 {
493 	string dev_path = "/dev/dri/card0";
494 
495 	OptionSet optionset = {
496 		Option("|device=", [&dev_path](string s)
497 		{
498 			dev_path = s;
499 		}),
500 		Option("l|list", []()
501 		{
502 			s_opts.print_list = true;
503 		}),
504 		Option("m|modes", []()
505 		{
506 			s_opts.print_modes = true;
507 		}),
508 		Option("p|props", []()
509 		{
510 			s_opts.print_props = true;
511 		}),
512 		Option("|xmode", []() {
513 			s_opts.x_modeline = true;
514 		}),
515 		Option("h|help", []()
516 		{
517 			usage();
518 			exit(-1);
519 		}),
520 	};
521 
522 	optionset.parse(argc, argv);
523 
524 	if (optionset.params().size() > 0) {
525 		usage();
526 		exit(-1);
527 	}
528 
529 	Card card(dev_path);
530 
531 	if (s_opts.print_modes) {
532 		print_modes(card);
533 		return 0;
534 	}
535 
536 	if (s_opts.print_list)
537 		print_as_list(card);
538 	else
539 		print_as_tree(card);
540 }
541