PERFORCE change 34133 for review
Marcel Moolenaar
marcel at FreeBSD.org
Sun Jul 6 12:51:53 PDT 2003
http://perforce.freebsd.org/chv.cgi?CH=34133
Change 34133 by marcel at 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_ */
More information about the p4-projects
mailing list