]> git.hungrycats.org Git - linux/commitdiff
[PATCH] s390: 3270 console driver
authorAndrew Morton <akpm@osdl.org>
Fri, 14 May 2004 12:50:11 +0000 (05:50 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 14 May 2004 12:50:11 +0000 (05:50 -0700)
From: Martin Schwidefsky <schwidefsky@de.ibm.com>

3270 device driver change:
 - Don't allow activation of views while the initial size sensing is still
   in progress. Replace RAW3270_FLAGS_SHUTDOWN with RAW3270_FLAGS_READY.
 - Make 3270 views loadable as modules.

arch/s390/defconfig
drivers/s390/Kconfig
drivers/s390/char/Makefile
drivers/s390/char/raw3270.c
drivers/s390/char/raw3270.h
drivers/s390/char/tty3270.c

index 3e53b256cb9a6a4ab8109b37d5e9fea445fcff7b..3fb8223963386f9997fd60eaa1454fdf979f8bba 100644 (file)
@@ -178,6 +178,8 @@ CONFIG_UNIX98_PTY_COUNT=2048
 # S/390 character device drivers
 #
 CONFIG_TN3270=y
+CONFIG_TN3270_TTY=y
+CONFIG_TN3270_FS=m
 CONFIG_TN3270_CONSOLE=y
 CONFIG_TN3215=y
 CONFIG_TN3215_CONSOLE=y
index 4ae29d32d7743d2184b0d4c822b4de753454bf05..e190f21ed2ee9b143b7cf19b7eaa24bca01522df 100644 (file)
@@ -54,17 +54,28 @@ config UNIX98_PTY_COUNT
 comment "S/390 character device drivers"
 
 config TN3270
-       tristate "Support for locally attached 3270 tubes"
+       tristate "Support for locally attached 3270 terminals"
        help
-         Include support for IBM 3270 line-mode terminals.
+         Include support for IBM 3270 terminals.
+
+config TN3270_TTY
+       tristate "Support for tty input/output on 3270 terminals"
+       depends on TN3270
+       help
+         Include support for using an IBM 3270 terminal as a Linux tty.
+
+config TN3270_FS
+       tristate "Support for fullscreen applications on 3270 terminals"
+       depends on TN3270
+       help
+         Include support for fullscreen applications on an IBM 3270 terminal.
 
 config TN3270_CONSOLE
-       bool "Support for console on 3270 line mode terminal"
-       depends on TN3270=y
+       bool "Support for console on 3270 terminal"
+       depends on TN3270=y && TN3270_TTY=y
        help
-         Include support for using an IBM 3270 line-mode terminal as a Linux
-         system console.  Available only if 3270 support is compiled in
-         statically.
+         Include support for using an IBM 3270 terminal as a Linux system
+         console.  Available only if 3270 support is compiled in statically.
 
 config TN3215
        bool "Support for 3215 line mode terminal"
index 6aa04e76fc8e759e5eaa4a56c3d88c90e874f41a..bd989f25c177eaf8a07414617906db88424b722d 100644 (file)
@@ -4,9 +4,10 @@
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o
 
-tub3270-objs := raw3270.o tty3270.o fs3270.o
-obj-$(CONFIG_TN3270) += tub3270.o
+obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
+obj-$(CONFIG_TN3270_TTY) += tty3270.o
+obj-$(CONFIG_TN3270_FS) += fs3270.o
 
 obj-$(CONFIG_TN3215) += con3215.o
 
index 2b422d36d78f36a6a7b7802e2f935c43b28f4be5..4a57ba6b16f9d98aea5ca7ae8f00e35b53843bd1 100644 (file)
@@ -47,11 +47,11 @@ struct raw3270 {
 #define RAW3270_FLAGS_14BITADDR        0       /* 14-bit buffer addresses */
 #define RAW3270_FLAGS_BUSY     1       /* Device busy, leave it alone */
 #define RAW3270_FLAGS_ATTN     2       /* Device sent an ATTN interrupt */
-#define RAW3270_FLAGS_SHUTDOWN 4       /* Device is in offline processing */
+#define RAW3270_FLAGS_READY    4       /* Device is useable by views */
 #define RAW3270_FLAGS_CONSOLE  8       /* Device is the console. */
 
-/* Lock to protect global data of raw3270 (devices, views, etc). */
-static spinlock_t raw3270_lock = SPIN_LOCK_UNLOCKED;
+/* Semaphore to protect global data of raw3270 (devices, views, etc). */
+static DECLARE_MUTEX(raw3270_sem);
 
 /* List of 3270 devices. */
 static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
@@ -308,7 +308,7 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)
        rp = view->dev;
        if (!rp || rp->view != view)
                rc = -EACCES;
-       else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+       else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
                rc = -ENODEV;
        else
                rc =  __raw3270_start(rp, view, rq);
@@ -603,7 +603,6 @@ __raw3270_size_device(struct raw3270 *rp)
 {
        static const unsigned char wbuf[] =
                { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
-       unsigned long flags;
        struct raw3270_ua *uap;
        unsigned short count;
        int rc;
@@ -638,6 +637,8 @@ __raw3270_size_device(struct raw3270 *rp)
        /* Wait for attention interrupt. */
 #ifdef CONFIG_TN3270_CONSOLE
        if (raw3270_registered == 0) {
+               unsigned long flags;
+
                spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
                while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags))
                        wait_cons_dev();
@@ -764,7 +765,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)
         * Add device to list and find the smallest unused minor
         * number for it.
         */
-       spin_lock(&raw3270_lock);
+       down(&raw3270_sem);
        /* Keep the list sorted. */
        minor = 0;
        rp->minor = -1;
@@ -781,7 +782,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)
                rp->minor = minor;
                list_add_tail(&rp->list, &raw3270_devices);
        }
-       spin_unlock(&raw3270_lock);
+       up(&raw3270_sem);
        /* No free minor number? Then give up. */
        if (rp->minor == -1)
                return -EUSERS;
@@ -811,6 +812,7 @@ raw3270_setup_console(struct ccw_device *cdev)
        raw3270_reset_device(rp);
        raw3270_size_device(rp);
        raw3270_reset_device(rp);
+       set_bit(RAW3270_FLAGS_READY, &rp->flags);
        return rp;
 }
 
@@ -872,7 +874,7 @@ raw3270_activate_view(struct raw3270_view *view)
        spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
        if (rp->view == view)
                rc = 0;
-       else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+       else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
                rc = -ENODEV;
        else {
                oldview = 0;
@@ -921,7 +923,7 @@ raw3270_deactivate_view(struct raw3270_view *view)
                list_del_init(&view->list);
                list_add_tail(&view->list, &rp->view_list);
                /* Try to activate another view. */
-               if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+               if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
                        list_for_each_entry(view, &rp->view_list, list)
                                if (view->fn->activate(view) == 0) {
                                        rp->view = view;
@@ -942,13 +944,13 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor)
        struct raw3270 *rp;
        int rc;
 
-       spin_lock(&raw3270_lock);
+       down(&raw3270_sem);
        rc = -ENODEV;
        list_for_each_entry(rp, &raw3270_devices, list) {
                if (rp->minor != minor)
                        continue;
                spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
-               if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+               if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
                        atomic_set(&view->ref_count, 2);
                        view->dev = rp;
                        view->fn = fn;
@@ -963,7 +965,7 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor)
                spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
                break;
        }
-       spin_unlock(&raw3270_lock);
+       up(&raw3270_sem);
        return rc;
 }
 
@@ -977,25 +979,26 @@ raw3270_find_view(struct raw3270_fn *fn, int minor)
        struct raw3270_view *view, *tmp;
        unsigned long flags;
 
-       spin_lock(&raw3270_lock);
+       down(&raw3270_sem);
        view = ERR_PTR(-ENODEV);
        list_for_each_entry(rp, &raw3270_devices, list) {
                if (rp->minor != minor)
                        continue;
                spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
-               if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+               if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
+                       view = ERR_PTR(-ENOENT);
                        list_for_each_entry(tmp, &rp->view_list, list) {
                                if (tmp->fn == fn) {
                                        raw3270_get_view(tmp);
                                        view = tmp;
+                                       break;
                                }
                        }
-               } else
-                       view = ERR_PTR(-ENOENT);
+               }
                spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
                break;
        }
-       spin_unlock(&raw3270_lock);
+       up(&raw3270_sem);
        return view;
 }
 
@@ -1007,6 +1010,7 @@ raw3270_del_view(struct raw3270_view *view)
 {
        unsigned long flags;
        struct raw3270 *rp;
+       struct raw3270_view *nv;
 
        rp = view->dev;
        spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
@@ -1015,17 +1019,18 @@ raw3270_del_view(struct raw3270_view *view)
                rp->view = 0;
        }
        list_del_init(&view->list);
-       if (!rp->view && !test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+       if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
                /* Try to activate another view. */
-               list_for_each_entry(view, &rp->view_list, list)
-                       if (view->fn->activate(view) == 0) {
-                               rp->view = view;
+               list_for_each_entry(nv, &rp->view_list, list) {
+                       if (nv->fn->activate(view) == 0) {
+                               rp->view = nv;
                                break;
                        }
+               }
        }
        spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
        /* Wait for reference counter to drop to zero. */
-       atomic_dec(&view->ref_count);
+       atomic_sub(2, &view->ref_count);
        wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0);
        if (view->fn->free)
                view->fn->free(view);
@@ -1040,9 +1045,9 @@ raw3270_delete_device(struct raw3270 *rp)
        struct ccw_device *cdev;
 
        /* Remove from device chain. */
-       spin_lock(&raw3270_lock);
+       down(&raw3270_sem);
        list_del_init(&rp->list);
-       spin_unlock(&raw3270_lock);
+       up(&raw3270_sem);
 
        /* Disconnect from ccw_device. */
        cdev = rp->cdev;
@@ -1109,8 +1114,48 @@ raw3270_create_attributes(struct raw3270 *rp)
        sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
 }
 
-/* Hackish. A notifier chain would be cleaner. */
-extern void tty3270_notifier(int index, int active);
+/*
+ * Notifier for device addition/removal
+ */
+struct raw3270_notifier {
+       struct list_head list;
+       void (*notifier)(int, int);
+};
+
+static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier);
+
+int raw3270_register_notifier(void (*notifier)(int, int))
+{
+       struct raw3270_notifier *np;
+       struct raw3270 *rp;
+
+       np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL);
+       if (!np)
+               return -ENOMEM;
+       np->notifier = notifier;
+       down(&raw3270_sem);
+       list_add_tail(&np->list, &raw3270_notifier);
+       list_for_each_entry(rp, &raw3270_devices, list) {
+               get_device(&rp->cdev->dev);
+               notifier(rp->minor, 1);
+       }
+       up(&raw3270_sem);
+       return 0;
+}
+
+void raw3270_unregister_notifier(void (*notifier)(int, int))
+{
+       struct raw3270_notifier *np;
+
+       down(&raw3270_sem);
+       list_for_each_entry(np, &raw3270_notifier, list)
+               if (np->notifier == notifier) {
+                       list_del(&np->list);
+                       kfree(np);
+                       break;
+               }
+       up(&raw3270_sem);
+}
 
 /*
  * Set 3270 device online.
@@ -1119,6 +1164,7 @@ static int
 raw3270_set_online (struct ccw_device *cdev)
 {
        struct raw3270 *rp;
+       struct raw3270_notifier *np;
 
        rp = raw3270_create_device(cdev);
        if (IS_ERR(rp))
@@ -1127,7 +1173,11 @@ raw3270_set_online (struct ccw_device *cdev)
        raw3270_size_device(rp);
        raw3270_reset_device(rp);
        raw3270_create_attributes(rp);
-       tty3270_notifier(rp->minor, 1);
+       set_bit(RAW3270_FLAGS_READY, &rp->flags);
+       down(&raw3270_sem);
+       list_for_each_entry(np, &raw3270_notifier, list)
+               np->notifier(rp->minor, 1);
+       up(&raw3270_sem);
        return 0;
 }
 
@@ -1140,9 +1190,10 @@ raw3270_remove (struct ccw_device *cdev)
        unsigned long flags;
        struct raw3270 *rp;
        struct raw3270_view *v;
+       struct raw3270_notifier *np;
 
        rp = cdev->dev.driver_data;
-       set_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags);
+       clear_bit(RAW3270_FLAGS_READY, &rp->flags);
 
        sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);
 
@@ -1162,7 +1213,10 @@ raw3270_remove (struct ccw_device *cdev)
        }
        spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
 
-       tty3270_notifier(rp->minor, 0);
+       down(&raw3270_sem);
+       list_for_each_entry(np, &raw3270_notifier, list)
+               np->notifier(rp->minor, 0);
+       up(&raw3270_sem);
 
        /* Reset 3270 device. */
        raw3270_reset_device(rp);
@@ -1210,7 +1264,7 @@ static struct ccw_driver raw3270_ccw_driver = {
        .set_offline    = &raw3270_set_offline,
 };
 
-int
+static int
 raw3270_init(void)
 {
        struct raw3270 *rp;
@@ -1222,18 +1276,17 @@ raw3270_init(void)
        rc = ccw_driver_register(&raw3270_ccw_driver);
        if (rc == 0) {
                /* Create attributes for early (= console) device. */
-               spin_lock(&raw3270_lock);
+               down(&raw3270_sem);
                list_for_each_entry(rp, &raw3270_devices, list) {
                        get_device(&rp->cdev->dev);
                        raw3270_create_attributes(rp);
-                       tty3270_notifier(rp->minor, 1);
                }
-               spin_unlock(&raw3270_lock);
+               up(&raw3270_sem);
        }
        return rc;
 }
 
-void
+static void
 raw3270_exit(void)
 {
        ccw_driver_unregister(&raw3270_ccw_driver);
@@ -1241,8 +1294,9 @@ raw3270_exit(void)
 
 MODULE_LICENSE("GPL");
 
-EXPORT_SYMBOL(raw3270_init);
-EXPORT_SYMBOL(raw3270_exit);
+module_init(raw3270_init);
+module_exit(raw3270_exit);
+
 EXPORT_SYMBOL(raw3270_request_alloc);
 EXPORT_SYMBOL(raw3270_request_free);
 EXPORT_SYMBOL(raw3270_request_reset);
@@ -1258,3 +1312,6 @@ EXPORT_SYMBOL(raw3270_activate_view);
 EXPORT_SYMBOL(raw3270_deactivate_view);
 EXPORT_SYMBOL(raw3270_start);
 EXPORT_SYMBOL(raw3270_start_irq);
+EXPORT_SYMBOL(raw3270_register_notifier);
+EXPORT_SYMBOL(raw3270_unregister_notifier);
+EXPORT_SYMBOL(raw3270_wait_queue);
index 8ce5fa56aa6dc5fa690953bb429f6ccbbdfc2df6..ed5d4eb9f623114508e7a2587508e41c84af766c 100644 (file)
@@ -78,9 +78,6 @@
 
 #define RAW3270_MAXDEVS        256
 
-int raw3270_init(void);
-void raw3270_exit(void);
-
 /* For TUBGETMOD and TUBSETMOD. Should include. */
 struct raw3270_iocb {
        short model;
@@ -190,6 +187,10 @@ raw3270_put_view(struct raw3270_view *view)
 struct raw3270 *raw3270_setup_console(struct ccw_device *cdev);
 void raw3270_wait_cons_dev(struct raw3270 *);
 
+/* Notifier for device addition/removal */
+int raw3270_register_notifier(void (*notifier)(int, int));
+void raw3270_unregister_notifier(void (*notifier)(int, int));
+
 /*
  * Little memory allocator for string objects. 
  */
index 9c816f008c110525662eea7d8180d4b9d4a896f9..debc2b02c20169f8c6afe76aa55668e07fb027b5 100644 (file)
@@ -35,6 +35,7 @@
 #define TTY3270_STRING_PAGES 5
 
 struct tty_driver *tty3270_driver;
+static int tty3270_max_index;
 
 struct raw3270_fn tty3270_fn;
 
@@ -836,6 +837,22 @@ tty3270_free(struct raw3270_view *view)
        tty3270_free_view((struct tty3270 *) view);
 }
 
+/*
+ * Delayed freeing of tty3270 views.
+ */
+static void
+tty3270_del_views(void)
+{
+       struct tty3270 *tp;
+       int i;
+
+       for (i = 0; i < tty3270_max_index; i++) {
+               tp = (struct tty3270 *) raw3270_find_view(&tty3270_fn, i);
+               if (!IS_ERR(tp))
+                       raw3270_del_view(&tp->view);
+       }
+}
+
 struct raw3270_fn tty3270_fn = {
        .activate = tty3270_activate,
        .deactivate = tty3270_deactivate,
@@ -867,6 +884,12 @@ tty3270_open(struct tty_struct *tty, struct file * filp)
                tp->inattr = TF_INPUT;
                return 0;
        }
+       if (tty3270_max_index < tty->index + 1)
+               tty3270_max_index = tty->index + 1;
+
+       /* Quick exit if there is no device for tty->index. */
+       if (PTR_ERR(tp) == -ENODEV)
+               return -ENODEV;
 
        /* Allocate tty3270 structure on first open. */
        tp = tty3270_alloc_view();
@@ -1778,9 +1801,6 @@ tty3270_init(void)
        struct tty_driver *driver;
        int ret;
 
-       ret = raw3270_init();
-       if (ret)
-               return ret;
        driver = alloc_tty_driver(256);
        if (!driver)
                return -ENOMEM;
@@ -1807,6 +1827,14 @@ tty3270_init(void)
                return ret;
        }
        tty3270_driver = driver;
+       ret = raw3270_register_notifier(tty3270_notifier);
+       if (ret) {
+               printk(KERN_ERR "tty3270 notifier registration failed "
+                      "with %d\n", ret);
+               put_tty_driver(driver);
+               return ret;
+
+       }
        return 0;
 }
 
@@ -1815,10 +1843,11 @@ tty3270_exit(void)
 {
        struct tty_driver *driver;
 
+       raw3270_unregister_notifier(tty3270_notifier);
        driver = tty3270_driver;
        tty3270_driver = 0;
        tty_unregister_driver(driver);
-       raw3270_exit();
+       tty3270_del_views();
 }
 
 MODULE_LICENSE("GPL");