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