linux/drivers/s390/char/fs3270.c
<<
>>
Prefs
   1/*
   2 *  drivers/s390/char/fs3270.c
   3 *    IBM/3270 Driver - fullscreen driver.
   4 *
   5 *  Author(s):
   6 *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
   7 *    Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com>
   8 *      -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
   9 */
  10
  11#include <linux/bootmem.h>
  12#include <linux/console.h>
  13#include <linux/init.h>
  14#include <linux/interrupt.h>
  15#include <linux/list.h>
  16#include <linux/types.h>
  17#include <linux/smp_lock.h>
  18
  19#include <asm/ccwdev.h>
  20#include <asm/cio.h>
  21#include <asm/ebcdic.h>
  22#include <asm/idals.h>
  23
  24#include "raw3270.h"
  25#include "ctrlchar.h"
  26
  27static struct raw3270_fn fs3270_fn;
  28
  29struct fs3270 {
  30        struct raw3270_view view;
  31        struct pid *fs_pid;             /* Pid of controlling program. */
  32        int read_command;               /* ccw command to use for reads. */
  33        int write_command;              /* ccw command to use for writes. */
  34        int attention;                  /* Got attention. */
  35        int active;                     /* Fullscreen view is active. */
  36        struct raw3270_request *init;   /* single init request. */
  37        wait_queue_head_t wait;         /* Init & attention wait queue. */
  38        struct idal_buffer *rdbuf;      /* full-screen-deactivate buffer */
  39        size_t rdbuf_size;              /* size of data returned by RDBUF */
  40};
  41
  42static void
  43fs3270_wake_up(struct raw3270_request *rq, void *data)
  44{
  45        wake_up((wait_queue_head_t *) data);
  46}
  47
  48static inline int
  49fs3270_working(struct fs3270 *fp)
  50{
  51        /*
  52         * The fullscreen view is in working order if the view
  53         * has been activated AND the initial request is finished.
  54         */
  55        return fp->active && raw3270_request_final(fp->init);
  56}
  57
  58static int
  59fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq)
  60{
  61        struct fs3270 *fp;
  62        int rc;
  63
  64        fp = (struct fs3270 *) view;
  65        rq->callback = fs3270_wake_up;
  66        rq->callback_data = &fp->wait;
  67
  68        do {
  69                if (!fs3270_working(fp)) {
  70                        /* Fullscreen view isn't ready yet. */
  71                        rc = wait_event_interruptible(fp->wait,
  72                                                      fs3270_working(fp));
  73                        if (rc != 0)
  74                                break;
  75                }
  76                rc = raw3270_start(view, rq);
  77                if (rc == 0) {
  78                        /* Started sucessfully. Now wait for completion. */
  79                        wait_event(fp->wait, raw3270_request_final(rq));
  80                }
  81        } while (rc == -EACCES);
  82        return rc;
  83}
  84
  85/*
  86 * Switch to the fullscreen view.
  87 */
  88static void
  89fs3270_reset_callback(struct raw3270_request *rq, void *data)
  90{
  91        struct fs3270 *fp;
  92
  93        fp = (struct fs3270 *) rq->view;
  94        raw3270_request_reset(rq);
  95        wake_up(&fp->wait);
  96}
  97
  98static void
  99fs3270_restore_callback(struct raw3270_request *rq, void *data)
 100{
 101        struct fs3270 *fp;
 102
 103        fp = (struct fs3270 *) rq->view;
 104        if (rq->rc != 0 || rq->rescnt != 0) {
 105                if (fp->fs_pid)
 106                        kill_pid(fp->fs_pid, SIGHUP, 1);
 107        }
 108        fp->rdbuf_size = 0;
 109        raw3270_request_reset(rq);
 110        wake_up(&fp->wait);
 111}
 112
 113static int
 114fs3270_activate(struct raw3270_view *view)
 115{
 116        struct fs3270 *fp;
 117        char *cp;
 118        int rc;
 119
 120        fp = (struct fs3270 *) view;
 121
 122        /* If an old init command is still running just return. */
 123        if (!raw3270_request_final(fp->init))
 124                return 0;
 125
 126        if (fp->rdbuf_size == 0) {
 127                /* No saved buffer. Just clear the screen. */
 128                raw3270_request_set_cmd(fp->init, TC_EWRITEA);
 129                fp->init->callback = fs3270_reset_callback;
 130        } else {
 131                /* Restore fullscreen buffer saved by fs3270_deactivate. */
 132                raw3270_request_set_cmd(fp->init, TC_EWRITEA);
 133                raw3270_request_set_idal(fp->init, fp->rdbuf);
 134                fp->init->ccw.count = fp->rdbuf_size;
 135                cp = fp->rdbuf->data[0];
 136                cp[0] = TW_KR;
 137                cp[1] = TO_SBA;
 138                cp[2] = cp[6];
 139                cp[3] = cp[7];
 140                cp[4] = TO_IC;
 141                cp[5] = TO_SBA;
 142                cp[6] = 0x40;
 143                cp[7] = 0x40;
 144                fp->init->rescnt = 0;
 145                fp->init->callback = fs3270_restore_callback;
 146        }
 147        rc = fp->init->rc = raw3270_start_locked(view, fp->init);
 148        if (rc)
 149                fp->init->callback(fp->init, NULL);
 150        else
 151                fp->active = 1;
 152        return rc;
 153}
 154
 155/*
 156 * Shutdown fullscreen view.
 157 */
 158static void
 159fs3270_save_callback(struct raw3270_request *rq, void *data)
 160{
 161        struct fs3270 *fp;
 162
 163        fp = (struct fs3270 *) rq->view;
 164
 165        /* Correct idal buffer element 0 address. */
 166        fp->rdbuf->data[0] -= 5;
 167        fp->rdbuf->size += 5;
 168
 169        /*
 170         * If the rdbuf command failed or the idal buffer is
 171         * to small for the amount of data returned by the
 172         * rdbuf command, then we have no choice but to send
 173         * a SIGHUP to the application.
 174         */
 175        if (rq->rc != 0 || rq->rescnt == 0) {
 176                if (fp->fs_pid)
 177                        kill_pid(fp->fs_pid, SIGHUP, 1);
 178                fp->rdbuf_size = 0;
 179        } else
 180                fp->rdbuf_size = fp->rdbuf->size - rq->rescnt;
 181        raw3270_request_reset(rq);
 182        wake_up(&fp->wait);
 183}
 184
 185static void
 186fs3270_deactivate(struct raw3270_view *view)
 187{
 188        struct fs3270 *fp;
 189
 190        fp = (struct fs3270 *) view;
 191        fp->active = 0;
 192
 193        /* If an old init command is still running just return. */
 194        if (!raw3270_request_final(fp->init))
 195                return;
 196
 197        /* Prepare read-buffer request. */
 198        raw3270_request_set_cmd(fp->init, TC_RDBUF);
 199        /*
 200         * Hackish: skip first 5 bytes of the idal buffer to make
 201         * room for the TW_KR/TO_SBA/<address>/<address>/TO_IC sequence
 202         * in the activation command.
 203         */
 204        fp->rdbuf->data[0] += 5;
 205        fp->rdbuf->size -= 5;
 206        raw3270_request_set_idal(fp->init, fp->rdbuf);
 207        fp->init->rescnt = 0;
 208        fp->init->callback = fs3270_save_callback;
 209
 210        /* Start I/O to read in the 3270 buffer. */
 211        fp->init->rc = raw3270_start_locked(view, fp->init);
 212        if (fp->init->rc)
 213                fp->init->callback(fp->init, NULL);
 214}
 215
 216static int
 217fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
 218{
 219        /* Handle ATTN. Set indication and wake waiters for attention. */
 220        if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 221                fp->attention = 1;
 222                wake_up(&fp->wait);
 223        }
 224
 225        if (rq) {
 226                if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 227                        rq->rc = -EIO;
 228                else
 229                        /* Normal end. Copy residual count. */
 230                        rq->rescnt = irb->scsw.cmd.count;
 231        }
 232        return RAW3270_IO_DONE;
 233}
 234
 235/*
 236 * Process reads from fullscreen 3270.
 237 */
 238static ssize_t
 239fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off)
 240{
 241        struct fs3270 *fp;
 242        struct raw3270_request *rq;
 243        struct idal_buffer *ib;
 244        ssize_t rc;
 245        
 246        if (count == 0 || count > 65535)
 247                return -EINVAL;
 248        fp = filp->private_data;
 249        if (!fp)
 250                return -ENODEV;
 251        ib = idal_buffer_alloc(count, 0);
 252        if (IS_ERR(ib))
 253                return -ENOMEM;
 254        rq = raw3270_request_alloc(0);
 255        if (!IS_ERR(rq)) {
 256                if (fp->read_command == 0 && fp->write_command != 0)
 257                        fp->read_command = 6;
 258                raw3270_request_set_cmd(rq, fp->read_command ? : 2);
 259                raw3270_request_set_idal(rq, ib);
 260                rc = wait_event_interruptible(fp->wait, fp->attention);
 261                fp->attention = 0;
 262                if (rc == 0) {
 263                        rc = fs3270_do_io(&fp->view, rq);
 264                        if (rc == 0) {
 265                                count -= rq->rescnt;
 266                                if (idal_buffer_to_user(ib, data, count) != 0)
 267                                        rc = -EFAULT;
 268                                else
 269                                        rc = count;
 270
 271                        }
 272                }
 273                raw3270_request_free(rq);
 274        } else
 275                rc = PTR_ERR(rq);
 276        idal_buffer_free(ib);
 277        return rc;
 278}
 279
 280/*
 281 * Process writes to fullscreen 3270.
 282 */
 283static ssize_t
 284fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off)
 285{
 286        struct fs3270 *fp;
 287        struct raw3270_request *rq;
 288        struct idal_buffer *ib;
 289        int write_command;
 290        ssize_t rc;
 291
 292        fp = filp->private_data;
 293        if (!fp)
 294                return -ENODEV;
 295        ib = idal_buffer_alloc(count, 0);
 296        if (IS_ERR(ib))
 297                return -ENOMEM;
 298        rq = raw3270_request_alloc(0);
 299        if (!IS_ERR(rq)) {
 300                if (idal_buffer_from_user(ib, data, count) == 0) {
 301                        write_command = fp->write_command ? : 1;
 302                        if (write_command == 5)
 303                                write_command = 13;
 304                        raw3270_request_set_cmd(rq, write_command);
 305                        raw3270_request_set_idal(rq, ib);
 306                        rc = fs3270_do_io(&fp->view, rq);
 307                        if (rc == 0)
 308                                rc = count - rq->rescnt;
 309                } else
 310                        rc = -EFAULT;
 311                raw3270_request_free(rq);
 312        } else
 313                rc = PTR_ERR(rq);
 314        idal_buffer_free(ib);
 315        return rc;
 316}
 317
 318/*
 319 * process ioctl commands for the tube driver
 320 */
 321static long
 322fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 323{
 324        struct fs3270 *fp;
 325        struct raw3270_iocb iocb;
 326        int rc;
 327
 328        fp = filp->private_data;
 329        if (!fp)
 330                return -ENODEV;
 331        rc = 0;
 332        lock_kernel();
 333        switch (cmd) {
 334        case TUBICMD:
 335                fp->read_command = arg;
 336                break;
 337        case TUBOCMD:
 338                fp->write_command = arg;
 339                break;
 340        case TUBGETI:
 341                rc = put_user(fp->read_command, (char __user *) arg);
 342                break;
 343        case TUBGETO:
 344                rc = put_user(fp->write_command,(char __user *) arg);
 345                break;
 346        case TUBGETMOD:
 347                iocb.model = fp->view.model;
 348                iocb.line_cnt = fp->view.rows;
 349                iocb.col_cnt = fp->view.cols;
 350                iocb.pf_cnt = 24;
 351                iocb.re_cnt = 20;
 352                iocb.map = 0;
 353                if (copy_to_user((char __user *) arg, &iocb,
 354                                 sizeof(struct raw3270_iocb)))
 355                        rc = -EFAULT;
 356                break;
 357        }
 358        unlock_kernel();
 359        return rc;
 360}
 361
 362/*
 363 * Allocate fs3270 structure.
 364 */
 365static struct fs3270 *
 366fs3270_alloc_view(void)
 367{
 368        struct fs3270 *fp;
 369
 370        fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL);
 371        if (!fp)
 372                return ERR_PTR(-ENOMEM);
 373        fp->init = raw3270_request_alloc(0);
 374        if (IS_ERR(fp->init)) {
 375                kfree(fp);
 376                return ERR_PTR(-ENOMEM);
 377        }
 378        return fp;
 379}
 380
 381/*
 382 * Free fs3270 structure.
 383 */
 384static void
 385fs3270_free_view(struct raw3270_view *view)
 386{
 387        struct fs3270 *fp;
 388
 389        fp = (struct fs3270 *) view;
 390        if (fp->rdbuf)
 391                idal_buffer_free(fp->rdbuf);
 392        raw3270_request_free(((struct fs3270 *) view)->init);
 393        kfree(view);
 394}
 395
 396/*
 397 * Unlink fs3270 data structure from filp.
 398 */
 399static void
 400fs3270_release(struct raw3270_view *view)
 401{
 402}
 403
 404/* View to a 3270 device. Can be console, tty or fullscreen. */
 405static struct raw3270_fn fs3270_fn = {
 406        .activate = fs3270_activate,
 407        .deactivate = fs3270_deactivate,
 408        .intv = (void *) fs3270_irq,
 409        .release = fs3270_release,
 410        .free = fs3270_free_view
 411};
 412
 413/*
 414 * This routine is called whenever a 3270 fullscreen device is opened.
 415 */
 416static int
 417fs3270_open(struct inode *inode, struct file *filp)
 418{
 419        struct fs3270 *fp;
 420        struct idal_buffer *ib;
 421        int minor, rc = 0;
 422
 423        if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR)
 424                return -ENODEV;
 425        minor = iminor(filp->f_path.dentry->d_inode);
 426        /* Check for minor 0 multiplexer. */
 427        if (minor == 0) {
 428                struct tty_struct *tty = get_current_tty();
 429                if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) {
 430                        tty_kref_put(tty);
 431                        return -ENODEV;
 432                }
 433                minor = tty->index + RAW3270_FIRSTMINOR;
 434                tty_kref_put(tty);
 435        }
 436        lock_kernel();
 437        /* Check if some other program is already using fullscreen mode. */
 438        fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
 439        if (!IS_ERR(fp)) {
 440                raw3270_put_view(&fp->view);
 441                rc = -EBUSY;
 442                goto out;
 443        }
 444        /* Allocate fullscreen view structure. */
 445        fp = fs3270_alloc_view();
 446        if (IS_ERR(fp)) {
 447                rc = PTR_ERR(fp);
 448                goto out;
 449        }
 450
 451        init_waitqueue_head(&fp->wait);
 452        fp->fs_pid = get_pid(task_pid(current));
 453        rc = raw3270_add_view(&fp->view, &fs3270_fn, minor);
 454        if (rc) {
 455                fs3270_free_view(&fp->view);
 456                goto out;
 457        }
 458
 459        /* Allocate idal-buffer. */
 460        ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0);
 461        if (IS_ERR(ib)) {
 462                raw3270_put_view(&fp->view);
 463                raw3270_del_view(&fp->view);
 464                rc = PTR_ERR(fp);
 465                goto out;
 466        }
 467        fp->rdbuf = ib;
 468
 469        rc = raw3270_activate_view(&fp->view);
 470        if (rc) {
 471                raw3270_put_view(&fp->view);
 472                raw3270_del_view(&fp->view);
 473                goto out;
 474        }
 475        filp->private_data = fp;
 476out:
 477        unlock_kernel();
 478        return rc;
 479}
 480
 481/*
 482 * This routine is called when the 3270 tty is closed. We wait
 483 * for the remaining request to be completed. Then we clean up.
 484 */
 485static int
 486fs3270_close(struct inode *inode, struct file *filp)
 487{
 488        struct fs3270 *fp;
 489
 490        fp = filp->private_data;
 491        filp->private_data = NULL;
 492        if (fp) {
 493                put_pid(fp->fs_pid);
 494                fp->fs_pid = NULL;
 495                raw3270_reset(&fp->view);
 496                raw3270_put_view(&fp->view);
 497                raw3270_del_view(&fp->view);
 498        }
 499        return 0;
 500}
 501
 502static const struct file_operations fs3270_fops = {
 503        .owner           = THIS_MODULE,         /* owner */
 504        .read            = fs3270_read,         /* read */
 505        .write           = fs3270_write,        /* write */
 506        .unlocked_ioctl  = fs3270_ioctl,        /* ioctl */
 507        .compat_ioctl    = fs3270_ioctl,        /* ioctl */
 508        .open           = fs3270_open,          /* open */
 509        .release        = fs3270_close,         /* release */
 510};
 511
 512/*
 513 * 3270 fullscreen driver initialization.
 514 */
 515static int __init
 516fs3270_init(void)
 517{
 518        int rc;
 519
 520        rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
 521        if (rc)
 522                return rc;
 523        return 0;
 524}
 525
 526static void __exit
 527fs3270_exit(void)
 528{
 529        unregister_chrdev(IBM_FS3270_MAJOR, "fs3270");
 530}
 531
 532MODULE_LICENSE("GPL");
 533MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR);
 534
 535module_init(fs3270_init);
 536module_exit(fs3270_exit);
 537