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> 3330ce1aadSOlivier Houchard #include <sys/file.h> 3430ce1aadSOlivier Houchard #include <sys/vnode.h> 3587f6c662SJulian Elischer 36ddf5b796SDima Dorfman static l_close_t snplclose; 37f09f49f1SDima Dorfman static l_write_t snplwrite; 3887f6c662SJulian Elischer static d_open_t snpopen; 3987f6c662SJulian Elischer static d_close_t snpclose; 4087f6c662SJulian Elischer static d_read_t snpread; 4187f6c662SJulian Elischer static d_write_t snpwrite; 4287f6c662SJulian Elischer static d_ioctl_t snpioctl; 43659ffb48SPeter Wemm static d_poll_t snppoll; 4487f6c662SJulian Elischer 454e2f199eSPoul-Henning Kamp static struct cdevsw snp_cdevsw = { 46dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 4763a97efcSBruce Evans .d_flags = D_PSEUDO | D_NEEDGIANT, 487ac40f5fSPoul-Henning Kamp .d_open = snpopen, 497ac40f5fSPoul-Henning Kamp .d_close = snpclose, 507ac40f5fSPoul-Henning Kamp .d_read = snpread, 517ac40f5fSPoul-Henning Kamp .d_write = snpwrite, 527ac40f5fSPoul-Henning Kamp .d_ioctl = snpioctl, 537ac40f5fSPoul-Henning Kamp .d_poll = snppoll, 547ac40f5fSPoul-Henning Kamp .d_name = "snp", 554e2f199eSPoul-Henning Kamp }; 5687f6c662SJulian Elischer 57f09f49f1SDima Dorfman static struct linesw snpdisc = { 58672c05d4SPoul-Henning Kamp .l_open = tty_open, 5966ea137dSRobert Watson .l_close = snplclose, 6066ea137dSRobert Watson .l_read = ttread, 6166ea137dSRobert Watson .l_write = snplwrite, 6266ea137dSRobert Watson .l_ioctl = l_nullioctl, 6366ea137dSRobert Watson .l_rint = ttyinput, 6466ea137dSRobert Watson .l_start = ttstart, 6566ea137dSRobert Watson .l_modem = ttymodem 66f09f49f1SDima Dorfman }; 6753ac6efbSJulian Elischer 68101f105dSDima Dorfman /* 69101f105dSDima Dorfman * This is the main snoop per-device structure. 70101f105dSDima Dorfman */ 71101f105dSDima Dorfman struct snoop { 72101f105dSDima Dorfman LIST_ENTRY(snoop) snp_list; /* List glue. */ 73cdd530caSDima Dorfman int snp_unit; /* Device number. */ 7489c9c53dSPoul-Henning Kamp struct cdev *snp_target; /* Target tty device. */ 75101f105dSDima Dorfman struct tty *snp_tty; /* Target tty pointer. */ 76101f105dSDima Dorfman u_long snp_len; /* Possible length. */ 77101f105dSDima Dorfman u_long snp_base; /* Data base. */ 78101f105dSDima Dorfman u_long snp_blen; /* Used length. */ 79101f105dSDima Dorfman caddr_t snp_buf; /* Allocation pointer. */ 80101f105dSDima Dorfman int snp_flags; /* Flags. */ 81101f105dSDima Dorfman struct selinfo snp_sel; /* Select info. */ 82101f105dSDima Dorfman int snp_olddisc; /* Old line discipline. */ 83101f105dSDima Dorfman }; 84101f105dSDima Dorfman 85101f105dSDima Dorfman /* 86101f105dSDima Dorfman * Possible flags. 87101f105dSDima Dorfman */ 88101f105dSDima Dorfman #define SNOOP_ASYNC 0x0002 89101f105dSDima Dorfman #define SNOOP_OPEN 0x0004 90101f105dSDima Dorfman #define SNOOP_RWAIT 0x0008 91101f105dSDima Dorfman #define SNOOP_OFLOW 0x0010 92101f105dSDima Dorfman #define SNOOP_DOWN 0x0020 93101f105dSDima Dorfman 94101f105dSDima Dorfman /* 95101f105dSDima Dorfman * Other constants. 96101f105dSDima Dorfman */ 97101f105dSDima Dorfman #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 98101f105dSDima Dorfman * 4K tested to be the minimum 99101f105dSDima Dorfman * for which on normal tty 100101f105dSDima Dorfman * usage there is no need to 101101f105dSDima Dorfman * allocate more. 102101f105dSDima Dorfman */ 103101f105dSDima Dorfman #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 104101f105dSDima Dorfman * If we grow more,something 105101f105dSDima Dorfman * really bad in this world.. 106101f105dSDima Dorfman */ 107101f105dSDima Dorfman 108959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 10947eaa5f5SDima Dorfman /* 11047eaa5f5SDima Dorfman * The number of the "snoop" line discipline. This gets determined at 11147eaa5f5SDima Dorfman * module load time. 11247eaa5f5SDima Dorfman */ 113f09f49f1SDima Dorfman static int snooplinedisc; 114ac60b28dSDima Dorfman 115ac60b28dSDima Dorfman static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 116b0b03348SPoul-Henning Kamp static struct clonedevs *snpclones; 117ac60b28dSDima Dorfman 11889c9c53dSPoul-Henning Kamp static struct tty *snpdevtotty(struct cdev *dev); 1196a113b3dSRobert Watson static void snp_clone(void *arg, struct ucred *cred, char *name, 12089c9c53dSPoul-Henning Kamp int namelen, struct cdev **dev); 121542a8db5SKonstantin Belousov static void snp_detach(void *arg); 122e51a25f8SAlfred Perlstein static int snp_down(struct snoop *snp); 123e51a25f8SAlfred Perlstein static int snp_in(struct snoop *snp, char *buf, int n); 124e51a25f8SAlfred Perlstein static int snp_modevent(module_t mod, int what, void *arg); 125b5128f41SOlivier Houchard static struct snoop *ttytosnp(struct tty *); 126b5128f41SOlivier Houchard 127b5128f41SOlivier Houchard static struct snoop * 128b5128f41SOlivier Houchard ttytosnp(struct tty *tp) 129b5128f41SOlivier Houchard { 130b5128f41SOlivier Houchard struct snoop *snp; 131b5128f41SOlivier Houchard 132b5128f41SOlivier Houchard LIST_FOREACH(snp, &snp_sclist, snp_list) { 133b5128f41SOlivier Houchard if (snp->snp_tty == tp) 134b5128f41SOlivier Houchard return (snp); 135b5128f41SOlivier Houchard } 136b5128f41SOlivier Houchard return (NULL); 137b5128f41SOlivier Houchard } 13847eaa5f5SDima Dorfman 13947eaa5f5SDima Dorfman static int 1407409f6cdSCraig Rodrigues snplclose(struct tty *tp, 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 1547409f6cdSCraig Rodrigues snplwrite(struct tty *tp, struct uio *uio, int flag) 15547eaa5f5SDima Dorfman { 15647eaa5f5SDima Dorfman struct iovec iov; 15747eaa5f5SDima Dorfman struct uio uio2; 158f09f49f1SDima Dorfman struct snoop *snp; 159f09f49f1SDima Dorfman int error, ilen; 160e692c40cSDima Dorfman char *ibuf; 16147eaa5f5SDima Dorfman 162e692c40cSDima Dorfman error = 0; 163e692c40cSDima Dorfman ibuf = NULL; 164b5128f41SOlivier Houchard snp = ttytosnp(tp); 16587826386SDima Dorfman while (uio->uio_resid > 0) { 166e692c40cSDima Dorfman ilen = imin(512, uio->uio_resid); 167a163d034SWarner Losh ibuf = malloc(ilen, M_SNP, M_WAITOK); 16847eaa5f5SDima Dorfman error = uiomove(ibuf, ilen, uio); 169f09f49f1SDima Dorfman if (error != 0) 170e692c40cSDima Dorfman break; 171f09f49f1SDima Dorfman snp_in(snp, ibuf, ilen); 172f09f49f1SDima Dorfman /* Hackish, but probably the least of all evils. */ 17347eaa5f5SDima Dorfman iov.iov_base = ibuf; 17447eaa5f5SDima Dorfman iov.iov_len = ilen; 17547eaa5f5SDima Dorfman uio2.uio_iov = &iov; 17647eaa5f5SDima Dorfman uio2.uio_iovcnt = 1; 17747eaa5f5SDima Dorfman uio2.uio_offset = 0; 17847eaa5f5SDima Dorfman uio2.uio_resid = ilen; 17947eaa5f5SDima Dorfman uio2.uio_segflg = UIO_SYSSPACE; 18047eaa5f5SDima Dorfman uio2.uio_rw = UIO_WRITE; 181b40ce416SJulian Elischer uio2.uio_td = uio->uio_td; 18247eaa5f5SDima Dorfman error = ttwrite(tp, &uio2, flag); 183f09f49f1SDima Dorfman if (error != 0) 184e692c40cSDima Dorfman break; 185e692c40cSDima Dorfman free(ibuf, M_SNP); 186e692c40cSDima Dorfman ibuf = NULL; 18747eaa5f5SDima Dorfman } 188e692c40cSDima Dorfman if (ibuf != NULL) 189e692c40cSDima Dorfman free(ibuf, M_SNP); 190e692c40cSDima Dorfman return (error); 191ddf5b796SDima Dorfman } 19247eaa5f5SDima Dorfman 19377f77631SPaul Traina static struct tty * 1947409f6cdSCraig Rodrigues snpdevtotty(struct cdev *dev) 19577f77631SPaul Traina { 196444f003cSBruce Evans struct cdevsw *cdp; 197969d098bSPoul-Henning Kamp struct tty *tp; 19877f77631SPaul Traina 199969d098bSPoul-Henning Kamp cdp = dev_refthread(dev); 200969d098bSPoul-Henning Kamp if (cdp == NULL) 201444f003cSBruce Evans return (NULL); 202969d098bSPoul-Henning Kamp if (!(cdp->d_flags & D_TTY)) 203969d098bSPoul-Henning Kamp tp = NULL; 204969d098bSPoul-Henning Kamp else 205969d098bSPoul-Henning Kamp tp = dev->si_tty; 206969d098bSPoul-Henning Kamp dev_relthread(dev); 207969d098bSPoul-Henning Kamp return (tp); 20877f77631SPaul Traina } 20977f77631SPaul Traina 2100739a0dcSUgen J.S. Antsilevich #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 2110739a0dcSUgen J.S. Antsilevich * interactive mode write is 3 bytes 2120739a0dcSUgen J.S. Antsilevich * length for function keys... 2130739a0dcSUgen J.S. Antsilevich */ 2140739a0dcSUgen J.S. Antsilevich 21587f6c662SJulian Elischer static int 2167409f6cdSCraig Rodrigues snpwrite(struct cdev *dev, struct uio *uio, int flag) 2170739a0dcSUgen J.S. Antsilevich { 218f09f49f1SDima Dorfman struct snoop *snp; 2190739a0dcSUgen J.S. Antsilevich struct tty *tp; 220f09f49f1SDima Dorfman int error, i, len; 221f4c5dfbbSDima Dorfman unsigned char c[SNP_INPUT_BUF]; 2220739a0dcSUgen J.S. Antsilevich 223f09f49f1SDima Dorfman snp = dev->si_drv1; 2240739a0dcSUgen J.S. Antsilevich tp = snp->snp_tty; 225f09f49f1SDima Dorfman if (tp == NULL) 226f09f49f1SDima Dorfman return (EIO); 227b5128f41SOlivier Houchard if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) 2280739a0dcSUgen J.S. Antsilevich goto tty_input; 2290739a0dcSUgen J.S. Antsilevich 230cdd530caSDima Dorfman printf("snp%d: attempt to write to bad tty\n", snp->snp_unit); 2310739a0dcSUgen J.S. Antsilevich return (EIO); 2320739a0dcSUgen J.S. Antsilevich 2330739a0dcSUgen J.S. Antsilevich tty_input: 2340739a0dcSUgen J.S. Antsilevich if (!(tp->t_state & TS_ISOPEN)) 2350739a0dcSUgen J.S. Antsilevich return (EIO); 2360739a0dcSUgen J.S. Antsilevich 2370739a0dcSUgen J.S. Antsilevich while (uio->uio_resid > 0) { 23887826386SDima Dorfman len = imin(uio->uio_resid, SNP_INPUT_BUF); 2390739a0dcSUgen J.S. Antsilevich if ((error = uiomove(c, len, uio)) != 0) 2400739a0dcSUgen J.S. Antsilevich return (error); 2410739a0dcSUgen J.S. Antsilevich for (i=0; i < len; i++) { 2420739a0dcSUgen J.S. Antsilevich if (ttyinput(c[i], tp)) 2430739a0dcSUgen J.S. Antsilevich return (EIO); 2440739a0dcSUgen J.S. Antsilevich } 2450739a0dcSUgen J.S. Antsilevich } 246f09f49f1SDima Dorfman return (0); 2470739a0dcSUgen J.S. Antsilevich } 2480739a0dcSUgen J.S. Antsilevich 2490739a0dcSUgen J.S. Antsilevich 25087f6c662SJulian Elischer static int 2517409f6cdSCraig Rodrigues snpread(struct cdev *dev, struct uio *uio, int flag) 252dde8a05bSUgen J.S. Antsilevich { 253f09f49f1SDima Dorfman struct snoop *snp; 254f09f49f1SDima Dorfman int error, len, n, nblen, s; 255dde8a05bSUgen J.S. Antsilevich caddr_t from; 256dde8a05bSUgen J.S. Antsilevich char *nbuf; 257dde8a05bSUgen J.S. Antsilevich 258f09f49f1SDima Dorfman snp = dev->si_drv1; 2595526d2d9SEivind Eklund KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 2605526d2d9SEivind Eklund ("snoop buffer error")); 26177f77631SPaul Traina 2620739a0dcSUgen J.S. Antsilevich if (snp->snp_tty == NULL) 263dde8a05bSUgen J.S. Antsilevich return (EIO); 264dde8a05bSUgen J.S. Antsilevich 265dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 266dde8a05bSUgen J.S. Antsilevich 267dde8a05bSUgen J.S. Antsilevich do { 268dde8a05bSUgen J.S. Antsilevich if (snp->snp_len == 0) { 2691ef0fc1dSPoul-Henning Kamp if (flag & O_NONBLOCK) 270d96bc99dSBruce Evans return (EWOULDBLOCK); 271dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_RWAIT; 272521f364bSDag-Erling Smørgrav error = tsleep(snp, (PZERO + 1) | PCATCH, 2732c1a6f21SDima Dorfman "snprd", 0); 2742c1a6f21SDima Dorfman if (error != 0) 2752c1a6f21SDima Dorfman return (error); 276dde8a05bSUgen J.S. Antsilevich } 277dde8a05bSUgen J.S. Antsilevich } while (snp->snp_len == 0); 278dde8a05bSUgen J.S. Antsilevich 279019b4d63SUgen J.S. Antsilevich n = snp->snp_len; 280dde8a05bSUgen J.S. Antsilevich 281f09f49f1SDima Dorfman error = 0; 282dde8a05bSUgen J.S. Antsilevich while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 28387826386SDima Dorfman len = min((unsigned)uio->uio_resid, snp->snp_len); 284dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 285dde8a05bSUgen J.S. Antsilevich if (len == 0) 286dde8a05bSUgen J.S. Antsilevich break; 287dde8a05bSUgen J.S. Antsilevich 288dde8a05bSUgen J.S. Antsilevich error = uiomove(from, len, uio); 289dde8a05bSUgen J.S. Antsilevich snp->snp_base += len; 290dde8a05bSUgen J.S. Antsilevich snp->snp_len -= len; 291dde8a05bSUgen J.S. Antsilevich } 292019b4d63SUgen J.S. Antsilevich if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 293dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 294019b4d63SUgen J.S. Antsilevich } 295dde8a05bSUgen J.S. Antsilevich s = spltty(); 296dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 297dde8a05bSUgen J.S. Antsilevich if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 298f09f49f1SDima Dorfman while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 299dde8a05bSUgen J.S. Antsilevich nblen = nblen / 2; 30076e90dbcSBrian Feldman if ((nbuf = malloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 301dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 30276e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 303dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 304dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 305dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 306dde8a05bSUgen J.S. Antsilevich } 307dde8a05bSUgen J.S. Antsilevich } 308dde8a05bSUgen J.S. Antsilevich splx(s); 309dde8a05bSUgen J.S. Antsilevich 310f09f49f1SDima Dorfman return (error); 311dde8a05bSUgen J.S. Antsilevich } 312dde8a05bSUgen J.S. Antsilevich 313f09f49f1SDima Dorfman static int 3147409f6cdSCraig Rodrigues snp_in(struct snoop *snp, char *buf, int n) 315dde8a05bSUgen J.S. Antsilevich { 316dde8a05bSUgen J.S. Antsilevich int s_free, s_tail; 317dde8a05bSUgen J.S. Antsilevich int s, len, nblen; 318dde8a05bSUgen J.S. Antsilevich caddr_t from, to; 319dde8a05bSUgen J.S. Antsilevich char *nbuf; 320dde8a05bSUgen J.S. Antsilevich 321219cbf59SEivind Eklund KASSERT(n >= 0, ("negative snoop char count")); 322dde8a05bSUgen J.S. Antsilevich 323dde8a05bSUgen J.S. Antsilevich if (n == 0) 324f09f49f1SDima Dorfman return (0); 325dde8a05bSUgen J.S. Antsilevich 326019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 327cdd530caSDima Dorfman printf("snp%d: more data to down interface\n", snp->snp_unit); 328f09f49f1SDima Dorfman return (0); 329019b4d63SUgen J.S. Antsilevich } 330964587caSUgen J.S. Antsilevich 331019b4d63SUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) { 332cdd530caSDima Dorfman printf("snp%d: buffer overflow\n", snp->snp_unit); 333019b4d63SUgen J.S. Antsilevich /* 334019b4d63SUgen J.S. Antsilevich * On overflow we just repeat the standart close 335019b4d63SUgen J.S. Antsilevich * procedure...yes , this is waste of space but.. Then next 336019b4d63SUgen J.S. Antsilevich * read from device will fail if one would recall he is 337019b4d63SUgen J.S. Antsilevich * snooping and retry... 338019b4d63SUgen J.S. Antsilevich */ 339dde8a05bSUgen J.S. Antsilevich 340f09f49f1SDima Dorfman return (snp_down(snp)); 341019b4d63SUgen J.S. Antsilevich } 342019b4d63SUgen J.S. Antsilevich s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 343019b4d63SUgen J.S. Antsilevich s_free = snp->snp_blen - snp->snp_len; 344dde8a05bSUgen J.S. Antsilevich 345dde8a05bSUgen J.S. Antsilevich 346dde8a05bSUgen J.S. Antsilevich if (n > s_free) { 347dde8a05bSUgen J.S. Antsilevich s = spltty(); 348dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen; 349dde8a05bSUgen J.S. Antsilevich while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 350dde8a05bSUgen J.S. Antsilevich nblen = snp->snp_blen * 2; 351dde8a05bSUgen J.S. Antsilevich s_free = nblen - (snp->snp_len + snp->snp_base); 352dde8a05bSUgen J.S. Antsilevich } 35376e90dbcSBrian Feldman if ((n <= s_free) && (nbuf = malloc(nblen, M_SNP, M_NOWAIT))) { 354dde8a05bSUgen J.S. Antsilevich bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 35576e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 356dde8a05bSUgen J.S. Antsilevich snp->snp_buf = nbuf; 357dde8a05bSUgen J.S. Antsilevich snp->snp_blen = nblen; 358dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 359dde8a05bSUgen J.S. Antsilevich } else { 360dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_OFLOW; 361dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 362dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 363521f364bSDag-Erling Smørgrav wakeup(snp); 364dde8a05bSUgen J.S. Antsilevich } 365dde8a05bSUgen J.S. Antsilevich splx(s); 366f09f49f1SDima Dorfman return (0); 367dde8a05bSUgen J.S. Antsilevich } 368dde8a05bSUgen J.S. Antsilevich splx(s); 369dde8a05bSUgen J.S. Antsilevich } 370dde8a05bSUgen J.S. Antsilevich if (n > s_tail) { 371dde8a05bSUgen J.S. Antsilevich from = (caddr_t)(snp->snp_buf + snp->snp_base); 372dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf); 373dde8a05bSUgen J.S. Antsilevich len = snp->snp_len; 374dde8a05bSUgen J.S. Antsilevich bcopy(from, to, len); 375dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 376dde8a05bSUgen J.S. Antsilevich } 377dde8a05bSUgen J.S. Antsilevich to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 378dde8a05bSUgen J.S. Antsilevich bcopy(buf, to, n); 379dde8a05bSUgen J.S. Antsilevich snp->snp_len += n; 380dde8a05bSUgen J.S. Antsilevich 381dde8a05bSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_RWAIT) { 382dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_RWAIT; 383521f364bSDag-Erling Smørgrav wakeup(snp); 384dde8a05bSUgen J.S. Antsilevich } 385512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 386dde8a05bSUgen J.S. Antsilevich 387f09f49f1SDima Dorfman return (n); 388dde8a05bSUgen J.S. Antsilevich } 389dde8a05bSUgen J.S. Antsilevich 39087f6c662SJulian Elischer static int 3917409f6cdSCraig Rodrigues snpopen(struct cdev *dev, int flag, int mode, struct thread *td) 392dde8a05bSUgen J.S. Antsilevich { 393dde8a05bSUgen J.S. Antsilevich struct snoop *snp; 394dde8a05bSUgen J.S. Antsilevich 39576e90dbcSBrian Feldman if (dev->si_drv1 == NULL) { 396b0b03348SPoul-Henning Kamp dev->si_flags &= ~SI_CHEAPCLONE; 397f09f49f1SDima Dorfman dev->si_drv1 = snp = malloc(sizeof(*snp), M_SNP, 398a163d034SWarner Losh M_WAITOK | M_ZERO); 399cdd530caSDima Dorfman snp->snp_unit = dev2unit(dev); 40076e90dbcSBrian Feldman } else 40176e90dbcSBrian Feldman return (EBUSY); 40277f77631SPaul Traina 403019b4d63SUgen J.S. Antsilevich /* 404019b4d63SUgen J.S. Antsilevich * We intentionally do not OR flags with SNOOP_OPEN, but set them so 405019b4d63SUgen J.S. Antsilevich * all previous settings (especially SNOOP_OFLOW) will be cleared. 406019b4d63SUgen J.S. Antsilevich */ 407019b4d63SUgen J.S. Antsilevich snp->snp_flags = SNOOP_OPEN; 408dde8a05bSUgen J.S. Antsilevich 409a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 410dde8a05bSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 411dde8a05bSUgen J.S. Antsilevich snp->snp_base = 0; 412dde8a05bSUgen J.S. Antsilevich snp->snp_len = 0; 413dde8a05bSUgen J.S. Antsilevich 414dde8a05bSUgen J.S. Antsilevich /* 4150739a0dcSUgen J.S. Antsilevich * snp_tty == NULL is for inactive snoop devices. 416dde8a05bSUgen J.S. Antsilevich */ 4170739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 418f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 419ac60b28dSDima Dorfman 420ac60b28dSDima Dorfman LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 421dde8a05bSUgen J.S. Antsilevich return (0); 422dde8a05bSUgen J.S. Antsilevich } 423dde8a05bSUgen J.S. Antsilevich 424019b4d63SUgen J.S. Antsilevich 425542a8db5SKonstantin Belousov static void 426542a8db5SKonstantin Belousov snp_detach(void *arg) 427019b4d63SUgen J.S. Antsilevich { 428542a8db5SKonstantin Belousov struct snoop *snp; 429019b4d63SUgen J.S. Antsilevich struct tty *tp; 430019b4d63SUgen J.S. Antsilevich 431542a8db5SKonstantin Belousov snp = (struct snoop *)arg; 432019b4d63SUgen J.S. Antsilevich snp->snp_base = 0; 433019b4d63SUgen J.S. Antsilevich snp->snp_len = 0; 434019b4d63SUgen J.S. Antsilevich 435019b4d63SUgen J.S. Antsilevich /* 436019b4d63SUgen J.S. Antsilevich * If line disc. changed we do not touch this pointer, SLIP/PPP will 437019b4d63SUgen J.S. Antsilevich * change it anyway. 438019b4d63SUgen J.S. Antsilevich */ 439f09f49f1SDima Dorfman tp = snp->snp_tty; 440f09f49f1SDima Dorfman if (tp == NULL) 441964587caSUgen J.S. Antsilevich goto detach_notty; 442019b4d63SUgen J.S. Antsilevich 443b5128f41SOlivier Houchard if ((tp->t_state & TS_SNOOP) && tp->t_line == snooplinedisc) { 444019b4d63SUgen J.S. Antsilevich tp->t_state &= ~TS_SNOOP; 44547eaa5f5SDima Dorfman tp->t_line = snp->snp_olddisc; 446019b4d63SUgen J.S. Antsilevich } else 447cdd530caSDima Dorfman printf("snp%d: bad attached tty data\n", snp->snp_unit); 448019b4d63SUgen J.S. Antsilevich 4490739a0dcSUgen J.S. Antsilevich snp->snp_tty = NULL; 450f3732fd1SPoul-Henning Kamp snp->snp_target = NULL; 451019b4d63SUgen J.S. Antsilevich 452964587caSUgen J.S. Antsilevich detach_notty: 453512824f8SSeigo Tanimura selwakeuppri(&snp->snp_sel, PZERO + 1); 45476e90dbcSBrian Feldman if ((snp->snp_flags & SNOOP_OPEN) == 0) 45576e90dbcSBrian Feldman free(snp, M_SNP); 456019b4d63SUgen J.S. Antsilevich } 457019b4d63SUgen J.S. Antsilevich 45887f6c662SJulian Elischer static int 4597409f6cdSCraig Rodrigues snpclose(struct cdev *dev, int flags, int fmt, struct thread *td) 460dde8a05bSUgen J.S. Antsilevich { 461f09f49f1SDima Dorfman struct snoop *snp; 462dde8a05bSUgen J.S. Antsilevich 463f09f49f1SDima Dorfman snp = dev->si_drv1; 464dde8a05bSUgen J.S. Antsilevich snp->snp_blen = 0; 465ac60b28dSDima Dorfman LIST_REMOVE(snp, snp_list); 46676e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 467019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OPEN; 46876e90dbcSBrian Feldman dev->si_drv1 = NULL; 469542a8db5SKonstantin Belousov destroy_dev_sched_cb(dev, snp_detach, snp); 470dde8a05bSUgen J.S. Antsilevich 471542a8db5SKonstantin Belousov return (0); 472dde8a05bSUgen J.S. Antsilevich } 473dde8a05bSUgen J.S. Antsilevich 474f09f49f1SDima Dorfman static int 4757409f6cdSCraig Rodrigues snp_down(struct snoop *snp) 476964587caSUgen J.S. Antsilevich { 47776e90dbcSBrian Feldman 47876e90dbcSBrian Feldman if (snp->snp_blen != SNOOP_MINLEN) { 47976e90dbcSBrian Feldman free(snp->snp_buf, M_SNP); 480a163d034SWarner Losh snp->snp_buf = malloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 481964587caSUgen J.S. Antsilevich snp->snp_blen = SNOOP_MINLEN; 48276e90dbcSBrian Feldman } 483964587caSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_DOWN; 484542a8db5SKonstantin Belousov snp_detach(snp); 485dde8a05bSUgen J.S. Antsilevich 486542a8db5SKonstantin Belousov return (0); 487964587caSUgen J.S. Antsilevich } 488dde8a05bSUgen J.S. Antsilevich 48987f6c662SJulian Elischer static int 4907409f6cdSCraig Rodrigues snpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 4917409f6cdSCraig Rodrigues struct thread *td) 492dde8a05bSUgen J.S. Antsilevich { 493f09f49f1SDima Dorfman struct snoop *snp; 494dde8a05bSUgen J.S. Antsilevich struct tty *tp, *tpo; 49589c9c53dSPoul-Henning Kamp struct cdev *tdev; 49630ce1aadSOlivier Houchard struct file *fp; 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: 50230ce1aadSOlivier Houchard s = *(int *)data; 5036ed0b39dSRuslan Ermilov if (s < 0) 5046ed0b39dSRuslan Ermilov return (snp_down(snp)); 5056ed0b39dSRuslan Ermilov if (fget(td, s, &fp) != 0) 50630ce1aadSOlivier Houchard return (EINVAL); 50730ce1aadSOlivier Houchard if (fp->f_type != DTYPE_VNODE || 5086ed0b39dSRuslan Ermilov fp->f_vnode->v_type != VCHR || 5096ed0b39dSRuslan Ermilov fp->f_vnode->v_rdev == NULL) { 51030ce1aadSOlivier Houchard fdrop(fp, td); 51130ce1aadSOlivier Houchard return (EINVAL); 51230ce1aadSOlivier Houchard } 51330ce1aadSOlivier Houchard tdev = fp->f_vnode->v_rdev; 51430ce1aadSOlivier Houchard fdrop(fp, td); 515964587caSUgen J.S. Antsilevich 516444f003cSBruce Evans tp = snpdevtotty(tdev); 51777f77631SPaul Traina if (!tp) 518dde8a05bSUgen J.S. Antsilevich return (EINVAL); 5192c9fb909SDima Dorfman if (tp->t_state & TS_SNOOP) 5202c9fb909SDima Dorfman return (EBUSY); 521dde8a05bSUgen J.S. Antsilevich 522dde8a05bSUgen J.S. Antsilevich s = spltty(); 52377f77631SPaul Traina 524f3732fd1SPoul-Henning Kamp if (snp->snp_target == NULL) { 5250739a0dcSUgen J.S. Antsilevich tpo = snp->snp_tty; 52677f77631SPaul Traina if (tpo) 527dde8a05bSUgen J.S. Antsilevich tpo->t_state &= ~TS_SNOOP; 528dde8a05bSUgen J.S. Antsilevich } 52977f77631SPaul Traina 530dde8a05bSUgen J.S. Antsilevich tp->t_state |= TS_SNOOP; 53147eaa5f5SDima Dorfman snp->snp_olddisc = tp->t_line; 532f09f49f1SDima Dorfman tp->t_line = snooplinedisc; 5330739a0dcSUgen J.S. Antsilevich snp->snp_tty = tp; 53477f77631SPaul Traina snp->snp_target = tdev; 53577f77631SPaul Traina 536964587caSUgen J.S. Antsilevich /* 537964587caSUgen J.S. Antsilevich * Clean overflow and down flags - 538964587caSUgen J.S. Antsilevich * we'll have a chance to get them in the future :))) 539964587caSUgen J.S. Antsilevich */ 540964587caSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_OFLOW; 541019b4d63SUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_DOWN; 542dde8a05bSUgen J.S. Antsilevich splx(s); 543dde8a05bSUgen J.S. Antsilevich break; 54477f77631SPaul Traina 545dde8a05bSUgen J.S. Antsilevich case SNPGTTY: 5460739a0dcSUgen J.S. Antsilevich /* 5470739a0dcSUgen J.S. Antsilevich * We keep snp_target field specially to make 5480739a0dcSUgen J.S. Antsilevich * SNPGTTY happy, else we can't know what is device 5490739a0dcSUgen J.S. Antsilevich * major/minor for tty. 5500739a0dcSUgen J.S. Antsilevich */ 551f3732fd1SPoul-Henning Kamp *((dev_t *)data) = dev2udev(snp->snp_target); 552dde8a05bSUgen J.S. Antsilevich break; 553dde8a05bSUgen J.S. Antsilevich 554dde8a05bSUgen J.S. Antsilevich case FIONBIO: 555dde8a05bSUgen J.S. Antsilevich break; 55677f77631SPaul Traina 557dde8a05bSUgen J.S. Antsilevich case FIOASYNC: 558dde8a05bSUgen J.S. Antsilevich if (*(int *)data) 559dde8a05bSUgen J.S. Antsilevich snp->snp_flags |= SNOOP_ASYNC; 560dde8a05bSUgen J.S. Antsilevich else 561dde8a05bSUgen J.S. Antsilevich snp->snp_flags &= ~SNOOP_ASYNC; 562dde8a05bSUgen J.S. Antsilevich break; 56377f77631SPaul Traina 564dde8a05bSUgen J.S. Antsilevich case FIONREAD: 565dde8a05bSUgen J.S. Antsilevich s = spltty(); 5660739a0dcSUgen J.S. Antsilevich if (snp->snp_tty != NULL) 567dde8a05bSUgen J.S. Antsilevich *(int *)data = snp->snp_len; 568dde8a05bSUgen J.S. Antsilevich else 569964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_DOWN) { 570964587caSUgen J.S. Antsilevich if (snp->snp_flags & SNOOP_OFLOW) 571964587caSUgen J.S. Antsilevich *(int *)data = SNP_OFLOW; 572964587caSUgen J.S. Antsilevich else 573964587caSUgen J.S. Antsilevich *(int *)data = SNP_TTYCLOSE; 574964587caSUgen J.S. Antsilevich } else { 575964587caSUgen J.S. Antsilevich *(int *)data = SNP_DETACH; 576964587caSUgen J.S. Antsilevich } 577dde8a05bSUgen J.S. Antsilevich splx(s); 578dde8a05bSUgen J.S. Antsilevich break; 57977f77631SPaul Traina 580dde8a05bSUgen J.S. Antsilevich default: 581dde8a05bSUgen J.S. Antsilevich return (ENOTTY); 582dde8a05bSUgen J.S. Antsilevich } 583dde8a05bSUgen J.S. Antsilevich return (0); 584dde8a05bSUgen J.S. Antsilevich } 585dde8a05bSUgen J.S. Antsilevich 58687f6c662SJulian Elischer static int 5877409f6cdSCraig Rodrigues snppoll(struct cdev *dev, int events, struct thread *td) 588dde8a05bSUgen J.S. Antsilevich { 589f09f49f1SDima Dorfman struct snoop *snp; 590f09f49f1SDima Dorfman int revents; 591dde8a05bSUgen J.S. Antsilevich 592f09f49f1SDima Dorfman snp = dev->si_drv1; 593f09f49f1SDima Dorfman revents = 0; 594019b4d63SUgen J.S. Antsilevich /* 595659ffb48SPeter Wemm * If snoop is down, we don't want to poll() forever so we return 1. 596019b4d63SUgen J.S. Antsilevich * Caller should see if we down via FIONREAD ioctl(). The last should 597019b4d63SUgen J.S. Antsilevich * return -1 to indicate down state. 598019b4d63SUgen J.S. Antsilevich */ 599dfd5dee1SPeter Wemm if (events & (POLLIN | POLLRDNORM)) { 600659ffb48SPeter Wemm if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 601659ffb48SPeter Wemm revents |= events & (POLLIN | POLLRDNORM); 602659ffb48SPeter Wemm else 603b40ce416SJulian Elischer selrecord(td, &snp->snp_sel); 604dfd5dee1SPeter Wemm } 605659ffb48SPeter Wemm return (revents); 606dde8a05bSUgen J.S. Antsilevich } 607dde8a05bSUgen J.S. Antsilevich 60887f6c662SJulian Elischer static void 6097409f6cdSCraig Rodrigues snp_clone(void *arg, struct ucred *cred, char *name, int namelen, 6107409f6cdSCraig Rodrigues struct cdev **dev) 61125c7870eSDima Dorfman { 612b0b03348SPoul-Henning Kamp int u, i; 61325c7870eSDima Dorfman 614f3732fd1SPoul-Henning Kamp if (*dev != NULL) 61525c7870eSDima Dorfman return; 61625c7870eSDima Dorfman if (dev_stdclone(name, NULL, "snp", &u) != 1) 61725c7870eSDima Dorfman return; 618b0b03348SPoul-Henning Kamp i = clone_create(&snpclones, &snp_cdevsw, &u, dev, 0); 619b0b03348SPoul-Henning Kamp if (i) 6205977b8feSKonstantin Belousov *dev = make_dev_credf(MAKEDEV_REF, &snp_cdevsw, unit2minor(u), 6215977b8feSKonstantin Belousov NULL, UID_ROOT, GID_WHEEL, 0600, "snp%d", u); 622f4f6abcbSPoul-Henning Kamp if (*dev != NULL) { 6232f7a3851SDima Dorfman (*dev)->si_flags |= SI_CHEAPCLONE; 62425c7870eSDima Dorfman } 625f4f6abcbSPoul-Henning Kamp } 62625c7870eSDima Dorfman 62747eaa5f5SDima Dorfman static int 6287409f6cdSCraig Rodrigues snp_modevent(module_t mod, int type, void *data) 62953ac6efbSJulian Elischer { 630f09f49f1SDima Dorfman static eventhandler_tag eh_tag; 63153ac6efbSJulian Elischer 63247eaa5f5SDima Dorfman switch (type) { 63347eaa5f5SDima Dorfman case MOD_LOAD: 634f09f49f1SDima Dorfman /* XXX error checking. */ 6359397290eSPoul-Henning Kamp clone_setup(&snpclones); 63647eaa5f5SDima Dorfman eh_tag = EVENTHANDLER_REGISTER(dev_clone, snp_clone, 0, 1000); 637f09f49f1SDima Dorfman snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 63847eaa5f5SDima Dorfman break; 63947eaa5f5SDima Dorfman case MOD_UNLOAD: 640ac60b28dSDima Dorfman if (!LIST_EMPTY(&snp_sclist)) 641ac60b28dSDima Dorfman return (EBUSY); 64247eaa5f5SDima Dorfman EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 6435977b8feSKonstantin Belousov drain_dev_clone_events(); 644b0b03348SPoul-Henning Kamp clone_cleanup(&snpclones); 6455977b8feSKonstantin Belousov destroy_dev_drain(&snp_cdevsw); 646f09f49f1SDima Dorfman ldisc_deregister(snooplinedisc); 64747eaa5f5SDima Dorfman break; 64847eaa5f5SDima Dorfman default: 6493e019deaSPoul-Henning Kamp return (EOPNOTSUPP); 65047eaa5f5SDima Dorfman break; 65147eaa5f5SDima Dorfman } 652f09f49f1SDima Dorfman return (0); 6537198bf47SJulian Elischer } 65453ac6efbSJulian Elischer 65547eaa5f5SDima Dorfman static moduledata_t snp_mod = { 65647eaa5f5SDima Dorfman "snp", 65747eaa5f5SDima Dorfman snp_modevent, 65847eaa5f5SDima Dorfman NULL 65947eaa5f5SDima Dorfman }; 660b0b03348SPoul-Henning Kamp DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 661