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