1 /*        $NetBSD: ser.c,v 1.86 2022/10/26 23:38:06 riastradh Exp $ */
2 
3 /*
4  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
5  * 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *        @(#)ser.c 7.12 (Berkeley) 6/27/91
32  */
33 /*
34  * XXX This file needs major cleanup it will never service more than one
35  * XXX unit.
36  */
37 
38 #include "opt_amigacons.h"
39 #include "opt_ddb.h"
40 #include "opt_kgdb.h"
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: ser.c,v 1.86 2022/10/26 23:38:06 riastradh Exp $");
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/ioctl.h>
48 #include <sys/device.h>
49 #include <sys/tty.h>
50 #include <sys/proc.h>
51 #include <sys/file.h>
52 #include <sys/uio.h>
53 #include <sys/kernel.h>
54 #include <sys/syslog.h>
55 #include <sys/queue.h>
56 #include <sys/conf.h>
57 #include <sys/kauth.h>
58 #include <machine/cpu.h>
59 #include <amiga/amiga/device.h>
60 #include <amiga/dev/serreg.h>
61 #include <amiga/amiga/custom.h>
62 #include <amiga/amiga/cia.h>
63 #include <amiga/amiga/cc.h>
64 
65 #include <dev/cons.h>
66 
67 #include <ddb/db_active.h>
68 
69 #include "ser.h"
70 #if NSER > 0
71 
72 void serattach(device_t, device_t, void *);
73 int sermatch(device_t, cfdata_t, void *);
74 
75 struct ser_softc {
76           struct tty *ser_tty;
77 };
78 
79 CFATTACH_DECL_NEW(ser, sizeof(struct ser_softc),
80     sermatch, serattach, NULL, NULL);
81 
82 extern struct cfdriver ser_cd;
83 
84 dev_type_open(seropen);
85 dev_type_close(serclose);
86 dev_type_read(serread);
87 dev_type_write(serwrite);
88 dev_type_ioctl(serioctl);
89 dev_type_stop(serstop);
90 dev_type_tty(sertty);
91 dev_type_poll(serpoll);
92 
93 const struct cdevsw ser_cdevsw = {
94           .d_open = seropen,
95           .d_close = serclose,
96           .d_read = serread,
97           .d_write = serwrite,
98           .d_ioctl = serioctl,
99           .d_stop = serstop,
100           .d_tty = sertty,
101           .d_poll = serpoll,
102           .d_mmap = nommap,
103           .d_kqfilter = ttykqfilter,
104           .d_discard = nodiscard,
105           .d_flag = D_TTY
106 };
107 
108 #ifndef SEROBUF_SIZE
109 #define SEROBUF_SIZE 32
110 #endif
111 #ifndef SERIBUF_SIZE
112 #define SERIBUF_SIZE 512
113 #endif
114 
115 #define splser() spl5()
116 
117 void      serstart(struct tty *);
118 void      ser_shutdown(struct ser_softc *);
119 int       serparam(struct tty *, struct termios *);
120 void      serintr(void);
121 int       serhwiflow(struct tty *, int);
122 int       sermctl(dev_t dev, int, int);
123 void      ser_fastint(void);
124 void      sereint(int);
125 static    void ser_putchar(struct tty *, u_short);
126 void      ser_outintr(void);
127 void      sercnprobe(struct consdev *);
128 void      sercninit(struct consdev *);
129 void      serinit(int);
130 int       sercngetc(dev_t dev);
131 void      sercnputc(dev_t, int);
132 void      sercnpollc(dev_t, int);
133 
134 int       nser = NSER;
135 #ifdef SERCONSOLE
136 int       serconsole = 0;
137 #else
138 int       serconsole = -1;
139 #endif
140 int       serconsinit;
141 int       serdefaultrate = TTYDEF_SPEED;
142 int       serswflags;
143 
144 struct    vbl_node ser_vbl_node;
145 struct    tty ser_cons;
146 struct    tty *ser_tty;
147 
148 static u_short serbuf[SERIBUF_SIZE];
149 static u_short *sbrpt = serbuf;
150 static u_short *sbwpt = serbuf;
151 static u_short sbcnt;
152 static u_short sbovfl;
153 static u_char serdcd;
154 
155 /*
156  * Since this UART is not particularly bright (to put it nicely), we'll
157  * have to do parity stuff on our own.  This table contains the 8th bit
158  * in 7bit character mode, for even parity.  If you want odd parity,
159  * flip the bit.  (for generation of the table, see genpar.c)
160  */
161 
162 u_char    even_parity[] = {
163           0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
164           1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
165           1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
166           0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
167           1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
168           0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
169           0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
170           1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
171 };
172 
173 /*
174  * Since we don't get interrupts for changes on the modem control line,
175  * we'll have to fake them by comparing current settings to the settings
176  * we remembered on last invocation.
177  */
178 
179 u_char    last_ciab_pra;
180 
181 extern int ser_open_speed;    /* current speed of open serial device */
182 
183 #ifdef KGDB
184 #include <machine/remote-sl.h>
185 
186 extern dev_t kgdb_dev;
187 extern int kgdb_rate;
188 extern int kgdb_debug_init;
189 #endif
190 
191 #ifdef DEBUG
192 long      fifoin[17];
193 long      fifoout[17];
194 long      serintrcount[16];
195 long      sermintcount[16];
196 #endif
197 
198 void      sermint(register int unit);
199 
200 int
sermatch(device_t parent,cfdata_t cf,void * aux)201 sermatch(device_t parent, cfdata_t cf, void *aux)
202 {
203           static int ser_matched = 0;
204           static int ser_matched_real = 0;
205 
206           /* Allow only once instance. */
207           if (matchname("ser", (char *)aux) == 0)
208                     return(0);
209 
210           if (amiga_realconfig) {
211                     if (ser_matched_real)
212                               return(0);
213                     ser_matched_real = 1;
214           } else {
215                     if (serconsole != 0)
216                               return(0);
217 
218                     if (ser_matched != 0)
219                               return(0);
220 
221                     ser_matched = 1;
222           }
223           return(1);
224 }
225 
226 
227 void
serattach(device_t parent,device_t self,void * aux)228 serattach(device_t parent, device_t self, void *aux)
229 {
230           struct ser_softc *sc;
231           struct tty *tp;
232           u_short ir;
233 
234           sc = device_private(self);
235 
236           ir = custom.intenar;
237           __USE(ir);
238           if (serconsole == 0)
239                     DELAY(100000);
240 
241           ser_vbl_node.function = (void (*) (void *)) sermint;
242           add_vbl_function(&ser_vbl_node, SER_VBL_PRIORITY, (void *) 0);
243 #ifdef KGDB
244           if (kgdb_dev == makedev(cdevsw_lookup_major(&ser_cdevsw), 0)) {
245                     if (serconsole == 0)
246                               kgdb_dev = NODEV; /* can't debug over console port */
247                     else {
248                               (void) serinit(kgdb_rate);
249                               serconsinit = 1;       /* don't re-init in serputc */
250                               if (kgdb_debug_init == 0)
251                                         printf(" kgdb enabled\n");
252                               else {
253                                         /*
254                                          * Print prefix of device name,
255                                          * let kgdb_connect print the rest.
256                                          */
257                                         printf("ser0: ");
258                                         kgdb_connect(1);
259                               }
260                     }
261           }
262 #endif
263           /*
264            * Need to reset baud rate, etc. of next print so reset serconsinit.
265            */
266           if (0 == serconsole)
267                     serconsinit = 0;
268 
269           tp = tty_alloc();
270           tp->t_oproc = (void (*) (struct tty *)) serstart;
271           tp->t_param = serparam;
272           tp->t_hwiflow = serhwiflow;
273           tty_attach(tp);
274           sc->ser_tty = ser_tty = tp;
275 
276           if (self)
277                     printf(": input fifo %d output fifo %d\n", SERIBUF_SIZE,
278                         SEROBUF_SIZE);
279 }
280 
281 
282 /* ARGSUSED */
283 int
seropen(dev_t dev,int flag,int mode,struct lwp * l)284 seropen(dev_t dev, int flag, int mode, struct lwp *l)
285 {
286           struct ser_softc *sc;
287           struct tty *tp;
288           int unit, error, s, s2;
289 
290           error = 0;
291           unit = SERUNIT(dev);
292 
293           sc = device_lookup_private(&ser_cd, unit);
294           if (sc == NULL)
295                     return (ENXIO);
296 
297           /* XXX com.c: insert KGDB check here */
298 
299           /* XXX ser.c had: s = spltty(); */
300 
301           tp = sc->ser_tty;
302 
303           if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
304                     return (EBUSY);
305 
306           s = spltty();
307 
308           /*
309            * If this is a first open...
310            */
311 
312           if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
313                     struct termios t;
314 
315                     tp->t_dev = dev;
316 
317                     s2 = splser();
318                     /*
319                      * XXX here: hw enable,
320                      */
321                     last_ciab_pra = ciab.pra;
322 
323                     splx(s2);
324                     t.c_ispeed = 0;
325 
326                     /* XXX serconsolerate? */
327                     t.c_ospeed = TTYDEF_SPEED;
328                     t.c_cflag = TTYDEF_CFLAG;
329 
330                     if (serswflags & TIOCFLAG_CLOCAL)
331                               t.c_cflag |= CLOCAL;
332                     if (serswflags & TIOCFLAG_CRTSCTS)
333                               t.c_cflag |= CRTSCTS;
334                     if (serswflags & TIOCFLAG_MDMBUF)
335                               t.c_cflag |= MDMBUF;
336 
337                     /* Make sure serparam() will do something. */
338                     tp->t_ospeed = 0;
339                     serparam(tp, &t);
340                     tp->t_iflag = TTYDEF_IFLAG;
341                     tp->t_oflag = TTYDEF_OFLAG;
342                     tp->t_lflag = TTYDEF_LFLAG;
343                     ttychars(tp);
344                     ttsetwater(tp);
345 
346                     s2 = splser();
347                     (void)sermctl(dev, TIOCM_DTR, DMSET);
348                     /* clear input ring */
349                     sbrpt = sbwpt = serbuf;
350                     sbcnt = 0;
351                     splx(s2);
352           }
353 
354           splx(s);
355 
356           error = ttyopen(tp, DIALOUT(dev), flag & O_NONBLOCK);
357           if (error)
358                     goto bad;
359 
360           error =  tp->t_linesw->l_open(dev, tp);
361           if (error)
362                     goto bad;
363 
364           return (0);
365 
366 bad:
367           if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
368                     ser_shutdown(sc);
369           }
370 
371           return (error);
372 }
373 
374 /*ARGSUSED*/
375 int
serclose(dev_t dev,int flag,int mode,struct lwp * l)376 serclose(dev_t dev, int flag, int mode, struct lwp *l)
377 {
378           struct ser_softc *sc;
379           struct tty *tp;
380 
381           sc = device_lookup_private(&ser_cd, SERUNIT(dev));
382           tp = ser_tty;
383 
384           /* XXX This is for cons.c, according to com.c */
385           if (!(tp->t_state & TS_ISOPEN))
386                     return (0);
387 
388           tp->t_linesw->l_close(tp, flag);
389           ttyclose(tp);
390 
391           if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
392                     ser_shutdown(sc);
393           }
394           return (0);
395 }
396 
397 void
ser_shutdown(struct ser_softc * sc)398 ser_shutdown(struct ser_softc *sc)
399 {
400           struct tty *tp = sc->ser_tty;
401           int s;
402 
403           s = splser();
404 
405           custom.adkcon = ADKCONF_UARTBRK;        /* clear break */
406 #if 0 /* XXX fix: #ifdef KGDB */
407           /*
408            * do not disable interrupts if debugging
409            */
410           if (dev != kgdb_dev)
411 #endif
412                     custom.intena = INTF_RBF | INTF_TBE;    /* disable interrupts */
413           custom.intreq = INTF_RBF | INTF_TBE;              /* clear intr request */
414 
415           /*
416            * If HUPCL is not set, leave DTR unchanged.
417            */
418           if (tp->t_cflag & HUPCL) {
419                     (void)sermctl(tp->t_dev, TIOCM_DTR, DMBIC);
420                     /*
421                      * Idea from dev/ic/com.c:
422                      * sleep a bit so that other side will notice, even if we
423                      * reopen immediately.
424                      */
425                     (void) tsleep(tp, TTIPRI, ttclos, hz);
426           }
427 
428 #if not_yet
429           if (tp != &ser_cons) {
430                     remove_vbl_function(&ser_vbl_node);
431                     tty_free(tp);
432                     ser_tty = (struct tty *) NULL;
433           }
434 #endif
435           ser_open_speed = tp->t_ispeed;
436           splx(s);
437           return;
438 }
439 
440 int
serread(dev_t dev,struct uio * uio,int flag)441 serread(dev_t dev, struct uio *uio, int flag)
442 {
443           /* ARGSUSED */
444 
445           return ser_tty->t_linesw->l_read(ser_tty, uio, flag);
446 }
447 
448 int
serwrite(dev_t dev,struct uio * uio,int flag)449 serwrite(dev_t dev, struct uio *uio, int flag)
450 {
451           /* ARGSUSED */
452 
453           return ser_tty->t_linesw->l_write(ser_tty, uio, flag);
454 }
455 
456 int
serpoll(dev_t dev,int events,struct lwp * l)457 serpoll(dev_t dev, int events, struct lwp *l)
458 {
459           /* ARGSUSED */
460 
461           return ser_tty->t_linesw->l_poll(ser_tty, events, l);
462 }
463 
464 struct tty *
sertty(dev_t dev)465 sertty(dev_t dev)
466 {
467           /* ARGSUSED */
468 
469           return (ser_tty);
470 }
471 
472 /*
473  * We don't do any processing of data here, so we store the raw code
474  * obtained from the uart register.  In theory, 110kBaud gives you
475  * 11kcps, so 16k buffer should be more than enough, interrupt
476  * latency of 1s should never happen, or something is seriously
477  * wrong..
478  * buffers moved to above seropen()     -is
479  */
480 
481 /*
482  * This is a replacement for the lack of a hardware fifo.  32k should be
483  * enough (there's only one unit anyway, so this is not going to
484  * accumulate).
485  */
486 void
ser_fastint(void)487 ser_fastint(void)
488 {
489           /*
490            * We're at RBE-level, which is higher than VBL-level which is used
491            * to periodically transmit contents of this buffer up one layer,
492            * so no spl-raising is necessary.
493            */
494           u_short code;
495 
496           /*
497            * This register contains both data and status bits!
498            */
499           code = custom.serdatr;
500 
501           /*
502            * Use SERDATF_RBF instead of INTF_RBF; they're equivalent, but
503            * we save one (slow) custom chip access.
504            */
505           if ((code & SERDATRF_RBF) == 0)
506                     return;
507 
508           /*
509            * clear interrupt
510            */
511           custom.intreq = INTF_RBF;
512 
513           /*
514            * check for buffer overflow.
515            */
516           if (sbcnt == SERIBUF_SIZE) {
517                     ++sbovfl;
518                     return;
519           }
520           /*
521            * store in buffer
522            */
523           *sbwpt++ = code;
524           if (sbwpt == serbuf + SERIBUF_SIZE)
525                     sbwpt = serbuf;
526           ++sbcnt;
527           if (sbcnt > SERIBUF_SIZE - 20)
528                     CLRRTS(ciab.pra);   /* drop RTS if buffer almost full */
529 }
530 
531 
532 void
serintr(void)533 serintr(void)
534 {
535           int s1, s2, ovfl;
536           struct tty *tp = ser_tty;
537 
538           /*
539            * Make sure we're not interrupted by another
540            * vbl, but allow level5 ints
541            */
542           s1 = spltty();
543 
544           /*
545            * pass along any accumulated information
546            */
547           while (sbcnt > 0 && (tp->t_state & TS_TBLOCK) == 0) {
548                     /*
549                      * no collision with ser_fastint()
550                      */
551                     sereint(*sbrpt++);
552 
553                     ovfl = 0;
554                     /* lock against ser_fastint() */
555                     s2 = splser();
556                     sbcnt--;
557                     if (sbrpt == serbuf + SERIBUF_SIZE)
558                               sbrpt = serbuf;
559                     if (sbovfl != 0) {
560                               ovfl = sbovfl;
561                               sbovfl = 0;
562                     }
563                     splx(s2);
564                     if (ovfl != 0)
565                               log(LOG_WARNING, "ser0: %d ring buffer overflows.\n",
566                                   ovfl);
567           }
568           s2 = splser();
569           if (sbcnt == 0 && (tp->t_state & TS_TBLOCK) == 0)
570                     SETRTS(ciab.pra);   /* start accepting data again */
571           splx(s2);
572           splx(s1);
573 }
574 
575 void
sereint(int stat)576 sereint(int stat)
577 {
578           static int break_in_progress = 0;
579           struct tty *tp;
580           u_char ch;
581           int c;
582 
583           tp = ser_tty;
584           ch = stat & 0xff;
585           c = ch;
586 
587           if ((tp->t_state & TS_ISOPEN) == 0) {
588 #ifdef KGDB
589                     int maj;
590 
591                     /* we don't care about parity errors */
592                     maj = cdevsw_lookup_major(&ser_cdevsw);
593                     if (kgdb_dev == makedev(maj, 0) && c == FRAME_END)
594                               kgdb_connect(0);    /* trap into kgdb */
595 #endif
596                     return;
597           }
598 
599           /*
600            * Check for break and (if enabled) parity error.
601            */
602           if ((stat & 0x1ff) == 0) {
603                     if (break_in_progress)
604                               return;
605 
606                     c = TTY_FE;
607                     break_in_progress = 1;
608 #ifdef DDB
609                     if (serconsole == 0) {
610                               if (!db_active) {
611                                         console_debugger();
612                                         return;
613                               }
614                     }
615 #endif
616           } else {
617                     break_in_progress = 0;
618                     if ((tp->t_cflag & PARENB) &&
619                               (((ch >> 7) + even_parity[ch & 0x7f]
620                                         + !!(tp->t_cflag & PARODD)) & 1))
621                               c |= TTY_PE;
622           }
623 
624           if (stat & SERDATRF_OVRUN)
625                     log(LOG_WARNING, "ser0: silo overflow\n");
626 
627           tp->t_linesw->l_rint(c, tp);
628 }
629 
630 /*
631  * This interrupt is periodically invoked in the vertical blank
632  * interrupt.  It's used to keep track of the modem control lines
633  * and (new with the fast_int code) to move accumulated data
634  * up into the tty layer.
635  */
636 void
sermint(int unit)637 sermint(int unit)
638 {
639           struct tty *tp;
640           u_char stat, last, istat;
641 
642           tp = ser_tty;
643           if (!tp)
644                     return;
645 
646           /*
647           if ((tp->t_state & TS_ISOPEN) == 0 || tp->t_wopen == 0) {
648                     sbrpt = sbwpt = serbuf;
649                     return;
650           }
651           */
652           /*
653            * empty buffer
654            */
655           serintr();
656 
657           stat = ciab.pra;
658           last = last_ciab_pra;
659           last_ciab_pra = stat;
660 
661           /*
662            * check whether any interesting signal changed state
663            */
664           istat = stat ^ last;
665 
666           if (istat & serdcd) {
667                     tp->t_linesw->l_modem(tp, ISDCD(stat));
668           }
669 
670           if ((istat & CIAB_PRA_CTS) && (tp->t_state & TS_ISOPEN) &&
671               (tp->t_cflag & CRTSCTS)) {
672 #if 0
673                     /* the line is up and we want to do rts/cts flow control */
674                     if (ISCTS(stat)) {
675                               tp->t_state &= ~TS_TTSTOP;
676                               ttstart(tp);
677                               /* cause tbe-int if we were stuck there */
678                               custom.intreq = INTF_SETCLR | INTF_TBE;
679                     } else
680                               tp->t_state |= TS_TTSTOP;
681 #else
682                     /* do this on hardware level, not with tty driver */
683                     if (ISCTS(stat)) {
684                               tp->t_state &= ~TS_TTSTOP;
685                               /* cause TBE interrupt */
686                               custom.intreq = INTF_SETCLR | INTF_TBE;
687                     }
688 #endif
689           }
690 }
691 
692 int
serioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)693 serioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
694 {
695           register struct tty *tp;
696           register int error;
697 
698           tp = ser_tty;
699           if (!tp)
700                     return ENXIO;
701 
702           error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
703           if (error != EPASSTHROUGH)
704                     return(error);
705 
706           error = ttioctl(tp, cmd, data, flag, l);
707           if (error != EPASSTHROUGH)
708                     return(error);
709 
710           switch (cmd) {
711           case TIOCSBRK:
712                     custom.adkcon = ADKCONF_SETCLR | ADKCONF_UARTBRK;
713                     break;
714 
715           case TIOCCBRK:
716                     custom.adkcon = ADKCONF_UARTBRK;
717                     break;
718 
719           case TIOCSDTR:
720                     (void) sermctl(dev, TIOCM_DTR, DMBIS);
721                     break;
722 
723           case TIOCCDTR:
724                     (void) sermctl(dev, TIOCM_DTR, DMBIC);
725                     break;
726 
727           case TIOCMSET:
728                     (void) sermctl(dev, *(int *) data, DMSET);
729                     break;
730 
731           case TIOCMBIS:
732                     (void) sermctl(dev, *(int *) data, DMBIS);
733                     break;
734 
735           case TIOCMBIC:
736                     (void) sermctl(dev, *(int *) data, DMBIC);
737                     break;
738 
739           case TIOCMGET:
740                     *(int *)data = sermctl(dev, 0, DMGET);
741                     break;
742           case TIOCGFLAGS:
743                     *(int *)data = serswflags;
744                     break;
745           case TIOCSFLAGS:
746                     error = kauth_authorize_device_tty(l->l_cred,
747                         KAUTH_DEVICE_TTY_PRIVSET, tp);
748                     if (error != 0)
749                               return(EPERM);
750 
751                     serswflags = *(int *)data;
752                 serswflags &= /* only allow valid flags */
753                   (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
754                     break;
755           default:
756                     return(EPASSTHROUGH);
757           }
758 
759           return(0);
760 }
761 
762 int
serparam(struct tty * tp,struct termios * t)763 serparam(struct tty *tp, struct termios *t)
764 {
765           int cflag, ospeed = 0;
766 
767           if (t->c_ospeed > 0) {
768                     if (t->c_ospeed < 110)
769                               return(EINVAL);
770                     ospeed = SERBRD(t->c_ospeed);
771           }
772 
773           if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
774                     return(EINVAL);
775 
776           if (serswflags & TIOCFLAG_SOFTCAR || serconsole == 0) {
777                     t->c_cflag = (t->c_cflag & ~HUPCL) | CLOCAL;
778           }
779 
780           /* if no changes, dont do anything. com.c explains why. */
781           if (tp->t_ospeed == t->c_ospeed &&
782               tp->t_cflag == t->c_cflag)
783                     return (0);
784 
785           cflag = t->c_cflag;
786 
787           if (cflag & (CLOCAL | MDMBUF))
788                     serdcd = 0;
789           else
790                     serdcd = CIAB_PRA_CD;
791 
792           /* TODO: support multiple flow control protocols like com.c */
793 
794           /*
795            * copy to tty
796            */
797           tp->t_ispeed = t->c_ispeed;
798           tp->t_ospeed = t->c_ospeed;
799           tp->t_cflag = cflag;
800           ser_open_speed = tp->t_ispeed;
801 
802           /*
803            * enable interrupts
804            */
805           custom.intena = INTF_SETCLR | INTF_RBF | INTF_TBE;
806           last_ciab_pra = ciab.pra;
807 
808           if (t->c_ospeed == 0)
809                     (void)sermctl(tp->t_dev, 0, DMSET);     /* hang up line */
810           else {
811                     /*
812                      * (re)enable DTR
813                      * and set baud rate. (8 bit mode)
814                      */
815                     (void)sermctl(tp->t_dev, TIOCM_DTR, DMSET);
816                     custom.serper = (0 << 15) | ospeed;
817           }
818           (void)tp->t_linesw->l_modem(tp, ISDCD(last_ciab_pra));
819 
820           return(0);
821 }
822 
serhwiflow(struct tty * tp,int flag)823 int serhwiflow(struct tty *tp, int flag)
824 {
825 #if 0
826           printf ("serhwiflow %d\n", flag);
827 #endif
828         if (flag)
829                     CLRRTS(ciab.pra);
830           else
831                   SETRTS(ciab.pra);
832         return 1;
833 }
834 
835 static void
ser_putchar(struct tty * tp,u_short c)836 ser_putchar(struct tty *tp, u_short c)
837 {
838           if ((tp->t_cflag & CSIZE) == CS7 || (tp->t_cflag & PARENB))
839                     c &= 0x7f;
840 
841           /*
842            * handle parity if necessary
843            */
844           if (tp->t_cflag & PARENB) {
845                     if (even_parity[c])
846                               c |= 0x80;
847                     if (tp->t_cflag & PARODD)
848                               c ^= 0x80;
849           }
850           /*
851            * add stop bit(s)
852            */
853           if (tp->t_cflag & CSTOPB)
854                     c |= 0x300;
855           else
856                     c |= 0x100;
857 
858           custom.serdat = c;
859 }
860 
861 
862 static u_char ser_outbuf[SEROBUF_SIZE];
863 static u_char *sob_ptr = ser_outbuf, *sob_end = ser_outbuf;
864 
865 void
ser_outintr(void)866 ser_outintr(void)
867 {
868           struct tty *tp;
869           int s;
870 
871           tp = ser_tty;
872           s = spltty();
873 
874           if (tp == 0)
875                     goto out;
876 
877           if ((custom.intreqr & INTF_TBE) == 0)
878                     goto out;
879 
880           /*
881            * clear interrupt
882            */
883           custom.intreq = INTF_TBE;
884 
885           if (sob_ptr == sob_end) {
886                     tp->t_state &= ~(TS_BUSY | TS_FLUSH);
887                     if (tp->t_linesw)
888                               tp->t_linesw->l_start(tp);
889                     else
890                               serstart(tp);
891                     goto out;
892           }
893 
894           /*
895            * Do hardware flow control here.  if the CTS line goes down, don't
896            * transmit anything.  That way, we'll be restarted by the periodic
897            * interrupt when CTS comes back up.
898            */
899           if (ISCTS(ciab.pra))
900                     ser_putchar(tp, *sob_ptr++);
901           else
902                     CLRCTS(last_ciab_pra);        /* Remember that CTS is off */
903 out:
904           splx(s);
905 }
906 
907 void
serstart(struct tty * tp)908 serstart(struct tty *tp)
909 {
910           int cc, s, hiwat;
911 #ifdef DIAGNOSTIC
912           int unit;
913 #endif
914 
915           hiwat = 0;
916 
917           if ((tp->t_state & TS_ISOPEN) == 0)
918                     return;
919 
920 #ifdef DIAGNOSTIC
921           unit = SERUNIT(tp->t_dev);
922           if (unit)
923                     panic("serstart: unit is %d", unit);
924 #endif
925 
926           s = spltty();
927           if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
928                     goto out;
929 
930           cc = tp->t_outq.c_cc;
931           if (!ttypull(tp) || (tp->t_state & TS_BUSY))
932                     goto out;
933 
934           /*
935            * We only do bulk transfers if using CTSRTS flow control, not for
936            * (probably sloooow) ixon/ixoff devices.
937            */
938           if ((tp->t_cflag & CRTSCTS) == 0)
939                     cc = 1;
940 
941           /*
942            * Limit the amount of output we do in one burst
943            * to prevent hogging the CPU.
944            */
945           if (cc > SEROBUF_SIZE) {
946                     hiwat++;
947                     cc = SEROBUF_SIZE;
948           }
949           cc = q_to_b(&tp->t_outq, ser_outbuf, cc);
950           if (cc > 0) {
951                     tp->t_state |= TS_BUSY;
952 
953                     sob_ptr = ser_outbuf;
954                     sob_end = ser_outbuf + cc;
955 
956                     /*
957                      * Get first character out, then have TBE-interrupts blow out
958                      * further characters, until buffer is empty, and TS_BUSY gets
959                      * cleared.
960                      */
961                     ser_putchar(tp, *sob_ptr++);
962           }
963 out:
964           splx(s);
965 }
966 
967 /*
968  * Stop output on a line.
969  */
970 /*ARGSUSED*/
971 void
serstop(struct tty * tp,int flag)972 serstop(struct tty *tp, int flag)
973 {
974           int s;
975 
976           s = spltty();
977           if (tp->t_state & TS_BUSY) {
978                     if ((tp->t_state & TS_TTSTOP) == 0)
979                               tp->t_state |= TS_FLUSH;
980           }
981           splx(s);
982 }
983 
984 int
sermctl(dev_t dev,int bits,int how)985 sermctl(dev_t dev, int bits, int how)
986 {
987           int s;
988           u_char ub = 0;
989 
990           /*
991            * convert TIOCM* mask into CIA mask
992            * which is active low
993            */
994           if (how != DMGET) {
995                     ub = 0;
996                     if (bits & TIOCM_DTR)
997                               ub |= CIAB_PRA_DTR;
998                     if (bits & TIOCM_RTS)
999                               ub |= CIAB_PRA_RTS;
1000                     if (bits & TIOCM_CTS)
1001                               ub |= CIAB_PRA_CTS;
1002                     if (bits & TIOCM_CD)
1003                               ub |= CIAB_PRA_CD;
1004                     if (bits & TIOCM_RI)
1005                               ub |= CIAB_PRA_SEL; /* collision with /dev/par ! */
1006                     if (bits & TIOCM_DSR)
1007                               ub |= CIAB_PRA_DSR;
1008           }
1009           s = spltty();
1010           switch (how) {
1011           case DMSET:
1012                     /* invert and set */
1013                     ciab.pra = ~ub;
1014                     break;
1015 
1016           case DMBIC:
1017                     ciab.pra |= ub;
1018                     ub = ~ciab.pra;
1019                     break;
1020 
1021           case DMBIS:
1022                     ciab.pra &= ~ub;
1023                     ub = ~ciab.pra;
1024                     break;
1025 
1026           case DMGET:
1027                     ub = ~ciab.pra;
1028                     break;
1029           }
1030           (void)splx(s);
1031 
1032           bits = 0;
1033           if (ub & CIAB_PRA_DTR)
1034                     bits |= TIOCM_DTR;
1035           if (ub & CIAB_PRA_RTS)
1036                     bits |= TIOCM_RTS;
1037           if (ub & CIAB_PRA_CTS)
1038                     bits |= TIOCM_CTS;
1039           if (ub & CIAB_PRA_CD)
1040                     bits |= TIOCM_CD;
1041           if (ub & CIAB_PRA_SEL)
1042                     bits |= TIOCM_RI;
1043           if (ub & CIAB_PRA_DSR)
1044                     bits |= TIOCM_DSR;
1045 
1046           return(bits);
1047 }
1048 
1049 /*
1050  * Following are all routines needed for SER to act as console
1051  */
1052 void
sercnprobe(struct consdev * cp)1053 sercnprobe(struct consdev *cp)
1054 {
1055           int maj, unit;
1056 #ifdef KGDB
1057           extern const struct cdevsw ctty_cdevsw;
1058 #endif
1059 
1060           /* locate the major number */
1061           maj = cdevsw_lookup_major(&ser_cdevsw);
1062 
1063 
1064           unit = CONUNIT;                         /* XXX: ick */
1065 
1066           /*
1067            * initialize required fields
1068            */
1069           cp->cn_dev = makedev(maj, unit);
1070           if (serconsole == unit)
1071                     cp->cn_pri = CN_REMOTE;
1072           else
1073                     cp->cn_pri = CN_NORMAL;
1074 #ifdef KGDB
1075           /* XXX */
1076           if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw)
1077                     kgdb_dev = makedev(maj, minor(kgdb_dev));
1078 #endif
1079 }
1080 
1081 void
sercninit(struct consdev * cp)1082 sercninit(struct consdev *cp)
1083 {
1084           int unit;
1085 
1086           unit = SERUNIT(cp->cn_dev);
1087 
1088           serinit(serdefaultrate);
1089           serconsole = unit;
1090           serconsinit = 1;
1091 }
1092 
1093 void
serinit(int rate)1094 serinit(int rate)
1095 {
1096           int s;
1097 
1098           s = splser();
1099           /*
1100            * might want to fiddle with the CIA later ???
1101            */
1102           custom.serper = (rate>=110 ? SERBRD(rate) : 0);
1103           splx(s);
1104 }
1105 
1106 int
sercngetc(dev_t dev)1107 sercngetc(dev_t dev)
1108 {
1109           u_short stat;
1110           int c, s;
1111 
1112           s = splser();
1113           /*
1114            * poll
1115            */
1116           while (((stat = custom.serdatr & 0xffff) & SERDATRF_RBF) == 0)
1117                     ;
1118 
1119           c = stat & 0xff;
1120           /*
1121            * clear interrupt
1122            */
1123           custom.intreq = INTF_RBF;
1124           splx(s);
1125 
1126           return(c);
1127 }
1128 
1129 /*
1130  * Console kernel output character routine.
1131  */
1132 void
sercnputc(dev_t dev,int c)1133 sercnputc(dev_t dev, int c)
1134 {
1135           register int timo;
1136           int s;
1137 
1138           s = splhigh();
1139 
1140           if (serconsinit == 0) {
1141                     (void)serinit(serdefaultrate);
1142                     serconsinit = 1;
1143           }
1144 
1145           /*
1146            * wait for any pending transmission to finish
1147            */
1148           timo = 50000;
1149           while (!(custom.serdatr & SERDATRF_TBE) && --timo);
1150 
1151           /*
1152            * transmit char.
1153            */
1154           custom.serdat = (c & 0xff) | 0x100;
1155 
1156           /*
1157            * wait for this transmission to complete
1158            */
1159           timo = 1500000;
1160           while (!(custom.serdatr & SERDATRF_TBE) && --timo)
1161                     ;
1162 
1163           /*
1164            * Wait for the device (my vt100..) to process the data, since we
1165            * don't do flow-control with cnputc
1166            */
1167           for (timo = 0; timo < 30000; timo++)
1168                     ;
1169 
1170           /*
1171            * We set TBE so that ser_outintr() is called right after to check
1172            * whether there still are chars to process.
1173            * We used to clear this, but it hung the tty output if the kernel
1174            * output a char while userland did on the same serial port.
1175            */
1176           custom.intreq = INTF_SETCLR | INTF_TBE;
1177           splx(s);
1178 }
1179 
1180 void
sercnpollc(dev_t dev,int on)1181 sercnpollc(dev_t dev, int on)
1182 {
1183 }
1184 #endif
1185