1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * SCLP VT220 terminal driver. |
4 | * |
5 | * Copyright IBM Corp. 2003, 2009 |
6 | * |
7 | * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/spinlock.h> |
12 | #include <linux/panic_notifier.h> |
13 | #include <linux/list.h> |
14 | #include <linux/wait.h> |
15 | #include <linux/timer.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/sysrq.h> |
18 | #include <linux/tty.h> |
19 | #include <linux/tty_driver.h> |
20 | #include <linux/tty_flip.h> |
21 | #include <linux/errno.h> |
22 | #include <linux/mm.h> |
23 | #include <linux/major.h> |
24 | #include <linux/console.h> |
25 | #include <linux/kdev_t.h> |
26 | #include <linux/interrupt.h> |
27 | #include <linux/init.h> |
28 | #include <linux/reboot.h> |
29 | #include <linux/slab.h> |
30 | |
31 | #include <linux/uaccess.h> |
32 | #include "sclp.h" |
33 | #include "ctrlchar.h" |
34 | |
35 | #define SCLP_VT220_MAJOR TTY_MAJOR |
36 | #define SCLP_VT220_MINOR 65 |
37 | #define SCLP_VT220_DRIVER_NAME "sclp_vt220" |
38 | #define SCLP_VT220_DEVICE_NAME "ttysclp" |
39 | #define SCLP_VT220_CONSOLE_NAME "ttysclp" |
40 | #define SCLP_VT220_CONSOLE_INDEX 0 /* console=ttysclp0 */ |
41 | |
42 | /* Representation of a single write request */ |
43 | struct sclp_vt220_request { |
44 | struct list_head list; |
45 | struct sclp_req sclp_req; |
46 | int retry_count; |
47 | }; |
48 | |
49 | /* VT220 SCCB */ |
50 | struct sclp_vt220_sccb { |
51 | struct sccb_header ; |
52 | struct evbuf_header evbuf; |
53 | }; |
54 | |
55 | #define SCLP_VT220_MAX_CHARS_PER_BUFFER (PAGE_SIZE - \ |
56 | sizeof(struct sclp_vt220_request) - \ |
57 | sizeof(struct sclp_vt220_sccb)) |
58 | |
59 | /* Structures and data needed to register tty driver */ |
60 | static struct tty_driver *sclp_vt220_driver; |
61 | |
62 | static struct tty_port sclp_vt220_port; |
63 | |
64 | /* Lock to protect internal data from concurrent access */ |
65 | static DEFINE_SPINLOCK(sclp_vt220_lock); |
66 | |
67 | /* List of empty pages to be used as write request buffers */ |
68 | static LIST_HEAD(sclp_vt220_empty); |
69 | |
70 | /* List of pending requests */ |
71 | static LIST_HEAD(sclp_vt220_outqueue); |
72 | |
73 | /* Flag that output queue is currently running */ |
74 | static int sclp_vt220_queue_running; |
75 | |
76 | /* Timer used for delaying write requests to merge subsequent messages into |
77 | * a single buffer */ |
78 | static struct timer_list sclp_vt220_timer; |
79 | |
80 | /* Pointer to current request buffer which has been partially filled but not |
81 | * yet sent */ |
82 | static struct sclp_vt220_request *sclp_vt220_current_request; |
83 | |
84 | /* Number of characters in current request buffer */ |
85 | static int sclp_vt220_buffered_chars; |
86 | |
87 | /* Counter controlling core driver initialization. */ |
88 | static int __initdata sclp_vt220_init_count; |
89 | |
90 | /* Flag indicating that sclp_vt220_current_request should really |
91 | * have been already queued but wasn't because the SCLP was processing |
92 | * another buffer */ |
93 | static int sclp_vt220_flush_later; |
94 | |
95 | static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf); |
96 | static int __sclp_vt220_emit(struct sclp_vt220_request *request); |
97 | static void sclp_vt220_emit_current(void); |
98 | |
99 | /* Registration structure for SCLP output event buffers */ |
100 | static struct sclp_register sclp_vt220_register = { |
101 | .send_mask = EVTYP_VT220MSG_MASK, |
102 | }; |
103 | |
104 | /* Registration structure for SCLP input event buffers */ |
105 | static struct sclp_register sclp_vt220_register_input = { |
106 | .receive_mask = EVTYP_VT220MSG_MASK, |
107 | .receiver_fn = sclp_vt220_receiver_fn, |
108 | }; |
109 | |
110 | |
111 | /* |
112 | * Put provided request buffer back into queue and check emit pending |
113 | * buffers if necessary. |
114 | */ |
115 | static void |
116 | sclp_vt220_process_queue(struct sclp_vt220_request *request) |
117 | { |
118 | unsigned long flags; |
119 | void *page; |
120 | |
121 | do { |
122 | /* Put buffer back to list of empty buffers */ |
123 | page = request->sclp_req.sccb; |
124 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
125 | /* Move request from outqueue to empty queue */ |
126 | list_del(entry: &request->list); |
127 | list_add_tail(new: (struct list_head *) page, head: &sclp_vt220_empty); |
128 | /* Check if there is a pending buffer on the out queue. */ |
129 | request = NULL; |
130 | if (!list_empty(head: &sclp_vt220_outqueue)) |
131 | request = list_entry(sclp_vt220_outqueue.next, |
132 | struct sclp_vt220_request, list); |
133 | if (!request) { |
134 | sclp_vt220_queue_running = 0; |
135 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
136 | break; |
137 | } |
138 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
139 | } while (__sclp_vt220_emit(request)); |
140 | if (request == NULL && sclp_vt220_flush_later) |
141 | sclp_vt220_emit_current(); |
142 | tty_port_tty_wakeup(port: &sclp_vt220_port); |
143 | } |
144 | |
145 | #define SCLP_BUFFER_MAX_RETRY 1 |
146 | |
147 | /* |
148 | * Callback through which the result of a write request is reported by the |
149 | * SCLP. |
150 | */ |
151 | static void |
152 | sclp_vt220_callback(struct sclp_req *request, void *data) |
153 | { |
154 | struct sclp_vt220_request *vt220_request; |
155 | struct sclp_vt220_sccb *sccb; |
156 | |
157 | vt220_request = (struct sclp_vt220_request *) data; |
158 | if (request->status == SCLP_REQ_FAILED) { |
159 | sclp_vt220_process_queue(request: vt220_request); |
160 | return; |
161 | } |
162 | sccb = (struct sclp_vt220_sccb *) vt220_request->sclp_req.sccb; |
163 | |
164 | /* Check SCLP response code and choose suitable action */ |
165 | switch (sccb->header.response_code) { |
166 | case 0x0020 : |
167 | break; |
168 | |
169 | case 0x05f0: /* Target resource in improper state */ |
170 | break; |
171 | |
172 | case 0x0340: /* Contained SCLP equipment check */ |
173 | if (++vt220_request->retry_count > SCLP_BUFFER_MAX_RETRY) |
174 | break; |
175 | /* Remove processed buffers and requeue rest */ |
176 | if (sclp_remove_processed(sccb: (struct sccb_header *) sccb) > 0) { |
177 | /* Not all buffers were processed */ |
178 | sccb->header.response_code = 0x0000; |
179 | vt220_request->sclp_req.status = SCLP_REQ_FILLED; |
180 | if (sclp_add_request(req: request) == 0) |
181 | return; |
182 | } |
183 | break; |
184 | |
185 | case 0x0040: /* SCLP equipment check */ |
186 | if (++vt220_request->retry_count > SCLP_BUFFER_MAX_RETRY) |
187 | break; |
188 | sccb->header.response_code = 0x0000; |
189 | vt220_request->sclp_req.status = SCLP_REQ_FILLED; |
190 | if (sclp_add_request(req: request) == 0) |
191 | return; |
192 | break; |
193 | |
194 | default: |
195 | break; |
196 | } |
197 | sclp_vt220_process_queue(request: vt220_request); |
198 | } |
199 | |
200 | /* |
201 | * Emit vt220 request buffer to SCLP. Return zero on success, non-zero |
202 | * otherwise. |
203 | */ |
204 | static int |
205 | __sclp_vt220_emit(struct sclp_vt220_request *request) |
206 | { |
207 | request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA; |
208 | request->sclp_req.status = SCLP_REQ_FILLED; |
209 | request->sclp_req.callback = sclp_vt220_callback; |
210 | request->sclp_req.callback_data = (void *) request; |
211 | |
212 | return sclp_add_request(req: &request->sclp_req); |
213 | } |
214 | |
215 | /* |
216 | * Queue and emit current request. |
217 | */ |
218 | static void |
219 | sclp_vt220_emit_current(void) |
220 | { |
221 | unsigned long flags; |
222 | struct sclp_vt220_request *request; |
223 | struct sclp_vt220_sccb *sccb; |
224 | |
225 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
226 | if (sclp_vt220_current_request) { |
227 | sccb = (struct sclp_vt220_sccb *) |
228 | sclp_vt220_current_request->sclp_req.sccb; |
229 | /* Only emit buffers with content */ |
230 | if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) { |
231 | list_add_tail(new: &sclp_vt220_current_request->list, |
232 | head: &sclp_vt220_outqueue); |
233 | sclp_vt220_current_request = NULL; |
234 | del_timer(timer: &sclp_vt220_timer); |
235 | } |
236 | sclp_vt220_flush_later = 0; |
237 | } |
238 | if (sclp_vt220_queue_running) |
239 | goto out_unlock; |
240 | if (list_empty(head: &sclp_vt220_outqueue)) |
241 | goto out_unlock; |
242 | request = list_first_entry(&sclp_vt220_outqueue, |
243 | struct sclp_vt220_request, list); |
244 | sclp_vt220_queue_running = 1; |
245 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
246 | |
247 | if (__sclp_vt220_emit(request)) |
248 | sclp_vt220_process_queue(request); |
249 | return; |
250 | out_unlock: |
251 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
252 | } |
253 | |
254 | #define SCLP_NORMAL_WRITE 0x00 |
255 | |
256 | /* |
257 | * Helper function to initialize a page with the sclp request structure. |
258 | */ |
259 | static struct sclp_vt220_request * |
260 | sclp_vt220_initialize_page(void *page) |
261 | { |
262 | struct sclp_vt220_request *request; |
263 | struct sclp_vt220_sccb *sccb; |
264 | |
265 | /* Place request structure at end of page */ |
266 | request = ((struct sclp_vt220_request *) |
267 | ((addr_t) page + PAGE_SIZE)) - 1; |
268 | request->retry_count = 0; |
269 | request->sclp_req.sccb = page; |
270 | /* SCCB goes at start of page */ |
271 | sccb = (struct sclp_vt220_sccb *) page; |
272 | memset((void *) sccb, 0, sizeof(struct sclp_vt220_sccb)); |
273 | sccb->header.length = sizeof(struct sclp_vt220_sccb); |
274 | sccb->header.function_code = SCLP_NORMAL_WRITE; |
275 | sccb->header.response_code = 0x0000; |
276 | sccb->evbuf.type = EVTYP_VT220MSG; |
277 | sccb->evbuf.length = sizeof(struct evbuf_header); |
278 | |
279 | return request; |
280 | } |
281 | |
282 | static inline unsigned int |
283 | sclp_vt220_space_left(struct sclp_vt220_request *request) |
284 | { |
285 | struct sclp_vt220_sccb *sccb; |
286 | sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb; |
287 | return PAGE_SIZE - sizeof(struct sclp_vt220_request) - |
288 | sccb->header.length; |
289 | } |
290 | |
291 | static inline unsigned int |
292 | sclp_vt220_chars_stored(struct sclp_vt220_request *request) |
293 | { |
294 | struct sclp_vt220_sccb *sccb; |
295 | sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb; |
296 | return sccb->evbuf.length - sizeof(struct evbuf_header); |
297 | } |
298 | |
299 | /* |
300 | * Add msg to buffer associated with request. Return the number of characters |
301 | * added. |
302 | */ |
303 | static int |
304 | sclp_vt220_add_msg(struct sclp_vt220_request *request, |
305 | const unsigned char *msg, int count, int convertlf) |
306 | { |
307 | struct sclp_vt220_sccb *sccb; |
308 | void *buffer; |
309 | unsigned char c; |
310 | int from; |
311 | int to; |
312 | |
313 | if (count > sclp_vt220_space_left(request)) |
314 | count = sclp_vt220_space_left(request); |
315 | if (count <= 0) |
316 | return 0; |
317 | |
318 | sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb; |
319 | buffer = (void *) ((addr_t) sccb + sccb->header.length); |
320 | |
321 | if (convertlf) { |
322 | /* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/ |
323 | for (from=0, to=0; |
324 | (from < count) && (to < sclp_vt220_space_left(request)); |
325 | from++) { |
326 | /* Retrieve character */ |
327 | c = msg[from]; |
328 | /* Perform conversion */ |
329 | if (c == 0x0a) { |
330 | if (to + 1 < sclp_vt220_space_left(request)) { |
331 | ((unsigned char *) buffer)[to++] = c; |
332 | ((unsigned char *) buffer)[to++] = 0x0d; |
333 | } else |
334 | break; |
335 | |
336 | } else |
337 | ((unsigned char *) buffer)[to++] = c; |
338 | } |
339 | sccb->header.length += to; |
340 | sccb->evbuf.length += to; |
341 | return from; |
342 | } else { |
343 | memcpy(buffer, (const void *) msg, count); |
344 | sccb->header.length += count; |
345 | sccb->evbuf.length += count; |
346 | return count; |
347 | } |
348 | } |
349 | |
350 | /* |
351 | * Emit buffer after having waited long enough for more data to arrive. |
352 | */ |
353 | static void |
354 | sclp_vt220_timeout(struct timer_list *unused) |
355 | { |
356 | sclp_vt220_emit_current(); |
357 | } |
358 | |
359 | #define BUFFER_MAX_DELAY HZ/20 |
360 | |
361 | /* |
362 | * Drop oldest console buffer if sclp_con_drop is set |
363 | */ |
364 | static int |
365 | sclp_vt220_drop_buffer(void) |
366 | { |
367 | struct list_head *list; |
368 | struct sclp_vt220_request *request; |
369 | void *page; |
370 | |
371 | if (!sclp_console_drop) |
372 | return 0; |
373 | list = sclp_vt220_outqueue.next; |
374 | if (sclp_vt220_queue_running) |
375 | /* The first element is in I/O */ |
376 | list = list->next; |
377 | if (list == &sclp_vt220_outqueue) |
378 | return 0; |
379 | list_del(entry: list); |
380 | request = list_entry(list, struct sclp_vt220_request, list); |
381 | page = request->sclp_req.sccb; |
382 | list_add_tail(new: (struct list_head *) page, head: &sclp_vt220_empty); |
383 | return 1; |
384 | } |
385 | |
386 | /* |
387 | * Internal implementation of the write function. Write COUNT bytes of data |
388 | * from memory at BUF |
389 | * to the SCLP interface. In case that the data does not fit into the current |
390 | * write buffer, emit the current one and allocate a new one. If there are no |
391 | * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE |
392 | * is non-zero, the buffer will be scheduled for emitting after a timeout - |
393 | * otherwise the user has to explicitly call the flush function. |
394 | * A non-zero CONVERTLF parameter indicates that 0x0a characters in the message |
395 | * buffer should be converted to 0x0a 0x0d. After completion, return the number |
396 | * of bytes written. |
397 | */ |
398 | static int |
399 | __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, |
400 | int convertlf, int may_fail) |
401 | { |
402 | unsigned long flags; |
403 | void *page; |
404 | int written; |
405 | int overall_written; |
406 | |
407 | if (count <= 0) |
408 | return 0; |
409 | overall_written = 0; |
410 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
411 | do { |
412 | /* Create an sclp output buffer if none exists yet */ |
413 | if (sclp_vt220_current_request == NULL) { |
414 | if (list_empty(head: &sclp_vt220_empty)) |
415 | sclp_console_full++; |
416 | while (list_empty(head: &sclp_vt220_empty)) { |
417 | if (may_fail) |
418 | goto out; |
419 | if (sclp_vt220_drop_buffer()) |
420 | break; |
421 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
422 | |
423 | sclp_sync_wait(); |
424 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
425 | } |
426 | page = (void *) sclp_vt220_empty.next; |
427 | list_del(entry: (struct list_head *) page); |
428 | sclp_vt220_current_request = |
429 | sclp_vt220_initialize_page(page); |
430 | } |
431 | /* Try to write the string to the current request buffer */ |
432 | written = sclp_vt220_add_msg(request: sclp_vt220_current_request, |
433 | msg: buf, count, convertlf); |
434 | overall_written += written; |
435 | if (written == count) |
436 | break; |
437 | /* |
438 | * Not all characters could be written to the current |
439 | * output buffer. Emit the buffer, create a new buffer |
440 | * and then output the rest of the string. |
441 | */ |
442 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
443 | sclp_vt220_emit_current(); |
444 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
445 | buf += written; |
446 | count -= written; |
447 | } while (count > 0); |
448 | /* Setup timer to output current console buffer after some time */ |
449 | if (sclp_vt220_current_request != NULL && |
450 | !timer_pending(timer: &sclp_vt220_timer) && do_schedule) { |
451 | sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; |
452 | add_timer(timer: &sclp_vt220_timer); |
453 | } |
454 | out: |
455 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
456 | return overall_written; |
457 | } |
458 | |
459 | /* |
460 | * This routine is called by the kernel to write a series of |
461 | * characters to the tty device. The characters may come from |
462 | * user space or kernel space. This routine will return the |
463 | * number of characters actually accepted for writing. |
464 | */ |
465 | static ssize_t |
466 | sclp_vt220_write(struct tty_struct *tty, const u8 *buf, size_t count) |
467 | { |
468 | return __sclp_vt220_write(buf, count, do_schedule: 1, convertlf: 0, may_fail: 1); |
469 | } |
470 | |
471 | #define SCLP_VT220_SESSION_ENDED 0x01 |
472 | #define SCLP_VT220_SESSION_STARTED 0x80 |
473 | #define SCLP_VT220_SESSION_DATA 0x00 |
474 | |
475 | #ifdef CONFIG_MAGIC_SYSRQ |
476 | |
477 | static int sysrq_pressed; |
478 | static struct sysrq_work sysrq; |
479 | |
480 | static void sclp_vt220_reset_session(void) |
481 | { |
482 | sysrq_pressed = 0; |
483 | } |
484 | |
485 | static void sclp_vt220_handle_input(const char *buffer, unsigned int count) |
486 | { |
487 | int i; |
488 | |
489 | for (i = 0; i < count; i++) { |
490 | /* Handle magic sys request */ |
491 | if (buffer[i] == ('O' ^ 0100)) { /* CTRL-O */ |
492 | /* |
493 | * If pressed again, reset sysrq_pressed |
494 | * and flip CTRL-O character |
495 | */ |
496 | sysrq_pressed = !sysrq_pressed; |
497 | if (sysrq_pressed) |
498 | continue; |
499 | } else if (sysrq_pressed) { |
500 | sysrq.key = buffer[i]; |
501 | schedule_sysrq_work(sw: &sysrq); |
502 | sysrq_pressed = 0; |
503 | continue; |
504 | } |
505 | tty_insert_flip_char(port: &sclp_vt220_port, ch: buffer[i], flag: 0); |
506 | } |
507 | } |
508 | |
509 | #else |
510 | |
511 | static void sclp_vt220_reset_session(void) |
512 | { |
513 | } |
514 | |
515 | static void sclp_vt220_handle_input(const char *buffer, unsigned int count) |
516 | { |
517 | tty_insert_flip_string(&sclp_vt220_port, buffer, count); |
518 | } |
519 | |
520 | #endif |
521 | |
522 | /* |
523 | * Called by the SCLP to report incoming event buffers. |
524 | */ |
525 | static void |
526 | sclp_vt220_receiver_fn(struct evbuf_header *evbuf) |
527 | { |
528 | char *buffer; |
529 | unsigned int count; |
530 | |
531 | buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header)); |
532 | count = evbuf->length - sizeof(struct evbuf_header); |
533 | |
534 | switch (*buffer) { |
535 | case SCLP_VT220_SESSION_ENDED: |
536 | case SCLP_VT220_SESSION_STARTED: |
537 | sclp_vt220_reset_session(); |
538 | break; |
539 | case SCLP_VT220_SESSION_DATA: |
540 | /* Send input to line discipline */ |
541 | buffer++; |
542 | count--; |
543 | sclp_vt220_handle_input(buffer, count); |
544 | tty_flip_buffer_push(port: &sclp_vt220_port); |
545 | break; |
546 | } |
547 | } |
548 | |
549 | /* |
550 | * This routine is called when a particular tty device is opened. |
551 | */ |
552 | static int |
553 | sclp_vt220_open(struct tty_struct *tty, struct file *filp) |
554 | { |
555 | if (tty->count == 1) { |
556 | tty_port_tty_set(port: &sclp_vt220_port, tty); |
557 | if (!tty->winsize.ws_row && !tty->winsize.ws_col) { |
558 | tty->winsize.ws_row = 24; |
559 | tty->winsize.ws_col = 80; |
560 | } |
561 | } |
562 | return 0; |
563 | } |
564 | |
565 | /* |
566 | * This routine is called when a particular tty device is closed. |
567 | */ |
568 | static void |
569 | sclp_vt220_close(struct tty_struct *tty, struct file *filp) |
570 | { |
571 | if (tty->count == 1) |
572 | tty_port_tty_set(port: &sclp_vt220_port, NULL); |
573 | } |
574 | |
575 | /* |
576 | * This routine is called by the kernel to write a single |
577 | * character to the tty device. If the kernel uses this routine, |
578 | * it must call the flush_chars() routine (if defined) when it is |
579 | * done stuffing characters into the driver. |
580 | */ |
581 | static int |
582 | sclp_vt220_put_char(struct tty_struct *tty, u8 ch) |
583 | { |
584 | return __sclp_vt220_write(buf: &ch, count: 1, do_schedule: 0, convertlf: 0, may_fail: 1); |
585 | } |
586 | |
587 | /* |
588 | * This routine is called by the kernel after it has written a |
589 | * series of characters to the tty device using put_char(). |
590 | */ |
591 | static void |
592 | sclp_vt220_flush_chars(struct tty_struct *tty) |
593 | { |
594 | if (!sclp_vt220_queue_running) |
595 | sclp_vt220_emit_current(); |
596 | else |
597 | sclp_vt220_flush_later = 1; |
598 | } |
599 | |
600 | /* |
601 | * This routine returns the numbers of characters the tty driver |
602 | * will accept for queuing to be written. This number is subject |
603 | * to change as output buffers get emptied, or if the output flow |
604 | * control is acted. |
605 | */ |
606 | static unsigned int |
607 | sclp_vt220_write_room(struct tty_struct *tty) |
608 | { |
609 | unsigned long flags; |
610 | struct list_head *l; |
611 | unsigned int count; |
612 | |
613 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
614 | count = 0; |
615 | if (sclp_vt220_current_request != NULL) |
616 | count = sclp_vt220_space_left(request: sclp_vt220_current_request); |
617 | list_for_each(l, &sclp_vt220_empty) |
618 | count += SCLP_VT220_MAX_CHARS_PER_BUFFER; |
619 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
620 | return count; |
621 | } |
622 | |
623 | /* |
624 | * Return number of buffered chars. |
625 | */ |
626 | static unsigned int |
627 | sclp_vt220_chars_in_buffer(struct tty_struct *tty) |
628 | { |
629 | unsigned long flags; |
630 | struct list_head *l; |
631 | struct sclp_vt220_request *r; |
632 | unsigned int count = 0; |
633 | |
634 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
635 | if (sclp_vt220_current_request != NULL) |
636 | count = sclp_vt220_chars_stored(request: sclp_vt220_current_request); |
637 | list_for_each(l, &sclp_vt220_outqueue) { |
638 | r = list_entry(l, struct sclp_vt220_request, list); |
639 | count += sclp_vt220_chars_stored(request: r); |
640 | } |
641 | spin_unlock_irqrestore(lock: &sclp_vt220_lock, flags); |
642 | return count; |
643 | } |
644 | |
645 | /* |
646 | * Pass on all buffers to the hardware. Return only when there are no more |
647 | * buffers pending. |
648 | */ |
649 | static void |
650 | sclp_vt220_flush_buffer(struct tty_struct *tty) |
651 | { |
652 | sclp_vt220_emit_current(); |
653 | } |
654 | |
655 | /* Release allocated pages. */ |
656 | static void __init __sclp_vt220_free_pages(void) |
657 | { |
658 | struct list_head *page, *p; |
659 | |
660 | list_for_each_safe(page, p, &sclp_vt220_empty) { |
661 | list_del(entry: page); |
662 | free_page((unsigned long) page); |
663 | } |
664 | } |
665 | |
666 | /* Release memory and unregister from sclp core. Controlled by init counting - |
667 | * only the last invoker will actually perform these actions. */ |
668 | static void __init __sclp_vt220_cleanup(void) |
669 | { |
670 | sclp_vt220_init_count--; |
671 | if (sclp_vt220_init_count != 0) |
672 | return; |
673 | sclp_unregister(reg: &sclp_vt220_register); |
674 | __sclp_vt220_free_pages(); |
675 | tty_port_destroy(port: &sclp_vt220_port); |
676 | } |
677 | |
678 | /* Allocate buffer pages and register with sclp core. Controlled by init |
679 | * counting - only the first invoker will actually perform these actions. */ |
680 | static int __init __sclp_vt220_init(int num_pages) |
681 | { |
682 | void *page; |
683 | int i; |
684 | int rc; |
685 | |
686 | sclp_vt220_init_count++; |
687 | if (sclp_vt220_init_count != 1) |
688 | return 0; |
689 | timer_setup(&sclp_vt220_timer, sclp_vt220_timeout, 0); |
690 | tty_port_init(port: &sclp_vt220_port); |
691 | sclp_vt220_current_request = NULL; |
692 | sclp_vt220_buffered_chars = 0; |
693 | sclp_vt220_flush_later = 0; |
694 | |
695 | /* Allocate pages for output buffering */ |
696 | rc = -ENOMEM; |
697 | for (i = 0; i < num_pages; i++) { |
698 | page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
699 | if (!page) |
700 | goto out; |
701 | list_add_tail(new: page, head: &sclp_vt220_empty); |
702 | } |
703 | rc = sclp_register(reg: &sclp_vt220_register); |
704 | out: |
705 | if (rc) { |
706 | __sclp_vt220_free_pages(); |
707 | sclp_vt220_init_count--; |
708 | tty_port_destroy(port: &sclp_vt220_port); |
709 | } |
710 | return rc; |
711 | } |
712 | |
713 | static const struct tty_operations sclp_vt220_ops = { |
714 | .open = sclp_vt220_open, |
715 | .close = sclp_vt220_close, |
716 | .write = sclp_vt220_write, |
717 | .put_char = sclp_vt220_put_char, |
718 | .flush_chars = sclp_vt220_flush_chars, |
719 | .write_room = sclp_vt220_write_room, |
720 | .chars_in_buffer = sclp_vt220_chars_in_buffer, |
721 | .flush_buffer = sclp_vt220_flush_buffer, |
722 | }; |
723 | |
724 | /* |
725 | * Register driver with SCLP and Linux and initialize internal tty structures. |
726 | */ |
727 | static int __init sclp_vt220_tty_init(void) |
728 | { |
729 | struct tty_driver *driver; |
730 | int rc; |
731 | |
732 | /* Note: we're not testing for CONSOLE_IS_SCLP here to preserve |
733 | * symmetry between VM and LPAR systems regarding ttyS1. */ |
734 | driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW); |
735 | if (IS_ERR(ptr: driver)) |
736 | return PTR_ERR(ptr: driver); |
737 | rc = __sclp_vt220_init(MAX_KMEM_PAGES); |
738 | if (rc) |
739 | goto out_driver; |
740 | |
741 | driver->driver_name = SCLP_VT220_DRIVER_NAME; |
742 | driver->name = SCLP_VT220_DEVICE_NAME; |
743 | driver->major = SCLP_VT220_MAJOR; |
744 | driver->minor_start = SCLP_VT220_MINOR; |
745 | driver->type = TTY_DRIVER_TYPE_SYSTEM; |
746 | driver->subtype = SYSTEM_TYPE_TTY; |
747 | driver->init_termios = tty_std_termios; |
748 | tty_set_operations(driver, op: &sclp_vt220_ops); |
749 | tty_port_link_device(port: &sclp_vt220_port, driver, index: 0); |
750 | |
751 | rc = tty_register_driver(driver); |
752 | if (rc) |
753 | goto out_init; |
754 | rc = sclp_register(reg: &sclp_vt220_register_input); |
755 | if (rc) |
756 | goto out_reg; |
757 | sclp_vt220_driver = driver; |
758 | return 0; |
759 | |
760 | out_reg: |
761 | tty_unregister_driver(driver); |
762 | out_init: |
763 | __sclp_vt220_cleanup(); |
764 | out_driver: |
765 | tty_driver_kref_put(driver); |
766 | return rc; |
767 | } |
768 | __initcall(sclp_vt220_tty_init); |
769 | |
770 | #ifdef CONFIG_SCLP_VT220_CONSOLE |
771 | |
772 | static void |
773 | sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count) |
774 | { |
775 | __sclp_vt220_write((const unsigned char *) buf, count, 1, 1, 0); |
776 | } |
777 | |
778 | static struct tty_driver * |
779 | sclp_vt220_con_device(struct console *c, int *index) |
780 | { |
781 | *index = 0; |
782 | return sclp_vt220_driver; |
783 | } |
784 | |
785 | /* |
786 | * This panic/reboot notifier runs in atomic context, so |
787 | * locking restrictions apply to prevent potential lockups. |
788 | */ |
789 | static int |
790 | sclp_vt220_notify(struct notifier_block *self, |
791 | unsigned long event, void *data) |
792 | { |
793 | unsigned long flags; |
794 | |
795 | if (spin_is_locked(&sclp_vt220_lock)) |
796 | return NOTIFY_DONE; |
797 | |
798 | sclp_vt220_emit_current(); |
799 | |
800 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
801 | del_timer(&sclp_vt220_timer); |
802 | while (sclp_vt220_queue_running) { |
803 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
804 | sclp_sync_wait(); |
805 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
806 | } |
807 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
808 | |
809 | return NOTIFY_DONE; |
810 | } |
811 | |
812 | static struct notifier_block on_panic_nb = { |
813 | .notifier_call = sclp_vt220_notify, |
814 | .priority = INT_MIN + 1, /* run the callback late */ |
815 | }; |
816 | |
817 | static struct notifier_block on_reboot_nb = { |
818 | .notifier_call = sclp_vt220_notify, |
819 | .priority = INT_MIN + 1, /* run the callback late */ |
820 | }; |
821 | |
822 | /* Structure needed to register with printk */ |
823 | static struct console sclp_vt220_console = |
824 | { |
825 | .name = SCLP_VT220_CONSOLE_NAME, |
826 | .write = sclp_vt220_con_write, |
827 | .device = sclp_vt220_con_device, |
828 | .flags = CON_PRINTBUFFER, |
829 | .index = SCLP_VT220_CONSOLE_INDEX |
830 | }; |
831 | |
832 | static int __init |
833 | sclp_vt220_con_init(void) |
834 | { |
835 | int rc; |
836 | |
837 | rc = __sclp_vt220_init(sclp_console_pages); |
838 | if (rc) |
839 | return rc; |
840 | /* Attach linux console */ |
841 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); |
842 | register_reboot_notifier(&on_reboot_nb); |
843 | register_console(&sclp_vt220_console); |
844 | return 0; |
845 | } |
846 | |
847 | console_initcall(sclp_vt220_con_init); |
848 | #endif /* CONFIG_SCLP_VT220_CONSOLE */ |
849 | |
850 | |