Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
ef27be58
Commit
ef27be58
authored
Sep 25, 2003
by
Martin Schwidefsky
Committed by
Linus Torvalds
Sep 25, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] s390: vt220 console.
Add support for vt220 console over sclp.
parent
49553319
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
838 additions
and
3 deletions
+838
-3
arch/s390/defconfig
arch/s390/defconfig
+1
-0
drivers/s390/Kconfig
drivers/s390/Kconfig
+13
-0
drivers/s390/char/Makefile
drivers/s390/char/Makefile
+1
-0
drivers/s390/char/sclp.h
drivers/s390/char/sclp.h
+2
-0
drivers/s390/char/sclp_cpi.c
drivers/s390/char/sclp_cpi.c
+3
-2
drivers/s390/char/sclp_tty.c
drivers/s390/char/sclp_tty.c
+1
-1
drivers/s390/char/sclp_vt220.c
drivers/s390/char/sclp_vt220.c
+817
-0
No files found.
arch/s390/defconfig
View file @
ef27be58
...
...
@@ -137,6 +137,7 @@ CONFIG_CCW_CONSOLE=y
CONFIG_SCLP=y
CONFIG_SCLP_TTY=y
CONFIG_SCLP_CONSOLE=y
# CONFIG_SCLP_VT220_TTY is not set
CONFIG_SCLP_CPI=m
CONFIG_S390_TAPE=m
...
...
drivers/s390/Kconfig
View file @
ef27be58
...
...
@@ -101,6 +101,19 @@ config SCLP_CONSOLE
Include support for using an IBM HWC line-mode terminal as the Linux
system console.
config SCLP_VT220_TTY
bool "Support for SCLP VT220-compatible terminal"
depends on SCLP
help
Include support for an IBM SCLP VT220-compatible terminal.
config SCLP_VT220_CONSOLE
bool "Support for console on SCLP VT220-compatible terminal"
depends on SCLP_VT220_TTY
help
Include support for using an IBM SCLP VT220-compatible terminal as a
Linux system console.
config SCLP_CPI
tristate "Control-Program Identification"
depends on SCLP
...
...
drivers/s390/char/Makefile
View file @
ef27be58
...
...
@@ -11,6 +11,7 @@ obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_SCLP)
+=
sclp.o sclp_rw.o
obj-$(CONFIG_SCLP_TTY)
+=
sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE)
+=
sclp_con.o
obj-$(CONFIG_SCLP_VT220_TTY)
+=
sclp_vt220.o
obj-$(CONFIG_SCLP_CPI)
+=
sclp_cpi.o
obj-$(CONFIG_TN3270)
+=
tub3270.o
tape-$(CONFIG_S390_TAPE_BLOCK)
+=
tape_block.o
...
...
drivers/s390/char/sclp.h
View file @
ef27be58
...
...
@@ -26,6 +26,7 @@
#define EvTyp_CntlProgOpCmd 0x20
#define EvTyp_CntlProgIdent 0x0B
#define EvTyp_SigQuiesce 0x1D
#define EvTyp_VT220Msg 0x1A
#define EvTyp_OpCmd_Mask 0x80000000
#define EvTyp_Msg_Mask 0x40000000
...
...
@@ -34,6 +35,7 @@
#define EvTyp_CtlProgOpCmd_Mask 0x00000001
#define EvTyp_CtlProgIdent_Mask 0x00200000
#define EvTyp_SigQuiesce_Mask 0x00000008
#define EvTyp_VT220Msg_Mask 0x00000040
#define GnrlMsgFlgs_DOM 0x8000
#define GnrlMsgFlgs_SndAlrm 0x4000
...
...
drivers/s390/char/sclp_cpi.c
View file @
ef27be58
...
...
@@ -9,6 +9,7 @@
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/string.h>
...
...
@@ -58,12 +59,12 @@ MODULE_DESCRIPTION(
"or zSeries hardware"
);
static
char
*
system_name
=
NULL
;
MODULE_PARM
(
system_name
,
"s"
);
module_param
(
system_name
,
charp
,
0
);
MODULE_PARM_DESC
(
system_name
,
"e.g. hostname - max. 8 characters"
);
static
char
*
sysplex_name
=
NULL
;
#ifdef ALLOW_SYSPLEX_NAME
MODULE_PARM
(
sysplex_name
,
"s"
);
module_param
(
sysplex_name
,
charp
,
0
);
MODULE_PARM_DESC
(
sysplex_name
,
"if applicable - max. 8 characters"
);
#endif
...
...
drivers/s390/char/sclp_tty.c
View file @
ef27be58
...
...
@@ -779,7 +779,7 @@ sclp_tty_init(void)
driver
->
owner
=
THIS_MODULE
;
driver
->
driver_name
=
"sclp_line"
;
driver
->
name
=
"
ttyS
"
;
driver
->
name
=
"
sclp_line
"
;
driver
->
major
=
TTY_MAJOR
;
driver
->
minor_start
=
64
;
driver
->
type
=
TTY_DRIVER_TYPE_SYSTEM
;
...
...
drivers/s390/char/sclp_vt220.c
0 → 100644
View file @
ef27be58
/*
* drivers/s390/char/sclp_vt220.c
* SCLP VT220 terminal driver.
*
* S390 version
* Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/console.h>
#include <linux/kdev_t.h>
#include <linux/bootmem.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "sclp.h"
#define SCLP_VT220_PRINT_HEADER "sclp vt220 tty driver: "
#define SCLP_VT220_MAJOR TTY_MAJOR
#define SCLP_VT220_MINOR 65
#define SCLP_VT220_DRIVER_NAME "sclp_vt220"
#define SCLP_VT220_DEVICE_NAME "sclp_vt"
#define SCLP_VT220_CONSOLE_NAME "ttyS"
#define SCLP_VT220_CONSOLE_INDEX 1
/* console=ttyS1 */
/* Representation of a single write request */
struct
sclp_vt220_request
{
struct
list_head
list
;
struct
sclp_req
sclp_req
;
int
retry_count
;
struct
timer_list
retry_timer
;
};
/* VT220 SCCB */
struct
sclp_vt220_sccb
{
struct
sccb_header
header
;
struct
evbuf_header
evbuf
;
};
#define SCLP_VT220_MAX_CHARS_PER_BUFFER (PAGE_SIZE - \
sizeof(struct sclp_vt220_request) - \
sizeof(struct sclp_vt220_sccb))
/* Structures and data needed to register tty driver */
static
struct
tty_driver
*
sclp_vt220_driver
;
/* The tty_struct that the kernel associated with us */
static
struct
tty_struct
*
sclp_vt220_tty
;
/* Lock to protect internal data from concurrent access */
static
spinlock_t
sclp_vt220_lock
;
/* List of empty pages to be used as write request buffers */
static
struct
list_head
sclp_vt220_empty
;
/* List of pending requests */
static
struct
list_head
sclp_vt220_outqueue
;
/* Number of requests in outqueue */
static
int
sclp_vt220_outqueue_count
;
/* Wait queue used to delay write requests while we've run out of buffers */
static
wait_queue_head_t
sclp_vt220_waitq
;
/* Timer used for delaying write requests to merge subsequent messages into
* a single buffer */
static
struct
timer_list
sclp_vt220_timer
;
/* Pointer to current request buffer which has been partially filled but not
* yet sent */
static
struct
sclp_vt220_request
*
sclp_vt220_current_request
;
/* Number of characters in current request buffer */
static
int
sclp_vt220_buffered_chars
;
/* Flag indicating whether this driver has already been initialized */
static
int
sclp_vt220_initialized
=
0
;
/* Flag indicating that sclp_vt220_current_request should really
* have been already queued but wasn't because the SCLP was processing
* another buffer */
static
int
sclp_vt220_flush_later
;
static
void
sclp_vt220_receiver_fn
(
struct
evbuf_header
*
evbuf
);
static
void
__sclp_vt220_emit
(
struct
sclp_vt220_request
*
request
);
static
void
sclp_vt220_emit_current
(
void
);
/* Registration structure for our interest in SCLP event buffers */
static
struct
sclp_register
sclp_vt220_register
=
{
.
send_mask
=
EvTyp_VT220Msg_Mask
,
.
receive_mask
=
EvTyp_VT220Msg_Mask
,
.
state_change_fn
=
NULL
,
.
receiver_fn
=
sclp_vt220_receiver_fn
};
/*
* Put provided request buffer back into queue and check emit pending
* buffers if necessary.
*/
static
void
sclp_vt220_process_queue
(
struct
sclp_vt220_request
*
request
)
{
unsigned
long
flags
;
struct
sclp_vt220_request
*
next
;
void
*
page
;
/* Put buffer back to list of empty buffers */
page
=
request
->
sclp_req
.
sccb
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
/* Move request from outqueue to empty queue */
list_del
(
&
request
->
list
);
sclp_vt220_outqueue_count
--
;
list_add_tail
((
struct
list_head
*
)
page
,
&
sclp_vt220_empty
);
/* Check if there is a pending buffer on the out queue. */
next
=
NULL
;
if
(
!
list_empty
(
&
sclp_vt220_outqueue
))
next
=
list_entry
(
sclp_vt220_outqueue
.
next
,
struct
sclp_vt220_request
,
list
);
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
if
(
next
!=
NULL
)
__sclp_vt220_emit
(
next
);
else
if
(
sclp_vt220_flush_later
)
sclp_vt220_emit_current
();
wake_up
(
&
sclp_vt220_waitq
);
/* Check if the tty needs a wake up call */
if
(
sclp_vt220_tty
!=
NULL
)
{
if
((
sclp_vt220_tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
(
sclp_vt220_tty
->
ldisc
.
write_wakeup
!=
NULL
))
(
sclp_vt220_tty
->
ldisc
.
write_wakeup
)(
sclp_vt220_tty
);
wake_up_interruptible
(
&
sclp_vt220_tty
->
write_wait
);
}
}
/*
* Retry sclp write request after waiting some time for an sclp equipment
* check to pass.
*/
static
void
sclp_vt220_retry
(
unsigned
long
data
)
{
struct
sclp_vt220_request
*
request
;
struct
sclp_vt220_sccb
*
sccb
;
request
=
(
struct
sclp_vt220_request
*
)
data
;
request
->
sclp_req
.
status
=
SCLP_REQ_FILLED
;
sccb
=
(
struct
sclp_vt220_sccb
*
)
request
->
sclp_req
.
sccb
;
sccb
->
header
.
response_code
=
0x0000
;
sclp_add_request
(
&
request
->
sclp_req
);
}
#define SCLP_BUFFER_MAX_RETRY 5
#define SCLP_BUFFER_RETRY_INTERVAL 2
/*
* Callback through which the result of a write request is reported by the
* SCLP.
*/
static
void
sclp_vt220_callback
(
struct
sclp_req
*
request
,
void
*
data
)
{
struct
sclp_vt220_request
*
vt220_request
;
struct
sclp_vt220_sccb
*
sccb
;
vt220_request
=
(
struct
sclp_vt220_request
*
)
data
;
if
(
request
->
status
==
SCLP_REQ_FAILED
)
{
sclp_vt220_process_queue
(
vt220_request
);
return
;
}
sccb
=
(
struct
sclp_vt220_sccb
*
)
vt220_request
->
sclp_req
.
sccb
;
/* Check SCLP response code and choose suitable action */
switch
(
sccb
->
header
.
response_code
)
{
case
0x0020
:
break
;
case
0x05f0
:
/* Target resource in improper state */
break
;
case
0x0340
:
/* Contained SCLP equipment check */
if
(
vt220_request
->
retry_count
++
>
SCLP_BUFFER_MAX_RETRY
)
break
;
/* Remove processed buffers and requeue rest */
if
(
sclp_remove_processed
((
struct
sccb_header
*
)
sccb
)
>
0
)
{
/* Not all buffers were processed */
sccb
->
header
.
response_code
=
0x0000
;
vt220_request
->
sclp_req
.
status
=
SCLP_REQ_FILLED
;
sclp_add_request
(
request
);
return
;
}
break
;
case
0x0040
:
/* SCLP equipment check */
if
(
vt220_request
->
retry_count
++
>
SCLP_BUFFER_MAX_RETRY
)
break
;
/* Wait some time, then retry request */
vt220_request
->
retry_timer
.
function
=
sclp_vt220_retry
;
vt220_request
->
retry_timer
.
data
=
(
unsigned
long
)
vt220_request
;
vt220_request
->
retry_timer
.
expires
=
jiffies
+
SCLP_BUFFER_RETRY_INTERVAL
*
HZ
;
add_timer
(
&
vt220_request
->
retry_timer
);
return
;
default:
break
;
}
sclp_vt220_process_queue
(
vt220_request
);
}
/*
* Emit vt220 request buffer to SCLP.
*/
static
void
__sclp_vt220_emit
(
struct
sclp_vt220_request
*
request
)
{
if
(
!
(
sclp_vt220_register
.
sclp_send_mask
&
EvTyp_VT220Msg_Mask
))
{
request
->
sclp_req
.
status
=
SCLP_REQ_FAILED
;
sclp_vt220_callback
(
&
request
->
sclp_req
,
(
void
*
)
request
);
return
;
}
request
->
sclp_req
.
command
=
SCLP_CMDW_WRITEDATA
;
request
->
sclp_req
.
status
=
SCLP_REQ_FILLED
;
request
->
sclp_req
.
callback
=
sclp_vt220_callback
;
request
->
sclp_req
.
callback_data
=
(
void
*
)
request
;
sclp_add_request
(
&
request
->
sclp_req
);
}
/*
* Queue and emit given request.
*/
static
void
sclp_vt220_emit
(
struct
sclp_vt220_request
*
request
)
{
unsigned
long
flags
;
int
count
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
list_add_tail
(
&
request
->
list
,
&
sclp_vt220_outqueue
);
count
=
sclp_vt220_outqueue_count
++
;
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
/* Emit only the first buffer immediately - callback takes care of
* the rest */
if
(
count
==
0
)
__sclp_vt220_emit
(
request
);
}
/*
* Queue and emit current request.
*/
static
void
sclp_vt220_emit_current
(
void
)
{
unsigned
long
flags
;
struct
sclp_vt220_request
*
request
;
struct
sclp_vt220_sccb
*
sccb
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
request
=
NULL
;
if
(
sclp_vt220_current_request
!=
NULL
)
{
sccb
=
(
struct
sclp_vt220_sccb
*
)
sclp_vt220_current_request
->
sclp_req
.
sccb
;
/* Only emit buffers with content */
if
(
sccb
->
header
.
length
!=
sizeof
(
struct
sclp_vt220_sccb
))
{
request
=
sclp_vt220_current_request
;
sclp_vt220_current_request
=
NULL
;
if
(
timer_pending
(
&
sclp_vt220_timer
))
del_timer
(
&
sclp_vt220_timer
);
}
sclp_vt220_flush_later
=
0
;
}
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
if
(
request
!=
NULL
)
sclp_vt220_emit
(
request
);
}
#define SCLP_NORMAL_WRITE 0x00
/*
* Helper function to initialize a page with the sclp request structure.
*/
static
struct
sclp_vt220_request
*
sclp_vt220_initialize_page
(
void
*
page
)
{
struct
sclp_vt220_request
*
request
;
struct
sclp_vt220_sccb
*
sccb
;
/* Place request structure at end of page */
request
=
((
struct
sclp_vt220_request
*
)
((
addr_t
)
page
+
PAGE_SIZE
))
-
1
;
init_timer
(
&
request
->
retry_timer
);
request
->
retry_count
=
0
;
request
->
sclp_req
.
sccb
=
page
;
/* SCCB goes at start of page */
sccb
=
(
struct
sclp_vt220_sccb
*
)
page
;
memset
((
void
*
)
sccb
,
0
,
sizeof
(
struct
sclp_vt220_sccb
));
sccb
->
header
.
length
=
sizeof
(
struct
sclp_vt220_sccb
);
sccb
->
header
.
function_code
=
SCLP_NORMAL_WRITE
;
sccb
->
header
.
response_code
=
0x0000
;
sccb
->
evbuf
.
type
=
EvTyp_VT220Msg
;
sccb
->
evbuf
.
length
=
sizeof
(
struct
evbuf_header
);
return
request
;
}
static
inline
unsigned
int
sclp_vt220_space_left
(
struct
sclp_vt220_request
*
request
)
{
struct
sclp_vt220_sccb
*
sccb
;
sccb
=
(
struct
sclp_vt220_sccb
*
)
request
->
sclp_req
.
sccb
;
return
PAGE_SIZE
-
sizeof
(
struct
sclp_vt220_request
)
-
sccb
->
header
.
length
;
}
static
inline
unsigned
int
sclp_vt220_chars_stored
(
struct
sclp_vt220_request
*
request
)
{
struct
sclp_vt220_sccb
*
sccb
;
sccb
=
(
struct
sclp_vt220_sccb
*
)
request
->
sclp_req
.
sccb
;
return
sccb
->
evbuf
.
length
-
sizeof
(
struct
evbuf_header
);
}
/*
* Add msg to buffer associated with request. Return the number of characters
* added or -EFAULT on error.
*/
static
int
sclp_vt220_add_msg
(
struct
sclp_vt220_request
*
request
,
const
unsigned
char
*
msg
,
int
count
,
int
from_user
,
int
convertlf
)
{
struct
sclp_vt220_sccb
*
sccb
;
void
*
buffer
;
unsigned
char
c
;
int
from
;
int
to
;
if
(
count
>
sclp_vt220_space_left
(
request
))
count
=
sclp_vt220_space_left
(
request
);
if
(
count
<=
0
)
return
0
;
sccb
=
(
struct
sclp_vt220_sccb
*
)
request
->
sclp_req
.
sccb
;
buffer
=
(
void
*
)
((
addr_t
)
sccb
+
sccb
->
header
.
length
);
if
(
convertlf
)
{
/* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/
for
(
from
=
0
,
to
=
0
;
(
from
<
count
)
&&
(
to
<
sclp_vt220_space_left
(
request
));
from
++
)
{
/* Retrieve character */
if
(
from_user
)
{
if
(
get_user
(
c
,
msg
+
from
)
!=
0
)
return
-
EFAULT
;
}
else
c
=
msg
[
from
];
/* Perform conversion */
if
(
c
==
0x0a
)
{
if
(
to
+
1
<
sclp_vt220_space_left
(
request
))
{
((
unsigned
char
*
)
buffer
)[
to
++
]
=
c
;
((
unsigned
char
*
)
buffer
)[
to
++
]
=
0x0d
;
}
else
break
;
}
else
((
unsigned
char
*
)
buffer
)[
to
++
]
=
c
;
}
sccb
->
header
.
length
+=
to
;
sccb
->
evbuf
.
length
+=
to
;
return
from
;
}
else
{
if
(
from_user
)
{
if
(
copy_from_user
(
buffer
,
(
void
*
)
msg
,
count
)
!=
0
)
return
-
EFAULT
;
}
else
memcpy
(
buffer
,
(
const
void
*
)
msg
,
count
);
sccb
->
header
.
length
+=
count
;
sccb
->
evbuf
.
length
+=
count
;
return
count
;
}
}
/*
* Emit buffer after having waited long enough for more data to arrive.
*/
static
void
sclp_vt220_timeout
(
unsigned
long
data
)
{
sclp_vt220_emit_current
();
}
#define BUFFER_MAX_DELAY HZ/2
/*
* Internal implementation of the write function. Write COUNT bytes of data
* from memory at BUF which may reside in user space (specified by FROM_USER)
* to the SCLP interface. In case that the data does not fit into the current
* write buffer, emit the current one and allocate a new one. If there are no
* more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
* is non-zero, the buffer will be scheduled for emitting after a timeout -
* otherwise the user has to explicitly call the flush function.
* A non-zero CONVERTLF parameter indicates that 0x0a characters in the message
* buffer should be converted to 0x0a 0x0d. After completion, return the number
* of bytes written.
*/
static
int
__sclp_vt220_write
(
int
from_user
,
const
unsigned
char
*
buf
,
int
count
,
int
do_schedule
,
int
convertlf
)
{
unsigned
long
flags
;
void
*
page
;
int
written
;
int
overall_written
;
if
(
count
<=
0
)
return
0
;
overall_written
=
0
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
do
{
/* Create a sclp output buffer if none exists yet */
if
(
sclp_vt220_current_request
==
NULL
)
{
while
(
list_empty
(
&
sclp_vt220_empty
))
{
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
if
(
in_interrupt
())
sclp_sync_wait
();
else
wait_event
(
sclp_vt220_waitq
,
!
list_empty
(
&
sclp_vt220_empty
));
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
}
page
=
(
void
*
)
sclp_vt220_empty
.
next
;
list_del
((
struct
list_head
*
)
page
);
sclp_vt220_current_request
=
sclp_vt220_initialize_page
(
page
);
}
/* Try to write the string to the current request buffer */
written
=
sclp_vt220_add_msg
(
sclp_vt220_current_request
,
buf
,
count
,
from_user
,
convertlf
);
if
(
written
>
0
)
overall_written
+=
written
;
if
(
written
==
-
EFAULT
||
written
==
count
)
break
;
/*
* Not all characters could be written to the current
* output buffer. Emit the buffer, create a new buffer
* and then output the rest of the string.
*/
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
sclp_vt220_emit_current
();
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
buf
+=
written
;
count
-=
written
;
}
while
(
count
>
0
);
/* Setup timer to output current console buffer after some time */
if
(
sclp_vt220_current_request
!=
NULL
&&
!
timer_pending
(
&
sclp_vt220_timer
)
&&
do_schedule
)
{
sclp_vt220_timer
.
function
=
sclp_vt220_timeout
;
sclp_vt220_timer
.
data
=
0UL
;
sclp_vt220_timer
.
expires
=
jiffies
+
BUFFER_MAX_DELAY
;
add_timer
(
&
sclp_vt220_timer
);
}
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
return
overall_written
;
}
/*
* This routine is called by the kernel to write a series of
* characters to the tty device. The characters may come from
* user space or kernel space. This routine will return the
* number of characters actually accepted for writing.
*/
static
int
sclp_vt220_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
unsigned
char
*
buf
,
int
count
)
{
return
__sclp_vt220_write
(
from_user
,
buf
,
count
,
1
,
0
);
}
#define SCLP_VT220_SESSION_ENDED 0x01
#define SCLP_VT220_SESSION_STARTED 0x80
#define SCLP_VT220_SESSION_DATA 0x00
/*
* Called by the SCLP to report incoming event buffers.
*/
static
void
sclp_vt220_receiver_fn
(
struct
evbuf_header
*
evbuf
)
{
char
*
buffer
;
unsigned
int
count
;
/* Ignore input if device is not open */
if
(
sclp_vt220_tty
==
NULL
)
return
;
buffer
=
(
char
*
)
((
addr_t
)
evbuf
+
sizeof
(
struct
evbuf_header
));
count
=
evbuf
->
length
-
sizeof
(
struct
evbuf_header
);
switch
(
*
buffer
)
{
case
SCLP_VT220_SESSION_ENDED
:
case
SCLP_VT220_SESSION_STARTED
:
break
;
case
SCLP_VT220_SESSION_DATA
:
/* Send input to line discipline */
buffer
++
;
count
--
;
/* Prevent buffer overrun by discarding input. Note that
* because buffer_push works asynchronously, we cannot wait
* for the buffer to be emptied. */
if
(
count
+
sclp_vt220_tty
->
flip
.
count
>
TTY_FLIPBUF_SIZE
)
count
=
TTY_FLIPBUF_SIZE
-
sclp_vt220_tty
->
flip
.
count
;
memcpy
(
sclp_vt220_tty
->
flip
.
char_buf_ptr
,
buffer
,
count
);
memset
(
sclp_vt220_tty
->
flip
.
flag_buf_ptr
,
TTY_NORMAL
,
count
);
sclp_vt220_tty
->
flip
.
char_buf_ptr
+=
count
;
sclp_vt220_tty
->
flip
.
flag_buf_ptr
+=
count
;
sclp_vt220_tty
->
flip
.
count
+=
count
;
tty_flip_buffer_push
(
sclp_vt220_tty
);
break
;
}
}
/*
* This routine is called when a particular tty device is opened.
*/
static
int
sclp_vt220_open
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
sclp_vt220_tty
=
tty
;
tty
->
driver_data
=
NULL
;
tty
->
low_latency
=
0
;
return
0
;
}
/*
* This routine is called when a particular tty device is closed.
*/
static
void
sclp_vt220_close
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
if
(
tty
->
count
>
1
)
return
;
sclp_vt220_tty
=
NULL
;
}
/*
* This routine is called by the kernel to write a single
* character to the tty device. If the kernel uses this routine,
* it must call the flush_chars() routine (if defined) when it is
* done stuffing characters into the driver.
*
* NOTE: include/linux/tty_driver.h specifies that a character should be
* ignored if there is no room in the queue. This driver implements a different
* semantic in that it will block when there is no more room left.
*/
static
void
sclp_vt220_put_char
(
struct
tty_struct
*
tty
,
unsigned
char
ch
)
{
__sclp_vt220_write
(
0
,
&
ch
,
1
,
0
,
0
);
}
/*
* This routine is called by the kernel after it has written a
* series of characters to the tty device using put_char().
*/
static
void
sclp_vt220_flush_chars
(
struct
tty_struct
*
tty
)
{
if
(
sclp_vt220_outqueue_count
==
0
)
sclp_vt220_emit_current
();
else
sclp_vt220_flush_later
=
1
;
}
/*
* This routine returns the numbers of characters the tty driver
* will accept for queuing to be written. This number is subject
* to change as output buffers get emptied, or if the output flow
* control is acted.
*/
static
int
sclp_vt220_write_room
(
struct
tty_struct
*
tty
)
{
unsigned
long
flags
;
struct
list_head
*
l
;
int
count
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
count
=
0
;
if
(
sclp_vt220_current_request
!=
NULL
)
count
=
sclp_vt220_space_left
(
sclp_vt220_current_request
);
list_for_each
(
l
,
&
sclp_vt220_empty
)
count
+=
SCLP_VT220_MAX_CHARS_PER_BUFFER
;
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
return
count
;
}
/*
* Return number of buffered chars.
*/
static
int
sclp_vt220_chars_in_buffer
(
struct
tty_struct
*
tty
)
{
unsigned
long
flags
;
struct
list_head
*
l
;
struct
sclp_vt220_request
*
r
;
int
count
;
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
count
=
0
;
if
(
sclp_vt220_current_request
!=
NULL
)
count
=
sclp_vt220_chars_stored
(
sclp_vt220_current_request
);
list_for_each
(
l
,
&
sclp_vt220_outqueue
)
{
r
=
list_entry
(
l
,
struct
sclp_vt220_request
,
list
);
count
+=
sclp_vt220_chars_stored
(
r
);
}
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
return
count
;
}
static
void
__sclp_vt220_flush_buffer
(
void
)
{
unsigned
long
flags
;
sclp_vt220_emit_current
();
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
if
(
timer_pending
(
&
sclp_vt220_timer
))
del_timer
(
&
sclp_vt220_timer
);
while
(
sclp_vt220_outqueue_count
>
0
)
{
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
sclp_sync_wait
();
spin_lock_irqsave
(
&
sclp_vt220_lock
,
flags
);
}
spin_unlock_irqrestore
(
&
sclp_vt220_lock
,
flags
);
}
/*
* Pass on all buffers to the hardware. Return only when there are no more
* buffers pending.
*/
static
void
sclp_vt220_flush_buffer
(
struct
tty_struct
*
tty
)
{
sclp_vt220_emit_current
();
}
/*
* Initialize all relevant components and register driver with system.
*/
static
int
__sclp_vt220_init
(
int
early
)
{
void
*
page
;
int
i
;
if
(
sclp_vt220_initialized
)
return
0
;
sclp_vt220_initialized
=
1
;
spin_lock_init
(
&
sclp_vt220_lock
);
INIT_LIST_HEAD
(
&
sclp_vt220_empty
);
INIT_LIST_HEAD
(
&
sclp_vt220_outqueue
);
init_waitqueue_head
(
&
sclp_vt220_waitq
);
init_timer
(
&
sclp_vt220_timer
);
sclp_vt220_current_request
=
NULL
;
sclp_vt220_buffered_chars
=
0
;
sclp_vt220_outqueue_count
=
0
;
sclp_vt220_tty
=
NULL
;
sclp_vt220_flush_later
=
0
;
/* Allocate pages for output buffering */
for
(
i
=
0
;
i
<
(
early
?
MAX_CONSOLE_PAGES
:
MAX_KMEM_PAGES
);
i
++
)
{
if
(
early
)
page
=
alloc_bootmem_low_pages
(
PAGE_SIZE
);
else
page
=
(
void
*
)
get_zeroed_page
(
GFP_KERNEL
|
GFP_DMA
);
if
(
!
page
)
return
-
ENOMEM
;
list_add_tail
((
struct
list_head
*
)
page
,
&
sclp_vt220_empty
);
}
return
0
;
}
static
struct
tty_operations
sclp_vt220_ops
=
{
.
open
=
sclp_vt220_open
,
.
close
=
sclp_vt220_close
,
.
write
=
sclp_vt220_write
,
.
put_char
=
sclp_vt220_put_char
,
.
flush_chars
=
sclp_vt220_flush_chars
,
.
write_room
=
sclp_vt220_write_room
,
.
chars_in_buffer
=
sclp_vt220_chars_in_buffer
,
.
flush_buffer
=
sclp_vt220_flush_buffer
};
/*
* Register driver with SCLP and Linux and initialize internal tty structures.
*/
int
__init
sclp_vt220_tty_init
(
void
)
{
struct
tty_driver
*
driver
;
int
rc
;
/* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
* symmetry between VM and LPAR systems regarding ttyS1. */
driver
=
alloc_tty_driver
(
1
);
if
(
!
driver
)
return
-
ENOMEM
;
rc
=
__sclp_vt220_init
(
0
);
if
(
rc
)
{
put_tty_driver
(
driver
);
return
rc
;
}
rc
=
sclp_register
(
&
sclp_vt220_register
);
if
(
rc
)
{
printk
(
KERN_ERR
SCLP_VT220_PRINT_HEADER
"could not register tty - "
"sclp_register returned %d
\n
"
,
rc
);
put_tty_driver
(
driver
);
return
rc
;
}
driver
->
owner
=
THIS_MODULE
;
driver
->
driver_name
=
SCLP_VT220_DRIVER_NAME
;
driver
->
name
=
SCLP_VT220_DEVICE_NAME
;
driver
->
major
=
SCLP_VT220_MAJOR
;
driver
->
minor_start
=
SCLP_VT220_MINOR
;
driver
->
type
=
TTY_DRIVER_TYPE_SYSTEM
;
driver
->
subtype
=
SYSTEM_TYPE_TTY
;
driver
->
init_termios
=
tty_std_termios
;
driver
->
flags
=
TTY_DRIVER_REAL_RAW
;
tty_set_operations
(
driver
,
&
sclp_vt220_ops
);
rc
=
tty_register_driver
(
driver
);
if
(
rc
)
{
printk
(
KERN_ERR
SCLP_VT220_PRINT_HEADER
"could not register tty - "
"tty_register_driver returned %d
\n
"
,
rc
);
put_tty_driver
(
driver
);
return
rc
;
}
sclp_vt220_driver
=
driver
;
return
0
;
}
module_init
(
sclp_vt220_tty_init
);
#ifdef CONFIG_SCLP_VT220_CONSOLE
static
void
sclp_vt220_con_write
(
struct
console
*
con
,
const
char
*
buf
,
unsigned
int
count
)
{
__sclp_vt220_write
(
0
,
(
const
unsigned
char
*
)
buf
,
count
,
1
,
1
);
}
static
struct
tty_driver
*
sclp_vt220_con_device
(
struct
console
*
c
,
int
*
index
)
{
*
index
=
0
;
return
sclp_vt220_driver
;
}
/*
* This routine is called from panic when the kernel is going to give up.
* We have to make sure that all buffers will be flushed to the SCLP.
* Note that this function may be called from within an interrupt context.
*/
static
void
sclp_vt220_con_unblank
(
void
)
{
__sclp_vt220_flush_buffer
();
}
/* Structure needed to register with printk */
static
struct
console
sclp_vt220_console
=
{
.
name
=
SCLP_VT220_CONSOLE_NAME
,
.
write
=
sclp_vt220_con_write
,
.
device
=
sclp_vt220_con_device
,
.
unblank
=
sclp_vt220_con_unblank
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
SCLP_VT220_CONSOLE_INDEX
};
static
int
__init
sclp_vt220_con_init
(
void
)
{
int
rc
;
if
(
!
CONSOLE_IS_SCLP
)
return
0
;
rc
=
__sclp_vt220_init
(
1
);
if
(
rc
)
return
rc
;
/* Attach linux console */
register_console
(
&
sclp_vt220_console
);
return
0
;
}
console_initcall
(
sclp_vt220_con_init
);
#endif
/* CONFIG_SCLP_VT220_CONSOLE */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment