2005-04-17 02:20:36 +04:00
/*
* 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"
2007-02-05 23:16:47 +03:00
static struct raw3270_fn fs3270_fn ;
2005-04-17 02:20:36 +04:00
struct fs3270 {
struct raw3270_view view ;
2006-10-02 13:17:28 +04:00
struct pid * fs_pid ; /* Pid of controlling program. */
2005-04-17 02:20:36 +04:00
int read_command ; /* ccw command to use for reads. */
int write_command ; /* ccw command to use for writes. */
int attention ; /* Got attention. */
2005-10-31 02:00:10 +03:00
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 */
2005-04-17 02:20:36 +04:00
} ;
static void
fs3270_wake_up ( struct raw3270_request * rq , void * data )
{
wake_up ( ( wait_queue_head_t * ) data ) ;
}
2005-10-31 02:00:10 +03:00
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 ) ;
}
2005-04-17 02:20:36 +04:00
static int
fs3270_do_io ( struct raw3270_view * view , struct raw3270_request * rq )
{
2005-10-31 02:00:10 +03:00
struct fs3270 * fp ;
2005-04-17 02:20:36 +04:00
int rc ;
2005-10-31 02:00:10 +03:00
fp = ( struct fs3270 * ) view ;
2005-04-17 02:20:36 +04:00
rq - > callback = fs3270_wake_up ;
2005-10-31 02:00:10 +03:00
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 ;
2005-04-17 02:20:36 +04:00
}
2005-10-31 02:00:10 +03:00
/*
* Switch to the fullscreen view .
*/
2005-04-17 02:20:36 +04:00
static void
fs3270_reset_callback ( struct raw3270_request * rq , void * data )
{
2005-10-31 02:00:10 +03:00
struct fs3270 * fp ;
fp = ( struct fs3270 * ) rq - > view ;
2005-04-17 02:20:36 +04:00
raw3270_request_reset ( rq ) ;
2005-10-31 02:00:10 +03:00
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 )
2006-10-02 13:17:28 +04:00
kill_pid ( fp - > fs_pid , SIGHUP , 1 ) ;
2005-10-31 02:00:10 +03:00
}
fp - > rdbuf_size = 0 ;
raw3270_request_reset ( rq ) ;
wake_up ( & fp - > wait ) ;
2005-04-17 02:20:36 +04:00
}
static int
fs3270_activate ( struct raw3270_view * view )
{
struct fs3270 * fp ;
2005-10-31 02:00:10 +03:00
char * cp ;
int rc ;
2005-04-17 02:20:36 +04:00
fp = ( struct fs3270 * ) view ;
2005-10-31 02:00:10 +03:00
/* 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 ;
2005-04-17 02:20:36 +04:00
}
/*
* Shutdown fullscreen view .
*/
2005-10-31 02:00:10 +03:00
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 )
2006-10-02 13:17:28 +04:00
kill_pid ( fp - > fs_pid , SIGHUP , 1 ) ;
2005-10-31 02:00:10 +03:00
fp - > rdbuf_size = 0 ;
} else
fp - > rdbuf_size = fp - > rdbuf - > size - rq - > rescnt ;
raw3270_request_reset ( rq ) ;
wake_up ( & fp - > wait ) ;
}
2005-04-17 02:20:36 +04:00
static void
fs3270_deactivate ( struct raw3270_view * view )
{
struct fs3270 * fp ;
fp = ( struct fs3270 * ) view ;
2005-10-31 02:00:10 +03:00
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 ) ;
2005-04-17 02:20:36 +04:00
}
static int
fs3270_irq ( struct fs3270 * fp , struct raw3270_request * rq , struct irb * irb )
{
/* Handle ATTN. Set indication and wake waiters for attention. */
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION ) {
2005-04-17 02:20:36 +04:00
fp - > attention = 1 ;
2005-10-31 02:00:10 +03:00
wake_up ( & fp - > wait ) ;
2005-04-17 02:20:36 +04:00
}
if ( rq ) {
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_UNIT_CHECK )
2005-04-17 02:20:36 +04:00
rq - > rc = - EIO ;
else
/* Normal end. Copy residual count. */
2008-07-14 11:58:50 +04:00
rq - > rescnt = irb - > scsw . cmd . count ;
2005-04-17 02:20:36 +04:00
}
return RAW3270_IO_DONE ;
}
/*
* Process reads from fullscreen 3270.
*/
static ssize_t
2006-07-12 18:41:55 +04:00
fs3270_read ( struct file * filp , char __user * data , size_t count , loff_t * off )
2005-04-17 02:20:36 +04:00
{
struct fs3270 * fp ;
struct raw3270_request * rq ;
struct idal_buffer * ib ;
2005-10-31 02:00:10 +03:00
ssize_t rc ;
2005-04-17 02:20:36 +04:00
if ( count = = 0 | | count > 65535 )
return - EINVAL ;
fp = filp - > private_data ;
if ( ! fp )
return - ENODEV ;
ib = idal_buffer_alloc ( count , 0 ) ;
2005-10-31 02:00:10 +03:00
if ( IS_ERR ( ib ) )
2005-04-17 02:20:36 +04:00
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 ) ;
2005-10-31 02:00:10 +03:00
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 ;
}
}
2005-04-17 02:20:36 +04:00
raw3270_request_free ( rq ) ;
} else
rc = PTR_ERR ( rq ) ;
idal_buffer_free ( ib ) ;
return rc ;
}
/*
* Process writes to fullscreen 3270.
*/
static ssize_t
2006-07-12 18:41:55 +04:00
fs3270_write ( struct file * filp , const char __user * data , size_t count , loff_t * off )
2005-04-17 02:20:36 +04:00
{
struct fs3270 * fp ;
struct raw3270_request * rq ;
struct idal_buffer * ib ;
int write_command ;
2005-10-31 02:00:10 +03:00
ssize_t rc ;
2005-04-17 02:20:36 +04:00
fp = filp - > private_data ;
if ( ! fp )
return - ENODEV ;
ib = idal_buffer_alloc ( count , 0 ) ;
2005-10-31 02:00:10 +03:00
if ( IS_ERR ( ib ) )
2005-04-17 02:20:36 +04:00
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 ) ;
2005-10-31 02:00:10 +03:00
if ( rc = = 0 )
rc = count - rq - > rescnt ;
2005-04-17 02:20:36 +04:00
} 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
*/
2006-01-10 07:52:04 +03:00
static long
fs3270_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
struct fs3270 * fp ;
struct raw3270_iocb iocb ;
int rc ;
fp = filp - > private_data ;
if ( ! fp )
return - ENODEV ;
rc = 0 ;
2006-01-10 07:52:04 +03:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case TUBICMD :
fp - > read_command = arg ;
break ;
case TUBOCMD :
fp - > write_command = arg ;
break ;
case TUBGETI :
2006-07-12 18:41:55 +04:00
rc = put_user ( fp - > read_command , ( char __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
break ;
case TUBGETO :
2006-07-12 18:41:55 +04:00
rc = put_user ( fp - > write_command , ( char __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
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 ;
2006-07-12 18:41:55 +04:00
if ( copy_to_user ( ( char __user * ) arg , & iocb ,
2005-04-17 02:20:36 +04:00
sizeof ( struct raw3270_iocb ) ) )
rc = - EFAULT ;
break ;
}
2006-01-10 07:52:04 +03:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
2005-10-31 02:00:10 +03:00
* Allocate fs3270 structure .
2005-04-17 02:20:36 +04:00
*/
static struct fs3270 *
fs3270_alloc_view ( void )
{
struct fs3270 * fp ;
2006-03-24 14:15:31 +03:00
fp = kzalloc ( sizeof ( struct fs3270 ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! fp )
return ERR_PTR ( - ENOMEM ) ;
2005-10-31 02:00:10 +03:00
fp - > init = raw3270_request_alloc ( 0 ) ;
if ( IS_ERR ( fp - > init ) ) {
2005-04-17 02:20:36 +04:00
kfree ( fp ) ;
return ERR_PTR ( - ENOMEM ) ;
}
return fp ;
}
/*
2005-10-31 02:00:10 +03:00
* Free fs3270 structure .
2005-04-17 02:20:36 +04:00
*/
static void
fs3270_free_view ( struct raw3270_view * view )
{
2005-10-31 02:00:10 +03:00
struct fs3270 * fp ;
fp = ( struct fs3270 * ) view ;
if ( fp - > rdbuf )
idal_buffer_free ( fp - > rdbuf ) ;
raw3270_request_free ( ( ( struct fs3270 * ) view ) - > init ) ;
2005-04-17 02:20:36 +04:00
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. */
2007-02-05 23:16:47 +03:00
static struct raw3270_fn fs3270_fn = {
2005-04-17 02:20:36 +04:00
. 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 ;
2005-10-31 02:00:10 +03:00
struct idal_buffer * ib ;
2005-04-17 02:20:36 +04:00
int minor , rc ;
2006-12-08 13:37:34 +03:00
if ( imajor ( filp - > f_path . dentry - > d_inode ) ! = IBM_FS3270_MAJOR )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2006-12-08 13:37:34 +03:00
minor = iminor ( filp - > f_path . dentry - > d_inode ) ;
2005-10-31 02:00:10 +03:00
/* Check for minor 0 multiplexer. */
if ( minor = = 0 ) {
2006-12-08 13:36:04 +03:00
struct tty_struct * tty ;
mutex_lock ( & tty_mutex ) ;
tty = get_current_tty ( ) ;
if ( ! tty | | tty - > driver - > major ! = IBM_TTY3270_MAJOR ) {
mutex_unlock ( & tty_mutex ) ;
2005-10-31 02:00:10 +03:00
return - ENODEV ;
2006-12-08 13:36:04 +03:00
}
minor = tty - > index + RAW3270_FIRSTMINOR ;
mutex_unlock ( & tty_mutex ) ;
2005-10-31 02:00:10 +03:00
}
2005-04-17 02:20:36 +04:00
/* 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 ) ;
2005-10-31 02:00:10 +03:00
init_waitqueue_head ( & fp - > wait ) ;
2006-10-02 13:17:28 +04:00
fp - > fs_pid = get_pid ( task_pid ( current ) ) ;
2005-04-17 02:20:36 +04:00
rc = raw3270_add_view ( & fp - > view , & fs3270_fn , minor ) ;
if ( rc ) {
fs3270_free_view ( & fp - > view ) ;
return rc ;
}
2005-10-31 02:00:10 +03:00
/* 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 ;
2005-04-17 02:20:36 +04:00
rc = raw3270_activate_view ( & fp - > view ) ;
if ( rc ) {
2005-10-31 02:00:10 +03:00
raw3270_put_view ( & fp - > view ) ;
2005-04-17 02:20:36 +04:00
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 ;
2006-07-12 18:41:55 +04:00
filp - > private_data = NULL ;
2005-10-31 02:00:10 +03:00
if ( fp ) {
2006-10-02 13:17:28 +04:00
put_pid ( fp - > fs_pid ) ;
fp - > fs_pid = NULL ;
2005-10-31 02:00:10 +03:00
raw3270_reset ( & fp - > view ) ;
raw3270_put_view ( & fp - > view ) ;
2005-04-17 02:20:36 +04:00
raw3270_del_view ( & fp - > view ) ;
2005-10-31 02:00:10 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations fs3270_fops = {
2006-01-10 07:52:04 +03:00
. 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 */
2005-04-17 02:20:36 +04:00
} ;
/*
* 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 ) ;