xref: /OK3568_Linux_fs/kernel/drivers/s390/char/con3270.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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