Commit 49d7127a authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: sclp driver part 1.

Reworked sclp driver part 1.
parent e540dbf8
...@@ -108,9 +108,10 @@ CONFIG_UNIX98_PTY_COUNT=2048 ...@@ -108,9 +108,10 @@ CONFIG_UNIX98_PTY_COUNT=2048
# CONFIG_TN3270 is not set # CONFIG_TN3270 is not set
CONFIG_TN3215=y CONFIG_TN3215=y
CONFIG_TN3215_CONSOLE=y CONFIG_TN3215_CONSOLE=y
CONFIG_HWC=y CONFIG_SCLP=y
CONFIG_HWC_CONSOLE=y CONFIG_SCLP_TTY=y
CONFIG_HWC_CPI=m CONFIG_SCLP_CONSOLE=y
CONFIG_SCLP_CPI=m
CONFIG_S390_TAPE=m CONFIG_S390_TAPE=m
# #
......
...@@ -164,9 +164,9 @@ __setup("condev=", condev_setup); ...@@ -164,9 +164,9 @@ __setup("condev=", condev_setup);
static int __init conmode_setup(char *str) static int __init conmode_setup(char *str)
{ {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
if (strncmp(str, "hwc", 4) == 0) if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
#if defined(CONFIG_TN3215_CONSOLE) #if defined(CONFIG_TN3215_CONSOLE)
if (strncmp(str, "3215", 5) == 0) if (strncmp(str, "3215", 5) == 0)
...@@ -198,8 +198,8 @@ static void __init conmode_default(void) ...@@ -198,8 +198,8 @@ static void __init conmode_default(void)
*/ */
cpcmd("TERM CONMODE 3215", NULL, 0); cpcmd("TERM CONMODE 3215", NULL, 0);
if (ptr == NULL) { if (ptr == NULL) {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
return; return;
} }
...@@ -208,16 +208,16 @@ static void __init conmode_default(void) ...@@ -208,16 +208,16 @@ static void __init conmode_default(void)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#elif defined(CONFIG_TN3215_CONSOLE) #elif defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215; SET_CONSOLE_3215;
#elif defined(CONFIG_HWC_CONSOLE) #elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} else if (strncmp(ptr + 8, "3215", 4) == 0) { } else if (strncmp(ptr + 8, "3215", 4) == 0) {
#if defined(CONFIG_TN3215_CONSOLE) #if defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215; SET_CONSOLE_3215;
#elif defined(CONFIG_TN3270_CONSOLE) #elif defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#elif defined(CONFIG_HWC_CONSOLE) #elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} }
} else if (MACHINE_IS_P390) { } else if (MACHINE_IS_P390) {
...@@ -227,8 +227,8 @@ static void __init conmode_default(void) ...@@ -227,8 +227,8 @@ static void __init conmode_default(void)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#endif #endif
} else { } else {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} }
} }
......
...@@ -166,9 +166,10 @@ CONFIG_UNIX98_PTY_COUNT=2048 ...@@ -166,9 +166,10 @@ CONFIG_UNIX98_PTY_COUNT=2048
# CONFIG_TN3270 is not set # CONFIG_TN3270 is not set
CONFIG_TN3215=y CONFIG_TN3215=y
CONFIG_TN3215_CONSOLE=y CONFIG_TN3215_CONSOLE=y
CONFIG_HWC=y CONFIG_SCLP=y
CONFIG_HWC_CONSOLE=y CONFIG_SCLP_TTY=y
CONFIG_HWC_CPI=m CONFIG_SCLP_CONSOLE=y
CONFIG_SCLP_CPI=m
CONFIG_S390_TAPE=m CONFIG_S390_TAPE=m
# #
......
...@@ -164,9 +164,9 @@ __setup("condev=", condev_setup); ...@@ -164,9 +164,9 @@ __setup("condev=", condev_setup);
static int __init conmode_setup(char *str) static int __init conmode_setup(char *str)
{ {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
if (strncmp(str, "hwc", 4) == 0) if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
#if defined(CONFIG_TN3215_CONSOLE) #if defined(CONFIG_TN3215_CONSOLE)
if (strncmp(str, "3215", 5) == 0) if (strncmp(str, "3215", 5) == 0)
...@@ -198,8 +198,8 @@ static void __init conmode_default(void) ...@@ -198,8 +198,8 @@ static void __init conmode_default(void)
*/ */
cpcmd("TERM CONMODE 3215", NULL, 0); cpcmd("TERM CONMODE 3215", NULL, 0);
if (ptr == NULL) { if (ptr == NULL) {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
return; return;
} }
...@@ -208,16 +208,16 @@ static void __init conmode_default(void) ...@@ -208,16 +208,16 @@ static void __init conmode_default(void)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#elif defined(CONFIG_TN3215_CONSOLE) #elif defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215; SET_CONSOLE_3215;
#elif defined(CONFIG_HWC_CONSOLE) #elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} else if (strncmp(ptr + 8, "3215", 4) == 0) { } else if (strncmp(ptr + 8, "3215", 4) == 0) {
#if defined(CONFIG_TN3215_CONSOLE) #if defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215; SET_CONSOLE_3215;
#elif defined(CONFIG_TN3270_CONSOLE) #elif defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#elif defined(CONFIG_HWC_CONSOLE) #elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} }
} else if (MACHINE_IS_P390) { } else if (MACHINE_IS_P390) {
...@@ -227,8 +227,8 @@ static void __init conmode_default(void) ...@@ -227,8 +227,8 @@ static void __init conmode_default(void)
SET_CONSOLE_3270; SET_CONSOLE_3270;
#endif #endif
} else { } else {
#if defined(CONFIG_HWC_CONSOLE) #if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_HWC; SET_CONSOLE_SCLP;
#endif #endif
} }
} }
......
...@@ -146,10 +146,9 @@ extern long serial167_console_init(void); ...@@ -146,10 +146,9 @@ extern long serial167_console_init(void);
extern void console_8xx_init(void); extern void console_8xx_init(void);
extern int rs_8xx_init(void); extern int rs_8xx_init(void);
extern void mac_scc_console_init(void); extern void mac_scc_console_init(void);
extern void hwc_console_init(void); extern void sclp_console_init(void);
extern void hwc_tty_init(void); extern void sclp_tty_init(void);
extern void con3215_init(void); extern void con3215_init(void);
extern void tty3215_init(void);
extern void tub3270_con_init(void); extern void tub3270_con_init(void);
extern void tub3270_init(void); extern void tub3270_init(void);
extern void uart_console_init(void); extern void uart_console_init(void);
...@@ -2208,8 +2207,8 @@ void __init console_init(void) ...@@ -2208,8 +2207,8 @@ void __init console_init(void)
#ifdef CONFIG_TN3215 #ifdef CONFIG_TN3215
con3215_init(); con3215_init();
#endif #endif
#ifdef CONFIG_HWC #ifdef CONFIG_SCLP_CONSOLE
hwc_console_init(); sclp_console_init();
#endif #endif
#ifdef CONFIG_STDIO_CONSOLE #ifdef CONFIG_STDIO_CONSOLE
stdio_console_init(); stdio_console_init();
...@@ -2349,8 +2348,8 @@ void __init tty_init(void) ...@@ -2349,8 +2348,8 @@ void __init tty_init(void)
#ifdef CONFIG_TN3215 #ifdef CONFIG_TN3215
tty3215_init(); tty3215_init();
#endif #endif
#ifdef CONFIG_HWC #ifdef CONFIG_SCLP
hwc_tty_init(); sclp_tty_init();
#endif #endif
#ifdef CONFIG_A2232 #ifdef CONFIG_A2232
a2232board_init(); a2232board_init();
......
...@@ -257,24 +257,30 @@ config TN3215_CONSOLE ...@@ -257,24 +257,30 @@ config TN3215_CONSOLE
Include support for using an IBM 3215 line-mode terminal as a Include support for using an IBM 3215 line-mode terminal as a
Linux system console. Linux system console.
config HWC config SCLP
bool "Support for HWC line mode terminal" bool "Support for SCLP"
help help
Include support for IBM HWC line-mode terminals. Include support for the SCLP interface to the service element.
config HWC_CONSOLE config SCLP_TTY
bool "console on HWC line mode terminal" bool "Support for SCLP line mode terminal"
depends on HWC depends on SCLP
help
Include support for IBM SCLP line-mode terminals.
config SCLP_CONSOLE
bool "Support for console on SCLP line mode terminal"
depends on SCLP_TTY
help help
Include support for using an IBM HWC line-mode terminal as the Linux Include support for using an IBM HWC line-mode terminal as the Linux
system console. system console.
config HWC_CPI config SCLP_CPI
tristate "Control-Program Identification" tristate "Control-Program Identification"
depends on HWC depends on SCLP
help help
This option enables the hardware console interface for system This option enables the hardware console interface for system
identification This is commonly used for workload management and identification. This is commonly used for workload management and
gives you a nice name for the system on the service element. gives you a nice name for the system on the service element.
Please select this option as a module since built-in operation is Please select this option as a module since built-in operation is
completely untested. completely untested.
......
...@@ -2,28 +2,23 @@ ...@@ -2,28 +2,23 @@
# S/390 character devices # S/390 character devices
# #
export-objs := hwc_rw.o tape.o tape34xx.o export-objs := sclp_rw.o tape_core.o tape_devmap.o tape_std.o
tub3270-objs := tuball.o tubfs.o tubtty.o \ tub3270-objs := tuball.o tubfs.o tubtty.o \
tubttyaid.o tubttybld.o tubttyscl.o \ tubttyaid.o tubttybld.o tubttyscl.o \
tubttyrcl.o tubttysiz.o tubttyrcl.o tubttysiz.o
tape390-$(CONFIG_S390_TAPE_CHAR) += tapechar.o
tape390-$(CONFIG_S390_TAPE_BLOCK) += tapeblock.o
tape390-objs := tape.o tape34xx.o $(sort $(tape390-y))
tape_3480_mod-objs := tape3480.o
tape_3490_mod-objs := tape3490.o
obj-y += ctrlchar.o obj-y += ctrlchar.o
obj-$(CONFIG_TN3215) += con3215.o obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_HWC) += hwc_con.o hwc_rw.o hwc_tty.o obj-$(CONFIG_SCLP) += sclp.o
obj-$(CONFIG_HWC_CPI) += hwc_cpi.o obj-$(CONFIG_SCLP_TTY) += sclp_rw.o sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
obj-$(CONFIG_TN3270) += tub3270.o obj-$(CONFIG_TN3270) += tub3270.o
obj-$(CONFIG_S390_TAPE) += tape390.o tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
obj-$(CONFIG_S390_TAPE_3480) += tape_3480_mod.o tape-$(CONFIG_PROC_FS) += tape_proc.o
obj-$(CONFIG_S390_TAPE_3490) += tape_3490_mod.o tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
obj-$(CONFIG_S390_TAPE) += tape.o
obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
* drivers/s390/char/hwc.h
*
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
*
*
*/
#ifndef __HWC_H__
#define __HWC_H__
#define HWC_EXT_INT_PARAM_ADDR 0xFFFFFFF8
#define HWC_EXT_INT_PARAM_PEND 0x00000001
#define ET_OpCmd 0x01
#define ET_Msg 0x02
#define ET_StateChange 0x08
#define ET_PMsgCmd 0x09
#define ET_CntlProgOpCmd 0x20
#define ET_CntlProgIdent 0x0B
#define ET_SigQuiesce 0x1D
#define ET_OpCmd_Mask 0x80000000
#define ET_Msg_Mask 0x40000000
#define ET_StateChange_Mask 0x01000000
#define ET_PMsgCmd_Mask 0x00800000
#define ET_CtlProgOpCmd_Mask 0x00000001
#define ET_CtlProgIdent_Mask 0x00200000
#define ET_SigQuiesce_Mask 0x00000008
#define GMF_DOM 0x8000
#define GMF_SndAlrm 0x4000
#define GMF_HoldMsg 0x2000
#define LTF_CntlText 0x8000
#define LTF_LabelText 0x4000
#define LTF_DataText 0x2000
#define LTF_EndText 0x1000
#define LTF_PromptText 0x0800
#define HWC_COMMAND_INITIATED 0
#define HWC_BUSY 2
#define HWC_NOT_OPERATIONAL 3
#define hwc_cmdw_t u32;
#define HWC_CMDW_READDATA 0x00770005
#define HWC_CMDW_WRITEDATA 0x00760005
#define HWC_CMDW_WRITEMASK 0x00780005
#define GDS_ID_MDSMU 0x1310
#define GDS_ID_MDSRouteInfo 0x1311
#define GDS_ID_AgUnWrkCorr 0x1549
#define GDS_ID_SNACondReport 0x1532
#define GDS_ID_CPMSU 0x1212
#define GDS_ID_RoutTargInstr 0x154D
#define GDS_ID_OpReq 0x8070
#define GDS_ID_TextCmd 0x1320
#define GDS_KEY_SelfDefTextMsg 0x31
#define _HWCB_HEADER u16 length; \
u8 function_code; \
u8 control_mask[3]; \
u16 response_code;
#define _EBUF_HEADER u16 length; \
u8 type; \
u8 flags; \
u16 _reserved;
typedef struct {
_EBUF_HEADER
} __attribute__ ((packed))
evbuf_t;
#define _MDB_HEADER u16 length; \
u16 type; \
u32 tag; \
u32 revision_code;
#define _GO_HEADER u16 length; \
u16 type; \
u32 domid; \
u8 hhmmss_time[8]; \
u8 th_time[3]; \
u8 _reserved_0; \
u8 dddyyyy_date[7]; \
u8 _reserved_1; \
u16 general_msg_flags; \
u8 _reserved_2[10]; \
u8 originating_system_name[8]; \
u8 job_guest_name[8];
#define _MTO_HEADER u16 length; \
u16 type; \
u16 line_type_flags; \
u8 alarm_control; \
u8 _reserved[3];
typedef struct {
_GO_HEADER
} __attribute__ ((packed))
go_t;
typedef struct {
go_t go;
} __attribute__ ((packed))
mdb_body_t;
typedef struct {
_MDB_HEADER
mdb_body_t mdb_body;
} __attribute__ ((packed))
mdb_t;
typedef struct {
_EBUF_HEADER
mdb_t mdb;
} __attribute__ ((packed))
msgbuf_t;
typedef struct {
_HWCB_HEADER
msgbuf_t msgbuf;
} __attribute__ ((packed))
write_hwcb_t;
typedef struct {
_MTO_HEADER
} __attribute__ ((packed))
mto_t;
/* FIXME: don't define static variables in a header!!! */
#warning
static write_hwcb_t write_hwcb_template =
{
sizeof (write_hwcb_t),
0x00,
{
0x00,
0x00,
0x00
},
0x0000,
{
sizeof (msgbuf_t),
ET_Msg,
0x00,
0x0000,
{
sizeof (mdb_t),
0x0001,
0xD4C4C240,
0x00000001,
{
{
sizeof (go_t),
0x0001
}
}
}
}
};
#warning
static mto_t mto_template =
{
sizeof (mto_t),
0x0004,
LTF_EndText,
0x00
};
typedef u32 _hwcb_mask_t;
typedef struct {
_HWCB_HEADER
u16 _reserved;
u16 mask_length;
_hwcb_mask_t cp_receive_mask;
_hwcb_mask_t cp_send_mask;
_hwcb_mask_t hwc_receive_mask;
_hwcb_mask_t hwc_send_mask;
} __attribute__ ((packed))
init_hwcb_t;
#warning
static init_hwcb_t init_hwcb_template =
{
sizeof (init_hwcb_t),
0x00,
{
0x00,
0x00,
0x00
},
0x0000,
0x0000,
sizeof (_hwcb_mask_t),
ET_OpCmd_Mask | ET_PMsgCmd_Mask |
ET_StateChange_Mask | ET_SigQuiesce_Mask,
ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask
};
typedef struct {
_EBUF_HEADER
u8 validity_hwc_active_facility_mask:1;
u8 validity_hwc_receive_mask:1;
u8 validity_hwc_send_mask:1;
u8 validity_read_data_function_mask:1;
u16 _zeros:12;
u16 mask_length;
u64 hwc_active_facility_mask;
_hwcb_mask_t hwc_receive_mask;
_hwcb_mask_t hwc_send_mask;
u32 read_data_function_mask;
} __attribute__ ((packed))
statechangebuf_t;
#define _GDS_VECTOR_HEADER u16 length; \
u16 gds_id;
#define _GDS_SUBVECTOR_HEADER u8 length; \
u8 key;
typedef struct {
_GDS_VECTOR_HEADER
} __attribute__ ((packed))
gds_vector_t;
typedef struct {
_GDS_SUBVECTOR_HEADER
} __attribute__ ((packed))
gds_subvector_t;
typedef struct {
_HWCB_HEADER
} __attribute__ ((packed))
read_hwcb_t;
#warning
static read_hwcb_t read_hwcb_template =
{
PAGE_SIZE,
0x00,
{
0x00,
0x00,
0x80
}
};
#endif /* __HWC_H__ */
/*
* drivers/s390/char/hwc_con.c
* HWC line mode console driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/kdev_t.h>
#include <linux/string.h>
#include <linux/console.h>
#include <linux/fs.h>
#include <linux/init.h>
#include "hwc_rw.h"
#ifdef CONFIG_HWC_CONSOLE
#define hwc_console_major 4
#define hwc_console_minor 64
#define hwc_console_name "console"
void hwc_console_write (struct console *, const char *, unsigned int);
kdev_t hwc_console_device (struct console *);
void hwc_console_unblank (void);
#define HWC_CON_PRINT_HEADER "hwc console driver: "
struct console hwc_console =
{
name:hwc_console_name,
write:hwc_console_write,
device:hwc_console_device,
unblank:hwc_console_unblank,
flags:CON_PRINTBUFFER,
};
void
hwc_console_write (
struct console *console,
const char *message,
unsigned int count)
{
if (kdev_val (console->device (console)) !=
kdev_val (hwc_console.device (&hwc_console))) {
hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER
"hwc_console_write() called with wrong "
"device number");
return;
}
hwc_write (0, message, count);
}
kdev_t
hwc_console_device (struct console * c)
{
return mk_kdev (hwc_console_major, hwc_console_minor);
}
void
hwc_console_unblank (void)
{
hwc_unblank ();
}
#endif
void __init
hwc_console_init (void)
{
if (!MACHINE_HAS_HWC)
return;
if (hwc_init () == 0) {
#ifdef CONFIG_HWC_CONSOLE
if (CONSOLE_IS_HWC)
register_console (&hwc_console);
#endif
} else
panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !");
return;
}
/*
* Author: Martin Peschke <mpeschke@de.ibm.com>
* Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
*/
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <asm/semaphore.h>
#include <asm/ebcdic.h>
#include "hwc_rw.h"
#include "hwc.h"
#define CPI_RETRIES 3
#define CPI_SLEEP_TICKS 50
#define CPI_LENGTH_SYSTEM_TYPE 8
#define CPI_LENGTH_SYSTEM_NAME 8
#define CPI_LENGTH_SYSPLEX_NAME 8
typedef struct {
_EBUF_HEADER
u8 id_format;
u8 reserved0;
u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
u64 reserved1;
u8 system_name[CPI_LENGTH_SYSTEM_NAME];
u64 reserved2;
u64 system_level;
u64 reserved3;
u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
u8 reserved4[16];
} __attribute__ ((packed))
cpi_evbuf_t;
typedef struct _cpi_hwcb_t {
_HWCB_HEADER
cpi_evbuf_t cpi_evbuf;
} __attribute__ ((packed))
cpi_hwcb_t;
cpi_hwcb_t *cpi_hwcb;
static int __init cpi_module_init (void);
static void __exit cpi_module_exit (void);
module_init (cpi_module_init);
module_exit (cpi_module_exit);
MODULE_AUTHOR (
"Martin Peschke, IBM Deutschland Entwicklung GmbH "
"<mpeschke@de.ibm.com>");
MODULE_DESCRIPTION (
"identify this operating system instance to the S/390 or zSeries hardware");
static char *system_name = NULL;
MODULE_PARM (system_name, "s");
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_PARM_DESC (sysplex_name, "if applicable - max. 8 characters");
#endif
static char *system_type = "LINUX";
hwc_request_t cpi_request =
{};
hwc_callback_t cpi_callback;
static DECLARE_MUTEX_LOCKED (sem);
static int __init
cpi_module_init (void)
{
int retval;
int system_type_length;
int system_name_length;
int sysplex_name_length = 0;
int retries;
if (!MACHINE_HAS_HWC) {
printk ("cpi: bug: hardware console not present\n");
retval = -EINVAL;
goto out;
}
if (!system_type) {
printk ("cpi: bug: no system type specified\n");
retval = -EINVAL;
goto out;
}
system_type_length = strlen (system_type);
if (system_type_length > CPI_LENGTH_SYSTEM_NAME) {
printk ("cpi: bug: system type has length of %i characters - "
"only %i characters supported\n",
system_type_length,
CPI_LENGTH_SYSTEM_TYPE);
retval = -EINVAL;
goto out;
}
if (!system_name) {
printk ("cpi: no system name specified\n");
retval = -EINVAL;
goto out;
}
system_name_length = strlen (system_name);
if (system_name_length > CPI_LENGTH_SYSTEM_NAME) {
printk ("cpi: system name has length of %i characters - "
"only %i characters supported\n",
system_name_length,
CPI_LENGTH_SYSTEM_NAME);
retval = -EINVAL;
goto out;
}
if (sysplex_name) {
sysplex_name_length = strlen (sysplex_name);
if (sysplex_name_length > CPI_LENGTH_SYSPLEX_NAME) {
printk ("cpi: sysplex name has length of %i characters - "
"only %i characters supported\n",
sysplex_name_length,
CPI_LENGTH_SYSPLEX_NAME);
retval = -EINVAL;
goto out;
}
}
cpi_hwcb = kmalloc (sizeof (cpi_hwcb_t), GFP_KERNEL);
if (!cpi_hwcb) {
printk ("cpi: no storage to fulfill request\n");
retval = -ENOMEM;
goto out;
}
memset (cpi_hwcb, 0, sizeof (cpi_hwcb_t));
cpi_hwcb->length = sizeof (cpi_hwcb_t);
cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t);
cpi_hwcb->cpi_evbuf.type = 0x0B;
memset (cpi_hwcb->cpi_evbuf.system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
memcpy (cpi_hwcb->cpi_evbuf.system_type, system_type, system_type_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
memset (cpi_hwcb->cpi_evbuf.system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
memcpy (cpi_hwcb->cpi_evbuf.system_name, system_name, system_name_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE;
if (sysplex_name) {
memset (cpi_hwcb->cpi_evbuf.sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
memcpy (cpi_hwcb->cpi_evbuf.sysplex_name, sysplex_name, sysplex_name_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
}
cpi_request.block = cpi_hwcb;
cpi_request.word = HWC_CMDW_WRITEDATA;
cpi_request.callback = cpi_callback;
for (retries = CPI_RETRIES; retries; retries--) {
retval = hwc_send (&cpi_request);
if (retval) {
set_current_state (TASK_INTERRUPTIBLE);
schedule_timeout (CPI_SLEEP_TICKS);
} else {
down (&sem);
switch (cpi_hwcb->response_code) {
case 0x0020:
printk ("cpi: succeeded\n");
break;
default:
printk ("cpi: failed with response code 0x%x\n",
cpi_hwcb->response_code);
}
goto free;
}
}
printk ("cpi: failed (%i)\n", retval);
free:
kfree (cpi_hwcb);
out:
return retval;
}
static void __exit
cpi_module_exit (void)
{
printk ("cpi: exit\n");
}
void
cpi_callback (hwc_request_t * req)
{
up (&sem);
}
/*
* drivers/s390/char/hwc_rw.c
* driver: reading from and writing to system console on S/390 via HWC
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
*
*
*
*
*
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <asm/ebcdic.h>
#include <asm/uaccess.h>
#include <asm/types.h>
#include <asm/bitops.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/s390_ext.h>
#include <asm/irq.h>
#ifndef MIN
#define MIN(a,b) (((a<b) ? a : b))
#endif
extern void ctrl_alt_del (void);
#define HWC_RW_PRINT_HEADER "hwc low level driver: "
#define USE_VM_DETECTION
#define DEFAULT_CASE_DELIMITER '%'
#undef DUMP_HWC_INIT_ERROR
#undef DUMP_HWC_WRITE_ERROR
#undef DUMP_HWC_WRITE_LIST_ERROR
#undef DUMP_HWC_READ_ERROR
#undef DUMP_HWCB_INPUT
#undef BUFFER_STRESS_TEST
typedef struct {
unsigned char *next;
unsigned short int mto_char_sum;
unsigned char mto_number;
unsigned char times_lost;
unsigned short int mto_number_lost;
unsigned long int mto_char_sum_lost;
} __attribute__ ((packed))
hwcb_list_t;
#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))
#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))
#define BUF_HWCB hwc_data.hwcb_list_tail
#define OUT_HWCB hwc_data.hwcb_list_head
#define ALL_HWCB_MTO hwc_data.mto_number
#define ALL_HWCB_CHAR hwc_data.mto_char_sum
#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))
#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)
#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)
#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)
#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)
#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)
#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)
#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)
#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)
#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)
#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)
#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)
#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)
#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)
#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)
#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)
#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)
#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)
#include "hwc.h"
#define __HWC_RW_C__
#include "hwc_rw.h"
#undef __HWC_RW_C__
static unsigned char _obuf[MAX_HWCB_ROOM];
static unsigned char
_page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
typedef unsigned long kmem_pages_t;
#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
#define HWC_WTIMER_RUNS 1
#define HWC_FLUSH 2
#define HWC_INIT 4
#define HWC_BROKEN 8
#define HWC_INTERRUPT 16
#define HWC_PTIMER_RUNS 32
static struct {
hwc_ioctls_t ioctls;
hwc_ioctls_t init_ioctls;
unsigned char *hwcb_list_head;
unsigned char *hwcb_list_tail;
unsigned short int mto_number;
unsigned int mto_char_sum;
unsigned char hwcb_count;
unsigned long kmem_start;
unsigned long kmem_end;
kmem_pages_t kmem_pages;
unsigned char *obuf;
unsigned short int obuf_cursor;
unsigned short int obuf_count;
unsigned short int obuf_start;
unsigned char *page;
u32 current_servc;
unsigned char *current_hwcb;
unsigned char write_nonprio:1;
unsigned char write_prio:1;
unsigned char read_nonprio:1;
unsigned char read_prio:1;
unsigned char read_statechange:1;
unsigned char sig_quiesce:1;
unsigned char flags;
hwc_high_level_calls_t *calls;
hwc_request_t *request;
spinlock_t lock;
struct timer_list write_timer;
struct timer_list poll_timer;
} hwc_data =
{
{
},
{
8,
0,
80,
1,
MAX_KMEM_PAGES,
MAX_KMEM_PAGES,
0,
0x6c
},
NULL,
NULL,
0,
0,
0,
0,
0,
0,
_obuf,
0,
0,
0,
_page,
0,
NULL,
0,
0,
0,
0,
0,
0,
0,
NULL,
NULL
};
static unsigned long cr0 __attribute__ ((aligned (8)));
static unsigned long cr0_save __attribute__ ((aligned (8)));
static unsigned char psw_mask __attribute__ ((aligned (8)));
static ext_int_info_t ext_int_info_hwc;
#define DELAYED_WRITE 0
#define IMMEDIATE_WRITE 1
static signed int do_hwc_write (int from_user, unsigned char *,
unsigned int,
unsigned char);
unsigned char hwc_ip_buf[512];
static asmlinkage int
internal_print (char write_time, char *fmt,...)
{
va_list args;
int i;
va_start (args, fmt);
i = vsprintf (hwc_ip_buf, fmt, args);
va_end (args);
return do_hwc_write (0, hwc_ip_buf, i, write_time);
}
int
hwc_printk (const char *fmt,...)
{
va_list args;
int i;
unsigned long flags;
int retval;
spin_lock_irqsave (&hwc_data.lock, flags);
i = vsprintf (hwc_ip_buf, fmt, args);
va_end (args);
retval = do_hwc_write (0, hwc_ip_buf, i, IMMEDIATE_WRITE);
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
#ifdef DUMP_HWCB_INPUT
static void
dump_storage_area (unsigned char *area, unsigned short int count)
{
unsigned short int index;
ioctl_nl_t old_final_nl;
if (!area || !count)
return;
old_final_nl = hwc_data.ioctls.final_nl;
hwc_data.ioctls.final_nl = 1;
internal_print (DELAYED_WRITE, "\n%8x ", area);
for (index = 0; index < count; index++) {
if (area[index] <= 0xF)
internal_print (DELAYED_WRITE, "0%x", area[index]);
else
internal_print (DELAYED_WRITE, "%x", area[index]);
if ((index & 0xF) == 0xF)
internal_print (DELAYED_WRITE, "\n%8x ",
&area[index + 1]);
else if ((index & 3) == 3)
internal_print (DELAYED_WRITE, " ");
}
internal_print (IMMEDIATE_WRITE, "\n");
hwc_data.ioctls.final_nl = old_final_nl;
}
#endif
static inline u32
service_call (
u32 hwc_command_word,
unsigned char hwcb[])
{
unsigned int condition_code = 1;
__asm__ __volatile__ ("L 1, 0(%0) \n\t"
"LRA 2, 0(%1) \n\t"
".long 0xB2200012 \n\t"
:
:"a" (&hwc_command_word), "a" (hwcb)
:"1", "2", "memory");
__asm__ __volatile__ ("IPM %0 \n\t"
"SRL %0, 28 \n\t"
:"=r" (condition_code));
return condition_code;
}
static inline unsigned long
hwc_ext_int_param (void)
{
u32 param;
__asm__ __volatile__ ("L %0,128\n\t"
:"=r" (param));
return (unsigned long) param;
}
static int
prepare_write_hwcb (void)
{
write_hwcb_t *hwcb;
if (!BUF_HWCB)
return -ENOMEM;
BUF_HWCB_MTO = 0;
BUF_HWCB_CHAR = 0;
hwcb = (write_hwcb_t *) BUF_HWCB;
memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t));
return 0;
}
static int
sane_write_hwcb (void)
{
unsigned short int lost_msg;
unsigned int lost_char;
unsigned char lost_hwcb;
unsigned char *bad_addr;
unsigned long page;
int page_nr;
if (!OUT_HWCB)
return -ENOMEM;
if ((unsigned long) OUT_HWCB & 0xFFF) {
bad_addr = OUT_HWCB;
#ifdef DUMP_HWC_WRITE_LIST_ERROR
__asm__ ("LHI 1,0xe30\n\t"
"LRA 2,0(%0) \n\t"
"J .+0 \n\t"
:
: "a" (bad_addr)
: "1", "2");
#endif
hwc_data.kmem_pages = 0;
if ((unsigned long) BUF_HWCB & 0xFFF) {
lost_hwcb = hwc_data.hwcb_count;
lost_msg = ALL_HWCB_MTO;
lost_char = ALL_HWCB_CHAR;
OUT_HWCB = NULL;
BUF_HWCB = NULL;
ALL_HWCB_MTO = 0;
ALL_HWCB_CHAR = 0;
hwc_data.hwcb_count = 0;
} else {
lost_hwcb = hwc_data.hwcb_count - 1;
lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO;
lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR;
OUT_HWCB = BUF_HWCB;
ALL_HWCB_MTO = BUF_HWCB_MTO;
ALL_HWCB_CHAR = BUF_HWCB_CHAR;
hwc_data.hwcb_count = 1;
page = (unsigned long) BUF_HWCB;
if (page >= hwc_data.kmem_start &&
page <= hwc_data.kmem_end) {
page_nr = (int)
((page - hwc_data.kmem_start) >> 12);
set_bit (page_nr, &hwc_data.kmem_pages);
}
}
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"found invalid HWCB at address 0x%lx. List corrupted. "
"Lost %i HWCBs with %i characters within up to %i "
"messages. Saved %i HWCB with last %i characters i"
"within up to %i messages.\n",
(unsigned long) bad_addr,
lost_hwcb, lost_char, lost_msg,
hwc_data.hwcb_count,
ALL_HWCB_CHAR, ALL_HWCB_MTO);
}
return 0;
}
static int
reuse_write_hwcb (void)
{
int retval;
if (hwc_data.hwcb_count < 2)
#ifdef DUMP_HWC_WRITE_LIST_ERROR
__asm__ ("LHI 1,0xe31\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (BUF_HWCB), "a" (OUT_HWCB)
: "1", "2", "3");
#else
return -EPERM;
#endif
if (hwc_data.current_hwcb == OUT_HWCB) {
if (hwc_data.hwcb_count > 2) {
BUF_HWCB_NEXT = OUT_HWCB_NEXT;
BUF_HWCB = OUT_HWCB_NEXT;
OUT_HWCB_NEXT = BUF_HWCB_NEXT;
BUF_HWCB_NEXT = NULL;
}
} else {
BUF_HWCB_NEXT = OUT_HWCB;
BUF_HWCB = OUT_HWCB;
OUT_HWCB = OUT_HWCB_NEXT;
BUF_HWCB_NEXT = NULL;
}
BUF_HWCB_TIMES_LOST += 1;
BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR;
BUF_HWCB_MTO_LOST += BUF_HWCB_MTO;
ALL_HWCB_MTO -= BUF_HWCB_MTO;
ALL_HWCB_CHAR -= BUF_HWCB_CHAR;
retval = prepare_write_hwcb ();
if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"reached my own limit of "
"allowed buffer space for output (%i HWCBs = %li "
"bytes), skipped content of oldest HWCB %i time(s) "
"(%i lines = %i characters)\n",
hwc_data.ioctls.max_hwcb,
hwc_data.ioctls.max_hwcb * PAGE_SIZE,
BUF_HWCB_TIMES_LOST,
BUF_HWCB_MTO_LOST,
BUF_HWCB_CHAR_LOST);
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"page allocation failed, "
"could not expand buffer for output (currently in "
"use: %i HWCBs = %li bytes), skipped content of "
"oldest HWCB %i time(s) (%i lines = %i characters)\n",
hwc_data.hwcb_count,
hwc_data.hwcb_count * PAGE_SIZE,
BUF_HWCB_TIMES_LOST,
BUF_HWCB_MTO_LOST,
BUF_HWCB_CHAR_LOST);
return retval;
}
static int
allocate_write_hwcb (void)
{
unsigned char *page;
int page_nr;
if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
return -ENOMEM;
page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
if (page_nr < hwc_data.ioctls.kmem_hwcb) {
page = (unsigned char *)
(hwc_data.kmem_start + (page_nr << 12));
set_bit (page_nr, &hwc_data.kmem_pages);
} else
page = (unsigned char *) __get_free_page (GFP_ATOMIC | GFP_DMA);
if (!page)
return -ENOMEM;
if (!OUT_HWCB)
OUT_HWCB = page;
else
BUF_HWCB_NEXT = page;
BUF_HWCB = page;
BUF_HWCB_NEXT = NULL;
hwc_data.hwcb_count++;
prepare_write_hwcb ();
BUF_HWCB_TIMES_LOST = 0;
BUF_HWCB_MTO_LOST = 0;
BUF_HWCB_CHAR_LOST = 0;
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
"*** " HWC_RW_PRINT_HEADER
"page #%i at 0x%x for buffering allocated. ***\n",
hwc_data.hwcb_count, page);
#endif
return 0;
}
static int
release_write_hwcb (void)
{
unsigned long page;
int page_nr;
if (!hwc_data.hwcb_count)
return -ENODATA;
if (hwc_data.hwcb_count == 1) {
prepare_write_hwcb ();
ALL_HWCB_CHAR = 0;
ALL_HWCB_MTO = 0;
BUF_HWCB_TIMES_LOST = 0;
BUF_HWCB_MTO_LOST = 0;
BUF_HWCB_CHAR_LOST = 0;
} else {
page = (unsigned long) OUT_HWCB;
ALL_HWCB_MTO -= OUT_HWCB_MTO;
ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
hwc_data.hwcb_count--;
OUT_HWCB = OUT_HWCB_NEXT;
if (page >= hwc_data.kmem_start &&
page <= hwc_data.kmem_end) {
/*memset((void *) page, 0, PAGE_SIZE); */
page_nr = (int) ((page - hwc_data.kmem_start) >> 12);
clear_bit (page_nr, &hwc_data.kmem_pages);
} else
free_page (page);
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
"*** " HWC_RW_PRINT_HEADER
"page at 0x%x released, %i pages still in use ***\n",
page, hwc_data.hwcb_count);
#endif
}
return 0;
}
static int
add_mto (
unsigned char *message,
unsigned short int count)
{
unsigned short int mto_size;
write_hwcb_t *hwcb;
mto_t *mto;
void *dest;
if (!BUF_HWCB)
return -ENOMEM;
if (BUF_HWCB == hwc_data.current_hwcb)
return -ENOMEM;
mto_size = sizeof (mto_t) + count;
hwcb = (write_hwcb_t *) BUF_HWCB;
if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
return -ENOMEM;
mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
memcpy (mto, &mto_template, sizeof (mto_t));
dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
memcpy (dest, message, count);
mto->length += count;
hwcb->length += mto_size;
hwcb->msgbuf.length += mto_size;
hwcb->msgbuf.mdb.length += mto_size;
BUF_HWCB_MTO++;
ALL_HWCB_MTO++;
BUF_HWCB_CHAR += count;
ALL_HWCB_CHAR += count;
return count;
}
static int write_event_data_1 (void);
static void
do_poll_hwc (unsigned long data)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
write_event_data_1 ();
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
void
start_poll_hwc (void)
{
init_timer (&hwc_data.poll_timer);
hwc_data.poll_timer.function = do_poll_hwc;
hwc_data.poll_timer.data = (unsigned long) NULL;
hwc_data.poll_timer.expires = jiffies + 2 * HZ;
add_timer (&hwc_data.poll_timer);
hwc_data.flags |= HWC_PTIMER_RUNS;
}
static int
write_event_data_1 (void)
{
unsigned short int condition_code;
int retval;
write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB;
if ((!hwc_data.write_prio) &&
(!hwc_data.write_nonprio) &&
hwc_data.read_statechange)
return -EOPNOTSUPP;
if (hwc_data.current_servc)
return -EBUSY;
retval = sane_write_hwcb ();
if (retval < 0)
return -EIO;
if (!OUT_HWCB_MTO)
return -ENODATA;
if (!hwc_data.write_nonprio && hwc_data.write_prio)
hwcb->msgbuf.type = ET_PMsgCmd;
else
hwcb->msgbuf.type = ET_Msg;
condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);
#ifdef DUMP_HWC_WRITE_ERROR
if (condition_code != HWC_COMMAND_INITIATED)
__asm__ ("LHI 1,0xe20\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (&condition_code), "a" (OUT_HWCB)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_WRITEDATA;
hwc_data.current_hwcb = OUT_HWCB;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
case HWC_NOT_OPERATIONAL:
start_poll_hwc ();
default:
retval = -EIO;
}
return retval;
}
static void
flush_hwcbs (void)
{
while (hwc_data.hwcb_count > 1)
release_write_hwcb ();
release_write_hwcb ();
hwc_data.flags &= ~HWC_FLUSH;
}
static int
write_event_data_2 (u32 ext_int_param)
{
write_hwcb_t *hwcb;
int retval = 0;
#ifdef DUMP_HWC_WRITE_ERROR
if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
!= (unsigned long) hwc_data.current_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"write_event_data_2 : "
"HWCB address does not fit "
"(expected: 0x%lx, got: 0x%lx).\n",
(unsigned long) hwc_data.current_hwcb,
ext_int_param);
return -EINVAL;
}
#endif
hwcb = (write_hwcb_t *) OUT_HWCB;
#ifdef DUMP_HWC_WRITE_LIST_ERROR
if (((unsigned char *) hwcb) != hwc_data.current_hwcb) {
__asm__ ("LHI 1,0xe22\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LRA 5,0(%3)\n\t"
"J .+0 \n\t"
:
: "a" (OUT_HWCB),
"a" (hwc_data.current_hwcb),
"a" (BUF_HWCB),
"a" (hwcb)
: "1", "2", "3", "4", "5");
}
#endif
#ifdef DUMP_HWC_WRITE_ERROR
if (hwcb->response_code != 0x0020) {
__asm__ ("LHI 1,0xe21\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LH 5,0(%3)\n\t"
"SRL 5,8\n\t"
"J .+0 \n\t"
:
: "a" (OUT_HWCB), "a" (hwc_data.current_hwcb),
"a" (BUF_HWCB),
"a" (&(hwc_data.hwcb_count))
: "1", "2", "3", "4", "5");
}
#endif
switch (hwcb->response_code) {
case 0x0020:
retval = OUT_HWCB_CHAR;
release_write_hwcb ();
break;
case 0x0040:
case 0x0340:
case 0x40F0:
if (!hwc_data.read_statechange) {
hwcb->response_code = 0;
start_poll_hwc ();
}
retval = -EIO;
break;
default:
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"write_event_data_2 : "
"failed operation "
"(response code: 0x%x "
"HWCB address: 0x%x).\n",
hwcb->response_code,
hwcb);
retval = -EIO;
}
if (retval == -EIO) {
hwcb->control_mask[0] = 0;
hwcb->control_mask[1] = 0;
hwcb->control_mask[2] = 0;
hwcb->response_code = 0;
}
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
if (hwc_data.flags & HWC_FLUSH)
flush_hwcbs ();
return retval;
}
static void
do_put_line (
unsigned char *message,
unsigned short count)
{
if (add_mto (message, count) != count) {
if (allocate_write_hwcb () < 0)
reuse_write_hwcb ();
#ifdef DUMP_HWC_WRITE_LIST_ERROR
if (add_mto (message, count) != count)
__asm__ ("LHI 1,0xe32\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LRA 5,0(%3)\n\t"
"J .+0 \n\t"
:
: "a" (message), "a" (&hwc_data.kmem_pages),
"a" (BUF_HWCB), "a" (OUT_HWCB)
: "1", "2", "3", "4", "5");
#else
add_mto (message, count);
#endif
}
}
static void
put_line (
unsigned char *message,
unsigned short count)
{
if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_WTIMER_RUNS)) {
del_timer (&hwc_data.write_timer);
hwc_data.flags &= ~HWC_WTIMER_RUNS;
}
hwc_data.obuf_start += count;
do_put_line (message, count);
hwc_data.obuf_start -= count;
}
static void
set_alarm (void)
{
write_hwcb_t *hwcb;
if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb))
allocate_write_hwcb ();
hwcb = (write_hwcb_t *) BUF_HWCB;
hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm;
}
static void
hwc_write_timeout (unsigned long data)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
hwc_data.obuf_start = hwc_data.obuf_count;
if (hwc_data.obuf_count)
put_line (hwc_data.obuf, hwc_data.obuf_count);
hwc_data.obuf_start = 0;
hwc_data.obuf_cursor = 0;
hwc_data.obuf_count = 0;
write_event_data_1 ();
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
static int
do_hwc_write (
int from_user,
unsigned char *msg,
unsigned int count,
unsigned char write_time)
{
unsigned int i_msg = 0;
unsigned short int spaces = 0;
unsigned int processed_characters = 0;
unsigned char ch;
unsigned short int obuf_count;
unsigned short int obuf_cursor;
unsigned short int obuf_columns;
if (hwc_data.obuf_start) {
obuf_cursor = 0;
obuf_count = 0;
obuf_columns = MIN (hwc_data.ioctls.columns,
MAX_MESSAGE_SIZE - hwc_data.obuf_start);
} else {
obuf_cursor = hwc_data.obuf_cursor;
obuf_count = hwc_data.obuf_count;
obuf_columns = hwc_data.ioctls.columns;
}
for (i_msg = 0; i_msg < count; i_msg++) {
if (from_user)
get_user (ch, msg + i_msg);
else
ch = msg[i_msg];
processed_characters++;
if ((obuf_cursor == obuf_columns) &&
(ch != '\n') &&
(ch != '\t')) {
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_columns);
obuf_cursor = 0;
obuf_count = 0;
}
switch (ch) {
case '\n':
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
break;
case '\a':
hwc_data.obuf_start += obuf_count;
set_alarm ();
hwc_data.obuf_start -= obuf_count;
break;
case '\t':
do {
if (obuf_cursor < obuf_columns) {
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor]
= HWC_ASCEBC (' ');
obuf_cursor++;
} else
break;
} while (obuf_cursor % hwc_data.ioctls.width_htab);
break;
case '\f':
case '\v':
spaces = obuf_cursor;
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_count = obuf_cursor;
while (spaces) {
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor - spaces]
= HWC_ASCEBC (' ');
spaces--;
}
break;
case '\b':
if (obuf_cursor)
obuf_cursor--;
break;
case '\r':
obuf_cursor = 0;
break;
case 0x00:
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
goto out;
default:
if (isprint (ch))
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor++]
= HWC_ASCEBC (ch);
}
if (obuf_cursor > obuf_count)
obuf_count = obuf_cursor;
}
if (obuf_cursor) {
if (hwc_data.obuf_start ||
(hwc_data.ioctls.final_nl == 0)) {
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
} else {
if (hwc_data.ioctls.final_nl > 0) {
if (hwc_data.flags & HWC_WTIMER_RUNS) {
mod_timer (&hwc_data.write_timer,
jiffies + hwc_data.ioctls.final_nl * HZ / 10);
} else {
init_timer (&hwc_data.write_timer);
hwc_data.write_timer.function =
hwc_write_timeout;
hwc_data.write_timer.data =
(unsigned long) NULL;
hwc_data.write_timer.expires =
jiffies +
hwc_data.ioctls.final_nl * HZ / 10;
add_timer (&hwc_data.write_timer);
hwc_data.flags |= HWC_WTIMER_RUNS;
}
} else;
}
} else;
out:
if (!hwc_data.obuf_start) {
hwc_data.obuf_cursor = obuf_cursor;
hwc_data.obuf_count = obuf_count;
}
if (write_time == IMMEDIATE_WRITE)
write_event_data_1 ();
return processed_characters;
}
signed int
hwc_write (int from_user, const unsigned char *msg, unsigned int count)
{
unsigned long flags;
int retval;
spin_lock_irqsave (&hwc_data.lock, flags);
retval = do_hwc_write (from_user, (unsigned char *) msg,
count, IMMEDIATE_WRITE);
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
unsigned int
hwc_chars_in_buffer (unsigned char flag)
{
unsigned short int number = 0;
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB)
number += ALL_HWCB_CHAR;
if (flag & IN_WRITE_BUF)
number += hwc_data.obuf_cursor;
spin_unlock_irqrestore (&hwc_data.lock, flags);
return number;
}
static inline int
nr_setbits (kmem_pages_t arg)
{
int i;
int nr = 0;
for (i = 0; i < (sizeof (arg) << 3); i++) {
if (arg & 1)
nr++;
arg >>= 1;
}
return nr;
}
unsigned int
hwc_write_room (unsigned char flag)
{
unsigned int number = 0;
unsigned long flags;
write_hwcb_t *hwcb;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB) {
if (BUF_HWCB) {
hwcb = (write_hwcb_t *) BUF_HWCB;
number += MAX_HWCB_ROOM - hwcb->length;
}
number += (hwc_data.ioctls.kmem_hwcb -
nr_setbits (hwc_data.kmem_pages)) *
(MAX_HWCB_ROOM -
(sizeof (write_hwcb_t) + sizeof (mto_t)));
}
if (flag & IN_WRITE_BUF)
number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
spin_unlock_irqrestore (&hwc_data.lock, flags);
return number;
}
void
hwc_flush_buffer (unsigned char flag)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB) {
if (hwc_data.current_servc != HWC_CMDW_WRITEDATA)
flush_hwcbs ();
else
hwc_data.flags |= HWC_FLUSH;
}
if (flag & IN_WRITE_BUF) {
hwc_data.obuf_cursor = 0;
hwc_data.obuf_count = 0;
}
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
unsigned short int
seperate_cases (unsigned char *buf, unsigned short int count)
{
unsigned short int i_in;
unsigned short int i_out = 0;
unsigned char _case = 0;
for (i_in = 0; i_in < count; i_in++) {
if (buf[i_in] == hwc_data.ioctls.delim) {
if ((i_in + 1 < count) &&
(buf[i_in + 1] == hwc_data.ioctls.delim)) {
buf[i_out] = hwc_data.ioctls.delim;
i_out++;
i_in++;
} else
_case = ~_case;
} else {
if (_case) {
if (hwc_data.ioctls.tolower)
buf[i_out] = _ebc_toupper[buf[i_in]];
else
buf[i_out] = _ebc_tolower[buf[i_in]];
} else
buf[i_out] = buf[i_in];
i_out++;
}
}
return i_out;
}
#ifdef DUMP_HWCB_INPUT
static int
gds_vector_name (u16 id, unsigned char name[])
{
int retval = 0;
switch (id) {
case GDS_ID_MDSMU:
name = "Multiple Domain Support Message Unit";
break;
case GDS_ID_MDSRouteInfo:
name = "MDS Routing Information";
break;
case GDS_ID_AgUnWrkCorr:
name = "Agent Unit of Work Correlator";
break;
case GDS_ID_SNACondReport:
name = "SNA Condition Report";
break;
case GDS_ID_CPMSU:
name = "CP Management Services Unit";
break;
case GDS_ID_RoutTargInstr:
name = "Routing and Targeting Instructions";
break;
case GDS_ID_OpReq:
name = "Operate Request";
break;
case GDS_ID_TextCmd:
name = "Text Command";
break;
default:
name = "unknown GDS variable";
retval = -EINVAL;
}
return retval;
}
#endif
inline static gds_vector_t *
find_gds_vector (
gds_vector_t * start, void *end, u16 id)
{
gds_vector_t *vec;
gds_vector_t *retval = NULL;
vec = start;
while (((void *) vec) < end) {
if (vec->gds_id == id) {
#ifdef DUMP_HWCB_INPUT
int retval_name;
unsigned char name[64];
retval_name = gds_vector_name (id, name);
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"%s at 0x%x up to 0x%x, length: %d",
name,
(unsigned long) vec,
((unsigned long) vec) + vec->length - 1,
vec->length);
if (retval_name < 0)
internal_print (
IMMEDIATE_WRITE,
", id: 0x%x\n",
vec->gds_id);
else
internal_print (
IMMEDIATE_WRITE,
"\n");
#endif
retval = vec;
break;
}
vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
}
return retval;
}
inline static gds_subvector_t *
find_gds_subvector (
gds_subvector_t * start, void *end, u8 key)
{
gds_subvector_t *subvec;
gds_subvector_t *retval = NULL;
subvec = start;
while (((void *) subvec) < end) {
if (subvec->key == key) {
retval = subvec;
break;
}
subvec = (gds_subvector_t *)
(((unsigned long) subvec) + subvec->length);
}
return retval;
}
inline static int
get_input (void *start, void *end)
{
int count;
count = ((unsigned long) end) - ((unsigned long) start);
if (hwc_data.ioctls.tolower)
EBC_TOLOWER (start, count);
if (hwc_data.ioctls.delim)
count = seperate_cases (start, count);
HWC_EBCASC_STR (start, count);
if (hwc_data.ioctls.echo)
do_hwc_write (0, start, count, IMMEDIATE_WRITE);
if (hwc_data.calls != NULL)
if (hwc_data.calls->move_input != NULL)
(hwc_data.calls->move_input) (start, count);
return count;
}
inline static int
eval_selfdeftextmsg (gds_subvector_t * start, void *end)
{
gds_subvector_t *subvec;
void *subvec_data;
void *subvec_end;
int retval = 0;
subvec = start;
while (((void *) subvec) < end) {
subvec = find_gds_subvector (subvec, end, 0x30);
if (!subvec)
break;
subvec_data = (void *)
(((unsigned long) subvec) +
sizeof (gds_subvector_t));
subvec_end = (void *)
(((unsigned long) subvec) + subvec->length);
retval += get_input (subvec_data, subvec_end);
subvec = (gds_subvector_t *) subvec_end;
}
return retval;
}
inline static int
eval_textcmd (gds_subvector_t * start, void *end)
{
gds_subvector_t *subvec;
gds_subvector_t *subvec_data;
void *subvec_end;
int retval = 0;
subvec = start;
while (((void *) subvec) < end) {
subvec = find_gds_subvector (
subvec, end, GDS_KEY_SelfDefTextMsg);
if (!subvec)
break;
subvec_data = (gds_subvector_t *)
(((unsigned long) subvec) +
sizeof (gds_subvector_t));
subvec_end = (void *)
(((unsigned long) subvec) + subvec->length);
retval += eval_selfdeftextmsg (subvec_data, subvec_end);
subvec = (gds_subvector_t *) subvec_end;
}
return retval;
}
inline static int
eval_cpmsu (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_subvector_t *vec_data;
void *vec_end;
int retval = 0;
vec = start;
while (((void *) vec) < end) {
vec = find_gds_vector (vec, end, GDS_ID_TextCmd);
if (!vec)
break;
vec_data = (gds_subvector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval += eval_textcmd (vec_data, vec_end);
vec = (gds_vector_t *) vec_end;
}
return retval;
}
inline static int
eval_mdsmu (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_vector_t *vec_data;
void *vec_end;
int retval = 0;
vec = find_gds_vector (start, end, GDS_ID_CPMSU);
if (vec) {
vec_data = (gds_vector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval = eval_cpmsu (vec_data, vec_end);
}
return retval;
}
static int
eval_evbuf (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_vector_t *vec_data;
void *vec_end;
int retval = 0;
vec = find_gds_vector (start, end, GDS_ID_MDSMU);
if (vec) {
vec_data = (gds_vector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval = eval_mdsmu (vec_data, vec_end);
}
return retval;
}
static inline int
eval_hwc_receive_mask (_hwcb_mask_t mask)
{
hwc_data.write_nonprio
= ((mask & ET_Msg_Mask) == ET_Msg_Mask);
hwc_data.write_prio
= ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
if (hwc_data.write_prio || hwc_data.write_nonprio) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can write messages\n");
return 0;
} else {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not write messages\n");
return -1;
}
}
static inline int
eval_hwc_send_mask (_hwcb_mask_t mask)
{
hwc_data.read_statechange
= ((mask & ET_StateChange_Mask) == ET_StateChange_Mask);
if (hwc_data.read_statechange)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read state change notifications\n");
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not read state change notifications\n");
hwc_data.sig_quiesce
= ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask);
if (hwc_data.sig_quiesce)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can receive signal quiesce\n");
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not receive signal quiesce\n");
hwc_data.read_nonprio
= ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask);
if (hwc_data.read_nonprio)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read commands\n");
hwc_data.read_prio
= ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
if (hwc_data.read_prio)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read priority commands\n");
if (hwc_data.read_prio || hwc_data.read_nonprio) {
return 0;
} else {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not read commands from operator\n");
return -1;
}
}
static int
eval_statechangebuf (statechangebuf_t * scbuf)
{
int retval = 0;
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"HWC state change detected\n");
if (scbuf->validity_hwc_active_facility_mask) {
}
if (scbuf->validity_hwc_receive_mask) {
if (scbuf->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe50\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (scbuf)
: "1", "2");
#endif
} else {
retval += eval_hwc_receive_mask
(scbuf->hwc_receive_mask);
}
}
if (scbuf->validity_hwc_send_mask) {
if (scbuf->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe51\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (scbuf)
: "1", "2");
#endif
} else {
retval += eval_hwc_send_mask
(scbuf->hwc_send_mask);
}
}
if (scbuf->validity_read_data_function_mask) {
}
return retval;
}
#ifdef CONFIG_SMP
static volatile unsigned long cpu_quiesce_map;
static void
do_load_quiesce_psw (void)
{
psw_t quiesce_psw;
clear_bit (smp_processor_id (), &cpu_quiesce_map);
if (smp_processor_id () == 0) {
while (cpu_quiesce_map != 0) ;
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw (quiesce_psw);
}
signal_processor (smp_processor_id (), sigp_stop);
}
static void
do_machine_quiesce (void)
{
cpu_quiesce_map = cpu_online_map;
smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
do_load_quiesce_psw ();
}
#else
static void
do_machine_quiesce (void)
{
psw_t quiesce_psw;
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
queisce_psw.addr = 0xfff;
__load_psw (quiesce_psw);
}
#endif
static int
process_evbufs (void *start, void *end)
{
int retval = 0;
evbuf_t *evbuf;
void *evbuf_end;
gds_vector_t *evbuf_data;
evbuf = (evbuf_t *) start;
while (((void *) evbuf) < end) {
evbuf_data = (gds_vector_t *)
(((unsigned long) evbuf) + sizeof (evbuf_t));
evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length);
switch (evbuf->type) {
case ET_OpCmd:
case ET_CntlProgOpCmd:
case ET_PMsgCmd:
#ifdef DUMP_HWCB_INPUT
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"event buffer "
"at 0x%x up to 0x%x, length: %d\n",
(unsigned long) evbuf,
(unsigned long) (evbuf_end - 1),
evbuf->length);
dump_storage_area ((void *) evbuf, evbuf->length);
#endif
retval += eval_evbuf (evbuf_data, evbuf_end);
break;
case ET_StateChange:
retval += eval_statechangebuf
((statechangebuf_t *) evbuf);
break;
case ET_SigQuiesce:
_machine_restart = do_machine_quiesce;
_machine_halt = do_machine_quiesce;
_machine_power_off = do_machine_quiesce;
ctrl_alt_del ();
break;
default:
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"unknown event buffer found, "
"type 0x%x",
evbuf->type);
retval = -ENOSYS;
}
evbuf = (evbuf_t *) evbuf_end;
}
return retval;
}
static int
unconditional_read_1 (void)
{
unsigned short int condition_code;
read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
int retval;
#if 0
if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio))
return -EOPNOTSUPP;
if (hwc_data.current_servc)
return -EBUSY;
#endif
memset (hwcb, 0x00, PAGE_SIZE);
memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
#ifdef DUMP_HWC_READ_ERROR
if (condition_code == HWC_NOT_OPERATIONAL)
__asm__ ("LHI 1,0xe40\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (&condition_code), "a" (hwc_data.page)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_READDATA;
hwc_data.current_hwcb = hwc_data.page;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
default:
retval = -EIO;
}
return retval;
}
static int
unconditional_read_2 (u32 ext_int_param)
{
read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
#ifdef DUMP_HWC_READ_ERROR
if ((hwcb->response_code != 0x0020) &&
(hwcb->response_code != 0x0220) &&
(hwcb->response_code != 0x60F0) &&
(hwcb->response_code != 0x62F0))
__asm__ ("LHI 1,0xe41\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (hwc_data.page), "a" (&(hwcb->response_code))
: "1", "2", "3");
#endif
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
switch (hwcb->response_code) {
case 0x0020:
case 0x0220:
return process_evbufs (
(void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
(void *) (((unsigned long) hwcb) + hwcb->length));
case 0x60F0:
case 0x62F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"got interrupt and tried to read input, "
"but nothing found (response code=0x%x).\n",
hwcb->response_code);
return 0;
case 0x0100:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: HWCB boundary violation - this "
"must not occur in a correct driver, please contact "
"author\n");
return -EIO;
case 0x0300:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"insufficient HWCB length - this must not occur in a "
"correct driver, please contact author\n");
return -EIO;
case 0x01F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"invalid command - this must not occur in a correct "
"driver, please contact author\n");
return -EIO;
case 0x40F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid function code\n");
return -EIO;
case 0x70F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid selection mask\n");
return -EIO;
case 0x0040:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: HWC equipment check\n");
return -EIO;
default:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid response code %x - this "
"must not occur in a correct driver, please contact "
"author\n",
hwcb->response_code);
return -EIO;
}
}
static int
write_event_mask_1 (void)
{
unsigned int condition_code;
int retval;
condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
#ifdef DUMP_HWC_INIT_ERROR
if (condition_code == HWC_NOT_OPERATIONAL)
__asm__ ("LHI 1,0xe10\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (&condition_code), "a" (hwc_data.page)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_WRITEMASK;
hwc_data.current_hwcb = hwc_data.page;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
default:
retval = -EIO;
}
return retval;
}
static int
write_event_mask_2 (u32 ext_int_param)
{
init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
int retval = 0;
if (hwcb->response_code != 0x0020) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe11\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (hwcb), "a" (&(hwcb->response_code))
: "1", "2", "3");
#else
retval = -1;
#endif
} else {
if (hwcb->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe52\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (hwcb)
: "1", "2");
#endif
} else {
retval += eval_hwc_receive_mask
(hwcb->hwc_receive_mask);
retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
}
}
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
return retval;
}
static int
set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
{
int retval = 0;
hwc_ioctls_t tmp;
if (ioctls->width_htab > MAX_MESSAGE_SIZE) {
if (correct)
tmp.width_htab = MAX_MESSAGE_SIZE;
else
retval = -EINVAL;
} else
tmp.width_htab = ioctls->width_htab;
tmp.echo = ioctls->echo;
if (ioctls->columns > MAX_MESSAGE_SIZE) {
if (correct)
tmp.columns = MAX_MESSAGE_SIZE;
else
retval = -EINVAL;
} else
tmp.columns = ioctls->columns;
tmp.final_nl = ioctls->final_nl;
if (ioctls->max_hwcb < 2) {
if (correct)
tmp.max_hwcb = 2;
else
retval = -EINVAL;
} else
tmp.max_hwcb = ioctls->max_hwcb;
tmp.tolower = ioctls->tolower;
if (ioctls->kmem_hwcb > ioctls->max_hwcb) {
if (correct)
tmp.kmem_hwcb = ioctls->max_hwcb;
else
retval = -EINVAL;
} else
tmp.kmem_hwcb = ioctls->kmem_hwcb;
if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
if (correct)
ioctls->kmem_hwcb = MAX_KMEM_PAGES;
else
retval = -EINVAL;
}
if (ioctls->kmem_hwcb < 2) {
if (correct)
ioctls->kmem_hwcb = 2;
else
retval = -EINVAL;
}
tmp.delim = ioctls->delim;
if (!(retval < 0))
hwc_data.ioctls = tmp;
return retval;
}
int
do_hwc_init (void)
{
int retval;
memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
do {
retval = write_event_mask_1 ();
if (retval == -EBUSY) {
hwc_data.flags |= HWC_INIT;
__ctl_store (cr0, 0, 0);
cr0_save = cr0;
cr0 |= 0x00000200;
cr0 &= 0xFFFFF3AC;
__ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01"
:"=m" (psw_mask)::"memory");
while (!(hwc_data.flags & HWC_INTERRUPT))
barrier ();
asm volatile ("STNSM %0,0xFE"
:"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0);
hwc_data.flags &= ~HWC_INIT;
}
} while (retval == -EBUSY);
if (retval == -EIO) {
hwc_data.flags |= HWC_BROKEN;
printk (HWC_RW_PRINT_HEADER "HWC not operational\n");
}
return retval;
}
void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
int
hwc_init (void)
{
int retval;
#ifdef BUFFER_STRESS_TEST
init_hwcb_t *hwcb;
int i;
#endif
if (register_early_external_interrupt (0x2401, hwc_interrupt_handler,
&ext_int_info_hwc) != 0)
panic ("Couldn't request external interrupts 0x2401");
spin_lock_init (&hwc_data.lock);
#ifdef USE_VM_DETECTION
if (MACHINE_IS_VM) {
if (hwc_data.init_ioctls.columns > 76)
hwc_data.init_ioctls.columns = 76;
hwc_data.init_ioctls.tolower = 1;
if (!hwc_data.init_ioctls.delim)
hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER;
} else {
hwc_data.init_ioctls.tolower = 0;
hwc_data.init_ioctls.delim = 0;
}
#endif
retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
hwc_data.kmem_start = (unsigned long)
alloc_bootmem_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
hwc_data.kmem_end = hwc_data.kmem_start +
hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
retval = do_hwc_init ();
ctl_set_bit (0, 9);
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"use %i bytes for buffering.\n",
hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
for (i = 0; i < 500; i++) {
hwcb = (init_hwcb_t *) BUF_HWCB;
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"This is stress test message #%i, free: %i bytes\n",
i,
MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t)));
}
#endif
return /*retval */ 0;
}
signed int
hwc_register_calls (hwc_high_level_calls_t * calls)
{
if (calls == NULL)
return -EINVAL;
if (hwc_data.calls != NULL)
return -EBUSY;
hwc_data.calls = calls;
return 0;
}
signed int
hwc_unregister_calls (hwc_high_level_calls_t * calls)
{
if (hwc_data.calls == NULL)
return -EINVAL;
if (calls != hwc_data.calls)
return -EINVAL;
hwc_data.calls = NULL;
return 0;
}
int
hwc_send (hwc_request_t * req)
{
unsigned long flags;
int retval;
int cc;
spin_lock_irqsave (&hwc_data.lock, flags);
if (!req || !req->callback || !req->block) {
retval = -EINVAL;
goto unlock;
}
if (hwc_data.request) {
retval = -ENOTSUPP;
goto unlock;
}
cc = service_call (req->word, req->block);
switch (cc) {
case 0:
hwc_data.request = req;
hwc_data.current_servc = req->word;
hwc_data.current_hwcb = req->block;
retval = 0;
break;
case 2:
retval = -EBUSY;
break;
default:
retval = -ENOSYS;
}
unlock:
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
EXPORT_SYMBOL (hwc_send);
void
do_hwc_callback (u32 ext_int_param)
{
if (!hwc_data.request || !hwc_data.request->callback)
return;
if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
!= (unsigned long) hwc_data.request->block)
return;
hwc_data.request->callback (hwc_data.request);
hwc_data.request = NULL;
hwc_data.current_hwcb = NULL;
hwc_data.current_servc = 0;
}
void
hwc_do_interrupt (u32 ext_int_param)
{
u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR;
u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND;
if (hwc_data.flags & HWC_PTIMER_RUNS) {
del_timer (&hwc_data.poll_timer);
hwc_data.flags &= ~HWC_PTIMER_RUNS;
}
if (finished_hwcb) {
if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"interrupt: mismatch: "
"ext. int param. (0x%x) vs. "
"current HWCB (0x%x)\n",
ext_int_param,
hwc_data.current_hwcb);
} else {
if (hwc_data.request) {
do_hwc_callback (ext_int_param);
} else {
switch (hwc_data.current_servc) {
case HWC_CMDW_WRITEMASK:
write_event_mask_2 (ext_int_param);
break;
case HWC_CMDW_WRITEDATA:
write_event_data_2 (ext_int_param);
break;
case HWC_CMDW_READDATA:
unconditional_read_2 (ext_int_param);
break;
default:
break;
}
}
}
} else {
if (hwc_data.current_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"interrupt: mismatch: "
"ext. int. param. (0x%x) vs. "
"current HWCB (0x%x)\n",
ext_int_param,
hwc_data.current_hwcb);
}
}
if (evbuf_pending) {
unconditional_read_1 ();
} else {
write_event_data_1 ();
}
if (!hwc_data.calls || !hwc_data.calls->wake_up)
return;
(hwc_data.calls->wake_up) ();
}
void
hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
{
u32 ext_int_param = hwc_ext_int_param ();
irq_enter ();
if (hwc_data.flags & HWC_INIT) {
hwc_data.flags |= HWC_INTERRUPT;
} else if (hwc_data.flags & HWC_BROKEN) {
if (!do_hwc_init ()) {
hwc_data.flags &= ~HWC_BROKEN;
internal_print (DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"delayed HWC setup after"
" temporary breakdown"
" (ext. int. parameter=0x%x)\n",
ext_int_param);
}
} else {
spin_lock (&hwc_data.lock);
hwc_do_interrupt (ext_int_param);
spin_unlock (&hwc_data.lock);
}
irq_exit ();
}
void
hwc_unblank (void)
{
spin_lock (&hwc_data.lock);
spin_unlock (&hwc_data.lock);
__ctl_store (cr0, 0, 0);
cr0_save = cr0;
cr0 |= 0x00000200;
cr0 &= 0xFFFFF3AC;
__ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory");
while (ALL_HWCB_CHAR)
barrier ();
asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0);
}
int
hwc_ioctl (unsigned int cmd, unsigned long arg)
{
hwc_ioctls_t tmp = hwc_data.ioctls;
int retval = 0;
unsigned long flags;
unsigned int obuf;
spin_lock_irqsave (&hwc_data.lock, flags);
switch (cmd) {
case TIOCHWCSHTAB:
if (get_user (tmp.width_htab, (ioctl_htab_t *) arg))
goto fault;
break;
case TIOCHWCSECHO:
if (get_user (tmp.echo, (ioctl_echo_t *) arg))
goto fault;
break;
case TIOCHWCSCOLS:
if (get_user (tmp.columns, (ioctl_cols_t *) arg))
goto fault;
break;
case TIOCHWCSNL:
if (get_user (tmp.final_nl, (ioctl_nl_t *) arg))
goto fault;
break;
case TIOCHWCSOBUF:
if (get_user (obuf, (unsigned int *) arg))
goto fault;
if (obuf & 0xFFF)
tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12);
else
tmp.max_hwcb = (obuf >> 12);
break;
case TIOCHWCSCASE:
if (get_user (tmp.tolower, (ioctl_case_t *) arg))
goto fault;
break;
case TIOCHWCSDELIM:
if (get_user (tmp.delim, (ioctl_delim_t *) arg))
goto fault;
break;
case TIOCHWCSINIT:
retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
break;
case TIOCHWCGHTAB:
if (put_user (tmp.width_htab, (ioctl_htab_t *) arg))
goto fault;
break;
case TIOCHWCGECHO:
if (put_user (tmp.echo, (ioctl_echo_t *) arg))
goto fault;
break;
case TIOCHWCGCOLS:
if (put_user (tmp.columns, (ioctl_cols_t *) arg))
goto fault;
break;
case TIOCHWCGNL:
if (put_user (tmp.final_nl, (ioctl_nl_t *) arg))
goto fault;
break;
case TIOCHWCGOBUF:
if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg))
goto fault;
break;
case TIOCHWCGKBUF:
if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg))
goto fault;
break;
case TIOCHWCGCASE:
if (put_user (tmp.tolower, (ioctl_case_t *) arg))
goto fault;
break;
case TIOCHWCGDELIM:
if (put_user (tmp.delim, (ioctl_delim_t *) arg))
goto fault;
break;
#if 0
case TIOCHWCGINIT:
if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg))
goto fault;
break;
case TIOCHWCGCURR:
if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg))
goto fault;
break;
#endif
default:
goto noioctlcmd;
}
if (_IOC_DIR (cmd) == _IOC_WRITE)
retval = set_hwc_ioctls (&tmp, 0);
goto out;
fault:
retval = -EFAULT;
goto out;
noioctlcmd:
retval = -ENOIOCTLCMD;
out:
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
/*
* drivers/s390/char/hwc_rw.h
* interface to the HWC-read/write driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*/
#ifndef __HWC_RW_H__
#define __HWC_RW_H__
#include <linux/ioctl.h>
typedef struct {
void (*move_input) (unsigned char *, unsigned int);
void (*wake_up) (void);
} hwc_high_level_calls_t;
struct _hwc_request;
typedef void hwc_callback_t (struct _hwc_request *);
typedef struct _hwc_request {
void *block;
u32 word;
hwc_callback_t *callback;
void *data;
} __attribute__ ((packed))
hwc_request_t;
#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x]))
#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c)))
#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c)))
#define IN_HWCB 1
#define IN_WRITE_BUF 2
#define IN_BUFS_TOTAL (IN_HWCB | IN_WRITE_BUF)
typedef unsigned short int ioctl_htab_t;
typedef unsigned char ioctl_echo_t;
typedef unsigned short int ioctl_cols_t;
typedef signed char ioctl_nl_t;
typedef unsigned short int ioctl_obuf_t;
typedef unsigned char ioctl_case_t;
typedef unsigned char ioctl_delim_t;
typedef struct {
ioctl_htab_t width_htab;
ioctl_echo_t echo;
ioctl_cols_t columns;
ioctl_nl_t final_nl;
ioctl_obuf_t max_hwcb;
ioctl_obuf_t kmem_hwcb;
ioctl_case_t tolower;
ioctl_delim_t delim;
} hwc_ioctls_t;
extern hwc_ioctls_t _hwc_ioctls;
#define HWC_IOCTL_LETTER 'B'
#define TIOCHWCSHTAB _IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
#define TIOCHWCSECHO _IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
#define TIOCHWCSCOLS _IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
#define TIOCHWCSNL _IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
#define TIOCHWCSOBUF _IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
#define TIOCHWCSINIT _IO(HWC_IOCTL_LETTER, 6)
#define TIOCHWCSCASE _IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
#define TIOCHWCSDELIM _IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
#define TIOCHWCGHTAB _IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
#define TIOCHWCGECHO _IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
#define TIOCHWCGCOLS _IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
#define TIOCHWCGNL _IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
#define TIOCHWCGOBUF _IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
#define TIOCHWCGINIT _IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
#define TIOCHWCGCASE _IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
#define TIOCHWCGDELIM _IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
#define TIOCHWCGKBUF _IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
#define TIOCHWCGCURR _IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
#ifndef __HWC_RW_C__
extern int hwc_init (void);
extern int hwc_write (int from_user, const unsigned char *, unsigned int);
extern unsigned int hwc_chars_in_buffer (unsigned char);
extern unsigned int hwc_write_room (unsigned char);
extern void hwc_flush_buffer (unsigned char);
extern void hwc_unblank (void);
extern signed int hwc_ioctl (unsigned int, unsigned long);
extern void do_hwc_interrupt (void);
extern int hwc_printk (const char *,...);
extern signed int hwc_register_calls (hwc_high_level_calls_t *);
extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
extern int hwc_send (hwc_request_t *);
#endif
#endif
/*
* drivers/s390/char/hwc_tty.c
* HWC line mode terminal driver.
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
* Thanks to Martin Schwidefsky.
*/
#include <linux/config.h>
#include <linux/major.h>
#include <linux/termios.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "hwc_rw.h"
#include "ctrlchar.h"
#define HWC_TTY_PRINT_HEADER "hwc tty driver: "
#define HWC_TTY_BUF_SIZE 512
typedef struct {
struct tty_struct *tty;
unsigned char buf[HWC_TTY_BUF_SIZE];
unsigned short int buf_count;
spinlock_t lock;
hwc_high_level_calls_t calls;
} hwc_tty_data_struct;
static hwc_tty_data_struct hwc_tty_data =
{ /* NULL/0 */ };
static struct tty_driver hwc_tty_driver;
static struct tty_struct *hwc_tty_table[1];
static struct termios *hwc_tty_termios[1];
static struct termios *hwc_tty_termios_locked[1];
static int hwc_tty_refcount = 0;
extern struct termios tty_std_termios;
void hwc_tty_wake_up (void);
void hwc_tty_input (unsigned char *, unsigned int);
static int
hwc_tty_open (struct tty_struct *tty,
struct file *filp)
{
if (minor (tty->device) - tty->driver.minor_start)
return -ENODEV;
tty->driver_data = &hwc_tty_data;
hwc_tty_data.buf_count = 0;
hwc_tty_data.tty = tty;
tty->low_latency = 0;
hwc_tty_data.calls.wake_up = hwc_tty_wake_up;
hwc_tty_data.calls.move_input = hwc_tty_input;
hwc_register_calls (&(hwc_tty_data.calls));
return 0;
}
static void
hwc_tty_close (struct tty_struct *tty,
struct file *filp)
{
if (minor (tty->device) != tty->driver.minor_start) {
printk (KERN_WARNING HWC_TTY_PRINT_HEADER
"do not close hwc tty because of wrong device number");
return;
}
if (tty->count > 1)
return;
hwc_tty_data.tty = NULL;
hwc_unregister_calls (&(hwc_tty_data.calls));
}
static int
hwc_tty_write_room (struct tty_struct *tty)
{
int retval;
retval = hwc_write_room (IN_BUFS_TOTAL);
return retval;
}
static int
hwc_tty_write (struct tty_struct *tty,
int from_user,
const unsigned char *buf,
int count)
{
int retval;
if (hwc_tty_data.buf_count > 0) {
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
}
retval = hwc_write (from_user, buf, count);
return retval;
}
static void
hwc_tty_put_char (struct tty_struct *tty,
unsigned char ch)
{
unsigned long flags;
spin_lock_irqsave (&hwc_tty_data.lock, flags);
if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) {
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
}
hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
hwc_tty_data.buf_count++;
spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
}
static void
hwc_tty_flush_chars (struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave (&hwc_tty_data.lock, flags);
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
}
static int
hwc_tty_chars_in_buffer (struct tty_struct *tty)
{
int retval;
retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
return retval;
}
static void
hwc_tty_flush_buffer (struct tty_struct *tty)
{
hwc_tty_wake_up ();
}
static int
hwc_tty_ioctl (
struct tty_struct *tty,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
return hwc_ioctl (cmd, arg);
}
void
hwc_tty_wake_up (void)
{
if (hwc_tty_data.tty == NULL)
return;
if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
hwc_tty_data.tty->ldisc.write_wakeup)
(hwc_tty_data.tty->ldisc.write_wakeup) (hwc_tty_data.tty);
wake_up_interruptible (&hwc_tty_data.tty->write_wait);
}
void
hwc_tty_input (unsigned char *buf, unsigned int count)
{
struct tty_struct *tty = hwc_tty_data.tty;
if (tty != NULL) {
unsigned int cchar = ctrlchar_handle(buf, count, tty);
switch (cchar & CTRLCHAR_MASK) {
case CTRLCHAR_SYSRQ:
return;
case CTRLCHAR_CTRL:
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = cchar;
break;
case CTRLCHAR_NONE:
memcpy (tty->flip.char_buf_ptr, buf, count);
if (count < 2 || (
strncmp (buf + count - 2, "^n", 2) ||
strncmp (buf + count - 2, "\0252n", 2))) {
tty->flip.char_buf_ptr[count] = '\n';
count++;
} else
count -= 2;
memset (tty->flip.flag_buf_ptr, TTY_NORMAL, count);
tty->flip.char_buf_ptr += count;
tty->flip.flag_buf_ptr += count;
tty->flip.count += count;
break;
}
tty_flip_buffer_push (tty);
hwc_tty_wake_up ();
}
}
void
hwc_tty_init (void)
{
if (!CONSOLE_IS_HWC)
return;
memset (&hwc_tty_driver, 0, sizeof (struct tty_driver));
memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct));
hwc_tty_driver.magic = TTY_DRIVER_MAGIC;
hwc_tty_driver.driver_name = "tty_hwc";
hwc_tty_driver.name = "ttyS";
hwc_tty_driver.name_base = 0;
hwc_tty_driver.major = TTY_MAJOR;
hwc_tty_driver.minor_start = 64;
hwc_tty_driver.num = 1;
hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
hwc_tty_driver.subtype = SYSTEM_TYPE_TTY;
hwc_tty_driver.init_termios = tty_std_termios;
hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
hwc_tty_driver.init_termios.c_oflag = ONLCR;
hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO;
hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW;
hwc_tty_driver.refcount = &hwc_tty_refcount;
hwc_tty_driver.table = hwc_tty_table;
hwc_tty_driver.termios = hwc_tty_termios;
hwc_tty_driver.termios_locked = hwc_tty_termios_locked;
hwc_tty_driver.open = hwc_tty_open;
hwc_tty_driver.close = hwc_tty_close;
hwc_tty_driver.write = hwc_tty_write;
hwc_tty_driver.put_char = hwc_tty_put_char;
hwc_tty_driver.flush_chars = hwc_tty_flush_chars;
hwc_tty_driver.write_room = hwc_tty_write_room;
hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer;
hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer;
hwc_tty_driver.ioctl = hwc_tty_ioctl;
hwc_tty_driver.throttle = NULL;
hwc_tty_driver.unthrottle = NULL;
hwc_tty_driver.send_xchar = NULL;
hwc_tty_driver.set_termios = NULL;
hwc_tty_driver.set_ldisc = NULL;
hwc_tty_driver.stop = NULL;
hwc_tty_driver.start = NULL;
hwc_tty_driver.hangup = NULL;
hwc_tty_driver.break_ctl = NULL;
hwc_tty_driver.wait_until_sent = NULL;
hwc_tty_driver.read_proc = NULL;
hwc_tty_driver.write_proc = NULL;
if (tty_register_driver (&hwc_tty_driver))
panic ("Couldn't register hwc_tty driver\n");
}
...@@ -31,7 +31,7 @@ extern unsigned long machine_flags; ...@@ -31,7 +31,7 @@ extern unsigned long machine_flags;
#define MACHINE_HAS_CSP (machine_flags & 8) #define MACHINE_HAS_CSP (machine_flags & 8)
#define MACHINE_HAS_MVPG (machine_flags & 16) #define MACHINE_HAS_MVPG (machine_flags & 16)
#define MACHINE_HAS_HWC (!MACHINE_IS_P390) #define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
/* /*
* Console mode. Override with conmode= * Console mode. Override with conmode=
...@@ -40,10 +40,10 @@ extern unsigned int console_mode; ...@@ -40,10 +40,10 @@ extern unsigned int console_mode;
extern unsigned int console_device; extern unsigned int console_device;
#define CONSOLE_IS_UNDEFINED (console_mode == 0) #define CONSOLE_IS_UNDEFINED (console_mode == 0)
#define CONSOLE_IS_HWC (console_mode == 1) #define CONSOLE_IS_SCLP (console_mode == 1)
#define CONSOLE_IS_3215 (console_mode == 2) #define CONSOLE_IS_3215 (console_mode == 2)
#define CONSOLE_IS_3270 (console_mode == 3) #define CONSOLE_IS_3270 (console_mode == 3)
#define SET_CONSOLE_HWC do { console_mode = 1; } while (0) #define SET_CONSOLE_SCLP do { console_mode = 1; } while (0)
#define SET_CONSOLE_3215 do { console_mode = 2; } while (0) #define SET_CONSOLE_3215 do { console_mode = 2; } while (0)
#define SET_CONSOLE_3270 do { console_mode = 3; } while (0) #define SET_CONSOLE_3270 do { console_mode = 3; } while (0)
......
...@@ -30,7 +30,7 @@ extern unsigned long machine_flags; ...@@ -30,7 +30,7 @@ extern unsigned long machine_flags;
#define MACHINE_HAS_MVPG (machine_flags & 16) #define MACHINE_HAS_MVPG (machine_flags & 16)
#define MACHINE_HAS_DIAG44 (machine_flags & 32) #define MACHINE_HAS_DIAG44 (machine_flags & 32)
#define MACHINE_HAS_HWC (!MACHINE_IS_P390) #define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
/* /*
* Console mode. Override with conmode= * Console mode. Override with conmode=
...@@ -39,10 +39,10 @@ extern unsigned int console_mode; ...@@ -39,10 +39,10 @@ extern unsigned int console_mode;
extern unsigned int console_device; extern unsigned int console_device;
#define CONSOLE_IS_UNDEFINED (console_mode == 0) #define CONSOLE_IS_UNDEFINED (console_mode == 0)
#define CONSOLE_IS_HWC (console_mode == 1) #define CONSOLE_IS_SCLP (console_mode == 1)
#define CONSOLE_IS_3215 (console_mode == 2) #define CONSOLE_IS_3215 (console_mode == 2)
#define CONSOLE_IS_3270 (console_mode == 3) #define CONSOLE_IS_3270 (console_mode == 3)
#define SET_CONSOLE_HWC do { console_mode = 1; } while (0) #define SET_CONSOLE_SCLP do { console_mode = 1; } while (0)
#define SET_CONSOLE_3215 do { console_mode = 2; } while (0) #define SET_CONSOLE_3215 do { console_mode = 2; } while (0)
#define SET_CONSOLE_3270 do { console_mode = 3; } while (0) #define SET_CONSOLE_3270 do { console_mode = 3; } while (0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment