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 | |
47 | srmcons_do_receive_chars(struct tty_struct *tty) | |
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) { | |
55 | tty_insert_flip_char(tty, (char)result.bits.c, 0); | |
56 | count++; | |
57 | } | |
58 | } while((result.bits.status & 1) && (++loops < 10)); | |
59 | ||
60 | if (count) | |
61 | tty_schedule_flip(tty); | |
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)) { | |
54089d4c | 76 | if (!srmcons_do_receive_chars(port->tty)) |
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 | |
91 | srmcons_do_write(struct tty_struct *tty, const char *buf, int count) | |
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 | /* | |
116 | * Check for pending input iff a tty was provided | |
117 | */ | |
118 | if (tty) | |
119 | srmcons_do_receive_chars(tty); | |
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); | |
138 | srmcons_do_write(tty, (const char *) buf, count); | |
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 | { | |
54089d4c | 208 | tty_port_init(&srmcons_singleton.port); |
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; | |
218 | driver->driver_name = "srm"; | |
219 | driver->name = "srm"; | |
220 | driver->major = 0; /* dynamic */ | |
221 | driver->minor_start = 0; | |
222 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | |
223 | driver->subtype = SYSTEM_TYPE_SYSCONS; | |
224 | driver->init_termios = tty_std_termios; | |
225 | tty_set_operations(driver, &srmcons_ops); | |
b19e2ca7 | 226 | tty_port_link_device(&srmcons_singleton.port, driver, 0); |
1da177e4 LT |
227 | err = tty_register_driver(driver); |
228 | if (err) { | |
229 | put_tty_driver(driver); | |
230 | return err; | |
231 | } | |
232 | srmcons_driver = driver; | |
233 | } | |
234 | ||
235 | return -ENODEV; | |
236 | } | |
237 | ||
238 | module_init(srmcons_init); | |
239 | ||
240 | \f | |
241 | /* | |
242 | * The console driver | |
243 | */ | |
244 | static void | |
245 | srm_console_write(struct console *co, const char *s, unsigned count) | |
246 | { | |
247 | unsigned long flags; | |
248 | ||
249 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
250 | srmcons_do_write(NULL, s, count); | |
251 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | |
252 | } | |
253 | ||
254 | static struct tty_driver * | |
255 | srm_console_device(struct console *co, int *index) | |
256 | { | |
257 | *index = co->index; | |
258 | return srmcons_driver; | |
259 | } | |
260 | ||
ebaf4fc1 | 261 | static int |
1da177e4 LT |
262 | srm_console_setup(struct console *co, char *options) |
263 | { | |
264 | return 0; | |
265 | } | |
266 | ||
267 | static struct console srmcons = { | |
268 | .name = "srm", | |
269 | .write = srm_console_write, | |
270 | .device = srm_console_device, | |
271 | .setup = srm_console_setup, | |
69331af7 | 272 | .flags = CON_PRINTBUFFER | CON_BOOT, |
1da177e4 LT |
273 | .index = -1, |
274 | }; | |
275 | ||
276 | void __init | |
277 | register_srm_console(void) | |
278 | { | |
279 | if (!srm_is_registered_console) { | |
280 | callback_open_console(); | |
281 | register_console(&srmcons); | |
282 | srm_is_registered_console = 1; | |
283 | } | |
284 | } | |
285 | ||
286 | void __init | |
287 | unregister_srm_console(void) | |
288 | { | |
289 | if (srm_is_registered_console) { | |
290 | callback_close_console(); | |
291 | unregister_console(&srmcons); | |
292 | srm_is_registered_console = 0; | |
293 | } | |
294 | } |