aos/usr/drivers/enet/enet_devq.c
2022-05-27 19:11:22 +02:00

493 lines
14 KiB
C

/**
* \file
* \brief imx8 NIC device queue
*/
/*
* Copyright (c) 2020, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <devif/queue_interface_backend.h>
#include <devif/backends/net/enet_devif.h>
#include <aos/aos.h>
#include <driverkit/driverkit.h>
#include <dev/imx8x/enet_dev.h>
#include <arch/aarch64/aos/cache.h>
#include "enet.h"
struct region_entry* enet_get_region(struct enet_queue* q, regionid_t rid)
{
struct region_entry* entry = q->regions;
while (entry != NULL) {
if (entry->rid == rid) {
return entry;
}
entry = entry->next;
}
return NULL;
}
static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid)
{
errval_t err;
struct enet_queue* queue = (struct enet_queue*) q;
// keep track of regions since we need the virtual address ...
struct region_entry* entry = calloc(1, sizeof(struct region_entry));
assert(entry);
entry->rid = rid;
entry->next = NULL;
struct frame_identity id;
err = frame_identify(cap, &id);
if (err_is_fail(err)) {
return err;
}
void* va;
err = paging_map_frame_attr(get_current_paging_state(), &va, id.bytes,
cap, VREGION_FLAGS_READ_WRITE);
if (err_is_fail(err)) {
return err;
}
entry->mem.devaddr = id.base;
entry->mem.vbase = (lvaddr_t) va;
entry->mem.mem = cap;
entry->mem.size = id.bytes;
ENET_DEBUG("register region id %d base=%lx \n", rid, entry->mem.devaddr);
// linked list of regions
struct region_entry* cur = queue->regions;
if (cur == NULL) {
queue->regions = entry;
return SYS_ERR_OK;
}
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = entry;
ENET_DEBUG("registerd region id %d base=%p len=%ld \n", rid,
(void*) entry->mem.vbase, entry->mem.size);
return SYS_ERR_OK;
}
static inline size_t enet_full_slots(struct enet_queue* q)
{
size_t head = q->head;
size_t tail = q->tail;
size_t size = q->size;
if (tail >= head) {
return (tail - head);
} else {
return (tail + size - head);
}
}
static void enet_activate_tx_ring(enet_t * d)
{
// bit is always set to 1 only when ring is empty then it is set to 0
enet_tdar_tdar_wrf(d, 1);
}
static void enet_activate_rx_ring(enet_t* d)
{
// bit is always set to 1 only when ring is empty then it is set to 0
enet_rdar_rdar_wrf(d, 1);
}
static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid,
genoffset_t* offset,
genoffset_t* length,
genoffset_t* valid_data,
genoffset_t* valid_length,
uint64_t* flags)
{
struct enet_queue* q = (struct enet_queue*) que;
enet_bufdesc_t desc = q->ring[q->head];
struct devq_buf* buf = &q->ring_bufs[q->head];
cpu_dcache_wbinv_range((lvaddr_t) &q->ring[q->head], sizeof(enet_bufdesc_t));
uint16_t status = enet_bufdesc_sc_extract(desc);
if (q->head == q->tail) {
return DEVQ_ERR_QUEUE_EMPTY;
}
/*
ENET_DEBUG("Try dequeue %d RADR %d ENABLED %d STATUS %lx \n", q->head,
enet_rdar_rdar_rdf(q->d), enet_ecr_etheren_rdf(q->d), status);
*/
if (!(status & ENET_RX_EMPTY)) {
// TODO error handling!
*valid_length = enet_bufdesc_len_extract(desc);
ENET_DEBUG("Received Packet len=%lu entry=%zu \n", *valid_length,
q->head);
ENET_DEBUG("offset=%lu length=%lu valid_data=%lu rid=%lu \n",
buf->offset, buf->length, 0, buf->rid);
*offset = buf->offset;
*valid_data = 0;
*length = 2048;
*rid = buf->rid;
*flags = buf->flags;
} else {
return DEVQ_ERR_QUEUE_EMPTY;
}
status &= ~ENET_RX_STATS;
// remove chached stuff in buffer
struct region_entry *entry = enet_get_region(q, *rid);
assert(entry);
lvaddr_t vaddr = (lvaddr_t) entry->mem.vbase + *offset + *valid_data;
cpu_dcache_wb_range(vaddr, *valid_length);
dmb();
enet_bufdesc_sc_insert(desc, status);
q->head = (q->head+1) & (q->size -1);
return SYS_ERR_OK;
}
static errval_t enet_tx_dequeue(struct devq* que, regionid_t* rid,
genoffset_t* offset,
genoffset_t* length,
genoffset_t* valid_data,
genoffset_t* valid_length,
uint64_t* flags)
{
struct enet_queue* q = (struct enet_queue*) que;
if (enet_full_slots(q)) {
enet_bufdesc_t desc = q->ring[q->head];
dmb();
cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head],
sizeof(enet_bufdesc_t));
desc = q->ring[q->head];
struct devq_buf* buf= &q->ring_bufs[q->head];
if (!(enet_bufdesc_sc_extract(desc) & ENET_TX_READY)) {
// ENET_DEBUG("We sent something!! \n");
*valid_length = buf->valid_length;
*offset = buf->offset;
*length = buf->length;
*valid_data = 0;
*rid = buf->rid;
*flags = buf->flags;
} else {
return DEVQ_ERR_QUEUE_EMPTY;
}
} else {
return DEVQ_ERR_QUEUE_EMPTY;
}
ENET_DEBUG("Deq TX head=%zu \n", q->head);
q->head = (q->head + 1) & (q->size -1);
return SYS_ERR_OK;
}
static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t offset,
genoffset_t length, genoffset_t valid_data,
genoffset_t valid_length, uint64_t flags)
{
struct enet_queue* q = (struct enet_queue*) que;
assert(valid_length > 0 && valid_length < ENET_MAX_PKT_SIZE);
if (enet_full_slots(q) == q->size) {
return DEVQ_ERR_QUEUE_FULL;
}
lpaddr_t addr = 0;
lvaddr_t vaddr = 0;
struct region_entry *entry = enet_get_region(q, rid);
assert(entry);
addr = (lpaddr_t) entry->mem.devaddr + offset + valid_data;
vaddr = (lvaddr_t) entry->mem.vbase + offset + valid_data;
struct devq_buf* buf= &q->ring_bufs[q->tail];
buf->offset = offset;
buf->length = length;
buf->valid_length = valid_length;
buf->valid_data = valid_data;
buf->rid = rid;
buf->flags = flags;
// TODO alignment
enet_bufdesc_t desc = q->ring[q->tail];
enet_bufdesc_addr_insert(desc, addr);
enet_bufdesc_len_insert(desc, valid_length);
cpu_dcache_wb_range(vaddr, valid_length);
dmb();
if (q->tail == (q->size -1)) {
enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC |
ENET_TX_LAST | ENET_TX_WRAP);
} else {
enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC | ENET_TX_LAST);
}
cpu_dcache_wb_range((lvaddr_t) &q->ring[q->tail], sizeof(enet_bufdesc_t));
// activate TX
enet_activate_tx_ring(q->d);
// wait until sent
int timeout = 5000;
while(timeout--) {
if (!(enet_tdar_tdar_rdf(q->d))) {
break;
}
cpu_dcache_wb_range((lvaddr_t) &q->ring[q->tail], sizeof(enet_bufdesc_t));
}
if (timeout == 0) {
ENET_DEBUG("Failed sending!! \n");
return NIC_ERR_TX_PKT;
} else {
q->tail = (q->tail + 1) & (q->size -1);
}
return SYS_ERR_OK;
}
static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t offset,
genoffset_t length, genoffset_t valid_data,
genoffset_t valid_length, uint64_t flags)
{
struct enet_queue* q = (struct enet_queue*) que;
//enet_bufdesc_addr_insert(desc, );
struct region_entry *entry = enet_get_region(q, rid);
assert(entry);
assert(valid_length > 0 && length <= ENET_MAX_BUF_SIZE);
if (enet_full_slots(q) == q->size) {
return DEVQ_ERR_QUEUE_FULL;
}
lpaddr_t addr = 0;
addr = (lpaddr_t) entry->mem.devaddr + offset;
struct devq_buf* buf= &q->ring_bufs[q->tail];
buf->offset = offset;
buf->length = length;
buf->valid_length = valid_length;
buf->valid_data = valid_data;
buf->rid = rid;
buf->flags = flags;
enet_bufdesc_t desc = q->ring[q->tail];
enet_bufdesc_addr_insert(desc, addr);
enet_bufdesc_len_insert(desc, 0);
dmb();
if (q->tail == (q->size -1)) {
enet_bufdesc_sc_insert(desc, ENET_SC_WRAP | ENET_RX_EMPTY);
} else {
enet_bufdesc_sc_insert(desc, ENET_RX_EMPTY);
}
arm64_dcache_wb_range((lvaddr_t) &q->ring[q->tail], sizeof(enet_bufdesc_t));
/*ENET_DEBUG("enqueue ring_buf[%d]=%p phys=%lx offset=%lx length=%zu\n", q->tail,
q->ring[q->tail], addr, offset, length);
*/
// activate RX (This is only needed if ring is empty)
enet_activate_rx_ring(q->d);
q->tail = (q->tail + 1) & (q->size -1);
return SYS_ERR_OK;
}
errval_t enet_rx_queue_create(struct enet_queue ** q, enet_t *dev)
{
errval_t err;
ENET_DEBUG("Allocating rx device queue \n");
struct enet_queue* rxq;
rxq = calloc(1, sizeof(struct enet_queue));
assert(rxq);
rxq->size = RX_RING_SIZE;
/* Initialize Mackerel binding */
rxq->d = dev;
rxq->align = 0x3f;
// TODO check for advanced descriptors
// TODO linking iommu driverkit library does not seem to work ...
ENET_DEBUG("Allocating RX descriptor ring \n");
size_t tot_size = (rxq->size)*sizeof(enet_bufdesc_t);
err = frame_alloc(&(rxq->desc_mem.mem), tot_size, (size_t *)&(rxq->desc_mem.size));
if (err_is_fail(err)) {
return err;
}
ENET_DEBUG("Mapping RX descriptor ring\n");
err = paging_map_frame_attr(get_current_paging_state(),
(void**) &(rxq->desc_mem.vbase), tot_size,
rxq->desc_mem.mem,
VREGION_FLAGS_READ_WRITE_NOCACHE);
if (err_is_fail(err)) {
cap_destroy(rxq->desc_mem.mem);
DEBUG_ERR(err, "vspace_map_one_frame failed");
return err;
}
struct frame_identity id;
err = frame_identify(rxq->desc_mem.mem, &id);
if (err_is_fail(err)) {
return err;
}
rxq->desc_mem.devaddr = id.base;
rxq->ring = (void*) rxq->desc_mem.vbase;
assert(rxq->ring);
assert((rxq->desc_mem.devaddr & rxq->align) == 0);
enet_rdsr_wr(rxq->d, rxq->desc_mem.devaddr);
memset(rxq->ring, 0, rxq->size*sizeof(enet_bufdesc_t));
rxq->ring_bufs = calloc(rxq->size, sizeof(struct devq_buf));
assert(rxq->ring_bufs);
enet_bufdesc_t desc;
ENET_DEBUG("RX %p ring init to default values \n", rxq->ring);
for (int i = 0; i < rxq->size; i++) {
desc = rxq->ring[i];
enet_bufdesc_sc_insert(desc, 0);
}
uint64_t val;
// set last one to wrap
desc = rxq->ring[rxq->size - 1];
val = enet_bufdesc_sc_extract(desc);
val |= ENET_SC_WRAP;
enet_bufdesc_sc_insert(desc, val);
rxq->head = 0;
rxq->tail = 0;
err = devq_init(&rxq->q, false);
if (err_is_fail(err)) {
ENET_DEBUG("enet devq_init error\n");
return err;
}
rxq->q.f.reg = enet_register;
rxq->q.f.enq = enet_rx_enqueue;
rxq->q.f.deq = enet_rx_dequeue;
*q = rxq;
return SYS_ERR_OK;
}
errval_t enet_tx_queue_create(struct enet_queue ** q, struct enet_t* dev)
{
errval_t err;
ENET_DEBUG("Allocating rx device queue \n");
struct enet_queue* txq;
txq = calloc(1, sizeof(struct enet_queue));
txq->size = TX_RING_SIZE;
/* Initialize Mackerel binding */
txq->d = dev;
assert(txq->d);
txq->align = 0x3f;
ENET_DEBUG("Allocating TX descriptor ring \n");
size_t tot_size = (txq->size)*sizeof(enet_bufdesc_t);
err = frame_alloc(&(txq->desc_mem.mem), tot_size, (size_t *)&(txq->desc_mem.size));
if (err_is_fail(err)) {
return err;
}
ENET_DEBUG("Mapping RX/TX descriptor ring\n");
err = paging_map_frame_attr(get_current_paging_state(),
(void**) &(txq->desc_mem.vbase), tot_size,
txq->desc_mem.mem, VREGION_FLAGS_READ_WRITE_NOCACHE);
if (err_is_fail(err)) {
cap_destroy(txq->desc_mem.mem);
DEBUG_ERR(err, "vspace_map_one_frame failed");
return err;
}
struct frame_identity id;
err = frame_identify(txq->desc_mem.mem, &id);
if (err_is_fail(err)) {
return err;
}
txq->desc_mem.devaddr = id.base;
txq->ring = (void*) txq->desc_mem.vbase;
assert(txq->ring);
assert((txq->desc_mem.devaddr & txq->align) == 0);
// Tell card beginning of rx/tx rings
enet_tdsr_wr(txq->d, txq->desc_mem.devaddr);
memset(txq->ring, 0, txq->size*sizeof(enet_bufdesc_t));
txq->ring_bufs = calloc(txq->size, sizeof(struct devq_buf));
assert(txq->ring_bufs);
ENET_DEBUG("TX %p ring init to default values \n", txq->ring);
enet_bufdesc_t desc;
// init send buffer descriptors
for (int i = 0; i < txq->size; i++) {
desc = txq->ring[i];
enet_bufdesc_sc_insert(desc, 0);
enet_bufdesc_addr_insert(desc, 0);
}
uint64_t val;
// set last one to wrap
desc = txq->ring[txq->size - 1];
val = enet_bufdesc_sc_extract(desc);
val |= ENET_SC_WRAP;
enet_bufdesc_sc_insert(desc, val);
txq->head = 0;
txq->tail = 0;
err = devq_init(&txq->q, false);
if (err_is_fail(err)) {
debug_printf("enet devq_init error\n");
return err;
}
txq->q.f.reg = enet_register;
txq->q.f.enq = enet_tx_enqueue;
txq->q.f.deq = enet_tx_dequeue;
*q = txq;
return SYS_ERR_OK;
}