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