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