#pragma once // The device model: one list of everything attached, whatever bus found it. // // Before this there were two registries in kernel/drivers/registry.c -- one // array of PCI drivers, one of USB drivers -- and nothing that knew about both. // That is fine for binding drivers to hardware, which is all it was for, and // useless for the three things wanted next: // // *Enumeration.* "What is in this machine, and what claimed it" had no // answer that covered a USB disk and a PCI display at once. A device manager // is exactly that question, so it needs exactly this list. // // *Classes.* A compositor should ask for "the display" and not care whether // that is the VBE framebuffer or virtio-gpu. Without a class, every consumer // hardcodes a driver, and adding the second driver means editing the // consumers -- which is how a framebuffer address ends up hardcoded in three // places, as it once was here. // // *A boundary for drivers that are not compiled in.* Drivers the user // installs (see docs/drivers.md) have to attach somewhere that is not "an // array in registry.c", because that array is fixed at link time. // // The shape is deliberately the one NeXTSTEP/macOS uses rather than Linux's: // a device is an object with a class and an ops vtable, and consumers talk to // the class. It is *not* a device tree -- there is no parent/child nesting // here, only a flat list with a bus tag, because nothing yet needs to walk a // hierarchy and a tree that nobody traverses is just a harder list. #define DEV_NAMELEN 24 // Which bus found it. This is provenance, not capability -- what the device // *does* is its class. enum dev_bus { DEVBUS_PLATFORM = 0, // found by looking, not by enumerating: PS/2, VBE DEVBUS_PCI, DEVBUS_USB, DEVBUS_NBUS }; // What it is. A consumer asks for a class and gets whatever is bound. enum dev_class { DEVCLASS_OTHER = 0, DEVCLASS_DISPLAY, DEVCLASS_INPUT, DEVCLASS_STORAGE, DEVCLASS_NET, DEVCLASS_BUS, // a controller that finds more devices (xHCI, hubs) DEVCLASS_NCLASS }; struct device { char name[DEV_NAMELEN]; // "vga0", "xhci0" -- unique, human-readable char desc[48]; // free text for the enumerator enum dev_bus bus; enum dev_class class; // Where it came from, for the enumerator to print. Zero when not applicable. uint16 vendor, product; uint8 b, s, f; // PCI bus/slot/func const char *driver; // whatever claimed it, or 0 for unclaimed void *ops; // class-specific vtable; see display.h void *priv; // the driver's own state int in_use; }; #define NDEVICE 32 // Register a device. Returns it, or 0 if the table is full. The name is made // unique by appending an index, so a caller passes "vga" and gets "vga0". struct device *device_add(const char *base, enum dev_bus bus, enum dev_class class, const char *driver); // Set the human-readable description. Bounded copy into the fixed field, so // callers do not each need to know how long it is. void device_set_desc(struct device *, const char *); // Walk. Returns 0 past the end; index is dense over registered devices. struct device *device_at(int i); int device_count(void); // First device of a class with a bound driver, or 0. This is how a consumer // finds "the display" without naming a driver. struct device *device_find_class(enum dev_class class); const char *dev_bus_name(enum dev_bus); const char *dev_class_name(enum dev_class);