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