Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/alpha/kernel/srmcons.c | |
3 | * | |
4 | * Callback based driver for SRM Console console device. | |
5 | * (TTY driver and console driver) | |
6 | */ | |
7 | ||
1da177e4 LT |
8 | #include <linux/kernel.h> |
9 | #include <linux/init.h> | |
10 | #include <linux/console.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/mm.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/spinlock.h> | |
15 | #include <linux/timer.h> | |
16 | #include <linux/tty.h> | |
17 | #include <linux/tty_driver.h> | |
18 | #include <linux/tty_flip.h> | |
19 | ||
20 | #include <asm/console.h> | |
21 | #include <asm/uaccess.h> | |
22 | ||
23 | ||
24 | static DEFINE_SPINLOCK(srmcons_callback_lock); | |
25 | static int srm_is_registered_console = 0; | |
26 | ||
27 | /* | |
28 | * The TTY driver | |
29 | */ | |
30 | #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ | |
31 | ||
32 | struct srmcons_private { | |
54089d4c | 33 | struct tty_port port; |
1da177e4 | 34 | struct timer_list timer; |
ee024d49 | 35 | } srmcons_singleton; |
1da177e4 LT |
36 | |
37 | typedef union _srmcons_result { | |
38 | struct { | |
39 | unsigned long c :61; | |
40 | unsigned long status :3; | |
41 | } bits; | |
42 | long as_long; | |
43 | } srmcons_result; | |
44 | ||
45 | /* called with callback_lock held */ | |
46 | static int | |
6732c8bb | 47 | srmcons_do_receive_chars(struct tty_port *port) |
1da177e4 LT |
48 | { |
49 | srmcons_result result; | |
50 | int count = 0, loops = 0; | |
51 | ||
52 | do { | |
53 | result.as_long = callback_getc(0); | |
54 | if (result.bits.status < 2) { | |
92a19f9c | 55 | tty_insert_flip_char(port, (char)result.bits.c, 0); |
1da177e4 LT |
56 | count++; |
57 | } | |
58 | } while((result.bits.status & 1) && (++loops < 10)); | |
59 | ||
60 | if (count) | |
6732c8bb | 61 | tty_schedule_flip(port); |
1da177e4 LT |
62 | |
63 | return count; | |
64 | } | |
65 | ||
66 | static void | |
67 | srmcons_receive_chars(unsigned long data) | |
68 | { | |
69 | struct srmcons_private *srmconsp = (struct srmcons_private *)data; | |
54089d4c | 70 | struct tty_port *port = &srmconsp->port; |
1da177e4 LT |
71 | unsigned long flags; |
72 | int incr = 10; | |
73 | ||
74 | local_irq_save(flags); | |
75 | if (spin_trylock(&srmcons_callback_lock)) { | |
6732c8bb | 76 | if (!srmcons_do_receive_chars(port)) |
1da177e4 LT |
77 | incr = 100; |
78 | spin_unlock(&srmcons_callback_lock); | |
79 | } | |
80 | ||
54089d4c JS |
81 | spin_lock(&port->lock); |
82 | if (port->tty) | |
5e88e6c4 | 83 | mod_timer(&srmconsp->timer, jiffies + incr); |
54089d4c | 84 | spin_unlock(&port->lock); |
1da177e4 LT |
85 | |
86 | local_irq_restore(flags); | |
87 | } | |
88 | ||
89 | /* called with callback_lock held */ | |
90 | static int | |
6732c8bb | 91 | srmcons_do_write(struct tty_port *port, const char *buf, int count) |
1da177e4 LT |
92 | { |
93 | static char str_cr[1] = "\r"; | |
94 | long c, remaining = count; | |
95 | srmcons_result result; | |
96 | char *cur; | |
97 | int need_cr; | |
98 | ||
99 | for (cur = (char *)buf; remaining > 0; ) { | |
100 | need_cr = 0; | |
101 | /* | |
102 | * Break it up into reasonable size chunks to allow a chance | |
103 | * for input to get in | |
104 | */ | |
105 | for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++) | |
106 | if (cur[c] == '\n') | |
107 | need_cr = 1; | |
108 | ||
109 | while (c > 0) { | |
110 | result.as_long = callback_puts(0, cur, c); | |
111 | c -= result.bits.c; | |
112 | remaining -= result.bits.c; | |
113 | cur += result.bits.c; | |
114 | ||
115 | /* | |
6732c8bb | 116 | * Check for pending input iff a tty port was provided |
1da177e4 | 117 | */ |
6732c8bb JS |
118 | if (port) |
119 | srmcons_do_receive_chars(port); | |
1da177e4 LT |
120 | } |
121 | ||
122 | while (need_cr) { | |
123 | result.as_long = callback_puts(0, str_cr, 1); | |
124 | if (result.bits.c > 0) | |
125 | need_cr = 0; | |
126 | } | |
127 | } | |
128 | return count; | |
129 | } | |
130 | ||
131 | static int | |
132 | srmcons_write(struct tty_struct *tty, | |
133 | const unsigned char *buf, int count) | |
134 | { | |
135 | unsigned long flags; | |
136 | ||
137 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
6732c8bb | 138 | srmcons_do_write(tty->port, (const char *) buf, count); |
1da177e4 LT |
139 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); |
140 | ||
141 | return count; | |
142 | } | |
143 | ||
144 | static int | |
145 | srmcons_write_room(struct tty_struct *tty) | |
146 | { | |
147 | return 512; | |
148 | } | |
149 | ||
150 | static int | |
151 | srmcons_chars_in_buffer(struct tty_struct *tty) | |
152 | { | |
153 | return 0; | |
154 | } | |
155 | ||
1da177e4 LT |
156 | static int |
157 | srmcons_open(struct tty_struct *tty, struct file *filp) | |
158 | { | |
ee024d49 | 159 | struct srmcons_private *srmconsp = &srmcons_singleton; |
54089d4c | 160 | struct tty_port *port = &srmconsp->port; |
1da177e4 | 161 | unsigned long flags; |
1da177e4 | 162 | |
54089d4c | 163 | spin_lock_irqsave(&port->lock, flags); |
1da177e4 | 164 | |
54089d4c | 165 | if (!port->tty) { |
1da177e4 | 166 | tty->driver_data = srmconsp; |
54089d4c JS |
167 | tty->port = port; |
168 | port->tty = tty; /* XXX proper refcounting */ | |
5e88e6c4 | 169 | mod_timer(&srmconsp->timer, jiffies + 10); |
1da177e4 LT |
170 | } |
171 | ||
54089d4c | 172 | spin_unlock_irqrestore(&port->lock, flags); |
1da177e4 LT |
173 | |
174 | return 0; | |
175 | } | |
176 | ||
177 | static void | |
178 | srmcons_close(struct tty_struct *tty, struct file *filp) | |
179 | { | |
180 | struct srmcons_private *srmconsp = tty->driver_data; | |
54089d4c | 181 | struct tty_port *port = &srmconsp->port; |
1da177e4 LT |
182 | unsigned long flags; |
183 | ||
54089d4c | 184 | spin_lock_irqsave(&port->lock, flags); |
1da177e4 LT |
185 | |
186 | if (tty->count == 1) { | |
54089d4c | 187 | port->tty = NULL; |
1da177e4 LT |
188 | del_timer(&srmconsp->timer); |
189 | } | |
190 | ||
54089d4c | 191 | spin_unlock_irqrestore(&port->lock, flags); |
1da177e4 LT |
192 | } |
193 | ||
194 | ||
195 | static struct tty_driver *srmcons_driver; | |
196 | ||
b68e31d0 | 197 | static const struct tty_operations srmcons_ops = { |
1da177e4 LT |
198 | .open = srmcons_open, |
199 | .close = srmcons_close, | |
200 | .write = srmcons_write, | |
201 | .write_room = srmcons_write_room, | |
202 | .chars_in_buffer= srmcons_chars_in_buffer, | |
203 | }; | |
204 | ||
205 | static int __init | |
206 | srmcons_init(void) | |
207 | { | |
ee024d49 JS |
208 | setup_timer(&srmcons_singleton.timer, srmcons_receive_chars, |
209 | (unsigned long)&srmcons_singleton); | |
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 | } |