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