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