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