diff options
Diffstat (limited to 'drivers/s390/char/con3270.c')
-rw-r--r-- | drivers/s390/char/con3270.c | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c new file mode 100644 index 000000000000..d52fb57a6b19 --- /dev/null +++ b/drivers/s390/char/con3270.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/con3270.c | ||
3 | * IBM/3270 Driver - console view. | ||
4 | * | ||
5 | * Author(s): | ||
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | ||
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/bootmem.h> | ||
13 | #include <linux/console.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/types.h> | ||
18 | |||
19 | #include <asm/ccwdev.h> | ||
20 | #include <asm/cio.h> | ||
21 | #include <asm/cpcmd.h> | ||
22 | #include <asm/ebcdic.h> | ||
23 | |||
24 | #include "raw3270.h" | ||
25 | #include "ctrlchar.h" | ||
26 | |||
27 | #define CON3270_OUTPUT_BUFFER_SIZE 1024 | ||
28 | #define CON3270_STRING_PAGES 4 | ||
29 | |||
30 | static struct raw3270_fn con3270_fn; | ||
31 | |||
32 | /* | ||
33 | * Main 3270 console view data structure. | ||
34 | */ | ||
35 | struct con3270 { | ||
36 | struct raw3270_view view; | ||
37 | spinlock_t lock; | ||
38 | struct list_head freemem; /* list of free memory for strings. */ | ||
39 | |||
40 | /* Output stuff. */ | ||
41 | struct list_head lines; /* list of lines. */ | ||
42 | struct list_head update; /* list of lines to update. */ | ||
43 | int line_nr; /* line number for next update. */ | ||
44 | int nr_lines; /* # lines in list. */ | ||
45 | int nr_up; /* # lines up in history. */ | ||
46 | unsigned long update_flags; /* Update indication bits. */ | ||
47 | struct string *cline; /* current output line. */ | ||
48 | struct string *status; /* last line of display. */ | ||
49 | struct raw3270_request *write; /* single write request. */ | ||
50 | struct timer_list timer; | ||
51 | |||
52 | /* Input stuff. */ | ||
53 | struct string *input; /* input string for read request. */ | ||
54 | struct raw3270_request *read; /* single read request. */ | ||
55 | struct raw3270_request *kreset; /* single keyboard reset request. */ | ||
56 | struct tasklet_struct readlet; /* tasklet to issue read request. */ | ||
57 | }; | ||
58 | |||
59 | static struct con3270 *condev; | ||
60 | |||
61 | /* con3270->update_flags. See con3270_update for details. */ | ||
62 | #define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ | ||
63 | #define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ | ||
64 | #define CON_UPDATE_STATUS 4 /* Update status line. */ | ||
65 | #define CON_UPDATE_ALL 7 | ||
66 | |||
67 | static void con3270_update(struct con3270 *); | ||
68 | |||
69 | /* | ||
70 | * Setup timeout for a device. On timeout trigger an update. | ||
71 | */ | ||
72 | void | ||
73 | con3270_set_timer(struct con3270 *cp, int expires) | ||
74 | { | ||
75 | if (expires == 0) { | ||
76 | if (timer_pending(&cp->timer)) | ||
77 | del_timer(&cp->timer); | ||
78 | return; | ||
79 | } | ||
80 | if (timer_pending(&cp->timer) && | ||
81 | mod_timer(&cp->timer, jiffies + expires)) | ||
82 | return; | ||
83 | cp->timer.function = (void (*)(unsigned long)) con3270_update; | ||
84 | cp->timer.data = (unsigned long) cp; | ||
85 | cp->timer.expires = jiffies + expires; | ||
86 | add_timer(&cp->timer); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * The status line is the last line of the screen. It shows the string | ||
91 | * "console view" in the lower left corner and "Running"/"More..."/"Holding" | ||
92 | * in the lower right corner of the screen. | ||
93 | */ | ||
94 | static void | ||
95 | con3270_update_status(struct con3270 *cp) | ||
96 | { | ||
97 | char *str; | ||
98 | |||
99 | str = (cp->nr_up != 0) ? "History" : "Running"; | ||
100 | memcpy(cp->status->string + 24, str, 7); | ||
101 | codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); | ||
102 | cp->update_flags |= CON_UPDATE_STATUS; | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | con3270_create_status(struct con3270 *cp) | ||
107 | { | ||
108 | static const unsigned char blueprint[] = | ||
109 | { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, | ||
110 | 'c','o','n','s','o','l','e',' ','v','i','e','w', | ||
111 | TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; | ||
112 | |||
113 | cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); | ||
114 | /* Copy blueprint to status line */ | ||
115 | memcpy(cp->status->string, blueprint, sizeof(blueprint)); | ||
116 | /* Set TO_RA addresses. */ | ||
117 | raw3270_buffer_address(cp->view.dev, cp->status->string + 1, | ||
118 | cp->view.cols * (cp->view.rows - 1)); | ||
119 | raw3270_buffer_address(cp->view.dev, cp->status->string + 21, | ||
120 | cp->view.cols * cp->view.rows - 8); | ||
121 | /* Convert strings to ebcdic. */ | ||
122 | codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); | ||
123 | codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Set output offsets to 3270 datastream fragment of a console string. | ||
128 | */ | ||
129 | static void | ||
130 | con3270_update_string(struct con3270 *cp, struct string *s, int nr) | ||
131 | { | ||
132 | if (s->len >= cp->view.cols - 5) | ||
133 | return; | ||
134 | raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, | ||
135 | cp->view.cols * (nr + 1)); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Rebuild update list to print all lines. | ||
140 | */ | ||
141 | static void | ||
142 | con3270_rebuild_update(struct con3270 *cp) | ||
143 | { | ||
144 | struct string *s, *n; | ||
145 | int nr; | ||
146 | |||
147 | /* | ||
148 | * Throw away update list and create a new one, | ||
149 | * containing all lines that will fit on the screen. | ||
150 | */ | ||
151 | list_for_each_entry_safe(s, n, &cp->update, update) | ||
152 | list_del_init(&s->update); | ||
153 | nr = cp->view.rows - 2 + cp->nr_up; | ||
154 | list_for_each_entry_reverse(s, &cp->lines, list) { | ||
155 | if (nr < cp->view.rows - 1) | ||
156 | list_add(&s->update, &cp->update); | ||
157 | if (--nr < 0) | ||
158 | break; | ||
159 | } | ||
160 | cp->line_nr = 0; | ||
161 | cp->update_flags |= CON_UPDATE_LIST; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Alloc string for size bytes. Free strings from history if necessary. | ||
166 | */ | ||
167 | static struct string * | ||
168 | con3270_alloc_string(struct con3270 *cp, size_t size) | ||
169 | { | ||
170 | struct string *s, *n; | ||
171 | |||
172 | s = alloc_string(&cp->freemem, size); | ||
173 | if (s) | ||
174 | return s; | ||
175 | list_for_each_entry_safe(s, n, &cp->lines, list) { | ||
176 | list_del(&s->list); | ||
177 | if (!list_empty(&s->update)) | ||
178 | list_del(&s->update); | ||
179 | cp->nr_lines--; | ||
180 | if (free_string(&cp->freemem, s) >= size) | ||
181 | break; | ||
182 | } | ||
183 | s = alloc_string(&cp->freemem, size); | ||
184 | BUG_ON(!s); | ||
185 | if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { | ||
186 | cp->nr_up = cp->nr_lines - cp->view.rows + 1; | ||
187 | con3270_rebuild_update(cp); | ||
188 | con3270_update_status(cp); | ||
189 | } | ||
190 | return s; | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * Write completion callback. | ||
195 | */ | ||
196 | static void | ||
197 | con3270_write_callback(struct raw3270_request *rq, void *data) | ||
198 | { | ||
199 | raw3270_request_reset(rq); | ||
200 | xchg(&((struct con3270 *) rq->view)->write, rq); | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * Update console display. | ||
205 | */ | ||
206 | static void | ||
207 | con3270_update(struct con3270 *cp) | ||
208 | { | ||
209 | struct raw3270_request *wrq; | ||
210 | char wcc, prolog[6]; | ||
211 | unsigned long flags; | ||
212 | unsigned long updated; | ||
213 | struct string *s, *n; | ||
214 | int rc; | ||
215 | |||
216 | wrq = xchg(&cp->write, 0); | ||
217 | if (!wrq) { | ||
218 | con3270_set_timer(cp, 1); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | spin_lock_irqsave(&cp->view.lock, flags); | ||
223 | updated = 0; | ||
224 | if (cp->update_flags & CON_UPDATE_ERASE) { | ||
225 | /* Use erase write alternate to initialize display. */ | ||
226 | raw3270_request_set_cmd(wrq, TC_EWRITEA); | ||
227 | updated |= CON_UPDATE_ERASE; | ||
228 | } else | ||
229 | raw3270_request_set_cmd(wrq, TC_WRITE); | ||
230 | |||
231 | wcc = TW_NONE; | ||
232 | raw3270_request_add_data(wrq, &wcc, 1); | ||
233 | |||
234 | /* | ||
235 | * Update status line. | ||
236 | */ | ||
237 | if (cp->update_flags & CON_UPDATE_STATUS) | ||
238 | if (raw3270_request_add_data(wrq, cp->status->string, | ||
239 | cp->status->len) == 0) | ||
240 | updated |= CON_UPDATE_STATUS; | ||
241 | |||
242 | if (cp->update_flags & CON_UPDATE_LIST) { | ||
243 | prolog[0] = TO_SBA; | ||
244 | prolog[3] = TO_SA; | ||
245 | prolog[4] = TAT_COLOR; | ||
246 | prolog[5] = TAC_TURQ; | ||
247 | raw3270_buffer_address(cp->view.dev, prolog + 1, | ||
248 | cp->view.cols * cp->line_nr); | ||
249 | raw3270_request_add_data(wrq, prolog, 6); | ||
250 | /* Write strings in the update list to the screen. */ | ||
251 | list_for_each_entry_safe(s, n, &cp->update, update) { | ||
252 | if (s != cp->cline) | ||
253 | con3270_update_string(cp, s, cp->line_nr); | ||
254 | if (raw3270_request_add_data(wrq, s->string, | ||
255 | s->len) != 0) | ||
256 | break; | ||
257 | list_del_init(&s->update); | ||
258 | if (s != cp->cline) | ||
259 | cp->line_nr++; | ||
260 | } | ||
261 | if (list_empty(&cp->update)) | ||
262 | updated |= CON_UPDATE_LIST; | ||
263 | } | ||
264 | wrq->callback = con3270_write_callback; | ||
265 | rc = raw3270_start(&cp->view, wrq); | ||
266 | if (rc == 0) { | ||
267 | cp->update_flags &= ~updated; | ||
268 | if (cp->update_flags) | ||
269 | con3270_set_timer(cp, 1); | ||
270 | } else { | ||
271 | raw3270_request_reset(wrq); | ||
272 | xchg(&cp->write, wrq); | ||
273 | } | ||
274 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
275 | } | ||
276 | |||
277 | /* | ||
278 | * Read tasklet. | ||
279 | */ | ||
280 | static void | ||
281 | con3270_read_tasklet(struct raw3270_request *rrq) | ||
282 | { | ||
283 | static char kreset_data = TW_KR; | ||
284 | struct con3270 *cp; | ||
285 | unsigned long flags; | ||
286 | int nr_up, deactivate; | ||
287 | |||
288 | cp = (struct con3270 *) rrq->view; | ||
289 | spin_lock_irqsave(&cp->view.lock, flags); | ||
290 | nr_up = cp->nr_up; | ||
291 | deactivate = 0; | ||
292 | /* Check aid byte. */ | ||
293 | switch (cp->input->string[0]) { | ||
294 | case 0x7d: /* enter: jump to bottom. */ | ||
295 | nr_up = 0; | ||
296 | break; | ||
297 | case 0xf3: /* PF3: deactivate the console view. */ | ||
298 | deactivate = 1; | ||
299 | break; | ||
300 | case 0x6d: /* clear: start from scratch. */ | ||
301 | con3270_rebuild_update(cp); | ||
302 | cp->update_flags = CON_UPDATE_ALL; | ||
303 | con3270_set_timer(cp, 1); | ||
304 | break; | ||
305 | case 0xf7: /* PF7: do a page up in the console log. */ | ||
306 | nr_up += cp->view.rows - 2; | ||
307 | if (nr_up + cp->view.rows - 1 > cp->nr_lines) { | ||
308 | nr_up = cp->nr_lines - cp->view.rows + 1; | ||
309 | if (nr_up < 0) | ||
310 | nr_up = 0; | ||
311 | } | ||
312 | break; | ||
313 | case 0xf8: /* PF8: do a page down in the console log. */ | ||
314 | nr_up -= cp->view.rows - 2; | ||
315 | if (nr_up < 0) | ||
316 | nr_up = 0; | ||
317 | break; | ||
318 | } | ||
319 | if (nr_up != cp->nr_up) { | ||
320 | cp->nr_up = nr_up; | ||
321 | con3270_rebuild_update(cp); | ||
322 | con3270_update_status(cp); | ||
323 | con3270_set_timer(cp, 1); | ||
324 | } | ||
325 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
326 | |||
327 | /* Start keyboard reset command. */ | ||
328 | raw3270_request_reset(cp->kreset); | ||
329 | raw3270_request_set_cmd(cp->kreset, TC_WRITE); | ||
330 | raw3270_request_add_data(cp->kreset, &kreset_data, 1); | ||
331 | raw3270_start(&cp->view, cp->kreset); | ||
332 | |||
333 | if (deactivate) | ||
334 | raw3270_deactivate_view(&cp->view); | ||
335 | |||
336 | raw3270_request_reset(rrq); | ||
337 | xchg(&cp->read, rrq); | ||
338 | raw3270_put_view(&cp->view); | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * Read request completion callback. | ||
343 | */ | ||
344 | static void | ||
345 | con3270_read_callback(struct raw3270_request *rq, void *data) | ||
346 | { | ||
347 | raw3270_get_view(rq->view); | ||
348 | /* Schedule tasklet to pass input to tty. */ | ||
349 | tasklet_schedule(&((struct con3270 *) rq->view)->readlet); | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Issue a read request. Called only from interrupt function. | ||
354 | */ | ||
355 | static void | ||
356 | con3270_issue_read(struct con3270 *cp) | ||
357 | { | ||
358 | struct raw3270_request *rrq; | ||
359 | int rc; | ||
360 | |||
361 | rrq = xchg(&cp->read, 0); | ||
362 | if (!rrq) | ||
363 | /* Read already scheduled. */ | ||
364 | return; | ||
365 | rrq->callback = con3270_read_callback; | ||
366 | rrq->callback_data = cp; | ||
367 | raw3270_request_set_cmd(rrq, TC_READMOD); | ||
368 | raw3270_request_set_data(rrq, cp->input->string, cp->input->len); | ||
369 | /* Issue the read modified request. */ | ||
370 | rc = raw3270_start_irq(&cp->view, rrq); | ||
371 | if (rc) | ||
372 | raw3270_request_reset(rrq); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * Switch to the console view. | ||
377 | */ | ||
378 | static int | ||
379 | con3270_activate(struct raw3270_view *view) | ||
380 | { | ||
381 | unsigned long flags; | ||
382 | struct con3270 *cp; | ||
383 | |||
384 | cp = (struct con3270 *) view; | ||
385 | spin_lock_irqsave(&cp->view.lock, flags); | ||
386 | cp->nr_up = 0; | ||
387 | con3270_rebuild_update(cp); | ||
388 | con3270_update_status(cp); | ||
389 | cp->update_flags = CON_UPDATE_ALL; | ||
390 | con3270_set_timer(cp, 1); | ||
391 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static void | ||
396 | con3270_deactivate(struct raw3270_view *view) | ||
397 | { | ||
398 | unsigned long flags; | ||
399 | struct con3270 *cp; | ||
400 | |||
401 | cp = (struct con3270 *) view; | ||
402 | spin_lock_irqsave(&cp->view.lock, flags); | ||
403 | del_timer(&cp->timer); | ||
404 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
405 | } | ||
406 | |||
407 | static int | ||
408 | con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) | ||
409 | { | ||
410 | /* Handle ATTN. Schedule tasklet to read aid. */ | ||
411 | if (irb->scsw.dstat & DEV_STAT_ATTENTION) | ||
412 | con3270_issue_read(cp); | ||
413 | |||
414 | if (rq) { | ||
415 | if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) | ||
416 | rq->rc = -EIO; | ||
417 | else | ||
418 | /* Normal end. Copy residual count. */ | ||
419 | rq->rescnt = irb->scsw.count; | ||
420 | } | ||
421 | return RAW3270_IO_DONE; | ||
422 | } | ||
423 | |||
424 | /* Console view to a 3270 device. */ | ||
425 | static struct raw3270_fn con3270_fn = { | ||
426 | .activate = con3270_activate, | ||
427 | .deactivate = con3270_deactivate, | ||
428 | .intv = (void *) con3270_irq | ||
429 | }; | ||
430 | |||
431 | static inline void | ||
432 | con3270_cline_add(struct con3270 *cp) | ||
433 | { | ||
434 | if (!list_empty(&cp->cline->list)) | ||
435 | /* Already added. */ | ||
436 | return; | ||
437 | list_add_tail(&cp->cline->list, &cp->lines); | ||
438 | cp->nr_lines++; | ||
439 | con3270_rebuild_update(cp); | ||
440 | } | ||
441 | |||
442 | static inline void | ||
443 | con3270_cline_insert(struct con3270 *cp, unsigned char c) | ||
444 | { | ||
445 | cp->cline->string[cp->cline->len++] = | ||
446 | cp->view.ascebc[(c < ' ') ? ' ' : c]; | ||
447 | if (list_empty(&cp->cline->update)) { | ||
448 | list_add_tail(&cp->cline->update, &cp->update); | ||
449 | cp->update_flags |= CON_UPDATE_LIST; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | static inline void | ||
454 | con3270_cline_end(struct con3270 *cp) | ||
455 | { | ||
456 | struct string *s; | ||
457 | unsigned int size; | ||
458 | |||
459 | /* Copy cline. */ | ||
460 | size = (cp->cline->len < cp->view.cols - 5) ? | ||
461 | cp->cline->len + 4 : cp->view.cols; | ||
462 | s = con3270_alloc_string(cp, size); | ||
463 | memcpy(s->string, cp->cline->string, cp->cline->len); | ||
464 | if (s->len < cp->view.cols - 5) { | ||
465 | s->string[s->len - 4] = TO_RA; | ||
466 | s->string[s->len - 1] = 0; | ||
467 | } else { | ||
468 | while (--size > cp->cline->len) | ||
469 | s->string[size] = cp->view.ascebc[' ']; | ||
470 | } | ||
471 | /* Replace cline with allocated line s and reset cline. */ | ||
472 | list_add(&s->list, &cp->cline->list); | ||
473 | list_del_init(&cp->cline->list); | ||
474 | if (!list_empty(&cp->cline->update)) { | ||
475 | list_add(&s->update, &cp->cline->update); | ||
476 | list_del_init(&cp->cline->update); | ||
477 | } | ||
478 | cp->cline->len = 0; | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * Write a string to the 3270 console | ||
483 | */ | ||
484 | static void | ||
485 | con3270_write(struct console *co, const char *str, unsigned int count) | ||
486 | { | ||
487 | struct con3270 *cp; | ||
488 | unsigned long flags; | ||
489 | unsigned char c; | ||
490 | |||
491 | cp = condev; | ||
492 | if (cp->view.dev) | ||
493 | raw3270_activate_view(&cp->view); | ||
494 | spin_lock_irqsave(&cp->view.lock, flags); | ||
495 | while (count-- > 0) { | ||
496 | c = *str++; | ||
497 | if (cp->cline->len == 0) | ||
498 | con3270_cline_add(cp); | ||
499 | if (c != '\n') | ||
500 | con3270_cline_insert(cp, c); | ||
501 | if (c == '\n' || cp->cline->len >= cp->view.cols) | ||
502 | con3270_cline_end(cp); | ||
503 | } | ||
504 | /* Setup timer to output current console buffer after 1/10 second */ | ||
505 | if (cp->view.dev && !timer_pending(&cp->timer)) | ||
506 | con3270_set_timer(cp, HZ/10); | ||
507 | spin_unlock_irqrestore(&cp->view.lock,flags); | ||
508 | } | ||
509 | |||
510 | extern struct tty_driver *tty3270_driver; | ||
511 | |||
512 | static struct tty_driver * | ||
513 | con3270_device(struct console *c, int *index) | ||
514 | { | ||
515 | *index = c->index; | ||
516 | return tty3270_driver; | ||
517 | } | ||
518 | |||
519 | /* | ||
520 | * Wait for end of write request. | ||
521 | */ | ||
522 | static void | ||
523 | con3270_wait_write(struct con3270 *cp) | ||
524 | { | ||
525 | while (!cp->write) { | ||
526 | raw3270_wait_cons_dev(cp->view.dev); | ||
527 | barrier(); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * panic() calls console_unblank before the system enters a | ||
533 | * disabled, endless loop. | ||
534 | */ | ||
535 | static void | ||
536 | con3270_unblank(void) | ||
537 | { | ||
538 | struct con3270 *cp; | ||
539 | unsigned long flags; | ||
540 | |||
541 | cp = condev; | ||
542 | if (!cp->view.dev) | ||
543 | return; | ||
544 | spin_lock_irqsave(&cp->view.lock, flags); | ||
545 | con3270_wait_write(cp); | ||
546 | cp->nr_up = 0; | ||
547 | con3270_rebuild_update(cp); | ||
548 | con3270_update_status(cp); | ||
549 | while (cp->update_flags != 0) { | ||
550 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
551 | con3270_update(cp); | ||
552 | spin_lock_irqsave(&cp->view.lock, flags); | ||
553 | con3270_wait_write(cp); | ||
554 | } | ||
555 | spin_unlock_irqrestore(&cp->view.lock, flags); | ||
556 | } | ||
557 | |||
558 | static int __init | ||
559 | con3270_consetup(struct console *co, char *options) | ||
560 | { | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /* | ||
565 | * The console structure for the 3270 console | ||
566 | */ | ||
567 | static struct console con3270 = { | ||
568 | .name = "tty3270", | ||
569 | .write = con3270_write, | ||
570 | .device = con3270_device, | ||
571 | .unblank = con3270_unblank, | ||
572 | .setup = con3270_consetup, | ||
573 | .flags = CON_PRINTBUFFER, | ||
574 | }; | ||
575 | |||
576 | /* | ||
577 | * 3270 console initialization code called from console_init(). | ||
578 | * NOTE: This is called before kmalloc is available. | ||
579 | */ | ||
580 | static int __init | ||
581 | con3270_init(void) | ||
582 | { | ||
583 | struct ccw_device *cdev; | ||
584 | struct raw3270 *rp; | ||
585 | void *cbuf; | ||
586 | int i; | ||
587 | |||
588 | /* Check if 3270 is to be the console */ | ||
589 | if (!CONSOLE_IS_3270) | ||
590 | return -ENODEV; | ||
591 | |||
592 | /* Set the console mode for VM */ | ||
593 | if (MACHINE_IS_VM) { | ||
594 | cpcmd("TERM CONMODE 3270", 0, 0); | ||
595 | cpcmd("TERM AUTOCR OFF", 0, 0); | ||
596 | } | ||
597 | |||
598 | cdev = ccw_device_probe_console(); | ||
599 | if (!cdev) | ||
600 | return -ENODEV; | ||
601 | rp = raw3270_setup_console(cdev); | ||
602 | if (IS_ERR(rp)) | ||
603 | return PTR_ERR(rp); | ||
604 | |||
605 | condev = (struct con3270 *) alloc_bootmem_low(sizeof(struct con3270)); | ||
606 | memset(condev, 0, sizeof(struct con3270)); | ||
607 | condev->view.dev = rp; | ||
608 | |||
609 | condev->read = raw3270_request_alloc_bootmem(0); | ||
610 | condev->read->callback = con3270_read_callback; | ||
611 | condev->read->callback_data = condev; | ||
612 | condev->write = | ||
613 | raw3270_request_alloc_bootmem(CON3270_OUTPUT_BUFFER_SIZE); | ||
614 | condev->kreset = raw3270_request_alloc_bootmem(1); | ||
615 | |||
616 | INIT_LIST_HEAD(&condev->lines); | ||
617 | INIT_LIST_HEAD(&condev->update); | ||
618 | init_timer(&condev->timer); | ||
619 | tasklet_init(&condev->readlet, | ||
620 | (void (*)(unsigned long)) con3270_read_tasklet, | ||
621 | (unsigned long) condev->read); | ||
622 | |||
623 | raw3270_add_view(&condev->view, &con3270_fn, 0); | ||
624 | |||
625 | INIT_LIST_HEAD(&condev->freemem); | ||
626 | for (i = 0; i < CON3270_STRING_PAGES; i++) { | ||
627 | cbuf = (void *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
628 | add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); | ||
629 | } | ||
630 | condev->cline = alloc_string(&condev->freemem, condev->view.cols); | ||
631 | condev->cline->len = 0; | ||
632 | con3270_create_status(condev); | ||
633 | condev->input = alloc_string(&condev->freemem, 80); | ||
634 | register_console(&con3270); | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | console_initcall(con3270_init); | ||