Commit 30c2df51173ea4e4755ad52be7f2914f01e32404
1 parent
fca894edd7
Exists in
master
and in
7 other branches
[S390] sclp: event buffer dissection
Move gds vector/subvector find functions to the sclp header file. Simplify event buffer dissection in sclp tty code. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Showing 2 changed files with 60 additions and 84 deletions Inline Diff
drivers/s390/char/sclp.h
1 | /* | 1 | /* |
2 | * Copyright IBM Corp. 1999, 2009 | 2 | * Copyright IBM Corp. 1999, 2009 |
3 | * | 3 | * |
4 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 4 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
5 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 5 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #ifndef __SCLP_H__ | 8 | #ifndef __SCLP_H__ |
9 | #define __SCLP_H__ | 9 | #define __SCLP_H__ |
10 | 10 | ||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
13 | #include <asm/sclp.h> | 13 | #include <asm/sclp.h> |
14 | #include <asm/ebcdic.h> | 14 | #include <asm/ebcdic.h> |
15 | 15 | ||
16 | /* maximum number of pages concerning our own memory management */ | 16 | /* maximum number of pages concerning our own memory management */ |
17 | #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) | 17 | #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) |
18 | #define MAX_CONSOLE_PAGES 6 | 18 | #define MAX_CONSOLE_PAGES 6 |
19 | 19 | ||
20 | #define EVTYP_OPCMD 0x01 | 20 | #define EVTYP_OPCMD 0x01 |
21 | #define EVTYP_MSG 0x02 | 21 | #define EVTYP_MSG 0x02 |
22 | #define EVTYP_STATECHANGE 0x08 | 22 | #define EVTYP_STATECHANGE 0x08 |
23 | #define EVTYP_PMSGCMD 0x09 | 23 | #define EVTYP_PMSGCMD 0x09 |
24 | #define EVTYP_CNTLPROGOPCMD 0x20 | 24 | #define EVTYP_CNTLPROGOPCMD 0x20 |
25 | #define EVTYP_CNTLPROGIDENT 0x0B | 25 | #define EVTYP_CNTLPROGIDENT 0x0B |
26 | #define EVTYP_SIGQUIESCE 0x1D | 26 | #define EVTYP_SIGQUIESCE 0x1D |
27 | #define EVTYP_VT220MSG 0x1A | 27 | #define EVTYP_VT220MSG 0x1A |
28 | #define EVTYP_CONFMGMDATA 0x04 | 28 | #define EVTYP_CONFMGMDATA 0x04 |
29 | #define EVTYP_SDIAS 0x1C | 29 | #define EVTYP_SDIAS 0x1C |
30 | #define EVTYP_ASYNC 0x0A | 30 | #define EVTYP_ASYNC 0x0A |
31 | 31 | ||
32 | #define EVTYP_OPCMD_MASK 0x80000000 | 32 | #define EVTYP_OPCMD_MASK 0x80000000 |
33 | #define EVTYP_MSG_MASK 0x40000000 | 33 | #define EVTYP_MSG_MASK 0x40000000 |
34 | #define EVTYP_STATECHANGE_MASK 0x01000000 | 34 | #define EVTYP_STATECHANGE_MASK 0x01000000 |
35 | #define EVTYP_PMSGCMD_MASK 0x00800000 | 35 | #define EVTYP_PMSGCMD_MASK 0x00800000 |
36 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 | 36 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 |
37 | #define EVTYP_CTLPROGIDENT_MASK 0x00200000 | 37 | #define EVTYP_CTLPROGIDENT_MASK 0x00200000 |
38 | #define EVTYP_SIGQUIESCE_MASK 0x00000008 | 38 | #define EVTYP_SIGQUIESCE_MASK 0x00000008 |
39 | #define EVTYP_VT220MSG_MASK 0x00000040 | 39 | #define EVTYP_VT220MSG_MASK 0x00000040 |
40 | #define EVTYP_CONFMGMDATA_MASK 0x10000000 | 40 | #define EVTYP_CONFMGMDATA_MASK 0x10000000 |
41 | #define EVTYP_SDIAS_MASK 0x00000010 | 41 | #define EVTYP_SDIAS_MASK 0x00000010 |
42 | #define EVTYP_ASYNC_MASK 0x00400000 | 42 | #define EVTYP_ASYNC_MASK 0x00400000 |
43 | 43 | ||
44 | #define GNRLMSGFLGS_DOM 0x8000 | 44 | #define GNRLMSGFLGS_DOM 0x8000 |
45 | #define GNRLMSGFLGS_SNDALRM 0x4000 | 45 | #define GNRLMSGFLGS_SNDALRM 0x4000 |
46 | #define GNRLMSGFLGS_HOLDMSG 0x2000 | 46 | #define GNRLMSGFLGS_HOLDMSG 0x2000 |
47 | 47 | ||
48 | #define LNTPFLGS_CNTLTEXT 0x8000 | 48 | #define LNTPFLGS_CNTLTEXT 0x8000 |
49 | #define LNTPFLGS_LABELTEXT 0x4000 | 49 | #define LNTPFLGS_LABELTEXT 0x4000 |
50 | #define LNTPFLGS_DATATEXT 0x2000 | 50 | #define LNTPFLGS_DATATEXT 0x2000 |
51 | #define LNTPFLGS_ENDTEXT 0x1000 | 51 | #define LNTPFLGS_ENDTEXT 0x1000 |
52 | #define LNTPFLGS_PROMPTTEXT 0x0800 | 52 | #define LNTPFLGS_PROMPTTEXT 0x0800 |
53 | 53 | ||
54 | typedef unsigned int sclp_cmdw_t; | 54 | typedef unsigned int sclp_cmdw_t; |
55 | 55 | ||
56 | #define SCLP_CMDW_READ_EVENT_DATA 0x00770005 | 56 | #define SCLP_CMDW_READ_EVENT_DATA 0x00770005 |
57 | #define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005 | 57 | #define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005 |
58 | #define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005 | 58 | #define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005 |
59 | 59 | ||
60 | #define GDS_ID_MDSMU 0x1310 | 60 | #define GDS_ID_MDSMU 0x1310 |
61 | #define GDS_ID_MDSROUTEINFO 0x1311 | 61 | #define GDS_ID_MDSROUTEINFO 0x1311 |
62 | #define GDS_ID_AGUNWRKCORR 0x1549 | 62 | #define GDS_ID_AGUNWRKCORR 0x1549 |
63 | #define GDS_ID_SNACONDREPORT 0x1532 | 63 | #define GDS_ID_SNACONDREPORT 0x1532 |
64 | #define GDS_ID_CPMSU 0x1212 | 64 | #define GDS_ID_CPMSU 0x1212 |
65 | #define GDS_ID_ROUTTARGINSTR 0x154D | 65 | #define GDS_ID_ROUTTARGINSTR 0x154D |
66 | #define GDS_ID_OPREQ 0x8070 | 66 | #define GDS_ID_OPREQ 0x8070 |
67 | #define GDS_ID_TEXTCMD 0x1320 | 67 | #define GDS_ID_TEXTCMD 0x1320 |
68 | 68 | ||
69 | #define GDS_KEY_SELFDEFTEXTMSG 0x31 | 69 | #define GDS_KEY_SELFDEFTEXTMSG 0x31 |
70 | 70 | ||
71 | enum sclp_pm_event { | 71 | enum sclp_pm_event { |
72 | SCLP_PM_EVENT_FREEZE, | 72 | SCLP_PM_EVENT_FREEZE, |
73 | SCLP_PM_EVENT_THAW, | 73 | SCLP_PM_EVENT_THAW, |
74 | SCLP_PM_EVENT_RESTORE, | 74 | SCLP_PM_EVENT_RESTORE, |
75 | }; | 75 | }; |
76 | 76 | ||
77 | #define SCLP_PANIC_PRIO 1 | 77 | #define SCLP_PANIC_PRIO 1 |
78 | #define SCLP_PANIC_PRIO_CLIENT 0 | 78 | #define SCLP_PANIC_PRIO_CLIENT 0 |
79 | 79 | ||
80 | typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ | 80 | typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ |
81 | 81 | ||
82 | struct sccb_header { | 82 | struct sccb_header { |
83 | u16 length; | 83 | u16 length; |
84 | u8 function_code; | 84 | u8 function_code; |
85 | u8 control_mask[3]; | 85 | u8 control_mask[3]; |
86 | u16 response_code; | 86 | u16 response_code; |
87 | } __attribute__((packed)); | 87 | } __attribute__((packed)); |
88 | 88 | ||
89 | extern u64 sclp_facilities; | 89 | extern u64 sclp_facilities; |
90 | #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) | 90 | #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) |
91 | #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) | 91 | #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) |
92 | #define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) | 92 | #define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) |
93 | #define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) | 93 | #define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) |
94 | 94 | ||
95 | 95 | ||
96 | struct gds_subvector { | 96 | struct gds_subvector { |
97 | u8 length; | 97 | u8 length; |
98 | u8 key; | 98 | u8 key; |
99 | } __attribute__((packed)); | 99 | } __attribute__((packed)); |
100 | 100 | ||
101 | struct gds_vector { | 101 | struct gds_vector { |
102 | u16 length; | 102 | u16 length; |
103 | u16 gds_id; | 103 | u16 gds_id; |
104 | } __attribute__((packed)); | 104 | } __attribute__((packed)); |
105 | 105 | ||
106 | struct evbuf_header { | 106 | struct evbuf_header { |
107 | u16 length; | 107 | u16 length; |
108 | u8 type; | 108 | u8 type; |
109 | u8 flags; | 109 | u8 flags; |
110 | u16 _reserved; | 110 | u16 _reserved; |
111 | } __attribute__((packed)); | 111 | } __attribute__((packed)); |
112 | 112 | ||
113 | struct sclp_req { | 113 | struct sclp_req { |
114 | struct list_head list; /* list_head for request queueing. */ | 114 | struct list_head list; /* list_head for request queueing. */ |
115 | sclp_cmdw_t command; /* sclp command to execute */ | 115 | sclp_cmdw_t command; /* sclp command to execute */ |
116 | void *sccb; /* pointer to the sccb to execute */ | 116 | void *sccb; /* pointer to the sccb to execute */ |
117 | char status; /* status of this request */ | 117 | char status; /* status of this request */ |
118 | int start_count; /* number of SVCs done for this req */ | 118 | int start_count; /* number of SVCs done for this req */ |
119 | /* Callback that is called after reaching final status. */ | 119 | /* Callback that is called after reaching final status. */ |
120 | void (*callback)(struct sclp_req *, void *data); | 120 | void (*callback)(struct sclp_req *, void *data); |
121 | void *callback_data; | 121 | void *callback_data; |
122 | }; | 122 | }; |
123 | 123 | ||
124 | #define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */ | 124 | #define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */ |
125 | #define SCLP_REQ_QUEUED 0x01 /* request is queued to be processed */ | 125 | #define SCLP_REQ_QUEUED 0x01 /* request is queued to be processed */ |
126 | #define SCLP_REQ_RUNNING 0x02 /* request is currently running */ | 126 | #define SCLP_REQ_RUNNING 0x02 /* request is currently running */ |
127 | #define SCLP_REQ_DONE 0x03 /* request is completed successfully */ | 127 | #define SCLP_REQ_DONE 0x03 /* request is completed successfully */ |
128 | #define SCLP_REQ_FAILED 0x05 /* request is finally failed */ | 128 | #define SCLP_REQ_FAILED 0x05 /* request is finally failed */ |
129 | 129 | ||
130 | /* function pointers that a high level driver has to use for registration */ | 130 | /* function pointers that a high level driver has to use for registration */ |
131 | /* of some routines it wants to be called from the low level driver */ | 131 | /* of some routines it wants to be called from the low level driver */ |
132 | struct sclp_register { | 132 | struct sclp_register { |
133 | struct list_head list; | 133 | struct list_head list; |
134 | /* User wants to receive: */ | 134 | /* User wants to receive: */ |
135 | sccb_mask_t receive_mask; | 135 | sccb_mask_t receive_mask; |
136 | /* User wants to send: */ | 136 | /* User wants to send: */ |
137 | sccb_mask_t send_mask; | 137 | sccb_mask_t send_mask; |
138 | /* H/W can receive: */ | 138 | /* H/W can receive: */ |
139 | sccb_mask_t sclp_receive_mask; | 139 | sccb_mask_t sclp_receive_mask; |
140 | /* H/W can send: */ | 140 | /* H/W can send: */ |
141 | sccb_mask_t sclp_send_mask; | 141 | sccb_mask_t sclp_send_mask; |
142 | /* called if event type availability changes */ | 142 | /* called if event type availability changes */ |
143 | void (*state_change_fn)(struct sclp_register *); | 143 | void (*state_change_fn)(struct sclp_register *); |
144 | /* called for events in cp_receive_mask/sclp_receive_mask */ | 144 | /* called for events in cp_receive_mask/sclp_receive_mask */ |
145 | void (*receiver_fn)(struct evbuf_header *); | 145 | void (*receiver_fn)(struct evbuf_header *); |
146 | /* called for power management events */ | 146 | /* called for power management events */ |
147 | void (*pm_event_fn)(struct sclp_register *, enum sclp_pm_event); | 147 | void (*pm_event_fn)(struct sclp_register *, enum sclp_pm_event); |
148 | /* pm event posted flag */ | 148 | /* pm event posted flag */ |
149 | int pm_event_posted; | 149 | int pm_event_posted; |
150 | }; | 150 | }; |
151 | 151 | ||
152 | /* externals from sclp.c */ | 152 | /* externals from sclp.c */ |
153 | int sclp_add_request(struct sclp_req *req); | 153 | int sclp_add_request(struct sclp_req *req); |
154 | void sclp_sync_wait(void); | 154 | void sclp_sync_wait(void); |
155 | int sclp_register(struct sclp_register *reg); | 155 | int sclp_register(struct sclp_register *reg); |
156 | void sclp_unregister(struct sclp_register *reg); | 156 | void sclp_unregister(struct sclp_register *reg); |
157 | int sclp_remove_processed(struct sccb_header *sccb); | 157 | int sclp_remove_processed(struct sccb_header *sccb); |
158 | int sclp_deactivate(void); | 158 | int sclp_deactivate(void); |
159 | int sclp_reactivate(void); | 159 | int sclp_reactivate(void); |
160 | int sclp_service_call(sclp_cmdw_t command, void *sccb); | 160 | int sclp_service_call(sclp_cmdw_t command, void *sccb); |
161 | 161 | ||
162 | int sclp_sdias_init(void); | 162 | int sclp_sdias_init(void); |
163 | void sclp_sdias_exit(void); | 163 | void sclp_sdias_exit(void); |
164 | 164 | ||
165 | /* useful inlines */ | 165 | /* useful inlines */ |
166 | 166 | ||
167 | /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ | 167 | /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ |
168 | /* translate single character from ASCII to EBCDIC */ | 168 | /* translate single character from ASCII to EBCDIC */ |
169 | static inline unsigned char | 169 | static inline unsigned char |
170 | sclp_ascebc(unsigned char ch) | 170 | sclp_ascebc(unsigned char ch) |
171 | { | 171 | { |
172 | return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch]; | 172 | return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch]; |
173 | } | 173 | } |
174 | 174 | ||
175 | /* translate string from EBCDIC to ASCII */ | 175 | /* translate string from EBCDIC to ASCII */ |
176 | static inline void | 176 | static inline void |
177 | sclp_ebcasc_str(unsigned char *str, int nr) | 177 | sclp_ebcasc_str(unsigned char *str, int nr) |
178 | { | 178 | { |
179 | (MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr); | 179 | (MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr); |
180 | } | 180 | } |
181 | 181 | ||
182 | /* translate string from ASCII to EBCDIC */ | 182 | /* translate string from ASCII to EBCDIC */ |
183 | static inline void | 183 | static inline void |
184 | sclp_ascebc_str(unsigned char *str, int nr) | 184 | sclp_ascebc_str(unsigned char *str, int nr) |
185 | { | 185 | { |
186 | (MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr); | 186 | (MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr); |
187 | } | 187 | } |
188 | 188 | ||
189 | static inline struct gds_vector * | ||
190 | sclp_find_gds_vector(void *start, void *end, u16 id) | ||
191 | { | ||
192 | struct gds_vector *v; | ||
193 | |||
194 | for (v = start; (void *) v < end; v = (void *) v + v->length) | ||
195 | if (v->gds_id == id) | ||
196 | return v; | ||
197 | return NULL; | ||
198 | } | ||
199 | |||
200 | static inline struct gds_subvector * | ||
201 | sclp_find_gds_subvector(void *start, void *end, u8 key) | ||
202 | { | ||
203 | struct gds_subvector *sv; | ||
204 | |||
205 | for (sv = start; (void *) sv < end; sv = (void *) sv + sv->length) | ||
206 | if (sv->key == key) | ||
207 | return sv; | ||
208 | return NULL; | ||
209 | } | ||
210 | |||
189 | #endif /* __SCLP_H__ */ | 211 | #endif /* __SCLP_H__ */ |
190 | 212 |
drivers/s390/char/sclp_tty.c
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_tty.c | 2 | * drivers/s390/char/sclp_tty.c |
3 | * SCLP line mode terminal driver. | 3 | * SCLP line mode terminal driver. |
4 | * | 4 | * |
5 | * S390 version | 5 | * S390 version |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/kmod.h> | 12 | #include <linux/kmod.h> |
13 | #include <linux/tty.h> | 13 | #include <linux/tty.h> |
14 | #include <linux/tty_driver.h> | 14 | #include <linux/tty_driver.h> |
15 | #include <linux/tty_flip.h> | 15 | #include <linux/tty_flip.h> |
16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/gfp.h> | 19 | #include <linux/gfp.h> |
20 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
21 | 21 | ||
22 | #include "ctrlchar.h" | 22 | #include "ctrlchar.h" |
23 | #include "sclp.h" | 23 | #include "sclp.h" |
24 | #include "sclp_rw.h" | 24 | #include "sclp_rw.h" |
25 | #include "sclp_tty.h" | 25 | #include "sclp_tty.h" |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * size of a buffer that collects single characters coming in | 28 | * size of a buffer that collects single characters coming in |
29 | * via sclp_tty_put_char() | 29 | * via sclp_tty_put_char() |
30 | */ | 30 | */ |
31 | #define SCLP_TTY_BUF_SIZE 512 | 31 | #define SCLP_TTY_BUF_SIZE 512 |
32 | 32 | ||
33 | /* | 33 | /* |
34 | * There is exactly one SCLP terminal, so we can keep things simple | 34 | * There is exactly one SCLP terminal, so we can keep things simple |
35 | * and allocate all variables statically. | 35 | * and allocate all variables statically. |
36 | */ | 36 | */ |
37 | 37 | ||
38 | /* Lock to guard over changes to global variables. */ | 38 | /* Lock to guard over changes to global variables. */ |
39 | static spinlock_t sclp_tty_lock; | 39 | static spinlock_t sclp_tty_lock; |
40 | /* List of free pages that can be used for console output buffering. */ | 40 | /* List of free pages that can be used for console output buffering. */ |
41 | static struct list_head sclp_tty_pages; | 41 | static struct list_head sclp_tty_pages; |
42 | /* List of full struct sclp_buffer structures ready for output. */ | 42 | /* List of full struct sclp_buffer structures ready for output. */ |
43 | static struct list_head sclp_tty_outqueue; | 43 | static struct list_head sclp_tty_outqueue; |
44 | /* Counter how many buffers are emitted. */ | 44 | /* Counter how many buffers are emitted. */ |
45 | static int sclp_tty_buffer_count; | 45 | static int sclp_tty_buffer_count; |
46 | /* Pointer to current console buffer. */ | 46 | /* Pointer to current console buffer. */ |
47 | static struct sclp_buffer *sclp_ttybuf; | 47 | static struct sclp_buffer *sclp_ttybuf; |
48 | /* Timer for delayed output of console messages. */ | 48 | /* Timer for delayed output of console messages. */ |
49 | static struct timer_list sclp_tty_timer; | 49 | static struct timer_list sclp_tty_timer; |
50 | 50 | ||
51 | static struct tty_struct *sclp_tty; | 51 | static struct tty_struct *sclp_tty; |
52 | static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; | 52 | static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; |
53 | static unsigned short int sclp_tty_chars_count; | 53 | static unsigned short int sclp_tty_chars_count; |
54 | 54 | ||
55 | struct tty_driver *sclp_tty_driver; | 55 | struct tty_driver *sclp_tty_driver; |
56 | 56 | ||
57 | static int sclp_tty_tolower; | 57 | static int sclp_tty_tolower; |
58 | static int sclp_tty_columns = 80; | 58 | static int sclp_tty_columns = 80; |
59 | 59 | ||
60 | #define SPACES_PER_TAB 8 | 60 | #define SPACES_PER_TAB 8 |
61 | #define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */ | 61 | #define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */ |
62 | 62 | ||
63 | /* This routine is called whenever we try to open a SCLP terminal. */ | 63 | /* This routine is called whenever we try to open a SCLP terminal. */ |
64 | static int | 64 | static int |
65 | sclp_tty_open(struct tty_struct *tty, struct file *filp) | 65 | sclp_tty_open(struct tty_struct *tty, struct file *filp) |
66 | { | 66 | { |
67 | sclp_tty = tty; | 67 | sclp_tty = tty; |
68 | tty->driver_data = NULL; | 68 | tty->driver_data = NULL; |
69 | tty->low_latency = 0; | 69 | tty->low_latency = 0; |
70 | return 0; | 70 | return 0; |
71 | } | 71 | } |
72 | 72 | ||
73 | /* This routine is called when the SCLP terminal is closed. */ | 73 | /* This routine is called when the SCLP terminal is closed. */ |
74 | static void | 74 | static void |
75 | sclp_tty_close(struct tty_struct *tty, struct file *filp) | 75 | sclp_tty_close(struct tty_struct *tty, struct file *filp) |
76 | { | 76 | { |
77 | if (tty->count > 1) | 77 | if (tty->count > 1) |
78 | return; | 78 | return; |
79 | sclp_tty = NULL; | 79 | sclp_tty = NULL; |
80 | } | 80 | } |
81 | 81 | ||
82 | /* | 82 | /* |
83 | * This routine returns the numbers of characters the tty driver | 83 | * This routine returns the numbers of characters the tty driver |
84 | * will accept for queuing to be written. This number is subject | 84 | * will accept for queuing to be written. This number is subject |
85 | * to change as output buffers get emptied, or if the output flow | 85 | * to change as output buffers get emptied, or if the output flow |
86 | * control is acted. This is not an exact number because not every | 86 | * control is acted. This is not an exact number because not every |
87 | * character needs the same space in the sccb. The worst case is | 87 | * character needs the same space in the sccb. The worst case is |
88 | * a string of newlines. Every newlines creates a new mto which | 88 | * a string of newlines. Every newlines creates a new mto which |
89 | * needs 8 bytes. | 89 | * needs 8 bytes. |
90 | */ | 90 | */ |
91 | static int | 91 | static int |
92 | sclp_tty_write_room (struct tty_struct *tty) | 92 | sclp_tty_write_room (struct tty_struct *tty) |
93 | { | 93 | { |
94 | unsigned long flags; | 94 | unsigned long flags; |
95 | struct list_head *l; | 95 | struct list_head *l; |
96 | int count; | 96 | int count; |
97 | 97 | ||
98 | spin_lock_irqsave(&sclp_tty_lock, flags); | 98 | spin_lock_irqsave(&sclp_tty_lock, flags); |
99 | count = 0; | 99 | count = 0; |
100 | if (sclp_ttybuf != NULL) | 100 | if (sclp_ttybuf != NULL) |
101 | count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto); | 101 | count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto); |
102 | list_for_each(l, &sclp_tty_pages) | 102 | list_for_each(l, &sclp_tty_pages) |
103 | count += NR_EMPTY_MTO_PER_SCCB; | 103 | count += NR_EMPTY_MTO_PER_SCCB; |
104 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 104 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
105 | return count; | 105 | return count; |
106 | } | 106 | } |
107 | 107 | ||
108 | static void | 108 | static void |
109 | sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) | 109 | sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) |
110 | { | 110 | { |
111 | unsigned long flags; | 111 | unsigned long flags; |
112 | void *page; | 112 | void *page; |
113 | 113 | ||
114 | do { | 114 | do { |
115 | page = sclp_unmake_buffer(buffer); | 115 | page = sclp_unmake_buffer(buffer); |
116 | spin_lock_irqsave(&sclp_tty_lock, flags); | 116 | spin_lock_irqsave(&sclp_tty_lock, flags); |
117 | /* Remove buffer from outqueue */ | 117 | /* Remove buffer from outqueue */ |
118 | list_del(&buffer->list); | 118 | list_del(&buffer->list); |
119 | sclp_tty_buffer_count--; | 119 | sclp_tty_buffer_count--; |
120 | list_add_tail((struct list_head *) page, &sclp_tty_pages); | 120 | list_add_tail((struct list_head *) page, &sclp_tty_pages); |
121 | /* Check if there is a pending buffer on the out queue. */ | 121 | /* Check if there is a pending buffer on the out queue. */ |
122 | buffer = NULL; | 122 | buffer = NULL; |
123 | if (!list_empty(&sclp_tty_outqueue)) | 123 | if (!list_empty(&sclp_tty_outqueue)) |
124 | buffer = list_entry(sclp_tty_outqueue.next, | 124 | buffer = list_entry(sclp_tty_outqueue.next, |
125 | struct sclp_buffer, list); | 125 | struct sclp_buffer, list); |
126 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 126 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
127 | } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); | 127 | } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); |
128 | /* check if the tty needs a wake up call */ | 128 | /* check if the tty needs a wake up call */ |
129 | if (sclp_tty != NULL) { | 129 | if (sclp_tty != NULL) { |
130 | tty_wakeup(sclp_tty); | 130 | tty_wakeup(sclp_tty); |
131 | } | 131 | } |
132 | } | 132 | } |
133 | 133 | ||
134 | static inline void | 134 | static inline void |
135 | __sclp_ttybuf_emit(struct sclp_buffer *buffer) | 135 | __sclp_ttybuf_emit(struct sclp_buffer *buffer) |
136 | { | 136 | { |
137 | unsigned long flags; | 137 | unsigned long flags; |
138 | int count; | 138 | int count; |
139 | int rc; | 139 | int rc; |
140 | 140 | ||
141 | spin_lock_irqsave(&sclp_tty_lock, flags); | 141 | spin_lock_irqsave(&sclp_tty_lock, flags); |
142 | list_add_tail(&buffer->list, &sclp_tty_outqueue); | 142 | list_add_tail(&buffer->list, &sclp_tty_outqueue); |
143 | count = sclp_tty_buffer_count++; | 143 | count = sclp_tty_buffer_count++; |
144 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 144 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
145 | if (count) | 145 | if (count) |
146 | return; | 146 | return; |
147 | rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback); | 147 | rc = sclp_emit_buffer(buffer, sclp_ttybuf_callback); |
148 | if (rc) | 148 | if (rc) |
149 | sclp_ttybuf_callback(buffer, rc); | 149 | sclp_ttybuf_callback(buffer, rc); |
150 | } | 150 | } |
151 | 151 | ||
152 | /* | 152 | /* |
153 | * When this routine is called from the timer then we flush the | 153 | * When this routine is called from the timer then we flush the |
154 | * temporary write buffer. | 154 | * temporary write buffer. |
155 | */ | 155 | */ |
156 | static void | 156 | static void |
157 | sclp_tty_timeout(unsigned long data) | 157 | sclp_tty_timeout(unsigned long data) |
158 | { | 158 | { |
159 | unsigned long flags; | 159 | unsigned long flags; |
160 | struct sclp_buffer *buf; | 160 | struct sclp_buffer *buf; |
161 | 161 | ||
162 | spin_lock_irqsave(&sclp_tty_lock, flags); | 162 | spin_lock_irqsave(&sclp_tty_lock, flags); |
163 | buf = sclp_ttybuf; | 163 | buf = sclp_ttybuf; |
164 | sclp_ttybuf = NULL; | 164 | sclp_ttybuf = NULL; |
165 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 165 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
166 | 166 | ||
167 | if (buf != NULL) { | 167 | if (buf != NULL) { |
168 | __sclp_ttybuf_emit(buf); | 168 | __sclp_ttybuf_emit(buf); |
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | /* | 172 | /* |
173 | * Write a string to the sclp tty. | 173 | * Write a string to the sclp tty. |
174 | */ | 174 | */ |
175 | static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail) | 175 | static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail) |
176 | { | 176 | { |
177 | unsigned long flags; | 177 | unsigned long flags; |
178 | void *page; | 178 | void *page; |
179 | int written; | 179 | int written; |
180 | int overall_written; | 180 | int overall_written; |
181 | struct sclp_buffer *buf; | 181 | struct sclp_buffer *buf; |
182 | 182 | ||
183 | if (count <= 0) | 183 | if (count <= 0) |
184 | return 0; | 184 | return 0; |
185 | overall_written = 0; | 185 | overall_written = 0; |
186 | spin_lock_irqsave(&sclp_tty_lock, flags); | 186 | spin_lock_irqsave(&sclp_tty_lock, flags); |
187 | do { | 187 | do { |
188 | /* Create a sclp output buffer if none exists yet */ | 188 | /* Create a sclp output buffer if none exists yet */ |
189 | if (sclp_ttybuf == NULL) { | 189 | if (sclp_ttybuf == NULL) { |
190 | while (list_empty(&sclp_tty_pages)) { | 190 | while (list_empty(&sclp_tty_pages)) { |
191 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 191 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
192 | if (may_fail) | 192 | if (may_fail) |
193 | goto out; | 193 | goto out; |
194 | else | 194 | else |
195 | sclp_sync_wait(); | 195 | sclp_sync_wait(); |
196 | spin_lock_irqsave(&sclp_tty_lock, flags); | 196 | spin_lock_irqsave(&sclp_tty_lock, flags); |
197 | } | 197 | } |
198 | page = sclp_tty_pages.next; | 198 | page = sclp_tty_pages.next; |
199 | list_del((struct list_head *) page); | 199 | list_del((struct list_head *) page); |
200 | sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns, | 200 | sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns, |
201 | SPACES_PER_TAB); | 201 | SPACES_PER_TAB); |
202 | } | 202 | } |
203 | /* try to write the string to the current output buffer */ | 203 | /* try to write the string to the current output buffer */ |
204 | written = sclp_write(sclp_ttybuf, str, count); | 204 | written = sclp_write(sclp_ttybuf, str, count); |
205 | overall_written += written; | 205 | overall_written += written; |
206 | if (written == count) | 206 | if (written == count) |
207 | break; | 207 | break; |
208 | /* | 208 | /* |
209 | * Not all characters could be written to the current | 209 | * Not all characters could be written to the current |
210 | * output buffer. Emit the buffer, create a new buffer | 210 | * output buffer. Emit the buffer, create a new buffer |
211 | * and then output the rest of the string. | 211 | * and then output the rest of the string. |
212 | */ | 212 | */ |
213 | buf = sclp_ttybuf; | 213 | buf = sclp_ttybuf; |
214 | sclp_ttybuf = NULL; | 214 | sclp_ttybuf = NULL; |
215 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 215 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
216 | __sclp_ttybuf_emit(buf); | 216 | __sclp_ttybuf_emit(buf); |
217 | spin_lock_irqsave(&sclp_tty_lock, flags); | 217 | spin_lock_irqsave(&sclp_tty_lock, flags); |
218 | str += written; | 218 | str += written; |
219 | count -= written; | 219 | count -= written; |
220 | } while (count > 0); | 220 | } while (count > 0); |
221 | /* Setup timer to output current console buffer after 1/10 second */ | 221 | /* Setup timer to output current console buffer after 1/10 second */ |
222 | if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) && | 222 | if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) && |
223 | !timer_pending(&sclp_tty_timer)) { | 223 | !timer_pending(&sclp_tty_timer)) { |
224 | init_timer(&sclp_tty_timer); | 224 | init_timer(&sclp_tty_timer); |
225 | sclp_tty_timer.function = sclp_tty_timeout; | 225 | sclp_tty_timer.function = sclp_tty_timeout; |
226 | sclp_tty_timer.data = 0UL; | 226 | sclp_tty_timer.data = 0UL; |
227 | sclp_tty_timer.expires = jiffies + HZ/10; | 227 | sclp_tty_timer.expires = jiffies + HZ/10; |
228 | add_timer(&sclp_tty_timer); | 228 | add_timer(&sclp_tty_timer); |
229 | } | 229 | } |
230 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 230 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
231 | out: | 231 | out: |
232 | return overall_written; | 232 | return overall_written; |
233 | } | 233 | } |
234 | 234 | ||
235 | /* | 235 | /* |
236 | * This routine is called by the kernel to write a series of characters to the | 236 | * This routine is called by the kernel to write a series of characters to the |
237 | * tty device. The characters may come from user space or kernel space. This | 237 | * tty device. The characters may come from user space or kernel space. This |
238 | * routine will return the number of characters actually accepted for writing. | 238 | * routine will return the number of characters actually accepted for writing. |
239 | */ | 239 | */ |
240 | static int | 240 | static int |
241 | sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) | 241 | sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) |
242 | { | 242 | { |
243 | if (sclp_tty_chars_count > 0) { | 243 | if (sclp_tty_chars_count > 0) { |
244 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); | 244 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); |
245 | sclp_tty_chars_count = 0; | 245 | sclp_tty_chars_count = 0; |
246 | } | 246 | } |
247 | return sclp_tty_write_string(buf, count, 1); | 247 | return sclp_tty_write_string(buf, count, 1); |
248 | } | 248 | } |
249 | 249 | ||
250 | /* | 250 | /* |
251 | * This routine is called by the kernel to write a single character to the tty | 251 | * This routine is called by the kernel to write a single character to the tty |
252 | * device. If the kernel uses this routine, it must call the flush_chars() | 252 | * device. If the kernel uses this routine, it must call the flush_chars() |
253 | * routine (if defined) when it is done stuffing characters into the driver. | 253 | * routine (if defined) when it is done stuffing characters into the driver. |
254 | * | 254 | * |
255 | * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver. | 255 | * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver. |
256 | * If the given character is a '\n' the contents of the SCLP write buffer | 256 | * If the given character is a '\n' the contents of the SCLP write buffer |
257 | * - including previous characters from sclp_tty_put_char() and strings from | 257 | * - including previous characters from sclp_tty_put_char() and strings from |
258 | * sclp_write() without final '\n' - will be written. | 258 | * sclp_write() without final '\n' - will be written. |
259 | */ | 259 | */ |
260 | static int | 260 | static int |
261 | sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) | 261 | sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) |
262 | { | 262 | { |
263 | sclp_tty_chars[sclp_tty_chars_count++] = ch; | 263 | sclp_tty_chars[sclp_tty_chars_count++] = ch; |
264 | if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { | 264 | if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { |
265 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); | 265 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); |
266 | sclp_tty_chars_count = 0; | 266 | sclp_tty_chars_count = 0; |
267 | } | 267 | } |
268 | return 1; | 268 | return 1; |
269 | } | 269 | } |
270 | 270 | ||
271 | /* | 271 | /* |
272 | * This routine is called by the kernel after it has written a series of | 272 | * This routine is called by the kernel after it has written a series of |
273 | * characters to the tty device using put_char(). | 273 | * characters to the tty device using put_char(). |
274 | */ | 274 | */ |
275 | static void | 275 | static void |
276 | sclp_tty_flush_chars(struct tty_struct *tty) | 276 | sclp_tty_flush_chars(struct tty_struct *tty) |
277 | { | 277 | { |
278 | if (sclp_tty_chars_count > 0) { | 278 | if (sclp_tty_chars_count > 0) { |
279 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); | 279 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); |
280 | sclp_tty_chars_count = 0; | 280 | sclp_tty_chars_count = 0; |
281 | } | 281 | } |
282 | } | 282 | } |
283 | 283 | ||
284 | /* | 284 | /* |
285 | * This routine returns the number of characters in the write buffer of the | 285 | * This routine returns the number of characters in the write buffer of the |
286 | * SCLP driver. The provided number includes all characters that are stored | 286 | * SCLP driver. The provided number includes all characters that are stored |
287 | * in the SCCB (will be written next time the SCLP is not busy) as well as | 287 | * in the SCCB (will be written next time the SCLP is not busy) as well as |
288 | * characters in the write buffer (will not be written as long as there is a | 288 | * characters in the write buffer (will not be written as long as there is a |
289 | * final line feed missing). | 289 | * final line feed missing). |
290 | */ | 290 | */ |
291 | static int | 291 | static int |
292 | sclp_tty_chars_in_buffer(struct tty_struct *tty) | 292 | sclp_tty_chars_in_buffer(struct tty_struct *tty) |
293 | { | 293 | { |
294 | unsigned long flags; | 294 | unsigned long flags; |
295 | struct list_head *l; | 295 | struct list_head *l; |
296 | struct sclp_buffer *t; | 296 | struct sclp_buffer *t; |
297 | int count; | 297 | int count; |
298 | 298 | ||
299 | spin_lock_irqsave(&sclp_tty_lock, flags); | 299 | spin_lock_irqsave(&sclp_tty_lock, flags); |
300 | count = 0; | 300 | count = 0; |
301 | if (sclp_ttybuf != NULL) | 301 | if (sclp_ttybuf != NULL) |
302 | count = sclp_chars_in_buffer(sclp_ttybuf); | 302 | count = sclp_chars_in_buffer(sclp_ttybuf); |
303 | list_for_each(l, &sclp_tty_outqueue) { | 303 | list_for_each(l, &sclp_tty_outqueue) { |
304 | t = list_entry(l, struct sclp_buffer, list); | 304 | t = list_entry(l, struct sclp_buffer, list); |
305 | count += sclp_chars_in_buffer(t); | 305 | count += sclp_chars_in_buffer(t); |
306 | } | 306 | } |
307 | spin_unlock_irqrestore(&sclp_tty_lock, flags); | 307 | spin_unlock_irqrestore(&sclp_tty_lock, flags); |
308 | return count; | 308 | return count; |
309 | } | 309 | } |
310 | 310 | ||
311 | /* | 311 | /* |
312 | * removes all content from buffers of low level driver | 312 | * removes all content from buffers of low level driver |
313 | */ | 313 | */ |
314 | static void | 314 | static void |
315 | sclp_tty_flush_buffer(struct tty_struct *tty) | 315 | sclp_tty_flush_buffer(struct tty_struct *tty) |
316 | { | 316 | { |
317 | if (sclp_tty_chars_count > 0) { | 317 | if (sclp_tty_chars_count > 0) { |
318 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); | 318 | sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); |
319 | sclp_tty_chars_count = 0; | 319 | sclp_tty_chars_count = 0; |
320 | } | 320 | } |
321 | } | 321 | } |
322 | 322 | ||
323 | /* | 323 | /* |
324 | * push input to tty | 324 | * push input to tty |
325 | */ | 325 | */ |
326 | static void | 326 | static void |
327 | sclp_tty_input(unsigned char* buf, unsigned int count) | 327 | sclp_tty_input(unsigned char* buf, unsigned int count) |
328 | { | 328 | { |
329 | unsigned int cchar; | 329 | unsigned int cchar; |
330 | 330 | ||
331 | /* | 331 | /* |
332 | * If this tty driver is currently closed | 332 | * If this tty driver is currently closed |
333 | * then throw the received input away. | 333 | * then throw the received input away. |
334 | */ | 334 | */ |
335 | if (sclp_tty == NULL) | 335 | if (sclp_tty == NULL) |
336 | return; | 336 | return; |
337 | cchar = ctrlchar_handle(buf, count, sclp_tty); | 337 | cchar = ctrlchar_handle(buf, count, sclp_tty); |
338 | switch (cchar & CTRLCHAR_MASK) { | 338 | switch (cchar & CTRLCHAR_MASK) { |
339 | case CTRLCHAR_SYSRQ: | 339 | case CTRLCHAR_SYSRQ: |
340 | break; | 340 | break; |
341 | case CTRLCHAR_CTRL: | 341 | case CTRLCHAR_CTRL: |
342 | tty_insert_flip_char(sclp_tty, cchar, TTY_NORMAL); | 342 | tty_insert_flip_char(sclp_tty, cchar, TTY_NORMAL); |
343 | tty_flip_buffer_push(sclp_tty); | 343 | tty_flip_buffer_push(sclp_tty); |
344 | break; | 344 | break; |
345 | case CTRLCHAR_NONE: | 345 | case CTRLCHAR_NONE: |
346 | /* send (normal) input to line discipline */ | 346 | /* send (normal) input to line discipline */ |
347 | if (count < 2 || | 347 | if (count < 2 || |
348 | (strncmp((const char *) buf + count - 2, "^n", 2) && | 348 | (strncmp((const char *) buf + count - 2, "^n", 2) && |
349 | strncmp((const char *) buf + count - 2, "\252n", 2))) { | 349 | strncmp((const char *) buf + count - 2, "\252n", 2))) { |
350 | /* add the auto \n */ | 350 | /* add the auto \n */ |
351 | tty_insert_flip_string(sclp_tty, buf, count); | 351 | tty_insert_flip_string(sclp_tty, buf, count); |
352 | tty_insert_flip_char(sclp_tty, '\n', TTY_NORMAL); | 352 | tty_insert_flip_char(sclp_tty, '\n', TTY_NORMAL); |
353 | } else | 353 | } else |
354 | tty_insert_flip_string(sclp_tty, buf, count - 2); | 354 | tty_insert_flip_string(sclp_tty, buf, count - 2); |
355 | tty_flip_buffer_push(sclp_tty); | 355 | tty_flip_buffer_push(sclp_tty); |
356 | break; | 356 | break; |
357 | } | 357 | } |
358 | } | 358 | } |
359 | 359 | ||
360 | /* | 360 | /* |
361 | * get a EBCDIC string in upper/lower case, | 361 | * get a EBCDIC string in upper/lower case, |
362 | * find out characters in lower/upper case separated by a special character, | 362 | * find out characters in lower/upper case separated by a special character, |
363 | * modifiy original string, | 363 | * modifiy original string, |
364 | * returns length of resulting string | 364 | * returns length of resulting string |
365 | */ | 365 | */ |
366 | static int sclp_switch_cases(unsigned char *buf, int count) | 366 | static int sclp_switch_cases(unsigned char *buf, int count) |
367 | { | 367 | { |
368 | unsigned char *ip, *op; | 368 | unsigned char *ip, *op; |
369 | int toggle; | 369 | int toggle; |
370 | 370 | ||
371 | /* initially changing case is off */ | 371 | /* initially changing case is off */ |
372 | toggle = 0; | 372 | toggle = 0; |
373 | ip = op = buf; | 373 | ip = op = buf; |
374 | while (count-- > 0) { | 374 | while (count-- > 0) { |
375 | /* compare with special character */ | 375 | /* compare with special character */ |
376 | if (*ip == CASE_DELIMITER) { | 376 | if (*ip == CASE_DELIMITER) { |
377 | /* followed by another special character? */ | 377 | /* followed by another special character? */ |
378 | if (count && ip[1] == CASE_DELIMITER) { | 378 | if (count && ip[1] == CASE_DELIMITER) { |
379 | /* | 379 | /* |
380 | * ... then put a single copy of the special | 380 | * ... then put a single copy of the special |
381 | * character to the output string | 381 | * character to the output string |
382 | */ | 382 | */ |
383 | *op++ = *ip++; | 383 | *op++ = *ip++; |
384 | count--; | 384 | count--; |
385 | } else | 385 | } else |
386 | /* | 386 | /* |
387 | * ... special character follower by a normal | 387 | * ... special character follower by a normal |
388 | * character toggles the case change behaviour | 388 | * character toggles the case change behaviour |
389 | */ | 389 | */ |
390 | toggle = ~toggle; | 390 | toggle = ~toggle; |
391 | /* skip special character */ | 391 | /* skip special character */ |
392 | ip++; | 392 | ip++; |
393 | } else | 393 | } else |
394 | /* not the special character */ | 394 | /* not the special character */ |
395 | if (toggle) | 395 | if (toggle) |
396 | /* but case switching is on */ | 396 | /* but case switching is on */ |
397 | if (sclp_tty_tolower) | 397 | if (sclp_tty_tolower) |
398 | /* switch to uppercase */ | 398 | /* switch to uppercase */ |
399 | *op++ = _ebc_toupper[(int) *ip++]; | 399 | *op++ = _ebc_toupper[(int) *ip++]; |
400 | else | 400 | else |
401 | /* switch to lowercase */ | 401 | /* switch to lowercase */ |
402 | *op++ = _ebc_tolower[(int) *ip++]; | 402 | *op++ = _ebc_tolower[(int) *ip++]; |
403 | else | 403 | else |
404 | /* no case switching, copy the character */ | 404 | /* no case switching, copy the character */ |
405 | *op++ = *ip++; | 405 | *op++ = *ip++; |
406 | } | 406 | } |
407 | /* return length of reformatted string. */ | 407 | /* return length of reformatted string. */ |
408 | return op - buf; | 408 | return op - buf; |
409 | } | 409 | } |
410 | 410 | ||
411 | static void | 411 | static void sclp_get_input(struct gds_subvector *sv) |
412 | sclp_get_input(unsigned char *start, unsigned char *end) | ||
413 | { | 412 | { |
413 | unsigned char *str; | ||
414 | int count; | 414 | int count; |
415 | 415 | ||
416 | count = end - start; | 416 | str = (unsigned char *) (sv + 1); |
417 | count = sv->length - sizeof(*sv); | ||
417 | if (sclp_tty_tolower) | 418 | if (sclp_tty_tolower) |
418 | EBC_TOLOWER(start, count); | 419 | EBC_TOLOWER(str, count); |
419 | count = sclp_switch_cases(start, count); | 420 | count = sclp_switch_cases(str, count); |
420 | /* convert EBCDIC to ASCII (modify original input in SCCB) */ | 421 | /* convert EBCDIC to ASCII (modify original input in SCCB) */ |
421 | sclp_ebcasc_str(start, count); | 422 | sclp_ebcasc_str(str, count); |
422 | 423 | ||
423 | /* transfer input to high level driver */ | 424 | /* transfer input to high level driver */ |
424 | sclp_tty_input(start, count); | 425 | sclp_tty_input(str, count); |
425 | } | 426 | } |
426 | 427 | ||
427 | static inline struct gds_vector * | 428 | static inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv) |
428 | find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) | ||
429 | { | 429 | { |
430 | struct gds_vector *vec; | 430 | void *end; |
431 | 431 | ||
432 | for (vec = start; vec < end; vec = (void *) vec + vec->length) | 432 | end = (void *) sv + sv->length; |
433 | if (vec->gds_id == id) | 433 | for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length) |
434 | return vec; | 434 | if (sv->key == 0x30) |
435 | return NULL; | 435 | sclp_get_input(sv); |
436 | } | 436 | } |
437 | 437 | ||
438 | static inline struct gds_subvector * | 438 | static inline void sclp_eval_textcmd(struct gds_vector *v) |
439 | find_gds_subvector(struct gds_subvector *start, | ||
440 | struct gds_subvector *end, u8 key) | ||
441 | { | 439 | { |
442 | struct gds_subvector *subvec; | 440 | struct gds_subvector *sv; |
441 | void *end; | ||
443 | 442 | ||
444 | for (subvec = start; subvec < end; | 443 | end = (void *) v + v->length; |
445 | subvec = (void *) subvec + subvec->length) | 444 | for (sv = (struct gds_subvector *) (v + 1); |
446 | if (subvec->key == key) | 445 | (void *) sv < end; sv = (void *) sv + sv->length) |
447 | return subvec; | 446 | if (sv->key == GDS_KEY_SELFDEFTEXTMSG) |
448 | return NULL; | 447 | sclp_eval_selfdeftextmsg(sv); |
449 | } | ||
450 | 448 | ||
451 | static inline void | ||
452 | sclp_eval_selfdeftextmsg(struct gds_subvector *start, | ||
453 | struct gds_subvector *end) | ||
454 | { | ||
455 | struct gds_subvector *subvec; | ||
456 | |||
457 | subvec = start; | ||
458 | while (subvec < end) { | ||
459 | subvec = find_gds_subvector(subvec, end, 0x30); | ||
460 | if (!subvec) | ||
461 | break; | ||
462 | sclp_get_input((unsigned char *)(subvec + 1), | ||
463 | (unsigned char *) subvec + subvec->length); | ||
464 | subvec = (void *) subvec + subvec->length; | ||
465 | } | ||
466 | } | 449 | } |
467 | 450 | ||
468 | static inline void | 451 | static inline void sclp_eval_cpmsu(struct gds_vector *v) |
469 | sclp_eval_textcmd(struct gds_subvector *start, | ||
470 | struct gds_subvector *end) | ||
471 | { | 452 | { |
472 | struct gds_subvector *subvec; | 453 | void *end; |
473 | 454 | ||
474 | subvec = start; | 455 | end = (void *) v + v->length; |
475 | while (subvec < end) { | 456 | for (v = v + 1; (void *) v < end; v = (void *) v + v->length) |
476 | subvec = find_gds_subvector(subvec, end, | 457 | if (v->gds_id == GDS_ID_TEXTCMD) |
477 | GDS_KEY_SELFDEFTEXTMSG); | 458 | sclp_eval_textcmd(v); |
478 | if (!subvec) | ||
479 | break; | ||
480 | sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), | ||
481 | (void *)subvec + subvec->length); | ||
482 | subvec = (void *) subvec + subvec->length; | ||
483 | } | ||
484 | } | 459 | } |
485 | 460 | ||
486 | static inline void | ||
487 | sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) | ||
488 | { | ||
489 | struct gds_vector *vec; | ||
490 | 461 | ||
491 | vec = start; | 462 | static inline void sclp_eval_mdsmu(struct gds_vector *v) |
492 | while (vec < end) { | ||
493 | vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD); | ||
494 | if (!vec) | ||
495 | break; | ||
496 | sclp_eval_textcmd((struct gds_subvector *)(vec + 1), | ||
497 | (void *) vec + vec->length); | ||
498 | vec = (void *) vec + vec->length; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | |||
503 | static inline void | ||
504 | sclp_eval_mdsmu(struct gds_vector *start, void *end) | ||
505 | { | 463 | { |
506 | struct gds_vector *vec; | 464 | v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU); |
507 | 465 | if (v) | |
508 | vec = find_gds_vector(start, end, GDS_ID_CPMSU); | 466 | sclp_eval_cpmsu(v); |
509 | if (vec) | ||
510 | sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length); | ||
511 | } | 467 | } |
512 | 468 | ||
513 | static void | 469 | static void sclp_tty_receiver(struct evbuf_header *evbuf) |
514 | sclp_tty_receiver(struct evbuf_header *evbuf) | ||
515 | { | 470 | { |
516 | struct gds_vector *start, *end, *vec; | 471 | struct gds_vector *v; |
517 | 472 | ||
518 | start = (struct gds_vector *)(evbuf + 1); | 473 | v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, |
519 | end = (void *) evbuf + evbuf->length; | 474 | GDS_ID_MDSMU); |
520 | vec = find_gds_vector(start, end, GDS_ID_MDSMU); | 475 | if (v) |
521 | if (vec) | 476 | sclp_eval_mdsmu(v); |
522 | sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length); | ||
523 | } | 477 | } |
524 | 478 | ||
525 | static void | 479 | static void |
526 | sclp_tty_state_change(struct sclp_register *reg) | 480 | sclp_tty_state_change(struct sclp_register *reg) |
527 | { | 481 | { |
528 | } | 482 | } |
529 | 483 | ||
530 | static struct sclp_register sclp_input_event = | 484 | static struct sclp_register sclp_input_event = |
531 | { | 485 | { |
532 | .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK, | 486 | .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK, |
533 | .state_change_fn = sclp_tty_state_change, | 487 | .state_change_fn = sclp_tty_state_change, |
534 | .receiver_fn = sclp_tty_receiver | 488 | .receiver_fn = sclp_tty_receiver |
535 | }; | 489 | }; |
536 | 490 | ||
537 | static const struct tty_operations sclp_ops = { | 491 | static const struct tty_operations sclp_ops = { |
538 | .open = sclp_tty_open, | 492 | .open = sclp_tty_open, |
539 | .close = sclp_tty_close, | 493 | .close = sclp_tty_close, |
540 | .write = sclp_tty_write, | 494 | .write = sclp_tty_write, |
541 | .put_char = sclp_tty_put_char, | 495 | .put_char = sclp_tty_put_char, |
542 | .flush_chars = sclp_tty_flush_chars, | 496 | .flush_chars = sclp_tty_flush_chars, |
543 | .write_room = sclp_tty_write_room, | 497 | .write_room = sclp_tty_write_room, |
544 | .chars_in_buffer = sclp_tty_chars_in_buffer, | 498 | .chars_in_buffer = sclp_tty_chars_in_buffer, |
545 | .flush_buffer = sclp_tty_flush_buffer, | 499 | .flush_buffer = sclp_tty_flush_buffer, |
546 | }; | 500 | }; |
547 | 501 | ||
548 | static int __init | 502 | static int __init |
549 | sclp_tty_init(void) | 503 | sclp_tty_init(void) |
550 | { | 504 | { |
551 | struct tty_driver *driver; | 505 | struct tty_driver *driver; |
552 | void *page; | 506 | void *page; |
553 | int i; | 507 | int i; |
554 | int rc; | 508 | int rc; |
555 | 509 | ||
556 | if (!CONSOLE_IS_SCLP) | 510 | if (!CONSOLE_IS_SCLP) |
557 | return 0; | 511 | return 0; |
558 | driver = alloc_tty_driver(1); | 512 | driver = alloc_tty_driver(1); |
559 | if (!driver) | 513 | if (!driver) |
560 | return -ENOMEM; | 514 | return -ENOMEM; |
561 | 515 | ||
562 | rc = sclp_rw_init(); | 516 | rc = sclp_rw_init(); |
563 | if (rc) { | 517 | if (rc) { |
564 | put_tty_driver(driver); | 518 | put_tty_driver(driver); |
565 | return rc; | 519 | return rc; |
566 | } | 520 | } |
567 | /* Allocate pages for output buffering */ | 521 | /* Allocate pages for output buffering */ |
568 | INIT_LIST_HEAD(&sclp_tty_pages); | 522 | INIT_LIST_HEAD(&sclp_tty_pages); |
569 | for (i = 0; i < MAX_KMEM_PAGES; i++) { | 523 | for (i = 0; i < MAX_KMEM_PAGES; i++) { |
570 | page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | 524 | page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
571 | if (page == NULL) { | 525 | if (page == NULL) { |
572 | put_tty_driver(driver); | 526 | put_tty_driver(driver); |
573 | return -ENOMEM; | 527 | return -ENOMEM; |
574 | } | 528 | } |
575 | list_add_tail((struct list_head *) page, &sclp_tty_pages); | 529 | list_add_tail((struct list_head *) page, &sclp_tty_pages); |
576 | } | 530 | } |
577 | INIT_LIST_HEAD(&sclp_tty_outqueue); | 531 | INIT_LIST_HEAD(&sclp_tty_outqueue); |
578 | spin_lock_init(&sclp_tty_lock); | 532 | spin_lock_init(&sclp_tty_lock); |
579 | init_timer(&sclp_tty_timer); | 533 | init_timer(&sclp_tty_timer); |
580 | sclp_ttybuf = NULL; | 534 | sclp_ttybuf = NULL; |
581 | sclp_tty_buffer_count = 0; | 535 | sclp_tty_buffer_count = 0; |
582 | if (MACHINE_IS_VM) { | 536 | if (MACHINE_IS_VM) { |
583 | /* | 537 | /* |
584 | * save 4 characters for the CPU number | 538 | * save 4 characters for the CPU number |
585 | * written at start of each line by VM/CP | 539 | * written at start of each line by VM/CP |
586 | */ | 540 | */ |
587 | sclp_tty_columns = 76; | 541 | sclp_tty_columns = 76; |
588 | /* case input lines to lowercase */ | 542 | /* case input lines to lowercase */ |
589 | sclp_tty_tolower = 1; | 543 | sclp_tty_tolower = 1; |
590 | } | 544 | } |
591 | sclp_tty_chars_count = 0; | 545 | sclp_tty_chars_count = 0; |
592 | sclp_tty = NULL; | 546 | sclp_tty = NULL; |
593 | 547 | ||
594 | rc = sclp_register(&sclp_input_event); | 548 | rc = sclp_register(&sclp_input_event); |
595 | if (rc) { | 549 | if (rc) { |
596 | put_tty_driver(driver); | 550 | put_tty_driver(driver); |
597 | return rc; | 551 | return rc; |
598 | } | 552 | } |
599 | 553 | ||
600 | driver->owner = THIS_MODULE; | 554 | driver->owner = THIS_MODULE; |
601 | driver->driver_name = "sclp_line"; | 555 | driver->driver_name = "sclp_line"; |
602 | driver->name = "sclp_line"; | 556 | driver->name = "sclp_line"; |
603 | driver->major = TTY_MAJOR; | 557 | driver->major = TTY_MAJOR; |
604 | driver->minor_start = 64; | 558 | driver->minor_start = 64; |
605 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | 559 | driver->type = TTY_DRIVER_TYPE_SYSTEM; |
606 | driver->subtype = SYSTEM_TYPE_TTY; | 560 | driver->subtype = SYSTEM_TYPE_TTY; |
607 | driver->init_termios = tty_std_termios; | 561 | driver->init_termios = tty_std_termios; |
608 | driver->init_termios.c_iflag = IGNBRK | IGNPAR; | 562 | driver->init_termios.c_iflag = IGNBRK | IGNPAR; |
609 | driver->init_termios.c_oflag = ONLCR | XTABS; | 563 | driver->init_termios.c_oflag = ONLCR | XTABS; |
610 | driver->init_termios.c_lflag = ISIG | ECHO; | 564 | driver->init_termios.c_lflag = ISIG | ECHO; |
611 | driver->flags = TTY_DRIVER_REAL_RAW; | 565 | driver->flags = TTY_DRIVER_REAL_RAW; |
612 | tty_set_operations(driver, &sclp_ops); | 566 | tty_set_operations(driver, &sclp_ops); |
613 | rc = tty_register_driver(driver); | 567 | rc = tty_register_driver(driver); |
614 | if (rc) { | 568 | if (rc) { |
615 | put_tty_driver(driver); | 569 | put_tty_driver(driver); |
616 | return rc; | 570 | return rc; |
617 | } | 571 | } |
618 | sclp_tty_driver = driver; | 572 | sclp_tty_driver = driver; |
619 | return 0; | 573 | return 0; |