5a108e7cfa1673792494bbeba79b8ad20c8f1f58
[dragonfly.git] / sys / dev / misc / snp / snp.c
1 /*
2  * Copyright (c) 1995 Ugen J.S.Antsilevich
3  *
4  * Redistribution and use in source forms, with and without modification,
5  * are permitted provided that this entire comment appears intact.
6  *
7  * Redistribution in binary form may occur without any restrictions.
8  * Obviously, it would be nice if you gave credit where credit is due
9  * but requiring it would be too onerous.
10  *
11  * This software is provided ``AS IS'' without any warranties of any kind.
12  *
13  * Snoop stuff.
14  *
15  * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $
16  * $DragonFly: src/sys/dev/misc/snp/snp.c,v 1.19 2007/05/08 02:31:41 dillon Exp $
17  */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/filio.h>
22 #include <sys/malloc.h>
23 #include <sys/tty.h>
24 #include <sys/conf.h>
25 #include <sys/poll.h>
26 #include <sys/kernel.h>
27 #include <sys/queue.h>
28 #include <sys/snoop.h>
29 #include <sys/thread2.h>
30 #include <sys/vnode.h>
31 #include <sys/device.h>
32 #include <sys/devfs.h>
33
34 static  l_close_t       snplclose;
35 static  l_write_t       snplwrite;
36 static  d_open_t        snpopen;
37 static  d_close_t       snpclose;
38 static  d_read_t        snpread;
39 static  d_write_t       snpwrite;
40 static  d_ioctl_t       snpioctl;
41 static  d_poll_t        snppoll;
42 static d_clone_t        snpclone;
43 DEVFS_DECLARE_CLONE_BITMAP(snp);
44 #define SNP_PREALLOCATED_UNITS  4
45
46 #define CDEV_MAJOR 53
47 static struct dev_ops snp_ops = {
48         { "snp", CDEV_MAJOR, 0 },
49         .d_open =       snpopen,
50         .d_close =      snpclose,
51         .d_read =       snpread,
52         .d_write =      snpwrite,
53         .d_ioctl =      snpioctl,
54         .d_poll =       snppoll,
55 };
56
57 static struct linesw snpdisc = {
58         ttyopen,        snplclose,      ttread,         snplwrite,
59         l_nullioctl,    ttyinput,       ttstart,        ttymodem
60 };
61
62 /*
63  * This is the main snoop per-device structure.
64  */
65 struct snoop {
66         LIST_ENTRY(snoop)       snp_list;       /* List glue. */
67         cdev_t                  snp_target;     /* Target tty device. */
68         struct tty              *snp_tty;       /* Target tty pointer. */
69         u_long                   snp_len;       /* Possible length. */
70         u_long                   snp_base;      /* Data base. */
71         u_long                   snp_blen;      /* Used length. */
72         caddr_t                  snp_buf;       /* Allocation pointer. */
73         int                      snp_flags;     /* Flags. */
74         struct selinfo           snp_sel;       /* Select info. */
75         int                      snp_olddisc;   /* Old line discipline. */
76 };
77
78 /*
79  * Possible flags.
80  */
81 #define SNOOP_ASYNC             0x0002
82 #define SNOOP_OPEN              0x0004
83 #define SNOOP_RWAIT             0x0008
84 #define SNOOP_OFLOW             0x0010
85 #define SNOOP_DOWN              0x0020
86
87 /*
88  * Other constants.
89  */
90 #define SNOOP_MINLEN            (4*1024)        /* This should be power of 2.
91                                                  * 4K tested to be the minimum
92                                                  * for which on normal tty
93                                                  * usage there is no need to
94                                                  * allocate more.
95                                                  */
96 #define SNOOP_MAXLEN            (64*1024)       /* This one also,64K enough
97                                                  * If we grow more,something
98                                                  * really bad in this world..
99                                                  */
100
101 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
102 /*
103  * The number of the "snoop" line discipline.  This gets determined at
104  * module load time.
105  */
106 static int snooplinedisc;
107
108
109 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
110
111 static struct tty       *snpdevtotty (cdev_t dev);
112 static int              snp_detach (struct snoop *snp);
113 static int              snp_down (struct snoop *snp);
114 static int              snp_in (struct snoop *snp, char *buf, int n);
115 static int              snp_modevent (module_t mod, int what, void *arg);
116
117 static int
118 snplclose(struct tty *tp, int flag)
119 {
120         struct snoop *snp;
121         int error;
122
123         snp = tp->t_sc;
124         error = snp_down(snp);
125         if (error != 0)
126                 return (error);
127         error = ttylclose(tp, flag);
128         return (error);
129 }
130
131 static int
132 snplwrite(struct tty *tp, struct uio *uio, int flag)
133 {
134         struct iovec iov;
135         struct uio uio2;
136         struct snoop *snp;
137         int error, ilen;
138         char *ibuf;
139
140         error = 0;
141         ibuf = NULL;
142         snp = tp->t_sc;
143         while (uio->uio_resid > 0) {
144                 ilen = imin(512, uio->uio_resid);
145                 ibuf = kmalloc(ilen, M_SNP, M_WAITOK);
146                 error = uiomove(ibuf, ilen, uio);
147                 if (error != 0)
148                         break;
149                 snp_in(snp, ibuf, ilen);
150                 /* Hackish, but probably the least of all evils. */
151                 iov.iov_base = ibuf;
152                 iov.iov_len = ilen;
153                 uio2.uio_iov = &iov;
154                 uio2.uio_iovcnt = 1;
155                 uio2.uio_offset = 0;
156                 uio2.uio_resid = ilen;
157                 uio2.uio_segflg = UIO_SYSSPACE;
158                 uio2.uio_rw = UIO_WRITE;
159                 uio2.uio_td = uio->uio_td;
160                 error = ttwrite(tp, &uio2, flag);
161                 if (error != 0)
162                         break;
163                 kfree(ibuf, M_SNP);
164                 ibuf = NULL;
165         }
166         if (ibuf != NULL)
167                 kfree(ibuf, M_SNP);
168         return (error);
169 }
170
171 static struct tty *
172 snpdevtotty(cdev_t dev)
173 {
174         if ((dev_dflags(dev) & D_TTY) == 0)
175                 return (NULL);
176         return (dev->si_tty);
177 }
178
179 #define SNP_INPUT_BUF   5       /* This is even too much, the maximal
180                                  * interactive mode write is 3 bytes
181                                  * length for function keys...
182                                  */
183
184 static int
185 snpwrite(struct dev_write_args *ap)
186 {
187         cdev_t dev = ap->a_head.a_dev;
188         struct uio *uio = ap->a_uio;
189         struct snoop *snp;
190         struct tty *tp;
191         int error, i, len;
192         unsigned char c[SNP_INPUT_BUF];
193
194         snp = dev->si_drv1;
195         tp = snp->snp_tty;
196         if (tp == NULL)
197                 return (EIO);
198         if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
199             tp->t_line == snooplinedisc)
200                 goto tty_input;
201
202         kprintf("Snoop: attempt to write to bad tty.\n");
203         return (EIO);
204
205 tty_input:
206         if (!(tp->t_state & TS_ISOPEN))
207                 return (EIO);
208
209         while (uio->uio_resid > 0) {
210                 len = imin(uio->uio_resid, SNP_INPUT_BUF);
211                 if ((error = uiomove(c, len, uio)) != 0)
212                         return (error);
213                 for (i=0; i < len; i++) {
214                         if (ttyinput(c[i], tp))
215                                 return (EIO);
216                 }
217         }
218         return (0);
219 }
220
221
222 static int
223 snpread(struct dev_read_args *ap)
224 {
225         cdev_t dev = ap->a_head.a_dev;
226         struct uio *uio = ap->a_uio;
227         struct snoop *snp;
228         int error, len, n, nblen;
229         caddr_t from;
230         char *nbuf;
231
232         snp = dev->si_drv1;
233         KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
234             ("snoop buffer error"));
235
236         if (snp->snp_tty == NULL)
237                 return (EIO);
238
239         snp->snp_flags &= ~SNOOP_RWAIT;
240
241         do {
242                 if (snp->snp_len == 0) {
243                         if (ap->a_ioflag & IO_NDELAY)
244                                 return (EWOULDBLOCK);
245                         snp->snp_flags |= SNOOP_RWAIT;
246                         error = tsleep((caddr_t)snp, PCATCH, "snprd", 0);
247                         if (error != 0)
248                                 return (error);
249                 }
250         } while (snp->snp_len == 0);
251
252         n = snp->snp_len;
253
254         error = 0;
255         while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
256                 len = min((unsigned)uio->uio_resid, snp->snp_len);
257                 from = (caddr_t)(snp->snp_buf + snp->snp_base);
258                 if (len == 0)
259                         break;
260
261                 error = uiomove(from, len, uio);
262                 snp->snp_base += len;
263                 snp->snp_len -= len;
264         }
265         if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
266                 snp->snp_flags &= ~SNOOP_OFLOW;
267         }
268         crit_enter();
269         nblen = snp->snp_blen;
270         if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
271                 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
272                         nblen = nblen / 2;
273                 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
274                         bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
275                         kfree(snp->snp_buf, M_SNP);
276                         snp->snp_buf = nbuf;
277                         snp->snp_blen = nblen;
278                         snp->snp_base = 0;
279                 }
280         }
281         crit_exit();
282
283         return (error);
284 }
285
286 static int
287 snp_in(struct snoop *snp, char *buf, int n)
288 {
289         int s_free, s_tail;
290         int len, nblen;
291         caddr_t from, to;
292         char *nbuf;
293
294         KASSERT(n >= 0, ("negative snoop char count"));
295
296         if (n == 0)
297                 return (0);
298
299         if (snp->snp_flags & SNOOP_DOWN) {
300                 kprintf("Snoop: more data to down interface.\n");
301                 return (0);
302         }
303
304         if (snp->snp_flags & SNOOP_OFLOW) {
305                 kprintf("Snoop: buffer overflow.\n");
306                 /*
307                  * On overflow we just repeat the standart close
308                  * procedure...yes , this is waste of space but.. Then next
309                  * read from device will fail if one would recall he is
310                  * snooping and retry...
311                  */
312
313                 return (snp_down(snp));
314         }
315         s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
316         s_free = snp->snp_blen - snp->snp_len;
317
318
319         if (n > s_free) {
320                 crit_enter();
321                 nblen = snp->snp_blen;
322                 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
323                         nblen = snp->snp_blen * 2;
324                         s_free = nblen - (snp->snp_len + snp->snp_base);
325                 }
326                 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) {
327                         bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
328                         kfree(snp->snp_buf, M_SNP);
329                         snp->snp_buf = nbuf;
330                         snp->snp_blen = nblen;
331                         snp->snp_base = 0;
332                 } else {
333                         snp->snp_flags |= SNOOP_OFLOW;
334                         if (snp->snp_flags & SNOOP_RWAIT) {
335                                 snp->snp_flags &= ~SNOOP_RWAIT;
336                                 wakeup((caddr_t)snp);
337                         }
338                         crit_exit();
339                         return (0);
340                 }
341                 crit_exit();
342         }
343         if (n > s_tail) {
344                 from = (caddr_t)(snp->snp_buf + snp->snp_base);
345                 to = (caddr_t)(snp->snp_buf);
346                 len = snp->snp_len;
347                 bcopy(from, to, len);
348                 snp->snp_base = 0;
349         }
350         to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
351         bcopy(buf, to, n);
352         snp->snp_len += n;
353
354         if (snp->snp_flags & SNOOP_RWAIT) {
355                 snp->snp_flags &= ~SNOOP_RWAIT;
356                 wakeup((caddr_t)snp);
357         }
358         selwakeup(&snp->snp_sel);
359         snp->snp_sel.si_pid = 0;
360
361         return (n);
362 }
363
364 static int
365 snpopen(struct dev_open_args *ap)
366 {
367         cdev_t dev = ap->a_head.a_dev;
368         struct snoop *snp;
369
370         if (dev->si_drv1 == NULL) {
371 #if 0
372                 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL,
373                     0600, "snp%d", minor(dev));
374 #endif
375                 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP,
376                     M_WAITOK | M_ZERO);
377         } else {
378                 return (EBUSY);
379         }
380
381         /*
382          * We intentionally do not OR flags with SNOOP_OPEN, but set them so
383          * all previous settings (especially SNOOP_OFLOW) will be cleared.
384          */
385         snp->snp_flags = SNOOP_OPEN;
386
387         snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
388         snp->snp_blen = SNOOP_MINLEN;
389         snp->snp_base = 0;
390         snp->snp_len = 0;
391
392         /*
393          * snp_tty == NULL  is for inactive snoop devices.
394          */
395         snp->snp_tty = NULL;
396         snp->snp_target = NULL;
397
398         LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
399         return (0);
400 }
401
402
403 static int
404 snp_detach(struct snoop *snp)
405 {
406         struct tty *tp;
407
408         snp->snp_base = 0;
409         snp->snp_len = 0;
410
411         /*
412          * If line disc. changed we do not touch this pointer, SLIP/PPP will
413          * change it anyway.
414          */
415         tp = snp->snp_tty;
416         if (tp == NULL)
417                 goto detach_notty;
418
419         if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
420             tp->t_line == snooplinedisc) {
421                 tp->t_sc = NULL;
422                 tp->t_state &= ~TS_SNOOP;
423                 tp->t_line = snp->snp_olddisc;
424         } else
425                 kprintf("Snoop: bad attached tty data.\n");
426
427         snp->snp_tty = NULL;
428         snp->snp_target = NULL;
429
430 detach_notty:
431         selwakeup(&snp->snp_sel);
432         snp->snp_sel.si_pid = 0;
433         if ((snp->snp_flags & SNOOP_OPEN) == 0) 
434                 kfree(snp, M_SNP);
435
436         return (0);
437 }
438
439 static int
440 snpclose(struct dev_close_args *ap)
441 {
442         cdev_t dev = ap->a_head.a_dev;
443         struct snoop *snp;
444
445         snp = dev->si_drv1;
446         snp->snp_blen = 0;
447         LIST_REMOVE(snp, snp_list);
448         kfree(snp->snp_buf, M_SNP);
449         snp->snp_flags &= ~SNOOP_OPEN;
450         dev->si_drv1 = NULL;
451         if (dev->si_uminor >= SNP_PREALLOCATED_UNITS) {
452                 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), dev->si_uminor);
453                 destroy_dev(dev);
454         }
455         return (snp_detach(snp));
456 }
457
458 static int
459 snp_down(struct snoop *snp)
460 {
461
462         if (snp->snp_blen != SNOOP_MINLEN) {
463                 kfree(snp->snp_buf, M_SNP);
464                 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
465                 snp->snp_blen = SNOOP_MINLEN;
466         }
467         snp->snp_flags |= SNOOP_DOWN;
468
469         return (snp_detach(snp));
470 }
471
472 static int
473 snpioctl(struct dev_ioctl_args *ap)
474 {
475         cdev_t dev = ap->a_head.a_dev;
476         struct snoop *snp;
477         struct tty *tp, *tpo;
478         cdev_t tdev;
479
480         snp = dev->si_drv1;
481         switch (ap->a_cmd) {
482         case SNPSTTY:
483                 tdev = udev2dev(*((udev_t *)ap->a_data), 0);
484                 if (tdev == NULL)
485                         return (snp_down(snp));
486
487                 tp = snpdevtotty(tdev);
488                 if (!tp)
489                         return (EINVAL);
490                 if (tp->t_state & TS_SNOOP)
491                         return (EBUSY);
492
493                 crit_enter();
494
495                 if (snp->snp_target == NULL) {
496                         tpo = snp->snp_tty;
497                         if (tpo)
498                                 tpo->t_state &= ~TS_SNOOP;
499                 }
500
501                 tp->t_sc = (caddr_t)snp;
502                 tp->t_state |= TS_SNOOP;
503                 snp->snp_olddisc = tp->t_line;
504                 tp->t_line = snooplinedisc;
505                 snp->snp_tty = tp;
506                 snp->snp_target = tdev;
507
508                 /*
509                  * Clean overflow and down flags -
510                  * we'll have a chance to get them in the future :)))
511                  */
512                 snp->snp_flags &= ~SNOOP_OFLOW;
513                 snp->snp_flags &= ~SNOOP_DOWN;
514                 crit_exit();
515                 break;
516
517         case SNPGTTY:
518                 /*
519                  * We keep snp_target field specially to make
520                  * SNPGTTY happy, else we can't know what is device
521                  * major/minor for tty.
522                  */
523                 *((cdev_t *)ap->a_data) = snp->snp_target;
524                 break;
525
526         case FIOASYNC:
527                 if (*(int *)ap->a_data)
528                         snp->snp_flags |= SNOOP_ASYNC;
529                 else
530                         snp->snp_flags &= ~SNOOP_ASYNC;
531                 break;
532
533         case FIONREAD:
534                 crit_enter();
535                 if (snp->snp_tty != NULL)
536                         *(int *)ap->a_data = snp->snp_len;
537                 else
538                         if (snp->snp_flags & SNOOP_DOWN) {
539                                 if (snp->snp_flags & SNOOP_OFLOW)
540                                         *(int *)ap->a_data = SNP_OFLOW;
541                                 else
542                                         *(int *)ap->a_data = SNP_TTYCLOSE;
543                         } else {
544                                 *(int *)ap->a_data = SNP_DETACH;
545                         }
546                 crit_exit();
547                 break;
548
549         default:
550                 return (ENOTTY);
551         }
552         return (0);
553 }
554
555 static int
556 snppoll(struct dev_poll_args *ap)
557 {
558         cdev_t dev = ap->a_head.a_dev;
559         struct snoop *snp;
560         int revents;
561
562         snp = dev->si_drv1;
563         revents = 0;
564         /*
565          * If snoop is down, we don't want to poll() forever so we return 1.
566          * Caller should see if we down via FIONREAD ioctl().  The last should
567          * return -1 to indicate down state.
568          */
569         if (ap->a_events & (POLLIN | POLLRDNORM)) {
570                 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
571                         revents |= ap->a_events & (POLLIN | POLLRDNORM);
572                 else
573                         selrecord(curthread, &snp->snp_sel);
574         }
575         ap->a_events = revents;
576         return (0);
577 }
578
579 static int
580 snpclone(struct dev_clone_args *ap)
581 {
582         int unit;
583         unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0);
584         ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600,
585                                                         "snp%d", unit);
586
587         return 0;
588 }
589
590 static int
591 snp_modevent(module_t mod, int type, void *data)
592 {
593         int i;
594
595         switch (type) {
596         case MOD_LOAD:
597                 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
598                 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp),
599                         snpclone, UID_ROOT, GID_WHEEL, 0600, "snp");
600
601                 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) {
602                         make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, "snp%d", i);
603                         devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i);
604                 }
605                 break;
606         case MOD_UNLOAD:
607                 if (!LIST_EMPTY(&snp_sclist))
608                         return (EBUSY);
609                 ldisc_deregister(snooplinedisc);
610                 devfs_clone_handler_del("snp");
611                 dev_ops_remove_all(&snp_ops);
612                 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp));
613                 break;
614         default:
615                 break;
616         }
617         return (0);
618 }
619
620 static moduledata_t snp_mod = {
621         "snp",
622         snp_modevent,
623         NULL
624 };
625 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR);