1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Merge pull request #47 from achilleasa/order-based-driver-probing

Order based driver detection/loading
This commit is contained in:
Achilleas Anagnostopoulos 2017-07-18 08:37:58 +01:00 committed by GitHub
commit 4b25971cef
9 changed files with 221 additions and 95 deletions

View File

@ -22,3 +22,68 @@ type Driver interface {
// ProbeFn is a function that scans for the presence of a particular // ProbeFn is a function that scans for the presence of a particular
// piece of hardware and returns a driver for it. // piece of hardware and returns a driver for it.
type ProbeFn func() Driver 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
}

View File

@ -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])
}
}
}

View File

@ -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
)

View File

@ -265,5 +265,8 @@ func probeForVT() device.Driver {
} }
func init() { func init() {
ProbeFuncs = append(ProbeFuncs, probeForVT) device.RegisterDriver(&device.DriverInfo{
Order: device.DetectOrderEarly,
Probe: probeForVT,
})
} }

View File

@ -3,9 +3,18 @@ package console
import ( import (
"gopheros/device/video/console/font" "gopheros/device/video/console/font"
"gopheros/device/video/console/logo" "gopheros/device/video/console/logo"
"gopheros/kernel/cpu"
"gopheros/kernel/hal/multiboot"
"gopheros/kernel/mem/vmm"
"image/color" "image/color"
) )
var (
mapRegionFn = vmm.MapRegion
portWriteByteFn = cpu.PortWriteByte
getFramebufferInfoFn = multiboot.GetFramebufferInfo
)
// ScrollDir defines a scroll direction. // ScrollDir defines a scroll direction.
type ScrollDir uint8 type ScrollDir uint8

View File

@ -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
)

View File

@ -603,5 +603,8 @@ func probeForVesaFbConsole() device.Driver {
} }
func init() { func init() {
ProbeFuncs = append(ProbeFuncs, probeForVesaFbConsole) device.RegisterDriver(&device.DriverInfo{
Order: device.DetectOrderEarly,
Probe: probeForVesaFbConsole,
})
} }

View File

@ -236,5 +236,8 @@ func probeForVgaTextConsole() device.Driver {
} }
func init() { func init() {
ProbeFuncs = append(ProbeFuncs, probeForVgaTextConsole) device.RegisterDriver(&device.DriverInfo{
Order: device.DetectOrderEarly,
Probe: probeForVgaTextConsole,
})
} }

View File

@ -9,12 +9,16 @@ import (
"gopheros/device/video/console/logo" "gopheros/device/video/console/logo"
"gopheros/kernel/hal/multiboot" "gopheros/kernel/hal/multiboot"
"gopheros/kernel/kfmt" "gopheros/kernel/kfmt"
"sort"
) )
// managedDevices contains the devices discovered by the HAL. // managedDevices contains the devices discovered by the HAL.
type managedDevices struct { type managedDevices struct {
activeConsole console.Device activeConsole console.Device
activeTTY tty.Device activeTTY tty.Device
// activeDrivers tracks all initialized device drivers.
activeDrivers []device.Driver
} }
var ( var (
@ -30,70 +34,20 @@ func ActiveTTY() tty.Device {
// DetectHardware probes for hardware devices and initializes the appropriate // DetectHardware probes for hardware devices and initializes the appropriate
// drivers. // drivers.
func DetectHardware() { func DetectHardware() {
consoles := probe(console.ProbeFuncs) // Get driver list and sort by detection priority
if len(consoles) != 0 { drivers := device.DriverList()
devices.activeConsole = consoles[0].(console.Device) sort.Sort(drivers)
if logoSetter, ok := (devices.activeConsole).(console.LogoSetter); ok { probe(drivers)
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 executes the supplied hw probe functions and attempts to initialize // probe executes the probe function for each driver and invokes
// each detected device. The function returns a list of device drivers that // onDriverInit for each successfully initialized driver.
// were successfully initialized. func probe(driverInfoList device.DriverInfoList) {
func probe(hwProbeFns []device.ProbeFn) []device.Driver { var w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()}
var (
drivers []device.Driver
w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()}
)
for _, probeFn := range hwProbeFns { for _, info := range driverInfoList {
drv := probeFn() drv := info.Probe()
if drv == nil { if drv == nil {
continue continue
} }
@ -109,8 +63,91 @@ func probe(hwProbeFns []device.ProbeFn) []device.Driver {
} }
kfmt.Fprintf(&w, "initialized\n") 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)
} }