idedrv Implement DMA reading/writing with interrupts
This commit is contained in:
@@ -113,6 +113,16 @@ static void ide_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rct
|
||||
|
||||
struct idedrv_request* req = idedrv->current_req;
|
||||
|
||||
if (idedrv->bm_support) {
|
||||
uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
|
||||
|
||||
if (!(bm_status & IDE_DMA_STATUS_INTR))
|
||||
return;
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
|
||||
bm_status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
}
|
||||
|
||||
if (req == NULL) {
|
||||
(void)inb(idedrv->io + IDE_REG_STATUS);
|
||||
return;
|
||||
@@ -120,6 +130,13 @@ static void ide_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rct
|
||||
|
||||
uint8_t status = inb(idedrv->io + IDE_REG_STATUS);
|
||||
|
||||
if (idedrv->bm_support) {
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
|
||||
atomic_store(&req->done, 1);
|
||||
idedrv->current_req = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((status & (IDE_ERR | IDE_DF))) {
|
||||
atomic_store(&req->done, 1);
|
||||
idedrv->current_req = NULL;
|
||||
@@ -280,10 +297,10 @@ DEFINE_DEVICE_INIT(idedrv_init) {
|
||||
idedrv->prdt_entry_count = PAGE_SIZE / sizeof(struct ide_prd_entry);
|
||||
idedrv->prdt = (struct ide_prd_entry*)((uintptr_t)hhdm->offset + idedrv->prdt_phys);
|
||||
|
||||
idedrv->bounce_buffer_phys = pmm_alloc_aligned(16, 16);
|
||||
idedrv->bounce_buffer_phys = pmm_alloc_aligned(64, 16);
|
||||
|
||||
if (idedrv->bounce_buffer_phys >= 0xFFFFFFFF) {
|
||||
pmm_free(idedrv->bounce_buffer_phys, 16);
|
||||
pmm_free(idedrv->bounce_buffer_phys, 64);
|
||||
pmm_free(idedrv->prdt_phys, 1);
|
||||
free(idedrv);
|
||||
return false;
|
||||
@@ -312,8 +329,8 @@ DEFINE_DEVICE_FINI(idedrv_fini) {
|
||||
irq_detach(idedrv->irq);
|
||||
|
||||
if (idedrv->bm_support) {
|
||||
pmm_free(idedrv->bounce_buffer_phys, 16);
|
||||
pmm_free(idedrv->prdt_phys, 16);
|
||||
pmm_free(idedrv->bounce_buffer_phys, 64);
|
||||
pmm_free(idedrv->prdt_phys, 1);
|
||||
}
|
||||
|
||||
free(idedrv);
|
||||
@@ -334,10 +351,42 @@ static int idedrv_do_read_irqs(struct idedrv* idedrv, size_t sector, size_t sect
|
||||
|
||||
idedrv->current_req = req;
|
||||
|
||||
if (idedrv->bm_support) {
|
||||
size_t rem = sector_count * idedrv->sector_size;
|
||||
uint32_t phys = idedrv->bounce_buffer_phys;
|
||||
size_t prd_idx = 0;
|
||||
|
||||
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
|
||||
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
|
||||
idedrv->prdt[prd_idx].phys_addr = phys;
|
||||
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
|
||||
|
||||
rem -= chunk;
|
||||
phys += chunk;
|
||||
|
||||
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
|
||||
prd_idx++;
|
||||
}
|
||||
|
||||
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08);
|
||||
|
||||
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
|
||||
ide_prepare(idedrv, sector, sector_count, true);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ_DMA48 : IDE_CMD_READ_DMA28;
|
||||
outb(idedrv->io + IDE_REG_CMD, cmd);
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08 | 0x01);
|
||||
} else {
|
||||
ide_prepare(idedrv, sector, sector_count, true);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ48 : IDE_CMD_READ28;
|
||||
outb(idedrv->io + IDE_REG_CMD, cmd);
|
||||
}
|
||||
|
||||
biglock_unlock();
|
||||
|
||||
@@ -348,36 +397,39 @@ static int idedrv_do_read_irqs(struct idedrv* idedrv, size_t sector, size_t sect
|
||||
|
||||
free(req);
|
||||
|
||||
if (idedrv->bm_support)
|
||||
memcpy(buffer, idedrv->bounce_buffer, sector_count * idedrv->sector_size);
|
||||
|
||||
return ST_OK;
|
||||
}
|
||||
|
||||
static int idedrv_do_read_no_irqs(struct idedrv* idedrv, size_t sector, size_t sector_count,
|
||||
void* buffer) {
|
||||
if (idedrv->bm_support) {
|
||||
size_t sectors_done = 0;
|
||||
size_t rem = sector_count * idedrv->sector_size;
|
||||
uint32_t phys = idedrv->bounce_buffer_phys;
|
||||
size_t prd_idx = 0;
|
||||
|
||||
while (sectors_done < sector_count) {
|
||||
size_t chunk_sectors = sector_count - sectors_done;
|
||||
size_t max_chunk = (16 * PAGE_SIZE) / idedrv->sector_size;
|
||||
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
|
||||
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
|
||||
idedrv->prdt[prd_idx].phys_addr = phys;
|
||||
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
|
||||
|
||||
if (chunk_sectors > max_chunk)
|
||||
chunk_sectors = max_chunk;
|
||||
rem -= chunk;
|
||||
phys += chunk;
|
||||
|
||||
size_t byte_count = chunk_sectors * idedrv->sector_size;
|
||||
|
||||
idedrv->prdt[0].phys_addr = (uint32_t)idedrv->bounce_buffer_phys;
|
||||
idedrv->prdt[0].size = (uint16_t)byte_count;
|
||||
idedrv->prdt[0].rsvd_eot = 0x8000;
|
||||
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
|
||||
prd_idx++;
|
||||
}
|
||||
|
||||
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08);
|
||||
|
||||
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
|
||||
status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
|
||||
ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false);
|
||||
ide_prepare(idedrv, sector, sector_count, false);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ_DMA48 : IDE_CMD_READ_DMA28;
|
||||
outb(idedrv->io + IDE_REG_CMD, cmd);
|
||||
@@ -402,11 +454,7 @@ static int idedrv_do_read_no_irqs(struct idedrv* idedrv, size_t sector, size_t s
|
||||
|
||||
inb(idedrv->io + IDE_REG_STATUS);
|
||||
|
||||
memcpy((void*)((uint16_t*)buffer + (sectors_done * (idedrv->sector_size / 2))),
|
||||
idedrv->bounce_buffer, byte_count);
|
||||
|
||||
sectors_done += chunk_sectors;
|
||||
}
|
||||
memcpy(buffer, idedrv->bounce_buffer, sector_count * idedrv->sector_size);
|
||||
} else {
|
||||
ide_prepare(idedrv, sector, sector_count, false);
|
||||
|
||||
@@ -438,9 +486,6 @@ DEFINE_DEVICE_OP(idedrv_read) {
|
||||
if (sector + sector_count > idedrv->sector_count)
|
||||
return -ST_OOB_ERROR;
|
||||
|
||||
if (idedrv->bm_support && ((sector_count * idedrv->sector_size) >= 16 * PAGE_SIZE))
|
||||
return -ST_OOB_ERROR;
|
||||
|
||||
if (idedrv->current_req != NULL)
|
||||
return -ST_TRY_AGAIN;
|
||||
|
||||
@@ -468,6 +513,39 @@ static int idedrv_do_write_irqs(struct idedrv* idedrv, size_t sector, size_t sec
|
||||
|
||||
idedrv->current_req = req;
|
||||
|
||||
if (idedrv->bm_support) {
|
||||
memcpy(idedrv->bounce_buffer, buffer, sector_count * idedrv->sector_size);
|
||||
|
||||
size_t rem = sector_count * idedrv->sector_size;
|
||||
uint32_t phys = idedrv->bounce_buffer_phys;
|
||||
size_t prd_idx = 0;
|
||||
|
||||
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
|
||||
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
|
||||
idedrv->prdt[prd_idx].phys_addr = phys;
|
||||
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
|
||||
|
||||
rem -= chunk;
|
||||
phys += chunk;
|
||||
|
||||
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
|
||||
prd_idx++;
|
||||
}
|
||||
|
||||
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
|
||||
|
||||
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
|
||||
ide_prepare(idedrv, sector, sector_count, true);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE_DMA48 : IDE_CMD_WRITE_DMA28;
|
||||
outb(idedrv->io + IDE_REG_CMD, cmd);
|
||||
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x01);
|
||||
} else {
|
||||
ide_prepare(idedrv, sector, sector_count, true);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE48 : IDE_CMD_WRITE28;
|
||||
@@ -482,6 +560,7 @@ static int idedrv_do_write_irqs(struct idedrv* idedrv, size_t sector, size_t sec
|
||||
outsw(idedrv->io + IDE_REG_DATA, buffer, idedrv->sector_size / 2);
|
||||
|
||||
req->sector_done_count = 1;
|
||||
}
|
||||
|
||||
biglock_unlock();
|
||||
|
||||
@@ -500,26 +579,26 @@ static int idedrv_do_write_no_irqs(struct idedrv* idedrv, size_t sector, size_t
|
||||
if (idedrv->bm_support) {
|
||||
memcpy(idedrv->bounce_buffer, buffer, sector_count * idedrv->sector_size);
|
||||
|
||||
size_t sectors_done = 0;
|
||||
size_t rem = sector_count * idedrv->sector_size;
|
||||
uint32_t phys = idedrv->bounce_buffer_phys;
|
||||
size_t prd_idx = 0;
|
||||
|
||||
while (sectors_done < sector_count) {
|
||||
size_t chunk_sectors = sector_count - sectors_done;
|
||||
size_t max_chunk = (16 * PAGE_SIZE) / idedrv->sector_size;
|
||||
while (rem > 0 && idedrv->prdt_entry_count) {
|
||||
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
|
||||
idedrv->prdt[prd_idx].phys_addr = phys;
|
||||
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
|
||||
|
||||
if (chunk_sectors > max_chunk)
|
||||
chunk_sectors = max_chunk;
|
||||
rem -= chunk;
|
||||
phys += chunk;
|
||||
|
||||
size_t byte_count = chunk_sectors * idedrv->sector_size;
|
||||
|
||||
idedrv->prdt[0].phys_addr = (uint32_t)idedrv->bounce_buffer_phys;
|
||||
idedrv->prdt[0].size = (uint16_t)byte_count;
|
||||
idedrv->prdt[0].rsvd_eot = 0x8000;
|
||||
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
|
||||
prd_idx++;
|
||||
}
|
||||
|
||||
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
|
||||
status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
|
||||
|
||||
ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false);
|
||||
ide_prepare(idedrv, sector, sector_count, false);
|
||||
|
||||
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE_DMA48 : IDE_CMD_WRITE_DMA28;
|
||||
outb(idedrv->io + IDE_REG_CMD, cmd);
|
||||
@@ -543,9 +622,6 @@ static int idedrv_do_write_no_irqs(struct idedrv* idedrv, size_t sector, size_t
|
||||
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
|
||||
|
||||
inb(idedrv->io + IDE_REG_STATUS);
|
||||
|
||||
sectors_done += chunk_sectors;
|
||||
}
|
||||
} else {
|
||||
ide_prepare(idedrv, sector, sector_count, false);
|
||||
|
||||
@@ -579,9 +655,6 @@ DEFINE_DEVICE_OP(idedrv_write) {
|
||||
if (sector + sector_count > idedrv->sector_count)
|
||||
return -ST_OOB_ERROR;
|
||||
|
||||
if (idedrv->bm_support && ((sector_count * idedrv->sector_size) >= 16 * PAGE_SIZE))
|
||||
return -ST_OOB_ERROR;
|
||||
|
||||
if (idedrv->current_req != NULL)
|
||||
return -ST_TRY_AGAIN;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user