diff options
Diffstat (limited to 'drivers/s390/char/sclp_tty.c')
-rw-r--r-- | drivers/s390/char/sclp_tty.c | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c new file mode 100644 index 000000000000..a20d7c89341d --- /dev/null +++ b/drivers/s390/char/sclp_tty.c | |||
@@ -0,0 +1,813 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/sclp_tty.c | ||
3 | * SCLP line mode terminal driver. | ||
4 | * | ||
5 | * S390 version | ||
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | ||
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/kmod.h> | ||
14 | #include <linux/tty.h> | ||
15 | #include <linux/tty_driver.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <asm/uaccess.h> | ||
23 | |||
24 | #include "ctrlchar.h" | ||
25 | #include "sclp.h" | ||
26 | #include "sclp_rw.h" | ||
27 | #include "sclp_tty.h" | ||
28 | |||
29 | #define SCLP_TTY_PRINT_HEADER "sclp tty driver: " | ||
30 | |||
31 | /* | ||
32 | * size of a buffer that collects single characters coming in | ||
33 | * via sclp_tty_put_char() | ||
34 | */ | ||
35 | #define SCLP_TTY_BUF_SIZE 512 | ||
36 | |||
37 | /* | ||
38 | * There is exactly one SCLP terminal, so we can keep things simple | ||
39 | * and allocate all variables statically. | ||
40 | */ | ||
41 | |||
42 | /* Lock to guard over changes to global variables. */ | ||
43 | static spinlock_t sclp_tty_lock; | ||
44 | /* List of free pages that can be used for console output buffering. */ | ||
45 | static struct list_head sclp_tty_pages; | ||
46 | /* List of full struct sclp_buffer structures ready for output. */ | ||
47 | static struct list_head sclp_tty_outqueue; | ||
48 | /* Counter how many buffers are emitted. */ | ||
49 | static int sclp_tty_buffer_count; | ||
50 | /* Pointer to current console buffer. */ | ||
51 | static struct sclp_buffer *sclp_ttybuf; | ||
52 | /* Timer for delayed output of console messages. */ | ||
53 | static struct timer_list sclp_tty_timer; | ||
54 | /* Waitqueue to wait for buffers to get empty. */ | ||
55 | static wait_queue_head_t sclp_tty_waitq; | ||
56 | |||
57 | static struct tty_struct *sclp_tty; | ||
58 | static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; | ||
59 | static unsigned short int sclp_tty_chars_count; | ||
60 | |||
61 | struct tty_driver *sclp_tty_driver; | ||
62 | |||
63 | extern struct termios tty_std_termios; | ||
64 | |||
65 | static struct sclp_ioctls sclp_ioctls; | ||
66 | static struct sclp_ioctls sclp_ioctls_init = | ||
67 | { | ||
68 | 8, /* 1 hor. tab. = 8 spaces */ | ||
69 | 0, /* no echo of input by this driver */ | ||
70 | 80, /* 80 characters/line */ | ||
71 | 1, /* write after 1/10 s without final new line */ | ||
72 | MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */ | ||
73 | MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */ | ||
74 | 0, /* do not convert to lower case */ | ||
75 | 0x6c /* to seprate upper and lower case */ | ||
76 | /* ('%' in EBCDIC) */ | ||
77 | }; | ||
78 | |||
79 | /* This routine is called whenever we try to open a SCLP terminal. */ | ||
80 | static int | ||
81 | sclp_tty_open(struct tty_struct *tty, struct file *filp) | ||
82 | { | ||
83 | sclp_tty = tty; | ||
84 | tty->driver_data = NULL; | ||
85 | tty->low_latency = 0; | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* This routine is called when the SCLP terminal is closed. */ | ||
90 | static void | ||
91 | sclp_tty_close(struct tty_struct *tty, struct file *filp) | ||
92 | { | ||
93 | if (tty->count > 1) | ||
94 | return; | ||
95 | sclp_tty = NULL; | ||
96 | } | ||
97 | |||
98 | /* execute commands to control the i/o behaviour of the SCLP tty at runtime */ | ||
99 | static int | ||
100 | sclp_tty_ioctl(struct tty_struct *tty, struct file * file, | ||
101 | unsigned int cmd, unsigned long arg) | ||
102 | { | ||
103 | unsigned long flags; | ||
104 | unsigned int obuf; | ||
105 | int check; | ||
106 | int rc; | ||
107 | |||
108 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
109 | return -EIO; | ||
110 | rc = 0; | ||
111 | check = 0; | ||
112 | switch (cmd) { | ||
113 | case TIOCSCLPSHTAB: | ||
114 | /* set width of horizontal tab */ | ||
115 | if (get_user(sclp_ioctls.htab, (unsigned short __user *) arg)) | ||
116 | rc = -EFAULT; | ||
117 | else | ||
118 | check = 1; | ||
119 | break; | ||
120 | case TIOCSCLPGHTAB: | ||
121 | /* get width of horizontal tab */ | ||
122 | if (put_user(sclp_ioctls.htab, (unsigned short __user *) arg)) | ||
123 | rc = -EFAULT; | ||
124 | break; | ||
125 | case TIOCSCLPSECHO: | ||
126 | /* enable/disable echo of input */ | ||
127 | if (get_user(sclp_ioctls.echo, (unsigned char __user *) arg)) | ||
128 | rc = -EFAULT; | ||
129 | break; | ||
130 | case TIOCSCLPGECHO: | ||
131 | /* Is echo of input enabled ? */ | ||
132 | if (put_user(sclp_ioctls.echo, (unsigned char __user *) arg)) | ||
133 | rc = -EFAULT; | ||
134 | break; | ||
135 | case TIOCSCLPSCOLS: | ||
136 | /* set number of columns for output */ | ||
137 | if (get_user(sclp_ioctls.columns, (unsigned short __user *) arg)) | ||
138 | rc = -EFAULT; | ||
139 | else | ||
140 | check = 1; | ||
141 | break; | ||
142 | case TIOCSCLPGCOLS: | ||
143 | /* get number of columns for output */ | ||
144 | if (put_user(sclp_ioctls.columns, (unsigned short __user *) arg)) | ||
145 | rc = -EFAULT; | ||
146 | break; | ||
147 | case TIOCSCLPSNL: | ||
148 | /* enable/disable writing without final new line character */ | ||
149 | if (get_user(sclp_ioctls.final_nl, (signed char __user *) arg)) | ||
150 | rc = -EFAULT; | ||
151 | break; | ||
152 | case TIOCSCLPGNL: | ||
153 | /* Is writing without final new line character enabled ? */ | ||
154 | if (put_user(sclp_ioctls.final_nl, (signed char __user *) arg)) | ||
155 | rc = -EFAULT; | ||
156 | break; | ||
157 | case TIOCSCLPSOBUF: | ||
158 | /* | ||
159 | * set the maximum buffers size for output, will be rounded | ||
160 | * up to next 4kB boundary and stored as number of SCCBs | ||
161 | * (4kB Buffers) limitation: 256 x 4kB | ||
162 | */ | ||
163 | if (get_user(obuf, (unsigned int __user *) arg) == 0) { | ||
164 | if (obuf & 0xFFF) | ||
165 | sclp_ioctls.max_sccb = (obuf >> 12) + 1; | ||
166 | else | ||
167 | sclp_ioctls.max_sccb = (obuf >> 12); | ||
168 | } else | ||
169 | rc = -EFAULT; | ||
170 | break; | ||
171 | case TIOCSCLPGOBUF: | ||
172 | /* get the maximum buffers size for output */ | ||
173 | obuf = sclp_ioctls.max_sccb << 12; | ||
174 | if (put_user(obuf, (unsigned int __user *) arg)) | ||
175 | rc = -EFAULT; | ||
176 | break; | ||
177 | case TIOCSCLPGKBUF: | ||
178 | /* get the number of buffers got from kernel at startup */ | ||
179 | if (put_user(sclp_ioctls.kmem_sccb, (unsigned short __user *) arg)) | ||
180 | rc = -EFAULT; | ||
181 | break; | ||
182 | case TIOCSCLPSCASE: | ||
183 | /* enable/disable conversion from upper to lower case */ | ||
184 | if (get_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) | ||
185 | rc = -EFAULT; | ||
186 | break; | ||
187 | case TIOCSCLPGCASE: | ||
188 | /* Is conversion from upper to lower case of input enabled? */ | ||
189 | if (put_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) | ||
190 | rc = -EFAULT; | ||
191 | break; | ||
192 | case TIOCSCLPSDELIM: | ||
193 | /* | ||
194 | * set special character used for separating upper and | ||
195 | * lower case, 0x00 disables this feature | ||
196 | */ | ||
197 | if (get_user(sclp_ioctls.delim, (unsigned char __user *) arg)) | ||
198 | rc = -EFAULT; | ||
199 | break; | ||
200 | case TIOCSCLPGDELIM: | ||
201 | /* | ||
202 | * get special character used for separating upper and | ||
203 | * lower case, 0x00 disables this feature | ||
204 | */ | ||
205 | if (put_user(sclp_ioctls.delim, (unsigned char __user *) arg)) | ||
206 | rc = -EFAULT; | ||
207 | break; | ||
208 | case TIOCSCLPSINIT: | ||
209 | /* set initial (default) sclp ioctls */ | ||
210 | sclp_ioctls = sclp_ioctls_init; | ||
211 | check = 1; | ||
212 | break; | ||
213 | default: | ||
214 | rc = -ENOIOCTLCMD; | ||
215 | break; | ||
216 | } | ||
217 | if (check) { | ||
218 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
219 | if (sclp_ttybuf != NULL) { | ||
220 | sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab); | ||
221 | sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns); | ||
222 | } | ||
223 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
224 | } | ||
225 | return rc; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * This routine returns the numbers of characters the tty driver | ||
230 | * will accept for queuing to be written. This number is subject | ||
231 | * to change as output buffers get emptied, or if the output flow | ||
232 | * control is acted. This is not an exact number because not every | ||
233 | * character needs the same space in the sccb. The worst case is | ||
234 | * a string of newlines. Every newlines creates a new mto which | ||
235 | * needs 8 bytes. | ||
236 | */ | ||
237 | static int | ||
238 | sclp_tty_write_room (struct tty_struct *tty) | ||
239 | { | ||
240 | unsigned long flags; | ||
241 | struct list_head *l; | ||
242 | int count; | ||
243 | |||
244 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
245 | count = 0; | ||
246 | if (sclp_ttybuf != NULL) | ||
247 | count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto); | ||
248 | list_for_each(l, &sclp_tty_pages) | ||
249 | count += NR_EMPTY_MTO_PER_SCCB; | ||
250 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
251 | return count; | ||
252 | } | ||
253 | |||
254 | static void | ||
255 | sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) | ||
256 | { | ||
257 | unsigned long flags; | ||
258 | void *page; | ||
259 | |||
260 | do { | ||
261 | page = sclp_unmake_buffer(buffer); | ||
262 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
263 | /* Remove buffer from outqueue */ | ||
264 | list_del(&buffer->list); | ||
265 | sclp_tty_buffer_count--; | ||
266 | list_add_tail((struct list_head *) page, &sclp_tty_pages); | ||
267 | /* Check if there is a pending buffer on the out queue. */ | ||
268 | buffer = NULL; | ||
269 | if (!list_empty(&sclp_tty_outqueue)) | ||
270 | buffer = list_entry(sclp_tty_outqueue.next, | ||
271 | struct sclp_buffer, list); | ||
272 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
273 | } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); | ||
274 | wake_up(&sclp_tty_waitq); | ||
275 | /* check if the tty needs a wake up call */ | ||
276 | if (sclp_tty != NULL) { | ||
277 | tty_wakeup(sclp_tty); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static inline void | ||
282 | __sclp_ttybuf_emit(struct sclp_buffer *buffer) | ||
283 | { | ||
284 | unsigned long flags; | ||
285 | int count; | ||
286 | int rc; | ||
287 | |||
288 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
289 | list_add_tail(&buffer->list, &sclp_tty_outqueue); | ||
290 | count = sclp_tty_buffer_count++; | ||
291 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
292 | if (count) | ||
293 | return; | ||
294 | rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback); | ||
295 | if (rc) | ||
296 | sclp_ttybuf_callback(buffer, rc); | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * When this routine is called from the timer then we flush the | ||
301 | * temporary write buffer. | ||
302 | */ | ||
303 | static void | ||
304 | sclp_tty_timeout(unsigned long data) | ||
305 | { | ||
306 | unsigned long flags; | ||
307 | struct sclp_buffer *buf; | ||
308 | |||
309 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
310 | buf = sclp_ttybuf; | ||
311 | sclp_ttybuf = NULL; | ||
312 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
313 | |||
314 | if (buf != NULL) { | ||
315 | __sclp_ttybuf_emit(buf); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Write a string to the sclp tty. | ||
321 | */ | ||
322 | static void | ||
323 | sclp_tty_write_string(const unsigned char *str, int count) | ||
324 | { | ||
325 | unsigned long flags; | ||
326 | void *page; | ||
327 | int written; | ||
328 | struct sclp_buffer *buf; | ||
329 | |||
330 | if (count <= 0) | ||
331 | return; | ||
332 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
333 | do { | ||
334 | /* Create a sclp output buffer if none exists yet */ | ||
335 | if (sclp_ttybuf == NULL) { | ||
336 | while (list_empty(&sclp_tty_pages)) { | ||
337 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
338 | if (in_interrupt()) | ||
339 | sclp_sync_wait(); | ||
340 | else | ||
341 | wait_event(sclp_tty_waitq, | ||
342 | !list_empty(&sclp_tty_pages)); | ||
343 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
344 | } | ||
345 | page = sclp_tty_pages.next; | ||
346 | list_del((struct list_head *) page); | ||
347 | sclp_ttybuf = sclp_make_buffer(page, | ||
348 | sclp_ioctls.columns, | ||
349 | sclp_ioctls.htab); | ||
350 | } | ||
351 | /* try to write the string to the current output buffer */ | ||
352 | written = sclp_write(sclp_ttybuf, str, count); | ||
353 | if (written == count) | ||
354 | break; | ||
355 | /* | ||
356 | * Not all characters could be written to the current | ||
357 | * output buffer. Emit the buffer, create a new buffer | ||
358 | * and then output the rest of the string. | ||
359 | */ | ||
360 | buf = sclp_ttybuf; | ||
361 | sclp_ttybuf = NULL; | ||
362 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
363 | __sclp_ttybuf_emit(buf); | ||
364 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
365 | str += written; | ||
366 | count -= written; | ||
367 | } while (count > 0); | ||
368 | /* Setup timer to output current console buffer after 1/10 second */ | ||
369 | if (sclp_ioctls.final_nl) { | ||
370 | if (sclp_ttybuf != NULL && | ||
371 | sclp_chars_in_buffer(sclp_ttybuf) != 0 && | ||
372 | !timer_pending(&sclp_tty_timer)) { | ||
373 | init_timer(&sclp_tty_timer); | ||
374 | sclp_tty_timer.function = sclp_tty_timeout; | ||
375 | sclp_tty_timer.data = 0UL; | ||
376 | sclp_tty_timer.expires = jiffies + HZ/10; | ||
377 | add_timer(&sclp_tty_timer); | ||
378 | } | ||
379 | } else { | ||
380 | if (sclp_ttybuf != NULL && | ||
381 | sclp_chars_in_buffer(sclp_ttybuf) != 0) { | ||
382 | buf = sclp_ttybuf; | ||
383 | sclp_ttybuf = NULL; | ||
384 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
385 | __sclp_ttybuf_emit(buf); | ||
386 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
387 | } | ||
388 | } | ||
389 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * This routine is called by the kernel to write a series of characters to the | ||
394 | * tty device. The characters may come from user space or kernel space. This | ||
395 | * routine will return the number of characters actually accepted for writing. | ||
396 | */ | ||
397 | static int | ||
398 | sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) | ||
399 | { | ||
400 | if (sclp_tty_chars_count > 0) { | ||
401 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); | ||
402 | sclp_tty_chars_count = 0; | ||
403 | } | ||
404 | sclp_tty_write_string(buf, count); | ||
405 | return count; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * This routine is called by the kernel to write a single character to the tty | ||
410 | * device. If the kernel uses this routine, it must call the flush_chars() | ||
411 | * routine (if defined) when it is done stuffing characters into the driver. | ||
412 | * | ||
413 | * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver. | ||
414 | * If the given character is a '\n' the contents of the SCLP write buffer | ||
415 | * - including previous characters from sclp_tty_put_char() and strings from | ||
416 | * sclp_write() without final '\n' - will be written. | ||
417 | */ | ||
418 | static void | ||
419 | sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) | ||
420 | { | ||
421 | sclp_tty_chars[sclp_tty_chars_count++] = ch; | ||
422 | if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { | ||
423 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); | ||
424 | sclp_tty_chars_count = 0; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * This routine is called by the kernel after it has written a series of | ||
430 | * characters to the tty device using put_char(). | ||
431 | */ | ||
432 | static void | ||
433 | sclp_tty_flush_chars(struct tty_struct *tty) | ||
434 | { | ||
435 | if (sclp_tty_chars_count > 0) { | ||
436 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); | ||
437 | sclp_tty_chars_count = 0; | ||
438 | } | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * This routine returns the number of characters in the write buffer of the | ||
443 | * SCLP driver. The provided number includes all characters that are stored | ||
444 | * in the SCCB (will be written next time the SCLP is not busy) as well as | ||
445 | * characters in the write buffer (will not be written as long as there is a | ||
446 | * final line feed missing). | ||
447 | */ | ||
448 | static int | ||
449 | sclp_tty_chars_in_buffer(struct tty_struct *tty) | ||
450 | { | ||
451 | unsigned long flags; | ||
452 | struct list_head *l; | ||
453 | struct sclp_buffer *t; | ||
454 | int count; | ||
455 | |||
456 | spin_lock_irqsave(&sclp_tty_lock, flags); | ||
457 | count = 0; | ||
458 | if (sclp_ttybuf != NULL) | ||
459 | count = sclp_chars_in_buffer(sclp_ttybuf); | ||
460 | list_for_each(l, &sclp_tty_outqueue) { | ||
461 | t = list_entry(l, struct sclp_buffer, list); | ||
462 | count += sclp_chars_in_buffer(t); | ||
463 | } | ||
464 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | ||
465 | return count; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * removes all content from buffers of low level driver | ||
470 | */ | ||
471 | static void | ||
472 | sclp_tty_flush_buffer(struct tty_struct *tty) | ||
473 | { | ||
474 | if (sclp_tty_chars_count > 0) { | ||
475 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); | ||
476 | sclp_tty_chars_count = 0; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * push input to tty | ||
482 | */ | ||
483 | static void | ||
484 | sclp_tty_input(unsigned char* buf, unsigned int count) | ||
485 | { | ||
486 | unsigned int cchar; | ||
487 | |||
488 | /* | ||
489 | * If this tty driver is currently closed | ||
490 | * then throw the received input away. | ||
491 | */ | ||
492 | if (sclp_tty == NULL) | ||
493 | return; | ||
494 | cchar = ctrlchar_handle(buf, count, sclp_tty); | ||
495 | switch (cchar & CTRLCHAR_MASK) { | ||
496 | case CTRLCHAR_SYSRQ: | ||
497 | break; | ||
498 | case CTRLCHAR_CTRL: | ||
499 | sclp_tty->flip.count++; | ||
500 | *sclp_tty->flip.flag_buf_ptr++ = TTY_NORMAL; | ||
501 | *sclp_tty->flip.char_buf_ptr++ = cchar; | ||
502 | tty_flip_buffer_push(sclp_tty); | ||
503 | break; | ||
504 | case CTRLCHAR_NONE: | ||
505 | /* send (normal) input to line discipline */ | ||
506 | memcpy(sclp_tty->flip.char_buf_ptr, buf, count); | ||
507 | if (count < 2 || | ||
508 | (strncmp ((const char *) buf + count - 2, "^n", 2) && | ||
509 | strncmp ((const char *) buf + count - 2, "\0252n", 2))) { | ||
510 | sclp_tty->flip.char_buf_ptr[count] = '\n'; | ||
511 | count++; | ||
512 | } else | ||
513 | count -= 2; | ||
514 | memset(sclp_tty->flip.flag_buf_ptr, TTY_NORMAL, count); | ||
515 | sclp_tty->flip.char_buf_ptr += count; | ||
516 | sclp_tty->flip.flag_buf_ptr += count; | ||
517 | sclp_tty->flip.count += count; | ||
518 | tty_flip_buffer_push(sclp_tty); | ||
519 | break; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | /* | ||
524 | * get a EBCDIC string in upper/lower case, | ||
525 | * find out characters in lower/upper case separated by a special character, | ||
526 | * modifiy original string, | ||
527 | * returns length of resulting string | ||
528 | */ | ||
529 | static int | ||
530 | sclp_switch_cases(unsigned char *buf, int count, | ||
531 | unsigned char delim, int tolower) | ||
532 | { | ||
533 | unsigned char *ip, *op; | ||
534 | int toggle; | ||
535 | |||
536 | /* initially changing case is off */ | ||
537 | toggle = 0; | ||
538 | ip = op = buf; | ||
539 | while (count-- > 0) { | ||
540 | /* compare with special character */ | ||
541 | if (*ip == delim) { | ||
542 | /* followed by another special character? */ | ||
543 | if (count && ip[1] == delim) { | ||
544 | /* | ||
545 | * ... then put a single copy of the special | ||
546 | * character to the output string | ||
547 | */ | ||
548 | *op++ = *ip++; | ||
549 | count--; | ||
550 | } else | ||
551 | /* | ||
552 | * ... special character follower by a normal | ||
553 | * character toggles the case change behaviour | ||
554 | */ | ||
555 | toggle = ~toggle; | ||
556 | /* skip special character */ | ||
557 | ip++; | ||
558 | } else | ||
559 | /* not the special character */ | ||
560 | if (toggle) | ||
561 | /* but case switching is on */ | ||
562 | if (tolower) | ||
563 | /* switch to uppercase */ | ||
564 | *op++ = _ebc_toupper[(int) *ip++]; | ||
565 | else | ||
566 | /* switch to lowercase */ | ||
567 | *op++ = _ebc_tolower[(int) *ip++]; | ||
568 | else | ||
569 | /* no case switching, copy the character */ | ||
570 | *op++ = *ip++; | ||
571 | } | ||
572 | /* return length of reformatted string. */ | ||
573 | return op - buf; | ||
574 | } | ||
575 | |||
576 | static void | ||
577 | sclp_get_input(unsigned char *start, unsigned char *end) | ||
578 | { | ||
579 | int count; | ||
580 | |||
581 | count = end - start; | ||
582 | /* | ||
583 | * if set in ioctl convert EBCDIC to lower case | ||
584 | * (modify original input in SCCB) | ||
585 | */ | ||
586 | if (sclp_ioctls.tolower) | ||
587 | EBC_TOLOWER(start, count); | ||
588 | |||
589 | /* | ||
590 | * if set in ioctl find out characters in lower or upper case | ||
591 | * (depends on current case) separated by a special character, | ||
592 | * works on EBCDIC | ||
593 | */ | ||
594 | if (sclp_ioctls.delim) | ||
595 | count = sclp_switch_cases(start, count, | ||
596 | sclp_ioctls.delim, | ||
597 | sclp_ioctls.tolower); | ||
598 | |||
599 | /* convert EBCDIC to ASCII (modify original input in SCCB) */ | ||
600 | sclp_ebcasc_str(start, count); | ||
601 | |||
602 | /* if set in ioctl write operators input to console */ | ||
603 | if (sclp_ioctls.echo) | ||
604 | sclp_tty_write(sclp_tty, start, count); | ||
605 | |||
606 | /* transfer input to high level driver */ | ||
607 | sclp_tty_input(start, count); | ||
608 | } | ||
609 | |||
610 | static inline struct gds_vector * | ||
611 | find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) | ||
612 | { | ||
613 | struct gds_vector *vec; | ||
614 | |||
615 | for (vec = start; vec < end; vec = (void *) vec + vec->length) | ||
616 | if (vec->gds_id == id) | ||
617 | return vec; | ||
618 | return NULL; | ||
619 | } | ||
620 | |||
621 | static inline struct gds_subvector * | ||
622 | find_gds_subvector(struct gds_subvector *start, | ||
623 | struct gds_subvector *end, u8 key) | ||
624 | { | ||
625 | struct gds_subvector *subvec; | ||
626 | |||
627 | for (subvec = start; subvec < end; | ||
628 | subvec = (void *) subvec + subvec->length) | ||
629 | if (subvec->key == key) | ||
630 | return subvec; | ||
631 | return NULL; | ||
632 | } | ||
633 | |||
634 | static inline void | ||
635 | sclp_eval_selfdeftextmsg(struct gds_subvector *start, | ||
636 | struct gds_subvector *end) | ||
637 | { | ||
638 | struct gds_subvector *subvec; | ||
639 | |||
640 | subvec = start; | ||
641 | while (subvec < end) { | ||
642 | subvec = find_gds_subvector(subvec, end, 0x30); | ||
643 | if (!subvec) | ||
644 | break; | ||
645 | sclp_get_input((unsigned char *)(subvec + 1), | ||
646 | (unsigned char *) subvec + subvec->length); | ||
647 | subvec = (void *) subvec + subvec->length; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static inline void | ||
652 | sclp_eval_textcmd(struct gds_subvector *start, | ||
653 | struct gds_subvector *end) | ||
654 | { | ||
655 | struct gds_subvector *subvec; | ||
656 | |||
657 | subvec = start; | ||
658 | while (subvec < end) { | ||
659 | subvec = find_gds_subvector(subvec, end, | ||
660 | GDS_KEY_SelfDefTextMsg); | ||
661 | if (!subvec) | ||
662 | break; | ||
663 | sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), | ||
664 | (void *)subvec + subvec->length); | ||
665 | subvec = (void *) subvec + subvec->length; | ||
666 | } | ||
667 | } | ||
668 | |||
669 | static inline void | ||
670 | sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) | ||
671 | { | ||
672 | struct gds_vector *vec; | ||
673 | |||
674 | vec = start; | ||
675 | while (vec < end) { | ||
676 | vec = find_gds_vector(vec, end, GDS_ID_TextCmd); | ||
677 | if (!vec) | ||
678 | break; | ||
679 | sclp_eval_textcmd((struct gds_subvector *)(vec + 1), | ||
680 | (void *) vec + vec->length); | ||
681 | vec = (void *) vec + vec->length; | ||
682 | } | ||
683 | } | ||
684 | |||
685 | |||
686 | static inline void | ||
687 | sclp_eval_mdsmu(struct gds_vector *start, void *end) | ||
688 | { | ||
689 | struct gds_vector *vec; | ||
690 | |||
691 | vec = find_gds_vector(start, end, GDS_ID_CPMSU); | ||
692 | if (vec) | ||
693 | sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length); | ||
694 | } | ||
695 | |||
696 | static void | ||
697 | sclp_tty_receiver(struct evbuf_header *evbuf) | ||
698 | { | ||
699 | struct gds_vector *start, *end, *vec; | ||
700 | |||
701 | start = (struct gds_vector *)(evbuf + 1); | ||
702 | end = (void *) evbuf + evbuf->length; | ||
703 | vec = find_gds_vector(start, end, GDS_ID_MDSMU); | ||
704 | if (vec) | ||
705 | sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length); | ||
706 | } | ||
707 | |||
708 | static void | ||
709 | sclp_tty_state_change(struct sclp_register *reg) | ||
710 | { | ||
711 | } | ||
712 | |||
713 | static struct sclp_register sclp_input_event = | ||
714 | { | ||
715 | .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask, | ||
716 | .state_change_fn = sclp_tty_state_change, | ||
717 | .receiver_fn = sclp_tty_receiver | ||
718 | }; | ||
719 | |||
720 | static struct tty_operations sclp_ops = { | ||
721 | .open = sclp_tty_open, | ||
722 | .close = sclp_tty_close, | ||
723 | .write = sclp_tty_write, | ||
724 | .put_char = sclp_tty_put_char, | ||
725 | .flush_chars = sclp_tty_flush_chars, | ||
726 | .write_room = sclp_tty_write_room, | ||
727 | .chars_in_buffer = sclp_tty_chars_in_buffer, | ||
728 | .flush_buffer = sclp_tty_flush_buffer, | ||
729 | .ioctl = sclp_tty_ioctl, | ||
730 | }; | ||
731 | |||
732 | int __init | ||
733 | sclp_tty_init(void) | ||
734 | { | ||
735 | struct tty_driver *driver; | ||
736 | void *page; | ||
737 | int i; | ||
738 | int rc; | ||
739 | |||
740 | if (!CONSOLE_IS_SCLP) | ||
741 | return 0; | ||
742 | driver = alloc_tty_driver(1); | ||
743 | if (!driver) | ||
744 | return -ENOMEM; | ||
745 | |||
746 | rc = sclp_rw_init(); | ||
747 | if (rc) { | ||
748 | printk(KERN_ERR SCLP_TTY_PRINT_HEADER | ||
749 | "could not register tty - " | ||
750 | "sclp_rw_init returned %d\n", rc); | ||
751 | put_tty_driver(driver); | ||
752 | return rc; | ||
753 | } | ||
754 | /* Allocate pages for output buffering */ | ||
755 | INIT_LIST_HEAD(&sclp_tty_pages); | ||
756 | for (i = 0; i < MAX_KMEM_PAGES; i++) { | ||
757 | page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
758 | if (page == NULL) { | ||
759 | put_tty_driver(driver); | ||
760 | return -ENOMEM; | ||
761 | } | ||
762 | list_add_tail((struct list_head *) page, &sclp_tty_pages); | ||
763 | } | ||
764 | INIT_LIST_HEAD(&sclp_tty_outqueue); | ||
765 | spin_lock_init(&sclp_tty_lock); | ||
766 | init_waitqueue_head(&sclp_tty_waitq); | ||
767 | init_timer(&sclp_tty_timer); | ||
768 | sclp_ttybuf = NULL; | ||
769 | sclp_tty_buffer_count = 0; | ||
770 | if (MACHINE_IS_VM) { | ||
771 | /* | ||
772 | * save 4 characters for the CPU number | ||
773 | * written at start of each line by VM/CP | ||
774 | */ | ||
775 | sclp_ioctls_init.columns = 76; | ||
776 | /* case input lines to lowercase */ | ||
777 | sclp_ioctls_init.tolower = 1; | ||
778 | } | ||
779 | sclp_ioctls = sclp_ioctls_init; | ||
780 | sclp_tty_chars_count = 0; | ||
781 | sclp_tty = NULL; | ||
782 | |||
783 | rc = sclp_register(&sclp_input_event); | ||
784 | if (rc) { | ||
785 | put_tty_driver(driver); | ||
786 | return rc; | ||
787 | } | ||
788 | |||
789 | driver->owner = THIS_MODULE; | ||
790 | driver->driver_name = "sclp_line"; | ||
791 | driver->name = "sclp_line"; | ||
792 | driver->major = TTY_MAJOR; | ||
793 | driver->minor_start = 64; | ||
794 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | ||
795 | driver->subtype = SYSTEM_TYPE_TTY; | ||
796 | driver->init_termios = tty_std_termios; | ||
797 | driver->init_termios.c_iflag = IGNBRK | IGNPAR; | ||
798 | driver->init_termios.c_oflag = ONLCR | XTABS; | ||
799 | driver->init_termios.c_lflag = ISIG | ECHO; | ||
800 | driver->flags = TTY_DRIVER_REAL_RAW; | ||
801 | tty_set_operations(driver, &sclp_ops); | ||
802 | rc = tty_register_driver(driver); | ||
803 | if (rc) { | ||
804 | printk(KERN_ERR SCLP_TTY_PRINT_HEADER | ||
805 | "could not register tty - " | ||
806 | "tty_register_driver returned %d\n", rc); | ||
807 | put_tty_driver(driver); | ||
808 | return rc; | ||
809 | } | ||
810 | sclp_tty_driver = driver; | ||
811 | return 0; | ||
812 | } | ||
813 | module_init(sclp_tty_init); | ||