2005-04-17 02:20:36 +04:00
/*
* IBM / 3270 Driver - tty functions .
*
* Author ( s ) :
* Original 3270 Code for 2.4 written by Richard Hitt ( UTS Global )
* Rewritten for 2.5 by Martin Schwidefsky < schwidefsky @ de . ibm . com >
2012-07-20 13:15:04 +04:00
* - - Copyright IBM Corp . 2003
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kdev_t.h>
# include <linux/tty.h>
# include <linux/vt_kern.h>
# include <linux/init.h>
# include <linux/console.h>
# include <linux/interrupt.h>
2013-01-04 17:55:13 +04:00
# include <linux/workqueue.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/bootmem.h>
2009-12-07 14:52:13 +03:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04:00
# include <asm/ccwdev.h>
# include <asm/cio.h>
# include <asm/ebcdic.h>
# include <asm/uaccess.h>
# include "raw3270.h"
2007-10-12 18:11:35 +04:00
# include "tty3270.h"
2005-04-17 02:20:36 +04:00
# include "keyboard.h"
# define TTY3270_CHAR_BUF_SIZE 256
# define TTY3270_OUTPUT_BUFFER_SIZE 1024
# define TTY3270_STRING_PAGES 5
struct tty_driver * tty3270_driver ;
static int tty3270_max_index ;
2007-02-05 23:16:47 +03:00
static struct raw3270_fn tty3270_fn ;
2005-04-17 02:20:36 +04:00
struct tty3270_cell {
unsigned char character ;
unsigned char highlight ;
unsigned char f_color ;
} ;
struct tty3270_line {
struct tty3270_cell * cells ;
int len ;
} ;
# define ESCAPE_NPAR 8
/*
* The main tty view data structure .
* FIXME :
* 1 ) describe line orientation & lines list concept against screen
* 2 ) describe conversion of screen to lines
* 3 ) describe line format .
*/
struct tty3270 {
struct raw3270_view view ;
2012-04-02 15:54:18 +04:00
struct tty_port port ;
2005-04-17 02:20:36 +04:00
void * * freemem_pages ; /* Array of pages used for freemem. */
struct list_head freemem ; /* List of free memory for strings. */
/* Output stuff. */
struct list_head lines ; /* List of lines. */
struct list_head update ; /* List of lines to update. */
unsigned char wcc ; /* Write control character. */
int nr_lines ; /* # lines in list. */
int nr_up ; /* # lines up in history. */
unsigned long update_flags ; /* Update indication bits. */
struct string * status ; /* Lower right of display. */
struct raw3270_request * write ; /* Single write request. */
struct timer_list timer ; /* Output delay timer. */
/* Current tty screen. */
unsigned int cx , cy ; /* Current output position. */
unsigned int highlight ; /* Blink/reverse/underscore */
unsigned int f_color ; /* Foreground color */
struct tty3270_line * screen ;
2013-01-04 17:55:13 +04:00
unsigned int n_model , n_cols , n_rows ; /* New model & size */
struct work_struct resize_work ;
2005-04-17 02:20:36 +04:00
/* Input stuff. */
struct string * prompt ; /* Output string for input area. */
struct string * input ; /* Input string for read request. */
struct raw3270_request * read ; /* Single read request. */
struct raw3270_request * kreset ; /* Single keyboard reset request. */
unsigned char inattr ; /* Visible/invisible input. */
int throttle , attn ; /* tty throttle/unthrottle. */
struct tasklet_struct readlet ; /* Tasklet to issue read request. */
struct kbd_data * kbd ; /* key_maps stuff. */
/* Escape sequence parsing. */
int esc_state , esc_ques , esc_npar ;
int esc_par [ ESCAPE_NPAR ] ;
unsigned int saved_cx , saved_cy ;
unsigned int saved_highlight , saved_f_color ;
/* Command recalling. */
struct list_head rcl_lines ; /* List of recallable lines. */
struct list_head * rcl_walk ; /* Point in rcl_lines list. */
int rcl_nr , rcl_max ; /* Number/max number of rcl_lines. */
/* Character array for put_char/flush_chars. */
unsigned int char_count ;
char char_buf [ TTY3270_CHAR_BUF_SIZE ] ;
} ;
/* tty3270->update_flags. See tty3270_update for details. */
# define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */
# define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */
# define TTY_UPDATE_INPUT 4 /* Update input line. */
# define TTY_UPDATE_STATUS 8 /* Update status line. */
2009-06-12 12:26:31 +04:00
# define TTY_UPDATE_ALL 16 /* Recreate screen. */
2005-04-17 02:20:36 +04:00
static void tty3270_update ( struct tty3270 * ) ;
2013-01-04 17:55:13 +04:00
static void tty3270_resize_work ( struct work_struct * work ) ;
2005-04-17 02:20:36 +04:00
/*
* Setup timeout for a device . On timeout trigger an update .
*/
2007-02-05 23:16:47 +03:00
static void tty3270_set_timer ( struct tty3270 * tp , int expires )
2005-04-17 02:20:36 +04:00
{
2013-12-04 17:29:11 +04:00
mod_timer ( & tp - > timer , jiffies + expires ) ;
2005-04-17 02:20:36 +04:00
}
/*
* The input line are the two last lines of the screen .
*/
static void
tty3270_update_prompt ( struct tty3270 * tp , char * input , int count )
{
struct string * line ;
unsigned int off ;
line = tp - > prompt ;
if ( count ! = 0 )
line - > string [ 5 ] = TF_INMDT ;
else
line - > string [ 5 ] = tp - > inattr ;
if ( count > tp - > view . cols * 2 - 11 )
count = tp - > view . cols * 2 - 11 ;
memcpy ( line - > string + 6 , input , count ) ;
line - > string [ 6 + count ] = TO_IC ;
/* Clear to end of input line. */
if ( count < tp - > view . cols * 2 - 11 ) {
line - > string [ 7 + count ] = TO_RA ;
line - > string [ 10 + count ] = 0 ;
off = tp - > view . cols * tp - > view . rows - 9 ;
raw3270_buffer_address ( tp - > view . dev , line - > string + count + 8 , off ) ;
line - > len = 11 + count ;
} else
line - > len = 7 + count ;
tp - > update_flags | = TTY_UPDATE_INPUT ;
}
static void
tty3270_create_prompt ( struct tty3270 * tp )
{
static const unsigned char blueprint [ ] =
{ TO_SBA , 0 , 0 , 0x6e , TO_SF , TF_INPUT ,
/* empty input string */
TO_IC , TO_RA , 0 , 0 , 0 } ;
struct string * line ;
unsigned int offset ;
line = alloc_string ( & tp - > freemem ,
sizeof ( blueprint ) + tp - > view . cols * 2 - 9 ) ;
tp - > prompt = line ;
tp - > inattr = TF_INPUT ;
/* Copy blueprint to status line */
memcpy ( line - > string , blueprint , sizeof ( blueprint ) ) ;
line - > len = sizeof ( blueprint ) ;
/* Set output offsets. */
offset = tp - > view . cols * ( tp - > view . rows - 2 ) ;
raw3270_buffer_address ( tp - > view . dev , line - > string + 1 , offset ) ;
offset = tp - > view . cols * tp - > view . rows - 9 ;
raw3270_buffer_address ( tp - > view . dev , line - > string + 8 , offset ) ;
/* Allocate input string for reading. */
tp - > input = alloc_string ( & tp - > freemem , tp - > view . cols * 2 - 9 + 6 ) ;
}
/*
* The status line is the last line of the screen . It shows the string
* " Running " / " Holding " in the lower right corner of the screen .
*/
static void
tty3270_update_status ( struct tty3270 * tp )
{
char * str ;
str = ( tp - > nr_up ! = 0 ) ? " History " : " Running " ;
memcpy ( tp - > status - > string + 8 , str , 7 ) ;
codepage_convert ( tp - > view . ascebc , tp - > status - > string + 8 , 7 ) ;
tp - > update_flags | = TTY_UPDATE_STATUS ;
}
static void
tty3270_create_status ( struct tty3270 * tp )
{
static const unsigned char blueprint [ ] =
{ TO_SBA , 0 , 0 , TO_SF , TF_LOG , TO_SA , TAT_COLOR , TAC_GREEN ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , TO_SF , TF_LOG , TO_SA , TAT_COLOR ,
TAC_RESET } ;
struct string * line ;
unsigned int offset ;
line = alloc_string ( & tp - > freemem , sizeof ( blueprint ) ) ;
tp - > status = line ;
/* Copy blueprint to status line */
memcpy ( line - > string , blueprint , sizeof ( blueprint ) ) ;
/* Set address to start of status string (= last 9 characters). */
offset = tp - > view . cols * tp - > view . rows - 9 ;
raw3270_buffer_address ( tp - > view . dev , line - > string + 1 , offset ) ;
}
/*
* Set output offsets to 3270 datastream fragment of a tty string .
* ( TO_SBA offset at the start and TO_RA offset at the end of the string )
*/
static void
tty3270_update_string ( struct tty3270 * tp , struct string * line , int nr )
{
unsigned char * cp ;
raw3270_buffer_address ( tp - > view . dev , line - > string + 1 ,
tp - > view . cols * nr ) ;
cp = line - > string + line - > len - 4 ;
if ( * cp = = TO_RA )
raw3270_buffer_address ( tp - > view . dev , cp + 1 ,
tp - > view . cols * ( nr + 1 ) ) ;
}
/*
* Rebuild update list to print all lines .
*/
static void
tty3270_rebuild_update ( struct tty3270 * tp )
{
struct string * s , * n ;
int line , nr_up ;
/*
* Throw away update list and create a new one ,
* containing all lines that will fit on the screen .
*/
list_for_each_entry_safe ( s , n , & tp - > update , update )
list_del_init ( & s - > update ) ;
line = tp - > view . rows - 3 ;
nr_up = tp - > nr_up ;
list_for_each_entry_reverse ( s , & tp - > lines , list ) {
if ( nr_up > 0 ) {
nr_up - - ;
continue ;
}
tty3270_update_string ( tp , s , line ) ;
list_add ( & s - > update , & tp - > update ) ;
if ( - - line < 0 )
break ;
}
tp - > update_flags | = TTY_UPDATE_LIST ;
}
/*
* Alloc string for size bytes . If there is not enough room in
* freemem , free strings until there is room .
*/
static struct string *
tty3270_alloc_string ( struct tty3270 * tp , size_t size )
{
struct string * s , * n ;
s = alloc_string ( & tp - > freemem , size ) ;
if ( s )
return s ;
list_for_each_entry_safe ( s , n , & tp - > lines , list ) {
BUG_ON ( tp - > nr_lines < = tp - > view . rows - 2 ) ;
list_del ( & s - > list ) ;
if ( ! list_empty ( & s - > update ) )
list_del ( & s - > update ) ;
tp - > nr_lines - - ;
if ( free_string ( & tp - > freemem , s ) > = size )
break ;
}
s = alloc_string ( & tp - > freemem , size ) ;
BUG_ON ( ! s ) ;
if ( tp - > nr_up ! = 0 & &
tp - > nr_up + tp - > view . rows - 2 > = tp - > nr_lines ) {
tp - > nr_up = tp - > nr_lines - tp - > view . rows + 2 ;
tty3270_rebuild_update ( tp ) ;
tty3270_update_status ( tp ) ;
}
return s ;
}
/*
* Add an empty line to the list .
*/
static void
tty3270_blank_line ( struct tty3270 * tp )
{
static const unsigned char blueprint [ ] =
{ TO_SBA , 0 , 0 , TO_SA , TAT_EXTHI , TAX_RESET ,
TO_SA , TAT_COLOR , TAC_RESET , TO_RA , 0 , 0 , 0 } ;
struct string * s ;
s = tty3270_alloc_string ( tp , sizeof ( blueprint ) ) ;
memcpy ( s - > string , blueprint , sizeof ( blueprint ) ) ;
s - > len = sizeof ( blueprint ) ;
list_add_tail ( & s - > list , & tp - > lines ) ;
tp - > nr_lines + + ;
if ( tp - > nr_up ! = 0 )
tp - > nr_up + + ;
}
/*
* Write request completion callback .
*/
static void
tty3270_write_callback ( struct raw3270_request * rq , void * data )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( rq - > view , struct tty3270 , view ) ;
2005-04-17 02:20:36 +04:00
if ( rq - > rc ! = 0 ) {
2011-03-31 05:57:33 +04:00
/* Write wasn't successful. Refresh all. */
2005-04-17 02:20:36 +04:00
tp - > update_flags = TTY_UPDATE_ALL ;
tty3270_set_timer ( tp , 1 ) ;
}
raw3270_request_reset ( rq ) ;
xchg ( & tp - > write , rq ) ;
}
/*
* Update 3270 display .
*/
static void
tty3270_update ( struct tty3270 * tp )
{
static char invalid_sba [ 2 ] = { 0xff , 0xff } ;
struct raw3270_request * wrq ;
unsigned long updated ;
struct string * s , * n ;
char * sba , * str ;
int rc , len ;
wrq = xchg ( & tp - > write , 0 ) ;
if ( ! wrq ) {
tty3270_set_timer ( tp , 1 ) ;
return ;
}
spin_lock ( & tp - > view . lock ) ;
updated = 0 ;
2009-06-12 12:26:31 +04:00
if ( tp - > update_flags & TTY_UPDATE_ALL ) {
tty3270_rebuild_update ( tp ) ;
tty3270_update_status ( tp ) ;
tp - > update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
TTY_UPDATE_INPUT | TTY_UPDATE_STATUS ;
}
2005-04-17 02:20:36 +04:00
if ( tp - > update_flags & TTY_UPDATE_ERASE ) {
/* Use erase write alternate to erase display. */
raw3270_request_set_cmd ( wrq , TC_EWRITEA ) ;
updated | = TTY_UPDATE_ERASE ;
} else
raw3270_request_set_cmd ( wrq , TC_WRITE ) ;
raw3270_request_add_data ( wrq , & tp - > wcc , 1 ) ;
tp - > wcc = TW_NONE ;
/*
* Update status line .
*/
if ( tp - > update_flags & TTY_UPDATE_STATUS )
if ( raw3270_request_add_data ( wrq , tp - > status - > string ,
tp - > status - > len ) = = 0 )
updated | = TTY_UPDATE_STATUS ;
/*
* Write input line .
*/
if ( tp - > update_flags & TTY_UPDATE_INPUT )
if ( raw3270_request_add_data ( wrq , tp - > prompt - > string ,
tp - > prompt - > len ) = = 0 )
updated | = TTY_UPDATE_INPUT ;
sba = invalid_sba ;
if ( tp - > update_flags & TTY_UPDATE_LIST ) {
/* Write strings in the update list to the screen. */
list_for_each_entry_safe ( s , n , & tp - > update , update ) {
str = s - > string ;
len = s - > len ;
/*
* Skip TO_SBA at the start of the string if the
* last output position matches the start address
* of this line .
*/
if ( s - > string [ 1 ] = = sba [ 0 ] & & s - > string [ 2 ] = = sba [ 1 ] )
str + = 3 , len - = 3 ;
if ( raw3270_request_add_data ( wrq , str , len ) ! = 0 )
break ;
list_del_init ( & s - > update ) ;
sba = s - > string + s - > len - 3 ;
}
if ( list_empty ( & tp - > update ) )
updated | = TTY_UPDATE_LIST ;
}
wrq - > callback = tty3270_write_callback ;
rc = raw3270_start ( & tp - > view , wrq ) ;
if ( rc = = 0 ) {
tp - > update_flags & = ~ updated ;
if ( tp - > update_flags )
tty3270_set_timer ( tp , 1 ) ;
} else {
raw3270_request_reset ( wrq ) ;
xchg ( & tp - > write , wrq ) ;
}
spin_unlock ( & tp - > view . lock ) ;
}
/*
* Command recalling .
*/
static void
tty3270_rcl_add ( struct tty3270 * tp , char * input , int len )
{
struct string * s ;
2006-07-12 18:41:55 +04:00
tp - > rcl_walk = NULL ;
2005-04-17 02:20:36 +04:00
if ( len < = 0 )
return ;
if ( tp - > rcl_nr > = tp - > rcl_max ) {
s = list_entry ( tp - > rcl_lines . next , struct string , list ) ;
list_del ( & s - > list ) ;
free_string ( & tp - > freemem , s ) ;
tp - > rcl_nr - - ;
}
s = tty3270_alloc_string ( tp , len ) ;
memcpy ( s - > string , input , len ) ;
list_add_tail ( & s - > list , & tp - > rcl_lines ) ;
tp - > rcl_nr + + ;
}
static void
tty3270_rcl_backward ( struct kbd_data * kbd )
{
2012-04-02 15:54:18 +04:00
struct tty3270 * tp = container_of ( kbd - > port , struct tty3270 , port ) ;
2005-04-17 02:20:36 +04:00
struct string * s ;
spin_lock_bh ( & tp - > view . lock ) ;
if ( tp - > inattr = = TF_INPUT ) {
if ( tp - > rcl_walk & & tp - > rcl_walk - > prev ! = & tp - > rcl_lines )
tp - > rcl_walk = tp - > rcl_walk - > prev ;
else if ( ! list_empty ( & tp - > rcl_lines ) )
tp - > rcl_walk = tp - > rcl_lines . prev ;
s = tp - > rcl_walk ?
2006-07-12 18:41:55 +04:00
list_entry ( tp - > rcl_walk , struct string , list ) : NULL ;
2005-04-17 02:20:36 +04:00
if ( tp - > rcl_walk ) {
s = list_entry ( tp - > rcl_walk , struct string , list ) ;
tty3270_update_prompt ( tp , s - > string , s - > len ) ;
} else
2006-07-12 18:41:55 +04:00
tty3270_update_prompt ( tp , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
tty3270_set_timer ( tp , 1 ) ;
}
spin_unlock_bh ( & tp - > view . lock ) ;
}
/*
* Deactivate tty view .
*/
static void
tty3270_exit_tty ( struct kbd_data * kbd )
{
2012-04-02 15:54:18 +04:00
struct tty3270 * tp = container_of ( kbd - > port , struct tty3270 , port ) ;
2005-04-17 02:20:36 +04:00
raw3270_deactivate_view ( & tp - > view ) ;
}
/*
* Scroll forward in history .
*/
static void
tty3270_scroll_forward ( struct kbd_data * kbd )
{
2012-04-02 15:54:18 +04:00
struct tty3270 * tp = container_of ( kbd - > port , struct tty3270 , port ) ;
2005-04-17 02:20:36 +04:00
int nr_up ;
spin_lock_bh ( & tp - > view . lock ) ;
nr_up = tp - > nr_up - tp - > view . rows + 2 ;
if ( nr_up < 0 )
nr_up = 0 ;
if ( nr_up ! = tp - > nr_up ) {
tp - > nr_up = nr_up ;
tty3270_rebuild_update ( tp ) ;
tty3270_update_status ( tp ) ;
tty3270_set_timer ( tp , 1 ) ;
}
spin_unlock_bh ( & tp - > view . lock ) ;
}
/*
* Scroll backward in history .
*/
static void
tty3270_scroll_backward ( struct kbd_data * kbd )
{
2012-04-02 15:54:18 +04:00
struct tty3270 * tp = container_of ( kbd - > port , struct tty3270 , port ) ;
2005-04-17 02:20:36 +04:00
int nr_up ;
spin_lock_bh ( & tp - > view . lock ) ;
nr_up = tp - > nr_up + tp - > view . rows - 2 ;
if ( nr_up + tp - > view . rows - 2 > tp - > nr_lines )
nr_up = tp - > nr_lines - tp - > view . rows + 2 ;
if ( nr_up ! = tp - > nr_up ) {
tp - > nr_up = nr_up ;
tty3270_rebuild_update ( tp ) ;
tty3270_update_status ( tp ) ;
tty3270_set_timer ( tp , 1 ) ;
}
spin_unlock_bh ( & tp - > view . lock ) ;
}
/*
* Pass input line to tty .
*/
static void
tty3270_read_tasklet ( struct raw3270_request * rrq )
{
static char kreset_data = TW_KR ;
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( rrq - > view , struct tty3270 , view ) ;
2005-04-17 02:20:36 +04:00
char * input ;
int len ;
spin_lock_bh ( & tp - > view . lock ) ;
/*
* Two AID keys are special : For 0x7d ( enter ) the input line
* has to be emitted to the tty and for 0x6d the screen
* needs to be redrawn .
*/
2006-07-12 18:41:55 +04:00
input = NULL ;
2005-04-17 02:20:36 +04:00
len = 0 ;
if ( tp - > input - > string [ 0 ] = = 0x7d ) {
/* Enter: write input to tty. */
input = tp - > input - > string + 6 ;
len = tp - > input - > len - 6 - rrq - > rescnt ;
if ( tp - > inattr ! = TF_INPUTN )
tty3270_rcl_add ( tp , input , len ) ;
if ( tp - > nr_up > 0 ) {
tp - > nr_up = 0 ;
tty3270_rebuild_update ( tp ) ;
tty3270_update_status ( tp ) ;
}
/* Clear input area. */
2006-07-12 18:41:55 +04:00
tty3270_update_prompt ( tp , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
tty3270_set_timer ( tp , 1 ) ;
} else if ( tp - > input - > string [ 0 ] = = 0x6d ) {
/* Display has been cleared. Redraw. */
tp - > update_flags = TTY_UPDATE_ALL ;
tty3270_set_timer ( tp , 1 ) ;
}
spin_unlock_bh ( & tp - > view . lock ) ;
/* Start keyboard reset command. */
raw3270_request_reset ( tp - > kreset ) ;
raw3270_request_set_cmd ( tp - > kreset , TC_WRITE ) ;
raw3270_request_add_data ( tp - > kreset , & kreset_data , 1 ) ;
raw3270_start ( & tp - > view , tp - > kreset ) ;
2012-04-02 15:54:18 +04:00
while ( len - - > 0 )
kbd_keycode ( tp - > kbd , * input + + ) ;
/* Emit keycode for AID byte. */
kbd_keycode ( tp - > kbd , 256 + tp - > input - > string [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
raw3270_request_reset ( rrq ) ;
xchg ( & tp - > read , rrq ) ;
raw3270_put_view ( & tp - > view ) ;
}
/*
* Read request completion callback .
*/
static void
tty3270_read_callback ( struct raw3270_request * rq , void * data )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( rq - > view , struct tty3270 , view ) ;
2005-04-17 02:20:36 +04:00
raw3270_get_view ( rq - > view ) ;
/* Schedule tasklet to pass input to tty. */
2012-04-02 15:54:16 +04:00
tasklet_schedule ( & tp - > readlet ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Issue a read request . Call with device lock .
*/
static void
tty3270_issue_read ( struct tty3270 * tp , int lock )
{
struct raw3270_request * rrq ;
int rc ;
rrq = xchg ( & tp - > read , 0 ) ;
if ( ! rrq )
/* Read already scheduled. */
return ;
rrq - > callback = tty3270_read_callback ;
rrq - > callback_data = tp ;
raw3270_request_set_cmd ( rrq , TC_READMOD ) ;
raw3270_request_set_data ( rrq , tp - > input - > string , tp - > input - > len ) ;
/* Issue the read modified request. */
if ( lock ) {
rc = raw3270_start ( & tp - > view , rrq ) ;
} else
rc = raw3270_start_irq ( & tp - > view , rrq ) ;
if ( rc ) {
raw3270_request_reset ( rrq ) ;
xchg ( & tp - > read , rrq ) ;
}
}
/*
* Switch to the tty view .
*/
static int
tty3270_activate ( struct raw3270_view * view )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( view , struct tty3270 , view ) ;
2005-04-17 02:20:36 +04:00
tp - > update_flags = TTY_UPDATE_ALL ;
tty3270_set_timer ( tp , 1 ) ;
return 0 ;
}
static void
tty3270_deactivate ( struct raw3270_view * view )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( view , struct tty3270 , view ) ;
2009-06-12 12:26:31 +04:00
del_timer ( & tp - > timer ) ;
2005-04-17 02:20:36 +04:00
}
static int
tty3270_irq ( struct tty3270 * tp , struct raw3270_request * rq , struct irb * irb )
{
/* Handle ATTN. Schedule tasklet to read aid. */
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION ) {
2005-04-17 02:20:36 +04:00
if ( ! tp - > throttle )
tty3270_issue_read ( tp , 0 ) ;
else
tp - > attn = 1 ;
}
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 ;
}
/*
* Allocate tty3270 structure .
*/
static struct tty3270 *
tty3270_alloc_view ( void )
{
struct tty3270 * tp ;
int pages ;
2006-03-24 14:15:31 +03:00
tp = kzalloc ( sizeof ( struct tty3270 ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! tp )
goto out_err ;
tp - > freemem_pages =
kmalloc ( sizeof ( void * ) * TTY3270_STRING_PAGES , GFP_KERNEL ) ;
if ( ! tp - > freemem_pages )
goto out_tp ;
INIT_LIST_HEAD ( & tp - > freemem ) ;
2012-04-02 15:54:15 +04:00
INIT_LIST_HEAD ( & tp - > lines ) ;
INIT_LIST_HEAD ( & tp - > update ) ;
INIT_LIST_HEAD ( & tp - > rcl_lines ) ;
tp - > rcl_max = 20 ;
2005-04-17 02:20:36 +04:00
for ( pages = 0 ; pages < TTY3270_STRING_PAGES ; pages + + ) {
tp - > freemem_pages [ pages ] = ( void * )
__get_free_pages ( GFP_KERNEL | GFP_DMA , 0 ) ;
if ( ! tp - > freemem_pages [ pages ] )
goto out_pages ;
add_string_memory ( & tp - > freemem ,
tp - > freemem_pages [ pages ] , PAGE_SIZE ) ;
}
tp - > write = raw3270_request_alloc ( TTY3270_OUTPUT_BUFFER_SIZE ) ;
2005-10-31 02:00:10 +03:00
if ( IS_ERR ( tp - > write ) )
2005-04-17 02:20:36 +04:00
goto out_pages ;
tp - > read = raw3270_request_alloc ( 0 ) ;
2005-10-31 02:00:10 +03:00
if ( IS_ERR ( tp - > read ) )
2005-04-17 02:20:36 +04:00
goto out_write ;
tp - > kreset = raw3270_request_alloc ( 1 ) ;
2005-10-31 02:00:10 +03:00
if ( IS_ERR ( tp - > kreset ) )
2005-04-17 02:20:36 +04:00
goto out_read ;
tp - > kbd = kbd_alloc ( ) ;
if ( ! tp - > kbd )
goto out_reset ;
2013-01-08 18:20:05 +04:00
tty_port_init ( & tp - > port ) ;
setup_timer ( & tp - > timer , ( void ( * ) ( unsigned long ) ) tty3270_update ,
( unsigned long ) tp ) ;
tasklet_init ( & tp - > readlet ,
( void ( * ) ( unsigned long ) ) tty3270_read_tasklet ,
( unsigned long ) tp - > read ) ;
2013-01-04 17:55:13 +04:00
INIT_WORK ( & tp - > resize_work , tty3270_resize_work ) ;
2013-01-08 18:20:05 +04:00
2005-04-17 02:20:36 +04:00
return tp ;
out_reset :
raw3270_request_free ( tp - > kreset ) ;
out_read :
raw3270_request_free ( tp - > read ) ;
out_write :
raw3270_request_free ( tp - > write ) ;
out_pages :
while ( pages - - )
free_pages ( ( unsigned long ) tp - > freemem_pages [ pages ] , 0 ) ;
kfree ( tp - > freemem_pages ) ;
2012-11-15 12:49:56 +04:00
tty_port_destroy ( & tp - > port ) ;
2005-04-17 02:20:36 +04:00
out_tp :
kfree ( tp ) ;
out_err :
return ERR_PTR ( - ENOMEM ) ;
}
/*
* Free tty3270 structure .
*/
static void
tty3270_free_view ( struct tty3270 * tp )
{
int pages ;
kbd_free ( tp - > kbd ) ;
raw3270_request_free ( tp - > kreset ) ;
raw3270_request_free ( tp - > read ) ;
raw3270_request_free ( tp - > write ) ;
for ( pages = 0 ; pages < TTY3270_STRING_PAGES ; pages + + )
free_pages ( ( unsigned long ) tp - > freemem_pages [ pages ] , 0 ) ;
kfree ( tp - > freemem_pages ) ;
2012-11-15 12:49:56 +04:00
tty_port_destroy ( & tp - > port ) ;
2005-04-17 02:20:36 +04:00
kfree ( tp ) ;
}
/*
* Allocate tty3270 screen .
*/
2013-01-04 17:55:13 +04:00
static struct tty3270_line *
tty3270_alloc_screen ( unsigned int rows , unsigned int cols )
2005-04-17 02:20:36 +04:00
{
2013-01-04 17:55:13 +04:00
struct tty3270_line * screen ;
2005-04-17 02:20:36 +04:00
unsigned long size ;
int lines ;
2013-01-04 17:55:13 +04:00
size = sizeof ( struct tty3270_line ) * ( rows - 2 ) ;
screen = kzalloc ( size , GFP_KERNEL ) ;
if ( ! screen )
2005-04-17 02:20:36 +04:00
goto out_err ;
2013-01-04 17:55:13 +04:00
for ( lines = 0 ; lines < rows - 2 ; lines + + ) {
size = sizeof ( struct tty3270_cell ) * cols ;
screen [ lines ] . cells = kzalloc ( size , GFP_KERNEL ) ;
if ( ! screen [ lines ] . cells )
2005-04-17 02:20:36 +04:00
goto out_screen ;
}
2013-01-04 17:55:13 +04:00
return screen ;
2005-04-17 02:20:36 +04:00
out_screen :
while ( lines - - )
2013-01-04 17:55:13 +04:00
kfree ( screen [ lines ] . cells ) ;
kfree ( screen ) ;
2005-04-17 02:20:36 +04:00
out_err :
2013-01-04 17:55:13 +04:00
return ERR_PTR ( - ENOMEM ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Free tty3270 screen .
*/
static void
2013-01-04 17:55:13 +04:00
tty3270_free_screen ( struct tty3270_line * screen , unsigned int rows )
2005-04-17 02:20:36 +04:00
{
int lines ;
2013-01-04 17:55:13 +04:00
for ( lines = 0 ; lines < rows - 2 ; lines + + )
kfree ( screen [ lines ] . cells ) ;
kfree ( screen ) ;
}
/*
* Resize tty3270 screen
*/
static void tty3270_resize_work ( struct work_struct * work )
{
struct tty3270 * tp = container_of ( work , struct tty3270 , resize_work ) ;
struct tty3270_line * screen , * oscreen ;
struct tty_struct * tty ;
unsigned int orows ;
struct winsize ws ;
screen = tty3270_alloc_screen ( tp - > n_rows , tp - > n_cols ) ;
2013-09-11 02:49:20 +04:00
if ( IS_ERR ( screen ) )
2013-01-04 17:55:13 +04:00
return ;
/* Switch to new output size */
spin_lock_bh ( & tp - > view . lock ) ;
oscreen = tp - > screen ;
orows = tp - > view . rows ;
tp - > view . model = tp - > n_model ;
tp - > view . rows = tp - > n_rows ;
tp - > view . cols = tp - > n_cols ;
tp - > screen = screen ;
free_string ( & tp - > freemem , tp - > prompt ) ;
free_string ( & tp - > freemem , tp - > status ) ;
tty3270_create_prompt ( tp ) ;
tty3270_create_status ( tp ) ;
tp - > nr_up = 0 ;
while ( tp - > nr_lines < tp - > view . rows - 2 )
tty3270_blank_line ( tp ) ;
tp - > update_flags = TTY_UPDATE_ALL ;
spin_unlock_bh ( & tp - > view . lock ) ;
tty3270_free_screen ( oscreen , orows ) ;
tty3270_set_timer ( tp , 1 ) ;
/* Informat tty layer about new size */
tty = tty_port_tty_get ( & tp - > port ) ;
if ( ! tty )
return ;
ws . ws_row = tp - > view . rows - 2 ;
ws . ws_col = tp - > view . cols ;
tty_do_resize ( tty , & ws ) ;
}
static void
tty3270_resize ( struct raw3270_view * view , int model , int rows , int cols )
{
struct tty3270 * tp = container_of ( view , struct tty3270 , view ) ;
tp - > n_model = model ;
tp - > n_rows = rows ;
tp - > n_cols = cols ;
schedule_work ( & tp - > resize_work ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Unlink tty3270 data structure from tty .
*/
static void
tty3270_release ( struct raw3270_view * view )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( view , struct tty3270 , view ) ;
2012-04-02 15:54:18 +04:00
struct tty_struct * tty = tty_port_tty_get ( & tp - > port ) ;
2005-04-17 02:20:36 +04:00
if ( tty ) {
2006-07-12 18:41:55 +04:00
tty - > driver_data = NULL ;
2012-04-02 15:54:18 +04:00
tty_port_tty_set ( & tp - > port , NULL ) ;
2005-04-17 02:20:36 +04:00
tty_hangup ( tty ) ;
raw3270_put_view ( & tp - > view ) ;
2012-04-02 15:54:18 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Free tty3270 data structure
*/
static void
tty3270_free ( struct raw3270_view * view )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = container_of ( view , struct tty3270 , view ) ;
2013-01-04 17:55:13 +04:00
2013-12-04 17:29:11 +04:00
del_timer_sync ( & tp - > timer ) ;
2013-01-04 17:55:13 +04:00
tty3270_free_screen ( tp - > screen , tp - > view . rows ) ;
2012-04-02 15:54:16 +04:00
tty3270_free_view ( tp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Delayed freeing of tty3270 views .
*/
static void
tty3270_del_views ( void )
{
int i ;
2013-01-08 18:31:11 +04:00
for ( i = RAW3270_FIRSTMINOR ; i < = tty3270_max_index ; i + + ) {
struct raw3270_view * view = raw3270_find_view ( & tty3270_fn , i ) ;
2012-04-02 15:54:16 +04:00
if ( ! IS_ERR ( view ) )
raw3270_del_view ( view ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-02-05 23:16:47 +03:00
static struct raw3270_fn tty3270_fn = {
2005-04-17 02:20:36 +04:00
. activate = tty3270_activate ,
. deactivate = tty3270_deactivate ,
. intv = ( void * ) tty3270_irq ,
. release = tty3270_release ,
2013-01-04 17:55:13 +04:00
. free = tty3270_free ,
. resize = tty3270_resize
2005-04-17 02:20:36 +04:00
} ;
/*
2012-08-07 23:48:03 +04:00
* This routine is called whenever a 3270 tty is opened first time .
2005-04-17 02:20:36 +04:00
*/
2012-08-07 23:48:03 +04:00
static int tty3270_install ( struct tty_driver * driver , struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2012-04-02 15:54:16 +04:00
struct raw3270_view * view ;
2005-04-17 02:20:36 +04:00
struct tty3270 * tp ;
int i , rc ;
/* Check if the tty3270 is already there. */
2013-03-21 16:07:18 +04:00
view = raw3270_find_view ( & tty3270_fn , tty - > index + RAW3270_FIRSTMINOR ) ;
2012-04-02 15:54:16 +04:00
if ( ! IS_ERR ( view ) ) {
tp = container_of ( view , struct tty3270 , view ) ;
2005-04-17 02:20:36 +04:00
tty - > driver_data = tp ;
tty - > winsize . ws_row = tp - > view . rows - 2 ;
tty - > winsize . ws_col = tp - > view . cols ;
2013-01-03 18:53:05 +04:00
tp - > port . low_latency = 0 ;
2012-04-02 15:54:18 +04:00
/* why to reassign? */
tty_port_tty_set ( & tp - > port , tty ) ;
2005-04-17 02:20:36 +04:00
tp - > inattr = TF_INPUT ;
2012-08-07 23:48:03 +04:00
return tty_port_install ( & tp - > port , driver , tty ) ;
2005-04-17 02:20:36 +04:00
}
2013-03-21 16:07:18 +04:00
if ( tty3270_max_index < tty - > index + 1 )
tty3270_max_index = tty - > index + 1 ;
2005-04-17 02:20:36 +04:00
/* Allocate tty3270 structure on first open. */
tp = tty3270_alloc_view ( ) ;
if ( IS_ERR ( tp ) )
return PTR_ERR ( tp ) ;
2013-03-21 16:07:18 +04:00
rc = raw3270_add_view ( & tp - > view , & tty3270_fn ,
tty - > index + RAW3270_FIRSTMINOR ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
tty3270_free_view ( tp ) ;
return rc ;
}
2013-12-18 17:36:18 +04:00
tp - > screen = tty3270_alloc_screen ( tp - > view . rows , tp - > view . cols ) ;
2013-01-04 17:55:13 +04:00
if ( IS_ERR ( tp - > screen ) ) {
rc = PTR_ERR ( tp - > screen ) ;
2005-04-17 02:20:36 +04:00
raw3270_put_view ( & tp - > view ) ;
2005-10-31 02:00:10 +03:00
raw3270_del_view ( & tp - > view ) ;
2013-01-04 17:55:13 +04:00
tty3270_free_view ( tp ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2012-04-02 15:54:18 +04:00
tty_port_tty_set ( & tp - > port , tty ) ;
2013-01-03 18:53:05 +04:00
tp - > port . low_latency = 0 ;
2005-04-17 02:20:36 +04:00
tty - > winsize . ws_row = tp - > view . rows - 2 ;
tty - > winsize . ws_col = tp - > view . cols ;
tty3270_create_prompt ( tp ) ;
tty3270_create_status ( tp ) ;
tty3270_update_status ( tp ) ;
/* Create blank line for every line in the tty output area. */
for ( i = 0 ; i < tp - > view . rows - 2 ; i + + )
tty3270_blank_line ( tp ) ;
2012-04-02 15:54:18 +04:00
tp - > kbd - > port = & tp - > port ;
2005-04-17 02:20:36 +04:00
tp - > kbd - > fn_handler [ KVAL ( K_INCRCONSOLE ) ] = tty3270_exit_tty ;
tp - > kbd - > fn_handler [ KVAL ( K_SCROLLBACK ) ] = tty3270_scroll_backward ;
tp - > kbd - > fn_handler [ KVAL ( K_SCROLLFORW ) ] = tty3270_scroll_forward ;
tp - > kbd - > fn_handler [ KVAL ( K_CONS ) ] = tty3270_rcl_backward ;
kbd_ascebc ( tp - > kbd , tp - > view . ascebc ) ;
raw3270_activate_view ( & tp - > view ) ;
2012-08-07 23:48:03 +04:00
rc = tty_port_install ( & tp - > port , driver , tty ) ;
if ( rc ) {
raw3270_put_view ( & tp - > view ) ;
return rc ;
}
tty - > driver_data = tp ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2013-01-08 18:15:12 +04:00
/*
* This routine is called whenever a 3270 tty is opened .
*/
static int
tty3270_open ( struct tty_struct * tty , struct file * filp )
{
struct tty3270 * tp = tty - > driver_data ;
struct tty_port * port = & tp - > port ;
port - > count + + ;
tty_port_tty_set ( port , tty ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* This routine is called when the 3270 tty is closed . We wait
* for the remaining request to be completed . Then we clean up .
*/
static void
tty3270_close ( struct tty_struct * tty , struct file * filp )
{
2012-04-02 15:54:16 +04:00
struct tty3270 * tp = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
if ( tty - > count > 1 )
return ;
if ( tp ) {
2006-07-12 18:41:55 +04:00
tty - > driver_data = NULL ;
2012-04-02 15:54:18 +04:00
tty_port_tty_set ( & tp - > port , NULL ) ;
2005-04-17 02:20:36 +04:00
}
}
2012-08-07 23:48:03 +04:00
static void tty3270_cleanup ( struct tty_struct * tty )
{
struct tty3270 * tp = tty - > driver_data ;
if ( tp )
raw3270_put_view ( & tp - > view ) ;
}
2005-04-17 02:20:36 +04:00
/*
* We always have room .
*/
static int
tty3270_write_room ( struct tty_struct * tty )
{
return INT_MAX ;
}
/*
* Insert character into the screen at the current position with the
* current color and highlight . This function does NOT do cursor movement .
*/
2008-05-07 11:22:58 +04:00
static void tty3270_put_character ( struct tty3270 * tp , char ch )
2005-04-17 02:20:36 +04:00
{
struct tty3270_line * line ;
struct tty3270_cell * cell ;
line = tp - > screen + tp - > cy ;
if ( line - > len < = tp - > cx ) {
while ( line - > len < tp - > cx ) {
cell = line - > cells + line - > len ;
cell - > character = tp - > view . ascebc [ ' ' ] ;
cell - > highlight = tp - > highlight ;
cell - > f_color = tp - > f_color ;
line - > len + + ;
}
line - > len + + ;
}
cell = line - > cells + tp - > cx ;
cell - > character = tp - > view . ascebc [ ( unsigned int ) ch ] ;
cell - > highlight = tp - > highlight ;
cell - > f_color = tp - > f_color ;
}
/*
* Convert a tty3270_line to a 3270 data fragment usable for output .
*/
static void
tty3270_convert_line ( struct tty3270 * tp , int line_nr )
{
struct tty3270_line * line ;
struct tty3270_cell * cell ;
struct string * s , * n ;
unsigned char highlight ;
unsigned char f_color ;
char * cp ;
int flen , i ;
/* Determine how long the fragment will be. */
flen = 3 ; /* Prefix (TO_SBA). */
line = tp - > screen + line_nr ;
flen + = line - > len ;
highlight = TAX_RESET ;
f_color = TAC_RESET ;
for ( i = 0 , cell = line - > cells ; i < line - > len ; i + + , cell + + ) {
if ( cell - > highlight ! = highlight ) {
flen + = 3 ; /* TO_SA to switch highlight. */
highlight = cell - > highlight ;
}
if ( cell - > f_color ! = f_color ) {
flen + = 3 ; /* TO_SA to switch color. */
f_color = cell - > f_color ;
}
}
if ( highlight ! = TAX_RESET )
flen + = 3 ; /* TO_SA to reset hightlight. */
if ( f_color ! = TAC_RESET )
flen + = 3 ; /* TO_SA to reset color. */
if ( line - > len < tp - > view . cols )
flen + = 4 ; /* Postfix (TO_RA). */
/* Find the line in the list. */
i = tp - > view . rows - 2 - line_nr ;
list_for_each_entry_reverse ( s , & tp - > lines , list )
if ( - - i < = 0 )
break ;
/*
* Check if the line needs to get reallocated .
*/
if ( s - > len ! = flen ) {
/* Reallocate string. */
n = tty3270_alloc_string ( tp , flen ) ;
list_add ( & n - > list , & s - > list ) ;
list_del_init ( & s - > list ) ;
if ( ! list_empty ( & s - > update ) )
list_del_init ( & s - > update ) ;
free_string ( & tp - > freemem , s ) ;
s = n ;
}
/* Write 3270 data fragment. */
cp = s - > string ;
* cp + + = TO_SBA ;
* cp + + = 0 ;
* cp + + = 0 ;
highlight = TAX_RESET ;
f_color = TAC_RESET ;
for ( i = 0 , cell = line - > cells ; i < line - > len ; i + + , cell + + ) {
if ( cell - > highlight ! = highlight ) {
* cp + + = TO_SA ;
* cp + + = TAT_EXTHI ;
* cp + + = cell - > highlight ;
highlight = cell - > highlight ;
}
if ( cell - > f_color ! = f_color ) {
* cp + + = TO_SA ;
* cp + + = TAT_COLOR ;
* cp + + = cell - > f_color ;
f_color = cell - > f_color ;
}
* cp + + = cell - > character ;
}
if ( highlight ! = TAX_RESET ) {
* cp + + = TO_SA ;
* cp + + = TAT_EXTHI ;
* cp + + = TAX_RESET ;
}
if ( f_color ! = TAC_RESET ) {
* cp + + = TO_SA ;
* cp + + = TAT_COLOR ;
* cp + + = TAC_RESET ;
}
if ( line - > len < tp - > view . cols ) {
* cp + + = TO_RA ;
* cp + + = 0 ;
* cp + + = 0 ;
* cp + + = 0 ;
}
if ( tp - > nr_up + line_nr < tp - > view . rows - 2 ) {
/* Line is currently visible on screen. */
tty3270_update_string ( tp , s , line_nr ) ;
/* Add line to update list. */
if ( list_empty ( & s - > update ) ) {
list_add_tail ( & s - > update , & tp - > update ) ;
tp - > update_flags | = TTY_UPDATE_LIST ;
}
}
}
/*
* Do carriage return .
*/
static void
tty3270_cr ( struct tty3270 * tp )
{
tp - > cx = 0 ;
}
/*
* Do line feed .
*/
static void
tty3270_lf ( struct tty3270 * tp )
{
struct tty3270_line temp ;
int i ;
tty3270_convert_line ( tp , tp - > cy ) ;
if ( tp - > cy < tp - > view . rows - 3 ) {
tp - > cy + + ;
return ;
}
/* Last line just filled up. Add new, blank line. */
tty3270_blank_line ( tp ) ;
temp = tp - > screen [ 0 ] ;
temp . len = 0 ;
for ( i = 0 ; i < tp - > view . rows - 3 ; i + + )
tp - > screen [ i ] = tp - > screen [ i + 1 ] ;
tp - > screen [ tp - > view . rows - 3 ] = temp ;
tty3270_rebuild_update ( tp ) ;
}
static void
tty3270_ri ( struct tty3270 * tp )
{
if ( tp - > cy > 0 ) {
tty3270_convert_line ( tp , tp - > cy ) ;
tp - > cy - - ;
}
}
/*
* Insert characters at current position .
*/
static void
tty3270_insert_characters ( struct tty3270 * tp , int n )
{
struct tty3270_line * line ;
int k ;
line = tp - > screen + tp - > cy ;
while ( line - > len < tp - > cx ) {
line - > cells [ line - > len ] . character = tp - > view . ascebc [ ' ' ] ;
line - > cells [ line - > len ] . highlight = TAX_RESET ;
line - > cells [ line - > len ] . f_color = TAC_RESET ;
line - > len + + ;
}
if ( n > tp - > view . cols - tp - > cx )
n = tp - > view . cols - tp - > cx ;
k = min_t ( int , line - > len - tp - > cx , tp - > view . cols - tp - > cx - n ) ;
while ( k - - )
line - > cells [ tp - > cx + n + k ] = line - > cells [ tp - > cx + k ] ;
line - > len + = n ;
if ( line - > len > tp - > view . cols )
line - > len = tp - > view . cols ;
while ( n - - > 0 ) {
line - > cells [ tp - > cx + n ] . character = tp - > view . ascebc [ ' ' ] ;
line - > cells [ tp - > cx + n ] . highlight = tp - > highlight ;
line - > cells [ tp - > cx + n ] . f_color = tp - > f_color ;
}
}
/*
* Delete characters at current position .
*/
static void
tty3270_delete_characters ( struct tty3270 * tp , int n )
{
struct tty3270_line * line ;
int i ;
line = tp - > screen + tp - > cy ;
if ( line - > len < = tp - > cx )
return ;
if ( line - > len - tp - > cx < = n ) {
line - > len = tp - > cx ;
return ;
}
for ( i = tp - > cx ; i + n < line - > len ; i + + )
line - > cells [ i ] = line - > cells [ i + n ] ;
line - > len - = n ;
}
/*
* Erase characters at current position .
*/
static void
tty3270_erase_characters ( struct tty3270 * tp , int n )
{
struct tty3270_line * line ;
struct tty3270_cell * cell ;
line = tp - > screen + tp - > cy ;
while ( line - > len > tp - > cx & & n - - > 0 ) {
cell = line - > cells + tp - > cx + + ;
cell - > character = ' ' ;
cell - > highlight = TAX_RESET ;
cell - > f_color = TAC_RESET ;
}
tp - > cx + = n ;
tp - > cx = min_t ( int , tp - > cx , tp - > view . cols - 1 ) ;
}
/*
* Erase line , 3 different cases :
* Esc [ 0 K Erase from current position to end of line inclusive
* Esc [ 1 K Erase from beginning of line to current position inclusive
* Esc [ 2 K Erase entire line ( without moving cursor )
*/
static void
tty3270_erase_line ( struct tty3270 * tp , int mode )
{
struct tty3270_line * line ;
struct tty3270_cell * cell ;
int i ;
line = tp - > screen + tp - > cy ;
if ( mode = = 0 )
line - > len = tp - > cx ;
else if ( mode = = 1 ) {
for ( i = 0 ; i < tp - > cx ; i + + ) {
cell = line - > cells + i ;
cell - > character = ' ' ;
cell - > highlight = TAX_RESET ;
cell - > f_color = TAC_RESET ;
}
if ( line - > len < = tp - > cx )
line - > len = tp - > cx + 1 ;
} else if ( mode = = 2 )
line - > len = 0 ;
tty3270_convert_line ( tp , tp - > cy ) ;
}
/*
* Erase display , 3 different cases :
* Esc [ 0 J Erase from current position to bottom of screen inclusive
* Esc [ 1 J Erase from top of screen to current position inclusive
* Esc [ 2 J Erase entire screen ( without moving the cursor )
*/
static void
tty3270_erase_display ( struct tty3270 * tp , int mode )
{
int i ;
if ( mode = = 0 ) {
tty3270_erase_line ( tp , 0 ) ;
for ( i = tp - > cy + 1 ; i < tp - > view . rows - 2 ; i + + ) {
tp - > screen [ i ] . len = 0 ;
tty3270_convert_line ( tp , i ) ;
}
} else if ( mode = = 1 ) {
for ( i = 0 ; i < tp - > cy ; i + + ) {
tp - > screen [ i ] . len = 0 ;
tty3270_convert_line ( tp , i ) ;
}
tty3270_erase_line ( tp , 1 ) ;
} else if ( mode = = 2 ) {
for ( i = 0 ; i < tp - > view . rows - 2 ; i + + ) {
tp - > screen [ i ] . len = 0 ;
tty3270_convert_line ( tp , i ) ;
}
}
tty3270_rebuild_update ( tp ) ;
}
/*
* Set attributes found in an escape sequence .
* Esc [ < attr > ; < attr > ; . . . m
*/
static void
tty3270_set_attributes ( struct tty3270 * tp )
{
static unsigned char f_colors [ ] = {
TAC_DEFAULT , TAC_RED , TAC_GREEN , TAC_YELLOW , TAC_BLUE ,
TAC_PINK , TAC_TURQ , TAC_WHITE , 0 , TAC_DEFAULT
} ;
int i , attr ;
for ( i = 0 ; i < = tp - > esc_npar ; i + + ) {
attr = tp - > esc_par [ i ] ;
switch ( attr ) {
case 0 : /* Reset */
tp - > highlight = TAX_RESET ;
tp - > f_color = TAC_RESET ;
break ;
/* Highlight. */
case 4 : /* Start underlining. */
tp - > highlight = TAX_UNDER ;
break ;
case 5 : /* Start blink. */
tp - > highlight = TAX_BLINK ;
break ;
case 7 : /* Start reverse. */
tp - > highlight = TAX_REVER ;
break ;
case 24 : /* End underlining */
if ( tp - > highlight = = TAX_UNDER )
tp - > highlight = TAX_RESET ;
break ;
case 25 : /* End blink. */
if ( tp - > highlight = = TAX_BLINK )
tp - > highlight = TAX_RESET ;
break ;
case 27 : /* End reverse. */
if ( tp - > highlight = = TAX_REVER )
tp - > highlight = TAX_RESET ;
break ;
/* Foreground color. */
case 30 : /* Black */
case 31 : /* Red */
case 32 : /* Green */
case 33 : /* Yellow */
case 34 : /* Blue */
case 35 : /* Magenta */
case 36 : /* Cyan */
case 37 : /* White */
case 39 : /* Black */
tp - > f_color = f_colors [ attr - 30 ] ;
break ;
}
}
}
static inline int
tty3270_getpar ( struct tty3270 * tp , int ix )
{
return ( tp - > esc_par [ ix ] > 0 ) ? tp - > esc_par [ ix ] : 1 ;
}
static void
tty3270_goto_xy ( struct tty3270 * tp , int cx , int cy )
{
2007-10-12 18:11:35 +04:00
int max_cx = max ( 0 , cx ) ;
int max_cy = max ( 0 , cy ) ;
tp - > cx = min_t ( int , tp - > view . cols - 1 , max_cx ) ;
cy = min_t ( int , tp - > view . rows - 3 , max_cy ) ;
2005-04-17 02:20:36 +04:00
if ( cy ! = tp - > cy ) {
tty3270_convert_line ( tp , tp - > cy ) ;
tp - > cy = cy ;
}
}
/*
* Process escape sequences . Known sequences :
* Esc 7 Save Cursor Position
* Esc 8 Restore Cursor Position
* Esc [ Pn ; Pn ; . . m Set attributes
* Esc [ Pn ; Pn H Cursor Position
* Esc [ Pn ; Pn f Cursor Position
* Esc [ Pn A Cursor Up
* Esc [ Pn B Cursor Down
* Esc [ Pn C Cursor Forward
* Esc [ Pn D Cursor Backward
* Esc [ Pn G Cursor Horizontal Absolute
* Esc [ Pn X Erase Characters
* Esc [ Ps J Erase in Display
* Esc [ Ps K Erase in Line
* // FIXME: add all the new ones.
*
* Pn is a numeric parameter , a string of zero or more decimal digits .
* Ps is a selective parameter .
*/
static void
tty3270_escape_sequence ( struct tty3270 * tp , char ch )
{
enum { ESnormal , ESesc , ESsquare , ESgetpars } ;
if ( tp - > esc_state = = ESnormal ) {
if ( ch = = 0x1b )
/* Starting new escape sequence. */
tp - > esc_state = ESesc ;
return ;
}
if ( tp - > esc_state = = ESesc ) {
tp - > esc_state = ESnormal ;
switch ( ch ) {
case ' [ ' :
tp - > esc_state = ESsquare ;
break ;
case ' E ' :
tty3270_cr ( tp ) ;
tty3270_lf ( tp ) ;
break ;
case ' M ' :
tty3270_ri ( tp ) ;
break ;
case ' D ' :
tty3270_lf ( tp ) ;
break ;
case ' Z ' : /* Respond ID. */
2012-04-02 15:54:18 +04:00
kbd_puts_queue ( & tp - > port , " \033 [?6c " ) ;
2005-04-17 02:20:36 +04:00
break ;
case ' 7 ' : /* Save cursor position. */
tp - > saved_cx = tp - > cx ;
tp - > saved_cy = tp - > cy ;
tp - > saved_highlight = tp - > highlight ;
tp - > saved_f_color = tp - > f_color ;
break ;
case ' 8 ' : /* Restore cursor position. */
tty3270_convert_line ( tp , tp - > cy ) ;
tty3270_goto_xy ( tp , tp - > saved_cx , tp - > saved_cy ) ;
tp - > highlight = tp - > saved_highlight ;
tp - > f_color = tp - > saved_f_color ;
break ;
case ' c ' : /* Reset terminal. */
tp - > cx = tp - > saved_cx = 0 ;
tp - > cy = tp - > saved_cy = 0 ;
tp - > highlight = tp - > saved_highlight = TAX_RESET ;
tp - > f_color = tp - > saved_f_color = TAC_RESET ;
tty3270_erase_display ( tp , 2 ) ;
break ;
}
return ;
}
if ( tp - > esc_state = = ESsquare ) {
tp - > esc_state = ESgetpars ;
memset ( tp - > esc_par , 0 , sizeof ( tp - > esc_par ) ) ;
tp - > esc_npar = 0 ;
tp - > esc_ques = ( ch = = ' ? ' ) ;
if ( tp - > esc_ques )
return ;
}
if ( tp - > esc_state = = ESgetpars ) {
if ( ch = = ' ; ' & & tp - > esc_npar < ESCAPE_NPAR - 1 ) {
tp - > esc_npar + + ;
return ;
}
if ( ch > = ' 0 ' & & ch < = ' 9 ' ) {
tp - > esc_par [ tp - > esc_npar ] * = 10 ;
tp - > esc_par [ tp - > esc_npar ] + = ch - ' 0 ' ;
return ;
}
}
tp - > esc_state = ESnormal ;
if ( ch = = ' n ' & & ! tp - > esc_ques ) {
if ( tp - > esc_par [ 0 ] = = 5 ) /* Status report. */
2012-04-02 15:54:18 +04:00
kbd_puts_queue ( & tp - > port , " \033 [0n " ) ;
2005-04-17 02:20:36 +04:00
else if ( tp - > esc_par [ 0 ] = = 6 ) { /* Cursor report. */
char buf [ 40 ] ;
sprintf ( buf , " \033 [%d;%dR " , tp - > cy + 1 , tp - > cx + 1 ) ;
2012-04-02 15:54:18 +04:00
kbd_puts_queue ( & tp - > port , buf ) ;
2005-04-17 02:20:36 +04:00
}
return ;
}
if ( tp - > esc_ques )
return ;
switch ( ch ) {
case ' m ' :
tty3270_set_attributes ( tp ) ;
break ;
case ' H ' : /* Set cursor position. */
case ' f ' :
tty3270_goto_xy ( tp , tty3270_getpar ( tp , 1 ) - 1 ,
tty3270_getpar ( tp , 0 ) - 1 ) ;
break ;
case ' d ' : /* Set y position. */
tty3270_goto_xy ( tp , tp - > cx , tty3270_getpar ( tp , 0 ) - 1 ) ;
break ;
case ' A ' : /* Cursor up. */
case ' F ' :
tty3270_goto_xy ( tp , tp - > cx , tp - > cy - tty3270_getpar ( tp , 0 ) ) ;
break ;
case ' B ' : /* Cursor down. */
case ' e ' :
case ' E ' :
tty3270_goto_xy ( tp , tp - > cx , tp - > cy + tty3270_getpar ( tp , 0 ) ) ;
break ;
case ' C ' : /* Cursor forward. */
case ' a ' :
tty3270_goto_xy ( tp , tp - > cx + tty3270_getpar ( tp , 0 ) , tp - > cy ) ;
break ;
case ' D ' : /* Cursor backward. */
tty3270_goto_xy ( tp , tp - > cx - tty3270_getpar ( tp , 0 ) , tp - > cy ) ;
break ;
case ' G ' : /* Set x position. */
case ' ` ' :
tty3270_goto_xy ( tp , tty3270_getpar ( tp , 0 ) , tp - > cy ) ;
break ;
case ' X ' : /* Erase Characters. */
tty3270_erase_characters ( tp , tty3270_getpar ( tp , 0 ) ) ;
break ;
case ' J ' : /* Erase display. */
tty3270_erase_display ( tp , tp - > esc_par [ 0 ] ) ;
break ;
case ' K ' : /* Erase line. */
tty3270_erase_line ( tp , tp - > esc_par [ 0 ] ) ;
break ;
case ' P ' : /* Delete characters. */
tty3270_delete_characters ( tp , tty3270_getpar ( tp , 0 ) ) ;
break ;
case ' @ ' : /* Insert characters. */
tty3270_insert_characters ( tp , tty3270_getpar ( tp , 0 ) ) ;
break ;
case ' s ' : /* Save cursor position. */
tp - > saved_cx = tp - > cx ;
tp - > saved_cy = tp - > cy ;
tp - > saved_highlight = tp - > highlight ;
tp - > saved_f_color = tp - > f_color ;
break ;
case ' u ' : /* Restore cursor position. */
tty3270_convert_line ( tp , tp - > cy ) ;
tty3270_goto_xy ( tp , tp - > saved_cx , tp - > saved_cy ) ;
tp - > highlight = tp - > saved_highlight ;
tp - > f_color = tp - > saved_f_color ;
break ;
}
}
/*
* String write routine for 3270 ttys
*/
static void
2012-04-02 15:54:17 +04:00
tty3270_do_write ( struct tty3270 * tp , struct tty_struct * tty ,
const unsigned char * buf , int count )
2005-04-17 02:20:36 +04:00
{
int i_msg , i ;
spin_lock_bh ( & tp - > view . lock ) ;
2012-04-02 15:54:17 +04:00
for ( i_msg = 0 ; ! tty - > stopped & & i_msg < count ; i_msg + + ) {
2005-04-17 02:20:36 +04:00
if ( tp - > esc_state ! = 0 ) {
/* Continue escape sequence. */
tty3270_escape_sequence ( tp , buf [ i_msg ] ) ;
continue ;
}
switch ( buf [ i_msg ] ) {
case 0x07 : /* '\a' -- Alarm */
tp - > wcc | = TW_PLUSALARM ;
break ;
case 0x08 : /* Backspace. */
if ( tp - > cx > 0 ) {
tp - > cx - - ;
tty3270_put_character ( tp , ' ' ) ;
}
break ;
case 0x09 : /* '\t' -- Tabulate */
for ( i = tp - > cx % 8 ; i < 8 ; i + + ) {
if ( tp - > cx > = tp - > view . cols ) {
tty3270_cr ( tp ) ;
tty3270_lf ( tp ) ;
break ;
}
tty3270_put_character ( tp , ' ' ) ;
tp - > cx + + ;
}
break ;
case 0x0a : /* '\n' -- New Line */
tty3270_cr ( tp ) ;
tty3270_lf ( tp ) ;
break ;
case 0x0c : /* '\f' -- Form Feed */
tty3270_erase_display ( tp , 2 ) ;
tp - > cx = tp - > cy = 0 ;
break ;
case 0x0d : /* '\r' -- Carriage Return */
tp - > cx = 0 ;
break ;
case 0x0f : /* SuSE "exit alternate mode" */
break ;
case 0x1b : /* Start escape sequence. */
tty3270_escape_sequence ( tp , buf [ i_msg ] ) ;
break ;
default : /* Insert normal character. */
if ( tp - > cx > = tp - > view . cols ) {
tty3270_cr ( tp ) ;
tty3270_lf ( tp ) ;
}
tty3270_put_character ( tp , buf [ i_msg ] ) ;
tp - > cx + + ;
break ;
}
}
/* Convert current line to 3270 data fragment. */
tty3270_convert_line ( tp , tp - > cy ) ;
/* Setup timer to update display after 1/10 second */
if ( ! timer_pending ( & tp - > timer ) )
tty3270_set_timer ( tp , HZ / 10 ) ;
spin_unlock_bh ( & tp - > view . lock ) ;
}
/*
* String write routine for 3270 ttys
*/
static int
tty3270_write ( struct tty_struct * tty ,
const unsigned char * buf , int count )
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return 0 ;
if ( tp - > char_count > 0 ) {
2012-04-02 15:54:17 +04:00
tty3270_do_write ( tp , tty , tp - > char_buf , tp - > char_count ) ;
2005-04-17 02:20:36 +04:00
tp - > char_count = 0 ;
}
2012-04-02 15:54:17 +04:00
tty3270_do_write ( tp , tty , buf , count ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
/*
* Put single characters to the ttys character buffer
*/
2008-05-07 11:22:58 +04:00
static int tty3270_put_char ( struct tty_struct * tty , unsigned char ch )
2005-04-17 02:20:36 +04:00
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
2008-05-07 11:22:58 +04:00
if ( ! tp | | tp - > char_count > = TTY3270_CHAR_BUF_SIZE )
return 0 ;
tp - > char_buf [ tp - > char_count + + ] = ch ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
/*
* Flush all characters from the ttys characeter buffer put there
* by tty3270_put_char .
*/
static void
tty3270_flush_chars ( struct tty_struct * tty )
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return ;
if ( tp - > char_count > 0 ) {
2012-04-02 15:54:17 +04:00
tty3270_do_write ( tp , tty , tp - > char_buf , tp - > char_count ) ;
2005-04-17 02:20:36 +04:00
tp - > char_count = 0 ;
}
}
/*
* Returns the number of characters in the output buffer . This is
* used in tty_wait_until_sent to wait until all characters have
* appeared on the screen .
*/
static int
tty3270_chars_in_buffer ( struct tty_struct * tty )
{
return 0 ;
}
static void
tty3270_flush_buffer ( struct tty_struct * tty )
{
}
/*
* Check for visible / invisible input switches
*/
static void
2006-12-08 13:38:45 +03:00
tty3270_set_termios ( struct tty_struct * tty , struct ktermios * old )
2005-04-17 02:20:36 +04:00
{
struct tty3270 * tp ;
int new ;
tp = tty - > driver_data ;
if ( ! tp )
return ;
spin_lock_bh ( & tp - > view . lock ) ;
if ( L_ICANON ( tty ) ) {
new = L_ECHO ( tty ) ? TF_INPUT : TF_INPUTN ;
if ( new ! = tp - > inattr ) {
tp - > inattr = new ;
2006-07-12 18:41:55 +04:00
tty3270_update_prompt ( tp , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
tty3270_set_timer ( tp , 1 ) ;
}
}
spin_unlock_bh ( & tp - > view . lock ) ;
}
/*
* Disable reading from a 3270 tty
*/
static void
tty3270_throttle ( struct tty_struct * tty )
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return ;
tp - > throttle = 1 ;
}
/*
* Enable reading from a 3270 tty
*/
static void
tty3270_unthrottle ( struct tty_struct * tty )
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return ;
tp - > throttle = 0 ;
if ( tp - > attn )
tty3270_issue_read ( tp , 1 ) ;
}
/*
* Hang up the tty device .
*/
static void
tty3270_hangup ( struct tty_struct * tty )
{
// FIXME: implement
}
static void
tty3270_wait_until_sent ( struct tty_struct * tty , int timeout )
{
}
2011-02-25 16:28:30 +03:00
static int tty3270_ioctl ( struct tty_struct * tty , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return - ENODEV ;
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) )
return - EIO ;
2011-02-25 16:28:30 +03:00
return kbd_ioctl ( tp - > kbd , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-07 14:52:13 +03:00
# ifdef CONFIG_COMPAT
2011-02-25 16:28:30 +03:00
static long tty3270_compat_ioctl ( struct tty_struct * tty ,
unsigned int cmd , unsigned long arg )
2009-12-07 14:52:13 +03:00
{
struct tty3270 * tp ;
tp = tty - > driver_data ;
if ( ! tp )
return - ENODEV ;
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) )
return - EIO ;
2011-02-25 16:28:30 +03:00
return kbd_ioctl ( tp - > kbd , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
2009-12-07 14:52:13 +03:00
}
# endif
2006-10-02 13:17:18 +04:00
static const struct tty_operations tty3270_ops = {
2012-08-07 23:48:03 +04:00
. install = tty3270_install ,
. cleanup = tty3270_cleanup ,
2013-01-08 18:15:12 +04:00
. open = tty3270_open ,
2005-04-17 02:20:36 +04:00
. close = tty3270_close ,
. write = tty3270_write ,
. put_char = tty3270_put_char ,
. flush_chars = tty3270_flush_chars ,
. write_room = tty3270_write_room ,
. chars_in_buffer = tty3270_chars_in_buffer ,
. flush_buffer = tty3270_flush_buffer ,
. throttle = tty3270_throttle ,
. unthrottle = tty3270_unthrottle ,
. hangup = tty3270_hangup ,
. wait_until_sent = tty3270_wait_until_sent ,
. ioctl = tty3270_ioctl ,
2009-12-07 14:52:13 +03:00
# ifdef CONFIG_COMPAT
. compat_ioctl = tty3270_compat_ioctl ,
# endif
2005-04-17 02:20:36 +04:00
. set_termios = tty3270_set_termios
} ;
2013-09-06 21:10:48 +04:00
static void tty3270_create_cb ( int minor )
2013-01-08 18:31:11 +04:00
{
2013-03-21 16:07:18 +04:00
tty_register_device ( tty3270_driver , minor - RAW3270_FIRSTMINOR , NULL ) ;
2013-01-08 18:31:11 +04:00
}
2013-09-06 21:10:48 +04:00
static void tty3270_destroy_cb ( int minor )
2013-01-08 18:31:11 +04:00
{
2013-03-21 16:07:18 +04:00
tty_unregister_device ( tty3270_driver , minor - RAW3270_FIRSTMINOR ) ;
2013-01-08 18:31:11 +04:00
}
2013-09-06 21:10:48 +04:00
static struct raw3270_notifier tty3270_notifier =
2013-01-08 18:31:11 +04:00
{
. create = tty3270_create_cb ,
. destroy = tty3270_destroy_cb ,
} ;
2005-04-17 02:20:36 +04:00
/*
* 3270 tty registration code called from tty_init ( ) .
* Most kernel services ( incl . kmalloc ) are available at this poimt .
*/
2007-02-05 23:16:47 +03:00
static int __init tty3270_init ( void )
2005-04-17 02:20:36 +04:00
{
struct tty_driver * driver ;
int ret ;
2013-01-08 18:31:11 +04:00
driver = tty_alloc_driver ( RAW3270_MAXDEVS ,
TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV |
TTY_DRIVER_RESET_TERMIOS ) ;
if ( IS_ERR ( driver ) )
return PTR_ERR ( driver ) ;
2005-04-17 02:20:36 +04:00
/*
* Initialize the tty_driver structure
* Entries in tty3270_driver that are NOT initialized :
* proc_entry , set_termios , flush_buffer , set_ldisc , write_proc
*/
2013-01-08 18:31:11 +04:00
driver - > driver_name = " tty3270 " ;
driver - > name = " 3270/tty " ;
2005-04-17 02:20:36 +04:00
driver - > major = IBM_TTY3270_MAJOR ;
2013-03-21 16:07:18 +04:00
driver - > minor_start = RAW3270_FIRSTMINOR ;
driver - > name_base = RAW3270_FIRSTMINOR ;
2005-04-17 02:20:36 +04:00
driver - > type = TTY_DRIVER_TYPE_SYSTEM ;
driver - > subtype = SYSTEM_TYPE_TTY ;
driver - > init_termios = tty_std_termios ;
tty_set_operations ( driver , & tty3270_ops ) ;
ret = tty_register_driver ( driver ) ;
if ( ret ) {
put_tty_driver ( driver ) ;
return ret ;
}
tty3270_driver = driver ;
2013-01-08 18:31:11 +04:00
raw3270_register_notifier ( & tty3270_notifier ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit
tty3270_exit ( void )
{
struct tty_driver * driver ;
2013-01-08 18:31:11 +04:00
raw3270_unregister_notifier ( & tty3270_notifier ) ;
2005-04-17 02:20:36 +04:00
driver = tty3270_driver ;
2006-07-12 18:41:55 +04:00
tty3270_driver = NULL ;
2005-04-17 02:20:36 +04:00
tty_unregister_driver ( driver ) ;
2012-08-07 23:47:41 +04:00
put_tty_driver ( driver ) ;
2005-04-17 02:20:36 +04:00
tty3270_del_views ( ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_CHARDEV_MAJOR ( IBM_TTY3270_MAJOR ) ;
module_init ( tty3270_init ) ;
module_exit ( tty3270_exit ) ;