Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
131 lines
5 KiB
C
131 lines
5 KiB
C
// The driver registry.
|
|
//
|
|
// One table, one line per driver. This is the same shape as
|
|
// kernel/sys/table.inc and kernel/dev/cdev.c's devsw: a single place that
|
|
// lists what exists, so a driver cannot be half-registered.
|
|
//
|
|
// It is a plain array rather than the linker-section trick a bigger kernel
|
|
// would use (a __attribute__((section(".drivers"))) entry per driver, walked
|
|
// between __start_ and __stop_ symbols). That approach lets a driver register
|
|
// itself by existing, which is genuinely nicer -- but it also means the list
|
|
// of what is in your kernel is not written down anywhere you can read, and
|
|
// a driver silently dropped by the linker looks exactly like one that was
|
|
// never written. At this size, being able to read the list wins.
|
|
//
|
|
// To add a driver: write kernel/drivers/<name>.c exporting a struct
|
|
// pci_driver, declare it here, and add it to the array and to the Makefile's
|
|
// KERN_DRIVERS.
|
|
|
|
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "core/param.h"
|
|
#include "core/spinlock.h"
|
|
#include "core/sleeplock.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/file.h"
|
|
#include "dev/pci.h"
|
|
|
|
#include "dev/usb.h"
|
|
#include "dev/device.h"
|
|
|
|
extern const struct pci_driver vga_pci_driver;
|
|
extern const struct pci_driver xhci_driver;
|
|
extern const struct pci_driver virtio_gpu_driver;
|
|
extern const struct pci_driver e1000_driver;
|
|
extern const struct pci_driver ac97_driver;
|
|
extern const struct pci_driver r8169_driver;
|
|
extern const struct pci_driver rtl8139_driver;
|
|
extern const struct pci_driver ahci_driver;
|
|
extern const struct pci_driver nvme_driver;
|
|
|
|
// Order matters here in one respect: virtio-gpu is offered the bus before the
|
|
// VBE framebuffer driver, so that on a machine with both, the device with real
|
|
// mode setting and an explicit flush wins the display class. vga_pci still
|
|
// claims the stdvga card underneath, which is what the boot splash and the
|
|
// text console are drawn on.
|
|
static const struct pci_driver *drivers[] = {
|
|
&virtio_gpu_driver,
|
|
&vga_pci_driver,
|
|
&e1000_driver,
|
|
&r8169_driver,
|
|
&rtl8139_driver,
|
|
&xhci_driver,
|
|
&ahci_driver,
|
|
&nvme_driver,
|
|
&ac97_driver,
|
|
};
|
|
|
|
void
|
|
pci_bind_all(void)
|
|
{
|
|
for (unsigned i = 0; i < sizeof(drivers) / sizeof(drivers[0]); i++)
|
|
pci_bind(drivers[i]);
|
|
}
|
|
|
|
// USB class drivers, same arrangement and for the same reason. These are not
|
|
// found on the PCI bus -- they are offered a device by the USB core once its
|
|
// interface descriptor has been read -- but a driver you cannot find in a
|
|
// list is a driver nobody knows is there, whichever bus it sits on.
|
|
//
|
|
// To add one: write kernel/drivers/usb_<name>.c exporting a struct
|
|
// usb_driver, declare it here, add it to the array and to KERN_DRIVERS.
|
|
|
|
extern const struct usb_driver usb_hid_driver;
|
|
extern const struct usb_driver usb_storage_driver;
|
|
extern const struct usb_driver usb_hub_driver;
|
|
|
|
static const struct usb_driver *usb_drivers[] = {
|
|
&usb_hid_driver,
|
|
&usb_storage_driver,
|
|
&usb_hub_driver,
|
|
};
|
|
|
|
int usb_offer(const struct usb_driver *, struct usb_dev *);
|
|
|
|
void
|
|
usb_bind_all(struct usb_dev *dev)
|
|
{
|
|
for (unsigned i = 0; i < sizeof(usb_drivers) / sizeof(usb_drivers[0]); i++)
|
|
if (usb_offer(usb_drivers[i], dev)) {
|
|
// Same as the PCI path: publish it in the bus-agnostic list so that
|
|
// "what is attached" has one answer covering every bus. Class comes from
|
|
// the driver that claimed it rather than the USB class code, because a
|
|
// driver knows what it made of the device and the descriptor only says
|
|
// what the device claims to be.
|
|
const char *n = usb_drivers[i]->name;
|
|
struct device *d = device_add(n, DEVBUS_USB, usb_drivers[i]->devclass, n);
|
|
if (d)
|
|
device_set_desc(d, usb_drivers[i]->name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Devices that are not enumerated by any bus -- they are simply known to be
|
|
// there on a PC, and the driver pokes fixed I/O ports to find out whether it
|
|
// is right. Listing them matters because a device manager that shows the
|
|
// graphics card but not the keyboard is not showing the machine.
|
|
//
|
|
// Registered here rather than inside each driver's init so that the whole set
|
|
// is visible in one place, which is the same reasoning as the driver arrays
|
|
// above.
|
|
void
|
|
platform_register_devices(uint64 fb_addr)
|
|
{
|
|
struct device *d;
|
|
|
|
// The framebuffer first, before anything that might want to draw. It is a
|
|
// driver call, and this file is where driver names belong -- main() used to
|
|
// call vga_init() directly, which put one display driver's name in the
|
|
// kernel's startup path and would have needed editing to boot on a machine
|
|
// whose framebuffer came from anywhere else.
|
|
vga_fb_init(fb_addr);
|
|
|
|
if ((d = device_add("kbd", DEVBUS_PLATFORM, DEVCLASS_INPUT, "ps2kbd")))
|
|
device_set_desc(d, "PS/2 keyboard");
|
|
if ((d = device_add("mouse", DEVBUS_PLATFORM, DEVCLASS_INPUT, "ps2mouse")))
|
|
device_set_desc(d, "PS/2 mouse");
|
|
if ((d = device_add("uart", DEVBUS_PLATFORM, DEVCLASS_OTHER, "uart8250")))
|
|
device_set_desc(d, "16550 serial port");
|
|
if ((d = device_add("ide", DEVBUS_PLATFORM, DEVCLASS_STORAGE, "ide")))
|
|
device_set_desc(d, "ATA disk controller");
|
|
}
|