Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * linux/arch/alpha/kernel/srmcons.c | |
4 | * | |
5 | * Callback based driver for SRM Console console device. | |
6 | * (TTY driver and console driver) | |
7 | */ | |
8 | ||
1da177e4 LT |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> | |
11 | #include <linux/console.h> | |
12 | #include <linux/delay.h> | |
13 | #include <linux/mm.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/spinlock.h> | |
16 | #include <linux/timer.h> | |
17 | #include <linux/tty.h> | |
18 | #include <linux/tty_driver.h> | |
19 | #include <linux/tty_flip.h> | |
20 | ||
21 | #include <asm/console.h> | |
7c0f6ba6 | 22 | #include <linux/uaccess.h> |
1da177e4 LT |
23 | |
24 | ||
25 | static DEFINE_SPINLOCK(srmcons_callback_lock); | |
26 | static int srm_is_registered_console = 0; | |
27 | ||
28 | /* | |
29 | * The TTY driver | |
30 | */ | |
31 | #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ | |
32 | ||
33 | struct srmcons_private { | |
54089d4c | 34 | struct tty_port port; |
1da177e4 | 35 | struct timer_list timer; |
ee024d49 | 36 | } srmcons_singleton; |
1da177e4 LT |
37 | |
38 | typedef union _srmcons_result { | |
39 | struct { | |
40 | unsigned long c :61; | |
41 | unsigned long status :3; | |
42 | } bits; | |
43 | long as_long; | |
44 | } srmcons_result; | |
45 | ||
46 | /* called with callback_lock held */ | |
47 | static int | |
6732c8bb | 48 | srmcons_do_receive_chars(struct tty_port *port) |
1da177e4 LT |
49 | { |
50 | srmcons_result result; | |
51 | int count = 0, loops = 0; | |
52 | ||
53 | do { | |
54 | result.as_long = callback_getc(0); | |
55 | if (result.bits.status < 2) { | |
92a19f9c | 56 | tty_insert_flip_char(port, (char)result.bits.c, 0); |
1da177e4 LT |
57 | count++; |
58 | } | |
59 | } while((result.bits.status & 1) && (++loops < 10)); | |
60 | ||
61 | if (count) | |
6732c8bb | 62 | tty_schedule_flip(port); |
1da177e4 LT |
63 | |
64 | return count; | |
65 | } | |
66 | ||
67 | static void | |
e99e88a9 | 68 | srmcons_receive_chars(struct timer_list *t) |
1da177e4 | 69 | { |
e99e88a9 | 70 | struct srmcons_private *srmconsp = from_timer(srmconsp, t, timer); |
54089d4c | 71 | struct tty_port *port = &srmconsp->port; |
1da177e4 LT |
72 | unsigned long flags; |
73 | int incr = 10; | |
74 | ||
75 | local_irq_save(flags); | |
76 | if (spin_trylock(&srmcons_callback_lock)) { | |
6732c8bb | 77 | if (!srmcons_do_receive_chars(port)) |
1da177e4 LT |
78 | incr = 100; |
79 | spin_unlock(&srmcons_callback_lock); | |
80 | } | |
81 | ||
54089d4c JS |
82 | spin_lock(&port->lock); |
83 | if (port->tty) | |
5e88e6c4 | 84 | mod_timer(&srmconsp->timer, jiffies + incr); |
54089d4c | 85 | spin_unlock(&port->lock); |
1da177e4 LT |
86 | |
87 | local_irq_restore(flags); | |
88 | } | |
89 | ||
90 | /* called with callback_lock held */ | |
91 | static int | |
6732c8bb | 92 | srmcons_do_write(struct tty_port *port, const char *buf, int count) |
1da177e4 LT |
93 | { |
94 | static char str_cr[1] = "\r"; | |
95 | long c, remaining = count; | |
96 | srmcons_result result; | |
97 | char *cur; | |
98 | int need_cr; | |
99 | ||
100 | for (cur = (char *)buf; remaining > 0; ) { | |
101 | need_cr = 0; | |
102 | /* | |
103 | * Break it up into reasonable size chunks to allow a chance | |
104 | * for input to get in | |
105 | */ | |
106 | for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++) | |
107 | if (cur[c] == '\n') | |
108 | need_cr = 1; | |
109 | ||
110 | while (c > 0) { | |
111 | result.as_long = callback_puts(0, cur, c); | |
112 | c -= result.bits.c; | |
113 | remaining -= result.bits.c; | |
114 | cur += result.bits.c; | |
115 | ||
116 | /* | |
6732c8bb | 117 | * Check for pending input iff a tty port was provided |
1da177e4 | 118 | */ |
6732c8bb JS |
119 | if (port) |
120 | srmcons_do_receive_chars(port); | |
1da177e4 LT |
121 | } |
122 | ||
123 | while (need_cr) { | |
124 | result.as_long = callback_puts(0, str_cr, 1); | |
125 | if (result.bits.c > 0) | |
126 | need_cr = 0; | |
127 | } | |
128 | } | |
129 | return count; | |
130 | } | |
131 | ||
132 | static int | |
133 | srmcons_write(struct tty_struct *tty, | |
134 | const unsigned char *buf, int count) | |
135 | { | |
136 | unsigned long flags; | |
137 | ||
138 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
6732c8bb | 139 | srmcons_do_write(tty->port, (const char *) buf, count); |
1da177e4 LT |
140 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); |
141 | ||
142 | return count; | |
143 | } | |
144 | ||
145 | static int | |
146 | srmcons_write_room(struct tty_struct *tty) | |
147 | { | |
148 | return 512; | |
149 | } | |
150 | ||
151 | static int | |
152 | srmcons_chars_in_buffer(struct tty_struct *tty) | |
153 | { | |
154 | return 0; | |
155 | } | |
156 | ||
1da177e4 LT |
157 | static int |
158 | srmcons_open(struct tty_struct *tty, struct file *filp) | |
159 | { | |
ee024d49 | 160 | struct srmcons_private *srmconsp = &srmcons_singleton; |
54089d4c | 161 | struct tty_port *port = &srmconsp->port; |
1da177e4 | 162 | unsigned long flags; |
1da177e4 | 163 | |
54089d4c | 164 | spin_lock_irqsave(&port->lock, flags); |
1da177e4 | 165 | |
54089d4c | 166 | if (!port->tty) { |
1da177e4 | 167 | tty->driver_data = srmconsp; |
54089d4c JS |
168 | tty->port = port; |
169 | port->tty = tty; /* XXX proper refcounting */ | |
5e88e6c4 | 170 | mod_timer(&srmconsp->timer, jiffies + 10); |
1da177e4 LT |
171 | } |
172 | ||
54089d4c | 173 | spin_unlock_irqrestore(&port->lock, flags); |
1da177e4 LT |
174 | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static void | |
179 | srmcons_close(struct tty_struct *tty, struct file *filp) | |
180 | { | |
181 | struct srmcons_private *srmconsp = tty->driver_data; | |
54089d4c | 182 | struct tty_port *port = &srmconsp->port; |
1da177e4 LT |
183 | unsigned long flags; |
184 | ||
54089d4c | 185 | spin_lock_irqsave(&port->lock, flags); |
1da177e4 LT |
186 | |
187 | if (tty->count == 1) { | |
54089d4c | 188 | port->tty = NULL; |
1da177e4 LT |
189 | del_timer(&srmconsp->timer); |
190 | } | |
191 | ||
54089d4c | 192 | spin_unlock_irqrestore(&port->lock, flags); |
1da177e4 LT |
193 | } |
194 | ||
195 | ||
196 | static struct tty_driver *srmcons_driver; | |
197 | ||
b68e31d0 | 198 | static const struct tty_operations srmcons_ops = { |
1da177e4 LT |
199 | .open = srmcons_open, |
200 | .close = srmcons_close, | |
201 | .write = srmcons_write, | |
202 | .write_room = srmcons_write_room, | |
203 | .chars_in_buffer= srmcons_chars_in_buffer, | |
204 | }; | |
205 | ||
206 | static int __init | |
207 | srmcons_init(void) | |
208 | { | |
e99e88a9 | 209 | timer_setup(&srmcons_singleton.timer, srmcons_receive_chars, 0); |
1da177e4 LT |
210 | if (srm_is_registered_console) { |
211 | struct tty_driver *driver; | |
212 | int err; | |
213 | ||
214 | driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); | |
215 | if (!driver) | |
216 | return -ENOMEM; | |
191c5f10 JS |
217 | |
218 | tty_port_init(&srmcons_singleton.port); | |
219 | ||
1da177e4 LT |
220 | driver->driver_name = "srm"; |
221 | driver->name = "srm"; | |
222 | driver->major = 0; /* dynamic */ | |
223 | driver->minor_start = 0; | |
224 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | |
225 | driver->subtype = SYSTEM_TYPE_SYSCONS; | |
226 | driver->init_termios = tty_std_termios; | |
227 | tty_set_operations(driver, &srmcons_ops); | |
b19e2ca7 | 228 | tty_port_link_device(&srmcons_singleton.port, driver, 0); |
1da177e4 LT |
229 | err = tty_register_driver(driver); |
230 | if (err) { | |
231 | put_tty_driver(driver); | |
191c5f10 | 232 | tty_port_destroy(&srmcons_singleton.port); |
1da177e4 LT |
233 | return err; |
234 | } | |
235 | srmcons_driver = driver; | |
236 | } | |
237 | ||
238 | return -ENODEV; | |
239 | } | |
11447c7c | 240 | device_initcall(srmcons_init); |
1da177e4 LT |
241 | |
242 | \f | |
243 | /* | |
244 | * The console driver | |
245 | */ | |
246 | static void | |
247 | srm_console_write(struct console *co, const char *s, unsigned count) | |
248 | { | |
249 | unsigned long flags; | |
250 | ||
251 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
252 | srmcons_do_write(NULL, s, count); | |
253 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | |
254 | } | |
255 | ||
256 | static struct tty_driver * | |
257 | srm_console_device(struct console *co, int *index) | |
258 | { | |
259 | *index = co->index; | |
260 | return srmcons_driver; | |
261 | } | |
262 | ||
ebaf4fc1 | 263 | static int |
1da177e4 LT |
264 | srm_console_setup(struct console *co, char *options) |
265 | { | |
266 | return 0; | |
267 | } | |
268 | ||
269 | static struct console srmcons = { | |
270 | .name = "srm", | |
271 | .write = srm_console_write, | |
272 | .device = srm_console_device, | |
273 | .setup = srm_console_setup, | |
69331af7 | 274 | .flags = CON_PRINTBUFFER | CON_BOOT, |
1da177e4 LT |
275 | .index = -1, |
276 | }; | |
277 | ||
278 | void __init | |
279 | register_srm_console(void) | |
280 | { | |
281 | if (!srm_is_registered_console) { | |
282 | callback_open_console(); | |
283 | register_console(&srmcons); | |
284 | srm_is_registered_console = 1; | |
285 | } | |
286 | } | |
287 | ||
288 | void __init | |
289 | unregister_srm_console(void) | |
290 | { | |
291 | if (srm_is_registered_console) { | |
292 | callback_close_console(); | |
293 | unregister_console(&srmcons); | |
294 | srm_is_registered_console = 0; | |
295 | } | |
296 | } |