From 73b51c851dfbaa34b54bd68dd5203719bc0decf8 Mon Sep 17 00:00:00 2001 From: Curle Date: Sun, 23 Aug 2020 00:50:10 +0100 Subject: [PATCH] Add basic PCI support. Currently can only enumerate the PCI bus with a basic process, and retrieve basic details on every valid device. --- chroma/inc/kernel/system/pci.h | 173 +++++++++++ chroma/system/pci.c | 513 +++++++++++++++++++++++++++++++++ 2 files changed, 686 insertions(+) create mode 100644 chroma/inc/kernel/system/pci.h create mode 100644 chroma/system/pci.c diff --git a/chroma/inc/kernel/system/pci.h b/chroma/inc/kernel/system/pci.h new file mode 100644 index 0000000..6c7c44e --- /dev/null +++ b/chroma/inc/kernel/system/pci.h @@ -0,0 +1,173 @@ +#pragma once +#include +#include + +/************************ + *** Team Kitty, 2020 *** + *** Chroma *** + ***********************/ + +/* This file contains all of the structures and definitions + * required to compatibly access the PCI bus, + * as well as set up new PCI devices, PCI bridges, and manipulate + * the connections of PCI lanes. + */ + +#define PCI_CONFIG_ADDRESS 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +extern pci_dev_t** pci_root_devices; +extern pci_entry_t* pci_map; + +int pci_init_early(); + +const char* pci_get_name(uint8_t devclass, uint8_t subclass, uint8_t progif); + +int pci_enumerate_devices(pci_dev_t* root); + +int pci_get_config(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t function, void** config); + +typedef struct __attribute__((packed)) { + uint8_t io_space : 1; // Device can respond to I/O access + uint8_t memory_space : 1; // Device can respond to memory access (device is MMIO mapped) + uint8_t bus_master : 1; // Device is Bus Master; can generate PCI addresses + uint8_t special_cycle : 1; // Device can monitor Special Cycle + uint8_t memory_write_and_invalidate : 1; // Device can generate Memory Write And Invalidate commands; else Memory Write must be used + uint8_t vga_palette : 1; // Device snoops the VGA palette on write; else is treated like a normal access + uint8_t parity_error_response : 1; // Device responds to Parity Errors by setting PERR#; else will set pci_status#parity_error and continue. + uint8_t _reserved : 1; // Hardwired to 0 + uint8_t serr : 1; // Enable SERR# driver; System ERRor + uint8_t fast_back_back : 1; // Device is allowed to generate fast back-to-back transactions to other agents. + uint8_t disable_interrupt : 1; // Disable assertion of INTx# signal; else enable. +} pci_command_t; + +typedef struct __attribute__((packed)) { + uint8_t _reserved : 3; // 3 bits hardwired to 0 + uint8_t interrupt : 1; // State of device's INTx# signal. If pci_command#disable_interrupt is 0 and this is 1, the signal will be asserted. + uint8_t capability_list : 1; // If set, device will implement New Capabilities list at 0x34 offset. + uint8_t freq_66_capable : 1; // Device can run at 66MHz. Else, device will run at 33MHz. + uint8_t _reserved1 : 1; // Reserved as of 3.0, Used in 2.1 as "Supports User Definable Features" + uint8_t fast_back_back : 1; // Device is allowed to accept fast back-to-back transactions from other agents. + uint8_t master_parity_error : 1; // Only set when PERR# is asserted by the Bus Master while pci_command#parity_error_response is set. + uint8_t devsel_timing : 2; // Read-Only; represents the slowest time a device will assert DEVSEL#. 00 = fast, 01 = medium, 11 = slow. + uint8_t target_signalled_abort : 1; // Target device terminated transaction via Target-Abort + uint8_t received_target_abort : 1; // Master's connection was terminated by Target-Abort + uint8_t received_master_abort : 1; // Master's connection was terminated by Master-Abort + uint8_t system_error_asserted : 1; // Device asserted SERR# + uint8_t parity_error : 1; // Device detected parity error. Parity error may not be handled. +} pci_status_t; + + +typedef struct __attribute__((packed)) { + uint16_t vendor_id; // 16 bit Vendor ID allocated by PCI-SIG. 0xFFFF is invalid. + uint16_t device_id; // 16 bit Device ID allocated by the vendor. + pci_command_t command; // 16 bit PCI_COMMAND register. + pci_status_t status; // 16 bit PCI_STATUS register. + uint8_t revision_id; // 8 bit register, revision identifier specified by vendor. + uint8_t progIF; // 8 bit register, identifies any programming interface the device may have. + uint8_t subclass; // 8 bit Subclass code; identifies the specific function of the device + uint8_t class_code; // 8 bit Class Code; identifies the function of the device + uint8_t cache_line_size; // 8 bit; specifies system cache line size in 32-bit blocks. Devices can limit this. Unsupported values are treated as 0. + uint8_t latency_timer; // 8 bit; specifies latency timer in (bus clock) units. + uint8_t header_type; // 8 bit; identifies the layout of the header and the type of device; 00 = device, 01 = pci-pci bridge, 11 = CardBus bridge. Multi-function defined by bit 7. + uint8_t bist; // 8 bit; status and control of a device's built-in self-test (BIST) +} pci_header_common_t; + +typedef struct __attribute__((packed)) { + // pci_header_common_t first, then.. + uint32_t bar[6]; // 6 x 32 bit Base Address Registers (BARs) + uint32_t cis_pointer; // Points to Card Information Structure for PCI devices that share silicon with CardBus + uint16_t subsystem_vendor_id; + uint16_t subsystem_id; + uint32_t expansion_bar; // Points to the base of an Expansion ROM. + uint8_t capabilities; // The pointer generated by pci_status#capabilities_list + uint8_t _reserved[3]; // 24 bit reserved at top end of register. + uint32_t _reserved2; + uint8_t interrupt_line; // Specifies the PIC pin that INTx# is connected to. Can be 0-15 because x86 PICs have 16 IRQs. 0xFF is no connection. + uint8_t interrupt; // Specifies the interrupt pin the device uses. 0x1 is INTA#, 0x2 is INTB#, 0x3 is INTC#, 0x4 is INTD#, 0x0 is no interrupt. + uint8_t min_grant; // Specifies the length of the burst period, in quarter-microsecond units + uint8_t max_latency; // Specifies how often the device accesses the PCI bus - in quarter-microseconds +} pci_header_device_t; + +typedef struct __attribute__((packed)) { + // pci_header_common_t first + uint32_t bar[2]; + uint8_t pri_bus; // Primary Bus Number. + uint8_t sec_bus; // Secondary Bus Number. + uint8_t sub_bus; // Subordinate Bus Number. + uint8_t sec_latency_timer; // Secondary Latency Timer. + uint8_t io_base; // IO Base is 24 bits. This is lower 8. + uint8_t io_limit; // IO Limit is 24 bits. This is lower 8. + pci_status_t sec_status; // Secondary Status. + uint16_t mem_base; + uint16_t mem_limit; + uint16_t mem_base_prefetch; // Prefetchable Memory Base is 48 bits. This is lower 16. + uint16_t mem_limit_prefetch; // Prefetchable Memory Limit is 48 bits. This is lower 16. + uint32_t mem_base_prefetch_upper; // Prefetchable Memory Base is 48 bits. This is upper 32. + uint32_t mem_limit_prefetch_upper; // Prefetchable Memory Limit is 48 bits. This is upper 32. + uint16_t io_base_upper; // IO Base is 24 bits. This is upper 16. + uint16_t io_limit_upper; // IO Limit is 24 bits. This is upper 16. + uint8_t capabilities; // Pointer generated by pci_status#capabilities_list + uint8_t _reserved[3]; // 24 reserved bits. + uint32_t expansion_bar; // Base of Expansion ROM. + uint8_t interrupt_line; // Specifies the PIC pin that INTx# is connected to. Can be 0-15 because x86 PICs have 16 IRQs. 0xFF is no connection. + uint8_t interrupt; // Specifies the interrupt pin the device uses. 0x1 is INTA#, 0x2 is INTB#, 0x3 is INTC#, 0x4 is INTD#, 0x0 is no interrupt. + uint16_t bridge_control; +} pci_header_bridge_t; + + +typedef struct { + uint16_t segment; + uint8_t bus; + uint8_t slot; + uint8_t function; +} pci_address_t; + +typedef struct { + uint8_t present : 1; + uint8_t mmio : 1; + + union { + size_t addr; + uint16_t port; + }; + size_t length; +} pci_bar_t; + +typedef struct pci_dev{ + + struct pci_dev* parent; // Parent of the device (for PCI hubs / splitters) + + uint16_t device_id; + uint16_t vendor_id; + + uint8_t devclass; + uint8_t subclass; + + uint8_t progif; + + pci_address_t address; + + pci_bar_t bars[6]; + + int irq; // The IRQ of the device if already handled + + // The headers! + volatile pci_header_common_t* header; + + union { // The device can only be one of these at a time, but they both form part of the config space. + volatile pci_header_bridge_t* bridge; + volatile pci_header_device_t* device; + }; + + struct pci_dev** children; // If this device is a hub, it has children... + + // acpi_node_t acpi; +} pci_dev_t; + +typedef struct { + pci_address_t key; + pci_dev_t* value; +} pci_entry_t; + + diff --git a/chroma/system/pci.c b/chroma/system/pci.c new file mode 100644 index 0000000..b2359a7 --- /dev/null +++ b/chroma/system/pci.c @@ -0,0 +1,513 @@ +#include + +/************************ + *** Team Kitty, 2020 *** + *** Chroma *** + ***********************/ + +/* This file contains functions for accessing the PCI bus, + * and devices contained wherein. + * + * It allows you to query the bus, as well as communicate with individual devices. + * + */ + +pci_dev_t** pci_root_devices = NULL; +pci_entry_t* pci_map = NULL; + + +void PCIEnumerate() { + + uint8_t bus = 0, device = 0, function = 0; + + uint32_t register; + + uint16_t device_id, vendor_id; + + uint8_t class_code, subclass_code; + + SerialPrintf("Started PCI Enumeration."); + + SerialPrintf("\nPCI Scan result:"); + for (bus = 0; bus <= 255; bus++) { + for (device = 0; device <= 31; device++) { + for(function = 0; function <= 7; function++) { + uint32_t temp = PCIReadConfig(bus, device, 0, 0xC); // Read BIST/Header/Latency/Cache Line register + uint8_t header = (uint8_t) ((temp & 0x00FF0000) >> 24); // Header is lower byte of upper word, so mask it off and shift it down + uint8_t multifunction_bit = header & 0x80; // The multifunction bit is the highest bit of the header + + temp = PCIReadConfig(bus, device, function, 0); // Read the Vendor/Device ID register + uint16_t vendor_id = (uint16_t) temp & 0x0000FFFF; // Vendor ID is bottom word + uint16_t device_id = (uint16_t) temp >> 16; // Device ID is top word + + temp = PCIReadConfig(bus, device, function, 8); // Read the Device Info register + uint8_t device_class = (uint16_t) temp >> 24; // Device class is top byte, so shift them down + uint8_t device_subclass = (uint16_t) (temp >> 16) & 0x00FF; // Device subclass is same as header - lower byte of higher word. Shift down and mask just like before. + uint8_t device_progif = (uint16_t) temp & 0x0000FF00 >> 8; // Device Programming Interface is higher byte of lower word, so mask and shift + uint8_t device_revision = (uint16_t) temp & 0x000000FF; // Device revision is lower byte of whole double word. Just mask it. + + + /* 0xFFFF is not a valid Vendor ID. This serves as a sanity check. + * If this check is true, then nothing is logged and we continue for the next loop. + */ + if(vendor_id != 0xFFFF) { + SerialPrintf("\n\t%x:%x:\n\t\tVendor: %x\n\t\tDevice: %x", bus, device, vendor_id, device_id); + SerialPrintf("\n\t\tClass: %s\n\t\tDevice Type: %s\n\t\tRevision: %s", PCIGetClassName(device_class), PCIGetDeviceName_Subclass(device_class, device_subclass, device_progif), device_revision); + } + + /* If the PCI Device header tells us that this is not a multifunction device, + * and we've already scanned function 0, then there is nothing else to see. + * Therefore, stop this loop and move onto device++ + */ + if (multifunction_bit == 0) + break; + + } + + } + } + + +} + + + + +uint32_t PCIReadConfig(uint8_t bus, uint8_t slot, uint8_t function, uint8_t offset) { + uint32_t address; + uint32_t busLong = (uint32_t) bus; + uint32_t slotLong = (uint32_t) slot; + uint32_t functionLong = (uint32_t) function; + + /* --------------------------------------------------------------- + * | 31 | 30 ... 24 | 23 ... 16 | 15 ... 11 | 10 ... 8 | 7 ... 0 | + * --------------------------------------------------------------- + * | EN | RESERVED | BUS NUM | DEVICE NO | FUNC NO | REG OFF | + * --------------------------------------------------------------- + * + * NOTE: REG OFF *must* have 0 last two bits (ie. & 0xFC) + */ + + address = (uint32_t) (( busLong << 16 ) | ( slotLong << 11 ) | + ( functionLong << 8 ) | ( offset & 0xFC) | ((uint32_t)0x80000000)); + + WritePort(0xCF8, address, 4); + + + return ReadPort(0xCFC, 4); +} + +const char* PCIGetDeviceName_Subclass(uint8_t devclass, uint8_t subclass, uint8_t progif) { + switch(devclass) { + + case 0x00: { + switch(subclass) { + case 0x00: return "Non-VGA-Compatible device"; + case 0x01: return "VGA-Compatible device"; + default: return "Unknown Unclassified"; + } + } + + case 0x01: { + switch(subclass) { + case 0x00: return "SCSI Bus Controller"; + case 0x01: { + switch(progif) { + case 0x00: return "ISA Compatibility Mode-only IDE Controller"; + case 0x05: return "PCI Native Mode-only IDE Controller"; + case 0x0A: return "ISA Compatibility Mode IDE Controller (supports PCI Native Mode)"; + case 0x0F: return "PCI Native Mode IDE Controller (supports ISA Compatibility Mode)"; + case 0x80: return "ISA Compatibilty Mode-only IDE Controller (supports Bus Mastering)"; + case 0x85: return "PCI Native Mode-only IDE Controller (supports Bus Mastering)"; + case 0x8A: return "ISA Compatibility Mode IDE Controller (supports PCI Native Mode & Bus Mastering)"; + case 0x8F: return "PCI Native Mode IDE Controller (supports ISA Compatibiliy Mode & Bus Mastering)"; + default: return "IDE Controller"; + } + } + case 0x02: return "Floppy Disk Controller"; + case 0x03: return "IPI Bus Controller"; + case 0x04: return "RAID Controller"; + case 0x05: { + switch(progif) { + case 0x20: return "Single-DMA ATA Controller"; + case 0x30: return "Chained-DMA ATA Controller"; + default: return "ATA Controller"; + } + } + + + case 0x06: { + switch(progif) { + case 0x00: return "Vendor-Specific Interface SATA Controller"; + case 0x01: return "AHCI 1.0 SATA Controller"; + case 0x02: return "Serial Storage Bus SATA Controller"; + default: return "Serial ATA Controller"; + } + } + case 0x07: return "Serial Attached SCSI (SAS)"; + + case 0x08:{ + switch(progif) { + case 0x01: return "NVMHCI Memory Controller"; + case 0x02: return "NVMe Memory Controller"; + default: return "Non-Volatile Memory Controller"; + + } + } + + case 0x80: return "Other"; + default: "Unknown Mass Storage Controller"; + } + } + + case 0x02: { + switch(subclass) { + case 0x00: return "Ethernet Controller"; + case 0x01: return "Token Ring Controller"; + case 0x02: return "FDDI Controller"; + case 0x03: return "ATM Controller"; + case 0x04: return "ISDN Controller"; + case 0x05: return "WorldFip Controller"; + case 0x06: return "PICMG 2.14 Multi Computing"; + case 0x07: return "Infiniband Controller"; + case 0x08: return "Fabric Controller"; + case 0x80: return "Other"; + default: "Unknown Network Controller"; + } + } + + case 0x03: { + switch(subclass) { + case 0x00: { + switch(progif) { + case 0x00: return "VGA Controller"; + case 0x01: return "8514 VGA Controller"; + default: return "VGA Compatible Controller"; + } + } + + case 0x01: return "XGA Controller"; + case 0x02: return "3D Controller (Not VGA-Compatible)"; + case 0x80: return "Other"; + default: "Unknown Display Controller"; + } + } + + case 0x04: { + switch(subclass) { + case 0x00: return "Multimedia Video Controller"; + case 0x01: return "Multimedia Audio Controller"; + case 0x02: return "Computer Telephony Device"; + case 0x03: return "Audio Device"; + case 0x80: return "Other"; + default: return "Unknown Multimedia Controller"; + } + } + + case 0x05: { + switch(subclass) { + case 0x00: return "RAM Controller"; + case 0x01: return "Flash Controller"; + case 0x80: return "Other"; + default: return "Unknown Memory Controller"; + } + } + + case 0x06: { + switch(subclass) { + case 0x00: return "Host Bridge"; + case 0x01: return "ISA Bridge"; + case 0x02: return "EISA Bridge"; + case 0x03: return "MCA Bridge"; + case 0x04: + case 0x09: + return "PCI-to-PCI Bridge"; + case 0x05: return "PCMCIA Bridge"; + case 0x06: return "NuBus Bridge"; + case 0x07: return "CardBus Bridge"; + case 0x08: return "RACEway Bridge"; + case 0x0A: return "InfiniBand-to-PCI Host Bridge"; + case 0x80: return "Other"; + default: return "Unknown Bridge Device"; + } + } + + case 0x07: { + switch(subclass) { + + case 0x00: { + switch(progif) { + case 0x00: return "Serial Controller <8250>"; + case 0x01: return "Serial controller <16450>"; + case 0x02: return "Serial controller <16550>"; + case 0x03: return "Serial controller <16650>"; + case 0x04: return "Serial controller <16750>"; + case 0x05: return "Serial controller <16850>"; + case 0x06: return "Serial controller <16950>"; + default: return "Serial Controller"; + + } + } + + case 0x01: { + switch(progif) { + case 0x00: return "Standard Parallel Port"; + case 0x01: return "Bi-Directional Parallel Port"; + case 0x02: return "ECP 1.X Compliant Parallel Port"; + case 0x03: return "IEEE 1284 Parallel Controller"; + case 0x04: return "IEE 1284 Parallel Target"; + default: return "Parallel Controller"; + } + } + case 0x02: return "Multiport Serial Controller"; + + case 0x03: { + switch(progif) { + case 0x00: return "Generic Modem"; + case 0x01: return "Hayes 16450 Compatible Modem"; + case 0x02: return "Hayes 16550 Compatible Modem"; + case 0x03: return "Hayes 16650 Compatible Modem"; + case 0x04: return "Hayes 16750 Compatible Modem"; + default: return "Modem"; + } + } + + case 0x04: return "IEEE 488.1/2 (GPIB) Controller"; + case 0x05: return "Smart Card"; + case 0x80: return "Other"; + default: return "Unknown Simple Comms Controller"; + } + } + + case 0x08: { + switch(subclass) { + case 0x00: { + switch(progif) { + case 0x00: return "Generic 8259-Compatible PIC"; + case 0x01: return "ISA-Compatible PIC"; + case 0x02: return "EISA-Compatible PIC"; + case 0x03: return "I/O APIC Interrupt Controller"; + case 0x04: return "I/O(x) APIC Interrupt Controller"; + default: "PIC"; + } + } + + case 0x01: { + switch(progif) { + case 0x00: return "Generic 8237-Compatible DMA Controller"; + case 0x01: return "ISA-Compatible DMA Controller"; + case 0x02: return "EISA-Compatible DMA Controller"; + default: "DMA Controller"; + } + } + + case 0x02: { + switch(progif) { + case 0x00: return "Generic 8254-Compatible Timer"; + case 0x01: return "ISA-Compatible Timer"; + case 0x02: return "EISA-Compatible Timer"; + case 0x03: return "HPET Timer"; + default: "Timer"; + } + } + + case 0x03: { + switch(progif) { + case 0x00: return "Generic RTC Controller"; + case 0x01: return "ISA-Compatible RTC Controller"; + default: "RTC Controller"; + } + } + + case 0x04: return "PCI Hot-Plug Controller"; + case 0x05: return "SD Host Controller"; + case 0x06: return "IOMMU"; + case 0x80: return "Other"; + + default: return "Unknown Base System Peripheral"; + } + } + + case 0x09: { + switch(subclass) { + case 0x00: return "Keyboard Controller"; + case 0x01: return "Digitiser Pen"; + case 0x02: return "Mouse Controller"; + case 0x03: return "Scanner Controller"; + + case 0x04: { + switch(progif) { + case 0x00: return "Generic Gameport Controller"; + case 0x10: return "Extended Gameport Controller"; + default: return "Gameport Controller"; + } + } + + case 0x80: return "Other"; + + default: return "Unknown Input Device Controller"; + } + } + + case 0x0A: { + switch(subclass) { + case 0x00: return "Generic Docking Station"; + case 0x80: return "Other"; + default: return "Unknown Docking Station"; + } + } + + case 0x0B: { + switch(subclass) { + case 0x00: return "386 Processor"; + case 0x01: return "486 Processor"; + case 0x02: return "Pentium Processor"; + case 0x03: return "Pentium Pro Processor"; + case 0x10: return "Alpha Processor"; + case 0x20: return "PowerPC Processor"; + case 0x30: return "MIPS Processor"; + case 0x40: return "Co-Processor"; + case 0x80: return "Other"; + default: return "Unknown Processor"; + } + } + + case 0x0C: { + switch(subclass) { + case 0x00: { + switch(progif) { + case 0x00: return "Generic Firewire Controller"; + case 0x10: return "OHCI Firewire Controller"; + default: return "FireWire (IEEE 1394) Controller"; + } + } + + case 0x01: return "ACCESS Bus"; + case 0x02: return "SSA"; + + case 0x03: { + switch(progif) { + case 0x00: return "UHCI USB Controller"; + case 0x10: return "OHCI USB Controller"; + case 0x20: return "EHCI USB (2.0) Controller"; + case 0x30: return "XHCI USB (3.0) Controller"; + case 0x80: return "Unspecified USB Controller"; + case 0xFE: return "USB Device (NOT a Controller)"; + default: return "USB Controller"; + } + } + + case 0x04: return "Fibre Channel"; + case 0x05: return "SMBus"; + case 0x06: return "InfiniBand"; + + case 0x07: { + switch(progif) { + case 0x00: return "SMIC IPMI Interface"; + case 0x01: return "Keyboard Controller-Style IPMI Interface"; + case 0x02: return "Block Transfer IPMI Interface"; + default: return "IPMI Interface"; + } + } + + case 0x08: return "SERCOS Interface (IEC 61491)"; + case 0x09: return "CANbus"; + case 0x80: return "Other"; + default: return "Unknown Serial Bus Controller"; + } + } + + case 0x0D: { + switch(subclass) { + case 0x00: return "IRDA Compatible Controller"; + case 0x01: return "Consumer IR Controller"; + case 0x10: return "RF Controller"; + case 0x11: return "Bluetooth Controller"; + case 0x12: return "Broadband Controller"; + case 0x20: return "Ethernet Controller (802.1a)"; + case 0x21: return "Ethernet Controller (802.1b)"; + case 0x80: return "Other"; + default: return "Unknown Wireless Controller"; + } + } + + case 0x0E: { + switch(subclass) { + case 0x00: return "I20"; + default: return "Unknown Intelligent Controller"; + } + } + + case 0x0F: { + switch(subclass) { + case 0x01: return "Satellite TV Controller"; + case 0x02: return "Satellite Audio Controller"; + case 0x03: return "Satellite Voice Controller"; + case 0x04: return "Satellite Data Controller"; + default: return "Unknown Satelllite Comms Controller"; + } + } + + case 0x10: { + switch(subclass) { + case 0x00: return "Network & Computing Codec"; + case 0x10: return "Entertainment Codec"; + case 0x80: return "Other Codec"; + default: return "Unknown Encryption Controller"; + } + } + + case 0x11: { + switch(subclass) { + case 0x00: return "DPIO Modules"; + case 0x01: return "Performance Counters"; + case 0x10: return "Communication Synchronizer"; + case 0x20: return "Signal Processing Management"; + case 0x80: return "Other"; + default: return "Unknown Signal Processing Controller"; + } + } + + case 0x12: return "Processing Accelerator"; + + case 0x13: return "Non-Essential Instrumentation"; + + case 0x14: + case 0x41: return "Reserved"; + + case 0x40: return "Co-Processor"; + + case 0xFF: return "Unassigned Class"; + + } +} + +const char* PCIGetClassName(uint8_t devclass) { + + switch(devclass) { + case 0x00: return "Unclassified"; + case 0x01: return "Mass Storage Controller"; + case 0x02: return "Network Controller"; + case 0x03: return "Display Controller"; + case 0x04: return "Multimedia Controller"; + case 0x05: return "Memory Controller"; + case 0x06: return "Bridge Device"; + case 0x07: return "Simple Communication Controller"; + case 0x08: return "Base System Peripheral"; + case 0x09: return "Input Device Controller"; + case 0x0A: return "Docking Station"; + case 0x0B: return "Processor"; + case 0x0C: return "Serial Bus Controller"; + case 0x0D: return "Wireless Controller"; + case 0x0E: return "Intelligent Controller"; + case 0x0F: return "Satellite Communication Controller"; + case 0x10: return "Encryption Controller"; + case 0x11: return "Signal Processing Controller"; + case 0x12: return "Processing Accelerator"; + case 0x13: return "Non-Essential Instrumentation"; + case 0x14: return "Reserved"; + case 0x40: return "Co-Processor"; + case 0x41: return "Reserved"; + case 0xFF: return "Unassigned Class"; + } +} \ No newline at end of file