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