summaryrefslogtreecommitdiff
path: root/src/kernel/arch/amd64
diff options
context:
space:
mode:
authordzwdz2022-08-17 16:40:54 +0200
committerdzwdz2022-08-17 16:40:54 +0200
commite05938ac4acd42d8acac123a880cae9582a93611 (patch)
tree6c20a1dacaa3f87a4b7eca58298f3958b3ebcd24 /src/kernel/arch/amd64
parent227f0aaf14844d951375cdf7ca81f98315222ca0 (diff)
amd64/rtl8139: tx
Diffstat (limited to 'src/kernel/arch/amd64')
-rw-r--r--src/kernel/arch/amd64/driver/rtl8139.c57
1 files changed, 49 insertions, 8 deletions
diff --git a/src/kernel/arch/amd64/driver/rtl8139.c b/src/kernel/arch/amd64/driver/rtl8139.c
index 3f6d07f..5b09027 100644
--- a/src/kernel/arch/amd64/driver/rtl8139.c
+++ b/src/kernel/arch/amd64/driver/rtl8139.c
@@ -6,6 +6,8 @@
#include <kernel/vfs/request.h>
#include <stdbool.h>
+#define WAIT -1000
+
static void accept(struct vfs_request *req);
static struct vfs_backend backend = BACKEND_KERN(accept);
static struct vfs_request *blocked_on = NULL;
@@ -13,13 +15,16 @@ static struct vfs_request *blocked_on = NULL;
enum {
MAC = 0,
+ TXSTATUS0 = 0x10,
+ TXSTART0 = 0x20,
RBSTART = 0x30,
CMD = 0x37,
CAPR = 0x38,
CBR = 0x3A,
INTRMASK = 0x3C,
INTRSTATUS = 0x3E,
- RCR = 0x44, /* receive configure */
+ TCR = 0x40,
+ RCR = 0x44,
CONFIG1 = 0x52,
};
@@ -30,6 +35,9 @@ static uint16_t iobase;
static char rxbuf[rxbuf_baselen + 16 + 1500];
static size_t rxpos;
+#define txbuf_len 2048
+static char txbuf[4][txbuf_len];
+
static void rx_irq_enable(bool v) {
uint16_t mask = 1 | 4; /* rx/tx ok */
port_out16(iobase + INTRMASK, v ? mask : 0);
@@ -53,6 +61,8 @@ void rtl8139_init(uint32_t bdf) {
assert((long)(void*)rxbuf <= 0xFFFFFFFF);
port_out32(iobase + RBSTART, (long)(void*)rxbuf);
+ port_out32(iobase + TCR, 0);
+
uint32_t rcr = 0;
// rcr |= 1 << 0; /* accept all packets */
rcr |= 1 << 1; /* accept packets with our mac */
@@ -75,6 +85,10 @@ void rtl8139_init(uint32_t bdf) {
void rtl8139_irq(void) {
uint16_t status = port_in16(iobase + INTRSTATUS);
+ if (status != 1) {
+ kprintf("bad rtl8139 status 0x%x\n", status);
+ panic_unimplemented();
+ }
// TODO don't assume this is an rx irq
do {
@@ -94,7 +108,7 @@ void rtl8139_irq(void) {
static int try_rx(struct pagedir *pages, void __user *dest, size_t dlen) {
uint16_t flags, size;
/* bit 0 - Rx Buffer Empty */
- if (port_in8(iobase + CMD) & 1) return -1;
+ if (port_in8(iobase + CMD) & 1) return WAIT;
/* https://github.com/qemu/qemu/blob/04ddcda6a/hw/net/rtl8139.c#L1169 */
/* https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139D_DataSheet.pdf page 12
@@ -109,7 +123,8 @@ static int try_rx(struct pagedir *pages, void __user *dest, size_t dlen) {
if (size == 0) panic_invalid_state();
// kprintf("packet size 0x%x, flags 0x%x, rxpos %x\n", size, flags, rxpos - 4);
- virt_cpy_to(pages, dest, rxbuf + rxpos, size);
+ if (dlen > size) dlen = size;
+ virt_cpy_to(pages, dest, rxbuf + rxpos, dlen);
rxpos += size;
rxpos = (rxpos + 3) & ~3;
@@ -119,19 +134,41 @@ static int try_rx(struct pagedir *pages, void __user *dest, size_t dlen) {
return size;
}
+static int try_tx(struct pagedir *pages, const void __user *src, size_t slen) {
+ static uint8_t desc = 0;
+
+ if (slen > 0xFFF) return -1;
+ if (slen > txbuf_len) return -1;
+
+ uint32_t status = port_in32(iobase + TXSTATUS0 + desc*4);
+ if (!(status & (1<<13))) {
+ /* can't (?) be caused (and thus, tested) on a vm */
+ kprintf("try_tx called with all descriptors full.");
+ panic_unimplemented();
+ }
+
+ virt_cpy_from(pages, txbuf[desc], src, slen);
+ assert((long)(void*)txbuf <= 0xFFFFFFFF);
+ port_out32(iobase + TXSTART0 + desc*4, (long)(void*)txbuf[desc]);
+ port_out32(iobase + TXSTATUS0 + desc*4, slen);
+
+ desc = (desc + 1) & 3;
+ return slen;
+}
+
static void accept(struct vfs_request *req) {
+ if (!req->caller) {
+ vfsreq_finish_short(req, -1);
+ return;
+ }
switch (req->type) {
long ret;
case VFSOP_OPEN:
vfsreq_finish_short(req, req->input.len == 0 ? 0 : -1);
break;
case VFSOP_READ:
- if (!req->caller) {
- vfsreq_finish_short(req, -1);
- break;
- }
ret = try_rx(req->caller->pages, req->output.buf, req->output.len);
- if (ret < 0) {
+ if (ret == WAIT) {
// TODO this is a pretty common pattern in drivers, try to make it unneeded
assert(!req->postqueue_next);
struct vfs_request **slot = &blocked_on;
@@ -142,6 +179,10 @@ static void accept(struct vfs_request *req) {
vfsreq_finish_short(req, ret);
}
break;
+ case VFSOP_WRITE:
+ assert(!req->input.kern);
+ vfsreq_finish_short(req, try_tx(req->caller->pages, req->input.buf, req->input.len));
+ break;
default:
vfsreq_finish_short(req, -1);
break;