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