File: /usr/src/linux/drivers/s390/char/tuball.c

1     /*
2      *  IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC
3      *
4      *  tuball.c -- Initialization, termination, irq lookup
5      *
6      *
7      *
8      *
9      *
10      *  Author:  Richard Hitt
11      */
12     #include <linux/config.h>
13     #include "tubio.h"
14     #ifndef MODULE
15     #include <linux/init.h>
16     #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
17     #include <asm/cpcmd.h>
18     #include <linux/bootmem.h>
19     #else
20     #include "../../../../arch/s390/kernel/cpcmd.h"
21     #endif
22     #endif
23     
24     /* Module parameters */
25     int tubdebug;
26     int tubscrolltime = -1;
27     int tubxcorrect = 1;            /* Do correct ebc<->asc tables */
28     #ifdef MODULE
29     MODULE_PARM(tubdebug, "i");
30     MODULE_PARM(tubscrolltime, "i");
31     MODULE_PARM(tubxcorrect, "i");
32     #endif
33     /*
34      * Values for tubdebug and their effects:
35      * 1 - print in hex on console the first 16 bytes received
36      * 2 - print address at which array tubminors is allocated
37      * 4 - attempt to register tty3270_driver
38      */
39     int tubnummins;
40     tub_t *(*tubminors)[TUBMAXMINS];
41     tub_t *(*(*tubirqs)[256])[256];
42     unsigned char tub_ascebc[256];
43     unsigned char tub_ebcasc[256];
44     int tubinitminors(void);
45     void tubfiniminors(void);
46     void tubint(int, void *, struct pt_regs *);
47     
48     /* Lookup-by-irq functions */
49     int tubaddbyirq(tub_t *, int);
50     tub_t *tubfindbyirq(int);
51     void tubdelbyirq(tub_t *, int);
52     void tubfiniirqs(void);
53     
54     extern int fs3270_init(void);
55     extern void fs3270_fini(void);
56     extern int tty3270_init(void);
57     extern void tty3270_fini(void);
58     
59     unsigned char tub_ebcgraf[64] =
60     	{ 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
61     	  0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
62     	  0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
63     	  0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
64     	  0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
65     	  0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
66     	  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
67     	  0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f };
68     
69     int tub3270_init(void);
70     
71     #ifndef MODULE
72     
73     /*
74      * Can't have this driver a module & support console at the same time
75      */
76     #ifdef CONFIG_TN3270_CONSOLE
77     static kdev_t tub3270_con_device(struct console *);
78     static void tub3270_con_unblank(void);
79     static void tub3270_con_write(struct console *, const char *,
80     	unsigned int);
81     
82     static struct console tub3270_con = {
83     	"tub3270",		/* name */
84     	tub3270_con_write,	/* write */
85     	NULL,			/* read */
86     	tub3270_con_device,	/* device */
87     	NULL,			/* wait_key */
88     	tub3270_con_unblank,	/* unblank */
89     	NULL,			/* setup */
90     	CON_PRINTBUFFER,	/* flags */
91     	0,			/* index */
92     	0,			/* cflag */
93     	NULL			/* next */
94     };
95     
96     bcb_t tub3270_con_bcb;			/* Buffer that receives con writes */
97     spinlock_t tub3270_con_bcblock;		/* Lock for the buffer */
98     int tub3270_con_irq = -1;		/* set nonneg by _activate() */
99     tub_t *tub3270_con_tubp;		/* set nonzero by _activate() */
100     struct tty_driver tty3270_con_driver;	/* for /dev/console at 4, 64 */
101     
102     #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
103     int tub3270_con_devno = -1;		/* set by tub3270_con_setup() */
104     __initfunc(void tub3270_con_setup(char *str, int *ints))
105     {
106     	int vdev;
107     
108     	vdev = simple_strtoul(str, 0, 16);
109     	if (vdev >= 0 && vdev < 65536)
110     		tub3270_con_devno = vdev;
111     	return;
112     }
113     
114     __initfunc (long tub3270_con_init(long kmem_start, long kmem_end))
115     {
116     	tub3270_con_bcb.bc_len = 65536;
117     	if (!MACHINE_IS_VM && !MACHINE_IS_P390)
118     		return kmem_start;
119     	tub3270_con_bcb.bc_buf = (void *)kmem_start;
120     	kmem_start += tub3270_con_bcb.bc_len;
121     	register_console(&tub3270_con);
122     	return kmem_start;
123     }
124     #else
125     #define tub3270_con_devno console_device
126     
127     void __init tub3270_con_init(void)
128     {
129     	tub3270_con_bcb.bc_len = 65536;
130     	if (!CONSOLE_IS_3270)
131     		return;
132     	tub3270_con_bcb.bc_buf = (void *)alloc_bootmem_low(
133     		tub3270_con_bcb.bc_len);
134     	register_console(&tub3270_con);
135     }
136     #endif
137     
138     static kdev_t
139     tub3270_con_device(struct console *conp)
140     {
141     	return MKDEV(IBM_TTY3270_MAJOR, conp->index + 1);
142     }
143     
144     static void
145     tub3270_con_unblank(void)
146     {
147     	/* flush everything:  panic has occurred */
148     }
149     
150     int tub3270_con_write_deadlock_ct;
151     int tub3270_con_write_deadlock_bytes;
152     static void
153     tub3270_con_write(struct console *conp,
154     	const char *buf, unsigned int count)
155     {
156     	long flags;
157     	tub_t *tubp = tub3270_con_tubp;
158     	void tty3270_sched_bh(tub_t *);
159     	int rc;
160     	bcb_t obcb;
161     
162     	obcb.bc_buf = (char *)buf;
163     	obcb.bc_len = obcb.bc_cnt = obcb.bc_wr =
164     		MIN(count, tub3270_con_bcb.bc_len);
165     	obcb.bc_rd = 0;
166     
167     	spin_lock_irqsave(&tub3270_con_bcblock, flags);
168     	rc = tub3270_movedata(&obcb, &tub3270_con_bcb, 0);
169     	spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
170     
171     	if (tubp && rc && TUBTRYLOCK(tubp->irq, flags)) {
172     		tty3270_sched_bh(tubp);
173     		TUBUNLOCK(tubp->irq, flags);
174     	}
175     }
176     	
177     int tub3270_con_copy(tub_t *tubp)
178     {
179     	long flags;
180     	int rc;
181     
182     	spin_lock_irqsave(&tub3270_con_bcblock, flags);
183     	rc = tub3270_movedata(&tub3270_con_bcb, &tubp->tty_bcb, 0);
184     	spin_unlock_irqrestore(&tub3270_con_bcblock, flags);
185     	return rc;
186     }
187     #endif /* CONFIG_TN3270_CONSOLE */
188     #else /* If generated as a MODULE */
189     /*
190      * module init:  find tubes; get a major nbr
191      */
192     int
193     init_module(void)
194     {
195     	if (tubnummins != 0) {
196     		printk(KERN_ERR "EEEK!!  Tube driver cobbigling!!\n");
197     		return -1;
198     	}
199     	return tub3270_init();
200     }
201     
202     /*
203      * remove driver:  unregister the major number
204      */
205     void
206     cleanup_module(void)
207     {
208     	fs3270_fini();
209     	tty3270_fini();
210     	tubfiniminors();
211     }
212     #endif /* Not a MODULE or a MODULE */
213     
214     void
215     tub_inc_use_count(void)
216     {
217     	MOD_INC_USE_COUNT;
218     }
219     
220     void
221     tub_dec_use_count(void)
222     {
223     	MOD_DEC_USE_COUNT;
224     }
225     
226     /*
227      * tub3270_init() called by kernel or module initialization
228      */
229     int
230     tub3270_init(void)
231     {
232     	s390_dev_info_t d;
233     	int i, rc;
234     
235     	/*
236     	 * Copy and correct ebcdic - ascii translate tables
237     	 */
238     	memcpy(tub_ascebc, _ascebc, sizeof tub_ascebc);
239     	memcpy(tub_ebcasc, _ebcasc, sizeof tub_ebcasc);
240     	if (tubxcorrect) {
241     		/* correct brackets and circumflex */
242     		tub_ascebc['['] = 0xad;
243     		tub_ascebc[']'] = 0xbd;
244     		tub_ebcasc[0xad] = '[';
245     		tub_ebcasc[0xbd] = ']';
246     		tub_ascebc['^'] = 0xb0;
247     		tub_ebcasc[0x5f] = '^';
248     	}
249     
250     	rc = tubinitminors();
251     	if (rc != 0)
252     		return rc;
253     
254     	if (fs3270_init() || tty3270_init()) {
255     		printk(KERN_ERR "fs3270_init() or tty3270_init() failed\n");
256     		fs3270_fini();
257     		tty3270_fini();
258     		tubfiniminors();
259     		return -1;
260     	}
261     
262     	for (i = get_irq_first(); i >= 0; i = get_irq_next(i)) {
263     		if ((rc = get_dev_info_by_irq(i, &d)))
264     			continue;
265     		if (d.status)
266     			continue;
267     
268     #ifdef CONFIG_TN3270_CONSOLE
269     #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
270     		if (d.sid_data.cu_type == 0x3215 && MACHINE_IS_VM) {
271     			cpcmd("TERM CONMODE 3270", NULL, 0);
272     			d.sid_data.cu_type = 0x3270;
273     		}
274     #else
275     		if (d.sid_data.cu_type == 0x3215 && CONSOLE_IS_3270) {
276     			cpcmd("TERM CONMODE 3270", NULL, 0);
277     			d.sid_data.cu_type = 0x3270;
278     		}
279     #endif /* LINUX_VERSION_CODE */
280     #endif /* CONFIG_TN3270_CONSOLE */
281     		if ((d.sid_data.cu_type & 0xfff0) != 0x3270)
282     			continue;
283     
284     		rc = tubmakemin(i, &d);
285     		if (rc < 0) {
286     			printk(KERN_WARNING 
287     			       "3270 tube registration ran out of memory"
288     			       " after %d devices\n", tubnummins - 1);
289     			break;
290     		} else {
291     			printk(KERN_INFO "3270: %.4x on sch %d, minor %d\n",
292     				d.devno, d.irq, rc);
293     		}
294     	}
295     
296     	return 0;
297     }
298     
299     /*
300      * tub3270_movedata(bcb_t *, bcb_t *) -- Move data stream
301      */
302     int
303     tub3270_movedata(bcb_t *ib, bcb_t *ob, int fromuser)
304     {
305     	int count;			/* Total move length */
306     	int rc;
307     
308     	rc = count = MIN(ib->bc_cnt, ob->bc_len - ob->bc_cnt);
309     	while (count > 0) {
310     		int len1;		/* Contig bytes avail in ib */
311     
312     		if (ib->bc_wr > ib->bc_rd)
313     			len1 = ib->bc_wr - ib->bc_rd;
314     		else
315     			len1 = ib->bc_len - ib->bc_rd;
316     		if (len1 > count)
317     			len1 = count;
318     
319     		while (len1 > 0) {
320     			int len2;	/* Contig space avail in ob */
321     
322     			if (ob->bc_rd > ob->bc_wr)
323     				len2 = ob->bc_rd - ob->bc_wr;
324     			else
325     				len2 = ob->bc_len - ob->bc_wr;
326     			if (len2 > len1)
327     				len2 = len1;
328     			
329     			if (fromuser) {
330     				len2 -= copy_from_user(ob->bc_buf + ob->bc_wr,
331     						       ib->bc_buf + ib->bc_rd,
332     						       len2);
333     				if (len2 == 0) {
334     					if (!rc)
335     						rc = -EFAULT;
336     					break;
337     				}
338     			} else
339     				memcpy(ob->bc_buf + ob->bc_wr,
340     				       ib->bc_buf + ib->bc_rd,
341     				       len2);
342     			
343     			ib->bc_rd += len2;
344     			if (ib->bc_rd == ib->bc_len)
345     				ib->bc_rd = 0;
346     			ib->bc_cnt -= len2;
347     
348     			ob->bc_wr += len2;
349     			if (ob->bc_wr == ob->bc_len)
350     				ob->bc_wr = 0;
351     			ob->bc_cnt += len2;
352     
353     			len1 -= len2;
354     			count -= len2;
355     		}
356     	}
357     	return rc;
358     }
359     
360     /*
361      * receive an interrupt
362      */
363     void
364     tubint(int irq, void *ipp, struct pt_regs *prp)
365     {
366     	devstat_t *dsp = ipp;
367     	tub_t *tubp;
368     
369     	if ((tubp = IRQ2TUB(irq)) && (tubp->intv))
370     		(tubp->intv)(tubp, dsp);
371     }
372     
373     /*
374      * Initialize array of pointers to minor structures tub_t.
375      * Returns 0 or -ENOMEM.
376      */
377     int
378     tubinitminors(void)
379     {
380     	tubminors = (tub_t *(*)[TUBMAXMINS])kmalloc(sizeof *tubminors,
381     		GFP_KERNEL);
382     	if (tubminors == NULL)
383     		return -ENOMEM;
384     	memset(tubminors, 0, sizeof *tubminors);
385     	return 0;
386     }
387     
388     /*
389      * Add a minor 327x device.  Argument is an irq value.
390      *
391      * Point elements of two arrays to the newly created tub_t:
392      * 1. (*tubminors)[minor]
393      * 2. (*(*tubirqs)[irqhi])[irqlo]
394      * The first looks up from minor number at context time; the second
395      * looks up from irq at interrupt time.
396      */
397     int
398     #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
399     tubmakemin(int irq, dev_info_t *dp)
400     #else
401     tubmakemin(int irq, s390_dev_info_t *dp)
402     #endif
403     {
404     	tub_t *tubp;
405     	int minor;
406     	long flags;
407     
408     	if ((minor = ++tubnummins) == TUBMAXMINS)
409     		return -ENODEV;
410     
411     	tubp = kmalloc(sizeof(tub_t), GFP_KERNEL);
412     	if (tubp == NULL) {
413     		return -ENOMEM;
414     	}
415     	if (tubaddbyirq(tubp, irq) != 0) {
416     		kfree(tubp);
417     		return -ENOMEM;
418     	}
419     	memset(tubp, 0, sizeof(tub_t));
420     	tubp->minor = minor;
421     	tubp->irq = irq;
422     	TUBLOCK(tubp->irq, flags);
423     	tubp->devno = dp->devno;
424     	tubp->geom_rows = _GEOM_ROWS;
425     	tubp->geom_cols = _GEOM_COLS;
426     	init_waitqueue_head(&tubp->waitq);
427     
428     	tubp->tty_bcb.bc_len = TTY_OUTPUT_SIZE;
429     	tubp->tty_bcb.bc_buf = (void *)kmalloc(tubp->tty_bcb.bc_len,
430     		GFP_KERNEL|GFP_DMA);
431     	if (tubp->tty_bcb.bc_buf == NULL) {
432     		TUBUNLOCK(tubp->irq, flags);
433     		tubdelbyirq(tubp, irq);
434     		kfree(tubp);
435     		return -ENOMEM;
436     	}
437     	tubp->tty_bcb.bc_cnt = 0;
438     	tubp->tty_bcb.bc_wr = 0;
439     	tubp->tty_bcb.bc_rd = 0;
440     	(*tubminors)[minor] = tubp;
441     
442     #ifdef CONFIG_TN3270_CONSOLE
443     	if (CONSOLE_IS_3270) {
444     		if (tub3270_con_tubp == NULL && 
445     		    tub3270_con_bcb.bc_buf != NULL &&
446     		    (tub3270_con_devno == -1 ||
447     		     tub3270_con_devno == dp->devno)) {
448     			extern void tty3270_int(tub_t *, devstat_t *);
449     			
450     			tub3270_con_devno = dp->devno;
451     			tubp->cmd = TBC_CONOPEN;
452     			tubp->flags |= TUB_OPEN_STET | TUB_INPUT_HACK;
453     			tty3270_size(tubp, &flags);
454     			tty3270_aid_init(tubp);
455     			tty3270_scl_init(tubp);
456     			tub3270_con_irq = tubp->irq;
457     			tub3270_con_tubp = tubp;
458     			tubp->intv = tty3270_int;
459     			tubp->cmd = TBC_UPDSTAT;
460     			tty3270_build(tubp);
461     		}
462     	}
463     #endif /* CONFIG_TN3270_CONSOLE */
464     
465     #ifdef CONFIG_DEVFS_FS
466     	fs3270_devfs_register(tubp);
467     #endif
468     
469     	TUBUNLOCK(tubp->irq, flags);
470     	return minor;
471     }
472     
473     /*
474      * Release array of pointers to minor structures tub_t, but first
475      * release any storage pointed to by them.
476      */
477     void
478     tubfiniminors(void)
479     {
480     	int i;
481     	tub_t **tubpp, *tubp;
482     
483     	if (tubminors == NULL)
484     		return;
485     
486     	for (i = 0; i < TUBMAXMINS; i++) {
487     		tubpp = &(*tubminors)[i];
488     		if ((tubp = *tubpp)) {
489     #ifdef CONFIG_DEVFS_FS
490     			fs3270_devfs_unregister(tubp);
491     #endif
492     			tubdelbyirq(tubp, tubp->irq);
493     			tty3270_rcl_fini(tubp);
494     			kfree(tubp->tty_bcb.bc_buf);
495     			tubp->tty_bcb.bc_buf = NULL;
496     			tubp->ttyscreen = NULL;
497     			kfree(tubp);
498     			*tubpp = NULL;
499     		}
500     	}
501     	kfree(tubminors);
502     	tubminors = NULL;
503     	tubfiniirqs();
504     }
505     
506     /*
507      * tubaddbyirq() -- Add tub_t for irq lookup in tubint()
508      */
509     int
510     tubaddbyirq(tub_t *tubp, int irq)
511     {
512     	int irqhi = (irq >> 8) & 255;
513     	int irqlo = irq & 255;
514     	tub_t *(*itubpp)[256];
515     
516     	/* Allocate array (*tubirqs)[] if first time */
517     	if (tubirqs == NULL) {
518     		tubirqs = (tub_t *(*(*)[256])[256])
519     			kmalloc(sizeof *tubirqs, GFP_KERNEL);
520     		if (tubirqs == NULL)
521     			return -ENOMEM;
522     		memset(tubirqs, 0, sizeof *tubirqs);
523     	}
524     
525     	/* Allocate subarray (*(*tubirqs)[])[] if first use */
526     	if ((itubpp = (*tubirqs)[irqhi]) == NULL) {
527     		itubpp = (tub_t *(*)[256])
528     			kmalloc(sizeof(*itubpp), GFP_KERNEL);
529     		if (itubpp == NULL) {
530     			if (tubnummins == 1) {  /* if first time */
531     				kfree(tubirqs);
532     				tubirqs = NULL;
533     			}
534     			return -ENOMEM;
535     		} else {
536     			memset(itubpp, 0, sizeof(*itubpp));
537     			(*tubirqs)[irqhi] = itubpp;
538     		}
539     	}
540     
541     	/* Request interrupt service */
542     	if ((tubp->irqrc = request_irq(irq, tubint, SA_INTERRUPT,
543     	    "3270 tube driver", &tubp->devstat)) != 0)
544     		return tubp->irqrc;
545     
546     	/* Fill in the proper subarray element */
547     	(*itubpp)[irqlo] = tubp;
548     	return 0;
549     }
550     
551     /*
552      * tubfindbyirq(irq)
553      */
554     tub_t *
555     tubfindbyirq(int irq)
556     {
557     	int irqhi = (irq >> 8) & 255;
558     	int irqlo = irq & 255;
559     	tub_t *tubp;
560     
561     	if (tubirqs == NULL)
562     		return NULL;
563     	if ((*tubirqs)[irqhi] == NULL)
564     		return NULL;
565     	tubp = (*(*tubirqs)[irqhi])[irqlo];
566     	if (tubp->irq == irq)
567     		return tubp;
568     	return NULL;
569     }
570     
571     /*
572      * tubdelbyirq(tub_t*, irq)
573      */
574     void
575     tubdelbyirq(tub_t *tubp, int irq)
576     {
577     	int irqhi = (irq >> 8) & 255;
578     	int irqlo = irq & 255;
579     	tub_t *(*itubpp)[256], *itubp;
580     
581     	if (tubirqs == NULL) {
582     		printk(KERN_ERR "tubirqs is NULL\n");
583     		return;
584     	}
585     	itubpp = (*tubirqs)[irqhi];
586     	if (itubpp == NULL) {
587     		printk(KERN_ERR "tubirqs[%d] is NULL\n", irqhi);
588     		return;
589     	}
590     	itubp = (*itubpp)[irqlo];
591     	if (itubp == NULL) {
592     		printk(KERN_ERR "tubirqs[%d][%d] is NULL\n", irqhi, irqlo);
593     		return;
594     	}
595     	if (itubp->irqrc == 0)
596     		free_irq(irq, &itubp->devstat);
597     	(*itubpp)[irqlo] = NULL;
598     }
599     
600     /*
601      * tubfiniirqs() -- clean up storage in tub_t *(*(*tubirqs)[256])[256]
602      */
603     void
604     tubfiniirqs(void)
605     {
606     	int i;
607     	tub_t *(*itubpp)[256];
608     
609     	if (tubirqs != NULL) {
610     		for (i = 0; i < 256; i++) {
611     			if ((itubpp = (*tubirqs)[i])) {
612     				kfree(itubpp);
613     				(*tubirqs)[i] = NULL;
614     			}
615     		}
616     		kfree(tubirqs);
617     		tubirqs = NULL;
618     	}
619     }
620