kernel - Remove D_KQFILTER flag
[dragonfly.git] / sys / dev / misc / nmdm / nmdm.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1982, 1986, 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: src/sys/dev/nmdm/nmdm.c,v 1.5.2.1 2001/08/11 00:54:14 mp Exp $
e7b4468c 34 * $DragonFly: src/sys/dev/misc/nmdm/nmdm.c,v 1.16 2008/01/05 14:02:37 swildner Exp $
984263bc
MD
35 */
36
37/*
38 * Pseudo-nulmodem Driver
39 */
40#include "opt_compat.h"
41#include <sys/param.h>
42#include <sys/systm.h>
43#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
44#include <sys/ioctl_compat.h>
45#endif
46#include <sys/proc.h>
895c1f85 47#include <sys/priv.h>
08cb819d 48#include <sys/thread2.h>
984263bc
MD
49#include <sys/tty.h>
50#include <sys/conf.h>
51#include <sys/fcntl.h>
984263bc
MD
52#include <sys/kernel.h>
53#include <sys/vnode.h>
54#include <sys/signalvar.h>
55#include <sys/malloc.h>
56
57MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
58
38e94a25
RG
59static void nmdmstart (struct tty *tp);
60static void nmdmstop (struct tty *tp, int rw);
61static void wakeup_other (struct tty *tp, int flag);
62static void nmdminit (int n);
984263bc
MD
63
64static d_open_t nmdmopen;
65static d_close_t nmdmclose;
66static d_read_t nmdmread;
67static d_write_t nmdmwrite;
68static d_ioctl_t nmdmioctl;
69
70#define CDEV_MAJOR 18
fef8985e 71static struct dev_ops nmdm_ops = {
d4b8aec4 72 { "pts", CDEV_MAJOR, D_TTY },
fef8985e
MD
73 .d_open = nmdmopen,
74 .d_close = nmdmclose,
75 .d_read = nmdmread,
76 .d_write = nmdmwrite,
77 .d_ioctl = nmdmioctl,
70e491c0 78 .d_kqfilter = ttykqfilter,
a32446b7 79 .d_revoke = ttyrevoke
984263bc
MD
80};
81
82#define BUFSIZ 100 /* Chunk size iomoved to/from user */
83
84struct softpart {
85 struct tty nm_tty;
b13267a5 86 cdev_t dev;
984263bc
MD
87 int modemsignals; /* bits defined in sys/ttycom.h */
88 int gotbreak;
89};
90
91struct nm_softc {
92 int pt_flags;
93 struct softpart part1, part2;
94 struct prison *pt_prison;
95};
96
97#define PF_STOPPED 0x10 /* user told stopped */
98
99static void
100nmdm_crossover(struct nm_softc *pti,
101 struct softpart *ourpart,
102 struct softpart *otherpart);
103
104#define GETPARTS(tp, ourpart, otherpart) \
105do { \
106 struct nm_softc *pti = tp->t_dev->si_drv1; \
107 if (tp == &pti->part1.nm_tty) { \
108 ourpart = &pti->part1; \
109 otherpart = &pti->part2; \
110 } else { \
111 ourpart = &pti->part2; \
112 otherpart = &pti->part1; \
113 } \
114} while (0)
115
116/*
117 * This function creates and initializes a pair of ttys.
118 */
119static void
c436375a 120nmdminit(int n)
984263bc 121{
b13267a5 122 cdev_t dev1, dev2;
984263bc
MD
123 struct nm_softc *pt;
124
e4c9c0c8
MD
125 /*
126 * Simplified unit number, use low 8 bits of minor number
127 * (remember, the minor number mask is 0xffff00ff).
128 */
129 if (n & ~0x7f)
984263bc
MD
130 return;
131
e7b4468c 132 pt = kmalloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO);
fef8985e 133 pt->part1.dev = dev1 = make_dev(&nmdm_ops, n << 1,
3e82b46c 134 0, 0, 0666, "nmdm%dA", n);
fef8985e 135 pt->part2.dev = dev2 = make_dev(&nmdm_ops, (n << 1) + 1,
3e82b46c 136 0, 0, 0666, "nmdm%dB", n);
984263bc
MD
137
138 dev1->si_drv1 = dev2->si_drv1 = pt;
139 dev1->si_tty = &pt->part1.nm_tty;
140 dev2->si_tty = &pt->part2.nm_tty;
141 ttyregister(&pt->part1.nm_tty);
142 ttyregister(&pt->part2.nm_tty);
143 pt->part1.nm_tty.t_oproc = nmdmstart;
144 pt->part2.nm_tty.t_oproc = nmdmstart;
145 pt->part1.nm_tty.t_stop = nmdmstop;
146 pt->part2.nm_tty.t_dev = dev1;
147 pt->part1.nm_tty.t_dev = dev2;
148 pt->part2.nm_tty.t_stop = nmdmstop;
149}
150
151/*ARGSUSED*/
152static int
fef8985e 153nmdmopen(struct dev_open_args *ap)
984263bc 154{
b13267a5 155 cdev_t dev = ap->a_head.a_dev;
41c20dac 156 struct tty *tp, *tp2;
984263bc
MD
157 int error;
158 int minr;
e4c9c0c8 159#if 0
b13267a5 160 cdev_t nextdev;
e4c9c0c8 161#endif
984263bc
MD
162 struct nm_softc *pti;
163 int is_b;
164 int pair;
165 struct softpart *ourpart, *otherpart;
166
e4c9c0c8
MD
167 minr = lminor(dev);
168 pair = minr >> 1;
169 is_b = minr & 1;
170
171#if 0
984263bc
MD
172 /*
173 * XXX: Gross hack for DEVFS:
174 * If we openned this device, ensure we have the
175 * next one too, so people can open it.
176 */
984263bc
MD
177 if (pair < 127) {
178 nextdev = makedev(major(dev), (pair+pair) + 1);
179 if (!nextdev->si_drv1) {
180 nmdminit(pair + 1);
181 }
182 }
e4c9c0c8 183#endif
984263bc
MD
184 if (!dev->si_drv1)
185 nmdminit(pair);
186
187 if (!dev->si_drv1)
188 return(ENXIO);
189
190 pti = dev->si_drv1;
191 if (is_b)
192 tp = &pti->part2.nm_tty;
193 else
194 tp = &pti->part1.nm_tty;
195 GETPARTS(tp, ourpart, otherpart);
196 tp2 = &otherpart->nm_tty;
197 ourpart->modemsignals |= TIOCM_LE;
198
199 if ((tp->t_state & TS_ISOPEN) == 0) {
200 ttychars(tp); /* Set up default chars */
201 tp->t_iflag = TTYDEF_IFLAG;
202 tp->t_oflag = TTYDEF_OFLAG;
203 tp->t_lflag = TTYDEF_LFLAG;
204 tp->t_cflag = TTYDEF_CFLAG;
205 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
895c1f85 206 } else if (tp->t_state & TS_XCLUDE && priv_check_cred(ap->a_cred, PRIV_ROOT, 0)) {
984263bc 207 return (EBUSY);
fef8985e 208 } else if (pti->pt_prison != ap->a_cred->cr_prison) {
984263bc
MD
209 return (EBUSY);
210 }
211
212 /*
213 * If the other side is open we have carrier
214 */
215 if (tp2->t_state & TS_ISOPEN) {
216 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
217 }
218
219 /*
220 * And the other side gets carrier as we are now open.
221 */
222 (void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
223
224 /* External processing makes no sense here */
225 tp->t_lflag &= ~EXTPROC;
226
227 /*
228 * Wait here if we don't have carrier.
229 */
230#if 0
231 while ((tp->t_state & TS_CARR_ON) == 0) {
232 if (flag & FNONBLOCK)
233 break;
377d4740 234 error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "nmdopn", 0);
984263bc
MD
235 if (error)
236 return (error);
237 }
238#endif
239
240 /*
241 * Give the line disciplin a chance to set this end up.
242 */
243 error = (*linesw[tp->t_line].l_open)(dev, tp);
244
245 /*
246 * Wake up the other side.
247 * Theoretically not needed.
248 */
249 ourpart->modemsignals |= TIOCM_DTR;
250 nmdm_crossover(pti, ourpart, otherpart);
251 if (error == 0)
252 wakeup_other(tp, FREAD|FWRITE); /* XXX */
253 return (error);
254}
255
fef8985e
MD
256static int
257nmdmclose(struct dev_close_args *ap)
984263bc 258{
b13267a5 259 cdev_t dev = ap->a_head.a_dev;
f96d6c88 260 struct tty *tp, *tp2;
984263bc
MD
261 int err;
262 struct softpart *ourpart, *otherpart;
263
264 /*
265 * let the other end know that the game is up
266 */
267 tp = dev->si_tty;
268 GETPARTS(tp, ourpart, otherpart);
269 tp2 = &otherpart->nm_tty;
270 (void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
271
272 /*
273 * XXX MDMBUF makes no sense for nmdms but would inhibit the above
274 * l_modem(). CLOCAL makes sense but isn't supported. Special
275 * l_modem()s that ignore carrier drop make no sense for nmdms but
276 * may be in use because other parts of the line discipline make
277 * sense for nmdms. Recover by doing everything that a normal
278 * ttymodem() would have done except for sending a SIGHUP.
279 */
280 if (tp2->t_state & TS_ISOPEN) {
281 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
282 tp2->t_state |= TS_ZOMBIE;
283 ttyflush(tp2, FREAD | FWRITE);
284 }
285
fef8985e 286 err = (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
984263bc
MD
287 ourpart->modemsignals &= ~TIOCM_DTR;
288 nmdm_crossover(dev->si_drv1, ourpart, otherpart);
289 nmdmstop(tp, FREAD|FWRITE);
290 (void) ttyclose(tp);
291 return (err);
292}
293
fef8985e
MD
294static int
295nmdmread(struct dev_read_args *ap)
984263bc 296{
b13267a5 297 cdev_t dev = ap->a_head.a_dev;
984263bc
MD
298 int error = 0;
299 struct tty *tp, *tp2;
300 struct softpart *ourpart, *otherpart;
301
302 tp = dev->si_tty;
303 GETPARTS(tp, ourpart, otherpart);
304 tp2 = &otherpart->nm_tty;
305
306#if 0
307 if (tp2->t_state & TS_ISOPEN) {
fef8985e 308 error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, flag);
984263bc
MD
309 wakeup_other(tp, FWRITE);
310 } else {
311 if (flag & IO_NDELAY) {
312 return (EWOULDBLOCK);
313 }
377d4740 314 error = tsleep(TSA_PTC_READ(tp), PCATCH, "nmdout", 0);
984263bc
MD
315 }
316 }
317#else
fef8985e 318 if ((error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag)) == 0)
984263bc
MD
319 wakeup_other(tp, FWRITE);
320#endif
321 return (error);
322}
323
324/*
325 * Write to pseudo-tty.
326 * Wakeups of controlling tty will happen
327 * indirectly, when tty driver calls nmdmstart.
328 */
329static int
fef8985e 330nmdmwrite(struct dev_write_args *ap)
984263bc 331{
b13267a5 332 cdev_t dev = ap->a_head.a_dev;
fef8985e 333 struct uio *uio = ap->a_uio;
f96d6c88 334 u_char *cp = 0;
e54488bb 335 size_t cc = 0;
984263bc
MD
336 u_char locbuf[BUFSIZ];
337 int cnt = 0;
338 int error = 0;
339 struct tty *tp1, *tp;
340 struct softpart *ourpart, *otherpart;
341
342 tp1 = dev->si_tty;
343 /*
344 * Get the other tty struct.
345 * basically we are writing into the INPUT side of the other device.
346 */
347 GETPARTS(tp1, ourpart, otherpart);
348 tp = &otherpart->nm_tty;
349
350again:
351 if ((tp->t_state & TS_ISOPEN) == 0)
352 return (EIO);
353 while (uio->uio_resid > 0 || cc > 0) {
354 /*
355 * Fill up the buffer if it's empty
356 */
357 if (cc == 0) {
e54488bb 358 cc = szmin(uio->uio_resid, BUFSIZ);
984263bc
MD
359 cp = locbuf;
360 error = uiomove((caddr_t)cp, cc, uio);
361 if (error)
362 return (error);
363 /* check again for safety */
364 if ((tp->t_state & TS_ISOPEN) == 0) {
365 /* adjust for data copied in but not written */
366 uio->uio_resid += cc;
367 return (EIO);
368 }
369 }
370 while (cc > 0) {
371 if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
372 && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
373 /*
374 * Come here to wait for space in outq,
375 * or space in rawq, or an empty canq.
376 */
377 wakeup(TSA_HUP_OR_INPUT(tp));
378 if ((tp->t_state & TS_CONNECTED) == 0) {
379 /*
380 * Data piled up because not connected.
381 * Adjust for data copied in but
382 * not written.
383 */
384 uio->uio_resid += cc;
385 return (EIO);
386 }
fef8985e 387 if (ap->a_ioflag & IO_NDELAY) {
984263bc
MD
388 /*
389 * Don't wait if asked not to.
390 * Adjust for data copied in but
391 * not written.
392 */
393 uio->uio_resid += cc;
394 if (cnt == 0)
395 return (EWOULDBLOCK);
396 return (0);
397 }
398 error = tsleep(TSA_PTC_WRITE(tp),
377d4740 399 PCATCH, "nmdout", 0);
984263bc
MD
400 if (error) {
401 /*
402 * Tsleep returned (signal?).
403 * Go find out what the user wants.
404 * adjust for data copied in but
405 * not written
406 */
407 uio->uio_resid += cc;
408 return (error);
409 }
410 goto again;
411 }
412 (*linesw[tp->t_line].l_rint)(*cp++, tp);
413 cnt++;
414 cc--;
415 }
416 cc = 0;
417 }
418 return (0);
419}
420
421/*
422 * Start output on pseudo-tty.
423 * Wake up process selecting or sleeping for input from controlling tty.
424 */
425static void
c436375a 426nmdmstart(struct tty *tp)
984263bc 427{
f96d6c88 428 struct nm_softc *pti = tp->t_dev->si_drv1;
984263bc
MD
429
430 if (tp->t_state & TS_TTSTOP)
431 return;
432 pti->pt_flags &= ~PF_STOPPED;
433 wakeup_other(tp, FREAD);
434}
435
436/* Wakes up the OTHER tty;*/
437static void
c436375a 438wakeup_other(struct tty *tp, int flag)
984263bc
MD
439{
440 struct softpart *ourpart, *otherpart;
441
442 GETPARTS(tp, ourpart, otherpart);
443 if (flag & FREAD) {
984263bc 444 wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
5b22f1a7 445 KNOTE(&otherpart->nm_tty.t_rkq.ki_note, 0);
984263bc
MD
446 }
447 if (flag & FWRITE) {
984263bc 448 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
5b22f1a7 449 KNOTE(&otherpart->nm_tty.t_wkq.ki_note, 0);
984263bc
MD
450 }
451}
452
453static void
c436375a 454nmdmstop(struct tty *tp, int flush)
984263bc
MD
455{
456 struct nm_softc *pti = tp->t_dev->si_drv1;
457 int flag;
458
459 /* note: FLUSHREAD and FLUSHWRITE already ok */
460 if (flush == 0) {
461 flush = TIOCPKT_STOP;
462 pti->pt_flags |= PF_STOPPED;
463 } else
464 pti->pt_flags &= ~PF_STOPPED;
465 /* change of perspective */
466 flag = 0;
467 if (flush & FREAD)
468 flag |= FWRITE;
469 if (flush & FWRITE)
470 flag |= FREAD;
471 wakeup_other(tp, flag);
472}
473
474/*ARGSUSED*/
475static int
fef8985e 476nmdmioctl(struct dev_ioctl_args *ap)
984263bc 477{
b13267a5 478 cdev_t dev = ap->a_head.a_dev;
f96d6c88 479 struct tty *tp = dev->si_tty;
984263bc 480 struct nm_softc *pti = dev->si_drv1;
08cb819d 481 int error;
f96d6c88 482 struct tty *tp2;
984263bc
MD
483 struct softpart *ourpart, *otherpart;
484
08cb819d 485 crit_enter();
984263bc
MD
486 GETPARTS(tp, ourpart, otherpart);
487 tp2 = &otherpart->nm_tty;
488
fef8985e
MD
489 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
490 ap->a_fflag, ap->a_cred);
984263bc 491 if (error == ENOIOCTL)
fef8985e 492 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
984263bc 493 if (error == ENOIOCTL) {
fef8985e 494 switch (ap->a_cmd) {
984263bc
MD
495 case TIOCSBRK:
496 otherpart->gotbreak = 1;
497 break;
498 case TIOCCBRK:
499 break;
500 case TIOCSDTR:
501 ourpart->modemsignals |= TIOCM_DTR;
502 break;
503 case TIOCCDTR:
504 ourpart->modemsignals &= TIOCM_DTR;
505 break;
506 case TIOCMSET:
fef8985e
MD
507 ourpart->modemsignals = *(int *)ap->a_data;
508 otherpart->modemsignals = *(int *)ap->a_data;
984263bc
MD
509 break;
510 case TIOCMBIS:
fef8985e 511 ourpart->modemsignals |= *(int *)ap->a_data;
984263bc
MD
512 break;
513 case TIOCMBIC:
fef8985e
MD
514 ourpart->modemsignals &= ~(*(int *)ap->a_data);
515 otherpart->modemsignals &= ~(*(int *)ap->a_data);
984263bc
MD
516 break;
517 case TIOCMGET:
fef8985e 518 *(int *)ap->a_data = ourpart->modemsignals;
984263bc
MD
519 break;
520 case TIOCMSDTRWAIT:
521 break;
522 case TIOCMGDTRWAIT:
fef8985e 523 *(int *)ap->a_data = 0;
984263bc
MD
524 break;
525 case TIOCTIMESTAMP:
526 case TIOCDCDTIMESTAMP:
527 default:
08cb819d 528 crit_exit();
984263bc
MD
529 error = ENOTTY;
530 return (error);
531 }
532 error = 0;
533 nmdm_crossover(pti, ourpart, otherpart);
534 }
08cb819d 535 crit_exit();
984263bc
MD
536 return (error);
537}
538
539static void
540nmdm_crossover(struct nm_softc *pti,
541 struct softpart *ourpart,
542 struct softpart *otherpart)
543{
544 otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
545 if (ourpart->modemsignals & TIOCM_RTS)
546 otherpart->modemsignals |= TIOCM_CTS;
547 if (ourpart->modemsignals & TIOCM_DTR)
548 otherpart->modemsignals |= TIOCM_CAR;
549}
550
551
552
38e94a25 553static void nmdm_drvinit (void *unused);
984263bc
MD
554
555static void
c436375a 556nmdm_drvinit(void *unused)
984263bc 557{
984263bc
MD
558 /* XXX: Gross hack for DEVFS */
559 nmdminit(0);
560}
561
562SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)