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