3 # Copyright (c) 2014 Marcel Moolenaar
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 Simple diagnostics program fo the AMD Am89c900 series ILACC.
32 This ethernet controller is emulated by VMware Fusion among
33 possibly other virtualization platforms.
35 The datasheet can be found here:
36 http://support.amd.com/TechDocs/18219.pdf
38 This example program sends a single DHCP discovery packet,
39 waits 2 seconds and then iterates over the receive ring for
42 For this program to function, connect the network interface
43 to a network with a DHCP server. In VMware Fusion this can
44 best be done by configuring the interface as a NAT interface
45 using the "Share with my Mac" setting.
54 sys.path.append('/usr/lib')
60 # ILACC initialization block definition
61 class initblock(ctypes.LittleEndianStructure):
62 _fields_ = [('mode', ctypes.c_uint32),
63 ('hwaddr', ctypes.c_uint8 * 6),
64 ('_pad1_', ctypes.c_uint16),
65 ('filter', ctypes.c_uint16 * 4),
66 ('rxdesc', ctypes.c_uint32),
67 ('txdesc', ctypes.c_uint32),
68 ('_pad2_', ctypes.c_uint32)]
71 # ILACC ring buffer descriptor
72 class bufdesc(ctypes.LittleEndianStructure):
73 _fields_ = [('buffer', ctypes.c_uint32),
74 ('flags', ctypes.c_uint32),
75 ('length', ctypes.c_uint32),
76 ('_pad_', ctypes.c_uint32)]
79 # The DHCP packet definition (incl. all headers)
80 class packet(ctypes.BigEndianStructure):
82 _fields_ = [('eth_dest', ctypes.c_uint8 * 6),
83 ('eth_src', ctypes.c_uint8 * 6),
84 ('eth_type', ctypes.c_uint16),
85 ('ip_vl', ctypes.c_uint8),
86 ('ip_de', ctypes.c_uint8),
87 ('ip_len', ctypes.c_uint16),
88 ('ip_id', ctypes.c_uint16),
89 ('ip_ff', ctypes.c_uint16),
90 ('ip_ttl', ctypes.c_uint8),
91 ('ip_proto', ctypes.c_uint8),
92 ('ip_cksum', ctypes.c_uint16),
93 ('ip_src', ctypes.c_uint32),
94 ('ip_dest', ctypes.c_uint32),
95 ('udp_src', ctypes.c_uint16),
96 ('udp_dest', ctypes.c_uint16),
97 ('udp_len', ctypes.c_uint16),
98 ('udp_cksum', ctypes.c_uint16),
99 ('bootp_op', ctypes.c_uint8),
100 ('bootp_htype', ctypes.c_uint8),
101 ('bootp_hlen', ctypes.c_uint8),
102 ('bootp_hops', ctypes.c_uint8),
103 ('bootp_xid', ctypes.c_uint32),
104 ('bootp_secs', ctypes.c_uint16),
105 ('bootp_flags', ctypes.c_uint16),
106 ('bootp_ciaddr', ctypes.c_uint32),
107 ('bootp_yiaddr', ctypes.c_uint32),
108 ('bootp_siaddr', ctypes.c_uint32),
109 ('bootp_giaddr', ctypes.c_uint32),
110 ('bootp_chaddr', ctypes.c_uint8 * 16),
111 ('bootp_sname', ctypes.c_uint8 * 64),
112 ('bootp_file', ctypes.c_uint8 * 128),
113 ('dhcp_magic', ctypes.c_uint32),
114 ('dhcp_options', ctypes.c_uint8 * 60)]
116 MACFMT = '%02x:%02x:%02x:%02x:%02x:%02x'
120 logging.basicConfig(level=logging.DEBUG)
122 pcicfg = bus.map(dev, 'pcicfg')
123 logging.debug('pcicfg=%s (%s)' % (pcicfg, dev))
125 vendor = bus.read_2(pcicfg, 0)
126 device = bus.read_2(pcicfg, 2)
127 if vendor != 0x1022 or device != 0x2000:
128 logging.error('Not an AMD PCnet-PCI (vendor=%x, device=%x)' %
132 command = bus.read_2(pcicfg, 4)
133 if not (command & 1):
134 logging.info('enabling I/O port decoding')
136 bus.write_2(pcicfg, 4, command)
138 if not (command & 4):
139 logging.info('enabling bus mastering')
141 bus.write_2(pcicfg, 4, command)
145 io = bus.map(dev, '10.io')
146 logging.debug('io=%s (%s)' % (io, dev))
150 time.sleep(msec / 1000.0)
154 y = (1 + (x ^ (x-1))) >> 1
155 return y.bit_length()
159 return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255,
171 return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5])
175 bus.write_2(io, 0x12, reg & 0xffff)
176 return bus.read_2(io, 0x16)
180 bus.write_2(io, 0x12, reg & 0xffff)
181 bus.write_2(io, 0x16, val & 0xffff)
185 bus.write_2(io, 0x12, reg & 0xffff)
186 return bus.read_2(io, 0x10)
190 bus.write_2(io, 0x12, reg & 0xffff)
191 bus.write_2(io, 0x10, val & 0xffff)
207 mac += (bus.read_1(io, o),)
209 logging.info('ethernet address = ' + MACFMT % mac)
213 wrcsr(3, 0) # byte swapping mode
214 wrbcr(2, rdbcr(2) | 2) # Autoneg
220 logging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" %
221 (memsize, ntxbufs, nrxbufs))
223 mem_tag = busdma.tag_create(dev, 16, 0, 0xffffffff, memsize, 1, memsize, 0, 0)
224 dmamem = busdma.mem_alloc(mem_tag, 0)
225 busseg = busdma.md_first_seg(dmamem, busdma.MD_BUS_SPACE)
226 cpuseg = busdma.md_first_seg(dmamem, busdma.MD_VIRT_SPACE)
227 busaddr = busdma.seg_get_addr(busseg)
228 cpuaddr = busdma.seg_get_addr(cpuseg)
229 logging.debug("DMA memory: CPU address: %#x, device address: %#x" %
232 addr_initblock = cpuaddr
233 addr_rxdesc = addr_initblock + ctypes.sizeof(initblock)
234 addr_txdesc = addr_rxdesc + ctypes.sizeof(bufdesc) * nrxbufs
235 addr_rxbufs = addr_txdesc + ctypes.sizeof(bufdesc) * ntxbufs
236 addr_txbufs = addr_rxbufs + bufsize * nrxbufs
238 ib = initblock.from_address(addr_initblock)
239 ib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20)
240 for i in xrange(len(mac)):
241 ib.hwaddr[i] = mac[i]
243 ib.filter[i] = 0xffff
244 ib.rxdesc = busaddr + (addr_rxdesc - cpuaddr)
245 ib.txdesc = busaddr + (addr_txdesc - cpuaddr)
249 for i in xrange(nrxbufs):
250 bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i)
251 bd.buffer = busaddr + (addr_rxbufs - cpuaddr) + bufsize * i
252 bd.flags = (1 << 31) | (15 << 12) | (-bufsize & 0xfff)
256 for i in xrange(ntxbufs):
257 bd = bufdesc.from_address(addr_txdesc + ctypes.sizeof(bufdesc) * i)
258 bd.buffer = busaddr + (addr_txbufs - cpuaddr) + bufsize * i
259 bd.flags = (15 << 12)
263 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr)
265 # Program address of DMA memory
267 wrcsr(2, busaddr >> 16)
270 # Initialize hardware
272 logging.debug('Waiting for initialization to complete')
274 while (csr & 0x100) == 0:
275 logging.debug('CSR=%#x' % (csr))
280 pkt = packet.from_address(addr_txbufs)
281 ctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt))
283 for i in xrange(len(options)):
284 pkt.dhcp_options[i] = options[i]
285 pkt.dhcp_magic = 0x63825363
287 pkt.bootp_chaddr[i] = mac[i]
291 pkt.udp_len = ctypes.sizeof(pkt) - 34
294 pkt.ip_dest = 0xffffffff
295 pkt.ip_cksum = 0x79a6
298 pkt.ip_len = ctypes.sizeof(pkt) - 14
300 pkt.eth_type = 0x0800
302 pkt.eth_src[i] = mac[i]
303 pkt.eth_dest[i] = bcast[i]
304 pktlen = ctypes.sizeof(pkt)
306 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize)
308 bd = bufdesc.from_address(addr_txdesc)
310 bd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff)
312 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr,
313 ctypes.sizeof(bufdesc))
317 logging.info('DHCP discovery packet sent')
319 # Now wait 2 seconds for a DHCP offer to be received.
320 logging.debug('Waiting 2 seconds for an offer to be received')
325 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr,
326 ctypes.sizeof(bufdesc) * nrxbufs)
328 for i in xrange(nrxbufs):
329 bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i)
330 if (bd.flags & 0x80000000):
332 pkt = packet.from_address(addr_rxbufs + i * bufsize)
333 if mac_is(pkt.eth_dest, bcast):
334 logging.debug('RX #%d: broadcast packet: length %u' % (i, bd.length))
336 if not mac_is(pkt.eth_dest, mac):
337 logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest)))
339 logging.debug('RX %d: packet from %s!' % (i, mac_str(pkt.eth_src)))
340 logging.info('Our IP address = %s' % (ip_str(pkt.ip_dest)))
342 busdma.mem_free(dmamem)
343 busdma.tag_destroy(mem_tag)