Support for the Ethernet-Audio device
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 4 Dec 2010 00:52:52 +0000 (16:52 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:57 +0000 (17:35 -0700)
Turn on support in your Makelocal.  Right now, the kernel copies
whatever it gets into a file (in /dev) that a program (like
test/eth_audio.c) can mmap.  When a packet comes in, the old buffer gets
sent back (including on the first time through).

Also adds some networking infrastructure (checksums, #defines), and the
eth_aud packets are intercepted (in a ghetto manner) from both the e1000
and the rl8186 NICs.

14 files changed:
Makeconfig
Makelocal.template
kern/arch/i686/e1000.c
kern/arch/i686/ne2k.c
kern/arch/i686/nic_common.h
kern/arch/i686/rl8168.c
kern/include/devfs.h
kern/include/eth_audio.h [new file with mode: 0644]
kern/include/net.h
kern/src/Makefrag
kern/src/eth_audio.c [new file with mode: 0644]
kern/src/init.c
kern/src/net.c [new file with mode: 0644]
tests/eth_audio.c [new file with mode: 0644]

index e2d0987..854ceac 100644 (file)
@@ -30,6 +30,7 @@ CONFIG_BOXBORO:=                   -D__CONFIG_BOXBORO__
 CONFIG_ARSC_SERVER:=               -D__CONFIG_ARSC_SERVER__
 CONFIG_RESET_STACKS:=              -D__CONFIG_RESET_STACKS__
 CONFIG_PCI_VERBOSE:=               -D__CONFIG_PCI_VERBOSE__
+CONFIG_ETH_AUDIO:=                 -D__CONFIG_ETH_AUDIO__
 
 # Userspace configuration parameters
 # By default, each of these options will be turned off
index b17a784..a51c2b4 100644 (file)
@@ -27,6 +27,8 @@
 # Resets stacks in smp_idle(), at the expense of backtrace
 #KERN_CFLAGS += $(CONFIG_RESET_STACKS)
 #KERN_CFLAGS += $(CONFIG_PCI_VERBOSE)
+# Rimas's Ethernet audio device
+#KERN_CFLAGS += $(CONFIG_ETH_AUDIO)
 
 #KERN_CFLAGS += -DDEVELOPER_NAME=waterman
 #KERN_CFLAGS += -DDEVELOPER_NAME=brho
index 251788d..0ec4c49 100644 (file)
@@ -32,6 +32,8 @@
 #include <frontend.h>
 #include <arch/frontend.h>
 
+#include <eth_audio.h>
+
 #define NUM_TX_DESCRIPTORS E1000_NUM_TX_DESCRIPTORS
 #define NUM_RX_DESCRIPTORS E1000_NUM_RX_DESCRIPTORS
 
@@ -623,7 +625,7 @@ void e1000_handle_rx_packet() {
        uint32_t num_frags = 0;
        
        char *rx_buffer = kmalloc(MAX_FRAME_SIZE, 0);
-       
+
        if (rx_buffer == NULL) panic ("Can't allocate page for incoming packet.");
        
        do {
@@ -696,6 +698,22 @@ void e1000_handle_rx_packet() {
        }
 #endif
 
+#ifdef __CONFIG_ETH_AUDIO__
+       /* TODO: move this, and all packet processing, out of this driver (including
+        * the ghetto buffer).  Note we don't handle IP fragment reassembly (though
+        * this isn't an issue for the eth_audio). */
+       struct ethaud_udp_packet *packet = (struct ethaud_udp_packet*)rx_buffer;
+       uint8_t protocol = packet->ip_hdr.protocol;
+       uint16_t udp_port = ntohs(packet->udp_hdr.dst_port);
+       if (protocol == IPPROTO_UDP && udp_port == ETH_AUDIO_RCV_PORT) {
+               eth_audio_newpacket(packet);
+               // Advance the tail pointer                             
+               e1000_rx_index = rx_des_loop_cur;
+               e1000_wr32(E1000_RDT, e1000_rx_index);
+               return;
+       }
+#endif /* __CONFIG_ETH_AUDIO__ */
+
        spin_lock(&packet_buffers_lock);
 
        if (num_packet_buffers >= MAX_PACKET_BUFFERS) {
index 8217313..01db318 100644 (file)
@@ -397,34 +397,6 @@ void ne2k_handle_rx_packet() {
        return;
 }
 
-// copied with love (and modification) from tcp/ip illistrated vl 2 1995 pg 236
-// bsd licenced
-uint16_t cksum(char *CT(len) ip, int len) {
-       
-       uint32_t sum = 0;
-       // Next two lines for ivy. Grr.
-       char *curr_ip = ip;
-       int curr_len = len;
-
-       while(curr_len > 1) {
-               sum += *((uint16_t*) curr_ip);
-               curr_ip = curr_ip + 2;
-               
-               if(sum & 0x80000000)    /* if high order bit set, fold */
-                       sum = (sum & 0xFFFF) + (sum >> 16);
-                       curr_len -= 2;
-               }
-
-               if(curr_len)             /* take care of left over byte */
-                       sum += *curr_ip;
-                 
-               while(sum>>16)
-                       sum = (sum & 0xFFFF) + (sum >> 16);
-
-               return ~sum;
-}
-
-
 // Main routine to send a frame. May be completely broken.
 int ne2k_send_frame(const char *data, size_t len) {
 
index e1ee330..9fcc17f 100644 (file)
@@ -8,7 +8,9 @@
 
 // Packet sizes
 #define MTU              1500
-#define MAX_FRAME_SIZE   (MTU + 14)
+/* 14 for the header, 4 for something else.  It's either the CRC at the end
+ * (which seems to be 0'd), or for the optional 802.1q tag. */
+#define MAX_FRAME_SIZE   (MTU + 18)
 #define MIN_FRAME_SIZE   60 // See the spec...
 
 // Maximum packet buffers we can handle at any given time
index d12b163..a13c00e 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <pmap.h>
 
+#include <eth_audio.h>
+
 /** @file
  * @brief Realtek RL8168 Driver
  *
@@ -499,6 +501,20 @@ void rl8168_handle_rx_packet() {
                return;
        }
 #endif
+#ifdef __CONFIG_ETH_AUDIO__
+       /* TODO: move this, and all packet processing, out of this driver (including
+        * the ghetto buffer).  Note we don't handle IP fragment reassembly (though
+        * this isn't an issue for the eth_audio). */
+       struct ethaud_udp_packet *packet = (struct ethaud_udp_packet*)rx_buffer;
+       uint8_t protocol = packet->ip_hdr.protocol;
+       uint16_t udp_port = ntohs(packet->udp_hdr.dst_port);
+       if (protocol == IPPROTO_UDP && udp_port == ETH_AUDIO_RCV_PORT) {
+               rx_des_cur = rx_des_loop_cur;
+               eth_audio_newpacket(packet);
+               kfree(rx_buffer);
+               return;
+       }
+#endif /* __CONFIG_ETH_AUDIO__ */
 
        spin_lock(&packet_buffers_lock);
 
index 825197e..cd9fae5 100644 (file)
@@ -3,7 +3,8 @@
  * See LICENSE for details.
  *
  * Devfs: filesystem interfaces to devices.  For now, we just create the
- * needed/discovered devices in KFS in its /dev/ folder. */
+ * needed/discovered devices in KFS in its /dev/ folder.  In the future, we
+ * might want to do something like nodes like other Unixes. */
 
 #ifndef ROS_KERN_DEVFS_H
 #define ROS_KERN_DEVFS_H
diff --git a/kern/include/eth_audio.h b/kern/include/eth_audio.h
new file mode 100644 (file)
index 0000000..ce2c4a7
--- /dev/null
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Rimas's Ethernet-Audio device */
+
+#ifndef ROS_KERN_ETH_AUDIO_H
+#define ROS_KERN_ETH_AUDIO_H
+
+#include <devfs.h>
+#include <net.h>
+
+#define ETH_AUDIO_RCV_PORT 1792
+#define ETH_AUDIO_SEND_PORT 1792
+/* 10 channels * 4 bytes/channel * 32 samples/packet = 1280.  + 2 bytes for a
+ * sequence ID */
+#define ETH_AUDIO_PAYLOAD_SZ 1282
+#define ETH_AUDIO_IP_HDR_SZ 20
+#define ETH_AUDIO_HEADER_OFF (ETH_HDR_SZ + ETH_AUDIO_IP_HDR_SZ + UDP_HDR_SZ)
+#define ETH_AUDIO_FRAME_SZ (ETH_AUDIO_PAYLOAD_SZ + ETH_AUDIO_HEADER_OFF)
+
+
+/* Ethernet frame for what one of our packets looks like (since we don't have
+ * the infrastructure to build one properly).  This implies that there are no
+ * IP or ethernet options. */
+struct ethaud_udp_packet {
+       struct ethernet_hdr                     eth_hdr;
+       struct ip_hdr                           ip_hdr;
+       struct udp_hdr                          udp_hdr;
+       char                                            payload[ETH_AUDIO_PAYLOAD_SZ];
+} __attribute__((packed));
+
+/* These two files are always open, like other device nodes.  Processes can open
+ * and mmap them.  Don't do shit like unlinking them. */
+struct file *ethaud_in, *ethaud_out;
+
+void eth_audio_init(void);
+/* This is called by net subsys when it detects an ethernet audio packet */
+void eth_audio_newpacket(void *buf);
+
+#endif /* ROS_KERN_ETH_AUDIO_H */
index f7bf4fc..e9b9ba9 100644 (file)
@@ -9,5 +9,49 @@
 
 #include <bits/netinet.h>
 
+/* A few other useful standard defines.  Note the IP header can change size. */
+#define ETH_HDR_SZ 14
+#define UDP_HDR_SZ 8
+#define IP_ETH_TYPE 0x0800
+
+/* ROS defaults: */
+#define DEFAULT_TTL 64
+
+/* Don't forget the bytes are in network order */
+struct ethernet_hdr {
+       uint8_t                                         dst_mac[6];
+       uint8_t                                         src_mac[6];
+       uint16_t                                        eth_type;
+       /* might be an optional 802.1q tag here */
+};
+
+/* For the bit-enumerated fields, note that you need to read "backwards" through
+ * the byte (first bits in memory are the "LSB" of the byte).  Can't seem to be
+ * able to do it with flags/fragments (3/13 bits each...). */
+struct ip_hdr {
+       unsigned                                        hdr_len : 4;
+       unsigned                                        version : 4;
+       uint8_t                                         tos;
+       uint16_t                                        packet_len;
+       uint16_t                                        id;
+       uint16_t                                        flags_frags;
+       uint8_t                                         ttl;
+       uint8_t                                         protocol;
+       uint16_t                                        checksum;
+       uint32_t                                        src_addr;
+       uint32_t                                        dst_addr;
+       /* Options could be here (depends on the hdr length) */
+} __attribute__((packed));
+
+struct udp_hdr {
+       uint16_t                                        src_port;
+       uint16_t                                        dst_port;
+       uint16_t                                        length;
+       uint16_t                                        checksum;
+};
+
+uint16_t __ip_checksum(void *buf, unsigned int len, uint32_t sum);
+uint16_t ip_checksum(struct ip_hdr *ip_hdr);
+uint16_t udp_checksum(struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr);
 
 #endif /* ROS_KERN_NET_H */
index 2cd1e46..17c3f99 100644 (file)
@@ -49,6 +49,8 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/testing.c \
                  $(KERN_SRC_DIR)/pagemap.c \
                  $(KERN_SRC_DIR)/kthread.c \
+                 $(KERN_SRC_DIR)/eth_audio.c \
+                 $(KERN_SRC_DIR)/net.c \
                  $(KERN_SRC_DIR)/arsc.c
 
 # Only build files if they exist.
diff --git a/kern/src/eth_audio.c b/kern/src/eth_audio.c
new file mode 100644 (file)
index 0000000..f39a8f6
--- /dev/null
@@ -0,0 +1,191 @@
+/* Copyright (c) 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Rimas's Ethernet-Audio device */
+
+#include <eth_audio.h>
+#include <string.h>
+#include <devfs.h>
+#include <page_alloc.h>
+#include <pmap.h>
+#include <arch/nic_common.h>
+
+struct file_operations ethaud_in_f_op;
+struct file_operations ethaud_out_f_op;
+struct page_map_operations ethaud_pm_op;
+
+/* There is only one packet we'll ever send out.  It's a full ethernet frame
+ * that we build and submit to send_frame() (which does another memcpy). */
+struct ethaud_udp_packet eth_udp_out;
+
+/* Builds the device nodes in /dev */
+void eth_audio_init(void)
+{
+       struct page *page;
+       ethaud_in = make_device("/dev/eth_audio_in", S_IRUSR, __S_IFBLK,
+                               &ethaud_in_f_op);
+       ethaud_out = make_device("/dev/eth_audio_out", S_IRUSR | S_IWUSR, __S_IFBLK,
+                                &ethaud_out_f_op);
+       /* make sure the inode tracks the right pm (not it's internal one) */
+       ethaud_in->f_dentry->d_inode->i_mapping->pm_op = &ethaud_pm_op;
+       ethaud_out->f_dentry->d_inode->i_mapping->pm_op = &ethaud_pm_op;
+       /* zalloc pages and associate them with the devices' inodes */
+       assert(!kpage_alloc(&page));
+       memset(page2kva(page), 0, PGSIZE);
+       ethaud_in->f_dentry->d_inode->i_fs_info = page;
+       assert(!kpage_alloc(&page));
+       memset(page2kva(page), 0, PGSIZE);
+       ethaud_out->f_dentry->d_inode->i_fs_info = page;
+}
+
+/* Lots of unnecessary copies.  For now, we need to build the ethernet frame,
+ * which means we prepend the ethernet header... */
+static void eth_audio_sendpacket(void *buf)
+{
+       int retval;
+       /* Fill the outgoing buffer (Copy #1.  2 is in send_frame, 3 is the DMA). */
+       memcpy(&eth_udp_out.payload, buf, ETH_AUDIO_PAYLOAD_SZ);
+       /* Make sure there is still a reasonable header. */
+       static_assert(sizeof(eth_udp_out) == ETH_AUDIO_FRAME_SZ);
+       /* Should compute the UDP checksum before sending the frame out.  The
+        * Eth-audio device shouldn't care (and Linux seems to be okay with packets
+        * that have no checksum (but not a wrong checksum)).  Technically, this
+        * hurts our performance a bit (and some NICs can offload this). */
+       eth_udp_out.udp_hdr.checksum = htons(udp_checksum(&eth_udp_out.ip_hdr,
+                                                         &eth_udp_out.udp_hdr));
+       /* Send it out */
+       retval = send_frame((const char*)&eth_udp_out, ETH_AUDIO_FRAME_SZ);
+       assert(retval >= 0);
+}
+
+/* This is how we know who to send the packet back to, since we have no real
+ * networking stack.  Lots of assumptions about how things stay in sync. */
+static void eth_audio_prep_response(struct ethaud_udp_packet *incoming,
+                                    struct ethaud_udp_packet *outgoing)
+{
+       /* If you're looking for optimizations, we can do this just once */
+       memcpy(&outgoing->eth_hdr.dst_mac, &incoming->eth_hdr.src_mac, 6); 
+       memcpy(&outgoing->eth_hdr.src_mac, device_mac, 6); 
+       outgoing->eth_hdr.eth_type = htons(IP_ETH_TYPE);
+       outgoing->ip_hdr.version = IPPROTO_IPV4;
+       outgoing->ip_hdr.hdr_len = ETH_AUDIO_IP_HDR_SZ >> 2;
+       outgoing->ip_hdr.tos = 0;
+       outgoing->ip_hdr.packet_len = htons(ETH_AUDIO_PAYLOAD_SZ + UDP_HDR_SZ +
+                                           ETH_AUDIO_IP_HDR_SZ);
+       outgoing->ip_hdr.id = htons(0);
+       outgoing->ip_hdr.flags_frags = htons(0);
+       outgoing->ip_hdr.ttl = DEFAULT_TTL;
+       outgoing->ip_hdr.protocol = IPPROTO_UDP;
+       /* Need a non-broadcast IP.  Picking one higher than the sender's */
+       outgoing->ip_hdr.src_addr = htonl(ntohl(incoming->ip_hdr.src_addr) + 1);
+       outgoing->ip_hdr.dst_addr = incoming->ip_hdr.src_addr;
+       /* Since the IP header is set already, we can compute the checksum. */
+       outgoing->ip_hdr.checksum = htons(ip_checksum(&outgoing->ip_hdr));
+       outgoing->udp_hdr.src_port = htons(ETH_AUDIO_SEND_PORT);
+       outgoing->udp_hdr.dst_port = incoming->udp_hdr.src_port;
+       outgoing->udp_hdr.length = htons(ETH_AUDIO_PAYLOAD_SZ + UDP_HDR_SZ);
+       outgoing->udp_hdr.checksum = htons(0);
+}
+
+/* This is called by net subsys when it detects an ethernet audio packet.  Make
+ * sure the in device has the contents, and send out the old frame. */
+void eth_audio_newpacket(void *buf)
+{
+       struct page *in_page, *out_page;
+       /* Put info from the packet into the outgoing packet */
+       eth_audio_prep_response((struct ethaud_udp_packet*)buf, &eth_udp_out);
+       in_page = (struct page*)ethaud_in->f_dentry->d_inode->i_fs_info;
+       out_page = (struct page*)ethaud_out->f_dentry->d_inode->i_fs_info;
+       /* third copy (1st being the NIC to RAM). */
+       memcpy(page2kva(in_page), buf + ETH_AUDIO_HEADER_OFF, ETH_AUDIO_PAYLOAD_SZ);
+       /* Send the current outbound packet (can consider doing this by fsync) */
+       eth_audio_sendpacket(page2kva(out_page));
+}
+
+/* mmap() calls this to do any FS specific mmap() work.  Since our files are
+ * defined to be only one page, we need to make sure they aren't mmapping too
+ * much, and that it isn't a PRIVATE mapping.
+ *
+ * Then we need to make sure the one page of the VMR is mapped in the page table
+ * (circumventing handle_page_fault and the page cache).  Avoiding the page
+ * cache means we won't need to worry about accidentally unmapping this under
+ * pressure and having to remap it (which will cause a readpage). */
+int eth_audio_mmap(struct file *file, struct vm_region *vmr)
+{
+       struct page *page = (struct page*)file->f_dentry->d_inode->i_fs_info;
+       /* Only allow mmaping from the start of the file */
+       if (vmr->vm_foff)
+               return -1;
+       /* Only allow mmaping of a page */
+       if (vmr->vm_end - vmr->vm_base != PGSIZE)
+               return -1;
+       /* No private mappings (would be ignored anyway) */
+       if (vmr->vm_flags & MAP_PRIVATE)
+               return -1;
+       assert(page);
+       /* Get the PTE for the page this VMR represents */
+       pte_t *pte = pgdir_walk(vmr->vm_proc->env_pgdir, (void*)vmr->vm_base, 1);
+       if (!pte)
+               return -ENOMEM;
+       /* If there was a page there, it should have be munmapp()d. */
+       assert(!PAGE_PRESENT(*pte));
+       /* update the page table */
+       int pte_prot = (vmr->vm_prot & PROT_WRITE) ? PTE_USER_RW :
+                      (vmr->vm_prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
+       /* Storing a reference to the page in the PTE */
+       page_incref(page);
+       *pte = PTE(page2ppn(page), PTE_P | pte_prot);
+       return 0;
+}
+
+/* This shouldn't be called.  It could be if we ever handled a PF on the device,
+ * and the device wasn't present in the page table.  This should not happen,
+ * since ea_mmap() should have sorted that out.  Even if there was an unmap()
+ * then a new mmap(), it still shouldn't happen. */
+int eth_audio_readpage(struct page_map *pm, struct page *page)
+{
+       warn("Eth audio readpage!  (Did you page fault?)");
+       return -1;
+}
+
+/* File operations */
+struct file_operations ethaud_in_f_op = {
+       dev_c_llseek,   /* Errors out, can't llseek */
+       0,                              /* Can't read, only mmap */
+       0,                              /* Can't write, only mmap */
+       kfs_readdir,    /* this will fail gracefully */
+       eth_audio_mmap,
+       kfs_open,
+       kfs_flush,
+       kfs_release,
+       0,                              /* fsync - makes no sense */
+       kfs_poll,
+       0,      /* readv */
+       0,      /* writev */
+       kfs_sendpage,
+       kfs_check_flags,
+};
+
+struct file_operations ethaud_out_f_op = {
+       dev_c_llseek,   /* Errors out, can't llseek */
+       0,                              /* Can't read, only mmap */
+       0,                              /* Can't write, only mmap */
+       kfs_readdir,    /* this will fail gracefully */
+       eth_audio_mmap,
+       kfs_open,
+       kfs_flush,
+       kfs_release,
+       0,                              /* fsync - TODO: make this send the packet */
+       kfs_poll,
+       0,      /* readv */
+       0,      /* writev */
+       kfs_sendpage,
+       kfs_check_flags,
+};
+
+/* Eth audio page map ops: */
+struct page_map_operations ethaud_pm_op = {
+       eth_audio_readpage,
+};
+
index b3b9a33..d560c57 100644 (file)
@@ -42,6 +42,8 @@
 #include <blockdev.h>
 #include <ext2fs.h>
 #include <kthread.h>
+#include <net.h>
+#include <eth_audio.h>
 
 // zra: flag for Ivy
 int booting = 1;
@@ -98,7 +100,10 @@ void kernel_init(multiboot_info_t *mboot_info)
        enable_irq();
 #ifdef __CONFIG_EXT2FS__
        mount_fs(&ext2_fs_type, "/dev/ramdisk", "/mnt", 0);
-#endif
+#endif /* __CONFIG_EXT2FS__ */
+#ifdef __CONFIG_ETH_AUDIO__
+       eth_audio_init();
+#endif /* __CONFIG_ETH_AUDIO__ */
                
        // zra: let's Ivy know we're done booting
        booting = 0;
diff --git a/kern/src/net.c b/kern/src/net.c
new file mode 100644 (file)
index 0000000..21f4768
--- /dev/null
@@ -0,0 +1,66 @@
+/* Copyright (c) 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Arch independent networking infrastructure */
+
+/* Computes an IP checksum over buf.  The checksum is the one's-comp of the
+ * one's complement 16 bit sum of the payload.  Anything above 16 bits gets
+ * added back to the lower 16 bits, which is what the mask and add is doing.
+ * Check out http://www.netfor2.com/checksum.html for more info.
+ *
+ * If you want to start with a sum from something else (like the UDP
+ * pseudo-header), pass it in as init_sum. */
+
+#include <net.h>
+#include <stdio.h>
+
+uint16_t __ip_checksum(void *buf, unsigned int len, uint32_t sum)
+{
+       /* Knock out 2 bytes at a time */
+       while (len > 1) {
+               /* Careful of endianness.  The packet is in network ordering */
+               sum += ntohs(*((uint16_t*)buf));
+               buf += sizeof(uint16_t);
+               /* In case we get close to overflowing while summing. */
+               if (sum & 0x80000000)
+                       sum = (sum & 0xFFFF) + (sum >> 16);
+               len -= 2;
+       }
+       /* Handle the last byte, if any */
+       if (len)
+               sum += *(uint8_t*)buf;
+       /* Add the top 16 bytes to the lower ones, til it is done */
+       while (sum >> 16)
+               sum = (sum & 0xFFFF) + (sum >> 16);
+       return ~sum;
+}
+
+/* Computes the checksum for the IP header.  We could write it in, but for now
+ * we'll return the checksum (in host-ordering) and have the caller store the
+ * value.  The retval is in host ordering. */
+uint16_t ip_checksum(struct ip_hdr *ip_hdr)
+{
+       unsigned int ip_hdr_len = ip_hdr->hdr_len * sizeof(uint32_t);
+       ip_hdr->checksum = 0;
+       return __ip_checksum(ip_hdr, ip_hdr_len, 0);
+}
+
+/* Computes the checksum for the UDP header.  We could write it in, but for now
+ * we'll return the checksum (in host-ordering) and have the caller store the
+ * value.  Note that the UDP header needs info from the IP header (strictly
+ * speaking, just the src and destination IPs).  */
+uint16_t udp_checksum(struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr)
+{
+       /* Add up the info for the UDP pseudo-header */
+       uint32_t udp_pseudosum = 0;
+       uint16_t udp_len = ntohs(udp_hdr->length);
+       udp_hdr->checksum = 0;
+       udp_pseudosum += ntohs(ip_hdr->src_addr & 0xffff);
+       udp_pseudosum += ntohs(ip_hdr->src_addr >> 16);
+       udp_pseudosum += ntohs(ip_hdr->dst_addr & 0xffff);
+       udp_pseudosum += ntohs(ip_hdr->dst_addr >> 16);
+       udp_pseudosum += ip_hdr->protocol;
+       udp_pseudosum += udp_len;
+       return __ip_checksum(udp_hdr, udp_len, udp_pseudosum);
+}
diff --git a/tests/eth_audio.c b/tests/eth_audio.c
new file mode 100644 (file)
index 0000000..e417645
--- /dev/null
@@ -0,0 +1,42 @@
+#include <stdio.h> 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <arch/arch.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <timing.h>
+#include <assert.h>
+
+/* Simple test program for the audio device.  Just mmaps the stuff and reads. */
+int main() 
+{ 
+       int in_fd, out_fd;
+       void *in_buf, *out_buf;
+       in_fd = open("/dev/eth_audio_in", O_RDONLY);
+       out_fd = open("/dev/eth_audio_out", O_RDWR);
+       assert(in_fd != -1);
+       assert(out_fd != -1);
+       in_buf = mmap(0, PGSIZE, PROT_READ, 0, in_fd, 0);
+       if (in_buf == MAP_FAILED) {
+               int err = errno;
+               perror("Can't mmap the input buf:");
+       }
+       out_buf = mmap(0, PGSIZE, PROT_READ | PROT_WRITE, MAP_POPULATE, out_fd, 0);
+       if (out_buf == MAP_FAILED) {
+               int err = errno;
+               perror("Can't mmap the output buf:");
+       }
+       strncpy(out_buf, "Nanwan loves you!\n", 19);
+
+       for (int i = 0; i < 20; i++) {
+               udelay(5000000);
+               printf("Contents: %s", in_buf);
+       }
+
+       close(in_fd);
+       close(out_fd);
+}