FreeBSD/Linux Kernel Cross Reference
sys/dev/misc/snp/snp.c
1 /*
2 * (MPSAFE)
3 *
4 * Copyright (c) 1995 Ugen J.S.Antsilevich
5 *
6 * Redistribution and use in source forms, with and without modification,
7 * are permitted provided that this entire comment appears intact.
8 *
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.
12 *
13 * This software is provided ``AS IS'' without any warranties of any kind.
14 *
15 * Snoop stuff.
16 *
17 * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $
18 */
19
20 #include "use_snp.h"
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/filio.h>
24 #include <sys/malloc.h>
25 #include <sys/tty.h>
26 #include <sys/conf.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>
35
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);
46
47 static void snpfilter_detach(struct knote *);
48 static int snpfilter_rd(struct knote *, long);
49 static int snpfilter_wr(struct knote *, long);
50
51 #if NSNP <= 1
52 #define SNP_PREALLOCATED_UNITS 4
53 #else
54 #define SNP_PREALLOCATED_UNITS NSNP
55 #endif
56
57 static struct dev_ops snp_ops = {
58 { "snp", 0, 0 },
59 .d_open = snpopen,
60 .d_close = snpclose,
61 .d_read = snpread,
62 .d_write = snpwrite,
63 .d_ioctl = snpioctl,
64 .d_kqfilter = snpkqfilter
65 };
66
67 static struct linesw snpdisc = {
68 ttyopen, snplclose, ttread, snplwrite,
69 l_nullioctl, ttyinput, ttstart, ttymodem
70 };
71
72 /*
73 * This is the main snoop per-device structure.
74 */
75 struct snoop {
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. */
86 };
87
88 /*
89 * Possible flags.
90 */
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
96
97 /*
98 * Other constants.
99 */
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
104 * allocate more.
105 */
106 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough
107 * If we grow more,something
108 * really bad in this world..
109 */
110
111 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data");
112 /*
113 * The number of the "snoop" line discipline. This gets determined at
114 * module load time.
115 */
116 static int snooplinedisc;
117
118
119 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist);
120
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);
126
127 static int
128 snplclose(struct tty *tp, int flag)
129 {
130 struct snoop *snp;
131 int error;
132
133 lwkt_gettoken(&tty_token);
134 snp = tp->t_sc;
135 error = snp_down(snp);
136 if (error != 0) {
137 lwkt_reltoken(&tty_token);
138 return (error);
139 }
140 error = ttylclose(tp, flag);
141 lwkt_reltoken(&tty_token);
142 return (error);
143 }
144
145 static int
146 snplwrite(struct tty *tp, struct uio *uio, int flag)
147 {
148 struct iovec iov;
149 struct uio uio2;
150 struct snoop *snp;
151 int error, ilen;
152 char *ibuf;
153
154 lwkt_gettoken(&tty_token);
155 error = 0;
156 ibuf = NULL;
157 snp = tp->t_sc;
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);
162 if (error != 0)
163 break;
164 snp_in(snp, ibuf, ilen);
165 /* Hackish, but probably the least of all evils. */
166 iov.iov_base = ibuf;
167 iov.iov_len = ilen;
168 uio2.uio_iov = &iov;
169 uio2.uio_iovcnt = 1;
170 uio2.uio_offset = 0;
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);
176 if (error != 0)
177 break;
178 kfree(ibuf, M_SNP);
179 ibuf = NULL;
180 }
181 if (ibuf != NULL)
182 kfree(ibuf, M_SNP);
183 lwkt_reltoken(&tty_token);
184 return (error);
185 }
186
187 static struct tty *
188 snpdevtotty(cdev_t dev)
189 {
190 if ((dev_dflags(dev) & D_TTY) == 0)
191 return (NULL);
192 return (dev->si_tty);
193 }
194
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...
198 */
199
200 static int
201 snpwrite(struct dev_write_args *ap)
202 {
203 cdev_t dev = ap->a_head.a_dev;
204 struct uio *uio = ap->a_uio;
205 struct snoop *snp;
206 struct tty *tp;
207 int error, i, len;
208 unsigned char c[SNP_INPUT_BUF];
209
210 lwkt_gettoken(&tty_token);
211 snp = dev->si_drv1;
212 tp = snp->snp_tty;
213 if (tp == NULL) {
214 lwkt_reltoken(&tty_token);
215 return (EIO);
216 }
217 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
218 tp->t_line == snooplinedisc)
219 goto tty_input;
220
221 kprintf("Snoop: attempt to write to bad tty.\n");
222 lwkt_reltoken(&tty_token);
223 return (EIO);
224
225 tty_input:
226 if (!(tp->t_state & TS_ISOPEN)) {
227 lwkt_reltoken(&tty_token);
228 return (EIO);
229 }
230
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);
235 return (error);
236 }
237 for (i=0; i < len; i++) {
238 if (ttyinput(c[i], tp)) {
239 lwkt_reltoken(&tty_token);
240 return (EIO);
241 }
242 }
243 }
244 lwkt_reltoken(&tty_token);
245 return (0);
246 }
247
248
249 static int
250 snpread(struct dev_read_args *ap)
251 {
252 cdev_t dev = ap->a_head.a_dev;
253 struct uio *uio = ap->a_uio;
254 struct snoop *snp;
255 int error, len, n, nblen;
256 caddr_t from;
257 char *nbuf;
258
259 lwkt_gettoken(&tty_token);
260 snp = dev->si_drv1;
261 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen,
262 ("snoop buffer error"));
263
264 if (snp->snp_tty == NULL) {
265 lwkt_reltoken(&tty_token);
266 return (EIO);
267 }
268
269 snp->snp_flags &= ~SNOOP_RWAIT;
270
271 do {
272 if (snp->snp_len == 0) {
273 if (ap->a_ioflag & IO_NDELAY) {
274 lwkt_reltoken(&tty_token);
275 return (EWOULDBLOCK);
276 }
277 snp->snp_flags |= SNOOP_RWAIT;
278 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0);
279 if (error != 0) {
280 lwkt_reltoken(&tty_token);
281 return (error);
282 }
283 }
284 } while (snp->snp_len == 0);
285
286 n = snp->snp_len;
287
288 error = 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);
292 if (len == 0)
293 break;
294
295 error = uiomove(from, (size_t)len, uio);
296 snp->snp_base += len;
297 snp->snp_len -= len;
298 }
299 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
300 snp->snp_flags &= ~SNOOP_OFLOW;
301 }
302 crit_enter();
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)
306 nblen = nblen / 2;
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);
310 snp->snp_buf = nbuf;
311 snp->snp_blen = nblen;
312 snp->snp_base = 0;
313 }
314 }
315 crit_exit();
316
317 lwkt_reltoken(&tty_token);
318 return (error);
319 }
320
321 /*
322 * NOTE: Must be called with tty_token held
323 */
324 static int
325 snp_in(struct snoop *snp, char *buf, int n)
326 {
327 int s_free, s_tail;
328 int len, nblen;
329 caddr_t from, to;
330 char *nbuf;
331
332 ASSERT_LWKT_TOKEN_HELD(&tty_token);
333 KASSERT(n >= 0, ("negative snoop char count"));
334
335 if (n == 0)
336 return (0);
337
338 if (snp->snp_flags & SNOOP_DOWN) {
339 kprintf("Snoop: more data to down interface.\n");
340 return (0);
341 }
342
343 if (snp->snp_flags & SNOOP_OFLOW) {
344 kprintf("Snoop: buffer overflow.\n");
345 /*
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...
350 */
351
352 return (snp_down(snp));
353 }
354 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
355 s_free = snp->snp_blen - snp->snp_len;
356
357
358 if (n > s_free) {
359 crit_enter();
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);
364 }
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);
368 snp->snp_buf = nbuf;
369 snp->snp_blen = nblen;
370 snp->snp_base = 0;
371 } else {
372 snp->snp_flags |= SNOOP_OFLOW;
373 if (snp->snp_flags & SNOOP_RWAIT) {
374 snp->snp_flags &= ~SNOOP_RWAIT;
375 wakeup((caddr_t)snp);
376 }
377 crit_exit();
378 return (0);
379 }
380 crit_exit();
381 }
382 if (n > s_tail) {
383 from = (caddr_t)(snp->snp_buf + snp->snp_base);
384 to = (caddr_t)(snp->snp_buf);
385 len = snp->snp_len;
386 bcopy(from, to, len);
387 snp->snp_base = 0;
388 }
389 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len);
390 bcopy(buf, to, n);
391 snp->snp_len += n;
392
393 if (snp->snp_flags & SNOOP_RWAIT) {
394 snp->snp_flags &= ~SNOOP_RWAIT;
395 wakeup((caddr_t)snp);
396 }
397 KNOTE(&snp->snp_kq.ki_note, 0);
398
399 return (n);
400 }
401
402 static int
403 snpopen(struct dev_open_args *ap)
404 {
405 cdev_t dev = ap->a_head.a_dev;
406 struct snoop *snp;
407
408 lwkt_gettoken(&tty_token);
409 if (dev->si_drv1 == NULL) {
410 #if 0
411 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL,
412 0600, "snp%d", minor(dev));
413 #endif
414 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP,
415 M_WAITOK | M_ZERO);
416 } else {
417 lwkt_reltoken(&tty_token);
418 return (EBUSY);
419 }
420
421 /*
422 * We intentionally do not OR flags with SNOOP_OPEN, but set them so
423 * all previous settings (especially SNOOP_OFLOW) will be cleared.
424 */
425 snp->snp_flags = SNOOP_OPEN;
426
427 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK);
428 snp->snp_blen = SNOOP_MINLEN;
429 snp->snp_base = 0;
430 snp->snp_len = 0;
431
432 /*
433 * snp_tty == NULL is for inactive snoop devices.
434 */
435 snp->snp_tty = NULL;
436 snp->snp_target = NULL;
437
438 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list);
439 lwkt_reltoken(&tty_token);
440 return (0);
441 }
442
443 /*
444 * NOTE: Must be called with tty_token held
445 */
446 static int
447 snp_detach(struct snoop *snp)
448 {
449 struct tty *tp;
450
451 ASSERT_LWKT_TOKEN_HELD(&tty_token);
452 snp->snp_base = 0;
453 snp->snp_len = 0;
454
455 /*
456 * If line disc. changed we do not touch this pointer, SLIP/PPP will
457 * change it anyway.
458 */
459 tp = snp->snp_tty;
460 if (tp == NULL)
461 goto detach_notty;
462
463 if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
464 tp->t_line == snooplinedisc) {
465 tp->t_sc = NULL;
466 tp->t_state &= ~TS_SNOOP;
467 tp->t_line = snp->snp_olddisc;
468 } else
469 kprintf("Snoop: bad attached tty data.\n");
470
471 snp->snp_tty = NULL;
472 snp->snp_target = NULL;
473
474 detach_notty:
475 KNOTE(&snp->snp_kq.ki_note, 0);
476 if ((snp->snp_flags & SNOOP_OPEN) == 0)
477 kfree(snp, M_SNP);
478
479 return (0);
480 }
481
482 static int
483 snpclose(struct dev_close_args *ap)
484 {
485 cdev_t dev = ap->a_head.a_dev;
486 struct snoop *snp;
487 int ret;
488
489 lwkt_gettoken(&tty_token);
490 snp = dev->si_drv1;
491 snp->snp_blen = 0;
492 LIST_REMOVE(snp, snp_list);
493 kfree(snp->snp_buf, M_SNP);
494 snp->snp_flags &= ~SNOOP_OPEN;
495 dev->si_drv1 = NULL;
496 if (dev->si_uminor >= SNP_PREALLOCATED_UNITS) {
497 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), dev->si_uminor);
498 destroy_dev(dev);
499 }
500 ret = snp_detach(snp);
501 lwkt_reltoken(&tty_token);
502 return ret;
503 }
504
505 /*
506 * NOTE: Must be called with tty_token held
507 */
508 static int
509 snp_down(struct snoop *snp)
510 {
511
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;
517 }
518 snp->snp_flags |= SNOOP_DOWN;
519
520 return (snp_detach(snp));
521 }
522
523 static int
524 snpioctl(struct dev_ioctl_args *ap)
525 {
526 cdev_t dev = ap->a_head.a_dev;
527 struct snoop *snp;
528 struct tty *tp, *tpo;
529 cdev_t tdev;
530 int ret;
531
532 lwkt_gettoken(&tty_token);
533 snp = dev->si_drv1;
534 switch (ap->a_cmd) {
535 case SNPSTTY:
536 tdev = udev2dev(*((udev_t *)ap->a_data), 0);
537 if (tdev == NULL) {
538 lwkt_reltoken(&tty_token);
539 ret = snp_down(snp);
540 return ret;
541 }
542
543 tp = snpdevtotty(tdev);
544 if (!tp) {
545 lwkt_reltoken(&tty_token);
546 return (EINVAL);
547 }
548 if (tp->t_state & TS_SNOOP) {
549 lwkt_reltoken(&tty_token);
550 return (EBUSY);
551 }
552
553 crit_enter();
554
555 if (snp->snp_target == NULL) {
556 tpo = snp->snp_tty;
557 if (tpo)
558 tpo->t_state &= ~TS_SNOOP;
559 }
560
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;
565 snp->snp_tty = tp;
566 snp->snp_target = tdev;
567
568 /*
569 * Clean overflow and down flags -
570 * we'll have a chance to get them in the future :)))
571 */
572 snp->snp_flags &= ~SNOOP_OFLOW;
573 snp->snp_flags &= ~SNOOP_DOWN;
574 crit_exit();
575 break;
576
577 case SNPGTTY:
578 /*
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.
582 */
583 *((cdev_t *)ap->a_data) = snp->snp_target;
584 break;
585
586 case FIOASYNC:
587 if (*(int *)ap->a_data)
588 snp->snp_flags |= SNOOP_ASYNC;
589 else
590 snp->snp_flags &= ~SNOOP_ASYNC;
591 break;
592
593 case FIONREAD:
594 crit_enter();
595 if (snp->snp_tty != NULL)
596 *(int *)ap->a_data = snp->snp_len;
597 else
598 if (snp->snp_flags & SNOOP_DOWN) {
599 if (snp->snp_flags & SNOOP_OFLOW)
600 *(int *)ap->a_data = SNP_OFLOW;
601 else
602 *(int *)ap->a_data = SNP_TTYCLOSE;
603 } else {
604 *(int *)ap->a_data = SNP_DETACH;
605 }
606 crit_exit();
607 break;
608
609 default:
610 lwkt_reltoken(&tty_token);
611 return (ENOTTY);
612 }
613 lwkt_reltoken(&tty_token);
614 return (0);
615 }
616
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 };
621
622 static int
623 snpkqfilter(struct dev_kqfilter_args *ap)
624 {
625 cdev_t dev = ap->a_head.a_dev;
626 struct snoop *snp = dev->si_drv1;
627 struct knote *kn = ap->a_kn;
628 struct klist *klist;
629
630 lwkt_gettoken(&tty_token);
631 ap->a_result = 0;
632
633 switch (kn->kn_filter) {
634 case EVFILT_READ:
635 kn->kn_fop = &snpfiltops_rd;
636 kn->kn_hook = (caddr_t)snp;
637 break;
638 case EVFILT_WRITE:
639 kn->kn_fop = &snpfiltops_wr;
640 kn->kn_hook = (caddr_t)snp;
641 break;
642 default:
643 ap->a_result = EOPNOTSUPP;
644 lwkt_reltoken(&tty_token);
645 return (0);
646 }
647
648 klist = &snp->snp_kq.ki_note;
649 knote_insert(klist, kn);
650
651 lwkt_reltoken(&tty_token);
652 return (0);
653 }
654
655 static void
656 snpfilter_detach(struct knote *kn)
657 {
658 struct snoop *snp = (struct snoop *)kn->kn_hook;
659 struct klist *klist;
660
661 klist = &snp->snp_kq.ki_note;
662 knote_remove(klist, kn);
663 }
664
665 static int
666 snpfilter_rd(struct knote *kn, long hint)
667 {
668 struct snoop *snp = (struct snoop *)kn->kn_hook;
669 int ready = 0;
670
671 lwkt_gettoken(&tty_token);
672 /*
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.
676 */
677 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0)
678 ready = 1;
679
680 lwkt_reltoken(&tty_token);
681 return (ready);
682 }
683
684 static int
685 snpfilter_wr(struct knote *kn, long hint)
686 {
687 /* Writing is always OK */
688 return (1);
689 }
690
691 static int
692 snpclone(struct dev_clone_args *ap)
693 {
694 int unit;
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,
698 "snp%d", unit);
699 lwkt_reltoken(&tty_token);
700 return 0;
701 }
702
703 static int
704 snp_modevent(module_t mod, int type, void *data)
705 {
706 int i;
707
708 lwkt_gettoken(&tty_token);
709 switch (type) {
710 case MOD_LOAD:
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");
714
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);
718 }
719 break;
720 case MOD_UNLOAD:
721 if (!LIST_EMPTY(&snp_sclist))
722 return (EBUSY);
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));
727 break;
728 default:
729 break;
730 }
731 lwkt_reltoken(&tty_token);
732 return (0);
733 }
734
735 static moduledata_t snp_mod = {
736 "snp",
737 snp_modevent,
738 NULL
739 };
740 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
Cache object: acec5f0ae1bed09db12683c1219d3f30
|