Backport more old code
This commit is contained in:
@@ -46,7 +46,9 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \
|
||||
$(O)/sys/amd64/syscall_s.o \
|
||||
$(O)/sys/amd64/syscall.o \
|
||||
$(O)/sys/amd64/binfmt_elf.o \
|
||||
$(O)/sys/amd64/hw/con.o
|
||||
$(O)/sys/amd64/hw/con.o \
|
||||
$(O)/sys/amd64/hw/pci/pci.o \
|
||||
$(O)/sys/amd64/hw/pci/ahci.o
|
||||
|
||||
kernel_LINKER=sys/amd64/link.ld
|
||||
kernel_LDFLAGS=-nostdlib \
|
||||
@@ -88,6 +90,7 @@ kernel_OBJS=$(O)/sys/amd64/crti.o \
|
||||
$(O)/sys/amd64/crtn.o
|
||||
|
||||
DIRS+=$(O)/sys/amd64/image/boot/grub \
|
||||
$(O)/sys/amd64/hw/pci \
|
||||
$(O)/sys/amd64/hw \
|
||||
$(O)/sys/amd64/sys \
|
||||
$(O)/sys/amd64/mm \
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
struct ahci_registers;
|
||||
struct pci_device;
|
||||
|
||||
struct ahci_fis_reg_h2d {
|
||||
uint8_t type;
|
||||
uint8_t cmd_port;
|
||||
uint8_t cmd;
|
||||
uint8_t feature_low;
|
||||
uint8_t lba0;
|
||||
uint8_t lba1;
|
||||
uint8_t lba2;
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t feature_high;
|
||||
|
||||
uint16_t count;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
|
||||
uint32_t __res0;
|
||||
};
|
||||
|
||||
struct ahci_controller {
|
||||
struct pci_device *pci_dev;
|
||||
uintptr_t abar_phys;
|
||||
struct ahci_registers *regs;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
#define ATA_CMD_PACKET 0xA0
|
||||
#define ATA_CMD_READ_DMA_EX 0x25
|
||||
#define ATA_CMD_WRITE_DMA_EX 0x35
|
||||
|
||||
#define ATA_CMD_PACKET_IDENTIFY 0xA1
|
||||
#define ATAPI_CMD_READ_SECTORS 0xA8
|
||||
|
||||
#define ATA_SR_BUSY (1 << 7)
|
||||
#define ATA_SR_DRQ (1 << 3)
|
||||
|
||||
// Response to ATA_CMD_IDENTIFY
|
||||
#define ATA_IDENT_DEVICE_TYPE 0x00
|
||||
#define ATA_IDENT_SERIAL 0x14
|
||||
#define ATA_IDENT_MODEL 0x36
|
||||
#define ATA_IDENT_CAPS 0x62
|
||||
#define ATA_IDENT_MAX_LBA 0x78
|
||||
#define ATA_IDENT_CMD_SETS 0xA4
|
||||
#define ATA_IDENT_MAX_LBAEXT 0xC8
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "sys/amd64/hw/irq.h"
|
||||
#include "sys/types.h"
|
||||
|
||||
#define PCI_PORT_CONFIG_ADDR 0xCF8
|
||||
#define PCI_PORT_CONFIG_DATA 0xCFC
|
||||
|
||||
#define PCI_CONFIG_ID 0x00
|
||||
#define PCI_CONFIG_CMD 0x04
|
||||
#define PCI_CONFIG_CLASS 0x08
|
||||
#define PCI_CONFIG_INFO 0x0C
|
||||
#define PCI_CONFIG_BAR(n) (0x10 + (n) * 4)
|
||||
#define PCI_CONFIG_SUBSYSTEM 0x2C
|
||||
#define PCI_CONFIG_CAPABILITIES 0x34
|
||||
#define PCI_CONFIG_IRQ 0x3C
|
||||
|
||||
#define PCI_CONFIG_BRIDGE 0x18
|
||||
|
||||
#define PCI_ID(vnd, dev) (((uint32_t) (vnd)) | ((uint32_t) (dev) << 16))
|
||||
|
||||
struct pci_device;
|
||||
typedef void (*pci_driver_func_t)(struct pci_device *dev);
|
||||
|
||||
void pci_init(void);
|
||||
void pci_add_root_bus(uint8_t n);
|
||||
|
||||
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off);
|
||||
void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx);
|
||||
|
||||
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func);
|
||||
|
||||
// pcidb.c
|
||||
const char *pci_class_string(uint16_t full_class);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
|
||||
//void pci_usb_uhci_init(pci_addr_t addr);
|
||||
@@ -0,0 +1,604 @@
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
#include "sys/amd64/hw/ata.h"
|
||||
#include "sys/amd64/mm/phys.h"
|
||||
#include "sys/amd64/hw/ahci.h"
|
||||
#include "sys/dev/blk.h"
|
||||
#include "sys/dev/dev.h"
|
||||
#include "sys/string.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/errno.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/heap.h"
|
||||
#include "sys/attr.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define AHCI_SPIN_WAIT_MAX 10000000
|
||||
#define AHCI_PRD_MAX_SIZE 8192
|
||||
|
||||
#define AHCI_PORT_SIG_SATA 0x00000101
|
||||
#define AHCI_PORT_SIG_SATAPI 0xEB140101
|
||||
|
||||
#define AHCI_MAX_PORTS 32
|
||||
#define AHCI_CMD_LIST_SIZE 32
|
||||
#define AHCI_CMD_TABLE_ENTSZ 256
|
||||
|
||||
#define AHCI_PORT_IS_TFES (1 << 30)
|
||||
|
||||
#define AHCI_PORT_SSTS_IPM_ACTIVE 0x01
|
||||
#define AHCI_PORT_SSTS_DET_CONNECTED 0x03
|
||||
|
||||
#define AHCI_FIS_REG_H2D 0x27
|
||||
// Tells the controller it's a command FIS
|
||||
#define AHCI_FIS_REG_H2D_COMMAND (1 << 7)
|
||||
|
||||
#define AHCI_PORT_CMD_LIST(port) \
|
||||
((struct ahci_cmd_list *) MM_VIRTUALIZE((port)->clb | (((uintptr_t) (port)->clbu) << 32)))
|
||||
#define AHCI_PORT_TABLE_ENTRY(list, n) \
|
||||
((struct ahci_cmd_table *) MM_VIRTUALIZE((list)[n].ctba))
|
||||
|
||||
// Start
|
||||
#define AHCI_PORT_CMD_ST (1 << 0)
|
||||
// FIS receive enable
|
||||
#define AHCI_PORT_CMD_FRE (1 << 4)
|
||||
// FIS receive running
|
||||
#define AHCI_PORT_CMD_FR (1 << 14)
|
||||
// Command list running
|
||||
#define AHCI_PORT_CMD_CR (1 << 15)
|
||||
|
||||
struct ahci_port {
|
||||
uint32_t clb; // 0x00
|
||||
uint32_t clbu; // 0x04
|
||||
uint32_t fb; // 0x08
|
||||
uint32_t fbu; // 0x0C
|
||||
uint32_t is; // 0x10
|
||||
uint32_t ie; // 0x14
|
||||
uint32_t cmd; // 0x18
|
||||
uint32_t __res0; // 0x1C
|
||||
uint32_t tfd; // 0x20
|
||||
uint32_t sig; // 0x24
|
||||
uint32_t ssts; // 0x28
|
||||
uint32_t sctl; // 0x2C
|
||||
uint32_t serr; // 0x30
|
||||
uint32_t sact; // 0x34
|
||||
uint32_t ci; // 0x38
|
||||
uint32_t __res1[13];// 0x3C
|
||||
uint32_t __vnd[4];
|
||||
};
|
||||
|
||||
struct ahci_registers {
|
||||
// 0x00 - 0x1F - Generic Host Control
|
||||
uint32_t cap; // 0x00
|
||||
uint32_t ghc; // 0x04
|
||||
uint32_t is; // 0x08
|
||||
uint32_t pi; // 0x0C
|
||||
uint32_t vs; // 0x10
|
||||
uint32_t __unspec[3];
|
||||
// 0x20 - 0x9F - Reserved
|
||||
uint32_t __res[32];
|
||||
// 0xA0 - 0xFF - Vendor specific registers
|
||||
uint32_t __vnd[24];
|
||||
struct ahci_port port[32];
|
||||
};
|
||||
|
||||
// Command list entry
|
||||
struct ahci_cmd_list {
|
||||
uint16_t attr;
|
||||
// PRD table length in entries
|
||||
uint16_t prdtl;
|
||||
// Byte count transferred
|
||||
volatile uint32_t prdbc;
|
||||
// Command table base address
|
||||
uint64_t ctba;
|
||||
uint32_t __res0[4];
|
||||
};
|
||||
|
||||
// Physical region descriptor entry
|
||||
struct ahci_prd {
|
||||
// Buffer address
|
||||
uint64_t dba;
|
||||
uint32_t __res0;
|
||||
uint32_t dbc;
|
||||
};
|
||||
|
||||
// Command table entry
|
||||
struct ahci_cmd_table {
|
||||
// Buffer for FIS to be sent to the device
|
||||
union {
|
||||
struct ahci_fis_reg_h2d fis_reg_h2d;
|
||||
uint8_t __cmd_fis[64];
|
||||
};
|
||||
uint8_t acmd[16];
|
||||
uint8_t __res0[48];
|
||||
// Any number of entries can follow
|
||||
struct ahci_prd prdt[0];
|
||||
};
|
||||
|
||||
//// Generic AHCI
|
||||
|
||||
static void ahci_port_stop(struct ahci_port *port) {
|
||||
// Disable command engine and FIS receive
|
||||
port->cmd &= ~(AHCI_PORT_CMD_ST | AHCI_PORT_CMD_FRE);
|
||||
|
||||
// Wait for controller to change status
|
||||
while (port->cmd & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_FR)) {
|
||||
asm ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_port_start(struct ahci_port *port) {
|
||||
// Wait while port may be processing any commands
|
||||
while (port->cmd & AHCI_PORT_CMD_CR) {
|
||||
asm ("pause");
|
||||
}
|
||||
|
||||
// Enable command engine and FIS receive
|
||||
port->cmd |= AHCI_PORT_CMD_FRE | AHCI_PORT_CMD_ST;
|
||||
}
|
||||
|
||||
static int ahci_port_cmd_alloc(struct ahci_port *port) {
|
||||
uint32_t slots = (port->sact | port->ci);
|
||||
for (int i = 0; i < AHCI_CMD_LIST_SIZE; ++i) {
|
||||
if (!(slots & (1 << i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ahci_port_alloc(struct ahci_port *port) {
|
||||
// Disable port command engine first
|
||||
ahci_port_stop(port);
|
||||
|
||||
// AHCI port memory:
|
||||
// * 1024 for command list
|
||||
// * 256 - FIS receive buffer
|
||||
// * 8192 - command table
|
||||
// Total:
|
||||
// 12288 bytes (page-aligned), 3 pages
|
||||
|
||||
// Command list and FIS buffer
|
||||
uintptr_t page0 = amd64_phys_alloc_page();
|
||||
if (page0 == MM_NADDR) {
|
||||
kerror("Failed to allocate a page\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
// Command table
|
||||
uintptr_t page1 = amd64_phys_alloc_contiguous(2);
|
||||
if (page1 == MM_NADDR) {
|
||||
kerror("Failed to allocate 2 pages\n");
|
||||
amd64_phys_free(page0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
// Cleanup the pages
|
||||
memset((void *) MM_VIRTUALIZE(page0), 0, 0x1000);
|
||||
memset((void *) MM_VIRTUALIZE(page1), 0, 0x2000);
|
||||
|
||||
// Setup an empty command list with proper addresses
|
||||
struct ahci_cmd_list *cmd_list = (struct ahci_cmd_list *) MM_VIRTUALIZE(page0);
|
||||
for (size_t i = 0; i < AHCI_CMD_LIST_SIZE; ++i) {
|
||||
cmd_list[i].prdtl = 8;
|
||||
cmd_list[i].ctba = page1 + i * AHCI_CMD_TABLE_ENTSZ;
|
||||
}
|
||||
|
||||
// Configure the port to use allocated pages
|
||||
port->clb = page0 & 0xFFFFFFFF;
|
||||
port->clbu = page0 >> 32;
|
||||
port->fb = (page0 + 0x400) & 0xFFFFFFFF;
|
||||
port->fbu = (page0 + 0x400) >> 32;
|
||||
|
||||
// Enable it again
|
||||
ahci_port_start(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ahci_port_ata_cmd(struct ahci_port *port, uint8_t ata, uintptr_t lba, void *data, size_t len) {
|
||||
// At least should be sector-aligned
|
||||
_assert(len % 512 == 0);
|
||||
|
||||
int cmd = ahci_port_cmd_alloc(port);
|
||||
if (cmd < 0) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
struct ahci_cmd_list *list = AHCI_PORT_CMD_LIST(port);
|
||||
struct ahci_cmd_list *list_entry = &list[cmd];
|
||||
struct ahci_cmd_table *table_entry = AHCI_PORT_TABLE_ENTRY(list, cmd);
|
||||
uint32_t spin = 0;
|
||||
|
||||
// TODO: support devices with different sector sizes
|
||||
size_t nsect = (len + 511) / 512;
|
||||
_assert((nsect & ~0xFFFF) == 0);
|
||||
size_t prd_count = (len + AHCI_PRD_MAX_SIZE - 1) / AHCI_PRD_MAX_SIZE;
|
||||
kdebug("Command requires %d PRDs\n", prd_count);
|
||||
|
||||
// Setup command table entry
|
||||
memset(table_entry, 0, sizeof(struct ahci_cmd_table) + sizeof(struct ahci_prd) * prd_count);
|
||||
for (size_t i = 0, bytes_left = len; i < prd_count; ++i) {
|
||||
size_t prd_size = MIN(AHCI_PRD_MAX_SIZE, len);
|
||||
_assert(prd_size);
|
||||
|
||||
uintptr_t prd_buf_phys = MM_PHYS(data) + i * AHCI_PRD_MAX_SIZE;
|
||||
|
||||
table_entry->prdt[i].dba = prd_buf_phys;
|
||||
table_entry->prdt[i].dbc = ((prd_size - 1) << 1) | 1;
|
||||
|
||||
kdebug("PRD%d: %S at %p\n", i, prd_size, prd_buf_phys);
|
||||
|
||||
if (i == prd_count - 1) {
|
||||
// Mark last PRDT entry
|
||||
table_entry->prdt[i].dbc |= 1 << 31;
|
||||
}
|
||||
|
||||
bytes_left -= prd_size;
|
||||
}
|
||||
|
||||
// Setup command list entry
|
||||
// attr = FIS size in dwords
|
||||
list_entry->attr = sizeof(struct ahci_fis_reg_h2d) / sizeof(uint32_t);
|
||||
list_entry->prdtl = prd_count;
|
||||
list_entry->prdbc = 0;
|
||||
|
||||
// Setup FIS packet
|
||||
struct ahci_fis_reg_h2d *fis = &table_entry->fis_reg_h2d;
|
||||
memset(fis, 0, sizeof(struct ahci_fis_reg_h2d));
|
||||
fis->type = AHCI_FIS_REG_H2D;
|
||||
fis->cmd = ata;
|
||||
fis->cmd_port = AHCI_FIS_REG_H2D_COMMAND;
|
||||
if (ata != ATA_CMD_IDENTIFY) {
|
||||
fis->device = 1 << 6; // LBA mode
|
||||
fis->lba0 = lba & 0xFF;
|
||||
fis->lba1 = (lba >> 8) & 0xFF;
|
||||
fis->lba2 = (lba >> 16) & 0xFF;
|
||||
fis->lba3 = (lba >> 24) & 0xFF;
|
||||
fis->lba4 = (lba >> 32) & 0xFF;
|
||||
fis->lba5 = (lba >> 40) & 0xFF;
|
||||
kdebug("nsect = %d\n", nsect);
|
||||
fis->count = nsect & 0xFFFF;
|
||||
}
|
||||
|
||||
// Wait for port to be free for commands
|
||||
while ((port->tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < AHCI_SPIN_WAIT_MAX) {
|
||||
++spin;
|
||||
}
|
||||
|
||||
if (spin == AHCI_SPIN_WAIT_MAX) {
|
||||
kerror("AHCI device hang\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Request HBA to execute the command
|
||||
port->ci |= 1 << cmd;
|
||||
|
||||
while (1) {
|
||||
if (!(port->ci & (1 << cmd))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (port->is & AHCI_PORT_IS_TFES) {
|
||||
kerror("Device signalled TFE error\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (port->is & AHCI_PORT_IS_TFES) {
|
||||
kerror("Device signalled TFE error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ahci_port_atapi_read(struct ahci_port *port, uint64_t lba, void *buf, size_t nsect) {
|
||||
int cmd = ahci_port_cmd_alloc(port);
|
||||
if (cmd < 0) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
struct ahci_cmd_list *list = AHCI_PORT_CMD_LIST(port);
|
||||
struct ahci_cmd_list *list_entry = &list[cmd];
|
||||
struct ahci_cmd_table *table_entry = AHCI_PORT_TABLE_ENTRY(list, cmd);
|
||||
uint32_t spin = 0;
|
||||
|
||||
// TODO: support devices with different sector sizes
|
||||
size_t len = nsect * 2048;
|
||||
_assert((nsect & ~0xFFFF) == 0);
|
||||
size_t prd_count = (len + AHCI_PRD_MAX_SIZE - 1) / AHCI_PRD_MAX_SIZE;
|
||||
kdebug("Command requires %d PRDs\n", prd_count);
|
||||
|
||||
// Setup command table entry
|
||||
memset(table_entry, 0, sizeof(struct ahci_cmd_table) + sizeof(struct ahci_prd) * prd_count);
|
||||
table_entry->acmd[0] = 0xA8;
|
||||
table_entry->acmd[9] = nsect;
|
||||
table_entry->acmd[5] = lba & 0xFF;
|
||||
table_entry->acmd[4] = (lba >> 8) & 0xFF;
|
||||
table_entry->acmd[3] = (lba >> 16) & 0xFF;
|
||||
table_entry->acmd[2] = (lba >> 24) & 0xFF;
|
||||
|
||||
for (size_t i = 0, bytes_left = len; i < prd_count; ++i) {
|
||||
size_t prd_size = MIN(AHCI_PRD_MAX_SIZE, len);
|
||||
_assert(prd_size);
|
||||
|
||||
uintptr_t prd_buf_phys = MM_PHYS(buf) + i * AHCI_PRD_MAX_SIZE;
|
||||
|
||||
table_entry->prdt[i].dba = prd_buf_phys;
|
||||
table_entry->prdt[i].dbc = ((prd_size - 1) << 1) | 1;
|
||||
|
||||
kdebug("PRD%d: %S at %p\n", i, prd_size, prd_buf_phys);
|
||||
|
||||
if (i == prd_count - 1) {
|
||||
// Mark last PRDT entry
|
||||
table_entry->prdt[i].dbc |= 1 << 31;
|
||||
}
|
||||
|
||||
bytes_left -= prd_size;
|
||||
}
|
||||
|
||||
// Setup command list entry
|
||||
// attr = FIS size in dwords
|
||||
list_entry->attr = sizeof(struct ahci_fis_reg_h2d) / sizeof(uint32_t);
|
||||
// This is ATAPI command
|
||||
list_entry->attr |= (1 << 5);
|
||||
list_entry->prdtl = 1;
|
||||
list_entry->prdbc = 0;
|
||||
|
||||
// Setup FIS packet
|
||||
struct ahci_fis_reg_h2d *fis = &table_entry->fis_reg_h2d;
|
||||
memset(fis, 0, sizeof(struct ahci_fis_reg_h2d));
|
||||
fis->type = AHCI_FIS_REG_H2D;
|
||||
fis->cmd = ATA_CMD_PACKET;
|
||||
fis->cmd_port = AHCI_FIS_REG_H2D_COMMAND;
|
||||
|
||||
fis->device = 1 << 6; // LBA mode
|
||||
fis->feature_low = 1;
|
||||
fis->lba0 = lba & 0xFF;
|
||||
fis->lba1 = (lba >> 8) & 0xFF;
|
||||
fis->lba2 = (lba >> 16) & 0xFF;
|
||||
fis->lba3 = (lba >> 24) & 0xFF;
|
||||
fis->lba4 = (lba >> 32) & 0xFF;
|
||||
fis->lba5 = (lba >> 40) & 0xFF;
|
||||
fis->count = nsect & 0xFFFF;
|
||||
|
||||
// Wait for port to be free for commands
|
||||
while ((port->tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < AHCI_SPIN_WAIT_MAX) {
|
||||
++spin;
|
||||
}
|
||||
|
||||
if (spin == AHCI_SPIN_WAIT_MAX) {
|
||||
kerror("AHCI device hang\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Request HBA to execute the command
|
||||
port->ci |= 1 << cmd;
|
||||
|
||||
while (1) {
|
||||
if (!(port->ci & (1 << cmd))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (port->is & AHCI_PORT_IS_TFES) {
|
||||
kerror("Device signalled TFE error\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (port->is & AHCI_PORT_IS_TFES) {
|
||||
kerror("Device signalled TFE error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ahci_port_identify(struct ahci_port *port) {
|
||||
char ident_buf[1024];
|
||||
size_t disk_size_lba;
|
||||
char model_string[41];
|
||||
char serial_string[11];
|
||||
uint32_t cmd_sets;
|
||||
int res, id_cmd;
|
||||
|
||||
if (port->sig == AHCI_PORT_SIG_SATA) {
|
||||
id_cmd = ATA_CMD_IDENTIFY;
|
||||
} else {
|
||||
id_cmd = ATA_CMD_PACKET_IDENTIFY;
|
||||
}
|
||||
|
||||
if ((res = ahci_port_ata_cmd(port, id_cmd, 0, ident_buf, sizeof(ident_buf))) != 0) {
|
||||
kwarn("Disk identify failed: %s\n", kstrerror(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
model_string[40] = 0;
|
||||
serial_string[10] = 0;
|
||||
cmd_sets = *(uint32_t *) (ident_buf + ATA_IDENT_CMD_SETS);
|
||||
|
||||
for (size_t i = 0; i < 40; i += 2) {
|
||||
model_string[i] = ident_buf[ATA_IDENT_MODEL + i + 1];
|
||||
model_string[i + 1] = ident_buf[ATA_IDENT_MODEL + i];
|
||||
}
|
||||
for (size_t i = 39; i > 0; --i) {
|
||||
if (model_string[i] != ' ') {
|
||||
break;
|
||||
}
|
||||
model_string[i] = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 10; i += 2) {
|
||||
serial_string[i] = ident_buf[ATA_IDENT_SERIAL + i + 1];
|
||||
serial_string[i + 1] = ident_buf[ATA_IDENT_SERIAL + i];
|
||||
}
|
||||
for (size_t i = 9; i > 0; --i) {
|
||||
if (serial_string[i] != ' ') {
|
||||
break;
|
||||
}
|
||||
serial_string[i] = 0;
|
||||
}
|
||||
|
||||
if (cmd_sets & (1 << 26)) {
|
||||
disk_size_lba = *(uint32_t *) (ident_buf + ATA_IDENT_MAX_LBAEXT);
|
||||
} else {
|
||||
disk_size_lba = *(uint32_t *) (ident_buf + ATA_IDENT_MAX_LBA);
|
||||
}
|
||||
|
||||
kdebug("%s drive: \"%s\", Serial: \"%s\", Capacity: %S\n",
|
||||
id_cmd == ATA_CMD_IDENTIFY ? "SATA" : "SATAPI",
|
||||
model_string, serial_string, disk_size_lba * 512);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ahci_blk_write(struct blkdev *blk, const void *buf, size_t off, size_t len) {
|
||||
if ((off % blk->block_size) != 0) {
|
||||
panic("Misaligned AHCI device write: offset of %lu, block size is %lu\n", off, blk->block_size);
|
||||
}
|
||||
if ((len % blk->block_size) != 0) {
|
||||
panic("Misaligned AHCI device write: size of %lu\n", len);
|
||||
}
|
||||
|
||||
kdebug("AHCI write %S at %p\n", len, off);
|
||||
struct ahci_port *port = blk->dev_data;
|
||||
_assert(port);
|
||||
uintptr_t lba = (off / blk->block_size);
|
||||
size_t n_blocks = len / blk->block_size;
|
||||
int res;
|
||||
|
||||
switch (port->sig) {
|
||||
case AHCI_PORT_SIG_SATA:
|
||||
if ((res = ahci_port_ata_cmd(port, ATA_CMD_WRITE_DMA_EX, lba, (void *) buf, len)) != 0) {
|
||||
return res;
|
||||
}
|
||||
return len;
|
||||
case AHCI_PORT_SIG_SATAPI:
|
||||
return -EROFS;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ahci_blk_read(struct blkdev *blk, void *buf, size_t off, size_t len) {
|
||||
if ((off % blk->block_size) != 0) {
|
||||
panic("Misaligned AHCI device read: offset of %lu, block size is %lu\n", off, blk->block_size);
|
||||
}
|
||||
if ((len % blk->block_size) != 0) {
|
||||
panic("Misaligned AHCI device read: size of %lu\n", len);
|
||||
}
|
||||
|
||||
kdebug("AHCI read %S at %p\n", len, off);
|
||||
struct ahci_port *port = blk->dev_data;
|
||||
_assert(port);
|
||||
uintptr_t lba = (off / blk->block_size);
|
||||
size_t n_blocks = len / blk->block_size;
|
||||
int res;
|
||||
|
||||
switch (port->sig) {
|
||||
case AHCI_PORT_SIG_SATA:
|
||||
if ((res = ahci_port_ata_cmd(port, ATA_CMD_READ_DMA_EX, lba, buf, len)) != 0) {
|
||||
return res;
|
||||
}
|
||||
return len;
|
||||
case AHCI_PORT_SIG_SATAPI:
|
||||
if ((res = ahci_port_atapi_read(port, lba, buf, n_blocks)) != 0) {
|
||||
return res;
|
||||
}
|
||||
return len;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_port_add(struct ahci_port *port) {
|
||||
struct blkdev *blk = kmalloc(sizeof(struct blkdev));
|
||||
_assert(blk);
|
||||
uint32_t subclass;
|
||||
|
||||
switch (port->sig) {
|
||||
case AHCI_PORT_SIG_SATA:
|
||||
blk->block_size = 512;
|
||||
subclass = DEV_BLOCK_SDx;
|
||||
break;
|
||||
case AHCI_PORT_SIG_SATAPI:
|
||||
blk->block_size = 2048;
|
||||
subclass = DEV_BLOCK_CDx;
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled AHCI device signature: %08x\n", port->sig);
|
||||
}
|
||||
|
||||
blk->dev_data = port;
|
||||
blk->read = ahci_blk_read;
|
||||
blk->write = ahci_blk_write;
|
||||
blk->flags = 0;
|
||||
|
||||
_assert(dev_add(DEV_CLASS_BLOCK, subclass, blk, NULL) == 0);
|
||||
}
|
||||
|
||||
static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port, int no) {
|
||||
kinfo("Initializing port %d\n", no);
|
||||
|
||||
// Allocate all the data structures required for port control
|
||||
if (ahci_port_alloc(port) != 0) {
|
||||
kwarn("Failed to allocate port data %d\n", no);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ahci_port_identify(port) != 0) {
|
||||
kwarn("Failed to identify device at port %d\n", no);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register new block device
|
||||
ahci_port_add(port);
|
||||
}
|
||||
|
||||
static void ahci_controller_init(struct ahci_controller *ahci) {
|
||||
// Check controller version
|
||||
kinfo("AHCI controller version is %02x.%02x\n", (ahci->regs->vs >> 16), (ahci->regs->vs & 0xFFFF));
|
||||
|
||||
// Check which ports are supported by AHCI controller
|
||||
for (size_t i = 0; i < AHCI_MAX_PORTS; ++i) {
|
||||
if (ahci->regs->pi & (1 << i)) {
|
||||
// Get SATA connnection status
|
||||
struct ahci_port *port = &ahci->regs->port[i];
|
||||
|
||||
uint32_t ssts = port->ssts;
|
||||
uint8_t ipm = (ssts >> 8) & 0x0F;
|
||||
uint8_t spd = (ssts >> 4) & 0x0F;
|
||||
uint8_t det = ssts & 0x0F;
|
||||
|
||||
if ((ipm != AHCI_PORT_SSTS_IPM_ACTIVE) || (det != AHCI_PORT_SSTS_DET_CONNECTED)) {
|
||||
// Skip not connected or inactive ports
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port->sig == AHCI_PORT_SIG_SATA || port->sig == AHCI_PORT_SIG_SATAPI) {
|
||||
ahci_port_init(ahci, port, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// PCI-specific
|
||||
|
||||
static void pci_ahci_init(struct pci_device *pci_dev) {
|
||||
// TODO: change pcie -> pci
|
||||
uint32_t abar_phys = pci_config_read_dword(pci_dev, PCI_CONFIG_BAR(5));
|
||||
if (abar_phys & 1) {
|
||||
kwarn("AHCI controller has ABAR in I/O space\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller));
|
||||
|
||||
obj->pci_dev = pci_dev;
|
||||
obj->abar_phys = abar_phys;
|
||||
obj->regs = (void *) MM_VIRTUALIZE(abar_phys);
|
||||
|
||||
ahci_controller_init(obj);
|
||||
}
|
||||
|
||||
static __init void ahci_register_class(void) {
|
||||
pci_add_class_driver(0x010601, pci_ahci_init);
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
#include "sys/amd64/hw/ioapic.h"
|
||||
#include "sys/amd64/hw/acpi.h"
|
||||
#include "sys/amd64/hw/io.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/panic.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/heap.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define PCI_MAX_DRIVERS 64
|
||||
|
||||
#define pcie_config_read_dword(dev, off) \
|
||||
(*(uint32_t *) ((dev)->pcie_config + (off)))
|
||||
|
||||
#define PCI_CAP_MSI_64 (1 << 7)
|
||||
#define PCI_CAP_MSI_EN (1 << 0)
|
||||
struct pci_cap_msi {
|
||||
uint8_t cap_id;
|
||||
uint8_t cap_link;
|
||||
uint16_t message_control;
|
||||
union {
|
||||
struct {
|
||||
uint32_t message_address;
|
||||
uint16_t message_data;
|
||||
} __attribute__((packed)) msi32;
|
||||
struct {
|
||||
uint64_t message_address;
|
||||
uint16_t message_data;
|
||||
} __attribute__((packed)) msi64;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pci_device {
|
||||
// PCI address
|
||||
uint8_t bus;
|
||||
uint8_t dev;
|
||||
uint8_t func;
|
||||
|
||||
// PCIe addressing: segment group number and configuration space pointer
|
||||
uint16_t pcie_segment_group;
|
||||
void *pcie_config;
|
||||
|
||||
// Interrupt resources
|
||||
struct pci_cap_msi *msi;
|
||||
int irq_pin;
|
||||
|
||||
// For list
|
||||
struct pci_device *next;
|
||||
};
|
||||
|
||||
struct pci_driver {
|
||||
pci_driver_func_t init_func;
|
||||
uint32_t type;
|
||||
uint32_t match;
|
||||
};
|
||||
|
||||
static struct pci_device *g_pci_devices = NULL;
|
||||
static struct pci_driver g_pci_drivers[PCI_MAX_DRIVERS] = {0};
|
||||
static size_t g_pci_driver_count;
|
||||
|
||||
#define PCI_DRIVER_CLASS 1
|
||||
#define PCI_DRIVER_DEV 2
|
||||
|
||||
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off) {
|
||||
// TODO: check if device is really PCIe
|
||||
return pcie_config_read_dword(dev, off);
|
||||
}
|
||||
|
||||
void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx) {
|
||||
if (dev->msi) {
|
||||
uint8_t vector;
|
||||
|
||||
if (irq_add_msi_handler(handler, ctx, &vector) != 0) {
|
||||
panic("Failed to add MSI handler\n");
|
||||
}
|
||||
|
||||
if (dev->msi->message_control & PCI_CAP_MSI_64) {
|
||||
dev->msi->msi64.message_address = 0xFEE00000;
|
||||
dev->msi->msi64.message_data = vector;
|
||||
} else {
|
||||
dev->msi->msi32.message_address = 0xFEE00000;
|
||||
dev->msi->msi32.message_data = vector;
|
||||
}
|
||||
dev->msi->message_control |= PCI_CAP_MSI_EN;
|
||||
}
|
||||
}
|
||||
|
||||
void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func) {
|
||||
if (g_pci_driver_count == PCI_MAX_DRIVERS) {
|
||||
panic("Too many PCI drivers loaded\n");
|
||||
}
|
||||
|
||||
struct pci_driver *driver = &g_pci_drivers[g_pci_driver_count++];
|
||||
|
||||
driver->init_func = func;
|
||||
driver->type = PCI_DRIVER_CLASS;
|
||||
driver->match = full_class & ~0xFF000000;
|
||||
}
|
||||
|
||||
void pci_add_root_bus(uint8_t n) {
|
||||
kwarn("%s: %02x\n", __func__, n);
|
||||
}
|
||||
|
||||
static void pci_pick_driver(uint32_t device_id, uint32_t class_id, pci_driver_func_t *class_driver, pci_driver_func_t *dev_driver) {
|
||||
pci_driver_func_t rclass = NULL, rdev = NULL;
|
||||
for (size_t i = 0; i < g_pci_driver_count; ++i) {
|
||||
if (rclass && rdev) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_pci_drivers[i].type == PCI_DRIVER_CLASS && !rclass) {
|
||||
uint32_t match = g_pci_drivers[i].match;
|
||||
// Check if prog_if has to be matched
|
||||
// 0xFF means (match all prog. IF)
|
||||
if ((match & 0xFF) == 0xFF) {
|
||||
match &= ~0xFF;
|
||||
class_id &= ~0xFF;
|
||||
}
|
||||
|
||||
if (match == class_id) {
|
||||
rclass = g_pci_drivers[i].init_func;
|
||||
continue;
|
||||
}
|
||||
} else if (g_pci_drivers[i].type == PCI_DRIVER_DEV && !rdev) {
|
||||
if (device_id == g_pci_drivers[i].match) {
|
||||
rdev = g_pci_drivers[i].init_func;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*class_driver = rclass;
|
||||
*dev_driver = rdev;
|
||||
}
|
||||
|
||||
static void pci_device_add(struct pci_device *dev) {
|
||||
dev->next = g_pci_devices;
|
||||
g_pci_devices = dev;
|
||||
}
|
||||
|
||||
static int pcie_device_setup(struct pci_device *dev) {
|
||||
uint32_t class, caps_offset, irq_info;
|
||||
uint32_t id;
|
||||
uint8_t irq_pin;
|
||||
|
||||
class = pcie_config_read_dword(dev, PCI_CONFIG_CLASS);
|
||||
caps_offset = pcie_config_read_dword(dev, PCI_CONFIG_CAPABILITIES) & 0xFF;
|
||||
irq_info = pcie_config_read_dword(dev, PCI_CONFIG_IRQ);
|
||||
id = pcie_config_read_dword(dev, PCI_CONFIG_ID);
|
||||
|
||||
kinfo("%02x:%02x:%02x:\n", dev->bus, dev->dev, dev->func);
|
||||
kinfo(" Class %02x:%02x:%02x\n", (class >> 24), (class >> 16) & 0xFF, (class >> 8) & 0xFF);
|
||||
kinfo(" Device %04x:%04x\n", id & 0xFFFF, (id >> 16) & 0xFFFF);
|
||||
|
||||
irq_pin = (irq_info >> 8) & 0xFF;
|
||||
if (irq_pin) {
|
||||
dev->irq_pin = irq_pin - 1;
|
||||
kinfo(" IRQ pin INT%c#\n", dev->irq_pin + 'A');
|
||||
}
|
||||
|
||||
while (caps_offset) {
|
||||
uint8_t *link = (uint8_t *) (dev->pcie_config + caps_offset);
|
||||
|
||||
switch (link[0]) {
|
||||
case 0x05:
|
||||
kinfo(" * MSI capability\n");
|
||||
dev->msi = (struct pci_cap_msi *) link;
|
||||
break;
|
||||
case 0x10:
|
||||
kinfo(" * PCIe capability\n");
|
||||
break;
|
||||
default:
|
||||
// Unknown capability
|
||||
kinfo(" * Device capability: %02x\n", link[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
caps_offset = link[1];
|
||||
}
|
||||
|
||||
pci_driver_func_t driver_class, driver_dev;
|
||||
pci_pick_driver(id, class >> 8, &driver_class, &driver_dev);
|
||||
|
||||
if (driver_dev) {
|
||||
driver_dev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (driver_class) {
|
||||
driver_class(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pcie_enumerate_device(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus, uint8_t dev_no) {
|
||||
void *cfg;
|
||||
uint32_t id;
|
||||
|
||||
for (uint8_t func = 0; func < 8; ++func) {
|
||||
uint32_t d = ((uint32_t) (bus - start_bus) << 20) | ((uint32_t) dev_no << 15) | ((uint32_t) func << 12);
|
||||
cfg = (void *) MM_VIRTUALIZE(base_address + d);
|
||||
id = *(uint32_t *) cfg;
|
||||
if ((id & 0xFFFF) == 0xFFFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct pci_device *dev = kmalloc(sizeof(struct pci_device));
|
||||
_assert(dev);
|
||||
dev->bus = bus;
|
||||
dev->dev = dev_no;
|
||||
dev->func = func;
|
||||
|
||||
dev->pcie_segment_group = seg;
|
||||
dev->pcie_config = cfg;
|
||||
dev->msi = NULL;
|
||||
dev->irq_pin = -1;
|
||||
|
||||
pcie_device_setup(dev);
|
||||
pci_device_add(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcie_enumerate_bus(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus) {
|
||||
for (uint8_t dev = 0; dev < 32; ++dev) {
|
||||
// Check if function 0 is present
|
||||
void *cfg = (void *) MM_VIRTUALIZE(base_address + (((bus - start_bus) << 20) | (dev << 15)));
|
||||
|
||||
if (((*(uint32_t *) cfg) & 0xFFFF) != 0xFFFF) {
|
||||
uint32_t header = *(uint32_t *) (cfg + 0x0C);
|
||||
header >>= 16;
|
||||
header &= 0x7F;
|
||||
|
||||
switch (header) {
|
||||
case 0x00:
|
||||
pcie_enumerate_device(base_address, seg, start_bus, bus, dev);
|
||||
break;
|
||||
default:
|
||||
kwarn("Skipping unsupported header type: %02x\n", header);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pcie_enumerate_segment(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t end_bus) {
|
||||
for (uint16_t bus = start_bus; bus < end_bus; ++bus) {
|
||||
pcie_enumerate_bus(base_address, seg, start_bus, bus);
|
||||
}
|
||||
}
|
||||
|
||||
void pci_init(void) {
|
||||
if (!acpi_mcfg) {
|
||||
panic("TODO: legacy PCI\n");
|
||||
}
|
||||
|
||||
uint32_t mcfg_entry_count = (acpi_mcfg->hdr.length - sizeof(struct acpi_header) - 8) / sizeof(struct acpi_mcfg_entry);
|
||||
kinfo("MCFG has %u entries:\n", mcfg_entry_count);
|
||||
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
|
||||
kinfo("%u:\n", i);
|
||||
kinfo(" Base address: %p\n", acpi_mcfg->entry[i].base_address);
|
||||
kinfo(" Segment group #%u\n", acpi_mcfg->entry[i].pci_segment_group);
|
||||
kinfo(" PCI buses: %02x-%02x\n", acpi_mcfg->entry[i].start_pci_bus, acpi_mcfg->entry[i].end_pci_bus);
|
||||
}
|
||||
|
||||
// Start enumerating buses specified in MCFG
|
||||
for (uint32_t i = 0; i < mcfg_entry_count; ++i) {
|
||||
pcie_enumerate_segment(acpi_mcfg->entry[i].base_address,
|
||||
acpi_mcfg->entry[i].pci_segment_group,
|
||||
acpi_mcfg->entry[i].start_pci_bus,
|
||||
acpi_mcfg->entry[i].end_pci_bus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
|
||||
static const char *pci_class_code_strings[] = {
|
||||
"Unclassified",
|
||||
"Mass storage controller",
|
||||
"Network controller",
|
||||
"Display controller",
|
||||
"Multimedia controller",
|
||||
"Memory controller",
|
||||
"Bridge device",
|
||||
"Simple communication controller",
|
||||
"Base system peripheral",
|
||||
"Input device controller",
|
||||
"Docking station",
|
||||
"Processor",
|
||||
"Serial bus controller",
|
||||
"Wireless controller",
|
||||
"Intelligent controller",
|
||||
"Satellite communication controller",
|
||||
"Encryption controller",
|
||||
"Signal processing controller",
|
||||
"Processing accelerator",
|
||||
"Non-essential instrumentation"
|
||||
};
|
||||
|
||||
const char *pci_class_string(uint16_t full_class) {
|
||||
// Better descriptions for well-known classes
|
||||
switch (full_class) {
|
||||
// Mass storage
|
||||
case 0x0101:
|
||||
return "IDE controller";
|
||||
case 0x0106:
|
||||
return "SATA controller";
|
||||
// Display controllers
|
||||
case 0x0300:
|
||||
return "VGA display controller";
|
||||
// Network controllers
|
||||
case 0x200:
|
||||
return "Ethernet controller";
|
||||
// Bridge devices
|
||||
case 0x0600:
|
||||
return "Host bridge";
|
||||
case 0x0601:
|
||||
return "ISA bridge";
|
||||
// Serial bus controllers
|
||||
case 0x0C03:
|
||||
return "USB controller";
|
||||
case 0x0C05:
|
||||
return "SMBus";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t class_code = full_class >> 8;
|
||||
if (class_code == 0xFF) {
|
||||
return "Unknown device";
|
||||
}
|
||||
|
||||
if (class_code >= sizeof(pci_class_code_strings) / sizeof(pci_class_code_strings[0])) {
|
||||
return "Reserved";
|
||||
}
|
||||
|
||||
return pci_class_code_strings[class_code];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
#include "sys/amd64/mm/phys.h"
|
||||
#include "sys/amd64/hw/irq.h"
|
||||
#include "sys/amd64/hw/io.h"
|
||||
#include "sys/net/eth.h"
|
||||
#include "sys/net/netdev.h"
|
||||
#include "sys/net/net.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/string.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/heap.h"
|
||||
#include "sys/attr.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define RTL8139_IDR0 0x00
|
||||
#define RTL8139_MAR0 0x08
|
||||
#define RTL8139_TSD(n) (0x10 + (n) * 4)
|
||||
#define RTL8139_TSAD(n) (0x20 + (n) * 4)
|
||||
#define RTL8139_RBSTART 0x30
|
||||
#define RTL8139_ERBCR 0x34
|
||||
#define RTL8139_ERSR 0x36
|
||||
#define RTL8139_CR 0x37
|
||||
#define RTL8139_CAPR 0x38
|
||||
#define RTL8139_CBR 0x3A
|
||||
#define RTL8139_IMR 0x3C
|
||||
#define RTL8139_ISR 0x3E
|
||||
#define RTL8139_TCR 0x40
|
||||
#define RTL8139_RCR 0x44
|
||||
#define RTL8139_TCTR 0x48
|
||||
#define RTL8139_MPC 0x4C
|
||||
#define RTL8139_CONFIG0 0x51
|
||||
#define RTL8139_CONFIG1 0x52
|
||||
#define RTL8139_MSR 0x58
|
||||
|
||||
#define RTL8139_TSD_OWN (1 << 13)
|
||||
|
||||
#define RTL8139_CR_RST (1 << 4)
|
||||
#define RTL8139_CR_RE (1 << 3)
|
||||
#define RTL8139_CR_TE (1 << 2)
|
||||
|
||||
#define RTL8139_RCR_WRAP (1 << 7)
|
||||
#define RTL8139_RCR_AB (1 << 0)
|
||||
#define RTL8139_RCR_AM (1 << 1)
|
||||
#define RTL8139_RCR_APM (1 << 2)
|
||||
#define RTL8139_RCR_AAP (1 << 3)
|
||||
#define RTL8139_RCR_ALL (RTL8139_RCR_AB | RTL8139_RCR_AM | RTL8139_RCR_APM | RTL8139_RCR_AAP)
|
||||
|
||||
#define RTL8139_ISR_TER (1 << 3)
|
||||
#define RTL8139_ISR_TOK (1 << 2)
|
||||
#define RTL8139_ISR_RER (1 << 1)
|
||||
#define RTL8139_ISR_ROK (1 << 0)
|
||||
|
||||
#define RTL8139_IMR_TER (1 << 3)
|
||||
#define RTL8139_IMR_TOK (1 << 2)
|
||||
#define RTL8139_IMR_RER (1 << 1)
|
||||
#define RTL8139_IMR_ROK (1 << 0)
|
||||
|
||||
struct rtl8139 {
|
||||
struct netdev net;
|
||||
|
||||
uint32_t bar0;
|
||||
// Receive buffer: 3 pages
|
||||
uint32_t page0;
|
||||
uint32_t tx_pages[4];
|
||||
size_t tx_sizes[4];
|
||||
// Bits:
|
||||
// 0..3: TX_BUSY Set by _tx(), cleared by _handle_tx(), means that
|
||||
// the descriptor cannot be allocated
|
||||
uint32_t tx_flag;
|
||||
uint32_t cbr_prev;
|
||||
uint32_t tx_number;
|
||||
};
|
||||
|
||||
static void rtl8139_handle_rx(struct rtl8139 *rtl);
|
||||
static void rtl8139_handle_tx(struct rtl8139 *rtl, int succ);
|
||||
|
||||
static uint32_t rtl8139_irq(void *ctx) {
|
||||
struct rtl8139 *rtl = ctx;
|
||||
uint16_t isr = inw(rtl->bar0 + RTL8139_ISR);
|
||||
|
||||
if (isr) {
|
||||
if (isr & RTL8139_ISR_ROK) {
|
||||
// Got a packet
|
||||
rtl8139_handle_rx(rtl);
|
||||
}
|
||||
if (isr & (RTL8139_ISR_TOK | RTL8139_ISR_TER)) {
|
||||
rtl8139_handle_tx(rtl, isr & RTL8139_ISR_TOK);
|
||||
}
|
||||
|
||||
// Implementation of RTL8139 in qemu differs
|
||||
// from the datasheet specification:
|
||||
// we need to WRITE to the register to clear the interrupts
|
||||
outw(rtl->bar0 + RTL8139_ISR, isr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_UNHANDLED;
|
||||
}
|
||||
|
||||
static void rtl8139_handle_rx(struct rtl8139 *rtl) {
|
||||
kdebug("Received a packet\n");
|
||||
uint16_t cbr = inw(rtl->bar0 + RTL8139_CBR);
|
||||
size_t packet_size;
|
||||
uint16_t packet_offset = rtl->cbr_prev;
|
||||
|
||||
if (cbr > rtl->cbr_prev) {
|
||||
packet_size = cbr - rtl->cbr_prev;
|
||||
} else {
|
||||
panic("TODO\n");
|
||||
}
|
||||
rtl->cbr_prev = cbr;
|
||||
|
||||
kdebug("Packet size: %u\n", packet_size);
|
||||
|
||||
char *pack = (char *) MM_VIRTUALIZE(rtl->page0) + packet_offset;
|
||||
struct eth_frame *eth_frame = (struct eth_frame *) &pack[4];
|
||||
|
||||
// First 4 octets - recv status and packet length
|
||||
uint16_t recv_status = ((uint16_t *) pack)[0];
|
||||
uint16_t recv_length = ((uint16_t *) pack)[1];
|
||||
|
||||
kdebug("Recv status: %u\n", recv_status);
|
||||
|
||||
//kdebug("Recv length = %u\n", recv_length);
|
||||
//kdebug("Packet size = %u\n", packet_size);
|
||||
//_assert(recv_length == (packet_size - 4));
|
||||
|
||||
eth_handle_frame(&rtl->net, eth_frame, recv_length);
|
||||
}
|
||||
|
||||
static void rtl8139_cmd_tx(struct rtl8139 *rtl) {
|
||||
kdebug("HW TX %d\n", rtl->tx_number);
|
||||
|
||||
uint8_t n = rtl->tx_number;
|
||||
|
||||
++rtl->tx_number;
|
||||
if (rtl->tx_number == 4) {
|
||||
rtl->tx_number = 0;
|
||||
}
|
||||
|
||||
outl(rtl->bar0 + RTL8139_TSD(n), rtl->tx_sizes[n]);
|
||||
}
|
||||
|
||||
static void rtl8139_handle_tx(struct rtl8139 *rtl, int s) {
|
||||
uint8_t prev = 3;
|
||||
if (rtl->tx_number != 0) {
|
||||
prev = rtl->tx_number - 1;
|
||||
}
|
||||
|
||||
kdebug("%s Tx on %d\n", s ? "Successful" : "Failed", prev);
|
||||
kdebug("Clearing TxD%d\n", prev);
|
||||
rtl->tx_flag &= ~(1 << prev);
|
||||
rtl->tx_sizes[prev] = 0;
|
||||
|
||||
kdebug("TX Done!\n");
|
||||
|
||||
// If after transmission we reached a new entry and it's
|
||||
// awaiting transmission - emit a Tx command
|
||||
while (rtl->tx_flag & (1 << rtl->tx_number)) {
|
||||
rtl8139_cmd_tx(rtl);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t rtl8139_alloc_tx(struct rtl8139 *rtl) {
|
||||
for (uint8_t i = rtl->tx_number; i < 4; ++i) {
|
||||
if (!(rtl->tx_flag & (1 << i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < rtl->tx_number; ++i) {
|
||||
if (!(rtl->tx_flag & (1 << i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static int rtl8139_tx(struct netdev *net, const void *packet, size_t size) {
|
||||
kdebug("Requested Tx\n");
|
||||
struct rtl8139 *rtl = (struct rtl8139 *) net;
|
||||
|
||||
uint8_t index = rtl8139_alloc_tx(rtl);
|
||||
// TODO: transmit queue for this?
|
||||
assert(index != 0xFF, "No free TSADx\n");
|
||||
|
||||
void *page = (void *) MM_VIRTUALIZE(rtl->tx_pages[index]);
|
||||
memcpy(page, packet, size);
|
||||
|
||||
rtl->tx_sizes[index] = size;
|
||||
|
||||
if (index == rtl->tx_number) {
|
||||
// Can tx right now
|
||||
rtl8139_cmd_tx(rtl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pci_rtl8139_init(pci_addr_t addr) {
|
||||
kdebug("Initializing RTL8139 at " PCI_FMTADDR "\n", PCI_VAADDR(addr));
|
||||
|
||||
struct rtl8139 *rtl = (struct rtl8139 *) kmalloc(sizeof(struct rtl8139));
|
||||
|
||||
uint32_t irq_config = pci_config_read_dword(addr, PCI_CONFIG_IRQ);
|
||||
|
||||
// Enable PCI busmastering
|
||||
uint32_t cmd = pci_config_read_dword(addr, PCI_CONFIG_CMD);
|
||||
cmd |= 1 << 2;
|
||||
pci_config_write_dword(addr, PCI_CONFIG_CMD, cmd);
|
||||
|
||||
rtl->bar0 = pci_config_read_dword(addr, PCI_CONFIG_BAR(0));
|
||||
assert(rtl->bar0 & 1, "RTL8139 BAR0 is not in I/O space\n");
|
||||
rtl->bar0 &= ~0x3;
|
||||
|
||||
// Board init
|
||||
// Set LWAKE + LWPTN to active high
|
||||
outb(rtl->bar0 + RTL8139_CONFIG1, 0);
|
||||
|
||||
// Software reset
|
||||
outb(rtl->bar0 + RTL8139_CR, RTL8139_CR_RST);
|
||||
|
||||
// Wait for the board to clear RST bit
|
||||
while (inb(rtl->bar0 + RTL8139_CR) & RTL8139_CR_RST);
|
||||
|
||||
// Initialize rx buffer
|
||||
rtl->page0 = amd64_phys_alloc_contiguous(3);
|
||||
rtl->cbr_prev = 0;
|
||||
assert(rtl->page0 != MM_NADDR, "Failed to allocate RxBuf\n");
|
||||
outl(rtl->bar0 + RTL8139_RBSTART, rtl->page0);
|
||||
|
||||
// Initialize tx buffers
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
rtl->tx_pages[i] = amd64_phys_alloc_page();
|
||||
rtl->tx_sizes[i] = 0;
|
||||
assert(rtl->tx_pages[i] != MM_NADDR, "Failed to allocate TxBuf\n");
|
||||
outl(rtl->bar0 + RTL8139_TSAD(i), rtl->tx_pages[i]);
|
||||
}
|
||||
rtl->tx_flag = 0;
|
||||
rtl->tx_number = 0;
|
||||
|
||||
// Accept all
|
||||
outl(rtl->bar0 + RTL8139_RCR, RTL8139_RCR_APM | RTL8139_RCR_AB | RTL8139_RCR_WRAP);
|
||||
|
||||
// Enable Rx/Tx
|
||||
outb(rtl->bar0 + RTL8139_CR, RTL8139_CR_TE | RTL8139_CR_RE);
|
||||
|
||||
uint8_t irq_pin = (irq_config >> 8) & 0xFF;
|
||||
if (irq_pin) {
|
||||
kdebug("Uses INT%c# IRQ pin\n\n", 'A' + irq_pin - 1);
|
||||
irq_add_pci_handler(addr, irq_pin - 1, rtl8139_irq, rtl);
|
||||
}
|
||||
|
||||
// Configure interrupt mask
|
||||
outw(rtl->bar0 + RTL8139_IMR, RTL8139_IMR_ROK | RTL8139_IMR_RER |
|
||||
RTL8139_IMR_TOK | RTL8139_IMR_TER);
|
||||
// Clear ISR
|
||||
outw(rtl->bar0 + RTL8139_ISR, 0);
|
||||
|
||||
// After the reset, controller uses Tx pair 0
|
||||
rtl->tx_number = 0;
|
||||
|
||||
// Setup a system network device
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
rtl->net.hwaddr[i] = inb(rtl->bar0 + RTL8139_IDR0 + i);
|
||||
}
|
||||
kdebug("RTL8139 MAC: " MAC_FMT "\n", MAC_VA(rtl->net.hwaddr));
|
||||
rtl->net.tx = rtl8139_tx;
|
||||
// TODO: call some kind of netdev_add to bind a name to the device
|
||||
|
||||
// XXX: for testing
|
||||
link_add_route_in(&rtl->net, 0x0002000A, 0x00FFFFFF, 0x0102000A);
|
||||
}
|
||||
|
||||
static __init void pci_rtl8139_register(void) {
|
||||
pci_add_device_driver(PCI_ID(0x10EC, 0x8139), pci_rtl8139_init);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
#include "sys/amd64/hw/pci/usb_uhci.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/attr.h"
|
||||
|
||||
//static void pci_usb_init(pci_addr_t addr) {
|
||||
// uint32_t class = pci_config_read_dword(addr, PCI_CONFIG_CLASS);
|
||||
// uint8_t prog_if = (class >> 8) & 0xFF;
|
||||
//
|
||||
// switch (prog_if) {
|
||||
//#if defined(PCI_UHCI_ENABLE)
|
||||
// case 0x00:
|
||||
// pci_usb_uhci_init(addr);
|
||||
// break;
|
||||
//#endif
|
||||
// default:
|
||||
// kwarn("Unsupported USB controller type: %02x\n", prog_if);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static __init void pci_register_usb(void) {
|
||||
// pci_add_class_driver(0x0C03, pci_usb_init);
|
||||
//}
|
||||
@@ -0,0 +1,494 @@
|
||||
#include "sys/amd64/hw/pci/usb_uhci.h"
|
||||
#include "sys/amd64/mm/phys.h"
|
||||
#include "sys/amd64/hw/io.h"
|
||||
#include "sys/usb/request.h"
|
||||
#include "sys/usb/device.h"
|
||||
#include "sys/usb/usb.h"
|
||||
#include "sys/string.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/heap.h"
|
||||
#include "sys/mm.h"
|
||||
|
||||
#define IO_USBCMD 0x00
|
||||
#define IO_USBSTS 0x02
|
||||
#define IO_USBINTR 0x04
|
||||
#define IO_FRNUM 0x06
|
||||
#define IO_FRBASEADD 0x08
|
||||
#define IO_SOFMOD 0x0C
|
||||
#define IO_PORTSC1 0x10
|
||||
#define IO_PORTSC2 0x12
|
||||
|
||||
#define USBCMD_RUN (1 << 0)
|
||||
#define USBCMD_HCRST (1 << 1)
|
||||
#define USBCMD_GRST (1 << 2)
|
||||
#define USBCMD_GSUS (1 << 3)
|
||||
#define USBCMD_GRES (1 << 4)
|
||||
#define USBCMD_SDBG (1 << 5)
|
||||
#define USBCMD_CONF (1 << 6)
|
||||
#define USBCMD_PMAX (1 << 7)
|
||||
|
||||
#define USBSTS_INTR (1 << 0)
|
||||
#define USBSTS_EINT (1 << 1)
|
||||
#define USBSTS_RESD (1 << 2)
|
||||
#define USBSTS_ESYS (1 << 3)
|
||||
#define USBSTS_EPROC (1 << 4)
|
||||
#define USBSTS_HALT (1 << 5)
|
||||
|
||||
#define USBINTR_TCRC (1 << 0)
|
||||
#define USBINTR_RES (1 << 1)
|
||||
#define USBINTR_DONE (1 << 2)
|
||||
#define USBINTR_SHORT (1 << 3)
|
||||
|
||||
#define USBPORT_CONN (1 << 0)
|
||||
#define USBPORT_CNCH (1 << 1)
|
||||
#define USBPORT_PEN (1 << 2)
|
||||
#define USBPORT_PENCH (1 << 3)
|
||||
#define USBPORT_RESD (1 << 6)
|
||||
#define USBPORT_LSPD (1 << 8)
|
||||
#define USBPORT_RST (1 << 9)
|
||||
#define USBPORT_SUSP (1 << 12)
|
||||
|
||||
#define USB_FRAME_EMPTY (1 << 0)
|
||||
#define USB_FRAME_QUEUE (1 << 1)
|
||||
#define USB_FRAME_FULLQ (1 << 2)
|
||||
|
||||
#define UHCI_MAX_QH 8
|
||||
#define UHCI_MAX_TD 32
|
||||
|
||||
#define UHCI_TD_USED (1 << 0)
|
||||
|
||||
struct uhci_td {
|
||||
uint32_t next_td;
|
||||
uint32_t status;
|
||||
uint32_t header;
|
||||
uint32_t buffer;
|
||||
union {
|
||||
uint32_t __sys0[4];
|
||||
struct {
|
||||
struct uhci_td *next;
|
||||
uint32_t flags;
|
||||
} system;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct uhci_qh {
|
||||
uint32_t head_td;
|
||||
uint32_t element_td;
|
||||
// System
|
||||
uint32_t list_index;
|
||||
struct usb_transfer *transfer;
|
||||
struct uhci_qh *prev, *next;
|
||||
struct uhci_td *head_real;
|
||||
uint32_t used;
|
||||
uintptr_t pad;
|
||||
};
|
||||
|
||||
struct uhci {
|
||||
struct usb_controller hc;
|
||||
|
||||
uint32_t iobase;
|
||||
uint32_t *frame_list;
|
||||
|
||||
uintptr_t pool_page;
|
||||
struct uhci_td *td_pool;
|
||||
struct uhci_qh *qh_pool;
|
||||
struct uhci_qh *qh_async;
|
||||
};
|
||||
|
||||
static struct uhci_td *uhci_alloc_td(struct uhci *hc) {
|
||||
for (size_t i = 0; i < UHCI_MAX_TD; ++i) {
|
||||
if (!(hc->td_pool[i].system.flags & UHCI_TD_USED)) {
|
||||
_assert(!(((uintptr_t) &hc->td_pool[i]) & 0xF));
|
||||
hc->td_pool[i].system.flags |= UHCI_TD_USED;
|
||||
return &hc->td_pool[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct uhci_qh *uhci_alloc_qh(struct uhci *hc) {
|
||||
for (size_t i = 0; i < UHCI_MAX_QH; ++i) {
|
||||
if (!hc->qh_pool[i].used) {
|
||||
_assert(!(((uintptr_t) &hc->qh_pool[i]) & 0xF));
|
||||
hc->qh_pool[i].used = 1;
|
||||
return &hc->qh_pool[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* static */ void uhci_data_init(struct uhci *data, uint32_t bar4) {
|
||||
uintptr_t frame_list_page = amd64_phys_alloc_contiguous(2);
|
||||
_assert(frame_list_page != MM_NADDR && frame_list_page < 0x100000000);
|
||||
uintptr_t pool_page = amd64_phys_alloc_page();
|
||||
_assert(pool_page != MM_NADDR && pool_page < 0x100000000);
|
||||
|
||||
data->iobase = bar4 & ~3;
|
||||
|
||||
data->frame_list = (uint32_t *) MM_VIRTUALIZE(frame_list_page);
|
||||
data->pool_page = pool_page;
|
||||
data->td_pool = (struct uhci_td *) MM_VIRTUALIZE(pool_page);
|
||||
data->qh_pool = (struct uhci_qh *) (MM_VIRTUALIZE(pool_page) + sizeof(struct uhci_td) * UHCI_MAX_TD);
|
||||
|
||||
memset(data->td_pool, 0, sizeof(struct uhci_td) * UHCI_MAX_TD);
|
||||
memset(data->qh_pool, 0, sizeof(struct uhci_qh) * UHCI_MAX_QH);
|
||||
|
||||
data->qh_async = uhci_alloc_qh(data);
|
||||
_assert(data->qh_async);
|
||||
data->qh_async->head_td = USB_FRAME_EMPTY;
|
||||
data->qh_async->element_td = USB_FRAME_EMPTY;
|
||||
data->qh_async->transfer = NULL;
|
||||
data->qh_async->prev = data->qh_async;
|
||||
data->qh_async->next = data->qh_async;
|
||||
|
||||
for (uint32_t i = 0; i < 2048; ++i) {
|
||||
data->frame_list[i] = USB_FRAME_QUEUE | MM_PHYS(data->qh_async);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t uhci_port_reset(struct uhci *uhci, int port) {
|
||||
uint16_t r;
|
||||
uint16_t reg = uhci->iobase + port * 2 + IO_PORTSC1;
|
||||
|
||||
r = inw(reg);
|
||||
r |= USBPORT_RST;
|
||||
outw(reg, r);
|
||||
for (size_t i = 0; i < 10000000; ++i);
|
||||
r = inw(reg);
|
||||
r &= ~USBPORT_RST;
|
||||
outw(reg, r);
|
||||
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
for (size_t _ = 0; _ < 10000000; ++_);
|
||||
|
||||
r = inw(reg);
|
||||
|
||||
// Port is not connected
|
||||
if (!(r & USBPORT_CONN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (r & (USBPORT_PENCH | USBPORT_CNCH)) {
|
||||
r &= ~(USBPORT_PENCH | USBPORT_CNCH);
|
||||
outw(reg, r);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if device is enabled
|
||||
if (r & USBPORT_PEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to enable the port
|
||||
r |= USBPORT_PEN;
|
||||
outw(reg, r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void uhci_start_qh(struct uhci *hc, struct uhci_qh *qh) {
|
||||
//kdebug("Assign QH: %p\n", qh);
|
||||
struct uhci_qh *list = hc->qh_async;
|
||||
_assert(list);
|
||||
struct uhci_qh *end = hc->qh_async->prev;
|
||||
_assert(end);
|
||||
|
||||
qh->head_td = USB_FRAME_EMPTY;
|
||||
end->head_td = (uint32_t) MM_PHYS(qh) | USB_FRAME_QUEUE;
|
||||
qh->prev = end;
|
||||
end->next = qh;
|
||||
list->prev = qh;
|
||||
qh->next = list;
|
||||
|
||||
//kdebug("---- After add ----\n");
|
||||
//uhci_dumpq(hc);
|
||||
//kdebug("---- After add ----\n");
|
||||
}
|
||||
|
||||
static void uhci_remove_qh(struct uhci *hc, struct uhci_qh *qh) {
|
||||
//kdebug("Unassign QH: %p\n", qh);
|
||||
struct uhci_qh *list = hc->qh_async;
|
||||
_assert(list);
|
||||
struct uhci_qh *prev = qh->prev;
|
||||
_assert(prev);
|
||||
|
||||
prev->head_td = qh->head_td;
|
||||
prev->next = list;
|
||||
list->prev = prev;
|
||||
|
||||
qh->next = NULL;
|
||||
qh->prev = NULL;
|
||||
|
||||
//kdebug("---- After remove ----\n");
|
||||
//uhci_dumpq(hc);
|
||||
//kdebug("---- After remove ----\n");
|
||||
}
|
||||
|
||||
static void uhci_free_td(struct uhci_td *td) {
|
||||
//kdebug("Free TD: %p\n", td);
|
||||
td->system.flags = 0;
|
||||
td->next_td = USB_FRAME_EMPTY;
|
||||
td->buffer = 0;
|
||||
}
|
||||
|
||||
static void uhci_free_qh(struct uhci_qh *qh) {
|
||||
//kdebug("Free QH: %p\n", qh);
|
||||
qh->transfer = 0;
|
||||
qh->used = 0;
|
||||
qh->head_real = NULL;
|
||||
qh->head_td = 0;
|
||||
qh->element_td = 0;
|
||||
}
|
||||
|
||||
static void uhci_process_qh(struct uhci *hc, struct uhci_qh *qh) {
|
||||
struct usb_transfer *t = qh->transfer;
|
||||
uint32_t elem_phys = qh->element_td & ~0xF;
|
||||
|
||||
if (!elem_phys) {
|
||||
t->flags = USB_TRANSFER_COMPLETE | USB_TRANSFER_SUCCESS;
|
||||
} else {
|
||||
struct uhci_td *td = (struct uhci_td *) MM_VIRTUALIZE(elem_phys);
|
||||
|
||||
if (td->status & (1 << 22)) {
|
||||
kwarn("Transfer descriptor is stalled\n");
|
||||
t->flags = USB_TRANSFER_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->flags & USB_TRANSFER_COMPLETE) {
|
||||
//kdebug("Transfer is complete\n");
|
||||
uhci_remove_qh(hc, qh);
|
||||
|
||||
(void) uhci_free_td;
|
||||
// Release TDs
|
||||
uint32_t td_ptr = 1234;
|
||||
struct uhci_td *td = qh->head_real;
|
||||
_assert(td);
|
||||
while (1) {
|
||||
td_ptr = td->next_td;
|
||||
|
||||
uhci_free_td(td);
|
||||
|
||||
if (!(td_ptr & ~0xF) || (td_ptr & USB_FRAME_EMPTY)) {
|
||||
break;
|
||||
}
|
||||
td = (struct uhci_td *) MM_VIRTUALIZE(td_ptr & ~0xF);
|
||||
}
|
||||
|
||||
uhci_free_qh(qh);
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_wait_qh(struct uhci *hc, struct uhci_qh *qh) {
|
||||
//kdebug("Wait for QH: %p\n", qh);
|
||||
struct usb_transfer *t = qh->transfer;
|
||||
while (!(t->flags & USB_TRANSFER_COMPLETE)) {
|
||||
uhci_process_qh(hc, qh);
|
||||
}
|
||||
kdebug("Transfer is complete\n");
|
||||
}
|
||||
|
||||
static void uhci_init_td(struct uhci_td *td, struct uhci_td *prev, uint8_t speed, uint8_t addr, uint8_t endp, uint16_t len, uint8_t packet_type, uint8_t toggle, void *buf) {
|
||||
// Packet header:
|
||||
// 0 .. 7 Packet type
|
||||
// 0x69 IN
|
||||
// 0xE1 OUT
|
||||
// 0x2D SETUP
|
||||
// 8 .. 14 Device address
|
||||
// 15 .. 18 Endpoint address
|
||||
// 19 Data toggle
|
||||
// 20 Reserved (0)
|
||||
// 21 .. 31 Maximum payload length - 1
|
||||
_assert((((uintptr_t) td) & 0xF) == 0);
|
||||
len = (len - 1) & 0x7FF;
|
||||
|
||||
if (prev) {
|
||||
prev->next_td = (uint32_t) MM_PHYS(td) | USB_FRAME_FULLQ;
|
||||
}
|
||||
|
||||
td->header = packet_type |
|
||||
(len << 21) |
|
||||
(toggle << 19) |
|
||||
(addr << 8) |
|
||||
(endp << 15);
|
||||
|
||||
// Status:
|
||||
// 23 Active
|
||||
// 26 Low speed
|
||||
// 27 .. 28 Error counter
|
||||
td->status = (1 << 23) |
|
||||
(3 << 27);
|
||||
|
||||
if (speed == USB_SPEED_LOW) {
|
||||
td->status |= 1 << 26;
|
||||
}
|
||||
|
||||
td->buffer = MM_PHYS(buf);
|
||||
}
|
||||
|
||||
static void uhci_device_interrupt(struct usb_device *dev, struct usb_transfer *t) {
|
||||
struct uhci *hc = dev->hc;
|
||||
|
||||
uint8_t speed = dev->speed;
|
||||
uint8_t addr = dev->address;
|
||||
uint8_t endp = dev->desc_endpoint.address & 0xF;
|
||||
|
||||
struct uhci_td *td = uhci_alloc_td(hc);
|
||||
_assert(td);
|
||||
|
||||
struct uhci_td *head = td;
|
||||
struct uhci_td *prev = NULL;
|
||||
|
||||
uint8_t toggle = dev->endpoint_toggle;
|
||||
uint8_t packet_type = 0x69;
|
||||
uint8_t packet_size = t->length;
|
||||
|
||||
uhci_init_td(td, prev, speed, addr, endp, toggle, packet_type, packet_size, t->data);
|
||||
|
||||
struct uhci_qh *qh = uhci_alloc_qh(hc);
|
||||
_assert(qh);
|
||||
qh->head_real = head;
|
||||
qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
|
||||
qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
|
||||
qh->transfer = t;
|
||||
|
||||
uhci_start_qh(hc, qh);
|
||||
}
|
||||
|
||||
static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) {
|
||||
struct uhci *hc = dev->hc;
|
||||
struct usb_request *req = t->request;
|
||||
uint8_t max_len = sizeof(struct usb_request);
|
||||
|
||||
struct uhci_td *td, *head, *prev;
|
||||
struct uhci_qh *qh;
|
||||
|
||||
td = uhci_alloc_td(hc);
|
||||
_assert(td);
|
||||
head = td;
|
||||
|
||||
uint8_t addr = dev->address;
|
||||
uint8_t endp = 0;
|
||||
uint8_t speed = dev->speed;
|
||||
uint16_t packet_size = sizeof(struct usb_request);
|
||||
uint8_t packet_type = 0x2D;
|
||||
uint8_t toggle = 0;
|
||||
|
||||
// Setup first packet (SETUP)
|
||||
uhci_init_td(td, NULL, speed, addr, endp, packet_size, packet_type, toggle, req);
|
||||
prev = td;
|
||||
|
||||
// Middle packets (IN)
|
||||
size_t off = 0;
|
||||
size_t rem = req->length;
|
||||
packet_type = (req->type & USB_REQUEST_TYPE_D2H) ? 0x69 : 0xE1;
|
||||
while (rem) {
|
||||
packet_size = MIN(rem, dev->max_packet);
|
||||
td = uhci_alloc_td(hc);
|
||||
_assert(td);
|
||||
|
||||
toggle ^= 1;
|
||||
uhci_init_td(td, prev, speed, addr, endp, packet_size, packet_type, toggle, t->data + off);
|
||||
|
||||
prev = td;
|
||||
off += packet_size;
|
||||
rem -= packet_size;
|
||||
}
|
||||
|
||||
// Ending packet (OUT)
|
||||
td = uhci_alloc_td(hc);
|
||||
_assert(td);
|
||||
|
||||
packet_type = (req->type & USB_REQUEST_TYPE_D2H) ? 0xE1 : 0x69;
|
||||
uhci_init_td(td, prev, speed, addr, endp, 0, packet_type, 1, NULL);
|
||||
|
||||
qh = uhci_alloc_qh(hc);
|
||||
_assert(qh);
|
||||
qh->head_real = head;
|
||||
qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
|
||||
qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ;
|
||||
qh->transfer = t;
|
||||
|
||||
uhci_start_qh(hc, qh);
|
||||
uhci_wait_qh(hc, qh);
|
||||
}
|
||||
|
||||
/* static */ void uhci_probe(struct uhci *uhci) {
|
||||
uint16_t portsc;
|
||||
|
||||
for (int port = 0; port < 2; ++port) {
|
||||
portsc = uhci_port_reset(uhci, port);
|
||||
|
||||
if (portsc & USBPORT_PEN) {
|
||||
kdebug("Port %d is enabled\n", port);
|
||||
|
||||
// Setup a new USB device
|
||||
struct usb_device *dev = usb_device_create();
|
||||
dev->hc = uhci;
|
||||
dev->max_packet = 8;
|
||||
dev->hc_control = uhci_device_control;
|
||||
dev->hc_interrupt = uhci_device_interrupt;
|
||||
dev->address = 0;
|
||||
dev->endpoint_toggle = 0;
|
||||
|
||||
if (portsc & USBPORT_LSPD) {
|
||||
dev->speed = USB_SPEED_LOW;
|
||||
} else {
|
||||
dev->speed = USB_SPEED_FULL;
|
||||
}
|
||||
|
||||
usb_device_init(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void uhci_poll(struct usb_controller *_hc) {
|
||||
struct uhci *hc = (struct uhci *) _hc;
|
||||
|
||||
struct uhci_qh *qh = hc->qh_async;
|
||||
struct uhci_qh *next;
|
||||
while (qh) {
|
||||
next = qh->next;
|
||||
|
||||
if (qh->transfer) {
|
||||
uhci_process_qh(hc, qh);
|
||||
break;
|
||||
}
|
||||
|
||||
qh = next;
|
||||
}
|
||||
}
|
||||
|
||||
//void pci_usb_uhci_init(pci_addr_t addr) {
|
||||
//}
|
||||
// uint32_t bar4;
|
||||
// struct uhci *hc = kmalloc(sizeof(struct uhci));
|
||||
// _assert(hc);
|
||||
//
|
||||
// kdebug("Initializing USB UHCI at " PCI_FMTADDR "\n", PCI_VAADDR(addr));
|
||||
//
|
||||
// bar4 = pci_config_read_dword(addr, PCI_CONFIG_BAR(4));
|
||||
// _assert(bar4 & 1);
|
||||
// uhci_data_init(hc, bar4);
|
||||
// kdebug("Base addr %p\n", hc->frame_list);
|
||||
//
|
||||
// // Disable IRQs
|
||||
// outw(hc->iobase + IO_USBINTR, 0);
|
||||
// // Setup frame lists
|
||||
// outl(hc->iobase + IO_FRBASEADD, (uint32_t) MM_PHYS(hc->frame_list));
|
||||
// outw(hc->iobase + IO_FRNUM, 0);
|
||||
// outw(hc->iobase + IO_SOFMOD, 0x40);
|
||||
// // Clear status
|
||||
// outw(hc->iobase + IO_USBSTS, 0xFFFF);
|
||||
// // Enable controller
|
||||
// outw(hc->iobase + IO_USBCMD, USBCMD_RUN);
|
||||
//
|
||||
// uhci_probe(hc);
|
||||
//
|
||||
// hc->hc.spec = USB_SPEC_UHCI;
|
||||
// hc->hc.hc_poll = uhci_poll;
|
||||
//
|
||||
// usb_controller_add((struct usb_controller *) hc);
|
||||
//}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "sys/amd64/loader/multiboot.h"
|
||||
#include "sys/amd64/asm/asm_irq.h"
|
||||
#include "sys/amd64/hw/pci/pci.h"
|
||||
#include "sys/amd64/hw/rs232.h"
|
||||
#include "sys/amd64/smp/smp.h"
|
||||
#include "sys/amd64/syscall.h"
|
||||
@@ -124,6 +125,16 @@ void kernel_main(struct amd64_loader_data *data) {
|
||||
kinfo("yggdrasil " KERNEL_VERSION_STR "\n");
|
||||
|
||||
amd64_apic_init();
|
||||
rtc_init();
|
||||
// Setup system time
|
||||
struct tm t;
|
||||
rtc_read(&t);
|
||||
system_boot_time = mktime(&t);
|
||||
kinfo("Boot time: %04u-%02u-%02u %02u:%02u:%02u\n",
|
||||
t.tm_year, t.tm_mon, t.tm_mday,
|
||||
t.tm_hour, t.tm_min, t.tm_sec);
|
||||
|
||||
pci_init();
|
||||
|
||||
vfs_init();
|
||||
tty_init();
|
||||
|
||||
Reference in New Issue
Block a user