]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/bus_space/examples/am79c900_diag.py
OpenZFS: MFV 2.0-rc3-gfc5966
[FreeBSD/FreeBSD.git] / tools / bus_space / examples / am79c900_diag.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2014 Marcel Moolenaar
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
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.
15 #
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.
26 #
27 # $FreeBSD$
28 #
29
30 '''
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.
34
35 The datasheet can be found here:
36     http://support.amd.com/TechDocs/18219.pdf
37
38 This example program sends a single DHCP discovery packet,
39 waits 2 seconds and then iterates over the receive ring for
40 a targeted packet.
41
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.
46 '''
47
48 import ctypes
49 import logging
50 import os
51 import sys
52 import time
53
54 sys.path.append('/usr/lib')
55
56 import bus
57 from bus import dma as busdma
58
59
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)]
69
70
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)]
77
78
79 # The DHCP packet definition (incl. all headers)
80 class packet(ctypes.BigEndianStructure):
81     _pack_ = 1
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)]
115
116 MACFMT = '%02x:%02x:%02x:%02x:%02x:%02x'
117
118 dev = 'pci0:2:1:0'
119
120 logging.basicConfig(level=logging.DEBUG)
121
122 pcicfg = bus.map(dev, 'pcicfg')
123 logging.debug('pcicfg=%s (%s)' % (pcicfg, dev))
124
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)' %
129                   (vendor, device))
130     sys.exit(1)
131
132 command = bus.read_2(pcicfg, 4)
133 if not (command & 1):
134     logging.info('enabling I/O port decoding')
135     command |= 1
136     bus.write_2(pcicfg, 4, command)
137
138 if not (command & 4):
139     logging.info('enabling bus mastering')
140     command |= 4
141     bus.write_2(pcicfg, 4, command)
142
143 bus.unmap(pcicfg)
144
145 io = bus.map(dev, '10.io')
146 logging.debug('io=%s (%s)' % (io, dev))
147
148
149 def delay(msec):
150     time.sleep(msec / 1000.0)
151
152
153 def ffs(x):
154     y = (1 + (x ^ (x-1))) >> 1
155     return y.bit_length()
156
157
158 def ip_str(a):
159     return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255,
160                             a & 255)
161
162
163 def mac_is(l, r):
164     for i in range(6):
165         if l[i] != r[i]:
166             return False
167     return True
168
169
170 def mac_str(m):
171     return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5])
172
173
174 def rdbcr(reg):
175     bus.write_2(io, 0x12, reg & 0xffff)
176     return bus.read_2(io, 0x16)
177
178
179 def wrbcr(reg, val):
180     bus.write_2(io, 0x12, reg & 0xffff)
181     bus.write_2(io, 0x16, val & 0xffff)
182
183
184 def rdcsr(reg):
185     bus.write_2(io, 0x12, reg & 0xffff)
186     return bus.read_2(io, 0x10)
187
188
189 def wrcsr(reg, val):
190     bus.write_2(io, 0x12, reg & 0xffff)
191     bus.write_2(io, 0x10, val & 0xffff)
192
193
194 def start():
195     wrcsr(0, 0x42)
196     delay(100)
197
198
199 def stop():
200     wrcsr(0, 4)
201     delay(100)
202
203
204 mac = ()
205 bcast = ()
206 for o in range(6):
207     mac += (bus.read_1(io, o),)
208     bcast += (0xff,)
209 logging.info('ethernet address = ' + MACFMT % mac)
210
211 stop()
212 wrbcr(20, 2)            # reset
213 wrcsr(3, 0)             # byte swapping mode
214 wrbcr(2, rdbcr(2) | 2)  # Autoneg
215
216 memsize = 32*1024
217 bufsize = 1536
218 nrxbufs = 16
219 ntxbufs = 4
220 logging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" %
221               (memsize, ntxbufs, nrxbufs))
222
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" %
230               (cpuaddr, busaddr))
231
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
237
238 ib = initblock.from_address(addr_initblock)
239 ib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20)
240 for i in range(len(mac)):
241     ib.hwaddr[i] = mac[i]
242 for i in range(4):
243     ib.filter[i] = 0xffff
244 ib.rxdesc = busaddr + (addr_rxdesc - cpuaddr)
245 ib.txdesc = busaddr + (addr_txdesc - cpuaddr)
246 ib._pad1_ = 0
247 ib._pad2_ = 0
248
249 for i in range(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)
253     bd.length = 0
254     bd._pad_ = 0
255
256 for i in range(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)
260     bd.length = 0
261     bd._pad_ = 0
262
263 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr)
264
265 # Program address of DMA memory
266 wrcsr(1, busaddr)
267 wrcsr(2, busaddr >> 16)
268 delay(100)
269
270 # Initialize hardware
271 wrcsr(0, 1)
272 logging.debug('Waiting for initialization to complete')
273 csr = rdcsr(0)
274 while (csr & 0x100) == 0:
275     logging.debug('CSR=%#x' % (csr))
276     csr = rdcsr(0)
277
278 start()
279
280 pkt = packet.from_address(addr_txbufs)
281 ctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt))
282 options = [53, 1, 1]
283 for i in range(len(options)):
284     pkt.dhcp_options[i] = options[i]
285 pkt.dhcp_magic = 0x63825363
286 for i in range(6):
287     pkt.bootp_chaddr[i] = mac[i]
288 pkt.bootp_hlen = 6
289 pkt.bootp_htype = 1
290 pkt.bootp_op = 1
291 pkt.udp_len = ctypes.sizeof(pkt) - 34
292 pkt.udp_dest = 67
293 pkt.udp_src = 68
294 pkt.ip_dest = 0xffffffff
295 pkt.ip_cksum = 0x79a6
296 pkt.ip_proto = 17
297 pkt.ip_ttl = 64
298 pkt.ip_len = ctypes.sizeof(pkt) - 14
299 pkt.ip_vl = 0x45
300 pkt.eth_type = 0x0800
301 for i in range(6):
302     pkt.eth_src[i] = mac[i]
303     pkt.eth_dest[i] = bcast[i]
304 pktlen = ctypes.sizeof(pkt)
305
306 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize)
307
308 bd = bufdesc.from_address(addr_txdesc)
309 bd.length = 0
310 bd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff)
311
312 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr,
313                   ctypes.sizeof(bufdesc))
314
315 wrcsr(0, 0x48)
316
317 logging.info('DHCP discovery packet sent')
318
319 # Now wait 2 seconds for a DHCP offer to be received.
320 logging.debug('Waiting 2 seconds for an offer to be received')
321 time.sleep(2)
322
323 stop()
324
325 busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr,
326                   ctypes.sizeof(bufdesc) * nrxbufs)
327
328 for i in range(nrxbufs):
329     bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i)
330     if (bd.flags & 0x80000000):
331         continue
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))
335         continue
336     if not mac_is(pkt.eth_dest, mac):
337         logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest)))
338         continue
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)))
341
342 busdma.mem_free(dmamem)
343 busdma.tag_destroy(mem_tag)
344 bus.unmap(io)