github.com/jspc/eggos@v0.5.1-0.20221028160421-556c75c878a5/console/console.go (about)

     1  package console
     2  
     3  import (
     4  	"io"
     5  	"sync"
     6  	"syscall"
     7  	"unsafe"
     8  
     9  	"github.com/jspc/eggos/drivers/cga"
    10  	"github.com/jspc/eggos/drivers/kbd"
    11  	"github.com/jspc/eggos/drivers/uart"
    12  )
    13  
    14  const (
    15  	CON_BUFLEN = 128
    16  )
    17  
    18  type console struct {
    19  	rawch chan byte
    20  
    21  	buf     [CON_BUFLEN]byte
    22  	r, w, e uint
    23  
    24  	tios syscall.Termios
    25  
    26  	mutex  sync.Mutex
    27  	notify *sync.Cond
    28  
    29  	wmutex sync.Mutex
    30  }
    31  
    32  var (
    33  	con *console
    34  )
    35  
    36  func newConsole() *console {
    37  	c := &console{
    38  		tios: syscall.Termios{
    39  			Lflag: syscall.ICANON | syscall.ECHO,
    40  		},
    41  	}
    42  	c.notify = sync.NewCond(&c.mutex)
    43  	return c
    44  }
    45  
    46  func ctrl(c byte) byte {
    47  	return c - '@'
    48  }
    49  
    50  //go:nosplit
    51  func (c *console) intr(ch byte) {
    52  	c.handleInput(ch)
    53  }
    54  
    55  func (c *console) rawmode() bool {
    56  	return c.tios.Lflag&syscall.ICANON == 0
    57  }
    58  
    59  func (c *console) handleRaw(ch byte) {
    60  	if c.e-c.r >= CON_BUFLEN {
    61  		return
    62  	}
    63  	idx := c.e % CON_BUFLEN
    64  	c.e++
    65  	c.buf[idx] = byte(ch)
    66  	c.w = c.e
    67  	c.notify.Broadcast()
    68  }
    69  
    70  func (c *console) handleInput(ch byte) {
    71  	c.mutex.Lock()
    72  	defer c.mutex.Unlock()
    73  	if c.rawmode() {
    74  		c.handleRaw(ch)
    75  		return
    76  	}
    77  
    78  	switch ch {
    79  	case 0x7f, ctrl('H'):
    80  		if c.e > c.w {
    81  			c.e--
    82  			c.putc(0x7f)
    83  		}
    84  		return
    85  	}
    86  
    87  	if c.e-c.r >= CON_BUFLEN {
    88  		return
    89  	}
    90  	if ch == '\r' {
    91  		ch = '\n'
    92  	}
    93  	idx := c.e % CON_BUFLEN
    94  	c.e++
    95  	c.buf[idx] = byte(ch)
    96  	c.putc(ch)
    97  	if ch == '\n' || c.e == c.r+CON_BUFLEN {
    98  		c.w = c.e
    99  		c.notify.Broadcast()
   100  	}
   101  }
   102  
   103  func (c *console) loop() {
   104  	for ch := range c.rawch {
   105  		c.handleInput(ch)
   106  	}
   107  }
   108  
   109  func (c *console) putc(ch byte) {
   110  	uart.WriteByte(ch)
   111  	cga.WriteByte(ch)
   112  }
   113  
   114  func (c *console) read(p []byte) int {
   115  	c.mutex.Lock()
   116  	defer c.mutex.Unlock()
   117  
   118  	i := 0
   119  	for i < len(p) {
   120  		for c.r == c.w {
   121  			c.notify.Wait()
   122  		}
   123  		idx := c.r
   124  		c.r++
   125  		ch := c.buf[idx%CON_BUFLEN]
   126  		p[i] = byte(ch)
   127  		i++
   128  		if ch == '\n' || c.rawmode() {
   129  			break
   130  		}
   131  	}
   132  	return i
   133  }
   134  
   135  func (c *console) Read(p []byte) (int, error) {
   136  	return c.read(p), nil
   137  }
   138  
   139  func (c *console) Write(p []byte) (int, error) {
   140  	c.wmutex.Lock()
   141  	defer c.wmutex.Unlock()
   142  	for _, ch := range p {
   143  		c.putc(ch)
   144  	}
   145  	return len(p), nil
   146  }
   147  
   148  func (c *console) Ioctl(op, arg uintptr) error {
   149  	switch op {
   150  	case syscall.TIOCGWINSZ:
   151  		w := (*winSize)(unsafe.Pointer(arg))
   152  		w.row = 25
   153  		w.col = 80
   154  		return nil
   155  	case syscall.TCGETS:
   156  		tios := (*syscall.Termios)(unsafe.Pointer(arg))
   157  		*tios = c.tios
   158  		return nil
   159  	case syscall.TCSETS:
   160  		tios := (*syscall.Termios)(unsafe.Pointer(arg))
   161  		c.tios = *tios
   162  		return nil
   163  
   164  	default:
   165  		return syscall.EINVAL
   166  	}
   167  }
   168  
   169  type winSize struct {
   170  	row, col       uint16
   171  	xpixel, ypixel uint16
   172  }
   173  
   174  var cononce sync.Once
   175  
   176  func Console() io.ReadWriter {
   177  	cononce.Do(func() {
   178  		con = newConsole()
   179  	})
   180  	return con
   181  }
   182  
   183  func Init() {
   184  	cononce.Do(func() {
   185  		con = newConsole()
   186  	})
   187  	uart.OnInput(con.intr)
   188  	kbd.OnInput(con.intr)
   189  }