patch-2.4.4 linux/drivers/s390/char/tuball.c
Next file: linux/drivers/s390/char/tubfs.c
Previous file: linux/drivers/s390/char/tapedefs.h
Back to the patch index
Back to the overall index
- Lines: 626
- Date:
Wed Apr 18 11:49:11 2001
- Orig file:
v2.4.3/linux/drivers/s390/char/tuball.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.3/linux/drivers/s390/char/tuball.c linux/drivers/s390/char/tuball.c
@@ -0,0 +1,625 @@
+/*
+ * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
+ *
+ * tuball.c -- Initialization, termination, irq lookup
+ *
+ *
+ *
+ *
+ *
+ * Author: Richard Hitt
+ */
+#include <linux/config.h>
+#include "tubio.h"
+#ifndef MODULE
+#include <linux/init.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+#include <asm/cpcmd.h>
+#include <linux/bootmem.h>
+#else
+#include "../../../../arch/s390/kernel/cpcmd.h"
+#endif
+#endif
+
+/* Module parameters */
+int tubdebug;
+int tubscrolltime;
+int tubscrollparm;
+int tubxcorrect = 1; /* Do correct ebc<->asc tables */
+#ifdef MODULE
+MODULE_PARM(tubdebug, "i");
+MODULE_PARM(tubscrolltime, "i");
+MODULE_PARM(tubxcorrect, "i");
+#endif
+/*
+ * Values for tubdebug and their effects:
+ * 1 - print in hex on console the first 16 bytes received
+ * 2 - print address at which array tubminors is allocated
+ * 4 - attempt to register tty3270_driver
+ */
+int tubnummins;
+tub_t *(*tubminors)[TUBMAXMINS];
+tub_t *(*(*tubirqs)[256])[256];
+unsigned char tub_ascebc[256];
+unsigned char tub_ebcasc[256];
+int tubinitminors(void);
+void tubfiniminors(void);
+void tubint(int, void *, struct pt_regs *);
+
+/* Lookup-by-irq functions */
+int tubaddbyirq(tub_t *, int);
+tub_t *tubfindbyirq(int);
+void tubdelbyirq(tub_t *, int);
+void tubfiniirqs(void);
+
+extern int fs3270_init(void);
+extern void fs3270_fini(void);
+extern int tty3270_init(void);
+extern void tty3270_fini(void);
+
+unsigned char tub_ebcgraf[64] =
+ { 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f };
+
+static int tub3270_init(void);
+
+#ifndef MODULE
+
+/*
+ * Can't have this driver a module & support console at the same time
+ */
+#ifdef CONFIG_3270_CONSOLE
+static kdev_t tub3270_con_device(struct console *);
+static void tub3270_con_unblank(void);
+static void tub3270_con_write(struct console *, const char *,
+ unsigned int);
+
+static struct console tub3270_con = {
+ "tub3270", /* name */
+ tub3270_con_write, /* write */
+ NULL, /* read */
+ tub3270_con_device, /* device */
+ NULL, /* wait_key */
+ tub3270_con_unblank, /* unblank */
+ NULL, /* setup */
+ CON_PRINTBUFFER, /* flags */
+ 0, /* index */
+ 0, /* cflag */
+ NULL /* next */
+};
+
+int tub3270_con_devno = -1; /* set by tub3270_con_setup() */
+bcb_t tub3270_con_bcb; /* Buffer that receives con writes */
+spinlock_t tub3270_con_bcblock; /* Lock for the buffer */
+int tub3270_con_irq = -1; /* set nonneg by _activate() */
+tub_t *tub3270_con_tubp; /* set nonzero by _activate() */
+struct tty_driver tty3270_con_driver; /* for /dev/console at 4, 64 */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+__initfunc(void tub3270_con_setup(char *str, int *ints))
+#else
+static int __init tub3270_con_setup(char *str)
+#endif
+{
+ int vdev;
+
+ vdev = simple_strtoul(str, 0, 16);
+ if (vdev >= 0 && vdev < 65536)
+ tub3270_con_devno = vdev;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+ return;
+#else
+ return 1;
+#endif
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+__setup("condev=", tub3270_con_setup);
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+__initfunc (long tub3270_con_init(long kmem_start, long kmem_end))
+#else
+void __init tub3270_con_init(void)
+#endif
+{
+ tub3270_con_bcb.bc_len = 65536;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+ if (!MACHINE_IS_VM && !MACHINE_IS_P390)
+ return kmem_start;
+ tub3270_con_bcb.bc_buf = (void *)kmem_start;
+ kmem_start += tub3270_con_bcb.bc_len;
+ register_console(&tub3270_con);
+ return kmem_start;
+#else
+ if (!MACHINE_IS_VM && !MACHINE_IS_P390)
+ return;
+ tub3270_con_bcb.bc_buf = (void *)alloc_bootmem_low(
+ tub3270_con_bcb.bc_len);
+ register_console(&tub3270_con);
+#endif
+}
+
+static kdev_t
+tub3270_con_device(struct console *conp)
+{
+ return MKDEV(IBM_TTY3270_MAJOR, conp->index);
+}
+
+static void
+tub3270_con_unblank(void)
+{
+ /* flush everything: panic has occurred */
+}
+
+int tub3270_con_write_deadlock_ct;
+int tub3270_con_write_deadlock_bytes;
+static void
+tub3270_con_write(struct console *conp,
+ const char *buf, unsigned int count)
+{
+ int flags;
+ tub_t *tubp = tub3270_con_tubp;
+ void tty3270_sched_bh(tub_t *);
+ int rc;
+ bcb_t obcb;
+
+ obcb.bc_buf = (char *)buf;
+ obcb.bc_len = obcb.bc_cnt = obcb.bc_wr =
+ MIN(count, tub3270_con_bcb.bc_len);
+ obcb.bc_rd = 0;
+
+ spin_lock_irqsave(&tub3270_con_bcblock, flags);
+ rc = tub3270_movedata(&obcb, &tub3270_con_bcb);
+ spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
+
+ if (tubp && rc && TUBTRYLOCK(tubp->irq, flags)) {
+ tty3270_sched_bh(tubp);
+ TUBUNLOCK(tubp->irq, flags);
+ }
+}
+
+int tub3270_con_copy(tub_t *tubp)
+{
+ int flags;
+ int rc;
+
+ spin_lock_irqsave(&tub3270_con_bcblock, flags);
+ rc = tub3270_movedata(&tub3270_con_bcb, &tubp->tty_bcb);
+ spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
+ return rc;
+}
+#endif /* CONFIG_3270_CONSOLE */
+
+
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+__initfunc(void tub3270_initfunc(void))
+#else
+void __init tub3270_initfunc(void)
+#endif
+{
+ tub3270_init();
+}
+#else /* If generated as a MODULE */
+/*
+ * module init: find tubes; get a major nbr
+ */
+int
+init_module(void)
+{
+ if (tubnummins != 0) {
+ printk(KERN_ERR "EEEK!! Tube driver cobbigling!!\n");
+ return -1;
+ }
+ return tub3270_init();
+}
+
+/*
+ * remove driver: unregister the major number
+ */
+void
+cleanup_module(void)
+{
+ fs3270_fini();
+ tty3270_fini();
+ tubfiniminors();
+}
+#endif /* Not a MODULE or a MODULE */
+
+void
+tub_inc_use_count(void)
+{
+ MOD_INC_USE_COUNT;
+}
+
+void
+tub_dec_use_count(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * tub3270_init() called by kernel or module initialization
+ */
+static int
+tub3270_init(void)
+{
+ s390_dev_info_t d;
+ int i, rc;
+
+ /*
+ * Initialize default scrolltime to either -1 or the
+ * module parameter tubscrolltime.
+ */
+ if (tubscrolltime)
+ tubscrollparm = tubscrolltime;
+ else
+ tubscrollparm = -1;
+
+ /*
+ * Copy and correct ebcdic - ascii translate tables
+ */
+ memcpy(tub_ascebc, _ascebc, sizeof tub_ascebc);
+ memcpy(tub_ebcasc, _ebcasc, sizeof tub_ebcasc);
+ if (tubxcorrect) {
+ /* correct brackets and circumflex */
+ tub_ascebc['['] = 0xad;
+ tub_ascebc[']'] = 0xbd;
+ tub_ebcasc[0xad] = '[';
+ tub_ebcasc[0xbd] = ']';
+ tub_ascebc['^'] = 0xb0;
+ tub_ebcasc[0x5f] = '^';
+ }
+
+ rc = tubinitminors();
+ if (rc != 0)
+ return rc;
+
+ for (i = get_irq_first(); i >= 0; i = get_irq_next(i)) {
+ if ((rc = get_dev_info_by_irq(i, &d)))
+ continue;
+ if (d.status)
+ continue;
+#ifdef CONFIG_3270_CONSOLE
+ if (d.sid_data.cu_type == 0x3215 && MACHINE_IS_VM) {
+ cpcmd("TERM CONMODE 3270", NULL, 0);
+ d.sid_data.cu_type = 0x3270;
+ }
+#endif /* CONFIG_3270_CONSOLE */
+ if ((d.sid_data.cu_type & 0xfff0) != 0x3270)
+ continue;
+
+ rc = tubmakemin(i, &d);
+ if (rc < 0) {
+ if (tubnummins == 1) { /* if first time */
+ tubfiniminors();
+ printk(KERN_ERR "No kernel memory available"
+ " for 3270 tube devices.\n");
+ return rc;
+ }
+ printk(KERN_WARNING "3270 tube registration ran out of memory"
+ " after %d devices\n", tubnummins - 1);
+ break;
+ } else {
+ printk(KERN_INFO "3270: %.4x on sch %d, minor %d\n",
+ d.devno, d.irq, rc);
+ }
+ }
+
+ if (fs3270_init() || tty3270_init()) {
+ printk(KERN_ERR "fs3270_init() or tty3270_init() failed\n");
+ fs3270_fini();
+ tty3270_fini();
+ tubfiniminors();
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * tub3270_movedata(bcb_t *, bcb_t *) -- Move data stream
+ */
+int
+tub3270_movedata(bcb_t *ib, bcb_t *ob)
+{
+ int count; /* Total move length */
+ int rc;
+
+ rc = count = MIN(ib->bc_cnt, ob->bc_len - ob->bc_cnt);
+ while (count > 0) {
+ int len1; /* Contig bytes avail in ib */
+
+ if (ib->bc_wr > ib->bc_rd)
+ len1 = ib->bc_wr - ib->bc_rd;
+ else
+ len1 = ib->bc_len - ib->bc_rd;
+ if (len1 > count)
+ len1 = count;
+
+ while (len1 > 0) {
+ int len2; /* Contig space avail in ob */
+
+ if (ob->bc_rd > ob->bc_wr)
+ len2 = ob->bc_rd - ob->bc_wr;
+ else
+ len2 = ob->bc_len - ob->bc_wr;
+ if (len2 > len1)
+ len2 = len1;
+
+ memcpy(ob->bc_buf + ob->bc_wr,
+ ib->bc_buf + ib->bc_rd,
+ len2);
+
+ ib->bc_rd += len2;
+ if (ib->bc_rd == ib->bc_len)
+ ib->bc_rd = 0;
+ ib->bc_cnt -= len2;
+
+ ob->bc_wr += len2;
+ if (ob->bc_wr == ob->bc_len)
+ ob->bc_wr = 0;
+ ob->bc_cnt += len2;
+
+ len1 -= len2;
+ count -= len2;
+ }
+ }
+ return rc;
+}
+
+/*
+ * receive an interrupt
+ */
+void
+tubint(int irq, void *ipp, struct pt_regs *prp)
+{
+ devstat_t *dsp = ipp;
+ tub_t *tubp;
+
+ if ((tubp = IRQ2TUB(irq)) && (tubp->intv))
+ (tubp->intv)(tubp, dsp);
+}
+
+/*
+ * Initialize array of pointers to minor structures tub_t.
+ * Returns 0 or -ENOMEM.
+ */
+int
+tubinitminors(void)
+{
+ tubminors = (tub_t *(*)[TUBMAXMINS])kmalloc(sizeof *tubminors,
+ GFP_KERNEL);
+ if (tubminors == NULL)
+ return -ENOMEM;
+ memset(tubminors, 0, sizeof *tubminors);
+ return 0;
+}
+
+/*
+ * Add a minor 327x device. Argument is an irq value.
+ *
+ * Point elements of two arrays to the newly created tub_t:
+ * 1. (*tubminors)[minor]
+ * 2. (*(*tubirqs)[irqhi])[irqlo]
+ * The first looks up from minor number at context time; the second
+ * looks up from irq at interrupt time.
+ */
+int
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+tubmakemin(int irq, dev_info_t *dp)
+#else
+tubmakemin(int irq, s390_dev_info_t *dp)
+#endif
+{
+ tub_t *tubp;
+ int minor;
+ int flags;
+
+ if ((minor = ++tubnummins) == TUBMAXMINS)
+ return -ENODEV;
+
+ tubp = kmalloc(sizeof(tub_t), GFP_KERNEL);
+ if (tubp == NULL) {
+ return -ENOMEM;
+ }
+ if (tubaddbyirq(tubp, irq) != 0) {
+ kfree(tubp);
+ return -ENOMEM;
+ }
+ memset(tubp, 0, sizeof(tub_t));
+ tubp->minor = minor;
+ tubp->irq = irq;
+ TUBLOCK(tubp->irq, flags);
+ tubp->devno = dp->devno;
+ tubp->geom_rows = _GEOM_ROWS;
+ tubp->geom_cols = _GEOM_COLS;
+ init_waitqueue_head(&tubp->waitq);
+
+ tubp->tty_bcb.bc_len = TTY_OUTPUT_SIZE;
+ tubp->tty_bcb.bc_buf = (void *)kmalloc(tubp->tty_bcb.bc_len,
+ GFP_KERNEL);
+ if (tubp->tty_bcb.bc_buf == NULL) {
+ TUBUNLOCK(tubp->irq, flags);
+ tubdelbyirq(tubp, irq);
+ kfree(tubp);
+ return -ENOMEM;
+ }
+ tubp->tty_bcb.bc_cnt = 0;
+ tubp->tty_bcb.bc_wr = 0;
+ tubp->tty_bcb.bc_rd = 0;
+ (*tubminors)[minor] = tubp;
+#ifdef CONFIG_3270_CONSOLE
+ if (tub3270_con_tubp == NULL && tub3270_con_bcb.bc_buf != NULL &&
+ (tub3270_con_devno == -1 ||
+ tub3270_con_devno == dp->devno)) {
+ extern void tty3270_int(tub_t *, devstat_t *);
+
+ tubp->cmd = TBC_CONOPEN;
+ tubp->flags |= TUB_OPEN_STET;
+ tty3270_size(tubp, &flags);
+ tty3270_aid_init(tubp);
+ tty3270_scl_init(tubp);
+ tub3270_con_irq = tubp->irq;
+ tub3270_con_tubp = tubp;
+ tubp->intv = tty3270_int;
+ tubp->cmd = TBC_UPDSTAT;
+ tty3270_build(tubp);
+ }
+#endif /* CONFIG_3270_CONSOLE */
+ TUBUNLOCK(tubp->irq, flags);
+ return minor;
+}
+
+/*
+ * Release array of pointers to minor structures tub_t, but first
+ * release any storage pointed to by them.
+ */
+void
+tubfiniminors(void)
+{
+ int i;
+ tub_t **tubpp, *tubp;
+
+ if (tubminors == NULL)
+ return;
+
+ for (i = 0; i < TUBMAXMINS; i++) {
+ tubpp = &(*tubminors)[i];
+ if ((tubp = *tubpp)) {
+ tubdelbyirq(tubp, tubp->irq);
+ tty3270_rcl_fini(tubp);
+ kfree(tubp->tty_bcb.bc_buf);
+ tubp->tty_bcb.bc_buf = NULL;
+ tubp->ttyscreen = NULL;
+ kfree(tubp);
+ *tubpp = NULL;
+ }
+ }
+ kfree(tubminors);
+ tubminors = NULL;
+ tubfiniirqs();
+}
+
+/*
+ * tubaddbyirq() -- Add tub_t for irq lookup in tubint()
+ */
+int
+tubaddbyirq(tub_t *tubp, int irq)
+{
+ int irqhi = (irq >> 8) & 255;
+ int irqlo = irq & 255;
+ tub_t *(*itubpp)[256];
+
+ /* Allocate array (*tubirqs)[] if first time */
+ if (tubirqs == NULL) {
+ tubirqs = (tub_t *(*(*)[256])[256])
+ kmalloc(sizeof *tubirqs, GFP_KERNEL);
+ if (tubirqs == NULL)
+ return -ENOMEM;
+ memset(tubirqs, 0, sizeof *tubirqs);
+ }
+
+ /* Allocate subarray (*(*tubirqs)[])[] if first use */
+ if ((itubpp = (*tubirqs)[irqhi]) == NULL) {
+ itubpp = (tub_t *(*)[256])
+ kmalloc(sizeof(*itubpp), GFP_KERNEL);
+ if (itubpp == NULL) {
+ if (tubnummins == 1) { /* if first time */
+ kfree(tubirqs);
+ tubirqs = NULL;
+ }
+ return -ENOMEM;
+ } else {
+ memset(itubpp, 0, sizeof(*itubpp));
+ (*tubirqs)[irqhi] = itubpp;
+ }
+ }
+
+ /* Request interrupt service */
+ if ((tubp->irqrc = request_irq(irq, tubint, SA_INTERRUPT,
+ "3270 tube driver", &tubp->devstat)) != 0)
+ return tubp->irqrc;
+
+ /* Fill in the proper subarray element */
+ (*itubpp)[irqlo] = tubp;
+ return 0;
+}
+
+/*
+ * tubfindbyirq(irq)
+ */
+tub_t *
+tubfindbyirq(int irq)
+{
+ int irqhi = (irq >> 8) & 255;
+ int irqlo = irq & 255;
+ tub_t *tubp;
+
+ if (tubirqs == NULL)
+ return NULL;
+ if ((*tubirqs)[irqhi] == NULL)
+ return NULL;
+ tubp = (*(*tubirqs)[irqhi])[irqlo];
+ if (tubp->irq == irq)
+ return tubp;
+ return NULL;
+}
+
+/*
+ * tubdelbyirq(tub_t*, irq)
+ */
+void
+tubdelbyirq(tub_t *tubp, int irq)
+{
+ int irqhi = (irq >> 8) & 255;
+ int irqlo = irq & 255;
+ tub_t *(*itubpp)[256], *itubp;
+
+ if (tubirqs == NULL) {
+ printk(KERN_ERR "tubirqs is NULL\n");
+ return;
+ }
+ itubpp = (*tubirqs)[irqhi];
+ if (itubpp == NULL) {
+ printk(KERN_ERR "tubirqs[%d] is NULL\n", irqhi);
+ return;
+ }
+ itubp = (*itubpp)[irqlo];
+ if (itubp == NULL) {
+ printk(KERN_ERR "tubirqs[%d][%d] is NULL\n", irqhi, irqlo);
+ return;
+ }
+ if (itubp->irqrc == 0)
+ free_irq(irq, &itubp->devstat);
+ (*itubpp)[irqlo] = NULL;
+}
+
+/*
+ * tubfiniirqs() -- clean up storage in tub_t *(*(*tubirqs)[256])[256]
+ */
+void
+tubfiniirqs(void)
+{
+ int i;
+ tub_t *(*itubpp)[256];
+
+ if (tubirqs != NULL) {
+ for (i = 0; i < 256; i++) {
+ if ((itubpp = (*tubirqs)[i])) {
+ kfree(itubpp);
+ (*tubirqs)[i] = NULL;
+ }
+ }
+ kfree(tubirqs);
+ tubirqs = NULL;
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)