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