1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2010 Werner Fink, Jiri Slaby |
4 | */ |
5 | |
6 | #include <linux/console.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/proc_fs.h> |
9 | #include <linux/seq_file.h> |
10 | #include <linux/tty_driver.h> |
11 | |
12 | /* |
13 | * This is handler for /proc/consoles |
14 | */ |
15 | static int show_console_dev(struct seq_file *m, void *v) |
16 | { |
17 | static const struct { |
18 | short flag; |
19 | char name; |
20 | } con_flags[] = { |
21 | { .flag: CON_ENABLED, .name: 'E' }, |
22 | { CON_CONSDEV, 'C' }, |
23 | { CON_BOOT, 'B' }, |
24 | { CON_PRINTBUFFER, 'p' }, |
25 | { CON_BRL, 'b' }, |
26 | { CON_ANYTIME, 'a' }, |
27 | }; |
28 | char flags[ARRAY_SIZE(con_flags) + 1]; |
29 | struct console *con = v; |
30 | unsigned int a; |
31 | dev_t dev = 0; |
32 | |
33 | if (con->device) { |
34 | const struct tty_driver *driver; |
35 | int index; |
36 | |
37 | /* |
38 | * Take console_lock to serialize device() callback with |
39 | * other console operations. For example, fg_console is |
40 | * modified under console_lock when switching vt. |
41 | */ |
42 | console_lock(); |
43 | driver = con->device(con, &index); |
44 | console_unlock(); |
45 | |
46 | if (driver) { |
47 | dev = MKDEV(driver->major, driver->minor_start); |
48 | dev += index; |
49 | } |
50 | } |
51 | |
52 | for (a = 0; a < ARRAY_SIZE(con_flags); a++) |
53 | flags[a] = (con->flags & con_flags[a].flag) ? |
54 | con_flags[a].name : ' '; |
55 | flags[a] = 0; |
56 | |
57 | seq_setwidth(m, size: 21 - 1); |
58 | seq_printf(m, fmt: "%s%d" , con->name, con->index); |
59 | seq_pad(m, c: ' '); |
60 | seq_printf(m, fmt: "%c%c%c (%s)" , con->read ? 'R' : '-', |
61 | con->write ? 'W' : '-', con->unblank ? 'U' : '-', |
62 | flags); |
63 | if (dev) |
64 | seq_printf(m, fmt: " %4d:%d" , MAJOR(dev), MINOR(dev)); |
65 | |
66 | seq_putc(m, c: '\n'); |
67 | return 0; |
68 | } |
69 | |
70 | static void *c_start(struct seq_file *m, loff_t *pos) |
71 | { |
72 | struct console *con; |
73 | loff_t off = 0; |
74 | |
75 | /* |
76 | * Hold the console_list_lock to guarantee safe traversal of the |
77 | * console list. SRCU cannot be used because there is no |
78 | * place to store the SRCU cookie. |
79 | */ |
80 | console_list_lock(); |
81 | for_each_console(con) |
82 | if (off++ == *pos) |
83 | break; |
84 | |
85 | return con; |
86 | } |
87 | |
88 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) |
89 | { |
90 | struct console *con = v; |
91 | |
92 | ++*pos; |
93 | return hlist_entry_safe(con->node.next, struct console, node); |
94 | } |
95 | |
96 | static void c_stop(struct seq_file *m, void *v) |
97 | { |
98 | console_list_unlock(); |
99 | } |
100 | |
101 | static const struct seq_operations consoles_op = { |
102 | .start = c_start, |
103 | .next = c_next, |
104 | .stop = c_stop, |
105 | .show = show_console_dev |
106 | }; |
107 | |
108 | static int __init proc_consoles_init(void) |
109 | { |
110 | proc_create_seq("consoles" , 0, NULL, &consoles_op); |
111 | return 0; |
112 | } |
113 | fs_initcall(proc_consoles_init); |
114 | |