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 = { 5566ea137dSRobert Watson .l_open = ttyopen, 5666ea137dSRobert Watson .l_close = snplclose, 5766ea137dSRobert Watson .l_read = ttread, 5866ea137dSRobert Watson .l_write = snplwrite, 5966ea137dSRobert Watson .l_ioctl = l_nullioctl, 6066ea137dSRobert Watson .l_rint = ttyinput, 6166ea137dSRobert Watson .l_start = ttstart, 6266ea137dSRobert Watson .l_modem = ttymodem 63f09f49f1SDima Dorfman }; 6453ac6efbSJulian Elischer 65101f105dSDima Dorfman /* 66101f105dSDima Dorfman * This is the main snoop per-device structure. 67101f105dSDima Dorfman */ 68101f105dSDima Dorfman struct snoop { 69101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 70cdd530caSDima Dorfman int snp_unit; /* Device number. */ 7189c9c53dSPoul-Henning Kamp struct cdev *snp_target; /* Target tty device. */ 72101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 73101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 74101f105dSDima Dorfman u_long snp_base; /* Data base. */ 75101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 76101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 77101f105dSDima Dorfman int snp_flags; /* Flags. */ 78101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 79101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 80101f105dSDima Dorfman }; 81101f105dSDima Dorfman 82101f105dSDima Dorfman /* 83101f105dSDima Dorfman * Possible flags. 84101f105dSDima Dorfman */ 85101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 86101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 87101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 88101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 89101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 90101f105dSDima Dorfman 91101f105dSDima Dorfman /* 92101f105dSDima Dorfman * Other constants. 93101f105dSDima Dorfman */ 94101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 95101f105dSDima Dorfman * 4K tested to be the minimum 96101f105dSDima Dorfman * for which on normal tty 97101f105dSDima Dorfman * usage there is no need to 98101f105dSDima Dorfman * allocate more. 99101f105dSDima Dorfman */ 100101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 101101f105dSDima Dorfman * If we grow more,something 102101f105dSDima Dorfman * really bad in this world.. 103101f105dSDima Dorfman */ 104101f105dSDima Dorfman 105959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 10647eaa5f5SDima Dorfman /* 10747eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 10847eaa5f5SDima Dorfman * module load time. 10947eaa5f5SDima Dorfman */ 110f09f49f1SDima Dorfman static int snooplinedisc; 111ac60b28dSDima Dorfman 112ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 113b0b03348SPoul-Henning Kamp static struct clonedevs *snpclones; 114ac60b28dSDima Dorfman 11589c9c53dSPoul-Henning Kamp static struct tty *snpdevtotty(struct cdev *dev); 116e51a25f8SAlfred Perlstein static void snp_clone(void *arg, char *name, 11789c9c53dSPoul-Henning Kamp int namelen, struct cdev **dev); 118e51a25f8SAlfred Perlstein static int snp_detach(struct snoop *snp); 119e51a25f8SAlfred Perlstein static int snp_down(struct snoop *snp); 120e51a25f8SAlfred Perlstein static int snp_in(struct snoop *snp, char *buf, int n); 121e51a25f8SAlfred Perlstein static int snp_modevent(module_t mod, int what, void *arg); 12247eaa5f5SDima Dorfman 12347eaa5f5SDima Dorfman static int 124ddf5b796SDima Dorfman snplclose(tp, flag) 125ddf5b796SDima Dorfman struct tty *tp; 126ddf5b796SDima Dorfman int flag; 127ddf5b796SDima Dorfman { 128ddf5b796SDima Dorfman struct snoop *snp; 129ddf5b796SDima Dorfman int error; 130ddf5b796SDima Dorfman 131ddf5b796SDima Dorfman snp = tp->t_sc; 132ddf5b796SDima Dorfman error = snp_down(snp); 133ddf5b796SDima Dorfman if (error != 0) 134ddf5b796SDima Dorfman return (error); 135ddf5b796SDima Dorfman error = ttylclose(tp, flag); 136ddf5b796SDima Dorfman return (error); 137ddf5b796SDima Dorfman } 138ddf5b796SDima Dorfman 139ddf5b796SDima Dorfman static int 140f09f49f1SDima Dorfman snplwrite(tp, uio, flag) 141f09f49f1SDima Dorfman struct tty *tp; 142f09f49f1SDima Dorfman struct uio *uio; 143f09f49f1SDima Dorfman int flag; 14447eaa5f5SDima Dorfman { 14547eaa5f5SDima Dorfman struct iovec iov; 14647eaa5f5SDima Dorfman struct uio uio2; 147f09f49f1SDima Dorfman struct snoop *snp; 148f09f49f1SDima Dorfman int error, ilen; 149e692c40cSDima Dorfman char *ibuf; 15047eaa5f5SDima Dorfman 151e692c40cSDima Dorfman error = 0; 152e692c40cSDima Dorfman ibuf = NULL; 153f09f49f1SDima Dorfman snp = tp->t_sc; 15487826386SDima Dorfman while (uio->uio_resid > 0) { 155e692c40cSDima Dorfman ilen = imin(512, uio->uio_resid); 156a163d034SWarner Losh ibuf = malloc(ilen, M_SNP, M_WAITOK); 15747eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 158f09f49f1SDima Dorfman if (error != 0) 159e692c40cSDima Dorfman break; 160f09f49f1SDima Dorfman snp_in(snp, ibuf, ilen); 161f09f49f1SDima Dorfman /* Hackish, but probably the least of all evils. */ 16247eaa5f5SDima Dorfman iov.iov_base = ibuf; 16347eaa5f5SDima Dorfman iov.iov_len = ilen; 16447eaa5f5SDima Dorfman uio2.uio_iov = &iov; 16547eaa5f5SDima Dorfman uio2.uio_iovcnt = 1; 16647eaa5f5SDima Dorfman uio2.uio_offset = 0; 16747eaa5f5SDima Dorfman uio2.uio_resid = ilen; 16847eaa5f5SDima Dorfman uio2.uio_segflg = UIO_SYSSPACE; 16947eaa5f5SDima Dorfman uio2.uio_rw = UIO_WRITE; 170b40ce416SJulian Elischer uio2.uio_td = uio->uio_td; 17147eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 172f09f49f1SDima Dorfman if (error != 0) 173e692c40cSDima Dorfman break; 174e692c40cSDima Dorfman free(ibuf, M_SNP); 175e692c40cSDima Dorfman ibuf = NULL; 17647eaa5f5SDima Dorfman } 177e692c40cSDima Dorfman if (ibuf != NULL) 178e692c40cSDima Dorfman free(ibuf, M_SNP); 179e692c40cSDima Dorfman return (error); 180ddf5b796SDima Dorfman } 18147eaa5f5SDima Dorfman 18277f77631SPaul Traina static struct tty * 183444f003cSBruce Evans snpdevtotty(dev) 18489c9c53dSPoul-Henning Kamp struct cdev *dev; 18577f77631SPaul Traina { 186444f003cSBruce Evans struct cdevsw *cdp; 18777f77631SPaul Traina 1884be2eb8cSPoul-Henning Kamp cdp = devsw(dev); 18987826386SDima Dorfman if (cdp == NULL || (cdp->d_flags & D_TTY) == 0) 190444f003cSBruce Evans return (NULL); 19187826386SDima Dorfman return (dev->si_tty); 19277f77631SPaul Traina } 19377f77631SPaul Traina 1940739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 1950739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 1960739a0dcSUgen J.S. Antsilevich * length for function keys... 1970739a0dcSUgen J.S. Antsilevich */ 1980739a0dcSUgen J.S. Antsilevich 19987f6c662SJulian Elischer static int 2000739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag) 20189c9c53dSPoul-Henning Kamp struct cdev *dev; 2020739a0dcSUgen J.S. Antsilevich struct uio *uio; 2030739a0dcSUgen J.S. Antsilevich int flag; 2040739a0dcSUgen J.S. Antsilevich { 205f09f49f1SDima Dorfman struct snoop *snp; 2060739a0dcSUgen J.S. Antsilevich struct tty *tp; 207f09f49f1SDima Dorfman int error, i, len; 208f4c5dfbbSDima Dorfman unsigned char c[SNP_INPUT_BUF]; 2090739a0dcSUgen J.S. Antsilevich 210f09f49f1SDima Dorfman snp = dev->si_drv1; 2110739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 212f09f49f1SDima Dorfman if (tp == NULL) 213f09f49f1SDima Dorfman return (EIO); 2140739a0dcSUgen J.S. Antsilevich if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 215f09f49f1SDima Dorfman tp->t_line == snooplinedisc) 2160739a0dcSUgen J.S. Antsilevich goto tty_input; 2170739a0dcSUgen J.S. Antsilevich 218cdd530caSDima Dorfman printf("snp%d: attempt to write to bad tty\n", snp->snp_unit); 2190739a0dcSUgen J.S. Antsilevich return (EIO); 2200739a0dcSUgen J.S. Antsilevich 2210739a0dcSUgen J.S. Antsilevich tty_input: 2220739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2230739a0dcSUgen J.S. Antsilevich return (EIO); 2240739a0dcSUgen J.S. Antsilevich 2250739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 22687826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2270739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2280739a0dcSUgen J.S. Antsilevich return (error); 2290739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2300739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2310739a0dcSUgen J.S. Antsilevich return (EIO); 2320739a0dcSUgen J.S. Antsilevich } 2330739a0dcSUgen J.S. Antsilevich } 234f09f49f1SDima Dorfman return (0); 2350739a0dcSUgen J.S. Antsilevich } 2360739a0dcSUgen J.S. Antsilevich 2370739a0dcSUgen J.S. Antsilevich 23887f6c662SJulian Elischer static int 239dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag) 24089c9c53dSPoul-Henning Kamp struct cdev *dev; 241dde8a05bSUgen J.S. Antsilevich struct uio *uio; 242dde8a05bSUgen J.S. Antsilevich int flag; 243dde8a05bSUgen J.S. Antsilevich { 244f09f49f1SDima Dorfman struct snoop *snp; 245f09f49f1SDima Dorfman int error, len, n, nblen, s; 246dde8a05bSUgen J.S. Antsilevich caddr_t from; 247dde8a05bSUgen J.S. Antsilevich char *nbuf; 248dde8a05bSUgen J.S. Antsilevich 249f09f49f1SDima Dorfman snp = dev->si_drv1; 2505526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2515526d2d9SEivind Eklund ("snoop buffer error")); 25277f77631SPaul Traina 2530739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 254dde8a05bSUgen J.S. Antsilevich return (EIO); 255dde8a05bSUgen J.S. Antsilevich 256dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 257dde8a05bSUgen J.S. Antsilevich 258dde8a05bSUgen J.S. Antsilevich do { 259dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 260d96bc99dSBruce Evans if (flag & IO_NDELAY) 261d96bc99dSBruce Evans return (EWOULDBLOCK); 262dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 263521f364bSDag-Erling Smørgrav error = tsleep(snp, (PZERO + 1) | PCATCH, 2642c1a6f21SDima Dorfman "snprd", 0); 2652c1a6f21SDima Dorfman if (error != 0) 2662c1a6f21SDima Dorfman return (error); 267dde8a05bSUgen J.S. Antsilevich } 268dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 269dde8a05bSUgen J.S. Antsilevich 270019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 271dde8a05bSUgen J.S. Antsilevich 272f09f49f1SDima Dorfman error = 0; 273dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 27487826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 275dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 276dde8a05bSUgen J.S. Antsilevich if (len == 0) 277dde8a05bSUgen J.S. Antsilevich break; 278dde8a05bSUgen J.S. Antsilevich 279dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 280dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 281dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 282dde8a05bSUgen J.S. Antsilevich } 283019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 284dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 285019b4d63SUgen J.S. Antsilevich } 286dde8a05bSUgen J.S. Antsilevich s = spltty(); 287dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 288dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 289f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 290dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 29176e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 292dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 29376e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 294dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 295dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 296dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 297dde8a05bSUgen J.S. Antsilevich } 298dde8a05bSUgen J.S. Antsilevich } 299dde8a05bSUgen J.S. Antsilevich splx(s); 300dde8a05bSUgen J.S. Antsilevich 301f09f49f1SDima Dorfman return (error); 302dde8a05bSUgen J.S. Antsilevich } 303dde8a05bSUgen J.S. Antsilevich 304f09f49f1SDima Dorfman static int 305f09f49f1SDima Dorfman snp_in(snp, buf, n) 306dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 307dde8a05bSUgen J.S. Antsilevich char *buf; 308dde8a05bSUgen J.S. Antsilevich int n; 309dde8a05bSUgen J.S. Antsilevich { 310dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 311dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 312dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 313dde8a05bSUgen J.S. Antsilevich char *nbuf; 314dde8a05bSUgen J.S. Antsilevich 315219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 316dde8a05bSUgen J.S. Antsilevich 317dde8a05bSUgen J.S. Antsilevich if (n == 0) 318f09f49f1SDima Dorfman return (0); 319dde8a05bSUgen J.S. Antsilevich 320019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 321cdd530caSDima Dorfman printf("snp%d: more data to down interface\n", snp->snp_unit); 322f09f49f1SDima Dorfman return (0); 323019b4d63SUgen J.S. Antsilevich } 324964587caSUgen J.S. Antsilevich 325019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 326cdd530caSDima Dorfman printf("snp%d: buffer overflow\n", snp->snp_unit); 327019b4d63SUgen J.S. Antsilevich /* 328019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 329019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 330019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 331019b4d63SUgen J.S. Antsilevich * snooping and retry... 332019b4d63SUgen J.S. Antsilevich */ 333dde8a05bSUgen J.S. Antsilevich 334f09f49f1SDima Dorfman return (snp_down(snp)); 335019b4d63SUgen J.S. Antsilevich } 336019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 337019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 338dde8a05bSUgen J.S. Antsilevich 339dde8a05bSUgen J.S. Antsilevich 340dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 341dde8a05bSUgen J.S. Antsilevich s = spltty(); 342dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 343dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 344dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 345dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 346dde8a05bSUgen J.S. Antsilevich } 34776e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 348dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 34976e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 350dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 351dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 352dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 353dde8a05bSUgen J.S. Antsilevich } else { 354dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 355dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 356dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 357521f364bSDag-Erling Smørgrav wakeup(snp); 358dde8a05bSUgen J.S. Antsilevich } 359dde8a05bSUgen J.S. Antsilevich splx(s); 360f09f49f1SDima Dorfman return (0); 361dde8a05bSUgen J.S. Antsilevich } 362dde8a05bSUgen J.S. Antsilevich splx(s); 363dde8a05bSUgen J.S. Antsilevich } 364dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 365dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 366dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 367dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 368dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 369dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 370dde8a05bSUgen J.S. Antsilevich } 371dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 372dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 373dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 374dde8a05bSUgen J.S. Antsilevich 375dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 376dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 377521f364bSDag-Erling Smørgrav wakeup(snp); 378dde8a05bSUgen J.S. Antsilevich } 379512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 380dde8a05bSUgen J.S. Antsilevich 381f09f49f1SDima Dorfman return (n); 382dde8a05bSUgen J.S. Antsilevich } 383dde8a05bSUgen J.S. Antsilevich 38487f6c662SJulian Elischer static int 385b40ce416SJulian Elischer snpopen(dev, flag, mode, td) 38689c9c53dSPoul-Henning Kamp struct cdev *dev; 387dde8a05bSUgen J.S. Antsilevich int flag, mode; 388b40ce416SJulian Elischer struct thread *td; 389dde8a05bSUgen J.S. Antsilevich { 390dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 391dde8a05bSUgen J.S. Antsilevich 39276e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 393b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CHEAPCLONE; 394f09f49f1SDima Dorfman dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, 395a163d034SWarner Losh M_WAITOK | M_ZERO); 396cdd530caSDima Dorfman snp->snp_unit = dev2unit(dev); 39776e90dbcSBrian Feldman } else 39876e90dbcSBrian Feldman return (EBUSY); 39977f77631SPaul Traina 400019b4d63SUgen J.S. Antsilevich /* 401019b4d63SUgen J.S. Antsilevich * We intentionally do not OR flags with SNOOP_OPEN, but set them so 402019b4d63SUgen J.S. Antsilevich * all previous settings (especially SNOOP_OFLOW) will be cleared. 403019b4d63SUgen J.S. Antsilevich */ 404019b4d63SUgen J.S. Antsilevich snp->snp_flags = SNOOP_OPEN; 405dde8a05bSUgen J.S. Antsilevich 406a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 407dde8a05bSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 408dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 409dde8a05bSUgen J.S. Antsilevich snp->snp_len = 0; 410dde8a05bSUgen J.S. Antsilevich 411dde8a05bSUgen J.S. Antsilevich /* 4120739a0dcSUgen J.S. Antsilevich * snp_tty == NULL is for inactive snoop devices. 413dde8a05bSUgen J.S. Antsilevich */ 4140739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 415f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 416ac60b28dSDima Dorfman 417ac60b28dSDima Dorfman LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 418dde8a05bSUgen J.S. Antsilevich return (0); 419dde8a05bSUgen J.S. Antsilevich } 420dde8a05bSUgen J.S. Antsilevich 421019b4d63SUgen J.S. Antsilevich 42287b6de2bSPoul-Henning Kamp static int 423019b4d63SUgen J.S. Antsilevich snp_detach(snp) 424019b4d63SUgen J.S. Antsilevich struct snoop *snp; 425019b4d63SUgen J.S. Antsilevich { 426019b4d63SUgen J.S. Antsilevich struct tty *tp; 427019b4d63SUgen J.S. Antsilevich 428019b4d63SUgen J.S. Antsilevich snp->snp_base = 0; 429019b4d63SUgen J.S. Antsilevich snp->snp_len = 0; 430019b4d63SUgen J.S. Antsilevich 431019b4d63SUgen J.S. Antsilevich /* 432019b4d63SUgen J.S. Antsilevich * If line disc. changed we do not touch this pointer, SLIP/PPP will 433019b4d63SUgen J.S. Antsilevich * change it anyway. 434019b4d63SUgen J.S. Antsilevich */ 435f09f49f1SDima Dorfman tp = snp->snp_tty; 436f09f49f1SDima Dorfman if (tp == NULL) 437964587caSUgen J.S. Antsilevich goto detach_notty; 438019b4d63SUgen J.S. Antsilevich 43977f77631SPaul Traina if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 440f09f49f1SDima Dorfman tp->t_line == snooplinedisc) { 441019b4d63SUgen J.S. Antsilevich tp->t_sc = NULL; 442019b4d63SUgen J.S. Antsilevich tp->t_state &= ~TS_SNOOP; 44347eaa5f5SDima Dorfman tp->t_line = snp->snp_olddisc; 444019b4d63SUgen J.S. Antsilevich } else 445cdd530caSDima Dorfman printf("snp%d: bad attached tty data\n", snp->snp_unit); 446019b4d63SUgen J.S. Antsilevich 4470739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 448f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 449019b4d63SUgen J.S. Antsilevich 450964587caSUgen J.S. Antsilevich detach_notty: 451512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 45276e90dbcSBrian Feldman if ((snp->snp_flags & SNOOP_OPEN) == 0) 45376e90dbcSBrian Feldman free(snp, M_SNP); 454019b4d63SUgen J.S. Antsilevich 455019b4d63SUgen J.S. Antsilevich return (0); 456019b4d63SUgen J.S. Antsilevich } 457019b4d63SUgen J.S. Antsilevich 45887f6c662SJulian Elischer static int 459b40ce416SJulian Elischer snpclose(dev, flags, fmt, td) 46089c9c53dSPoul-Henning Kamp struct cdev *dev; 46160039670SBruce Evans int flags; 46260039670SBruce Evans int fmt; 463b40ce416SJulian Elischer struct thread *td; 464dde8a05bSUgen J.S. Antsilevich { 465f09f49f1SDima Dorfman struct snoop *snp; 466dde8a05bSUgen J.S. Antsilevich 467f09f49f1SDima Dorfman snp = dev->si_drv1; 468dde8a05bSUgen J.S. Antsilevich snp->snp_blen = 0; 469ac60b28dSDima Dorfman LIST_REMOVE(snp, snp_list); 47076e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 471019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OPEN; 47276e90dbcSBrian Feldman dev->si_drv1 = NULL; 473b0b03348SPoul-Henning Kamp destroy_dev(dev); 474dde8a05bSUgen J.S. Antsilevich 475019b4d63SUgen J.S. Antsilevich return (snp_detach(snp)); 476dde8a05bSUgen J.S. Antsilevich } 477dde8a05bSUgen J.S. Antsilevich 478f09f49f1SDima Dorfman static int 479f09f49f1SDima Dorfman snp_down(snp) 480964587caSUgen J.S. Antsilevich struct snoop *snp; 481964587caSUgen J.S. Antsilevich { 48276e90dbcSBrian Feldman 48376e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 48476e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 485a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 486964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 48776e90dbcSBrian Feldman } 488964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 489dde8a05bSUgen J.S. Antsilevich 490964587caSUgen J.S. Antsilevich return (snp_detach(snp)); 491964587caSUgen J.S. Antsilevich } 492dde8a05bSUgen J.S. Antsilevich 49387f6c662SJulian Elischer static int 494b40ce416SJulian Elischer snpioctl(dev, cmd, data, flags, td) 49589c9c53dSPoul-Henning Kamp struct cdev *dev; 496ecbb00a2SDoug Rabson u_long cmd; 497dde8a05bSUgen J.S. Antsilevich caddr_t data; 49860039670SBruce Evans int flags; 499b40ce416SJulian Elischer struct thread *td; 500dde8a05bSUgen J.S. Antsilevich { 501f09f49f1SDima Dorfman struct snoop *snp; 502dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 50389c9c53dSPoul-Henning Kamp struct cdev *tdev; 50476e90dbcSBrian Feldman int s; 505dde8a05bSUgen J.S. Antsilevich 506f09f49f1SDima Dorfman snp = dev->si_drv1; 507dde8a05bSUgen J.S. Antsilevich switch (cmd) { 508dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 509f3732fd1SPoul-Henning Kamp tdev = findcdev(*((dev_t *)data)); 510f3732fd1SPoul-Henning Kamp if (tdev == NULL) 511f09f49f1SDima Dorfman return (snp_down(snp)); 512964587caSUgen J.S. Antsilevich 513444f003cSBruce Evans tp = snpdevtotty(tdev); 51477f77631SPaul Traina if (!tp) 515dde8a05bSUgen J.S. Antsilevich return (EINVAL); 5162c9fb909SDima Dorfman if (tp->t_state & TS_SNOOP) 5172c9fb909SDima Dorfman return (EBUSY); 518dde8a05bSUgen J.S. Antsilevich 519dde8a05bSUgen J.S. Antsilevich s = spltty(); 52077f77631SPaul Traina 521f3732fd1SPoul-Henning Kamp if (snp->snp_target == NULL) { 5220739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 52377f77631SPaul Traina if (tpo) 524dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 525dde8a05bSUgen J.S. Antsilevich } 52677f77631SPaul Traina 527dde8a05bSUgen J.S. Antsilevich tp->t_sc = (caddr_t)snp; 528dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 52947eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 530f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5310739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 53277f77631SPaul Traina snp->snp_target = tdev; 53377f77631SPaul Traina 534964587caSUgen J.S. Antsilevich /* 535964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 536964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 537964587caSUgen J.S. Antsilevich */ 538964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 539019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 540dde8a05bSUgen J.S. Antsilevich splx(s); 541dde8a05bSUgen J.S. Antsilevich break; 54277f77631SPaul Traina 543dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5440739a0dcSUgen J.S. Antsilevich /* 5450739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5460739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5470739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5480739a0dcSUgen J.S. Antsilevich */ 549f3732fd1SPoul-Henning Kamp *((dev_t *)data) = dev2udev(snp->snp_target); 550dde8a05bSUgen J.S. Antsilevich break; 551dde8a05bSUgen J.S. Antsilevich 552dde8a05bSUgen J.S. Antsilevich case FIONBIO: 553dde8a05bSUgen J.S. Antsilevich break; 55477f77631SPaul Traina 555dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 556dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 557dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 558dde8a05bSUgen J.S. Antsilevich else 559dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 560dde8a05bSUgen J.S. Antsilevich break; 56177f77631SPaul Traina 562dde8a05bSUgen J.S. Antsilevich case FIONREAD: 563dde8a05bSUgen J.S. Antsilevich s = spltty(); 5640739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 565dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 566dde8a05bSUgen J.S. Antsilevich else 567964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 568964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 569964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 570964587caSUgen J.S. Antsilevich else 571964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 572964587caSUgen J.S. Antsilevich } else { 573964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 574964587caSUgen J.S. Antsilevich } 575dde8a05bSUgen J.S. Antsilevich splx(s); 576dde8a05bSUgen J.S. Antsilevich break; 57777f77631SPaul Traina 578dde8a05bSUgen J.S. Antsilevich default: 579dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 580dde8a05bSUgen J.S. Antsilevich } 581dde8a05bSUgen J.S. Antsilevich return (0); 582dde8a05bSUgen J.S. Antsilevich } 583dde8a05bSUgen J.S. Antsilevich 58487f6c662SJulian Elischer static int 585b40ce416SJulian Elischer snppoll(dev, events, td) 58689c9c53dSPoul-Henning Kamp struct cdev *dev; 587659ffb48SPeter Wemm int events; 588b40ce416SJulian Elischer struct thread *td; 589dde8a05bSUgen J.S. Antsilevich { 590f09f49f1SDima Dorfman struct snoop *snp; 591f09f49f1SDima Dorfman int revents; 592dde8a05bSUgen J.S. Antsilevich 593f09f49f1SDima Dorfman snp = dev->si_drv1; 594f09f49f1SDima Dorfman revents = 0; 595019b4d63SUgen J.S. Antsilevich /* 596659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 597019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 598019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 599019b4d63SUgen J.S. Antsilevich */ 600dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 601659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 602659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 603659ffb48SPeter Wemm else 604b40ce416SJulian Elischer selrecord(td, &snp->snp_sel); 605dfd5dee1SPeter Wemm } 606659ffb48SPeter Wemm return (revents); 607dde8a05bSUgen J.S. Antsilevich } 608dde8a05bSUgen J.S. Antsilevich 60987f6c662SJulian Elischer static void 610f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev) 611f09f49f1SDima Dorfman void *arg; 612f09f49f1SDima Dorfman char *name; 613f09f49f1SDima Dorfman int namelen; 61489c9c53dSPoul-Henning Kamp struct cdev **dev; 61525c7870eSDima Dorfman { 616b0b03348SPoul-Henning Kamp int u, i; 61725c7870eSDima Dorfman 618f3732fd1SPoul-Henning Kamp if (*dev != NULL) 61925c7870eSDima Dorfman return; 62025c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 62125c7870eSDima Dorfman return; 622b0b03348SPoul-Henning Kamp i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0); 623b0b03348SPoul-Henning Kamp if (i) 624b0b03348SPoul-Henning Kamp *dev = make_dev(&snp_cdevsw, unit2minor(u), 625b0b03348SPoul-Henning Kamp UID_ROOT, GID_WHEEL, 0600, "snp%d", u); 626b0b03348SPoul-Henning Kamp if (*dev != NULL) 6272f7a3851SDima Dorfman (*dev)->si_flags |= SI_CHEAPCLONE; 62825c7870eSDima Dorfman } 62925c7870eSDima Dorfman 63047eaa5f5SDima Dorfman static int 631f09f49f1SDima Dorfman snp_modevent(mod, type, data) 632f09f49f1SDima Dorfman module_t mod; 633f09f49f1SDima Dorfman int type; 634f09f49f1SDima Dorfman void *data; 63553ac6efbSJulian Elischer { 636f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 63753ac6efbSJulian Elischer 63847eaa5f5SDima Dorfman switch (type) { 63947eaa5f5SDima Dorfman case MOD_LOAD: 640f09f49f1SDima Dorfman /* XXX error checking. */ 6419397290eSPoul-Henning Kamp clone_setup(&snpclones); 64247eaa5f5SDima Dorfman eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000); 643f09f49f1SDima Dorfman snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 64447eaa5f5SDima Dorfman break; 64547eaa5f5SDima Dorfman case MOD_UNLOAD: 646ac60b28dSDima Dorfman if (!LIST_EMPTY(&snp_sclist)) 647ac60b28dSDima Dorfman return (EBUSY); 64847eaa5f5SDima Dorfman EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 649b0b03348SPoul-Henning Kamp clone_cleanup(&snpclones); 650f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 65147eaa5f5SDima Dorfman break; 65247eaa5f5SDima Dorfman default: 6533e019deaSPoul-Henning Kamp return (EOPNOTSUPP); 65447eaa5f5SDima Dorfman break; 65547eaa5f5SDima Dorfman } 656f09f49f1SDima Dorfman return (0); 6577198bf47SJulian Elischer } 65853ac6efbSJulian Elischer 65947eaa5f5SDima Dorfman static moduledata_t snp_mod = { 66047eaa5f5SDima Dorfman "snp", 66147eaa5f5SDima Dorfman snp_modevent, 66247eaa5f5SDima Dorfman NULL 66347eaa5f5SDima Dorfman }; 664b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 665