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