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 = { 417ac40f5fSPoul-Henning Kamp .d_open = snpopen, 427ac40f5fSPoul-Henning Kamp .d_close = snpclose, 437ac40f5fSPoul-Henning Kamp .d_read = snpread, 447ac40f5fSPoul-Henning Kamp .d_write = snpwrite, 457ac40f5fSPoul-Henning Kamp .d_ioctl = snpioctl, 467ac40f5fSPoul-Henning Kamp .d_poll = snppoll, 477ac40f5fSPoul-Henning Kamp .d_name = "snp", 487ac40f5fSPoul-Henning Kamp .d_maj = CDEV_MAJOR, 494e2f199eSPoul-Henning Kamp }; 5087f6c662SJulian Elischer 51f09f49f1SDima Dorfman static struct linesw snpdisc = { 52ddf5b796SDima Dorfman ttyopen, snplclose, ttread, snplwrite, 53f09f49f1SDima Dorfman l_nullioctl, ttyinput, ttstart, ttymodem 54f09f49f1SDima Dorfman }; 5553ac6efbSJulian Elischer 56101f105dSDima Dorfman /* 57101f105dSDima Dorfman * This is the main snoop per-device structure. 58101f105dSDima Dorfman */ 59101f105dSDima Dorfman struct snoop { 60101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 61cdd530caSDima Dorfman int snp_unit; /* Device number. */ 62101f105dSDima Dorfman dev_t snp_target; /* Target tty device. */ 63101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 64101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 65101f105dSDima Dorfman u_long snp_base; /* Data base. */ 66101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 67101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 68101f105dSDima Dorfman int snp_flags; /* Flags. */ 69101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 70101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 71101f105dSDima Dorfman }; 72101f105dSDima Dorfman 73101f105dSDima Dorfman /* 74101f105dSDima Dorfman * Possible flags. 75101f105dSDima Dorfman */ 76101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 77101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 78101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 79101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 80101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 81101f105dSDima Dorfman 82101f105dSDima Dorfman /* 83101f105dSDima Dorfman * Other constants. 84101f105dSDima Dorfman */ 85101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 86101f105dSDima Dorfman * 4K tested to be the minimum 87101f105dSDima Dorfman * for which on normal tty 88101f105dSDima Dorfman * usage there is no need to 89101f105dSDima Dorfman * allocate more. 90101f105dSDima Dorfman */ 91101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 92101f105dSDima Dorfman * If we grow more,something 93101f105dSDima Dorfman * really bad in this world.. 94101f105dSDima Dorfman */ 95101f105dSDima Dorfman 96959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 9747eaa5f5SDima Dorfman /* 9847eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 9947eaa5f5SDima Dorfman * module load time. 10047eaa5f5SDima Dorfman */ 101f09f49f1SDima Dorfman static int snooplinedisc; 1022f7a3851SDima Dorfman static udev_t snpbasedev = NOUDEV; 103f09f49f1SDima Dorfman 104ac60b28dSDima Dorfman 105ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 106ac60b28dSDima Dorfman 107e51a25f8SAlfred Perlstein static struct tty *snpdevtotty(dev_t dev); 108e51a25f8SAlfred Perlstein static void snp_clone(void *arg, char *name, 109e51a25f8SAlfred Perlstein int namelen, dev_t *dev); 110e51a25f8SAlfred Perlstein static int snp_detach(struct snoop *snp); 111e51a25f8SAlfred Perlstein static int snp_down(struct snoop *snp); 112e51a25f8SAlfred Perlstein static int snp_in(struct snoop *snp, char *buf, int n); 113e51a25f8SAlfred Perlstein static int snp_modevent(module_t mod, int what, void *arg); 11447eaa5f5SDima Dorfman 11547eaa5f5SDima Dorfman static int 116ddf5b796SDima Dorfman snplclose(tp, flag) 117ddf5b796SDima Dorfman struct tty *tp; 118ddf5b796SDima Dorfman int flag; 119ddf5b796SDima Dorfman { 120ddf5b796SDima Dorfman struct snoop *snp; 121ddf5b796SDima Dorfman int error; 122ddf5b796SDima Dorfman 123ddf5b796SDima Dorfman snp = tp->t_sc; 124ddf5b796SDima Dorfman error = snp_down(snp); 125ddf5b796SDima Dorfman if (error != 0) 126ddf5b796SDima Dorfman return (error); 127ddf5b796SDima Dorfman error = ttylclose(tp, flag); 128ddf5b796SDima Dorfman return (error); 129ddf5b796SDima Dorfman } 130ddf5b796SDima Dorfman 131ddf5b796SDima Dorfman static int 132f09f49f1SDima Dorfman snplwrite(tp, uio, flag) 133f09f49f1SDima Dorfman struct tty *tp; 134f09f49f1SDima Dorfman struct uio *uio; 135f09f49f1SDima Dorfman int flag; 13647eaa5f5SDima Dorfman { 13747eaa5f5SDima Dorfman struct iovec iov; 13847eaa5f5SDima Dorfman struct uio uio2; 139f09f49f1SDima Dorfman struct snoop *snp; 140f09f49f1SDima Dorfman int error, ilen; 141e692c40cSDima Dorfman char *ibuf; 14247eaa5f5SDima Dorfman 143e692c40cSDima Dorfman error = 0; 144e692c40cSDima Dorfman ibuf = NULL; 145f09f49f1SDima Dorfman snp = tp->t_sc; 14687826386SDima Dorfman while (uio->uio_resid > 0) { 147e692c40cSDima Dorfman ilen = imin(512, uio->uio_resid); 148a163d034SWarner Losh ibuf = malloc(ilen, M_SNP, M_WAITOK); 14947eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 150f09f49f1SDima Dorfman if (error != 0) 151e692c40cSDima Dorfman break; 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; 162b40ce416SJulian Elischer uio2.uio_td = uio->uio_td; 16347eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 164f09f49f1SDima Dorfman if (error != 0) 165e692c40cSDima Dorfman break; 166e692c40cSDima Dorfman free(ibuf, M_SNP); 167e692c40cSDima Dorfman ibuf = NULL; 16847eaa5f5SDima Dorfman } 169e692c40cSDima Dorfman if (ibuf != NULL) 170e692c40cSDima Dorfman free(ibuf, M_SNP); 171e692c40cSDima Dorfman return (error); 172ddf5b796SDima Dorfman } 17347eaa5f5SDima Dorfman 17477f77631SPaul Traina static struct tty * 175444f003cSBruce Evans snpdevtotty(dev) 17677f77631SPaul Traina dev_t dev; 17777f77631SPaul Traina { 178444f003cSBruce Evans struct cdevsw *cdp; 17977f77631SPaul Traina 1804be2eb8cSPoul-Henning Kamp cdp = devsw(dev); 18187826386SDima Dorfman if (cdp == NULL || (cdp->d_flags & D_TTY) == 0) 182444f003cSBruce Evans return (NULL); 18387826386SDima Dorfman return (dev->si_tty); 18477f77631SPaul Traina } 18577f77631SPaul Traina 1860739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 1870739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 1880739a0dcSUgen J.S. Antsilevich * length for function keys... 1890739a0dcSUgen J.S. Antsilevich */ 1900739a0dcSUgen J.S. Antsilevich 19187f6c662SJulian Elischer static int 1920739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag) 1930739a0dcSUgen J.S. Antsilevich dev_t dev; 1940739a0dcSUgen J.S. Antsilevich struct uio *uio; 1950739a0dcSUgen J.S. Antsilevich int flag; 1960739a0dcSUgen J.S. Antsilevich { 197f09f49f1SDima Dorfman struct snoop *snp; 1980739a0dcSUgen J.S. Antsilevich struct tty *tp; 199f09f49f1SDima Dorfman int error, i, len; 200f4c5dfbbSDima Dorfman unsigned char c[SNP_INPUT_BUF]; 2010739a0dcSUgen J.S. Antsilevich 202f09f49f1SDima Dorfman snp = dev->si_drv1; 2030739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 204f09f49f1SDima Dorfman if (tp == NULL) 205f09f49f1SDima Dorfman return (EIO); 2060739a0dcSUgen J.S. Antsilevich if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 207f09f49f1SDima Dorfman tp->t_line == snooplinedisc) 2080739a0dcSUgen J.S. Antsilevich goto tty_input; 2090739a0dcSUgen J.S. Antsilevich 210cdd530caSDima Dorfman printf("snp%d: attempt to write to bad tty\n", snp->snp_unit); 2110739a0dcSUgen J.S. Antsilevich return (EIO); 2120739a0dcSUgen J.S. Antsilevich 2130739a0dcSUgen J.S. Antsilevich tty_input: 2140739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2150739a0dcSUgen J.S. Antsilevich return (EIO); 2160739a0dcSUgen J.S. Antsilevich 2170739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 21887826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2190739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2200739a0dcSUgen J.S. Antsilevich return (error); 2210739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2220739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2230739a0dcSUgen J.S. Antsilevich return (EIO); 2240739a0dcSUgen J.S. Antsilevich } 2250739a0dcSUgen J.S. Antsilevich } 226f09f49f1SDima Dorfman return (0); 2270739a0dcSUgen J.S. Antsilevich } 2280739a0dcSUgen J.S. Antsilevich 2290739a0dcSUgen J.S. Antsilevich 23087f6c662SJulian Elischer static int 231dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag) 232dde8a05bSUgen J.S. Antsilevich dev_t dev; 233dde8a05bSUgen J.S. Antsilevich struct uio *uio; 234dde8a05bSUgen J.S. Antsilevich int flag; 235dde8a05bSUgen J.S. Antsilevich { 236f09f49f1SDima Dorfman struct snoop *snp; 237f09f49f1SDima Dorfman int error, len, n, nblen, s; 238dde8a05bSUgen J.S. Antsilevich caddr_t from; 239dde8a05bSUgen J.S. Antsilevich char *nbuf; 240dde8a05bSUgen J.S. Antsilevich 241f09f49f1SDima Dorfman snp = dev->si_drv1; 2425526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2435526d2d9SEivind Eklund ("snoop buffer error")); 24477f77631SPaul Traina 2450739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 246dde8a05bSUgen J.S. Antsilevich return (EIO); 247dde8a05bSUgen J.S. Antsilevich 248dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 249dde8a05bSUgen J.S. Antsilevich 250dde8a05bSUgen J.S. Antsilevich do { 251dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 252d96bc99dSBruce Evans if (flag & IO_NDELAY) 253d96bc99dSBruce Evans return (EWOULDBLOCK); 254dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 255521f364bSDag-Erling Smørgrav error = tsleep(snp, (PZERO + 1) | PCATCH, 2562c1a6f21SDima Dorfman "snprd", 0); 2572c1a6f21SDima Dorfman if (error != 0) 2582c1a6f21SDima Dorfman return (error); 259dde8a05bSUgen J.S. Antsilevich } 260dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 261dde8a05bSUgen J.S. Antsilevich 262019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 263dde8a05bSUgen J.S. Antsilevich 264f09f49f1SDima Dorfman error = 0; 265dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 26687826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 267dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 268dde8a05bSUgen J.S. Antsilevich if (len == 0) 269dde8a05bSUgen J.S. Antsilevich break; 270dde8a05bSUgen J.S. Antsilevich 271dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 272dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 273dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 274dde8a05bSUgen J.S. Antsilevich } 275019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 276dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 277019b4d63SUgen J.S. Antsilevich } 278dde8a05bSUgen J.S. Antsilevich s = spltty(); 279dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 280dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 281f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 282dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 28376e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 284dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 28576e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 286dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 287dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 288dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 289dde8a05bSUgen J.S. Antsilevich } 290dde8a05bSUgen J.S. Antsilevich } 291dde8a05bSUgen J.S. Antsilevich splx(s); 292dde8a05bSUgen J.S. Antsilevich 293f09f49f1SDima Dorfman return (error); 294dde8a05bSUgen J.S. Antsilevich } 295dde8a05bSUgen J.S. Antsilevich 296f09f49f1SDima Dorfman static int 297f09f49f1SDima Dorfman snp_in(snp, buf, n) 298dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 299dde8a05bSUgen J.S. Antsilevich char *buf; 300dde8a05bSUgen J.S. Antsilevich int n; 301dde8a05bSUgen J.S. Antsilevich { 302dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 303dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 304dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 305dde8a05bSUgen J.S. Antsilevich char *nbuf; 306dde8a05bSUgen J.S. Antsilevich 307219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 308dde8a05bSUgen J.S. Antsilevich 309dde8a05bSUgen J.S. Antsilevich if (n == 0) 310f09f49f1SDima Dorfman return (0); 311dde8a05bSUgen J.S. Antsilevich 312019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 313cdd530caSDima Dorfman printf("snp%d: more data to down interface\n", snp->snp_unit); 314f09f49f1SDima Dorfman return (0); 315019b4d63SUgen J.S. Antsilevich } 316964587caSUgen J.S. Antsilevich 317019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 318cdd530caSDima Dorfman printf("snp%d: buffer overflow\n", snp->snp_unit); 319019b4d63SUgen J.S. Antsilevich /* 320019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 321019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 322019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 323019b4d63SUgen J.S. Antsilevich * snooping and retry... 324019b4d63SUgen J.S. Antsilevich */ 325dde8a05bSUgen J.S. Antsilevich 326f09f49f1SDima Dorfman return (snp_down(snp)); 327019b4d63SUgen J.S. Antsilevich } 328019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 329019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 330dde8a05bSUgen J.S. Antsilevich 331dde8a05bSUgen J.S. Antsilevich 332dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 333dde8a05bSUgen J.S. Antsilevich s = spltty(); 334dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 335dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 336dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 337dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 338dde8a05bSUgen J.S. Antsilevich } 33976e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 340dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 34176e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 342dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 343dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 344dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 345dde8a05bSUgen J.S. Antsilevich } else { 346dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 347dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 348dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 349521f364bSDag-Erling Smørgrav wakeup(snp); 350dde8a05bSUgen J.S. Antsilevich } 351dde8a05bSUgen J.S. Antsilevich splx(s); 352f09f49f1SDima Dorfman return (0); 353dde8a05bSUgen J.S. Antsilevich } 354dde8a05bSUgen J.S. Antsilevich splx(s); 355dde8a05bSUgen J.S. Antsilevich } 356dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 357dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 358dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 359dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 360dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 361dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 362dde8a05bSUgen J.S. Antsilevich } 363dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 364dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 365dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 366dde8a05bSUgen J.S. Antsilevich 367dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 368dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 369521f364bSDag-Erling Smørgrav wakeup(snp); 370dde8a05bSUgen J.S. Antsilevich } 371dde8a05bSUgen J.S. Antsilevich selwakeup(&snp->snp_sel); 372dde8a05bSUgen J.S. Antsilevich 373f09f49f1SDima Dorfman return (n); 374dde8a05bSUgen J.S. Antsilevich } 375dde8a05bSUgen J.S. Antsilevich 37687f6c662SJulian Elischer static int 377b40ce416SJulian Elischer snpopen(dev, flag, mode, td) 378dde8a05bSUgen J.S. Antsilevich dev_t dev; 379dde8a05bSUgen J.S. Antsilevich int flag, mode; 380b40ce416SJulian Elischer struct thread *td; 381dde8a05bSUgen J.S. Antsilevich { 382dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 383dde8a05bSUgen J.S. Antsilevich 38476e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 38525c7870eSDima Dorfman if (!(dev->si_flags & SI_NAMED)) 38625c7870eSDima Dorfman make_dev(&snp_cdevsw, minor(dev), UID_ROOT, GID_WHEEL, 38725c7870eSDima Dorfman 0600, "snp%d", dev2unit(dev)); 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: 445019b4d63SUgen J.S. Antsilevich selwakeup(&snp->snp_sel); 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; 467dde8a05bSUgen J.S. Antsilevich 468019b4d63SUgen J.S. Antsilevich return (snp_detach(snp)); 469dde8a05bSUgen J.S. Antsilevich } 470dde8a05bSUgen J.S. Antsilevich 471f09f49f1SDima Dorfman static int 472f09f49f1SDima Dorfman snp_down(snp) 473964587caSUgen J.S. Antsilevich struct snoop *snp; 474964587caSUgen J.S. Antsilevich { 47576e90dbcSBrian Feldman 47676e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 47776e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 478a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 479964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 48076e90dbcSBrian Feldman } 481964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 482dde8a05bSUgen J.S. Antsilevich 483964587caSUgen J.S. Antsilevich return (snp_detach(snp)); 484964587caSUgen J.S. Antsilevich } 485dde8a05bSUgen J.S. Antsilevich 48687f6c662SJulian Elischer static int 487b40ce416SJulian Elischer snpioctl(dev, cmd, data, flags, td) 488dde8a05bSUgen J.S. Antsilevich dev_t dev; 489ecbb00a2SDoug Rabson u_long cmd; 490dde8a05bSUgen J.S. Antsilevich caddr_t data; 49160039670SBruce Evans int flags; 492b40ce416SJulian Elischer struct thread *td; 493dde8a05bSUgen J.S. Antsilevich { 494f09f49f1SDima Dorfman struct snoop *snp; 495dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 496f09f49f1SDima Dorfman dev_t tdev; 49776e90dbcSBrian Feldman int s; 498dde8a05bSUgen J.S. Antsilevich 499f09f49f1SDima Dorfman snp = dev->si_drv1; 500dde8a05bSUgen J.S. Antsilevich switch (cmd) { 501dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 502212dfe6fSBruce Evans tdev = udev2dev(*((udev_t *)data), 0); 503784fc12fSPoul-Henning Kamp if (tdev == NODEV) 504f09f49f1SDima Dorfman return (snp_down(snp)); 505964587caSUgen J.S. Antsilevich 506444f003cSBruce Evans tp = snpdevtotty(tdev); 50777f77631SPaul Traina if (!tp) 508dde8a05bSUgen J.S. Antsilevich return (EINVAL); 5092c9fb909SDima Dorfman if (tp->t_state & TS_SNOOP) 5102c9fb909SDima Dorfman return (EBUSY); 511dde8a05bSUgen J.S. Antsilevich 512dde8a05bSUgen J.S. Antsilevich s = spltty(); 51377f77631SPaul Traina 514784fc12fSPoul-Henning Kamp if (snp->snp_target == NODEV) { 5150739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 51677f77631SPaul Traina if (tpo) 517dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 518dde8a05bSUgen J.S. Antsilevich } 51977f77631SPaul Traina 520dde8a05bSUgen J.S. Antsilevich tp->t_sc = (caddr_t)snp; 521dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 52247eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 523f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5240739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 52577f77631SPaul Traina snp->snp_target = tdev; 52677f77631SPaul Traina 527964587caSUgen J.S. Antsilevich /* 528964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 529964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 530964587caSUgen J.S. Antsilevich */ 531964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 532019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 533dde8a05bSUgen J.S. Antsilevich splx(s); 534dde8a05bSUgen J.S. Antsilevich break; 53577f77631SPaul Traina 536dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5370739a0dcSUgen J.S. Antsilevich /* 5380739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5390739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5400739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5410739a0dcSUgen J.S. Antsilevich */ 54220207b60SThomas Moestl *((udev_t *)data) = dev2udev(snp->snp_target); 543dde8a05bSUgen J.S. Antsilevich break; 544dde8a05bSUgen J.S. Antsilevich 545dde8a05bSUgen J.S. Antsilevich case FIONBIO: 546dde8a05bSUgen J.S. Antsilevich break; 54777f77631SPaul Traina 548dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 549dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 550dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 551dde8a05bSUgen J.S. Antsilevich else 552dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 553dde8a05bSUgen J.S. Antsilevich break; 55477f77631SPaul Traina 555dde8a05bSUgen J.S. Antsilevich case FIONREAD: 556dde8a05bSUgen J.S. Antsilevich s = spltty(); 5570739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 558dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 559dde8a05bSUgen J.S. Antsilevich else 560964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 561964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 562964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 563964587caSUgen J.S. Antsilevich else 564964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 565964587caSUgen J.S. Antsilevich } else { 566964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 567964587caSUgen J.S. Antsilevich } 568dde8a05bSUgen J.S. Antsilevich splx(s); 569dde8a05bSUgen J.S. Antsilevich break; 57077f77631SPaul Traina 571dde8a05bSUgen J.S. Antsilevich default: 572dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 573dde8a05bSUgen J.S. Antsilevich } 574dde8a05bSUgen J.S. Antsilevich return (0); 575dde8a05bSUgen J.S. Antsilevich } 576dde8a05bSUgen J.S. Antsilevich 57787f6c662SJulian Elischer static int 578b40ce416SJulian Elischer snppoll(dev, events, td) 579dde8a05bSUgen J.S. Antsilevich dev_t dev; 580659ffb48SPeter Wemm int events; 581b40ce416SJulian Elischer struct thread *td; 582dde8a05bSUgen J.S. Antsilevich { 583f09f49f1SDima Dorfman struct snoop *snp; 584f09f49f1SDima Dorfman int revents; 585dde8a05bSUgen J.S. Antsilevich 586f09f49f1SDima Dorfman snp = dev->si_drv1; 587f09f49f1SDima Dorfman revents = 0; 588019b4d63SUgen J.S. Antsilevich /* 589659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 590019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 591019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 592019b4d63SUgen J.S. Antsilevich */ 593dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 594659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 595659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 596659ffb48SPeter Wemm else 597b40ce416SJulian Elischer selrecord(td, &snp->snp_sel); 598dfd5dee1SPeter Wemm } 599659ffb48SPeter Wemm return (revents); 600dde8a05bSUgen J.S. Antsilevich } 601dde8a05bSUgen J.S. Antsilevich 60287f6c662SJulian Elischer static void 603f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev) 604f09f49f1SDima Dorfman void *arg; 605f09f49f1SDima Dorfman char *name; 606f09f49f1SDima Dorfman int namelen; 607f09f49f1SDima Dorfman dev_t *dev; 60825c7870eSDima Dorfman { 60925c7870eSDima Dorfman int u; 61025c7870eSDima Dorfman 61125c7870eSDima Dorfman if (*dev != NODEV) 61225c7870eSDima Dorfman return; 61325c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 61425c7870eSDima Dorfman return; 61525c7870eSDima Dorfman *dev = make_dev(&snp_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, 0600, 61625c7870eSDima Dorfman "snp%d", u); 6172f7a3851SDima Dorfman if (snpbasedev == NOUDEV) 6182f7a3851SDima Dorfman snpbasedev = (*dev)->si_udev; 6192f7a3851SDima Dorfman else { 6202f7a3851SDima Dorfman (*dev)->si_flags |= SI_CHEAPCLONE; 6212f7a3851SDima Dorfman dev_depends(udev2dev(snpbasedev, 0), *dev); 6222f7a3851SDima Dorfman } 62325c7870eSDima Dorfman } 62425c7870eSDima Dorfman 62547eaa5f5SDima Dorfman static int 626f09f49f1SDima Dorfman snp_modevent(mod, type, data) 627f09f49f1SDima Dorfman module_t mod; 628f09f49f1SDima Dorfman int type; 629f09f49f1SDima Dorfman void *data; 63053ac6efbSJulian Elischer { 631f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 63253ac6efbSJulian Elischer 63347eaa5f5SDima Dorfman switch (type) { 63447eaa5f5SDima Dorfman case MOD_LOAD: 635f09f49f1SDima Dorfman /* XXX error checking. */ 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); 6432f7a3851SDima Dorfman if (snpbasedev != NOUDEV) 6442f7a3851SDima Dorfman destroy_dev(udev2dev(snpbasedev, 0)); 645f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 64647eaa5f5SDima Dorfman break; 64747eaa5f5SDima Dorfman default: 64847eaa5f5SDima Dorfman break; 64947eaa5f5SDima Dorfman } 650f09f49f1SDima Dorfman return (0); 6517198bf47SJulian Elischer } 65253ac6efbSJulian Elischer 65347eaa5f5SDima Dorfman static moduledata_t snp_mod = { 65447eaa5f5SDima Dorfman "snp", 65547eaa5f5SDima Dorfman snp_modevent, 65647eaa5f5SDima Dorfman NULL 65747eaa5f5SDima Dorfman }; 65847eaa5f5SDima Dorfman DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR); 659