4 * Copyright (c) 1995 Ugen J.S.Antsilevich
6 * Redistribution and use in source forms, with and without modification,
7 * are permitted provided that this entire comment appears intact.
9 * Redistribution in binary form may occur without any restrictions.
10 * Obviously, it would be nice if you gave credit where credit is due
11 * but requiring it would be too onerous.
13 * This software is provided ``AS IS'' without any warranties of any kind.
17 * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/filio.h>
24 #include <sys/malloc.h>
27 #include <sys/event.h>
28 #include <sys/kernel.h>
29 #include <sys/queue.h>
30 #include <sys/snoop.h>
31 #include <sys/thread2.h>
32 #include <sys/vnode.h>
33 #include <sys/device.h>
34 #include <sys/devfs.h>
36 static l_close_t snplclose;
37 static l_write_t snplwrite;
38 static d_open_t snpopen;
39 static d_close_t snpclose;
40 static d_read_t snpread;
41 static d_write_t snpwrite;
42 static d_ioctl_t snpioctl;
43 static d_kqfilter_t snpkqfilter;
44 static d_clone_t snpclone;
45 DEVFS_DECLARE_CLONE_BITMAP(snp);
47 static void snpfilter_detach(struct knote *);
48 static int snpfilter_rd(struct knote *, long);
49 static int snpfilter_wr(struct knote *, long);
52 #define SNP_PREALLOCATED_UNITS 4
54 #define SNP_PREALLOCATED_UNITS NSNP
57 static struct dev_ops snp_ops = {
64 .d_kqfilter = snpkqfilter
67 static struct linesw snpdisc = {
68 ttyopen, snplclose, ttread, snplwrite,
69 l_nullioctl, ttyinput, ttstart, ttymodem
73 * This is the main snoop per-device structure.
76 LIST_ENTRY(snoop) snp_list; /* List glue. */
77 cdev_t snp_target; /* Target tty device. */
78 struct tty *snp_tty; /* Target tty pointer. */
79 u_long snp_len; /* Possible length. */
80 u_long snp_base; /* Data base. */
81 u_long snp_blen; /* Used length. */
82 caddr_t snp_buf; /* Allocation pointer. */
83 int snp_flags; /* Flags. */
84 struct kqinfo snp_kq; /* Kqueue info. */
85 int snp_olddisc; /* Old line discipline. */
91 #define SNOOP_ASYNC 0x0002
92 #define SNOOP_OPEN 0x0004
93 #define SNOOP_RWAIT 0x0008
94 #define SNOOP_OFLOW 0x0010
95 #define SNOOP_DOWN 0x0020
100 #define SNOOP_MINLEN (4*1024) /* This should be power of 2.
101 * 4K tested to be the minimum
102 * for which on normal tty
103 * usage there is no need to
106 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough
107 * If we grow more,something
108 * really bad in this world..
111 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
113 * The number of the "snoop" line discipline. This gets determined at
116 static int snooplinedisc;
119 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
121 static struct tty *snpdevtotty (cdev_t dev);
122 static int snp_detach (struct snoop *snp);
123 static int snp_down (struct snoop *snp);
124 static int snp_in (struct snoop *snp, char *buf, int n);
125 static int snp_modevent (module_t mod, int what, void *arg);
128 snplclose(struct tty *tp, int flag)
133 lwkt_gettoken(&tty_token);
135 error = snp_down(snp);
137 lwkt_reltoken(&tty_token);
140 error = ttylclose(tp, flag);
141 lwkt_reltoken(&tty_token);
146 snplwrite(struct tty *tp, struct uio *uio, int flag)
154 lwkt_gettoken(&tty_token);
158 while (uio->uio_resid > 0) {
159 ilen = (int)szmin(512, uio->uio_resid);
160 ibuf = kmalloc(ilen, M_SNP, M_WAITOK);
161 error = uiomove(ibuf, (size_t)ilen, uio);
164 snp_in(snp, ibuf, ilen);
165 /* Hackish, but probably the least of all evils. */
171 uio2.uio_resid = ilen;
172 uio2.uio_segflg = UIO_SYSSPACE;
173 uio2.uio_rw = UIO_WRITE;
174 uio2.uio_td = uio->uio_td;
175 error = ttwrite(tp, &uio2, flag);
183 lwkt_reltoken(&tty_token);
188 snpdevtotty(cdev_t dev)
190 if ((dev_dflags(dev) & D_TTY) == 0)
192 return (dev->si_tty);
195 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal
196 * interactive mode write is 3 bytes
197 * length for function keys...
201 snpwrite(struct dev_write_args *ap)
203 cdev_t dev = ap->a_head.a_dev;
204 struct uio *uio = ap->a_uio;
208 unsigned char c[SNP_INPUT_BUF];
210 lwkt_gettoken(&tty_token);
214 lwkt_reltoken(&tty_token);
217 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
218 tp->t_line == snooplinedisc)
221 kprintf("Snoop: attempt to write to bad tty.\n");
222 lwkt_reltoken(&tty_token);
226 if (!(tp->t_state & TS_ISOPEN)) {
227 lwkt_reltoken(&tty_token);
231 while (uio->uio_resid > 0) {
232 len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF);
233 if ((error = uiomove(c, (size_t)len, uio)) != 0) {
234 lwkt_reltoken(&tty_token);
237 for (i=0; i < len; i++) {
238 if (ttyinput(c[i], tp)) {
239 lwkt_reltoken(&tty_token);
244 lwkt_reltoken(&tty_token);
250 snpread(struct dev_read_args *ap)
252 cdev_t dev = ap->a_head.a_dev;
253 struct uio *uio = ap->a_uio;
255 int error, len, n, nblen;
259 lwkt_gettoken(&tty_token);
261 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
262 ("snoop buffer error"));
264 if (snp->snp_tty == NULL) {
265 lwkt_reltoken(&tty_token);
269 snp->snp_flags &= ~SNOOP_RWAIT;
272 if (snp->snp_len == 0) {
273 if (ap->a_ioflag & IO_NDELAY) {
274 lwkt_reltoken(&tty_token);
275 return (EWOULDBLOCK);
277 snp->snp_flags |= SNOOP_RWAIT;
278 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0);
280 lwkt_reltoken(&tty_token);
284 } while (snp->snp_len == 0);
289 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
290 len = (int)szmin(uio->uio_resid, snp->snp_len);
291 from = (caddr_t)(snp->snp_buf + snp->snp_base);
295 error = uiomove(from, (size_t)len, uio);
296 snp->snp_base += len;
299 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
300 snp->snp_flags &= ~SNOOP_OFLOW;
303 nblen = snp->snp_blen;
304 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
305 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN)
307 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) {
308 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
309 kfree(snp->snp_buf, M_SNP);
311 snp->snp_blen = nblen;
317 lwkt_reltoken(&tty_token);
322 * NOTE: Must be called with tty_token held
325 snp_in(struct snoop *snp, char *buf, int n)
332 ASSERT_LWKT_TOKEN_HELD(&tty_token);
333 KASSERT(n >= 0, ("negative snoop char count"));
338 if (snp->snp_flags & SNOOP_DOWN) {
339 kprintf("Snoop: more data to down interface.\n");
343 if (snp->snp_flags & SNOOP_OFLOW) {
344 kprintf("Snoop: buffer overflow.\n");
346 * On overflow we just repeat the standart close
347 * procedure...yes , this is waste of space but.. Then next
348 * read from device will fail if one would recall he is
349 * snooping and retry...
352 return (snp_down(snp));
354 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
355 s_free = snp->snp_blen - snp->snp_len;
360 nblen = snp->snp_blen;
361 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
362 nblen = snp->snp_blen * 2;
363 s_free = nblen - (snp->snp_len + snp->snp_base);
365 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) {
366 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
367 kfree(snp->snp_buf, M_SNP);
369 snp->snp_blen = nblen;
372 snp->snp_flags |= SNOOP_OFLOW;
373 if (snp->snp_flags & SNOOP_RWAIT) {
374 snp->snp_flags &= ~SNOOP_RWAIT;
375 wakeup((caddr_t)snp);
383 from = (caddr_t)(snp->snp_buf + snp->snp_base);
384 to = (caddr_t)(snp->snp_buf);
386 bcopy(from, to, len);
389 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
393 if (snp->snp_flags & SNOOP_RWAIT) {
394 snp->snp_flags &= ~SNOOP_RWAIT;
395 wakeup((caddr_t)snp);
397 KNOTE(&snp->snp_kq.ki_note, 0);
403 snpopen(struct dev_open_args *ap)
405 cdev_t dev = ap->a_head.a_dev;
408 lwkt_gettoken(&tty_token);
409 if (dev->si_drv1 == NULL) {
411 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL,
412 0600, "snp%d", minor(dev));
414 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP,
417 lwkt_reltoken(&tty_token);
422 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
423 * all previous settings (especially SNOOP_OFLOW) will be cleared.
425 snp->snp_flags = SNOOP_OPEN;
427 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
428 snp->snp_blen = SNOOP_MINLEN;
433 * snp_tty == NULL is for inactive snoop devices.
436 snp->snp_target = NULL;
438 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
439 lwkt_reltoken(&tty_token);
444 * NOTE: Must be called with tty_token held
447 snp_detach(struct snoop *snp)
451 ASSERT_LWKT_TOKEN_HELD(&tty_token);
456 * If line disc. changed we do not touch this pointer, SLIP/PPP will
463 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
464 tp->t_line == snooplinedisc) {
466 tp->t_state &= ~TS_SNOOP;
467 tp->t_line = snp->snp_olddisc;
469 kprintf("Snoop: bad attached tty data.\n");
472 snp->snp_target = NULL;
475 KNOTE(&snp->snp_kq.ki_note, 0);
476 if ((snp->snp_flags & SNOOP_OPEN) == 0)
483 snpclose(struct dev_close_args *ap)
485 cdev_t dev = ap->a_head.a_dev;
489 lwkt_gettoken(&tty_token);
492 LIST_REMOVE(snp, snp_list);
493 kfree(snp->snp_buf, M_SNP);
494 snp->snp_flags &= ~SNOOP_OPEN;
496 if (dev->si_uminor >= SNP_PREALLOCATED_UNITS) {
497 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), dev->si_uminor);
500 ret = snp_detach(snp);
501 lwkt_reltoken(&tty_token);
506 * NOTE: Must be called with tty_token held
509 snp_down(struct snoop *snp)
512 ASSERT_LWKT_TOKEN_HELD(&tty_token);
513 if (snp->snp_blen != SNOOP_MINLEN) {
514 kfree(snp->snp_buf, M_SNP);
515 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
516 snp->snp_blen = SNOOP_MINLEN;
518 snp->snp_flags |= SNOOP_DOWN;
520 return (snp_detach(snp));
524 snpioctl(struct dev_ioctl_args *ap)
526 cdev_t dev = ap->a_head.a_dev;
528 struct tty *tp, *tpo;
532 lwkt_gettoken(&tty_token);
536 tdev = udev2dev(*((udev_t *)ap->a_data), 0);
538 lwkt_reltoken(&tty_token);
543 tp = snpdevtotty(tdev);
545 lwkt_reltoken(&tty_token);
548 if (tp->t_state & TS_SNOOP) {
549 lwkt_reltoken(&tty_token);
555 if (snp->snp_target == NULL) {
558 tpo->t_state &= ~TS_SNOOP;
561 tp->t_sc = (caddr_t)snp;
562 tp->t_state |= TS_SNOOP;
563 snp->snp_olddisc = tp->t_line;
564 tp->t_line = snooplinedisc;
566 snp->snp_target = tdev;
569 * Clean overflow and down flags -
570 * we'll have a chance to get them in the future :)))
572 snp->snp_flags &= ~SNOOP_OFLOW;
573 snp->snp_flags &= ~SNOOP_DOWN;
579 * We keep snp_target field specially to make
580 * SNPGTTY happy, else we can't know what is device
581 * major/minor for tty.
583 *((cdev_t *)ap->a_data) = snp->snp_target;
587 if (*(int *)ap->a_data)
588 snp->snp_flags |= SNOOP_ASYNC;
590 snp->snp_flags &= ~SNOOP_ASYNC;
595 if (snp->snp_tty != NULL)
596 *(int *)ap->a_data = snp->snp_len;
598 if (snp->snp_flags & SNOOP_DOWN) {
599 if (snp->snp_flags & SNOOP_OFLOW)
600 *(int *)ap->a_data = SNP_OFLOW;
602 *(int *)ap->a_data = SNP_TTYCLOSE;
604 *(int *)ap->a_data = SNP_DETACH;
610 lwkt_reltoken(&tty_token);
613 lwkt_reltoken(&tty_token);
617 static struct filterops snpfiltops_rd =
618 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_rd };
619 static struct filterops snpfiltops_wr =
620 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_wr };
623 snpkqfilter(struct dev_kqfilter_args *ap)
625 cdev_t dev = ap->a_head.a_dev;
626 struct snoop *snp = dev->si_drv1;
627 struct knote *kn = ap->a_kn;
630 lwkt_gettoken(&tty_token);
633 switch (kn->kn_filter) {
635 kn->kn_fop = &snpfiltops_rd;
636 kn->kn_hook = (caddr_t)snp;
639 kn->kn_fop = &snpfiltops_wr;
640 kn->kn_hook = (caddr_t)snp;
643 ap->a_result = EOPNOTSUPP;
644 lwkt_reltoken(&tty_token);
648 klist = &snp->snp_kq.ki_note;
649 knote_insert(klist, kn);
651 lwkt_reltoken(&tty_token);
656 snpfilter_detach(struct knote *kn)
658 struct snoop *snp = (struct snoop *)kn->kn_hook;
661 klist = &snp->snp_kq.ki_note;
662 knote_remove(klist, kn);
666 snpfilter_rd(struct knote *kn, long hint)
668 struct snoop *snp = (struct snoop *)kn->kn_hook;
671 lwkt_gettoken(&tty_token);
673 * If snoop is down, we don't want to poll forever so we return 1.
674 * Caller should see if we down via FIONREAD ioctl(). The last should
675 * return -1 to indicate down state.
677 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
680 lwkt_reltoken(&tty_token);
685 snpfilter_wr(struct knote *kn, long hint)
687 /* Writing is always OK */
692 snpclone(struct dev_clone_args *ap)
695 lwkt_gettoken(&tty_token);
696 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0);
697 ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600,
699 lwkt_reltoken(&tty_token);
704 snp_modevent(module_t mod, int type, void *data)
708 lwkt_gettoken(&tty_token);
711 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc);
712 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp),
713 snpclone, UID_ROOT, GID_WHEEL, 0600, "snp");
715 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) {
716 make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, "snp%d", i);
717 devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i);
721 if (!LIST_EMPTY(&snp_sclist))
723 ldisc_deregister(snooplinedisc);
724 devfs_clone_handler_del("snp");
725 dev_ops_remove_all(&snp_ops);
726 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp));
731 lwkt_reltoken(&tty_token);
735 static moduledata_t snp_mod = {
740 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);