DOS_Video/pci.c
2026-04-13 19:40:45 -05:00

307 lines
11 KiB
C

// pci.c -- PCI configuration space access for DOS/DJGPP
//
// Implements PCI mechanism 1 (CONFIG_ADDRESS at 0xCF8, CONFIG_DATA
// at 0xCFC). This is the standard PCI configuration access method
// supported by all PCI-capable chipsets.
//
// How mechanism 1 works:
// 1. Write a 32-bit address to port 0xCF8 with bit 31 set (enable),
// bus/dev/func/register fields encoded in bits 23:0
// 2. Read or write the 32-bit data at port 0xCFC
// 3. For sub-dword access (8/16-bit), read the full dword and
// mask/shift, or write with a read-modify-write
//
// Detection: write 0x80000000 to 0xCF8 and read back. If the value
// matches, mechanism 1 is present. This works because bit 31 is the
// enable bit -- on non-PCI systems, port 0xCF8 is either absent
// (reads back 0xFF) or belongs to a different device.
#include "pci.h"
#include <pc.h>
// PCI configuration mechanism 1 I/O ports
#define PCI_CONFIG_ADDR 0x0CF8
#define PCI_CONFIG_DATA 0x0CFC
// ============================================================
// Prototypes
// ============================================================
uint32_t pciBuildAddress(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg);
bool pciDetect(void);
int32_t pciEnumerate(PciEnumCallbackT cb, void *userData);
bool pciFindDevice(uint16_t vendorId, uint16_t deviceId, PciDeviceT *dev);
bool pciFindDeviceList(const uint16_t *idPairs, PciDeviceT *dev, int32_t *matchIdx);
uint8_t pciRead8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg);
uint16_t pciRead16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg);
uint32_t pciRead32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg);
void pciWrite8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint8_t val);
void pciWrite16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint16_t val);
void pciWrite32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t val);
// ============================================================
// pciBuildAddress
// ============================================================
//
// Constructs a PCI configuration space address for mechanism 1.
// Format: [31]=enable, [23:16]=bus, [15:11]=device, [10:8]=function,
// [7:2]=register (dword-aligned), [1:0]=0
uint32_t pciBuildAddress(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) {
return 0x80000000
| ((uint32_t)bus << 16)
| ((uint32_t)dev << 11)
| ((uint32_t)func << 8)
| ((uint32_t)reg & 0xFC);
}
// ============================================================
// pciDetect
// ============================================================
//
// Checks for PCI mechanism 1 by writing the enable bit to the
// CONFIG_ADDRESS port and reading it back. Saves and restores
// the original port value to avoid disturbing any in-progress
// PCI transaction.
bool pciDetect(void) {
uint32_t saved = inportl(PCI_CONFIG_ADDR);
outportl(PCI_CONFIG_ADDR, 0x80000000);
uint32_t readBack = inportl(PCI_CONFIG_ADDR);
outportl(PCI_CONFIG_ADDR, saved);
return (readBack == 0x80000000);
}
// ============================================================
// pciEnumerate
// ============================================================
//
// Scans all bus/device/function combinations for present devices.
// A device is present if its vendor ID is not 0xFFFF. Multi-function
// devices are detected by checking bit 7 of the header type register
// on function 0; single-function devices only probe function 0.
int32_t pciEnumerate(PciEnumCallbackT cb, void *userData) {
int32_t count = 0;
for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) {
for (int32_t dev = 0; dev < PCI_MAX_DEV; dev++) {
uint16_t vendor0 = pciRead16(bus, dev, 0, PCI_VENDOR_ID);
if (vendor0 == 0xFFFF) {
continue;
}
// Check if multi-function device
uint8_t headerType = pciRead8(bus, dev, 0, PCI_HEADER_TYPE);
int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1;
for (int32_t func = 0; func < maxFunc; func++) {
uint16_t vendorId = pciRead16(bus, dev, func, PCI_VENDOR_ID);
if (vendorId == 0xFFFF) {
continue;
}
PciDeviceT device;
device.bus = bus;
device.dev = dev;
device.func = func;
device.vendorId = vendorId;
device.deviceId = pciRead16(bus, dev, func, PCI_DEVICE_ID);
device.revision = pciRead8(bus, dev, func, PCI_REVISION_ID);
device.baseClass = pciRead8(bus, dev, func, PCI_BASE_CLASS);
device.subClass = pciRead8(bus, dev, func, PCI_SUBCLASS);
for (int32_t i = 0; i < 6; i++) {
device.bar[i] = pciRead32(bus, dev, func, PCI_BAR0 + i * 4);
}
count++;
if (cb && cb(&device, userData)) {
return count;
}
}
}
}
return count;
}
// ============================================================
// pciFindDevice
// ============================================================
bool pciFindDevice(uint16_t vendorId, uint16_t deviceId, PciDeviceT *dev) {
for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) {
for (int32_t d = 0; d < PCI_MAX_DEV; d++) {
uint16_t vendor0 = pciRead16(bus, d, 0, PCI_VENDOR_ID);
if (vendor0 == 0xFFFF) {
continue;
}
uint8_t headerType = pciRead8(bus, d, 0, PCI_HEADER_TYPE);
int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1;
for (int32_t func = 0; func < maxFunc; func++) {
uint16_t vid = pciRead16(bus, d, func, PCI_VENDOR_ID);
uint16_t did = pciRead16(bus, d, func, PCI_DEVICE_ID);
if (vid == vendorId && did == deviceId) {
dev->bus = bus;
dev->dev = d;
dev->func = func;
dev->vendorId = vid;
dev->deviceId = did;
dev->revision = pciRead8(bus, d, func, PCI_REVISION_ID);
dev->baseClass = pciRead8(bus, d, func, PCI_BASE_CLASS);
dev->subClass = pciRead8(bus, d, func, PCI_SUBCLASS);
for (int32_t i = 0; i < 6; i++) {
dev->bar[i] = pciRead32(bus, d, func, PCI_BAR0 + i * 4);
}
return true;
}
}
}
}
return false;
}
// ============================================================
// pciFindDeviceList
// ============================================================
//
// Searches for the first PCI device matching any vendor/device pair
// in the given list. The list is an array of uint16_t pairs:
// { vendor1, device1, vendor2, device2, ..., 0, 0 }
// On match, fills dev and sets matchIdx to the pair index (0-based).
bool pciFindDeviceList(const uint16_t *idPairs, PciDeviceT *dev, int32_t *matchIdx) {
for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) {
for (int32_t d = 0; d < PCI_MAX_DEV; d++) {
uint16_t vendor0 = pciRead16(bus, d, 0, PCI_VENDOR_ID);
if (vendor0 == 0xFFFF) {
continue;
}
uint8_t headerType = pciRead8(bus, d, 0, PCI_HEADER_TYPE);
int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1;
for (int32_t func = 0; func < maxFunc; func++) {
uint16_t vid = pciRead16(bus, d, func, PCI_VENDOR_ID);
uint16_t did = pciRead16(bus, d, func, PCI_DEVICE_ID);
if (vid == 0xFFFF) {
continue;
}
for (int32_t idx = 0; idPairs[idx * 2] != 0; idx++) {
if (vid == idPairs[idx * 2] && did == idPairs[idx * 2 + 1]) {
dev->bus = bus;
dev->dev = d;
dev->func = func;
dev->vendorId = vid;
dev->deviceId = did;
dev->revision = pciRead8(bus, d, func, PCI_REVISION_ID);
dev->baseClass = pciRead8(bus, d, func, PCI_BASE_CLASS);
dev->subClass = pciRead8(bus, d, func, PCI_SUBCLASS);
for (int32_t i = 0; i < 6; i++) {
dev->bar[i] = pciRead32(bus, d, func, PCI_BAR0 + i * 4);
}
if (matchIdx) {
*matchIdx = idx;
}
return true;
}
}
}
}
}
return false;
}
// ============================================================
// pciRead8
// ============================================================
uint8_t pciRead8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
uint32_t dword = inportl(PCI_CONFIG_DATA);
return (dword >> ((reg & 3) * 8)) & 0xFF;
}
// ============================================================
// pciRead16
// ============================================================
uint16_t pciRead16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
uint32_t dword = inportl(PCI_CONFIG_DATA);
return (dword >> ((reg & 2) * 8)) & 0xFFFF;
}
// ============================================================
// pciRead32
// ============================================================
uint32_t pciRead32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
return inportl(PCI_CONFIG_DATA);
}
// ============================================================
// pciWrite8
// ============================================================
void pciWrite8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint8_t val) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
uint32_t dword = inportl(PCI_CONFIG_DATA);
int32_t shift = (reg & 3) * 8;
dword = (dword & ~(0xFF << shift)) | ((uint32_t)val << shift);
outportl(PCI_CONFIG_DATA, dword);
}
// ============================================================
// pciWrite16
// ============================================================
void pciWrite16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint16_t val) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
uint32_t dword = inportl(PCI_CONFIG_DATA);
int32_t shift = (reg & 2) * 8;
dword = (dword & ~(0xFFFF << shift)) | ((uint32_t)val << shift);
outportl(PCI_CONFIG_DATA, dword);
}
// ============================================================
// pciWrite32
// ============================================================
void pciWrite32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t val) {
outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg));
outportl(PCI_CONFIG_DATA, val);
}