SchOS/include/pci.h
Nexoniarz d0f7acf9eb Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
2026-08-09 13:47:01 +02:00

110 lines
4.2 KiB
C

#pragma once
// PCI bus enumeration and driver binding.
//
// Until this existed, every driver in Schunix found its hardware by knowing
// where it was: IDE at 0x1F0 because IDE has always been at 0x1F0, the
// framebuffer wherever the BIOS's VBE call said. That works for exactly the
// devices that predate PCI and for nothing else. A NIC, an AHCI controller,
// a virtio device or a real GPU has no fixed address at all -- you find it by
// asking the bus what is plugged into it, which is what this does.
// ---------------------------------------------------------------------------
// Config space
// ---------------------------------------------------------------------------
#define PCI_CONFIG_ADDR 0xCF8
#define PCI_CONFIG_DATA 0xCFC
#define PCI_VENDOR_ID 0x00
#define PCI_DEVICE_ID 0x02
#define PCI_COMMAND 0x04
#define PCI_STATUS 0x06
#define PCI_REVISION 0x08
#define PCI_PROG_IF 0x09
#define PCI_SUBCLASS 0x0A
#define PCI_CLASS 0x0B
#define PCI_HEADER_TYPE 0x0E
#define PCI_BAR0 0x10
#define PCI_SECONDARY_BUS 0x19
#define PCI_INTERRUPT_LINE 0x3C
#define PCI_INTERRUPT_PIN 0x3D
// PCI_COMMAND bits a driver usually wants set before touching its device.
#define PCI_CMD_IO 0x0001
#define PCI_CMD_MEMORY 0x0002
#define PCI_CMD_BUS_MASTER 0x0004
// Base classes worth naming. The full list is long and mostly irrelevant here.
#define PCI_CLASS_STORAGE 0x01
#define PCI_CLASS_NETWORK 0x02
#define PCI_CLASS_DISPLAY 0x03
#define PCI_CLASS_MULTIMEDIA 0x04
#define PCI_CLASS_BRIDGE 0x06
#define PCI_CLASS_SERIAL_BUS 0x0C
#define PCI_STATUS_CAPLIST 0x0010 // device has a capability list
#define PCI_CAP_PTR 0x34 // offset of the first capability
#define PCI_CAP_MSI 0x05
#define PCI_CAP_MSIX 0x11
#define PCI_CAP_VENDOR 0x09 // virtio's configuration lives here
#define PCI_CAP_PCIE 0x10
#define PCI_ANY 0xFFFF // wildcard in a match table
#define NPCI 64 // devices we will remember
struct pci_bar {
uint64 base; // already masked; 0 means "no BAR here"
uint64 size; // determined by write-ones-and-read-back
int is_io; // I/O port range rather than memory
int is_64; // consumed the following BAR slot too
int prefetch;
};
struct pci_dev {
uint8 bus, slot, func;
uint16 vendor, device;
uint8 class, subclass, prog_if, revision;
uint8 irq; // interrupt line, 0xFF when unrouted
uint8 header_type;
struct pci_bar bar[6];
const char *driver; // name of whatever claimed it, or 0
};
// One row of a driver's match table. Terminate the table with a row whose
// vendor is 0.
struct pci_match {
uint16 vendor, device; // PCI_ANY to ignore
uint8 class, subclass; // used only when vendor is PCI_ANY
};
// Declared here rather than defs.h because both take a struct pci_dev, which
// only this header defines. See kernel/dev/device.c for the model they feed.
// device.h is included rather than forward-declaring `enum dev_class`, which
// is a compiler extension and warns.
#include "device.h"
enum dev_class pci_class_of(const struct pci_dev *);
void pci_describe(const struct pci_dev *, char *out, int cap);
struct pci_driver {
const char *name;
// Base for the device's name in /proc/devices, if the driver's own name
// makes a bad one. `e1000` plus an index reads as "e10000"; a NIC wants to
// be `eth0`. Optional -- null means use `name`.
const char *devname;
const struct pci_match *match;
// Class-specific vtable, published on the device when this driver claims
// it -- struct display_ops for a display, and so on. Optional; a driver
// with nothing to offer a consumer leaves it null.
//
// It lives on the driver rather than being set inside probe() because
// pci_bind() registers the device *after* probe returns, so a driver has
// nothing to attach it to at the point it would want to.
const void *ops;
// Return 0 to claim the device. A driver that returns non-zero leaves it
// unclaimed for anything else that matches.
int (*probe)(struct pci_dev *);
};