Index: uspace/drv/nic/rtl8169/defs.h
===================================================================
--- uspace/drv/nic/rtl8169/defs.h	(revision 59b3095656fce3d8f29777d27bf531042dca1c58)
+++ uspace/drv/nic/rtl8169/defs.h	(revision be971233f9c3c0f33740878628a3df5c2f256650)
@@ -282,4 +282,21 @@
 };
 
+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;
Index: uspace/drv/nic/rtl8169/driver.c
===================================================================
--- uspace/drv/nic/rtl8169/driver.c	(revision 59b3095656fce3d8f29777d27bf531042dca1c58)
+++ uspace/drv/nic/rtl8169/driver.c	(revision be971233f9c3c0f33740878628a3df5c2f256650)
@@ -93,4 +93,6 @@
 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 */
@@ -243,4 +245,6 @@
 		return rc;
 
+	memset(rtl8169->tx_ring, 0, TX_RING_SIZE);
+
 	/* Allocate RX ring */
 	rc = dmamem_map_anonymous(RX_RING_SIZE, DMAMEM_4GiB, 
@@ -250,4 +254,6 @@
 	if (rc != EOK)
 		return rc;
+
+	memset(rtl8169->rx_ring, 0, RX_RING_SIZE);
 
 	/* Allocate TX buffers */
@@ -514,4 +520,28 @@
 }
 
+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)
 {
@@ -526,4 +556,7 @@
 	/* 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 */
@@ -534,8 +567,32 @@
 	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);
@@ -623,7 +680,8 @@
 	while (tail != head) {
 		descr = &rtl8169->tx_ring[tail];
+		descr->control |= CONTROL_OWN;
+		ddf_msg(LVL_NOTE, "TX status for descr %d: 0x%08x", tail, descr->control);
+
 		tail = (tail + 1) % TX_BUFFERS_COUNT;
-
-		ddf_msg(LVL_NOTE, "TX status for descr %d: 0x%08x", tail, descr->control);
 	}
 
@@ -631,4 +689,63 @@
 
 	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);
+
 }
 
@@ -643,5 +760,5 @@
 	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 */
@@ -649,10 +766,6 @@
 		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);
 
@@ -660,12 +773,6 @@
 		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);
@@ -690,6 +797,6 @@
 
 	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);
 
@@ -697,6 +804,7 @@
 	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);
 	}
@@ -720,8 +828,9 @@
 	descr->buf_low = buff_phys & 0xffffffff;
 	descr->buf_high = (buff_phys >> 32) & 0xffffffff;
+
+	if (head == TX_BUFFERS_COUNT - 1)
+		descr->control |= CONTROL_EOR;
+
 	rtl8169->tx_head = (head + 1) % TX_BUFFERS_COUNT;
-
-	/* Lift EOR flag from previous descriptor */
-	prev->control &= ~CONTROL_EOR;
 
 	write_barrier();
