Patchwork [5/8] partially working network driver, needs more comparison with real hardware before it can be made fully working

login
register
mail settings
Submitter Bryce Lanham
Date Aug. 17, 2011, 10:09 p.m.
Message ID <1313618952-14774-7-git-send-email-blanham@gmail.com>
Download mbox | patch
Permalink /patch/110456/
State New
Headers show

Comments

Bryce Lanham - Aug. 17, 2011, 10:09 p.m.
Signed-off-by: Bryce Lanham <blanham@gmail.com>
---
 hw/next-net.c |  513 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/next-net.h |    2 +
 2 files changed, 515 insertions(+), 0 deletions(-)
 create mode 100644 hw/next-net.c
 create mode 100644 hw/next-net.h

Patch

diff --git a/hw/next-net.c b/hw/next-net.c
new file mode 100644
index 0000000..94739a1
--- /dev/null
+++ b/hw/next-net.c
@@ -0,0 +1,513 @@ 
+/*
+ * QEMU NeXT Network (MB8795) emulation
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "sysemu.h"//only needed for vm_stop
+#include "hw.h"
+#include "net.h"
+#include "next-net.h"
+#include "sysbus.h"
+/* debug NeXT ethernet */
+#define DEBUG_NET
+
+#ifdef DEBUG_NET
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("NET: " fmt , ## __VA_ARGS__); } while (0);
+#else
+#define DPRINTF(fmt, ...)
+#endif
+    
+/* IRQs should be moved to header later */
+#define TX_I_DMA 0
+#define RX_I_DMA 1
+#define TX_I 	 2
+#define RX_I     3
+/* names could be better */
+typedef struct NextDMA {
+	uint32_t csr;
+	uint32_t savedbase;
+	uint32_t savedlimit;
+	
+    uint32_t baser;
+    uint32_t base;
+    uint32_t limit;
+    uint32_t chainbase;
+    uint32_t chainlimit;
+	uint32_t basew;
+
+	
+} NextDMA;
+
+typedef struct NextNetState {
+    uint8_t mac[6];
+    
+    qemu_irq *irq;
+    
+    NICState *nic;
+    NICConf c;
+	
+    NextDMA tx_dma;
+	uint8_t tx_stat;
+	uint8_t tx_mask;
+    uint8_t tx_mode;
+	
+    NextDMA rx_dma;
+    uint8_t rx_stat;
+    uint8_t rx_mask;
+    uint8_t rx_mode;
+
+    uint8_t rst_mode;
+
+} NextNetState;
+
+NextNetState nextnet_state;
+
+void *env_g;
+
+void nextnet_irq(void *opaque, int n, int level);
+static int nextnet_can_rx(VLANClientState *nc);
+static ssize_t nextnet_rx(VLANClientState *nc, const uint8_t *buf, size_t size);
+static void nextnet_cleanup(VLANClientState *nc);
+
+static NetClientInfo nextnet_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = nextnet_can_rx,
+    .receive = nextnet_rx,
+    .cleanup = nextnet_cleanup,
+};
+/* these need to be somewhere else */
+extern uint32_t int_status;//should be set in a global irq handler
+extern uint32_t int_mask;
+extern uint32_t event_test;
+
+void nextnet_reset(NextNetState *s);
+void nextnet_reset(NextNetState *s)
+{
+
+
+}
+
+static uint32_t net_readb(void*opaque, target_phys_addr_t addr);
+static uint32_t net_readw(void*opaque, target_phys_addr_t addr);
+static uint32_t net_readl(void*opaque, target_phys_addr_t addr);
+static CPUReadMemoryFunc *net_read[3] = {
+	net_readb,
+	net_readw,
+	net_readl
+};
+
+static void net_writeb(void*opaque, target_phys_addr_t addr, uint32_t value);
+static void net_writew(void*opaque, target_phys_addr_t addr, uint32_t value);
+static void net_writel(void*opaque, target_phys_addr_t addr, uint32_t value);
+static CPUWriteMemoryFunc * const net_write[3] = {
+	net_writeb,
+	net_writew,
+	net_writel
+};
+
+
+void nextnet_init(void *opaque)
+{
+    NextNetState *s = &nextnet_state;
+	CPUState *env = (CPUState *)opaque; 
+    env_g = opaque;//used this as a global during testing, should be removed
+    
+	/*register device register space */
+    cpu_register_physical_memory((uint32_t)0x2106000,0x1000,
+        cpu_register_io_memory(net_read,net_write, (void *)&nextnet_state,DEVICE_NATIVE_ENDIAN));
+  cpu_register_physical_memory((uint32_t)0x2006000,0x1000,
+        cpu_register_io_memory(net_read,net_write, (void *)&nextnet_state,DEVICE_NATIVE_ENDIAN));
+
+
+    /* and ethernet control/status registers *///including DMA for now, will seperate out later
+    cpu_register_physical_memory((uint32_t)0x2000110,0x4400,
+        cpu_register_io_memory(net_read,net_write, (void *)&nextnet_state,DEVICE_NATIVE_ENDIAN));
+
+    /* connect to virtual lan */
+    s->c.vlan = qemu_find_vlan(0,1); 
+    
+    /* register nic */
+    s->nic = qemu_new_nic(&nextnet_info,&s->c, "NeXT MB8795\0", NULL, s);
+
+    /* this needs to be read, will have to load rom file i guess */
+    uint8_t mac[6] = {0,0,0xf,0,0xf3,0x2};
+    memcpy(&s->mac,mac,6); 	
+    
+	/* allocate TX/RX and DMA irqs - will be global later */
+    s->irq = qemu_allocate_irqs(nextnet_irq, env, 4);
+    
+    /* this sets the nic's info */
+    qemu_format_nic_info_str(&s->nic->nc, mac);
+
+
+}
+/* It is likely that all register reads are bytes, while all CSR r/w are longs */
+static uint32_t net_readb(void*opaque, target_phys_addr_t addr)
+{
+    NextNetState *s = (NextNetState *)opaque;
+    
+//	CPUState *env = (CPUState *)env_g; 
+    switch(addr)
+    {
+        case 0x6000://TXSTAT
+            // DPRINTF("TXSTAT \tRead\n");
+            return s->tx_stat;
+        
+        case 0x6001:
+            DPRINTF("TXMASK \tRead\n");
+            return s->tx_mask;
+
+        case 0x6002:
+          //  DPRINTF("RXSTAT \tRead  %x @ %x\n",s->rx_stat,env->pc);
+            return s->rx_stat;
+
+        case 0x6003:
+          //  DPRINTF("RXMASK \tRead\n");
+            return s->rx_mask;
+
+        case 0x6004:
+            DPRINTF("TXMODE \tRead\n");
+            return s->tx_mode;
+        
+        case 0x6005:
+          //  DPRINTF("RXMODE \tRead\n");
+            return s->rx_mode;
+        
+        case 0x6006:
+            DPRINTF("RSTMODE \tRead\n");
+            return s->rst_mode;
+        
+        default:
+            fprintf(stderr,"NET Read B @ %x\n",addr);
+            return 0;
+    }
+}
+static uint32_t net_readw(void*opaque, target_phys_addr_t addr)
+{
+	fprintf(stderr,"S Read W @ %x\n",addr);
+	return 0;
+}
+static uint32_t net_readl(void*opaque, target_phys_addr_t addr)
+{
+    NextNetState *s = (NextNetState *)opaque;
+	CPUState *env = (CPUState *)env_g; 
+	switch(addr)
+    {
+        case 0x110:
+
+            //DPRINTF("TXCSR Read\n");
+            return s->tx_dma.csr;
+        case 0x4100: 
+            fprintf(stderr,"SAVEDBASE Read\n");
+            return s->tx_dma.savedbase;
+		case 0x4104: 
+            fprintf(stderr,"SAVELIMIT Read\n");
+			return s->tx_dma.savedlimit;
+		case 0x4114: 
+            fprintf(stderr,"TXLIMIT Read\n");
+			return s->tx_dma.limit;
+		case 0x4310:
+            fprintf(stderr,"TXBASE Read\n");
+			/* FUTURE :return nextdma_read(device, addr); */	
+			return s->tx_dma.basew;
+
+		case 0x150:
+           // fprintf(stderr,"RXCSR Read %x\n",s->rx_dma.csr);
+            return s->rx_dma.csr;
+        
+		case 0x4140:
+			return s->rx_dma.savedbase;
+        case 0x4144:
+            DPRINTF("SAVELIMIT %x @ %x\n",s->rx_dma.savedlimit,env->pc);
+            return s->rx_dma.savedlimit;
+		
+        default:
+        fprintf(stderr,"NET Read l @ %x\n",addr);
+        return 0;
+    }
+}
+#define NET_TXSTAT_CLEAR 0xFF
+#define NET_RXSTAT_CLEAR 0xFF
+static void net_writeb(void*opaque, target_phys_addr_t addr, uint32_t value)
+{
+//	CPUState *env = (CPUState *)env_g; 
+    NextNetState *s = (NextNetState *)opaque;
+    switch(addr)
+    {
+    	case 0x6000:
+            DPRINTF("TXSTAT \tWrite: %x\n",value);
+            if(value == NET_TXSTAT_CLEAR)
+               s->tx_stat = 0x80;
+            else
+                s->tx_stat = value;
+            break;
+        case 0x6001:
+            DPRINTF("TXMASK \tWrite: %x\n",value);
+            s->tx_mask = value;
+            break;
+        case 0x6002:
+         //   DPRINTF("RXSTAT \tWrite: %x @ %x\n",value,env->pc);
+            if(value == NET_RXSTAT_CLEAR)
+                s->rx_stat = 0;
+            else
+                s->rx_stat = value;
+            break;
+        case 0x6003:
+        //    DPRINTF("RXMASK \tWrite: %x\n",value);
+            s->rx_mask = value;
+            break;
+        case 0x6004:
+            DPRINTF("TXMODE \tWrite: %x\n",value);
+            s->tx_mode = value;
+            break;
+        case 0x6005:
+          //  DPRINTF("RXMODE \tWrite: %x\n",value);
+            s->rx_mode = value;
+            break;
+        case 0x6006:
+            DPRINTF("RSTMODE \tWrite: %x\n",value);
+            s->rst_mode = value;
+            break;
+        case 0x600d:
+        s->mac[(addr&0xF)-8] = value;         
+        DPRINTF("Set MAC ADDR %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+            s->mac[0],s->mac[1],s->mac[2],s->mac[3],s->mac[4],s->mac[5]);
+        qemu_macaddr_default_if_unset((MACAddr *)&s->mac);
+        break;
+        case 0x6008:case 0x6009:case 0x600a: case 0x600b: case 0x600c:
+        s->mac[(addr&0xF)-8] = value;         
+        break; 
+        case 0x6010:case 0x6011: case 0x6012: case 0x6013: case 0x6014:
+     //   break;
+        default:
+        fprintf(stderr,"NET Write B @ %x with %x\n",addr,value);
+    }
+}
+static void net_writew(void*opaque, target_phys_addr_t addr, uint32_t value)
+{
+	fprintf(stderr,"NET W w @ %x with %x\n",addr,value);
+}
+#define DMA_ENABLE		0x01000000
+#define DMA_SUPDATE		0x02000000
+#define DMA_COMPLETE	0x08000000
+
+#define DMA_M2DEV       0x0
+#define DMA_SETENABLE   0x00010000
+#define DMA_SETSUPDATE  0x00020000
+#define DMA_DEV2M		0x00040000
+#define DMA_CLRCOMPLETE 0x00080000
+#define DMA_RESET		0x00100000
+static void net_writel(void*opaque, target_phys_addr_t addr, uint32_t value)
+{
+	static int tx_count = 0;
+    NextNetState *s = (NextNetState *)opaque;
+	switch(addr)
+    {
+        case 0x110:
+        {
+				
+								
+				if(value & DMA_SETENABLE)
+				{
+                tx_count++;
+			//	if(tx_count % 4) return;
+				size_t len = (0xFFFFFFF & s->tx_dma.limit) - s->tx_dma.base;
+              DPRINTF("TXDMA ENABLE: %x len: %zu\n",s->tx_dma.base, len);
+				DPRINTF("TX Enable\n");
+                uint8_t buf[1600];//needs to be in dma struct?
+                cpu_physical_memory_read(s->tx_dma.base, buf, len); 
+                
+				qemu_send_packet(&s->nic->nc, buf,len);   
+               	s->tx_dma.csr |= DMA_COMPLETE | DMA_SUPDATE;
+			    s->tx_stat =  0x80;
+			//	if(tx_count > 1510) vm_stop(VMSTOP_DEBUG);
+				
+    			qemu_set_irq(s->irq[TX_I_DMA],3);
+
+				}
+				if(value & DMA_SETSUPDATE)
+					s->tx_dma.csr |= DMA_SUPDATE;	
+		
+				if(value & DMA_CLRCOMPLETE)
+					s->tx_dma.csr &= ~DMA_COMPLETE;
+
+				if(value & DMA_RESET)
+					s->tx_dma.csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE);	        
+		}
+        break;
+        
+        case 0x4100: 
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+			s->tx_dma.savedbase = value;
+		    break;
+		
+		case 0x4104: 
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+			s->tx_dma.savedlimit = value;
+		    break;
+		case 0x4110:
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+			s->tx_dma.base = value;
+			break;	
+        case 0x4114: 
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+			s->tx_dma.limit = value;
+		    break;
+		
+        case 0x4310:
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+			s->tx_dma.base = value;
+			/* FUTURE :nextdma_write(device, addr, value); */	
+            break;
+
+        case 0x150:
+            	if(value & DMA_DEV2M)
+                {
+                    DPRINTF("RX Dev to Memory\n");
+				}	
+				
+                if(value & DMA_SETENABLE)
+                    s->rx_dma.csr |= DMA_ENABLE;
+                
+				if(value & DMA_SETSUPDATE)
+					s->rx_dma.csr |= DMA_SUPDATE;	
+		
+				if(value & DMA_CLRCOMPLETE)
+					s->rx_dma.csr &= ~DMA_COMPLETE;
+
+				if(value & DMA_RESET)
+					s->rx_dma.csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE);				
+				//
+	
+				DPRINTF("RXCSR \tWrite: %x\n",value);
+	        	break;
+        
+        case 0x4150:
+            
+       // DPRINTF("Write l @ %x with %x\n",addr,value);
+            s->rx_dma.base = value;
+		//	s->rx_dma.savedbase = value;
+            break;
+
+        case 0x4154:
+            s->rx_dma.limit = value;
+       // DPRINTF("Write l @ %x with %x\n",addr,value);
+            break;
+
+        case 0x4158:
+            s->rx_dma.chainbase = value;
+       // DPRINTF("Write l @ %x with %x\n",addr,value);
+            break;
+    
+        case 0x415c:
+            s->rx_dma.chainlimit = value;
+       // DPRINTF("Write l @ %x with %x\n",addr,value);
+            //DPRINTF("Pointer write %x w %x\n",addr,value);
+            break;
+        default:
+        DPRINTF("Write l @ %x with %x\n",addr,value);
+    }
+
+}
+
+static int nextnet_can_rx(VLANClientState *nc)
+{
+    NextNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    if(s->rx_mode & 0x3)
+		return 1;
+	else
+		return -1;
+}
+
+static ssize_t nextnet_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+	NextNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    
+	DPRINTF("received packet %zu\n",size);
+
+	/* Ethernet DMA is supposedly 32 byte aligned */	
+	if((size % 32) != 0)
+	{
+		size -= size % 32;
+		size += 32;
+	}
+	
+	/* write the packet into memory */
+	cpu_physical_memory_write(s->rx_dma.base,buf,size);
+	
+	/* saved limit is checked to calculate packet size
+		by both the rom and netbsd */ 
+	s->rx_dma.savedlimit = (s->rx_dma.base + size);
+	s->rx_dma.savedbase = (s->rx_dma.base);
+	
+	/*32 bytes under savedbase seems to be some kind of register
+	of which the purpose is unknown as of yet*/
+	//stl_phys(s->rx_dma.base-32,0xFFFFFFFF);
+	
+	if((s->rx_dma.csr & DMA_SUPDATE)){	
+		s->rx_dma.base = s->rx_dma.chainbase;
+		s->rx_dma.limit = s->rx_dma.chainlimit;
+	}
+	//we received a packet
+    s->rx_stat = 0x80;
+	
+	//Set dma registers and raise an irq
+	s->rx_dma.csr |= DMA_COMPLETE; //DON'T CHANGE THIS!!!!
+   	qemu_set_irq(s->irq[RX_I_DMA],6);
+    
+	return size;
+}
+
+static void nextnet_cleanup(VLANClientState *nc)
+{
+}
+
+/* level and vector values taken from Plan 9 source */
+void nextnet_irq(void *opaque, int n, int level)
+{
+    CPUM68KState *s = (CPUM68KState *)opaque;
+    switch(n)
+	{
+		case TX_I:
+			int_status = 1<<10;
+			m68k_set_irq_level(s,3,27);
+			break;
+		
+		case RX_I:
+			int_status = 1<<9;
+			m68k_set_irq_level(s,3,27);
+			break;
+		
+		case TX_I_DMA:
+			int_status = 1<<28;
+			m68k_set_irq_level(s,6,30);
+            break;
+		
+		case RX_I_DMA:
+			int_status = 1<<27;
+			m68k_set_irq_level(s,6,30);
+			break;
+	}
+
+	
+}		
diff --git a/hw/next-net.h b/hw/next-net.h
new file mode 100644
index 0000000..28b7358
--- /dev/null
+++ b/hw/next-net.h
@@ -0,0 +1,2 @@ 
+
+void nextnet_init(void *opaque);