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