#include <kernel/kernel.h> #include <kernel/driver.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> /*! * \ingroup drivers * \defgroup pci PCI bus * @{ */ typedef struct pci_cfg_t pci_cfg_t; struct pci_cfg_t { /* normal header stuff */ word vendor_id; word device_id; word command; word status; byte revision_id; byte interface; byte sub_class; byte base_class; byte cache_line_size; byte latency_timer; byte header_type; byte bist; /* device info */ byte bus; byte dev; byte func; byte irq; /* base registers */ dword base[6]; dword size[6]; word subsys_vendor; word subsys; }; typedef struct confadd { byte reg:8; byte func:3; byte dev:5; byte bus:8; byte rsvd:7; byte enable:1; } confadd; const struct { byte base_class; byte sub_class; byte interface; const wchar_t* name; } classes[] = { { 0x00, 0x00, 0x00, L"Undefined" }, { 0x00, 0x01, 0x00, L"VGA" }, { 0x01, 0x00, 0x00, L"SCSI" }, { 0x01, 0x01, 0x00, L"IDE" }, { 0x01, 0x02, 0x00, L"Floppy" }, { 0x01, 0x03, 0x00, L"IPI" }, { 0x01, 0x04, 0x00, L"RAID" }, { 0x01, 0x80, 0x00, L"Other" }, { 0x02, 0x00, 0x00, L"Ethernet" }, { 0x02, 0x01, 0x00, L"Token Ring" }, { 0x02, 0x02, 0x00, L"FDDI" }, { 0x02, 0x03, 0x00, L"ATM" }, { 0x02, 0x04, 0x00, L"ISDN" }, { 0x02, 0x80, 0x00, L"Other" }, { 0x03, 0x00, 0x00, L"VGA" }, { 0x03, 0x00, 0x01, L"VGA+8514" }, { 0x03, 0x01, 0x00, L"XGA" }, { 0x03, 0x02, 0x00, L"3D" }, { 0x03, 0x80, 0x00, L"Other" }, { 0x04, 0x00, 0x00, L"Video" }, { 0x04, 0x01, 0x00, L"Audio" }, { 0x04, 0x02, 0x00, L"Telephony" }, { 0x04, 0x80, 0x00, L"Other" }, { 0x05, 0x00, 0x00, L"RAM" }, { 0x05, 0x01, 0x00, L"Flash" }, { 0x05, 0x80, 0x00, L"Other" }, { 0x06, 0x00, 0x00, L"PCI to HOST" }, { 0x06, 0x01, 0x00, L"PCI to ISA" }, { 0x06, 0x02, 0x00, L"PCI to EISA" }, { 0x06, 0x03, 0x00, L"PCI to MCA" }, { 0x06, 0x04, 0x00, L"PCI to PCI" }, { 0x06, 0x04, 0x01, L"PCI to PCI (Subtractive Decode)" }, { 0x06, 0x05, 0x00, L"PCI to PCMCIA" }, { 0x06, 0x06, 0x00, L"PCI to NuBUS" }, { 0x06, 0x07, 0x00, L"PCI to Cardbus" }, { 0x06, 0x08, 0x00, L"PCI to RACEway" }, { 0x06, 0x09, 0x00, L"PCI to PCI" }, { 0x06, 0x0A, 0x00, L"PCI to InfiBand" }, { 0x06, 0x80, 0x00, L"PCI to Other" }, { 0x07, 0x00, 0x00, L"Serial" }, { 0x07, 0x00, 0x01, L"Serial - 16450" }, { 0x07, 0x00, 0x02, L"Serial - 16550" }, { 0x07, 0x00, 0x03, L"Serial - 16650" }, { 0x07, 0x00, 0x04, L"Serial - 16750" }, { 0x07, 0x00, 0x05, L"Serial - 16850" }, { 0x07, 0x00, 0x06, L"Serial - 16950" }, { 0x07, 0x01, 0x00, L"Parallel" }, { 0x07, 0x01, 0x01, L"Parallel - BiDir" }, { 0x07, 0x01, 0x02, L"Parallel - ECP" }, { 0x07, 0x01, 0x03, L"Parallel - IEEE1284" }, { 0x07, 0x01, 0xFE, L"Parallel - IEEE1284 Target" }, { 0x07, 0x02, 0x00, L"Multiport Serial" }, { 0x07, 0x03, 0x00, L"Hayes Compatible Modem" }, { 0x07, 0x03, 0x01, L"Hayes Compatible Modem, 16450" }, { 0x07, 0x03, 0x02, L"Hayes Compatible Modem, 16550" }, { 0x07, 0x03, 0x03, L"Hayes Compatible Modem, 16650" }, { 0x07, 0x03, 0x04, L"Hayes Compatible Modem, 16750" }, { 0x07, 0x80, 0x00, L"Other" }, { 0x08, 0x00, 0x00, L"PIC" }, { 0x08, 0x00, 0x01, L"ISA PIC" }, { 0x08, 0x00, 0x02, L"EISA PIC" }, { 0x08, 0x00, 0x10, L"I/O APIC" }, { 0x08, 0x00, 0x20, L"I/O(x) APIC" }, { 0x08, 0x01, 0x00, L"DMA" }, { 0x08, 0x01, 0x01, L"ISA DMA" }, { 0x08, 0x01, 0x02, L"EISA DMA" }, { 0x08, 0x02, 0x00, L"Timer" }, { 0x08, 0x02, 0x01, L"ISA Timer" }, { 0x08, 0x02, 0x02, L"EISA Timer" }, { 0x08, 0x03, 0x00, L"RTC" }, { 0x08, 0x03, 0x00, L"ISA RTC" }, { 0x08, 0x03, 0x00, L"Hot-Plug" }, { 0x08, 0x80, 0x00, L"Other" }, { 0x09, 0x00, 0x00, L"Keyboard" }, { 0x09, 0x01, 0x00, L"Pen" }, { 0x09, 0x02, 0x00, L"Mouse" }, { 0x09, 0x03, 0x00, L"Scanner" }, { 0x09, 0x04, 0x00, L"Game Port" }, { 0x09, 0x80, 0x00, L"Other" }, { 0x0a, 0x00, 0x00, L"Generic" }, { 0x0a, 0x80, 0x00, L"Other" }, { 0x0b, 0x00, 0x00, L"386" }, { 0x0b, 0x01, 0x00, L"486" }, { 0x0b, 0x02, 0x00, L"Pentium" }, { 0x0b, 0x03, 0x00, L"PentiumPro" }, { 0x0b, 0x10, 0x00, L"DEC Alpha" }, { 0x0b, 0x20, 0x00, L"PowerPC" }, { 0x0b, 0x30, 0x00, L"MIPS" }, { 0x0b, 0x40, 0x00, L"Coprocessor" }, { 0x0b, 0x80, 0x00, L"Other" }, { 0x0c, 0x00, 0x00, L"FireWire" }, { 0x0c, 0x00, 0x10, L"OHCI FireWire" }, { 0x0c, 0x01, 0x00, L"Access.bus" }, { 0x0c, 0x02, 0x00, L"SSA" }, { 0x0c, 0x03, 0x00, L"USB (UHCI)" }, { 0x0c, 0x03, 0x10, L"USB (OHCI)" }, { 0x0c, 0x03, 0x80, L"USB" }, { 0x0c, 0x03, 0xFE, L"USB Device" }, { 0x0c, 0x04, 0x00, L"Fiber" }, { 0x0c, 0x05, 0x00, L"SMBus Controller" }, { 0x0c, 0x06, 0x00, L"InfiniBand" }, { 0x0c, 0x80, 0x00, L"Other" }, { 0x0d, 0x00, 0x00, L"iRDA" }, { 0x0d, 0x01, 0x00, L"Consumer IR" }, { 0x0d, 0x10, 0x00, L"RF" }, { 0x0d, 0x80, 0x00, L"Other" }, { 0x0e, 0x00, 0x00, L"I2O" }, { 0x0e, 0x80, 0x00, L"Other" }, { 0x0f, 0x01, 0x00, L"TV" }, { 0x0f, 0x02, 0x00, L"Audio" }, { 0x0f, 0x03, 0x00, L"Voice" }, { 0x0f, 0x04, 0x00, L"Data" }, { 0x0f, 0x80, 0x00, L"Other" }, { 0x10, 0x00, 0x00, L"Network" }, { 0x10, 0x10, 0x00, L"Entertainment" }, { 0x10, 0x80, 0x00, L"Other" }, { 0x11, 0x00, 0x00, L"DPIO Modules" }, { 0x11, 0x01, 0x00, L"Performance Counters" }, { 0x11, 0x10, 0x00, L"Comm Sync, Time+Frequency Measurement" }, { 0x11, 0x80, 0x00, L"Other" }, }; dword pciRead(int bus, int dev, int func, int reg, int bytes) { word base; union { confadd c; dword n; } u; u.n = 0; u.c.enable = 1; u.c.rsvd = 0; u.c.bus = bus; u.c.dev = dev; u.c.func = func; u.c.reg = reg & 0xFC; out32(0xCF8, u.n); base = 0xCFC + (reg & 0x03); switch(bytes){ case 1: return in(base); case 2: return in16(base); case 4: return in32(base); default: return 0; } } void pciWrite(int bus, int dev, int func, int reg, dword v, int bytes) { word base; union { confadd c; dword n; } u; u.n = 0; u.c.enable = 1; u.c.rsvd = 0; u.c.bus = bus; u.c.dev = dev; u.c.func = func; u.c.reg = reg & 0xFC; base = 0xCFC + (reg & 0x03); out32(0xCF8, u.n); switch(bytes){ case 1: out(base, (byte) v); break; case 2: out16(base, (word) v); break; case 4: out32(base, v); break; } } bool pciProbe(int bus, int dev, int func, pci_cfg_t *cfg) { dword *word = (dword *) cfg; dword v; int i; for(i=0;i<4;i++){ word[i] = pciRead(bus,dev,func,4*i,4); } if(cfg->vendor_id == 0xffff) return false; cfg->bus = bus; cfg->dev = dev; cfg->func = func; cfg->subsys_vendor = pciRead(bus, dev, func, 0x2c, 2); cfg->subsys = pciRead(bus, dev, func, 0x2e, 2); #if 0 wprintf(L"Device Info: /bus/pci/%d/%d/%d\n",bus,dev,func); wprintf(L" * Vendor: %X Device: %X Class/SubClass/Interface %X/%X/%X\n", cfg->vendor_id,cfg->device_id,cfg->base_class,cfg->sub_class,cfg->interface); wprintf(L" * Status: %X Command: %X BIST/Type/Lat/CLS: %X/%X/%X/%X\n", cfg->status, cfg->command, cfg->bist, cfg->header_type, cfg->latency_timer, cfg->cache_line_size); #endif switch(cfg->header_type & 0x7F){ case 0: /* normal device */ for(i=0;i<6;i++){ v = pciRead(bus,dev,func,i*4 + 0x10, 4); if(v) { int v2; pciWrite(bus,dev,func,i*4 + 0x10, 0xffffffff, 4); v2 = pciRead(bus,dev,func,i*4+0x10, 4) & 0xfffffff0; pciWrite(bus,dev,func,i*4 + 0x10, v, 4); v2 = 1 + ~v2; if(v & 1) { // printf(" * Base Register %d IO: %x (%x)\n",i,v&0xfff0,v2&0xffff); cfg->base[i] = v & 0xffff; cfg->size[i] = v2 & 0xffff; } else { // printf(" * Base Register %d MM: %x (%x)\n",i,v&0xfffffff0,v2); cfg->base[i] = v; cfg->size[i] = v2; } } else { cfg->base[i] = 0; cfg->size[i] = 0; } } v = pciRead(bus,dev,func,0x3c,1); cfg->irq = (v == 0xff ? 0 : v); //wprintf(L" * Interrupt Line: %X\n",cfg->irq); break; case 1: //wprintf(L" * PCI <-> PCI Bridge\n"); break; default: //wprintf(L" * Unknown Header Type\n"); } return true; } bool STDCALL INIT_CODE drvInit(driver_t *drv) { word bus, dev, func; device_t *pci; wchar_t name[20]; device_config_t* cfg; pci_cfg_t pcfg; int i, j; pci = hndAlloc(sizeof(device_t), NULL); pci->driver = drv; pci->req_first = pci->req_last = NULL; pci->request = NULL; devRegister(L"pci", pci, NULL); for (bus = 0; bus < 2/*255*/; bus++) { wprintf(L"PCI scan: %d%%\r", (bus * 100) / 255); for (dev = 0; dev < 32; dev++) { for (func = 0; func < 8; func++) { if (pciProbe(bus, dev, func, &pcfg)) { //swprintf(name, L"pci/%d/%d", bus, dev); wcscpy(name, L"pcidev"); cfg = hndAlloc(sizeof(device_config_t), NULL); cfg->parent = pci; cfg->vendor_id = pcfg.vendor_id; cfg->device_id = pcfg.device_id; cfg->subsystem = pcfg.subsys_vendor << 16 | pcfg.subsys; if (pcfg.irq) cfg->num_resources = 1; else cfg->num_resources = 0; for (i = 0; i < countof(pcfg.base); i++) if (pcfg.base[i]) cfg->num_resources++; cfg->resources = hndAlloc(sizeof(device_resource_t) * cfg->num_resources, NULL); for (i = 0, j = 0; i < countof(pcfg.base); i++) { if (pcfg.base[i]) { if ((pcfg.base[i] & 0xffff0000) == 0) { cfg->resources[j].cls = dresIo; cfg->resources[j].u.io.base = pcfg.base[i]; cfg->resources[j].u.io.length = pcfg.size[i]; } else { cfg->resources[j].cls = dresMemory; cfg->resources[j].u.memory.base = pcfg.base[i]; cfg->resources[j].u.memory.length = pcfg.size[i]; } j++; } } if (pcfg.irq) { cfg->resources[j].cls = dresIrq; cfg->resources[j].u.irq = pcfg.irq; } for (i = 0; i < countof(classes); i++) if (pcfg.base_class == classes[i].base_class && pcfg.sub_class == classes[i].sub_class && pcfg.interface == classes[i].interface) { _cputws(classes[i].name); putwchar(' '); break; } devRegister(name, NULL, cfg); } } } } wprintf(L"PCI: finished\n"); return true; } //@}