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