1 /*
2  *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
3  *
4  *  tubttybld.c -- Linemode tty driver screen-building functions
5  *
6  *
7  *
8  *
9  *
10  *  Author:  Richard Hitt
11  */
12 
13 #include "tubio.h"
14 
15 extern int tty3270_io(tub_t *);
16 static void tty3270_set_status_area(tub_t *, char **);
17 static int tty3270_next_char(tub_t *);
18 static void tty3270_unnext_char(tub_t *, char);
19 static void tty3270_update_log_area(tub_t *, char **);
20 static int tty3270_update_log_area_esc(tub_t *, char **, int *);
21 static void tty3270_clear_log_area(tub_t *, char **);
22 static void tty3270_tub_bufadr(tub_t *, int, char **);
23 static void tty3270_set_bufadr(tub_t *, char **, int *);
24 
25 /*
26  * tty3270_clear_log_area(tub_t *tubp, char **cpp)
27  */
28 static void
tty3270_clear_log_area(tub_t * tubp,char ** cpp)29 tty3270_clear_log_area(tub_t *tubp, char **cpp)
30 {
31 	*(*cpp)++ = TO_SBA;
32 	TUB_BUFADR(GEOM_LOG, cpp);
33 	*(*cpp)++ = TO_SF;
34 	*(*cpp)++ = TF_LOG;
35 	*(*cpp)++ = TO_RA;
36 	TUB_BUFADR(GEOM_INPUT, cpp);
37 	*(*cpp)++ = '\0';
38 	tubp->tty_oucol = tubp->tty_nextlogx = 0;
39 	*(*cpp)++ = TO_SBA;
40 	TUB_BUFADR(tubp->tty_nextlogx, cpp);
41 }
42 
43 static void
tty3270_update_log_area(tub_t * tubp,char ** cpp)44 tty3270_update_log_area(tub_t *tubp, char **cpp)
45 {
46 	int lastx = GEOM_INPUT;
47 	int c;
48 	int next, fill, i;
49 	int sba_needed = 1;
50 	char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH];
51 
52 	/* Check for possible ESC sequence work to do */
53 	if (tubp->tty_escx != 0) {
54 		/* If compiling new escape sequence */
55 		if (tubp->tty_esca[0] == 0x1b) {
56 			if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed))
57 				return;
58 		/* If esc seq needs refreshing after a write */
59 		} else if (tubp->tty_esca[0] == TO_SA) {
60 			tty3270_set_bufadr(tubp, cpp, &sba_needed);
61 			for (i = 0; i < tubp->tty_escx; i++)
62 				*(*cpp)++ = tubp->tty_esca[i];
63 		} else {
64 			printk(KERN_WARNING "tty3270_update_log_area esca "
65 			"character surprising:  %.2x\n", tubp->tty_esca[0]);
66 		}
67 	}
68 
69 	/* Place characters */
70 	while (tubp->tty_bcb.bc_cnt != 0) {
71 		/* Check for room.  TAB could take up to 4 chars. */
72 		if (&(*cpp)[4] >= overrun)
73 			break;
74 
75 		/* Fetch a character */
76 		if ((c = tty3270_next_char(tubp)) == -1)
77 			break;
78 
79 		switch(c) {
80 		default:
81 			if (tubp->tty_nextlogx >= lastx) {
82 				if (sba_needed == 0 ||
83 				    tubp->stat == TBS_RUNNING) {
84 					tty3270_unnext_char(tubp, c);
85 					tubp->stat = TBS_MORE;
86 					tty3270_set_status_area(tubp, cpp);
87 					tty3270_scl_settimer(tubp);
88 				}
89 				goto do_return;
90 			}
91 			tty3270_set_bufadr(tubp, cpp, &sba_needed);
92 			/* Use blank if we don't know the character */
93 			*(*cpp)++ = tub_ascebc[(int)(c < ' '? ' ': c)];
94 			tubp->tty_nextlogx++;
95 			tubp->tty_oucol++;
96 			break;
97 		case 0x1b:              /* ESC */
98 			tubp->tty_escx = 0;
99 			if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed))
100 				return;
101 			break;
102 		case '\r':		/* 0x0d -- Carriage Return */
103 			tubp->tty_nextlogx -=
104 				tubp->tty_nextlogx % GEOM_COLS;
105 			sba_needed = 1;
106 			break;
107 		case '\n':		/* 0x0a -- New Line */
108 			if (tubp->tty_oucol == GEOM_COLS) {
109 				tubp->tty_oucol = 0;
110 				break;
111 			}
112 			next = (tubp->tty_nextlogx + GEOM_COLS) /
113 				GEOM_COLS * GEOM_COLS;
114 			tubp->tty_nextlogx = next;
115 			tubp->tty_oucol = 0;
116 			sba_needed = 1;
117 			break;
118 		case '\t':		/* 0x09 -- Tabulate */
119 			tty3270_set_bufadr(tubp, cpp, &sba_needed);
120 			fill = (tubp->tty_nextlogx % GEOM_COLS) % 8;
121 			for (; fill < 8; fill++) {
122 				if (tubp->tty_nextlogx >= lastx)
123 					break;
124 				*(*cpp)++ = tub_ascebc[' '];
125 				tubp->tty_nextlogx++;
126 				tubp->tty_oucol++;
127 			}
128 			break;
129 		case '\a':		/* 0x07 -- Alarm */
130 			tubp->flags |= TUB_ALARM;
131 			break;
132 		case '\f':		/* 0x0c -- Form Feed */
133 			tty3270_clear_log_area(tubp, cpp);
134 			break;
135 		case 0xf:	/* SuSE "exit alternate mode" */
136 			break;
137 		}
138 	}
139 do_return:
140 }
141 
142 #define NUMQUANT 8
143 static int
tty3270_update_log_area_esc(tub_t * tubp,char ** cpp,int * sba_needed)144 tty3270_update_log_area_esc(tub_t *tubp, char **cpp, int *sba_needed)
145 {
146 	int c;
147 	int i, j;
148 	int start, end, next;
149 	int quant[NUMQUANT];
150 	char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH];
151 	char sabuf[NUMQUANT*3], *sap = sabuf, *cp;
152 
153 	/* If starting a sequence, stuff ESC at [0] */
154 	if (tubp->tty_escx == 0)
155 		tubp->tty_esca[tubp->tty_escx++] = 0x1b;
156 
157 	/* Now that sequence is started, see if room in buffer */
158 	if (&(*cpp)[NUMQUANT * 3] >= overrun)
159 		return tubp->tty_escx;
160 
161 	/* Gather the rest of the sequence's characters */
162 	while (tubp->tty_escx < sizeof tubp->tty_esca) {
163 		if ((c = tty3270_next_char(tubp)) == -1)
164 			return tubp->tty_escx;
165 		if (tubp->tty_escx == 1) {
166 			switch(c) {
167 			case '[':
168 				tubp->tty_esca[tubp->tty_escx++] = c;
169 				continue;
170 			case '7':
171 				tubp->tty_savecursor = tubp->tty_nextlogx;
172 				goto done_return;
173 			case '8':
174 				next = tubp->tty_savecursor;
175 				goto do_setcur;
176 			default:
177 				goto error_return;
178 			}
179 		}
180 		tubp->tty_esca[tubp->tty_escx++] = c;
181 		if (c != ';' && (c < '0' || c > '9'))
182 			break;
183 	}
184 
185 	/* Check for overrun */
186 	if (tubp->tty_escx == sizeof tubp->tty_esca)
187 		goto error_return;
188 
189 	/* Parse potentially empty string "nn;nn;nn..." */
190 	i = -1;
191 	j = 2;		/* skip ESC, [ */
192 	c = ';';
193 	do {
194 		if (c == ';') {
195 			if (++i == NUMQUANT)
196 				goto error_return;
197 			quant[i] = 0;
198 		} else if (c < '0' || c > '9') {
199 			break;
200 		} else {
201 			quant[i] = quant[i] * 10 + c - '0';
202 		}
203 		c = tubp->tty_esca[j];
204 	} while (j++ < tubp->tty_escx);
205 
206 	/* Add 3270 data stream output to execute the sequence */
207 	switch(c) {
208 	case 'm':		/* Set Attribute */
209 		for (next = 0; next <= i; next++) {
210 			int type = -1, value = 0;
211 
212 			switch(quant[next]) {
213 			case 0:		/* Reset */
214 				next = tubp->tty_nextlogx;
215 				tty3270_set_bufadr(tubp, cpp, sba_needed);
216 				*(*cpp)++ = TO_SA;
217 				*(*cpp)++ = TAT_EXTHI;
218 				*(*cpp)++ = TAX_RESET;
219 				*(*cpp)++ = TO_SA;
220 				*(*cpp)++ = TAT_COLOR;
221 				*(*cpp)++ = TAC_RESET;
222 				tubp->tty_nextlogx = next;
223 				*sba_needed = 1;
224 				sap = sabuf;
225 				break;
226 			case 1:		/* Bright */
227 				break;
228 			case 2:		/* Dim */
229 				break;
230 			case 4:		/* Underscore */
231 				type = TAT_EXTHI; value = TAX_UNDER;
232 				break;
233 			case 5:		/* Blink */
234 				type = TAT_EXTHI; value = TAX_BLINK;
235 				break;
236 			case 7:		/* Reverse */
237 				type = TAT_EXTHI; value = TAX_REVER;
238 				break;
239 			case 8:		/* Hidden */
240 				break;		/* For now ... */
241 			/* Foreground Colors */
242 			case 30:	/* Black */
243 				type = TAT_COLOR; value = TAC_DEFAULT;
244 				break;
245 			case 31:	/* Red */
246 				type = TAT_COLOR; value = TAC_RED;
247 				break;
248 			case 32:	/* Green */
249 				type = TAT_COLOR; value = TAC_GREEN;
250 				break;
251 			case 33:	/* Yellow */
252 				type = TAT_COLOR; value = TAC_YELLOW;
253 				break;
254 			case 34:	/* Blue */
255 				type = TAT_COLOR; value = TAC_BLUE;
256 				break;
257 			case 35:	/* Magenta */
258 				type = TAT_COLOR; value = TAC_PINK;
259 				break;
260 			case 36:	/* Cyan */
261 				type = TAT_COLOR; value = TAC_TURQ;
262 				break;
263 			case 37:	/* White */
264 				type = TAT_COLOR; value = TAC_WHITE;
265 				break;
266 			case 39:	/* Black */
267 				type = TAT_COLOR; value = TAC_DEFAULT;
268 				break;
269 			/* Background Colors */
270 			case 40:	/* Black */
271 			case 41:	/* Red */
272 			case 42:	/* Green */
273 			case 43:	/* Yellow */
274 			case 44:	/* Blue */
275 			case 45:	/* Magenta */
276 			case 46:	/* Cyan */
277 			case 47:	/* White */
278 				break;		/* For now ... */
279 			/* Oops */
280 			default:
281 				break;
282 			}
283 			if (type != -1) {
284 				tty3270_set_bufadr(tubp, cpp, sba_needed);
285 				*(*cpp)++ = TO_SA;
286 				*(*cpp)++ = type;
287 				*(*cpp)++ = value;
288 				*sap++ = TO_SA;
289 				*sap++ = type;
290 				*sap++ = value;
291 			}
292 		}
293 		break;
294 
295 	case 'H':		/* Cursor Home */
296 	case 'f':		/* Force Cursor Position */
297 		if (quant[0]) quant[0]--;
298 		if (quant[1]) quant[1]--;
299 		next = quant[0] * GEOM_COLS + quant[1];
300 		goto do_setcur;
301 	case 'A':		/* Cursor Up */
302 		if (quant[i] == 0) quant[i] = 1;
303 		next = tubp->tty_nextlogx - GEOM_COLS * quant[i];
304 		goto do_setcur;
305 	case 'B':		/* Cursor Down */
306 		if (quant[i] == 0) quant[i] = 1;
307 		next = tubp->tty_nextlogx + GEOM_COLS * quant[i];
308 		goto do_setcur;
309 	case 'C':		/* Cursor Forward */
310 		if (quant[i] == 0) quant[i] = 1;
311 		next = tubp->tty_nextlogx % GEOM_COLS;
312 		start = tubp->tty_nextlogx - next;
313 		next = start + MIN(next + quant[i], GEOM_COLS - 1);
314 		goto do_setcur;
315 	case 'D':		/* Cursor Backward */
316 		if (quant[i] == 0) quant[i] = 1;
317 		next = MIN(quant[i], tubp->tty_nextlogx % GEOM_COLS);
318 		next = tubp->tty_nextlogx - next;
319 		goto do_setcur;
320 	case 'G':
321 		if (quant[0]) quant[0]--;
322 		next = tubp->tty_nextlogx / GEOM_COLS * GEOM_COLS + quant[0];
323 do_setcur:
324 		if (next < 0)
325 			break;
326 		tubp->tty_nextlogx = next;
327 		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
328 		*sba_needed = 1;
329 		break;
330 
331 	case 'r':		/* Define scroll area */
332 		start = quant[0];
333 		if (start <= 0) start = 1;
334 		if (start > GEOM_ROWS - 2) start = GEOM_ROWS - 2;
335 		tubp->tty_nextlogx = (start - 1) * GEOM_COLS;
336 		tubp->tty_oucol = 0;
337 		*sba_needed = 1;
338 		break;
339 
340 	case 'X':		/* Erase for n chars from cursor */
341 		start = tubp->tty_nextlogx;
342 		end = start + (quant[0]?: 1);
343 		goto do_fill;
344 	case 'J':		/* Erase to screen end from cursor */
345 		*(*cpp)++ = TO_SBA;
346 		TUB_BUFADR(tubp->tty_nextlogx, cpp);
347 		*(*cpp)++ = TO_RA;
348 		TUB_BUFADR(GEOM_INPUT, cpp);
349 		*(*cpp)++ = tub_ascebc[' '];
350 		*(*cpp)++ = TO_SBA;
351 		TUB_BUFADR(tubp->tty_nextlogx, cpp);
352 		break;
353 	case 'K':
354 		start = tubp->tty_nextlogx;
355 		end = (start + GEOM_COLS) / GEOM_COLS * GEOM_COLS;
356 do_fill:
357 		if (start >= GEOM_INPUT)
358 			break;
359 		if (end > GEOM_INPUT)
360 			end = GEOM_INPUT;
361 		if (end <= start)
362 			break;
363 		*(*cpp)++ = TO_SBA;
364 		TUB_BUFADR(start, cpp);
365 		if (end - start > 4) {
366 			*(*cpp)++ = TO_RA;
367 			TUB_BUFADR(end, cpp);
368 			*(*cpp)++ = tub_ascebc[' '];
369 		} else while (start++ < end) {
370 			*(*cpp)++ = tub_ascebc[' '];
371 		}
372 		tubp->tty_nextlogx = end;
373 		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
374 		*sba_needed = 1;
375 		break;
376 	}
377 done_return:
378 	tubp->tty_escx = 0;
379 	cp = sabuf;
380 	while (cp != sap)
381 		tubp->tty_esca[tubp->tty_escx++] = *cp++;
382 	return 0;
383 error_return:
384 	tubp->tty_escx = 0;
385 	return 0;
386 }
387 
388 static int
tty3270_next_char(tub_t * tubp)389 tty3270_next_char(tub_t *tubp)
390 {
391 	int c;
392 	bcb_t *ib;
393 
394 	ib = &tubp->tty_bcb;
395 	if (ib->bc_cnt == 0)
396 		return -1;
397 	c = ib->bc_buf[ib->bc_rd++];
398 	if (ib->bc_rd == ib->bc_len)
399 		ib->bc_rd = 0;
400 	ib->bc_cnt--;
401 	return c;
402 }
403 
404 static void
tty3270_unnext_char(tub_t * tubp,char c)405 tty3270_unnext_char(tub_t *tubp, char c)
406 {
407 	bcb_t *ib;
408 
409 	ib = &tubp->tty_bcb;
410 	if (ib->bc_rd == 0)
411 		ib->bc_rd = ib->bc_len;
412 	ib->bc_buf[--ib->bc_rd] = c;
413 	ib->bc_cnt++;
414 }
415 
416 
417 static void
tty3270_clear_input_area(tub_t * tubp,char ** cpp)418 tty3270_clear_input_area(tub_t *tubp, char **cpp)
419 {
420 	*(*cpp)++ = TO_SBA;
421 	TUB_BUFADR(GEOM_INPUT, cpp);
422 	*(*cpp)++ = TO_SF;
423 	*(*cpp)++ = tubp->tty_inattr;
424 	*(*cpp)++ = TO_IC;
425 	*(*cpp)++ = TO_RA;
426 	TUB_BUFADR(GEOM_STAT, cpp);
427 	*(*cpp)++ = '\0';
428 }
429 
430 static void
tty3270_update_input_area(tub_t * tubp,char ** cpp)431 tty3270_update_input_area(tub_t *tubp, char **cpp)
432 {
433 	int len;
434 
435 	*(*cpp)++ = TO_SBA;
436 	TUB_BUFADR(GEOM_INPUT, cpp);
437 	*(*cpp)++ = TO_SF;
438 	*(*cpp)++ = TF_INMDT;
439 	len = strlen(tubp->tty_input);
440 	memcpy(*cpp, tubp->tty_input, len);
441 	*cpp += len;
442 	*(*cpp)++ = TO_IC;
443 	len = GEOM_INPLEN - len;
444 	if (len > 4) {
445 		*(*cpp)++ = TO_RA;
446 		TUB_BUFADR(GEOM_STAT, cpp);
447 		*(*cpp)++ = '\0';
448 	} else {
449 		for (; len > 0; len--)
450 			*(*cpp)++ = '\0';
451 	}
452 }
453 
454 /*
455  * tty3270_set_status_area(tub_t *tubp, char **cpp)
456  */
457 static void
tty3270_set_status_area(tub_t * tubp,char ** cpp)458 tty3270_set_status_area(tub_t *tubp, char **cpp)
459 {
460 	char *sp;
461 
462 	if (tubp->stat == TBS_RUNNING)
463 		sp = TS_RUNNING;
464 	else if (tubp->stat == TBS_MORE)
465 		sp = TS_MORE;
466 	else if (tubp->stat == TBS_HOLD)
467 		sp = TS_HOLD;
468 	else
469 		sp = "Linux Whatstat";
470 
471 	*(*cpp)++ = TO_SBA;
472 	TUB_BUFADR(GEOM_STAT, cpp);
473 	*(*cpp)++ = TO_SF;
474 	*(*cpp)++ = TF_STAT;
475 	memcpy(*cpp, sp, sizeof TS_RUNNING);
476 	TUB_ASCEBC(*cpp, sizeof TS_RUNNING);
477 	*cpp += sizeof TS_RUNNING;
478 }
479 
480 /*
481  * tty3270_build() -- build an output stream
482  */
483 int
tty3270_build(tub_t * tubp)484 tty3270_build(tub_t *tubp)
485 {
486 	char *cp, *startcp;
487 	int chancmd;
488 	int writecc = TW_KR;
489 	int force = 0;
490 
491 	if (tubp->mode == TBM_FS)
492 		return 0;
493 
494 	cp = startcp = *tubp->ttyscreen + 1;
495 
496 	switch(tubp->cmd) {
497 	default:
498 		printk(KERN_WARNING "tty3270_build unknown command %d\n", tubp->cmd);
499 		return 0;
500 	case TBC_OPEN:
501 tbc_open:
502 		tubp->flags &= ~TUB_INPUT_HACK;
503 		chancmd = TC_EWRITEA;
504 		tty3270_clear_input_area(tubp, &cp);
505 		tty3270_set_status_area(tubp, &cp);
506 		tty3270_clear_log_area(tubp, &cp);
507 		break;
508 	case TBC_UPDLOG:
509 		if (tubp->flags & TUB_INPUT_HACK)
510 			goto tbc_open;
511 		chancmd = TC_WRITE;
512 		writecc = TW_NONE;
513 		tty3270_update_log_area(tubp, &cp);
514 		break;
515 	case TBC_KRUPDLOG:
516 		chancmd = TC_WRITE;
517 		force = 1;
518 		tty3270_update_log_area(tubp, &cp);
519 		break;
520 	case TBC_CLRUPDLOG:
521 		chancmd = TC_WRITE;
522 		tty3270_set_status_area(tubp, &cp);
523 		tty3270_clear_log_area(tubp, &cp);
524 		tty3270_update_log_area(tubp, &cp);
525 		break;
526 	case TBC_UPDATE:
527 		chancmd = TC_EWRITEA;
528 		tubp->tty_oucol = tubp->tty_nextlogx = 0;
529 		tty3270_clear_input_area(tubp, &cp);
530 		tty3270_set_status_area(tubp, &cp);
531 		tty3270_update_log_area(tubp, &cp);
532 		break;
533 	case TBC_UPDSTAT:
534 		chancmd = TC_WRITE;
535 		tty3270_set_status_area(tubp, &cp);
536 		break;
537 	case TBC_CLRINPUT:
538 		chancmd = TC_WRITE;
539 		tty3270_clear_input_area(tubp, &cp);
540 		break;
541 	case TBC_UPDINPUT:
542 		chancmd = TC_WRITE;
543 		tty3270_update_input_area(tubp, &cp);
544 		break;
545 	}
546 
547 	/* Set Write Control Character and start I/O */
548 	if (force == 0 && cp == startcp &&
549 	    (tubp->flags & TUB_ALARM) == 0)
550 		return 0;
551 	if (tubp->flags & TUB_ALARM) {
552 		tubp->flags &= ~TUB_ALARM;
553 		writecc |= TW_PLUSALARM;
554 	}
555 	**tubp->ttyscreen = writecc;
556 	tubp->ttyccw.cmd_code = chancmd;
557 	tubp->ttyccw.flags = CCW_FLAG_SLI;
558 	tubp->ttyccw.cda = virt_to_phys(*tubp->ttyscreen);
559 	tubp->ttyccw.count = cp - *tubp->ttyscreen;
560 	tty3270_io(tubp);
561 	return 1;
562 }
563 
564 static void
tty3270_tub_bufadr(tub_t * tubp,int adr,char ** cpp)565 tty3270_tub_bufadr(tub_t *tubp, int adr, char **cpp)
566 {
567 	if (tubp->tty_14bitadr) {
568 		*(*cpp)++ = (adr >> 8) & 0x3f;
569 		*(*cpp)++ = adr & 0xff;
570 	} else {
571 		*(*cpp)++ = tub_ebcgraf[(adr >> 6) & 0x3f];
572 		*(*cpp)++ = tub_ebcgraf[adr & 0x3f];
573 	}
574 }
575 
576 static void
tty3270_set_bufadr(tub_t * tubp,char ** cpp,int * sba_needed)577 tty3270_set_bufadr(tub_t *tubp, char **cpp, int *sba_needed)
578 {
579 	if (!*sba_needed)
580 		return;
581 	if (tubp->tty_nextlogx >= GEOM_INPUT) {
582 		tubp->tty_nextlogx = GEOM_INPUT - 1;
583 		tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS;
584 	}
585 	*(*cpp)++ = TO_SBA;
586 	TUB_BUFADR(tubp->tty_nextlogx, cpp);
587 	*sba_needed = 0;
588 }
589