diff options
Diffstat (limited to 'drivers/s390/char/fs3270.c')
-rw-r--r-- | drivers/s390/char/fs3270.c | 234 |
1 files changed, 195 insertions, 39 deletions
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 60afcdcf91c2..735a7fcdeff5 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c | |||
@@ -33,8 +33,11 @@ struct fs3270 { | |||
33 | int read_command; /* ccw command to use for reads. */ | 33 | int read_command; /* ccw command to use for reads. */ |
34 | int write_command; /* ccw command to use for writes. */ | 34 | int write_command; /* ccw command to use for writes. */ |
35 | int attention; /* Got attention. */ | 35 | int attention; /* Got attention. */ |
36 | struct raw3270_request *clear; /* single clear request. */ | 36 | int active; /* Fullscreen view is active. */ |
37 | wait_queue_head_t attn_wait; /* Attention wait queue. */ | 37 | struct raw3270_request *init; /* single init request. */ |
38 | wait_queue_head_t wait; /* Init & attention wait queue. */ | ||
39 | struct idal_buffer *rdbuf; /* full-screen-deactivate buffer */ | ||
40 | size_t rdbuf_size; /* size of data returned by RDBUF */ | ||
38 | }; | 41 | }; |
39 | 42 | ||
40 | static void | 43 | static void |
@@ -43,58 +46,172 @@ fs3270_wake_up(struct raw3270_request *rq, void *data) | |||
43 | wake_up((wait_queue_head_t *) data); | 46 | wake_up((wait_queue_head_t *) data); |
44 | } | 47 | } |
45 | 48 | ||
49 | static inline int | ||
50 | fs3270_working(struct fs3270 *fp) | ||
51 | { | ||
52 | /* | ||
53 | * The fullscreen view is in working order if the view | ||
54 | * has been activated AND the initial request is finished. | ||
55 | */ | ||
56 | return fp->active && raw3270_request_final(fp->init); | ||
57 | } | ||
58 | |||
46 | static int | 59 | static int |
47 | fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) | 60 | fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) |
48 | { | 61 | { |
49 | wait_queue_head_t wq; | 62 | struct fs3270 *fp; |
50 | int rc; | 63 | int rc; |
51 | 64 | ||
52 | init_waitqueue_head(&wq); | 65 | fp = (struct fs3270 *) view; |
53 | rq->callback = fs3270_wake_up; | 66 | rq->callback = fs3270_wake_up; |
54 | rq->callback_data = &wq; | 67 | rq->callback_data = &fp->wait; |
55 | rc = raw3270_start(view, rq); | 68 | |
56 | if (rc) | 69 | do { |
57 | return rc; | 70 | if (!fs3270_working(fp)) { |
58 | /* Started sucessfully. Now wait for completion. */ | 71 | /* Fullscreen view isn't ready yet. */ |
59 | wait_event(wq, raw3270_request_final(rq)); | 72 | rc = wait_event_interruptible(fp->wait, |
60 | return rq->rc; | 73 | fs3270_working(fp)); |
74 | if (rc != 0) | ||
75 | break; | ||
76 | } | ||
77 | rc = raw3270_start(view, rq); | ||
78 | if (rc == 0) { | ||
79 | /* Started sucessfully. Now wait for completion. */ | ||
80 | wait_event(fp->wait, raw3270_request_final(rq)); | ||
81 | } | ||
82 | } while (rc == -EACCES); | ||
83 | return rc; | ||
61 | } | 84 | } |
62 | 85 | ||
86 | /* | ||
87 | * Switch to the fullscreen view. | ||
88 | */ | ||
63 | static void | 89 | static void |
64 | fs3270_reset_callback(struct raw3270_request *rq, void *data) | 90 | fs3270_reset_callback(struct raw3270_request *rq, void *data) |
65 | { | 91 | { |
92 | struct fs3270 *fp; | ||
93 | |||
94 | fp = (struct fs3270 *) rq->view; | ||
66 | raw3270_request_reset(rq); | 95 | raw3270_request_reset(rq); |
96 | wake_up(&fp->wait); | ||
97 | } | ||
98 | |||
99 | static void | ||
100 | fs3270_restore_callback(struct raw3270_request *rq, void *data) | ||
101 | { | ||
102 | struct fs3270 *fp; | ||
103 | |||
104 | fp = (struct fs3270 *) rq->view; | ||
105 | if (rq->rc != 0 || rq->rescnt != 0) { | ||
106 | if (fp->fs_pid) | ||
107 | kill_proc(fp->fs_pid, SIGHUP, 1); | ||
108 | } | ||
109 | fp->rdbuf_size = 0; | ||
110 | raw3270_request_reset(rq); | ||
111 | wake_up(&fp->wait); | ||
67 | } | 112 | } |
68 | 113 | ||
69 | /* | ||
70 | * Switch to the fullscreen view. | ||
71 | */ | ||
72 | static int | 114 | static int |
73 | fs3270_activate(struct raw3270_view *view) | 115 | fs3270_activate(struct raw3270_view *view) |
74 | { | 116 | { |
75 | struct fs3270 *fp; | 117 | struct fs3270 *fp; |
118 | char *cp; | ||
119 | int rc; | ||
76 | 120 | ||
77 | fp = (struct fs3270 *) view; | 121 | fp = (struct fs3270 *) view; |
78 | raw3270_request_set_cmd(fp->clear, TC_EWRITEA); | 122 | |
79 | fp->clear->callback = fs3270_reset_callback; | 123 | /* If an old init command is still running just return. */ |
80 | return raw3270_start(view, fp->clear); | 124 | if (!raw3270_request_final(fp->init)) |
125 | return 0; | ||
126 | |||
127 | if (fp->rdbuf_size == 0) { | ||
128 | /* No saved buffer. Just clear the screen. */ | ||
129 | raw3270_request_set_cmd(fp->init, TC_EWRITEA); | ||
130 | fp->init->callback = fs3270_reset_callback; | ||
131 | } else { | ||
132 | /* Restore fullscreen buffer saved by fs3270_deactivate. */ | ||
133 | raw3270_request_set_cmd(fp->init, TC_EWRITEA); | ||
134 | raw3270_request_set_idal(fp->init, fp->rdbuf); | ||
135 | fp->init->ccw.count = fp->rdbuf_size; | ||
136 | cp = fp->rdbuf->data[0]; | ||
137 | cp[0] = TW_KR; | ||
138 | cp[1] = TO_SBA; | ||
139 | cp[2] = cp[6]; | ||
140 | cp[3] = cp[7]; | ||
141 | cp[4] = TO_IC; | ||
142 | cp[5] = TO_SBA; | ||
143 | cp[6] = 0x40; | ||
144 | cp[7] = 0x40; | ||
145 | fp->init->rescnt = 0; | ||
146 | fp->init->callback = fs3270_restore_callback; | ||
147 | } | ||
148 | rc = fp->init->rc = raw3270_start_locked(view, fp->init); | ||
149 | if (rc) | ||
150 | fp->init->callback(fp->init, NULL); | ||
151 | else | ||
152 | fp->active = 1; | ||
153 | return rc; | ||
81 | } | 154 | } |
82 | 155 | ||
83 | /* | 156 | /* |
84 | * Shutdown fullscreen view. | 157 | * Shutdown fullscreen view. |
85 | */ | 158 | */ |
86 | static void | 159 | static void |
160 | fs3270_save_callback(struct raw3270_request *rq, void *data) | ||
161 | { | ||
162 | struct fs3270 *fp; | ||
163 | |||
164 | fp = (struct fs3270 *) rq->view; | ||
165 | |||
166 | /* Correct idal buffer element 0 address. */ | ||
167 | fp->rdbuf->data[0] -= 5; | ||
168 | fp->rdbuf->size += 5; | ||
169 | |||
170 | /* | ||
171 | * If the rdbuf command failed or the idal buffer is | ||
172 | * to small for the amount of data returned by the | ||
173 | * rdbuf command, then we have no choice but to send | ||
174 | * a SIGHUP to the application. | ||
175 | */ | ||
176 | if (rq->rc != 0 || rq->rescnt == 0) { | ||
177 | if (fp->fs_pid) | ||
178 | kill_proc(fp->fs_pid, SIGHUP, 1); | ||
179 | fp->rdbuf_size = 0; | ||
180 | } else | ||
181 | fp->rdbuf_size = fp->rdbuf->size - rq->rescnt; | ||
182 | raw3270_request_reset(rq); | ||
183 | wake_up(&fp->wait); | ||
184 | } | ||
185 | |||
186 | static void | ||
87 | fs3270_deactivate(struct raw3270_view *view) | 187 | fs3270_deactivate(struct raw3270_view *view) |
88 | { | 188 | { |
89 | // FIXME: is this a good idea? The user program using fullscreen 3270 | ||
90 | // will die just because a console message appeared. On the other | ||
91 | // hand the fullscreen device is unoperational now. | ||
92 | struct fs3270 *fp; | 189 | struct fs3270 *fp; |
93 | 190 | ||
94 | fp = (struct fs3270 *) view; | 191 | fp = (struct fs3270 *) view; |
95 | if (fp->fs_pid != 0) | 192 | fp->active = 0; |
96 | kill_proc(fp->fs_pid, SIGHUP, 1); | 193 | |
97 | fp->fs_pid = 0; | 194 | /* If an old init command is still running just return. */ |
195 | if (!raw3270_request_final(fp->init)) | ||
196 | return; | ||
197 | |||
198 | /* Prepare read-buffer request. */ | ||
199 | raw3270_request_set_cmd(fp->init, TC_RDBUF); | ||
200 | /* | ||
201 | * Hackish: skip first 5 bytes of the idal buffer to make | ||
202 | * room for the TW_KR/TO_SBA/<address>/<address>/TO_IC sequence | ||
203 | * in the activation command. | ||
204 | */ | ||
205 | fp->rdbuf->data[0] += 5; | ||
206 | fp->rdbuf->size -= 5; | ||
207 | raw3270_request_set_idal(fp->init, fp->rdbuf); | ||
208 | fp->init->rescnt = 0; | ||
209 | fp->init->callback = fs3270_save_callback; | ||
210 | |||
211 | /* Start I/O to read in the 3270 buffer. */ | ||
212 | fp->init->rc = raw3270_start_locked(view, fp->init); | ||
213 | if (fp->init->rc) | ||
214 | fp->init->callback(fp->init, NULL); | ||
98 | } | 215 | } |
99 | 216 | ||
100 | static int | 217 | static int |
@@ -103,7 +220,7 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) | |||
103 | /* Handle ATTN. Set indication and wake waiters for attention. */ | 220 | /* Handle ATTN. Set indication and wake waiters for attention. */ |
104 | if (irb->scsw.dstat & DEV_STAT_ATTENTION) { | 221 | if (irb->scsw.dstat & DEV_STAT_ATTENTION) { |
105 | fp->attention = 1; | 222 | fp->attention = 1; |
106 | wake_up(&fp->attn_wait); | 223 | wake_up(&fp->wait); |
107 | } | 224 | } |
108 | 225 | ||
109 | if (rq) { | 226 | if (rq) { |
@@ -125,7 +242,7 @@ fs3270_read(struct file *filp, char *data, size_t count, loff_t *off) | |||
125 | struct fs3270 *fp; | 242 | struct fs3270 *fp; |
126 | struct raw3270_request *rq; | 243 | struct raw3270_request *rq; |
127 | struct idal_buffer *ib; | 244 | struct idal_buffer *ib; |
128 | int rc; | 245 | ssize_t rc; |
129 | 246 | ||
130 | if (count == 0 || count > 65535) | 247 | if (count == 0 || count > 65535) |
131 | return -EINVAL; | 248 | return -EINVAL; |
@@ -133,7 +250,7 @@ fs3270_read(struct file *filp, char *data, size_t count, loff_t *off) | |||
133 | if (!fp) | 250 | if (!fp) |
134 | return -ENODEV; | 251 | return -ENODEV; |
135 | ib = idal_buffer_alloc(count, 0); | 252 | ib = idal_buffer_alloc(count, 0); |
136 | if (!ib) | 253 | if (IS_ERR(ib)) |
137 | return -ENOMEM; | 254 | return -ENOMEM; |
138 | rq = raw3270_request_alloc(0); | 255 | rq = raw3270_request_alloc(0); |
139 | if (!IS_ERR(rq)) { | 256 | if (!IS_ERR(rq)) { |
@@ -141,10 +258,19 @@ fs3270_read(struct file *filp, char *data, size_t count, loff_t *off) | |||
141 | fp->read_command = 6; | 258 | fp->read_command = 6; |
142 | raw3270_request_set_cmd(rq, fp->read_command ? : 2); | 259 | raw3270_request_set_cmd(rq, fp->read_command ? : 2); |
143 | raw3270_request_set_idal(rq, ib); | 260 | raw3270_request_set_idal(rq, ib); |
144 | wait_event(fp->attn_wait, fp->attention); | 261 | rc = wait_event_interruptible(fp->wait, fp->attention); |
145 | rc = fs3270_do_io(&fp->view, rq); | 262 | fp->attention = 0; |
146 | if (rc == 0 && idal_buffer_to_user(ib, data, count)) | 263 | if (rc == 0) { |
147 | rc = -EFAULT; | 264 | rc = fs3270_do_io(&fp->view, rq); |
265 | if (rc == 0) { | ||
266 | count -= rq->rescnt; | ||
267 | if (idal_buffer_to_user(ib, data, count) != 0) | ||
268 | rc = -EFAULT; | ||
269 | else | ||
270 | rc = count; | ||
271 | |||
272 | } | ||
273 | } | ||
148 | raw3270_request_free(rq); | 274 | raw3270_request_free(rq); |
149 | } else | 275 | } else |
150 | rc = PTR_ERR(rq); | 276 | rc = PTR_ERR(rq); |
@@ -162,13 +288,13 @@ fs3270_write(struct file *filp, const char *data, size_t count, loff_t *off) | |||
162 | struct raw3270_request *rq; | 288 | struct raw3270_request *rq; |
163 | struct idal_buffer *ib; | 289 | struct idal_buffer *ib; |
164 | int write_command; | 290 | int write_command; |
165 | int rc; | 291 | ssize_t rc; |
166 | 292 | ||
167 | fp = filp->private_data; | 293 | fp = filp->private_data; |
168 | if (!fp) | 294 | if (!fp) |
169 | return -ENODEV; | 295 | return -ENODEV; |
170 | ib = idal_buffer_alloc(count, 0); | 296 | ib = idal_buffer_alloc(count, 0); |
171 | if (!ib) | 297 | if (IS_ERR(ib)) |
172 | return -ENOMEM; | 298 | return -ENOMEM; |
173 | rq = raw3270_request_alloc(0); | 299 | rq = raw3270_request_alloc(0); |
174 | if (!IS_ERR(rq)) { | 300 | if (!IS_ERR(rq)) { |
@@ -179,6 +305,8 @@ fs3270_write(struct file *filp, const char *data, size_t count, loff_t *off) | |||
179 | raw3270_request_set_cmd(rq, write_command); | 305 | raw3270_request_set_cmd(rq, write_command); |
180 | raw3270_request_set_idal(rq, ib); | 306 | raw3270_request_set_idal(rq, ib); |
181 | rc = fs3270_do_io(&fp->view, rq); | 307 | rc = fs3270_do_io(&fp->view, rq); |
308 | if (rc == 0) | ||
309 | rc = count - rq->rescnt; | ||
182 | } else | 310 | } else |
183 | rc = -EFAULT; | 311 | rc = -EFAULT; |
184 | raw3270_request_free(rq); | 312 | raw3270_request_free(rq); |
@@ -232,7 +360,7 @@ fs3270_ioctl(struct inode *inode, struct file *filp, | |||
232 | } | 360 | } |
233 | 361 | ||
234 | /* | 362 | /* |
235 | * Allocate tty3270 structure. | 363 | * Allocate fs3270 structure. |
236 | */ | 364 | */ |
237 | static struct fs3270 * | 365 | static struct fs3270 * |
238 | fs3270_alloc_view(void) | 366 | fs3270_alloc_view(void) |
@@ -243,8 +371,8 @@ fs3270_alloc_view(void) | |||
243 | if (!fp) | 371 | if (!fp) |
244 | return ERR_PTR(-ENOMEM); | 372 | return ERR_PTR(-ENOMEM); |
245 | memset(fp, 0, sizeof(struct fs3270)); | 373 | memset(fp, 0, sizeof(struct fs3270)); |
246 | fp->clear = raw3270_request_alloc(0); | 374 | fp->init = raw3270_request_alloc(0); |
247 | if (!IS_ERR(fp->clear)) { | 375 | if (IS_ERR(fp->init)) { |
248 | kfree(fp); | 376 | kfree(fp); |
249 | return ERR_PTR(-ENOMEM); | 377 | return ERR_PTR(-ENOMEM); |
250 | } | 378 | } |
@@ -252,12 +380,17 @@ fs3270_alloc_view(void) | |||
252 | } | 380 | } |
253 | 381 | ||
254 | /* | 382 | /* |
255 | * Free tty3270 structure. | 383 | * Free fs3270 structure. |
256 | */ | 384 | */ |
257 | static void | 385 | static void |
258 | fs3270_free_view(struct raw3270_view *view) | 386 | fs3270_free_view(struct raw3270_view *view) |
259 | { | 387 | { |
260 | raw3270_request_free(((struct fs3270 *) view)->clear); | 388 | struct fs3270 *fp; |
389 | |||
390 | fp = (struct fs3270 *) view; | ||
391 | if (fp->rdbuf) | ||
392 | idal_buffer_free(fp->rdbuf); | ||
393 | raw3270_request_free(((struct fs3270 *) view)->init); | ||
261 | kfree(view); | 394 | kfree(view); |
262 | } | 395 | } |
263 | 396 | ||
@@ -285,11 +418,20 @@ static int | |||
285 | fs3270_open(struct inode *inode, struct file *filp) | 418 | fs3270_open(struct inode *inode, struct file *filp) |
286 | { | 419 | { |
287 | struct fs3270 *fp; | 420 | struct fs3270 *fp; |
421 | struct idal_buffer *ib; | ||
288 | int minor, rc; | 422 | int minor, rc; |
289 | 423 | ||
290 | if (imajor(filp->f_dentry->d_inode) != IBM_FS3270_MAJOR) | 424 | if (imajor(filp->f_dentry->d_inode) != IBM_FS3270_MAJOR) |
291 | return -ENODEV; | 425 | return -ENODEV; |
292 | minor = iminor(filp->f_dentry->d_inode); | 426 | minor = iminor(filp->f_dentry->d_inode); |
427 | /* Check for minor 0 multiplexer. */ | ||
428 | if (minor == 0) { | ||
429 | if (!current->signal->tty) | ||
430 | return -ENODEV; | ||
431 | if (current->signal->tty->driver->major != IBM_TTY3270_MAJOR) | ||
432 | return -ENODEV; | ||
433 | minor = current->signal->tty->index + RAW3270_FIRSTMINOR; | ||
434 | } | ||
293 | /* Check if some other program is already using fullscreen mode. */ | 435 | /* Check if some other program is already using fullscreen mode. */ |
294 | fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); | 436 | fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); |
295 | if (!IS_ERR(fp)) { | 437 | if (!IS_ERR(fp)) { |
@@ -301,7 +443,7 @@ fs3270_open(struct inode *inode, struct file *filp) | |||
301 | if (IS_ERR(fp)) | 443 | if (IS_ERR(fp)) |
302 | return PTR_ERR(fp); | 444 | return PTR_ERR(fp); |
303 | 445 | ||
304 | init_waitqueue_head(&fp->attn_wait); | 446 | init_waitqueue_head(&fp->wait); |
305 | fp->fs_pid = current->pid; | 447 | fp->fs_pid = current->pid; |
306 | rc = raw3270_add_view(&fp->view, &fs3270_fn, minor); | 448 | rc = raw3270_add_view(&fp->view, &fs3270_fn, minor); |
307 | if (rc) { | 449 | if (rc) { |
@@ -309,8 +451,18 @@ fs3270_open(struct inode *inode, struct file *filp) | |||
309 | return rc; | 451 | return rc; |
310 | } | 452 | } |
311 | 453 | ||
454 | /* Allocate idal-buffer. */ | ||
455 | ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0); | ||
456 | if (IS_ERR(ib)) { | ||
457 | raw3270_put_view(&fp->view); | ||
458 | raw3270_del_view(&fp->view); | ||
459 | return PTR_ERR(fp); | ||
460 | } | ||
461 | fp->rdbuf = ib; | ||
462 | |||
312 | rc = raw3270_activate_view(&fp->view); | 463 | rc = raw3270_activate_view(&fp->view); |
313 | if (rc) { | 464 | if (rc) { |
465 | raw3270_put_view(&fp->view); | ||
314 | raw3270_del_view(&fp->view); | 466 | raw3270_del_view(&fp->view); |
315 | return rc; | 467 | return rc; |
316 | } | 468 | } |
@@ -329,8 +481,12 @@ fs3270_close(struct inode *inode, struct file *filp) | |||
329 | 481 | ||
330 | fp = filp->private_data; | 482 | fp = filp->private_data; |
331 | filp->private_data = 0; | 483 | filp->private_data = 0; |
332 | if (fp) | 484 | if (fp) { |
485 | fp->fs_pid = 0; | ||
486 | raw3270_reset(&fp->view); | ||
487 | raw3270_put_view(&fp->view); | ||
333 | raw3270_del_view(&fp->view); | 488 | raw3270_del_view(&fp->view); |
489 | } | ||
334 | return 0; | 490 | return 0; |
335 | } | 491 | } |
336 | 492 | ||