diff --git a/src/gopheros/device/driver.go b/src/gopheros/device/driver.go index 15a6316..4effec1 100644 --- a/src/gopheros/device/driver.go +++ b/src/gopheros/device/driver.go @@ -22,3 +22,68 @@ type Driver interface { // ProbeFn is a function that scans for the presence of a particular // piece of hardware and returns a driver for it. type ProbeFn func() Driver + +// DetectOrder specifies when each driver's probe function will be invoked +// by the hal package. +type DetectOrder int8 + +const ( + // DetectOrderEarly specifies that the driver's probe function should + // be executed at the beginning of the HW detection phase. It is used + // by some of the console and TTY device drivers. + DetectOrderEarly DetectOrder = -128 + + // DetectOrderBeforeACPI specifies that the driver's probe function + // should be executed before attempting any ACPI-based HW detection but + // after any drivers with DetectOrderEarly. + DetectOrderBeforeACPI = -127 + + // DetectOrderACPI specifies that the driver's probe function should + // be executed after parsing the ACPI tables. This is the default (zero + // value) for all drivers. + DetectOrderACPI = 0 + + // DetectOrderLast specifies that the driver's probe function should + // be executed at the end of the HW detection phase. + DetectOrderLast = 127 +) + +// DriverInfo is a driver-defined struct that is passed to calls to RegisterDriver. +type DriverInfo struct { + // Order specifies at which stage of the HW detection step should + // the probe function be invoked. + Order DetectOrder + + // Probe is a function that checks for the presence of a particular + // piece of hardware and returns back a driver for it. + Probe ProbeFn +} + +// DriverInfoList is a list of registered drivers that implements sort.Sort. +type DriverInfoList []*DriverInfo + +// Len returns the length of the driver info list. +func (l DriverInfoList) Len() int { return len(l) } + +// Swap exchanges 2 elements in the driver info list. +func (l DriverInfoList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + +// Less compares 2 elements of the driver info list. +func (l DriverInfoList) Less(i, j int) bool { return l[i].Order < l[j].Order } + +var ( + // registeredDrivers tracks the drivers registered via a call to + // RegisterDriver. + registeredDrivers DriverInfoList +) + +// RegisterDriver adds the supplied driver info to the list of registered +// drivers. The list can be retrieved by a call to DriverList(). +func RegisterDriver(info *DriverInfo) { + registeredDrivers = append(registeredDrivers, info) +} + +// DriverList returns the list of registered drivers. +func DriverList() DriverInfoList { + return registeredDrivers +} diff --git a/src/gopheros/device/driver_test.go b/src/gopheros/device/driver_test.go new file mode 100644 index 0000000..13f4a3f --- /dev/null +++ b/src/gopheros/device/driver_test.go @@ -0,0 +1,36 @@ +package device + +import ( + "sort" + "testing" +) + +func TestDriverInfoListSorting(t *testing.T) { + defer func() { + registeredDrivers = nil + }() + + origlist := []*DriverInfo{ + &DriverInfo{Order: DetectOrderACPI}, + &DriverInfo{Order: DetectOrderLast}, + &DriverInfo{Order: DetectOrderBeforeACPI}, + &DriverInfo{Order: DetectOrderEarly}, + } + + for _, drv := range origlist { + RegisterDriver(drv) + } + + registeredList := DriverList() + if exp, got := len(origlist), len(registeredList); got != exp { + t.Fatalf("expected DriverList() to return %d entries; got %d", exp, got) + } + + sort.Sort(registeredList) + expOrder := []int{3, 2, 0, 1} + for i, exp := range expOrder { + if registeredList[i] != origlist[exp] { + t.Errorf("expected sorted entry %d to be %v; got %v", i, registeredList[exp], origlist[i]) + } + } +} diff --git a/src/gopheros/device/tty/probe.go b/src/gopheros/device/tty/probe.go deleted file mode 100644 index ebc070a..0000000 --- a/src/gopheros/device/tty/probe.go +++ /dev/null @@ -1,11 +0,0 @@ -package tty - -import "gopheros/device" - -var ( - // ProbeFuncs is a slice of device probe functions - // that is used by the hal package to probe for TTY - // hardware. Each driver should use an init() block - // to append its probe function to this list. - ProbeFuncs []device.ProbeFn -) diff --git a/src/gopheros/device/tty/vt.go b/src/gopheros/device/tty/vt.go index 71b85bf..b9ea8ed 100644 --- a/src/gopheros/device/tty/vt.go +++ b/src/gopheros/device/tty/vt.go @@ -265,5 +265,8 @@ func probeForVT() device.Driver { } func init() { - ProbeFuncs = append(ProbeFuncs, probeForVT) + device.RegisterDriver(&device.DriverInfo{ + Order: device.DetectOrderEarly, + Probe: probeForVT, + }) } diff --git a/src/gopheros/device/video/console/device.go b/src/gopheros/device/video/console/device.go index 53c5506..7b98274 100644 --- a/src/gopheros/device/video/console/device.go +++ b/src/gopheros/device/video/console/device.go @@ -3,9 +3,18 @@ package console import ( "gopheros/device/video/console/font" "gopheros/device/video/console/logo" + "gopheros/kernel/cpu" + "gopheros/kernel/hal/multiboot" + "gopheros/kernel/mem/vmm" "image/color" ) +var ( + mapRegionFn = vmm.MapRegion + portWriteByteFn = cpu.PortWriteByte + getFramebufferInfoFn = multiboot.GetFramebufferInfo +) + // ScrollDir defines a scroll direction. type ScrollDir uint8 diff --git a/src/gopheros/device/video/console/probe.go b/src/gopheros/device/video/console/probe.go deleted file mode 100644 index 1937343..0000000 --- a/src/gopheros/device/video/console/probe.go +++ /dev/null @@ -1,19 +0,0 @@ -package console - -import ( - "gopheros/device" - "gopheros/kernel/cpu" - "gopheros/kernel/hal/multiboot" - "gopheros/kernel/mem/vmm" -) - -var ( - mapRegionFn = vmm.MapRegion - portWriteByteFn = cpu.PortWriteByte - getFramebufferInfoFn = multiboot.GetFramebufferInfo - - // ProbeFuncs is a slice of device probe functions that is used by - // the hal package to probe for console device hardware. Each driver - // should use an init() block to append its probe function to this list. - ProbeFuncs []device.ProbeFn -) diff --git a/src/gopheros/device/video/console/vesa_fb.go b/src/gopheros/device/video/console/vesa_fb.go index ec355cd..096170b 100644 --- a/src/gopheros/device/video/console/vesa_fb.go +++ b/src/gopheros/device/video/console/vesa_fb.go @@ -603,5 +603,8 @@ func probeForVesaFbConsole() device.Driver { } func init() { - ProbeFuncs = append(ProbeFuncs, probeForVesaFbConsole) + device.RegisterDriver(&device.DriverInfo{ + Order: device.DetectOrderEarly, + Probe: probeForVesaFbConsole, + }) } diff --git a/src/gopheros/device/video/console/vga_text.go b/src/gopheros/device/video/console/vga_text.go index eee6737..b763fc6 100644 --- a/src/gopheros/device/video/console/vga_text.go +++ b/src/gopheros/device/video/console/vga_text.go @@ -236,5 +236,8 @@ func probeForVgaTextConsole() device.Driver { } func init() { - ProbeFuncs = append(ProbeFuncs, probeForVgaTextConsole) + device.RegisterDriver(&device.DriverInfo{ + Order: device.DetectOrderEarly, + Probe: probeForVgaTextConsole, + }) } diff --git a/src/gopheros/kernel/hal/hal.go b/src/gopheros/kernel/hal/hal.go index a73eed5..713759f 100644 --- a/src/gopheros/kernel/hal/hal.go +++ b/src/gopheros/kernel/hal/hal.go @@ -9,12 +9,16 @@ import ( "gopheros/device/video/console/logo" "gopheros/kernel/hal/multiboot" "gopheros/kernel/kfmt" + "sort" ) // managedDevices contains the devices discovered by the HAL. type managedDevices struct { activeConsole console.Device activeTTY tty.Device + + // activeDrivers tracks all initialized device drivers. + activeDrivers []device.Driver } var ( @@ -30,70 +34,20 @@ func ActiveTTY() tty.Device { // DetectHardware probes for hardware devices and initializes the appropriate // drivers. func DetectHardware() { - consoles := probe(console.ProbeFuncs) - if len(consoles) != 0 { - devices.activeConsole = consoles[0].(console.Device) + // Get driver list and sort by detection priority + drivers := device.DriverList() + sort.Sort(drivers) - if logoSetter, ok := (devices.activeConsole).(console.LogoSetter); ok { - disableLogo := false - for k, v := range multiboot.GetBootCmdLine() { - if k == "consoleLogo" && v == "off" { - disableLogo = true - break - } - } - - if !disableLogo { - consW, consH := devices.activeConsole.Dimensions(console.Pixels) - logoSetter.SetLogo(logo.BestFit(consW, consH)) - } - } - - if fontSetter, ok := (devices.activeConsole).(console.FontSetter); ok { - consW, consH := devices.activeConsole.Dimensions(console.Pixels) - - // Check boot cmdline for a font request - var selFont *font.Font - for k, v := range multiboot.GetBootCmdLine() { - if k != "consoleFont" { - continue - } - - if selFont = font.FindByName(v); selFont != nil { - break - } - } - - if selFont == nil { - selFont = font.BestFit(consW, consH) - } - - fontSetter.SetFont(selFont) - } - } - - ttys := probe(tty.ProbeFuncs) - if len(ttys) != 0 { - devices.activeTTY = ttys[0].(tty.Device) - devices.activeTTY.AttachTo(devices.activeConsole) - kfmt.SetOutputSink(devices.activeTTY) - - // Sync terminal contents with console - devices.activeTTY.SetState(tty.StateActive) - } + probe(drivers) } -// probe executes the supplied hw probe functions and attempts to initialize -// each detected device. The function returns a list of device drivers that -// were successfully initialized. -func probe(hwProbeFns []device.ProbeFn) []device.Driver { - var ( - drivers []device.Driver - w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()} - ) +// probe executes the probe function for each driver and invokes +// onDriverInit for each successfully initialized driver. +func probe(driverInfoList device.DriverInfoList) { + var w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()} - for _, probeFn := range hwProbeFns { - drv := probeFn() + for _, info := range driverInfoList { + drv := info.Probe() if drv == nil { continue } @@ -109,8 +63,91 @@ func probe(hwProbeFns []device.ProbeFn) []device.Driver { } kfmt.Fprintf(&w, "initialized\n") - drivers = append(drivers, drv) + onDriverInit(info, drv) + devices.activeDrivers = append(devices.activeDrivers, drv) + } +} + +// onDriverInit is invoked by probe() whenever a piece of hardware is detected +// and successfully initialized. +func onDriverInit(info *device.DriverInfo, drv device.Driver) { + switch drvImpl := drv.(type) { + case console.Device: + onConsoleInit(drvImpl) + case tty.Device: + if devices.activeTTY != nil { + return + } + + devices.activeTTY = drvImpl + if devices.activeConsole != nil { + linkTTYToConsole() + } + } +} + +// onConsoleInit is invoked whenever a console is initialized. If this is the +// first found console it automatically becomes the active console. In +// addition, if the console supports fonts and/or logos this function ensures +// that they are loaded and attached to the console. Finally, if an active TTY +// device is present, it will be automatically linked to the first active +// console via a call to linkTTYToConsole. +func onConsoleInit(cons console.Device) { + if devices.activeConsole != nil { + return } - return drivers + devices.activeConsole = cons + + if logoSetter, ok := (devices.activeConsole).(console.LogoSetter); ok { + disableLogo := false + for k, v := range multiboot.GetBootCmdLine() { + if k == "consoleLogo" && v == "off" { + disableLogo = true + break + } + } + + if !disableLogo { + consW, consH := devices.activeConsole.Dimensions(console.Pixels) + logoSetter.SetLogo(logo.BestFit(consW, consH)) + } + } + + if fontSetter, ok := (devices.activeConsole).(console.FontSetter); ok { + consW, consH := devices.activeConsole.Dimensions(console.Pixels) + + // Check boot cmdline for a font request + var selFont *font.Font + for k, v := range multiboot.GetBootCmdLine() { + if k != "consoleFont" { + continue + } + + if selFont = font.FindByName(v); selFont != nil { + break + } + } + + if selFont == nil { + selFont = font.BestFit(consW, consH) + } + + fontSetter.SetFont(selFont) + } + + if devices.activeTTY != nil { + linkTTYToConsole() + } +} + +// linkTTYToConsole connects the active TTY device to the active console device +// and syncs their contents. +func linkTTYToConsole() { + devices.activeTTY.AttachTo(devices.activeConsole) + kfmt.SetOutputSink(devices.activeTTY) + + // Sync terminal contents with console + devices.activeTTY.SetState(tty.StateActive) + }