1286212Smarcel#!/usr/bin/env python
2286212Smarcel#
3286212Smarcel# Copyright (c) 2014 Marcel Moolenaar
4286212Smarcel# All rights reserved.
5286212Smarcel#
6286212Smarcel# Redistribution and use in source and binary forms, with or without
7286212Smarcel# modification, are permitted provided that the following conditions
8286212Smarcel# are met:
9286212Smarcel#
10286212Smarcel# 1. Redistributions of source code must retain the above copyright
11286212Smarcel#    notice, this list of conditions and the following disclaimer.
12286212Smarcel# 2. Redistributions in binary form must reproduce the above copyright
13286212Smarcel#    notice, this list of conditions and the following disclaimer in the
14286212Smarcel#    documentation and/or other materials provided with the distribution.
15286212Smarcel#
16286212Smarcel# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17286212Smarcel# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18286212Smarcel# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19286212Smarcel# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20286212Smarcel# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21286212Smarcel# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22286212Smarcel# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23286212Smarcel# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24286212Smarcel# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25286212Smarcel# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26286212Smarcel#
27286212Smarcel# $FreeBSD$
28286212Smarcel#
29286212Smarcel
30286212Smarcel'''
31286212SmarcelSimple diagnostics program fo the AMD Am89c900 series ILACC.
32286212SmarcelThis ethernet controller is emulated by VMware Fusion among
33286212Smarcelpossibly other virtualization platforms.
34286212Smarcel
35286212SmarcelThe datasheet can be found here:
36286212Smarcel    http://support.amd.com/TechDocs/18219.pdf
37286212Smarcel
38286212SmarcelThis example program sends a single DHCP discovery packet,
39286212Smarcelwaits 2 seconds and then iterates over the receive ring for
40286212Smarcela targeted packet.
41286212Smarcel
42286212SmarcelFor this program to function, connect the network interface
43286212Smarcelto a network with a DHCP server. In VMware Fusion this can
44286212Smarcelbest be done by configuring the interface as a NAT interface
45286212Smarcelusing the "Share with my Mac" setting.
46286212Smarcel'''
47286212Smarcel
48286212Smarcelimport ctypes
49286212Smarcelimport logging
50286212Smarcelimport os
51286212Smarcelimport sys
52286212Smarcelimport time
53286212Smarcel
54286212Smarcelsys.path.append('/usr/lib')
55286212Smarcel
56286212Smarcelimport bus
57286212Smarcelimport busdma
58286212Smarcel
59286212Smarcel
60286212Smarcel# ILACC initialization block definition
61286212Smarcelclass initblock(ctypes.LittleEndianStructure):
62286212Smarcel    _fields_ = [('mode', ctypes.c_uint32),
63286212Smarcel                ('hwaddr', ctypes.c_uint8 * 6),
64286212Smarcel                ('_pad1_', ctypes.c_uint16),
65286212Smarcel                ('filter', ctypes.c_uint16 * 4),
66286212Smarcel                ('rxdesc', ctypes.c_uint32),
67286212Smarcel                ('txdesc', ctypes.c_uint32),
68286212Smarcel                ('_pad2_', ctypes.c_uint32)]
69286212Smarcel
70286212Smarcel
71286212Smarcel# ILACC ring buffer descriptor
72286212Smarcelclass bufdesc(ctypes.LittleEndianStructure):
73286212Smarcel    _fields_ = [('buffer', ctypes.c_uint32),
74286212Smarcel                ('flags', ctypes.c_uint32),
75286212Smarcel                ('length', ctypes.c_uint32),
76286212Smarcel                ('_pad_', ctypes.c_uint32)]
77286212Smarcel
78286212Smarcel
79286212Smarcel# The DHCP packet definition (incl. all headers)
80286212Smarcelclass packet(ctypes.BigEndianStructure):
81286212Smarcel    _pack_ = 1
82286212Smarcel    _fields_ = [('eth_dest', ctypes.c_uint8 * 6),
83286212Smarcel                ('eth_src', ctypes.c_uint8 * 6),
84286212Smarcel                ('eth_type', ctypes.c_uint16),
85286212Smarcel                ('ip_vl', ctypes.c_uint8),
86286212Smarcel                ('ip_de', ctypes.c_uint8),
87286212Smarcel                ('ip_len', ctypes.c_uint16),
88286212Smarcel                ('ip_id', ctypes.c_uint16),
89286212Smarcel                ('ip_ff', ctypes.c_uint16),
90286212Smarcel                ('ip_ttl', ctypes.c_uint8),
91286212Smarcel                ('ip_proto', ctypes.c_uint8),
92286212Smarcel                ('ip_cksum', ctypes.c_uint16),
93286212Smarcel                ('ip_src', ctypes.c_uint32),
94286212Smarcel                ('ip_dest', ctypes.c_uint32),
95286212Smarcel                ('udp_src', ctypes.c_uint16),
96286212Smarcel                ('udp_dest', ctypes.c_uint16),
97286212Smarcel                ('udp_len', ctypes.c_uint16),
98286212Smarcel                ('udp_cksum', ctypes.c_uint16),
99286212Smarcel                ('bootp_op', ctypes.c_uint8),
100286212Smarcel                ('bootp_htype', ctypes.c_uint8),
101286212Smarcel                ('bootp_hlen', ctypes.c_uint8),
102286212Smarcel                ('bootp_hops', ctypes.c_uint8),
103286212Smarcel                ('bootp_xid', ctypes.c_uint32),
104286212Smarcel                ('bootp_secs', ctypes.c_uint16),
105286212Smarcel                ('bootp_flags', ctypes.c_uint16),
106286212Smarcel                ('bootp_ciaddr', ctypes.c_uint32),
107286212Smarcel                ('bootp_yiaddr', ctypes.c_uint32),
108286212Smarcel                ('bootp_siaddr', ctypes.c_uint32),
109286212Smarcel                ('bootp_giaddr', ctypes.c_uint32),
110286212Smarcel                ('bootp_chaddr', ctypes.c_uint8 * 16),
111286212Smarcel                ('bootp_sname', ctypes.c_uint8 * 64),
112286212Smarcel                ('bootp_file', ctypes.c_uint8 * 128),
113286212Smarcel                ('dhcp_magic', ctypes.c_uint32),
114286212Smarcel                ('dhcp_options', ctypes.c_uint8 * 60)]
115286212Smarcel
116286212SmarcelMACFMT = '%02x:%02x:%02x:%02x:%02x:%02x'
117286212Smarcel
118286212Smarceldev = 'pci0:2:1:0'
119286212Smarcel
120286212Smarcellogging.basicConfig(level=logging.DEBUG)
121286212Smarcel
122286212Smarcelpcicfg = bus.map(dev, 'pcicfg')
123286212Smarcellogging.debug('pcicfg=%s (%s)' % (pcicfg, dev))
124286212Smarcel
125286212Smarcelvendor = bus.read_2(pcicfg, 0)
126286212Smarceldevice = bus.read_2(pcicfg, 2)
127286212Smarcelif vendor != 0x1022 or device != 0x2000:
128286212Smarcel    logging.error('Not an AMD PCnet-PCI (vendor=%x, device=%x)' %
129286212Smarcel                  (vendor, device))
130286212Smarcel    sys.exit(1)
131286212Smarcel
132286212Smarcelcommand = bus.read_2(pcicfg, 4)
133286212Smarcelif not (command & 1):
134286212Smarcel    logging.info('enabling I/O port decoding')
135286212Smarcel    command |= 1
136286212Smarcel    bus.write_2(pcicfg, 4, command)
137286212Smarcel
138286212Smarcelif not (command & 4):
139286212Smarcel    logging.info('enabling bus mastering')
140286212Smarcel    command |= 4
141286212Smarcel    bus.write_2(pcicfg, 4, command)
142286212Smarcel
143286212Smarcelbus.unmap(pcicfg)
144286212Smarcel
145286212Smarcelio = bus.map(dev, '10.io')
146286212Smarcellogging.debug('io=%s (%s)' % (io, dev))
147286212Smarcel
148286212Smarcel
149286212Smarceldef delay(msec):
150286212Smarcel    time.sleep(msec / 1000.0)
151286212Smarcel
152286212Smarcel
153286212Smarceldef ffs(x):
154286212Smarcel    y = (1 + (x ^ (x-1))) >> 1
155286212Smarcel    return y.bit_length()
156286212Smarcel
157286212Smarcel
158286212Smarceldef ip_str(a):
159286212Smarcel    return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255,
160286212Smarcel                            a & 255)
161286212Smarcel
162286212Smarcel
163286212Smarceldef mac_is(l, r):
164286212Smarcel    for i in xrange(6):
165286212Smarcel        if l[i] != r[i]:
166286212Smarcel            return False
167286212Smarcel    return True
168286212Smarcel
169286212Smarcel
170286212Smarceldef mac_str(m):
171286212Smarcel    return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5])
172286212Smarcel
173286212Smarcel
174286212Smarceldef rdbcr(reg):
175286212Smarcel    bus.write_2(io, 0x12, reg & 0xffff)
176286212Smarcel    return bus.read_2(io, 0x16)
177286212Smarcel
178286212Smarcel
179286212Smarceldef wrbcr(reg, val):
180286212Smarcel    bus.write_2(io, 0x12, reg & 0xffff)
181286212Smarcel    bus.write_2(io, 0x16, val & 0xffff)
182286212Smarcel
183286212Smarcel
184286212Smarceldef rdcsr(reg):
185286212Smarcel    bus.write_2(io, 0x12, reg & 0xffff)
186286212Smarcel    return bus.read_2(io, 0x10)
187286212Smarcel
188286212Smarcel
189286212Smarceldef wrcsr(reg, val):
190286212Smarcel    bus.write_2(io, 0x12, reg & 0xffff)
191286212Smarcel    bus.write_2(io, 0x10, val & 0xffff)
192286212Smarcel
193286212Smarcel
194286212Smarceldef start():
195286212Smarcel    wrcsr(0, 0x42)
196286212Smarcel    delay(100)
197286212Smarcel
198286212Smarcel
199286212Smarceldef stop():
200286212Smarcel    wrcsr(0, 4)
201286212Smarcel    delay(100)
202286212Smarcel
203286212Smarcel
204286212Smarcelmac = ()
205286212Smarcelbcast = ()
206286212Smarcelfor o in xrange(6):
207286212Smarcel    mac += (bus.read_1(io, o),)
208286212Smarcel    bcast += (0xff,)
209286212Smarcellogging.info('ethernet address = ' + MACFMT % mac)
210286212Smarcel
211286212Smarcelstop()
212286212Smarcelwrbcr(20, 2)            # reset
213286212Smarcelwrcsr(3, 0)             # byte swapping mode
214286212Smarcelwrbcr(2, rdbcr(2) | 2)  # Autoneg
215286212Smarcel
216286212Smarcelmemsize = 32*1024
217286212Smarcelbufsize = 1536
218286212Smarcelnrxbufs = 16
219286212Smarcelntxbufs = 4
220286212Smarcellogging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" %
221286212Smarcel              (memsize, ntxbufs, nrxbufs))
222286212Smarcel
223286212Smarcelmem_tag = busdma.tag_create(dev, 16, 0, 0xffffffff, memsize, 1, memsize, 0, 0)
224286212Smarceldmamem = busdma.mem_alloc(mem_tag, 0)
225286212Smarcelbusseg = busdma.md_first_seg(dmamem, busdma.MD_BUS_SPACE)
226286212Smarcelcpuseg = busdma.md_first_seg(dmamem, busdma.MD_VIRT_SPACE)
227286212Smarcelbusaddr = busdma.seg_get_addr(busseg)
228286212Smarcelcpuaddr = busdma.seg_get_addr(cpuseg)
229286212Smarcellogging.debug("DMA memory: CPU address: %#x, device address: %#x" %
230286212Smarcel              (cpuaddr, busaddr))
231286212Smarcel
232286212Smarceladdr_initblock = cpuaddr
233286212Smarceladdr_rxdesc = addr_initblock + ctypes.sizeof(initblock)
234286212Smarceladdr_txdesc = addr_rxdesc + ctypes.sizeof(bufdesc) * nrxbufs
235286212Smarceladdr_rxbufs = addr_txdesc + ctypes.sizeof(bufdesc) * ntxbufs
236286212Smarceladdr_txbufs = addr_rxbufs + bufsize * nrxbufs
237286212Smarcel
238286212Smarcelib = initblock.from_address(addr_initblock)
239286212Smarcelib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20)
240286212Smarcelfor i in xrange(len(mac)):
241286212Smarcel    ib.hwaddr[i] = mac[i]
242286212Smarcelfor i in xrange(4):
243286212Smarcel    ib.filter[i] = 0xffff
244286212Smarcelib.rxdesc = busaddr + (addr_rxdesc - cpuaddr)
245286212Smarcelib.txdesc = busaddr + (addr_txdesc - cpuaddr)
246286212Smarcelib._pad1_ = 0
247286212Smarcelib._pad2_ = 0
248286212Smarcel
249286212Smarcelfor i in xrange(nrxbufs):
250286212Smarcel    bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i)
251286212Smarcel    bd.buffer = busaddr + (addr_rxbufs - cpuaddr) + bufsize * i
252286212Smarcel    bd.flags = (1 << 31) | (15 << 12) | (-bufsize & 0xfff)
253286212Smarcel    bd.length = 0
254286212Smarcel    bd._pad_ = 0
255286212Smarcel
256286212Smarcelfor i in xrange(ntxbufs):
257286212Smarcel    bd = bufdesc.from_address(addr_txdesc + ctypes.sizeof(bufdesc) * i)
258286212Smarcel    bd.buffer = busaddr + (addr_txbufs - cpuaddr) + bufsize * i
259286212Smarcel    bd.flags = (15 << 12)
260286212Smarcel    bd.length = 0
261286212Smarcel    bd._pad_ = 0
262286212Smarcel
263286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr)
264286212Smarcel
265286212Smarcel# Program address of DMA memory
266286212Smarcelwrcsr(1, busaddr)
267286212Smarcelwrcsr(2, busaddr >> 16)
268286212Smarceldelay(100)
269286212Smarcel
270286212Smarcel# Initialize hardware
271286212Smarcelwrcsr(0, 1)
272286212Smarcellogging.debug('Waiting for initialization to complete')
273286212Smarcelcsr = rdcsr(0)
274286212Smarcelwhile (csr & 0x100) == 0:
275286212Smarcel    logging.debug('CSR=%#x' % (csr))
276286212Smarcel    csr = rdcsr(0)
277286212Smarcel
278286212Smarcelstart()
279286212Smarcel
280286212Smarcelpkt = packet.from_address(addr_txbufs)
281286212Smarcelctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt))
282286212Smarceloptions = [53, 1, 1]
283286212Smarcelfor i in xrange(len(options)):
284286212Smarcel    pkt.dhcp_options[i] = options[i]
285286212Smarcelpkt.dhcp_magic = 0x63825363
286286212Smarcelfor i in xrange(6):
287286212Smarcel    pkt.bootp_chaddr[i] = mac[i]
288286212Smarcelpkt.bootp_hlen = 6
289286212Smarcelpkt.bootp_htype = 1
290286212Smarcelpkt.bootp_op = 1
291286212Smarcelpkt.udp_len = ctypes.sizeof(pkt) - 34
292286212Smarcelpkt.udp_dest = 67
293286212Smarcelpkt.udp_src = 68
294286212Smarcelpkt.ip_dest = 0xffffffff
295286212Smarcelpkt.ip_cksum = 0x79a6
296286212Smarcelpkt.ip_proto = 17
297286212Smarcelpkt.ip_ttl = 64
298286212Smarcelpkt.ip_len = ctypes.sizeof(pkt) - 14
299286212Smarcelpkt.ip_vl = 0x45
300286212Smarcelpkt.eth_type = 0x0800
301286212Smarcelfor i in xrange(6):
302286212Smarcel    pkt.eth_src[i] = mac[i]
303286212Smarcel    pkt.eth_dest[i] = bcast[i]
304286212Smarcelpktlen = ctypes.sizeof(pkt)
305286212Smarcel
306286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize)
307286212Smarcel
308286212Smarcelbd = bufdesc.from_address(addr_txdesc)
309286212Smarcelbd.length = 0
310286212Smarcelbd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff)
311286212Smarcel
312286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr,
313286212Smarcel                  ctypes.sizeof(bufdesc))
314286212Smarcel
315286212Smarcelwrcsr(0, 0x48)
316286212Smarcel
317286212Smarcellogging.info('DHCP discovery packet sent')
318286212Smarcel
319286212Smarcel# Now wait 2 seconds for a DHCP offer to be received.
320286212Smarcellogging.debug('Waiting 2 seconds for an offer to be received')
321286212Smarceltime.sleep(2)
322286212Smarcel
323286212Smarcelstop()
324286212Smarcel
325286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr,
326286212Smarcel                  ctypes.sizeof(bufdesc) * nrxbufs)
327286212Smarcel
328286212Smarcelfor i in xrange(nrxbufs):
329286212Smarcel    bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i)
330286212Smarcel    if (bd.flags & 0x80000000):
331286212Smarcel        continue
332286212Smarcel    pkt = packet.from_address(addr_rxbufs + i * bufsize)
333286212Smarcel    if mac_is(pkt.eth_dest, bcast):
334286212Smarcel        logging.debug('RX #%d: broadcast packet: length %u' % (i, bd.length))
335286212Smarcel        continue
336286212Smarcel    if not mac_is(pkt.eth_dest, mac):
337286212Smarcel        logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest)))
338286212Smarcel        continue
339286212Smarcel    logging.debug('RX %d: packet from %s!' % (i, mac_str(pkt.eth_src)))
340286212Smarcel    logging.info('Our IP address = %s' % (ip_str(pkt.ip_dest)))
341286212Smarcel
342286212Smarcelbusdma.mem_free(dmamem)
343286212Smarcelbusdma.tag_destroy(mem_tag)
344286212Smarcelbus.unmap(io)
345