fs3270.c 11.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 *  drivers/s390/char/fs3270.c
 *    IBM/3270 Driver - fullscreen driver.
 *
 *  Author(s):
 *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
 *    Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com>
 *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
 */

#include <linux/bootmem.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/types.h>

#include <asm/ccwdev.h>
#include <asm/cio.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>

#include "raw3270.h"
#include "ctrlchar.h"

struct raw3270_fn fs3270_fn;

struct fs3270 {
	struct raw3270_view view;
30
	struct pid *fs_pid;		/* Pid of controlling program. */
L
Linus Torvalds 已提交
31 32 33
	int read_command;		/* ccw command to use for reads. */
	int write_command;		/* ccw command to use for writes. */
	int attention;			/* Got attention. */
R
Richard Hitt 已提交
34 35 36 37 38
	int active;			/* Fullscreen view is active. */
	struct raw3270_request *init;	/* single init request. */
	wait_queue_head_t wait;		/* Init & attention wait queue. */
	struct idal_buffer *rdbuf;	/* full-screen-deactivate buffer */
	size_t rdbuf_size;		/* size of data returned by RDBUF */
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46
};

static void
fs3270_wake_up(struct raw3270_request *rq, void *data)
{
	wake_up((wait_queue_head_t *) data);
}

R
Richard Hitt 已提交
47 48 49 50 51 52 53 54 55 56
static inline int
fs3270_working(struct fs3270 *fp)
{
	/*
	 * The fullscreen view is in working order if the view
	 * has been activated AND the initial request is finished.
	 */
	return fp->active && raw3270_request_final(fp->init);
}

L
Linus Torvalds 已提交
57 58 59
static int
fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq)
{
R
Richard Hitt 已提交
60
	struct fs3270 *fp;
L
Linus Torvalds 已提交
61 62
	int rc;

R
Richard Hitt 已提交
63
	fp = (struct fs3270 *) view;
L
Linus Torvalds 已提交
64
	rq->callback = fs3270_wake_up;
R
Richard Hitt 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
	rq->callback_data = &fp->wait;

	do {
		if (!fs3270_working(fp)) {
			/* Fullscreen view isn't ready yet. */
			rc = wait_event_interruptible(fp->wait,
						      fs3270_working(fp));
			if (rc != 0)
				break;
		}
		rc = raw3270_start(view, rq);
		if (rc == 0) {
			/* Started sucessfully. Now wait for completion. */
			wait_event(fp->wait, raw3270_request_final(rq));
		}
	} while (rc == -EACCES);
	return rc;
L
Linus Torvalds 已提交
82 83
}

R
Richard Hitt 已提交
84 85 86
/*
 * Switch to the fullscreen view.
 */
L
Linus Torvalds 已提交
87 88 89
static void
fs3270_reset_callback(struct raw3270_request *rq, void *data)
{
R
Richard Hitt 已提交
90 91 92
	struct fs3270 *fp;

	fp = (struct fs3270 *) rq->view;
L
Linus Torvalds 已提交
93
	raw3270_request_reset(rq);
R
Richard Hitt 已提交
94 95 96 97 98 99 100 101 102 103 104
	wake_up(&fp->wait);
}

static void
fs3270_restore_callback(struct raw3270_request *rq, void *data)
{
	struct fs3270 *fp;

	fp = (struct fs3270 *) rq->view;
	if (rq->rc != 0 || rq->rescnt != 0) {
		if (fp->fs_pid)
105
			kill_pid(fp->fs_pid, SIGHUP, 1);
R
Richard Hitt 已提交
106 107 108 109
	}
	fp->rdbuf_size = 0;
	raw3270_request_reset(rq);
	wake_up(&fp->wait);
L
Linus Torvalds 已提交
110 111 112 113 114 115
}

static int
fs3270_activate(struct raw3270_view *view)
{
	struct fs3270 *fp;
R
Richard Hitt 已提交
116 117
	char *cp;
	int rc;
L
Linus Torvalds 已提交
118 119

	fp = (struct fs3270 *) view;
R
Richard Hitt 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

	/* If an old init command is still running just return. */
	if (!raw3270_request_final(fp->init))
		return 0;

	if (fp->rdbuf_size == 0) {
		/* No saved buffer. Just clear the screen. */
		raw3270_request_set_cmd(fp->init, TC_EWRITEA);
		fp->init->callback = fs3270_reset_callback;
	} else {
		/* Restore fullscreen buffer saved by fs3270_deactivate. */
		raw3270_request_set_cmd(fp->init, TC_EWRITEA);
		raw3270_request_set_idal(fp->init, fp->rdbuf);
		fp->init->ccw.count = fp->rdbuf_size;
		cp = fp->rdbuf->data[0];
		cp[0] = TW_KR;
		cp[1] = TO_SBA;
		cp[2] = cp[6];
		cp[3] = cp[7];
		cp[4] = TO_IC;
		cp[5] = TO_SBA;
		cp[6] = 0x40;
		cp[7] = 0x40;
		fp->init->rescnt = 0;
		fp->init->callback = fs3270_restore_callback;
	}
	rc = fp->init->rc = raw3270_start_locked(view, fp->init);
	if (rc)
		fp->init->callback(fp->init, NULL);
	else
		fp->active = 1;
	return rc;
L
Linus Torvalds 已提交
152 153 154 155 156
}

/*
 * Shutdown fullscreen view.
 */
R
Richard Hitt 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
static void
fs3270_save_callback(struct raw3270_request *rq, void *data)
{
	struct fs3270 *fp;

	fp = (struct fs3270 *) rq->view;

	/* Correct idal buffer element 0 address. */
	fp->rdbuf->data[0] -= 5;
	fp->rdbuf->size += 5;

	/*
	 * If the rdbuf command failed or the idal buffer is
	 * to small for the amount of data returned by the
	 * rdbuf command, then we have no choice but to send
	 * a SIGHUP to the application.
	 */
	if (rq->rc != 0 || rq->rescnt == 0) {
		if (fp->fs_pid)
176
			kill_pid(fp->fs_pid, SIGHUP, 1);
R
Richard Hitt 已提交
177 178 179 180 181 182 183
		fp->rdbuf_size = 0;
	} else
		fp->rdbuf_size = fp->rdbuf->size - rq->rescnt;
	raw3270_request_reset(rq);
	wake_up(&fp->wait);
}

L
Linus Torvalds 已提交
184 185 186 187 188 189
static void
fs3270_deactivate(struct raw3270_view *view)
{
	struct fs3270 *fp;

	fp = (struct fs3270 *) view;
R
Richard Hitt 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	fp->active = 0;

	/* If an old init command is still running just return. */
	if (!raw3270_request_final(fp->init))
		return;

	/* Prepare read-buffer request. */
	raw3270_request_set_cmd(fp->init, TC_RDBUF);
	/*
	 * Hackish: skip first 5 bytes of the idal buffer to make
	 * room for the TW_KR/TO_SBA/<address>/<address>/TO_IC sequence
	 * in the activation command.
	 */
	fp->rdbuf->data[0] += 5;
	fp->rdbuf->size -= 5;
	raw3270_request_set_idal(fp->init, fp->rdbuf);
	fp->init->rescnt = 0;
	fp->init->callback = fs3270_save_callback;

	/* Start I/O to read in the 3270 buffer. */
	fp->init->rc = raw3270_start_locked(view, fp->init);
	if (fp->init->rc)
		fp->init->callback(fp->init, NULL);
L
Linus Torvalds 已提交
213 214 215 216 217 218 219 220
}

static int
fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
{
	/* Handle ATTN. Set indication and wake waiters for attention. */
	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
		fp->attention = 1;
R
Richard Hitt 已提交
221
		wake_up(&fp->wait);
L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	}

	if (rq) {
		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
			rq->rc = -EIO;
		else
			/* Normal end. Copy residual count. */
			rq->rescnt = irb->scsw.count;
	}
	return RAW3270_IO_DONE;
}

/*
 * Process reads from fullscreen 3270.
 */
static ssize_t
H
Heiko Carstens 已提交
238
fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off)
L
Linus Torvalds 已提交
239 240 241 242
{
	struct fs3270 *fp;
	struct raw3270_request *rq;
	struct idal_buffer *ib;
R
Richard Hitt 已提交
243
	ssize_t rc;
L
Linus Torvalds 已提交
244 245 246 247 248 249 250
	
	if (count == 0 || count > 65535)
		return -EINVAL;
	fp = filp->private_data;
	if (!fp)
		return -ENODEV;
	ib = idal_buffer_alloc(count, 0);
R
Richard Hitt 已提交
251
	if (IS_ERR(ib))
L
Linus Torvalds 已提交
252 253 254 255 256 257 258
		return -ENOMEM;
	rq = raw3270_request_alloc(0);
	if (!IS_ERR(rq)) {
		if (fp->read_command == 0 && fp->write_command != 0)
			fp->read_command = 6;
		raw3270_request_set_cmd(rq, fp->read_command ? : 2);
		raw3270_request_set_idal(rq, ib);
R
Richard Hitt 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271
		rc = wait_event_interruptible(fp->wait, fp->attention);
		fp->attention = 0;
		if (rc == 0) {
			rc = fs3270_do_io(&fp->view, rq);
			if (rc == 0) {
				count -= rq->rescnt;
				if (idal_buffer_to_user(ib, data, count) != 0)
					rc = -EFAULT;
				else
					rc = count;

			}
		}
L
Linus Torvalds 已提交
272 273 274 275 276 277 278 279 280 281 282
		raw3270_request_free(rq);
	} else
		rc = PTR_ERR(rq);
	idal_buffer_free(ib);
	return rc;
}

/*
 * Process writes to fullscreen 3270.
 */
static ssize_t
H
Heiko Carstens 已提交
283
fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off)
L
Linus Torvalds 已提交
284 285 286 287 288
{
	struct fs3270 *fp;
	struct raw3270_request *rq;
	struct idal_buffer *ib;
	int write_command;
R
Richard Hitt 已提交
289
	ssize_t rc;
L
Linus Torvalds 已提交
290 291 292 293 294

	fp = filp->private_data;
	if (!fp)
		return -ENODEV;
	ib = idal_buffer_alloc(count, 0);
R
Richard Hitt 已提交
295
	if (IS_ERR(ib))
L
Linus Torvalds 已提交
296 297 298 299 300 301 302 303 304 305
		return -ENOMEM;
	rq = raw3270_request_alloc(0);
	if (!IS_ERR(rq)) {
		if (idal_buffer_from_user(ib, data, count) == 0) {
			write_command = fp->write_command ? : 1;
			if (write_command == 5)
				write_command = 13;
			raw3270_request_set_cmd(rq, write_command);
			raw3270_request_set_idal(rq, ib);
			rc = fs3270_do_io(&fp->view, rq);
R
Richard Hitt 已提交
306 307
			if (rc == 0)
				rc = count - rq->rescnt;
L
Linus Torvalds 已提交
308 309 310 311 312 313 314 315 316 317 318 319
		} else
			rc = -EFAULT;
		raw3270_request_free(rq);
	} else
		rc = PTR_ERR(rq);
	idal_buffer_free(ib);
	return rc;
}

/*
 * process ioctl commands for the tube driver
 */
320 321
static long
fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329 330
{
	struct fs3270 *fp;
	struct raw3270_iocb iocb;
	int rc;

	fp = filp->private_data;
	if (!fp)
		return -ENODEV;
	rc = 0;
331
	lock_kernel();
L
Linus Torvalds 已提交
332 333 334 335 336 337 338 339
	switch (cmd) {
	case TUBICMD:
		fp->read_command = arg;
		break;
	case TUBOCMD:
		fp->write_command = arg;
		break;
	case TUBGETI:
H
Heiko Carstens 已提交
340
		rc = put_user(fp->read_command, (char __user *) arg);
L
Linus Torvalds 已提交
341 342
		break;
	case TUBGETO:
H
Heiko Carstens 已提交
343
		rc = put_user(fp->write_command,(char __user *) arg);
L
Linus Torvalds 已提交
344 345 346 347 348 349 350 351
		break;
	case TUBGETMOD:
		iocb.model = fp->view.model;
		iocb.line_cnt = fp->view.rows;
		iocb.col_cnt = fp->view.cols;
		iocb.pf_cnt = 24;
		iocb.re_cnt = 20;
		iocb.map = 0;
H
Heiko Carstens 已提交
352
		if (copy_to_user((char __user *) arg, &iocb,
L
Linus Torvalds 已提交
353 354 355 356
				 sizeof(struct raw3270_iocb)))
			rc = -EFAULT;
		break;
	}
357
	unlock_kernel();
L
Linus Torvalds 已提交
358 359 360 361
	return rc;
}

/*
R
Richard Hitt 已提交
362
 * Allocate fs3270 structure.
L
Linus Torvalds 已提交
363 364 365 366 367 368
 */
static struct fs3270 *
fs3270_alloc_view(void)
{
	struct fs3270 *fp;

369
	fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL);
L
Linus Torvalds 已提交
370 371
	if (!fp)
		return ERR_PTR(-ENOMEM);
R
Richard Hitt 已提交
372 373
	fp->init = raw3270_request_alloc(0);
	if (IS_ERR(fp->init)) {
L
Linus Torvalds 已提交
374 375 376 377 378 379 380
		kfree(fp);
		return ERR_PTR(-ENOMEM);
	}
	return fp;
}

/*
R
Richard Hitt 已提交
381
 * Free fs3270 structure.
L
Linus Torvalds 已提交
382 383 384 385
 */
static void
fs3270_free_view(struct raw3270_view *view)
{
R
Richard Hitt 已提交
386 387 388 389 390 391
	struct fs3270 *fp;

	fp = (struct fs3270 *) view;
	if (fp->rdbuf)
		idal_buffer_free(fp->rdbuf);
	raw3270_request_free(((struct fs3270 *) view)->init);
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
	kfree(view);
}

/*
 * Unlink fs3270 data structure from filp.
 */
static void
fs3270_release(struct raw3270_view *view)
{
}

/* View to a 3270 device. Can be console, tty or fullscreen. */
struct raw3270_fn fs3270_fn = {
	.activate = fs3270_activate,
	.deactivate = fs3270_deactivate,
	.intv = (void *) fs3270_irq,
	.release = fs3270_release,
	.free = fs3270_free_view
};

/*
 * This routine is called whenever a 3270 fullscreen device is opened.
 */
static int
fs3270_open(struct inode *inode, struct file *filp)
{
	struct fs3270 *fp;
R
Richard Hitt 已提交
419
	struct idal_buffer *ib;
L
Linus Torvalds 已提交
420 421 422 423 424
	int minor, rc;

	if (imajor(filp->f_dentry->d_inode) != IBM_FS3270_MAJOR)
		return -ENODEV;
	minor = iminor(filp->f_dentry->d_inode);
R
Richard Hitt 已提交
425 426
	/* Check for minor 0 multiplexer. */
	if (minor == 0) {
427 428 429 430 431
		struct tty_struct *tty;
		mutex_lock(&tty_mutex);
		tty = get_current_tty();
		if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) {
			mutex_unlock(&tty_mutex);
R
Richard Hitt 已提交
432
			return -ENODEV;
433 434 435
		}
		minor = tty->index + RAW3270_FIRSTMINOR;
		mutex_unlock(&tty_mutex);
R
Richard Hitt 已提交
436
	}
L
Linus Torvalds 已提交
437 438 439 440 441 442 443 444 445 446 447
	/* Check if some other program is already using fullscreen mode. */
	fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
	if (!IS_ERR(fp)) {
		raw3270_put_view(&fp->view);
		return -EBUSY;
	}
	/* Allocate fullscreen view structure. */
	fp = fs3270_alloc_view();
	if (IS_ERR(fp))
		return PTR_ERR(fp);

R
Richard Hitt 已提交
448
	init_waitqueue_head(&fp->wait);
449
	fp->fs_pid = get_pid(task_pid(current));
L
Linus Torvalds 已提交
450 451 452 453 454 455
	rc = raw3270_add_view(&fp->view, &fs3270_fn, minor);
	if (rc) {
		fs3270_free_view(&fp->view);
		return rc;
	}

R
Richard Hitt 已提交
456 457 458 459 460 461 462 463 464
	/* Allocate idal-buffer. */
	ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0);
	if (IS_ERR(ib)) {
		raw3270_put_view(&fp->view);
		raw3270_del_view(&fp->view);
		return PTR_ERR(fp);
	}
	fp->rdbuf = ib;

L
Linus Torvalds 已提交
465 466
	rc = raw3270_activate_view(&fp->view);
	if (rc) {
R
Richard Hitt 已提交
467
		raw3270_put_view(&fp->view);
L
Linus Torvalds 已提交
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
		raw3270_del_view(&fp->view);
		return rc;
	}
	filp->private_data = fp;
	return 0;
}

/*
 * This routine is called when the 3270 tty is closed. We wait
 * for the remaining request to be completed. Then we clean up.
 */
static int
fs3270_close(struct inode *inode, struct file *filp)
{
	struct fs3270 *fp;

	fp = filp->private_data;
H
Heiko Carstens 已提交
485
	filp->private_data = NULL;
R
Richard Hitt 已提交
486
	if (fp) {
487 488
		put_pid(fp->fs_pid);
		fp->fs_pid = NULL;
R
Richard Hitt 已提交
489 490
		raw3270_reset(&fp->view);
		raw3270_put_view(&fp->view);
L
Linus Torvalds 已提交
491
		raw3270_del_view(&fp->view);
R
Richard Hitt 已提交
492
	}
L
Linus Torvalds 已提交
493 494 495 496
	return 0;
}

static struct file_operations fs3270_fops = {
497 498 499 500 501 502 503
	.owner		 = THIS_MODULE,		/* owner */
	.read		 = fs3270_read,		/* read */
	.write		 = fs3270_write,	/* write */
	.unlocked_ioctl	 = fs3270_ioctl,	/* ioctl */
	.compat_ioctl	 = fs3270_ioctl,	/* ioctl */
	.open	 	= fs3270_open,		/* open */
	.release 	= fs3270_close,		/* release */
L
Linus Torvalds 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
};

/*
 * 3270 fullscreen driver initialization.
 */
static int __init
fs3270_init(void)
{
	int rc;

	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
	if (rc) {
		printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n",
		       IBM_FS3270_MAJOR, rc);
		return rc;
	}
	return 0;
}

static void __exit
fs3270_exit(void)
{
	unregister_chrdev(IBM_FS3270_MAJOR, "fs3270");
}

MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR);

module_init(fs3270_init);
module_exit(fs3270_exit);