// 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/.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_.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"); }