1 /*
2  *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
3  *
4  *  tubttysiz.c -- Linemode screen-size determiner
5  *
6  *
7  *
8  *
9  *
10  *  Author:  Richard Hitt
11  */
12 #include "tubio.h"
13 static int tty3270_size_io(tub_t *tubp);
14 static void tty3270_size_int(tub_t *tubp, devstat_t *dsp);
15 static int tty3270_size_wait(tub_t *tubp, long *flags, int stat);
16 
17 /*
18  * Structure representing Usable Area Query Reply Base
19  */
20 typedef struct {
21 	short l;                /* Length of this structured field */
22 	char sfid;              /* 0x81 if Query Reply */
23 	char qcode;             /* 0x81 if Usable Area */
24 #define QCODE_UA 0x81
25 	char flags0;
26 #define FLAGS0_ADDR 0x0f
27 #define FLAGS0_ADDR_12_14       1       /* 12/14-bit adrs ok */
28 #define FLAGS0_ADDR_12_14_16    3       /* 12/14/16-bit adrs ok */
29 	char flags1;
30 	short w;                /* Width of usable area */
31 	short h;                /* Heigth of usavle area */
32 	char units;             /* 0x00:in; 0x01:mm */
33 	int xr;
34 	int yr;
35 	char aw;
36 	char ah;
37 	short buffsz;           /* Character buffer size, bytes */
38 	char xmin;
39 	char ymin;
40 	char xmax;
41 	char ymax;
42 } __attribute__ ((packed)) uab_t;
43 
44 /*
45  * Structure representing Alternate Usable Area Self-Defining Parameter
46  */
47 typedef struct {
48 	char l;                 /* Length of this Self-Defining Parm */
49 	char sdpid;             /* 0x02 if Alternate Usable Area */
50 #define SDPID_AUA 0x02
51 	char res;
52 	char auaid;             /* 0x01 is Id for the A U A */
53 	short wauai;            /* Width of AUAi */
54 	short hauai;            /* Height of AUAi */
55 	char auaunits;          /* 0x00:in, 0x01:mm */
56 	int auaxr;
57 	int auayr;
58 	char awauai;
59 	char ahauai;
60 } __attribute__ ((packed)) aua_t;
61 
62 /*
63  * Structure representing one followed by the other
64  */
65 typedef struct {
66 	uab_t uab;
67 	aua_t aua;
68 } __attribute__ ((packed)) ua_t;
69 
70 /*
71  * Try to determine screen size using Read Partition (Query)
72  */
73 int
tty3270_size(tub_t * tubp,long * flags)74 tty3270_size(tub_t *tubp, long *flags)
75 {
76 	char wbuf[7] = { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
77 	int     rc = 0;
78 	int     count;
79 	unsigned char *cp;
80 	ua_t *uap;
81 	char miniscreen[256];
82 	char (*screen)[];
83 	int screenl;
84 	int geom_rows, geom_cols, fourteenbitadr;
85 	void (*oldint)(struct tub_s *, devstat_t *);
86 
87 	if (tubp->flags & TUB_SIZED)
88 		return 0;
89 	fourteenbitadr = 0;
90 	geom_rows = tubp->geom_rows;
91 	geom_cols = tubp->geom_cols;
92 
93 	oldint = tubp->intv;
94 	tubp->intv = tty3270_size_int;
95 
96 	if (tubp->cmd == TBC_CONOPEN) {
97 		tubp->ttyccw.cmd_code = TC_EWRITEA;
98 		cp = miniscreen;
99 		*cp++ = TW_KR;
100 		/* more? */
101 		tubp->ttyccw.flags = CCW_FLAG_SLI;
102 		tubp->ttyccw.cda = virt_to_phys(miniscreen);
103 		tubp->ttyccw.count = (char *)cp - miniscreen;
104 		rc = tty3270_size_io(tubp);
105 		rc = tty3270_size_wait(tubp, flags, 0);
106 	}
107 
108 	tubp->ttyccw.cmd_code = TC_WRITESF;
109 	tubp->ttyccw.flags = CCW_FLAG_SLI;
110 	tubp->ttyccw.cda = virt_to_phys(wbuf);
111 	tubp->ttyccw.count = sizeof wbuf;
112 
113 try_again:
114 	rc = tty3270_size_io(tubp);
115 	if (rc)
116 		printk("tty3270_size_io returned %d\n", rc);
117 
118 	rc = tty3270_size_wait(tubp, flags, 0);
119 	if (rc != 0) {
120 		goto do_return;
121 	}
122 
123 	/*
124 	 * Unit-Check Processing:
125 	 * Expect Command Reject or Intervention Required.
126 	 * For Command Reject assume old hdwe/software and
127 	 * set a default size of 80x24.
128 	 * For Intervention Required, wait for signal pending
129 	 * or Unsolicited Device End; if the latter, retry.
130 	 */
131 	if (tubp->dstat & DEV_STAT_UNIT_CHECK) {
132 		if (tubp->sense.data[0] & SNS0_CMD_REJECT) {
133 			goto use_diag210; /* perhaps it's tn3270 */
134 		} else if (tubp->sense.data[0] & SNS0_INTERVENTION_REQ) {
135 			if ((rc = tty3270_size_wait(tubp, flags,
136 			    DEV_STAT_DEV_END)))
137 				goto do_return;
138 			goto try_again;
139 		} else {
140 			printk("tty3270_size(): unkn sense %.2x\n",
141 				tubp->sense.data[0]);
142 			goto do_return;
143 		}
144 	}
145 	if ((rc = tty3270_size_wait(tubp, flags, DEV_STAT_ATTENTION)))
146 		goto do_return;
147 
148 	/* Set up a read ccw and issue it */
149 	tubp->ttyccw.cmd_code = TC_READMOD;
150 	tubp->ttyccw.flags = CCW_FLAG_SLI;
151 	tubp->ttyccw.cda = virt_to_phys(miniscreen);
152 	tubp->ttyccw.count = sizeof miniscreen;
153 	tty3270_size_io(tubp);
154 	rc = tty3270_size_wait(tubp, flags, 0);
155 	if (rc != 0)
156 		goto do_return;
157 
158 	count = sizeof miniscreen - tubp->cswl;
159 	cp = miniscreen;
160 	if (*cp++ != 0x88)
161 		goto do_return;
162 	uap = (void *)cp;
163 	if (uap->uab.qcode != QCODE_UA)
164 		goto do_return;
165 	geom_rows = uap->uab.h;
166 	geom_cols = uap->uab.w;
167 	if ((uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14 ||
168 	    (uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14_16)
169 		fourteenbitadr = 1;
170 	if (uap->uab.l <= sizeof uap->uab)
171 		goto do_return;
172 	if (uap->aua.sdpid != SDPID_AUA) {
173 		printk("AUA sdpid was 0x%.2x, expecting 0x%.2x\n",
174 			uap->aua.sdpid, SDPID_AUA);
175 		goto do_return;
176 	}
177 	geom_rows = uap->aua.hauai;
178 	geom_cols = uap->aua.wauai;
179 	goto do_return;
180 
181 use_diag210:
182 	if (MACHINE_IS_VM) {
183 		diag210_t d210;
184 
185 		d210.vrdcdvno = tubp->devno;
186 		d210.vrdclen = sizeof d210;
187 		rc = diag210(&d210);
188 		if (rc) {
189 			printk("tty3270_size: diag210 for 0x%.4x "
190 				"returned %d\n", tubp->devno, rc);
191 			goto do_return;
192 		}
193 		switch(d210.vrdccrmd) {
194 		case 2:
195 			geom_rows = 24;
196 			geom_cols = 80;
197 			goto do_return;
198 		case 3:
199 			geom_rows = 32;
200 			geom_cols = 80;
201 			goto do_return;
202 		case 4:
203 			geom_rows = 43;
204 			geom_cols = 80;
205 			goto do_return;
206 		case 5:
207 			geom_rows = 27;
208 			geom_cols = 132;
209 			goto do_return;
210 		default:
211 			printk("vrdccrmd is 0x%.8x\n", d210.vrdccrmd);
212 		}
213 	}
214 
215 do_return:
216 	if (geom_rows == 0) {
217 		geom_rows = _GEOM_ROWS;
218 		geom_cols = _GEOM_COLS;
219 	}
220 	tubp->tubiocb.pf_cnt = 24;
221 	tubp->tubiocb.re_cnt = 20;
222 	tubp->tubiocb.map = 0;
223 
224 	screenl = geom_rows * geom_cols + 100;
225 	screen = (char (*)[])kmalloc(screenl, GFP_KERNEL);
226 	if (screen == NULL) {
227 		printk("ttyscreen size %d unavailable\n", screenl);
228 	} else {
229 		if (tubp->ttyscreen)
230 			kfree(tubp->ttyscreen);
231 		tubp->tubiocb.line_cnt = tubp->geom_rows = geom_rows;
232 		tubp->tubiocb.col_cnt = tubp->geom_cols = geom_cols;
233 		tubp->tty_14bitadr = fourteenbitadr;
234 		tubp->ttyscreen = screen;
235 		tubp->ttyscreenl = screenl;
236 		if (geom_rows == 24 && geom_cols == 80)
237 			tubp->tubiocb.model = 2;
238 		else if (geom_rows == 32 && geom_cols == 80)
239 			tubp->tubiocb.model = 3;
240 		else if (geom_rows == 43 && geom_cols == 80)
241 			tubp->tubiocb.model = 4;
242 		else if (geom_rows == 27 && geom_cols == 132)
243 			tubp->tubiocb.model = 5;
244 		else
245 			tubp->tubiocb.model = 0;
246 		tubp->flags |= TUB_SIZED;
247 	}
248 	if (rc == 0 && tubp->ttyscreen == NULL)
249 		rc = -ENOMEM;
250 	tubp->intv = oldint;
251 	return rc;
252 }
253 
254 static int
tty3270_size_io(tub_t * tubp)255 tty3270_size_io(tub_t *tubp)
256 {
257 	tubp->flags |= TUB_WORKING;
258 	tubp->dstat = 0;
259 
260 	return do_IO(tubp->irq, &tubp->ttyccw, tubp->irq, 0, 0);
261 }
262 
263 static void
tty3270_size_int(tub_t * tubp,devstat_t * dsp)264 tty3270_size_int(tub_t *tubp, devstat_t *dsp)
265 {
266 #define DEV_NOT_WORKING \
267   (DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK)
268 
269 	tubp->dstat = dsp->dstat;
270 	if (dsp->dstat & DEV_STAT_CHN_END)
271 		tubp->cswl = dsp->rescnt;
272 	if (dsp->dstat & DEV_NOT_WORKING)
273 		tubp->flags &= ~TUB_WORKING;
274 	if (dsp->dstat & DEV_STAT_UNIT_CHECK)
275 		tubp->sense = dsp->ii.sense;
276 
277 	wake_up_interruptible(&tubp->waitq);
278 }
279 
280 /*
281  * Wait for something.  If the third arg is zero, wait until
282  * tty3270_size_int() turns off TUB_WORKING.  If the third arg
283  * is not zero, it is a device-status bit; wait until dstat
284  * has the bit turned on.  Never wait if signal is pending.
285  * Return 0 unless signal pending, in which case -ERESTARTSYS.
286  */
287 static int
tty3270_size_wait(tub_t * tubp,long * flags,int stat)288 tty3270_size_wait(tub_t *tubp, long *flags, int stat)
289 {
290 	DECLARE_WAITQUEUE(wait, current);
291 
292 	add_wait_queue(&tubp->waitq, &wait);
293 	while (!signal_pending(current) &&
294 	    (stat? (tubp->dstat & stat) == 0:
295 	     (tubp->flags & TUB_WORKING) != 0)) {
296 		current->state = TASK_INTERRUPTIBLE;
297 		TUBUNLOCK(tubp->irq, *flags);
298 		schedule();
299 		current->state = TASK_RUNNING;
300 		TUBLOCK(tubp->irq, *flags);
301 	}
302 	remove_wait_queue(&tubp->waitq, &wait);
303 	return signal_pending(current)? -ERESTARTSYS: 0;
304 }
305