162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 Werner Fink, Jiri Slaby
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/console.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/proc_fs.h>
962306a36Sopenharmony_ci#include <linux/seq_file.h>
1062306a36Sopenharmony_ci#include <linux/tty_driver.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * This is handler for /proc/consoles
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic int show_console_dev(struct seq_file *m, void *v)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	static const struct {
1862306a36Sopenharmony_ci		short flag;
1962306a36Sopenharmony_ci		char name;
2062306a36Sopenharmony_ci	} con_flags[] = {
2162306a36Sopenharmony_ci		{ CON_ENABLED,		'E' },
2262306a36Sopenharmony_ci		{ CON_CONSDEV,		'C' },
2362306a36Sopenharmony_ci		{ CON_BOOT,		'B' },
2462306a36Sopenharmony_ci		{ CON_PRINTBUFFER,	'p' },
2562306a36Sopenharmony_ci		{ CON_BRL,		'b' },
2662306a36Sopenharmony_ci		{ CON_ANYTIME,		'a' },
2762306a36Sopenharmony_ci	};
2862306a36Sopenharmony_ci	char flags[ARRAY_SIZE(con_flags) + 1];
2962306a36Sopenharmony_ci	struct console *con = v;
3062306a36Sopenharmony_ci	unsigned int a;
3162306a36Sopenharmony_ci	dev_t dev = 0;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (con->device) {
3462306a36Sopenharmony_ci		const struct tty_driver *driver;
3562306a36Sopenharmony_ci		int index;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci		/*
3862306a36Sopenharmony_ci		 * Take console_lock to serialize device() callback with
3962306a36Sopenharmony_ci		 * other console operations. For example, fg_console is
4062306a36Sopenharmony_ci		 * modified under console_lock when switching vt.
4162306a36Sopenharmony_ci		 */
4262306a36Sopenharmony_ci		console_lock();
4362306a36Sopenharmony_ci		driver = con->device(con, &index);
4462306a36Sopenharmony_ci		console_unlock();
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		if (driver) {
4762306a36Sopenharmony_ci			dev = MKDEV(driver->major, driver->minor_start);
4862306a36Sopenharmony_ci			dev += index;
4962306a36Sopenharmony_ci		}
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for (a = 0; a < ARRAY_SIZE(con_flags); a++)
5362306a36Sopenharmony_ci		flags[a] = (con->flags & con_flags[a].flag) ?
5462306a36Sopenharmony_ci			con_flags[a].name : ' ';
5562306a36Sopenharmony_ci	flags[a] = 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	seq_setwidth(m, 21 - 1);
5862306a36Sopenharmony_ci	seq_printf(m, "%s%d", con->name, con->index);
5962306a36Sopenharmony_ci	seq_pad(m, ' ');
6062306a36Sopenharmony_ci	seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-',
6162306a36Sopenharmony_ci			con->write ? 'W' : '-', con->unblank ? 'U' : '-',
6262306a36Sopenharmony_ci			flags);
6362306a36Sopenharmony_ci	if (dev)
6462306a36Sopenharmony_ci		seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev));
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	seq_putc(m, '\n');
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void *c_start(struct seq_file *m, loff_t *pos)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct console *con;
7362306a36Sopenharmony_ci	loff_t off = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * Hold the console_list_lock to guarantee safe traversal of the
7762306a36Sopenharmony_ci	 * console list. SRCU cannot be used because there is no
7862306a36Sopenharmony_ci	 * place to store the SRCU cookie.
7962306a36Sopenharmony_ci	 */
8062306a36Sopenharmony_ci	console_list_lock();
8162306a36Sopenharmony_ci	for_each_console(con)
8262306a36Sopenharmony_ci		if (off++ == *pos)
8362306a36Sopenharmony_ci			break;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return con;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void *c_next(struct seq_file *m, void *v, loff_t *pos)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct console *con = v;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	++*pos;
9362306a36Sopenharmony_ci	return hlist_entry_safe(con->node.next, struct console, node);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void c_stop(struct seq_file *m, void *v)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	console_list_unlock();
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct seq_operations consoles_op = {
10262306a36Sopenharmony_ci	.start	= c_start,
10362306a36Sopenharmony_ci	.next	= c_next,
10462306a36Sopenharmony_ci	.stop	= c_stop,
10562306a36Sopenharmony_ci	.show	= show_console_dev
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int __init proc_consoles_init(void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	proc_create_seq("consoles", 0, NULL, &consoles_op);
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_cifs_initcall(proc_consoles_init);
114