/* * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC * * tubttysiz.c -- Linemode screen-size determiner * * * * * * Author: Richard Hitt */ #include "tubio.h" static int tty3270_size_io(tub_t *tubp); static void tty3270_size_int(tub_t *tubp, devstat_t *dsp); static int tty3270_size_wait(tub_t *tubp, long *flags, int stat); /* * Structure representing Usable Area Query Reply Base */ typedef struct { short l; /* Length of this structured field */ char sfid; /* 0x81 if Query Reply */ char qcode; /* 0x81 if Usable Area */ #define QCODE_UA 0x81 char flags0; #define FLAGS0_ADDR 0x0f #define FLAGS0_ADDR_12_14 1 /* 12/14-bit adrs ok */ #define FLAGS0_ADDR_12_14_16 3 /* 12/14/16-bit adrs ok */ char flags1; short w; /* Width of usable area */ short h; /* Heigth of usavle area */ char units; /* 0x00:in; 0x01:mm */ int xr; int yr; char aw; char ah; short buffsz; /* Character buffer size, bytes */ char xmin; char ymin; char xmax; char ymax; } __attribute__ ((packed)) uab_t; /* * Structure representing Alternate Usable Area Self-Defining Parameter */ typedef struct { char l; /* Length of this Self-Defining Parm */ char sdpid; /* 0x02 if Alternate Usable Area */ #define SDPID_AUA 0x02 char res; char auaid; /* 0x01 is Id for the A U A */ short wauai; /* Width of AUAi */ short hauai; /* Height of AUAi */ char auaunits; /* 0x00:in, 0x01:mm */ int auaxr; int auayr; char awauai; char ahauai; } __attribute__ ((packed)) aua_t; /* * Structure representing one followed by the other */ typedef struct { uab_t uab; aua_t aua; } __attribute__ ((packed)) ua_t; /* * Try to determine screen size using Read Partition (Query) */ int tty3270_size(tub_t *tubp, long *flags) { char wbuf[7] = { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; int rc = 0; int count; unsigned char *cp; ua_t *uap; char miniscreen[256]; char (*screen)[]; int screenl; int geom_rows, geom_cols, fourteenbitadr; void (*oldint)(struct tub_s *, devstat_t *); if (tubp->flags & TUB_SIZED) return 0; fourteenbitadr = 0; geom_rows = tubp->geom_rows; geom_cols = tubp->geom_cols; oldint = tubp->intv; tubp->intv = tty3270_size_int; if (tubp->cmd == TBC_CONOPEN) { tubp->ttyccw.cmd_code = TC_EWRITEA; cp = miniscreen; *cp++ = TW_KR; /* more? */ tubp->ttyccw.flags = CCW_FLAG_SLI; tubp->ttyccw.cda = virt_to_phys(miniscreen); tubp->ttyccw.count = (char *)cp - miniscreen; rc = tty3270_size_io(tubp); rc = tty3270_size_wait(tubp, flags, 0); } tubp->ttyccw.cmd_code = TC_WRITESF; tubp->ttyccw.flags = CCW_FLAG_SLI; tubp->ttyccw.cda = virt_to_phys(wbuf); tubp->ttyccw.count = sizeof wbuf; try_again: rc = tty3270_size_io(tubp); if (rc) printk("tty3270_size_io returned %d\n", rc); rc = tty3270_size_wait(tubp, flags, 0); if (rc != 0) { goto do_return; } /* * Unit-Check Processing: * Expect Command Reject or Intervention Required. * For Command Reject assume old hdwe/software and * set a default size of 80x24. * For Intervention Required, wait for signal pending * or Unsolicited Device End; if the latter, retry. */ if (tubp->dstat & DEV_STAT_UNIT_CHECK) { if (tubp->sense.data[0] & SNS0_CMD_REJECT) { goto use_diag210; /* perhaps it's tn3270 */ } else if (tubp->sense.data[0] & SNS0_INTERVENTION_REQ) { if ((rc = tty3270_size_wait(tubp, flags, DEV_STAT_DEV_END))) goto do_return; goto try_again; } else { printk("tty3270_size(): unkn sense %.2x\n", tubp->sense.data[0]); goto do_return; } } if ((rc = tty3270_size_wait(tubp, flags, DEV_STAT_ATTENTION))) goto do_return; /* Set up a read ccw and issue it */ tubp->ttyccw.cmd_code = TC_READMOD; tubp->ttyccw.flags = CCW_FLAG_SLI; tubp->ttyccw.cda = virt_to_phys(miniscreen); tubp->ttyccw.count = sizeof miniscreen; tty3270_size_io(tubp); rc = tty3270_size_wait(tubp, flags, 0); if (rc != 0) goto do_return; count = sizeof miniscreen - tubp->cswl; cp = miniscreen; if (*cp++ != 0x88) goto do_return; uap = (void *)cp; if (uap->uab.qcode != QCODE_UA) goto do_return; geom_rows = uap->uab.h; geom_cols = uap->uab.w; if ((uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14 || (uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14_16) fourteenbitadr = 1; if (uap->uab.l <= sizeof uap->uab) goto do_return; if (uap->aua.sdpid != SDPID_AUA) { printk("AUA sdpid was 0x%.2x, expecting 0x%.2x\n", uap->aua.sdpid, SDPID_AUA); goto do_return; } geom_rows = uap->aua.hauai; geom_cols = uap->aua.wauai; goto do_return; use_diag210: if (MACHINE_IS_VM) { diag210_t d210; d210.vrdcdvno = tubp->devno; d210.vrdclen = sizeof d210; rc = diag210(&d210); if (rc) { printk("tty3270_size: diag210 for 0x%.4x " "returned %d\n", tubp->devno, rc); goto do_return; } switch(d210.vrdccrmd) { case 2: geom_rows = 24; geom_cols = 80; goto do_return; case 3: geom_rows = 32; geom_cols = 80; goto do_return; case 4: geom_rows = 43; geom_cols = 80; goto do_return; case 5: geom_rows = 27; geom_cols = 132; goto do_return; default: printk("vrdccrmd is 0x%.8x\n", d210.vrdccrmd); } } do_return: if (geom_rows == 0) { geom_rows = _GEOM_ROWS; geom_cols = _GEOM_COLS; } tubp->tubiocb.pf_cnt = 24; tubp->tubiocb.re_cnt = 20; tubp->tubiocb.map = 0; screenl = geom_rows * geom_cols + 100; screen = (char (*)[])kmalloc(screenl, GFP_KERNEL); if (screen == NULL) { printk("ttyscreen size %d unavailable\n", screenl); } else { if (tubp->ttyscreen) kfree(tubp->ttyscreen); tubp->tubiocb.line_cnt = tubp->geom_rows = geom_rows; tubp->tubiocb.col_cnt = tubp->geom_cols = geom_cols; tubp->tty_14bitadr = fourteenbitadr; tubp->ttyscreen = screen; tubp->ttyscreenl = screenl; if (geom_rows == 24 && geom_cols == 80) tubp->tubiocb.model = 2; else if (geom_rows == 32 && geom_cols == 80) tubp->tubiocb.model = 3; else if (geom_rows == 43 && geom_cols == 80) tubp->tubiocb.model = 4; else if (geom_rows == 27 && geom_cols == 132) tubp->tubiocb.model = 5; else tubp->tubiocb.model = 0; tubp->flags |= TUB_SIZED; } if (rc == 0 && tubp->ttyscreen == NULL) rc = -ENOMEM; tubp->intv = oldint; return rc; } static int tty3270_size_io(tub_t *tubp) { tubp->flags |= TUB_WORKING; tubp->dstat = 0; return do_IO(tubp->irq, &tubp->ttyccw, tubp->irq, 0, 0); } static void tty3270_size_int(tub_t *tubp, devstat_t *dsp) { #define DEV_NOT_WORKING \ (DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK) tubp->dstat = dsp->dstat; if (dsp->dstat & DEV_STAT_CHN_END) tubp->cswl = dsp->rescnt; if (dsp->dstat & DEV_NOT_WORKING) tubp->flags &= ~TUB_WORKING; if (dsp->dstat & DEV_STAT_UNIT_CHECK) tubp->sense = dsp->ii.sense; wake_up_interruptible(&tubp->waitq); } /* * Wait for something. If the third arg is zero, wait until * tty3270_size_int() turns off TUB_WORKING. If the third arg * is not zero, it is a device-status bit; wait until dstat * has the bit turned on. Never wait if signal is pending. * Return 0 unless signal pending, in which case -ERESTARTSYS. */ static int tty3270_size_wait(tub_t *tubp, long *flags, int stat) { DECLARE_WAITQUEUE(wait, current); add_wait_queue(&tubp->waitq, &wait); while (!signal_pending(current) && (stat? (tubp->dstat & stat) == 0: (tubp->flags & TUB_WORKING) != 0)) { current->state = TASK_INTERRUPTIBLE; TUBUNLOCK(tubp->irq, *flags); schedule(); current->state = TASK_RUNNING; TUBLOCK(tubp->irq, *flags); } remove_wait_queue(&tubp->waitq, &wait); return signal_pending(current)? -ERESTARTSYS: 0; }