brute-forced more changes from MontaVista's tree. SCSI partition table read still...
[linux-2.4.git] / drivers / s390 / char / tubttysiz.c
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
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
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
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
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 }