[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[tyndur-devel] [PATCH 4/4] ata: Unterstuetzung fuer DMA
+ ata: Unterstuetzung fuer DMA
Signed-off-by: Antoine Kaufmann <toni@xxxxxxxxxx>
---
build/config/grub_hd.cfg | 1 +
src/modules/cdi/ata/ata.c | 68 ++++++++++++++++-------
src/modules/cdi/ata/device.c | 26 ++++++++-
src/modules/cdi/ata/device.h | 53 ++++++++++++++++--
src/modules/cdi/ata/main.c | 69 ++++++++++++++++++++---
src/modules/cdi/ata/request.c | 122 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 302 insertions(+), 37 deletions(-)
diff --git a/build/config/grub_hd.cfg b/build/config/grub_hd.cfg
index f29cd6a..cd0f4f2 100644
--- a/build/config/grub_hd.cfg
+++ b/build/config/grub_hd.cfg
@@ -1,6 +1,7 @@
title tyndur
kernel /boot/tyndur debug=s
module /modules/init boot=file:/
+module /modules/pci
module /modules/ata
module /modules/ext2
module /modules/console servmgr:/term servmgr:/term
diff --git a/src/modules/cdi/ata/ata.c b/src/modules/cdi/ata/ata.c
index 14e777b..0646be9 100644
--- a/src/modules/cdi/ata/ata.c
+++ b/src/modules/cdi/ata/ata.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ * Copyright (c) 2007-2009 The tyndur Project. All rights reserved.
*
* This code is derived from software contributed to the tyndur Project
* by Antoine Kaufmann.
@@ -86,6 +86,11 @@ int ata_drv_identify(struct ata_device* dev)
dev->dev.storage.block_count = id.lba_sector_count;
}
+ // Pruefen ob DMA unterstuetzt wird
+ if (id.capabilities.dma) {
+ dev->dma = 1;
+ }
+
// Wenn keiner der LBA-Modi unterstuetzt wird, muss abgebrochen werden, da
// CHS noch nicht implementiert ist.
if (!dev->lba48 && !dev->lba28) {
@@ -122,38 +127,59 @@ static int ata_drv_rw_sectors(struct ata_device* dev, int direction,
uint16_t current_count;
void* current_buffer = buffer;
uint64_t lba = start;
-
+ int max_count;
// Anzahl der Sektoren die noch uebrig sind
size_t count_left = count;
- // Solange wie noch Sektoren uebrig sind, wird gelesen
- while (count_left > 0) {
- // Entscheiden wieviele Sektoren im aktuellen Durchlauf gelesen werden
- if (count_left > 256) {
- current_count = 256;
+
+
+ // Request vorbereiten
+ request.dev = dev;
+ request.flags.poll = 0;
+ request.flags.ata = 0;
+ request.flags.lba = 1;
+
+ // Richtung festlegen
+ if (direction == 0) {
+ request.flags.direction = READ;
+ } else {
+ request.flags.direction = WRITE;
+ }
+
+
+ if (dev->dma && dev->controller->dma_use) {
+ // DMA
+ max_count = ATA_DMA_MAXSIZE / ATA_SECTOR_SIZE;
+ if (direction) {
+ request.registers.ata.command = WRITE_SECTORS_DMA;
} else {
- current_count = count_left;
+ request.registers.ata.command = READ_SECTORS_DMA;
+ }
+ request.protocol = DMA;
+ } else {
+ // PIO
+ max_count = 256;
+ if (direction) {
+ request.registers.ata.command = WRITE_SECTORS;
+ } else {
+ request.registers.ata.command = READ_SECTORS;
}
-
- // Request vorbereiten
- request.dev = dev;
- // TODO: DMA, UltraDMA...
request.protocol = PIO;
- // FIXME
+ // Mit PIO pollen wir lieber, da IRQs dort _wirklich langsam_ sind
request.flags.poll = 1;
- request.flags.ata = 0;
- request.flags.lba = 1;
+ }
- // Richtung festlegen
- if (direction == 0) {
- request.flags.direction = READ;
- request.registers.ata.command = READ_SECTORS;
+ // Solange wie noch Sektoren uebrig sind, wird gelesen
+ while (count_left > 0) {
+ // Entscheiden wieviele Sektoren im aktuellen Durchlauf gelesen werden
+ if (count_left > max_count) {
+ current_count = max_count;
} else {
- request.flags.direction = WRITE;
- request.registers.ata.command = WRITE_SECTORS;
+ current_count = count_left;
}
+
// Achtung: Beim casten nach uint8_t wird bei 256 Sektoren eine 0.
// Das macht aber nichts, da in der Spezifikation festgelegt ist,
// dass 256 Sektoren gelesen werden sollen, wenn im count-Register
diff --git a/src/modules/cdi/ata/device.c b/src/modules/cdi/ata/device.c
index 7c896b8..4ec7696 100644
--- a/src/modules/cdi/ata/device.c
+++ b/src/modules/cdi/ata/device.c
@@ -231,8 +231,13 @@ void ata_init_controller(struct ata_controller* controller)
}
if (cdi_ioports_alloc(controller->port_ctl_base, 1) != 0) {
DEBUG("Fehler beim allozieren der I/O-Ports\n");
- cdi_ioports_free(controller->port_cmd_base, 8);
- return;
+ goto error_free_cmdbase;
+ }
+ if (controller->port_bmr_base &&
+ (cdi_ioports_alloc(controller->port_bmr_base, 8) != 0))
+ {
+ DEBUG("ata: Fehler beim allozieren der I/O-Ports\n");
+ goto error_free_ctlbase;
}
// Da NIEN nicht ueberall sauber funktioniert, muss jetzt trotzdem schon
@@ -315,6 +320,23 @@ void ata_init_controller(struct ata_controller* controller)
free(dev);
}
}
+
+ // Abschliessend wird noch DMA vorbereitet, wenn moeglich
+ if (controller->port_bmr_base) {
+ cdi_alloc_phys_mem(sizeof(uint64_t), (void**) &controller->prdt_virt,
+ (void**) &controller->prdt_phys);
+ cdi_alloc_phys_mem(ATA_DMA_MAXSIZE, (void**) &controller->dma_buf_virt,
+ (void**) &controller->dma_buf_phys);
+
+ controller->dma_use = 1;
+ }
+
+ return;
+
+error_free_ctlbase:
+ cdi_ioports_free(controller->port_ctl_base, 1);
+error_free_cmdbase:
+ cdi_ioports_free(controller->port_cmd_base, 8);
}
void ata_remove_controller(struct ata_controller* controller)
diff --git a/src/modules/cdi/ata/device.h b/src/modules/cdi/ata/device.h
index 54dfb80..36a86d1 100644
--- a/src/modules/cdi/ata/device.h
+++ b/src/modules/cdi/ata/device.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ * Copyright (c) 2007-2009 The tyndur Project. All rights reserved.
*
* This code is derived from software contributed to the tyndur Project
* by Antoine Kaufmann.
@@ -48,6 +48,10 @@
#define ATAPI_ENABLE
+#define PCI_CLASS_ATA 0x01
+#define PCI_SUBCLASS_ATA 0x01
+#define PCI_VENDOR_VIA 0x1106
+
#define ATA_PRIMARY_CMD_BASE 0x1F0
#define ATA_PRIMARY_CTL_BASE 0x3F6
#define ATA_PRIMARY_IRQ 14
@@ -76,6 +80,7 @@
// Diese Register werden ueber die port_cmd_base angesprochen
#define REG_DATA 0x0
#define REG_ERROR 0x1
+#define REG_FEATURES 0x1
#define REG_SEC_CNT 0x2
#define REG_LBA_LOW 0x3
#define REG_LBA_MID 0x4
@@ -110,6 +115,24 @@
#define CONTROL_SRST (1 << 2)
#define CONTROL_NIEN (1 << 1)
+
+// Busmaster-Register (in port_bmr_base)
+#define BMR_COMMAND 0x0
+#define BMR_STATUS 0x2
+#define BMR_PRDT 0x4
+
+// Bits im Busmaster Command Register
+#define BMR_CMD_START (1 << 0)
+#define BMR_CMD_WRITE (1 << 3)
+
+// Bits im Busmaster Status Register
+#define BMR_STATUS_ERROR (1 << 1)
+#define BMR_STATUS_IRQ (1 << 2)
+
+/// Maximale Groesse eines DMA-Transfers
+#define ATA_DMA_MAXSIZE (64 * 1024)
+
+
// Debug
#ifdef DEBUG_ENABLE
#define DEBUG(fmt, ...) printf("ata: " fmt, __VA_ARGS__)
@@ -320,7 +343,10 @@ struct ata_device {
// 1 Wenn das Geraet lba28 unterstuetzt
uint8_t lba28;
-
+
+ /// 1 wenn das Geraet DMA unterstuetzt
+ uint8_t dma;
+
// Funktionen fuer den Zugriff auf dieses Geraet
int (*read_sectors) (struct ata_device* dev, uint64_t start, size_t count,
void* dest);
@@ -335,14 +361,26 @@ struct ata_controller {
uint8_t id;
uint16_t port_cmd_base;
uint16_t port_ctl_base;
+ uint16_t port_bmr_base;
uint16_t irq;
- uint16_t irq_cnt;
// Wird auf 1 gesetzt wenn IRQs benutzt werden sollen, also das NIEN-Bit im
// Control register nicht aktiviert ist.
int irq_use;
+ /// Wird auf 1 gesetzt wenn DMA benutzt werden darf
+ int dma_use;
// HACKKK ;-)
struct ata_device irq_dev;
+
+
+ /// Physische Adresse der Physical Region Descriptor Table (fuer DMA)
+ uint64_t* prdt_phys;
+ /// Virtuelle Adresse der Physical Region Descriptor Table (fuer DMA)
+ uint64_t* prdt_virt;
+ /// Physische Adresse des DMA-Puffers
+ void* dma_buf_phys;
+ /// Virtuelle Adresse des DMA-Puffers
+ void* dma_buf_virt;
};
struct ata_request {
@@ -350,7 +388,8 @@ struct ata_request {
enum {
NON_DATA,
- PIO
+ PIO,
+ DMA,
} protocol;
// Flags fuer die Uebertragung
@@ -378,10 +417,14 @@ struct ata_request {
IDENTIFY_PACKET_DEVICE = 0xA1,
PACKET = 0xA0,
READ_SECTORS = 0x20,
- WRITE_SECTORS = 0x30
+ READ_SECTORS_DMA = 0xC8,
+ SET_FEATURES = 0xEF,
+ WRITE_SECTORS = 0x30,
+ WRITE_SECTORS_DMA = 0xCA,
} command;
uint8_t count;
uint64_t lba;
+ uint8_t features;
} ata;
} registers;
diff --git a/src/modules/cdi/ata/main.c b/src/modules/cdi/ata/main.c
index 750f7b2..97b470d 100644
--- a/src/modules/cdi/ata/main.c
+++ b/src/modules/cdi/ata/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ * Copyright (c) 2007-2009 The tyndur Project. All rights reserved.
*
* This code is derived from software contributed to the tyndur Project
* by Antoine Kaufmann.
@@ -39,6 +39,7 @@
#include "cdi/storage.h"
#include "cdi/lists.h"
+#include "cdi/pci.h"
#include "device.h"
@@ -48,18 +49,18 @@ static const char* driver_storage_name = "ata";
static const char* driver_scsi_name = "atapi";
static cdi_list_t controller_list = NULL;
-static void ata_driver_init(void);
+static void ata_driver_init(int argc, char* argv[]);
static void ata_driver_destroy(struct cdi_driver* driver);
static void atapi_driver_destroy(struct cdi_driver* driver);
#ifdef CDI_STANDALONE
-int main(void)
+int main(int argc, char* argv[])
#else
-int init_ata(void)
+int init_ata(int argc, char* argv[])
#endif
{
cdi_init();
- ata_driver_init();
+ ata_driver_init(argc, argv);
cdi_storage_driver_register((struct cdi_storage_driver*) &driver_storage);
cdi_scsi_driver_register((struct cdi_scsi_driver*) &driver_scsi);
@@ -73,9 +74,14 @@ int init_ata(void)
/**
* Initialisiert die Datenstrukturen fuer den sis900-Treiber
*/
-static void ata_driver_init()
+static void ata_driver_init(int argc, char* argv[])
{
struct ata_controller* controller;
+ uint16_t busmaster_regbase = 0;
+ struct cdi_pci_device* pci_dev;
+ cdi_list_t pci_devices;
+ int i;
+ int j;
// Konstruktor der Vaterklasse
cdi_storage_driver_init((struct cdi_storage_driver*) &driver_storage);
@@ -99,11 +105,55 @@ static void ata_driver_init()
// Liste mit Controllern initialisieren
controller_list = cdi_list_create();
-
+
+
+ // PCI-Geraet fuer Controller finden
+ pci_devices = cdi_list_create();
+ cdi_pci_get_all_devices(pci_devices);
+ for (i = 0; (pci_dev = cdi_list_get(pci_devices, i)) && !busmaster_regbase;
+ i++)
+ {
+ struct cdi_pci_resource* res;
+
+ if ((pci_dev->class_id != PCI_CLASS_ATA) ||
+ (pci_dev->subclass_id != PCI_SUBCLASS_ATA))
+ {
+ continue;
+ }
+
+ // Jetzt noch die Ressource finden mit den Busmaster-Registern
+ // TODO: Das funktioniert so vermutlich nicht ueberall, da es
+ // warscheinlich auch Kontroller mit nur einem Kanal gibt und solche bei
+ // denen die BM-Register im Speicher gemappt sind
+ for (j = 0; (res = cdi_list_get(pci_dev->resources, j)); j++) {
+ if ((res->type == CDI_PCI_IOPORTS) && (res->length == 16)) {
+ busmaster_regbase = res->start;
+ break;
+ }
+ }
+ }
+
+ // Kaputte VIA-Kontroller sollten nur PIO benutzen, da es bei DMA zu
+ // haengern kommt. Dabei handelt es sich um den Kontroller 82C686B
+ if (pci_dev && (pci_dev->vendor_id == PCI_VENDOR_VIA) &&
+ (pci_dev->device_id == 0x686))
+ {
+ busmaster_regbase = 0;
+ } else {
+ // Wenn der nodma-Parameter uebergeben wurde, deaktivieren wir dma auch
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "nodma")) {
+ busmaster_regbase = 0;
+ break;
+ }
+ }
+ }
+
// Primaeren Controller vorbereiten
- controller = malloc(sizeof(*controller));
+ controller = calloc(1, sizeof(*controller));
controller->port_cmd_base = ATA_PRIMARY_CMD_BASE;
controller->port_ctl_base = ATA_PRIMARY_CTL_BASE;
+ controller->port_bmr_base = busmaster_regbase;
controller->irq = ATA_PRIMARY_IRQ;
controller->id = 0;
controller->storage = (struct cdi_storage_driver*) &driver_storage;
@@ -112,9 +162,10 @@ static void ata_driver_init()
cdi_list_push(controller_list, controller);
// Sekundaeren Controller vorbereiten
- controller = malloc(sizeof(*controller));
+ controller = calloc(1, sizeof(*controller));
controller->port_cmd_base = ATA_SECONDARY_CMD_BASE;
controller->port_ctl_base = ATA_SECONDARY_CTL_BASE;
+ controller->port_bmr_base = (busmaster_regbase ? busmaster_regbase : 0);
controller->irq = ATA_SECONDARY_IRQ;
controller->id = 1;
controller->storage = (struct cdi_storage_driver*) &driver_storage;
diff --git a/src/modules/cdi/ata/request.c b/src/modules/cdi/ata/request.c
index 90e0d67..957661e 100644
--- a/src/modules/cdi/ata/request.c
+++ b/src/modules/cdi/ata/request.c
@@ -120,6 +120,9 @@ static int ata_request_command(struct ata_request* request)
// TODO: HOB
ata_reg_outb(ctrl, REG_CONTROL, control);
+ // Features-Register schreiben
+ ata_reg_outb(ctrl, REG_FEATURES, request->registers.ata.features);
+
// Count-Register schrieben
ata_reg_outb(ctrl, REG_SEC_CNT, request->registers.ata.count);
@@ -415,6 +418,109 @@ int ata_protocol_pio_out(struct ata_request* request)
}
/**
+ * Initialisiert DMA fuer einen Transfer
+ */
+static int ata_request_dma_init(struct ata_request* request)
+{
+ struct ata_device* dev = request->dev;
+ struct ata_controller* ctrl = dev->controller;
+ uint64_t size = request->block_size * request->block_count;
+
+ *ctrl->prdt_virt = (uint32_t) ctrl->dma_buf_phys;
+ // Groesse nicht ueber 64K, 0 == 64K
+ *ctrl->prdt_virt |= (size & (ATA_DMA_MAXSIZE - 1)) << 32L;
+ // Letzter Eintrag in PRDT
+ *ctrl->prdt_virt |= (uint64_t) 1L << 63L;
+
+ // Die laufenden Transfers anhalten
+ cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 0);
+ cdi_outb(ctrl->port_bmr_base + BMR_STATUS,
+ cdi_inb(ctrl->port_bmr_base + BMR_STATUS) | BMR_STATUS_ERROR |
+ BMR_STATUS_IRQ);
+
+ // Adresse der PRDT eintragen
+ cdi_outl(ctrl->port_bmr_base + BMR_PRDT, (uint32_t) ctrl->prdt_phys);
+
+ if (request->flags.direction != READ) {
+ memcpy(ctrl->dma_buf_virt, request->buffer, size);
+ }
+ return 1;
+}
+
+/**
+ * Verarbeitet einen ATA-Request bei dem Daten ueber DMA uebertragen werden
+ * sollen
+ */
+static int ata_protocol_dma(struct ata_request* request)
+{
+ struct ata_device* dev = request->dev;
+ struct ata_controller* ctrl = dev->controller;
+
+ // Aktueller Status im Protokoll
+ enum {
+ IRQ_WAIT,
+ CHECK_STATUS,
+ } state;
+
+ // Wozu das lesen und dieser Register gut ist, weiss ich nicht, doch ich
+ // habe es so in verschiedenen Treibern gesehen, deshalb gehe ich mal davon
+ // aus, dass es notwendig ist.
+ cdi_inb(ctrl->port_bmr_base + BMR_COMMAND);
+ cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
+ // Busmastering starten
+ if (request->flags.direction != READ) {
+ cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, BMR_CMD_START |
+ BMR_CMD_WRITE);
+ } else {
+ cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, BMR_CMD_START);
+ }
+ cdi_inb(ctrl->port_bmr_base + BMR_COMMAND);
+ cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
+
+
+ if (request->flags.poll) {
+ state = CHECK_STATUS;
+ } else {
+ state = IRQ_WAIT;
+ }
+
+ while (1) {
+ switch (state) {
+ case IRQ_WAIT:
+ // Auf IRQ warten
+ if (!ata_wait_irq(ctrl, ATA_IRQ_TIMEOUT)) {
+ request->error = IRQ_TIMEOUT;
+ DEBUG("dma IRQ-Timeout\n");
+ return 0;
+ }
+
+ state = CHECK_STATUS;
+ break;
+
+ case CHECK_STATUS: {
+ uint8_t status = ata_reg_inb(ctrl, REG_STATUS);
+
+ // Sicherstellen dass der Transfer abgeschlossen ist. Bei
+ // polling ist das hier noch nicht umbedingt der Fall.
+ if ((status & (STATUS_BSY | STATUS_DRQ)) == 0) {
+ cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
+ cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 0);
+ goto out_success;
+ }
+ break;
+ }
+ }
+ }
+
+out_success:
+ if (request->flags.direction == READ) {
+ memcpy(request->buffer, ctrl->dma_buf_virt,
+ request->block_size * request->block_count);
+ }
+ return 1;
+}
+
+/**
* Fuehrt einen ATA-Request aus.
*
* @return 1 Wenn der Request erfolgreich bearbeitet wurde, 0 sonst
@@ -422,6 +528,13 @@ int ata_protocol_pio_out(struct ata_request* request)
int ata_request(struct ata_request* request)
{
// printf("ata: [%d:%d] Request command=%x count=%x lba=%llx protocol=%x\n", request->dev->controller->id, request->dev->id, request->registers.ata.command, request->registers.ata.count, request->registers.ata.lba, request->protocol);
+
+ // Bei einem DMA-Request muss der DMA-Controller erst vorbereitet werden
+ if ((request->protocol == DMA) && !ata_request_dma_init(request)) {
+ DEBUG("ata: Fehler beim Initialisieren des DMA-Controllers\n");
+ return 0;
+ }
+
// Befehl ausfuehren
if (!ata_request_command(request)) {
DEBUG("Fehler bei der Befehlsausfuehrung\n");
@@ -447,6 +560,15 @@ int ata_request(struct ata_request* request)
return 0;
}
break;
+
+ case DMA:
+ if (!ata_protocol_dma(request)) {
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
}
return 1;
}
--
1.6.0.6