sbcv2: add lots of comments as this is meant to be a porting reference
authorAlan Cox <alan@linux.intel.com>
Mon, 27 Aug 2018 23:22:06 +0000 (00:22 +0100)
committerAlan Cox <alan@linux.intel.com>
Mon, 27 Aug 2018 23:22:06 +0000 (00:22 +0100)
Kernel/platform-sbcv2/devices.c
Kernel/platform-sbcv2/devtty.c
Kernel/platform-sbcv2/main.c

index b91e2db..1aa1560 100644 (file)
@@ -9,6 +9,16 @@
 #include <printf.h>
 #include <devfd.h>
 
+/*
+ *     This table is the glue that holds all the kernel device driver
+ *     logic together. Each device driver provides methods for
+ *     open, close, read, write and ioctl, although it can opt to use
+ *     defaults as well.
+ *
+ *     The validdev function is the same for all platforms but has to live
+ *     in the same file as the table. Just paste it into each.
+ */
+
 struct devsw dev_tab[] =  /* The device driver switch table */
 {
   /* 0: /dev/hd                Hard disc block devices */
index d2d53c1..3674e01 100644 (file)
@@ -5,6 +5,12 @@
  *     - Hardware flow control
  *     - Support for abuse of 16x50 as interrupt controller
  *     - Support for timer hack
+ *
+ *     This file implements the serial ports for the platform. Fuzix implements
+ *     a reasonable subset of the System 5 termios. Certain things that are
+ *     rarely relevant like XCASE, delay fills and parity are left to the
+ *     driver if desired.
+ *
  */
 
 #include <kernel.h>
@@ -14,6 +20,7 @@
 #include <devtty.h>
 #include <propio2.h>
 
+/* The 16x50 UART I/O ports */
 __sfr __at 0x68 uart_tx;
 __sfr __at 0x68 uart_rx;
 __sfr __at 0x68 uart_ls;
@@ -26,21 +33,35 @@ __sfr __at 0x6D uart_lsr;
 __sfr __at 0x6E uart_msr;
 __sfr __at 0x6F uart_scr;
 
+/*
+ *     One buffer for each tty
+ */
 static char tbuf1[TTYSIZ];
 static char tbuf2[TTYSIZ];
 
-/* Updated early in boot to 0,2,1 if PropIO present */
-uint8_t ttymap[NUM_DEV_TTY + 1] = {
-       0, 1, 2
-};
-
+/*
+ *     One entry per tty. The 0th entry is never used as tty minor 0 is
+ *     special (/dev/tty) and it's cheaper to waste a few bytes that keep
+ *     doing subtractions.
+ */
 struct s_queue ttyinq[NUM_DEV_TTY + 1] = {     /* ttyinq[0] is never used */
        {NULL, NULL, NULL, 0, 0, 0},
        {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
        {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
 };
 
-/* Write to system console */
+
+/* Updated early in boot to 0,2,1 if PropIO present. This table works both
+   ways purely because of the possible mappings. If that changes we'll need
+   a forward and backward table. Most platforms have a fixed idea of the console
+   so don't need this remapping layer */
+uint8_t ttymap[NUM_DEV_TTY + 1] = {
+       0, 1, 2
+};
+
+/* Write to system console. This is the backend to all the kernel messages,
+   kprintf(), panic() etc. */
+
 void kputchar(char c)
 {
        if (c == '\n')
@@ -48,6 +69,26 @@ void kputchar(char c)
        tty_putc(1, c);
 }
 
+/*
+ *     See if the given tty is able to transmit data without blocking. This
+ *     may be done by checking the hardware, or if there is a software
+ *     transmit queue by checking the queue is full.
+ *
+ *     There are three possible returns
+ *     TTY_READY_NOW means fire away
+ *     TTY_READY_SOON means we will spin trying until pre-empted. As the
+ *             8bit processors are slow relative to baud rates it's often
+ *             more efficient to do this
+ *     TTY_READY_LATER means we will give up the CPU. This is best if the
+ *             baud rate is low, the link is blocked by flow control signals
+ *             or the CPU is fast.
+ *
+ *     If TTY_READY_LATER is returned then the kernel will also call
+ *     tty_sleeping(minor) before sleeping on the tty so that the driver
+ *     can turn on or off tx complete interrupts.
+ *
+ *     A video display that never blocks will just return TTY_READY_NOW
+ */
 uint8_t tty_writeready(uint8_t minor)
 {
        minor;
@@ -57,6 +98,15 @@ uint8_t tty_writeready(uint8_t minor)
        return prop_tty_writeready();
 }
 
+/*
+ *     Write a character to a tty. This is the normal user space path for
+ *     each outbound byte. It gets called in the normal tty flow, but may
+ *     also be called from an interrupt to echo characters even if the
+ *     tty is busy. This one reason to implement a small transmit queue.
+ *
+ *     If the character echo doesn't fit just drop it. It should pretty much
+ *     never occur and there is nothing else to do.
+ */
 void tty_putc(uint8_t minor, unsigned char c)
 {
        minor;
@@ -66,6 +116,10 @@ void tty_putc(uint8_t minor, unsigned char c)
                prop_tty_write(c);
 }
 
+/*
+ *     16x50 conversion betwen a Bxxxx speed rate (see tty.h) and the values
+ *     to stuff into the chip.
+ */
 static uint16_t clocks[] = {
        12,             /* Not a real rate */
        2304,
@@ -85,6 +139,15 @@ static uint16_t clocks[] = {
        1
 };
 
+/*
+ *     This function is called whenever the terminal interface is opened
+ *     or the settings changed. It is responsible for making the requested
+ *     changes to the port if possible. Strictly speaking it should write
+ *     back anything that cannot be implemented to the state it selected.
+ *
+ *     That needs tidying up in many platforms and we also need a proper way
+ *     to say 'this port is fixed config' before making it so.
+ */
 void tty_setup(uint8_t minor)
 {
        uint8_t d;
@@ -92,7 +155,7 @@ void tty_setup(uint8_t minor)
        struct termios *t = &ttydata[minor].termios;
        if (ttymap[minor] == 1) {
                /* 16x50. Can actually be configured */
-               d = 0x80;       /* DLAB */
+               d = 0x80;       /* DLAB (so we can write the speed) */
                d |= (t->c_cflag & CSIZE) >> 4;
                if(t->c_cflag & CSTOPB)
                        d |= 0x04;
@@ -112,11 +175,20 @@ void tty_setup(uint8_t minor)
        }
 }
 
+/*
+ *     This function is called when the kernel is about to sleep on a tty.
+ *     We don't care about this.
+ */
 void tty_sleeping(uint8_t minor)
 {
        minor;
 }
 
+/*
+ *     Return 1 if the carrier on the terminal is raised. If the port has
+ *     no carrier signal always return 1. It is used to block a port on open
+ *     until carrier.
+ */
 int tty_carrier(uint8_t minor)
 {
         if (ttymap[minor] == 1)
@@ -124,18 +196,40 @@ int tty_carrier(uint8_t minor)
        return 1;
 }
 
+/*
+ *     When the input queue is part drained this method is called from the
+ *     kernel so that hardware flow control signals can be updated.
+ */
 void tty_data_consumed(uint8_t minor)
 {
 }
 
+/*
+ *     Our platform specific code so we have a function to call to poll the
+ *     serial ports for activity.
+ */
 void tty_poll(void)
 {      
+       uint8_t msr;
+       uint8_t minor = ttymap[1];      /* UART minor number */
+
        /* Should be IRQ driven but we might not be so poll anyway if
           pending. IRQs are off here so this is safe */
        if (uart_lsr & 0x01)
-               tty_inproc(ttymap[1], uart_rx);
+               tty_inproc(minor, uart_rx);
+       msr = uart_msr;
        /* If we have a 10MHz clock wired to DSR then do timer interrupts */
-       if (timermsr && (uart_msr & 0x40))
+       if (timermsr && (msr & 0x04))
                timer_interrupt();
+       /* DCD changed - tell the kernel so it can hangup or open ports */
+       if (msr & 0x08) {
+               if (msr & 0x80)
+                       tty_carrier_raise(minor);
+               else
+                       tty_carrier_drop(minor);
+       }
+       /* TODO: CTS/RTS */
+
+       /* Now as the PropIO driver to poll its input */
        prop_tty_poll(ttymap[2]);
 }
index 4de9430..8945a0b 100644 (file)
@@ -10,28 +10,49 @@ uint16_t ramtop = PROGTOP;
 uint16_t swap_dev = 0xFFFF;
 uint8_t timermsr = 0;
 
-/* On idle we spin checking for the terminals. Gives us more responsiveness
-   for the polled ports */
+/*
+ *     This routine is called continually when the machine has nothing else
+ *     it needs to execute. On a machine with entirely interrupt driven
+ *     hardware this could just halt for interrupt.
+ *
+ *     The SBCv2 has no interrupts so we must call sync_clock(), and as the
+ *     PropIO tty is not interrupt driven we also poll the ttys. This gives
+ *     us a nice interactive feel when the machine is idle, even if a polled
+ *     tty can otherwise suck.
+ */
 void platform_idle(void)
 {
+       /* Disable interrupts so we don't accidentally process a polled tty
+          and interrupt call at once and make a mess */
        irqflags_t irq = di();
        sync_clock();
        tty_poll();
+       /* Restore prior state. */
        irqrestore(irq);
 }
 
+/*
+ *     This routine is called from the interrupt handler code to process
+ *     interrupts. All of the nasty stuff (register saving, bank switching,
+ *     reti instructions) is dealt with for you.
+ *
+ *     Most platforms would read something to identify the interrupt source
+ *     but in our case the only possible source is the serial uart.
+ */
 void platform_interrupt(void)
 {
        tty_poll();
 }
 
+/* This points to the last buffer in the disk buffers. There must be at least
+   four buffers to avoid deadlocks. */
 struct blkbuf *bufpool_end = bufpool + NBUFS;
 
 /*
  *     We pack discard into the memory image is if it were just normal
  *     code but place it at the end after the buffers. When we finish up
  *     booting we turn everything from the buffer pool to common into
- *     buffers.
+ *     buffers. This blows away the _DISCARD segment.
  */
 void platform_discard(void)
 {
@@ -62,7 +83,12 @@ static uint8_t oldticks;
 
 static uint8_t re_enter;
 
-void sync_clock_read(void)
+/*
+ *     Hardware specific logic to get the seconds. We really ought to enhance
+ *     this to check minutes as well just in case something gets stuck for
+ *     ages.
+ */
+static void sync_clock_read(void)
 {
        uint8_t s;
        oldticks = newticks;
@@ -71,6 +97,21 @@ void sync_clock_read(void)
        newticks = s;
 }
 
+/*
+ *     The OS core will invoke this routine when idle (via platform_idle) but
+ *     also after a system call and in certain other spots to ensure the clock
+ *     is roughly valid. It may be called from interrupts, without interrupts
+ *     or even recursively so it must protect itself using the framework
+ *     below.
+ *
+ *     Having worked out how much time has passed in 1/10ths of a second it
+ *     performs that may timer_interrupt events in order to advance the clock.
+ *     The core kernel logic ensures that we won't do anything silly from a
+ *     jump forward of many seconds.
+ *
+ *     We also choose to poll the ttys here so the user has some chance of
+ *     getting control back on a messed up process.
+ */
 void sync_clock(void)
 {
        if (!timermsr) {
@@ -86,6 +127,7 @@ void sync_clock(void)
                                while(tmp--) {
                                        timer_interrupt();
                                }
+                               /* Poll the PropIOv2 */
                                platform_interrupt();
                        }
                        re_enter--;
@@ -94,6 +136,11 @@ void sync_clock(void)
        }
 }
 
+/*
+ *     This method is called if the kernel has changed the system clock. We
+ *     don't work out how much work we need to do by using it as a reference
+ *     so we don't care.
+ */
 void update_sync_clock(void)
 {
 }