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 | |
68 | srmcons_receive_chars(unsigned long data) | |
69 | { | |
70 | struct srmcons_private *srmconsp = (struct srmcons_private *)data; | |
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 | { | |
ee024d49 JS |
209 | setup_timer(&srmcons_singleton.timer, srmcons_receive_chars, |
210 | (unsigned long)&srmcons_singleton); | |
1da177e4 LT |
211 | if (srm_is_registered_console) { |
212 | struct tty_driver *driver; | |
213 | int err; | |
214 | ||
215 | driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); | |
216 | if (!driver) | |
217 | return -ENOMEM; | |
191c5f10 JS |
218 | |
219 | tty_port_init(&srmcons_singleton.port); | |
220 | ||
1da177e4 LT |
221 | driver->driver_name = "srm"; |
222 | driver->name = "srm"; | |
223 | driver->major = 0; /* dynamic */ | |
224 | driver->minor_start = 0; | |
225 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | |
226 | driver->subtype = SYSTEM_TYPE_SYSCONS; | |
227 | driver->init_termios = tty_std_termios; | |
228 | tty_set_operations(driver, &srmcons_ops); | |
b19e2ca7 | 229 | tty_port_link_device(&srmcons_singleton.port, driver, 0); |
1da177e4 LT |
230 | err = tty_register_driver(driver); |
231 | if (err) { | |
232 | put_tty_driver(driver); | |
191c5f10 | 233 | tty_port_destroy(&srmcons_singleton.port); |
1da177e4 LT |
234 | return err; |
235 | } | |
236 | srmcons_driver = driver; | |
237 | } | |
238 | ||
239 | return -ENODEV; | |
240 | } | |
11447c7c | 241 | device_initcall(srmcons_init); |
1da177e4 LT |
242 | |
243 | \f | |
244 | /* | |
245 | * The console driver | |
246 | */ | |
247 | static void | |
248 | srm_console_write(struct console *co, const char *s, unsigned count) | |
249 | { | |
250 | unsigned long flags; | |
251 | ||
252 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
253 | srmcons_do_write(NULL, s, count); | |
254 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | |
255 | } | |
256 | ||
257 | static struct tty_driver * | |
258 | srm_console_device(struct console *co, int *index) | |
259 | { | |
260 | *index = co->index; | |
261 | return srmcons_driver; | |
262 | } | |
263 | ||
ebaf4fc1 | 264 | static int |
1da177e4 LT |
265 | srm_console_setup(struct console *co, char *options) |
266 | { | |
267 | return 0; | |
268 | } | |
269 | ||
270 | static struct console srmcons = { | |
271 | .name = "srm", | |
272 | .write = srm_console_write, | |
273 | .device = srm_console_device, | |
274 | .setup = srm_console_setup, | |
69331af7 | 275 | .flags = CON_PRINTBUFFER | CON_BOOT, |
1da177e4 LT |
276 | .index = -1, |
277 | }; | |
278 | ||
279 | void __init | |
280 | register_srm_console(void) | |
281 | { | |
282 | if (!srm_is_registered_console) { | |
283 | callback_open_console(); | |
284 | register_console(&srmcons); | |
285 | srm_is_registered_console = 1; | |
286 | } | |
287 | } | |
288 | ||
289 | void __init | |
290 | unregister_srm_console(void) | |
291 | { | |
292 | if (srm_is_registered_console) { | |
293 | callback_close_console(); | |
294 | unregister_console(&srmcons); | |
295 | srm_is_registered_console = 0; | |
296 | } | |
297 | } |