added mtd driver
[linux-2.4.git] / drivers / s390 / char / tubfs.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }