diff options
author | marcel <marcel@FreeBSD.org> | 2015-08-02 21:24:03 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2015-08-02 21:24:03 +0000 |
commit | 3e1c95104e63f08d7c46d1af23950782fda1ecc6 (patch) | |
tree | 0e738b59c82ea5656049a602df719bd08bbc37eb /tools | |
parent | 9a7989cc8d070902390d02dc79e67cfcffe82b4e (diff) | |
download | FreeBSD-src-3e1c95104e63f08d7c46d1af23950782fda1ecc6.zip FreeBSD-src-3e1c95104e63f08d7c46d1af23950782fda1ecc6.tar.gz |
Add an example program (in Python) for the AMD Am79c900 (ILACC)
ethernet controller. The ethernet controller is emulated by VMware
Fusion (for example) and is a good device to demonstrate how to use
the bus space and busdma functions due to its simple programming.
The program sets up the DMA structures, sends a DHCP discover packet,
waits 2 seconds, and iterates over the receive ring for an offer.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bus_space/examples/am79c900_diag.py | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/tools/bus_space/examples/am79c900_diag.py b/tools/bus_space/examples/am79c900_diag.py new file mode 100644 index 0000000..bcea31b --- /dev/null +++ b/tools/bus_space/examples/am79c900_diag.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +# +# Copyright (c) 2014 Marcel Moolenaar +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ +# + +''' +Simple diagnostics program fo the AMD Am89c900 series ILACC. +This ethernet controller is emulated by VMware Fusion among +possibly other virtualization platforms. + +The datasheet can be found here: + http://support.amd.com/TechDocs/18219.pdf + +This example program sends a single DHCP discovery packet, +waits 2 seconds and then iterates over the receive ring for +a targeted packet. + +For this program to function, connect the network interface +to a network with a DHCP server. In VMware Fusion this can +best be done by configuring the interface as a NAT interface +using the "Share with my Mac" setting. +''' + +import ctypes +import logging +import os +import sys +import time + +sys.path.append('/usr/lib') + +import bus +import busdma + + +# ILACC initialization block definition +class initblock(ctypes.LittleEndianStructure): + _fields_ = [('mode', ctypes.c_uint32), + ('hwaddr', ctypes.c_uint8 * 6), + ('_pad1_', ctypes.c_uint16), + ('filter', ctypes.c_uint16 * 4), + ('rxdesc', ctypes.c_uint32), + ('txdesc', ctypes.c_uint32), + ('_pad2_', ctypes.c_uint32)] + + +# ILACC ring buffer descriptor +class bufdesc(ctypes.LittleEndianStructure): + _fields_ = [('buffer', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('length', ctypes.c_uint32), + ('_pad_', ctypes.c_uint32)] + + +# The DHCP packet definition (incl. all headers) +class packet(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [('eth_dest', ctypes.c_uint8 * 6), + ('eth_src', ctypes.c_uint8 * 6), + ('eth_type', ctypes.c_uint16), + ('ip_vl', ctypes.c_uint8), + ('ip_de', ctypes.c_uint8), + ('ip_len', ctypes.c_uint16), + ('ip_id', ctypes.c_uint16), + ('ip_ff', ctypes.c_uint16), + ('ip_ttl', ctypes.c_uint8), + ('ip_proto', ctypes.c_uint8), + ('ip_cksum', ctypes.c_uint16), + ('ip_src', ctypes.c_uint32), + ('ip_dest', ctypes.c_uint32), + ('udp_src', ctypes.c_uint16), + ('udp_dest', ctypes.c_uint16), + ('udp_len', ctypes.c_uint16), + ('udp_cksum', ctypes.c_uint16), + ('bootp_op', ctypes.c_uint8), + ('bootp_htype', ctypes.c_uint8), + ('bootp_hlen', ctypes.c_uint8), + ('bootp_hops', ctypes.c_uint8), + ('bootp_xid', ctypes.c_uint32), + ('bootp_secs', ctypes.c_uint16), + ('bootp_flags', ctypes.c_uint16), + ('bootp_ciaddr', ctypes.c_uint32), + ('bootp_yiaddr', ctypes.c_uint32), + ('bootp_siaddr', ctypes.c_uint32), + ('bootp_giaddr', ctypes.c_uint32), + ('bootp_chaddr', ctypes.c_uint8 * 16), + ('bootp_sname', ctypes.c_uint8 * 64), + ('bootp_file', ctypes.c_uint8 * 128), + ('dhcp_magic', ctypes.c_uint32), + ('dhcp_options', ctypes.c_uint8 * 60)] + +MACFMT = '%02x:%02x:%02x:%02x:%02x:%02x' + +dev = 'pci0:2:1:0' + +logging.basicConfig(level=logging.DEBUG) + +pcicfg = bus.map(dev, 'pcicfg') +logging.debug('pcicfg=%s (%s)' % (pcicfg, dev)) + +vendor = bus.read_2(pcicfg, 0) +device = bus.read_2(pcicfg, 2) +if vendor != 0x1022 or device != 0x2000: + logging.error('Not an AMD PCnet-PCI (vendor=%x, device=%x)' % + (vendor, device)) + sys.exit(1) + +command = bus.read_2(pcicfg, 4) +if not (command & 1): + logging.info('enabling I/O port decoding') + command |= 1 + bus.write_2(pcicfg, 4, command) + +if not (command & 4): + logging.info('enabling bus mastering') + command |= 4 + bus.write_2(pcicfg, 4, command) + +bus.unmap(pcicfg) + +io = bus.map(dev, '10.io') +logging.debug('io=%s (%s)' % (io, dev)) + + +def delay(msec): + time.sleep(msec / 1000.0) + + +def ffs(x): + y = (1 + (x ^ (x-1))) >> 1 + return y.bit_length() + + +def ip_str(a): + return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255, + a & 255) + + +def mac_is(l, r): + for i in xrange(6): + if l[i] != r[i]: + return False + return True + + +def mac_str(m): + return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5]) + + +def rdbcr(reg): + bus.write_2(io, 0x12, reg & 0xffff) + return bus.read_2(io, 0x16) + + +def wrbcr(reg, val): + bus.write_2(io, 0x12, reg & 0xffff) + bus.write_2(io, 0x16, val & 0xffff) + + +def rdcsr(reg): + bus.write_2(io, 0x12, reg & 0xffff) + return bus.read_2(io, 0x10) + + +def wrcsr(reg, val): + bus.write_2(io, 0x12, reg & 0xffff) + bus.write_2(io, 0x10, val & 0xffff) + + +def start(): + wrcsr(0, 0x42) + delay(100) + + +def stop(): + wrcsr(0, 4) + delay(100) + + +mac = () +bcast = () +for o in xrange(6): + mac += (bus.read_1(io, o),) + bcast += (0xff,) +logging.info('ethernet address = ' + MACFMT % mac) + +stop() +wrbcr(20, 2) # reset +wrcsr(3, 0) # byte swapping mode +wrbcr(2, rdbcr(2) | 2) # Autoneg + +memsize = 32*1024 +bufsize = 1536 +nrxbufs = 16 +ntxbufs = 4 +logging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" % + (memsize, ntxbufs, nrxbufs)) + +mem_tag = busdma.tag_create(dev, 16, 0, 0xffffffff, memsize, 1, memsize, 0, 0) +dmamem = busdma.mem_alloc(mem_tag, 0) +busseg = busdma.md_first_seg(dmamem, busdma.MD_BUS_SPACE) +cpuseg = busdma.md_first_seg(dmamem, busdma.MD_VIRT_SPACE) +busaddr = busdma.seg_get_addr(busseg) +cpuaddr = busdma.seg_get_addr(cpuseg) +logging.debug("DMA memory: CPU address: %#x, device address: %#x" % + (cpuaddr, busaddr)) + +addr_initblock = cpuaddr +addr_rxdesc = addr_initblock + ctypes.sizeof(initblock) +addr_txdesc = addr_rxdesc + ctypes.sizeof(bufdesc) * nrxbufs +addr_rxbufs = addr_txdesc + ctypes.sizeof(bufdesc) * ntxbufs +addr_txbufs = addr_rxbufs + bufsize * nrxbufs + +ib = initblock.from_address(addr_initblock) +ib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20) +for i in xrange(len(mac)): + ib.hwaddr[i] = mac[i] +for i in xrange(4): + ib.filter[i] = 0xffff +ib.rxdesc = busaddr + (addr_rxdesc - cpuaddr) +ib.txdesc = busaddr + (addr_txdesc - cpuaddr) +ib._pad1_ = 0 +ib._pad2_ = 0 + +for i in xrange(nrxbufs): + bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) + bd.buffer = busaddr + (addr_rxbufs - cpuaddr) + bufsize * i + bd.flags = (1 << 31) | (15 << 12) | (-bufsize & 0xfff) + bd.length = 0 + bd._pad_ = 0 + +for i in xrange(ntxbufs): + bd = bufdesc.from_address(addr_txdesc + ctypes.sizeof(bufdesc) * i) + bd.buffer = busaddr + (addr_txbufs - cpuaddr) + bufsize * i + bd.flags = (15 << 12) + bd.length = 0 + bd._pad_ = 0 + +busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr) + +# Program address of DMA memory +wrcsr(1, busaddr) +wrcsr(2, busaddr >> 16) +delay(100) + +# Initialize hardware +wrcsr(0, 1) +logging.debug('Waiting for initialization to complete') +csr = rdcsr(0) +while (csr & 0x100) == 0: + logging.debug('CSR=%#x' % (csr)) + csr = rdcsr(0) + +start() + +pkt = packet.from_address(addr_txbufs) +ctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt)) +options = [53, 1, 1] +for i in xrange(len(options)): + pkt.dhcp_options[i] = options[i] +pkt.dhcp_magic = 0x63825363 +for i in xrange(6): + pkt.bootp_chaddr[i] = mac[i] +pkt.bootp_hlen = 6 +pkt.bootp_htype = 1 +pkt.bootp_op = 1 +pkt.udp_len = ctypes.sizeof(pkt) - 34 +pkt.udp_dest = 67 +pkt.udp_src = 68 +pkt.ip_dest = 0xffffffff +pkt.ip_cksum = 0x79a6 +pkt.ip_proto = 17 +pkt.ip_ttl = 64 +pkt.ip_len = ctypes.sizeof(pkt) - 14 +pkt.ip_vl = 0x45 +pkt.eth_type = 0x0800 +for i in xrange(6): + pkt.eth_src[i] = mac[i] + pkt.eth_dest[i] = bcast[i] +pktlen = ctypes.sizeof(pkt) + +busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize) + +bd = bufdesc.from_address(addr_txdesc) +bd.length = 0 +bd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff) + +busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr, + ctypes.sizeof(bufdesc)) + +wrcsr(0, 0x48) + +logging.info('DHCP discovery packet sent') + +# Now wait 2 seconds for a DHCP offer to be received. +logging.debug('Waiting 2 seconds for an offer to be received') +time.sleep(2) + +stop() + +busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr, + ctypes.sizeof(bufdesc) * nrxbufs) + +for i in xrange(nrxbufs): + bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) + if (bd.flags & 0x80000000): + continue + pkt = packet.from_address(addr_rxbufs + i * bufsize) + if mac_is(pkt.eth_dest, bcast): + logging.debug('RX #%d: broadcast packet: length %u' % (i, bd.length)) + continue + if not mac_is(pkt.eth_dest, mac): + logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest))) + continue + logging.debug('RX %d: packet from %s!' % (i, mac_str(pkt.eth_src))) + logging.info('Our IP address = %s' % (ip_str(pkt.ip_dest))) + +busdma.mem_free(dmamem) +busdma.tag_destroy(mem_tag) +bus.unmap(io) |