1 /*        $NetBSD: ixp12x0_com.c,v 1.50 2022/07/21 10:09:20 andvar Exp $ */
2 /*
3  * Copyright (c) 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Ichiro FUKUHARA and Naoto Shimazaki.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by IWAMOTO Toshihiro.
11  *
12  * This code is derived from software contributed to The NetBSD Foundation
13  * by Charles M. Hannum.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1991 The Regents of the University of California.
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  *
65  *      @(#)com.c       7.5 (Berkeley) 5/16/91
66  */
67 
68 #include <sys/cdefs.h>
69 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_com.c,v 1.50 2022/07/21 10:09:20 andvar Exp $");
70 
71 #include "opt_ddb.h"
72 #include "opt_kgdb.h"
73 
74 #ifdef RND_COM
75 #include <sys/rndsource.h>
76 #endif
77 
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/types.h>
81 #include <sys/conf.h>
82 #include <sys/file.h>
83 #include <sys/device.h>
84 #include <sys/kernel.h>
85 #include <sys/kmem.h>
86 #include <sys/tty.h>
87 #include <sys/uio.h>
88 #include <sys/vnode.h>
89 #include <sys/kauth.h>
90 #include <sys/lwp.h>
91 
92 #include <machine/intr.h>
93 #include <sys/bus.h>
94 
95 #include <arm/ixp12x0/ixp12x0_comreg.h>
96 #include <arm/ixp12x0/ixp12x0_comvar.h>
97 #include <arm/ixp12x0/ixp12x0reg.h>
98 #include <arm/ixp12x0/ixp12x0var.h>
99 
100 #include <arm/ixp12x0/ixpsipvar.h>
101 
102 #include <dev/cons.h>
103 #include "ixpcom.h"
104 
105 static int          ixpcomparam(struct tty *, struct termios *);
106 static void         ixpcomstart(struct tty *);
107 static int          ixpcomhwiflow(struct tty *, int);
108 
109 static u_int        cflag2cr(tcflag_t);
110 static void         ixpcom_iflush(struct ixpcom_softc *);
111 static void         ixpcom_set_cr(struct ixpcom_softc *);
112 
113 int             ixpcomcngetc(dev_t);
114 void            ixpcomcnputc(dev_t, int);
115 void            ixpcomcnpollc(dev_t, int);
116 
117 static void         ixpcomsoft(void* arg);
118 inline static void  ixpcom_txsoft(struct ixpcom_softc *, struct tty *);
119 inline static void  ixpcom_rxsoft(struct ixpcom_softc *, struct tty *);
120 
121 void            ixpcomcnprobe(struct consdev *);
122 void            ixpcomcninit(struct consdev *);
123 
124 uint32_t  ixpcom_cr = 0;                /* tell cr to *_intr.c */
125 uint32_t  ixpcom_imask = 0;   /* interrupt mask from *_intr.c */
126 
127 
128 static struct ixpcom_cons_softc {
129           bus_space_tag_t               sc_iot;
130           bus_space_handle_t  sc_ioh;
131           bus_addr_t                    sc_baseaddr;
132           int                           sc_ospeed;
133           tcflag_t            sc_cflag;
134           int                           sc_attached;
135 } ixpcomcn_sc;
136 
137 static struct cnm_state ixpcom_cnm_state;
138 
139 struct ixpcom_softc* ixpcom_sc = NULL;
140 
141 extern struct cfdriver ixpcom_cd;
142 
143 dev_type_open(ixpcomopen);
144 dev_type_close(ixpcomclose);
145 dev_type_read(ixpcomread);
146 dev_type_write(ixpcomwrite);
147 dev_type_ioctl(ixpcomioctl);
148 dev_type_stop(ixpcomstop);
149 dev_type_tty(ixpcomtty);
150 dev_type_poll(ixpcompoll);
151 
152 const struct cdevsw ixpcom_cdevsw = {
153           .d_open = ixpcomopen,
154           .d_close = ixpcomclose,
155           .d_read = ixpcomread,
156           .d_write = ixpcomwrite,
157           .d_ioctl = ixpcomioctl,
158           .d_stop = ixpcomstop,
159           .d_tty = ixpcomtty,
160           .d_poll = ixpcompoll,
161           .d_mmap = nommap,
162           .d_kqfilter = ttykqfilter,
163           .d_discard = nodiscard,
164           .d_flag = D_TTY
165 };
166 
167 struct consdev ixpcomcons = {
168           NULL, NULL, ixpcomcngetc, ixpcomcnputc, ixpcomcnpollc, NULL,
169           NULL, NULL, NODEV, CN_NORMAL
170 };
171 
172 #ifndef DEFAULT_COMSPEED
173 #define DEFAULT_COMSPEED 38400
174 #endif
175 
176 #define COMUNIT(x)  TTUNIT(x)
177 #define COMDIALOUT(x)         TTDIALOUT(x)
178 
179 #define COM_ISALIVE(sc)       ((sc)->enabled != 0 && \
180                                device_is_active((sc)->sc_dev))
181 
182 #define COM_BARRIER(t, h, f) bus_space_barrier((t), (h), 0, COM_NPORTS, (f))
183 
184 #define   COM_LOCK(sc);
185 #define   COM_UNLOCK(sc);
186 
187 #define CFLAGS2CR_MASK        (CR_PE | CR_OES | CR_SBS | CR_DSS | CR_BRD)
188 
189 void
ixpcom_attach_subr(struct ixpcom_softc * sc)190 ixpcom_attach_subr(struct ixpcom_softc *sc)
191 {
192           struct tty *tp;
193 
194           ixpcom_sc = sc;
195 
196           /* force to use ixpcom0 for console */
197           if (sc->sc_iot == ixpcomcn_sc.sc_iot
198               && sc->sc_baseaddr == ixpcomcn_sc.sc_baseaddr) {
199                     ixpcomcn_sc.sc_attached = 1;
200                     sc->sc_speed = IXPCOMSPEED2BRD(ixpcomcn_sc.sc_ospeed);
201 
202                     /* Make sure the console is always "hardwired". */
203                     /* XXX IXPCOM_SR should be checked  */
204                     delay(10000);       /* wait for output to finish */
205                     SET(sc->sc_hwflags, COM_HW_CONSOLE);
206                     SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
207           }
208 
209           tp = tty_alloc();
210           tp->t_oproc = ixpcomstart;
211           tp->t_param = ixpcomparam;
212           tp->t_hwiflow = ixpcomhwiflow;
213 
214           sc->sc_tty = tp;
215           sc->sc_rbuf = kmem_alloc(IXPCOM_RING_SIZE << 1, KM_SLEEP);
216           sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
217           sc->sc_rbavail = IXPCOM_RING_SIZE;
218           sc->sc_ebuf = sc->sc_rbuf + (IXPCOM_RING_SIZE << 1);
219           sc->sc_tbc = 0;
220 
221           sc->sc_rie = sc->sc_xie = 0;
222           ixpcom_cr = IXPCOMSPEED2BRD(DEFAULT_COMSPEED)
223                     | CR_UE | sc->sc_rie | sc->sc_xie | DSS_8BIT;
224 
225           tty_attach(tp);
226 
227           if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
228                     int maj;
229 
230                     /* locate the major number */
231                     maj = cdevsw_lookup_major(&ixpcom_cdevsw);
232 
233                     cn_tab->cn_dev = makedev(maj, device_unit(sc->sc_dev));
234 
235                     aprint_normal("%s: console\n", device_xname(sc->sc_dev));
236           }
237 
238           sc->sc_si = softint_establish(SOFTINT_SERIAL, ixpcomsoft, sc);
239 
240 #ifdef RND_COM
241           rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
242                                 RND_TYPE_TTY, RND_FLAG_COLLECT_TIME|
243                                                   RND_FLAG_ESTIMATE_TIME);
244 #endif
245 
246           /* if there are no enable/disable functions, assume the device
247              is always enabled */
248           if (!sc->enable)
249                     sc->enabled = 1;
250 
251           /* XXX configure register */
252           /* xxx_config(sc) */
253 
254           SET(sc->sc_hwflags, COM_HW_DEV_OK);
255 }
256 
257 static int
ixpcomparam(struct tty * tp,struct termios * t)258 ixpcomparam(struct tty *tp, struct termios *t)
259 {
260           struct ixpcom_softc *sc
261                     = device_lookup_private(&ixpcom_cd, COMUNIT(tp->t_dev));
262           uint32_t cr;
263           int s;
264 
265           if (COM_ISALIVE(sc) == 0)
266                     return (EIO);
267 
268           cr = IXPCOMSPEED2BRD(t->c_ospeed);
269 
270           if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
271                     return (EINVAL);
272 
273           /*
274            * For the console, always force CLOCAL and !HUPCL, so that the port
275            * is always active.
276            */
277           if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
278               ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
279                     SET(t->c_cflag, CLOCAL);
280                     CLR(t->c_cflag, HUPCL);
281           }
282 
283           /*
284            * If there were no changes, don't do anything.  This avoids dropping
285            * input and improves performance when all we did was frob things like
286            * VMIN and VTIME.
287            */
288           if (tp->t_ospeed == t->c_ospeed &&
289               tp->t_cflag == t->c_cflag)
290                     return (0);
291 
292           cr |= cflag2cr(t->c_cflag);
293 
294           s = splserial();
295           COM_LOCK(sc);
296 
297           ixpcom_cr = (ixpcom_cr & ~CFLAGS2CR_MASK) | cr;
298 
299           /*
300            * ixpcom don't have any hardware flow control.
301            * we skip it.
302            */
303 
304           /* And copy to tty. */
305           tp->t_ispeed = 0;
306           tp->t_ospeed = t->c_ospeed;
307           tp->t_cflag = t->c_cflag;
308 
309           if (!sc->sc_heldchange) {
310                     if (sc->sc_tx_busy) {
311                               sc->sc_heldtbc = sc->sc_tbc;
312                               sc->sc_tbc = 0;
313                               sc->sc_heldchange = 1;
314                     } else
315                               ixpcom_set_cr(sc);
316           }
317 
318           COM_UNLOCK(sc);
319           splx(s);
320 
321           /*
322            * Update the tty layer's idea of the carrier bit.
323            * We tell tty the carrier is always on.
324            */
325           (void) (*tp->t_linesw->l_modem)(tp, 1);
326 
327 #ifdef COM_DEBUG
328           if (com_debug)
329                     comstatus(sc, "comparam ");
330 #endif
331 
332           if (!ISSET(t->c_cflag, CHWFLOW)) {
333                     if (sc->sc_tx_stopped) {
334                               sc->sc_tx_stopped = 0;
335                               ixpcomstart(tp);
336                     }
337           }
338 
339           return (0);
340 }
341 
342 static int
ixpcomhwiflow(struct tty * tp,int block)343 ixpcomhwiflow(struct tty *tp, int block)
344 {
345           return (0);
346 }
347 
348 static void
ixpcom_filltx(struct ixpcom_softc * sc)349 ixpcom_filltx(struct ixpcom_softc *sc)
350 {
351           bus_space_tag_t iot = sc->sc_iot;
352           bus_space_handle_t ioh = sc->sc_ioh;
353           int n;
354 
355           n = 0;
356         while (bus_space_read_4(iot, ioh, IXPCOM_SR) & SR_TXR) {
357                     if (n >= sc->sc_tbc)
358                               break;
359                     bus_space_write_4(iot, ioh, IXPCOM_DR,
360                                           0xff & *(sc->sc_tba + n));
361                     n++;
362         }
363         sc->sc_tbc -= n;
364         sc->sc_tba += n;
365 }
366 
367 static void
ixpcomstart(struct tty * tp)368 ixpcomstart(struct tty *tp)
369 {
370           struct ixpcom_softc *sc
371                     = device_lookup_private(&ixpcom_cd, COMUNIT(tp->t_dev));
372           int s;
373 
374           if (COM_ISALIVE(sc) == 0)
375                     return;
376 
377           s = spltty();
378           if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
379                     goto out;
380           if (sc->sc_tx_stopped)
381                     goto out;
382 
383           if (!ttypull(tp))
384                     goto out;
385 
386           /* Grab the first contiguous region of buffer space. */
387           {
388                     u_char *tba;
389                     int tbc;
390 
391                     tba = tp->t_outq.c_cf;
392                     tbc = ndqb(&tp->t_outq, 0);
393 
394                     (void)splserial();
395                     COM_LOCK(sc);
396 
397                     sc->sc_tba = tba;
398                     sc->sc_tbc = tbc;
399           }
400 
401           SET(tp->t_state, TS_BUSY);
402           sc->sc_tx_busy = 1;
403 
404           /* Enable transmit completion interrupts if necessary. */
405           if (!ISSET(sc->sc_xie, CR_XIE)) {
406                     SET(sc->sc_xie, CR_XIE);
407                     ixpcom_set_cr(sc);
408           }
409 
410           /* Output the first chunk of the contiguous buffer. */
411           ixpcom_filltx(sc);
412 
413           COM_UNLOCK(sc);
414 out:
415           splx(s);
416           return;
417 }
418 
419 static void
ixpcom_break(struct ixpcom_softc * sc,int onoff)420 ixpcom_break(struct ixpcom_softc *sc, int onoff)
421 {
422           if (onoff)
423                     SET(ixpcom_cr, CR_BRK);
424           else
425                     CLR(ixpcom_cr, CR_BRK);
426           if (!sc->sc_heldchange) {
427                     if (sc->sc_tx_busy) {
428                               sc->sc_heldtbc = sc->sc_tbc;
429                               sc->sc_tbc = 0;
430                               sc->sc_heldchange = 1;
431                     } else
432                               ixpcom_set_cr(sc);
433           }
434 }
435 
436 static void
ixpcom_shutdown(struct ixpcom_softc * sc)437 ixpcom_shutdown(struct ixpcom_softc *sc)
438 {
439           int s;
440 
441           s = splserial();
442           COM_LOCK(sc);
443 
444           /* Clear any break condition set with TIOCSBRK. */
445           ixpcom_break(sc, 0);
446 
447           /* Turn off interrupts. */
448           sc->sc_rie = sc->sc_xie = 0;
449           ixpcom_set_cr(sc);
450 
451           if (sc->disable) {
452 #ifdef DIAGNOSTIC
453                     if (!sc->enabled)
454                               panic("ixpcom_shutdown: not enabled?");
455 #endif
456                     (*sc->disable)(sc);
457                     sc->enabled = 0;
458           }
459           COM_UNLOCK(sc);
460           splx(s);
461 }
462 
463 int
ixpcomopen(dev_t dev,int flag,int mode,struct lwp * l)464 ixpcomopen(dev_t dev, int flag, int mode, struct lwp *l)
465 {
466           struct ixpcom_softc *sc;
467           struct tty *tp;
468           int s, s2;
469           int error;
470 
471           sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
472           if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) ||
473                     sc->sc_rbuf == NULL)
474                     return (ENXIO);
475 
476           if (!device_is_active(sc->sc_dev))
477                     return (ENXIO);
478 
479 #ifdef KGDB
480           /*
481            * If this is the kgdb port, no other use is permitted.
482            */
483           if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
484                     return (EBUSY);
485 #endif
486 
487           tp = sc->sc_tty;
488 
489           if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
490                     return (EBUSY);
491 
492           s = spltty();
493 
494           /*
495            * Do the following iff this is a first open.
496            */
497           if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
498                     struct termios t;
499 
500                     tp->t_dev = dev;
501 
502                     s2 = splserial();
503                     COM_LOCK(sc);
504 
505                     if (sc->enable) {
506                               if ((*sc->enable)(sc)) {
507                                         COM_UNLOCK(sc);
508                                         splx(s2);
509                                         splx(s);
510                                         printf("%s: device enable failed\n",
511                                                device_xname(sc->sc_dev));
512                                         return (EIO);
513                               }
514                               sc->enabled = 1;
515 #if 0
516 /* XXXXXXXXXXXXXXX */
517                               com_config(sc);
518 #endif
519                     }
520 
521                     /* Turn on interrupts. */
522                     SET(sc->sc_rie, CR_RIE);
523                     ixpcom_set_cr(sc);
524 
525 #if 0
526                     /* Fetch the current modem control status, needed later. */
527                     sc->sc_msr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, com_msr);
528 
529                     /* Clear PPS capture state on first open. */
530                     sc->sc_ppsmask = 0;
531                     sc->ppsparam.mode = 0;
532 #endif
533 
534                     COM_UNLOCK(sc);
535                     splx(s2);
536 
537                     /*
538                      * Initialize the termios status to the defaults.  Add in the
539                      * sticky bits from TIOCSFLAGS.
540                      */
541                     t.c_ispeed = 0;
542                     if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
543                               t.c_ospeed = ixpcomcn_sc.sc_ospeed;
544                               t.c_cflag = ixpcomcn_sc.sc_cflag;
545                     } else {
546                               t.c_ospeed = TTYDEF_SPEED;
547                               t.c_cflag = TTYDEF_CFLAG;
548                     }
549                     if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
550                               SET(t.c_cflag, CLOCAL);
551                     if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
552                               SET(t.c_cflag, CRTSCTS);
553                     if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
554                               SET(t.c_cflag, MDMBUF);
555                     /* Make sure ixpcomparam() will do something. */
556                     tp->t_ospeed = 0;
557                     (void) ixpcomparam(tp, &t);
558                     tp->t_iflag = TTYDEF_IFLAG;
559                     tp->t_oflag = TTYDEF_OFLAG;
560                     tp->t_lflag = TTYDEF_LFLAG;
561                     ttychars(tp);
562                     ttsetwater(tp);
563 
564                     s2 = splserial();
565                     COM_LOCK(sc);
566 
567                     /* Clear the input ring, and unblock. */
568                     sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
569                     sc->sc_rbavail = IXPCOM_RING_SIZE;
570                     ixpcom_iflush(sc);
571                     CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
572 
573 #ifdef COM_DEBUG
574                     if (ixpcom_debug)
575                               comstatus(sc, "ixpcomopen  ");
576 #endif
577 
578                     COM_UNLOCK(sc);
579                     splx(s2);
580           }
581 
582           splx(s);
583 
584           error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
585           if (error)
586                     goto bad;
587 
588           error = (*tp->t_linesw->l_open)(dev, tp);
589           if (error)
590                     goto bad;
591 
592           return (0);
593 
594 bad:
595           if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
596                     /*
597                      * We failed to open the device, and nobody else had it opened.
598                      * Clean up the state as appropriate.
599                      */
600                     ixpcom_shutdown(sc);
601           }
602 
603           return (error);
604 }
605 
606 int
ixpcomclose(dev_t dev,int flag,int mode,struct lwp * l)607 ixpcomclose(dev_t dev, int flag, int mode, struct lwp *l)
608 {
609           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
610           struct tty *tp = sc->sc_tty;
611 
612           /* XXX This is for cons.c. */
613           if (!ISSET(tp->t_state, TS_ISOPEN))
614                     return (0);
615 
616           (*tp->t_linesw->l_close)(tp, flag);
617           ttyclose(tp);
618 
619           if (COM_ISALIVE(sc) == 0)
620                     return (0);
621 
622           if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
623                     /*
624                      * Although we got a last close, the device may still be in
625                      * use; e.g. if this was the dialout node, and there are still
626                      * processes waiting for carrier on the non-dialout node.
627                      */
628                     ixpcom_shutdown(sc);
629           }
630 
631           return (0);
632 }
633 
634 int
ixpcomread(dev_t dev,struct uio * uio,int flag)635 ixpcomread(dev_t dev, struct uio *uio, int flag)
636 {
637           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
638           struct tty *tp = sc->sc_tty;
639 
640           if (COM_ISALIVE(sc) == 0)
641                     return (EIO);
642 
643           return ((*tp->t_linesw->l_read)(tp, uio, flag));
644 }
645 
646 int
ixpcomwrite(dev_t dev,struct uio * uio,int flag)647 ixpcomwrite(dev_t dev, struct uio *uio, int flag)
648 {
649           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
650           struct tty *tp = sc->sc_tty;
651 
652           if (COM_ISALIVE(sc) == 0)
653                     return (EIO);
654 
655           return ((*tp->t_linesw->l_write)(tp, uio, flag));
656 }
657 
658 int
ixpcompoll(dev_t dev,int events,struct lwp * l)659 ixpcompoll(dev_t dev, int events, struct lwp *l)
660 {
661           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
662           struct tty *tp = sc->sc_tty;
663 
664           if (COM_ISALIVE(sc) == 0)
665                     return (EIO);
666 
667           return ((*tp->t_linesw->l_poll)(tp, events, l));
668 }
669 
670 struct tty *
ixpcomtty(dev_t dev)671 ixpcomtty(dev_t dev)
672 {
673           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
674           struct tty *tp = sc->sc_tty;
675 
676           return (tp);
677 }
678 
679 int
ixpcomioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)680 ixpcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
681 {
682           struct ixpcom_softc *sc = device_lookup_private(&ixpcom_cd, COMUNIT(dev));
683           struct tty *tp = sc->sc_tty;
684           int error;
685           int s;
686 
687           if (COM_ISALIVE(sc) == 0)
688                     return (EIO);
689 
690           error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
691           if (error != EPASSTHROUGH)
692                     return (error);
693 
694           error = ttioctl(tp, cmd, data, flag, l);
695           if (error != EPASSTHROUGH)
696                     return (error);
697 
698           error = 0;
699 
700           s = splserial();
701           COM_LOCK(sc);
702 
703           switch (cmd) {
704           case TIOCSBRK:
705                     ixpcom_break(sc, 1);
706                     break;
707 
708           case TIOCCBRK:
709                     ixpcom_break(sc, 0);
710                     break;
711 
712           case TIOCGFLAGS:
713                     *(int *)data = sc->sc_swflags;
714                     break;
715 
716           case TIOCSFLAGS:
717                     error = kauth_authorize_device_tty(l->l_cred,
718                         KAUTH_DEVICE_TTY_PRIVSET, tp);
719                     if (error)
720                               break;
721                     sc->sc_swflags = *(int *)data;
722                     break;
723 
724           default:
725                     error = EPASSTHROUGH;
726                     break;
727           }
728 
729           COM_UNLOCK(sc);
730           splx(s);
731 
732           return (error);
733 }
734 
735 /*
736  * Stop output on a line.
737  */
738 void
ixpcomstop(struct tty * tp,int flag)739 ixpcomstop(struct tty *tp, int flag)
740 {
741           struct ixpcom_softc *sc
742                     = device_lookup_private(&ixpcom_cd, COMUNIT(tp->t_dev));
743           int s;
744 
745           s = splserial();
746           COM_LOCK(sc);
747           if (ISSET(tp->t_state, TS_BUSY)) {
748                     /* Stop transmitting at the next chunk. */
749                     sc->sc_tbc = 0;
750                     sc->sc_heldtbc = 0;
751                     if (!ISSET(tp->t_state, TS_TTSTOP))
752                               SET(tp->t_state, TS_FLUSH);
753           }
754           COM_UNLOCK(sc);
755           splx(s);
756 }
757 
758 static u_int
cflag2cr(tcflag_t cflag)759 cflag2cr(tcflag_t cflag)
760 {
761           u_int cr;
762 
763           cr  = (cflag & PARENB) ? CR_PE : 0;
764           cr |= (cflag & PARODD) ? CR_OES : 0;
765           cr |= (cflag & CSTOPB) ? CR_SBS : 0;
766           cr |= ((cflag & CSIZE) == CS8) ? DSS_8BIT : 0;
767 
768           return (cr);
769 }
770 
771 static void
ixpcom_iflush(struct ixpcom_softc * sc)772 ixpcom_iflush(struct ixpcom_softc *sc)
773 {
774           bus_space_tag_t iot = sc->sc_iot;
775           bus_space_handle_t ioh = sc->sc_ioh;
776 #ifdef DIAGNOSTIC
777           int reg;
778 #endif
779           int timo;
780 
781 #ifdef DIAGNOSTIC
782           reg = 0xffff;
783 #endif
784           timo = 50000;
785           /* flush any pending I/O */
786           while (ISSET(bus_space_read_4(iot, ioh, IXPCOM_SR), SR_RXR)
787                  && --timo)
788 #ifdef DIAGNOSTIC
789                     reg =
790 #else
791                               (void)
792 #endif
793                               bus_space_read_4(iot, ioh, IXPCOM_DR);
794 #ifdef DIAGNOSTIC
795           if (!timo)
796                     printf("%s: com_iflush timeout %02x\n", device_xname(sc->sc_dev),
797                            reg);
798 #endif
799 }
800 
801 static void
ixpcom_set_cr(struct ixpcom_softc * sc)802 ixpcom_set_cr(struct ixpcom_softc *sc)
803 {
804           /* XXX */
805           ixpcom_cr &= ~(CR_RIE | CR_XIE);
806           ixpcom_cr |= sc->sc_rie | sc->sc_xie;
807           bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCOM_CR,
808                                 ixpcom_cr & ~ixpcom_imask);
809 }
810 
811 int
ixpcomcnattach(bus_space_tag_t iot,bus_addr_t iobase,bus_space_handle_t ioh,int ospeed,tcflag_t cflag)812 ixpcomcnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh, int ospeed, tcflag_t cflag)
813 {
814           int cr;
815 
816           cn_tab = &ixpcomcons;
817           cn_init_magic(&ixpcom_cnm_state);
818           /*
819            * XXX
820            *
821            * ixpcom cannot detect a break.  It can only detect framing
822            * errors.  And, a sequence of "framing error, framing error"
823            * meens a break.  So I made this hack:
824            *
825            * 1. Set default magic is a sequence of "BREAK, BREAK".
826            * 2. Tell cn_check_magic() a "framing error" as BREAK.
827            *
828            * see ixpcom_intr() too.
829            *
830            */
831           /* default magic is a couple of BREAK. */
832           cn_set_magic("\047\001\047\001");
833 
834           ixpcomcn_sc.sc_iot = iot;
835           ixpcomcn_sc.sc_ioh = ioh;
836           ixpcomcn_sc.sc_baseaddr = iobase;
837           ixpcomcn_sc.sc_ospeed = ospeed;
838           ixpcomcn_sc.sc_cflag = cflag;
839 
840           cr = cflag2cr(cflag);
841           cr |= IXPCOMSPEED2BRD(ospeed);
842           cr |= CR_UE;
843           ixpcom_cr = cr;
844 
845           /* enable the UART */
846           bus_space_write_4(iot, iobase, IXPCOM_CR, cr);
847 
848           return (0);
849 }
850 
851 void
ixpcomcnprobe(struct consdev * cp)852 ixpcomcnprobe(struct consdev *cp)
853 {
854           cp->cn_pri = CN_REMOTE;
855 }
856 
857 void
ixpcomcnpollc(dev_t dev,int on)858 ixpcomcnpollc(dev_t dev, int on)
859 {
860 }
861 
862 void
ixpcomcnputc(dev_t dev,int c)863 ixpcomcnputc(dev_t dev, int c)
864 {
865           int                           s;
866           bus_space_tag_t               iot = ixpcomcn_sc.sc_iot;
867           bus_space_handle_t  ioh = ixpcomcn_sc.sc_ioh;
868 
869           s = splserial();
870 
871           while(!(bus_space_read_4(iot, ioh, IXPCOM_SR) & SR_TXR))
872                     ;
873 
874           bus_space_write_4(iot, ioh, IXPCOM_DR, c);
875 
876 #ifdef DEBUG
877           if (c == '\r') {
878                     while(!(bus_space_read_4(iot, ioh, IXPCOM_SR) & SR_TXE))
879                               ;
880           }
881 #endif
882 
883           splx(s);
884 }
885 
886 int
ixpcomcngetc(dev_t dev)887 ixpcomcngetc(dev_t dev)
888 {
889           int                           c;
890           int                           s;
891           bus_space_tag_t               iot = ixpcomcn_sc.sc_iot;
892           bus_space_handle_t  ioh = ixpcomcn_sc.sc_ioh;
893 
894         s = splserial();
895 
896           while(!(bus_space_read_4(iot, ioh, IXPCOM_SR) & SR_RXR))
897                     ;
898 
899           c = bus_space_read_4(iot, ioh, IXPCOM_DR);
900           c &= 0xff;
901           splx(s);
902 
903           return (c);
904 }
905 
906 inline static void
ixpcom_txsoft(struct ixpcom_softc * sc,struct tty * tp)907 ixpcom_txsoft(struct ixpcom_softc *sc, struct tty *tp)
908 {
909           CLR(tp->t_state, TS_BUSY);
910           if (ISSET(tp->t_state, TS_FLUSH))
911                     CLR(tp->t_state, TS_FLUSH);
912         else
913                     ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
914           (*tp->t_linesw->l_start)(tp);
915 }
916 
917 inline static void
ixpcom_rxsoft(struct ixpcom_softc * sc,struct tty * tp)918 ixpcom_rxsoft(struct ixpcom_softc *sc, struct tty *tp)
919 {
920           int (*rint)(int, struct tty *) = tp->t_linesw->l_rint;
921           u_char *get, *end;
922           u_int cc, scc;
923           u_char lsr;
924           int code;
925           int s;
926 
927           end = sc->sc_ebuf;
928           get = sc->sc_rbget;
929           scc = cc = IXPCOM_RING_SIZE - sc->sc_rbavail;
930 #if 0
931           if (cc == IXPCOM_RING_SIZE) {
932                     sc->sc_floods++;
933                     if (sc->sc_errors++ == 0)
934                               callout_reset(&sc->sc_diag_callout, 60 * hz,
935                                   comdiag, sc);
936           }
937 #endif
938           while (cc) {
939                     code = get[0];
940                     lsr = get[1];
941                     if (ISSET(lsr, DR_ROR | DR_FRE | DR_PRE)) {
942 #if 0
943                               if (ISSET(lsr, DR_ROR)) {
944                                         sc->sc_overflows++;
945                                         if (sc->sc_errors++ == 0)
946                                                   callout_reset(&sc->sc_diag_callout,
947                                                       60 * hz, comdiag, sc);
948                               }
949 #endif
950                               if (ISSET(lsr, DR_FRE))
951                                         SET(code, TTY_FE);
952                               if (ISSET(lsr, DR_PRE))
953                                         SET(code, TTY_PE);
954                     }
955                     if ((*rint)(code, tp) == -1) {
956                               /*
957                                * The line discipline's buffer is out of space.
958                                */
959                               if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
960                                         /*
961                                          * We're either not using flow control, or the
962                                          * line discipline didn't tell us to block for
963                                          * some reason.  Either way, we have no way to
964                                          * know when there's more space available, so
965                                          * just drop the rest of the data.
966                                          */
967                                         get += cc << 1;
968                                         if (get >= end)
969                                                   get -= IXPCOM_RING_SIZE << 1;
970                                         cc = 0;
971                               } else {
972                                         /*
973                                          * Don't schedule any more receive processing
974                                          * until the line discipline tells us there's
975                                          * space available (through comhwiflow()).
976                                          * Leave the rest of the data in the input
977                                          * buffer.
978                                          */
979                                         SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
980                               }
981                               break;
982                     }
983                     get += 2;
984                     if (get >= end)
985                               get = sc->sc_rbuf;
986                     cc--;
987           }
988 
989           if (cc != scc) {
990                     sc->sc_rbget = get;
991                     s = splserial();
992                     COM_LOCK(sc);
993 
994                     cc = sc->sc_rbavail += scc - cc;
995                     /* Buffers should be ok again, release possible block. */
996                     if (cc >= 1) {
997                               if (ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
998                                         CLR(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
999                                         SET(sc->sc_rie, CR_RIE);
1000                                         ixpcom_set_cr(sc);
1001                               }
1002                               if (ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED)) {
1003                                         CLR(sc->sc_rx_flags, RX_IBUF_BLOCKED);
1004 #if 0
1005                                         com_hwiflow(sc);
1006 #endif
1007                               }
1008                     }
1009                     COM_UNLOCK(sc);
1010                     splx(s);
1011           }
1012 }
1013 
1014 static void
ixpcomsoft(void * arg)1015 ixpcomsoft(void* arg)
1016 {
1017           struct ixpcom_softc *sc = arg;
1018 
1019           if (COM_ISALIVE(sc) == 0)
1020                     return;
1021 
1022           if (sc->sc_rx_ready) {
1023                     sc->sc_rx_ready = 0;
1024                     ixpcom_rxsoft(sc, sc->sc_tty);
1025           }
1026           if (sc->sc_tx_done) {
1027                     sc->sc_tx_done = 0;
1028                     ixpcom_txsoft(sc, sc->sc_tty);
1029           }
1030 }
1031 
1032 int
ixpcomintr(void * arg)1033 ixpcomintr(void* arg)
1034 {
1035           struct ixpcom_softc *sc = arg;
1036           bus_space_tag_t iot = sc->sc_iot;
1037           bus_space_handle_t ioh = sc->sc_ioh;
1038           u_char *put, *end;
1039           u_int cc;
1040           u_int cr;
1041           u_int sr;
1042           uint32_t c;
1043 
1044           if (COM_ISALIVE(sc) == 0)
1045                     return (0);
1046 
1047           COM_LOCK(sc);
1048           cr = bus_space_read_4(iot, ioh, IXPCOM_CR) & CR_UE;
1049 
1050           if (!cr) {
1051                     COM_UNLOCK(sc);
1052                     return (0);
1053           }
1054 
1055           sr = bus_space_read_4(iot, ioh, IXPCOM_SR);
1056           if (!ISSET(sr, SR_TXR | SR_RXR))
1057                     return (0);
1058 
1059           /*
1060            * XXX
1061            *
1062            * ixpcom cannot detect a break.  It can only detect framing
1063            * errors.  And, a sequence of "framing error, framing error"
1064            * meens a break.  So I made this hack:
1065            *
1066            * 1. Set default magic is a sequence of "BREAK, BREAK".
1067            * 2. Tell cn_check_magic() a "framing error" as BREAK.
1068            *
1069            * see ixpcomcnattach() too.
1070            *
1071            */
1072           if (ISSET(sr, SR_FRE)) {
1073                     cn_check_magic(sc->sc_tty->t_dev,
1074                                      CNC_BREAK, ixpcom_cnm_state);
1075           }
1076 
1077           end = sc->sc_ebuf;
1078           put = sc->sc_rbput;
1079           cc = sc->sc_rbavail;
1080 
1081           if (ISSET(sr, SR_RXR)) {
1082                     if (!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
1083                               while (cc > 0) {
1084                                         if (!ISSET(sr, SR_RXR))
1085                                                   break;
1086                                         c = bus_space_read_4(iot, ioh, IXPCOM_DR);
1087                                         if (ISSET(c, DR_FRE)) {
1088                                                   cn_check_magic(sc->sc_tty->t_dev,
1089                                                                    CNC_BREAK,
1090                                                                    ixpcom_cnm_state);
1091                                         }
1092                                         put[0] = c & 0xff;
1093                                         put[1] = (c >> 8) & 0xff;
1094                                         cn_check_magic(sc->sc_tty->t_dev,
1095                                                          put[0], ixpcom_cnm_state);
1096                                         put += 2;
1097                                         if (put >= end)
1098                                                   put = sc->sc_rbuf;
1099                                         cc--;
1100 
1101                                         sr = bus_space_read_4(iot, ioh, IXPCOM_SR);
1102                               }
1103 
1104                               /*
1105                                * Current string of incoming characters ended because
1106                                * no more data was available or we ran out of space.
1107                                * Schedule a receive event if any data was received.
1108                                * If we're out of space, turn off receive interrupts.
1109                                */
1110                               sc->sc_rbput = put;
1111                               sc->sc_rbavail = cc;
1112                               if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED))
1113                                         sc->sc_rx_ready = 1;
1114 
1115                               /*
1116                                * See if we are in danger of overflowing a buffer. If
1117                                * so, use hardware flow control to ease the pressure.
1118                                */
1119 
1120                               /* but ixpcom cannot. X-( */
1121 
1122                               /*
1123                                * If we're out of space, disable receive interrupts
1124                                * until the queue has drained a bit.
1125                                */
1126                               if (!cc) {
1127                                         SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
1128                                         CLR(sc->sc_rie, CR_RIE);
1129                                         ixpcom_set_cr(sc);
1130                               }
1131                     } else {
1132 #ifdef DIAGNOSTIC
1133                               panic("ixpcomintr: we shouldn't reach here");
1134 #endif
1135                               CLR(sc->sc_rie, CR_RIE);
1136                               ixpcom_set_cr(sc);
1137                     }
1138           }
1139 
1140           /*
1141            * Done handling any receive interrupts. See if data can be
1142            * transmitted as well. Schedule tx done event if no data left
1143            * and tty was marked busy.
1144            */
1145           sr = bus_space_read_4(iot, ioh, IXPCOM_SR);
1146           if (ISSET(sr, SR_TXR)) {
1147                     /*
1148                      * If we've delayed a parameter change, do it now, and restart
1149                      * output.
1150                      */
1151                     if (sc->sc_heldchange) {
1152                               ixpcom_set_cr(sc);
1153                               sc->sc_heldchange = 0;
1154                               sc->sc_tbc = sc->sc_heldtbc;
1155                               sc->sc_heldtbc = 0;
1156                     }
1157 
1158                     /* Output the next chunk of the contiguous buffer, if any. */
1159                     if (sc->sc_tbc > 0) {
1160                               ixpcom_filltx(sc);
1161                     } else {
1162                               /* Disable transmit completion interrupts if necessary. */
1163                               if (ISSET(sc->sc_xie, CR_XIE)) {
1164                                         CLR(sc->sc_xie, CR_XIE);
1165                                         ixpcom_set_cr(sc);
1166                               }
1167                               if (sc->sc_tx_busy) {
1168                                         sc->sc_tx_busy = 0;
1169                                         sc->sc_tx_done = 1;
1170                               }
1171                     }
1172           }
1173           COM_UNLOCK(sc);
1174 
1175           /* Wake up the poller. */
1176           softint_schedule(sc->sc_si);
1177 
1178 #ifdef RND_COM
1179           rnd_add_uint32(&sc->rnd_source, iir | lsr);
1180 #endif
1181           return (1);
1182 }
1183