1dde8a05bSUgen J.S. Antsilevich /* 2dde8a05bSUgen J.S. Antsilevich * Copyright (c) 1995 Ugen J.S.Antsilevich 3dde8a05bSUgen J.S. Antsilevich * 4dde8a05bSUgen J.S. Antsilevich * Redistribution and use in source forms, with and without modification, 5dde8a05bSUgen J.S. Antsilevich * are permitted provided that this entire comment appears intact. 6dde8a05bSUgen J.S. Antsilevich * 7dde8a05bSUgen J.S. Antsilevich * Redistribution in binary form may occur without any restrictions. 8dde8a05bSUgen J.S. Antsilevich * Obviously, it would be nice if you gave credit where credit is due 9dde8a05bSUgen J.S. Antsilevich * but requiring it would be too onerous. 10dde8a05bSUgen J.S. Antsilevich * 11dde8a05bSUgen J.S. Antsilevich * This software is provided ``AS IS'' without any warranties of any kind. 12dde8a05bSUgen J.S. Antsilevich * 13dde8a05bSUgen J.S. Antsilevich * Snoop stuff. 140625ba2fSGary Palmer * 15c3aac50fSPeter Wemm * $FreeBSD$ 16dde8a05bSUgen J.S. Antsilevich */ 17dde8a05bSUgen J.S. Antsilevich 18dde8a05bSUgen J.S. Antsilevich #include <sys/param.h> 19dde8a05bSUgen J.S. Antsilevich #include <sys/systm.h> 2071455815SBruce Evans #include <sys/filio.h> 21a1c995b6SPoul-Henning Kamp #include <sys/malloc.h> 22dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h> 23dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h> 24659ffb48SPeter Wemm #include <sys/poll.h> 25dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h> 26ac60b28dSDima Dorfman #include <sys/queue.h> 2787f6c662SJulian Elischer #include <sys/snoop.h> 28d96bc99dSBruce Evans #include <sys/vnode.h> 2987f6c662SJulian Elischer 30ddf5b796SDima Dorfman static l_close_t snplclose; 31f09f49f1SDima Dorfman static l_write_t snplwrite; 3287f6c662SJulian Elischer static d_open_t snpopen; 3387f6c662SJulian Elischer static d_close_t snpclose; 3487f6c662SJulian Elischer static d_read_t snpread; 3587f6c662SJulian Elischer static d_write_t snpwrite; 3687f6c662SJulian Elischer static d_ioctl_t snpioctl; 37659ffb48SPeter Wemm static d_poll_t snppoll; 3887f6c662SJulian Elischer 3953ac6efbSJulian Elischer #define CDEV_MAJOR 53 404e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = { 414e2f199eSPoul-Henning Kamp /* open */ snpopen, 424e2f199eSPoul-Henning Kamp /* close */ snpclose, 434e2f199eSPoul-Henning Kamp /* read */ snpread, 444e2f199eSPoul-Henning Kamp /* write */ snpwrite, 454e2f199eSPoul-Henning Kamp /* ioctl */ snpioctl, 464e2f199eSPoul-Henning Kamp /* poll */ snppoll, 474e2f199eSPoul-Henning Kamp /* mmap */ nommap, 484e2f199eSPoul-Henning Kamp /* strategy */ nostrategy, 494e2f199eSPoul-Henning Kamp /* name */ "snp", 504e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 514e2f199eSPoul-Henning Kamp /* dump */ nodump, 524e2f199eSPoul-Henning Kamp /* psize */ nopsize, 534e2f199eSPoul-Henning Kamp /* flags */ 0, 544e2f199eSPoul-Henning Kamp }; 5587f6c662SJulian Elischer 56f09f49f1SDima Dorfman static struct linesw snpdisc = { 57ddf5b796SDima Dorfman ttyopen, snplclose, ttread, snplwrite, 58f09f49f1SDima Dorfman l_nullioctl, ttyinput, ttstart, ttymodem 59f09f49f1SDima Dorfman }; 6053ac6efbSJulian Elischer 61101f105dSDima Dorfman /* 62101f105dSDima Dorfman * This is the main snoop per-device structure. 63101f105dSDima Dorfman */ 64101f105dSDima Dorfman struct snoop { 65101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 66101f105dSDima Dorfman dev_t snp_target; /* Target tty device. */ 67101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 68101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 69101f105dSDima Dorfman u_long snp_base; /* Data base. */ 70101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 71101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 72101f105dSDima Dorfman int snp_flags; /* Flags. */ 73101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 74101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 75101f105dSDima Dorfman }; 76101f105dSDima Dorfman 77101f105dSDima Dorfman /* 78101f105dSDima Dorfman * Possible flags. 79101f105dSDima Dorfman */ 80101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 81101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 82101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 83101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 84101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 85101f105dSDima Dorfman 86101f105dSDima Dorfman /* 87101f105dSDima Dorfman * Other constants. 88101f105dSDima Dorfman */ 89101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 90101f105dSDima Dorfman * 4K tested to be the minimum 91101f105dSDima Dorfman * for which on normal tty 92101f105dSDima Dorfman * usage there is no need to 93101f105dSDima Dorfman * allocate more. 94101f105dSDima Dorfman */ 95101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 96101f105dSDima Dorfman * If we grow more,something 97101f105dSDima Dorfman * really bad in this world.. 98101f105dSDima Dorfman */ 99101f105dSDima Dorfman 100959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 10147eaa5f5SDima Dorfman /* 10247eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 10347eaa5f5SDima Dorfman * module load time. 10447eaa5f5SDima Dorfman */ 105f09f49f1SDima Dorfman static int snooplinedisc; 106f09f49f1SDima Dorfman 107ac60b28dSDima Dorfman 108ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 109ac60b28dSDima Dorfman 110f09f49f1SDima Dorfman static struct tty *snpdevtotty __P((dev_t dev)); 111f09f49f1SDima Dorfman static void snp_clone __P((void *arg, char *name, 112f09f49f1SDima Dorfman int namelen, dev_t *dev)); 113f09f49f1SDima Dorfman static int snp_detach __P((struct snoop *snp)); 114f09f49f1SDima Dorfman static int snp_down __P((struct snoop *snp)); 115f09f49f1SDima Dorfman static int snp_in __P((struct snoop *snp, char *buf, int n)); 116f09f49f1SDima Dorfman static int snp_modevent __P((module_t mod, int what, void *arg)); 11747eaa5f5SDima Dorfman 11847eaa5f5SDima Dorfman static int 119ddf5b796SDima Dorfman snplclose(tp, flag) 120ddf5b796SDima Dorfman struct tty *tp; 121ddf5b796SDima Dorfman int flag; 122ddf5b796SDima Dorfman { 123ddf5b796SDima Dorfman struct snoop *snp; 124ddf5b796SDima Dorfman int error; 125ddf5b796SDima Dorfman 126ddf5b796SDima Dorfman snp = tp->t_sc; 127ddf5b796SDima Dorfman error = snp_down(snp); 128ddf5b796SDima Dorfman if (error != 0) 129ddf5b796SDima Dorfman return (error); 130ddf5b796SDima Dorfman error = ttylclose(tp, flag); 131ddf5b796SDima Dorfman return (error); 132ddf5b796SDima Dorfman } 133ddf5b796SDima Dorfman 134ddf5b796SDima Dorfman static int 135f09f49f1SDima Dorfman snplwrite(tp, uio, flag) 136f09f49f1SDima Dorfman struct tty *tp; 137f09f49f1SDima Dorfman struct uio *uio; 138f09f49f1SDima Dorfman int flag; 13947eaa5f5SDima Dorfman { 14047eaa5f5SDima Dorfman struct iovec iov; 14147eaa5f5SDima Dorfman struct uio uio2; 142f09f49f1SDima Dorfman struct snoop *snp; 143f09f49f1SDima Dorfman int error, ilen; 144f09f49f1SDima Dorfman char ibuf[512]; 14547eaa5f5SDima Dorfman 146f09f49f1SDima Dorfman snp = tp->t_sc; 14787826386SDima Dorfman while (uio->uio_resid > 0) { 14887826386SDima Dorfman ilen = imin(sizeof(ibuf), uio->uio_resid); 14947eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 150f09f49f1SDima Dorfman if (error != 0) 151ddf5b796SDima Dorfman return (error); 152f09f49f1SDima Dorfman snp_in(snp, ibuf, ilen); 153f09f49f1SDima Dorfman /* Hackish, but probably the least of all evils. */ 15447eaa5f5SDima Dorfman iov.iov_base = ibuf; 15547eaa5f5SDima Dorfman iov.iov_len = ilen; 15647eaa5f5SDima Dorfman uio2.uio_iov = &iov; 15747eaa5f5SDima Dorfman uio2.uio_iovcnt = 1; 15847eaa5f5SDima Dorfman uio2.uio_offset = 0; 15947eaa5f5SDima Dorfman uio2.uio_resid = ilen; 16047eaa5f5SDima Dorfman uio2.uio_segflg = UIO_SYSSPACE; 16147eaa5f5SDima Dorfman uio2.uio_rw = UIO_WRITE; 16247eaa5f5SDima Dorfman uio2.uio_procp = uio->uio_procp; 16347eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 164f09f49f1SDima Dorfman if (error != 0) 16547eaa5f5SDima Dorfman return (error); 16647eaa5f5SDima Dorfman } 167ddf5b796SDima Dorfman return (0); 168ddf5b796SDima Dorfman } 16947eaa5f5SDima Dorfman 17077f77631SPaul Traina static struct tty * 171444f003cSBruce Evans snpdevtotty(dev) 17277f77631SPaul Traina dev_t dev; 17377f77631SPaul Traina { 174444f003cSBruce Evans struct cdevsw *cdp; 17577f77631SPaul Traina 1764be2eb8cSPoul-Henning Kamp cdp = devsw(dev); 17787826386SDima Dorfman if (cdp == NULL || (cdp->d_flags & D_TTY) == 0) 178444f003cSBruce Evans return (NULL); 17987826386SDima Dorfman return (dev->si_tty); 18077f77631SPaul Traina } 18177f77631SPaul Traina 1820739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 1830739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 1840739a0dcSUgen J.S. Antsilevich * length for function keys... 1850739a0dcSUgen J.S. Antsilevich */ 1860739a0dcSUgen J.S. Antsilevich 18787f6c662SJulian Elischer static int 1880739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag) 1890739a0dcSUgen J.S. Antsilevich dev_t dev; 1900739a0dcSUgen J.S. Antsilevich struct uio *uio; 1910739a0dcSUgen J.S. Antsilevich int flag; 1920739a0dcSUgen J.S. Antsilevich { 193f09f49f1SDima Dorfman struct snoop *snp; 1940739a0dcSUgen J.S. Antsilevich struct tty *tp; 195f09f49f1SDima Dorfman int error, i, len; 1960739a0dcSUgen J.S. Antsilevich char c[SNP_INPUT_BUF]; 1970739a0dcSUgen J.S. Antsilevich 198f09f49f1SDima Dorfman snp = dev->si_drv1; 1990739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 200f09f49f1SDima Dorfman if (tp == NULL) 201f09f49f1SDima Dorfman return (EIO); 2020739a0dcSUgen J.S. Antsilevich if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 203f09f49f1SDima Dorfman tp->t_line == snooplinedisc) 2040739a0dcSUgen J.S. Antsilevich goto tty_input; 2050739a0dcSUgen J.S. Antsilevich 2060739a0dcSUgen J.S. Antsilevich printf("Snoop: attempt to write to bad tty.\n"); 2070739a0dcSUgen J.S. Antsilevich return (EIO); 2080739a0dcSUgen J.S. Antsilevich 2090739a0dcSUgen J.S. Antsilevich tty_input: 2100739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2110739a0dcSUgen J.S. Antsilevich return (EIO); 2120739a0dcSUgen J.S. Antsilevich 2130739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 21487826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2150739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2160739a0dcSUgen J.S. Antsilevich return (error); 2170739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2180739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2190739a0dcSUgen J.S. Antsilevich return (EIO); 2200739a0dcSUgen J.S. Antsilevich } 2210739a0dcSUgen J.S. Antsilevich } 222f09f49f1SDima Dorfman return (0); 2230739a0dcSUgen J.S. Antsilevich } 2240739a0dcSUgen J.S. Antsilevich 2250739a0dcSUgen J.S. Antsilevich 22687f6c662SJulian Elischer static int 227dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag) 228dde8a05bSUgen J.S. Antsilevich dev_t dev; 229dde8a05bSUgen J.S. Antsilevich struct uio *uio; 230dde8a05bSUgen J.S. Antsilevich int flag; 231dde8a05bSUgen J.S. Antsilevich { 232f09f49f1SDima Dorfman struct snoop *snp; 233f09f49f1SDima Dorfman int error, len, n, nblen, s; 234dde8a05bSUgen J.S. Antsilevich caddr_t from; 235dde8a05bSUgen J.S. Antsilevich char *nbuf; 236dde8a05bSUgen J.S. Antsilevich 237f09f49f1SDima Dorfman snp = dev->si_drv1; 2385526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2395526d2d9SEivind Eklund ("snoop buffer error")); 24077f77631SPaul Traina 2410739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 242dde8a05bSUgen J.S. Antsilevich return (EIO); 243dde8a05bSUgen J.S. Antsilevich 244dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 245dde8a05bSUgen J.S. Antsilevich 246dde8a05bSUgen J.S. Antsilevich do { 247dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 248d96bc99dSBruce Evans if (flag & IO_NDELAY) 249d96bc99dSBruce Evans return (EWOULDBLOCK); 250dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 25176e90dbcSBrian Feldman tsleep((caddr_t)snp, (PZERO + 1) | PCATCH, "snprd", 0); 252dde8a05bSUgen J.S. Antsilevich } 253dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 254dde8a05bSUgen J.S. Antsilevich 255019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 256dde8a05bSUgen J.S. Antsilevich 257f09f49f1SDima Dorfman error = 0; 258dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 25987826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 260dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 261dde8a05bSUgen J.S. Antsilevich if (len == 0) 262dde8a05bSUgen J.S. Antsilevich break; 263dde8a05bSUgen J.S. Antsilevich 264dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 265dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 266dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 267dde8a05bSUgen J.S. Antsilevich } 268019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 269dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 270019b4d63SUgen J.S. Antsilevich } 271dde8a05bSUgen J.S. Antsilevich s = spltty(); 272dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 273dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 274f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 275dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 27676e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 277dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 27876e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 279dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 280dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 281dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 282dde8a05bSUgen J.S. Antsilevich } 283dde8a05bSUgen J.S. Antsilevich } 284dde8a05bSUgen J.S. Antsilevich splx(s); 285dde8a05bSUgen J.S. Antsilevich 286f09f49f1SDima Dorfman return (error); 287dde8a05bSUgen J.S. Antsilevich } 288dde8a05bSUgen J.S. Antsilevich 289f09f49f1SDima Dorfman static int 290f09f49f1SDima Dorfman snp_in(snp, buf, n) 291dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 292dde8a05bSUgen J.S. Antsilevich char *buf; 293dde8a05bSUgen J.S. Antsilevich int n; 294dde8a05bSUgen J.S. Antsilevich { 295dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 296dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 297dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 298dde8a05bSUgen J.S. Antsilevich char *nbuf; 299dde8a05bSUgen J.S. Antsilevich 300219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 301dde8a05bSUgen J.S. Antsilevich 302dde8a05bSUgen J.S. Antsilevich if (n == 0) 303f09f49f1SDima Dorfman return (0); 304dde8a05bSUgen J.S. Antsilevich 305019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 306019b4d63SUgen J.S. Antsilevich printf("Snoop: more data to down interface.\n"); 307f09f49f1SDima Dorfman return (0); 308019b4d63SUgen J.S. Antsilevich } 309964587caSUgen J.S. Antsilevich 310019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 311019b4d63SUgen J.S. Antsilevich printf("Snoop: buffer overflow.\n"); 312019b4d63SUgen J.S. Antsilevich /* 313019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 314019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 315019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 316019b4d63SUgen J.S. Antsilevich * snooping and retry... 317019b4d63SUgen J.S. Antsilevich */ 318dde8a05bSUgen J.S. Antsilevich 319f09f49f1SDima Dorfman return (snp_down(snp)); 320019b4d63SUgen J.S. Antsilevich } 321019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 322019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 323dde8a05bSUgen J.S. Antsilevich 324dde8a05bSUgen J.S. Antsilevich 325dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 326dde8a05bSUgen J.S. Antsilevich s = spltty(); 327dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 328dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 329dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 330dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 331dde8a05bSUgen J.S. Antsilevich } 33276e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 333dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 33476e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 335dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 336dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 337dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 338dde8a05bSUgen J.S. Antsilevich } else { 339dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 340dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 341dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 342dde8a05bSUgen J.S. Antsilevich wakeup((caddr_t)snp); 343dde8a05bSUgen J.S. Antsilevich } 344dde8a05bSUgen J.S. Antsilevich splx(s); 345f09f49f1SDima Dorfman return (0); 346dde8a05bSUgen J.S. Antsilevich } 347dde8a05bSUgen J.S. Antsilevich splx(s); 348dde8a05bSUgen J.S. Antsilevich } 349dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 350dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 351dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 352dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 353dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 354dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 355dde8a05bSUgen J.S. Antsilevich } 356dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 357dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 358dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 359dde8a05bSUgen J.S. Antsilevich 360dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 361dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 362dde8a05bSUgen J.S. Antsilevich wakeup((caddr_t)snp); 363dde8a05bSUgen J.S. Antsilevich } 364dde8a05bSUgen J.S. Antsilevich selwakeup(&snp->snp_sel); 365dde8a05bSUgen J.S. Antsilevich snp->snp_sel.si_pid = 0; 366dde8a05bSUgen J.S. Antsilevich 367f09f49f1SDima Dorfman return (n); 368dde8a05bSUgen J.S. Antsilevich } 369dde8a05bSUgen J.S. Antsilevich 37087f6c662SJulian Elischer static int 371dde8a05bSUgen J.S. Antsilevich snpopen(dev, flag, mode, p) 372dde8a05bSUgen J.S. Antsilevich dev_t dev; 373dde8a05bSUgen J.S. Antsilevich int flag, mode; 374dde8a05bSUgen J.S. Antsilevich struct proc *p; 375dde8a05bSUgen J.S. Antsilevich { 376dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 37776e90dbcSBrian Feldman int error; 378dde8a05bSUgen J.S. Antsilevich 379f711d546SPoul-Henning Kamp if ((error = suser(p)) != 0) 380dde8a05bSUgen J.S. Antsilevich return (error); 381dde8a05bSUgen J.S. Antsilevich 38276e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 38325c7870eSDima Dorfman if (!(dev->si_flags & SI_NAMED)) 38425c7870eSDima Dorfman make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL, 38525c7870eSDima Dorfman 0600, "snp%d", dev2unit(dev)); 386f09f49f1SDima Dorfman dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, 387f09f49f1SDima Dorfman M_WAITOK | M_ZERO); 38876e90dbcSBrian Feldman } else 38976e90dbcSBrian Feldman return (EBUSY); 39077f77631SPaul Traina 391019b4d63SUgen J.S. Antsilevich /* 392019b4d63SUgen J.S. Antsilevich * We intentionally do not OR flags with SNOOP_OPEN, but set them so 393019b4d63SUgen J.S. Antsilevich * all previous settings (especially SNOOP_OFLOW) will be cleared. 394019b4d63SUgen J.S. Antsilevich */ 395019b4d63SUgen J.S. Antsilevich snp->snp_flags = SNOOP_OPEN; 396dde8a05bSUgen J.S. Antsilevich 39776e90dbcSBrian Feldman snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 398dde8a05bSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 399dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 400dde8a05bSUgen J.S. Antsilevich snp->snp_len = 0; 401dde8a05bSUgen J.S. Antsilevich 402dde8a05bSUgen J.S. Antsilevich /* 4030739a0dcSUgen J.S. Antsilevich * snp_tty == NULL is for inactive snoop devices. 404dde8a05bSUgen J.S. Antsilevich */ 4050739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 406784fc12fSPoul-Henning Kamp snp->snp_target = NODEV; 407ac60b28dSDima Dorfman 408ac60b28dSDima Dorfman LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 409dde8a05bSUgen J.S. Antsilevich return (0); 410dde8a05bSUgen J.S. Antsilevich } 411dde8a05bSUgen J.S. Antsilevich 412019b4d63SUgen J.S. Antsilevich 41387b6de2bSPoul-Henning Kamp static int 414019b4d63SUgen J.S. Antsilevich snp_detach(snp) 415019b4d63SUgen J.S. Antsilevich struct snoop *snp; 416019b4d63SUgen J.S. Antsilevich { 417019b4d63SUgen J.S. Antsilevich struct tty *tp; 418019b4d63SUgen J.S. Antsilevich 419019b4d63SUgen J.S. Antsilevich snp->snp_base = 0; 420019b4d63SUgen J.S. Antsilevich snp->snp_len = 0; 421019b4d63SUgen J.S. Antsilevich 422019b4d63SUgen J.S. Antsilevich /* 423019b4d63SUgen J.S. Antsilevich * If line disc. changed we do not touch this pointer, SLIP/PPP will 424019b4d63SUgen J.S. Antsilevich * change it anyway. 425019b4d63SUgen J.S. Antsilevich */ 426f09f49f1SDima Dorfman tp = snp->snp_tty; 427f09f49f1SDima Dorfman if (tp == NULL) 428964587caSUgen J.S. Antsilevich goto detach_notty; 429019b4d63SUgen J.S. Antsilevich 43077f77631SPaul Traina if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 431f09f49f1SDima Dorfman tp->t_line == snooplinedisc) { 432019b4d63SUgen J.S. Antsilevich tp->t_sc = NULL; 433019b4d63SUgen J.S. Antsilevich tp->t_state &= ~TS_SNOOP; 43447eaa5f5SDima Dorfman tp->t_line = snp->snp_olddisc; 435019b4d63SUgen J.S. Antsilevich } else 436019b4d63SUgen J.S. Antsilevich printf("Snoop: bad attached tty data.\n"); 437019b4d63SUgen J.S. Antsilevich 4380739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 439784fc12fSPoul-Henning Kamp snp->snp_target = NODEV; 440019b4d63SUgen J.S. Antsilevich 441964587caSUgen J.S. Antsilevich detach_notty: 442019b4d63SUgen J.S. Antsilevich selwakeup(&snp->snp_sel); 443019b4d63SUgen J.S. Antsilevich snp->snp_sel.si_pid = 0; 44476e90dbcSBrian Feldman if ((snp->snp_flags & SNOOP_OPEN) == 0) 44576e90dbcSBrian Feldman free(snp, M_SNP); 446019b4d63SUgen J.S. Antsilevich 447019b4d63SUgen J.S. Antsilevich return (0); 448019b4d63SUgen J.S. Antsilevich } 449019b4d63SUgen J.S. Antsilevich 45087f6c662SJulian Elischer static int 45160039670SBruce Evans snpclose(dev, flags, fmt, p) 452dde8a05bSUgen J.S. Antsilevich dev_t dev; 45360039670SBruce Evans int flags; 45460039670SBruce Evans int fmt; 45560039670SBruce Evans struct proc *p; 456dde8a05bSUgen J.S. Antsilevich { 457f09f49f1SDima Dorfman struct snoop *snp; 458dde8a05bSUgen J.S. Antsilevich 459f09f49f1SDima Dorfman snp = dev->si_drv1; 460dde8a05bSUgen J.S. Antsilevich snp->snp_blen = 0; 461ac60b28dSDima Dorfman LIST_REMOVE(snp, snp_list); 46276e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 463019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OPEN; 46476e90dbcSBrian Feldman dev->si_drv1 = NULL; 46525c7870eSDima Dorfman destroy_dev(dev); 466dde8a05bSUgen J.S. Antsilevich 467019b4d63SUgen J.S. Antsilevich return (snp_detach(snp)); 468dde8a05bSUgen J.S. Antsilevich } 469dde8a05bSUgen J.S. Antsilevich 470f09f49f1SDima Dorfman static int 471f09f49f1SDima Dorfman snp_down(snp) 472964587caSUgen J.S. Antsilevich struct snoop *snp; 473964587caSUgen J.S. Antsilevich { 47476e90dbcSBrian Feldman 47576e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 47676e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 47776e90dbcSBrian Feldman snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 478964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 47976e90dbcSBrian Feldman } 480964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 481dde8a05bSUgen J.S. Antsilevich 482964587caSUgen J.S. Antsilevich return (snp_detach(snp)); 483964587caSUgen J.S. Antsilevich } 484dde8a05bSUgen J.S. Antsilevich 48587f6c662SJulian Elischer static int 48660039670SBruce Evans snpioctl(dev, cmd, data, flags, p) 487dde8a05bSUgen J.S. Antsilevich dev_t dev; 488ecbb00a2SDoug Rabson u_long cmd; 489dde8a05bSUgen J.S. Antsilevich caddr_t data; 49060039670SBruce Evans int flags; 49160039670SBruce Evans struct proc *p; 492dde8a05bSUgen J.S. Antsilevich { 493f09f49f1SDima Dorfman struct snoop *snp; 494dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 495f09f49f1SDima Dorfman dev_t tdev; 49676e90dbcSBrian Feldman int s; 497dde8a05bSUgen J.S. Antsilevich 498f09f49f1SDima Dorfman snp = dev->si_drv1; 499dde8a05bSUgen J.S. Antsilevich switch (cmd) { 500dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 501212dfe6fSBruce Evans tdev = udev2dev(*((udev_t *)data), 0); 502784fc12fSPoul-Henning Kamp if (tdev == NODEV) 503f09f49f1SDima Dorfman return (snp_down(snp)); 504964587caSUgen J.S. Antsilevich 505444f003cSBruce Evans tp = snpdevtotty(tdev); 50677f77631SPaul Traina if (!tp) 507dde8a05bSUgen J.S. Antsilevich return (EINVAL); 508dde8a05bSUgen J.S. Antsilevich 509dde8a05bSUgen J.S. Antsilevich s = spltty(); 51077f77631SPaul Traina 511784fc12fSPoul-Henning Kamp if (snp->snp_target == NODEV) { 5120739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 51377f77631SPaul Traina if (tpo) 514dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 515dde8a05bSUgen J.S. Antsilevich } 51677f77631SPaul Traina 517dde8a05bSUgen J.S. Antsilevich tp->t_sc = (caddr_t)snp; 518dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 51947eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 520f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5210739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 52277f77631SPaul Traina snp->snp_target = tdev; 52377f77631SPaul Traina 524964587caSUgen J.S. Antsilevich /* 525964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 526964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 527964587caSUgen J.S. Antsilevich */ 528964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 529019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 530dde8a05bSUgen J.S. Antsilevich splx(s); 531dde8a05bSUgen J.S. Antsilevich break; 53277f77631SPaul Traina 533dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5340739a0dcSUgen J.S. Antsilevich /* 5350739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5360739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5370739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5380739a0dcSUgen J.S. Antsilevich */ 53977f77631SPaul Traina *((dev_t *)data) = snp->snp_target; 540dde8a05bSUgen J.S. Antsilevich break; 541dde8a05bSUgen J.S. Antsilevich 542dde8a05bSUgen J.S. Antsilevich case FIONBIO: 543dde8a05bSUgen J.S. Antsilevich break; 54477f77631SPaul Traina 545dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 546dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 547dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 548dde8a05bSUgen J.S. Antsilevich else 549dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 550dde8a05bSUgen J.S. Antsilevich break; 55177f77631SPaul Traina 552dde8a05bSUgen J.S. Antsilevich case FIONREAD: 553dde8a05bSUgen J.S. Antsilevich s = spltty(); 5540739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 555dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 556dde8a05bSUgen J.S. Antsilevich else 557964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 558964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 559964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 560964587caSUgen J.S. Antsilevich else 561964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 562964587caSUgen J.S. Antsilevich } else { 563964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 564964587caSUgen J.S. Antsilevich } 565dde8a05bSUgen J.S. Antsilevich splx(s); 566dde8a05bSUgen J.S. Antsilevich break; 56777f77631SPaul Traina 568dde8a05bSUgen J.S. Antsilevich default: 569dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 570dde8a05bSUgen J.S. Antsilevich } 571dde8a05bSUgen J.S. Antsilevich return (0); 572dde8a05bSUgen J.S. Antsilevich } 573dde8a05bSUgen J.S. Antsilevich 57487f6c662SJulian Elischer static int 575659ffb48SPeter Wemm snppoll(dev, events, p) 576dde8a05bSUgen J.S. Antsilevich dev_t dev; 577659ffb48SPeter Wemm int events; 578dde8a05bSUgen J.S. Antsilevich struct proc *p; 579dde8a05bSUgen J.S. Antsilevich { 580f09f49f1SDima Dorfman struct snoop *snp; 581f09f49f1SDima Dorfman int revents; 582dde8a05bSUgen J.S. Antsilevich 583f09f49f1SDima Dorfman snp = dev->si_drv1; 584f09f49f1SDima Dorfman revents = 0; 585019b4d63SUgen J.S. Antsilevich /* 586659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 587019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 588019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 589019b4d63SUgen J.S. Antsilevich */ 590dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 591659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 592659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 593659ffb48SPeter Wemm else 594dde8a05bSUgen J.S. Antsilevich selrecord(p, &snp->snp_sel); 595dfd5dee1SPeter Wemm } 596659ffb48SPeter Wemm return (revents); 597dde8a05bSUgen J.S. Antsilevich } 598dde8a05bSUgen J.S. Antsilevich 59987f6c662SJulian Elischer static void 600f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev) 601f09f49f1SDima Dorfman void *arg; 602f09f49f1SDima Dorfman char *name; 603f09f49f1SDima Dorfman int namelen; 604f09f49f1SDima Dorfman dev_t *dev; 60525c7870eSDima Dorfman { 60625c7870eSDima Dorfman int u; 60725c7870eSDima Dorfman 60825c7870eSDima Dorfman if (*dev != NODEV) 60925c7870eSDima Dorfman return; 61025c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 61125c7870eSDima Dorfman return; 61225c7870eSDima Dorfman *dev = make_dev(&snp_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, 0600, 61325c7870eSDima Dorfman "snp%d", u); 61425c7870eSDima Dorfman } 61525c7870eSDima Dorfman 61647eaa5f5SDima Dorfman static int 617f09f49f1SDima Dorfman snp_modevent(mod, type, data) 618f09f49f1SDima Dorfman module_t mod; 619f09f49f1SDima Dorfman int type; 620f09f49f1SDima Dorfman void *data; 62153ac6efbSJulian Elischer { 622f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 62353ac6efbSJulian Elischer 62447eaa5f5SDima Dorfman switch (type) { 62547eaa5f5SDima Dorfman case MOD_LOAD: 626f09f49f1SDima Dorfman /* XXX error checking. */ 62747eaa5f5SDima Dorfman eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000); 628f09f49f1SDima Dorfman snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 62976e90dbcSBrian Feldman cdevsw_add(&snp_cdevsw); 63047eaa5f5SDima Dorfman break; 63147eaa5f5SDima Dorfman case MOD_UNLOAD: 632ac60b28dSDima Dorfman if (!LIST_EMPTY(&snp_sclist)) 633ac60b28dSDima Dorfman return (EBUSY); 63447eaa5f5SDima Dorfman EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 635f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 63647eaa5f5SDima Dorfman cdevsw_remove(&snp_cdevsw); 63747eaa5f5SDima Dorfman break; 63847eaa5f5SDima Dorfman default: 63947eaa5f5SDima Dorfman break; 64047eaa5f5SDima Dorfman } 641f09f49f1SDima Dorfman return (0); 6427198bf47SJulian Elischer } 64353ac6efbSJulian Elischer 64447eaa5f5SDima Dorfman static moduledata_t snp_mod = { 64547eaa5f5SDima Dorfman "snp", 64647eaa5f5SDima Dorfman snp_modevent, 64747eaa5f5SDima Dorfman NULL 64847eaa5f5SDima Dorfman }; 64947eaa5f5SDima Dorfman DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR); 650