root/arch/x86/boot/tty.c
// SPDX-License-Identifier: GPL-2.0-only
/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *   Copyright 2009 Intel Corporation; author H. Peter Anvin
 *
 * ----------------------------------------------------------------------- */

/*
 * Very simple screen and serial I/O
 */

#include "boot.h"

int early_serial_base;

#define XMTRDY          0x20

#define TXR             0       /*  Transmit register (WRITE) */
#define LSR             5       /*  Line Status               */

/*
 * These functions are in .inittext so they can be used to signal
 * error during initialization.
 */

static void __section(".inittext") serial_putchar(int ch)
{
        unsigned timeout = 0xffff;

        while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
                cpu_relax();

        outb(ch, early_serial_base + TXR);
}

static void __section(".inittext") bios_putchar(int ch)
{
        struct biosregs ireg;

        initregs(&ireg);
        ireg.bx = 0x0007;
        ireg.cx = 0x0001;
        ireg.ah = 0x0e;
        ireg.al = ch;
        intcall(0x10, &ireg, NULL);
}

void __section(".inittext") putchar(int ch)
{
        if (ch == '\n')
                putchar('\r');  /* \n -> \r\n */

        bios_putchar(ch);

        if (early_serial_base != 0)
                serial_putchar(ch);
}

void __section(".inittext") puts(const char *str)
{
        while (*str)
                putchar(*str++);
}

/*
 * Read the CMOS clock through the BIOS, and return the
 * seconds in BCD.
 */

static u8 gettime(void)
{
        struct biosregs ireg, oreg;

        initregs(&ireg);
        ireg.ah = 0x02;
        intcall(0x1a, &ireg, &oreg);

        return oreg.dh;
}

/*
 * Read from the keyboard
 */
int getchar(void)
{
        struct biosregs ireg, oreg;

        initregs(&ireg);
        /* ireg.ah = 0x00; */
        intcall(0x16, &ireg, &oreg);

        return oreg.al;
}

static int kbd_pending(void)
{
        struct biosregs ireg, oreg;

        initregs(&ireg);
        ireg.ah = 0x01;
        intcall(0x16, &ireg, &oreg);

        return !(oreg.eflags & X86_EFLAGS_ZF);
}

void kbd_flush(void)
{
        for (;;) {
                if (!kbd_pending())
                        break;
                getchar();
        }
}

int getchar_timeout(void)
{
        int cnt = 30;
        int t0, t1;

        t0 = gettime();

        while (cnt) {
                if (kbd_pending())
                        return getchar();

                t1 = gettime();
                if (t0 != t1) {
                        cnt--;
                        t0 = t1;
                }
        }

        return 0;               /* Timeout! */
}