From be971233f9c3c0f33740878628a3df5c2f256650 Mon Sep 17 00:00:00 2001 From: Agnieszka Tabaka Date: Mon, 9 Jun 2014 22:44:00 +0200 Subject: [PATCH] Initial receive support. Also added TX interrupt handling (still buggy as it losts some of interrupts, though). --- uspace/drv/nic/rtl8169/defs.h | 17 +++++ uspace/drv/nic/rtl8169/driver.c | 153 ++++++++++++++++++++++++++++++++++------ 2 files changed, 148 insertions(+), 22 deletions(-) diff --git a/uspace/drv/nic/rtl8169/defs.h b/uspace/drv/nic/rtl8169/defs.h index 811722f3a..8fb90081b 100644 --- a/uspace/drv/nic/rtl8169/defs.h +++ b/uspace/drv/nic/rtl8169/defs.h @@ -281,6 +281,23 @@ enum rtl8169_descr_txstatus { TXSTATUS_EXCESSCOL = (1 << 20) }; +enum rtl8169_descr_rxstatus { + RXSTATUS_MAR = (1 << 27), + RXSTATUS_PAM = (1 << 26), + RXSTATUS_BAR = (1 << 25), + RXSTATUS_BOVF = (1 << 24), + RXSTATUS_FOVF = (1 << 23), + RXSTATUS_RWT = (1 << 22), + RXSTATUS_RES = (1 << 21), + RXSTATUS_RUNT = (1 << 20), + RXSTATUS_CRC = (1 << 19), + RXSTATUS_PID1 = (1 << 18), + RXSTATUS_PID0 = (1 << 17), + RXSTATUS_IPF = (1 << 16), + RXSTATUS_UDPF = (1 << 15), + RXSTATUS_TCPF = (1 << 14) +}; + typedef struct rtl8169_descr { uint32_t control; uint32_t vlan; diff --git a/uspace/drv/nic/rtl8169/driver.c b/uspace/drv/nic/rtl8169/driver.c index 0f14b5a5f..e39a73767 100644 --- a/uspace/drv/nic/rtl8169/driver.c +++ b/uspace/drv/nic/rtl8169/driver.c @@ -92,6 +92,8 @@ static int rtl8169_multicast_set(nic_t *nic_data, nic_multicast_mode_t mode, const nic_address_t *addr, size_t addr_count); static int rtl8169_broadcast_set(nic_t *nic_data, nic_broadcast_mode_t mode); +static void rtl8169_rx_ring_refill(rtl8169_t *rtl8169, unsigned int first, + unsigned int last); /** Network interface options for RTL8169 card driver */ static nic_iface_t rtl8169_nic_iface = { @@ -242,6 +244,8 @@ static int rtl8169_allocate_buffers(rtl8169_t *rtl8169) if (rc != EOK) return rc; + memset(rtl8169->tx_ring, 0, TX_RING_SIZE); + /* Allocate RX ring */ rc = dmamem_map_anonymous(RX_RING_SIZE, DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &rtl8169->rx_ring_phys, @@ -250,6 +254,8 @@ static int rtl8169_allocate_buffers(rtl8169_t *rtl8169) if (rc != EOK) return rc; + memset(rtl8169->rx_ring, 0, RX_RING_SIZE); + /* Allocate TX buffers */ rc = dmamem_map_anonymous(TX_BUFFERS_SIZE, DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &rtl8169->tx_buff_phys, @@ -513,6 +519,30 @@ static int rtl8169_defective_set_mode(ddf_fun_t *fun, uint32_t mode) return EOK; } +static void rtl8169_rx_ring_refill(rtl8169_t *rtl8169, unsigned int first, + unsigned int last) +{ + rtl8169_descr_t *descr; + uintptr_t buff_phys; + unsigned int i = first; + + for (;;) { + descr = &rtl8169->rx_ring[i]; + buff_phys = rtl8169->rx_buff_phys + (BUFFER_SIZE * i); + descr->control = BUFFER_SIZE | CONTROL_OWN; + descr->buf_low = buff_phys & 0xffffffff; + descr->buf_high = (buff_phys >> 32) & 0xffffffff; + + if (i == RX_BUFFERS_COUNT - 1) + descr->control |= CONTROL_EOR; + + if (i == last) + break; + + i = (i + 1) % RX_BUFFERS_COUNT; + } +} + static int rtl8169_on_activated(nic_t *nic_data) { ddf_msg(LVL_NOTE, "Activating device"); @@ -526,6 +556,9 @@ static int rtl8169_on_activated(nic_t *nic_data) /* Allocate buffers */ rtl8169_allocate_buffers(rtl8169); + /* Initialize RX ring */ + rtl8169_rx_ring_refill(rtl8169, 0, RX_BUFFERS_COUNT - 1); + /* Write address of descriptor as start of TX ring */ pio_write_32(rtl8169->regs + TNPDS, rtl8169->tx_ring_phys & 0xffffffff); pio_write_32(rtl8169->regs + TNPDS + 4, (rtl8169->tx_ring_phys >> 32) & 0xffffffff); @@ -533,10 +566,34 @@ static int rtl8169_on_activated(nic_t *nic_data) rtl8169->tx_tail = 0; rtl8169->tx_ring[15].control = CONTROL_EOR; + /* Write RX ring address */ + pio_write_32(rtl8169->regs + RDSAR, rtl8169->rx_ring_phys & 0xffffffff); + pio_write_32(rtl8169->regs + RDSAR + 4, (rtl8169->rx_ring_phys >> 32) & 0xffffffff); + rtl8169->rx_head = 0; + rtl8169->rx_tail = 0; + + /* Clear pending interrupts */ + pio_write_16(rtl8169->regs + ISR, 0xffff); + /* Enable TX and RX */ uint8_t cr = pio_read_8(rtl8169->regs + CR); cr |= CR_TE | CR_RE; pio_write_8(rtl8169->regs + CR, cr); + pio_write_32(rtl8169->regs + MAR0, 0xffffffff); + pio_write_32(rtl8169->regs + MAR0 + 4, 0xffffffff); + + /* Configure Receive Control Register */ + pio_write_8(rtl8169->regs + 0x50, 0xc0); + uint32_t rcr = pio_read_32(rtl8169->regs + RCR); + rcr |= RCR_ACCEPT_ALL_PHYS | RCR_ACCEPT_PHYS_MATCH \ + | RCR_ACCEPT_BROADCAST | RCR_ACCEPT_ERROR \ + | RCR_ACCEPT_RUNT; + pio_write_32(rtl8169->regs + RCR, rcr); + pio_write_16(rtl8169->regs + RMS, BUFFER_SIZE); + pio_write_8(rtl8169->regs + 0x50, 0x00); + + ddf_msg(LVL_NOTE, "RCR: 0x%08x", pio_read_32(rtl8169->regs + RCR)); + pio_write_16(rtl8169->regs + IMR, 0xffff); nic_enable_interrupt(nic_data, rtl8169->irq); @@ -622,9 +679,10 @@ static void rtl8169_transmit_done(ddf_dev_t *dev) while (tail != head) { descr = &rtl8169->tx_ring[tail]; - tail = (tail + 1) % TX_BUFFERS_COUNT; - + descr->control |= CONTROL_OWN; ddf_msg(LVL_NOTE, "TX status for descr %d: 0x%08x", tail, descr->control); + + tail = (tail + 1) % TX_BUFFERS_COUNT; } rtl8169->tx_tail = tail; @@ -632,6 +690,65 @@ static void rtl8169_transmit_done(ddf_dev_t *dev) fibril_mutex_unlock(&rtl8169->tx_lock); } +static void rtl8169_receive_done(ddf_dev_t *dev) +{ + nic_t *nic_data = nic_get_from_ddf_dev(dev); + rtl8169_t *rtl8169 = nic_get_specific(nic_data); + rtl8169_descr_t *descr; + nic_frame_list_t *frames = nic_alloc_frame_list(); + nic_frame_t *frame; + void *buffer; + unsigned int tail, fsidx = 0; + int frame_size; + + ddf_msg(LVL_NOTE, "rtl8169_receive_done()"); + + fibril_mutex_lock(&rtl8169->rx_lock); + + tail = rtl8169->rx_tail; + + for (;;) { + descr = &rtl8169->rx_ring[tail]; + + if (descr->control & CONTROL_OWN) + break; + + if (descr->control & RXSTATUS_RES) { + ddf_msg(LVL_NOTE, "error at slot %d: 0x%08x\n", tail, descr->control); + tail = (tail + 1) % RX_BUFFERS_COUNT; + continue; + } + + if (descr->control & CONTROL_FS) + fsidx = tail; + + if (descr->control & CONTROL_LS) { + + ddf_msg(LVL_NOTE, "received message at slot %d, control 0x%08x", tail, descr->control); + + if (fsidx != tail) + ddf_msg(LVL_WARN, "single frame spanning multiple descriptors"); + + frame_size = descr->control & 0x1fff; + buffer = rtl8169->rx_buff + (BUFFER_SIZE * tail); + frame = nic_alloc_frame(nic_data, frame_size); + memcpy(frame->data, buffer, frame_size); + nic_frame_list_append(frames, frame); + } + + tail = (tail + 1) % RX_BUFFERS_COUNT; + } + + rtl8169_rx_ring_refill(rtl8169, rtl8169->rx_tail, tail); + + rtl8169->rx_tail = tail; + + fibril_mutex_unlock(&rtl8169->rx_lock); + + nic_received_frame_list(nic_data, frames); + +} + static void rtl8169_irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *icall) { @@ -642,31 +759,21 @@ static void rtl8169_irq_handler(ddf_dev_t *dev, ipc_callid_t iid, nic_t *nic_data = nic_get_from_ddf_dev(dev); rtl8169_t *rtl8169 = nic_get_specific(nic_data); - //ddf_msg(LVL_NOTE, "rtl8169_irq_handler(): isr=0x%04x", isr); + ddf_msg(LVL_NOTE, "rtl8169_irq_handler(): isr=0x%04x", isr); /* Packet underrun or link change */ if (isr & INT_PUN) rtl8169_link_change(dev); - /* Frame(s) successfully transmitted */ - if (isr & INT_TOK) - rtl8169_transmit_done(dev); - - /* Transmit error */ - if (isr & INT_TER) + /* Transmit notification */ + if (isr & (INT_TER | INT_TOK | INT_TDU)) rtl8169_transmit_done(dev); if (isr & INT_SERR) ddf_msg(LVL_ERROR, "System error interrupt"); - if (isr & INT_TDU) - ddf_msg(LVL_ERROR, "Transmit descriptor unavailable"); - - if (isr & INT_RER) - ddf_msg(LVL_NOTE, "RX error interrupt"); - - if (isr & INT_ROK) - ddf_msg(LVL_NOTE, "RX OK interrupt"); + if (isr & (INT_RER | INT_ROK)) + rtl8169_receive_done(dev); pio_write_16(rtl8169->regs + ISR, 0xffff); pio_write_16(rtl8169->regs + IMR, 0xffff); @@ -689,15 +796,16 @@ static void rtl8169_send_frame(nic_t *nic_data, void *data, size_t size) fibril_mutex_lock(&rtl8169->tx_lock); ddf_msg(LVL_NOTE, "send_frame()"); + ddf_msg(LVL_NOTE, "size: %ld", size); ddf_msg(LVL_NOTE, "tx ring virtual at %p", rtl8169->tx_ring); - ddf_msg(LVL_NOTE, "tx ring physical at 0x%08lx", rtl8169->tx_ring_phys); ddf_msg(LVL_NOTE, "tx_head=%d tx_tail=%d", rtl8169->tx_head, rtl8169->tx_tail); head = rtl8169->tx_head; tail = rtl8169->tx_tail; - if ((tail + 1) % TX_BUFFERS_COUNT == head) { + if ((head + 1) % TX_BUFFERS_COUNT == tail) { /* Queue is full */ + ddf_msg(LVL_WARN, "TX queue full!"); nic_set_tx_busy(nic_data, 1); } @@ -719,10 +827,11 @@ static void rtl8169_send_frame(nic_t *nic_data, void *data, size_t size) descr->vlan = 0; descr->buf_low = buff_phys & 0xffffffff; descr->buf_high = (buff_phys >> 32) & 0xffffffff; - rtl8169->tx_head = (head + 1) % TX_BUFFERS_COUNT; - /* Lift EOR flag from previous descriptor */ - prev->control &= ~CONTROL_EOR; + if (head == TX_BUFFERS_COUNT - 1) + descr->control |= CONTROL_EOR; + + rtl8169->tx_head = (head + 1) % TX_BUFFERS_COUNT; write_barrier(); -- 2.11.4.GIT