| Commit | Line | Data |
|---|---|---|
| 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 $ | |
| 028066b1 | 16 | * $DragonFly: src/sys/dev/misc/snp/snp.c,v 1.19 2007/05/08 02:31:41 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> | |
| 12cc2857 | 29 | #include <sys/thread2.h> |
| 984263bc | 30 | #include <sys/vnode.h> |
| f15db79e | 31 | #include <sys/device.h> |
| 2c1e28dd | 32 | #include <sys/devfs.h> |
| 984263bc MD |
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; | |
| 8be7edad MD |
42 | static d_clone_t snpclone; |
| 43 | DEVFS_DECLARE_CLONE_BITMAP(snp); | |
| 44 | #define SNP_PREALLOCATED_UNITS 4 | |
| 984263bc MD |
45 | |
| 46 | #define CDEV_MAJOR 53 | |
| fef8985e MD |
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, | |
| 984263bc MD |
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. */ | |
| b13267a5 | 67 | cdev_t snp_target; /* Target tty device. */ |
| 984263bc MD |
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 | ||
| b13267a5 | 111 | static struct tty *snpdevtotty (cdev_t dev); |
| 38e94a25 RG |
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); | |
| 984263bc MD |
116 | |
| 117 | static int | |
| c436375a | 118 | snplclose(struct tty *tp, int flag) |
| 984263bc MD |
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 | |
| c436375a | 132 | snplwrite(struct tty *tp, struct uio *uio, int flag) |
| 984263bc MD |
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) { | |
| e54488bb | 144 | ilen = (int)szmin(512, uio->uio_resid); |
| efda3bd0 | 145 | ibuf = kmalloc(ilen, M_SNP, M_WAITOK); |
| e54488bb | 146 | error = uiomove(ibuf, (size_t)ilen, uio); |
| 984263bc MD |
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; | |
| dadab5e9 | 159 | uio2.uio_td = uio->uio_td; |
| 984263bc MD |
160 | error = ttwrite(tp, &uio2, flag); |
| 161 | if (error != 0) | |
| 162 | break; | |
| efda3bd0 | 163 | kfree(ibuf, M_SNP); |
| 984263bc MD |
164 | ibuf = NULL; |
| 165 | } | |
| 166 | if (ibuf != NULL) | |
| efda3bd0 | 167 | kfree(ibuf, M_SNP); |
| 984263bc MD |
168 | return (error); |
| 169 | } | |
| 170 | ||
| 171 | static struct tty * | |
| b13267a5 | 172 | snpdevtotty(cdev_t dev) |
| 984263bc | 173 | { |
| f15db79e | 174 | if ((dev_dflags(dev) & D_TTY) == 0) |
| 984263bc MD |
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 | |
| fef8985e | 185 | snpwrite(struct dev_write_args *ap) |
| 984263bc | 186 | { |
| b13267a5 | 187 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 188 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
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 | ||
| e3869ec7 | 202 | kprintf("Snoop: attempt to write to bad tty.\n"); |
| 984263bc MD |
203 | return (EIO); |
| 204 | ||
| 205 | tty_input: | |
| 206 | if (!(tp->t_state & TS_ISOPEN)) | |
| 207 | return (EIO); | |
| 208 | ||
| 209 | while (uio->uio_resid > 0) { | |
| e54488bb MD |
210 | len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF); |
| 211 | if ((error = uiomove(c, (size_t)len, uio)) != 0) | |
| 984263bc MD |
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 | |
| fef8985e | 223 | snpread(struct dev_read_args *ap) |
| 984263bc | 224 | { |
| b13267a5 | 225 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 226 | struct uio *uio = ap->a_uio; |
| 984263bc | 227 | struct snoop *snp; |
| 12cc2857 | 228 | int error, len, n, nblen; |
| 984263bc MD |
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) { | |
| fef8985e | 243 | if (ap->a_ioflag & IO_NDELAY) |
| 984263bc MD |
244 | return (EWOULDBLOCK); |
| 245 | snp->snp_flags |= SNOOP_RWAIT; | |
| 377d4740 | 246 | error = tsleep((caddr_t)snp, PCATCH, "snprd", 0); |
| 984263bc MD |
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) { | |
| e54488bb | 256 | len = (int)szmin(uio->uio_resid, snp->snp_len); |
| 984263bc MD |
257 | from = (caddr_t)(snp->snp_buf + snp->snp_base); |
| 258 | if (len == 0) | |
| 259 | break; | |
| 260 | ||
| e54488bb | 261 | error = uiomove(from, (size_t)len, uio); |
| 984263bc MD |
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 | } | |
| 12cc2857 | 268 | crit_enter(); |
| 984263bc MD |
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; | |
| efda3bd0 | 273 | if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) { |
| 984263bc | 274 | bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); |
| efda3bd0 | 275 | kfree(snp->snp_buf, M_SNP); |
| 984263bc MD |
276 | snp->snp_buf = nbuf; |
| 277 | snp->snp_blen = nblen; | |
| 278 | snp->snp_base = 0; | |
| 279 | } | |
| 280 | } | |
| 12cc2857 | 281 | crit_exit(); |
| 984263bc MD |
282 | |
| 283 | return (error); | |
| 284 | } | |
| 285 | ||
| 286 | static int | |
| c436375a | 287 | snp_in(struct snoop *snp, char *buf, int n) |
| 984263bc MD |
288 | { |
| 289 | int s_free, s_tail; | |
| 12cc2857 | 290 | int len, nblen; |
| 984263bc MD |
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) { | |
| e3869ec7 | 300 | kprintf("Snoop: more data to down interface.\n"); |
| 984263bc MD |
301 | return (0); |
| 302 | } | |
| 303 | ||
| 304 | if (snp->snp_flags & SNOOP_OFLOW) { | |
| e3869ec7 | 305 | kprintf("Snoop: buffer overflow.\n"); |
| 984263bc MD |
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) { | |
| 12cc2857 | 320 | crit_enter(); |
| 984263bc MD |
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 | } | |
| efda3bd0 | 326 | if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) { |
| 984263bc | 327 | bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); |
| efda3bd0 | 328 | kfree(snp->snp_buf, M_SNP); |
| 984263bc MD |
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 | } | |
| 12cc2857 | 338 | crit_exit(); |
| 984263bc MD |
339 | return (0); |
| 340 | } | |
| 12cc2857 | 341 | crit_exit(); |
| 984263bc MD |
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 | |
| fef8985e | 365 | snpopen(struct dev_open_args *ap) |
| 984263bc | 366 | { |
| b13267a5 | 367 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
368 | struct snoop *snp; |
| 369 | ||
| 370 | if (dev->si_drv1 == NULL) { | |
| 8be7edad | 371 | #if 0 |
| fef8985e | 372 | make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL, |
| 984263bc | 373 | 0600, "snp%d", minor(dev)); |
| 8be7edad | 374 | #endif |
| efda3bd0 | 375 | dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP, |
| 984263bc | 376 | M_WAITOK | M_ZERO); |
| e4c9c0c8 | 377 | } else { |
| 984263bc | 378 | return (EBUSY); |
| e4c9c0c8 | 379 | } |
| 984263bc MD |
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 | ||
| efda3bd0 | 387 | snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); |
| 984263bc MD |
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; | |
| 028066b1 | 396 | snp->snp_target = NULL; |
| 984263bc MD |
397 | |
| 398 | LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); | |
| 399 | return (0); | |
| 400 | } | |
| 401 | ||
| 402 | ||
| 403 | static int | |
| c436375a | 404 | snp_detach(struct snoop *snp) |
| 984263bc MD |
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 | |
| e3869ec7 | 425 | kprintf("Snoop: bad attached tty data.\n"); |
| 984263bc MD |
426 | |
| 427 | snp->snp_tty = NULL; | |
| 028066b1 | 428 | snp->snp_target = NULL; |
| 984263bc MD |
429 | |
| 430 | detach_notty: | |
| 431 | selwakeup(&snp->snp_sel); | |
| 432 | snp->snp_sel.si_pid = 0; | |
| 433 | if ((snp->snp_flags & SNOOP_OPEN) == 0) | |
| efda3bd0 | 434 | kfree(snp, M_SNP); |
| 984263bc MD |
435 | |
| 436 | return (0); | |
| 437 | } | |
| 438 | ||
| 439 | static int | |
| fef8985e | 440 | snpclose(struct dev_close_args *ap) |
| 984263bc | 441 | { |
| b13267a5 | 442 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
443 | struct snoop *snp; |
| 444 | ||
| 445 | snp = dev->si_drv1; | |
| 446 | snp->snp_blen = 0; | |
| 447 | LIST_REMOVE(snp, snp_list); | |
| efda3bd0 | 448 | kfree(snp->snp_buf, M_SNP); |
| 984263bc MD |
449 | snp->snp_flags &= ~SNOOP_OPEN; |
| 450 | dev->si_drv1 = NULL; | |
| 8be7edad MD |
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 | } | |
| 984263bc MD |
455 | return (snp_detach(snp)); |
| 456 | } | |
| 457 | ||
| 458 | static int | |
| c436375a | 459 | snp_down(struct snoop *snp) |
| 984263bc MD |
460 | { |
| 461 | ||
| 462 | if (snp->snp_blen != SNOOP_MINLEN) { | |
| efda3bd0 MD |
463 | kfree(snp->snp_buf, M_SNP); |
| 464 | snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); | |
| 984263bc MD |
465 | snp->snp_blen = SNOOP_MINLEN; |
| 466 | } | |
| 467 | snp->snp_flags |= SNOOP_DOWN; | |
| 468 | ||
| 469 | return (snp_detach(snp)); | |
| 470 | } | |
| 471 | ||
| 472 | static int | |
| fef8985e | 473 | snpioctl(struct dev_ioctl_args *ap) |
| 984263bc | 474 | { |
| b13267a5 | 475 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
476 | struct snoop *snp; |
| 477 | struct tty *tp, *tpo; | |
| b13267a5 | 478 | cdev_t tdev; |
| 984263bc MD |
479 | |
| 480 | snp = dev->si_drv1; | |
| fef8985e | 481 | switch (ap->a_cmd) { |
| 984263bc | 482 | case SNPSTTY: |
| fef8985e | 483 | tdev = udev2dev(*((udev_t *)ap->a_data), 0); |
| 028066b1 | 484 | if (tdev == NULL) |
| 984263bc MD |
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 | ||
| 12cc2857 | 493 | crit_enter(); |
| 984263bc | 494 | |
| 028066b1 | 495 | if (snp->snp_target == NULL) { |
| 984263bc MD |
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; | |
| 12cc2857 | 514 | crit_exit(); |
| 984263bc MD |
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 | */ | |
| b13267a5 | 523 | *((cdev_t *)ap->a_data) = snp->snp_target; |
| 984263bc MD |
524 | break; |
| 525 | ||
| 984263bc | 526 | case FIOASYNC: |
| fef8985e | 527 | if (*(int *)ap->a_data) |
| 984263bc MD |
528 | snp->snp_flags |= SNOOP_ASYNC; |
| 529 | else | |
| 530 | snp->snp_flags &= ~SNOOP_ASYNC; | |
| 531 | break; | |
| 532 | ||
| 533 | case FIONREAD: | |
| 12cc2857 | 534 | crit_enter(); |
| 984263bc | 535 | if (snp->snp_tty != NULL) |
| fef8985e | 536 | *(int *)ap->a_data = snp->snp_len; |
| 984263bc MD |
537 | else |
| 538 | if (snp->snp_flags & SNOOP_DOWN) { | |
| 539 | if (snp->snp_flags & SNOOP_OFLOW) | |
| fef8985e | 540 | *(int *)ap->a_data = SNP_OFLOW; |
| 984263bc | 541 | else |
| fef8985e | 542 | *(int *)ap->a_data = SNP_TTYCLOSE; |
| 984263bc | 543 | } else { |
| fef8985e | 544 | *(int *)ap->a_data = SNP_DETACH; |
| 984263bc | 545 | } |
| 12cc2857 | 546 | crit_exit(); |
| 984263bc MD |
547 | break; |
| 548 | ||
| 549 | default: | |
| 550 | return (ENOTTY); | |
| 551 | } | |
| 552 | return (0); | |
| 553 | } | |
| 554 | ||
| 555 | static int | |
| fef8985e | 556 | snppoll(struct dev_poll_args *ap) |
| 984263bc | 557 | { |
| b13267a5 | 558 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
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 | */ | |
| fef8985e | 569 | if (ap->a_events & (POLLIN | POLLRDNORM)) { |
| 984263bc | 570 | if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) |
| fef8985e | 571 | revents |= ap->a_events & (POLLIN | POLLRDNORM); |
| 984263bc | 572 | else |
| fef8985e | 573 | selrecord(curthread, &snp->snp_sel); |
| 984263bc | 574 | } |
| fef8985e MD |
575 | ap->a_events = revents; |
| 576 | return (0); | |
| 984263bc MD |
577 | } |
| 578 | ||
| 579 | static int | |
| 8be7edad MD |
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 | |
| c436375a | 591 | snp_modevent(module_t mod, int type, void *data) |
| 984263bc | 592 | { |
| 8be7edad | 593 | int i; |
| 984263bc MD |
594 | |
| 595 | switch (type) { | |
| 596 | case MOD_LOAD: | |
| 597 | snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); | |
| b96f3782 AH |
598 | make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp), |
| 599 | snpclone, UID_ROOT, GID_WHEEL, 0600, "snp"); | |
| 8be7edad MD |
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 | } | |
| 984263bc MD |
605 | break; |
| 606 | case MOD_UNLOAD: | |
| 607 | if (!LIST_EMPTY(&snp_sclist)) | |
| 608 | return (EBUSY); | |
| 609 | ldisc_deregister(snooplinedisc); | |
| 8be7edad | 610 | devfs_clone_handler_del("snp"); |
| cd29885a | 611 | dev_ops_remove_all(&snp_ops); |
| 8be7edad | 612 | devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp)); |
| 984263bc MD |
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); |