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