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