]>
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 { | |
33 | struct tty_struct *tty; | |
34 | struct timer_list timer; | |
35 | spinlock_t lock; | |
36 | }; | |
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 | |
48 | srmcons_do_receive_chars(struct tty_struct *tty) | |
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) { | |
56 | tty_insert_flip_char(tty, (char)result.bits.c, 0); | |
57 | count++; | |
58 | } | |
59 | } while((result.bits.status & 1) && (++loops < 10)); | |
60 | ||
61 | if (count) | |
62 | tty_schedule_flip(tty); | |
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; | |
71 | unsigned long flags; | |
72 | int incr = 10; | |
73 | ||
74 | local_irq_save(flags); | |
75 | if (spin_trylock(&srmcons_callback_lock)) { | |
76 | if (!srmcons_do_receive_chars(srmconsp->tty)) | |
77 | incr = 100; | |
78 | spin_unlock(&srmcons_callback_lock); | |
79 | } | |
80 | ||
81 | spin_lock(&srmconsp->lock); | |
82 | if (srmconsp->tty) { | |
83 | srmconsp->timer.expires = jiffies + incr; | |
84 | add_timer(&srmconsp->timer); | |
85 | } | |
86 | spin_unlock(&srmconsp->lock); | |
87 | ||
88 | local_irq_restore(flags); | |
89 | } | |
90 | ||
91 | /* called with callback_lock held */ | |
92 | static int | |
93 | srmcons_do_write(struct tty_struct *tty, const char *buf, int count) | |
94 | { | |
95 | static char str_cr[1] = "\r"; | |
96 | long c, remaining = count; | |
97 | srmcons_result result; | |
98 | char *cur; | |
99 | int need_cr; | |
100 | ||
101 | for (cur = (char *)buf; remaining > 0; ) { | |
102 | need_cr = 0; | |
103 | /* | |
104 | * Break it up into reasonable size chunks to allow a chance | |
105 | * for input to get in | |
106 | */ | |
107 | for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++) | |
108 | if (cur[c] == '\n') | |
109 | need_cr = 1; | |
110 | ||
111 | while (c > 0) { | |
112 | result.as_long = callback_puts(0, cur, c); | |
113 | c -= result.bits.c; | |
114 | remaining -= result.bits.c; | |
115 | cur += result.bits.c; | |
116 | ||
117 | /* | |
118 | * Check for pending input iff a tty was provided | |
119 | */ | |
120 | if (tty) | |
121 | srmcons_do_receive_chars(tty); | |
122 | } | |
123 | ||
124 | while (need_cr) { | |
125 | result.as_long = callback_puts(0, str_cr, 1); | |
126 | if (result.bits.c > 0) | |
127 | need_cr = 0; | |
128 | } | |
129 | } | |
130 | return count; | |
131 | } | |
132 | ||
133 | static int | |
134 | srmcons_write(struct tty_struct *tty, | |
135 | const unsigned char *buf, int count) | |
136 | { | |
137 | unsigned long flags; | |
138 | ||
139 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
140 | srmcons_do_write(tty, (const char *) buf, count); | |
141 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | |
142 | ||
143 | return count; | |
144 | } | |
145 | ||
146 | static int | |
147 | srmcons_write_room(struct tty_struct *tty) | |
148 | { | |
149 | return 512; | |
150 | } | |
151 | ||
152 | static int | |
153 | srmcons_chars_in_buffer(struct tty_struct *tty) | |
154 | { | |
155 | return 0; | |
156 | } | |
157 | ||
158 | static int | |
159 | srmcons_get_private_struct(struct srmcons_private **ps) | |
160 | { | |
161 | static struct srmcons_private *srmconsp = NULL; | |
162 | static DEFINE_SPINLOCK(srmconsp_lock); | |
163 | unsigned long flags; | |
164 | int retval = 0; | |
165 | ||
166 | if (srmconsp == NULL) { | |
2af0bc94 | 167 | srmconsp = kmalloc(sizeof(*srmconsp), GFP_KERNEL); |
1da177e4 LT |
168 | spin_lock_irqsave(&srmconsp_lock, flags); |
169 | ||
1da177e4 LT |
170 | if (srmconsp == NULL) |
171 | retval = -ENOMEM; | |
172 | else { | |
173 | srmconsp->tty = NULL; | |
174 | spin_lock_init(&srmconsp->lock); | |
175 | init_timer(&srmconsp->timer); | |
176 | } | |
177 | ||
178 | spin_unlock_irqrestore(&srmconsp_lock, flags); | |
179 | } | |
180 | ||
181 | *ps = srmconsp; | |
182 | return retval; | |
183 | } | |
184 | ||
185 | static int | |
186 | srmcons_open(struct tty_struct *tty, struct file *filp) | |
187 | { | |
188 | struct srmcons_private *srmconsp; | |
189 | unsigned long flags; | |
190 | int retval; | |
191 | ||
192 | retval = srmcons_get_private_struct(&srmconsp); | |
193 | if (retval) | |
194 | return retval; | |
195 | ||
196 | spin_lock_irqsave(&srmconsp->lock, flags); | |
197 | ||
198 | if (!srmconsp->tty) { | |
199 | tty->driver_data = srmconsp; | |
200 | ||
201 | srmconsp->tty = tty; | |
202 | srmconsp->timer.function = srmcons_receive_chars; | |
203 | srmconsp->timer.data = (unsigned long)srmconsp; | |
204 | srmconsp->timer.expires = jiffies + 10; | |
205 | add_timer(&srmconsp->timer); | |
206 | } | |
207 | ||
208 | spin_unlock_irqrestore(&srmconsp->lock, flags); | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | static void | |
214 | srmcons_close(struct tty_struct *tty, struct file *filp) | |
215 | { | |
216 | struct srmcons_private *srmconsp = tty->driver_data; | |
217 | unsigned long flags; | |
218 | ||
219 | spin_lock_irqsave(&srmconsp->lock, flags); | |
220 | ||
221 | if (tty->count == 1) { | |
222 | srmconsp->tty = NULL; | |
223 | del_timer(&srmconsp->timer); | |
224 | } | |
225 | ||
226 | spin_unlock_irqrestore(&srmconsp->lock, flags); | |
227 | } | |
228 | ||
229 | ||
230 | static struct tty_driver *srmcons_driver; | |
231 | ||
b68e31d0 | 232 | static const struct tty_operations srmcons_ops = { |
1da177e4 LT |
233 | .open = srmcons_open, |
234 | .close = srmcons_close, | |
235 | .write = srmcons_write, | |
236 | .write_room = srmcons_write_room, | |
237 | .chars_in_buffer= srmcons_chars_in_buffer, | |
238 | }; | |
239 | ||
240 | static int __init | |
241 | srmcons_init(void) | |
242 | { | |
243 | if (srm_is_registered_console) { | |
244 | struct tty_driver *driver; | |
245 | int err; | |
246 | ||
247 | driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); | |
248 | if (!driver) | |
249 | return -ENOMEM; | |
250 | driver->driver_name = "srm"; | |
251 | driver->name = "srm"; | |
252 | driver->major = 0; /* dynamic */ | |
253 | driver->minor_start = 0; | |
254 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | |
255 | driver->subtype = SYSTEM_TYPE_SYSCONS; | |
256 | driver->init_termios = tty_std_termios; | |
257 | tty_set_operations(driver, &srmcons_ops); | |
258 | err = tty_register_driver(driver); | |
259 | if (err) { | |
260 | put_tty_driver(driver); | |
261 | return err; | |
262 | } | |
263 | srmcons_driver = driver; | |
264 | } | |
265 | ||
266 | return -ENODEV; | |
267 | } | |
268 | ||
269 | module_init(srmcons_init); | |
270 | ||
271 | \f | |
272 | /* | |
273 | * The console driver | |
274 | */ | |
275 | static void | |
276 | srm_console_write(struct console *co, const char *s, unsigned count) | |
277 | { | |
278 | unsigned long flags; | |
279 | ||
280 | spin_lock_irqsave(&srmcons_callback_lock, flags); | |
281 | srmcons_do_write(NULL, s, count); | |
282 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | |
283 | } | |
284 | ||
285 | static struct tty_driver * | |
286 | srm_console_device(struct console *co, int *index) | |
287 | { | |
288 | *index = co->index; | |
289 | return srmcons_driver; | |
290 | } | |
291 | ||
292 | static int __init | |
293 | srm_console_setup(struct console *co, char *options) | |
294 | { | |
295 | return 0; | |
296 | } | |
297 | ||
298 | static struct console srmcons = { | |
299 | .name = "srm", | |
300 | .write = srm_console_write, | |
301 | .device = srm_console_device, | |
302 | .setup = srm_console_setup, | |
303 | .flags = CON_PRINTBUFFER, | |
304 | .index = -1, | |
305 | }; | |
306 | ||
307 | void __init | |
308 | register_srm_console(void) | |
309 | { | |
310 | if (!srm_is_registered_console) { | |
311 | callback_open_console(); | |
312 | register_console(&srmcons); | |
313 | srm_is_registered_console = 1; | |
314 | } | |
315 | } | |
316 | ||
317 | void __init | |
318 | unregister_srm_console(void) | |
319 | { | |
320 | if (srm_is_registered_console) { | |
321 | callback_close_console(); | |
322 | unregister_console(&srmcons); | |
323 | srm_is_registered_console = 0; | |
324 | } | |
325 | } |