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