xref: /openbmc/linux/drivers/s390/char/raw3270.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
16f05e69eSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * IBM/3270 Driver - core functions.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Author(s):
61da177e4SLinus Torvalds  *   Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
71da177e4SLinus Torvalds  *   Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
84b214a0cSMartin Schwidefsky  *     Copyright IBM Corp. 2003, 2009
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/err.h>
131da177e4SLinus Torvalds #include <linux/init.h>
141da177e4SLinus Torvalds #include <linux/interrupt.h>
151da177e4SLinus Torvalds #include <linux/list.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/wait.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <asm/ccwdev.h>
211da177e4SLinus Torvalds #include <asm/cio.h>
221da177e4SLinus Torvalds #include <asm/ebcdic.h>
230a87c5cfSMichael Holzheu #include <asm/diag.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include "raw3270.h"
261da177e4SLinus Torvalds 
27ed3cb6f0SRichard Hitt #include <linux/major.h>
28ed3cb6f0SRichard Hitt #include <linux/kdev_t.h>
29ed3cb6f0SRichard Hitt #include <linux/device.h>
3014cc3e2bSIngo Molnar #include <linux/mutex.h>
31ed3cb6f0SRichard Hitt 
32c95571e6SMartin Schwidefsky struct class *class3270;
33754f66b5SSven Schnelle EXPORT_SYMBOL(class3270);
34ed3cb6f0SRichard Hitt 
351da177e4SLinus Torvalds /* The main 3270 data structure. */
361da177e4SLinus Torvalds struct raw3270 {
371da177e4SLinus Torvalds 	struct list_head list;
381da177e4SLinus Torvalds 	struct ccw_device *cdev;
391da177e4SLinus Torvalds 	int minor;
401da177e4SLinus Torvalds 
4191621ba7SSven Schnelle 	int model, rows, cols;
4291621ba7SSven Schnelle 	int old_model, old_rows, old_cols;
434d334fd1SMartin Schwidefsky 	unsigned int state;
441da177e4SLinus Torvalds 	unsigned long flags;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	struct list_head req_queue;	/* Request queue. */
471da177e4SLinus Torvalds 	struct list_head view_list;	/* List of available views. */
481da177e4SLinus Torvalds 	struct raw3270_view *view;	/* Active view. */
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	struct timer_list timer;	/* Device timer. */
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	unsigned char *ascebc;		/* ascii -> ebcdic table */
53132fab13SMartin Schwidefsky 
544d334fd1SMartin Schwidefsky 	struct raw3270_view init_view;
554d334fd1SMartin Schwidefsky 	struct raw3270_request init_reset;
564d334fd1SMartin Schwidefsky 	struct raw3270_request init_readpart;
574d334fd1SMartin Schwidefsky 	struct raw3270_request init_readmod;
58132fab13SMartin Schwidefsky 	unsigned char init_data[256];
5991621ba7SSven Schnelle 	struct work_struct resize_work;
601da177e4SLinus Torvalds };
611da177e4SLinus Torvalds 
624d334fd1SMartin Schwidefsky /* raw3270->state */
634d334fd1SMartin Schwidefsky #define RAW3270_STATE_INIT	0	/* Initial state */
644d334fd1SMartin Schwidefsky #define RAW3270_STATE_RESET	1	/* Reset command is pending */
654d334fd1SMartin Schwidefsky #define RAW3270_STATE_W4ATTN	2	/* Wait for attention interrupt */
664d334fd1SMartin Schwidefsky #define RAW3270_STATE_READMOD	3	/* Read partition is pending */
674d334fd1SMartin Schwidefsky #define RAW3270_STATE_READY	4	/* Device is usable by views */
684d334fd1SMartin Schwidefsky 
691da177e4SLinus Torvalds /* raw3270->flags */
701da177e4SLinus Torvalds #define RAW3270_FLAGS_14BITADDR	0	/* 14-bit buffer addresses */
711da177e4SLinus Torvalds #define RAW3270_FLAGS_BUSY	1	/* Device busy, leave it alone */
724d334fd1SMartin Schwidefsky #define RAW3270_FLAGS_CONSOLE	2	/* Device is the console. */
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds /* Semaphore to protect global data of raw3270 (devices, views, etc). */
7514cc3e2bSIngo Molnar static DEFINE_MUTEX(raw3270_mutex);
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds /* List of 3270 devices. */
78c11ca97eSDenis Cheng static LIST_HEAD(raw3270_devices);
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds /*
811da177e4SLinus Torvalds  * Flag to indicate if the driver has been registered. Some operations
821da177e4SLinus Torvalds  * like waiting for the end of i/o need to be done differently as long
831da177e4SLinus Torvalds  * as the kernel is still starting up (console support).
841da177e4SLinus Torvalds  */
851da177e4SLinus Torvalds static int raw3270_registered;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /* Module parameters */
88970ba6acSHeiko Carstens static bool tubxcorrect;
891da177e4SLinus Torvalds module_param(tubxcorrect, bool, 0);
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds  * Wait queue for device init/delete, view delete.
931da177e4SLinus Torvalds  */
941da177e4SLinus Torvalds DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue);
95754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_wait_queue);
961da177e4SLinus Torvalds 
977e36eff1SMartin Schwidefsky static void __raw3270_disconnect(struct raw3270 *rp);
987e36eff1SMartin Schwidefsky 
991da177e4SLinus Torvalds /*
1001da177e4SLinus Torvalds  * Encode array for 12 bit 3270 addresses.
1011da177e4SLinus Torvalds  */
1022b67fc46SHeiko Carstens static unsigned char raw3270_ebcgraf[64] =	{
1031da177e4SLinus Torvalds 	0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
1041da177e4SLinus Torvalds 	0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
1051da177e4SLinus Torvalds 	0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
1061da177e4SLinus Torvalds 	0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
1071da177e4SLinus Torvalds 	0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
1081da177e4SLinus Torvalds 	0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
1091da177e4SLinus Torvalds 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
1101da177e4SLinus Torvalds 	0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
1111da177e4SLinus Torvalds };
1121da177e4SLinus Torvalds 
raw3270_state_ready(struct raw3270 * rp)1134d334fd1SMartin Schwidefsky static inline int raw3270_state_ready(struct raw3270 *rp)
1144d334fd1SMartin Schwidefsky {
1154d334fd1SMartin Schwidefsky 	return rp->state == RAW3270_STATE_READY;
1164d334fd1SMartin Schwidefsky }
1174d334fd1SMartin Schwidefsky 
raw3270_buffer_address(struct raw3270 * rp,char * cp,int x,int y)118f77f936aSSven Schnelle void raw3270_buffer_address(struct raw3270 *rp, char *cp, int x, int y)
1191da177e4SLinus Torvalds {
120f77f936aSSven Schnelle 	int addr;
121f77f936aSSven Schnelle 
122f77f936aSSven Schnelle 	if (x < 0)
123f77f936aSSven Schnelle 		x = max_t(int, 0, rp->view->cols + x);
124f77f936aSSven Schnelle 	if (y < 0)
125f77f936aSSven Schnelle 		y = max_t(int, 0, rp->view->rows + y);
126f77f936aSSven Schnelle 	addr = (y * rp->view->cols) + x;
1271da177e4SLinus Torvalds 	if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) {
1281da177e4SLinus Torvalds 		cp[0] = (addr >> 8) & 0x3f;
1291da177e4SLinus Torvalds 		cp[1] = addr & 0xff;
1301da177e4SLinus Torvalds 	} else {
1311da177e4SLinus Torvalds 		cp[0] = raw3270_ebcgraf[(addr >> 6) & 0x3f];
1321da177e4SLinus Torvalds 		cp[1] = raw3270_ebcgraf[addr & 0x3f];
1331da177e4SLinus Torvalds 	}
1341da177e4SLinus Torvalds }
135754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_buffer_address);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds /*
1381da177e4SLinus Torvalds  * Allocate a new 3270 ccw request
1391da177e4SLinus Torvalds  */
raw3270_request_alloc(size_t size)14013d4999aSSven Schnelle struct raw3270_request *raw3270_request_alloc(size_t size)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	struct raw3270_request *rq;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	/* Allocate request structure */
145ff61744cSSven Schnelle 	rq = kzalloc(sizeof(*rq), GFP_KERNEL | GFP_DMA);
1461da177e4SLinus Torvalds 	if (!rq)
1471da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	/* alloc output buffer. */
1501da177e4SLinus Torvalds 	if (size > 0) {
1511da177e4SLinus Torvalds 		rq->buffer = kmalloc(size, GFP_KERNEL | GFP_DMA);
1521da177e4SLinus Torvalds 		if (!rq->buffer) {
1531da177e4SLinus Torvalds 			kfree(rq);
1541da177e4SLinus Torvalds 			return ERR_PTR(-ENOMEM);
1551da177e4SLinus Torvalds 		}
1561da177e4SLinus Torvalds 	}
1571da177e4SLinus Torvalds 	rq->size = size;
1581da177e4SLinus Torvalds 	INIT_LIST_HEAD(&rq->list);
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	/*
1611da177e4SLinus Torvalds 	 * Setup ccw.
1621da177e4SLinus Torvalds 	 */
1631da177e4SLinus Torvalds 	rq->ccw.cda = __pa(rq->buffer);
1641da177e4SLinus Torvalds 	rq->ccw.flags = CCW_FLAG_SLI;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	return rq;
1671da177e4SLinus Torvalds }
168754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_alloc);
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds /*
1711da177e4SLinus Torvalds  * Free 3270 ccw request
1721da177e4SLinus Torvalds  */
raw3270_request_free(struct raw3270_request * rq)17313d4999aSSven Schnelle void raw3270_request_free(struct raw3270_request *rq)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds 	kfree(rq->buffer);
1761da177e4SLinus Torvalds 	kfree(rq);
1771da177e4SLinus Torvalds }
178754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_free);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds /*
1811da177e4SLinus Torvalds  * Reset request to initial state.
1821da177e4SLinus Torvalds  */
raw3270_request_reset(struct raw3270_request * rq)1837aeeeb92SSven Schnelle int raw3270_request_reset(struct raw3270_request *rq)
1841da177e4SLinus Torvalds {
1857aeeeb92SSven Schnelle 	if (WARN_ON_ONCE(!list_empty(&rq->list)))
1867aeeeb92SSven Schnelle 		return -EBUSY;
1871da177e4SLinus Torvalds 	rq->ccw.cmd_code = 0;
1881da177e4SLinus Torvalds 	rq->ccw.count = 0;
1891da177e4SLinus Torvalds 	rq->ccw.cda = __pa(rq->buffer);
1901da177e4SLinus Torvalds 	rq->ccw.flags = CCW_FLAG_SLI;
1911da177e4SLinus Torvalds 	rq->rescnt = 0;
1921da177e4SLinus Torvalds 	rq->rc = 0;
1937aeeeb92SSven Schnelle 	return 0;
1941da177e4SLinus Torvalds }
195754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_reset);
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds  * Set command code to ccw of a request.
1991da177e4SLinus Torvalds  */
raw3270_request_set_cmd(struct raw3270_request * rq,u8 cmd)20013d4999aSSven Schnelle void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	rq->ccw.cmd_code = cmd;
2031da177e4SLinus Torvalds }
204754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_set_cmd);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds /*
2071da177e4SLinus Torvalds  * Add data fragment to output buffer.
2081da177e4SLinus Torvalds  */
raw3270_request_add_data(struct raw3270_request * rq,void * data,size_t size)20913d4999aSSven Schnelle int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	if (size + rq->ccw.count > rq->size)
2121da177e4SLinus Torvalds 		return -E2BIG;
2131da177e4SLinus Torvalds 	memcpy(rq->buffer + rq->ccw.count, data, size);
2141da177e4SLinus Torvalds 	rq->ccw.count += size;
2151da177e4SLinus Torvalds 	return 0;
2161da177e4SLinus Torvalds }
217754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_add_data);
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds /*
2201da177e4SLinus Torvalds  * Set address/length pair to ccw of a request.
2211da177e4SLinus Torvalds  */
raw3270_request_set_data(struct raw3270_request * rq,void * data,size_t size)22213d4999aSSven Schnelle void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size)
2231da177e4SLinus Torvalds {
2241da177e4SLinus Torvalds 	rq->ccw.cda = __pa(data);
2251da177e4SLinus Torvalds 	rq->ccw.count = size;
2261da177e4SLinus Torvalds }
227754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_set_data);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds /*
2301da177e4SLinus Torvalds  * Set idal buffer to ccw of a request.
2311da177e4SLinus Torvalds  */
raw3270_request_set_idal(struct raw3270_request * rq,struct idal_buffer * ib)23213d4999aSSven Schnelle void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	rq->ccw.cda = __pa(ib->data);
2351da177e4SLinus Torvalds 	rq->ccw.count = ib->size;
2361da177e4SLinus Torvalds 	rq->ccw.flags |= CCW_FLAG_IDA;
2371da177e4SLinus Torvalds }
238754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_request_set_idal);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds /*
2411da177e4SLinus Torvalds  * Add the request to the request queue, try to start it if the
2421da177e4SLinus Torvalds  * 3270 device is idle. Return without waiting for end of i/o.
2431da177e4SLinus Torvalds  */
__raw3270_start(struct raw3270 * rp,struct raw3270_view * view,struct raw3270_request * rq)24413d4999aSSven Schnelle static int __raw3270_start(struct raw3270 *rp, struct raw3270_view *view,
2451da177e4SLinus Torvalds 			   struct raw3270_request *rq)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	rq->view = view;
2481da177e4SLinus Torvalds 	raw3270_get_view(view);
2491da177e4SLinus Torvalds 	if (list_empty(&rp->req_queue) &&
2501da177e4SLinus Torvalds 	    !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
2511da177e4SLinus Torvalds 		/* No other requests are on the queue. Start this one. */
2521da177e4SLinus Torvalds 		rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
2531da177e4SLinus Torvalds 					  (unsigned long)rq, 0, 0);
2541da177e4SLinus Torvalds 		if (rq->rc) {
2551da177e4SLinus Torvalds 			raw3270_put_view(view);
2561da177e4SLinus Torvalds 			return rq->rc;
2571da177e4SLinus Torvalds 		}
2581da177e4SLinus Torvalds 	}
2591da177e4SLinus Torvalds 	list_add_tail(&rq->list, &rp->req_queue);
2601da177e4SLinus Torvalds 	return 0;
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
raw3270_view_active(struct raw3270_view * view)26313d4999aSSven Schnelle int raw3270_view_active(struct raw3270_view *view)
264233faec9SMartin Schwidefsky {
265233faec9SMartin Schwidefsky 	struct raw3270 *rp = view->dev;
266233faec9SMartin Schwidefsky 
2671cf69b7bSVineeth Vijayan 	return rp && rp->view == view;
268233faec9SMartin Schwidefsky }
269233faec9SMartin Schwidefsky 
raw3270_start(struct raw3270_view * view,struct raw3270_request * rq)27013d4999aSSven Schnelle int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)
2711da177e4SLinus Torvalds {
2721da177e4SLinus Torvalds 	unsigned long flags;
2731da177e4SLinus Torvalds 	struct raw3270 *rp;
2741da177e4SLinus Torvalds 	int rc;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
2771da177e4SLinus Torvalds 	rp = view->dev;
2781cf69b7bSVineeth Vijayan 	if (!rp || rp->view != view)
2791da177e4SLinus Torvalds 		rc = -EACCES;
2804d334fd1SMartin Schwidefsky 	else if (!raw3270_state_ready(rp))
2814d334fd1SMartin Schwidefsky 		rc = -EBUSY;
2821da177e4SLinus Torvalds 	else
2831da177e4SLinus Torvalds 		rc =  __raw3270_start(rp, view, rq);
2841da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags);
2851da177e4SLinus Torvalds 	return rc;
2861da177e4SLinus Torvalds }
287754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_start);
2881da177e4SLinus Torvalds 
raw3270_start_request(struct raw3270_view * view,struct raw3270_request * rq,int cmd,void * data,size_t len)289f08e3155SSven Schnelle int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq,
290f08e3155SSven Schnelle 			  int cmd, void *data, size_t len)
291f08e3155SSven Schnelle {
292f08e3155SSven Schnelle 	int rc;
293f08e3155SSven Schnelle 
2947aeeeb92SSven Schnelle 	rc = raw3270_request_reset(rq);
2957aeeeb92SSven Schnelle 	if (rc)
2967aeeeb92SSven Schnelle 		return rc;
297f08e3155SSven Schnelle 	raw3270_request_set_cmd(rq, cmd);
298f08e3155SSven Schnelle 	rc = raw3270_request_add_data(rq, data, len);
299f08e3155SSven Schnelle 	if (rc)
300f08e3155SSven Schnelle 		return rc;
301f08e3155SSven Schnelle 	return raw3270_start(view, rq);
302f08e3155SSven Schnelle }
303754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_start_request);
304f08e3155SSven Schnelle 
raw3270_start_locked(struct raw3270_view * view,struct raw3270_request * rq)30513d4999aSSven Schnelle int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq)
306ed3cb6f0SRichard Hitt {
307ed3cb6f0SRichard Hitt 	struct raw3270 *rp;
308ed3cb6f0SRichard Hitt 	int rc;
309ed3cb6f0SRichard Hitt 
310ed3cb6f0SRichard Hitt 	rp = view->dev;
3111cf69b7bSVineeth Vijayan 	if (!rp || rp->view != view)
312ed3cb6f0SRichard Hitt 		rc = -EACCES;
3134d334fd1SMartin Schwidefsky 	else if (!raw3270_state_ready(rp))
3144d334fd1SMartin Schwidefsky 		rc = -EBUSY;
315ed3cb6f0SRichard Hitt 	else
316ed3cb6f0SRichard Hitt 		rc =  __raw3270_start(rp, view, rq);
317ed3cb6f0SRichard Hitt 	return rc;
318ed3cb6f0SRichard Hitt }
319754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_start_locked);
320ed3cb6f0SRichard Hitt 
raw3270_start_irq(struct raw3270_view * view,struct raw3270_request * rq)32113d4999aSSven Schnelle int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	struct raw3270 *rp;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	rp = view->dev;
3261da177e4SLinus Torvalds 	rq->view = view;
3271da177e4SLinus Torvalds 	raw3270_get_view(view);
3281da177e4SLinus Torvalds 	list_add_tail(&rq->list, &rp->req_queue);
3291da177e4SLinus Torvalds 	return 0;
3301da177e4SLinus Torvalds }
331754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_start_irq);
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds /*
3341da177e4SLinus Torvalds  * 3270 interrupt routine, called from the ccw_device layer
3351da177e4SLinus Torvalds  */
raw3270_irq(struct ccw_device * cdev,unsigned long intparm,struct irb * irb)33613d4999aSSven Schnelle static void raw3270_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
3371da177e4SLinus Torvalds {
3381da177e4SLinus Torvalds 	struct raw3270 *rp;
3391da177e4SLinus Torvalds 	struct raw3270_view *view;
3401da177e4SLinus Torvalds 	struct raw3270_request *rq;
3411da177e4SLinus Torvalds 
342dff59b64SGreg Kroah-Hartman 	rp = dev_get_drvdata(&cdev->dev);
3431da177e4SLinus Torvalds 	if (!rp)
3441da177e4SLinus Torvalds 		return;
3451da177e4SLinus Torvalds 	rq = (struct raw3270_request *)intparm;
3461da177e4SLinus Torvalds 	view = rq ? rq->view : rp->view;
3471da177e4SLinus Torvalds 
3488340ab60SMartin Schwidefsky 	if (!IS_ERR(irb)) {
3491da177e4SLinus Torvalds 		/* Handle CE-DE-UE and subsequent UDE */
3508340ab60SMartin Schwidefsky 		if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END)
3511da177e4SLinus Torvalds 			clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
3528340ab60SMartin Schwidefsky 		if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END |
3538340ab60SMartin Schwidefsky 					    DEV_STAT_DEV_END |
3548340ab60SMartin Schwidefsky 					    DEV_STAT_UNIT_EXCEP))
3558340ab60SMartin Schwidefsky 			set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
3568340ab60SMartin Schwidefsky 		/* Handle disconnected devices */
3578340ab60SMartin Schwidefsky 		if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
3587e36eff1SMartin Schwidefsky 		    (irb->ecw[0] & SNS0_INTERVENTION_REQ)) {
3598340ab60SMartin Schwidefsky 			set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
3607e36eff1SMartin Schwidefsky 			if (rp->state > RAW3270_STATE_RESET)
3617e36eff1SMartin Schwidefsky 				__raw3270_disconnect(rp);
3627e36eff1SMartin Schwidefsky 		}
3638340ab60SMartin Schwidefsky 		/* Call interrupt handler of the view */
3648340ab60SMartin Schwidefsky 		if (view)
3658340ab60SMartin Schwidefsky 			view->fn->intv(view, rq, irb);
3661da177e4SLinus Torvalds 	}
3678340ab60SMartin Schwidefsky 
3688340ab60SMartin Schwidefsky 	if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags))
3698340ab60SMartin Schwidefsky 		/* Device busy, do not start I/O */
3708340ab60SMartin Schwidefsky 		return;
3718340ab60SMartin Schwidefsky 
3727e36eff1SMartin Schwidefsky 	if (rq && !list_empty(&rq->list)) {
3731da177e4SLinus Torvalds 		/* The request completed, remove from queue and do callback. */
3741da177e4SLinus Torvalds 		list_del_init(&rq->list);
3751da177e4SLinus Torvalds 		if (rq->callback)
3761da177e4SLinus Torvalds 			rq->callback(rq, rq->callback_data);
3771da177e4SLinus Torvalds 		/* Do put_device for get_device in raw3270_start. */
3781da177e4SLinus Torvalds 		raw3270_put_view(view);
3791da177e4SLinus Torvalds 	}
3808340ab60SMartin Schwidefsky 
3811da177e4SLinus Torvalds 	/*
3821da177e4SLinus Torvalds 	 * Try to start each request on request queue until one is
3831da177e4SLinus Torvalds 	 * started successful.
3841da177e4SLinus Torvalds 	 */
3851da177e4SLinus Torvalds 	while (!list_empty(&rp->req_queue)) {
3861da177e4SLinus Torvalds 		rq = list_entry(rp->req_queue.next, struct raw3270_request, list);
3871da177e4SLinus Torvalds 		rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
3881da177e4SLinus Torvalds 					  (unsigned long)rq, 0, 0);
3891da177e4SLinus Torvalds 		if (rq->rc == 0)
3901da177e4SLinus Torvalds 			break;
3911da177e4SLinus Torvalds 		/* Start failed. Remove request and do callback. */
3921da177e4SLinus Torvalds 		list_del_init(&rq->list);
3931da177e4SLinus Torvalds 		if (rq->callback)
3941da177e4SLinus Torvalds 			rq->callback(rq, rq->callback_data);
3951da177e4SLinus Torvalds 		/* Do put_device for get_device in raw3270_start. */
3961da177e4SLinus Torvalds 		raw3270_put_view(view);
3971da177e4SLinus Torvalds 	}
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds /*
4014d334fd1SMartin Schwidefsky  * To determine the size of the 3270 device we need to do:
4024d334fd1SMartin Schwidefsky  * 1) send a 'read partition' data stream to the device
4034d334fd1SMartin Schwidefsky  * 2) wait for the attn interrupt that precedes the query reply
4044d334fd1SMartin Schwidefsky  * 3) do a read modified to get the query reply
4054d334fd1SMartin Schwidefsky  * To make things worse we have to cope with intervention
4064d334fd1SMartin Schwidefsky  * required (3270 device switched to 'stand-by') and command
4074d334fd1SMartin Schwidefsky  * rejects (old devices that can't do 'read partition').
4081da177e4SLinus Torvalds  */
4091da177e4SLinus Torvalds struct raw3270_ua {	/* Query Reply structure for Usable Area */
4101da177e4SLinus Torvalds 	struct {	/* Usable Area Query Reply Base */
4111da177e4SLinus Torvalds 		short l;	/* Length of this structured field */
4121da177e4SLinus Torvalds 		char  sfid;	/* 0x81 if Query Reply */
4131da177e4SLinus Torvalds 		char  qcode;	/* 0x81 if Usable Area */
4141da177e4SLinus Torvalds 		char  flags0;
4151da177e4SLinus Torvalds 		char  flags1;
4161da177e4SLinus Torvalds 		short w;	/* Width of usable area */
4171da177e4SLinus Torvalds 		short h;	/* Heigth of usavle area */
4181da177e4SLinus Torvalds 		char  units;	/* 0x00:in; 0x01:mm */
4191da177e4SLinus Torvalds 		int   xr;
4201da177e4SLinus Torvalds 		int   yr;
4211da177e4SLinus Torvalds 		char  aw;
4221da177e4SLinus Torvalds 		char  ah;
4231da177e4SLinus Torvalds 		short buffsz;	/* Character buffer size, bytes */
4241da177e4SLinus Torvalds 		char  xmin;
4251da177e4SLinus Torvalds 		char  ymin;
4261da177e4SLinus Torvalds 		char  xmax;
4271da177e4SLinus Torvalds 		char  ymax;
428562baff5SSven Schnelle 	} __packed uab;
4291da177e4SLinus Torvalds 	struct {	/* Alternate Usable Area Self-Defining Parameter */
4301da177e4SLinus Torvalds 		char  l;	/* Length of this Self-Defining Parm */
4311da177e4SLinus Torvalds 		char  sdpid;	/* 0x02 if Alternate Usable Area */
4321da177e4SLinus Torvalds 		char  res;
4331da177e4SLinus Torvalds 		char  auaid;	/* 0x01 is Id for the A U A */
4341da177e4SLinus Torvalds 		short wauai;	/* Width of AUAi */
4351da177e4SLinus Torvalds 		short hauai;	/* Height of AUAi */
4361da177e4SLinus Torvalds 		char  auaunits;	/* 0x00:in, 0x01:mm */
4371da177e4SLinus Torvalds 		int   auaxr;
4381da177e4SLinus Torvalds 		int   auayr;
4391da177e4SLinus Torvalds 		char  awauai;
4401da177e4SLinus Torvalds 		char  ahauai;
441562baff5SSven Schnelle 	} __packed aua;
442562baff5SSven Schnelle } __packed;
4431da177e4SLinus Torvalds 
raw3270_size_device_vm(struct raw3270 * rp)44413d4999aSSven Schnelle static void raw3270_size_device_vm(struct raw3270 *rp)
4451da177e4SLinus Torvalds {
4461da177e4SLinus Torvalds 	int rc, model;
4479a92fe48SCornelia Huck 	struct ccw_dev_id dev_id;
4484d334fd1SMartin Schwidefsky 	struct diag210 diag_data;
449fbaee746SSven Schnelle 	struct diag8c diag8c_data;
4501da177e4SLinus Torvalds 
4519a92fe48SCornelia Huck 	ccw_device_get_id(rp->cdev, &dev_id);
452fbaee746SSven Schnelle 	rc = diag8c(&diag8c_data, &dev_id);
453fbaee746SSven Schnelle 	if (!rc) {
454fbaee746SSven Schnelle 		rp->model = 2;
455fbaee746SSven Schnelle 		rp->rows = diag8c_data.height;
456fbaee746SSven Schnelle 		rp->cols = diag8c_data.width;
457fbaee746SSven Schnelle 		if (diag8c_data.flags & 1)
458fbaee746SSven Schnelle 			set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags);
459fbaee746SSven Schnelle 		return;
460fbaee746SSven Schnelle 	}
461fbaee746SSven Schnelle 
4624d334fd1SMartin Schwidefsky 	diag_data.vrdcdvno = dev_id.devno;
4634d334fd1SMartin Schwidefsky 	diag_data.vrdclen = sizeof(struct diag210);
4644d334fd1SMartin Schwidefsky 	rc = diag210(&diag_data);
4654d334fd1SMartin Schwidefsky 	model = diag_data.vrdccrmd;
4664d334fd1SMartin Schwidefsky 	/* Use default model 2 if the size could not be detected */
4674d334fd1SMartin Schwidefsky 	if (rc || model < 2 || model > 5)
4684d334fd1SMartin Schwidefsky 		model = 2;
4691da177e4SLinus Torvalds 	switch (model) {
4701da177e4SLinus Torvalds 	case 2:
4711da177e4SLinus Torvalds 		rp->model = model;
4721da177e4SLinus Torvalds 		rp->rows = 24;
4731da177e4SLinus Torvalds 		rp->cols = 80;
4741da177e4SLinus Torvalds 		break;
4751da177e4SLinus Torvalds 	case 3:
4761da177e4SLinus Torvalds 		rp->model = model;
4771da177e4SLinus Torvalds 		rp->rows = 32;
4781da177e4SLinus Torvalds 		rp->cols = 80;
4791da177e4SLinus Torvalds 		break;
4801da177e4SLinus Torvalds 	case 4:
4811da177e4SLinus Torvalds 		rp->model = model;
4821da177e4SLinus Torvalds 		rp->rows = 43;
4831da177e4SLinus Torvalds 		rp->cols = 80;
4841da177e4SLinus Torvalds 		break;
4851da177e4SLinus Torvalds 	case 5:
4861da177e4SLinus Torvalds 		rp->model = model;
4871da177e4SLinus Torvalds 		rp->rows = 27;
4881da177e4SLinus Torvalds 		rp->cols = 132;
4891da177e4SLinus Torvalds 		break;
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
raw3270_size_device(struct raw3270 * rp,char * init_data)493cbb36313SSven Schnelle static void raw3270_size_device(struct raw3270 *rp, char *init_data)
4941da177e4SLinus Torvalds {
4951da177e4SLinus Torvalds 	struct raw3270_ua *uap;
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	/* Got a Query Reply */
498cbb36313SSven Schnelle 	uap = (struct raw3270_ua *)(init_data + 1);
4991da177e4SLinus Torvalds 	/* Paranoia check. */
500cbb36313SSven Schnelle 	if (init_data[0] != 0x88 || uap->uab.qcode != 0x81) {
5014d334fd1SMartin Schwidefsky 		/* Couldn't detect size. Use default model 2. */
5024d334fd1SMartin Schwidefsky 		rp->model = 2;
5034d334fd1SMartin Schwidefsky 		rp->rows = 24;
5044d334fd1SMartin Schwidefsky 		rp->cols = 80;
5054d334fd1SMartin Schwidefsky 		return;
5064d334fd1SMartin Schwidefsky 	}
5071da177e4SLinus Torvalds 	/* Copy rows/columns of default Usable Area */
5081da177e4SLinus Torvalds 	rp->rows = uap->uab.h;
5091da177e4SLinus Torvalds 	rp->cols = uap->uab.w;
5101da177e4SLinus Torvalds 	/* Check for 14 bit addressing */
5111da177e4SLinus Torvalds 	if ((uap->uab.flags0 & 0x0d) == 0x01)
5121da177e4SLinus Torvalds 		set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags);
5131da177e4SLinus Torvalds 	/* Check for Alternate Usable Area */
5141da177e4SLinus Torvalds 	if (uap->uab.l == sizeof(struct raw3270_ua) &&
5151da177e4SLinus Torvalds 	    uap->aua.sdpid == 0x02) {
5161da177e4SLinus Torvalds 		rp->rows = uap->aua.hauai;
5171da177e4SLinus Torvalds 		rp->cols = uap->aua.wauai;
5181da177e4SLinus Torvalds 	}
5191da177e4SLinus Torvalds 	/* Try to find a model. */
5201da177e4SLinus Torvalds 	rp->model = 0;
5211da177e4SLinus Torvalds 	if (rp->rows == 24 && rp->cols == 80)
5221da177e4SLinus Torvalds 		rp->model = 2;
5231da177e4SLinus Torvalds 	if (rp->rows == 32 && rp->cols == 80)
5241da177e4SLinus Torvalds 		rp->model = 3;
5251da177e4SLinus Torvalds 	if (rp->rows == 43 && rp->cols == 80)
5261da177e4SLinus Torvalds 		rp->model = 4;
5271da177e4SLinus Torvalds 	if (rp->rows == 27 && rp->cols == 132)
5281da177e4SLinus Torvalds 		rp->model = 5;
5291da177e4SLinus Torvalds }
5304d334fd1SMartin Schwidefsky 
raw3270_resize_work(struct work_struct * work)53191621ba7SSven Schnelle static void raw3270_resize_work(struct work_struct *work)
5324d334fd1SMartin Schwidefsky {
53391621ba7SSven Schnelle 	struct raw3270 *rp = container_of(work, struct raw3270, resize_work);
5344d334fd1SMartin Schwidefsky 	struct raw3270_view *view;
5354d334fd1SMartin Schwidefsky 
5364d334fd1SMartin Schwidefsky 	/* Notify views about new size */
53791621ba7SSven Schnelle 	list_for_each_entry(view, &rp->view_list, list) {
5384d334fd1SMartin Schwidefsky 		if (view->fn->resize)
53991621ba7SSven Schnelle 			view->fn->resize(view, rp->model, rp->rows, rp->cols,
54091621ba7SSven Schnelle 					 rp->old_model, rp->old_rows, rp->old_cols);
54191621ba7SSven Schnelle 	}
54291621ba7SSven Schnelle 	rp->old_cols = rp->cols;
54391621ba7SSven Schnelle 	rp->old_rows = rp->rows;
54491621ba7SSven Schnelle 	rp->old_model = rp->model;
5454d334fd1SMartin Schwidefsky 	/* Setup processing done, now activate a view */
5464d334fd1SMartin Schwidefsky 	list_for_each_entry(view, &rp->view_list, list) {
5474d334fd1SMartin Schwidefsky 		rp->view = view;
5484d334fd1SMartin Schwidefsky 		if (view->fn->activate(view) == 0)
5494d334fd1SMartin Schwidefsky 			break;
5504d334fd1SMartin Schwidefsky 		rp->view = NULL;
5514d334fd1SMartin Schwidefsky 	}
5524d334fd1SMartin Schwidefsky }
5534d334fd1SMartin Schwidefsky 
raw3270_size_device_done(struct raw3270 * rp)55491621ba7SSven Schnelle static void raw3270_size_device_done(struct raw3270 *rp)
55591621ba7SSven Schnelle {
55691621ba7SSven Schnelle 	rp->view = NULL;
55791621ba7SSven Schnelle 	rp->state = RAW3270_STATE_READY;
55891621ba7SSven Schnelle 	schedule_work(&rp->resize_work);
55991621ba7SSven Schnelle }
56091621ba7SSven Schnelle 
raw3270_read_modified_cb(struct raw3270_request * rq,void * data)561cbb36313SSven Schnelle void raw3270_read_modified_cb(struct raw3270_request *rq, void *data)
5624d334fd1SMartin Schwidefsky {
5634d334fd1SMartin Schwidefsky 	struct raw3270 *rp = rq->view->dev;
564ff61744cSSven Schnelle 
565cbb36313SSven Schnelle 	raw3270_size_device(rp, data);
5664d334fd1SMartin Schwidefsky 	raw3270_size_device_done(rp);
5674d334fd1SMartin Schwidefsky }
568754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_read_modified_cb);
5694d334fd1SMartin Schwidefsky 
raw3270_read_modified(struct raw3270 * rp)57013d4999aSSven Schnelle static void raw3270_read_modified(struct raw3270 *rp)
5714d334fd1SMartin Schwidefsky {
5724d334fd1SMartin Schwidefsky 	if (rp->state != RAW3270_STATE_W4ATTN)
5734d334fd1SMartin Schwidefsky 		return;
5744d334fd1SMartin Schwidefsky 	/* Use 'read modified' to get the result of a read partition. */
5754d334fd1SMartin Schwidefsky 	memset(&rp->init_readmod, 0, sizeof(rp->init_readmod));
5764d334fd1SMartin Schwidefsky 	memset(&rp->init_data, 0, sizeof(rp->init_data));
5774d334fd1SMartin Schwidefsky 	rp->init_readmod.ccw.cmd_code = TC_READMOD;
5784d334fd1SMartin Schwidefsky 	rp->init_readmod.ccw.flags = CCW_FLAG_SLI;
5794d334fd1SMartin Schwidefsky 	rp->init_readmod.ccw.count = sizeof(rp->init_data);
5804d334fd1SMartin Schwidefsky 	rp->init_readmod.ccw.cda = (__u32)__pa(rp->init_data);
5814d334fd1SMartin Schwidefsky 	rp->init_readmod.callback = raw3270_read_modified_cb;
582cbb36313SSven Schnelle 	rp->init_readmod.callback_data = rp->init_data;
5834d334fd1SMartin Schwidefsky 	rp->state = RAW3270_STATE_READMOD;
5844d334fd1SMartin Schwidefsky 	raw3270_start_irq(&rp->init_view, &rp->init_readmod);
5854d334fd1SMartin Schwidefsky }
5864d334fd1SMartin Schwidefsky 
raw3270_writesf_readpart(struct raw3270 * rp)58713d4999aSSven Schnelle static void raw3270_writesf_readpart(struct raw3270 *rp)
5884d334fd1SMartin Schwidefsky {
589ff61744cSSven Schnelle 	static const unsigned char wbuf[] = {
590ff61744cSSven Schnelle 		0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81
591ff61744cSSven Schnelle 	};
5924d334fd1SMartin Schwidefsky 
5934d334fd1SMartin Schwidefsky 	/* Store 'read partition' data stream to init_data */
5944d334fd1SMartin Schwidefsky 	memset(&rp->init_readpart, 0, sizeof(rp->init_readpart));
5954d334fd1SMartin Schwidefsky 	memset(&rp->init_data, 0, sizeof(rp->init_data));
5964d334fd1SMartin Schwidefsky 	memcpy(&rp->init_data, wbuf, sizeof(wbuf));
5974d334fd1SMartin Schwidefsky 	rp->init_readpart.ccw.cmd_code = TC_WRITESF;
5984d334fd1SMartin Schwidefsky 	rp->init_readpart.ccw.flags = CCW_FLAG_SLI;
5994d334fd1SMartin Schwidefsky 	rp->init_readpart.ccw.count = sizeof(wbuf);
6004d334fd1SMartin Schwidefsky 	rp->init_readpart.ccw.cda = (__u32)__pa(&rp->init_data);
6014d334fd1SMartin Schwidefsky 	rp->state = RAW3270_STATE_W4ATTN;
6024d334fd1SMartin Schwidefsky 	raw3270_start_irq(&rp->init_view, &rp->init_readpart);
6034d334fd1SMartin Schwidefsky }
6044d334fd1SMartin Schwidefsky 
6054d334fd1SMartin Schwidefsky /*
6064d334fd1SMartin Schwidefsky  * Device reset
6074d334fd1SMartin Schwidefsky  */
raw3270_reset_device_cb(struct raw3270_request * rq,void * data)60813d4999aSSven Schnelle static void raw3270_reset_device_cb(struct raw3270_request *rq, void *data)
6094d334fd1SMartin Schwidefsky {
6104d334fd1SMartin Schwidefsky 	struct raw3270 *rp = rq->view->dev;
6114d334fd1SMartin Schwidefsky 
6124d334fd1SMartin Schwidefsky 	if (rp->state != RAW3270_STATE_RESET)
6134d334fd1SMartin Schwidefsky 		return;
614dc3ac5ffSMartin Schwidefsky 	if (rq->rc) {
6154d334fd1SMartin Schwidefsky 		/* Reset command failed. */
6164d334fd1SMartin Schwidefsky 		rp->state = RAW3270_STATE_INIT;
61701a7cfa2SMartin Schwidefsky 	} else if (MACHINE_IS_VM) {
6184d334fd1SMartin Schwidefsky 		raw3270_size_device_vm(rp);
6194d334fd1SMartin Schwidefsky 		raw3270_size_device_done(rp);
620ff61744cSSven Schnelle 	} else {
6214d334fd1SMartin Schwidefsky 		raw3270_writesf_readpart(rp);
622ff61744cSSven Schnelle 	}
623bd1cb5deSMartin Schwidefsky 	memset(&rp->init_reset, 0, sizeof(rp->init_reset));
6244d334fd1SMartin Schwidefsky }
6254d334fd1SMartin Schwidefsky 
__raw3270_reset_device(struct raw3270 * rp)62613d4999aSSven Schnelle static int __raw3270_reset_device(struct raw3270 *rp)
6274d334fd1SMartin Schwidefsky {
6284d334fd1SMartin Schwidefsky 	int rc;
6294d334fd1SMartin Schwidefsky 
630bd1cb5deSMartin Schwidefsky 	/* Check if reset is already pending */
631bd1cb5deSMartin Schwidefsky 	if (rp->init_reset.view)
632bd1cb5deSMartin Schwidefsky 		return -EBUSY;
6334d334fd1SMartin Schwidefsky 	/* Store reset data stream to init_data/init_reset */
6344d334fd1SMartin Schwidefsky 	rp->init_data[0] = TW_KR;
6354d334fd1SMartin Schwidefsky 	rp->init_reset.ccw.cmd_code = TC_EWRITEA;
6364d334fd1SMartin Schwidefsky 	rp->init_reset.ccw.flags = CCW_FLAG_SLI;
6374d334fd1SMartin Schwidefsky 	rp->init_reset.ccw.count = 1;
6384d334fd1SMartin Schwidefsky 	rp->init_reset.ccw.cda = (__u32)__pa(rp->init_data);
6394d334fd1SMartin Schwidefsky 	rp->init_reset.callback = raw3270_reset_device_cb;
6404d334fd1SMartin Schwidefsky 	rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset);
6414d334fd1SMartin Schwidefsky 	if (rc == 0 && rp->state == RAW3270_STATE_INIT)
6424d334fd1SMartin Schwidefsky 		rp->state = RAW3270_STATE_RESET;
6431da177e4SLinus Torvalds 	return rc;
6441da177e4SLinus Torvalds }
6451da177e4SLinus Torvalds 
raw3270_reset_device(struct raw3270 * rp)64613d4999aSSven Schnelle static int raw3270_reset_device(struct raw3270 *rp)
6471da177e4SLinus Torvalds {
6484d334fd1SMartin Schwidefsky 	unsigned long flags;
6491da177e4SLinus Torvalds 	int rc;
6501da177e4SLinus Torvalds 
6514d334fd1SMartin Schwidefsky 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
6524d334fd1SMartin Schwidefsky 	rc = __raw3270_reset_device(rp);
6534d334fd1SMartin Schwidefsky 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
6541da177e4SLinus Torvalds 	return rc;
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
raw3270_reset(struct raw3270_view * view)65713d4999aSSven Schnelle int raw3270_reset(struct raw3270_view *view)
658ed3cb6f0SRichard Hitt {
659ed3cb6f0SRichard Hitt 	struct raw3270 *rp;
660ed3cb6f0SRichard Hitt 	int rc;
661ed3cb6f0SRichard Hitt 
662ed3cb6f0SRichard Hitt 	rp = view->dev;
6631cf69b7bSVineeth Vijayan 	if (!rp || rp->view != view)
664ed3cb6f0SRichard Hitt 		rc = -EACCES;
6654d334fd1SMartin Schwidefsky 	else if (!raw3270_state_ready(rp))
6664d334fd1SMartin Schwidefsky 		rc = -EBUSY;
667ed3cb6f0SRichard Hitt 	else
668ed3cb6f0SRichard Hitt 		rc = raw3270_reset_device(view->dev);
669ed3cb6f0SRichard Hitt 	return rc;
670ed3cb6f0SRichard Hitt }
671754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_reset);
672ed3cb6f0SRichard Hitt 
__raw3270_disconnect(struct raw3270 * rp)67313d4999aSSven Schnelle static void __raw3270_disconnect(struct raw3270 *rp)
6747e36eff1SMartin Schwidefsky {
6757e36eff1SMartin Schwidefsky 	struct raw3270_request *rq;
6767e36eff1SMartin Schwidefsky 	struct raw3270_view *view;
6777e36eff1SMartin Schwidefsky 
6787e36eff1SMartin Schwidefsky 	rp->state = RAW3270_STATE_INIT;
6797e36eff1SMartin Schwidefsky 	rp->view = &rp->init_view;
6807e36eff1SMartin Schwidefsky 	/* Cancel all queued requests */
6817e36eff1SMartin Schwidefsky 	while (!list_empty(&rp->req_queue)) {
6827e36eff1SMartin Schwidefsky 		rq = list_entry(rp->req_queue.next, struct raw3270_request, list);
6837e36eff1SMartin Schwidefsky 		view = rq->view;
6847e36eff1SMartin Schwidefsky 		rq->rc = -EACCES;
6857e36eff1SMartin Schwidefsky 		list_del_init(&rq->list);
6867e36eff1SMartin Schwidefsky 		if (rq->callback)
6877e36eff1SMartin Schwidefsky 			rq->callback(rq, rq->callback_data);
6887e36eff1SMartin Schwidefsky 		raw3270_put_view(view);
6897e36eff1SMartin Schwidefsky 	}
6907e36eff1SMartin Schwidefsky 	/* Start from scratch */
6917e36eff1SMartin Schwidefsky 	__raw3270_reset_device(rp);
6927e36eff1SMartin Schwidefsky }
6937e36eff1SMartin Schwidefsky 
raw3270_init_irq(struct raw3270_view * view,struct raw3270_request * rq,struct irb * irb)69413d4999aSSven Schnelle static void raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
6954d334fd1SMartin Schwidefsky 			     struct irb *irb)
6964d334fd1SMartin Schwidefsky {
6974d334fd1SMartin Schwidefsky 	struct raw3270 *rp;
6984d334fd1SMartin Schwidefsky 
6994d334fd1SMartin Schwidefsky 	if (rq) {
7004d334fd1SMartin Schwidefsky 		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
7014d334fd1SMartin Schwidefsky 			if (irb->ecw[0] & SNS0_CMD_REJECT)
7024d334fd1SMartin Schwidefsky 				rq->rc = -EOPNOTSUPP;
7034d334fd1SMartin Schwidefsky 			else
7044d334fd1SMartin Schwidefsky 				rq->rc = -EIO;
7054d334fd1SMartin Schwidefsky 		}
7064d334fd1SMartin Schwidefsky 	}
7074d334fd1SMartin Schwidefsky 	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
7084d334fd1SMartin Schwidefsky 		/* Queue read modified after attention interrupt */
7094d334fd1SMartin Schwidefsky 		rp = view->dev;
7104d334fd1SMartin Schwidefsky 		raw3270_read_modified(rp);
7114d334fd1SMartin Schwidefsky 	}
7124d334fd1SMartin Schwidefsky }
7134d334fd1SMartin Schwidefsky 
7144d334fd1SMartin Schwidefsky static struct raw3270_fn raw3270_init_fn = {
7154d334fd1SMartin Schwidefsky 	.intv = raw3270_init_irq
7164d334fd1SMartin Schwidefsky };
7174d334fd1SMartin Schwidefsky 
7181da177e4SLinus Torvalds /*
7191da177e4SLinus Torvalds  * Setup new 3270 device.
7201da177e4SLinus Torvalds  */
raw3270_setup_device(struct ccw_device * cdev,struct raw3270 * rp,char * ascebc)72113d4999aSSven Schnelle static int raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp,
72213d4999aSSven Schnelle 				char *ascebc)
7231da177e4SLinus Torvalds {
7241da177e4SLinus Torvalds 	struct list_head *l;
7251da177e4SLinus Torvalds 	struct raw3270 *tmp;
7261da177e4SLinus Torvalds 	int minor;
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds 	memset(rp, 0, sizeof(struct raw3270));
7291da177e4SLinus Torvalds 	/* Copy ebcdic -> ascii translation table. */
7301da177e4SLinus Torvalds 	memcpy(ascebc, _ascebc, 256);
7311da177e4SLinus Torvalds 	if (tubxcorrect) {
7321da177e4SLinus Torvalds 		/* correct brackets and circumflex */
7331da177e4SLinus Torvalds 		ascebc['['] = 0xad;
7341da177e4SLinus Torvalds 		ascebc[']'] = 0xbd;
7351da177e4SLinus Torvalds 		ascebc['^'] = 0xb0;
7361da177e4SLinus Torvalds 	}
7371da177e4SLinus Torvalds 	rp->ascebc = ascebc;
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 	/* Set defaults. */
7401da177e4SLinus Torvalds 	rp->rows = 24;
7411da177e4SLinus Torvalds 	rp->cols = 80;
74291621ba7SSven Schnelle 	rp->old_rows = rp->rows;
74391621ba7SSven Schnelle 	rp->old_cols = rp->cols;
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	INIT_LIST_HEAD(&rp->req_queue);
7461da177e4SLinus Torvalds 	INIT_LIST_HEAD(&rp->view_list);
7471da177e4SLinus Torvalds 
7484d334fd1SMartin Schwidefsky 	rp->init_view.dev = rp;
7494d334fd1SMartin Schwidefsky 	rp->init_view.fn = &raw3270_init_fn;
7504d334fd1SMartin Schwidefsky 	rp->view = &rp->init_view;
75191621ba7SSven Schnelle 	INIT_WORK(&rp->resize_work, raw3270_resize_work);
7524d334fd1SMartin Schwidefsky 
7531da177e4SLinus Torvalds 	/*
7541da177e4SLinus Torvalds 	 * Add device to list and find the smallest unused minor
755ed3cb6f0SRichard Hitt 	 * number for it. Note: there is no device with minor 0,
756ed3cb6f0SRichard Hitt 	 * see special case for fs3270.c:fs3270_open().
7571da177e4SLinus Torvalds 	 */
75814cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
7591da177e4SLinus Torvalds 	/* Keep the list sorted. */
760ed3cb6f0SRichard Hitt 	minor = RAW3270_FIRSTMINOR;
7611da177e4SLinus Torvalds 	rp->minor = -1;
7621da177e4SLinus Torvalds 	list_for_each(l, &raw3270_devices) {
7631da177e4SLinus Torvalds 		tmp = list_entry(l, struct raw3270, list);
7641da177e4SLinus Torvalds 		if (tmp->minor > minor) {
7651da177e4SLinus Torvalds 			rp->minor = minor;
7661da177e4SLinus Torvalds 			__list_add(&rp->list, l->prev, l);
7671da177e4SLinus Torvalds 			break;
7681da177e4SLinus Torvalds 		}
7691da177e4SLinus Torvalds 		minor++;
7701da177e4SLinus Torvalds 	}
771ed3cb6f0SRichard Hitt 	if (rp->minor == -1 && minor < RAW3270_MAXDEVS + RAW3270_FIRSTMINOR) {
7721da177e4SLinus Torvalds 		rp->minor = minor;
7731da177e4SLinus Torvalds 		list_add_tail(&rp->list, &raw3270_devices);
7741da177e4SLinus Torvalds 	}
77514cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
7761da177e4SLinus Torvalds 	/* No free minor number? Then give up. */
7771da177e4SLinus Torvalds 	if (rp->minor == -1)
7781da177e4SLinus Torvalds 		return -EUSERS;
7791da177e4SLinus Torvalds 	rp->cdev = cdev;
780dff59b64SGreg Kroah-Hartman 	dev_set_drvdata(&cdev->dev, rp);
7811da177e4SLinus Torvalds 	cdev->handler = raw3270_irq;
7821da177e4SLinus Torvalds 	return 0;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds #ifdef CONFIG_TN3270_CONSOLE
7862253e8d7SSebastian Ott /* Tentative definition - see below for actual definition. */
7872253e8d7SSebastian Ott static struct ccw_driver raw3270_ccw_driver;
7882253e8d7SSebastian Ott 
raw3270_state_final(struct raw3270 * rp)789e036ea81SHeiko Carstens static inline int raw3270_state_final(struct raw3270 *rp)
790e036ea81SHeiko Carstens {
791e036ea81SHeiko Carstens 	return rp->state == RAW3270_STATE_INIT ||
792e036ea81SHeiko Carstens 		rp->state == RAW3270_STATE_READY;
793e036ea81SHeiko Carstens }
794e036ea81SHeiko Carstens 
7951da177e4SLinus Torvalds /*
7961da177e4SLinus Torvalds  * Setup 3270 device configured as console.
7971da177e4SLinus Torvalds  */
raw3270_setup_console(void)7982253e8d7SSebastian Ott struct raw3270 __init *raw3270_setup_console(void)
7991da177e4SLinus Torvalds {
8002253e8d7SSebastian Ott 	struct ccw_device *cdev;
8014d334fd1SMartin Schwidefsky 	unsigned long flags;
8021da177e4SLinus Torvalds 	struct raw3270 *rp;
8031da177e4SLinus Torvalds 	char *ascebc;
8041da177e4SLinus Torvalds 	int rc;
8051da177e4SLinus Torvalds 
8061e532096SSebastian Ott 	cdev = ccw_device_create_console(&raw3270_ccw_driver);
8072253e8d7SSebastian Ott 	if (IS_ERR(cdev))
8082253e8d7SSebastian Ott 		return ERR_CAST(cdev);
8092253e8d7SSebastian Ott 
810ff61744cSSven Schnelle 	rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA);
81133403dcfSHeiko Carstens 	ascebc = kzalloc(256, GFP_KERNEL);
8121da177e4SLinus Torvalds 	rc = raw3270_setup_device(cdev, rp, ascebc);
8131da177e4SLinus Torvalds 	if (rc)
8141da177e4SLinus Torvalds 		return ERR_PTR(rc);
8151da177e4SLinus Torvalds 	set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags);
8161e532096SSebastian Ott 
8171e532096SSebastian Ott 	rc = ccw_device_enable_console(cdev);
8181e532096SSebastian Ott 	if (rc) {
8191e532096SSebastian Ott 		ccw_device_destroy_console(cdev);
8201e532096SSebastian Ott 		return ERR_PTR(rc);
8211e532096SSebastian Ott 	}
8221e532096SSebastian Ott 
8234d334fd1SMartin Schwidefsky 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
8244d334fd1SMartin Schwidefsky 	do {
8254d334fd1SMartin Schwidefsky 		__raw3270_reset_device(rp);
8264d334fd1SMartin Schwidefsky 		while (!raw3270_state_final(rp)) {
827188561a4SSebastian Ott 			ccw_device_wait_idle(rp->cdev);
8284d334fd1SMartin Schwidefsky 			barrier();
8294d334fd1SMartin Schwidefsky 		}
8304d334fd1SMartin Schwidefsky 	} while (rp->state != RAW3270_STATE_READY);
8314d334fd1SMartin Schwidefsky 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
8321da177e4SLinus Torvalds 	return rp;
8331da177e4SLinus Torvalds }
8341da177e4SLinus Torvalds 
raw3270_wait_cons_dev(struct raw3270 * rp)83513d4999aSSven Schnelle void raw3270_wait_cons_dev(struct raw3270 *rp)
8361da177e4SLinus Torvalds {
8371da177e4SLinus Torvalds 	unsigned long flags;
8381da177e4SLinus Torvalds 
8391da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
840188561a4SSebastian Ott 	ccw_device_wait_idle(rp->cdev);
8411da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds #endif
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds /*
8471da177e4SLinus Torvalds  * Create a 3270 device structure.
8481da177e4SLinus Torvalds  */
raw3270_create_device(struct ccw_device * cdev)84913d4999aSSven Schnelle static struct raw3270 *raw3270_create_device(struct ccw_device *cdev)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct raw3270 *rp;
8521da177e4SLinus Torvalds 	char *ascebc;
8531da177e4SLinus Torvalds 	int rc;
8541da177e4SLinus Torvalds 
855ff61744cSSven Schnelle 	rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA);
8561da177e4SLinus Torvalds 	if (!rp)
8571da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
8581da177e4SLinus Torvalds 	ascebc = kmalloc(256, GFP_KERNEL);
8591da177e4SLinus Torvalds 	if (!ascebc) {
8601da177e4SLinus Torvalds 		kfree(rp);
8611da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
8621da177e4SLinus Torvalds 	}
8631da177e4SLinus Torvalds 	rc = raw3270_setup_device(cdev, rp, ascebc);
8641da177e4SLinus Torvalds 	if (rc) {
8651da177e4SLinus Torvalds 		kfree(rp->ascebc);
8661da177e4SLinus Torvalds 		kfree(rp);
8671da177e4SLinus Torvalds 		rp = ERR_PTR(rc);
8681da177e4SLinus Torvalds 	}
8691da177e4SLinus Torvalds 	/* Get reference to ccw_device structure. */
8701da177e4SLinus Torvalds 	get_device(&cdev->dev);
8711da177e4SLinus Torvalds 	return rp;
8721da177e4SLinus Torvalds }
8731da177e4SLinus Torvalds 
8741da177e4SLinus Torvalds /*
8754ae46db9SGuilherme G. Piccoli  * This helper just validates that it is safe to activate a
8764ae46db9SGuilherme G. Piccoli  * view in the panic() context, due to locking restrictions.
8774ae46db9SGuilherme G. Piccoli  */
raw3270_view_lock_unavailable(struct raw3270_view * view)8784ae46db9SGuilherme G. Piccoli int raw3270_view_lock_unavailable(struct raw3270_view *view)
8794ae46db9SGuilherme G. Piccoli {
8804ae46db9SGuilherme G. Piccoli 	struct raw3270 *rp = view->dev;
8814ae46db9SGuilherme G. Piccoli 
8824ae46db9SGuilherme G. Piccoli 	if (!rp)
8834ae46db9SGuilherme G. Piccoli 		return -ENODEV;
8844ae46db9SGuilherme G. Piccoli 	if (spin_is_locked(get_ccwdev_lock(rp->cdev)))
8854ae46db9SGuilherme G. Piccoli 		return -EBUSY;
8864ae46db9SGuilherme G. Piccoli 	return 0;
8874ae46db9SGuilherme G. Piccoli }
8884ae46db9SGuilherme G. Piccoli 
raw3270_assign_activate_view(struct raw3270 * rp,struct raw3270_view * view)889420105f4SSven Schnelle static int raw3270_assign_activate_view(struct raw3270 *rp, struct raw3270_view *view)
890420105f4SSven Schnelle {
891420105f4SSven Schnelle 	rp->view = view;
892420105f4SSven Schnelle 	return view->fn->activate(view);
893420105f4SSven Schnelle }
894420105f4SSven Schnelle 
__raw3270_activate_view(struct raw3270 * rp,struct raw3270_view * view)895420105f4SSven Schnelle static int __raw3270_activate_view(struct raw3270 *rp, struct raw3270_view *view)
896420105f4SSven Schnelle {
897420105f4SSven Schnelle 	struct raw3270_view *oldview = NULL, *nv;
898420105f4SSven Schnelle 	int rc;
899420105f4SSven Schnelle 
900420105f4SSven Schnelle 	if (rp->view == view)
901420105f4SSven Schnelle 		return 0;
902420105f4SSven Schnelle 
903420105f4SSven Schnelle 	if (!raw3270_state_ready(rp))
904420105f4SSven Schnelle 		return -EBUSY;
905420105f4SSven Schnelle 
906420105f4SSven Schnelle 	if (rp->view && rp->view->fn->deactivate) {
907420105f4SSven Schnelle 		oldview = rp->view;
908420105f4SSven Schnelle 		oldview->fn->deactivate(oldview);
909420105f4SSven Schnelle 	}
910420105f4SSven Schnelle 
911420105f4SSven Schnelle 	rc = raw3270_assign_activate_view(rp, view);
912420105f4SSven Schnelle 	if (!rc)
913420105f4SSven Schnelle 		return 0;
914420105f4SSven Schnelle 
915420105f4SSven Schnelle 	/* Didn't work. Try to reactivate the old view. */
916420105f4SSven Schnelle 	if (oldview) {
917420105f4SSven Schnelle 		rc = raw3270_assign_activate_view(rp, oldview);
918420105f4SSven Schnelle 		if (!rc)
919420105f4SSven Schnelle 			return 0;
920420105f4SSven Schnelle 	}
921420105f4SSven Schnelle 
922420105f4SSven Schnelle 	/* Didn't work as well. Try any other view. */
923420105f4SSven Schnelle 	list_for_each_entry(nv, &rp->view_list, list) {
924420105f4SSven Schnelle 		if (nv == view || nv == oldview)
925420105f4SSven Schnelle 			continue;
926420105f4SSven Schnelle 		rc = raw3270_assign_activate_view(rp, nv);
927420105f4SSven Schnelle 		if (!rc)
928420105f4SSven Schnelle 			break;
929420105f4SSven Schnelle 		rp->view = NULL;
930420105f4SSven Schnelle 	}
931420105f4SSven Schnelle 	return rc;
932420105f4SSven Schnelle }
933420105f4SSven Schnelle 
9344ae46db9SGuilherme G. Piccoli /*
9351da177e4SLinus Torvalds  * Activate a view.
9361da177e4SLinus Torvalds  */
raw3270_activate_view(struct raw3270_view * view)93713d4999aSSven Schnelle int raw3270_activate_view(struct raw3270_view *view)
9381da177e4SLinus Torvalds {
9391da177e4SLinus Torvalds 	struct raw3270 *rp;
9401da177e4SLinus Torvalds 	unsigned long flags;
9411da177e4SLinus Torvalds 	int rc;
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds 	rp = view->dev;
9441da177e4SLinus Torvalds 	if (!rp)
9451da177e4SLinus Torvalds 		return -ENODEV;
9461da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
947420105f4SSven Schnelle 	rc = __raw3270_activate_view(rp, view);
9481da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
9491da177e4SLinus Torvalds 	return rc;
9501da177e4SLinus Torvalds }
951754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_activate_view);
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds /*
9541da177e4SLinus Torvalds  * Deactivate current view.
9551da177e4SLinus Torvalds  */
raw3270_deactivate_view(struct raw3270_view * view)95613d4999aSSven Schnelle void raw3270_deactivate_view(struct raw3270_view *view)
9571da177e4SLinus Torvalds {
9581da177e4SLinus Torvalds 	unsigned long flags;
9591da177e4SLinus Torvalds 	struct raw3270 *rp;
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 	rp = view->dev;
9621da177e4SLinus Torvalds 	if (!rp)
9631da177e4SLinus Torvalds 		return;
9641da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
9651da177e4SLinus Torvalds 	if (rp->view == view) {
9661da177e4SLinus Torvalds 		view->fn->deactivate(view);
967d2c993d8SHeiko Carstens 		rp->view = NULL;
9681da177e4SLinus Torvalds 		/* Move deactivated view to end of list. */
9691da177e4SLinus Torvalds 		list_del_init(&view->list);
9701da177e4SLinus Torvalds 		list_add_tail(&view->list, &rp->view_list);
9711da177e4SLinus Torvalds 		/* Try to activate another view. */
9721cf69b7bSVineeth Vijayan 		if (raw3270_state_ready(rp)) {
973ed3cb6f0SRichard Hitt 			list_for_each_entry(view, &rp->view_list, list) {
9741da177e4SLinus Torvalds 				rp->view = view;
975ed3cb6f0SRichard Hitt 				if (view->fn->activate(view) == 0)
9761da177e4SLinus Torvalds 					break;
977d2c993d8SHeiko Carstens 				rp->view = NULL;
9781da177e4SLinus Torvalds 			}
9791da177e4SLinus Torvalds 		}
9801da177e4SLinus Torvalds 	}
9811da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
9821da177e4SLinus Torvalds }
983754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_deactivate_view);
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds /*
9861da177e4SLinus Torvalds  * Add view to device with minor "minor".
9871da177e4SLinus Torvalds  */
raw3270_add_view(struct raw3270_view * view,struct raw3270_fn * fn,int minor,int subclass)98813d4999aSSven Schnelle int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn,
98913d4999aSSven Schnelle 		     int minor, int subclass)
9901da177e4SLinus Torvalds {
9911da177e4SLinus Torvalds 	unsigned long flags;
9921da177e4SLinus Torvalds 	struct raw3270 *rp;
9931da177e4SLinus Torvalds 	int rc;
9941da177e4SLinus Torvalds 
995ed3cb6f0SRichard Hitt 	if (minor <= 0)
996ed3cb6f0SRichard Hitt 		return -ENODEV;
99714cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
9981da177e4SLinus Torvalds 	rc = -ENODEV;
9991da177e4SLinus Torvalds 	list_for_each_entry(rp, &raw3270_devices, list) {
10001da177e4SLinus Torvalds 		if (rp->minor != minor)
10011da177e4SLinus Torvalds 			continue;
10021da177e4SLinus Torvalds 		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
10031da177e4SLinus Torvalds 		atomic_set(&view->ref_count, 2);
10041da177e4SLinus Torvalds 		view->dev = rp;
10051da177e4SLinus Torvalds 		view->fn = fn;
10061da177e4SLinus Torvalds 		view->model = rp->model;
10071da177e4SLinus Torvalds 		view->rows = rp->rows;
10081da177e4SLinus Torvalds 		view->cols = rp->cols;
10091da177e4SLinus Torvalds 		view->ascebc = rp->ascebc;
10101da177e4SLinus Torvalds 		spin_lock_init(&view->lock);
10115712f330SMartin Schwidefsky 		lockdep_set_subclass(&view->lock, subclass);
1012ed3cb6f0SRichard Hitt 		list_add(&view->list, &rp->view_list);
10131da177e4SLinus Torvalds 		rc = 0;
10141da177e4SLinus Torvalds 		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
10151da177e4SLinus Torvalds 		break;
10161da177e4SLinus Torvalds 	}
101714cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
10181da177e4SLinus Torvalds 	return rc;
10191da177e4SLinus Torvalds }
1020754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_add_view);
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds /*
10231da177e4SLinus Torvalds  * Find specific view of device with minor "minor".
10241da177e4SLinus Torvalds  */
raw3270_find_view(struct raw3270_fn * fn,int minor)102513d4999aSSven Schnelle struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor)
10261da177e4SLinus Torvalds {
10271da177e4SLinus Torvalds 	struct raw3270 *rp;
10281da177e4SLinus Torvalds 	struct raw3270_view *view, *tmp;
10291da177e4SLinus Torvalds 	unsigned long flags;
10301da177e4SLinus Torvalds 
103114cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
10321da177e4SLinus Torvalds 	view = ERR_PTR(-ENODEV);
10331da177e4SLinus Torvalds 	list_for_each_entry(rp, &raw3270_devices, list) {
10341da177e4SLinus Torvalds 		if (rp->minor != minor)
10351da177e4SLinus Torvalds 			continue;
10361da177e4SLinus Torvalds 		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
10371da177e4SLinus Torvalds 		list_for_each_entry(tmp, &rp->view_list, list) {
10381da177e4SLinus Torvalds 			if (tmp->fn == fn) {
10391da177e4SLinus Torvalds 				raw3270_get_view(tmp);
10401da177e4SLinus Torvalds 				view = tmp;
10411da177e4SLinus Torvalds 				break;
10421da177e4SLinus Torvalds 			}
10431da177e4SLinus Torvalds 		}
10441da177e4SLinus Torvalds 		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
10451da177e4SLinus Torvalds 		break;
10461da177e4SLinus Torvalds 	}
104714cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
10481da177e4SLinus Torvalds 	return view;
10491da177e4SLinus Torvalds }
1050754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_find_view);
10511da177e4SLinus Torvalds 
10521da177e4SLinus Torvalds /*
10531da177e4SLinus Torvalds  * Remove view from device and free view structure via call to view->fn->free.
10541da177e4SLinus Torvalds  */
raw3270_del_view(struct raw3270_view * view)105513d4999aSSven Schnelle void raw3270_del_view(struct raw3270_view *view)
10561da177e4SLinus Torvalds {
10571da177e4SLinus Torvalds 	unsigned long flags;
10581da177e4SLinus Torvalds 	struct raw3270 *rp;
10591da177e4SLinus Torvalds 	struct raw3270_view *nv;
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 	rp = view->dev;
10621da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
10631da177e4SLinus Torvalds 	if (rp->view == view) {
10641da177e4SLinus Torvalds 		view->fn->deactivate(view);
1065d2c993d8SHeiko Carstens 		rp->view = NULL;
10661da177e4SLinus Torvalds 	}
10671da177e4SLinus Torvalds 	list_del_init(&view->list);
10681cf69b7bSVineeth Vijayan 	if (!rp->view && raw3270_state_ready(rp)) {
10691da177e4SLinus Torvalds 		/* Try to activate another view. */
10701da177e4SLinus Torvalds 		list_for_each_entry(nv, &rp->view_list, list) {
1071ed3cb6f0SRichard Hitt 			if (nv->fn->activate(nv) == 0) {
10721da177e4SLinus Torvalds 				rp->view = nv;
10731da177e4SLinus Torvalds 				break;
10741da177e4SLinus Torvalds 			}
10751da177e4SLinus Torvalds 		}
10761da177e4SLinus Torvalds 	}
10771da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
10781da177e4SLinus Torvalds 	/* Wait for reference counter to drop to zero. */
10791da177e4SLinus Torvalds 	atomic_dec(&view->ref_count);
10801da177e4SLinus Torvalds 	wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0);
10811da177e4SLinus Torvalds 	if (view->fn->free)
10821da177e4SLinus Torvalds 		view->fn->free(view);
10831da177e4SLinus Torvalds }
1084754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_del_view);
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds /*
10871da177e4SLinus Torvalds  * Remove a 3270 device structure.
10881da177e4SLinus Torvalds  */
raw3270_delete_device(struct raw3270 * rp)108913d4999aSSven Schnelle static void raw3270_delete_device(struct raw3270 *rp)
10901da177e4SLinus Torvalds {
10911da177e4SLinus Torvalds 	struct ccw_device *cdev;
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	/* Remove from device chain. */
109414cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
10951da177e4SLinus Torvalds 	list_del_init(&rp->list);
109614cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	/* Disconnect from ccw_device. */
10991da177e4SLinus Torvalds 	cdev = rp->cdev;
1100d2c993d8SHeiko Carstens 	rp->cdev = NULL;
1101dff59b64SGreg Kroah-Hartman 	dev_set_drvdata(&cdev->dev, NULL);
1102d2c993d8SHeiko Carstens 	cdev->handler = NULL;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	/* Put ccw_device structure. */
11051da177e4SLinus Torvalds 	put_device(&cdev->dev);
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	/* Now free raw3270 structure. */
11081da177e4SLinus Torvalds 	kfree(rp->ascebc);
11091da177e4SLinus Torvalds 	kfree(rp);
11101da177e4SLinus Torvalds }
11111da177e4SLinus Torvalds 
raw3270_probe(struct ccw_device * cdev)111213d4999aSSven Schnelle static int raw3270_probe(struct ccw_device *cdev)
11131da177e4SLinus Torvalds {
11141da177e4SLinus Torvalds 	return 0;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds /*
11181da177e4SLinus Torvalds  * Additional attributes for a 3270 device
11191da177e4SLinus Torvalds  */
model_show(struct device * dev,struct device_attribute * attr,char * buf)112082df96d8SSven Schnelle static ssize_t model_show(struct device *dev, struct device_attribute *attr,
112113d4999aSSven Schnelle 			  char *buf)
11221da177e4SLinus Torvalds {
11234b9e0436SQing Wang 	return sysfs_emit(buf, "%i\n",
1124dff59b64SGreg Kroah-Hartman 			  ((struct raw3270 *)dev_get_drvdata(dev))->model);
11251da177e4SLinus Torvalds }
112682df96d8SSven Schnelle static DEVICE_ATTR_RO(model);
11271da177e4SLinus Torvalds 
rows_show(struct device * dev,struct device_attribute * attr,char * buf)112882df96d8SSven Schnelle static ssize_t rows_show(struct device *dev, struct device_attribute *attr,
112913d4999aSSven Schnelle 			 char *buf)
11301da177e4SLinus Torvalds {
11314b9e0436SQing Wang 	return sysfs_emit(buf, "%i\n",
1132dff59b64SGreg Kroah-Hartman 			  ((struct raw3270 *)dev_get_drvdata(dev))->rows);
11331da177e4SLinus Torvalds }
113482df96d8SSven Schnelle static DEVICE_ATTR_RO(rows);
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds static ssize_t
columns_show(struct device * dev,struct device_attribute * attr,char * buf)113782df96d8SSven Schnelle columns_show(struct device *dev, struct device_attribute *attr,
113813d4999aSSven Schnelle 	     char *buf)
11391da177e4SLinus Torvalds {
11404b9e0436SQing Wang 	return sysfs_emit(buf, "%i\n",
1141dff59b64SGreg Kroah-Hartman 			  ((struct raw3270 *)dev_get_drvdata(dev))->cols);
11421da177e4SLinus Torvalds }
114382df96d8SSven Schnelle static DEVICE_ATTR_RO(columns);
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds static struct attribute *raw3270_attrs[] = {
11461da177e4SLinus Torvalds 	&dev_attr_model.attr,
11471da177e4SLinus Torvalds 	&dev_attr_rows.attr,
11481da177e4SLinus Torvalds 	&dev_attr_columns.attr,
11491da177e4SLinus Torvalds 	NULL,
11501da177e4SLinus Torvalds };
11511da177e4SLinus Torvalds 
1152cb0259caSArvind Yadav static const struct attribute_group raw3270_attr_group = {
11531da177e4SLinus Torvalds 	.attrs = raw3270_attrs,
11541da177e4SLinus Torvalds };
11551da177e4SLinus Torvalds 
raw3270_create_attributes(struct raw3270 * rp)1156d7cf0d57SHeiko Carstens static int raw3270_create_attributes(struct raw3270 *rp)
11571da177e4SLinus Torvalds {
1158c95571e6SMartin Schwidefsky 	return sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
11591da177e4SLinus Torvalds }
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds /*
11621da177e4SLinus Torvalds  * Notifier for device addition/removal
11631da177e4SLinus Torvalds  */
1164c11ca97eSDenis Cheng static LIST_HEAD(raw3270_notifier);
11651da177e4SLinus Torvalds 
raw3270_register_notifier(struct raw3270_notifier * notifier)1166c95571e6SMartin Schwidefsky int raw3270_register_notifier(struct raw3270_notifier *notifier)
11671da177e4SLinus Torvalds {
11681da177e4SLinus Torvalds 	struct raw3270 *rp;
11691da177e4SLinus Torvalds 
117014cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
1171c95571e6SMartin Schwidefsky 	list_add_tail(&notifier->list, &raw3270_notifier);
1172c95571e6SMartin Schwidefsky 	list_for_each_entry(rp, &raw3270_devices, list)
1173c95571e6SMartin Schwidefsky 		notifier->create(rp->minor);
117414cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
11751da177e4SLinus Torvalds 	return 0;
11761da177e4SLinus Torvalds }
1177754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_register_notifier);
11781da177e4SLinus Torvalds 
raw3270_unregister_notifier(struct raw3270_notifier * notifier)1179c95571e6SMartin Schwidefsky void raw3270_unregister_notifier(struct raw3270_notifier *notifier)
11801da177e4SLinus Torvalds {
1181c95571e6SMartin Schwidefsky 	struct raw3270 *rp;
11821da177e4SLinus Torvalds 
118314cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
1184c95571e6SMartin Schwidefsky 	list_for_each_entry(rp, &raw3270_devices, list)
1185c95571e6SMartin Schwidefsky 		notifier->destroy(rp->minor);
1186c95571e6SMartin Schwidefsky 	list_del(&notifier->list);
118714cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
11881da177e4SLinus Torvalds }
1189754f66b5SSven Schnelle EXPORT_SYMBOL(raw3270_unregister_notifier);
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds /*
11921da177e4SLinus Torvalds  * Set 3270 device online.
11931da177e4SLinus Torvalds  */
raw3270_set_online(struct ccw_device * cdev)119413d4999aSSven Schnelle static int raw3270_set_online(struct ccw_device *cdev)
11951da177e4SLinus Torvalds {
11961da177e4SLinus Torvalds 	struct raw3270_notifier *np;
1197c95571e6SMartin Schwidefsky 	struct raw3270 *rp;
11981da177e4SLinus Torvalds 	int rc;
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds 	rp = raw3270_create_device(cdev);
12011da177e4SLinus Torvalds 	if (IS_ERR(rp))
12021da177e4SLinus Torvalds 		return PTR_ERR(rp);
1203d7cf0d57SHeiko Carstens 	rc = raw3270_create_attributes(rp);
1204d7cf0d57SHeiko Carstens 	if (rc)
1205d7cf0d57SHeiko Carstens 		goto failure;
12064d334fd1SMartin Schwidefsky 	raw3270_reset_device(rp);
120714cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
12081da177e4SLinus Torvalds 	list_for_each_entry(np, &raw3270_notifier, list)
1209c95571e6SMartin Schwidefsky 		np->create(rp->minor);
121014cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
12111da177e4SLinus Torvalds 	return 0;
1212ed3cb6f0SRichard Hitt 
1213ed3cb6f0SRichard Hitt failure:
1214ed3cb6f0SRichard Hitt 	raw3270_delete_device(rp);
1215ed3cb6f0SRichard Hitt 	return rc;
12161da177e4SLinus Torvalds }
12171da177e4SLinus Torvalds 
12181da177e4SLinus Torvalds /*
12191da177e4SLinus Torvalds  * Remove 3270 device structure.
12201da177e4SLinus Torvalds  */
raw3270_remove(struct ccw_device * cdev)122113d4999aSSven Schnelle static void raw3270_remove(struct ccw_device *cdev)
12221da177e4SLinus Torvalds {
12231da177e4SLinus Torvalds 	unsigned long flags;
12241da177e4SLinus Torvalds 	struct raw3270 *rp;
12251da177e4SLinus Torvalds 	struct raw3270_view *v;
12261da177e4SLinus Torvalds 	struct raw3270_notifier *np;
12271da177e4SLinus Torvalds 
1228dff59b64SGreg Kroah-Hartman 	rp = dev_get_drvdata(&cdev->dev);
1229ed3cb6f0SRichard Hitt 	/*
1230ed3cb6f0SRichard Hitt 	 * _remove is the opposite of _probe; it's probe that
1231ed3cb6f0SRichard Hitt 	 * should set up rp.  raw3270_remove gets entered for
1232ed3cb6f0SRichard Hitt 	 * devices even if they haven't been varied online.
1233ed3cb6f0SRichard Hitt 	 * Thus, rp may validly be NULL here.
1234ed3cb6f0SRichard Hitt 	 */
123531bc2324SSven Schnelle 	if (!rp)
1236ed3cb6f0SRichard Hitt 		return;
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds 	/* Deactivate current view and remove all views. */
12411da177e4SLinus Torvalds 	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
12421da177e4SLinus Torvalds 	if (rp->view) {
12434d334fd1SMartin Schwidefsky 		if (rp->view->fn->deactivate)
12441da177e4SLinus Torvalds 			rp->view->fn->deactivate(rp->view);
1245d2c993d8SHeiko Carstens 		rp->view = NULL;
12461da177e4SLinus Torvalds 	}
12471da177e4SLinus Torvalds 	while (!list_empty(&rp->view_list)) {
12481da177e4SLinus Torvalds 		v = list_entry(rp->view_list.next, struct raw3270_view, list);
12491da177e4SLinus Torvalds 		if (v->fn->release)
12501da177e4SLinus Torvalds 			v->fn->release(v);
12511da177e4SLinus Torvalds 		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
12521da177e4SLinus Torvalds 		raw3270_del_view(v);
12531da177e4SLinus Torvalds 		spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
12541da177e4SLinus Torvalds 	}
12551da177e4SLinus Torvalds 	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
12561da177e4SLinus Torvalds 
125714cc3e2bSIngo Molnar 	mutex_lock(&raw3270_mutex);
12581da177e4SLinus Torvalds 	list_for_each_entry(np, &raw3270_notifier, list)
1259c95571e6SMartin Schwidefsky 		np->destroy(rp->minor);
126014cc3e2bSIngo Molnar 	mutex_unlock(&raw3270_mutex);
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	/* Reset 3270 device. */
12631da177e4SLinus Torvalds 	raw3270_reset_device(rp);
12641da177e4SLinus Torvalds 	/* And finally remove it. */
12651da177e4SLinus Torvalds 	raw3270_delete_device(rp);
12661da177e4SLinus Torvalds }
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds /*
12691da177e4SLinus Torvalds  * Set 3270 device offline.
12701da177e4SLinus Torvalds  */
raw3270_set_offline(struct ccw_device * cdev)127113d4999aSSven Schnelle static int raw3270_set_offline(struct ccw_device *cdev)
12721da177e4SLinus Torvalds {
12731da177e4SLinus Torvalds 	struct raw3270 *rp;
12741da177e4SLinus Torvalds 
1275dff59b64SGreg Kroah-Hartman 	rp = dev_get_drvdata(&cdev->dev);
12761da177e4SLinus Torvalds 	if (test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags))
12771da177e4SLinus Torvalds 		return -EBUSY;
12781da177e4SLinus Torvalds 	raw3270_remove(cdev);
12791da177e4SLinus Torvalds 	return 0;
12801da177e4SLinus Torvalds }
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds static struct ccw_device_id raw3270_id[] = {
12831da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3270, 0) },
12841da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3271, 0) },
12851da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3272, 0) },
12861da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3273, 0) },
12871da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3274, 0) },
12881da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3275, 0) },
12891da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3276, 0) },
12901da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3277, 0) },
12911da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3278, 0) },
12921da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3279, 0) },
12931da177e4SLinus Torvalds 	{ CCW_DEVICE(0x3174, 0) },
12941da177e4SLinus Torvalds 	{ /* end of list */ },
12951da177e4SLinus Torvalds };
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds static struct ccw_driver raw3270_ccw_driver = {
12983bda058bSSebastian Ott 	.driver = {
12991da177e4SLinus Torvalds 		.name	= "3270",
13001da177e4SLinus Torvalds 		.owner	= THIS_MODULE,
13013bda058bSSebastian Ott 	},
13021da177e4SLinus Torvalds 	.ids		= raw3270_id,
13031da177e4SLinus Torvalds 	.probe		= &raw3270_probe,
13041da177e4SLinus Torvalds 	.remove		= &raw3270_remove,
13051da177e4SLinus Torvalds 	.set_online	= &raw3270_set_online,
13061da177e4SLinus Torvalds 	.set_offline	= &raw3270_set_offline,
1307420f42ecSHeiko Carstens 	.int_class	= IRQIO_C70,
13081da177e4SLinus Torvalds };
13091da177e4SLinus Torvalds 
raw3270_init(void)131013d4999aSSven Schnelle static int raw3270_init(void)
13111da177e4SLinus Torvalds {
13121da177e4SLinus Torvalds 	struct raw3270 *rp;
13131da177e4SLinus Torvalds 	int rc;
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	if (raw3270_registered)
13161da177e4SLinus Torvalds 		return 0;
13171da177e4SLinus Torvalds 	raw3270_registered = 1;
13181da177e4SLinus Torvalds 	rc = ccw_driver_register(&raw3270_ccw_driver);
13191da177e4SLinus Torvalds 	if (rc == 0) {
13201da177e4SLinus Torvalds 		/* Create attributes for early (= console) device. */
132114cc3e2bSIngo Molnar 		mutex_lock(&raw3270_mutex);
1322*1aaba11dSGreg Kroah-Hartman 		class3270 = class_create("3270");
13231da177e4SLinus Torvalds 		list_for_each_entry(rp, &raw3270_devices, list) {
13241da177e4SLinus Torvalds 			get_device(&rp->cdev->dev);
13251da177e4SLinus Torvalds 			raw3270_create_attributes(rp);
13261da177e4SLinus Torvalds 		}
132714cc3e2bSIngo Molnar 		mutex_unlock(&raw3270_mutex);
13281da177e4SLinus Torvalds 	}
13291da177e4SLinus Torvalds 	return rc;
13301da177e4SLinus Torvalds }
13311da177e4SLinus Torvalds 
raw3270_exit(void)133213d4999aSSven Schnelle static void raw3270_exit(void)
13331da177e4SLinus Torvalds {
13341da177e4SLinus Torvalds 	ccw_driver_unregister(&raw3270_ccw_driver);
1335ed3cb6f0SRichard Hitt 	class_destroy(class3270);
13361da177e4SLinus Torvalds }
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds MODULE_LICENSE("GPL");
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds module_init(raw3270_init);
13411da177e4SLinus Torvalds module_exit(raw3270_exit);
1342