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