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 * 15dde8a05bSUgen J.S. Antsilevich */ 16dde8a05bSUgen J.S. Antsilevich 17945ff31aSDavid E. O'Brien #include <sys/cdefs.h> 18945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 19945ff31aSDavid E. O'Brien 20dde8a05bSUgen J.S. Antsilevich #include <sys/param.h> 21dde8a05bSUgen J.S. Antsilevich #include <sys/systm.h> 2271455815SBruce Evans #include <sys/filio.h> 23a1c995b6SPoul-Henning Kamp #include <sys/malloc.h> 24dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h> 25dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h> 26659ffb48SPeter Wemm #include <sys/poll.h> 27dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h> 28fe12f24bSPoul-Henning Kamp #include <sys/module.h> 29ac60b28dSDima Dorfman #include <sys/queue.h> 3087f6c662SJulian Elischer #include <sys/snoop.h> 31d96bc99dSBruce Evans #include <sys/vnode.h> 3287f6c662SJulian Elischer 33ddf5b796SDima Dorfman static l_close_t snplclose; 34f09f49f1SDima Dorfman static l_write_t snplwrite; 3587f6c662SJulian Elischer static d_open_t snpopen; 3687f6c662SJulian Elischer static d_close_t snpclose; 3787f6c662SJulian Elischer static d_read_t snpread; 3887f6c662SJulian Elischer static d_write_t snpwrite; 3987f6c662SJulian Elischer static d_ioctl_t snpioctl; 40659ffb48SPeter Wemm static d_poll_t snppoll; 4187f6c662SJulian Elischer 424e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = { 43dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 4463a97efcSBruce Evans .d_flags = D_PSEUDO | D_NEEDGIANT, 457ac40f5fSPoul-Henning Kamp .d_open = snpopen, 467ac40f5fSPoul-Henning Kamp .d_close = snpclose, 477ac40f5fSPoul-Henning Kamp .d_read = snpread, 487ac40f5fSPoul-Henning Kamp .d_write = snpwrite, 497ac40f5fSPoul-Henning Kamp .d_ioctl = snpioctl, 507ac40f5fSPoul-Henning Kamp .d_poll = snppoll, 517ac40f5fSPoul-Henning Kamp .d_name = "snp", 524e2f199eSPoul-Henning Kamp }; 5387f6c662SJulian Elischer 54f09f49f1SDima Dorfman static struct linesw snpdisc = { 55ddf5b796SDima Dorfman ttyopen, snplclose, ttread, snplwrite, 56f09f49f1SDima Dorfman l_nullioctl, ttyinput, ttstart, ttymodem 57f09f49f1SDima Dorfman }; 5853ac6efbSJulian Elischer 59101f105dSDima Dorfman /* 60101f105dSDima Dorfman * This is the main snoop per-device structure. 61101f105dSDima Dorfman */ 62101f105dSDima Dorfman struct snoop { 63101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 64cdd530caSDima Dorfman int snp_unit; /* Device number. */ 65101f105dSDima Dorfman dev_t snp_target; /* Target tty device. */ 66101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 67101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 68101f105dSDima Dorfman u_long snp_base; /* Data base. */ 69101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 70101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 71101f105dSDima Dorfman int snp_flags; /* Flags. */ 72101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 73101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 74101f105dSDima Dorfman }; 75101f105dSDima Dorfman 76101f105dSDima Dorfman /* 77101f105dSDima Dorfman * Possible flags. 78101f105dSDima Dorfman */ 79101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 80101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 81101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 82101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 83101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 84101f105dSDima Dorfman 85101f105dSDima Dorfman /* 86101f105dSDima Dorfman * Other constants. 87101f105dSDima Dorfman */ 88101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 89101f105dSDima Dorfman * 4K tested to be the minimum 90101f105dSDima Dorfman * for which on normal tty 91101f105dSDima Dorfman * usage there is no need to 92101f105dSDima Dorfman * allocate more. 93101f105dSDima Dorfman */ 94101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 95101f105dSDima Dorfman * If we grow more,something 96101f105dSDima Dorfman * really bad in this world.. 97101f105dSDima Dorfman */ 98101f105dSDima Dorfman 99959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 10047eaa5f5SDima Dorfman /* 10147eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 10247eaa5f5SDima Dorfman * module load time. 10347eaa5f5SDima Dorfman */ 104f09f49f1SDima Dorfman static int snooplinedisc; 105ac60b28dSDima Dorfman 106ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 107b0b03348SPoul-Henning Kamp static struct clonedevs *snpclones; 108ac60b28dSDima Dorfman 109e51a25f8SAlfred Perlstein static struct tty *snpdevtotty(dev_t dev); 110e51a25f8SAlfred Perlstein static void snp_clone(void *arg, char *name, 111e51a25f8SAlfred Perlstein int namelen, dev_t *dev); 112e51a25f8SAlfred Perlstein static int snp_detach(struct snoop *snp); 113e51a25f8SAlfred Perlstein static int snp_down(struct snoop *snp); 114e51a25f8SAlfred Perlstein static int snp_in(struct snoop *snp, char *buf, int n); 115e51a25f8SAlfred Perlstein static int snp_modevent(module_t mod, int what, void *arg); 11647eaa5f5SDima Dorfman 11747eaa5f5SDima Dorfman static int 118ddf5b796SDima Dorfman snplclose(tp, flag) 119ddf5b796SDima Dorfman struct tty *tp; 120ddf5b796SDima Dorfman int flag; 121ddf5b796SDima Dorfman { 122ddf5b796SDima Dorfman struct snoop *snp; 123ddf5b796SDima Dorfman int error; 124ddf5b796SDima Dorfman 125ddf5b796SDima Dorfman snp = tp->t_sc; 126ddf5b796SDima Dorfman error = snp_down(snp); 127ddf5b796SDima Dorfman if (error != 0) 128ddf5b796SDima Dorfman return (error); 129ddf5b796SDima Dorfman error = ttylclose(tp, flag); 130ddf5b796SDima Dorfman return (error); 131ddf5b796SDima Dorfman } 132ddf5b796SDima Dorfman 133ddf5b796SDima Dorfman static int 134f09f49f1SDima Dorfman snplwrite(tp, uio, flag) 135f09f49f1SDima Dorfman struct tty *tp; 136f09f49f1SDima Dorfman struct uio *uio; 137f09f49f1SDima Dorfman int flag; 13847eaa5f5SDima Dorfman { 13947eaa5f5SDima Dorfman struct iovec iov; 14047eaa5f5SDima Dorfman struct uio uio2; 141f09f49f1SDima Dorfman struct snoop *snp; 142f09f49f1SDima Dorfman int error, ilen; 143e692c40cSDima Dorfman char *ibuf; 14447eaa5f5SDima Dorfman 145e692c40cSDima Dorfman error = 0; 146e692c40cSDima Dorfman ibuf = NULL; 147f09f49f1SDima Dorfman snp = tp->t_sc; 14887826386SDima Dorfman while (uio->uio_resid > 0) { 149e692c40cSDima Dorfman ilen = imin(512, uio->uio_resid); 150a163d034SWarner Losh ibuf = malloc(ilen, M_SNP, M_WAITOK); 15147eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 152f09f49f1SDima Dorfman if (error != 0) 153e692c40cSDima Dorfman break; 154f09f49f1SDima Dorfman snp_in(snp, ibuf, ilen); 155f09f49f1SDima Dorfman /* Hackish, but probably the least of all evils. */ 15647eaa5f5SDima Dorfman iov.iov_base = ibuf; 15747eaa5f5SDima Dorfman iov.iov_len = ilen; 15847eaa5f5SDima Dorfman uio2.uio_iov = &iov; 15947eaa5f5SDima Dorfman uio2.uio_iovcnt = 1; 16047eaa5f5SDima Dorfman uio2.uio_offset = 0; 16147eaa5f5SDima Dorfman uio2.uio_resid = ilen; 16247eaa5f5SDima Dorfman uio2.uio_segflg = UIO_SYSSPACE; 16347eaa5f5SDima Dorfman uio2.uio_rw = UIO_WRITE; 164b40ce416SJulian Elischer uio2.uio_td = uio->uio_td; 16547eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 166f09f49f1SDima Dorfman if (error != 0) 167e692c40cSDima Dorfman break; 168e692c40cSDima Dorfman free(ibuf, M_SNP); 169e692c40cSDima Dorfman ibuf = NULL; 17047eaa5f5SDima Dorfman } 171e692c40cSDima Dorfman if (ibuf != NULL) 172e692c40cSDima Dorfman free(ibuf, M_SNP); 173e692c40cSDima Dorfman return (error); 174ddf5b796SDima Dorfman } 17547eaa5f5SDima Dorfman 17677f77631SPaul Traina static struct tty * 177444f003cSBruce Evans snpdevtotty(dev) 17877f77631SPaul Traina dev_t dev; 17977f77631SPaul Traina { 180444f003cSBruce Evans struct cdevsw *cdp; 18177f77631SPaul Traina 1824be2eb8cSPoul-Henning Kamp cdp = devsw(dev); 18387826386SDima Dorfman if (cdp == NULL || (cdp->d_flags & D_TTY) == 0) 184444f003cSBruce Evans return (NULL); 18587826386SDima Dorfman return (dev->si_tty); 18677f77631SPaul Traina } 18777f77631SPaul Traina 1880739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 1890739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 1900739a0dcSUgen J.S. Antsilevich * length for function keys... 1910739a0dcSUgen J.S. Antsilevich */ 1920739a0dcSUgen J.S. Antsilevich 19387f6c662SJulian Elischer static int 1940739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag) 1950739a0dcSUgen J.S. Antsilevich dev_t dev; 1960739a0dcSUgen J.S. Antsilevich struct uio *uio; 1970739a0dcSUgen J.S. Antsilevich int flag; 1980739a0dcSUgen J.S. Antsilevich { 199f09f49f1SDima Dorfman struct snoop *snp; 2000739a0dcSUgen J.S. Antsilevich struct tty *tp; 201f09f49f1SDima Dorfman int error, i, len; 202f4c5dfbbSDima Dorfman unsigned char c[SNP_INPUT_BUF]; 2030739a0dcSUgen J.S. Antsilevich 204f09f49f1SDima Dorfman snp = dev->si_drv1; 2050739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 206f09f49f1SDima Dorfman if (tp == NULL) 207f09f49f1SDima Dorfman return (EIO); 2080739a0dcSUgen J.S. Antsilevich if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 209f09f49f1SDima Dorfman tp->t_line == snooplinedisc) 2100739a0dcSUgen J.S. Antsilevich goto tty_input; 2110739a0dcSUgen J.S. Antsilevich 212cdd530caSDima Dorfman printf("snp%d: attempt to write to bad tty\n", snp->snp_unit); 2130739a0dcSUgen J.S. Antsilevich return (EIO); 2140739a0dcSUgen J.S. Antsilevich 2150739a0dcSUgen J.S. Antsilevich tty_input: 2160739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2170739a0dcSUgen J.S. Antsilevich return (EIO); 2180739a0dcSUgen J.S. Antsilevich 2190739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 22087826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2210739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2220739a0dcSUgen J.S. Antsilevich return (error); 2230739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2240739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2250739a0dcSUgen J.S. Antsilevich return (EIO); 2260739a0dcSUgen J.S. Antsilevich } 2270739a0dcSUgen J.S. Antsilevich } 228f09f49f1SDima Dorfman return (0); 2290739a0dcSUgen J.S. Antsilevich } 2300739a0dcSUgen J.S. Antsilevich 2310739a0dcSUgen J.S. Antsilevich 23287f6c662SJulian Elischer static int 233dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag) 234dde8a05bSUgen J.S. Antsilevich dev_t dev; 235dde8a05bSUgen J.S. Antsilevich struct uio *uio; 236dde8a05bSUgen J.S. Antsilevich int flag; 237dde8a05bSUgen J.S. Antsilevich { 238f09f49f1SDima Dorfman struct snoop *snp; 239f09f49f1SDima Dorfman int error, len, n, nblen, s; 240dde8a05bSUgen J.S. Antsilevich caddr_t from; 241dde8a05bSUgen J.S. Antsilevich char *nbuf; 242dde8a05bSUgen J.S. Antsilevich 243f09f49f1SDima Dorfman snp = dev->si_drv1; 2445526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2455526d2d9SEivind Eklund ("snoop buffer error")); 24677f77631SPaul Traina 2470739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 248dde8a05bSUgen J.S. Antsilevich return (EIO); 249dde8a05bSUgen J.S. Antsilevich 250dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 251dde8a05bSUgen J.S. Antsilevich 252dde8a05bSUgen J.S. Antsilevich do { 253dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 254d96bc99dSBruce Evans if (flag & IO_NDELAY) 255d96bc99dSBruce Evans return (EWOULDBLOCK); 256dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 257521f364bSDag-Erling Smørgrav error = tsleep(snp, (PZERO + 1) | PCATCH, 2582c1a6f21SDima Dorfman "snprd", 0); 2592c1a6f21SDima Dorfman if (error != 0) 2602c1a6f21SDima Dorfman return (error); 261dde8a05bSUgen J.S. Antsilevich } 262dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 263dde8a05bSUgen J.S. Antsilevich 264019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 265dde8a05bSUgen J.S. Antsilevich 266f09f49f1SDima Dorfman error = 0; 267dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 26887826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 269dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 270dde8a05bSUgen J.S. Antsilevich if (len == 0) 271dde8a05bSUgen J.S. Antsilevich break; 272dde8a05bSUgen J.S. Antsilevich 273dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 274dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 275dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 276dde8a05bSUgen J.S. Antsilevich } 277019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 278dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 279019b4d63SUgen J.S. Antsilevich } 280dde8a05bSUgen J.S. Antsilevich s = spltty(); 281dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 282dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 283f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 284dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 28576e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 286dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 28776e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 288dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 289dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 290dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 291dde8a05bSUgen J.S. Antsilevich } 292dde8a05bSUgen J.S. Antsilevich } 293dde8a05bSUgen J.S. Antsilevich splx(s); 294dde8a05bSUgen J.S. Antsilevich 295f09f49f1SDima Dorfman return (error); 296dde8a05bSUgen J.S. Antsilevich } 297dde8a05bSUgen J.S. Antsilevich 298f09f49f1SDima Dorfman static int 299f09f49f1SDima Dorfman snp_in(snp, buf, n) 300dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 301dde8a05bSUgen J.S. Antsilevich char *buf; 302dde8a05bSUgen J.S. Antsilevich int n; 303dde8a05bSUgen J.S. Antsilevich { 304dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 305dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 306dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 307dde8a05bSUgen J.S. Antsilevich char *nbuf; 308dde8a05bSUgen J.S. Antsilevich 309219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 310dde8a05bSUgen J.S. Antsilevich 311dde8a05bSUgen J.S. Antsilevich if (n == 0) 312f09f49f1SDima Dorfman return (0); 313dde8a05bSUgen J.S. Antsilevich 314019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 315cdd530caSDima Dorfman printf("snp%d: more data to down interface\n", snp->snp_unit); 316f09f49f1SDima Dorfman return (0); 317019b4d63SUgen J.S. Antsilevich } 318964587caSUgen J.S. Antsilevich 319019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 320cdd530caSDima Dorfman printf("snp%d: buffer overflow\n", snp->snp_unit); 321019b4d63SUgen J.S. Antsilevich /* 322019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 323019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 324019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 325019b4d63SUgen J.S. Antsilevich * snooping and retry... 326019b4d63SUgen J.S. Antsilevich */ 327dde8a05bSUgen J.S. Antsilevich 328f09f49f1SDima Dorfman return (snp_down(snp)); 329019b4d63SUgen J.S. Antsilevich } 330019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 331019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 332dde8a05bSUgen J.S. Antsilevich 333dde8a05bSUgen J.S. Antsilevich 334dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 335dde8a05bSUgen J.S. Antsilevich s = spltty(); 336dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 337dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 338dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 339dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 340dde8a05bSUgen J.S. Antsilevich } 34176e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 342dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 34376e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 344dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 345dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 346dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 347dde8a05bSUgen J.S. Antsilevich } else { 348dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 349dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 350dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 351521f364bSDag-Erling Smørgrav wakeup(snp); 352dde8a05bSUgen J.S. Antsilevich } 353dde8a05bSUgen J.S. Antsilevich splx(s); 354f09f49f1SDima Dorfman return (0); 355dde8a05bSUgen J.S. Antsilevich } 356dde8a05bSUgen J.S. Antsilevich splx(s); 357dde8a05bSUgen J.S. Antsilevich } 358dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 359dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 360dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 361dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 362dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 363dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 364dde8a05bSUgen J.S. Antsilevich } 365dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 366dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 367dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 368dde8a05bSUgen J.S. Antsilevich 369dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 370dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 371521f364bSDag-Erling Smørgrav wakeup(snp); 372dde8a05bSUgen J.S. Antsilevich } 373512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 374dde8a05bSUgen J.S. Antsilevich 375f09f49f1SDima Dorfman return (n); 376dde8a05bSUgen J.S. Antsilevich } 377dde8a05bSUgen J.S. Antsilevich 37887f6c662SJulian Elischer static int 379b40ce416SJulian Elischer snpopen(dev, flag, mode, td) 380dde8a05bSUgen J.S. Antsilevich dev_t dev; 381dde8a05bSUgen J.S. Antsilevich int flag, mode; 382b40ce416SJulian Elischer struct thread *td; 383dde8a05bSUgen J.S. Antsilevich { 384dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 385dde8a05bSUgen J.S. Antsilevich 38676e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 387b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CHEAPCLONE; 388f09f49f1SDima Dorfman dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, 389a163d034SWarner Losh M_WAITOK | M_ZERO); 390cdd530caSDima Dorfman snp->snp_unit = dev2unit(dev); 39176e90dbcSBrian Feldman } else 39276e90dbcSBrian Feldman return (EBUSY); 39377f77631SPaul Traina 394019b4d63SUgen J.S. Antsilevich /* 395019b4d63SUgen J.S. Antsilevich * We intentionally do not OR flags with SNOOP_OPEN, but set them so 396019b4d63SUgen J.S. Antsilevich * all previous settings (especially SNOOP_OFLOW) will be cleared. 397019b4d63SUgen J.S. Antsilevich */ 398019b4d63SUgen J.S. Antsilevich snp->snp_flags = SNOOP_OPEN; 399dde8a05bSUgen J.S. Antsilevich 400a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 401dde8a05bSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 402dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 403dde8a05bSUgen J.S. Antsilevich snp->snp_len = 0; 404dde8a05bSUgen J.S. Antsilevich 405dde8a05bSUgen J.S. Antsilevich /* 4060739a0dcSUgen J.S. Antsilevich * snp_tty == NULL is for inactive snoop devices. 407dde8a05bSUgen J.S. Antsilevich */ 4080739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 409784fc12fSPoul-Henning Kamp snp->snp_target = NODEV; 410ac60b28dSDima Dorfman 411ac60b28dSDima Dorfman LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 412dde8a05bSUgen J.S. Antsilevich return (0); 413dde8a05bSUgen J.S. Antsilevich } 414dde8a05bSUgen J.S. Antsilevich 415019b4d63SUgen J.S. Antsilevich 41687b6de2bSPoul-Henning Kamp static int 417019b4d63SUgen J.S. Antsilevich snp_detach(snp) 418019b4d63SUgen J.S. Antsilevich struct snoop *snp; 419019b4d63SUgen J.S. Antsilevich { 420019b4d63SUgen J.S. Antsilevich struct tty *tp; 421019b4d63SUgen J.S. Antsilevich 422019b4d63SUgen J.S. Antsilevich snp->snp_base = 0; 423019b4d63SUgen J.S. Antsilevich snp->snp_len = 0; 424019b4d63SUgen J.S. Antsilevich 425019b4d63SUgen J.S. Antsilevich /* 426019b4d63SUgen J.S. Antsilevich * If line disc. changed we do not touch this pointer, SLIP/PPP will 427019b4d63SUgen J.S. Antsilevich * change it anyway. 428019b4d63SUgen J.S. Antsilevich */ 429f09f49f1SDima Dorfman tp = snp->snp_tty; 430f09f49f1SDima Dorfman if (tp == NULL) 431964587caSUgen J.S. Antsilevich goto detach_notty; 432019b4d63SUgen J.S. Antsilevich 43377f77631SPaul Traina if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 434f09f49f1SDima Dorfman tp->t_line == snooplinedisc) { 435019b4d63SUgen J.S. Antsilevich tp->t_sc = NULL; 436019b4d63SUgen J.S. Antsilevich tp->t_state &= ~TS_SNOOP; 43747eaa5f5SDima Dorfman tp->t_line = snp->snp_olddisc; 438019b4d63SUgen J.S. Antsilevich } else 439cdd530caSDima Dorfman printf("snp%d: bad attached tty data\n", snp->snp_unit); 440019b4d63SUgen J.S. Antsilevich 4410739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 442784fc12fSPoul-Henning Kamp snp->snp_target = NODEV; 443019b4d63SUgen J.S. Antsilevich 444964587caSUgen J.S. Antsilevich detach_notty: 445512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 44676e90dbcSBrian Feldman if ((snp->snp_flags & SNOOP_OPEN) == 0) 44776e90dbcSBrian Feldman free(snp, M_SNP); 448019b4d63SUgen J.S. Antsilevich 449019b4d63SUgen J.S. Antsilevich return (0); 450019b4d63SUgen J.S. Antsilevich } 451019b4d63SUgen J.S. Antsilevich 45287f6c662SJulian Elischer static int 453b40ce416SJulian Elischer snpclose(dev, flags, fmt, td) 454dde8a05bSUgen J.S. Antsilevich dev_t dev; 45560039670SBruce Evans int flags; 45660039670SBruce Evans int fmt; 457b40ce416SJulian Elischer struct thread *td; 458dde8a05bSUgen J.S. Antsilevich { 459f09f49f1SDima Dorfman struct snoop *snp; 460dde8a05bSUgen J.S. Antsilevich 461f09f49f1SDima Dorfman snp = dev->si_drv1; 462dde8a05bSUgen J.S. Antsilevich snp->snp_blen = 0; 463ac60b28dSDima Dorfman LIST_REMOVE(snp, snp_list); 46476e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 465019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OPEN; 46676e90dbcSBrian Feldman dev->si_drv1 = NULL; 467b0b03348SPoul-Henning Kamp destroy_dev(dev); 468dde8a05bSUgen J.S. Antsilevich 469019b4d63SUgen J.S. Antsilevich return (snp_detach(snp)); 470dde8a05bSUgen J.S. Antsilevich } 471dde8a05bSUgen J.S. Antsilevich 472f09f49f1SDima Dorfman static int 473f09f49f1SDima Dorfman snp_down(snp) 474964587caSUgen J.S. Antsilevich struct snoop *snp; 475964587caSUgen J.S. Antsilevich { 47676e90dbcSBrian Feldman 47776e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 47876e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 479a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 480964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 48176e90dbcSBrian Feldman } 482964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 483dde8a05bSUgen J.S. Antsilevich 484964587caSUgen J.S. Antsilevich return (snp_detach(snp)); 485964587caSUgen J.S. Antsilevich } 486dde8a05bSUgen J.S. Antsilevich 48787f6c662SJulian Elischer static int 488b40ce416SJulian Elischer snpioctl(dev, cmd, data, flags, td) 489dde8a05bSUgen J.S. Antsilevich dev_t dev; 490ecbb00a2SDoug Rabson u_long cmd; 491dde8a05bSUgen J.S. Antsilevich caddr_t data; 49260039670SBruce Evans int flags; 493b40ce416SJulian Elischer struct thread *td; 494dde8a05bSUgen J.S. Antsilevich { 495f09f49f1SDima Dorfman struct snoop *snp; 496dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 497f09f49f1SDima Dorfman dev_t tdev; 49876e90dbcSBrian Feldman int s; 499dde8a05bSUgen J.S. Antsilevich 500f09f49f1SDima Dorfman snp = dev->si_drv1; 501dde8a05bSUgen J.S. Antsilevich switch (cmd) { 502dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 503816d62bbSPoul-Henning Kamp tdev = udev2dev(*((udev_t *)data)); 504784fc12fSPoul-Henning Kamp if (tdev == NODEV) 505f09f49f1SDima Dorfman return (snp_down(snp)); 506964587caSUgen J.S. Antsilevich 507444f003cSBruce Evans tp = snpdevtotty(tdev); 50877f77631SPaul Traina if (!tp) 509dde8a05bSUgen J.S. Antsilevich return (EINVAL); 5102c9fb909SDima Dorfman if (tp->t_state & TS_SNOOP) 5112c9fb909SDima Dorfman return (EBUSY); 512dde8a05bSUgen J.S. Antsilevich 513dde8a05bSUgen J.S. Antsilevich s = spltty(); 51477f77631SPaul Traina 515784fc12fSPoul-Henning Kamp if (snp->snp_target == NODEV) { 5160739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 51777f77631SPaul Traina if (tpo) 518dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 519dde8a05bSUgen J.S. Antsilevich } 52077f77631SPaul Traina 521dde8a05bSUgen J.S. Antsilevich tp->t_sc = (caddr_t)snp; 522dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 52347eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 524f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5250739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 52677f77631SPaul Traina snp->snp_target = tdev; 52777f77631SPaul Traina 528964587caSUgen J.S. Antsilevich /* 529964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 530964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 531964587caSUgen J.S. Antsilevich */ 532964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 533019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 534dde8a05bSUgen J.S. Antsilevich splx(s); 535dde8a05bSUgen J.S. Antsilevich break; 53677f77631SPaul Traina 537dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5380739a0dcSUgen J.S. Antsilevich /* 5390739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5400739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5410739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5420739a0dcSUgen J.S. Antsilevich */ 54320207b60SThomas Moestl *((udev_t *)data) = dev2udev(snp->snp_target); 544dde8a05bSUgen J.S. Antsilevich break; 545dde8a05bSUgen J.S. Antsilevich 546dde8a05bSUgen J.S. Antsilevich case FIONBIO: 547dde8a05bSUgen J.S. Antsilevich break; 54877f77631SPaul Traina 549dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 550dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 551dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 552dde8a05bSUgen J.S. Antsilevich else 553dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 554dde8a05bSUgen J.S. Antsilevich break; 55577f77631SPaul Traina 556dde8a05bSUgen J.S. Antsilevich case FIONREAD: 557dde8a05bSUgen J.S. Antsilevich s = spltty(); 5580739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 559dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 560dde8a05bSUgen J.S. Antsilevich else 561964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 562964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 563964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 564964587caSUgen J.S. Antsilevich else 565964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 566964587caSUgen J.S. Antsilevich } else { 567964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 568964587caSUgen J.S. Antsilevich } 569dde8a05bSUgen J.S. Antsilevich splx(s); 570dde8a05bSUgen J.S. Antsilevich break; 57177f77631SPaul Traina 572dde8a05bSUgen J.S. Antsilevich default: 573dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 574dde8a05bSUgen J.S. Antsilevich } 575dde8a05bSUgen J.S. Antsilevich return (0); 576dde8a05bSUgen J.S. Antsilevich } 577dde8a05bSUgen J.S. Antsilevich 57887f6c662SJulian Elischer static int 579b40ce416SJulian Elischer snppoll(dev, events, td) 580dde8a05bSUgen J.S. Antsilevich dev_t dev; 581659ffb48SPeter Wemm int events; 582b40ce416SJulian Elischer struct thread *td; 583dde8a05bSUgen J.S. Antsilevich { 584f09f49f1SDima Dorfman struct snoop *snp; 585f09f49f1SDima Dorfman int revents; 586dde8a05bSUgen J.S. Antsilevich 587f09f49f1SDima Dorfman snp = dev->si_drv1; 588f09f49f1SDima Dorfman revents = 0; 589019b4d63SUgen J.S. Antsilevich /* 590659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 591019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 592019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 593019b4d63SUgen J.S. Antsilevich */ 594dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 595659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 596659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 597659ffb48SPeter Wemm else 598b40ce416SJulian Elischer selrecord(td, &snp->snp_sel); 599dfd5dee1SPeter Wemm } 600659ffb48SPeter Wemm return (revents); 601dde8a05bSUgen J.S. Antsilevich } 602dde8a05bSUgen J.S. Antsilevich 60387f6c662SJulian Elischer static void 604f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev) 605f09f49f1SDima Dorfman void *arg; 606f09f49f1SDima Dorfman char *name; 607f09f49f1SDima Dorfman int namelen; 608f09f49f1SDima Dorfman dev_t *dev; 60925c7870eSDima Dorfman { 610b0b03348SPoul-Henning Kamp int u, i; 61125c7870eSDima Dorfman 61225c7870eSDima Dorfman if (*dev != NODEV) 61325c7870eSDima Dorfman return; 61425c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 61525c7870eSDima Dorfman return; 616b0b03348SPoul-Henning Kamp i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0); 617b0b03348SPoul-Henning Kamp if (i) 618b0b03348SPoul-Henning Kamp *dev = make_dev(&snp_cdevsw, unit2minor(u), 619b0b03348SPoul-Henning Kamp UID_ROOT, GID_WHEEL, 0600, "snp%d", u); 620b0b03348SPoul-Henning Kamp if (*dev != NULL) 6212f7a3851SDima Dorfman (*dev)->si_flags |= SI_CHEAPCLONE; 62225c7870eSDima Dorfman } 62325c7870eSDima Dorfman 62447eaa5f5SDima Dorfman static int 625f09f49f1SDima Dorfman snp_modevent(mod, type, data) 626f09f49f1SDima Dorfman module_t mod; 627f09f49f1SDima Dorfman int type; 628f09f49f1SDima Dorfman void *data; 62953ac6efbSJulian Elischer { 630f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 63153ac6efbSJulian Elischer 63247eaa5f5SDima Dorfman switch (type) { 63347eaa5f5SDima Dorfman case MOD_LOAD: 634f09f49f1SDima Dorfman /* XXX error checking. */ 6359397290eSPoul-Henning Kamp clone_setup(&snpclones); 63647eaa5f5SDima Dorfman eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000); 637f09f49f1SDima Dorfman snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 63847eaa5f5SDima Dorfman break; 63947eaa5f5SDima Dorfman case MOD_UNLOAD: 640ac60b28dSDima Dorfman if (!LIST_EMPTY(&snp_sclist)) 641ac60b28dSDima Dorfman return (EBUSY); 64247eaa5f5SDima Dorfman EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 643b0b03348SPoul-Henning Kamp clone_cleanup(&snpclones); 644f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 64547eaa5f5SDima Dorfman break; 64647eaa5f5SDima Dorfman default: 64747eaa5f5SDima Dorfman break; 64847eaa5f5SDima Dorfman } 649f09f49f1SDima Dorfman return (0); 6507198bf47SJulian Elischer } 65153ac6efbSJulian Elischer 65247eaa5f5SDima Dorfman static moduledata_t snp_mod = { 65347eaa5f5SDima Dorfman "snp", 65447eaa5f5SDima Dorfman snp_modevent, 65547eaa5f5SDima Dorfman NULL 65647eaa5f5SDima Dorfman }; 657b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 658