1098ca2bdSWarner Losh /*- 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> 221ef0fc1dSPoul-Henning Kamp #include <sys/fcntl.h> 2371455815SBruce Evans #include <sys/filio.h> 24a1c995b6SPoul-Henning Kamp #include <sys/malloc.h> 25dde8a05bSUgen J.S. Antsilevich #include <sys/tty.h> 26dde8a05bSUgen J.S. Antsilevich #include <sys/conf.h> 27659ffb48SPeter Wemm #include <sys/poll.h> 28dde8a05bSUgen J.S. Antsilevich #include <sys/kernel.h> 29fe12f24bSPoul-Henning Kamp #include <sys/module.h> 30ac60b28dSDima Dorfman #include <sys/queue.h> 3187f6c662SJulian Elischer #include <sys/snoop.h> 321ef0fc1dSPoul-Henning Kamp #include <sys/uio.h> 3387f6c662SJulian Elischer 34ddf5b796SDima Dorfman static l_close_t snplclose; 35f09f49f1SDima Dorfman static l_write_t snplwrite; 3687f6c662SJulian Elischer static d_open_t snpopen; 3787f6c662SJulian Elischer static d_close_t snpclose; 3887f6c662SJulian Elischer static d_read_t snpread; 3987f6c662SJulian Elischer static d_write_t snpwrite; 4087f6c662SJulian Elischer static d_ioctl_t snpioctl; 41659ffb48SPeter Wemm static d_poll_t snppoll; 4287f6c662SJulian Elischer 434e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = { 44dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 4563a97efcSBruce Evans .d_flags = D_PSEUDO | D_NEEDGIANT, 467ac40f5fSPoul-Henning Kamp .d_open = snpopen, 477ac40f5fSPoul-Henning Kamp .d_close = snpclose, 487ac40f5fSPoul-Henning Kamp .d_read = snpread, 497ac40f5fSPoul-Henning Kamp .d_write = snpwrite, 507ac40f5fSPoul-Henning Kamp .d_ioctl = snpioctl, 517ac40f5fSPoul-Henning Kamp .d_poll = snppoll, 527ac40f5fSPoul-Henning Kamp .d_name = "snp", 534e2f199eSPoul-Henning Kamp }; 5487f6c662SJulian Elischer 55f09f49f1SDima Dorfman static struct linesw snpdisc = { 56672c05d4SPoul-Henning Kamp .l_open = tty_open, 5766ea137dSRobert Watson .l_close = snplclose, 5866ea137dSRobert Watson .l_read = ttread, 5966ea137dSRobert Watson .l_write = snplwrite, 6066ea137dSRobert Watson .l_ioctl = l_nullioctl, 6166ea137dSRobert Watson .l_rint = ttyinput, 6266ea137dSRobert Watson .l_start = ttstart, 6366ea137dSRobert Watson .l_modem = ttymodem 64f09f49f1SDima Dorfman }; 6553ac6efbSJulian Elischer 66101f105dSDima Dorfman /* 67101f105dSDima Dorfman * This is the main snoop per-device structure. 68101f105dSDima Dorfman */ 69101f105dSDima Dorfman struct snoop { 70101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 71cdd530caSDima Dorfman int snp_unit; /* Device number. */ 7289c9c53dSPoul-Henning Kamp struct cdev *snp_target; /* Target tty device. */ 73101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 74101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 75101f105dSDima Dorfman u_long snp_base; /* Data base. */ 76101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 77101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 78101f105dSDima Dorfman int snp_flags; /* Flags. */ 79101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 80101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 81101f105dSDima Dorfman }; 82101f105dSDima Dorfman 83101f105dSDima Dorfman /* 84101f105dSDima Dorfman * Possible flags. 85101f105dSDima Dorfman */ 86101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 87101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 88101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 89101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 90101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 91101f105dSDima Dorfman 92101f105dSDima Dorfman /* 93101f105dSDima Dorfman * Other constants. 94101f105dSDima Dorfman */ 95101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 96101f105dSDima Dorfman * 4K tested to be the minimum 97101f105dSDima Dorfman * for which on normal tty 98101f105dSDima Dorfman * usage there is no need to 99101f105dSDima Dorfman * allocate more. 100101f105dSDima Dorfman */ 101101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 102101f105dSDima Dorfman * If we grow more,something 103101f105dSDima Dorfman * really bad in this world.. 104101f105dSDima Dorfman */ 105101f105dSDima Dorfman 106959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 10747eaa5f5SDima Dorfman /* 10847eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 10947eaa5f5SDima Dorfman * module load time. 11047eaa5f5SDima Dorfman */ 111f09f49f1SDima Dorfman static int snooplinedisc; 112ac60b28dSDima Dorfman 113ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 114b0b03348SPoul-Henning Kamp static struct clonedevs *snpclones; 115ac60b28dSDima Dorfman 11689c9c53dSPoul-Henning Kamp static struct tty *snpdevtotty(struct cdev *dev); 117e51a25f8SAlfred Perlstein static void snp_clone(void *arg, char *name, 11889c9c53dSPoul-Henning Kamp int namelen, struct cdev **dev); 119e51a25f8SAlfred Perlstein static int snp_detach(struct snoop *snp); 120e51a25f8SAlfred Perlstein static int snp_down(struct snoop *snp); 121e51a25f8SAlfred Perlstein static int snp_in(struct snoop *snp, char *buf, int n); 122e51a25f8SAlfred Perlstein static int snp_modevent(module_t mod, int what, void *arg); 123b5128f41SOlivier Houchard static struct snoop *ttytosnp(struct tty *); 124b5128f41SOlivier Houchard 125b5128f41SOlivier Houchard static struct snoop * 126b5128f41SOlivier Houchard ttytosnp(struct tty *tp) 127b5128f41SOlivier Houchard { 128b5128f41SOlivier Houchard struct snoop *snp; 129b5128f41SOlivier Houchard 130b5128f41SOlivier Houchard LIST_FOREACH(snp, &snp_sclist, snp_list) { 131b5128f41SOlivier Houchard if (snp->snp_tty == tp) 132b5128f41SOlivier Houchard return (snp); 133b5128f41SOlivier Houchard } 134b5128f41SOlivier Houchard return (NULL); 135b5128f41SOlivier Houchard } 13647eaa5f5SDima Dorfman 13747eaa5f5SDima Dorfman static int 138ddf5b796SDima Dorfman snplclose(tp, flag) 139ddf5b796SDima Dorfman struct tty *tp; 140ddf5b796SDima Dorfman int flag; 141ddf5b796SDima Dorfman { 142ddf5b796SDima Dorfman struct snoop *snp; 143ddf5b796SDima Dorfman int error; 144ddf5b796SDima Dorfman 145b5128f41SOlivier Houchard snp = ttytosnp(tp); 146ddf5b796SDima Dorfman error = snp_down(snp); 147ddf5b796SDima Dorfman if (error != 0) 148ddf5b796SDima Dorfman return (error); 149ddf5b796SDima Dorfman error = ttylclose(tp, flag); 150ddf5b796SDima Dorfman return (error); 151ddf5b796SDima Dorfman } 152ddf5b796SDima Dorfman 153ddf5b796SDima Dorfman static int 154f09f49f1SDima Dorfman snplwrite(tp, uio, flag) 155f09f49f1SDima Dorfman struct tty *tp; 156f09f49f1SDima Dorfman struct uio *uio; 157f09f49f1SDima Dorfman int flag; 15847eaa5f5SDima Dorfman { 15947eaa5f5SDima Dorfman struct iovec iov; 16047eaa5f5SDima Dorfman struct uio uio2; 161f09f49f1SDima Dorfman struct snoop *snp; 162f09f49f1SDima Dorfman int error, ilen; 163e692c40cSDima Dorfman char *ibuf; 16447eaa5f5SDima Dorfman 165e692c40cSDima Dorfman error = 0; 166e692c40cSDima Dorfman ibuf = NULL; 167b5128f41SOlivier Houchard snp = ttytosnp(tp); 16887826386SDima Dorfman while (uio->uio_resid > 0) { 169e692c40cSDima Dorfman ilen = imin(512, uio->uio_resid); 170a163d034SWarner Losh ibuf = malloc(ilen, M_SNP, M_WAITOK); 17147eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 172f09f49f1SDima Dorfman if (error != 0) 173e692c40cSDima Dorfman break; 174f09f49f1SDima Dorfman snp_in(snp, ibuf, ilen); 175f09f49f1SDima Dorfman /* Hackish, but probably the least of all evils. */ 17647eaa5f5SDima Dorfman iov.iov_base = ibuf; 17747eaa5f5SDima Dorfman iov.iov_len = ilen; 17847eaa5f5SDima Dorfman uio2.uio_iov = &iov; 17947eaa5f5SDima Dorfman uio2.uio_iovcnt = 1; 18047eaa5f5SDima Dorfman uio2.uio_offset = 0; 18147eaa5f5SDima Dorfman uio2.uio_resid = ilen; 18247eaa5f5SDima Dorfman uio2.uio_segflg = UIO_SYSSPACE; 18347eaa5f5SDima Dorfman uio2.uio_rw = UIO_WRITE; 184b40ce416SJulian Elischer uio2.uio_td = uio->uio_td; 18547eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 186f09f49f1SDima Dorfman if (error != 0) 187e692c40cSDima Dorfman break; 188e692c40cSDima Dorfman free(ibuf, M_SNP); 189e692c40cSDima Dorfman ibuf = NULL; 19047eaa5f5SDima Dorfman } 191e692c40cSDima Dorfman if (ibuf != NULL) 192e692c40cSDima Dorfman free(ibuf, M_SNP); 193e692c40cSDima Dorfman return (error); 194ddf5b796SDima Dorfman } 19547eaa5f5SDima Dorfman 19677f77631SPaul Traina static struct tty * 197444f003cSBruce Evans snpdevtotty(dev) 19889c9c53dSPoul-Henning Kamp struct cdev *dev; 19977f77631SPaul Traina { 200444f003cSBruce Evans struct cdevsw *cdp; 201969d098bSPoul-Henning Kamp struct tty *tp; 20277f77631SPaul Traina 203969d098bSPoul-Henning Kamp cdp = dev_refthread(dev); 204969d098bSPoul-Henning Kamp if (cdp == NULL) 205444f003cSBruce Evans return (NULL); 206969d098bSPoul-Henning Kamp if (!(cdp->d_flags & D_TTY)) 207969d098bSPoul-Henning Kamp tp = NULL; 208969d098bSPoul-Henning Kamp else 209969d098bSPoul-Henning Kamp tp = dev->si_tty; 210969d098bSPoul-Henning Kamp dev_relthread(dev); 211969d098bSPoul-Henning Kamp return (tp); 21277f77631SPaul Traina } 21377f77631SPaul Traina 2140739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 2150739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 2160739a0dcSUgen J.S. Antsilevich * length for function keys... 2170739a0dcSUgen J.S. Antsilevich */ 2180739a0dcSUgen J.S. Antsilevich 21987f6c662SJulian Elischer static int 2200739a0dcSUgen J.S. Antsilevich snpwrite(dev, uio, flag) 22189c9c53dSPoul-Henning Kamp struct cdev *dev; 2220739a0dcSUgen J.S. Antsilevich struct uio *uio; 2230739a0dcSUgen J.S. Antsilevich int flag; 2240739a0dcSUgen J.S. Antsilevich { 225f09f49f1SDima Dorfman struct snoop *snp; 2260739a0dcSUgen J.S. Antsilevich struct tty *tp; 227f09f49f1SDima Dorfman int error, i, len; 228f4c5dfbbSDima Dorfman unsigned char c[SNP_INPUT_BUF]; 2290739a0dcSUgen J.S. Antsilevich 230f09f49f1SDima Dorfman snp = dev->si_drv1; 2310739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 232f09f49f1SDima Dorfman if (tp == NULL) 233f09f49f1SDima Dorfman return (EIO); 234b5128f41SOlivier Houchard if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) 2350739a0dcSUgen J.S. Antsilevich goto tty_input; 2360739a0dcSUgen J.S. Antsilevich 237cdd530caSDima Dorfman printf("snp%d: attempt to write to bad tty\n", snp->snp_unit); 2380739a0dcSUgen J.S. Antsilevich return (EIO); 2390739a0dcSUgen J.S. Antsilevich 2400739a0dcSUgen J.S. Antsilevich tty_input: 2410739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2420739a0dcSUgen J.S. Antsilevich return (EIO); 2430739a0dcSUgen J.S. Antsilevich 2440739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 24587826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2460739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2470739a0dcSUgen J.S. Antsilevich return (error); 2480739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2490739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2500739a0dcSUgen J.S. Antsilevich return (EIO); 2510739a0dcSUgen J.S. Antsilevich } 2520739a0dcSUgen J.S. Antsilevich } 253f09f49f1SDima Dorfman return (0); 2540739a0dcSUgen J.S. Antsilevich } 2550739a0dcSUgen J.S. Antsilevich 2560739a0dcSUgen J.S. Antsilevich 25787f6c662SJulian Elischer static int 258dde8a05bSUgen J.S. Antsilevich snpread(dev, uio, flag) 25989c9c53dSPoul-Henning Kamp struct cdev *dev; 260dde8a05bSUgen J.S. Antsilevich struct uio *uio; 261dde8a05bSUgen J.S. Antsilevich int flag; 262dde8a05bSUgen J.S. Antsilevich { 263f09f49f1SDima Dorfman struct snoop *snp; 264f09f49f1SDima Dorfman int error, len, n, nblen, s; 265dde8a05bSUgen J.S. Antsilevich caddr_t from; 266dde8a05bSUgen J.S. Antsilevich char *nbuf; 267dde8a05bSUgen J.S. Antsilevich 268f09f49f1SDima Dorfman snp = dev->si_drv1; 2695526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2705526d2d9SEivind Eklund ("snoop buffer error")); 27177f77631SPaul Traina 2720739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 273dde8a05bSUgen J.S. Antsilevich return (EIO); 274dde8a05bSUgen J.S. Antsilevich 275dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 276dde8a05bSUgen J.S. Antsilevich 277dde8a05bSUgen J.S. Antsilevich do { 278dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 2791ef0fc1dSPoul-Henning Kamp if (flag & O_NONBLOCK) 280d96bc99dSBruce Evans return (EWOULDBLOCK); 281dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 282521f364bSDag-Erling Smørgrav error = tsleep(snp, (PZERO + 1) | PCATCH, 2832c1a6f21SDima Dorfman "snprd", 0); 2842c1a6f21SDima Dorfman if (error != 0) 2852c1a6f21SDima Dorfman return (error); 286dde8a05bSUgen J.S. Antsilevich } 287dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 288dde8a05bSUgen J.S. Antsilevich 289019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 290dde8a05bSUgen J.S. Antsilevich 291f09f49f1SDima Dorfman error = 0; 292dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 29387826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 294dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 295dde8a05bSUgen J.S. Antsilevich if (len == 0) 296dde8a05bSUgen J.S. Antsilevich break; 297dde8a05bSUgen J.S. Antsilevich 298dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 299dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 300dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 301dde8a05bSUgen J.S. Antsilevich } 302019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 303dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 304019b4d63SUgen J.S. Antsilevich } 305dde8a05bSUgen J.S. Antsilevich s = spltty(); 306dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 307dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 308f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 309dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 31076e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 311dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 31276e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 313dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 314dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 315dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 316dde8a05bSUgen J.S. Antsilevich } 317dde8a05bSUgen J.S. Antsilevich } 318dde8a05bSUgen J.S. Antsilevich splx(s); 319dde8a05bSUgen J.S. Antsilevich 320f09f49f1SDima Dorfman return (error); 321dde8a05bSUgen J.S. Antsilevich } 322dde8a05bSUgen J.S. Antsilevich 323f09f49f1SDima Dorfman static int 324f09f49f1SDima Dorfman snp_in(snp, buf, n) 325dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 326dde8a05bSUgen J.S. Antsilevich char *buf; 327dde8a05bSUgen J.S. Antsilevich int n; 328dde8a05bSUgen J.S. Antsilevich { 329dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 330dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 331dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 332dde8a05bSUgen J.S. Antsilevich char *nbuf; 333dde8a05bSUgen J.S. Antsilevich 334219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 335dde8a05bSUgen J.S. Antsilevich 336dde8a05bSUgen J.S. Antsilevich if (n == 0) 337f09f49f1SDima Dorfman return (0); 338dde8a05bSUgen J.S. Antsilevich 339019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 340cdd530caSDima Dorfman printf("snp%d: more data to down interface\n", snp->snp_unit); 341f09f49f1SDima Dorfman return (0); 342019b4d63SUgen J.S. Antsilevich } 343964587caSUgen J.S. Antsilevich 344019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 345cdd530caSDima Dorfman printf("snp%d: buffer overflow\n", snp->snp_unit); 346019b4d63SUgen J.S. Antsilevich /* 347019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 348019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 349019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 350019b4d63SUgen J.S. Antsilevich * snooping and retry... 351019b4d63SUgen J.S. Antsilevich */ 352dde8a05bSUgen J.S. Antsilevich 353f09f49f1SDima Dorfman return (snp_down(snp)); 354019b4d63SUgen J.S. Antsilevich } 355019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 356019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 357dde8a05bSUgen J.S. Antsilevich 358dde8a05bSUgen J.S. Antsilevich 359dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 360dde8a05bSUgen J.S. Antsilevich s = spltty(); 361dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 362dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 363dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 364dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 365dde8a05bSUgen J.S. Antsilevich } 36676e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 367dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 36876e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 369dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 370dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 371dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 372dde8a05bSUgen J.S. Antsilevich } else { 373dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 374dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 375dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 376521f364bSDag-Erling Smørgrav wakeup(snp); 377dde8a05bSUgen J.S. Antsilevich } 378dde8a05bSUgen J.S. Antsilevich splx(s); 379f09f49f1SDima Dorfman return (0); 380dde8a05bSUgen J.S. Antsilevich } 381dde8a05bSUgen J.S. Antsilevich splx(s); 382dde8a05bSUgen J.S. Antsilevich } 383dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 384dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 385dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 386dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 387dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 388dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 389dde8a05bSUgen J.S. Antsilevich } 390dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 391dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 392dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 393dde8a05bSUgen J.S. Antsilevich 394dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 395dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 396521f364bSDag-Erling Smørgrav wakeup(snp); 397dde8a05bSUgen J.S. Antsilevich } 398512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 399dde8a05bSUgen J.S. Antsilevich 400f09f49f1SDima Dorfman return (n); 401dde8a05bSUgen J.S. Antsilevich } 402dde8a05bSUgen J.S. Antsilevich 40387f6c662SJulian Elischer static int 404b40ce416SJulian Elischer snpopen(dev, flag, mode, td) 40589c9c53dSPoul-Henning Kamp struct cdev *dev; 406dde8a05bSUgen J.S. Antsilevich int flag, mode; 407b40ce416SJulian Elischer struct thread *td; 408dde8a05bSUgen J.S. Antsilevich { 409dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 410dde8a05bSUgen J.S. Antsilevich 41176e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 412b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CHEAPCLONE; 413f09f49f1SDima Dorfman dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, 414a163d034SWarner Losh M_WAITOK | M_ZERO); 415cdd530caSDima Dorfman snp->snp_unit = dev2unit(dev); 41676e90dbcSBrian Feldman } else 41776e90dbcSBrian Feldman return (EBUSY); 41877f77631SPaul Traina 419019b4d63SUgen J.S. Antsilevich /* 420019b4d63SUgen J.S. Antsilevich * We intentionally do not OR flags with SNOOP_OPEN, but set them so 421019b4d63SUgen J.S. Antsilevich * all previous settings (especially SNOOP_OFLOW) will be cleared. 422019b4d63SUgen J.S. Antsilevich */ 423019b4d63SUgen J.S. Antsilevich snp->snp_flags = SNOOP_OPEN; 424dde8a05bSUgen J.S. Antsilevich 425a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 426dde8a05bSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 427dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 428dde8a05bSUgen J.S. Antsilevich snp->snp_len = 0; 429dde8a05bSUgen J.S. Antsilevich 430dde8a05bSUgen J.S. Antsilevich /* 4310739a0dcSUgen J.S. Antsilevich * snp_tty == NULL is for inactive snoop devices. 432dde8a05bSUgen J.S. Antsilevich */ 4330739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 434f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 435ac60b28dSDima Dorfman 436ac60b28dSDima Dorfman LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 437dde8a05bSUgen J.S. Antsilevich return (0); 438dde8a05bSUgen J.S. Antsilevich } 439dde8a05bSUgen J.S. Antsilevich 440019b4d63SUgen J.S. Antsilevich 44187b6de2bSPoul-Henning Kamp static int 442019b4d63SUgen J.S. Antsilevich snp_detach(snp) 443019b4d63SUgen J.S. Antsilevich struct snoop *snp; 444019b4d63SUgen J.S. Antsilevich { 445019b4d63SUgen J.S. Antsilevich struct tty *tp; 446019b4d63SUgen J.S. Antsilevich 447019b4d63SUgen J.S. Antsilevich snp->snp_base = 0; 448019b4d63SUgen J.S. Antsilevich snp->snp_len = 0; 449019b4d63SUgen J.S. Antsilevich 450019b4d63SUgen J.S. Antsilevich /* 451019b4d63SUgen J.S. Antsilevich * If line disc. changed we do not touch this pointer, SLIP/PPP will 452019b4d63SUgen J.S. Antsilevich * change it anyway. 453019b4d63SUgen J.S. Antsilevich */ 454f09f49f1SDima Dorfman tp = snp->snp_tty; 455f09f49f1SDima Dorfman if (tp == NULL) 456964587caSUgen J.S. Antsilevich goto detach_notty; 457019b4d63SUgen J.S. Antsilevich 458b5128f41SOlivier Houchard if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) { 459019b4d63SUgen J.S. Antsilevich tp->t_state &= ~TS_SNOOP; 46047eaa5f5SDima Dorfman tp->t_line = snp->snp_olddisc; 461019b4d63SUgen J.S. Antsilevich } else 462cdd530caSDima Dorfman printf("snp%d: bad attached tty data\n", snp->snp_unit); 463019b4d63SUgen J.S. Antsilevich 4640739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 465f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 466019b4d63SUgen J.S. Antsilevich 467964587caSUgen J.S. Antsilevich detach_notty: 468512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 46976e90dbcSBrian Feldman if ((snp->snp_flags & SNOOP_OPEN) == 0) 47076e90dbcSBrian Feldman free(snp, M_SNP); 471019b4d63SUgen J.S. Antsilevich 472019b4d63SUgen J.S. Antsilevich return (0); 473019b4d63SUgen J.S. Antsilevich } 474019b4d63SUgen J.S. Antsilevich 47587f6c662SJulian Elischer static int 476b40ce416SJulian Elischer snpclose(dev, flags, fmt, td) 47789c9c53dSPoul-Henning Kamp struct cdev *dev; 47860039670SBruce Evans int flags; 47960039670SBruce Evans int fmt; 480b40ce416SJulian Elischer struct thread *td; 481dde8a05bSUgen J.S. Antsilevich { 482f09f49f1SDima Dorfman struct snoop *snp; 483dde8a05bSUgen J.S. Antsilevich 484f09f49f1SDima Dorfman snp = dev->si_drv1; 485dde8a05bSUgen J.S. Antsilevich snp->snp_blen = 0; 486ac60b28dSDima Dorfman LIST_REMOVE(snp, snp_list); 48776e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 488019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OPEN; 48976e90dbcSBrian Feldman dev->si_drv1 = NULL; 490b0b03348SPoul-Henning Kamp destroy_dev(dev); 491dde8a05bSUgen J.S. Antsilevich 492019b4d63SUgen J.S. Antsilevich return (snp_detach(snp)); 493dde8a05bSUgen J.S. Antsilevich } 494dde8a05bSUgen J.S. Antsilevich 495f09f49f1SDima Dorfman static int 496f09f49f1SDima Dorfman snp_down(snp) 497964587caSUgen J.S. Antsilevich struct snoop *snp; 498964587caSUgen J.S. Antsilevich { 49976e90dbcSBrian Feldman 50076e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 50176e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 502a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 503964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 50476e90dbcSBrian Feldman } 505964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 506dde8a05bSUgen J.S. Antsilevich 507964587caSUgen J.S. Antsilevich return (snp_detach(snp)); 508964587caSUgen J.S. Antsilevich } 509dde8a05bSUgen J.S. Antsilevich 51087f6c662SJulian Elischer static int 511b40ce416SJulian Elischer snpioctl(dev, cmd, data, flags, td) 51289c9c53dSPoul-Henning Kamp struct cdev *dev; 513ecbb00a2SDoug Rabson u_long cmd; 514dde8a05bSUgen J.S. Antsilevich caddr_t data; 51560039670SBruce Evans int flags; 516b40ce416SJulian Elischer struct thread *td; 517dde8a05bSUgen J.S. Antsilevich { 518f09f49f1SDima Dorfman struct snoop *snp; 519dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 52089c9c53dSPoul-Henning Kamp struct cdev *tdev; 52176e90dbcSBrian Feldman int s; 522dde8a05bSUgen J.S. Antsilevich 523f09f49f1SDima Dorfman snp = dev->si_drv1; 524dde8a05bSUgen J.S. Antsilevich switch (cmd) { 525dde8a05bSUgen J.S. Antsilevich case SNPSTTY: 526f3732fd1SPoul-Henning Kamp tdev = findcdev(*((dev_t *)data)); 527f3732fd1SPoul-Henning Kamp if (tdev == NULL) 528f09f49f1SDima Dorfman return (snp_down(snp)); 529964587caSUgen J.S. Antsilevich 530444f003cSBruce Evans tp = snpdevtotty(tdev); 53177f77631SPaul Traina if (!tp) 532dde8a05bSUgen J.S. Antsilevich return (EINVAL); 5332c9fb909SDima Dorfman if (tp->t_state & TS_SNOOP) 5342c9fb909SDima Dorfman return (EBUSY); 535dde8a05bSUgen J.S. Antsilevich 536dde8a05bSUgen J.S. Antsilevich s = spltty(); 53777f77631SPaul Traina 538f3732fd1SPoul-Henning Kamp if (snp->snp_target == NULL) { 5390739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 54077f77631SPaul Traina if (tpo) 541dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 542dde8a05bSUgen J.S. Antsilevich } 54377f77631SPaul Traina 544dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 54547eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 546f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5470739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 54877f77631SPaul Traina snp->snp_target = tdev; 54977f77631SPaul Traina 550964587caSUgen J.S. Antsilevich /* 551964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 552964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 553964587caSUgen J.S. Antsilevich */ 554964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 555019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 556dde8a05bSUgen J.S. Antsilevich splx(s); 557dde8a05bSUgen J.S. Antsilevich break; 55877f77631SPaul Traina 559dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5600739a0dcSUgen J.S. Antsilevich /* 5610739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5620739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5630739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5640739a0dcSUgen J.S. Antsilevich */ 565f3732fd1SPoul-Henning Kamp *((dev_t *)data) = dev2udev(snp->snp_target); 566dde8a05bSUgen J.S. Antsilevich break; 567dde8a05bSUgen J.S. Antsilevich 568dde8a05bSUgen J.S. Antsilevich case FIONBIO: 569dde8a05bSUgen J.S. Antsilevich break; 57077f77631SPaul Traina 571dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 572dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 573dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 574dde8a05bSUgen J.S. Antsilevich else 575dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 576dde8a05bSUgen J.S. Antsilevich break; 57777f77631SPaul Traina 578dde8a05bSUgen J.S. Antsilevich case FIONREAD: 579dde8a05bSUgen J.S. Antsilevich s = spltty(); 5800739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 581dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 582dde8a05bSUgen J.S. Antsilevich else 583964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 584964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 585964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 586964587caSUgen J.S. Antsilevich else 587964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 588964587caSUgen J.S. Antsilevich } else { 589964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 590964587caSUgen J.S. Antsilevich } 591dde8a05bSUgen J.S. Antsilevich splx(s); 592dde8a05bSUgen J.S. Antsilevich break; 59377f77631SPaul Traina 594dde8a05bSUgen J.S. Antsilevich default: 595dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 596dde8a05bSUgen J.S. Antsilevich } 597dde8a05bSUgen J.S. Antsilevich return (0); 598dde8a05bSUgen J.S. Antsilevich } 599dde8a05bSUgen J.S. Antsilevich 60087f6c662SJulian Elischer static int 601b40ce416SJulian Elischer snppoll(dev, events, td) 60289c9c53dSPoul-Henning Kamp struct cdev *dev; 603659ffb48SPeter Wemm int events; 604b40ce416SJulian Elischer struct thread *td; 605dde8a05bSUgen J.S. Antsilevich { 606f09f49f1SDima Dorfman struct snoop *snp; 607f09f49f1SDima Dorfman int revents; 608dde8a05bSUgen J.S. Antsilevich 609f09f49f1SDima Dorfman snp = dev->si_drv1; 610f09f49f1SDima Dorfman revents = 0; 611019b4d63SUgen J.S. Antsilevich /* 612659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 613019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 614019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 615019b4d63SUgen J.S. Antsilevich */ 616dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 617659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 618659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 619659ffb48SPeter Wemm else 620b40ce416SJulian Elischer selrecord(td, &snp->snp_sel); 621dfd5dee1SPeter Wemm } 622659ffb48SPeter Wemm return (revents); 623dde8a05bSUgen J.S. Antsilevich } 624dde8a05bSUgen J.S. Antsilevich 62587f6c662SJulian Elischer static void 626f09f49f1SDima Dorfman snp_clone(arg, name, namelen, dev) 627f09f49f1SDima Dorfman void *arg; 628f09f49f1SDima Dorfman char *name; 629f09f49f1SDima Dorfman int namelen; 63089c9c53dSPoul-Henning Kamp struct cdev **dev; 63125c7870eSDima Dorfman { 632b0b03348SPoul-Henning Kamp int u, i; 63325c7870eSDima Dorfman 634f3732fd1SPoul-Henning Kamp if (*dev != NULL) 63525c7870eSDima Dorfman return; 63625c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 63725c7870eSDima Dorfman return; 638b0b03348SPoul-Henning Kamp i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0); 639b0b03348SPoul-Henning Kamp if (i) 640b0b03348SPoul-Henning Kamp *dev = make_dev(&snp_cdevsw, unit2minor(u), 641b0b03348SPoul-Henning Kamp UID_ROOT, GID_WHEEL, 0600, "snp%d", u); 642b0b03348SPoul-Henning Kamp if (*dev != NULL) 6432f7a3851SDima Dorfman (*dev)->si_flags |= SI_CHEAPCLONE; 64425c7870eSDima Dorfman } 64525c7870eSDima Dorfman 64647eaa5f5SDima Dorfman static int 647f09f49f1SDima Dorfman snp_modevent(mod, type, data) 648f09f49f1SDima Dorfman module_t mod; 649f09f49f1SDima Dorfman int type; 650f09f49f1SDima Dorfman void *data; 65153ac6efbSJulian Elischer { 652f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 65353ac6efbSJulian Elischer 65447eaa5f5SDima Dorfman switch (type) { 65547eaa5f5SDima Dorfman case MOD_LOAD: 656f09f49f1SDima Dorfman /* XXX error checking. */ 6579397290eSPoul-Henning Kamp clone_setup(&snpclones); 65847eaa5f5SDima Dorfman eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000); 659f09f49f1SDima Dorfman snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 66047eaa5f5SDima Dorfman break; 66147eaa5f5SDima Dorfman case MOD_UNLOAD: 662ac60b28dSDima Dorfman if (!LIST_EMPTY(&snp_sclist)) 663ac60b28dSDima Dorfman return (EBUSY); 66447eaa5f5SDima Dorfman EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 665b0b03348SPoul-Henning Kamp clone_cleanup(&snpclones); 666f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 66747eaa5f5SDima Dorfman break; 66847eaa5f5SDima Dorfman default: 6693e019deaSPoul-Henning Kamp return (EOPNOTSUPP); 67047eaa5f5SDima Dorfman break; 67147eaa5f5SDima Dorfman } 672f09f49f1SDima Dorfman return (0); 6737198bf47SJulian Elischer } 67453ac6efbSJulian Elischer 67547eaa5f5SDima Dorfman static moduledata_t snp_mod = { 67647eaa5f5SDima Dorfman "snp", 67747eaa5f5SDima Dorfman snp_modevent, 67847eaa5f5SDima Dorfman NULL 67947eaa5f5SDima Dorfman }; 680b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 681