Deleted Added
sdiff udiff text old ( 12813 ) new ( 12819 )
full compact
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#include "snp.h"
17
18#if NSNP > 0
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/ioctl.h>
23#include <sys/ioctl_compat.h> /* Oooh..We need O/NTTYDISC */
24#include <sys/proc.h>
25#define TTYDEFCHARS
26#include <sys/tty.h>
27#undef TTYDEFCHARS
28#include <sys/file.h>
29#include <sys/conf.h>
30#include <sys/uio.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#ifdef DEVFS
34#include <sys/devfsext.h>
35#endif /*DEVFS*/
36
37#include <sys/snoop.h>
38
39static d_open_t snpopen;
40static d_close_t snpclose;
41static d_read_t snpread;
42static d_write_t snpwrite;
43static d_ioctl_t snpioctl;
44static d_select_t snpselect;
45
46#define CDEV_MAJOR 53
47static struct cdevsw snp_cdevsw =
48 { snpopen, snpclose, snpread, snpwrite, /*53*/
49 snpioctl, nostop, nullreset, nodevtotty,/* snoop */
50 snpselect, nommap, NULL, "snp", NULL, -1 };
51
52
53#ifndef MIN
54#define MIN(a,b) (((a)<(b))?(a):(b))
55#endif
56
57static struct snoop snoopsw[NSNP];
58
59static struct tty *devtotty __P((dev_t dev));
60
61static struct tty *
62devtotty (dev)
63 dev_t dev;
64{
65 if (major(dev) > nchrdev)
66 return (NULL); /* no such device available */
67
68 return (*cdevsw[major(dev)]->d_devtotty)(dev);
69}
70
71#define SNP_INPUT_BUF 5 /* This is even too much,the maximal
72 * interactive mode write is 3 bytes
73 * length for function keys...
74 */
75
76static int
77snpwrite(dev, uio, flag)
78 dev_t dev;
79 struct uio *uio;
80 int flag;
81{
82 int unit = minor(dev), len, i, error;
83 struct snoop *snp = &snoopsw[unit];
84 struct tty *tp;
85 char c[SNP_INPUT_BUF];
86
87 if (snp->snp_tty == NULL)
88 return (EIO);
89
90 tp = snp->snp_tty;
91
92 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
93 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC))
94 goto tty_input;
95
96 printf("Snoop: attempt to write to bad tty.\n");
97 return (EIO);
98
99tty_input:
100 if (!(tp->t_state & TS_ISOPEN))
101 return (EIO);
102
103 while (uio->uio_resid > 0) {
104 len = MIN(uio->uio_resid,SNP_INPUT_BUF);
105 if ((error = uiomove(c, len, uio)) != 0)
106 return (error);
107 for (i=0;i<len;i++) {
108 if (ttyinput(c[i] , tp))
109 return (EIO);
110 }
111 }
112 return 0;
113
114}
115
116
117static int
118snpread(dev, uio, flag)
119 dev_t dev;
120 struct uio *uio;
121 int flag;
122{
123 int unit = minor(dev), s;
124 struct snoop *snp = &snoopsw[unit];
125 int len, n, nblen, error = 0;
126 caddr_t from;
127 char *nbuf;
128
129#ifdef DIAGNOSTIC
130 if ((snp->snp_len + snp->snp_base) > snp->snp_blen)
131 panic("snoop buffer error");
132#endif
133
134 if (snp->snp_tty == NULL)
135 return (EIO);
136
137 snp->snp_flags &= ~SNOOP_RWAIT;
138
139 do {
140 if (snp->snp_len == 0) {
141 if (snp->snp_flags & SNOOP_NBIO) {
142 return EWOULDBLOCK;
143 }
144 snp->snp_flags |= SNOOP_RWAIT;
145 tsleep((caddr_t) snp, (PZERO + 1) | PCATCH, "snoopread", 0);
146 }
147 } while (snp->snp_len == 0);
148
149 n = snp->snp_len;
150
151 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
152 len = MIN(uio->uio_resid, snp->snp_len);
153 from = (caddr_t) (snp->snp_buf + snp->snp_base);
154 if (len == 0)
155 break;
156
157 error = uiomove(from, len, uio);
158 snp->snp_base += len;
159 snp->snp_len -= len;
160 }
161 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
162 snp->snp_flags &= ~SNOOP_OFLOW;
163 }
164 s = spltty();
165 nblen = snp->snp_blen;
166 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
167 while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN))
168 nblen = nblen / 2;
169 if (nbuf = malloc(nblen, M_TTYS, M_NOWAIT)) {
170 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
171 free(snp->snp_buf, M_TTYS);
172 snp->snp_buf = nbuf;
173 snp->snp_blen = nblen;
174 snp->snp_base = 0;
175 }
176 }
177 splx(s);
178
179 return error;
180}
181
182int
183snpinc(snp, c)
184 struct snoop *snp;
185 char c;
186{
187 char buf[1];
188
189 buf[0]=c;
190 return (snpin(snp,buf,1));
191}
192
193
194int
195snpin(snp, buf, n)
196 struct snoop *snp;
197 char *buf;
198 int n;
199{
200 int s_free, s_tail;
201 int s, len, nblen;
202 caddr_t from, to;
203 char *nbuf;
204
205
206 if (n == 0)
207 return 0;
208
209#ifdef DIAGNOSTIC
210 if (n < 0)
211 panic("bad snoop char count");
212
213 if (!(snp->snp_flags & SNOOP_OPEN)) {
214 printf("Snoop: data coming to closed device.\n");
215 return 0;
216 }
217#endif
218 if (snp->snp_flags & SNOOP_DOWN) {
219 printf("Snoop: more data to down interface.\n");
220 return 0;
221 }
222
223 if (snp->snp_flags & SNOOP_OFLOW) {
224 printf("Snoop: buffer overflow.\n");
225 /*
226 * On overflow we just repeat the standart close
227 * procedure...yes , this is waste of space but.. Then next
228 * read from device will fail if one would recall he is
229 * snooping and retry...
230 */
231
232 return (snpdown(snp));
233 }
234 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
235 s_free = snp->snp_blen - snp->snp_len;
236
237
238 if (n > s_free) {
239 s = spltty();
240 nblen = snp->snp_blen;
241 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
242 nblen = snp->snp_blen * 2;
243 s_free = nblen - (snp->snp_len + snp->snp_base);
244 }
245 if ((n <= s_free) && (nbuf = malloc(nblen, M_TTYS, M_NOWAIT))) {
246 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
247 free(snp->snp_buf, M_TTYS);
248 snp->snp_buf = nbuf;
249 snp->snp_blen = nblen;
250 snp->snp_base = 0;
251 } else {
252 snp->snp_flags |= SNOOP_OFLOW;
253 if (snp->snp_flags & SNOOP_RWAIT) {
254 snp->snp_flags &= ~SNOOP_RWAIT;
255 wakeup((caddr_t) snp);
256 }
257 splx(s);
258 return 0;
259 }
260 splx(s);
261 }
262 if (n > s_tail) {
263 from = (caddr_t) (snp->snp_buf + snp->snp_base);
264 to = (caddr_t) (snp->snp_buf);
265 len = snp->snp_len;
266 bcopy(from, to, len);
267 snp->snp_base = 0;
268 }
269 to = (caddr_t) (snp->snp_buf + snp->snp_base + snp->snp_len);
270 bcopy(buf, to, n);
271 snp->snp_len += n;
272
273 if (snp->snp_flags & SNOOP_RWAIT) {
274 snp->snp_flags &= ~SNOOP_RWAIT;
275 wakeup((caddr_t) snp);
276 }
277 selwakeup(&snp->snp_sel);
278 snp->snp_sel.si_pid = 0;
279
280 return n;
281}
282
283static int
284snpopen(dev, flag, mode, p)
285 dev_t dev;
286 int flag, mode;
287 struct proc *p;
288{
289 struct snoop *snp;
290 register int unit, error;
291
292 if (error = suser(p->p_ucred, &p->p_acflag))
293 return (error);
294
295 if ((unit = minor(dev)) >= NSNP)
296 return (ENXIO);
297
298 snp = &snoopsw[unit];
299
300 if (snp->snp_flags & SNOOP_OPEN)
301 return (ENXIO);
302
303 /*
304 * We intentionally do not OR flags with SNOOP_OPEN,but set them so
305 * all previous settings (especially SNOOP_OFLOW) will be cleared.
306 */
307 snp->snp_flags = SNOOP_OPEN;
308
309 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
310 snp->snp_blen = SNOOP_MINLEN;
311 snp->snp_base = 0;
312 snp->snp_len = 0;
313
314 /*
315 * snp_tty == NULL is for inactive snoop devices.
316 */
317 snp->snp_tty = NULL;
318 snp->snp_target = -1;
319 return (0);
320}
321
322
323int
324snp_detach(snp)
325 struct snoop *snp;
326{
327 struct tty *tp;
328
329 snp->snp_base = 0;
330 snp->snp_len = 0;
331
332 /*
333 * If line disc. changed we do not touch this pointer,SLIP/PPP will
334 * change it anyway.
335 */
336
337 if (snp->snp_tty == NULL)
338 goto detach_notty;
339
340 tp = snp->snp_tty;
341
342 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
343 (tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) {
344 tp->t_sc = NULL;
345 tp->t_state &= ~TS_SNOOP;
346 } else
347 printf("Snoop: bad attached tty data.\n");
348
349 snp->snp_tty = NULL;
350 snp->snp_target = -1;
351
352detach_notty:
353 selwakeup(&snp->snp_sel);
354 snp->snp_sel.si_pid = 0;
355
356 return (0);
357}
358
359static int
360snpclose(dev, flags, fmt, p)
361 dev_t dev;
362 int flags;
363 int fmt;
364 struct proc *p;
365{
366 register int unit = minor(dev);
367 struct snoop *snp = &snoopsw[unit];
368
369 snp->snp_blen = 0;
370 free(snp->snp_buf, M_TTYS);
371 snp->snp_flags &= ~SNOOP_OPEN;
372
373 return (snp_detach(snp));
374}
375
376int
377snpdown(snp)
378 struct snoop *snp;
379{
380 snp->snp_blen = SNOOP_MINLEN;
381 free(snp->snp_buf, M_TTYS);
382 snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
383 snp->snp_flags |= SNOOP_DOWN;
384
385 return (snp_detach(snp));
386}
387
388
389static int
390snpioctl(dev, cmd, data, flags, p)
391 dev_t dev;
392 int cmd;
393 caddr_t data;
394 int flags;
395 struct proc *p;
396{
397 int unit = minor(dev), s;
398 dev_t tdev;
399 struct snoop *snp = &snoopsw[unit];
400 struct tty *tp, *tpo;
401
402 switch (cmd) {
403 case SNPSTTY:
404 tdev = *((dev_t *) data);
405 if (tdev == -1)
406 return (snpdown(snp));
407
408 tp = devtotty(tdev);
409 if (!tp)
410 return (EINVAL);
411
412 if ((tp->t_sc != (caddr_t) snp) && (tp->t_state & TS_SNOOP))
413 return (EBUSY);
414
415 if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC))
416 return (EBUSY);
417
418 s = spltty();
419
420 if (snp->snp_target == -1) {
421 tpo = snp->snp_tty;
422 if (tpo)
423 tpo->t_state &= ~TS_SNOOP;
424 }
425
426 tp->t_sc = (caddr_t) snp;
427 tp->t_state |= TS_SNOOP;
428 snp->snp_tty = tp;
429 snp->snp_target = tdev;
430
431 /*
432 * Clean overflow and down flags -
433 * we'll have a chance to get them in the future :)))
434 */
435 snp->snp_flags &= ~SNOOP_OFLOW;
436 snp->snp_flags &= ~SNOOP_DOWN;
437 splx(s);
438 break;
439
440 case SNPGTTY:
441 /*
442 * We keep snp_target field specially to make
443 * SNPGTTY happy,else we can't know what is device
444 * major/minor for tty.
445 */
446 *((dev_t *) data) = snp->snp_target;
447 break;
448
449 case FIONBIO:
450 if (*(int *) data)
451 snp->snp_flags |= SNOOP_NBIO;
452 else
453 snp->snp_flags &= ~SNOOP_NBIO;
454 break;
455
456 case FIOASYNC:
457 if (*(int *) data)
458 snp->snp_flags |= SNOOP_ASYNC;
459 else
460 snp->snp_flags &= ~SNOOP_ASYNC;
461 break;
462
463 case FIONREAD:
464 s = spltty();
465 if (snp->snp_tty != NULL)
466 *(int *) data = snp->snp_len;
467 else
468 if (snp->snp_flags & SNOOP_DOWN) {
469 if (snp->snp_flags & SNOOP_OFLOW)
470 *(int *) data = SNP_OFLOW;
471 else
472 *(int *) data = SNP_TTYCLOSE;
473 } else {
474 *(int *) data = SNP_DETACH;
475 }
476 splx(s);
477 break;
478
479 default:
480 return (ENOTTY);
481 }
482 return (0);
483}
484
485
486static int
487snpselect(dev, rw, p)
488 dev_t dev;
489 int rw;
490 struct proc *p;
491{
492 int unit = minor(dev);
493 struct snoop *snp = &snoopsw[unit];
494
495 if (rw != FREAD)
496 return 1;
497
498 if (snp->snp_len > 0)
499 return 1;
500
501 /*
502 * If snoop is down,we don't want to select() forever so we return 1.
503 * Caller should see if we down via FIONREAD ioctl().The last should
504 * return -1 to indicate down state.
505 */
506 if (snp->snp_flags & SNOOP_DOWN)
507 return 1;
508
509 selrecord(p, &snp->snp_sel);
510 return 0;
511}
512
513static void *snp_devfs_token[NSNP];
514static snp_devsw_installed = 0;
515
516static void
517snp_drvinit(void *unused)
518{
519 dev_t dev;
520 char name[32];
521 int i;
522
523 if( ! snp_devsw_installed ) {
524 dev = makedev(CDEV_MAJOR, 0);
525 cdevsw_add(&dev,&snp_cdevsw, NULL);
526 snp_devsw_installed = 1;
527#ifdef DEVFS
528 for ( i = 0 ; i < NSNP ; i++) {
529 sprintf(name,"snp%d",i);
530 snp_devfs_token[i] =
531 devfs_add_devsw( "/", name, &snp_cdevsw, i,
532 DV_CHR, 0, 0, 0600);
533 }
534#endif
535 }
536}
537
538SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL)
539
540
541#endif