Date: Sun, 6 Jul 2003 12:51:49 -0700 (PDT) From: Marcel Moolenaar <marcel@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 34133 for review Message-ID: <200307061951.h66JpnAR022295@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=34133 Change 34133 by marcel@marcel_nfs on 2003/07/06 12:51:16 Rework some of the bones and add some meat to try it on for size: o Redefine the interrupt sources/priorities to have more relation to the higher level operation. This gives a better abstraction and therefore better hardware independence. o Add a flag to the softc for when there's no interrupt resource. We don't do anything with it yet. o Add transmit and receive buffers. Make them OBUFSIZ and IBUFSIZ in size (resp). Both buffers are circular. o Flesh-out the low-level (hardware) interrupt handler and add the software interrupt stuff for the high-level processing. o Improve uart_bus_probe() to properly create the KOBJ instance of the UART class with which we're going to work. UART drivers can now access their class specific data in all cases. o Get the fundamentals of uart_bus_detach() filled in. UARTs are likely to come and go, so we want this to work right. o Add new UART interface methods to the ns8250 and the sab82532 drivers. We need to start fleshing those out next. Affected files ... .. //depot/projects/uart/dev/uart/uart_bus.h#3 edit .. //depot/projects/uart/dev/uart/uart_core.c#3 edit .. //depot/projects/uart/dev/uart/uart_dev_ns8250.c#2 edit .. //depot/projects/uart/dev/uart/uart_dev_sab82532.c#3 edit .. //depot/projects/uart/dev/uart/uart_if.m#3 edit .. //depot/projects/uart/dev/uart/uart_tty.c#2 edit .. //depot/projects/uart/dev/uart/uart_tty.h#2 edit Differences ... ==== //depot/projects/uart/dev/uart/uart_bus.h#3 (text+ko) ==== @@ -29,17 +29,17 @@ #ifndef _DEV_UART_BUS_H_ #define _DEV_UART_BUS_H_ -/* - * Interrupt sources (in priority order): - * line - line errors (overrun, framing) - * recv - received data - * xmit - transmitter idle - * ctrl - line- and modem signals - */ -#define UART_IPEND_LINE 0x01 -#define UART_IPEND_RECV 0x02 -#define UART_IPEND_XMIT 0x04 -#define UART_IPEND_CTRL 0x08 +/* Interrupt sources (in priority order). See also uart_core.c */ +#define UART_IPEND_OVERRUN 0x0001 +#define UART_IPEND_BREAK 0x0002 +#define UART_IPEND_RXREADY 0x0004 +#define UART_IPEND_SIGCHG 0x0008 +#define UART_IPEND_TXIDLE 0x0010 + +/* Received character status bits. */ +#define UART_STAT_BREAK 0x1000 +#define UART_STAT_OVERRUN 0x2000 +#define UART_STAT_PARERR 0x4000 /* * UART class & instance (=softc) @@ -69,14 +69,28 @@ int sc_console:1; /* This UART is a console. */ int sc_dbgport:1; /* This UART is a debug port. */ int sc_fastintr:1; /* This UART uses fast interrupts. */ - int sc_hasfifo:1; /* This UART has a FIFO. */ + int sc_hasfifo:1; /* This UART has FIFOs. */ int sc_leaving:1; /* This UART is going away. */ + int sc_polled:1; /* This UART has no interrupts. */ + /* Receiver data. */ + uint16_t *sc_rxbuf; + int sc_rxbufsz; + int sc_rxput; + int sc_rxget; int sc_rxfifosz; /* Size of RX FIFO. */ + + /* Transmitter data. */ + uint8_t *sc_txbuf; + int sc_txbufsz; + int sc_txput; + int sc_txget; int sc_txfifosz; /* Size of TX FIFO. */ dev_t sc_si; + void *sc_softih; struct tty *sc_tty; + uint32_t sc_ttypend; }; extern devclass_t uart_devclass; @@ -86,4 +100,17 @@ int uart_bus_detach(device_t dev); int uart_bus_probe(device_t dev, int regshft, int rclk, int rid); +static __inline int +uart_rx_put(struct uart_softc *sc, int xc) +{ + int ptr; + + ptr = (sc->sc_rxput + 1 < sc->sc_rxbufsz) ? sc->sc_rxput + 1 : 0; + if (ptr == sc->sc_rxget) + return (ENOSPC); + sc->sc_rxbuf[sc->sc_rxput] = xc; + sc->sc_rxput = ptr; + return (0); +} + #endif /* _DEV_UART_BUS_H_ */ ==== //depot/projects/uart/dev/uart/uart_core.c#3 (text+ko) ==== @@ -32,11 +32,14 @@ #include <sys/bus.h> #include <sys/conf.h> #include <sys/cons.h> +#include <sys/interrupt.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/reboot.h> #include <machine/bus.h> #include <sys/rman.h> +#include <sys/termios.h> +#include <sys/tty.h> #include <machine/resource.h> #include <dev/uart/uart.h> @@ -54,28 +57,87 @@ MALLOC_DEFINE(M_UART, "UART", "UART driver"); -static int -uart_intr_line(struct uart_softc *sc) +/* + * A break condition has been detected. We treat the break condition as + * a special case that should not happen during normal operation. When + * the break condition is to be passed to higher levels in the form of + * a NUL character, we really want the break to be in the right place in + * the input stream. The overhead to achieve that is not in relation to + * the exceptional nature of the break condition, so we permit ourselves + * to be sloppy. + */ +static void +uart_intr_break(struct uart_softc *sc) +{ +#if defined(DDB) && defined(BREAK_TO_DEBUGGER) + breakpoint(); +#else + if (sc->sc_tty == NULL || sc->sc_tty->t_iflag & IGNBRK) + return; + if (uart_rx_put(sc, UART_STAT_BREAK)) + sc->sc_rxbuf[sc->sc_rxput] |= UART_STAT_BREAK; + atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY); +#endif +} + +/* + * Handle a receiver overrun situation. We lost at least 1 byte in the + * input stream and it's our job to contain the situation. We grab as + * much of the data we can, but otherwise flush the receiver FIFO to + * create some breathing room. The net effect is that we avoid the + * overrun condition to happen for the next X characters, where X is + * related to the FIFO size at the cost of loosing data right away. + * So, instead of having multiple overrun interrupts in close proximity + * to each other and possibly pessimizing UART interrupt latency for + * other UARTs in a multiport configuration, we create a longer segment + * of missing characters by freeing up the FIFO. + * Each overrun condition is marked in the input buffer by a token. The + * token represents the loss of at least one, but possible more bytes in + * the input stream. + */ +static void +uart_intr_overrun(struct uart_softc *sc) { - return (0); + UART_RECEIVE(sc); + UART_RXFLUSH(sc); + if (uart_rx_put(sc, UART_STAT_OVERRUN)) + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; + atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY); } -static int -uart_intr_recv(struct uart_softc *sc) +/* + * Received data ready. + */ +static void +uart_intr_rxready(struct uart_softc *sc) { - return (0); + UART_RECEIVE(sc); + atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY); } -static int -uart_intr_xmit(struct uart_softc *sc) +/* + * Line or modem status change (OOB signalling). + */ +static void +uart_intr_sigchg(struct uart_softc *sc) { - return (0); + int sig; + + sig = UART_GETSIG(sc); + /* + * TODO: process the signals. + */ } -static int -uart_intr_ctrl(struct uart_softc *sc) +/* + * The transmitter can accept more data. + */ +static void +uart_intr_txidle(struct uart_softc *sc) { - return (0); + if (sc->sc_txget != sc->sc_txput) + UART_TRANSMIT(sc); + atomic_set_32(&sc->sc_ttypend, UART_IPEND_TXIDLE); } static void @@ -84,48 +146,46 @@ struct uart_softc *sc = arg; int ipend; + if (sc->sc_leaving) + return; + ipend = UART_IPEND(sc); - while (ipend != 0) { - if (ipend & UART_IPEND_LINE) - uart_intr_line(sc); - if (ipend & UART_IPEND_RECV) - uart_intr_recv(sc); - if (ipend & UART_IPEND_XMIT) - uart_intr_xmit(sc); - if (ipend & UART_IPEND_CTRL) - uart_intr_ctrl(sc); - ipend = UART_IPEND(sc); - } + if (ipend & UART_IPEND_OVERRUN) + uart_intr_overrun(sc); + if (ipend & UART_IPEND_BREAK) + uart_intr_break(sc); + if (ipend & UART_IPEND_RXREADY) + uart_intr_rxready(sc); + if (ipend & UART_IPEND_SIGCHG) + uart_intr_sigchg(sc); + if (ipend & UART_IPEND_TXIDLE) + uart_intr_txidle(sc); + if (sc->sc_ttypend != 0) + swi_sched(sc->sc_softih, 0); } int uart_bus_probe(device_t dev, int regshft, int rclk, int rid) { - struct uart_softc *sc; + struct uart_softc *sc, *sc0; int error; - sc = device_get_softc(dev); - sc->sc_dev = dev; - /* * The sc_class field defines the type of UART we're going to work - * with. Initialize the softc (=object/instance) so that we can use - * the class methods. Initializing the softc also compiles the - * class if not already compiled. Note that at this time the class - * does not match the softc. The softc belongs to class uart_class - * (the abstract base class), while the sc_class field points to - * a derived class (e.g. ns8250_class). The object of the derived - * class can be (and likely is) larger than the base class. We - * therefore depend on kobj_init to not assume that the object is - * in fact an instantiation of the class we give it. When we attach - * the device, the assignment (device->driver) is definite and we'll - * make sure the softc is replaced with the correct one. This means - * that the UART_PROBE() method cannot use any of the class specific - * data items. This is a bit annoying, because a driver may need to - * do extensive probing to accurately set the device description and - * it's a pain to have to do it again because we couldn't save the - * results of the previous probing. This may need to be revisited. + * with and thus the size of the softc. Replace the generic softc + * with one that matches the UART and initialize it. Initialization + * also compiles the class if not already compiled. We also set the + * initial device description equal to the UART class name, unless + * a description has already been set. This name can be overwritten + * with a more specific description. */ + sc0 = device_get_softc(dev); + sc = malloc(sc0->sc_class->size, M_UART, M_WAITOK|M_ZERO); + bcopy(sc0, sc, sizeof(*sc)); + device_set_softc(dev, sc); + sc->sc_dev = dev; + if (device_get_desc(dev) == NULL) + device_set_desc(dev, sc->sc_class->name); kobj_init((kobj_t)sc, (kobj_class_t)sc->sc_class); /* @@ -159,11 +219,8 @@ sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk; error = UART_PROBE(sc); - if (error) { - bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, - sc->sc_rres); - return (error); - } + if (error) + goto out; /* * Figure out if this UART is a console device or a debug port and @@ -183,22 +240,17 @@ if (!sc->sc_console && !sc->sc_dbgport) { error = UART_RESET(sc); - if (error) { - bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, - sc->sc_rres); - return (error); - } + if (error) + goto out; } error = UART_INITFIFO(sc); - if (error) { - bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, - sc->sc_rres); - return (error); - } + if (error) + goto out; - device_set_desc(dev, sc->sc_class->name); error = UART_DESCRIBE(sc); + + out: bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); return (error); } @@ -243,12 +295,11 @@ } } if (sc->sc_ires == NULL) { - /* - * XXX no interrupt resource. Force polled mode. - */ + /* XXX no interrupt resource. Force polled mode. */ + sc->sc_polled = 1; } - if (sc->sc_console || sc->sc_dbgport || sc->sc_fastintr) { + if (sc->sc_console || sc->sc_dbgport) { sep = ""; device_print_prettyname(dev); if (sc->sc_console) { @@ -261,13 +312,30 @@ printf("%sdebug port", sep); sep = ", "; } + printf("\n"); + } + + if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { + sep = ""; + device_print_prettyname(dev); if (sc->sc_fastintr) { printf("%sfast interrupt", sep); sep = ", "; } + if (sc->sc_polled) { + printf("%spolled mode", sep); + sep = ", "; + } printf("\n"); } + sc->sc_rxbufsz = IBUFSIZ; + sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf), + M_UART, M_WAITOK); + sc->sc_txbufsz = OBUFSIZ; + sc->sc_txbuf = malloc(sc->sc_txbufsz * sizeof(*sc->sc_txbuf), + M_UART, M_WAITOK); + uart_tty_attach(sc); return (0); @@ -279,13 +347,21 @@ sc = device_get_softc(dev); + sc->sc_leaving = 1; + uart_tty_detach(sc); + free(sc->sc_txbuf, M_UART); + free(sc->sc_rxbuf, M_UART); + if (sc->sc_ires != NULL) { bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); } bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + + UART_SHUTDOWN(sc); + return (0); } ==== //depot/projects/uart/dev/uart/uart_dev_ns8250.c#2 (text+ko) ==== @@ -253,15 +253,29 @@ }; static int ns8250_bus_describe(struct uart_softc *); +static int ns8250_bus_getsig(struct uart_softc *); static int ns8250_bus_initfifo(struct uart_softc *); +static int ns8250_bus_ipend(struct uart_softc *); static int ns8250_bus_probe(struct uart_softc *); +static int ns8250_bus_receive(struct uart_softc *); static int ns8250_bus_reset(struct uart_softc *); +static int ns8250_bus_rxflush(struct uart_softc *); +static int ns8250_bus_shutdown(struct uart_softc *); +static int ns8250_bus_transmit(struct uart_softc *); +static int ns8250_bus_txflush(struct uart_softc *); static kobj_method_t ns8250_methods[] = { KOBJMETHOD(uart_describe, ns8250_bus_describe), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_initfifo, ns8250_bus_initfifo), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_reset, ns8250_bus_reset), + KOBJMETHOD(uart_rxflush, ns8250_bus_rxflush), + KOBJMETHOD(uart_shutdown, ns8250_bus_shutdown), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD(uart_txflush, ns8250_bus_txflush), { 0, 0 } }; @@ -313,6 +327,13 @@ } static int +ns8250_bus_getsig(struct uart_softc *sc) +{ + + return (0); +} + +static int ns8250_bus_initfifo(struct uart_softc *sc) { @@ -320,6 +341,13 @@ } static int +ns8250_bus_ipend(struct uart_softc *sc) +{ + + return (0); +} + +static int ns8250_bus_probe(struct uart_softc *sc) { @@ -327,9 +355,44 @@ } static int +ns8250_bus_receive(struct uart_softc *sc) +{ + + return (0); +} + +static int ns8250_bus_reset(struct uart_softc *sc) { ns8250_init(&sc->sc_bas, 9600, 8, 1, UART_PARITY_NONE); return (0); } + +static int +ns8250_bus_rxflush(struct uart_softc *sc) +{ + + return (0); +} + +static int +ns8250_bus_shutdown(struct uart_softc *sc) +{ + + return (0); +} + +static int +ns8250_bus_transmit(struct uart_softc *sc) +{ + + return (0); +} + +static int +ns8250_bus_txflush(struct uart_softc *sc) +{ + + return (0); +} ==== //depot/projects/uart/dev/uart/uart_dev_sab82532.c#3 (text+ko) ==== @@ -115,15 +115,29 @@ }; static int sab82532_bus_describe(struct uart_softc *); +static int sab82532_bus_getsig(struct uart_softc *); static int sab82532_bus_initfifo(struct uart_softc *); +static int sab82532_bus_ipend(struct uart_softc *); static int sab82532_bus_probe(struct uart_softc *); +static int sab82532_bus_receive(struct uart_softc *); static int sab82532_bus_reset(struct uart_softc *); +static int sab82532_bus_rxflush(struct uart_softc *); +static int sab82532_bus_shutdown(struct uart_softc *); +static int sab82532_bus_transmit(struct uart_softc *); +static int sab82532_bus_txflush(struct uart_softc *); static kobj_method_t sab82532_methods[] = { KOBJMETHOD(uart_describe, sab82532_bus_describe), + KOBJMETHOD(uart_getsig, sab82532_bus_getsig), KOBJMETHOD(uart_initfifo, sab82532_bus_initfifo), + KOBJMETHOD(uart_ipend, sab82532_bus_ipend), KOBJMETHOD(uart_probe, sab82532_bus_probe), + KOBJMETHOD(uart_receive, sab82532_bus_receive), KOBJMETHOD(uart_reset, sab82532_bus_reset), + KOBJMETHOD(uart_rxflush, sab82532_bus_rxflush), + KOBJMETHOD(uart_shutdown, sab82532_bus_shutdown), + KOBJMETHOD(uart_transmit, sab82532_bus_transmit), + KOBJMETHOD(uart_txflush, sab82532_bus_txflush), { 0, 0 } }; @@ -151,12 +165,19 @@ default: vstr = "v4?"; break; } - snprintf(buf, sizeof(buf), "Siemens SAB 82532 %s (ch %s)", vstr, ch); + snprintf(buf, sizeof(buf), "SAB 82532 %s, channel %s", vstr, ch); device_set_desc_copy(sc->sc_dev, buf); return (0); } static int +sab82532_bus_getsig(struct uart_softc *sc) +{ + + return (0); +} + +static int sab82532_bus_initfifo(struct uart_softc *sc) { @@ -164,6 +185,13 @@ } static int +sab82532_bus_ipend(struct uart_softc *sc) +{ + + return (0); +} + +static int sab82532_bus_probe(struct uart_softc *sc) { @@ -171,9 +199,44 @@ } static int +sab82532_bus_receive(struct uart_softc *sc) +{ + + return (0); +} + +static int sab82532_bus_reset(struct uart_softc *sc) { sab82532_init(&sc->sc_bas, 9600, 8, 1, UART_PARITY_NONE); return (0); } + +static int +sab82532_bus_rxflush(struct uart_softc *sc) +{ + + return (0); +} + +static int +sab82532_bus_shutdown(struct uart_softc *sc) +{ + + return (0); +} + +static int +sab82532_bus_transmit(struct uart_softc *sc) +{ + + return (0); +} + +static int +sab82532_bus_txflush(struct uart_softc *sc) +{ + + return (0); +} ==== //depot/projects/uart/dev/uart/uart_if.m#3 (text+ko) ==== @@ -34,7 +34,7 @@ INTERFACE uart; -# describe() - set the device description +# describe() - set the device description. # This method is called after FIFOs are initialized and is used to set the # device description to match the actual hardware (as much as is possible). # The method is free to perform whatever UART programming is required to @@ -44,7 +44,13 @@ struct uart_softc *this; }; -# initfifo() - detect and size FIFOs +# getsig() - get line and modem signals. +# XXX needs explanation. +METHOD int getsig { + struct uart_softc *this; +}; + +# initfifo() - detect and size FIFOs. # This method is called after the UART is reset and is responsible for finding # out the size of the transmitter and receiver FIFOs. The method is allowed # to reprogram the UART, but should not permanently disrupt console or debug @@ -53,7 +59,7 @@ struct uart_softc *this; }; -# ipend() - query UART for pending interrupts +# ipend() - query UART for pending interrupts. # When an interrupt is signalled, the handler will call this method to find # out which of the interrupt sources needs attention. The handler will use # this information to dispatch service routines that deal with each of the @@ -74,6 +80,13 @@ struct uart_softc *this; }; +# receive() - move data from the receive FIFO to the receive buffer. +# This method is called to move received data to the receive buffer and +# additionally should make sure the receive interrupt should be cleared. +METHOD int receive { + struct uart_softc *this; +}; + # reset() - program an initial state. # It is unspecified at this time what the initial state is and/or whether it # should be a state that is common for all supported UARTs. This method is @@ -81,3 +94,27 @@ METHOD int reset { struct uart_softc *this; }; + +# rxflush() - flush the receiver and receiver FIFO. +# XXX needs explanation. +METHOD int rxflush { + struct uart_softc *this; +}; + +# shutdown() - disable or inactivate UART. +# XXX needs explanation. +METHOD int shutdown { + struct uart_softc *this; +}; + +# transmit() - move data from the transmit buffer to the transmit FIFO. +# XXX needs explanation. +METHOD int transmit { + struct uart_softc *this; +}; + +# txflush() - flush the transmitter and transmitter FIFO. +# XXX needs explanation. +METHOD int txflush { + struct uart_softc *this; +}; ==== //depot/projects/uart/dev/uart/uart_tty.c#2 (text+ko) ==== @@ -36,6 +36,7 @@ #include <machine/bus.h> #include <sys/bus.h> #include <sys/conf.h> +#include <sys/interrupt.h> #include <sys/kobj.h> #include <sys/tty.h> @@ -143,6 +144,15 @@ return (ENOTTY); } +void +uart_tty_intr(void *arg) +{ + struct uart_softc *sc = arg; + + if (sc->sc_leaving) + return; +} + int uart_tty_attach(struct uart_softc *sc) { @@ -158,6 +168,9 @@ tp->t_dev = sc->sc_si; + swi_add(&tty_ithd, uart_driver_name, uart_tty_intr, sc, SWI_TTY, + INTR_TYPE_TTY, &sc->sc_softih); + return (0); } @@ -165,6 +178,8 @@ uart_tty_detach(struct uart_softc *sc) { - /* XXX */ + ithread_remove_handler(sc->sc_softih); + destroy_dev(sc->sc_si); + /* ttyfree(sc->sc_tty); */ return (0); } ==== //depot/projects/uart/dev/uart/uart_tty.h#2 (text+ko) ==== @@ -33,5 +33,6 @@ int uart_tty_attach(struct uart_softc *); int uart_tty_detach(struct uart_softc *); +void uart_tty_intr(void *arg); #endif /* _DEV_UART_TTY_H_ */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200307061951.h66JpnAR022295>