1 /*
2  *  IBM/3270 Driver -- Copyright (C) UTS Global LLC
3  *
4  *  tubfs.c -- Fullscreen driver
5  *
6  *
7  *
8  *
9  *
10  *  Author:  Richard Hitt
11  */
12 #include "tubio.h"
13 
14 int fs3270_major = -1;			/* init to impossible -1 */
15 
16 static int fs3270_open(struct inode *, struct file *);
17 static int fs3270_close(struct inode *, struct file *);
18 static int fs3270_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
19 static ssize_t fs3270_read(struct file *, char *, size_t, loff_t *);
20 static ssize_t fs3270_write(struct file *, const char *, size_t, loff_t *);
21 static int fs3270_wait(tub_t *, long *);
22 static void fs3270_int(tub_t *tubp, devstat_t *dsp);
23 extern void tty3270_refresh(tub_t *);
24 
25 static struct file_operations fs3270_fops = {
26 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
27 	owner: THIS_MODULE,		/* owner */
28 #endif
29 	read: 	fs3270_read,	/* read */
30 	write:	fs3270_write,	/* write */
31 	ioctl:	fs3270_ioctl,	/* ioctl */
32 	open: 	fs3270_open,	/* open */
33 	release:fs3270_close,	/* release */
34 };
35 
36 #ifdef CONFIG_DEVFS_FS
37 devfs_handle_t fs3270_devfs_dir;
38 devfs_handle_t fs3270_devfs_tub;
39 extern struct file_operations tty_fops;
40 
fs3270_devfs_register(tub_t * tubp)41 void fs3270_devfs_register(tub_t *tubp)
42 {
43 	char name[16];
44 
45 	sprintf(name, "tub%.4x", tubp->devno);
46 	devfs_register(fs3270_devfs_dir, name, DEVFS_FL_DEFAULT,
47 		       IBM_FS3270_MAJOR, tubp->minor,
48 		       S_IFCHR | S_IRUSR | S_IWUSR, &fs3270_fops, NULL);
49 	sprintf(name, "tty%.4x", tubp->devno);
50 	tty_register_devfs_name(&tty3270_driver, 0, tubp->minor,
51 				fs3270_devfs_dir, name);
52 }
53 
fs3270_devfs_unregister(tub_t * tubp)54 void fs3270_devfs_unregister(tub_t *tubp)
55 {
56 	char name[16];
57 	devfs_handle_t handle;
58 
59 	sprintf(name, "tub%.4x", tubp->devno);
60 	handle = devfs_find_handle (fs3270_devfs_dir, name,
61 				    IBM_FS3270_MAJOR, tubp->minor,
62 				    DEVFS_SPECIAL_CHR, 0);
63 	devfs_unregister (handle);
64 	sprintf(name, "tty%.4x", tubp->devno);
65 	handle = devfs_find_handle (fs3270_devfs_dir, name,
66 				    IBM_TTY3270_MAJOR, tubp->minor,
67 				    DEVFS_SPECIAL_CHR, 0);
68 	devfs_unregister(handle);
69 }
70 #endif
71 
72 /*
73  * fs3270_init() -- Initialize fullscreen tubes
74  */
75 int
fs3270_init(void)76 fs3270_init(void)
77 {
78 	int rc;
79 
80 #ifdef CONFIG_DEVFS_FS
81 	rc = devfs_register_chrdev (IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
82 	if (rc) {
83 		printk(KERN_ERR "tubmod can't get major nbr %d: error %d\n",
84 			IBM_FS3270_MAJOR, rc);
85 		return -1;
86 	}
87 	fs3270_devfs_dir = devfs_mk_dir(NULL, "3270", NULL);
88 	fs3270_devfs_tub =
89 		devfs_register(fs3270_devfs_dir, "tub", DEVFS_FL_DEFAULT,
90 			       IBM_FS3270_MAJOR, 0,
91 			       S_IFCHR | S_IRUGO | S_IWUGO,
92 			       &fs3270_fops, NULL);
93 #else
94 	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
95 	if (rc) {
96 		printk(KERN_ERR "tubmod can't get major nbr %d: error %d\n",
97 			IBM_FS3270_MAJOR, rc);
98 		return -1;
99 	}
100 #endif
101 	fs3270_major = IBM_FS3270_MAJOR;
102 	return 0;
103 }
104 
105 /*
106  * fs3270_fini() -- Uninitialize fullscreen tubes
107  */
108 void
fs3270_fini(void)109 fs3270_fini(void)
110 {
111 	if (fs3270_major != -1) {
112 #ifdef CONFIG_DEVFS_FS
113 		devfs_unregister(fs3270_devfs_tub);
114 		devfs_unregister(fs3270_devfs_dir);
115 #endif
116 		unregister_chrdev(fs3270_major, "fs3270");
117 		fs3270_major = -1;
118 	}
119 }
120 
121 /*
122  * fs3270_open
123  */
124 static int
fs3270_open(struct inode * ip,struct file * fp)125 fs3270_open(struct inode *ip, struct file *fp)
126 {
127 	tub_t *tubp;
128 	long flags;
129 
130 	/* See INODE2TUB(ip) for handling of "/dev/3270/tub" */
131 	if ((tubp = INODE2TUB(ip)) == NULL)
132 		return -ENOENT;
133 
134 	TUBLOCK(tubp->irq, flags);
135 	if (tubp->mode == TBM_FS || tubp->mode == TBM_FSLN) {
136 		TUBUNLOCK(tubp->irq, flags);
137 		return -EBUSY;
138 	}
139 
140 	tub_inc_use_count();
141 	fp->private_data = ip;
142 	tubp->mode = TBM_FS;
143 	tubp->intv = fs3270_int;
144 	tubp->dstat = 0;
145 	tubp->fs_pid = current->pid;
146 	tubp->fsopen = 1;
147 	TUBUNLOCK(tubp->irq, flags);
148 	return 0;
149 }
150 
151 /*
152  * fs3270_close aka release:  free the irq
153  */
154 static int
fs3270_close(struct inode * ip,struct file * fp)155 fs3270_close(struct inode *ip, struct file *fp)
156 {
157 	tub_t *tubp;
158 	long flags;
159 
160 	if ((tubp = INODE2TUB(ip)) == NULL)
161 		return -ENODEV;
162 
163 	fs3270_wait(tubp, &flags);
164 	tubp->fsopen = 0;
165 	tubp->fs_pid = 0;
166 	tub_dec_use_count();
167 	tubp->intv = NULL;
168 	tubp->mode = 0;
169 	tty3270_refresh(tubp);
170 	TUBUNLOCK(tubp->irq, flags);
171 	return 0;
172 }
173 
174 /*
175  * fs3270_release() called from tty3270_hangup()
176  */
177 void
fs3270_release(tub_t * tubp)178 fs3270_release(tub_t *tubp)
179 {
180 	long flags;
181 
182 	if (tubp->mode != TBM_FS)
183 		return;
184 	fs3270_wait(tubp, &flags);
185 	tubp->fsopen = 0;
186 	tubp->fs_pid = 0;
187 	tub_dec_use_count();
188 	tubp->intv = NULL;
189 	tubp->mode = 0;
190 	/*tty3270_refresh(tubp);*/
191 	TUBUNLOCK(tubp->irq, flags);
192 }
193 
194 /*
195  * fs3270_wait(tub_t *tubp, int *flags) -- Wait to use tube
196  * Entered without irq lock
197  * On return:
198  *      * Lock is held
199  *      * Value is 0 or -ERESTARTSYS
200  */
201 static int
fs3270_wait(tub_t * tubp,long * flags)202 fs3270_wait(tub_t *tubp, long *flags)
203 {
204 	DECLARE_WAITQUEUE(wait, current);
205 
206 	TUBLOCK(tubp->irq, *flags);
207 	add_wait_queue(&tubp->waitq, &wait);
208 	while (!signal_pending(current) &&
209 	    ((tubp->mode != TBM_FS) ||
210 	     (tubp->flags & (TUB_WORKING | TUB_RDPENDING)) != 0)) {
211 		current->state = TASK_INTERRUPTIBLE;
212 		TUBUNLOCK(tubp->irq, *flags);
213 		schedule();
214 		current->state = TASK_RUNNING;
215 		TUBLOCK(tubp->irq, *flags);
216 	}
217 	remove_wait_queue(&tubp->waitq, &wait);
218 	return signal_pending(current)? -ERESTARTSYS: 0;
219 }
220 
221 /*
222  * fs3270_io(tubp, ccw1_t*) -- start I/O on the tube
223  * Entered with irq lock held, WORKING off
224  */
225 static int
fs3270_io(tub_t * tubp,ccw1_t * ccwp)226 fs3270_io(tub_t *tubp, ccw1_t *ccwp)
227 {
228 	int rc;
229 
230 	rc = do_IO(tubp->irq, ccwp, tubp->irq, 0, 0);
231 	tubp->flags |= TUB_WORKING;
232 	tubp->dstat = 0;
233 	return rc;
234 }
235 
236 /*
237  * fs3270_bh(tubp) -- Perform back-half processing
238  */
239 static void
fs3270_bh(void * data)240 fs3270_bh(void *data)
241 {
242 	long flags;
243 	tub_t *tubp;
244 
245 	tubp = data;
246 	TUBLOCK(tubp->irq, flags);
247 	tubp->flags &= ~TUB_BHPENDING;
248 
249 	if (tubp->wbuf) {       /* if we were writing */
250 		idal_buffer_free(tubp->wbuf);
251 		tubp->wbuf = NULL;
252 	}
253 
254 	if ((tubp->flags & (TUB_ATTN | TUB_RDPENDING)) ==
255 	    (TUB_ATTN | TUB_RDPENDING)) {
256 		fs3270_io(tubp, &tubp->rccw);
257 		tubp->flags &= ~(TUB_ATTN | TUB_RDPENDING);
258 	}
259 
260 	if ((tubp->flags & TUB_WORKING) == 0)
261 		wake_up_interruptible(&tubp->waitq);
262 
263 	TUBUNLOCK(tubp->irq, flags);
264 }
265 
266 /*
267  * fs3270_sched_bh(tubp) -- Schedule the back half
268  * Irq lock must be held on entry and remains held on exit.
269  */
270 static void
fs3270_sched_bh(tub_t * tubp)271 fs3270_sched_bh(tub_t *tubp)
272 {
273 	if (tubp->flags & TUB_BHPENDING)
274 		return;
275 	tubp->flags |= TUB_BHPENDING;
276 	tubp->tqueue.routine = fs3270_bh;
277 	tubp->tqueue.data = tubp;
278 	queue_task(&tubp->tqueue, &tq_immediate);
279 	mark_bh(IMMEDIATE_BH);
280 }
281 
282 /*
283  * fs3270_int(tubp, prp) -- Process interrupt from tube in FS mode
284  * This routine is entered with irq lock held (see do_IRQ in s390io.c)
285  */
286 static void
fs3270_int(tub_t * tubp,devstat_t * dsp)287 fs3270_int(tub_t *tubp, devstat_t *dsp)
288 {
289 #define	DEV_UE_BUSY \
290 	(DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)
291 
292 #ifdef RBHNOTYET
293 	/* XXX needs more work; must save 2d arg to fs370_io() */
294 	/* Handle CE-DE-UE and subsequent UDE */
295 	if (dsp->dstat == DEV_UE_BUSY) {
296 		tubp->flags |= TUB_UE_BUSY;
297 		return;
298 	} else if (tubp->flags & TUB_UE_BUSY) {
299 		tubp->flags &= ~TUB_UE_BUSY;
300 		if (dsp->dstat == DEV_STAT_DEV_END &&
301 		    (tubp->flags & TUB_WORKING) != 0) {
302 			fs3270_io(tubp);
303 			return;
304 		}
305 	}
306 #endif
307 
308 	/* Handle ATTN */
309 	if (dsp->dstat & DEV_STAT_ATTENTION)
310 		tubp->flags |= TUB_ATTN;
311 
312 	if (dsp->dstat & DEV_STAT_CHN_END) {
313 		tubp->cswl = dsp->rescnt;
314 		if ((dsp->dstat & DEV_STAT_DEV_END) == 0)
315 			tubp->flags |= TUB_EXPECT_DE;
316 		else
317 			tubp->flags &= ~TUB_EXPECT_DE;
318 	} else if (dsp->dstat & DEV_STAT_DEV_END) {
319 		if ((tubp->flags & TUB_EXPECT_DE) == 0)
320 			tubp->flags |= TUB_UNSOL_DE;
321 		tubp->flags &= ~TUB_EXPECT_DE;
322 	}
323 	if (dsp->dstat & DEV_STAT_DEV_END)
324 		tubp->flags &= ~TUB_WORKING;
325 
326 	if ((tubp->flags & TUB_WORKING) == 0)
327 		fs3270_sched_bh(tubp);
328 }
329 
330 /*
331  * process ioctl commands for the tube driver
332  */
333 static int
fs3270_ioctl(struct inode * ip,struct file * fp,unsigned int cmd,unsigned long arg)334 fs3270_ioctl(struct inode *ip, struct file *fp,
335 	unsigned int cmd, unsigned long arg)
336 {
337 	tub_t *tubp;
338 	int rc = 0;
339 	long flags;
340 
341 	if ((tubp = INODE2TUB(ip)) == NULL)
342 		return -ENODEV;
343 	if ((rc = fs3270_wait(tubp, &flags))) {
344 		TUBUNLOCK(tubp->irq, flags);
345 		return rc;
346 	}
347 
348 	switch(cmd) {
349 	case TUBICMD: tubp->icmd = arg; break;
350 	case TUBOCMD: tubp->ocmd = arg; break;
351 	case TUBGETI: put_user(tubp->icmd, (char *)arg); break;
352 	case TUBGETO: put_user(tubp->ocmd, (char *)arg); break;
353 	case TUBGETMOD:
354 		if (copy_to_user((char *)arg, &tubp->tubiocb,
355 		    sizeof tubp->tubiocb))
356 			rc = -EFAULT;
357 		break;
358 	}
359 	TUBUNLOCK(tubp->irq, flags);
360 	return rc;
361 }
362 
363 /*
364  * process read commands for the tube driver
365  */
366 static ssize_t
fs3270_read(struct file * fp,char * dp,size_t len,loff_t * off)367 fs3270_read(struct file *fp, char *dp, size_t len, loff_t *off)
368 {
369 	tub_t *tubp;
370 	ccw1_t *cp;
371 	int rc;
372 	long flags;
373 	struct idal_buffer *idal_buffer;
374 
375 	if (len == 0 || len > 65535) {
376 		return -EINVAL;
377 	}
378 
379 	if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL)
380 		return -ENODEV;
381 
382 	if((idal_buffer = idal_buffer_alloc(len, 0)) == NULL) {
383 		len = -ENOMEM;
384 		goto do_cleanup;
385 	}
386 
387 	if ((rc = fs3270_wait(tubp, &flags)) != 0) {
388 		TUBUNLOCK(tubp->irq, flags);
389 		len = rc;
390 		goto do_cleanup;
391 	}
392 	cp = &tubp->rccw;
393 	if (tubp->icmd == 0 && tubp->ocmd != 0)  tubp->icmd = 6;
394 	cp->cmd_code = tubp->icmd?:2;
395 	idal_buffer_set_cda(idal_buffer, cp);
396 	cp->flags |= CCW_FLAG_SLI;
397 	tubp->flags |= TUB_RDPENDING;
398 	TUBUNLOCK(tubp->irq, flags);
399 
400 	if ((rc = fs3270_wait(tubp, &flags)) != 0) {
401 		tubp->flags &= ~TUB_RDPENDING;
402 		len = rc;
403 		TUBUNLOCK(tubp->irq, flags);
404 		goto do_cleanup;
405 	}
406 	TUBUNLOCK(tubp->irq, flags);
407 
408 	len -= tubp->cswl;
409 	if (idal_buffer_to_user(idal_buffer, dp, len) != 0) {
410 		len = -EFAULT;
411 		goto do_cleanup;
412 	}
413 
414 do_cleanup:
415 	idal_buffer_free(idal_buffer);
416 
417 	return len;
418 }
419 
420 /*
421  * process write commands for the tube driver
422  */
423 static ssize_t
fs3270_write(struct file * fp,const char * dp,size_t len,loff_t * off)424 fs3270_write(struct file *fp, const char *dp, size_t len, loff_t *off)
425 {
426 	tub_t *tubp;
427 	ccw1_t *cp;
428 	int rc;
429 	long flags;
430 	struct idal_buffer *idal_buffer;
431 
432 	if (len > 65535 || len == 0)
433 		return -EINVAL;
434 
435 	/* Locate the tube */
436 	if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL)
437 		return -ENODEV;
438 
439 	if ((idal_buffer = idal_buffer_alloc(len, 0)) == NULL)
440 		return -ENOMEM;
441 
442 	if (idal_buffer_from_user(idal_buffer, dp, len) != 0) {
443 		len = -EFAULT;
444 		goto do_cleanup;
445 	}
446 
447 	/* Wait till tube's not working or signal is pending */
448 	if ((rc = fs3270_wait(tubp, &flags))) {
449 		len = rc;
450 		TUBUNLOCK(tubp->irq, flags);
451 		goto do_cleanup;
452 	}
453 
454 	/* Make CCW and start I/O.  Back end will free buffers & idal. */
455 	cp = &tubp->wccw;
456 	cp->cmd_code = tubp->ocmd? tubp->ocmd == 5? 13: tubp->ocmd: 1;
457 	tubp->wbuf = idal_buffer;
458 	idal_buffer_set_cda(idal_buffer, cp);
459 	cp->flags |= CCW_FLAG_SLI;
460 	fs3270_io(tubp, cp);
461 	TUBUNLOCK(tubp->irq, flags);
462 
463 	return len;
464 
465 do_cleanup:
466 	idal_buffer_free(idal_buffer);
467 
468 	return len;
469 }
470