summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md400
-rw-r--r--CONTRIBUTING.rst813
-rw-r--r--Makefile.todo10
-rw-r--r--README.md246
-rw-r--r--README.rst889
-rwxr-xr-xbuilder.py5
-rw-r--r--buildset/default.ini3
-rw-r--r--buildset/everything.ini3
-rw-r--r--dhcpcd/dhcpcd.c2
m---------freebsd-org0
-rw-r--r--freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h2
-rw-r--r--freebsd/contrib/tcpdump/tcpdump.c95
-rw-r--r--freebsd/contrib/wpa/src/utils/eloop.c19
-rw-r--r--freebsd/crypto/openssl/apps/ocsp.c2
-rw-r--r--freebsd/crypto/openssl/apps/openssl.c2
-rw-r--r--freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h2
-rw-r--r--freebsd/crypto/openssl/crypto/ui/ui_openssl.c10
-rw-r--r--freebsd/lib/libc/include/libc_private.h4
-rw-r--r--freebsd/lib/libc/stdio/local.h4
-rw-r--r--freebsd/sbin/pfctl/pfctl_altq.c1
-rw-r--r--freebsd/sbin/pfctl/pfctl_parser.c10
-rw-r--r--freebsd/sbin/pfctl/pfctl_parser.h2
-rw-r--r--freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h1
-rw-r--r--freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_altq-data.h2
-rw-r--r--freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h1
-rw-r--r--freebsd/sbin/ping/ping.c8
-rw-r--r--freebsd/sbin/ping6/ping6.c16
-rw-r--r--freebsd/sys/arm64/include/machine/in_cksum.h43
-rw-r--r--freebsd/sys/dev/cadence/if_cgem.c78
-rw-r--r--freebsd/sys/dev/ffec/if_ffec.c128
-rw-r--r--freebsd/sys/dev/mii/tiphy.h57
-rw-r--r--freebsd/sys/dev/mmc/mmcsd.c6
-rw-r--r--freebsd/sys/dev/ofw/ofwpci.c693
-rw-r--r--freebsd/sys/dev/ofw/ofwpci.h87
-rw-r--r--freebsd/sys/dev/pci/pci.c7
-rw-r--r--freebsd/sys/dev/pci/pci_pci.c3
-rw-r--r--freebsd/sys/dev/pci/pci_subr.c388
-rw-r--r--freebsd/sys/dev/xdma/xdma.c501
-rw-r--r--freebsd/sys/dev/xdma/xdma.h285
-rw-r--r--freebsd/sys/dev/xdma/xdma_bank.c100
-rw-r--r--freebsd/sys/dev/xdma/xdma_mbuf.c151
-rw-r--r--freebsd/sys/dev/xdma/xdma_queue.c126
-rw-r--r--freebsd/sys/dev/xdma/xdma_sg.c661
-rw-r--r--freebsd/sys/dev/xdma/xdma_sglist.c103
-rw-r--r--freebsd/sys/dev/xilinx/axidma.c683
-rw-r--r--freebsd/sys/dev/xilinx/axidma.h96
-rw-r--r--freebsd/sys/dev/xilinx/if_xae.c1108
-rw-r--r--freebsd/sys/dev/xilinx/if_xaereg.h122
-rw-r--r--freebsd/sys/dev/xilinx/if_xaevar.h80
-rw-r--r--freebsd/sys/fs/nfs/nfs_commonkrpc.c23
-rw-r--r--freebsd/sys/fs/nfs/nfs_commonport.c1
-rw-r--r--freebsd/sys/fs/nfs/nfs_commonsubs.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs.h3
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clbio.c3
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clkrpc.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clport.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clrpcops.c14
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clstate.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clsubs.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clvfsops.c1
-rw-r--r--freebsd/sys/fs/nfsclient/nfs_clvnops.c51
-rw-r--r--freebsd/sys/kern/kern_prot.c1
-rw-r--r--freebsd/sys/kern/subr_bus.c2
-rw-r--r--freebsd/sys/kern/vfs_bio.c2
-rw-r--r--freebsd/sys/kern/vfs_export.c1
-rw-r--r--freebsd/sys/kern/vfs_syscalls.c1
-rw-r--r--freebsd/sys/kern/vfs_vnops.c1
-rw-r--r--freebsd/sys/microblaze/include/machine/in_cksum.h83
-rw-r--r--freebsd/sys/nfs/nfs_nfssvc.c1
-rw-r--r--freebsd/sys/powerpc/include/machine/hid.h224
-rw-r--r--freebsd/sys/powerpc/include/machine/pio.h306
-rw-r--r--freebsd/sys/powerpc/include/machine/platformvar.h91
-rw-r--r--freebsd/sys/powerpc/include/machine/spr.h3
-rw-r--r--freebsd/sys/powerpc/mpc85xx/mpc85xx.c403
-rw-r--r--freebsd/sys/powerpc/mpc85xx/mpc85xx.h179
-rw-r--r--freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c996
-rw-r--r--freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c111
-rw-r--r--freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c710
-rw-r--r--freebsd/sys/powerpc/ofw/ofw_pcib_pci.c178
-rw-r--r--freebsd/sys/rpc/svc.c1
-rw-r--r--freebsd/sys/rpc/svc_auth.c1
-rw-r--r--freebsd/sys/sys/_domainset.h2
-rw-r--r--freebsd/sys/sys/buf.h4
-rw-r--r--freebsd/sys/sys/domainset.h52
-rw-r--r--freebsd/sys/sys/vnode.h4
-rw-r--r--freebsd/sys/vm/uma_core.c2
-rw-r--r--freebsd/tools/tools/net80211/wlanstats/main.c22
-rw-r--r--freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h2
-rw-r--r--freebsd/usr.bin/netstat/if.c19
-rw-r--r--freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h2
-rw-r--r--ipsec-tools/src/libipsec/pfkey.c10
-rw-r--r--ipsec-tools/src/racoon/privsep.c12
-rw-r--r--ipsec-tools/src/racoon/rtems-bsd-racoon-data.h2
-rw-r--r--ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h3
-rw-r--r--ipsec-tools/src/racoon/session.c30
-rw-r--r--libbsd.py108
-rw-r--r--libbsd.txt1331
-rw-r--r--rtemsbsd/arm/include/arm/lpc/probe.h43
-rw-r--r--rtemsbsd/fs/nfsclient/nfs.c31
-rw-r--r--rtemsbsd/include/bsp/mv643xx_eth.h397
-rw-r--r--rtemsbsd/include/bsp/nexus-devices.h43
-rw-r--r--rtemsbsd/include/machine/_kernel_if.h14
-rw-r--r--rtemsbsd/include/machine/_kernel_socket.h1
-rw-r--r--rtemsbsd/include/machine/bus.h9
-rw-r--r--rtemsbsd/include/machine/resource.h1
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h1
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-space.h11
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-libio.h6
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-nexus-bus.h45
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-program.h8
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-user-space.h1
-rwxr-xr-xrtemsbsd/include/rtems/bsd/bsd.h22
-rw-r--r--rtemsbsd/include/rtems/bsd/local/pic_if.h133
-rw-r--r--rtemsbsd/include/rtems/bsd/local/xdma_if.h144
-rw-r--r--rtemsbsd/local/pic_if.c69
-rw-r--r--rtemsbsd/local/xdma_if.c56
-rw-r--r--rtemsbsd/nfsclient/nfs.c2
-rw-r--r--rtemsbsd/pppd/rtemspppd.c2
-rw-r--r--rtemsbsd/rtems/program-internal.h7
-rw-r--r--rtemsbsd/rtems/rtems-bsd-mountroot.c4
-rw-r--r--rtemsbsd/rtems/rtems-bsd-racoon.c2
-rw-r--r--rtemsbsd/rtems/rtems-bsd-rc-conf-net.c10
-rw-r--r--rtemsbsd/rtems/rtems-bsd-rc-conf.c25
-rw-r--r--rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c2
-rw-r--r--rtemsbsd/rtems/rtems-bsd-syscall-api.c66
-rw-r--r--rtemsbsd/rtems/rtems-kernel-bus-dma.c6
-rw-r--r--rtemsbsd/rtems/rtems-kernel-init.c4
-rw-r--r--rtemsbsd/rtems/rtems-kernel-lockmgr.c2
-rw-r--r--rtemsbsd/rtems/rtems-kernel-nexus.c21
-rw-r--r--rtemsbsd/rtems/rtems-kernel-pager.c4
-rw-r--r--rtemsbsd/rtems/rtems-kernel-pci_bus.c2
-rw-r--r--rtemsbsd/rtems/rtems-kernel-thread.c11
-rw-r--r--rtemsbsd/rtems/rtems-kernel-vfs.c10
-rw-r--r--rtemsbsd/rtems/rtems-program.c51
-rw-r--r--rtemsbsd/rtems/rtems-routes.c4
-rwxr-xr-xrtemsbsd/sys/arm/lpc/if_lpe.c2855
-rw-r--r--rtemsbsd/sys/arm/lpc/if_lpereg.h210
-rw-r--r--rtemsbsd/sys/arm64/xilinx/versal_slcr.c30
-rw-r--r--rtemsbsd/sys/arm64/xilinx/versal_slcr.h6
-rw-r--r--rtemsbsd/sys/dev/atsam/if_atsam.c1170
-rw-r--r--rtemsbsd/sys/dev/mve/if_mve.c2389
-rw-r--r--rtemsbsd/sys/dev/mve/if_mve_nexus.c935
-rw-r--r--rtemsbsd/sys/dev/sdhci/arasan_sdhci.c70
-rwxr-xr-xrtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c17
-rw-r--r--rtemsbsd/sys/dev/vme/tsi148.c145
-rw-r--r--rtemsbsd/sys/dev/vme/vme-rtems-compat.c143
-rw-r--r--rtemsbsd/sys/net/if_ppp.c11
-rw-r--r--rtemsbsd/sys/net/if_pppvar.h1
-rw-r--r--rtemsbsd/sys/net/ppp_tty.c32
-rw-r--r--rtemsbsd/sys/powerpc/platform_mpc85xx.c48
-rw-r--r--rtemsbsd/ttcp/ttcp.c41
-rw-r--r--testsuite/dhcpcd01/test_main.c5
-rw-r--r--testsuite/dhcpcd02/test_main.c5
-rw-r--r--testsuite/evdev01/init.c8
-rw-r--r--testsuite/foobarclient/test_main.c3
-rw-r--r--testsuite/ftpd01/test_main.c3
-rwxr-xr-xtestsuite/include/rtems/bsd/test/network-config.h.in2
-rw-r--r--testsuite/loopback01/test_main.c4
-rw-r--r--testsuite/media01/test_main.c2
-rw-r--r--testsuite/nfs01/test_main.c109
-rw-r--r--testsuite/openssl02/test_main.c2
-rw-r--r--testsuite/pf02/test_main.c5
-rw-r--r--testsuite/ppp01/test_main.c3
-rw-r--r--testsuite/tcpdump01/test_main.c292
-rw-r--r--testsuite/telnetd01/test_main.c3
-rw-r--r--testsuite/thread01/test_main.c5
-rw-r--r--testsuite/ttcpshell01/test_main.c5
-rw-r--r--testsuite/usbkbd01/init.c4
-rw-r--r--testsuite/usbmouse01/init.c4
-rw-r--r--testsuite/vme01/test_main.c80
-rw-r--r--testsuite/zerocopy01/test_main.c3
-rwxr-xr-xwaf16
-rw-r--r--waf_libbsd.py37
-rw-r--r--wscript4
174 files changed, 18542 insertions, 4494 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 5336b22e..00000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,400 +0,0 @@
-Guidelines for Developing and Contributing Code
-===============================================
-
-Introduction
-------------
-
-This guide aims to help developing and contributing code to the libbsd. One
-goal of the libbsd is to stay in synchronization with FreeBSD. This is only
-feasible if certain rules are in place. Otherwise, managing more than a
-thousand imported source files will become too labour intensive eventually.
-
-What is in the Git Repository
------------------------------
-
-The libbsd a self-contained kit with FreeBSD and RTEMS components pre-merged.
-The Waf wscript in libbsd is automatically generated.
-
-Any changes to source in the `freebsd` directories will need to be merged
-upstream into our master FreeBSD checkout, the `freebsd-org` submodule.
-
-The repository contains two FreeBSD source trees. In the `freebsd` directory
-are the so called *managed* FreeBSD sources used to build the BSD library. The
-FreeBSD source in `freebsd-org` is the *master* version. The
-`freebsd-to-rtems.py` script is used to transfer files between the two trees.
-In general terms, if you have modified managed FreeBSD sources, you will need
-to run the script in *revert* or *reverse* mode using the `-R` switch. This
-will copy the source back to your local copy of the master FreeBSD source so
-you can run `git diff` against the upstream FreeBSD source. If you want to
-transfer source files from the master FreeBSD source to the manged FreeBSD
-sources, then you must run the script in *forward* mode (the default).
-
-Organization
-------------
-
-The top level directory contains a few directories and files. The following
-are important to understand
-
-* `freebsd-to-rtems.py` - script to convert to and free FreeBSD and RTEMS trees,
-* `create-kernel-namespace.sh` - script to create the kernel namespace header <machine/rtems-bsd-kernel-namespace.h,
-* `wscript` - automatically generated,
-* `freebsd/` - from FreeBSD by script,
-* `rtemsbsd/` - RTEMS specific implementations of FreeBSD kernel support routines,
-* `testsuite/` - RTEMS specific tests, and
-* `libbsd.txt` - documentation in Asciidoc.
-
-Moving Code Between Managed and Master FreeBSD Source
------------------------------------------------------
-
-The script `freebsd-to-rtems.py` is used to copy code from FreeBSD to the
-rtems-libbsd tree and to reverse this process. This script attempts to
-automate this process as much as possible and performs some transformations
-on the FreeBSD code. Its command line arguments are shown below:
-
-```
-freebsd-to-rtems.py [args]
- -?|-h|--help print this and exit
- -d|--dry-run run program but no modifications
- -D|--diff provide diff of files between trees
- -e|--early-exit evaluate arguments, print results, and exit
- -m|--makefile Warning: depreciated and will be removed
- -b|--buildscripts just generate the build scripts
- -S|--stats Print a statistics report
- -R|--reverse default FreeBSD -> RTEMS, reverse that
- -r|--rtems RTEMS Libbsd directory (default: '.')
- -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org')
- -v|--verbose enable verbose output mode
-```
-
-In its default mode of operation, freebsd-to-rtems.py is used to copy code
-from FreeBSD to the rtems-libbsd tree and perform transformations.
-
-In *reverse mode*, this script undoes those transformations and copies
-the source code back to the *master* FreeBSD tree. This allows us to do
-'git diff', evaluate changes made by the RTEMS Project, and report changes
-back to FreeBSD upstream.
-
-In either mode, the script may be asked to perform a dry-run or be verbose.
-Also, in either mode, the script is also smart enough to avoid copying over
-files which have not changed. This means that the timestamps of files are
-not changed unless the contents change. The script will also report the
-number of files which changed. In verbose mode, the script will print
-the name of the files which are changed.
-
-To add or update files in the RTEMS FreeBSD tree first run the *reverse mode*
-and move the current set of patches FreeBSD. The script may warn you if a file
-is not present at the destination for the direction. This can happen as files
-not avaliable at the FreeBSD snapshot point have been specially added to the
-RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of
-files in libbsd.py. The reverse mode will result in the FreeBSD having
-uncommitted changes. You can ignore these. Once the reverse process has
-finished edit libbsd.py and add any new files then run the forwad mode to bring
-those files into the RTEMS FreeBSD tree.
-
-The following is an example forward run with no changes.
-
-```
-$ ./freebsd-to-rtems.py -v
-Verbose: yes (1)
-Dry Run: no
-Diff Mode Enabled: no
-Only Generate Build Scripts: no
-RTEMS Libbsd Directory: .
-FreeBSD SVN Directory: freebsd-org
-Direction: forward
-Forward from FreeBSD GIT into .
-0 file(s) were changed:
-```
-
-The script may also be used to generate a diff in either forward or reverse
-direction.
-
-You can add more than one verbose option (-v) to the command line and get more
-detail and debug level information from the command.
-
-FreeBSD Baseline
-----------------
-
-Use
-```
-$ git log freebsd-org
-```
-to figure out the current FreeBSD baseline.
-
-Updates to FreeBSD or RTEMS Kernel Support
-------------------------------------------
-
-If you update code or change any defines that effect the generated
-code in the following paths:
-
-* `freebsd/sys/*.[ch]`
-* `rtemsbsd/rtems/rtems-kernel-*.c`
-
-you need to see if any new kernel symbols have been generated or
-exposed. The tool `rtems-kern-symbols` command supports checking and
-updating the kernel symbol namespace.
-
-The public (global) kernel symbosl need to reside in a private
-namespace to avoid clashing with symbols in the user space code or
-applications. The FreeBSD kernel names functions and variables
-assuming a private kernel only symbols space. RTEMS builds FreeBSD
-kernel and user space code in the same symbols space so there can be
-clashes. We manage this by maintaining a header file that maps the
-global kernel symbols to a private namespace. For example `malloc` is
-mapped to `_bsd_malloc`.
-
-The set of symbols to map is not easy to obtain because symbols may be
-the result of complex preprocessing of the source, the code is
-specific to a BSP or the code is controlled by a buildset.
-
-The approach we use is to not remove symbols unless you are certain
-the symbols have been removed from the FreeBSD kernel source. This is
-a manual operation.
-
-You are required to check symbols with a 32bit and 64bit
-architecture.
-
-If you are working on a specific BSP and related drivers please make
-sure the kernel symbols are checked. It is too much to ask every
-developer to build all BSPs and check.
-
-RTEMS Kernel Symbols Tool
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The python tool `rtems-kern-symbols` can read a kernel header loading
-a previously generated version. This maintains the current symbol set
-without you needing to build the object files previously scanned.
-
-The kernel namespace header can be regenerated from the original
-header. This checks the kernel header is already sorted. If you think
-there is a sorting issue in the existing header please regenerate
-without adding new symbols.
-
-```
-$ ./rtems-kern-symbols --regenerate --output=tmp.h
-```
-
-This command needs access to your built RTEMS tools. You can set your
-environment `PATH` variable or you can specify the top level path as an argument:
-```
-$ ./rtems-kern-symbols --rtems-tools=/opt/work/rtems/6
-```
-
-Options:
-
-* You can provide a different kernel header using the `--kern-header`
-argument. The default is the LibbSD header.
-
-* The `--report` option provides a report.
-
-* The `--diff` option provides a unified diff of any changes.
-
-* The `--write` option is needed to write any changes
-
-* The `--output` option lets you control the output kernel header file
- change are written too
-
-The tool manages a number of exlcuded symbols. These are symbols in
-the kernel space that are not mapped to the RTEMS kernel namespace.
-
-How to Import Code from FreeBSD
--------------------------------
-
-* In case you import files from a special FreeBSD version, then update the list above.
-* Run `git status` and make sure your working directory is clean.
-* Run `./freebsd-to-rtems.py -R`
-* Run `./freebsd-to-rtems.py`
-* Run `git status` and make sure your working directory is clean. If you see modified files, then the `freebsd-to-rtems.py` script needs to be fixed first.
-* Add the files to import to `libbsd.py` and your intended build set (for example `buildset/default.ini`.
-* Run `./freebsd-to-rtems.py`
-* Immediately check in the imported files without the changes to `libbsd.py` and the buildsets. Do not touch the imported files yourself at this point.
-* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'.
-* Add a test to the testsuite if possible.
-* Run `./rtems-kern-symbols` as discussed above
-* Create one commit from this.
-
-The -S or --stats option generates reports the changes we have made to
-FreeBSD. If the code has been reserved into the original FreeBSD tree it will
-show nothing has changed. To see what we have change:
-
-```
-$ cd freebsd-org
-$ git checkout -- .
-$ cd ..
-$ ./freebsd-to-rtems.py -R -S -d
- ```
-
-The report lists the files change based on the opacity level. The opacity is a
-measure on how much of a file differs from the original FreeBSD source. The
-lower the value the more transparent the source file it.
-
-Porting of User-Space Utilities
-------------------------------
-
-The theory behind the described method is to put all BSS and initialized data
-objects into a named section. This section then will be saved before the code is
-executed and restored after it has finished. This method limits to a single
-threaded execution of the application but minimizes the necessary changes to the
-original FreeBSD code.
-
-* Import and commit the unchanged source files like described above.
-* Add the files to the [libbsd.py](libbsd.py) and build them.
-* Check the sources for everything that can be made const. This type of patches
- should go back to the upstream FreeBSD sources.
-* Move static variables out of functions if necessary (search for
- "\tstatic"). These patches most likely will not be accepted into FreeBSD.
-* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file
- containing the main function (e.g. PROGNAME = pfctl). For an example look at
- `rtems_bsd_command_pfctl()` in [pfctl.c](freebsd/sbin/pfctl/pfctl.c).
-* You probably have to use getopt_r() instead of getopt(). Have a look at
- [pfctl.c](freebsd/sbin/pfctl/pfctl.c).
-* Build the libbsd without optimization.
-* Use the `userspace-header-gen.py` to generate some necessary header
- files. It will generate one `rtems-bsd-PROGNAME-MODULE-data.h` per object file, one
- `rtems-bsd-PROGNAME-namespace.h` and one `rtems-bsd-PROGNAME-data.h`. To call
- the script, you have to compile the objects and afterwards run the helper
- script with a call similar to this one:
- `python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl`
- Replace the name (given via -p option) by the name of the userspace tool. It
- has to match the name that is used in the RTEMS linker set further below.
-* If you regenerated files that have already been generated, you may have to
- remove RTEMS-specific names from the namespace. The defaults (linker set names
- and rtems_bsd_program_.*) should already be filtered.
-* Put the generated header files into the same folder like the source files.
-* At the top of each source file place the following right after the user-space header:
- ```c
- #ifdef __rtems__
- #include <machine/rtems-bsd-program.h>
- #include "rtems-bsd-PROGNAME-namespace.h"
- #endif /* __rtems__ */
- ```
- The following command may be useful:
- ```
- sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c
- ```
-* At the bottom of each source file place the follwing:
- ```c
- #ifdef __rtems__
- #include "rtems-bsd-PROGNAME-FILE-data.h"
- #endif /* __rtems__ */
- ```
- The following command may be useful:
- ```
- for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done
- ```
-* Create one compilable commit.
-
-Rules for Modifying FreeBSD Source
-----------------------------------
-
-Changes in FreeBSD files must be done using `__rtems__` C pre-processor guards.
-This makes synchronization with the FreeBSD upstream easier and is very
-important. Patches which do not follow these rules will be rejected. Only add
-lines. If your patch contains lines starting with a `-`, then this is wrong.
-Subtract code by added `#ifndef __rtems__`. For example:
-
-```c
-/* Global variables for the kernel. */
-
-#ifndef __rtems__
-/* 1.1 */
-extern char kernelname[MAXPATHLEN];
-#endif /* __rtems__ */
-
-extern int tick; /* usec per tick (1000000 / hz) */
-```
-
-```c
-#if defined(_KERNEL) || defined(_WANT_FILE)
-#ifdef __rtems__
-#include <rtems/libio_.h>
-#include <sys/fcntl.h>
-#endif /* __rtems__ */
-/*
- * Kernel descriptor table.
- * One entry for each open kernel vnode and socket.
- *
- * Below is the list of locks that protects members in struct file.
- *
- * (f) protected with mtx_lock(mtx_pool_find(fp))
- * (d) cdevpriv_mtx
- * none not locked
- */
-```
-
-```c
-extern int profprocs; /* number of process's profiling */
-#ifndef __rtems__
-extern volatile int ticks;
-#else /* __rtems__ */
-#include <rtems/score/watchdogimpl.h>
-#define ticks _Watchdog_Ticks_since_boot
-#endif /* __rtems__ */
-
-#endif /* _KERNEL */
-```
-
-Add nothing (even blank lines) before or after the `__rtems__` guards. Always
-include a `__rtems__` in the guards to make searches easy, so use
-
-* `#ifndef __rtems__`,
-* `#ifdef __rtems__`,
-* `#else /* __rtems__ */`, and
-* `#endif /* __rtems__ */`.
-
-The guards must start at the begin of the line. Examples for wrong guards:
-
-```c
-static void
-guards_must_start_at_the_begin_of_the_line(int j)
-{
-
- /* WRONG */
- #ifdef __rtems__
- return (j + 1);
- #else /* __rtems__ */
- return (j + 2);
- #endif /* __rtems__ */
-}
-
-static void
-missing_rtems_comments_in_the_guards(int j)
-{
-
-#ifdef __rtems__
- return (j + 3);
-/* WRONG */
-#else
- return (j + 4);
-#endif
-}
-```
-
-The FreeBSD build and configuration system uses option header files, e.g.
-`#include "opt_xyz.h"` in an unmodified FreeBSD file. This include is
-transformed by the import script into `#include <rtems/bsd/local/opt_xyz.h>`. Do
-not disable option header includes via guards. Instead, add an empty option
-header, e.g. `touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h`.
-```c
-/* WRONG */
-#ifndef __rtems__
-#include <rtems/bsd/local/opt_xyz.h>
-#endif /* __rtems__ */
-```
-
-In general, provide empty header files and do not guard includes.
-
-For new code use
-[STYLE(9)](http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9).
-
-Do not format original FreeBSD code. Do not perform white space changes even
-if you get git commit warnings.
-
-Automatically Generated FreeBSD Files
--------------------------------------
-
-Some source and header files are automatically generated during the FreeBSD
-build process. The `Makefile.todo` file performs this manually. The should be
-included in `freebsd-to-rtems.py` script some time in the future. For details,
-see also
-[KOBJ(9)](http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0).
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 00000000..4a49df56
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,813 @@
+Guidelines for Developing and Contributing Code
+***********************************************
+
+Introduction
+============
+
+This guide aims to help developing and contributing code to the LibBSD. One
+goal of the LibBSD is to stay in synchronization with FreeBSD. This is only
+feasible if certain rules are in place. Otherwise, managing more than a
+thousand imported source files will become too labour intensive eventually.
+
+The LibBSD makes FreeBSD subsystems like TCP/IP, USB, SD/MMC, PCIe, and some
+more usable for RTEMS. It tries to follow the FreeBSD development as close as
+possible and therefore is updated to the latest FreeBSD HEAD revision of the
+associated FreeBSD branch from time to time. To find out which version of
+FreeBSD is currently used as the base version for LibBSD please take a look at
+the ``freebsd-org`` submodule.
+
+This guide captures information on the process of merging code from FreeBSD,
+RTEMS specific support files, general guidelines on what modifications to the
+FreeBSD source are permitted, and some other topics. For building the library,
+see the `README <README.rst>`_.
+
+Goals of the LibBSD activity are
+
+* provide functionality from FreeBSD to RTEMS,
+* ease updating to future FreeBSD versions,
+* ease tracking changes in FreeBSD code,
+* minimize manual changes in FreeBSD code.
+
+We will work to push our changes upstream to the FreeBSD Project and minimize
+changes required at each update point.
+
+What is in the Git Repository
+=============================
+
+The LibBSD a self-contained kit with FreeBSD and RTEMS components
+pre-merged. The Waf wscript in LibBSD automatically generates the build when
+you run waf by reading the modules and module's source, header, defines and
+special flags from ``libbsd.py``. This is the same module data used to manage
+the FreeBSD source.
+
+Any changes to source in the ``freebsd`` directories will need to be merged
+upstream into our master FreeBSD checkout, the ``freebsd-org`` submodule.
+
+The repository contains two FreeBSD source trees. In the ``freebsd`` directory
+are the so called *managed* FreeBSD sources used to build the BSD library.
+The FreeBSD source in ``freebsd-org`` is the *master* version. The
+``freebsd-to-rtems.py`` script is used to transfer files between the two trees
+using the module defnitions in ``libbsd.py``. In general terms, if you have
+modified managed FreeBSD sources, you will need to run the script in *revert*
+or *reverse* mode using the ``-R`` switch. This will copy the source back to
+your local copy of the master FreeBSD source so you can run ``git diff`` against
+the upstream FreeBSD source. If you want to transfer source files from the
+master FreeBSD source to the manged FreeBSD sources, then you must run the
+script in *forward* mode (the default).
+
+Kernel and User Space
+=====================
+
+FreeBSD uses virtual memory to run separate address spaces. The kernel is one
+address space and each process the kernel runs is another separate address
+space. The FreeBSD build system understands the separation and separately
+linked executable for the kernel and user land maintains the separation.
+
+RTEMS is a single address space operating system and that means the kernel and
+user space code have to be linked to together and be able to run side by
+side. This creates additional complexity when working with the FreeBSD code,
+for example the FreeBSD kernel has a ``malloc`` call with a different signature
+to the user land ``malloc`` call. The RTEMS LibBSD support code provides
+structured ways to manage the separation.
+
+LibBSD manages the integration of kernel and user code by knowing the context
+of the source code. This lets the merge process handle specific changes each
+type of file needs. The build system also uses this information to control the
+include paths a source file sees. The kernel code sees the kernel, CPU
+specific and build system generated include paths in that order. User code
+sees the user include paths then the kernel, CPU specific and build system
+generated include paths in that order. The FreeBSD OS include path
+``/usr/include`` has a mix of kernel and user space header files. The kernel
+headers let user space code cleanly access structures the kernel exports. If a
+user header file has the same name as a kernel header file the user file will
+be used in the user code rather than the kernel file. If the user code
+includes a kernel header that file will be found and included.
+
+Organization
+============
+
+The top level directory contains a few directories and files. The following
+are important to understand
+
+* ``freebsd-to-rtems.py`` - script to convert to and free FreeBSD and RTEMS trees,
+* ``create-kernel-namespace.sh`` - script to create the kernel namespace header ``<machine/rtems-bsd-kernel-namespace.h>``,
+* ``wscript`` - automatically generates the build from libbsd.py,
+* ``libbsd.py`` - modules, sources, compile flags, and dependencies
+* ``freebsd/`` - from FreeBSD by script,
+* ``rtemsbsd/`` - RTEMS specific implementations of FreeBSD kernel support routines,
+* ``testsuite/`` - RTEMS specific tests, and
+* ``libbsd.txt`` - documentation in Asciidoc.
+
+Moving Code Between Managed and Master FreeBSD Source
+=====================================================
+
+The script ``freebsd-to-rtems.py`` is used to copy code from FreeBSD to the
+rtems-libbsd tree and to reverse this process. This script attempts to
+automate this process as much as possible and performs some transformations
+on the FreeBSD code. Its command line arguments are shown below:
+
+.. code-block:: none
+
+ freebsd-to-rtems.py [args]
+ -?|-h|--help print this and exit
+ -d|--dry-run run program but no modifications
+ -D|--diff provide diff of files between trees
+ -e|--early-exit evaluate arguments, print results, and exit
+ -m|--makefile Warning: depreciated and will be removed
+ -b|--buildscripts just generate the build scripts
+ -S|--stats Print a statistics report
+ -R|--reverse default FreeBSD -> RTEMS, reverse that
+ -r|--rtems RTEMS Libbsd directory (default: '.')
+ -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org')
+ -c|--config Output the configuration then exit
+ -v|--verbose enable verbose output mode
+
+In its default mode of operation, ``freebsd-to-rtems.py`` is used to copy code
+from FreeBSD to the rtems-libbsd tree and perform transformations.
+
+In *reverse mode*, this script undoes those transformations and copies
+the source code back to the *master* FreeBSD tree. This allows us to do
+'git diff', evaluate changes made by the RTEMS Project, and report changes
+back to FreeBSD upstream.
+
+In either mode, the script may be asked to perform a dry-run or be verbose.
+Also, in either mode, the script is also smart enough to avoid copying over
+files which have not changed. This means that the timestamps of files are
+not changed unless the contents change. The script will also report the
+number of files which changed. In verbose mode, the script will print
+the name of the files which are changed.
+
+To add or update files in the RTEMS FreeBSD tree first run the *reverse mode*
+and move the current set of patches FreeBSD. The script may warn you if a file
+is not present at the destination for the direction. This can happen as files
+not avaliable at the FreeBSD snapshot point have been specially added to the
+RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of
+files in libbsd.py. The reverse mode will result in the FreeBSD having
+uncommitted changes. You can ignore these. Once the reverse process has
+finished edit libbsd.py and add any new files then run the forwad mode to bring
+those files into the RTEMS FreeBSD tree.
+
+The following is an example forward run with no changes.
+
+.. code-block:: none
+
+ $ ./freebsd-to-rtems.py -v
+ Verbose: yes (1)
+ Dry Run: no
+ Diff Mode Enabled: no
+ Only Generate Build Scripts: no
+ RTEMS Libbsd Directory: .
+ FreeBSD SVN Directory: freebsd-org
+ Direction: forward
+ Forward from FreeBSD GIT into .
+ 0 file(s) were changed:
+
+The script may also be used to generate a diff in either forward or reverse
+direction.
+
+You can add more than one verbose option (-v) to the command line and get more
+detail and debug level information from the command.
+
+FreeBSD Baseline
+================
+
+Use
+
+.. code-block:: none
+
+ $ git log freebsd-org
+
+to figure out the current FreeBSD baseline.
+
+Updates to FreeBSD or RTEMS Kernel Support
+==========================================
+
+If you update code or change any defines that effect the generated
+code in the following paths:
+
+* ``freebsd/sys/*.[ch]``
+* ``rtemsbsd/rtems/rtems-kernel-*.c``
+
+you need to see if any new kernel symbols have been generated or
+exposed. The tool ``rtems-kern-symbols`` command supports checking and
+updating the kernel symbol namespace.
+
+The public (global) kernel symbosl need to reside in a private
+namespace to avoid clashing with symbols in the user space code or
+applications. The FreeBSD kernel names functions and variables
+assuming a private kernel only symbols space. RTEMS builds FreeBSD
+kernel and user space code in the same symbols space so there can be
+clashes. We manage this by maintaining a header file that maps the
+global kernel symbols to a private namespace. For example ``malloc`` is
+mapped to ``_bsd_malloc``.
+
+The set of symbols to map is not easy to obtain because symbols may be
+the result of complex preprocessing of the source, the code is
+specific to a BSP or the code is controlled by a buildset.
+
+The approach we use is to not remove symbols unless you are certain
+the symbols have been removed from the FreeBSD kernel source. This is
+a manual operation.
+
+You are required to check symbols with a 32bit and 64bit
+architecture.
+
+If you are working on a specific BSP and related drivers please make
+sure the kernel symbols are checked. It is too much to ask every
+developer to build all BSPs and check.
+
+RTEMS Kernel Symbols Tool
+=========================
+
+The python tool ``rtems-kern-symbols`` can read a kernel header loading
+a previously generated version. This maintains the current symbol set
+without you needing to build the object files previously scanned.
+
+The kernel namespace header can be regenerated from the original
+header. This checks the kernel header is already sorted. If you think
+there is a sorting issue in the existing header please regenerate
+without adding new symbols.
+
+.. code-block:: none
+
+ ./rtems-kern-symbols --regenerate --output=tmp.h
+
+This command needs access to your built RTEMS tools. You can set your
+environment ``PATH`` variable or you can specify the top level path as an argument:
+
+.. code-block:: none
+
+ ./rtems-kern-symbols --rtems-tools=/opt/work/rtems/6
+
+Options:
+
+* You can provide a different kernel header using the ``--kern-header``
+argument. The default is the LibbSD header.
+
+* The ``--report`` option provides a report.
+
+* The ``--diff`` option provides a unified diff of any changes.
+
+* The ``--write`` option is needed to write any changes
+
+* The ``--output`` option lets you control the output kernel header file
+ change are written too
+
+The tool manages a number of exlcuded symbols. These are symbols in
+the kernel space that are not mapped to the RTEMS kernel namespace.
+
+How to Import Code from FreeBSD
+===============================
+
+* In case you import files from a special FreeBSD version, then update the list above.
+* Run ``git status`` and make sure your working directory is clean.
+* Run ``./freebsd-to-rtems.py -R``
+* Run ``./freebsd-to-rtems.py``
+* Run ``git status`` and make sure your working directory is clean. If you see modified files, then the ``freebsd-to-rtems.py`` script needs to be fixed first.
+* Add the files to import to ``libbsd.py`` and your intended build set (for example ``buildset/default.ini``.
+* Run ``./freebsd-to-rtems.py``
+* Immediately check in the imported files without the changes to ``libbsd.py`` and the buildsets. Do not touch the imported files yourself at this point.
+* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'.
+* Add a test to the testsuite if possible.
+* Run `./rtems-kern-symbols` as discussed above
+* Create one commit from this.
+
+The -S or --stats option generates reports the changes we have made to
+FreeBSD. If the code has been reserved into the original FreeBSD tree it will
+show nothing has changed. To see what we have change:
+
+.. code-block:: none
+
+ $ cd freebsd-org
+ $ git checkout -- .
+ $ cd ..
+ $ ./freebsd-to-rtems.py -R -S -d
+
+The report lists the files change based on the opacity level. The opacity is a
+measure on how much of a file differs from the original FreeBSD source. The
+lower the value the more transparent the source file it.
+
+Porting of User-Space Utilities
+===============================
+
+The theory behind the described method is to put all BSS and initialized data
+objects into a named section. This section then will be saved before the code is
+executed and restored after it has finished. This method limits to a single
+threaded execution of the application but minimizes the necessary changes to the
+original FreeBSD code.
+
+* Import and commit the unchanged source files like described above.
+* Add the files to the `<libbsd.py>`_ and build them.
+* Check the sources for everything that can be made const. This type of patches
+ should go back to the upstream FreeBSD sources.
+* Move static variables out of functions if necessary (search for
+ "\tstatic"). These patches most likely will not be accepted into FreeBSD.
+* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file
+ containing the main function (e.g. PROGNAME = pfctl). For an example look at
+ ``rtems_bsd_command_pfctl()`` in `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_.
+* You probably have to use getopt_r() instead of getopt(). Have a look at
+ `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_.
+* Build the LibBSD without optimization.
+* Use the ``userspace-header-gen.py`` to generate some necessary header
+ files. It will generate one ``rtems-bsd-PROGNAME-MODULE-data.h`` per object file, one
+ ``rtems-bsd-PROGNAME-namespace.h`` and one ``rtems-bsd-PROGNAME-data.h``. To call
+ the script, you have to compile the objects and afterwards run the helper
+ script with a call similar to this one:
+ ``python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl``
+ Replace the name (given via -p option) by the name of the userspace tool. It
+ has to match the name that is used in the RTEMS linker set further below.
+ ``Note:`` the script ``userspace-header-gen.py`` depends on pyelftools. It can be
+ installed using pip:
+ ``pip install --user pyelftools``
+* If you regenerated files that have already been generated, you may have to
+ remove RTEMS-specific names from the namespace. The defaults (linker set names
+ and rtems_bsd_program_.*) should already be filtered.
+* Put the generated header files into the same folder like the source files.
+* At the top of each source file place the following right after the user-space header:
+
+ .. code-block:: c
+
+ #ifdef __rtems__
+ #include <machine/rtems-bsd-program.h>
+ #include "rtems-bsd-PROGNAME-namespace.h"
+ #endif /* __rtems__ */
+
+ The following command may be useful:
+
+ .. code-block:: none
+
+ sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c
+
+* At the bottom of each source file place the follwing:
+
+ .. code-block:: c
+
+ #ifdef __rtems__
+ #include "rtems-bsd-PROGNAME-FILE-data.h"
+ #endif /* __rtems__ */
+
+ The following command may be useful:
+
+ .. code-block:: none
+
+ for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done
+* Create one compilable commit.
+
+Rules for Modifying FreeBSD Source
+==================================
+
+Do not reformat original FreeBSD code. Do not perform white space changes even
+if you get git commit warnings. **Check your editor settings so that it does
+not perform white space changes automatically**, for example adding a newline
+to the end of the file. White space changes may result in conflicts during
+updates, especially changes at the end of a file.
+
+Changes in FreeBSD files must be done using ``__rtems__`` C pre-processor guards.
+This makes synchronization with the FreeBSD upstream easier and is very
+important. Patches which do not follow these rules will be rejected. Only add
+lines. If your patch contains lines starting with a ``-``, then this is wrong.
+Subtract code by added ``#ifndef __rtems__``. For example:
+
+.. code-block:: c
+
+ /* Global variables for the kernel. */
+
+ #ifndef __rtems__
+ /* 1.1 */
+ extern char kernelname[MAXPATHLEN];
+ #endif /* __rtems__ */
+
+ extern int tick; /* usec per tick (1000000 / hz) */
+
+.. code-block:: c
+
+ #if defined(_KERNEL) || defined(_WANT_FILE)
+ #ifdef __rtems__
+ #include <rtems/libio_.h>
+ #include <sys/fcntl.h>
+ #endif /* __rtems__ */
+ /*
+ * Kernel descriptor table.
+ * One entry for each open kernel vnode and socket.
+ *
+ * Below is the list of locks that protects members in struct file.
+ *
+ * (f) protected with mtx_lock(mtx_pool_find(fp))
+ * (d) cdevpriv_mtx
+ * none not locked
+ */
+
+.. code-block:: c
+
+ extern int profprocs; /* number of process's profiling */
+ #ifndef __rtems__
+ extern volatile int ticks;
+ #else /* __rtems__ */
+ #include <rtems/score/watchdogimpl.h>
+ #define ticks _Watchdog_Ticks_since_boot
+ #endif /* __rtems__ */
+
+ #endif /* _KERNEL */
+
+Add nothing (even blank lines) before or after the ``__rtems__`` guards. Always
+include a ``__rtems__`` in the guards to make searches easy, so use
+
+* ``#ifndef __rtems__``,
+* ``#ifdef __rtems__``,
+* ``#else /* __rtems__ */``, and
+* ``#endif /* __rtems__ */``.
+
+The guards must start at the begin of the line. Examples for wrong guards:
+
+.. code-block:: c
+
+ static void
+ guards_must_start_at_the_begin_of_the_line(int j)
+ {
+
+ /* WRONG */
+ #ifdef __rtems__
+ return (j + 1);
+ #else /* __rtems__ */
+ return (j + 2);
+ #endif /* __rtems__ */
+ }
+
+ static void
+ missing_rtems_comments_in_the_guards(int j)
+ {
+
+ #ifdef __rtems__
+ return (j + 3);
+ /* WRONG */
+ #else
+ return (j + 4);
+ #endif
+ }
+
+The FreeBSD build and configuration system uses option header files, e.g.
+``#include "opt_xyz.h"`` in an unmodified FreeBSD file. This include is
+transformed by the import script into ``#include <rtems/bsd/local/opt_xyz.h>``. Do
+not disable option header includes via guards. Instead, add an empty option
+header, e.g. ``touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h``.
+
+.. code-block:: c
+
+ /* WRONG */
+ #ifndef __rtems__
+ #include <rtems/bsd/local/opt_xyz.h>
+ #endif /* __rtems__ */
+
+In general, provide empty header files and do not guard includes.
+
+For new code use
+`STYLE(9) <http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9>`_.
+
+Update FreeBSD Baseline
+=======================
+
+Perform the following steps to do a FreeBSD baseline update:
+
+* Update ``__FreeBSD_version`` in ``rtemsbsd/include/machine/rtems-bsd-version.h``
+
+* Update the namespace header file.
+
+* Review all code blocks with the ``REVIEW-AFTER-FREEBSD-BASELINE-UPDATE`` tag.
+
+Automatically Generated FreeBSD Files
+=====================================
+
+Some source and header files are automatically generated during the FreeBSD
+build process. The ``Makefile.todo`` file performs this manually. The should be
+included in ``freebsd-to-rtems.py`` script some time in the future. For details,
+see also
+`KOBJ(9) <http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0>`_.
+
+Reference Board Support Package
+===============================
+
+The reference BSP for LibBSD development is ``arm/xilinx_zynq_a9_qemu``. All
+patches shall be tested for this BSP. The BSP runs on the Qemu simulator which
+has some benefits for development and test of the LibBSD
+
+* ``NULL`` pointer read and write protection,
+* Qemu is a fast simulator,
+* Qemu provides support for GDB watchpoints,
+* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge
+ devices (you can run multiple test instances on one virtual network).
+
+Board Support Package Requirements
+==================================
+
+In FreeBSD, interrupt handler may use mutexes. In RTEMS, using mutexes from
+within interrupt context is not allowed, so the Board Support Package (BSP)
+should support the
+`Interrupt Manager <https://docs.rtems.org/branches/master/c-user/interrupt/directives.html#rtems-interrupt-server-handler-install>`_
+in general.
+
+Network Interface Drivers Hints
+===============================
+
+Link Up/Down Events
+-------------------
+
+You can notifiy the application space of link up/down events in your network
+interface driver via the
+``if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN)`` function. The
+DHCPCD(8) client is a consumer of these events for example. Make sure that the
+interface flag ``IFF_UP`` and the interface driver flag ``IFF_DRV_RUNNING`` is
+set in case the link is up, otherwise ``ether_output()`` will return the error
+status ``ENETDOWN``.
+
+FreeBSD Kernel Features Ported to LibBSD
+========================================
+
+All lock based synchronization primitives are implemented through mutexes using
+the priority inheritance protocol.
+
+* `BUS_DMA(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_dma&sektion=9>`_: Bus and Machine Independent DMA Mapping Interface
+* `BUS_SPACE(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_space&sektion=9>`_: Bus space manipulation functions
+* `CALLOUT(9) <http://www.freebsd.org/cgi/man.cgi?query=callout&sektion=9>`_: Execute a function after a specified length of time
+* `CONDVAR(9) <http://www.freebsd.org/cgi/man.cgi?query=condvar&sektion=9>`_: Kernel condition variable
+* `DEVICE(9) <http://www.freebsd.org/cgi/man.cgi?query=device&sektion=9>`_: An abstract representation of a device
+* `DRIVER(9) <http://www.freebsd.org/cgi/man.cgi?query=driver&sektion=9>`_: Structure describing a device driver
+* `EPOCH(9) <http://www.freebsd.org/cgi/man.cgi?query=epoch&sektion=9>`_: Kernel epoch based reclamation
+* `MUTEX(9) <http://www.freebsd.org/cgi/man.cgi?query=mutex&sektion=9>`_: Kernel synchronization primitives
+* `RMAN(9) <http://www.freebsd.org/cgi/man.cgi?query=rman&sektion=9>`_: Resource management functions
+* `RMLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rmlock&sektion=9>`_: Kernel reader/writer lock optimized for read-mostly access patterns
+* `RWLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rwlock&sektion=9>`_: Kernel reader/writer lock
+* `SX(9) <http://www.freebsd.org/cgi/man.cgi?query=sx&sektion=9>`_: Kernel shared/exclusive lock
+* `SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_: Dynamic and static sysctl MIB creation functions
+* `SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_: A framework for dynamic kernel initialization
+* `TASKQUEUE(9) <http://www.freebsd.org/cgi/man.cgi?query=taskqueue&sektion=9>`_: Asynchronous task execution
+* `UMA(9) <http://www.freebsd.org/cgi/man.cgi?query=uma&sektion=9>`_: General-purpose kernel object allocator
+
+LibBSD Initialization Details
+=============================
+
+The initialization of LibBSD is based on the FreeBSD
+`SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_
+infrastructure. The key to initializing a system is to ensure that the desired
+device drivers are explicitly pulled into the linked application. This plus
+linking against the LibBSD (``libbsd.a``) will pull in the necessary FreeBSD
+infrastructure.
+
+The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of
+object files linked together. If we have a library, then creating the
+executable is simple. We begin with a start symbol and recursively resolve all
+references. With a bunch of object files linked together we need a different
+mechanism. Most object files don't know each other. Lets say we have a driver
+module. The rest of the system has no references to this driver module. The
+driver module needs a way to tell the rest of the system: Hey, kernel I am
+here, please use my services!
+
+This registration of independent components is performed by SYSINIT(9) and
+specializations
+
+The SYSINIT(9) uses some global data structures that are placed in a certain
+section. In the linker command file we need this:
+
+.. code-block:: none
+
+ .rtemsroset : {
+ KEEP (*(SORT(.rtemsroset.*)))
+ }
+
+ .rtemsrwset : {
+ KEEP (*(SORT(.rtemsrwset.*)))
+ }
+
+This results for example in this executable layout:
+
+.. code-block:: none
+
+ [...]
+ *(SORT(.rtemsroset.*))
+ .rtemsroset.bsd.modmetadata_set.begin
+ 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o)
+ 0x000000000025fe00 _bsd__start_set_modmetadata_set
+ .rtemsroset.bsd.modmetadata_set.content
+ 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o)
+ .rtemsroset.bsd.modmetadata_set.content
+ 0x000000000025fe08 0x4 libbsd.a(kern_module.o)
+ [...]
+ .rtemsroset.bsd.modmetadata_set.content
+ 0x000000000025fe68 0x4 libbsd.a(mii.o)
+ .rtemsroset.bsd.modmetadata_set.content
+ 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o)
+ .rtemsroset.bsd.modmetadata_set.end
+ 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o)
+ 0x000000000025fe70 _bsd__stop_set_modmetadata_set
+ [...]
+ .rtemsrwset 0x000000000030bad0 0x290
+ *(SORT(.rtemsrwset.*))
+ .rtemsrwset.bsd.sysinit_set.begin
+ 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o)
+ 0x000000000030bad0 _bsd__start_set_sysinit_set
+ .rtemsrwset.bsd.sysinit_set.content
+ 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o)
+ .rtemsrwset.bsd.sysinit_set.content
+ 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o)
+ .rtemsrwset.bsd.sysinit_set.content
+ 0x000000000030badc 0x4 libbsd.a(init_main.o)
+ [...]
+ .rtemsrwset.bsd.sysinit_set.content
+ 0x000000000030bd54 0x4 libbsd.a(frag6.o)
+ .rtemsrwset.bsd.sysinit_set.content
+ 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o)
+ .rtemsrwset.bsd.sysinit_set.end
+ 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o)
+ 0x000000000030bd60 _bsd__stop_set_sysinit_set
+ [...]
+
+Here you can see, that some global data structures are collected into
+continuous memory areas. This memory area can be identified by start and stop
+symbols. This constructs a table of uniform items.
+
+The low level FreeBSD code calls at some time during the initialization the
+mi_startup() function (machine independent startup). This function will sort
+the SYSINIT(9) set and call handler functions which perform further
+initialization. The last step is the scheduler invocation.
+
+The SYSINIT(9) routines are run in ``mi_startup()`` which is called by
+``rtems_bsd_initialize()``. This is also explained in "The Design and
+Implementation of the FreeBSD Operating System" section 14.3 "Kernel
+Initialization".
+
+In RTEMS, we have a library and not a bunch of object files. Thus we need a
+way to pull-in the desired services out of the libbsd. Here the
+``rtems-bsd-sysinit.h`` comes into play. The SYSINIT(9) macros have been
+modified and extended for RTEMS in ``<sys/kernel.h>``:
+
+.. code-block:: none
+
+ #ifndef __rtems__
+ #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
+ static struct sysinit uniquifier ## _sys_init = { \
+ subsystem, \
+ order, \
+ func, \
+ (ident) \
+ }; \
+ DATA_SET(sysinit_set,uniquifier ## _sys_init)
+ #else /* __rtems__ */
+ #define SYSINIT_ENTRY_NAME(uniquifier) \
+ _bsd_ ## uniquifier ## _sys_init
+ #define SYSINIT_REFERENCE_NAME(uniquifier) \
+ _bsd_ ## uniquifier ## _sys_init_ref
+ #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
+ struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \
+ subsystem, \
+ order, \
+ func, \
+ (ident) \
+ }; \
+ RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier))
+ #define SYSINIT_REFERENCE(uniquifier) \
+ extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \
+ static struct sysinit const * const \
+ SYSINIT_REFERENCE_NAME(uniquifier) __used \
+ = &SYSINIT_ENTRY_NAME(uniquifier)
+ #define SYSINIT_MODULE_REFERENCE(mod) \
+ SYSINIT_REFERENCE(mod ## module)
+ #define SYSINIT_DRIVER_REFERENCE(driver, bus) \
+ SYSINIT_MODULE_REFERENCE(driver ## _ ## bus)
+ #define SYSINIT_DOMAIN_REFERENCE(dom) \
+ SYSINIT_REFERENCE(domain_add_ ## dom)
+ #endif /* __rtems__ */
+
+Here you see that the SYSINIT(9) entries are no longer static. The
+``*_REFERENCE()`` macros will create references to the corresponding modules
+which are later resolved by the linker. The application has to provide an
+object file with references to all required FreeBSD modules.
+
+System Control Hints
+====================
+
+If you get undefined references to ``_bsd_sysctl_*`` symbols, then you have to
+locate and add the associated system control node, see
+`SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_.
+
+Issues and TODO
+===============
+
+* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map().
+
+* Priority queues are broken with clustered scheduling.
+
+* Per-CPU data should be enabled once the new stack is ready for SMP.
+
+* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP.
+
+* Multiple routing tables are not supported. Every FIB value is set to zero
+ (= BSD_DEFAULT_FIB).
+
+* Process identifiers are not supported. Every PID value is set to zero
+ (= BSD_DEFAULT_PID).
+
+* User credentials are not supported. The following functions allow the
+ operation for everyone
+
+ * prison_equal_ip4(),
+ * chgsbsize(),
+ * cr_cansee(),
+ * cr_canseesocket() and
+ * cr_canseeinpcb().
+
+* A basic USB functionality test that is known to work on Qemu is desirable.
+
+* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model
+ so that those architectures can use new TCP/IP and USB code.
+
+* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old
+ RTEMS TCP/IP stack. For some reason, the __printflike markers do not
+ compile in this environment. We may want to use the FreeBSD syslog.h
+ and get this addressed.
+
+* in_cksum implementations for architectures not supported by FreeBSD.
+ This will require figuring out where to put implementations that do
+ not originate from FreeBSD and are populated via the script.
+
+* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c").
+
+* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML
+ parser and mmap().
+
+* get_cyclecount(): The implementation is a security problem.
+
+* What to do with the priority parameter present in the FreeBSD synchronization
+ primitives and the thread creation functions?
+
+* TASKQUEUE(9): Support spin mutexes.
+
+* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c.
+
+* KQUEUE(2): Choose proper lock for global kqueue list.
+
+* TIMEOUT(9): Maybe use special task instead of timer server to call
+ callout_tick().
+
+* sysctl_handle_opaque(): Implement reliable snapshots.
+
+* PING6(8): What to do with SIGALARM?
+
+* <sys/param.h>: Update Newlib to use a MSIZE of 256.
+
+* BPF(4): Add support for zero-copy buffers.
+
+* UNIX(4): Fix race conditions in the area of socket object and file node
+ destruction. Add support for file descriptor transmission via control
+ messages.
+
+* PRINTF(9): Add support for log(), the %D format specifier is missing in the
+ normal printf() family.
+
+* Why is the interrupt server used? The BSD interrupt handlers can block on
+ synchronization primitives like mutexes. This is in contrast to RTEMS
+ interrupt service routines. The BSPs using the generic interrupt support
+ must implement the ``bsp_interrupt_vector_enable()`` and
+ ``bsp_interrupt_vector_disable()`` routines. They normally enable/disable a
+ particular interrupt source at the interrupt controller. This can be used to
+ implement the interrupt server. The interrupt server is a task that wakes-up
+ in case an associated interrupt happens. The interrupt source is disabled in
+ a generic interrupt handler that wakes-up the interrupt server task. Once
+ the postponed interrupt processing is performed in the interrupt server the
+ interrupt source is enabled again.
+
+* Convert all BSP linkcmds to use a linkcmds.base so the sections are
+ easier to insert.
+
+* NIC Device Drivers
+* Only common PCI NIC drivers have been included in the initial set. These
+ do not include any system on chip or ISA drivers.
+* PCI configuration probe does not appear to happen to determine if a
+ NIC is in I/O or memory space. We have worked around this by using a
+ static hint to tell the fxp driver the correct mode. But this needs to
+ be addressed.
+* The ISA drivers require more BSD infrastructure to be addressed. This was
+ outside the scope of the initial porting effort.
+
+* devfs (Device file system): There is a minimal implementation based on IMFS.
+ The mount point is fixed to "/dev". Note that the devfs is only used by the
+ cdev subsystem. cdev has been adapted so that the full path (including the
+ leading "/dev") is given to devfs. This saves some copy operations.
+
+ devfs_create() first creates the full path and then creates an IMFS generic
+ node for the device.
+
+ TBD: remove empty paths on devfs_destroy().
+
+* altq_subr.c - Arbitrary choices were made in this file that RTEMS would not
+ support tsc frequency change. Additionally, the clock frequency for
+ machclk_freq is always measured for RTEMS.
+
+* conf.h - In order to add make_dev and destroy_dev, variables in the cdev
+ structure that were not being used were conditionally compiled out. The
+ capability of supporting children did not appear to be needed and was not
+ implemented in the rtems version of these routines.
+
+* Problem to report to FreeBSD: The MMAP_NOT_AVAILABLE define is inverted on
+ its usage. When it is defined the mmap method is called. Additionally, it is
+ not used thoroughly. It is not used in the unmap portion of the source. The
+ file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap the call to mmap
+ and file rec_close.c uses the munmap method.
diff --git a/Makefile.todo b/Makefile.todo
index 36951c55..111beb07 100644
--- a/Makefile.todo
+++ b/Makefile.todo
@@ -34,6 +34,8 @@ GENERATED += $(LOCAL_INC)/pci_if.h
GENERATED += $(LOCAL_SRC)/pci_if.c
GENERATED += $(LOCAL_INC)/pcib_if.h
GENERATED += $(LOCAL_SRC)/pcib_if.c
+GENERATED += $(LOCAL_INC)/pic_if.h
+GENERATED += $(LOCAL_SRC)/pic_if.c
GENERATED += $(LOCAL_INC)/mmcbr_if.h
GENERATED += $(LOCAL_SRC)/mmcbr_if.c
GENERATED += $(LOCAL_INC)/mmcbus_if.h
@@ -166,6 +168,14 @@ $(LOCAL_SRC)/pcib_if.c: $(FREEBSD_SRC)/sys/dev/pci/pcib_if.m
awk -f $(TOOLS)/makeobjops.awk $< -c
mv pcib_if.c $@
+$(LOCAL_INC)/pic_if.h: $(FREEBSD_SRC)/sys/powerpc/powerpc/pic_if.m
+ awk -f $(TOOLS)/makeobjops.awk $< -h
+ mv pic_if.h $@
+
+$(LOCAL_SRC)/pic_if.c: $(FREEBSD_SRC)/sys/powerpc/powerpc/pic_if.m
+ awk -f $(TOOLS)/makeobjops.awk $< -c
+ mv pic_if.c $@
+
$(LOCAL_INC)/mmcbus_if.h: $(FREEBSD_SRC)/sys/dev/mmc/mmcbus_if.m
awk -f $(TOOLS)/makeobjops.awk $< -h
mv mmcbus_if.h $@
diff --git a/README.md b/README.md
deleted file mode 100644
index c9b525cf..00000000
--- a/README.md
+++ /dev/null
@@ -1,246 +0,0 @@
-RTEMS LibBSD
-============
-
-Welcome to building LibBSD for RTEMS using Waf. This package is a library
-containing various parts of the FreeBSD kernel ported to RTEMS. The library
-replaces the networking port of FreeBSD in the RTEMS kernel sources. This
-package is designed to be updated from the FreeBSD kernel sources and contains
-more than just the networking code.
-
-To build this package you need a current RTEMS tool set for your architecture,
-and a recent RTEMS kernel for your BSP configured with networking disabled
-built and installed. If you already have this you can skip to step 3 of the
-build procedure.
-
-Building and Installing LibBSD
-------------------------------
-
-The following instructions show you how to build and install RTEMS Tools and
-RTEMS kernel for your BSP in separate paths. Using separate paths for the tools
-and BSPs lets you manage what you have installed. If you are happy with a
-single path you can use the same path in each stage.
-
-The Waf build support for RTEMS requires you provide your BSP name as an
-architecture and BSP pair. You must provide both or Waf will generate an error
-message during the configure phase.
-
-We will build an Xilinx Zynq QEMU BSP using the name
-*arm/xilinx_zynq_a9_qemu*. You can copy and paste the shell commands below to
-do this. The individual steps are explained afterwards.
-
-```
-sandbox="$PWD/sandbox"
-mkdir sandbox
-cd "$sandbox"
-git clone git://git.rtems.org/rtems-source-builder.git
-git clone git://git.rtems.org/rtems.git
-git clone git://git.rtems.org/rtems-libbsd.git
-cd "$sandbox"
-cd rtems-source-builder/rtems
-../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm
-cd "$sandbox"
-cd rtems
-PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap
-cd "$sandbox"
-mkdir b-xilinx_zynq_a9_qemu
-cd b-xilinx_zynq_a9_qemu
-PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \
- --target=arm-rtems5 --prefix="$sandbox/rtems/5" \
- --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu
-PATH="$sandbox/rtems/5/bin:$PATH" make
-PATH="$sandbox/rtems/5/bin:$PATH" make install
-cd "$sandbox"
-cd rtems-libbsd
-git submodule init
-git submodule update rtems_waf
-./waf configure --prefix="$sandbox/rtems/5" \
- --rtems-bsps=arm/xilinx_zynq_a9_qemu \
- --buildset=buildset/default.ini
-./waf
-./waf install
-qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none \
- -nographic -M xilinx-zynq-a9 -m 256M \
- -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/selectpollkqueue01.exe
-```
-
-1. Create a sandbox directory:
-
-```
-$ sandbox="$PWD/sandbox"
-$ mkdir sandbox
-```
-
-2. Clone the repositories:
-
-```
-$ cd "$sandbox"
-$ git clone git://git.rtems.org/rtems-source-builder.git
-$ git clone git://git.rtems.org/rtems.git
-$ git clone git://git.rtems.org/rtems-libbsd.git
-```
-
-3. Build and install the tools:
-
-```
-$ cd "$sandbox"
-$ cd rtems-source-builder/rtems
-$ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm
-```
-
-4. Bootstrap the RTEMS sources:
-
-```
-$ cd "$sandbox"
-$ cd rtems
-$ PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap
-```
-
-5. Build and install the RTEMS Board Support Packages (BSP) you want to use:
-
-```
-$ cd "$sandbox"
-$ mkdir b-xilinx_zynq_a9_qemu
-$ cd b-xilinx_zynq_a9_qemu
-$ PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \
- --target=arm-rtems5 --prefix="$sandbox/rtems/5" \
- --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu
-$ PATH="$sandbox/rtems/5/bin:$PATH" make
-$ PATH="$sandbox/rtems/5/bin:$PATH" make install
-```
-
-6. Populate the rtems_waf git submodule. Note, make sure you specify
- 'rtems_waf' or the FreeBSD kernel source will be cloned:
-
-```
-$ cd "$sandbox"
-$ cd rtems-libbsd
-$ git submodule init
-$ git submodule update rtems_waf
-```
-
-7. Run Waf's configure with your specific settings. In this case the path to
- the tools and RTEMS are provided on the command line and so do not need to
- be in your path or environment [1]. You can use
- '--rtems-archs=arm,sparc,i386' or
- '--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586' to build for
- more than BSP at a time. Note, you must provide the architecture and BSP as
- a pair. Providing just the BSP name will fail. This call also explicitly
- provides a buildset via the '--buildset=buildset/default.ini' option. If no
- buildset is provided the default one (which is the same as the one provided
- explicitly here) will be used. You can also provide multiple buildsets as a
- coma separated list or via multiple '--buildset=x' options.
-
-```
-$ cd "$sandbox"
-$ cd rtems-libbsd
-$ ./waf configure --prefix="$sandbox/rtems/5" \
- --rtems-bsps=arm/xilinx_zynq_a9_qemu \
- --buildset=buildset/default.ini
-```
-
-8. Build and install. The LibBSD package will be installed into the prefix
- provided to configure:
-
-```
-$ cd "$sandbox"
-$ cd rtems-libbsd
-$ ./waf
-$ ./waf install
-```
-
-9. Run the tests on QEMU, for example:
-
-```
-$ qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none \
-$ -nographic -M xilinx-zynq-a9 -m 256M \
-$ -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/selectpollkqueue01.exe
-```
-
-[1] It is good practice to keep your environment as empty as possible. Setting
- paths to tools or specific values to configure or control a build is
- dangerous because settings can leak between different builds and change
- what you expect a build to do. The Waf tool used here lets you specify on
- the command line the tools and RTEMS paths and this is embedded in Waf's
- configuration information. If you have a few source trees working at any
- one time with different tool sets or configurations you can easly move
- between them safe in the knowledge that one build will not infect another.
-
-Updating RTEMS Waf Support
---------------------------
-
-If you have a working libbsd repository and new changes to the `rtems_waf`
-submodule has been made, you will need update. A `git status` will indicate
-there are new commits with:
-
-```
-$ git status
- [ snip output ]
- modified: rtems_waf (new commits)
- [ snip output ]
-```
-
-To update:
-
-```
-$ git submodule update rtems_waf
-```
-
-Please make sure you use the exact command or you might find you are cloning
-the whole of the FreeBSD source tree. If that happens simply git ^C and try
-again.
-
-The following is for maintainer only who need to move libbsd to a newer
-versions:
-
-```
-$ git submodule update rtems_waf
-$ cd rtems_waf
-$ git checkout master
-$ git pull
-$ cd ..
-$ git commit -m "Update rtems_waf" rtems_waf
-```
-
-FreeBSD Developer Support
--------------------------
-
-The --freebsd-option provides a tool you can set special kernel options. This
-is a developer tool and should only be used if you are familiar with the
-internals of the FreeBSD kernel and what these options do.
-
-The options are listed in:
-
-https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES
-
-An example to turn on a verbose kernel boot, verbose sysinit and bus debugging
-configure with:
-
-```
---freebsd-options=bootverbose,verbose_sysinit,bus_debug,debug_locks,ktr,ktr_verbose
-```
-
-The LibBSD Waf support splits the options and converts them to uppercase and
-adds them -D options on the compiler command line.
-
-The list is:
-
- bootverbose: Verbose boot of the kernel
- verbose_sysinit: Verbose printing of all the SYSINIT calls
- bus_debug: Bus debugging support
- ktr: Kernel trace
- ktr_verbose: Verbose kernel trace
- debug_locks: FreeBSD locks debugging
- invariants: Invariants build of the kernel
- invariant_support: Support for Invariants (needed with invariants)
- rtems_bsd_descrip_trace: RTEMS BSD descriptor maping trace
- rtems_bsd_syscall_trace: RTEMS BSD system call trace
- rtems_bsd_vfs_trace RTEMS VFS to libio trace
-
-SMP Requirements
-----------------
-
-In order to support
-[EPOCH(9)](https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9)
-a scheduler with thread pinning support is required. This is the case if you
-use the default scheduler configuration. EPOCH(9) is a central synchronization
-mechanism of the network stack.
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..447f5885
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,889 @@
+RTEMS LibBSD
+************
+
+Welcome to building LibBSD for RTEMS using Waf. This package is a library
+containing various parts of the FreeBSD kernel ported to RTEMS. The library
+replaces the networking port of FreeBSD in the RTEMS kernel sources. This
+package is designed to be updated from the FreeBSD kernel sources and contains
+more than just the networking code.
+
+To build this package you need a current RTEMS tool set for your architecture,
+and a recent RTEMS kernel for your BSP installed. If you already have this, you
+can skip to step 5 of the build procedure.
+
+Building and Installing LibBSD
+==============================
+
+The following instructions show you how to build and install the RTEMS Tool
+Suite for the ``arm`` target, the RTEMS kernel using the
+``arm/xilinx_zynq_a9_qemu`` Board Support Package (BSP), and the LibBSD for this
+BSP.
+
+The Waf build support for RTEMS requires you provide your BSP name as an
+architecture and BSP pair. You must provide both or Waf will generate an error
+message during the configure phase.
+
+We will build an Xilinx Zynq Qemu BSP using the name
+``arm/xilinx_zynq_a9_qemu``. You can copy and paste the shell commands below to
+do this. The individual steps are explained afterwards.
+
+.. code-block:: none
+
+ sandbox="$PWD/sandbox"
+ mkdir sandbox
+ cd "$sandbox"
+ git clone git://git.rtems.org/rtems-source-builder.git
+ git clone git://git.rtems.org/rtems.git
+ git clone git://git.rtems.org/rtems-libbsd.git
+ cd "$sandbox"
+ cd rtems-source-builder/rtems
+ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm
+ cd "$sandbox"
+ cd rtems
+ echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini
+ ./waf configure --prefix "$sandbox/rtems/6"
+ ./waf
+ ./waf install
+ cd "$sandbox"
+ cd rtems-libbsd
+ git submodule init
+ git submodule update rtems_waf
+ ./waf configure --prefix="$sandbox/rtems/6" \
+ --rtems-bsps=arm/xilinx_zynq_a9_qemu \
+ --buildset=buildset/default.ini
+ ./waf
+ ./waf install
+ ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build
+
+1. Create a sandbox directory:
+
+ .. code-block:: none
+
+ $ sandbox="$PWD/sandbox"
+ $ mkdir sandbox
+
+2. Clone the repositories:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ git clone git://git.rtems.org/rtems-source-builder.git
+ $ git clone git://git.rtems.org/rtems.git
+ $ git clone git://git.rtems.org/rtems-libbsd.git
+
+3. Build and install the tools:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems-source-builder/rtems
+ $ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm
+
+4. Build and install the RTEMS Board Support Packages (BSP) you want to use:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems
+ $ echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini
+ $ ./waf configure --prefix "$sandbox/rtems/6"
+ $ ./waf
+ $ ./waf install
+
+5. Populate the ``rtems_waf`` git submodule. Note, make sure you specify
+ ``rtems_waf`` or the FreeBSD kernel source will be cloned:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems-libbsd
+ $ git submodule init
+ $ git submodule update rtems_waf
+
+6. Run Waf's configure with your specific settings. In this case the path to
+ the tools and RTEMS are provided on the command line and so do not need to
+ be in your path or environment, see comment below. You can use
+ ``--rtems-archs=arm,sparc,i386`` or
+ ``--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586`` to build for
+ more than BSP at a time. Note, you must provide the architecture and BSP as
+ a pair. Providing just the BSP name will fail. This call also explicitly
+ provides a buildset via the ``--buildset=buildset/default.ini`` option. If no
+ buildset is provided the default one (which is the same as the one provided
+ explicitly here) will be used. You can also provide multiple buildsets as a
+ coma separated list or via multiple ``--buildset=x`` options.
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems-libbsd
+ $ ./waf configure --prefix="$sandbox/rtems/6" \
+ --rtems-bsps=arm/xilinx_zynq_a9_qemu \
+ --buildset=buildset/default.ini
+
+7. Build and install. The LibBSD package will be installed into the prefix
+ provided to configure:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems-libbsd
+ $ ./waf
+ $ ./waf install
+
+9. Run the tests:
+
+ .. code-block:: none
+
+ $ cd "$sandbox"
+ $ cd rtems-libbsd
+ $ ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build
+
+It is good practice to keep your environment as empty as possible. Setting
+paths to tools or specific values to configure or control a build is dangerous
+because settings can leak between different builds and change what you expect a
+build to do. The Waf tool used here lets you specify on the command line the
+tools and RTEMS paths and this is embedded in Waf's configuration information.
+If you have a few source trees working at any one time with different tool sets
+or configurations you can easly move between them safe in the knowledge that
+one build will not infect another.
+
+Buildsets
+=========
+
+Note that the LibBSD supports different buildsets. These can be selected with
+the ``--buildset=some.ini`` option during the configure phase. Take a look at
+the comments in ``buildset/*.ini`` to see which build sets are officially
+supported.
+
+You can also create and provide your own buildset configuration. But remember
+that it's quite easy to break something by disabling the wrong modules. Only
+the configurations in the ``buildset`` directory are officially maintained.
+
+Initialization
+==============
+
+To initialise the LibBSD create a suitable ``rc.conf`` file. The FreeBSD man
+page `RC.CONF(5) <https://www.freebsd.org/cgi/man.cgi?rc.conf>`_ provides the
+details needed to create a suitable format file
+
+You can call one of three functions to run the initialisation once LibBSD has
+initialised:
+
+* ``rtems_bsd_run_etc_rc_conf()``: Run ``/etc/rc.conf``.
+* ``rtems_bsd_run_rc_conf()``: Run a user supplied file.
+* ``rtems_bsd_run_rc_conf_script()``: Run the in memory line feed separated text string.
+
+For exapmle:
+
+.. code-block:: c
+
+ void
+ network_init(void)
+ {
+ rtems_status_code sc;
+
+ sc = rtems_bsd_initialize();
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rtems_bsd_run_etc_rc_conf(true); /* verbose = true */
+ }
+
+By default the networking support is builtin. Other directives can be added and
+are found in ``machine/rtems-bsd-rc-conf-directives.h``. Please check the file
+for the list.
+
+The following network names are supported:
+
+.. code-block:: none
+
+ cloned_interfaces
+ ifconfig_'interface'
+ defaultrouter
+ hostname
+
+For example:
+
+.. code-block:: none
+
+ #
+ # My BSD initialisation.
+ #
+ hostname="myhost"
+ cloned_interfaces="vlan0 vlan1"
+ ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0"
+ fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0"
+ defaultrouter="10.10.10.1"
+
+You can also intialise the LibBSD using code. The following code to
+initialize the LibBSD:
+
+.. code-block:: c
+
+ #include <assert.h>
+ #include <sysexits.h>
+
+ #include <rtems/bsd/bsd.h>
+
+ void
+ network_init(void)
+ {
+ rtems_status_code sc;
+ int exit_code;
+
+ sc = rtems_bsd_initialize();
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ exit_code = rtems_bsd_ifconfig_lo0();
+ assert(exit_code == EX_OK);
+ }
+
+This performs the basic network stack initialization with a loopback interface.
+Further initialization must be done using the standard FreeBSD network
+configuration commands
+`IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_
+using ``rtems_bsd_command_ifconfig()`` and
+`ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_
+using ``rtems_bsd_command_route()``. For an example, please have a look at
+`default-network-init.h <testsuite/include/rtems/bsd/test/default-network-init.h>`_.
+
+Task Priorities and Stack Size
+==============================
+
+The default task priority is 96 for the interrupt server task (name "IRQS"), 98
+for the timer server task (name "TIME") and 100 for all other tasks. The
+application may provide their own implementation of the
+``rtems_bsd_get_task_priority()`` function if different values are desired (for
+example in the translation unit which calls ``rtems_bsd_initialize()``).
+
+The task stack size is determined by the ``rtems_bsd_get_task_stack_size()``
+function which may be provided by the application in case the default is not
+appropriate.
+
+Size for Allocator Domains
+==========================
+
+The size for an allocator domain can be specified via the
+``rtems_bsd_get_allocator_domain_size()`` function. The application may provide
+their own implementation of the ``rtems_bsd_get_allocator_domain_size()``
+function (for example in the module which calls ``rtems_bsd_initialize()``) if
+different values are desired. The default size is 8MiB for all domains.
+
+Redirecting or Disabling the Output
+===================================
+
+A lot of system messages are printed to the ``stdout`` by default. If you want to
+redirect them you can overwrite the default print handler. That can even be done
+before the libbsd initialization to catch all messages. An example would look
+like follows:
+
+.. code-block:: c
+
+ int my_vprintf_handler(int level, const char *fmt, va_list ap) {
+ /* Do something with the messages. */
+
+ return number_of_printed_chars;
+ }
+
+ ...
+ /* In your initialization: */
+ rtems_bsd_vprintf_handler old;
+ old = rtems_bsd_set_vprintf_handler(my_vprintf_handler);
+ ...
+
+As a special case, you can set the ``rtems_bsd_vprintf_handler_mute(...)``
+provided by LibBSD to suppress all output.
+
+Branches
+========
+
+master
+ This branch is intended for the RTEMS master which tracks the FreeBSD
+ master branch. This branch must be used for libbsd development. Back
+ ports to the 6-freebsd-12 are allowed.
+
+6-freebsd-12
+ This branch is intended for RTEMS 6 which tracks the FreeBSD stable/12
+ branch. This branch is maintained and regular updates from FreeBSD are
+ planned. It is recommended for production systems.
+
+5-freebsd-12
+ This branch belongs to the RTEMS 5 release. It is based on FreeBSD
+ stable/12 branch. It is recommended for production systems that use
+ RTEMS 5.
+
+5
+ This branch belongs to the RTEMS 5 release. It is based on a FreeBSD
+ development version. This branch is unmaintained. Use 5-freebsd-12 for
+ RTEMS 5.
+
+freebsd-9.3
+ Is the branch for some RTEMS version with a FreeBSD 9.3 baseline. This
+ branch is unmaintained. It is recommended to update to RTEMS 5 or 6.
+
+4.11
+ Is the branch for the RTEMS 4.11 release series. This branch is
+ unmaintained. It is recommended to update to RTEMS 5 or 6.
+
+Features
+========
+
+The following features are available in LibBSD. Some features need device
+driver support for a particular target platform.
+
+* `BPF(4) <http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4>`_: Berkeley Packet Filter
+* `DHCPCD(8) <http://roy.marples.name/projects/dhcpcd/index>`_: DHCP client
+* `dns_sd.h <mDNSResponder/mDNSShared/dns_sd.h>`_: DNS Service Discovery
+* `GETHOSTBYNAME(3) <http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3>`_: Get network host entry
+* `IF_BRIDGE(4) <http://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4>`_: Network bridge device
+* `INET(4) <http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4>`_: Internet protocol family
+* `INET6(4) <http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4>`_: Internet protocol version 6 family
+* `IPSEC(4) <http://www.freebsd.org/cgi/man.cgi?query=ipsec&sektion=4>`_: Internet Protocol Security protocol
+* `KQUEUE(2) <http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2>`_: Kernel event notification mechanism
+* `LAGG(4) <http://www.freebsd.org/cgi/man.cgi?query=lagg&sektion=4>`_: Link aggregation and link failover interface
+* `mDNSEmbeddedAPI.h <mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h>`_: Multi-Cast DNS
+* `MMC(4) <http://www.freebsd.org/cgi/man.cgi?query=mmc&sektion=4>`_: MultiMediaCard and SD Card bus driver
+* `NET80211(4) <http://www.freebsd.org/cgi/man.cgi?query=net80211&sektion=4>`_: Standard interface to IEEE 802.11 devices
+* `NVME(4) <http://www.freebsd.org/cgi/man.cgi?query=nvme&sektion=4>`_: NVM Express core driver
+* `PCI(4) <http://www.freebsd.org/cgi/man.cgi?query=pci&sektion=4>`_: Generic PCI/PCIe bus driver
+* `PF(4) <http://www.freebsd.org/cgi/man.cgi?query=pf&sektion=4>`_: Packet filter
+* `POLL(2) <http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2>`_: Synchronous I/O multiplexing
+* `RESOLVER(3) <http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3>`_: Resolver routines
+* `ROUTE(4) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4>`_: Kernel packet forwarding database
+* `SELECT(2) <http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2>`_: Synchronous I/O multiplexing
+* `SOCKET(2) <http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2>`_: Create an endpoint for communication
+* `SSL(7) <http://www.freebsd.org/cgi/man.cgi?query=ssl&sektion=7>`_: OpenSSL SSL/TLS library
+* `SYSCTL(3) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3>`_: Get or set system information
+* `TCP(4) <http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4>`_: Internet Transmission Control Protocol
+* `UDP(4) <http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4>`_: Internet User Datagram Protocol
+* `UMASS(4) <http://www.freebsd.org/cgi/man.cgi?query=umass&sektion=4>`_: USB Mass Storage Devices driver
+* `UNIX(4) <http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4>`_: UNIX-domain protocol family
+* `USB(4) <http://www.freebsd.org/cgi/man.cgi?query=usb&sektion=4>`_: Universal Serial Bus
+* `VLAN(4) <http://www.freebsd.org/cgi/man.cgi?query=vlan&sektion=4>`_: IEEE 802.1Q VLAN network interface
+
+Commands
+========
+
+In LibBSD the following ports of FreeBSD command line tools are available. You
+can invoke the commands in the RTEMS Shell or through function calls, for
+example ``rtems_bsd_command_ifconfig()``. The functions declarations are
+available through
+`#include <machine/rtems-bsd-commands.h> <rtemsbsd/include/machine/rtems-bsd-commands.h>`_.
+
+* `ARP(8) <http://www.freebsd.org/cgi/man.cgi?query=arp&sektion=8>`_: Address resolution display and control
+* `HOSTNAME(1) <http://www.freebsd.org/cgi/man.cgi?query=hostname&sektion=1>`_: Set or print name of current host system
+* `IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_: Configure network interface parameters
+* `IFMCSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=ifmcstat&sektion=8>`_: Dump multicast group management statistics per interface
+* `NETSTAT(1) <http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1>`_: Show network status
+* `NVMECONTROL(8) <http://www.freebsd.org/cgi/man.cgi?query=nvmecontrol&sektion=8>`_: NVM Express control utility
+* `OPENSSL(1) <http://www.freebsd.org/cgi/man.cgi?query=openssl&sektion=1>`_: OpenSSL command line tool
+* `PFCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_: Control the packet filter (PF) device
+* `PING6(8) <http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8>`_: Send ICMPv6 ECHO_REQUEST packets to network hosts
+* `PING(8) <http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8>`_: Send ICMP ECHO_REQUEST packets to network hosts
+* `RACOON(8) <http://www.freebsd.org/cgi/man.cgi?query=racoon&sektion=8>`_: IKE (ISAKMP/Oakley) key management daemon
+* `ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_: Manually manipulate the routing tables
+* `SETKEY(8) <http://www.freebsd.org/cgi/man.cgi?query=setkey&sektion=8>`_: Manually manipulate the IPsec SA/SP database
+* `STTY(1) <http://www.freebsd.org/cgi/man.cgi?query=stty&sektion=1>`_: Set the options for a terminal device interface
+* `SYSCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=8>`_: Get or set kernel state
+* `TCPDUMP(1) <http://www.freebsd.org/cgi/man.cgi?query=tcpdump&sektion=1>`_: Dump traffic on a network
+* `VMSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=vmstat&sektion=8>`_: Report virtual memory statistics
+* `WPA_SUPPLICANT(8) <http://www.freebsd.org/cgi/man.cgi?query=wpa_supplicant&sektion=8>`_: WPA/802.11i Supplicant for wireless network devices
+
+Command specific notes are listed below.
+
+HOSTNAME(1)
+ In addition to the standard options the RTEMS version of the HOSTNAME(1)
+ command supports the -m flag to set/get the multicast hostname of the mDNS
+ resolver instance. See also ``rtems_mdns_sethostname()`` and
+ ``rtems_mdns_gethostname()``.
+
+Packet Filter (PF, Firewall)
+============================
+
+It is possible to use PF as a firewall. See the
+`FreeBSD Handbook <https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-pf>`_
+for details on the range of functions and for how to configure the firewall.
+
+Configuration
+-------------
+
+The following is necessary to use PF on RTEMS:
+
+* You have to provide a ``/etc/pf.os`` file. The firewall can use it for passive
+ OS fingerprinting. If you don't want to use this feature, the file may contain
+ nothing except a line of comment (for example "# empty").
+
+* If some filters use protocol names (like ``tcp`` or ``udp``) you have to provide a
+ ``/etc/protocols`` file.
+
+* If some filters use service names (like ``http`` or ``https``) you have to provide a
+ ``/etc/services`` file.
+
+* Create a rule file (normally ``/etc/pf.conf``). See the FreeBSD manual for the
+ syntax.
+
+* Load the rule file using the
+ `pfctl <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_
+ command and enable PF. Please note that the pfctl command needs a lot of
+ stack. You should use at least RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of
+ stack. An example initialisation can look like follows:
+
+ .. code-block:: c
+
+ int exit_code;
+ char *argv[] = {
+ "pfctl",
+ "-f",
+ "/etc/pf.conf",
+ "-e",
+ NULL
+ };
+
+ exit_code = rtems_bsd_command_pfctl(ARGC(argv), argv);
+ assert(exit_code == EXIT_SUCCSESS);
+
+Known Restrictions
+------------------
+
+Currently, PF on RTEMS always uses the configuration for memory restricted
+systems (on FreeBSD that means systems with less than 100 MB RAM). This is
+fixed in ``pfctl_init_options()``.
+
+Wireless Network (WLAN)
+=======================
+
+The LibBSD provides a basic support for WLAN. Note that currently this support
+is still in an early state. The WLAN support is _not_ enabled in the default
+buildset. You have to configure LibBSD with the
+``--buildset=buildset/everything.ini`` to enable that feature.
+
+Configuration
+-------------
+
+The following gives a rough overview over the necessary steps to connect to an
+encrypted network with an RTL8188EU based WiFi dongle:
+
+* Reference all necessary module for your BSP. For some BSPs this is already
+ done in the ``nexus-devices.h``:
+
+ .. code-block:: none
+
+ SYSINIT_MODULE_REFERENCE(wlan_ratectl_none);
+ SYSINIT_MODULE_REFERENCE(wlan_sta);
+ SYSINIT_MODULE_REFERENCE(wlan_amrr);
+ SYSINIT_MODULE_REFERENCE(wlan_wep);
+ SYSINIT_MODULE_REFERENCE(wlan_tkip);
+ SYSINIT_MODULE_REFERENCE(wlan_ccmp);
+ SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub);
+
+* Create your wlan device using ifconfig:
+
+ .. code-block:: none
+
+ ifconfig wlan0 create wlandev rtwn0 up
+
+* Start a ``wpa_supplicant`` instance for that device:
+
+ .. code-block:: none
+
+ wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf
+
+Note that the wpa_supplicant will only be active till the device goes down. A
+workaround is to just restart it every time it exits.
+
+Known Restrictions
+------------------
+
+* The network interface (e.g. wlan0) is currently not automatically created. It
+ would be nice, if some service would create it as soon as for example a USB
+ device is connected. In FreeBSD the names are assigned via rc.conf with lines
+ like ``wlans_rtwn0="wlan0"``.
+
+* ``wpa_supplicant`` hast to be started after the device is created. It has to be
+ restarted every time the connection goes down. Instead of this behaviour,
+ there should be some service that starts and restarts ``wpa_supplicant``
+ automatically if a interface is ready. Probably the dhcpcd hooks could be used
+ for that.
+
+* The current ``wpa_supplicant`` implementation is protected with a lock so it can't
+ be started more than one time. If multiple interface should be used, all have
+ to be handled by that single instance. That makes it hard to add interfaces
+ dynamically. ``wpa_supplicant`` should be reviewed thoroughly whether multiple
+ instances could be started in parallel.
+
+* The control interface of ``wpa_supplicant`` most likely doesn't work. The wpa_cli
+ application is not ported.
+
+IPSec
+=====
+
+The IPSec support is optional in LibBSD. It is disabled in the default build
+set. Please make sure to use a build set with ``netipsec = on``.
+
+Configuration
+-------------
+
+To use IPSec the following configuration is necessary:
+
+.. code-block:: none
+
+ SYSINIT_MODULE_REFERENCE(if_gif);
+ SYSINIT_MODULE_REFERENCE(cryptodev);
+ RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec)
+ RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL);
+
+Alternatively, you can use the ``RTEMS_BSD_CONFIG_IPSEC`` which also includes the
+rc.conf support for ipsec. It's still necessary to include a crypto device in
+your config (``cryptosoft`` in the above sample).
+
+The necessary initialization steps for a IPSec connection are similar to the
+steps on a FreeBSD-System. The example assumes the following setup:
+
+- RTEMS external IP: 192.168.10.1/24
+- RTEMS internal IP: 10.10.1.1/24
+- remote external IP: 192.168.10.10/24
+- remote internal IP: 172.24.0.1/24
+- shared key: "mysecretkey"
+
+With this the following steps are necessary:
+
+* Create a gif0 device:
+
+ .. code-block:: none
+
+ ifconfig gif0 create
+
+* Configure the gif0 device:
+
+ .. code-block:: none
+
+ ifconfig gif0 10.10.1.1 172.24.0.1
+ ifconfig gif0 tunnel 192.168.10.1 192.168.10.10
+
+* Add a route to the remote net via the remote IP:
+
+ .. code-block:: none
+
+ route add 172.24.0.0/24 172.24.0.1
+
+* Create a correct rule set in ``/etc/setkey.conf``:
+
+ .. code-block:: none
+
+ flush;
+ spdflush;
+ spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use;
+ spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use;
+
+* Call ``setkey``:
+
+ .. code-block:: none
+
+ setkey -f /etc/setkey.conf
+
+* Create a correct configuration in ``/etc/racoon.conf``:
+
+ .. code-block:: none
+
+ path pre_shared_key "/etc/racoon_psk.txt";
+ log info;
+
+ padding # options are not to be changed
+ {
+ maximum_length 20;
+ randomize off;
+ strict_check off;
+ exclusive_tail off;
+ }
+
+ listen # address [port] that racoon will listen on
+ {
+ isakmp 192.168.10.1[500];
+ }
+
+ remote 192.168.10.10 [500]
+ {
+ exchange_mode main;
+ my_identifier address 192.168.10.1;
+ peers_identifier address 192.168.10.10;
+ proposal_check obey;
+ proposal {
+ encryption_algorithm 3des;
+ hash_algorithm md5;
+ authentication_method pre_shared_key;
+ lifetime time 3600 sec;
+ dh_group 2;
+ }
+ }
+
+ sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any)
+ {
+ pfs_group 2;
+ lifetime time 28800 sec;
+ encryption_algorithm 3des;
+ authentication_algorithm hmac_md5;
+ compression_algorithm deflate;
+ }
+
+* Create a correct configuration in ``/etc/racoon_psk.txt``:
+
+ .. code-block:: none
+
+ 192.168.10.10 mysecretkey
+
+* Start a ike-daemon (racoon):
+
+ .. code-block:: none
+
+ racoon -F -f /etc/racoon.conf
+----
+
+All commands can be called via the respective API functions. For racoon there is
+a ``rtems_bsd_racoon_daemon()`` function that forks of racoon as a task.
+
+Alternatively, IPSec can also be configured via rc.conf entries:
+
+.. code-block:: none
+
+ cloned_interfaces="gif0"
+ ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10"
+ ike_enable="YES"
+ ike_program="racoon"
+ ike_flags="-F -f /etc/racoon.conf"
+ ike_priority="250"
+
+ ipsec_enable="YES"
+ ipsec_file="/etc/setkey.conf"
+
+ATTENTION: It is possible that the first packets slip through the tunnel without
+encryption (true for FreeBSD as well as RTEMS). You might want to set up a
+firewall rule to prevent that.
+
+Updating RTEMS Waf Support
+==========================
+
+If you have a working libbsd repository and new changes to the ``rtems_waf``
+submodule has been made, you will need update. A ``git status`` will indicate
+there are new commits with:
+
+.. code-block:: none
+
+ $ git status
+ [ snip output ]
+ modified: rtems_waf (new commits)
+ [ snip output ]
+
+To update:
+
+.. code-block:: none
+
+ $ git submodule update rtems_waf
+
+Please make sure you use the exact command or you might find you are cloning
+the whole of the FreeBSD source tree. If that happens simply git ^C and try
+again.
+
+FreeBSD Kernel Options
+======================
+
+You can set FreeBSD kernel options during build configuration with the
+--freebsd-option=a,b,c,... configuration command option. This is an advanced
+option and should only be used if you are familiar with the internals of the
+FreeBSD kernel and what these options do. Each of the comma separated options
+is converted to uppercase and passed as a compiler command line define (-D).
+
+The options are listed in the FreeBSD
+`NOTES <https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES>`_
+file.
+
+An example to turn on a verbose kernel boot, verbose sysinit and bus debugging
+configure with:
+
+.. code-block:: none
+
+ --freebsd-options=bootverbose,verbose_sysinit,bus_debug,debug_locks,ktr,ktr_verbose
+
+To enable kernel internal consistency checking use:
+
+.. code-block:: none
+
+ --freebsd-options=invariants,invariant_support
+
+The LibBSD Waf support splits the options and converts them to uppercase and
+adds them -D options on the compiler command line. The supported options are:
+
+bootverbose
+ Verbose boot of the kernel
+
+verbose_sysinit
+ Verbose printing of all the SYSINIT calls
+
+bus_debug
+ Bus debugging support
+
+ktr
+ Kernel trace
+
+ktr_verbose
+ Verbose kernel trace
+
+debug_locks
+ FreeBSD locks debugging
+
+invariants
+ Invariants build of the kernel
+
+invariant_support
+ Support for Invariants (needed with invariants)
+
+rtems_bsd_descrip_trace
+ RTEMS BSD descriptor maping trace
+
+rtems_bsd_syscall_trace
+ RTEMS BSD system call trace
+
+rtems_bsd_vfs_trace
+ RTEMS VFS to libio trace
+
+SMP Requirements
+================
+
+In order to support
+`EPOCH(9) <https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9>`_
+a scheduler with thread pinning support is required. This is the case if you
+use the default scheduler configuration. EPOCH(9) is a central synchronization
+mechanism of the network stack.
+
+Configuration for Network Tests
+===============================
+
+If you need some other IP configuration for the network tests that use a fixed
+IP config you can copy ``config.inc`` to a location outside to the source tree and
+adapt it. Then use the option ``--net-test-config=NET_CONFIG`` to pass the file to
+Waf's configure command.
+
+.. code-block:: none
+
+ NET_CFG_SELF_IP = 10.0.0.2
+ NET_CFG_NETMASK = 255.255.0.0
+ NET_CFG_PEER_IP = 10.0.0.1
+ NET_CFG_GATEWAY_IP = 10.0.0.1
+
+Qemu and Networking
+===================
+
+You can use the Qemu simulator to run a LibBSD based application and connect it
+to a virtual network on your host.
+
+Networking with TAP Interface
+-----------------------------
+
+One option for networking with Qemu is using a TAP interface (virtual
+Ethernet). You can create a TAP interface with these commands on Linux:
+
+.. code-block:: none
+
+ sudo ip tuntap add qtap mode tap user $(whoami)
+ sudo ip link set dev qtap up
+ sudo ip addr add 169.254.1.1/16 dev qtap
+
+You can show the interface state with the following command:
+
+.. code-block:: none
+
+ $ ip addr show qtap
+ 27: qtap: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
+ link/ether 8e:50:a2:fb:e1:3b brd ff:ff:ff:ff:ff:ff
+ inet 169.254.1.1/16 scope global qtap
+ valid_lft forever preferred_lft forever
+
+You may have to assign the interface to a firewall zone.
+
+The Qemu command line varies by board support package, here is an example for
+the arm/xilinx_zynq_a9_qemu BSP:
+
+.. code-block:: none
+
+ qemu-system-arm -serial null -serial mon:stdio -nographic \
+ -M xilinx-zynq-a9 -m 256M \
+ -net nic,model=cadence_gem \
+ -net tap,ifname=qtap,script=no,downscript=no \
+ -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/media01.exe
+
+Make sure that each Qemu instance uses its own MAC address to avoid an address
+conflict (or otherwise use it as a test). After some seconds it will acquire a
+IPv4 link-local address, for example:
+
+.. code-block:: none
+
+ info: cgem0: probing for an IPv4LL address
+ debug: cgem0: checking for 169.254.159.156
+
+You can connect to the target via telnet, for example:
+
+.. code-block:: none
+
+ $ telnet 169.254.159.156
+ Trying 169.254.159.156...
+ Connected to 169.254.159.156.
+ Escape character is '^]'.
+
+ RTEMS Shell on /dev/pty4. Use 'help' to list commands.
+ TLNT [/] #
+
+Virtual Distributed Ethernet (VDE)
+----------------------------------
+
+You can use a Virtual Distributed Ethernet (VDE) to create a network
+environment that does not need to run Qemu as root or needing to drop the tap's
+privileges to run Qemu.
+
+VDE creates a software switch with a default of 32 ports which means a single
+kernel tap can support 32 Qemu networking sessions.
+
+To use VDE you need to build Qemu with VDE support. The RSB can detect a VDE
+plug and enable VDE support in Qemu when building. On FreeBSD install the VDE
+support with:
+
+.. code-block:: none
+
+ pkg install -u vde2
+
+Build Qemu with the RSB.
+
+To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD
+add to your ``/etc/rc.conf``:
+
+.. code-block:: none
+
+ cloned_interfaces="bridge0 tap0"
+ autobridge_interfaces="bridge0"
+ autobridge_bridge0="re0 tap0"
+ ifconfig_re0="up"
+ ifconfig_tap0="up"
+ ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0"
+ defaultrouter="10.10.1.1"
+
+Start the VDE switch as root:
+
+.. code-block:: none
+
+ sysctl net.link.tap.user_open=1
+ sysctl net.link.tap.up_on_open=1
+ vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660
+ chmod 660 /dev/tap0
+
+You can connect to the VDE switch's management channel using:
+
+.. code-block:: none
+
+ vdeterm /tmp/mgmt1
+
+To run Qemu:
+
+.. code-block:: none
+
+ qemu-system-arm -serial null -serial mon:stdio -nographic \
+ -M xilinx-zynq-a9 -m 256M \
+ -net nic,model=cadence_gem \
+ -net vde,id=vde0,sock=/tmp/vde1
+ -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/rcconf02.exe
diff --git a/builder.py b/builder.py
index c3320325..b43eb618 100755
--- a/builder.py
+++ b/builder.py
@@ -221,6 +221,9 @@ def revertFixLocalIncludes(data):
data = re.sub('#include <rtems/bsd/local/([^>]*)>', '#include "\\1"', data)
return data
+def assertNothing(path):
+ pass
+
def assertHeaderFile(path):
if path[-2] != '.' or path[-1] != 'h':
print("*** " + path + " does not end in .h")
@@ -880,7 +883,7 @@ class Module(object):
]
return files
- def addPlainTextFile(self, files):
+ def addPlainTextFiles(self, files):
self.files += self.addFiles('user', files,
FreeBSDPathComposer(), Converter(),
Converter(), assertNothing)
diff --git a/buildset/default.ini b/buildset/default.ini
index 454cc74e..acb1226d 100644
--- a/buildset/default.ini
+++ b/buildset/default.ini
@@ -23,6 +23,7 @@ dev_nic = on
dev_nic_broadcomm = on
dev_nic_dc = on
dev_nic_e1000 = on
+dev_nic_xilinx = on
dev_nic_fxp = on
dev_nic_re = on
dev_nic_smc = on
@@ -42,6 +43,7 @@ evdev = on
fdt = on
fs_nfs = on
fs_nfsclient = on
+if_mve = on
imx = on
in_cksum = on
mdnsresponder = on
@@ -63,6 +65,7 @@ rpc = on
rpc_user = on
rtems = on
tests = on
+tsi148 = off
tty = on
user_space = on
user_space_wlanstats = off
diff --git a/buildset/everything.ini b/buildset/everything.ini
index 0eee99e7..b4e57246 100644
--- a/buildset/everything.ini
+++ b/buildset/everything.ini
@@ -17,3 +17,6 @@ usr_sbin_wpa_supplicant = on
# IPSec
netipsec = on
+
+# VME
+tsi148 = on
diff --git a/dhcpcd/dhcpcd.c b/dhcpcd/dhcpcd.c
index b7839d49..93620727 100644
--- a/dhcpcd/dhcpcd.c
+++ b/dhcpcd/dhcpcd.c
@@ -1155,7 +1155,7 @@ dhcpcd_task(rtems_task_argument arg)
(*config->destroy)(config, exit_code);
}
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
rtems_status_code
diff --git a/freebsd-org b/freebsd-org
-Subproject 0d1c391321b34b3025cf0e72f2231d836ff76da
+Subproject c6c89ab952f9ffe895939a2621180acc99ae8b8
diff --git a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h
index d021ae99..2c8ff3b8 100644
--- a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h
+++ b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h
@@ -139,7 +139,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern int nd_smi_module_loaded);
/* print-sll.c */
/* print-slow.c */
/* print-smb.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern u_char const *startbuf);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern unsigned char const *startbuf);
/* print-smtp.c */
/* print-snmp.c */
/* print-stp.c */
diff --git a/freebsd/contrib/tcpdump/tcpdump.c b/freebsd/contrib/tcpdump/tcpdump.c
index b1e7f0d1..5b658412 100644
--- a/freebsd/contrib/tcpdump/tcpdump.c
+++ b/freebsd/contrib/tcpdump/tcpdump.c
@@ -139,6 +139,7 @@ The Regents of the University of California. All rights reserved.\n";
#include <sys/sysctl.h>
#include <machine/rtems-bsd-commands.h>
#include <assert.h>
+#include <sched.h>
#include <rtems.h>
#include <rtems/linkersets.h>
#define setpriority(a, b, c)
@@ -206,8 +207,10 @@ cap_channel_t *capdns;
static void error(FORMAT_STRING(const char *), ...) NORETURN PRINTFLIKE(1, 2);
static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
static void exit_tcpdump(int) NORETURN;
+#ifndef __rtems__
static RETSIGTYPE cleanup(int);
static RETSIGTYPE child_cleanup(int);
+#endif /* __rtems__ */
static void print_version(void);
static void print_usage(void);
static void show_tstamp_types_and_exit(pcap_t *, const char *device) NORETURN;
@@ -219,6 +222,7 @@ static void show_devices_and_exit (void) NORETURN;
static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *);
static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+#ifndef __rtems__
static void droproot(const char *, const char *);
#ifdef SIGNAL_REQ_INFO
@@ -232,6 +236,7 @@ RETSIGTYPE requestinfo(int);
#elif defined(HAVE_ALARM)
static void verbose_stats_dump(int sig);
#endif
+#endif /* __rtems__ */
static void info(int);
static u_int packets_captured;
@@ -623,6 +628,7 @@ static const struct option longopts[] = {
{ NULL, 0, NULL, 0 }
};
+#ifndef __rtems__
#ifndef _WIN32
/* Drop root privileges and chroot if necessary */
static void
@@ -654,7 +660,6 @@ droproot(const char *username, const char *chroot_dir)
fprintf(stderr, "dropped privs to %s\n", username);
}
#else
-#ifndef __rtems__
if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
fprintf(stderr, "%s: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n",
@@ -667,7 +672,6 @@ droproot(const char *username, const char *chroot_dir)
else {
fprintf(stderr, "dropped privs to %s\n", username);
}
-#endif /* __rtems__ */
#endif /* HAVE_LIBCAP_NG */
}
else {
@@ -689,6 +693,7 @@ droproot(const char *username, const char *chroot_dir)
}
#endif /* _WIN32 */
+#endif /* __rtems__ */
static int
getWflagChars(int x)
@@ -1196,26 +1201,22 @@ typedef struct {
FILE *in;
pcap_t *pd;
rtems_id master;
+ bool terminate;
} pcap_loop_context;
static void
pcap_loop_monitor(rtems_task_argument arg)
{
- pcap_loop_context *ctx;
+ const pcap_loop_context *ctx;
FILE *in;
pcap_t *pd;
- rtems_id master;
rtems_status_code sc;
- ctx = (pcap_loop_context *)arg;
+ ctx = (const pcap_loop_context *)arg;
in = ctx->in;
pd = ctx->pd;
- master = ctx->master;
-
- sc = rtems_event_transient_send(master);
- assert(sc == RTEMS_SUCCESSFUL);
- while (true) {
+ while (!ctx->terminate) {
int c;
c = fgetc(in);
@@ -1224,19 +1225,22 @@ pcap_loop_monitor(rtems_task_argument arg)
pcap_breakloop(pd);
break;
}
+
+ sched_yield();
}
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ sc = rtems_event_transient_send(ctx->master);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rtems_task_exit();
}
-static int
-pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud)
+static void
+pcap_create_loop_monitor(pcap_loop_context *ctx, pcap_t *pd)
{
rtems_status_code sc;
rtems_task_priority priority;
rtems_id id;
- pcap_loop_context ctx;
sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY,
&priority);
@@ -1246,27 +1250,38 @@ pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud)
RTEMS_MINIMUM_STACK_SIZE, RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES, &id);
if (sc != RTEMS_SUCCESSFUL) {
- fprintf(stderr, "tcpdump: cannot create helper thread: %s\n",
+ error("cannot create pcap loop monitor thread: %s\n",
rtems_status_text(sc));
- return (-1);
}
fprintf(stdout, "tcpdump: press <ENTER> or 'q' or 'Q' to quit\n");
- ctx.in = stdin;
- ctx.pd = pd;
- ctx.master = rtems_task_self();
+ ctx->in = stdin;
+ ctx->pd = pd;
+ ctx->master = rtems_task_self();
+ ctx->terminate = false;
sc = rtems_task_start(id, pcap_loop_monitor,
- (rtems_task_argument)&ctx);
+ (rtems_task_argument)ctx);
assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void
+pcap_terminate_loop_monitor(pcap_loop_context *ctx)
+{
+ rtems_status_code sc;
+
+ ctx->terminate = true;
sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
assert(sc == RTEMS_SUCCESSFUL);
-
- return (pcap_loop(pd, cnt, cb, ud));
}
-#define pcap_loop(pd, cnt, cb, ud) pcap_loop_wrapper(pd, cnt, cb, ud)
+static void
+destroy_pcap_dumper(void *arg)
+{
+
+ pcap_dump_close(arg);
+}
#endif /* __rtems__ */
int
#ifndef __rtems__
@@ -1283,15 +1298,19 @@ main(int argc, char **argv)
int dlt;
const char *dlt_name;
struct bpf_program fcode;
+#ifndef __rtems__
#ifndef _WIN32
RETSIGTYPE (*oldhandler)(int);
#endif
+#endif /* __rtems__ */
struct dump_info dumpinfo;
u_char *pcap_userdata;
char ebuf[PCAP_ERRBUF_SIZE];
char VFileLine[PATH_MAX + 1];
+#ifndef __rtems__
char *username = NULL;
char *chroot_dir = NULL;
+#endif /* __rtems__ */
char *ret = NULL;
char *end;
#ifdef HAVE_PCAP_FINDALLDEVS
@@ -1663,9 +1682,11 @@ main(int argc, char **argv)
zflag = optarg;
break;
+#ifndef __rtems__
case 'Z':
username = optarg;
break;
+#endif /* __rtems__ */
case '#':
ndo->ndo_packet_number = 1;
@@ -1962,6 +1983,7 @@ main(int argc, char **argv)
init_print(ndo, localnet, netmask, timezone_offset);
+#ifndef __rtems__
#ifndef _WIN32
(void)setsignal(SIGPIPE, cleanup);
(void)setsignal(SIGTERM, cleanup);
@@ -2029,6 +2051,7 @@ main(int argc, char **argv)
}
#endif /* _WIN32 */
+#endif /* __rtems__ */
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
@@ -2123,6 +2146,12 @@ main(int argc, char **argv)
if (Uflag)
pcap_dump_flush(p);
#endif
+#ifdef __rtems__
+ if (rtems_bsd_program_add_destructor(destroy_pcap_dumper, p) ==
+ NULL) {
+ error("cannot add destructor");
+ }
+#endif /* __rtems__ */
} else {
dlt = pcap_datalink(pd);
ndo->ndo_if_printer = get_if_printer(ndo, dlt);
@@ -2130,6 +2159,7 @@ main(int argc, char **argv)
pcap_userdata = (u_char *)ndo;
}
+#ifndef __rtems__
#ifdef SIGNAL_REQ_INFO
/*
* We can't get statistics when reading from a file rather
@@ -2154,6 +2184,7 @@ main(int argc, char **argv)
alarm(1);
#endif
}
+#endif /* __rtems__ */
if (RFileName == NULL) {
/*
@@ -2196,7 +2227,19 @@ main(int argc, char **argv)
#endif /* HAVE_CAPSICUM */
do {
+#ifdef __rtems__
+ pcap_loop_context ctx;
+
+ if (RFileName == NULL) {
+ pcap_create_loop_monitor(&ctx, pd);
+ }
+#endif /* __rtems__ */
status = pcap_loop(pd, cnt, callback, pcap_userdata);
+#ifdef __rtems__
+ if (RFileName == NULL) {
+ pcap_terminate_loop_monitor(&ctx);
+ }
+#endif /* __rtems__ */
if (WFileName == NULL) {
/*
* We're printing packets. Flush the printed output,
@@ -2315,6 +2358,7 @@ main(int argc, char **argv)
exit_tcpdump(status == -1 ? 1 : 0);
}
+#ifndef __rtems__
/* make a clean exit on interrupts */
static RETSIGTYPE
cleanup(int signo _U_)
@@ -2367,6 +2411,7 @@ child_cleanup(int signo _U_)
wait(NULL);
}
#endif /* HAVE_FORK && HAVE_VFORK */
+#endif /* __rtems__ */
static void
info(register int verbose)
@@ -2727,6 +2772,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
char Wpcap_version[]="3.1";
#endif
+#ifndef __rtems__
#ifdef SIGNAL_REQ_INFO
RETSIGTYPE requestinfo(int signo _U_)
{
@@ -2755,6 +2801,7 @@ static void verbose_stats_dump(int sig _U_)
alarm(1);
}
#endif
+#endif /* __rtems__ */
USES_APPLE_DEPRECATED_API
static void
diff --git a/freebsd/contrib/wpa/src/utils/eloop.c b/freebsd/contrib/wpa/src/utils/eloop.c
index 41de0f79..09493b89 100644
--- a/freebsd/contrib/wpa/src/utils/eloop.c
+++ b/freebsd/contrib/wpa/src/utils/eloop.c
@@ -16,6 +16,9 @@
#include "list.h"
#include "eloop.h"
+#ifdef __rtems__
+#define CONFIG_ELOOP_KQUEUE
+#endif /* __rtems__ */
#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
#error Do not define both of poll and epoll
#endif
@@ -955,6 +958,7 @@ int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
}
+#ifndef __rtems__
#ifndef CONFIG_NATIVE_WINDOWS
static void eloop_handle_alarm(int sig)
{
@@ -966,8 +970,10 @@ static void eloop_handle_alarm(int sig)
exit(1);
}
#endif /* CONFIG_NATIVE_WINDOWS */
+#endif /* __rtems__ */
+#ifndef __rtems__
static void eloop_handle_signal(int sig)
{
int i;
@@ -990,6 +996,7 @@ static void eloop_handle_signal(int sig)
}
}
}
+#endif /* __rtems__ */
static void eloop_process_pending_signals(void)
@@ -1001,9 +1008,11 @@ static void eloop_process_pending_signals(void)
eloop.signaled = 0;
if (eloop.pending_terminate) {
+#ifndef __rtems__
#ifndef CONFIG_NATIVE_WINDOWS
alarm(0);
#endif /* CONFIG_NATIVE_WINDOWS */
+#endif /* __rtems__ */
eloop.pending_terminate = 0;
}
@@ -1017,6 +1026,7 @@ static void eloop_process_pending_signals(void)
}
+#ifndef __rtems__
int eloop_register_signal(int sig, eloop_signal_handler handler,
void *user_data)
{
@@ -1037,26 +1047,35 @@ int eloop_register_signal(int sig, eloop_signal_handler handler,
return 0;
}
+#endif /* __rtems__ */
int eloop_register_signal_terminate(eloop_signal_handler handler,
void *user_data)
{
+#ifndef __rtems__
int ret = eloop_register_signal(SIGINT, handler, user_data);
if (ret == 0)
ret = eloop_register_signal(SIGTERM, handler, user_data);
return ret;
+#else /* __rtems__ */
+ return 0;
+#endif /* __rtems__ */
}
int eloop_register_signal_reconfig(eloop_signal_handler handler,
void *user_data)
{
+#ifndef __rtems__
#ifdef CONFIG_NATIVE_WINDOWS
return 0;
#else /* CONFIG_NATIVE_WINDOWS */
return eloop_register_signal(SIGHUP, handler, user_data);
#endif /* CONFIG_NATIVE_WINDOWS */
+#else /* __rtems__ */
+ return 0;
+#endif /* __rtems__ */
}
diff --git a/freebsd/crypto/openssl/apps/ocsp.c b/freebsd/crypto/openssl/apps/ocsp.c
index 7ff6a20c..dba8e6a9 100644
--- a/freebsd/crypto/openssl/apps/ocsp.c
+++ b/freebsd/crypto/openssl/apps/ocsp.c
@@ -58,7 +58,7 @@ NON_EMPTY_TRANSLATION_UNIT
#endif
# if !defined(NO_FORK) && !defined(OPENSSL_NO_SOCK) \
- && !defined(OPENSSL_NO_POSIX_IO)
+ && !defined(OPENSSL_NO_POSIX_IO) && !defined(__rtems__)
# define OCSP_DAEMON
# include <sys/types.h>
# include <sys/wait.h>
diff --git a/freebsd/crypto/openssl/apps/openssl.c b/freebsd/crypto/openssl/apps/openssl.c
index 31ec58d8..cdbb262b 100644
--- a/freebsd/crypto/openssl/apps/openssl.c
+++ b/freebsd/crypto/openssl/apps/openssl.c
@@ -89,9 +89,11 @@ static void calculate_columns(DISPLAY_COLUMNS *dc)
static int apps_startup(void)
{
+#ifndef __rtems__
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
+#endif /* __rtems__ */
/* Set non-default library initialisation settings */
if (!OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN
diff --git a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h
index 2c2b926e..90043fb0 100644
--- a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h
+++ b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h
@@ -3,6 +3,4 @@
#include "rtems-bsd-openssl-data.h"
/* ocsp.c */
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static char *prog);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int acfd);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int multi);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int termsig);
diff --git a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c
index 03596eee..6a040553 100644
--- a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c
+++ b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c
@@ -158,11 +158,13 @@ struct IOSB {
# endif
/* Define globals. They are protected by a lock */
+#ifndef __rtems__
# ifdef SIGACTION
static struct sigaction savsig[NX509_SIG];
# else
static void (*savsig[NX509_SIG]) (int);
# endif
+#endif /* __rtems__ */
# ifdef OPENSSL_SYS_VMS
static struct IOSB iosb;
@@ -185,7 +187,9 @@ static int is_a_tty;
/* Declare static functions */
# if !defined(OPENSSL_SYS_WINCE)
static int read_till_nl(FILE *);
+#ifndef __rtems__
static void recsig(int);
+#endif /* __rtems__ */
static void pushsig(void);
static void popsig(void);
# endif
@@ -588,6 +592,7 @@ static int close_console(UI *ui)
/* Internal functions to handle signals and act on them */
static void pushsig(void)
{
+#ifndef __rtems__
# ifndef OPENSSL_SYS_WIN32
int i;
# endif
@@ -630,10 +635,12 @@ static void pushsig(void)
# ifdef SIGWINCH
signal(SIGWINCH, SIG_DFL);
# endif
+#endif /* __rtems__ */
}
static void popsig(void)
{
+#ifndef __rtems__
# ifdef OPENSSL_SYS_WIN32
signal(SIGABRT, savsig[SIGABRT]);
signal(SIGFPE, savsig[SIGFPE]);
@@ -659,12 +666,15 @@ static void popsig(void)
# endif
}
# endif
+#endif /* __rtems__ */
}
+#ifndef __rtems__
static void recsig(int i)
{
intr_signal = i;
}
+#endif /* __rtems__ */
# endif
/* Internal functions specific for Windows */
diff --git a/freebsd/lib/libc/include/libc_private.h b/freebsd/lib/libc/include/libc_private.h
index fb3a4bb2..4a699e93 100644
--- a/freebsd/lib/libc/include/libc_private.h
+++ b/freebsd/lib/libc/include/libc_private.h
@@ -36,8 +36,12 @@
#ifndef _LIBC_PRIVATE_H_
#define _LIBC_PRIVATE_H_
+#ifndef __rtems__
#include <sys/_types.h>
#include <sys/_pthreadtypes.h>
+#else /* __rtems__ */
+#include <sys/types.h>
+#endif /* __rtems__ */
/*
* This global flag is non-zero when a process has created one
diff --git a/freebsd/lib/libc/stdio/local.h b/freebsd/lib/libc/stdio/local.h
index bed0b232..5ec1d3f5 100644
--- a/freebsd/lib/libc/stdio/local.h
+++ b/freebsd/lib/libc/stdio/local.h
@@ -78,13 +78,15 @@ extern int __srefill(FILE *);
*/
extern int __srefill_r(struct _reent *,FILE *);
-#define __srefill(_x) __srefill_r(__getreent(), _x)
+#define __srefill(_x) __srefill_r(_REENT, _x)
#endif /* __rtems__ */
extern int __sread(void *, char *, int);
extern int __swrite(void *, char const *, int);
extern fpos_t __sseek(void *, fpos_t, int);
extern int __sclose(void *);
+#ifndef __rtems__
extern void __sinit(void);
+#endif /* __rtems__ */
extern void _cleanup(void);
extern void __smakebuf(FILE *);
extern int __swhatbuf(FILE *, size_t *, int *);
diff --git a/freebsd/sbin/pfctl/pfctl_altq.c b/freebsd/sbin/pfctl/pfctl_altq.c
index 7cf72b43..05c7da22 100644
--- a/freebsd/sbin/pfctl/pfctl_altq.c
+++ b/freebsd/sbin/pfctl/pfctl_altq.c
@@ -31,6 +31,7 @@
__FBSDID("$FreeBSD$");
#define PFIOC_USE_LATEST
+#define _WANT_FREEBSD_BITSET
#include <sys/types.h>
#include <sys/bitset.h>
diff --git a/freebsd/sbin/pfctl/pfctl_parser.c b/freebsd/sbin/pfctl/pfctl_parser.c
index 56f548ba..8a93b39e 100644
--- a/freebsd/sbin/pfctl/pfctl_parser.c
+++ b/freebsd/sbin/pfctl/pfctl_parser.c
@@ -1351,10 +1351,17 @@ get_socket_domain(void)
return (sdom);
}
+#ifdef __rtems__
+static int pfctl_s = -1;
+#endif /* __rtems__ */
int
get_query_socket(void)
{
+#ifndef __rtems__
static int s = -1;
+#else /* __rtems__ */
+#define s pfctl_s
+#endif /* __rtems__ */
if (s == -1) {
if ((s = socket(get_socket_domain(), SOCK_DGRAM, 0)) == -1)
@@ -1362,6 +1369,9 @@ get_query_socket(void)
}
return (s);
+#ifdef __rtems__
+#undef s
+#endif /* __rtems__ */
}
/*
diff --git a/freebsd/sbin/pfctl/pfctl_parser.h b/freebsd/sbin/pfctl/pfctl_parser.h
index aa6d98d7..7d92b1db 100644
--- a/freebsd/sbin/pfctl/pfctl_parser.h
+++ b/freebsd/sbin/pfctl/pfctl_parser.h
@@ -178,7 +178,7 @@ struct node_queue_opt {
};
#define QPRI_BITSET_SIZE 256
-BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE);
+__BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE);
LIST_HEAD(gen_sc, segment);
struct pfctl_altq {
diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h
index 3d805ea1..c43866bb 100644
--- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h
+++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h
@@ -35,6 +35,7 @@
#define parseport _bsd_pfctl_parseport
#define pfctl_cmdline_symset _bsd_pfctl_pfctl_cmdline_symset
#define pfctl_load_anchors _bsd_pfctl_pfctl_load_anchors
+#define pfctl_s _bsd_pfctl_s
#define pfctlychar _bsd_pfctl_pfctlychar
#define pfctlydebug _bsd_pfctl_pfctlydebug
#define pfctlyerrflag _bsd_pfctl_pfctlyerrflag
diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_altq-data.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_altq-data.h
index 4c39bea9..59b3541d 100644
--- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_altq-data.h
+++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_altq-data.h
@@ -4,8 +4,6 @@
/* pfctl_altq.c */
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static char r2sbuf[][16]);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static int idx);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct gen_sc lssc);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct gen_sc rtsc);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct hsearch_data if_map);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct hsearch_data qid_map);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct hsearch_data queue_map);
diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
index f9a7f49f..ac6655cc 100644
--- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
+++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
@@ -4,3 +4,4 @@
/* pfctl_parser.c */
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct hsearch_data isgroup_map);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct node_host *iftab);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static int pfctl_s);
diff --git a/freebsd/sbin/ping/ping.c b/freebsd/sbin/ping/ping.c
index 71976058..0daa5f45 100644
--- a/freebsd/sbin/ping/ping.c
+++ b/freebsd/sbin/ping/ping.c
@@ -306,7 +306,9 @@ main(int argc, char *const *argv)
#endif
struct sockaddr_in *to;
double t;
+#ifndef __rtems__
u_long alarmtimeout;
+#endif /* __rtems__ */
long ltmp;
int almost_done, ch, df, hold, i, icmp_len, mib[4], preload;
int ssend_errno, srecv_errno, tos, ttl;
@@ -370,7 +372,9 @@ main(int argc, char *const *argv)
err(EX_OSERR, "srecv socket");
}
+#ifndef __rtems__
alarmtimeout = df = preload = tos = 0;
+#endif /* __rtems__ */
outpack = outpackhdr + sizeof(struct ip);
while ((ch = getopt(argc, argv,
@@ -569,6 +573,7 @@ main(int argc, char *const *argv)
mttl = ltmp;
options |= F_MTTL;
break;
+#ifndef __rtems__
case 't':
alarmtimeout = strtoul(optarg, &ep, 0);
if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
@@ -579,6 +584,7 @@ main(int argc, char *const *argv)
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
+#endif /* __rtems__ */
case 'v':
options |= F_VERBOSE;
break;
@@ -1529,8 +1535,10 @@ static void
finish(void)
{
+#ifndef __rtems__
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGALRM, SIG_IGN);
+#endif /* __rtems__ */
(void)putchar('\n');
(void)fflush(stdout);
(void)printf("--- %s ping statistics ---\n", hostname);
diff --git a/freebsd/sbin/ping6/ping6.c b/freebsd/sbin/ping6/ping6.c
index 96bd60e3..67ad46eb 100644
--- a/freebsd/sbin/ping6/ping6.c
+++ b/freebsd/sbin/ping6/ping6.c
@@ -287,7 +287,9 @@ static void fill(char *, char *);
static int get_hoplim(struct msghdr *);
static int get_pathmtu(struct msghdr *);
static struct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
+#ifndef __rtems__
static void onsignal(int);
+#endif /* __rtems__ */
static void onint(int);
static size_t pingerlen(void);
static int pinger(void);
@@ -347,7 +349,9 @@ main(int argc, char *argv[])
struct timespec last, intvl;
struct sockaddr_in6 from, *sin6;
struct addrinfo hints, *res;
+#ifndef __rtems__
struct sigaction si_sa;
+#endif /* __rtems__ */
int cc, i;
int almost_done, ch, hold, packlen, preload, optval, error;
int nig_oldmcprefix = -1;
@@ -374,7 +378,9 @@ main(int argc, char *argv[])
char *policy_out = NULL;
#endif
double t;
+#ifndef __rtems__
u_long alarmtimeout;
+#endif /* __rtems__ */
size_t rthlen;
#ifdef IPV6_USE_MIN_MTU
int mflag = 0;
@@ -400,7 +406,9 @@ main(int argc, char *argv[])
intvl.tv_sec = interval / 1000;
intvl.tv_nsec = interval % 1000 * 1000000;
+#ifndef __rtems__
alarmtimeout = preload = 0;
+#endif /* __rtems__ */
datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
capdns = capdns_setup();
#ifndef IPSEC
@@ -628,6 +636,7 @@ main(int argc, char *argv[])
options |= F_WAITTIME;
waittime = (int)t;
break;
+#ifndef __rtems__
case 'X':
alarmtimeout = strtoul(optarg, &e, 0);
if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
@@ -638,6 +647,7 @@ main(int argc, char *argv[])
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
+#endif /* __rtems__ */
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
case 'P':
@@ -1174,6 +1184,7 @@ main(int argc, char *argv[])
}
clock_gettime(CLOCK_MONOTONIC, &last);
+#ifndef __rtems__
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = onsignal;
@@ -1189,6 +1200,7 @@ main(int argc, char *argv[])
if (sigaction(SIGALRM, &si_sa, 0) == -1)
err(EX_OSERR, "sigaction SIGALRM");
}
+#endif /* __rtems__ */
if (options & F_FLOOD) {
intvl.tv_sec = 0;
intvl.tv_nsec = 10000000;
@@ -1310,11 +1322,13 @@ main(int argc, char *argv[])
}
}
}
+#ifndef __rtems__
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &si_sa, 0);
sigaction(SIGALRM, &si_sa, 0);
+#endif /* __rtems__ */
summary();
if(packet != NULL)
@@ -1323,6 +1337,7 @@ main(int argc, char *argv[])
exit(nreceived == 0 ? 2 : 0);
}
+#ifndef __rtems__
static void
onsignal(int sig)
{
@@ -1339,6 +1354,7 @@ onsignal(int sig)
#endif
}
}
+#endif /* __rtems__ */
/*
* pinger --
diff --git a/freebsd/sys/arm64/include/machine/in_cksum.h b/freebsd/sys/arm64/include/machine/in_cksum.h
index d55b838b..522ba005 100644
--- a/freebsd/sys/arm64/include/machine/in_cksum.h
+++ b/freebsd/sys/arm64/include/machine/in_cksum.h
@@ -1,6 +1,4 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause
- *
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
@@ -31,7 +29,6 @@
* from tahoe: in_cksum.c 1.2 86/01/05
* from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
* from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
- * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg
* $FreeBSD$
*/
@@ -40,44 +37,16 @@
#include <sys/cdefs.h>
+#ifdef _KERNEL
#define in_cksum(m, len) in_cksum_skip(m, len, 0)
-
+u_short in_addword(u_short sum, u_short b);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+u_int do_cksum(const void *, int);
#if defined(IPVERSION) && (IPVERSION == 4)
-/*
- * It it useful to have an Internet checksum routine which is inlineable
- * and optimized specifically for the task of computing IP header checksums
- * in the normal case (where there are no options and the header length is
- * therefore always exactly five 32-bit words.
- */
-#ifdef __CC_SUPPORTS___INLINE
-
-static __inline void
-in_cksum_update(struct ip *ip)
-{
- int __tmpsum;
- __tmpsum = (int)ntohs(ip->ip_sum) + 256;
- ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
-}
-
-#else
-
-#define in_cksum_update(ip) \
- do { \
- int __tmpsum; \
- __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
- ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
- } while(0)
-
-#endif
+u_int in_cksum_hdr(const struct ip *);
#endif
-#ifdef _KERNEL
-#if defined(IPVERSION) && (IPVERSION == 4)
-u_int in_cksum_hdr(const struct ip *ip);
-#endif
-u_short in_addword(u_short sum, u_short b);
u_short in_pseudo(u_int sum, u_int b, u_int c);
-u_short in_cksum_skip(struct mbuf *m, int len, int skip);
-#endif
+#endif /* _KERNEL */
#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/freebsd/sys/dev/cadence/if_cgem.c b/freebsd/sys/dev/cadence/if_cgem.c
index 3eaaf6b2..c1c88e77 100644
--- a/freebsd/sys/dev/cadence/if_cgem.c
+++ b/freebsd/sys/dev/cadence/if_cgem.c
@@ -72,11 +72,9 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
#include <net/bpfdesc.h>
-#ifndef __rtems__
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#endif /* __rtems__ */
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
@@ -92,6 +90,7 @@ __FBSDID("$FreeBSD$");
#pragma GCC diagnostic ignored "-Wpointer-sign"
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
#include <rtems/bsd/bsd.h>
+#include <rtems/bsd/local/opt_platform.h>
#endif /* __rtems__ */
#define IF_CGEM_NAME "cgem"
@@ -111,13 +110,14 @@ __FBSDID("$FreeBSD$");
#define CGEM_CKSUM_ASSIST (CSUM_IP | CSUM_TCP | CSUM_UDP | \
CSUM_TCP_IPV6 | CSUM_UDP_IPV6)
-#ifndef __rtems__
static struct ofw_compat_data compat_data[] = {
{ "cadence,gem", 1 },
{ "cdns,macb", 1 },
+#ifdef __rtems__
+ { "cdns,gem", 1 },
+#endif
{ NULL, 0 },
};
-#endif /* __rtems__ */
struct cgem_softc {
if_t ifp;
@@ -133,7 +133,7 @@ struct cgem_softc {
uint32_t net_ctl_shadow;
#ifdef __rtems__
uint32_t net_cfg_shadow;
- int neednullqs;
+ int phy_contype;
#endif /* __rtems__ */
int ref_clk_num;
#ifndef __rtems__
@@ -457,9 +457,8 @@ cgem_setup_descs(struct cgem_softc *sc)
int desc_rings_size = CGEM_NUM_RX_DESCS * sizeof(struct cgem_rx_desc) +
CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc);
- if (sc->neednullqs)
- desc_rings_size += sizeof(struct cgem_rx_desc) +
- sizeof(struct cgem_tx_desc);
+ desc_rings_size += sizeof(struct cgem_rx_desc) +
+ sizeof(struct cgem_tx_desc);
#endif /* __rtems__ */
sc->txring = NULL;
sc->rxring = NULL;
@@ -608,13 +607,11 @@ cgem_setup_descs(struct cgem_softc *sc)
sc->txring_queued = 0;
#ifdef __rtems__
- if (sc->neednullqs) {
- sc->null_qs = (void *)(sc->txring + CGEM_NUM_TX_DESCS);
- sc->null_qs_physaddr = sc->txring_physaddr +
- CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc);
+ sc->null_qs = (void *)(sc->txring + CGEM_NUM_TX_DESCS);
+ sc->null_qs_physaddr = sc->txring_physaddr +
+ CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc);
- cgem_null_qs(sc);
- }
+ cgem_null_qs(sc);
#endif /* __rtems__ */
return (0);
@@ -1296,6 +1293,14 @@ cgem_config(struct cgem_softc *sc)
CGEM_NET_CFG_FULL_DUPLEX |
CGEM_NET_CFG_SPEED100;
+#ifdef __rtems__
+ /* Check connection type, enable SGMII bits if necessary. */
+ if ( sc->phy_contype == MII_CONTYPE_SGMII ) {
+ net_cfg |= CGEM_NET_CFG_SGMII_EN;
+ net_cfg |= CGEM_NET_CFG_PCS_SEL;
+ }
+#endif
+
/* Enable receive checksum offloading? */
if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0)
net_cfg |= CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN;
@@ -1947,13 +1952,20 @@ static int
cgem_probe(device_t dev)
{
-#ifndef __rtems__
+#ifdef __rtems__
+#ifdef FDT
+ if (bsp_fdt_get()) {
+#else
+ if (0) {
+#endif
+#endif /* __rtems__ */
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
-#else /* __rtems__ */
+#ifdef __rtems__
+ }
struct cgem_softc *sc = device_get_softc(dev);
int val, rid = 0;
@@ -1983,24 +1995,35 @@ cgem_attach(device_t dev)
{
struct cgem_softc *sc = device_get_softc(dev);
if_t ifp = NULL;
-#ifndef __rtems__
phandle_t node;
pcell_t cell;
-#endif /* __rtems__ */
int rid, err;
u_char eaddr[ETHER_ADDR_LEN];
sc->dev = dev;
CGEM_LOCK_INIT(sc);
-#ifndef __rtems__
+#ifdef __rtems__
+#ifdef FDT
+ if (bsp_fdt_get()) {
+#else
+ if (0) {
+#endif
+#endif /* __rtems__ */
/* Get reference clock number and base divider from fdt. */
node = ofw_bus_get_node(dev);
sc->ref_clk_num = 0;
if (OF_getprop(node, "ref-clock-num", &cell, sizeof(cell)) > 0)
sc->ref_clk_num = fdt32_to_cpu(cell);
-#else /* __rtems__ */
- sc->ref_clk_num = device_get_unit(dev);
+#ifdef __rtems__
+ /* Else for ref-clock-num OF_getprop */
+ else
+ sc->ref_clk_num = device_get_unit(dev);
+ sc->phy_contype = mii_fdt_get_contype(node);
+ } else {
+ sc->ref_clk_num = device_get_unit(dev);
+ sc->phy_contype = MII_CONTYPE_RGMII_ID;
+ }
#endif /* __rtems__ */
/* Get memory resource. */
@@ -2047,15 +2070,7 @@ cgem_attach(device_t dev)
sc->if_old_flags = if_getflags(ifp);
sc->rxbufs = DEFAULT_NUM_RX_BUFS;
-#if defined(CGEM64) && defined(__rtems__)
- uint32_t design_cfg6 = RD4(sc, CGEM_DESIGN_CFG6);
- /*
- * QEMU does not have PBUF_CUTTHRU defined and is broken when trying
- * to use nullqs
- */
- if ((design_cfg6 & CGEM_DESIGN_CFG6_PBUF_CUTTHRU))
- sc->neednullqs = 1;
-#else
+#if !defined(CGEM64) && defined(__rtems__)
sc->rxhangwar = 1;
#endif
@@ -2228,9 +2243,8 @@ static driver_t cgem_driver = {
sizeof(struct cgem_softc),
};
-#ifndef __rtems__
DRIVER_MODULE(cgem, simplebus, cgem_driver, cgem_devclass, NULL, NULL);
-#else /* __rtems__ */
+#ifdef __rtems__
DRIVER_MODULE(cgem, nexus, cgem_driver, cgem_devclass, NULL, NULL);
#endif /* __rtems__ */
DRIVER_MODULE(miibus, cgem, miibus_driver, miibus_devclass, NULL, NULL);
diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c
index 47c0f770..316e077c 100644
--- a/freebsd/sys/dev/ffec/if_ffec.c
+++ b/freebsd/sys/dev/ffec/if_ffec.c
@@ -139,9 +139,17 @@ static struct ofw_compat_data compat_data[] = {
/*
* Driver data and defines. The descriptor counts must be a power of two.
*/
+#ifndef __rtems__
#define RX_DESC_COUNT 512
+#else /* __rtems__ */
+#define RX_DESC_COUNT 64
+#endif /* __rtems__ */
#define RX_DESC_SIZE (sizeof(struct ffec_hwdesc) * RX_DESC_COUNT)
+#ifndef __rtems__
#define TX_DESC_COUNT 512
+#else /* __rtems__ */
+#define TX_DESC_COUNT 64
+#endif /* __rtems__ */
#define TX_DESC_SIZE (sizeof(struct ffec_hwdesc) * TX_DESC_COUNT)
#define TX_MAX_DMA_SEGS 8
@@ -201,6 +209,11 @@ struct ffec_softc {
int rx_ic_count; /* RW, valid values 0..255 */
int tx_ic_time;
int tx_ic_count;
+#ifdef __rtems__
+
+ device_t mdio_device;
+ struct mtx mdio_mtx;
+#endif /* __rtems__ */
};
static struct resource_spec irq_res_spec[MAX_IRQ_COUNT + 1] = {
@@ -368,6 +381,13 @@ ffec_miibus_readreg(device_t dev, int phy, int reg)
int val;
sc = device_get_softc(dev);
+#ifdef __rtems__
+ if (sc->mdio_device) {
+ return (MIIBUS_READREG(sc->mdio_device, phy, reg));
+ }
+
+ mtx_lock(&sc->mdio_mtx);
+#endif /* __rtems__ */
WR4(sc, FEC_IER_REG, FEC_IER_MII);
@@ -378,11 +398,17 @@ ffec_miibus_readreg(device_t dev, int phy, int reg)
if (!ffec_miibus_iowait(sc)) {
device_printf(dev, "timeout waiting for mii read\n");
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (-1); /* All-ones is a symptom of bad mdio. */
}
val = RD4(sc, FEC_MMFR_REG) & FEC_MMFR_DATA_MASK;
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (val);
}
@@ -392,6 +418,13 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val)
struct ffec_softc *sc;
sc = device_get_softc(dev);
+#ifdef __rtems__
+ if (sc->mdio_device) {
+ return (MIIBUS_WRITEREG(sc->mdio_device, phy, reg, val));
+ }
+
+ mtx_lock(&sc->mdio_mtx);
+#endif /* __rtems__ */
WR4(sc, FEC_IER_REG, FEC_IER_MII);
@@ -403,9 +436,15 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val)
if (!ffec_miibus_iowait(sc)) {
device_printf(dev, "timeout waiting for mii write\n");
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (-1);
}
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (0);
}
@@ -1569,6 +1608,9 @@ ffec_detach(device_t dev)
if (sc->mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+#ifdef __rtems__
+ mtx_destroy(&sc->mtx);
+#endif /* __rtems__ */
FFEC_LOCK_DESTROY(sc);
return (0);
}
@@ -1718,6 +1760,80 @@ ffec_set_txic(struct ffec_softc *sc)
ffec_set_ic(sc, FEC_TXIC0_REG, sc->tx_ic_count, sc->tx_ic_time);
}
+#ifdef __rtems__
+int
+ffec_get_phy_information(
+ struct ffec_softc *sc,
+ phandle_t node,
+ device_t dev,
+ int *phy_addr
+)
+{
+ phandle_t phy_node;
+ phandle_t parent_node;
+ pcell_t phy_handle, phy_reg;
+ device_t other;
+ phandle_t xref;
+
+ /* Search for the phy-handle and get the address */
+
+ if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
+ sizeof(phy_handle)) <= 0)
+ return (ENXIO);
+
+ phy_node = OF_node_from_xref(phy_handle);
+
+ if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
+ sizeof(phy_reg)) <= 0)
+ return (ENXIO);
+
+ *phy_addr = phy_reg;
+
+ /* Detect whether PHY handle is connected to this or another FFEC. */
+ parent_node = phy_node;
+
+ while (parent_node != 0) {
+ if (parent_node == node) {
+ /* PHY is directly connected. That's easy. */
+ sc->mdio_device = NULL;
+ return 0;
+ }
+
+ /*
+ * Check whether the node is also an Ethernet controller. Do
+ * that by just assuming that every Ethernet controller has a
+ * PHY attached to it.
+ */
+ if (OF_getencprop(parent_node, "phy-handle",
+ (void *)&phy_handle, sizeof(phy_handle)) > 0) {
+ /*
+ * Try to find the device of the other Ethernet
+ * controller and use that for MDIO communication.
+ * Note: This is not really a nice workaround but it
+ * works.
+ */
+ xref = OF_xref_from_node(parent_node);
+ if (xref == 0) {
+ device_printf(dev,
+ "Couldn't get device that handles PHY\n");
+ return (ENXIO);
+ }
+ other = OF_device_from_xref(xref);
+ if (other == 0) {
+ device_printf(dev,
+ "Couldn't get device that handles PHY\n");
+ return (ENXIO);
+ }
+ sc->mdio_device = other;
+ return 0;
+ }
+
+ parent_node = OF_parent(parent_node);
+ }
+ return (ENXIO);
+}
+
+#endif /* __rtems__ */
static int
ffec_attach(device_t dev)
{
@@ -1735,6 +1851,10 @@ ffec_attach(device_t dev)
sc->dev = dev;
FFEC_LOCK_INIT(sc);
+#ifdef __rtems__
+ mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+#endif /* __rtems__ */
/*
* There are differences in the implementation and features of the FEC
@@ -2039,9 +2159,17 @@ ffec_attach(device_t dev)
ffec_miigasket_setup(sc);
/* Attach the mii driver. */
+#ifdef __rtems__
+ OF_device_register_xref(OF_xref_from_node(ofw_node), dev);
+ if (ffec_get_phy_information(sc, ofw_node, dev, &phynum) != 0) {
+ phynum = MII_PHY_ANY;
+ }
+ (void) dummy;
+#else /* __rtems__ */
if (fdt_get_phyaddr(ofw_node, dev, &phynum, &dummy) != 0) {
phynum = MII_PHY_ANY;
}
+#endif /* __rtems__ */
error = mii_attach(dev, &sc->miibus, ifp, ffec_media_change,
ffec_media_status, BMSR_DEFCAPMASK, phynum, MII_OFFSET_ANY,
(sc->fecflags & FECTYPE_MVF) ? MIIF_FORCEANEG : 0);
diff --git a/freebsd/sys/dev/mii/tiphy.h b/freebsd/sys/dev/mii/tiphy.h
new file mode 100644
index 00000000..d3c35575
--- /dev/null
+++ b/freebsd/sys/dev/mii/tiphy.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+/*
+ * Texas Instruments DP83867IR/CR Robust, High Immunity
+ * 10/100/1000 Ethernet Physical Layer Transceiver.
+ */
+
+#ifndef _DEV_MII_TIPHY_H_
+#define _DEV_MII_TIPHY_H_
+
+#define DP83867_PHYCR 0x10 /* PHY Control Register */
+#define PHYCR_SGMII_EN (1 << 11)
+#define DP83867_CFG2 0x14 /* Configuration Register 2 */
+#define CFG2_SPEED_OPT_10M_EN (1 << 6) /* Speed Optimization */
+#define CFG2_SPEED_OPT_ENHANCED_EN (1 << 8)
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_S 10
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_M (0x3 << CFG2_SPEED_OPT_ATTEMPT_CNT_S)
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_1 (0 << CFG2_SPEED_OPT_ATTEMPT_CNT_S)
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_2 (1 << CFG2_SPEED_OPT_ATTEMPT_CNT_S)
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_4 (2 << CFG2_SPEED_OPT_ATTEMPT_CNT_S)
+#define CFG2_SPEED_OPT_ATTEMPT_CNT_8 (3 << CFG2_SPEED_OPT_ATTEMPT_CNT_S)
+#define CFG2_INTERRUPT_POLARITY (1 << 13) /* Int pin is active low. */
+#define DP83867_CFG4 0x31 /* Configuration Register 4 */
+
+#endif /* !_DEV_MII_TIPHY_H_ */
diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c
index bd45b419..1369f7f1 100644
--- a/freebsd/sys/dev/mmc/mmcsd.c
+++ b/freebsd/sys/dev/mmc/mmcsd.c
@@ -546,6 +546,11 @@ mmcsd_attach(device_t dev)
*/
rev = ext_csd[EXT_CSD_REV];
+/*
+ * Cache flush functions are currently not available. Use of on-device cache can
+ * cause data loss.
+ */
+#ifndef __rtems__
/*
* With revision 1.5 (MMC v4.5, EXT_CSD_REV == 6) and later, take
* advantage of the device R/W cache if present and useage is not
@@ -567,6 +572,7 @@ mmcsd_attach(device_t dev)
sc->flags |= MMCSD_FLUSH_CACHE;
}
}
+#endif
/*
* Ignore user-creatable enhanced user data area and general purpose
diff --git a/freebsd/sys/dev/ofw/ofwpci.c b/freebsd/sys/dev/ofw/ofwpci.c
new file mode 100644
index 00000000..c18de9d2
--- /dev/null
+++ b/freebsd/sys/dev/ofw/ofwpci.c
@@ -0,0 +1,693 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2011 Nathan Whitehorn
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofwpci.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/bus.h>
+#ifndef __rtems__
+#include <machine/md_var.h>
+#endif /* __rtems__ */
+#include <machine/resource.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <rtems/bsd/local/pcib_if.h>
+
+/*
+ * If it is necessary to set another value of this for
+ * some platforms it should be set at fdt.h file
+ */
+#ifndef PCI_MAP_INTR
+#define PCI_MAP_INTR 4
+#endif
+
+#define PCI_INTR_PINS 4
+
+/*
+ * bus interface.
+ */
+static struct resource * ofw_pci_alloc_resource(device_t, device_t,
+ int, int *, rman_res_t, rman_res_t, rman_res_t, u_int);
+static int ofw_pci_release_resource(device_t, device_t, int, int,
+ struct resource *);
+static int ofw_pci_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int ofw_pci_deactivate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int ofw_pci_adjust_resource(device_t, device_t, int,
+ struct resource *, rman_res_t, rman_res_t);
+
+#ifndef __rtems__
+#ifdef __powerpc__
+static bus_space_tag_t ofw_pci_bus_get_bus_tag(device_t, device_t);
+#endif
+#endif /* __rtems__ */
+
+/*
+ * pcib interface
+ */
+static int ofw_pci_maxslots(device_t);
+
+/*
+ * ofw_bus interface
+ */
+static phandle_t ofw_pci_get_node(device_t, device_t);
+
+/*
+ * local methods
+ */
+static int ofw_pci_fill_ranges(phandle_t, struct ofw_pci_range *);
+static struct rman *ofw_pci_get_rman(struct ofw_pci_softc *, int, u_int);
+
+/*
+ * Driver methods.
+ */
+static device_method_t ofw_pci_methods[] = {
+
+ /* Device interface */
+ DEVMETHOD(device_attach, ofw_pci_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar),
+ DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ofw_pci_release_resource),
+ DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource),
+#ifndef __rtems__
+#ifdef __powerpc__
+ DEVMETHOD(bus_get_bus_tag, ofw_pci_bus_get_bus_tag),
+#endif
+#endif /* __rtems__ */
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, ofw_pci_maxslots),
+ DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0);
+
+int
+ofw_pci_init(device_t dev)
+{
+ struct ofw_pci_softc *sc;
+ phandle_t node;
+ u_int32_t busrange[2];
+ struct ofw_pci_range *rp;
+ int i, error;
+ struct ofw_pci_cell_info *cell_info;
+
+ node = ofw_bus_get_node(dev);
+ sc = device_get_softc(dev);
+ sc->sc_initialized = 1;
+ sc->sc_range = NULL;
+ sc->sc_pci_domain = device_get_unit(dev);
+
+ cell_info = (struct ofw_pci_cell_info *)malloc(sizeof(*cell_info),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ sc->sc_cell_info = cell_info;
+
+ if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
+ busrange[0] = 0;
+
+ sc->sc_dev = dev;
+ sc->sc_node = node;
+ sc->sc_bus = busrange[0];
+
+ if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) {
+ phandle_t c;
+ int n, i;
+
+ sc->sc_nrange = 0;
+ for (c = OF_child(node); c != 0; c = OF_peer(c)) {
+ n = ofw_pci_nranges(c, cell_info);
+ if (n > 0)
+ sc->sc_nrange += n;
+ }
+ if (sc->sc_nrange == 0) {
+ error = ENXIO;
+ goto out;
+ }
+ sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
+ M_DEVBUF, M_WAITOK);
+ i = 0;
+ for (c = OF_child(node); c != 0; c = OF_peer(c)) {
+ n = ofw_pci_fill_ranges(c, &sc->sc_range[i]);
+ if (n > 0)
+ i += n;
+ }
+ KASSERT(i == sc->sc_nrange, ("range count mismatch"));
+ } else {
+ sc->sc_nrange = ofw_pci_nranges(node, cell_info);
+ if (sc->sc_nrange <= 0) {
+ device_printf(dev, "could not getranges\n");
+ error = ENXIO;
+ goto out;
+ }
+ sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
+ M_DEVBUF, M_WAITOK);
+ ofw_pci_fill_ranges(node, sc->sc_range);
+ }
+
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ sc->sc_io_rman.rm_descr = "PCI I/O Ports";
+ error = rman_init(&sc->sc_io_rman);
+ if (error != 0) {
+ device_printf(dev, "rman_init() failed. error = %d\n", error);
+ goto out;
+ }
+
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "PCI Non Prefetchable Memory";
+ error = rman_init(&sc->sc_mem_rman);
+ if (error != 0) {
+ device_printf(dev, "rman_init() failed. error = %d\n", error);
+ goto out;
+ }
+
+ sc->sc_pmem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_pmem_rman.rm_descr = "PCI Prefetchable Memory";
+ error = rman_init(&sc->sc_pmem_rman);
+ if (error != 0) {
+ device_printf(dev, "rman_init() failed. error = %d\n", error);
+ goto out;
+ }
+
+ for (i = 0; i < sc->sc_nrange; i++) {
+ error = 0;
+ rp = sc->sc_range + i;
+
+ if (sc->sc_range_mask & ((uint64_t)1 << i))
+ continue;
+ switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
+ case OFW_PCI_PHYS_HI_SPACE_CONFIG:
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_IO:
+ error = rman_manage_region(&sc->sc_io_rman, rp->pci,
+ rp->pci + rp->size - 1);
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_MEM32:
+ case OFW_PCI_PHYS_HI_SPACE_MEM64:
+ if (rp->pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) {
+ sc->sc_have_pmem = 1;
+ error = rman_manage_region(&sc->sc_pmem_rman,
+ rp->pci, rp->pci + rp->size - 1);
+ } else {
+ error = rman_manage_region(&sc->sc_mem_rman,
+ rp->pci, rp->pci + rp->size - 1);
+ }
+ break;
+ }
+
+ if (error != 0) {
+ device_printf(dev,
+ "rman_manage_region(%x, %#jx, %#jx) failed. "
+ "error = %d\n", rp->pci_hi &
+ OFW_PCI_PHYS_HI_SPACEMASK, rp->pci,
+ rp->pci + rp->size - 1, error);
+ goto out;
+ }
+ }
+
+ ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t));
+ return (0);
+
+out:
+ free(cell_info, M_DEVBUF);
+ free(sc->sc_range, M_DEVBUF);
+ rman_fini(&sc->sc_io_rman);
+ rman_fini(&sc->sc_mem_rman);
+ rman_fini(&sc->sc_pmem_rman);
+
+ return (error);
+}
+
+int
+ofw_pci_attach(device_t dev)
+{
+ struct ofw_pci_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ if (!sc->sc_initialized) {
+ error = ofw_pci_init(dev);
+ if (error != 0)
+ return (error);
+ }
+
+ device_add_child(dev, "pci", -1);
+ return (bus_generic_attach(dev));
+}
+
+static int
+ofw_pci_maxslots(device_t dev)
+{
+
+ return (PCI_SLOTMAX);
+}
+
+int
+ofw_pci_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct ofw_pci_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr[PCI_MAP_INTR];
+ int intrcells;
+ phandle_t iparent;
+
+ sc = device_get_softc(bus);
+ pintr = pin;
+
+ /* Fabricate imap information in case this isn't an OFW device */
+ bzero(&reg, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev),
+ &sc->sc_pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
+ mintr, sizeof(mintr), &iparent);
+ if (intrcells != 0) {
+ pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
+ return (pintr);
+ }
+
+ /*
+ * Maybe it's a real interrupt, not an intpin
+ */
+ if (pin > PCI_INTR_PINS)
+ return (pin);
+
+ device_printf(bus, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+int
+ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct ofw_pci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = sc->sc_pci_domain;
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_bus;
+ return (0);
+ default:
+ break;
+ }
+
+ return (ENOENT);
+}
+
+int
+ofw_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct ofw_pci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->sc_bus = value;
+ return (0);
+ default:
+ break;
+ }
+
+ return (ENOENT);
+}
+
+int
+ofw_pci_nranges(phandle_t node, struct ofw_pci_cell_info *info)
+{
+ ssize_t nbase_ranges;
+
+ if (info == NULL)
+ return (-1);
+
+ info->host_address_cells = 1;
+ info->size_cells = 2;
+ info->pci_address_cell = 3;
+
+ OF_getencprop(OF_parent(node), "#address-cells",
+ &(info->host_address_cells), sizeof(info->host_address_cells));
+ OF_getencprop(node, "#address-cells",
+ &(info->pci_address_cell), sizeof(info->pci_address_cell));
+ OF_getencprop(node, "#size-cells", &(info->size_cells),
+ sizeof(info->size_cells));
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges <= 0)
+ return (-1);
+
+ return (nbase_ranges / sizeof(cell_t) /
+ (info->pci_address_cell + info->host_address_cells +
+ info->size_cells));
+}
+
+static struct resource *
+ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct ofw_pci_softc *sc;
+ struct resource *rv;
+ struct rman *rm;
+ int needactivate;
+
+
+ needactivate = flags & RF_ACTIVE;
+ flags &= ~RF_ACTIVE;
+
+ sc = device_get_softc(bus);
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ if (type == PCI_RES_BUS) {
+ return (pci_domain_alloc_bus(sc->sc_pci_domain, child, rid,
+ start, end, count, flags | needactivate));
+ }
+#endif
+
+ rm = ofw_pci_get_rman(sc, type, flags);
+ if (rm == NULL) {
+ return (bus_generic_alloc_resource(bus, child, type, rid,
+ start, end, count, flags | needactivate));
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL) {
+ device_printf(bus, "failed to reserve resource for %s\n",
+ device_get_nameunit(child));
+ return (NULL);
+ }
+
+ rman_set_rid(rv, *rid);
+
+ if (needactivate) {
+ if (bus_activate_resource(child, type, *rid, rv) != 0) {
+ device_printf(bus,
+ "failed to activate resource for %s\n",
+ device_get_nameunit(child));
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+ofw_pci_release_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ struct ofw_pci_softc *sc;
+ struct rman *rm;
+ int error;
+
+ sc = device_get_softc(bus);
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ if (type == PCI_RES_BUS)
+ return (pci_domain_release_bus(sc->sc_pci_domain, child, rid,
+ res));
+#endif
+
+ rm = ofw_pci_get_rman(sc, type, rman_get_flags(res));
+ if (rm == NULL) {
+ return (bus_generic_release_resource(bus, child, type, rid,
+ res));
+ }
+ KASSERT(rman_is_region_manager(res, rm), ("rman mismatch"));
+
+ if (rman_get_flags(res) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type, rid, res);
+ if (error != 0)
+ return (error);
+ }
+ return (rman_release_resource(res));
+}
+
+static int
+ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ struct ofw_pci_softc *sc;
+ bus_space_handle_t handle;
+ bus_space_tag_t tag;
+ struct ofw_pci_range *rp;
+ vm_paddr_t start;
+ int space;
+ int rv;
+
+ sc = device_get_softc(bus);
+
+ if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) {
+ return (bus_generic_activate_resource(bus, child, type, rid,
+ res));
+ }
+
+ start = (vm_paddr_t)rman_get_start(res);
+
+ /*
+ * Map this through the ranges list
+ */
+ for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange &&
+ rp->pci_hi != 0; rp++) {
+ if (start < rp->pci || start >= rp->pci + rp->size)
+ continue;
+
+ switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
+ case OFW_PCI_PHYS_HI_SPACE_IO:
+ space = SYS_RES_IOPORT;
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_MEM32:
+ case OFW_PCI_PHYS_HI_SPACE_MEM64:
+ space = SYS_RES_MEMORY;
+ break;
+ default:
+ space = -1;
+ }
+
+ if (type == space) {
+ start += (rp->host - rp->pci);
+ break;
+ }
+ }
+
+ if (bootverbose)
+ printf("ofw_pci mapdev: start %jx, len %jd\n",
+ (rman_res_t)start, rman_get_size(res));
+
+#ifndef __rtems__
+ tag = BUS_GET_BUS_TAG(child, child);
+ if (tag == NULL)
+ return (ENOMEM);
+#else /* __rtems__ */
+ tag = 0;
+#endif /* __rtems__ */
+
+ rman_set_bustag(res, tag);
+ rv = bus_space_map(tag, start,
+ rman_get_size(res), 0, &handle);
+ if (rv != 0)
+ return (ENOMEM);
+
+ rman_set_bushandle(res, handle);
+ rman_set_virtual(res, (void *)handle); /* XXX for powerpc only ? */
+
+ return (rman_activate_resource(res));
+}
+
+#ifndef __rtems__
+#ifdef __powerpc__
+static bus_space_tag_t
+ofw_pci_bus_get_bus_tag(device_t bus, device_t child)
+{
+
+ return (&bs_le_tag);
+}
+#endif
+#endif /* __rtems__ */
+
+static int
+ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+#ifndef __rtems__
+ vm_size_t psize;
+#endif /* __rtems__ */
+
+ if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) {
+ return (bus_generic_deactivate_resource(bus, child, type, rid,
+ res));
+ }
+
+#ifndef __rtems__
+ psize = rman_get_size(res);
+ pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
+#endif /* __rtems__ */
+
+ return (rman_deactivate_resource(res));
+}
+
+static int
+ofw_pci_adjust_resource(device_t bus, device_t child, int type,
+ struct resource *res, rman_res_t start, rman_res_t end)
+{
+ struct rman *rm;
+ struct ofw_pci_softc *sc;
+
+ sc = device_get_softc(bus);
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ if (type == PCI_RES_BUS)
+ return (pci_domain_adjust_bus(sc->sc_pci_domain, child, res,
+ start, end));
+#endif
+
+ rm = ofw_pci_get_rman(sc, type, rman_get_flags(res));
+ if (rm == NULL) {
+ return (bus_generic_adjust_resource(bus, child, type, res,
+ start, end));
+ }
+ KASSERT(rman_is_region_manager(res, rm), ("rman mismatch"));
+ KASSERT(!(rman_get_flags(res) & RF_ACTIVE),
+ ("active resources cannot be adjusted"));
+
+ return (rman_adjust_resource(res, start, end));
+}
+
+static phandle_t
+ofw_pci_get_node(device_t bus, device_t dev)
+{
+ struct ofw_pci_softc *sc;
+
+ sc = device_get_softc(bus);
+ /* We only have one child, the PCI bus, which needs our own node. */
+
+ return (sc->sc_node);
+}
+
+static int
+ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges)
+{
+ int host_address_cells = 1, pci_address_cells = 3, size_cells = 2;
+ cell_t *base_ranges;
+ ssize_t nbase_ranges;
+ int nranges;
+ int i, j, k;
+
+ OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells,
+ sizeof(host_address_cells));
+ OF_getencprop(node, "#address-cells", &pci_address_cells,
+ sizeof(pci_address_cells));
+ OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells));
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges <= 0)
+ return (-1);
+ nranges = nbase_ranges / sizeof(cell_t) /
+ (pci_address_cells + host_address_cells + size_cells);
+
+ base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+ for (i = 0, j = 0; i < nranges; i++) {
+ ranges[i].pci_hi = base_ranges[j++];
+ ranges[i].pci = 0;
+ for (k = 0; k < pci_address_cells - 1; k++) {
+ ranges[i].pci <<= 32;
+ ranges[i].pci |= base_ranges[j++];
+ }
+ ranges[i].host = 0;
+ for (k = 0; k < host_address_cells; k++) {
+ ranges[i].host <<= 32;
+ ranges[i].host |= base_ranges[j++];
+ }
+ ranges[i].size = 0;
+ for (k = 0; k < size_cells; k++) {
+ ranges[i].size <<= 32;
+ ranges[i].size |= base_ranges[j++];
+ }
+ }
+
+ free(base_ranges, M_DEVBUF);
+ return (nranges);
+}
+
+static struct rman *
+ofw_pci_get_rman(struct ofw_pci_softc *sc, int type, u_int flags)
+{
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ return (&sc->sc_io_rman);
+ case SYS_RES_MEMORY:
+ if (sc->sc_have_pmem && (flags & RF_PREFETCHABLE))
+ return (&sc->sc_pmem_rman);
+ else
+ return (&sc->sc_mem_rman);
+ default:
+ break;
+ }
+
+ return (NULL);
+}
diff --git a/freebsd/sys/dev/ofw/ofwpci.h b/freebsd/sys/dev/ofw/ofwpci.h
new file mode 100644
index 00000000..3257fe68
--- /dev/null
+++ b/freebsd/sys/dev/ofw/ofwpci.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2011 Nathan Whitehorn
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _DEV_OFW_OFWPCI_H_
+#define _DEV_OFW_OFWPCI_H_
+
+/*
+ * Export class definition for inheritance purposes
+ */
+DECLARE_CLASS(ofw_pci_driver);
+
+struct ofw_pci_cell_info {
+ pcell_t host_address_cells;
+ pcell_t pci_address_cell;
+ pcell_t size_cells;
+ };
+
+struct ofw_pci_range {
+ uint32_t pci_hi;
+ uint64_t pci;
+ uint64_t host;
+ uint64_t size;
+};
+
+/*
+ * Quirks for some adapters
+ */
+enum {
+ OFW_PCI_QUIRK_RANGES_ON_CHILDREN = 1,
+};
+
+struct ofw_pci_softc {
+ device_t sc_dev;
+ phandle_t sc_node;
+ int sc_bus;
+ int sc_initialized;
+ int sc_quirks;
+ int sc_have_pmem;
+
+ struct ofw_pci_range *sc_range;
+ int sc_nrange;
+ uint64_t sc_range_mask;
+ struct ofw_pci_cell_info *sc_cell_info;
+
+ struct rman sc_io_rman;
+ struct rman sc_mem_rman;
+ struct rman sc_pmem_rman;
+ bus_space_tag_t sc_memt;
+ bus_dma_tag_t sc_dmat;
+ int sc_pci_domain;
+
+ struct ofw_bus_iinfo sc_pci_iinfo;
+};
+
+int ofw_pci_init(device_t);
+int ofw_pci_attach(device_t);
+int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *);
+int ofw_pci_write_ivar(device_t, device_t, int, uintptr_t);
+int ofw_pci_route_interrupt(device_t, device_t, int);
+int ofw_pci_nranges(phandle_t, struct ofw_pci_cell_info *);
+
+#endif /* _DEV_OFW_OFWPCI_H_ */
diff --git a/freebsd/sys/dev/pci/pci.c b/freebsd/sys/dev/pci/pci.c
index f1501208..5c76f1d5 100644
--- a/freebsd/sys/dev/pci/pci.c
+++ b/freebsd/sys/dev/pci/pci.c
@@ -233,7 +233,8 @@ static device_method_t pci_methods[] = {
DEFINE_CLASS_0(pci, pci_driver, pci_methods, sizeof(struct pci_softc));
static devclass_t pci_devclass;
-DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, NULL);
+EARLY_DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, NULL,
+ BUS_PASS_BUS);
MODULE_VERSION(pci, 1);
static char *pci_vendordata;
@@ -470,7 +471,6 @@ pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func)
return (NULL);
}
-#ifndef __rtems__
/* Find a device_t by vendor/device ID */
device_t
@@ -487,7 +487,6 @@ pci_find_device(uint16_t vendor, uint16_t device)
return (NULL);
}
-#endif /* __rtems__ */
device_t
pci_find_class(uint8_t class, uint8_t subclass)
@@ -3694,6 +3693,7 @@ pci_reserve_secbus(device_t bus, device_t dev, pcicfgregs *cfg,
}
break;
+#ifndef __rtems__
case 0x00dd10de:
/* Compaq R3000 BIOS sets wrong subordinate bus number. */
if ((cp = kern_getenv("smbios.planar.maker")) == NULL)
@@ -3715,6 +3715,7 @@ pci_reserve_secbus(device_t bus, device_t dev, pcicfgregs *cfg,
PCI_WRITE_CONFIG(bus, dev, sub_reg, sub_bus, 1);
}
break;
+#endif /* __rtems__ */
}
if (bootverbose)
diff --git a/freebsd/sys/dev/pci/pci_pci.c b/freebsd/sys/dev/pci/pci_pci.c
index 5ba3e9a0..c1e73a3c 100644
--- a/freebsd/sys/dev/pci/pci_pci.c
+++ b/freebsd/sys/dev/pci/pci_pci.c
@@ -136,7 +136,8 @@ static device_method_t pcib_methods[] = {
static devclass_t pcib_devclass;
DEFINE_CLASS_0(pcib, pcib_driver, pcib_methods, sizeof(struct pcib_softc));
-DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, NULL, NULL);
+EARLY_DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, NULL, NULL,
+ BUS_PASS_BUS);
#if defined(NEW_PCIB) || defined(PCI_HP)
SYSCTL_DECL(_hw_pci);
diff --git a/freebsd/sys/dev/pci/pci_subr.c b/freebsd/sys/dev/pci/pci_subr.c
new file mode 100644
index 00000000..1b0fac29
--- /dev/null
+++ b/freebsd/sys/dev/pci/pci_subr.c
@@ -0,0 +1,388 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Support APIs for Host to PCI bridge drivers and drivers that
+ * provide PCI domains.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
+
+/*
+ * Try to read the bus number of a host-PCI bridge using appropriate config
+ * registers.
+ */
+int
+host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func,
+ uint8_t *busnum)
+{
+ uint32_t id;
+
+ id = read_config(bus, slot, func, PCIR_DEVVENDOR, 4);
+ if (id == 0xffffffff)
+ return (0);
+
+ switch (id) {
+ case 0x12258086:
+ /* Intel 824?? */
+ /* XXX This is a guess */
+ /* *busnum = read_config(bus, slot, func, 0x41, 1); */
+ *busnum = bus;
+ break;
+ case 0x84c48086:
+ /* Intel 82454KX/GX (Orion) */
+ *busnum = read_config(bus, slot, func, 0x4a, 1);
+ break;
+ case 0x84ca8086:
+ /*
+ * For the 450nx chipset, there is a whole bundle of
+ * things pretending to be host bridges. The MIOC will
+ * be seen first and isn't really a pci bridge (the
+ * actual buses are attached to the PXB's). We need to
+ * read the registers of the MIOC to figure out the
+ * bus numbers for the PXB channels.
+ *
+ * Since the MIOC doesn't have a pci bus attached, we
+ * pretend it wasn't there.
+ */
+ return (0);
+ case 0x84cb8086:
+ switch (slot) {
+ case 0x12:
+ /* Intel 82454NX PXB#0, Bus#A */
+ *busnum = read_config(bus, 0x10, func, 0xd0, 1);
+ break;
+ case 0x13:
+ /* Intel 82454NX PXB#0, Bus#B */
+ *busnum = read_config(bus, 0x10, func, 0xd1, 1) + 1;
+ break;
+ case 0x14:
+ /* Intel 82454NX PXB#1, Bus#A */
+ *busnum = read_config(bus, 0x10, func, 0xd3, 1);
+ break;
+ case 0x15:
+ /* Intel 82454NX PXB#1, Bus#B */
+ *busnum = read_config(bus, 0x10, func, 0xd4, 1) + 1;
+ break;
+ }
+ break;
+
+ /* ServerWorks -- vendor 0x1166 */
+ case 0x00051166:
+ case 0x00061166:
+ case 0x00081166:
+ case 0x00091166:
+ case 0x00101166:
+ case 0x00111166:
+ case 0x00171166:
+ case 0x01011166:
+ case 0x010f1014:
+ case 0x01101166:
+ case 0x02011166:
+ case 0x02251166:
+ case 0x03021014:
+ *busnum = read_config(bus, slot, func, 0x44, 1);
+ break;
+
+ /* Compaq/HP -- vendor 0x0e11 */
+ case 0x60100e11:
+ *busnum = read_config(bus, slot, func, 0xc8, 1);
+ break;
+ default:
+ /* Don't know how to read bus number. */
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef NEW_PCIB
+/*
+ * Return a pointer to a pretty name for a PCI device. If the device
+ * has a driver attached, the device's name is used, otherwise a name
+ * is generated from the device's PCI address.
+ */
+const char *
+pcib_child_name(device_t child)
+{
+ static char buf[64];
+
+ if (device_get_nameunit(child) != NULL)
+ return (device_get_nameunit(child));
+ snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child),
+ pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
+ return (buf);
+}
+
+/*
+ * Some Host-PCI bridge drivers know which resource ranges they can
+ * decode and should only allocate subranges to child PCI devices.
+ * This API provides a way to manage this. The bridge driver should
+ * initialize this structure during attach and call
+ * pcib_host_res_decodes() on each resource range it decodes. It can
+ * then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper
+ * routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE(). This
+ * API assumes that resources for any decoded ranges can be safely
+ * allocated from the parent via bus_generic_alloc_resource().
+ */
+int
+pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr)
+{
+
+ hr->hr_pcib = pcib;
+ resource_list_init(&hr->hr_rl);
+ return (0);
+}
+
+int
+pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr)
+{
+
+ resource_list_free(&hr->hr_rl);
+ return (0);
+}
+
+int
+pcib_host_res_decodes(struct pcib_host_resources *hr, int type, rman_res_t start,
+ rman_res_t end, u_int flags)
+{
+ struct resource_list_entry *rle;
+ int rid;
+
+ if (bootverbose)
+ device_printf(hr->hr_pcib, "decoding %d %srange %#jx-%#jx\n",
+ type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start,
+ end);
+ rid = resource_list_add_next(&hr->hr_rl, type, start, end,
+ end - start + 1);
+ if (flags & RF_PREFETCHABLE) {
+ KASSERT(type == SYS_RES_MEMORY,
+ ("only memory is prefetchable"));
+ rle = resource_list_find(&hr->hr_rl, type, rid);
+ rle->flags = RLE_PREFETCH;
+ }
+ return (0);
+}
+
+struct resource *
+pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type,
+ int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct resource_list_entry *rle;
+ struct resource *r;
+ rman_res_t new_start, new_end;
+
+ if (flags & RF_PREFETCHABLE)
+ KASSERT(type == SYS_RES_MEMORY,
+ ("only memory is prefetchable"));
+
+ rle = resource_list_find(&hr->hr_rl, type, 0);
+ if (rle == NULL) {
+ /*
+ * No decoding ranges for this resource type, just pass
+ * the request up to the parent.
+ */
+ return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
+ start, end, count, flags));
+ }
+
+restart:
+ /* Try to allocate from each decoded range. */
+ for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
+ if (rle->type != type)
+ continue;
+ if (((flags & RF_PREFETCHABLE) != 0) !=
+ ((rle->flags & RLE_PREFETCH) != 0))
+ continue;
+ new_start = ummax(start, rle->start);
+ new_end = ummin(end, rle->end);
+ if (new_start > new_end ||
+ new_start + count - 1 > new_end ||
+ new_start + count < new_start)
+ continue;
+ r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
+ new_start, new_end, count, flags);
+ if (r != NULL) {
+ if (bootverbose)
+ device_printf(hr->hr_pcib,
+ "allocated type %d (%#jx-%#jx) for rid %x of %s\n",
+ type, rman_get_start(r), rman_get_end(r),
+ *rid, pcib_child_name(dev));
+ return (r);
+ }
+ }
+
+ /*
+ * If we failed to find a prefetch range for a memory
+ * resource, try again without prefetch.
+ */
+ if (flags & RF_PREFETCHABLE) {
+ flags &= ~RF_PREFETCHABLE;
+ rle = resource_list_find(&hr->hr_rl, type, 0);
+ goto restart;
+ }
+ return (NULL);
+}
+
+int
+pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type,
+ struct resource *r, rman_res_t start, rman_res_t end)
+{
+ struct resource_list_entry *rle;
+
+ rle = resource_list_find(&hr->hr_rl, type, 0);
+ if (rle == NULL) {
+ /*
+ * No decoding ranges for this resource type, just pass
+ * the request up to the parent.
+ */
+ return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r,
+ start, end));
+ }
+
+ /* Only allow adjustments that stay within a decoded range. */
+ for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
+ if (rle->start <= start && rle->end >= end)
+ return (bus_generic_adjust_resource(hr->hr_pcib, dev,
+ type, r, start, end));
+ }
+ return (ERANGE);
+}
+
+#ifdef PCI_RES_BUS
+struct pci_domain {
+ int pd_domain;
+ struct rman pd_bus_rman;
+ TAILQ_ENTRY(pci_domain) pd_link;
+};
+
+static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains);
+
+/*
+ * Each PCI domain maintains its own resource manager for PCI bus
+ * numbers in that domain. Domain objects are created on first use.
+ * Host to PCI bridge drivers and PCI-PCI bridge drivers should
+ * allocate their bus ranges from their domain.
+ */
+static struct pci_domain *
+pci_find_domain(int domain)
+{
+ struct pci_domain *d;
+ char buf[64];
+ int error;
+
+ TAILQ_FOREACH(d, &domains, pd_link) {
+ if (d->pd_domain == domain)
+ return (d);
+ }
+
+ snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain);
+ d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO);
+ d->pd_domain = domain;
+ d->pd_bus_rman.rm_start = 0;
+ d->pd_bus_rman.rm_end = PCI_BUSMAX;
+ d->pd_bus_rman.rm_type = RMAN_ARRAY;
+ strcpy((char *)(d + 1), buf);
+ d->pd_bus_rman.rm_descr = (char *)(d + 1);
+ error = rman_init(&d->pd_bus_rman);
+ if (error == 0)
+ error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX);
+ if (error)
+ panic("Failed to initialize PCI domain %d rman", domain);
+ TAILQ_INSERT_TAIL(&domains, d, pd_link);
+ return (d);
+}
+
+struct resource *
+pci_domain_alloc_bus(int domain, device_t dev, int *rid, rman_res_t start,
+ rman_res_t end, rman_res_t count, u_int flags)
+{
+ struct pci_domain *d;
+ struct resource *res;
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (NULL);
+ d = pci_find_domain(domain);
+ res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags,
+ dev);
+ if (res == NULL)
+ return (NULL);
+
+ rman_set_rid(res, *rid);
+ return (res);
+}
+
+int
+pci_domain_adjust_bus(int domain, device_t dev, struct resource *r,
+ rman_res_t start, rman_res_t end)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_adjust_resource(r, start, end));
+}
+
+int
+pci_domain_release_bus(int domain, device_t dev, int rid, struct resource *r)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_release_resource(r));
+}
+#endif /* PCI_RES_BUS */
+
+#endif /* NEW_PCIB */
diff --git a/freebsd/sys/dev/xdma/xdma.c b/freebsd/sys/dev/xdma/xdma.c
new file mode 100644
index 00000000..98426f07
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma.c
@@ -0,0 +1,501 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016-2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+#ifdef __rtems__
+#define IN_XDMA_C
+#endif /* __rtems__ */
+
+#include <dev/xdma/xdma.h>
+
+#include <rtems/bsd/local/xdma_if.h>
+
+/*
+ * Multiple xDMA controllers may work with single DMA device,
+ * so we have global lock for physical channel management.
+ */
+static struct mtx xdma_mtx;
+
+#define XDMA_LOCK() mtx_lock(&xdma_mtx)
+#define XDMA_UNLOCK() mtx_unlock(&xdma_mtx)
+#define XDMA_ASSERT_LOCKED() mtx_assert(&xdma_mtx, MA_OWNED)
+
+#define FDT_REG_CELLS 4
+
+/*
+ * Allocate virtual xDMA channel.
+ */
+xdma_channel_t *
+xdma_channel_alloc(xdma_controller_t *xdma, uint32_t caps)
+{
+ xdma_channel_t *xchan;
+ int ret;
+
+ xchan = malloc(sizeof(xdma_channel_t), M_XDMA, M_WAITOK | M_ZERO);
+ xchan->xdma = xdma;
+ xchan->caps = caps;
+
+ XDMA_LOCK();
+
+ /* Request a real channel from hardware driver. */
+ ret = XDMA_CHANNEL_ALLOC(xdma->dma_dev, xchan);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't request hardware channel.\n", __func__);
+ XDMA_UNLOCK();
+ free(xchan, M_XDMA);
+
+ return (NULL);
+ }
+
+ TAILQ_INIT(&xchan->ie_handlers);
+
+ mtx_init(&xchan->mtx_lock, "xDMA chan", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_qin_lock, "xDMA qin", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_qout_lock, "xDMA qout", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_bank_lock, "xDMA bank", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_proc_lock, "xDMA proc", NULL, MTX_DEF);
+
+ TAILQ_INIT(&xchan->bank);
+ TAILQ_INIT(&xchan->queue_in);
+ TAILQ_INIT(&xchan->queue_out);
+ TAILQ_INIT(&xchan->processing);
+
+ TAILQ_INSERT_TAIL(&xdma->channels, xchan, xchan_next);
+
+ XDMA_UNLOCK();
+
+ return (xchan);
+}
+
+int
+xdma_channel_free(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ int err;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ XDMA_LOCK();
+
+ /* Free the real DMA channel. */
+ err = XDMA_CHANNEL_FREE(xdma->dma_dev, xchan);
+ if (err != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't free real hw channel.\n", __func__);
+ XDMA_UNLOCK();
+ return (-1);
+ }
+
+ if (xchan->flags & XCHAN_TYPE_SG)
+ xdma_channel_free_sg(xchan);
+
+ xdma_teardown_all_intr(xchan);
+
+ mtx_destroy(&xchan->mtx_lock);
+ mtx_destroy(&xchan->mtx_qin_lock);
+ mtx_destroy(&xchan->mtx_qout_lock);
+ mtx_destroy(&xchan->mtx_bank_lock);
+ mtx_destroy(&xchan->mtx_proc_lock);
+
+ TAILQ_REMOVE(&xdma->channels, xchan, xchan_next);
+
+ free(xchan, M_XDMA);
+
+ XDMA_UNLOCK();
+
+ return (0);
+}
+
+int
+xdma_setup_intr(xdma_channel_t *xchan,
+ int (*cb)(void *, xdma_transfer_status_t *),
+ void *arg, void **ihandler)
+{
+ struct xdma_intr_handler *ih;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ /* Sanity check. */
+ if (cb == NULL) {
+ device_printf(xdma->dev,
+ "%s: Can't setup interrupt handler.\n",
+ __func__);
+
+ return (-1);
+ }
+
+ ih = malloc(sizeof(struct xdma_intr_handler),
+ M_XDMA, M_WAITOK | M_ZERO);
+ ih->cb = cb;
+ ih->cb_user = arg;
+
+ XCHAN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->ie_handlers, ih, ih_next);
+ XCHAN_UNLOCK(xchan);
+
+ if (ihandler != NULL)
+ *ihandler = ih;
+
+ return (0);
+}
+
+int
+xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih)
+{
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ /* Sanity check. */
+ if (ih == NULL) {
+ device_printf(xdma->dev,
+ "%s: Can't teardown interrupt.\n", __func__);
+ return (-1);
+ }
+
+ TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
+ free(ih, M_XDMA);
+
+ return (0);
+}
+
+int
+xdma_teardown_all_intr(xdma_channel_t *xchan)
+{
+ struct xdma_intr_handler *ih_tmp;
+ struct xdma_intr_handler *ih;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
+ TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
+ free(ih, M_XDMA);
+ }
+
+ return (0);
+}
+
+int
+xdma_request(xdma_channel_t *xchan, struct xdma_request *req)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ XCHAN_LOCK(xchan);
+ ret = XDMA_CHANNEL_REQUEST(xdma->dma_dev, xchan, req);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't request a transfer.\n", __func__);
+ XCHAN_UNLOCK(xchan);
+
+ return (-1);
+ }
+ XCHAN_UNLOCK(xchan);
+
+ return (0);
+}
+
+int
+xdma_control(xdma_channel_t *xchan, enum xdma_command cmd)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, cmd);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't process command.\n", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+xdma_callback(xdma_channel_t *xchan, xdma_transfer_status_t *status)
+{
+ struct xdma_intr_handler *ih_tmp;
+ struct xdma_intr_handler *ih;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp)
+ if (ih->cb != NULL)
+ ih->cb(ih->cb_user, status);
+
+ if (xchan->flags & XCHAN_TYPE_SG)
+ xdma_queue_submit(xchan);
+}
+
+#ifdef FDT
+/*
+ * Notify the DMA driver we have machine-dependent data in FDT.
+ */
+static int
+xdma_ofw_md_data(xdma_controller_t *xdma, pcell_t *cells, int ncells)
+{
+ uint32_t ret;
+
+ ret = XDMA_OFW_MD_DATA(xdma->dma_dev,
+ cells, ncells, (void **)&xdma->data);
+
+ return (ret);
+}
+
+static int
+xdma_handle_mem_node(vmem_t *vmem, phandle_t memory)
+{
+ pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
+ pcell_t *regp;
+ int addr_cells, size_cells;
+ int i, reg_len, ret, tuple_size, tuples;
+ u_long mem_start, mem_size;
+
+ if ((ret = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
+ &size_cells)) != 0)
+ return (ret);
+
+ if (addr_cells > 2)
+ return (ERANGE);
+
+ tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
+ reg_len = OF_getproplen(memory, "reg");
+ if (reg_len <= 0 || reg_len > sizeof(reg))
+ return (ERANGE);
+
+ if (OF_getprop(memory, "reg", reg, reg_len) <= 0)
+ return (ENXIO);
+
+ tuples = reg_len / tuple_size;
+ regp = (pcell_t *)&reg;
+ for (i = 0; i < tuples; i++) {
+ ret = fdt_data_to_res(regp, addr_cells, size_cells,
+ &mem_start, &mem_size);
+ if (ret != 0)
+ return (ret);
+
+ vmem_add(vmem, mem_start, mem_size, 0);
+ regp += addr_cells + size_cells;
+ }
+
+ return (0);
+}
+
+vmem_t *
+xdma_get_memory(device_t dev)
+{
+ phandle_t mem_node, node;
+ pcell_t mem_handle;
+ vmem_t *vmem;
+
+ node = ofw_bus_get_node(dev);
+ if (node <= 0) {
+ device_printf(dev,
+ "%s called on not ofw based device.\n", __func__);
+ return (NULL);
+ }
+
+ if (!OF_hasprop(node, "memory-region"))
+ return (NULL);
+
+ if (OF_getencprop(node, "memory-region", (void *)&mem_handle,
+ sizeof(mem_handle)) <= 0)
+ return (NULL);
+
+ vmem = vmem_create("xDMA vmem", 0, 0, PAGE_SIZE,
+ PAGE_SIZE, M_BESTFIT | M_WAITOK);
+ if (vmem == NULL)
+ return (NULL);
+
+ mem_node = OF_node_from_xref(mem_handle);
+ if (xdma_handle_mem_node(vmem, mem_node) != 0) {
+ vmem_destroy(vmem);
+ return (NULL);
+ }
+
+ return (vmem);
+}
+
+void
+xdma_put_memory(vmem_t *vmem)
+{
+
+ vmem_destroy(vmem);
+}
+
+void
+xchan_set_memory(xdma_channel_t *xchan, vmem_t *vmem)
+{
+
+ xchan->vmem = vmem;
+}
+
+/*
+ * Allocate xdma controller.
+ */
+xdma_controller_t *
+xdma_ofw_get(device_t dev, const char *prop)
+{
+ phandle_t node, parent;
+ xdma_controller_t *xdma;
+ device_t dma_dev;
+ pcell_t *cells;
+ int ncells;
+ int error;
+ int ndmas;
+ int idx;
+
+ node = ofw_bus_get_node(dev);
+ if (node <= 0)
+ device_printf(dev,
+ "%s called on not ofw based device.\n", __func__);
+
+ error = ofw_bus_parse_xref_list_get_length(node,
+ "dmas", "#dma-cells", &ndmas);
+ if (error) {
+ device_printf(dev,
+ "%s can't get dmas list.\n", __func__);
+ return (NULL);
+ }
+
+ if (ndmas == 0) {
+ device_printf(dev,
+ "%s dmas list is empty.\n", __func__);
+ return (NULL);
+ }
+
+ error = ofw_bus_find_string_index(node, "dma-names", prop, &idx);
+ if (error != 0) {
+ device_printf(dev,
+ "%s can't find string index.\n", __func__);
+ return (NULL);
+ }
+
+ error = ofw_bus_parse_xref_list_alloc(node, "dmas", "#dma-cells",
+ idx, &parent, &ncells, &cells);
+ if (error != 0) {
+ device_printf(dev,
+ "%s can't get dma device xref.\n", __func__);
+ return (NULL);
+ }
+
+ dma_dev = OF_device_from_xref(parent);
+ if (dma_dev == NULL) {
+ device_printf(dev,
+ "%s can't get dma device.\n", __func__);
+ return (NULL);
+ }
+
+ xdma = malloc(sizeof(struct xdma_controller),
+ M_XDMA, M_WAITOK | M_ZERO);
+ xdma->dev = dev;
+ xdma->dma_dev = dma_dev;
+
+ TAILQ_INIT(&xdma->channels);
+
+ xdma_ofw_md_data(xdma, cells, ncells);
+ free(cells, M_OFWPROP);
+
+ return (xdma);
+}
+#endif
+
+/*
+ * Free xDMA controller object.
+ */
+int
+xdma_put(xdma_controller_t *xdma)
+{
+
+ XDMA_LOCK();
+
+ /* Ensure no channels allocated. */
+ if (!TAILQ_EMPTY(&xdma->channels)) {
+ device_printf(xdma->dev, "%s: Can't free xDMA\n", __func__);
+ return (-1);
+ }
+
+ free(xdma->data, M_DEVBUF);
+ free(xdma, M_XDMA);
+
+ XDMA_UNLOCK();
+
+ return (0);
+}
+
+static void
+xdma_init(void)
+{
+
+ mtx_init(&xdma_mtx, "xDMA", NULL, MTX_DEF);
+}
+
+SYSINIT(xdma, SI_SUB_DRIVERS, SI_ORDER_FIRST, xdma_init, NULL);
diff --git a/freebsd/sys/dev/xdma/xdma.h b/freebsd/sys/dev/xdma/xdma.h
new file mode 100644
index 00000000..685047ab
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma.h
@@ -0,0 +1,285 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016-2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _DEV_XDMA_XDMA_H_
+#define _DEV_XDMA_XDMA_H_
+
+#include <sys/proc.h>
+#include <sys/vmem.h>
+#ifdef __rtems__
+#include <dev/ofw/openfirm.h>
+#endif /* __rtems__ */
+
+enum xdma_direction {
+ XDMA_MEM_TO_MEM,
+ XDMA_MEM_TO_DEV,
+ XDMA_DEV_TO_MEM,
+ XDMA_DEV_TO_DEV,
+};
+
+enum xdma_operation_type {
+ XDMA_MEMCPY,
+ XDMA_CYCLIC,
+ XDMA_FIFO,
+ XDMA_SG,
+};
+
+enum xdma_request_type {
+ XR_TYPE_PHYS,
+ XR_TYPE_VIRT,
+ XR_TYPE_MBUF,
+ XR_TYPE_BIO,
+};
+
+enum xdma_command {
+ XDMA_CMD_BEGIN,
+ XDMA_CMD_PAUSE,
+ XDMA_CMD_TERMINATE,
+};
+
+struct xdma_transfer_status {
+ uint32_t transferred;
+ int error;
+};
+
+typedef struct xdma_transfer_status xdma_transfer_status_t;
+
+struct xdma_controller {
+ device_t dev; /* DMA consumer device_t. */
+ device_t dma_dev; /* A real DMA device_t. */
+ void *data; /* OFW MD part. */
+ vmem_t *vmem; /* Bounce memory. */
+
+ /* List of virtual channels allocated. */
+ TAILQ_HEAD(xdma_channel_list, xdma_channel) channels;
+};
+
+typedef struct xdma_controller xdma_controller_t;
+
+struct xchan_buf {
+ bus_dmamap_t map;
+ uint32_t nsegs;
+ uint32_t nsegs_left;
+ vm_offset_t vaddr;
+ vm_offset_t paddr;
+ vm_size_t size;
+};
+
+struct xdma_request {
+ struct mbuf *m;
+ struct bio *bp;
+ enum xdma_operation_type operation;
+ enum xdma_request_type req_type;
+ enum xdma_direction direction;
+ bus_addr_t src_addr;
+ bus_addr_t dst_addr;
+ uint8_t src_width;
+ uint8_t dst_width;
+ bus_size_t block_num;
+ bus_size_t block_len;
+ xdma_transfer_status_t status;
+ void *user;
+ TAILQ_ENTRY(xdma_request) xr_next;
+ struct xchan_buf buf;
+};
+
+struct xdma_sglist {
+ bus_addr_t src_addr;
+ bus_addr_t dst_addr;
+ size_t len;
+ uint8_t src_width;
+ uint8_t dst_width;
+ enum xdma_direction direction;
+ bool first;
+ bool last;
+};
+
+struct xdma_channel {
+ xdma_controller_t *xdma;
+ vmem_t *vmem;
+
+ uint32_t flags;
+#define XCHAN_BUFS_ALLOCATED (1 << 0)
+#define XCHAN_SGLIST_ALLOCATED (1 << 1)
+#define XCHAN_CONFIGURED (1 << 2)
+#define XCHAN_TYPE_CYCLIC (1 << 3)
+#define XCHAN_TYPE_MEMCPY (1 << 4)
+#define XCHAN_TYPE_FIFO (1 << 5)
+#define XCHAN_TYPE_SG (1 << 6)
+
+ uint32_t caps;
+#define XCHAN_CAP_BUSDMA (1 << 0)
+#define XCHAN_CAP_NOSEG (1 << 1)
+#define XCHAN_CAP_NOBUFS (1 << 2)
+
+ /* A real hardware driver channel. */
+ void *chan;
+
+ /* Interrupt handlers. */
+ TAILQ_HEAD(, xdma_intr_handler) ie_handlers;
+ TAILQ_ENTRY(xdma_channel) xchan_next;
+
+ struct mtx mtx_lock;
+ struct mtx mtx_qin_lock;
+ struct mtx mtx_qout_lock;
+ struct mtx mtx_bank_lock;
+ struct mtx mtx_proc_lock;
+
+ /* Request queue. */
+ bus_dma_tag_t dma_tag_bufs;
+ struct xdma_request *xr_mem;
+ uint32_t xr_num;
+
+ /* Bus dma tag options. */
+ bus_size_t maxsegsize;
+ bus_size_t maxnsegs;
+ bus_size_t alignment;
+ bus_addr_t boundary;
+ bus_addr_t lowaddr;
+ bus_addr_t highaddr;
+
+ struct xdma_sglist *sg;
+
+ TAILQ_HEAD(, xdma_request) bank;
+ TAILQ_HEAD(, xdma_request) queue_in;
+ TAILQ_HEAD(, xdma_request) queue_out;
+ TAILQ_HEAD(, xdma_request) processing;
+};
+
+typedef struct xdma_channel xdma_channel_t;
+
+struct xdma_intr_handler {
+ int (*cb)(void *cb_user, xdma_transfer_status_t *status);
+ void *cb_user;
+ TAILQ_ENTRY(xdma_intr_handler) ih_next;
+};
+
+#ifndef __rtems__
+static MALLOC_DEFINE(M_XDMA, "xdma", "xDMA framework");
+#else /* __rtems__ */
+#ifdef IN_XDMA_C
+MALLOC_DEFINE(M_XDMA, "xdma", "xDMA framework");
+#else
+MALLOC_DECLARE(M_XDMA);
+#endif
+#endif /* __rtems__ */
+
+#define XCHAN_LOCK(xchan) mtx_lock(&(xchan)->mtx_lock)
+#define XCHAN_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_lock)
+#define XCHAN_ASSERT_LOCKED(xchan) \
+ mtx_assert(&(xchan)->mtx_lock, MA_OWNED)
+
+#define QUEUE_IN_LOCK(xchan) mtx_lock(&(xchan)->mtx_qin_lock)
+#define QUEUE_IN_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_qin_lock)
+#define QUEUE_IN_ASSERT_LOCKED(xchan) \
+ mtx_assert(&(xchan)->mtx_qin_lock, MA_OWNED)
+
+#define QUEUE_OUT_LOCK(xchan) mtx_lock(&(xchan)->mtx_qout_lock)
+#define QUEUE_OUT_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_qout_lock)
+#define QUEUE_OUT_ASSERT_LOCKED(xchan) \
+ mtx_assert(&(xchan)->mtx_qout_lock, MA_OWNED)
+
+#define QUEUE_BANK_LOCK(xchan) mtx_lock(&(xchan)->mtx_bank_lock)
+#define QUEUE_BANK_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_bank_lock)
+#define QUEUE_BANK_ASSERT_LOCKED(xchan) \
+ mtx_assert(&(xchan)->mtx_bank_lock, MA_OWNED)
+
+#define QUEUE_PROC_LOCK(xchan) mtx_lock(&(xchan)->mtx_proc_lock)
+#define QUEUE_PROC_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_proc_lock)
+#define QUEUE_PROC_ASSERT_LOCKED(xchan) \
+ mtx_assert(&(xchan)->mtx_proc_lock, MA_OWNED)
+
+#define XDMA_SGLIST_MAXLEN 2048
+#define XDMA_MAX_SEG 128
+
+/* xDMA controller ops */
+xdma_controller_t *xdma_ofw_get(device_t dev, const char *prop);
+int xdma_put(xdma_controller_t *xdma);
+vmem_t * xdma_get_memory(device_t dev);
+void xdma_put_memory(vmem_t *vmem);
+
+/* xDMA channel ops */
+xdma_channel_t * xdma_channel_alloc(xdma_controller_t *, uint32_t caps);
+int xdma_channel_free(xdma_channel_t *);
+int xdma_request(xdma_channel_t *xchan, struct xdma_request *r);
+void xchan_set_memory(xdma_channel_t *xchan, vmem_t *vmem);
+
+/* SG interface */
+int xdma_prep_sg(xdma_channel_t *, uint32_t,
+ bus_size_t, bus_size_t, bus_size_t, bus_addr_t, bus_addr_t, bus_addr_t);
+void xdma_channel_free_sg(xdma_channel_t *xchan);
+int xdma_queue_submit_sg(xdma_channel_t *xchan);
+void xchan_seg_done(xdma_channel_t *xchan, xdma_transfer_status_t *);
+
+/* Queue operations */
+int xdma_dequeue_mbuf(xdma_channel_t *xchan, struct mbuf **m,
+ xdma_transfer_status_t *);
+int xdma_enqueue_mbuf(xdma_channel_t *xchan, struct mbuf **m, uintptr_t addr,
+ uint8_t, uint8_t, enum xdma_direction dir);
+int xdma_dequeue_bio(xdma_channel_t *xchan, struct bio **bp,
+ xdma_transfer_status_t *status);
+int xdma_enqueue_bio(xdma_channel_t *xchan, struct bio **bp, bus_addr_t addr,
+ uint8_t, uint8_t, enum xdma_direction dir);
+int xdma_dequeue(xdma_channel_t *xchan, void **user,
+ xdma_transfer_status_t *status);
+int xdma_enqueue(xdma_channel_t *xchan, uintptr_t src, uintptr_t dst,
+ uint8_t, uint8_t, bus_size_t, enum xdma_direction dir, void *);
+int xdma_queue_submit(xdma_channel_t *xchan);
+
+/* Mbuf operations */
+uint32_t xdma_mbuf_defrag(xdma_channel_t *xchan, struct xdma_request *xr);
+uint32_t xdma_mbuf_chain_count(struct mbuf *m0);
+
+/* Channel Control */
+int xdma_control(xdma_channel_t *xchan, enum xdma_command cmd);
+
+/* Interrupt callback */
+int xdma_setup_intr(xdma_channel_t *xchan, int (*cb)(void *,
+ xdma_transfer_status_t *), void *arg, void **);
+int xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih);
+int xdma_teardown_all_intr(xdma_channel_t *xchan);
+void xdma_callback(struct xdma_channel *xchan, xdma_transfer_status_t *status);
+
+/* Sglist */
+int xchan_sglist_alloc(xdma_channel_t *xchan);
+void xchan_sglist_free(xdma_channel_t *xchan);
+int xdma_sglist_add(struct xdma_sglist *sg, struct bus_dma_segment *seg,
+ uint32_t nsegs, struct xdma_request *xr);
+
+/* Requests bank */
+void xchan_bank_init(xdma_channel_t *xchan);
+int xchan_bank_free(xdma_channel_t *xchan);
+struct xdma_request * xchan_bank_get(xdma_channel_t *xchan);
+int xchan_bank_put(xdma_channel_t *xchan, struct xdma_request *xr);
+
+#endif /* !_DEV_XDMA_XDMA_H_ */
diff --git a/freebsd/sys/dev/xdma/xdma_bank.c b/freebsd/sys/dev/xdma/xdma_bank.c
new file mode 100644
index 00000000..96ddef97
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma_bank.c
@@ -0,0 +1,100 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2018-2019 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/xdma/xdma.h>
+
+void
+xchan_bank_init(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+ int i;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ xchan->xr_mem = malloc(sizeof(struct xdma_request) * xchan->xr_num,
+ M_XDMA, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ TAILQ_INSERT_TAIL(&xchan->bank, xr, xr_next);
+ }
+}
+
+int
+xchan_bank_free(xdma_channel_t *xchan)
+{
+
+ free(xchan->xr_mem, M_XDMA);
+
+ return (0);
+}
+
+struct xdma_request *
+xchan_bank_get(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+
+ QUEUE_BANK_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->bank, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->bank, xr, xr_next);
+ break;
+ }
+ QUEUE_BANK_UNLOCK(xchan);
+
+ return (xr);
+}
+
+int
+xchan_bank_put(xdma_channel_t *xchan, struct xdma_request *xr)
+{
+
+ QUEUE_BANK_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->bank, xr, xr_next);
+ QUEUE_BANK_UNLOCK(xchan);
+
+ return (0);
+}
diff --git a/freebsd/sys/dev/xdma/xdma_mbuf.c b/freebsd/sys/dev/xdma/xdma_mbuf.c
new file mode 100644
index 00000000..cbd32984
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma_mbuf.c
@@ -0,0 +1,151 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2017-2019 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+
+#include <machine/bus.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/xdma/xdma.h>
+
+int
+xdma_dequeue_mbuf(xdma_channel_t *xchan, struct mbuf **mp,
+ xdma_transfer_status_t *status)
+{
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_out, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->queue_out, xr, xr_next);
+ break;
+ }
+ QUEUE_OUT_UNLOCK(xchan);
+
+ if (xr == NULL)
+ return (-1);
+
+ *mp = xr->m;
+ status->error = xr->status.error;
+ status->transferred = xr->status.transferred;
+
+ xchan_bank_put(xchan, xr);
+
+ return (0);
+}
+
+int
+xdma_enqueue_mbuf(xdma_channel_t *xchan, struct mbuf **mp,
+ uintptr_t addr, uint8_t src_width, uint8_t dst_width,
+ enum xdma_direction dir)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+
+ xr = xchan_bank_get(xchan);
+ if (xr == NULL)
+ return (-1); /* No space is available yet. */
+
+ xr->direction = dir;
+ xr->m = *mp;
+ xr->req_type = XR_TYPE_MBUF;
+ if (dir == XDMA_MEM_TO_DEV) {
+ xr->dst_addr = addr;
+ xr->src_addr = 0;
+ } else {
+ xr->src_addr = addr;
+ xr->dst_addr = 0;
+ }
+ xr->src_width = src_width;
+ xr->dst_width = dst_width;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ return (0);
+}
+
+uint32_t
+xdma_mbuf_chain_count(struct mbuf *m0)
+{
+ struct mbuf *m;
+ uint32_t c;
+
+ c = 0;
+
+ for (m = m0; m != NULL; m = m->m_next)
+ c++;
+
+ return (c);
+}
+
+uint32_t
+xdma_mbuf_defrag(xdma_channel_t *xchan, struct xdma_request *xr)
+{
+ xdma_controller_t *xdma;
+ struct mbuf *m;
+ uint32_t c;
+
+ xdma = xchan->xdma;
+
+ c = xdma_mbuf_chain_count(xr->m);
+ if (c == 1)
+ return (c); /* Nothing to do. */
+
+ if ((m = m_defrag(xr->m, M_NOWAIT)) == NULL) {
+ device_printf(xdma->dma_dev,
+ "%s: Can't defrag mbuf\n",
+ __func__);
+ return (c);
+ }
+
+ xr->m = m;
+ c = 1;
+
+ return (c);
+}
diff --git a/freebsd/sys/dev/xdma/xdma_queue.c b/freebsd/sys/dev/xdma/xdma_queue.c
new file mode 100644
index 00000000..22d7e2e2
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma_queue.c
@@ -0,0 +1,126 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2018-2019 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/xdma/xdma.h>
+
+int
+xdma_dequeue(xdma_channel_t *xchan, void **user,
+ xdma_transfer_status_t *status)
+{
+ struct xdma_request *xr_tmp;
+ struct xdma_request *xr;
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_out, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->queue_out, xr, xr_next);
+ break;
+ }
+ QUEUE_OUT_UNLOCK(xchan);
+
+ if (xr == NULL)
+ return (-1);
+
+ *user = xr->user;
+ status->error = xr->status.error;
+ status->transferred = xr->status.transferred;
+
+ xchan_bank_put(xchan, xr);
+
+ return (0);
+}
+
+int
+xdma_enqueue(xdma_channel_t *xchan, uintptr_t src, uintptr_t dst,
+ uint8_t src_width, uint8_t dst_width, bus_size_t len,
+ enum xdma_direction dir, void *user)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ xr = xchan_bank_get(xchan);
+ if (xr == NULL)
+ return (-1); /* No space is available. */
+
+ xr->user = user;
+ xr->direction = dir;
+ xr->m = NULL;
+ xr->bp = NULL;
+ xr->block_num = 1;
+ xr->block_len = len;
+ xr->req_type = XR_TYPE_VIRT;
+ xr->src_addr = src;
+ xr->dst_addr = dst;
+ xr->src_width = src_width;
+ xr->dst_width = dst_width;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ return (0);
+}
+
+int
+xdma_queue_submit(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ ret = 0;
+
+ XCHAN_LOCK(xchan);
+
+ if (xchan->flags & XCHAN_TYPE_SG)
+ ret = xdma_queue_submit_sg(xchan);
+
+ XCHAN_UNLOCK(xchan);
+
+ return (ret);
+}
diff --git a/freebsd/sys/dev/xdma/xdma_sg.c b/freebsd/sys/dev/xdma/xdma_sg.c
new file mode 100644
index 00000000..f0e5f187
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma_sg.c
@@ -0,0 +1,661 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_page.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/xdma/xdma.h>
+
+#include <rtems/bsd/local/xdma_if.h>
+
+struct seg_load_request {
+ struct bus_dma_segment *seg;
+ uint32_t nsegs;
+ uint32_t error;
+};
+
+static void
+xchan_bufs_free_reserved(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ vm_size_t size;
+ int i;
+
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ size = xr->buf.size;
+#ifndef __rtems__
+ if (xr->buf.vaddr) {
+ pmap_kremove_device(xr->buf.vaddr, size);
+ kva_free(xr->buf.vaddr, size);
+ xr->buf.vaddr = 0;
+ }
+#endif /* __rtems__ */
+ if (xr->buf.paddr) {
+ vmem_free(xchan->vmem, xr->buf.paddr, size);
+ xr->buf.paddr = 0;
+ }
+ xr->buf.size = 0;
+ }
+}
+
+static int
+xchan_bufs_alloc_reserved(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ struct xdma_request *xr;
+ vmem_addr_t addr;
+ vm_size_t size;
+ int i;
+
+ xdma = xchan->xdma;
+
+#ifndef __rtems__
+ if (xchan->vmem == NULL)
+ return (ENOBUFS);
+#endif /* __rtems__ */
+
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ size = round_page(xchan->maxsegsize);
+#ifndef __rtems__
+ if (vmem_alloc(xchan->vmem, size,
+ M_BESTFIT | M_NOWAIT, &addr)) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate memory\n", __func__);
+ xchan_bufs_free_reserved(xchan);
+ return (ENOMEM);
+ }
+
+ xr->buf.size = size;
+ xr->buf.paddr = addr;
+ xr->buf.vaddr = kva_alloc(size);
+#else /* __rtems__ */
+ xr->buf.vaddr = calloc(1,size);
+ xr->buf.paddr = xr->buf.vaddr;
+#endif /* __rtems__ */
+ if (xr->buf.vaddr == 0) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate KVA\n", __func__);
+ xchan_bufs_free_reserved(xchan);
+ return (ENOMEM);
+ }
+#ifndef __rtems__
+ pmap_kenter_device(xr->buf.vaddr, size, addr);
+#endif /* __rtems__ */
+ }
+
+ return (0);
+}
+
+static int
+xchan_bufs_alloc_busdma(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ struct xdma_request *xr;
+ int err;
+ int i;
+
+ xdma = xchan->xdma;
+
+ /* Create bus_dma tag */
+ err = bus_dma_tag_create(
+ bus_get_dma_tag(xdma->dev), /* Parent tag. */
+ xchan->alignment, /* alignment */
+ xchan->boundary, /* boundary */
+ xchan->lowaddr, /* lowaddr */
+ xchan->highaddr, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ xchan->maxsegsize * xchan->maxnsegs, /* maxsize */
+ xchan->maxnsegs, /* nsegments */
+ xchan->maxsegsize, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &xchan->dma_tag_bufs);
+ if (err != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't create bus_dma tag.\n", __func__);
+ return (-1);
+ }
+
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ err = bus_dmamap_create(xchan->dma_tag_bufs, 0,
+ &xr->buf.map);
+ if (err != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't create buf DMA map.\n", __func__);
+
+ /* Cleanup. */
+ bus_dma_tag_destroy(xchan->dma_tag_bufs);
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+xchan_bufs_alloc(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+
+ if (xdma == NULL) {
+ printf("%s: Channel was not allocated properly.\n", __func__);
+ return (-1);
+ }
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA)
+ ret = xchan_bufs_alloc_busdma(xchan);
+ else {
+ ret = xchan_bufs_alloc_reserved(xchan);
+ }
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate bufs.\n", __func__);
+ return (-1);
+ }
+
+ xchan->flags |= XCHAN_BUFS_ALLOCATED;
+
+ return (0);
+}
+
+static int
+xchan_bufs_free(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ struct xchan_buf *b;
+ int i;
+
+ if ((xchan->flags & XCHAN_BUFS_ALLOCATED) == 0)
+ return (-1);
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ b = &xr->buf;
+ bus_dmamap_destroy(xchan->dma_tag_bufs, b->map);
+ }
+ bus_dma_tag_destroy(xchan->dma_tag_bufs);
+ } else
+ xchan_bufs_free_reserved(xchan);
+
+ xchan->flags &= ~XCHAN_BUFS_ALLOCATED;
+
+ return (0);
+}
+
+void
+xdma_channel_free_sg(xdma_channel_t *xchan)
+{
+
+ xchan_bufs_free(xchan);
+ xchan_sglist_free(xchan);
+ xchan_bank_free(xchan);
+}
+
+/*
+ * Prepare xchan for a scatter-gather transfer.
+ * xr_num - xdma requests queue size,
+ * maxsegsize - maximum allowed scatter-gather list element size in bytes
+ */
+int
+xdma_prep_sg(xdma_channel_t *xchan, uint32_t xr_num,
+ bus_size_t maxsegsize, bus_size_t maxnsegs,
+ bus_size_t alignment, bus_addr_t boundary,
+ bus_addr_t lowaddr, bus_addr_t highaddr)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ if (xchan->flags & XCHAN_CONFIGURED) {
+ device_printf(xdma->dev,
+ "%s: Channel is already configured.\n", __func__);
+ return (-1);
+ }
+
+ xchan->xr_num = xr_num;
+ xchan->maxsegsize = maxsegsize;
+ xchan->maxnsegs = maxnsegs;
+ xchan->alignment = alignment;
+ xchan->boundary = boundary;
+ xchan->lowaddr = lowaddr;
+ xchan->highaddr = highaddr;
+
+ if (xchan->maxnsegs > XDMA_MAX_SEG) {
+ device_printf(xdma->dev, "%s: maxnsegs is too big\n",
+ __func__);
+ return (-1);
+ }
+
+ xchan_bank_init(xchan);
+
+ /* Allocate sglist. */
+ ret = xchan_sglist_alloc(xchan);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate sglist.\n", __func__);
+ return (-1);
+ }
+
+ /* Allocate buffers if required. */
+ if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0) {
+ ret = xchan_bufs_alloc(xchan);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate bufs.\n", __func__);
+
+ /* Cleanup */
+ xchan_sglist_free(xchan);
+ xchan_bank_free(xchan);
+
+ return (-1);
+ }
+ }
+
+ xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_SG);
+
+ XCHAN_LOCK(xchan);
+ ret = XDMA_CHANNEL_PREP_SG(xdma->dma_dev, xchan);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't prepare SG transfer.\n", __func__);
+ XCHAN_UNLOCK(xchan);
+
+ return (-1);
+ }
+ XCHAN_UNLOCK(xchan);
+
+ return (0);
+}
+
+void
+xchan_seg_done(xdma_channel_t *xchan,
+ struct xdma_transfer_status *st)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+ struct xchan_buf *b;
+
+ xdma = xchan->xdma;
+
+ xr = TAILQ_FIRST(&xchan->processing);
+ if (xr == NULL)
+ panic("request not found\n");
+
+ b = &xr->buf;
+
+ atomic_subtract_int(&b->nsegs_left, 1);
+
+ if (b->nsegs_left == 0) {
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ if (xr->direction == XDMA_MEM_TO_DEV)
+ bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
+ BUS_DMASYNC_POSTWRITE);
+ else
+ bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(xchan->dma_tag_bufs, b->map);
+ } else {
+ if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0 &&
+ xr->req_type == XR_TYPE_MBUF &&
+ xr->direction == XDMA_DEV_TO_MEM)
+ m_copyback(xr->m, 0, st->transferred,
+ (void *)xr->buf.vaddr);
+ }
+ xr->status.error = st->error;
+ xr->status.transferred = st->transferred;
+
+ QUEUE_PROC_LOCK(xchan);
+ TAILQ_REMOVE(&xchan->processing, xr, xr_next);
+ QUEUE_PROC_UNLOCK(xchan);
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_out, xr, xr_next);
+ QUEUE_OUT_UNLOCK(xchan);
+ }
+}
+
+static void
+xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct seg_load_request *slr;
+ struct bus_dma_segment *seg;
+ int i;
+
+ slr = arg;
+ seg = slr->seg;
+
+ if (error != 0) {
+ slr->error = error;
+ return;
+ }
+
+ slr->nsegs = nsegs;
+
+ for (i = 0; i < nsegs; i++) {
+ seg[i].ds_addr = segs[i].ds_addr;
+ seg[i].ds_len = segs[i].ds_len;
+ }
+}
+
+static int
+_xdma_load_data_busdma(xdma_channel_t *xchan, struct xdma_request *xr,
+ struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ struct seg_load_request slr;
+ uint32_t nsegs;
+ void *addr;
+ int error;
+
+ xdma = xchan->xdma;
+
+ error = 0;
+ nsegs = 0;
+
+ switch (xr->req_type) {
+ case XR_TYPE_MBUF:
+ error = bus_dmamap_load_mbuf_sg(xchan->dma_tag_bufs,
+ xr->buf.map, xr->m, seg, &nsegs, BUS_DMA_NOWAIT);
+ break;
+#ifndef __rtems__
+ case XR_TYPE_BIO:
+ slr.nsegs = 0;
+ slr.error = 0;
+ slr.seg = seg;
+ error = bus_dmamap_load_bio(xchan->dma_tag_bufs,
+ xr->buf.map, xr->bp, xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
+ if (slr.error != 0) {
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed, err %d\n",
+ __func__, slr.error);
+ return (0);
+ }
+ nsegs = slr.nsegs;
+ break;
+#endif /* __rtems__ */
+ case XR_TYPE_VIRT:
+ switch (xr->direction) {
+ case XDMA_MEM_TO_DEV:
+ addr = (void *)xr->src_addr;
+ break;
+ case XDMA_DEV_TO_MEM:
+ addr = (void *)xr->dst_addr;
+ break;
+ default:
+ device_printf(xdma->dma_dev,
+ "%s: Direction is not supported\n", __func__);
+ return (0);
+ }
+ slr.nsegs = 0;
+ slr.error = 0;
+ slr.seg = seg;
+ error = bus_dmamap_load(xchan->dma_tag_bufs, xr->buf.map,
+ addr, (xr->block_len * xr->block_num),
+ xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
+ if (slr.error != 0) {
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed, err %d\n",
+ __func__, slr.error);
+ return (0);
+ }
+ nsegs = slr.nsegs;
+ break;
+ default:
+ break;
+ }
+
+ if (error != 0) {
+ if (error == ENOMEM) {
+ /*
+ * Out of memory. Try again later.
+ * TODO: count errors.
+ */
+ } else
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed with err %d\n",
+ __func__, error);
+ return (0);
+ }
+
+ if (xr->direction == XDMA_MEM_TO_DEV)
+ bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
+ BUS_DMASYNC_PREWRITE);
+ else
+ bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
+ BUS_DMASYNC_PREREAD);
+
+ return (nsegs);
+}
+
+static int
+_xdma_load_data(xdma_channel_t *xchan, struct xdma_request *xr,
+ struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ struct mbuf *m;
+ uint32_t nsegs;
+
+ xdma = xchan->xdma;
+
+ m = xr->m;
+
+ nsegs = 1;
+
+ switch (xr->req_type) {
+ case XR_TYPE_MBUF:
+ if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0) {
+ if (xr->direction == XDMA_MEM_TO_DEV)
+ m_copydata(m, 0, m->m_pkthdr.len,
+ (void *)xr->buf.vaddr);
+ seg[0].ds_addr = (bus_addr_t)xr->buf.paddr;
+ } else
+ seg[0].ds_addr = mtod(m, bus_addr_t);
+ seg[0].ds_len = m->m_pkthdr.len;
+ break;
+ case XR_TYPE_BIO:
+ case XR_TYPE_VIRT:
+ default:
+ panic("implement me\n");
+ }
+
+ return (nsegs);
+}
+
+static int
+xdma_load_data(xdma_channel_t *xchan,
+ struct xdma_request *xr, struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ int error;
+ int nsegs;
+
+ xdma = xchan->xdma;
+
+ error = 0;
+ nsegs = 0;
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA)
+ nsegs = _xdma_load_data_busdma(xchan, xr, seg);
+ else
+ nsegs = _xdma_load_data(xchan, xr, seg);
+ if (nsegs == 0)
+ return (0); /* Try again later. */
+
+ xr->buf.nsegs = nsegs;
+ xr->buf.nsegs_left = nsegs;
+
+ return (nsegs);
+}
+
+static int
+xdma_process(xdma_channel_t *xchan,
+ struct xdma_sglist *sg)
+{
+ struct bus_dma_segment seg[XDMA_MAX_SEG];
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+ xdma_controller_t *xdma;
+ uint32_t capacity;
+ uint32_t n;
+ uint32_t c;
+ int nsegs;
+ int ret;
+
+ XCHAN_ASSERT_LOCKED(xchan);
+
+ xdma = xchan->xdma;
+
+ n = 0;
+ c = 0;
+
+ ret = XDMA_CHANNEL_CAPACITY(xdma->dma_dev, xchan, &capacity);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't get DMA controller capacity.\n", __func__);
+ return (-1);
+ }
+
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_in, xr_next, xr_tmp) {
+ switch (xr->req_type) {
+ case XR_TYPE_MBUF:
+ if ((xchan->caps & XCHAN_CAP_NOSEG) ||
+ (c > xchan->maxnsegs))
+ c = xdma_mbuf_defrag(xchan, xr);
+ break;
+ case XR_TYPE_BIO:
+ case XR_TYPE_VIRT:
+ default:
+ c = 1;
+ }
+
+ if (capacity <= (c + n)) {
+ /*
+ * No space yet available for the entire
+ * request in the DMA engine.
+ */
+ break;
+ }
+
+ if ((c + n + xchan->maxnsegs) >= XDMA_SGLIST_MAXLEN) {
+ /* Sglist is full. */
+ break;
+ }
+
+ nsegs = xdma_load_data(xchan, xr, seg);
+ if (nsegs == 0)
+ break;
+
+ xdma_sglist_add(&sg[n], seg, nsegs, xr);
+ n += nsegs;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_REMOVE(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ QUEUE_PROC_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->processing, xr, xr_next);
+ QUEUE_PROC_UNLOCK(xchan);
+ }
+
+ return (n);
+}
+
+int
+xdma_queue_submit_sg(xdma_channel_t *xchan)
+{
+ struct xdma_sglist *sg;
+ xdma_controller_t *xdma;
+ uint32_t sg_n;
+ int ret;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ XCHAN_ASSERT_LOCKED(xchan);
+
+ sg = xchan->sg;
+
+ if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0 &&
+ (xchan->flags & XCHAN_BUFS_ALLOCATED) == 0) {
+ device_printf(xdma->dev,
+ "%s: Can't submit a transfer: no bufs\n",
+ __func__);
+ return (-1);
+ }
+
+ sg_n = xdma_process(xchan, sg);
+ if (sg_n == 0)
+ return (0); /* Nothing to submit */
+
+ /* Now submit sglist to DMA engine driver. */
+ ret = XDMA_CHANNEL_SUBMIT_SG(xdma->dma_dev, xchan, sg, sg_n);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't submit an sglist.\n", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/freebsd/sys/dev/xdma/xdma_sglist.c b/freebsd/sys/dev/xdma/xdma_sglist.c
new file mode 100644
index 00000000..8c3c5ab0
--- /dev/null
+++ b/freebsd/sys/dev/xdma/xdma_sglist.c
@@ -0,0 +1,103 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2017-2018 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/xdma/xdma.h>
+
+int
+xchan_sglist_alloc(xdma_channel_t *xchan)
+{
+ uint32_t sz;
+
+ if (xchan->flags & XCHAN_SGLIST_ALLOCATED)
+ return (-1);
+
+ sz = (sizeof(struct xdma_sglist) * XDMA_SGLIST_MAXLEN);
+ xchan->sg = malloc(sz, M_XDMA, M_WAITOK | M_ZERO);
+ xchan->flags |= XCHAN_SGLIST_ALLOCATED;
+
+ return (0);
+}
+
+void
+xchan_sglist_free(xdma_channel_t *xchan)
+{
+
+ if (xchan->flags & XCHAN_SGLIST_ALLOCATED)
+ free(xchan->sg, M_XDMA);
+
+ xchan->flags &= ~XCHAN_SGLIST_ALLOCATED;
+}
+
+int
+xdma_sglist_add(struct xdma_sglist *sg, struct bus_dma_segment *seg,
+ uint32_t nsegs, struct xdma_request *xr)
+{
+ int i;
+
+ if (nsegs == 0)
+ return (-1);
+
+ for (i = 0; i < nsegs; i++) {
+ sg[i].src_width = xr->src_width;
+ sg[i].dst_width = xr->dst_width;
+
+ if (xr->direction == XDMA_MEM_TO_DEV) {
+ sg[i].src_addr = seg[i].ds_addr;
+ sg[i].dst_addr = xr->dst_addr;
+ } else {
+ sg[i].src_addr = xr->src_addr;
+ sg[i].dst_addr = seg[i].ds_addr;
+ }
+ sg[i].len = seg[i].ds_len;
+ sg[i].direction = xr->direction;
+
+ sg[i].first = 0;
+ sg[i].last = 0;
+ }
+
+ sg[0].first = 1;
+ sg[nsegs - 1].last = 1;
+
+ return (0);
+}
diff --git a/freebsd/sys/dev/xilinx/axidma.c b/freebsd/sys/dev/xilinx/axidma.c
new file mode 100644
index 00000000..77025c0a
--- /dev/null
+++ b/freebsd/sys/dev/xilinx/axidma.c
@@ -0,0 +1,683 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+/* Xilinx AXI DMA controller driver. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_page.h>
+
+#ifndef __rtems__
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+#else
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif /* __rtems__ */
+
+#include <dev/xdma/xdma.h>
+#include <dev/xilinx/axidma.h>
+
+#include <rtems/bsd/local/xdma_if.h>
+
+#define AXIDMA_DEBUG
+#undef AXIDMA_DEBUG
+
+#ifdef __rtems__
+#include <sys/endian.h>
+
+#define AXIDMA_DESCRIPTOR_ALIGNMENT 64
+#endif /* __rtems__ */
+
+#ifdef AXIDMA_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define AXIDMA_NCHANNELS 2
+#define AXIDMA_DESCS_NUM 512
+#define AXIDMA_TX_CHAN 0
+#define AXIDMA_RX_CHAN 1
+
+extern struct bus_space memmap_bus;
+
+struct axidma_fdt_data {
+ int id;
+};
+
+struct axidma_channel {
+ struct axidma_softc *sc;
+ xdma_channel_t *xchan;
+ bool used;
+ int idx_head;
+ int idx_tail;
+
+ struct axidma_desc **descs;
+ vm_paddr_t *descs_phys;
+ uint32_t descs_num;
+
+ vm_size_t mem_size;
+ vm_offset_t mem_paddr;
+ vm_offset_t mem_vaddr;
+
+ uint32_t descs_used_count;
+};
+
+struct axidma_softc {
+ device_t dev;
+ struct resource *res[3];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih[2];
+ struct axidma_desc desc;
+ struct axidma_channel channels[AXIDMA_NCHANNELS];
+};
+
+static struct resource_spec axidma_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define HWTYPE_NONE 0
+#define HWTYPE_STD 1
+
+static struct ofw_compat_data compat_data[] = {
+ { "xlnx,eth-dma", HWTYPE_STD },
+ { NULL, HWTYPE_NONE },
+};
+
+static int axidma_probe(device_t dev);
+static int axidma_attach(device_t dev);
+static int axidma_detach(device_t dev);
+
+static inline uint32_t
+axidma_next_desc(struct axidma_channel *chan, uint32_t curidx)
+{
+
+ return ((curidx + 1) % chan->descs_num);
+}
+
+static void
+axidma_intr(struct axidma_softc *sc,
+ struct axidma_channel *chan)
+{
+ xdma_transfer_status_t status;
+ xdma_transfer_status_t st;
+ struct axidma_fdt_data *data;
+ xdma_controller_t *xdma;
+ struct axidma_desc *desc;
+ struct xdma_channel *xchan;
+ uint32_t tot_copied;
+ int pending;
+ int errors;
+
+ xchan = chan->xchan;
+ xdma = xchan->xdma;
+ data = xdma->data;
+
+ pending = READ4(sc, AXI_DMASR(data->id));
+ WRITE4(sc, AXI_DMASR(data->id), pending);
+
+ errors = (pending & (DMASR_DMAINTERR | DMASR_DMASLVERR
+ | DMASR_DMADECOREERR | DMASR_SGINTERR
+ | DMASR_SGSLVERR | DMASR_SGDECERR));
+
+ dprintf("%s: AXI_DMASR %x\n", __func__,
+ READ4(sc, AXI_DMASR(data->id)));
+ dprintf("%s: AXI_CURDESC %x\n", __func__,
+ READ4(sc, AXI_CURDESC(data->id)));
+ dprintf("%s: AXI_TAILDESC %x\n", __func__,
+ READ4(sc, AXI_TAILDESC(data->id)));
+
+ tot_copied = 0;
+
+ while (chan->idx_tail != chan->idx_head) {
+ desc = chan->descs[chan->idx_tail];
+ if ((desc->status & BD_STATUS_CMPLT) == 0)
+ break;
+
+ st.error = errors;
+ st.transferred = desc->status & BD_CONTROL_LEN_M;
+#ifdef __rtems__
+ /* Handle Control / Status Streams. */
+ if (!st.transferred) {
+ st.transferred = desc->app4 & BD_STATUS_TRANSFERRED_M;
+ }
+#endif /* __rtems__ */
+ tot_copied += st.transferred;
+ xchan_seg_done(xchan, &st);
+
+ chan->idx_tail = axidma_next_desc(chan, chan->idx_tail);
+ atomic_subtract_int(&chan->descs_used_count, 1);
+ }
+
+ /* Finish operation */
+ status.error = errors;
+ status.transferred = tot_copied;
+ xdma_callback(chan->xchan, &status);
+}
+
+static void
+axidma_intr_rx(void *arg)
+{
+ struct axidma_softc *sc;
+ struct axidma_channel *chan;
+
+ dprintf("%s\n", __func__);
+
+ sc = arg;
+ chan = &sc->channels[AXIDMA_RX_CHAN];
+
+ axidma_intr(sc, chan);
+}
+
+static void
+axidma_intr_tx(void *arg)
+{
+ struct axidma_softc *sc;
+ struct axidma_channel *chan;
+
+ dprintf("%s\n", __func__);
+
+ sc = arg;
+ chan = &sc->channels[AXIDMA_TX_CHAN];
+
+ axidma_intr(sc, chan);
+}
+
+static int
+axidma_reset(struct axidma_softc *sc, int chan_id)
+{
+ int timeout;
+
+ WRITE4(sc, AXI_DMACR(chan_id), DMACR_RESET);
+
+ timeout = 100;
+ do {
+ if ((READ4(sc, AXI_DMACR(chan_id)) & DMACR_RESET) == 0)
+ break;
+ } while (timeout--);
+
+ dprintf("timeout %d\n", timeout);
+
+ if (timeout == 0)
+ return (-1);
+
+ dprintf("%s: read control after reset: %x\n",
+ __func__, READ4(sc, AXI_DMACR(chan_id)));
+
+ return (0);
+}
+
+static int
+axidma_probe(device_t dev)
+{
+ int hwtype;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (hwtype == HWTYPE_NONE)
+ return (ENXIO);
+
+ device_set_desc(dev, "Xilinx AXI DMA");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+axidma_attach(device_t dev)
+{
+ struct axidma_softc *sc;
+ phandle_t xref, node;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, axidma_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources.\n");
+ return (ENXIO);
+ }
+
+ /* CSR memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, axidma_intr_tx, sc, &sc->ih[0]);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, axidma_intr_rx, sc, &sc->ih[1]);
+ if (err) {
+ device_printf(dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_get_node(dev);
+ xref = OF_xref_from_node(node);
+ OF_device_register_xref(xref, dev);
+
+ return (0);
+}
+
+static int
+axidma_detach(device_t dev)
+{
+ struct axidma_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_teardown_intr(dev, sc->res[1], sc->ih[0]);
+ bus_teardown_intr(dev, sc->res[2], sc->ih[1]);
+ bus_release_resources(dev, axidma_spec, sc->res);
+
+ return (0);
+}
+
+static int
+axidma_desc_free(struct axidma_softc *sc, struct axidma_channel *chan)
+{
+ struct xdma_channel *xchan;
+ int nsegments;
+
+ nsegments = chan->descs_num;
+ xchan = chan->xchan;
+
+ free(chan->descs, M_DEVBUF);
+ free(chan->descs_phys, M_DEVBUF);
+
+#ifndef __rtems__
+ pmap_kremove_device(chan->mem_vaddr, chan->mem_size);
+ kva_free(chan->mem_vaddr, chan->mem_size);
+#endif /* __rtems__ */
+ vmem_free(xchan->vmem, chan->mem_paddr, chan->mem_size);
+
+ return (0);
+}
+
+static int
+axidma_desc_alloc(struct axidma_softc *sc, struct xdma_channel *xchan,
+ uint32_t desc_size)
+{
+ struct axidma_channel *chan;
+ int nsegments;
+ int i;
+
+ chan = (struct axidma_channel *)xchan->chan;
+ nsegments = chan->descs_num;
+
+ chan->descs = malloc(nsegments * sizeof(struct axidma_desc *),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (chan->descs == NULL) {
+ device_printf(sc->dev,
+ "%s: Can't allocate memory.\n", __func__);
+ return (-1);
+ }
+
+ chan->descs_phys = malloc(nsegments * sizeof(bus_dma_segment_t),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ chan->mem_size = desc_size * nsegments;
+#ifndef __rtems__
+ if (vmem_alloc(xchan->vmem, chan->mem_size, M_FIRSTFIT | M_NOWAIT,
+ &chan->mem_paddr)) {
+ device_printf(sc->dev, "Failed to allocate memory.\n");
+ return (-1);
+ }
+ chan->mem_vaddr = kva_alloc(chan->mem_size);
+ pmap_kenter_device(chan->mem_vaddr, chan->mem_size, chan->mem_paddr);
+#else /* __rtems__ */
+ /* Align DMA descriptors */
+ chan->mem_vaddr = calloc(1, chan->mem_size + AXIDMA_DESCRIPTOR_ALIGNMENT - 1);
+ chan->mem_vaddr = ((uintptr_t)chan->mem_vaddr +
+ AXIDMA_DESCRIPTOR_ALIGNMENT - 1) & ~0x3F;
+ chan->mem_paddr = chan->mem_vaddr;
+#endif /* __rtems__ */
+
+ device_printf(sc->dev, "Allocated chunk %lx %d\n",
+ chan->mem_paddr, chan->mem_size);
+
+ for (i = 0; i < nsegments; i++) {
+ chan->descs[i] = (struct axidma_desc *)
+ ((uint64_t)chan->mem_vaddr + desc_size * i);
+ chan->descs_phys[i] = chan->mem_paddr + desc_size * i;
+ }
+
+ return (0);
+}
+
+static int
+axidma_channel_alloc(device_t dev, struct xdma_channel *xchan)
+{
+ xdma_controller_t *xdma;
+ struct axidma_fdt_data *data;
+ struct axidma_channel *chan;
+ struct axidma_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ device_printf(sc->dev,
+ "Error: busdma operation is not implemented.");
+ return (-1);
+ }
+
+ xdma = xchan->xdma;
+ data = xdma->data;
+
+ chan = &sc->channels[data->id];
+ if (chan->used == false) {
+ if (axidma_reset(sc, data->id) != 0)
+ return (-1);
+ chan->xchan = xchan;
+ xchan->chan = (void *)chan;
+ chan->sc = sc;
+ chan->used = true;
+ chan->idx_head = 0;
+ chan->idx_tail = 0;
+ chan->descs_used_count = 0;
+ chan->descs_num = AXIDMA_DESCS_NUM;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+static int
+axidma_channel_free(device_t dev, struct xdma_channel *xchan)
+{
+ struct axidma_channel *chan;
+ struct axidma_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ chan = (struct axidma_channel *)xchan->chan;
+
+ axidma_desc_free(sc, chan);
+
+ chan->used = false;
+
+ return (0);
+}
+
+static int
+axidma_channel_capacity(device_t dev, xdma_channel_t *xchan,
+ uint32_t *capacity)
+{
+ struct axidma_channel *chan;
+ uint32_t c;
+
+ chan = (struct axidma_channel *)xchan->chan;
+
+ /* At least one descriptor must be left empty. */
+ c = (chan->descs_num - chan->descs_used_count - 1);
+
+ *capacity = c;
+
+ return (0);
+}
+
+static int
+axidma_channel_submit_sg(device_t dev, struct xdma_channel *xchan,
+ struct xdma_sglist *sg, uint32_t sg_n)
+{
+ xdma_controller_t *xdma;
+ struct axidma_fdt_data *data;
+ struct axidma_channel *chan;
+ struct axidma_desc *desc;
+ struct axidma_softc *sc;
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint32_t addr;
+ uint32_t len;
+ uint32_t tmp;
+ int i;
+ int tail;
+
+ dprintf("%s: sg_n %d\n", __func__, sg_n);
+
+ sc = device_get_softc(dev);
+
+ chan = (struct axidma_channel *)xchan->chan;
+ xdma = xchan->xdma;
+ data = xdma->data;
+
+ if (sg_n == 0)
+ return (0);
+
+ tail = chan->idx_head;
+
+ tmp = 0;
+
+ for (i = 0; i < sg_n; i++) {
+ src_addr = (uint32_t)sg[i].src_addr;
+ dst_addr = (uint32_t)sg[i].dst_addr;
+ len = (uint32_t)sg[i].len;
+
+ dprintf("%s(%d): src %x dst %x len %d\n", __func__,
+ data->id, src_addr, dst_addr, len);
+
+ desc = chan->descs[chan->idx_head];
+ if (sg[i].direction == XDMA_MEM_TO_DEV)
+ desc->phys = src_addr;
+ else
+ desc->phys = dst_addr;
+ desc->status = 0;
+ desc->control = len;
+ if (sg[i].first == 1)
+ desc->control |= BD_CONTROL_TXSOF;
+ if (sg[i].last == 1)
+ desc->control |= BD_CONTROL_TXEOF;
+
+ tmp = chan->idx_head;
+
+ atomic_add_int(&chan->descs_used_count, 1);
+ chan->idx_head = axidma_next_desc(chan, chan->idx_head);
+ }
+
+ dprintf("%s(%d): _curdesc %x\n", __func__, data->id,
+ READ8(sc, AXI_CURDESC(data->id)));
+ dprintf("%s(%d): _curdesc %x\n", __func__, data->id,
+ READ8(sc, AXI_CURDESC(data->id)));
+ dprintf("%s(%d): status %x\n", __func__, data->id,
+ READ4(sc, AXI_DMASR(data->id)));
+
+ addr = chan->descs_phys[tmp];
+ WRITE8(sc, AXI_TAILDESC(data->id), addr);
+
+ return (0);
+}
+
+static int
+axidma_channel_prep_sg(device_t dev, struct xdma_channel *xchan)
+{
+ xdma_controller_t *xdma;
+ struct axidma_fdt_data *data;
+ struct axidma_channel *chan;
+ struct axidma_desc *desc;
+ struct axidma_softc *sc;
+ uint32_t addr;
+ uint32_t reg;
+ int ret;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ chan = (struct axidma_channel *)xchan->chan;
+ xdma = xchan->xdma;
+ data = xdma->data;
+
+ dprintf("%s(%d)\n", __func__, data->id);
+
+ ret = axidma_desc_alloc(sc, xchan, sizeof(struct axidma_desc));
+ if (ret != 0) {
+ device_printf(sc->dev,
+ "%s: Can't allocate descriptors.\n", __func__);
+ return (-1);
+ }
+
+ for (i = 0; i < chan->descs_num; i++) {
+ desc = chan->descs[i];
+ bzero(desc, sizeof(struct axidma_desc));
+
+ if (i == (chan->descs_num - 1))
+ desc->next = chan->descs_phys[0];
+ else
+ desc->next = chan->descs_phys[i + 1];
+ desc->status = 0;
+ desc->control = 0;
+
+#ifndef __rtems__
+ dprintf("%s(%d): desc %d vaddr %lx next paddr %x\n", __func__,
+ data->id, i, (uint64_t)desc, le32toh(desc->next));
+#else /* __rtems__ */
+ dprintf("%s(%d): desc %d vaddr %lx next paddr %x\n", __func__,
+ data->id, i, desc, le32toh(desc->next));
+#endif /* __rtems__ */
+ }
+
+ addr = chan->descs_phys[0];
+ WRITE8(sc, AXI_CURDESC(data->id), addr);
+
+ reg = READ4(sc, AXI_DMACR(data->id));
+ reg |= DMACR_IOC_IRQEN | DMACR_DLY_IRQEN | DMACR_ERR_IRQEN;
+ WRITE4(sc, AXI_DMACR(data->id), reg);
+ reg |= DMACR_RS;
+ WRITE4(sc, AXI_DMACR(data->id), reg);
+
+ return (0);
+}
+
+static int
+axidma_channel_control(device_t dev, xdma_channel_t *xchan, int cmd)
+{
+ struct axidma_channel *chan;
+ struct axidma_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ chan = (struct axidma_channel *)xchan->chan;
+
+ switch (cmd) {
+ case XDMA_CMD_BEGIN:
+ case XDMA_CMD_TERMINATE:
+ case XDMA_CMD_PAUSE:
+ /* TODO: implement me */
+ return (-1);
+ }
+
+ return (0);
+}
+
+#ifdef FDT
+static int
+axidma_ofw_md_data(device_t dev, pcell_t *cells, int ncells, void **ptr)
+{
+ struct axidma_fdt_data *data;
+
+ if (ncells != 1)
+ return (-1);
+
+ data = malloc(sizeof(struct axidma_fdt_data),
+ M_DEVBUF, (M_WAITOK | M_ZERO));
+ data->id = cells[0];
+
+ *ptr = data;
+
+ return (0);
+}
+#endif
+
+static device_method_t axidma_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axidma_probe),
+ DEVMETHOD(device_attach, axidma_attach),
+ DEVMETHOD(device_detach, axidma_detach),
+
+ /* xDMA Interface */
+ DEVMETHOD(xdma_channel_alloc, axidma_channel_alloc),
+ DEVMETHOD(xdma_channel_free, axidma_channel_free),
+ DEVMETHOD(xdma_channel_control, axidma_channel_control),
+
+ /* xDMA SG Interface */
+ DEVMETHOD(xdma_channel_capacity, axidma_channel_capacity),
+ DEVMETHOD(xdma_channel_prep_sg, axidma_channel_prep_sg),
+ DEVMETHOD(xdma_channel_submit_sg, axidma_channel_submit_sg),
+
+#ifdef FDT
+ DEVMETHOD(xdma_ofw_md_data, axidma_ofw_md_data),
+#endif
+
+ DEVMETHOD_END
+};
+
+static driver_t axidma_driver = {
+ "axidma",
+ axidma_methods,
+ sizeof(struct axidma_softc),
+};
+
+static devclass_t axidma_devclass;
+
+EARLY_DRIVER_MODULE(axidma, simplebus, axidma_driver, axidma_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/freebsd/sys/dev/xilinx/axidma.h b/freebsd/sys/dev/xilinx/axidma.h
new file mode 100644
index 00000000..8e5d8387
--- /dev/null
+++ b/freebsd/sys/dev/xilinx/axidma.h
@@ -0,0 +1,96 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _DEV_XILINX_AXIDMA_H_
+#define _DEV_XILINX_AXIDMA_H_
+
+#define AXI_DMACR(n) (0x00 + 0x30 * (n)) /* DMA Control register */
+#define DMACR_RS (1 << 0) /* Run / Stop. */
+#define DMACR_RESET (1 << 2) /* Soft reset the AXI DMA core. */
+#define DMACR_IOC_IRQEN (1 << 12) /* Interrupt on Complete (IOC) Interrupt Enable. */
+#define DMACR_DLY_IRQEN (1 << 13) /* Interrupt on Delay Timer Interrupt Enable. */
+#define DMACR_ERR_IRQEN (1 << 14) /* Interrupt on Error Interrupt Enable. */
+#define AXI_DMASR(n) (0x04 + 0x30 * (n)) /* DMA Status register */
+#define DMASR_HALTED (1 << 0)
+#define DMASR_IDLE (1 << 1)
+#define DMASR_SGINCLD (1 << 3) /* Scatter Gather Enabled */
+#define DMASR_DMAINTERR (1 << 4) /* DMA Internal Error. */
+#define DMASR_DMASLVERR (1 << 5) /* DMA Slave Error. */
+#define DMASR_DMADECOREERR (1 << 6) /* Decode Error. */
+#define DMASR_SGINTERR (1 << 8) /* Scatter Gather Internal Error. */
+#define DMASR_SGSLVERR (1 << 9) /* Scatter Gather Slave Error. */
+#define DMASR_SGDECERR (1 << 10) /* Scatter Gather Decode Error. */
+#define DMASR_IOC_IRQ (1 << 12) /* Interrupt on Complete. */
+#define DMASR_DLY_IRQ (1 << 13) /* Interrupt on Delay. */
+#define DMASR_ERR_IRQ (1 << 14) /* Interrupt on Error. */
+#define AXI_CURDESC(n) (0x08 + 0x30 * (n)) /* Current Descriptor Pointer. Lower 32 bits of the address. */
+#define AXI_CURDESC_MSB(n) (0x0C + 0x30 * (n)) /* Current Descriptor Pointer. Upper 32 bits of address. */
+#define AXI_TAILDESC(n) (0x10 + 0x30 * (n)) /* Tail Descriptor Pointer. Lower 32 bits. */
+#define AXI_TAILDESC_MSB(n) (0x14 + 0x30 * (n)) /* Tail Descriptor Pointer. Upper 32 bits of address. */
+#define AXI_SG_CTL 0x2C /* Scatter/Gather User and Cache */
+
+#define READ4(_sc, _reg) \
+ bus_space_read_4(_sc->bst, _sc->bsh, _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
+#define READ8(_sc, _reg) \
+ bus_space_read_8(_sc->bst, _sc->bsh, _reg)
+#define WRITE8(_sc, _reg, _val) \
+ bus_space_write_8(_sc->bst, _sc->bsh, _reg, _val)
+
+struct axidma_desc {
+ uint32_t next;
+ uint32_t reserved1;
+ uint32_t phys;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t reserved4;
+ uint32_t control;
+#define BD_CONTROL_TXSOF (1 << 27) /* Start of Frame. */
+#define BD_CONTROL_TXEOF (1 << 26) /* End of Frame. */
+#define BD_CONTROL_LEN_S 0 /* Buffer Length. */
+#define BD_CONTROL_LEN_M (0x3ffffff << BD_CONTROL_LEN_S)
+ uint32_t status;
+#define BD_STATUS_CMPLT (1 << 31)
+#define BD_STATUS_TRANSFERRED_S 0
+#define BD_STATUS_TRANSFERRED_M (0x7fffff << BD_STATUS_TRANSFERRED_S)
+ uint32_t app0;
+ uint32_t app1;
+ uint32_t app2;
+ uint32_t app3;
+ uint32_t app4;
+ uint32_t reserved[3];
+};
+
+#endif /* !_DEV_XILINX_AXIDMA_H_ */
diff --git a/freebsd/sys/dev/xilinx/if_xae.c b/freebsd/sys/dev/xilinx/if_xae.c
new file mode 100644
index 00000000..9b05ae1b
--- /dev/null
+++ b/freebsd/sys/dev/xilinx/if_xae.c
@@ -0,0 +1,1108 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+
+#include <machine/bus.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mii/tiphy.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/xilinx/if_xaereg.h>
+#include <dev/xilinx/if_xaevar.h>
+
+#include <rtems/bsd/local/miibus_if.h>
+
+#define READ4(_sc, _reg) \
+ bus_read_4((_sc)->res[0], _reg)
+#define WRITE4(_sc, _reg, _val) \
+ bus_write_4((_sc)->res[0], _reg, _val)
+
+#define READ8(_sc, _reg) \
+ bus_read_8((_sc)->res[0], _reg)
+#define WRITE8(_sc, _reg, _val) \
+ bus_write_8((_sc)->res[0], _reg, _val)
+
+#define XAE_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define XAE_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define XAE_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+#define XAE_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
+
+#define XAE_DEBUG
+#undef XAE_DEBUG
+
+#ifdef XAE_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define RX_QUEUE_SIZE 64
+#define TX_QUEUE_SIZE 64
+#define NUM_RX_MBUF 16
+#define BUFRING_SIZE 8192
+#define MDIO_CLK_DIV_DEFAULT 29
+
+#define PHY1_RD(sc, _r) \
+ xae_miibus_read_reg(sc->dev, 1, _r)
+#define PHY1_WR(sc, _r, _v) \
+ xae_miibus_write_reg(sc->dev, 1, _r, _v)
+
+#define PHY_RD(sc, _r) \
+ xae_miibus_read_reg(sc->dev, sc->phy_addr, _r)
+#define PHY_WR(sc, _r, _v) \
+ xae_miibus_write_reg(sc->dev, sc->phy_addr, _r, _v)
+
+/* Use this macro to access regs > 0x1f */
+#define WRITE_TI_EREG(sc, reg, data) { \
+ PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK); \
+ PHY_WR(sc, MII_MMDAADR, reg); \
+ PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK | MMDACR_FN_DATANPI); \
+ PHY_WR(sc, MII_MMDAADR, data); \
+}
+
+/* Not documented, Xilinx VCU118 workaround */
+#define CFG4_SGMII_TMR 0x160 /* bits 8:7 MUST be '10' */
+#define DP83867_SGMIICTL1 0xD3 /* not documented register */
+#define SGMIICTL1_SGMII_6W (1 << 14) /* no idea what it is */
+
+static struct resource_spec xae_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static void xae_stop_locked(struct xae_softc *sc);
+static void xae_setup_rxfilter(struct xae_softc *sc);
+
+static int
+xae_rx_enqueue(struct xae_softc *sc, uint32_t n)
+{
+ struct mbuf *m;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ device_printf(sc->dev,
+ "%s: Can't alloc rx mbuf\n", __func__);
+ return (-1);
+ }
+
+ m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+#ifdef __rtems__
+ m_adj(m, ETHER_ALIGN);
+#endif /* __rtems__ */
+ xdma_enqueue_mbuf(sc->xchan_rx, &m, 0, 4, 4, XDMA_DEV_TO_MEM);
+ }
+
+ return (0);
+}
+
+static int
+xae_get_phyaddr(phandle_t node, int *phy_addr)
+{
+ phandle_t phy_node;
+ pcell_t phy_handle, phy_reg;
+
+ if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
+ sizeof(phy_handle)) <= 0)
+ return (ENXIO);
+
+ phy_node = OF_node_from_xref(phy_handle);
+
+ if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
+ sizeof(phy_reg)) <= 0)
+ return (ENXIO);
+
+ *phy_addr = phy_reg;
+
+ return (0);
+}
+
+static int
+xae_xdma_tx_intr(void *arg, xdma_transfer_status_t *status)
+{
+ xdma_transfer_status_t st;
+ struct xae_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int err;
+
+ sc = arg;
+
+ XAE_LOCK(sc);
+
+ ifp = sc->ifp;
+
+ for (;;) {
+ err = xdma_dequeue_mbuf(sc->xchan_tx, &m, &st);
+ if (err != 0) {
+ break;
+ }
+
+ if (st.error != 0) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ }
+
+ m_freem(m);
+ }
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ XAE_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+xae_xdma_rx_intr(void *arg, xdma_transfer_status_t *status)
+{
+ xdma_transfer_status_t st;
+ struct xae_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int err;
+ uint32_t cnt_processed;
+
+ sc = arg;
+
+ dprintf("%s\n", __func__);
+
+ XAE_LOCK(sc);
+
+ ifp = sc->ifp;
+
+ cnt_processed = 0;
+ for (;;) {
+ err = xdma_dequeue_mbuf(sc->xchan_rx, &m, &st);
+ if (err != 0) {
+ break;
+ }
+ cnt_processed++;
+
+ if (st.error != 0) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ continue;
+ }
+
+ m->m_pkthdr.len = m->m_len = st.transferred;
+ m->m_pkthdr.rcvif = ifp;
+ XAE_UNLOCK(sc);
+ (*ifp->if_input)(ifp, m);
+ XAE_LOCK(sc);
+ }
+
+ xae_rx_enqueue(sc, cnt_processed);
+
+ XAE_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+xae_qflush(struct ifnet *ifp)
+{
+ struct xae_softc *sc;
+
+ sc = ifp->if_softc;
+}
+
+static int
+xae_transmit_locked(struct ifnet *ifp)
+{
+ struct xae_softc *sc;
+ struct mbuf *m;
+ struct buf_ring *br;
+ int error;
+ int enq;
+
+ dprintf("%s\n", __func__);
+
+ sc = ifp->if_softc;
+ br = sc->br;
+
+ enq = 0;
+
+ while ((m = drbr_peek(ifp, br)) != NULL) {
+ error = xdma_enqueue_mbuf(sc->xchan_tx,
+ &m, 0, 4, 4, XDMA_MEM_TO_DEV);
+ if (error != 0) {
+ /* No space in request queue available yet. */
+ drbr_putback(ifp, br, m);
+ break;
+ }
+
+ drbr_advance(ifp, br);
+
+ enq++;
+
+ /* If anyone is interested give them a copy. */
+ ETHER_BPF_MTAP(ifp, m);
+ }
+
+ if (enq > 0)
+ xdma_queue_submit(sc->xchan_tx);
+
+ return (0);
+}
+
+static int
+xae_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct xae_softc *sc;
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ sc = ifp->if_softc;
+
+ XAE_LOCK(sc);
+
+ error = drbr_enqueue(ifp, sc->br, m);
+ if (error) {
+ XAE_UNLOCK(sc);
+ return (error);
+ }
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING) {
+ XAE_UNLOCK(sc);
+ return (0);
+ }
+
+ if (!sc->link_is_up) {
+ XAE_UNLOCK(sc);
+ return (0);
+ }
+
+ error = xae_transmit_locked(ifp);
+
+ XAE_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+xae_stop_locked(struct xae_softc *sc)
+{
+ struct ifnet *ifp;
+ uint32_t reg;
+
+ XAE_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ callout_stop(&sc->xae_callout);
+
+ /* Stop the transmitter */
+ reg = READ4(sc, XAE_TC);
+ reg &= ~TC_TX;
+ WRITE4(sc, XAE_TC, reg);
+
+ /* Stop the receiver. */
+ reg = READ4(sc, XAE_RCW1);
+ reg &= ~RCW1_RX;
+ WRITE4(sc, XAE_RCW1, reg);
+}
+
+static uint64_t
+xae_stat(struct xae_softc *sc, int counter_id)
+{
+ uint64_t new, old;
+ uint64_t delta;
+
+ KASSERT(counter_id < XAE_MAX_COUNTERS,
+ ("counter %d is out of range", counter_id));
+
+ new = READ8(sc, XAE_STATCNT(counter_id));
+ old = sc->counters[counter_id];
+
+ if (new >= old)
+ delta = new - old;
+ else
+ delta = UINT64_MAX - old + new;
+ sc->counters[counter_id] = new;
+
+ return (delta);
+}
+
+static void
+xae_harvest_stats(struct xae_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->ifp;
+
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, xae_stat(sc, RX_GOOD_FRAMES));
+ if_inc_counter(ifp, IFCOUNTER_IMCASTS, xae_stat(sc, RX_GOOD_MCASTS));
+ if_inc_counter(ifp, IFCOUNTER_IERRORS,
+ xae_stat(sc, RX_FRAME_CHECK_SEQ_ERROR) +
+ xae_stat(sc, RX_LEN_OUT_OF_RANGE) +
+ xae_stat(sc, RX_ALIGNMENT_ERRORS));
+
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, xae_stat(sc, TX_BYTES));
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, xae_stat(sc, TX_GOOD_FRAMES));
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, xae_stat(sc, TX_GOOD_MCASTS));
+ if_inc_counter(ifp, IFCOUNTER_OERRORS,
+ xae_stat(sc, TX_GOOD_UNDERRUN_ERRORS));
+
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
+ xae_stat(sc, TX_SINGLE_COLLISION_FRAMES) +
+ xae_stat(sc, TX_MULTI_COLLISION_FRAMES) +
+ xae_stat(sc, TX_LATE_COLLISIONS) +
+ xae_stat(sc, TX_EXCESS_COLLISIONS));
+}
+
+static void
+xae_tick(void *arg)
+{
+ struct xae_softc *sc;
+ struct ifnet *ifp;
+ int link_was_up;
+
+ sc = arg;
+
+ XAE_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ return;
+
+ /* Gather stats from hardware counters. */
+ xae_harvest_stats(sc);
+
+ /* Check the media status. */
+ link_was_up = sc->link_is_up;
+ mii_tick(sc->mii_softc);
+ if (sc->link_is_up && !link_was_up)
+ xae_transmit_locked(sc->ifp);
+
+ /* Schedule another check one second from now. */
+ callout_reset(&sc->xae_callout, hz, xae_tick, sc);
+}
+
+static void
+xae_init_locked(struct xae_softc *sc)
+{
+ struct ifnet *ifp;
+
+ XAE_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ return;
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ xae_setup_rxfilter(sc);
+
+ /* Enable the transmitter */
+ WRITE4(sc, XAE_TC, TC_TX);
+
+ /* Enable the receiver. */
+ WRITE4(sc, XAE_RCW1, RCW1_RX);
+
+ /*
+ * Call mii_mediachg() which will call back into xae_miibus_statchg()
+ * to set up the remaining config registers based on current media.
+ */
+ mii_mediachg(sc->mii_softc);
+ callout_reset(&sc->xae_callout, hz, xae_tick, sc);
+}
+
+static void
+xae_init(void *arg)
+{
+ struct xae_softc *sc;
+
+ sc = arg;
+
+ XAE_LOCK(sc);
+ xae_init_locked(sc);
+ XAE_UNLOCK(sc);
+}
+
+static void
+xae_media_status(struct ifnet * ifp, struct ifmediareq *ifmr)
+{
+ struct xae_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = sc->mii_softc;
+
+ XAE_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ XAE_UNLOCK(sc);
+}
+
+static int
+xae_media_change_locked(struct xae_softc *sc)
+{
+
+ return (mii_mediachg(sc->mii_softc));
+}
+
+static int
+xae_media_change(struct ifnet * ifp)
+{
+ struct xae_softc *sc;
+ int error;
+
+ sc = ifp->if_softc;
+
+ XAE_LOCK(sc);
+ error = xae_media_change_locked(sc);
+ XAE_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+xae_setup_rxfilter(struct xae_softc *sc)
+{
+ struct ifmultiaddr *ifma;
+ struct ifnet *ifp;
+ uint32_t reg;
+ uint8_t *ma;
+ int i;
+
+ XAE_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+
+ /*
+ * Set the multicast (group) filter hash.
+ */
+ if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
+ reg = READ4(sc, XAE_FFC);
+ reg |= FFC_PM;
+ WRITE4(sc, XAE_FFC, reg);
+ } else {
+ reg = READ4(sc, XAE_FFC);
+ reg &= ~FFC_PM;
+ WRITE4(sc, XAE_FFC, reg);
+
+ if_maddr_rlock(ifp);
+
+ i = 0;
+ CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ if (i >= XAE_MULTICAST_TABLE_SIZE)
+ break;
+
+ ma = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
+
+ reg = READ4(sc, XAE_FFC) & 0xffffff00;
+ reg |= i++;
+ WRITE4(sc, XAE_FFC, reg);
+
+ reg = (ma[0]);
+ reg |= (ma[1] << 8);
+ reg |= (ma[2] << 16);
+ reg |= (ma[3] << 24);
+ WRITE4(sc, XAE_FFV(0), reg);
+
+ reg = ma[4];
+ reg |= ma[5] << 8;
+ WRITE4(sc, XAE_FFV(1), reg);
+ }
+ if_maddr_runlock(ifp);
+ }
+
+ /*
+ * Set the primary address.
+ */
+ reg = sc->macaddr[0];
+ reg |= (sc->macaddr[1] << 8);
+ reg |= (sc->macaddr[2] << 16);
+ reg |= (sc->macaddr[3] << 24);
+ WRITE4(sc, XAE_UAW0, reg);
+
+ reg = sc->macaddr[4];
+ reg |= (sc->macaddr[5] << 8);
+ WRITE4(sc, XAE_UAW1, reg);
+}
+
+static int
+xae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct xae_softc *sc;
+ struct mii_data *mii;
+ struct ifreq *ifr;
+ int mask, error;
+
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ XAE_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI))
+ xae_setup_rxfilter(sc);
+ } else {
+ if (!sc->is_detaching)
+ xae_init_locked(sc);
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ xae_stop_locked(sc);
+ }
+ sc->if_flags = ifp->if_flags;
+ XAE_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ XAE_LOCK(sc);
+ xae_setup_rxfilter(sc);
+ XAE_UNLOCK(sc);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ mii = sc->mii_softc;
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ break;
+ case SIOCSIFCAP:
+ mask = ifp->if_capenable ^ ifr->ifr_reqcap;
+ if (mask & IFCAP_VLAN_MTU) {
+ /* No work to do except acknowledge the change took */
+ ifp->if_capenable ^= IFCAP_VLAN_MTU;
+ }
+ break;
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void
+xae_intr(void *arg)
+{
+
+}
+
+static int
+xae_get_hwaddr(struct xae_softc *sc, uint8_t *hwaddr)
+{
+ phandle_t node;
+ int len;
+
+ node = ofw_bus_get_node(sc->dev);
+
+ /* Check if there is property */
+ if ((len = OF_getproplen(node, "local-mac-address")) <= 0)
+ return (EINVAL);
+
+ if (len != ETHER_ADDR_LEN)
+ return (EINVAL);
+
+ OF_getprop(node, "local-mac-address", hwaddr,
+ ETHER_ADDR_LEN);
+
+ return (0);
+}
+
+static int
+mdio_wait(struct xae_softc *sc)
+{
+ uint32_t reg;
+ int timeout;
+
+ timeout = 200;
+
+ do {
+ reg = READ4(sc, XAE_MDIO_CTRL);
+ if (reg & MDIO_CTRL_READY)
+ break;
+ DELAY(1);
+ } while (timeout--);
+
+ if (timeout <= 0) {
+ printf("Failed to get MDIO ready\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+xae_miibus_read_reg(device_t dev, int phy, int reg)
+{
+ struct xae_softc *sc;
+ uint32_t mii;
+ int rv;
+
+ sc = device_get_softc(dev);
+
+ if (mdio_wait(sc))
+ return (0);
+
+ mii = MDIO_CTRL_TX_OP_READ | MDIO_CTRL_INITIATE;
+ mii |= (reg << MDIO_TX_REGAD_S);
+ mii |= (phy << MDIO_TX_PHYAD_S);
+
+ WRITE4(sc, XAE_MDIO_CTRL, mii);
+
+ if (mdio_wait(sc))
+ return (0);
+
+ rv = READ4(sc, XAE_MDIO_READ);
+
+#ifndef __rtems__
+ return (rv);
+#else /* __rtems__ */
+ return (rv & 0xFFFF);
+#endif /* __rtems__ */
+}
+
+static int
+xae_miibus_write_reg(device_t dev, int phy, int reg, int val)
+{
+ struct xae_softc *sc;
+ uint32_t mii;
+
+ sc = device_get_softc(dev);
+
+ if (mdio_wait(sc))
+ return (1);
+
+ mii = MDIO_CTRL_TX_OP_WRITE | MDIO_CTRL_INITIATE;
+ mii |= (reg << MDIO_TX_REGAD_S);
+ mii |= (phy << MDIO_TX_PHYAD_S);
+
+ WRITE4(sc, XAE_MDIO_WRITE, val);
+ WRITE4(sc, XAE_MDIO_CTRL, mii);
+
+ if (mdio_wait(sc))
+ return (1);
+
+ return (0);
+}
+
+static void
+xae_phy_fixup(struct xae_softc *sc)
+{
+ uint32_t reg;
+ device_t dev;
+
+ dev = sc->dev;
+
+ do {
+ WRITE_TI_EREG(sc, DP83867_SGMIICTL1, SGMIICTL1_SGMII_6W);
+ PHY_WR(sc, DP83867_PHYCR, PHYCR_SGMII_EN);
+
+ reg = PHY_RD(sc, DP83867_CFG2);
+ reg &= ~CFG2_SPEED_OPT_ATTEMPT_CNT_M;
+ reg |= (CFG2_SPEED_OPT_ATTEMPT_CNT_4);
+ reg |= CFG2_INTERRUPT_POLARITY;
+ reg |= CFG2_SPEED_OPT_ENHANCED_EN;
+ reg |= CFG2_SPEED_OPT_10M_EN;
+ PHY_WR(sc, DP83867_CFG2, reg);
+
+ WRITE_TI_EREG(sc, DP83867_CFG4, CFG4_SGMII_TMR);
+ PHY_WR(sc, MII_BMCR,
+ BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_RESET);
+ } while (PHY1_RD(sc, MII_BMCR) == 0x0ffff);
+
+ do {
+ PHY1_WR(sc, MII_BMCR,
+ BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_STARTNEG);
+ DELAY(40000);
+ } while ((PHY1_RD(sc, MII_BMSR) & BMSR_ACOMP) == 0);
+}
+
+static int
+setup_xdma(struct xae_softc *sc)
+{
+ device_t dev;
+ vmem_t *vmem;
+ int error;
+
+ dev = sc->dev;
+
+ /* Get xDMA controller */
+ sc->xdma_tx = xdma_ofw_get(sc->dev, "tx");
+ if (sc->xdma_tx == NULL) {
+ device_printf(dev, "Could not find DMA controller.\n");
+ return (ENXIO);
+ }
+
+ sc->xdma_rx = xdma_ofw_get(sc->dev, "rx");
+ if (sc->xdma_rx == NULL) {
+ device_printf(dev, "Could not find DMA controller.\n");
+ return (ENXIO);
+ }
+
+ /* Alloc xDMA TX virtual channel. */
+ sc->xchan_tx = xdma_channel_alloc(sc->xdma_tx, 0);
+ if (sc->xchan_tx == NULL) {
+ device_printf(dev, "Can't alloc virtual DMA TX channel.\n");
+ return (ENXIO);
+ }
+
+ /* Setup interrupt handler. */
+ error = xdma_setup_intr(sc->xchan_tx,
+ xae_xdma_tx_intr, sc, &sc->ih_tx);
+ if (error) {
+ device_printf(sc->dev,
+ "Can't setup xDMA TX interrupt handler.\n");
+ return (ENXIO);
+ }
+
+ /* Alloc xDMA RX virtual channel. */
+ sc->xchan_rx = xdma_channel_alloc(sc->xdma_rx, 0);
+ if (sc->xchan_rx == NULL) {
+ device_printf(dev, "Can't alloc virtual DMA RX channel.\n");
+ return (ENXIO);
+ }
+
+ /* Setup interrupt handler. */
+ error = xdma_setup_intr(sc->xchan_rx,
+ xae_xdma_rx_intr, sc, &sc->ih_rx);
+ if (error) {
+ device_printf(sc->dev,
+ "Can't setup xDMA RX interrupt handler.\n");
+ return (ENXIO);
+ }
+
+#ifndef __rtems__
+ /* Setup bounce buffer */
+ vmem = xdma_get_memory(dev);
+ if (vmem) {
+ xchan_set_memory(sc->xchan_tx, vmem);
+ xchan_set_memory(sc->xchan_rx, vmem);
+ }
+#endif /* __rtems__ */
+
+ xdma_prep_sg(sc->xchan_tx,
+ TX_QUEUE_SIZE, /* xchan requests queue size */
+ MCLBYTES, /* maxsegsize */
+ 8, /* maxnsegs */
+ 16, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR);
+
+ xdma_prep_sg(sc->xchan_rx,
+ RX_QUEUE_SIZE, /* xchan requests queue size */
+ MCLBYTES, /* maxsegsize */
+ 1, /* maxnsegs */
+ 16, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR);
+
+ return (0);
+}
+
+static int
+xae_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "xlnx,axi-ethernet-1.00.a"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Xilinx AXI Ethernet");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+xae_attach(device_t dev)
+{
+ struct xae_softc *sc;
+ struct ifnet *ifp;
+ phandle_t node;
+ uint32_t reg;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ if (setup_xdma(sc) != 0) {
+ device_printf(dev, "Could not setup xDMA.\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ sc->br = buf_ring_alloc(BUFRING_SIZE, M_DEVBUF,
+ M_NOWAIT, &sc->mtx);
+ if (sc->br == NULL)
+ return (ENOMEM);
+
+ if (bus_alloc_resources(dev, xae_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ device_printf(sc->dev, "Identification: %x\n",
+ READ4(sc, XAE_IDENT));
+
+ /* Get MAC addr */
+ if (xae_get_hwaddr(sc, sc->macaddr)) {
+ device_printf(sc->dev, "can't get mac\n");
+ return (ENXIO);
+ }
+
+ /* Enable MII clock */
+ reg = (MDIO_CLK_DIV_DEFAULT << MDIO_SETUP_CLK_DIV_S);
+ reg |= MDIO_SETUP_ENABLE;
+ WRITE4(sc, XAE_MDIO_SETUP, reg);
+ if (mdio_wait(sc))
+ return (ENXIO);
+
+ callout_init_mtx(&sc->xae_callout, &sc->mtx, 0);
+
+ /* Setup interrupt handler. */
+ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, xae_intr, sc, &sc->intr_cookie);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler.\n");
+ return (ENXIO);
+ }
+
+ /* Set up the ethernet interface. */
+ sc->ifp = ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "could not allocate ifp.\n");
+ return (ENXIO);
+ }
+
+ ifp->if_softc = sc;
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_transmit = xae_transmit;
+ ifp->if_qflush = xae_qflush;
+ ifp->if_ioctl = xae_ioctl;
+ ifp->if_init = xae_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
+ ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if (xae_get_phyaddr(node, &sc->phy_addr) != 0)
+ return (ENXIO);
+
+ /* Attach the mii driver. */
+ error = mii_attach(dev, &sc->miibus, ifp, xae_media_change,
+ xae_media_status, BMSR_DEFCAPMASK, sc->phy_addr,
+ MII_OFFSET_ANY, 0);
+
+ if (error != 0) {
+ device_printf(dev, "PHY attach failed\n");
+ return (ENXIO);
+ }
+ sc->mii_softc = device_get_softc(sc->miibus);
+
+ /* Apply vcu118 workaround. */
+ if (OF_getproplen(node, "xlnx,vcu118") >= 0)
+ xae_phy_fixup(sc);
+
+ /* All ready to run, attach the ethernet interface. */
+ ether_ifattach(ifp, sc->macaddr);
+ sc->is_attached = true;
+
+ xae_rx_enqueue(sc, NUM_RX_MBUF);
+ xdma_queue_submit(sc->xchan_rx);
+
+ return (0);
+}
+
+static int
+xae_detach(device_t dev)
+{
+ struct xae_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+
+ KASSERT(mtx_initialized(&sc->mtx), ("%s: mutex not initialized",
+ device_get_nameunit(dev)));
+
+ ifp = sc->ifp;
+
+ /* Only cleanup if attach succeeded. */
+ if (device_is_attached(dev)) {
+ XAE_LOCK(sc);
+ xae_stop_locked(sc);
+ XAE_UNLOCK(sc);
+ callout_drain(&sc->xae_callout);
+ ether_ifdetach(ifp);
+ }
+
+ if (sc->miibus != NULL)
+ device_delete_child(dev, sc->miibus);
+
+ if (ifp != NULL)
+ if_free(ifp);
+
+ mtx_destroy(&sc->mtx);
+
+ bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
+
+ bus_release_resources(dev, xae_spec, sc->res);
+
+ xdma_channel_free(sc->xchan_tx);
+ xdma_channel_free(sc->xchan_rx);
+ xdma_put(sc->xdma_tx);
+ xdma_put(sc->xdma_rx);
+
+ return (0);
+}
+
+static void
+xae_miibus_statchg(device_t dev)
+{
+ struct xae_softc *sc;
+ struct mii_data *mii;
+ uint32_t reg;
+
+ /*
+ * Called by the MII bus driver when the PHY establishes
+ * link to set the MAC interface registers.
+ */
+
+ sc = device_get_softc(dev);
+
+ XAE_ASSERT_LOCKED(sc);
+
+ mii = sc->mii_softc;
+
+ if (mii->mii_media_status & IFM_ACTIVE)
+ sc->link_is_up = true;
+ else
+ sc->link_is_up = false;
+
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ case IFM_1000_SX:
+ reg = SPEED_1000;
+ break;
+ case IFM_100_TX:
+ reg = SPEED_100;
+ break;
+ case IFM_10_T:
+ reg = SPEED_10;
+ break;
+ case IFM_NONE:
+ sc->link_is_up = false;
+ return;
+ default:
+ sc->link_is_up = false;
+ device_printf(dev, "Unsupported media %u\n",
+ IFM_SUBTYPE(mii->mii_media_active));
+ return;
+ }
+
+ WRITE4(sc, XAE_SPEED, reg);
+}
+
+static device_method_t xae_methods[] = {
+ DEVMETHOD(device_probe, xae_probe),
+ DEVMETHOD(device_attach, xae_attach),
+ DEVMETHOD(device_detach, xae_detach),
+
+ /* MII Interface */
+ DEVMETHOD(miibus_readreg, xae_miibus_read_reg),
+ DEVMETHOD(miibus_writereg, xae_miibus_write_reg),
+ DEVMETHOD(miibus_statchg, xae_miibus_statchg),
+
+ { 0, 0 }
+};
+
+driver_t xae_driver = {
+ "xae",
+ xae_methods,
+ sizeof(struct xae_softc),
+};
+
+static devclass_t xae_devclass;
+
+DRIVER_MODULE(xae, simplebus, xae_driver, xae_devclass, 0, 0);
+DRIVER_MODULE(miibus, xae, miibus_driver, miibus_devclass, 0, 0);
+
+MODULE_DEPEND(xae, ether, 1, 1, 1);
+MODULE_DEPEND(xae, miibus, 1, 1, 1);
diff --git a/freebsd/sys/dev/xilinx/if_xaereg.h b/freebsd/sys/dev/xilinx/if_xaereg.h
new file mode 100644
index 00000000..1c4f0493
--- /dev/null
+++ b/freebsd/sys/dev/xilinx/if_xaereg.h
@@ -0,0 +1,122 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _DEV_XILINX_IF_XAE_H_
+#define _DEV_XILINX_IF_XAE_H_
+
+#define XAE_RAF 0x00000 /* Reset and Address Filter RW */
+#define XAE_TPF 0x00004 /* Transmit Pause Frame RW */
+#define XAE_IFGP 0x00008 /* Transmit Inter Frame Gap Adjustment RW */
+#define XAE_IS 0x0000C /* Interrupt Status register RW */
+#define XAE_IP 0x00010 /* Interrupt Pending register RO */
+#define XAE_IE 0x00014 /* Interrupt Enable register RW */
+#define XAE_TTAG 0x00018 /* Transmit VLAN Tag RW */
+#define XAE_RTAG 0x0001C /* Receive VLAN Tag RW */
+#define XAE_UAWL 0x00020 /* Unicast Address Word Lower RW */
+#define XAE_UAWU 0x00024 /* Unicast Address Word Upper RW */
+#define XAE_TPID0 0x00028 /* VLAN TPID Word 0 RW */
+#define XAE_TPID1 0x0002C /* VLAN TPID Word 1 RW */
+#define XAE_PPST 0x00030 /* PCS PMA Status register RO */
+#define XAE_STATCNT(n) (0x00200 + 0x8 * (n)) /* Statistics Counters RO */
+#define XAE_RCW0 0x00400 /* Receive Configuration Word 0 Register RW */
+#define XAE_RCW1 0x00404 /* Receive Configuration Word 1 Register RW */
+#define RCW1_RX (1 << 28) /* Receive Enable */
+#define XAE_TC 0x00408 /* Transmitter Configuration register RW */
+#define TC_TX (1 << 28) /* Transmit Enable */
+#define XAE_FCC 0x0040C /* Flow Control Configuration register RW */
+#define FCC_FCRX (1 << 29) /* Flow Control Enable (RX) */
+#define XAE_SPEED 0x00410 /* MAC Speed Configuration Word RW */
+#define SPEED_CONF_S 30
+#define SPEED_10 (0 << SPEED_CONF_S)
+#define SPEED_100 (1 << SPEED_CONF_S)
+#define SPEED_1000 (2 << SPEED_CONF_S)
+#define XAE_RX_MAXFRAME 0x00414 /* RX Max Frame Configuration RW */
+#define XAE_TX_MAXFRAME 0x00418 /* TX Max Frame Configuration RW */
+#define XAE_TX_TIMESTMP 0x0041C /* TX timestamp adjust control register RW */
+#define XAE_IDENT 0x004F8 /* Identification register RO */
+#define XAE_ABILITY 0x004FC /* Ability register RO */
+#define XAE_MDIO_SETUP 0x00500 /* MDIO Setup register RW */
+#define MDIO_SETUP_ENABLE (1 << 6) /* MDIO Enable */
+#define MDIO_SETUP_CLK_DIV_S 0 /* Clock Divide */
+#define XAE_MDIO_CTRL 0x00504 /* MDIO Control RW */
+#define MDIO_TX_REGAD_S 16 /* This controls the register address being accessed. */
+#define MDIO_TX_REGAD_M (0x1f << MDIO_TX_REGAD_S)
+#define MDIO_TX_PHYAD_S 24 /* This controls the PHY address being accessed. */
+#define MDIO_TX_PHYAD_M (0x1f << MDIO_TX_PHYAD_S)
+#define MDIO_CTRL_TX_OP_S 14 /* Type of access performed. */
+#define MDIO_CTRL_TX_OP_M (0x3 << MDIO_CTRL_TX_OP_S)
+#define MDIO_CTRL_TX_OP_READ (0x2 << MDIO_CTRL_TX_OP_S)
+#define MDIO_CTRL_TX_OP_WRITE (0x1 << MDIO_CTRL_TX_OP_S)
+#define MDIO_CTRL_INITIATE (1 << 11) /* Start an MDIO transfer. */
+#define MDIO_CTRL_READY (1 << 7) /* MDIO is ready for a new xfer */
+#define XAE_MDIO_WRITE 0x00508 /* MDIO Write Data RW */
+#define XAE_MDIO_READ 0x0050C /* MDIO Read Data RO */
+#define XAE_INT_STATUS 0x00600 /* Interrupt Status Register RW */
+#define XAE_INT_PEND 0x00610 /* Interrupt Pending Register RO */
+#define XAE_INT_ENABLE 0x00620 /* Interrupt Enable Register RW */
+#define XAE_INT_CLEAR 0x00630 /* Interrupt Clear Register RW */
+#define XAE_UAW0 0x00700 /* Unicast Address Word 0 register (UAW0) RW */
+#define XAE_UAW1 0x00704 /* Unicast Address Word 1 register (UAW1) RW */
+#define XAE_FFC 0x00708 /* Frame Filter Control RW */
+#define FFC_PM (1 << 31) /* Promiscuous Mode */
+#define XAE_FFV(n) (0x00710 + 0x4 * (n)) /* Frame Filter Value RW */
+#define XAE_FFMV(n) (0x00750 + 0x4 * (n)) /* Frame Filter Mask Value RW */
+#define XAE_TX_VLAN(n) (0x04000 + 0x4 * (n)) /* Transmit VLAN Data Table RW */
+#define XAE_RX_VLAN(n) (0x08000 + 0x4 * (n)) /* Receive VLAN Data Table RW */
+#define XAE_AVB(n) (0x10000 + 0x4 * (n)) /* Ethernet AVB RW */
+#define XAE_MAT(n) (0x20000 + 0x4 * (n)) /* Multicast Address Table RW */
+
+#define XAE_MULTICAST_TABLE_SIZE 4
+
+/* RX statistical counters. */
+#define RX_BYTES 0
+#define RX_GOOD_FRAMES 18
+#define RX_FRAME_CHECK_SEQ_ERROR 19
+#define RX_GOOD_MCASTS 21
+#define RX_LEN_OUT_OF_RANGE 23
+#define RX_ALIGNMENT_ERRORS 40
+
+/* TX statistical counters. */
+#define TX_BYTES 1
+#define TX_GOOD_FRAMES 27
+#define TX_GOOD_MCASTS 29
+#define TX_GOOD_UNDERRUN_ERRORS 30
+#define TX_SINGLE_COLLISION_FRAMES 34
+#define TX_MULTI_COLLISION_FRAMES 35
+#define TX_LATE_COLLISIONS 37
+#define TX_EXCESS_COLLISIONS 38
+
+#define XAE_MAX_COUNTERS 43
+
+#endif /* _DEV_XILINX_IF_XAE_H_ */
diff --git a/freebsd/sys/dev/xilinx/if_xaevar.h b/freebsd/sys/dev/xilinx/if_xaevar.h
new file mode 100644
index 00000000..30396537
--- /dev/null
+++ b/freebsd/sys/dev/xilinx/if_xaevar.h
@@ -0,0 +1,80 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _DEV_XILINX_IF_XAEVAR_H_
+#define _DEV_XILINX_IF_XAEVAR_H_
+
+#include <dev/xdma/xdma.h>
+
+/*
+ * Driver data and defines.
+ */
+#define RX_DESC_COUNT 1024
+#define TX_DESC_COUNT 1024
+
+struct xae_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+ uint8_t macaddr[ETHER_ADDR_LEN];
+ device_t miibus;
+ struct mii_data * mii_softc;
+ struct ifnet *ifp;
+ int if_flags;
+ struct mtx mtx;
+ void * intr_cookie;
+ struct callout xae_callout;
+ boolean_t link_is_up;
+ boolean_t is_attached;
+ boolean_t is_detaching;
+ int phy_addr;
+
+ /* xDMA TX */
+ xdma_controller_t *xdma_tx;
+ xdma_channel_t *xchan_tx;
+ void *ih_tx;
+
+ /* xDMA RX */
+ xdma_controller_t *xdma_rx;
+ xdma_channel_t *xchan_rx;
+ void *ih_rx;
+
+ struct buf_ring *br;
+
+ /* Counters */
+ uint64_t counters[XAE_MAX_COUNTERS];
+};
+
+#endif /* _DEV_XILINX_IF_XAEVAR_H_ */
diff --git a/freebsd/sys/fs/nfs/nfs_commonkrpc.c b/freebsd/sys/fs/nfs/nfs_commonkrpc.c
index 8ddcc6bb..7cfd5269 100644
--- a/freebsd/sys/fs/nfs/nfs_commonkrpc.c
+++ b/freebsd/sys/fs/nfs/nfs_commonkrpc.c
@@ -219,7 +219,7 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp,
nconf = getnetconfigent("udp6");
else
nconf = getnetconfigent("tcp6");
-
+
pktscale = nfs_bufpackets;
if (pktscale < 2)
pktscale = 2;
@@ -237,7 +237,7 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp,
*/
so = NULL;
saddr = NFSSOCKADDR(nrp->nr_nam, struct sockaddr *);
- error = socreate(saddr->sa_family, &so, nrp->nr_sotype,
+ error = socreate(saddr->sa_family, &so, nrp->nr_sotype,
nrp->nr_soproto, td->td_ucred, td);
if (error) {
td->td_ucred = origcred;
@@ -391,7 +391,7 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp,
/*
* For UDP, there are 2 timeouts:
* - CLSET_RETRY_TIMEOUT sets the initial timeout for the timer
- * that does a retransmit of an RPC request using the same
+ * that does a retransmit of an RPC request using the same
* socket and xid. This is what you normally want to do,
* since NFS servers depend on "same xid" for their
* Duplicate Request Cache.
@@ -741,7 +741,7 @@ newnfs_request(struct nfsrv_descript *nd, struct nfsmount *nmp,
if (dtrace_nfscl_nfs234_start_probe != NULL) {
uint32_t probe_id;
int probe_procnum;
-
+
if (nd->nd_flag & ND_NFSV4) {
probe_id =
nfscl_nfs4_start_probes[nd->nd_procnum];
@@ -1143,9 +1143,9 @@ tryagain:
j != NFSERR_STALESTATEID &&
j != NFSERR_BADSTATEID &&
j != NFSERR_BADSEQID &&
- j != NFSERR_BADXDR &&
+ j != NFSERR_BADXDR &&
j != NFSERR_RESOURCE &&
- j != NFSERR_NOFILEHANDLE)))
+ j != NFSERR_NOFILEHANDLE)))
nd->nd_flag |= ND_INCRSEQID;
}
/*
@@ -1264,13 +1264,13 @@ static int
nfs_sig_pending(sigset_t set)
{
int i;
-
+
for (i = 0 ; i < nitems(newnfs_sig_set); i++)
if (SIGISMEMBER(set, newnfs_sig_set[i]))
return (1);
return (0);
}
-
+
/*
* The set/restore sigmask functions are used to (temporarily) overwrite
* the thread td_sigmask during an RPC call (for example). These are also
@@ -1283,7 +1283,7 @@ newnfs_set_sigmask(struct thread *td, sigset_t *oldset)
sigset_t newset;
int i;
struct proc *p;
-
+
SIGFILLSET(newset);
if (td == NULL)
td = curthread; /* XXX */
@@ -1349,7 +1349,7 @@ newnfs_sigintr(struct nfsmount *nmp, struct thread *td)
#ifndef __rtems__
struct proc *p;
sigset_t tmpset;
-
+
/* Terminate all requests while attempting a forced unmount. */
if (NFSCL_FORCEDISM(nmp->nm_mountp))
return (EIO);
@@ -1433,7 +1433,7 @@ nfs_up(struct nfsmount *nmp, struct thread *td, const char *msg,
VQ_NOTRESP, 1);
} else
mtx_unlock(&nmp->nm_mtx);
-
+
mtx_lock(&nmp->nm_mtx);
if ((flags & NFSSTA_LOCKTIMEO) && (nmp->nm_state & NFSSTA_LOCKTIMEO)) {
nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
@@ -1443,3 +1443,4 @@ nfs_up(struct nfsmount *nmp, struct thread *td, const char *msg,
} else
mtx_unlock(&nmp->nm_mtx);
}
+
diff --git a/freebsd/sys/fs/nfs/nfs_commonport.c b/freebsd/sys/fs/nfs/nfs_commonport.c
index 80648a13..f3b4ecef 100644
--- a/freebsd/sys/fs/nfs/nfs_commonport.c
+++ b/freebsd/sys/fs/nfs/nfs_commonport.c
@@ -818,3 +818,4 @@ DECLARE_MODULE(nfscommon, nfscommon_mod, SI_SUB_VFS, SI_ORDER_ANY);
MODULE_VERSION(nfscommon, 1);
MODULE_DEPEND(nfscommon, nfssvc, 1, 1, 1);
MODULE_DEPEND(nfscommon, krpc, 1, 1, 1);
+
diff --git a/freebsd/sys/fs/nfs/nfs_commonsubs.c b/freebsd/sys/fs/nfs/nfs_commonsubs.c
index f7c93ce0..c1dbedf4 100644
--- a/freebsd/sys/fs/nfs/nfs_commonsubs.c
+++ b/freebsd/sys/fs/nfs/nfs_commonsubs.c
@@ -4770,3 +4770,4 @@ nfsv4_findmirror(struct nfsmount *nmp)
}
return (ds);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs.h b/freebsd/sys/fs/nfsclient/nfs.h
index f76a60a7..ce1747a2 100644
--- a/freebsd/sys/fs/nfsclient/nfs.h
+++ b/freebsd/sys/fs/nfsclient/nfs.h
@@ -96,8 +96,7 @@ void ncl_doio_directwrite(struct buf *);
int ncl_bioread(struct vnode *, struct uio *, int, struct ucred *);
int ncl_biowrite(struct vnode *, struct uio *, int, struct ucred *);
int ncl_vinvalbuf(struct vnode *, int, struct thread *, int);
-int ncl_asyncio(struct
- nfsmount *, struct buf *, struct ucred *,
+int ncl_asyncio(struct nfsmount *, struct buf *, struct ucred *,
struct thread *);
int ncl_doio(struct vnode *, struct buf *, struct ucred *, struct thread *,
int);
diff --git a/freebsd/sys/fs/nfsclient/nfs_clbio.c b/freebsd/sys/fs/nfsclient/nfs_clbio.c
index cd157db6..9467b6ae 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clbio.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clbio.c
@@ -1695,7 +1695,7 @@ ncl_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td,
PROC_UNLOCK(p);
#else /* __rtems__ */
panic("nfsclient: text file modification: want to killproc");
-#endif /* _-rtems__ */
+#endif /* __rtems__ */
} else
NFSUNLOCKNODE(np);
}
@@ -1923,3 +1923,4 @@ ncl_meta_setsize(struct vnode *vp, struct thread *td, u_quad_t nsize)
}
return(error);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clkrpc.c b/freebsd/sys/fs/nfsclient/nfs_clkrpc.c
index c3260ccf..4974e998 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clkrpc.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clkrpc.c
@@ -298,3 +298,4 @@ nfsrvd_cbinit(int terminating)
NFSD_LOCK();
}
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clport.c b/freebsd/sys/fs/nfsclient/nfs_clport.c
index ed307700..5358170a 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clport.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clport.c
@@ -1452,3 +1452,4 @@ MODULE_DEPEND(nfscl, nfscommon, 1, 1, 1);
MODULE_DEPEND(nfscl, krpc, 1, 1, 1);
MODULE_DEPEND(nfscl, nfssvc, 1, 1, 1);
MODULE_DEPEND(nfscl, nfslock, 1, 1, 1);
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clrpcops.c b/freebsd/sys/fs/nfsclient/nfs_clrpcops.c
index e3101f76..ce0aad47 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clrpcops.c
@@ -3205,10 +3205,23 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
} else {
dp->d_fileno = nfsva.na_fileid;
}
+#ifndef __rtems__
*tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
ncookie.lval[0];
+#else /* __rtems__ */
+ memcpy(tl2, &ncookie.lval[0], sizeof(*tl2));
+ tl2++;
+ cookiep->nfsuquad[0] = cookie.lval[0] =
+ ncookie.lval[0];
+#endif /* __rtems__ */
+#ifndef __rtems__
*tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
ncookie.lval[1];
+#else /* __rtems__ */
+ memcpy(tl2, &ncookie.lval[1], sizeof(*tl2));
+ cookiep->nfsuquad[1] = cookie.lval[1] =
+ ncookie.lval[1];
+#endif /* __rtems__ */
}
more_dirs = fxdr_unsigned(int, *tl);
}
@@ -7727,3 +7740,4 @@ out:
}
return (laystat);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clstate.c b/freebsd/sys/fs/nfsclient/nfs_clstate.c
index 6a93b183..1a82fd73 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clstate.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clstate.c
@@ -5457,3 +5457,4 @@ tryagain:
NFSUNLOCKCLSTATE();
return (0);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clsubs.c b/freebsd/sys/fs/nfsclient/nfs_clsubs.c
index af0f3c62..a0c794dd 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clsubs.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clsubs.c
@@ -394,3 +394,4 @@ ncl_init(struct vfsconf *vfsp)
return (0);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clvfsops.c b/freebsd/sys/fs/nfsclient/nfs_clvfsops.c
index 70a897fe..95731499 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clvfsops.c
@@ -2060,3 +2060,4 @@ void nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen)
nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen);
nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen);
}
+
diff --git a/freebsd/sys/fs/nfsclient/nfs_clvnops.c b/freebsd/sys/fs/nfsclient/nfs_clvnops.c
index d52d1b28..8da6bfe2 100644
--- a/freebsd/sys/fs/nfsclient/nfs_clvnops.c
+++ b/freebsd/sys/fs/nfsclient/nfs_clvnops.c
@@ -817,7 +817,7 @@ nfs_close(struct vop_close_args *ap)
int cm = newnfs_commit_on_close ? 1 : 0;
error = ncl_flush(vp, MNT_WAIT, ap->a_td, cm, 0);
/* np->n_flag &= ~NMODIFIED; */
- } else if (NFS_ISV4(vp)) {
+ } else if (NFS_ISV4(vp)) {
if (nfscl_mustflush(vp) != 0) {
int cm = newnfs_commit_on_close ? 1 : 0;
error = ncl_flush(vp, MNT_WAIT, ap->a_td,
@@ -833,10 +833,10 @@ nfs_close(struct vop_close_args *ap)
}
NFSLOCKNODE(np);
}
- /*
+ /*
* Invalidate the attribute cache in all cases.
* An open is going to fetch fresh attrs any way, other procs
- * on this node that have file open will be forced to do an
+ * on this node that have file open will be forced to do an
* otw attr fetch, but this is safe.
* --> A user found that their RPC count dropped by 20% when
* this was commented out and I can't see any requirement
@@ -891,7 +891,7 @@ nfs_close(struct vop_close_args *ap)
np->n_directio_asyncwr));
if (newnfs_directio_enable && (fmode & O_DIRECT) && (vp->v_type == VREG)) {
NFSLOCKNODE(np);
- KASSERT((np->n_directio_opens > 0),
+ KASSERT((np->n_directio_opens > 0),
("nfs_close: unexpectedly value (0) of n_directio_opens\n"));
np->n_directio_opens--;
if (np->n_directio_opens == 0)
@@ -1021,7 +1021,7 @@ nfs_setattr(struct vop_setattr_args *ap)
vap->va_mode == (mode_t)VNOVAL &&
vap->va_uid == (uid_t)VNOVAL &&
vap->va_gid == (gid_t)VNOVAL)
- return (0);
+ return (0);
vap->va_size = VNOVAL;
break;
default:
@@ -1072,7 +1072,7 @@ nfs_setattr(struct vop_setattr_args *ap)
}
} else {
NFSLOCKNODE(np);
- if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) &&
+ if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) &&
(np->n_flag & NMODIFIED) && vp->v_type == VREG) {
NFSUNLOCKNODE(np);
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
@@ -1146,7 +1146,7 @@ nfs_lookup(struct vop_lookup_args *ap)
struct nfsvattr dnfsva, nfsva;
struct vattr vattr;
struct timespec nctime;
-
+
*vpp = NULLVP;
if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
@@ -1223,7 +1223,7 @@ nfs_lookup(struct vop_lookup_args *ap)
cache_purge(newvp);
if (dvp != newvp)
vput(newvp);
- else
+ else
vrele(newvp);
*vpp = NULLVP;
} else if (error == ENOENT) {
@@ -1379,7 +1379,7 @@ nfs_lookup(struct vop_lookup_args *ap)
(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
0, 1);
else if ((flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) &&
- !(np->n_flag & NMODIFIED)) {
+ !(np->n_flag & NMODIFIED)) {
/*
* Flush the attribute cache when opening a
* leaf node to ensure that fresh attributes
@@ -1711,7 +1711,7 @@ again:
/* try again without setting uid/gid */
vap->va_uid = (uid_t)VNOVAL;
vap->va_gid = (uid_t)VNOVAL;
- error = nfsrpc_setattr(newvp, vap, NULL,
+ error = nfsrpc_setattr(newvp, vap, NULL,
cnp->cn_cred, cnp->cn_thread, &nfsva,
&attrflag, NULL);
}
@@ -1899,7 +1899,7 @@ nfs_rename(struct vop_rename_args *ap)
* under NFSV3. NFSV2 does not have this problem because
* ( as far as I can tell ) it flushes dirty buffers more
* often.
- *
+ *
* Skip the rename operation if the fsync fails, this can happen
* due to the server's volume being full, when we pushed out data
* that was written back to our cache earlier. Not checking for
@@ -2311,10 +2311,10 @@ nfs_readdir(struct vop_readdir_args *ap)
ssize_t tresid, left;
int error = 0;
struct vattr vattr;
-
+
if (ap->a_eofflag != NULL)
*ap->a_eofflag = 0;
- if (vp->v_type != VDIR)
+ if (vp->v_type != VDIR)
return(EPERM);
/*
@@ -2358,7 +2358,7 @@ nfs_readdir(struct vop_readdir_args *ap)
if (ap->a_eofflag != NULL)
*ap->a_eofflag = 1;
}
-
+
/* Add the partial DIRBLKSIZ (left) back in. */
uio->uio_resid += left;
return (error);
@@ -2392,7 +2392,7 @@ ncl_readdirrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
cookie = *cookiep;
ncl_dircookie_unlock(dnp);
} else {
- ncl_dircookie_unlock(dnp);
+ ncl_dircookie_unlock(dnp);
return (NFSERR_BAD_COOKIE);
}
@@ -2510,11 +2510,11 @@ nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
sp->s_dvp = dvp;
VREF(dvp);
- /*
+ /*
* Fudge together a funny name.
- * Changing the format of the funny name to accommodate more
+ * Changing the format of the funny name to accommodate more
* sillynames per directory.
- * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is
+ * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is
* CPU ticks since boot.
*/
#ifndef __rtems__
@@ -2524,8 +2524,8 @@ nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
#endif /* __rtems__ */
lticks = (unsigned int)ticks;
for ( ; ; ) {
- sp->s_namlen = sprintf(sp->s_name,
- ".nfs.%08x.%04x4.4", lticks,
+ sp->s_namlen = sprintf(sp->s_name,
+ ".nfs.%08x.%04x4.4", lticks,
pid);
if (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_thread, NULL))
@@ -3061,12 +3061,12 @@ loop:
while (np->n_directio_asyncwr > 0) {
np->n_flag |= NFSYNCWAIT;
error = newnfs_msleep(td, &np->n_directio_asyncwr,
- &np->n_mtx, slpflag | (PRIBIO + 1),
+ &np->n_mtx, slpflag | (PRIBIO + 1),
"nfsfsync", 0);
if (error) {
if (newnfs_sigintr(nmp, td)) {
NFSUNLOCKNODE(np);
- error = EINTR;
+ error = EINTR;
goto done;
}
}
@@ -3126,7 +3126,7 @@ nfs_advlock(struct vop_advlock_args *ap)
struct vattr va;
int ret, error = EOPNOTSUPP;
u_quad_t size;
-
+
ret = NFSVOPLOCK(vp, LK_SHARED);
if (ret != 0)
return (EBADF);
@@ -3261,7 +3261,7 @@ nfs_advlockasync(struct vop_advlockasync_args *ap)
struct vnode *vp = ap->a_vp;
u_quad_t size;
int error;
-
+
if (NFS_ISV4(vp))
return (EOPNOTSUPP);
error = NFSVOPLOCK(vp, LK_SHARED);
@@ -3409,7 +3409,7 @@ nfsfifo_read(struct vop_read_args *ap)
vfs_timestamp(&np->n_atim);
NFSUNLOCKNODE(np);
error = fifo_specops.vop_read(ap);
- return error;
+ return error;
#else /* __rtems__ */
return (EINVAL);
#endif /* __rtems__ */
@@ -3646,3 +3646,4 @@ nfs_pathconf(struct vop_pathconf_args *ap)
}
return (error);
}
+
diff --git a/freebsd/sys/kern/kern_prot.c b/freebsd/sys/kern/kern_prot.c
index 0df18a12..02db0cc7 100644
--- a/freebsd/sys/kern/kern_prot.c
+++ b/freebsd/sys/kern/kern_prot.c
@@ -1852,6 +1852,7 @@ crget(void)
struct ucred *
crhold(struct ucred *cr)
{
+
#ifdef __rtems__
if (cr == NULL)
return (cr);
diff --git a/freebsd/sys/kern/subr_bus.c b/freebsd/sys/kern/subr_bus.c
index e43d0030..c4b8c04d 100644
--- a/freebsd/sys/kern/subr_bus.c
+++ b/freebsd/sys/kern/subr_bus.c
@@ -5588,7 +5588,6 @@ bus_data_generation_update(void)
bus_data_generation++;
}
-#ifndef __rtems__
int
bus_free_resource(device_t dev, int type, struct resource *r)
{
@@ -5597,6 +5596,7 @@ bus_free_resource(device_t dev, int type, struct resource *r)
return (bus_release_resource(dev, type, rman_get_rid(r), r));
}
+#ifndef __rtems__
device_t
device_lookup_by_name(const char *name)
{
diff --git a/freebsd/sys/kern/vfs_bio.c b/freebsd/sys/kern/vfs_bio.c
index 50e87ff8..b96ff7fe 100644
--- a/freebsd/sys/kern/vfs_bio.c
+++ b/freebsd/sys/kern/vfs_bio.c
@@ -2967,7 +2967,7 @@ vfs_vmio_invalidate(struct buf *bp)
BUF_CHECK_MAPPED(bp);
#ifndef __rtems__
pmap_qremove(trunc_page((vm_offset_t)bp->b_data), bp->b_npages);
-#endif /* _-rtems__ */
+#endif /* __rtems__ */
} else
BUF_CHECK_UNMAPPED(bp);
/*
diff --git a/freebsd/sys/kern/vfs_export.c b/freebsd/sys/kern/vfs_export.c
index 511ab05d..5bf7e09a 100644
--- a/freebsd/sys/kern/vfs_export.c
+++ b/freebsd/sys/kern/vfs_export.c
@@ -538,3 +538,4 @@ vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
return (0);
}
+
diff --git a/freebsd/sys/kern/vfs_syscalls.c b/freebsd/sys/kern/vfs_syscalls.c
index 0b7e054a..ae1c5dd7 100644
--- a/freebsd/sys/kern/vfs_syscalls.c
+++ b/freebsd/sys/kern/vfs_syscalls.c
@@ -3536,7 +3536,6 @@ again:
AUDITVNODE1, pathseg, old, oldfd,
&cap_renameat_source_rights, td);
#else
-
NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1,
pathseg, old, oldfd,
&cap_renameat_source_rights, td);
diff --git a/freebsd/sys/kern/vfs_vnops.c b/freebsd/sys/kern/vfs_vnops.c
index 023d387d..30504b76 100644
--- a/freebsd/sys/kern/vfs_vnops.c
+++ b/freebsd/sys/kern/vfs_vnops.c
@@ -1252,7 +1252,6 @@ int
vn_io_fault_uiomove(char *data, int xfersize, struct uio *uio)
{
#ifndef __rtems__
- return (EFAULT);
struct uio transp_uio;
struct iovec transp_iov[1];
struct thread *td;
diff --git a/freebsd/sys/microblaze/include/machine/in_cksum.h b/freebsd/sys/microblaze/include/machine/in_cksum.h
new file mode 100644
index 00000000..d55b838b
--- /dev/null
+++ b/freebsd/sys/microblaze/include/machine/in_cksum.h
@@ -0,0 +1,83 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990 The Regents of the University of California.
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
+ * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#include <sys/cdefs.h>
+
+#define in_cksum(m, len) in_cksum_skip(m, len, 0)
+
+#if defined(IPVERSION) && (IPVERSION == 4)
+/*
+ * It it useful to have an Internet checksum routine which is inlineable
+ * and optimized specifically for the task of computing IP header checksums
+ * in the normal case (where there are no options and the header length is
+ * therefore always exactly five 32-bit words.
+ */
+#ifdef __CC_SUPPORTS___INLINE
+
+static __inline void
+in_cksum_update(struct ip *ip)
+{
+ int __tmpsum;
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256;
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
+}
+
+#else
+
+#define in_cksum_update(ip) \
+ do { \
+ int __tmpsum; \
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
+ } while(0)
+
+#endif
+#endif
+
+#ifdef _KERNEL
+#if defined(IPVERSION) && (IPVERSION == 4)
+u_int in_cksum_hdr(const struct ip *ip);
+#endif
+u_short in_addword(u_short sum, u_short b);
+u_short in_pseudo(u_int sum, u_int b, u_int c);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+#endif
+
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/freebsd/sys/nfs/nfs_nfssvc.c b/freebsd/sys/nfs/nfs_nfssvc.c
index ce03773a..b171393a 100644
--- a/freebsd/sys/nfs/nfs_nfssvc.c
+++ b/freebsd/sys/nfs/nfs_nfssvc.c
@@ -161,3 +161,4 @@ DECLARE_MODULE(nfssvc, nfssvc_mod, SI_SUB_VFS, SI_ORDER_ANY);
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfssvc, 1);
+
diff --git a/freebsd/sys/powerpc/include/machine/hid.h b/freebsd/sys/powerpc/include/machine/hid.h
new file mode 100644
index 00000000..1b038111
--- /dev/null
+++ b/freebsd/sys/powerpc/include/machine/hid.h
@@ -0,0 +1,224 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2000 Tsubai Masanari. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ * $NetBSD: hid.h,v 1.2 2001/08/22 21:05:25 matt Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _POWERPC_HID_H_
+#define _POWERPC_HID_H_
+
+/* Hardware Implementation Dependent registers for the PowerPC */
+#define HID0_RADIX 0x0080000000000000 /* Enable Radix page tables (POWER9) */
+
+#define HID0_EMCP 0x80000000 /* Enable machine check pin */
+#define HID0_DBP 0x40000000 /* Disable 60x bus parity generation */
+#define HID0_EBA 0x20000000 /* Enable 60x bus address parity checking */
+#define HID0_EBD 0x10000000 /* Enable 60x bus data parity checking */
+#define HID0_BCLK 0x08000000 /* CLK_OUT clock type selection */
+#define HID0_EICE 0x04000000 /* Enable ICE output */
+#define HID0_ECLK 0x02000000 /* CLK_OUT clock type selection */
+#define HID0_PAR 0x01000000 /* Disable precharge of ARTRY */
+#define HID0_STEN 0x01000000 /* Software table search enable (7450) */
+#define HID0_DEEPNAP 0x01000000 /* Enable deep nap mode (970) */
+#define HID0_HBATEN 0x00800000 /* High BAT enable (74[45][578]) */
+#define HID0_DOZE 0x00800000 /* Enable doze mode */
+#define HID0_NAP 0x00400000 /* Enable nap mode */
+#define HID0_SLEEP 0x00200000 /* Enable sleep mode */
+#define HID0_DPM 0x00100000 /* Enable Dynamic power management */
+#define HID0_RISEG 0x00080000 /* Read I-SEG */
+#define HID0_TG 0x00040000 /* Timebase Granularity (OEA64) */
+#define HID0_BHTCLR 0x00040000 /* Clear branch history table (7450) */
+#define HID0_EIEC 0x00040000 /* Enable internal error checking */
+#define HID0_XAEN 0x00020000 /* Enable eXtended Addressing (7450) */
+#define HID0_NHR 0x00010000 /* Not hard reset */
+#define HID0_ICE 0x00008000 /* Enable i-cache */
+#define HID0_DCE 0x00004000 /* Enable d-cache */
+#define HID0_ILOCK 0x00002000 /* i-cache lock */
+#define HID0_DLOCK 0x00001000 /* d-cache lock */
+#define HID0_ICFI 0x00000800 /* i-cache flush invalidate */
+#define HID0_DCFI 0x00000400 /* d-cache flush invalidate */
+#define HID0_SPD 0x00000200 /* Disable speculative cache access */
+#define HID0_XBSEN 0x00000100 /* Extended BAT block-size enable (7457) */
+#define HID0_IFEM 0x00000100 /* Enable M-bit for I-fetch */
+#define HID0_XBSEN 0x00000100 /* Extended BAT block size enable (7455+)*/
+#define HID0_SGE 0x00000080 /* Enable store gathering */
+#define HID0_DCFA 0x00000040 /* Data cache flush assist */
+#define HID0_BTIC 0x00000020 /* Enable BTIC */
+#define HID0_LRSTK 0x00000010 /* Link register stack enable (7450) */
+#define HID0_ABE 0x00000008 /* Enable address broadcast */
+#define HID0_FOLD 0x00000008 /* Branch folding enable (7450) */
+#define HID0_BHT 0x00000004 /* Enable branch history table */
+#define HID0_NOPTI 0x00000001 /* No-op the dcbt(st) */
+
+#define HID0_AIM_TBEN 0x04000000 /* Time base enable (7450) */
+
+#define HID0_E500_TBEN 0x00004000 /* Time Base and decr. enable */
+#define HID0_E500_SEL_TBCLK 0x00002000 /* Select Time Base clock */
+#define HID0_E500_MAS7UPDEN 0x00000080 /* Enable MAS7 update (e500v2) */
+
+#define HID0_E500MC_L2MMU_MHD 0x40000000 /* L2MMU Multiple Hit Detection */
+
+#define HID0_BITMASK \
+ "\20" \
+ "\040EMCP\037DBP\036EBA\035EBD\034BCLK\033EICE\032ECLK\031PAR" \
+ "\030DOZE\027NAP\026SLEEP\025DPM\024RISEG\023EIEC\022res\021NHR" \
+ "\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011IFEM" \
+ "\010SGE\007DCFA\006BTIC\005FBIOB\004ABE\003BHT\002NOPDST\001NOPTI"
+
+#define HID0_7450_BITMASK \
+ "\20" \
+ "\040EMCP\037b1\036b2\035b3\034b4\033TBEN\032b6\031STEN" \
+ "\030HBATEN\027NAP\026SLEEP\025DPM\024b12\023BHTCLR\022XAEN\021NHR" \
+ "\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011XBSEN" \
+ "\010SGE\007b25\006BTIC\005LRSTK\004FOLD\003BHT\002NOPDST\001NOPTI"
+
+#define HID0_E500_BITMASK \
+ "\20" \
+ "\040EMCP\037b1\036b2\035b3\034b4\033b5\032b6\031b7" \
+ "\030DOZE\027NAP\026SLEEP\025b11\024b12\023b13\022b14\021b15" \
+ "\020b16\017TBEN\016SEL_TBCLK\015b19\014b20\013b21\012b22\011b23" \
+ "\010EN_MAS7_UPDATE\007DCFA\006b26\005b27\004b28\003b29\002b30\001NOPTI"
+
+#define HID0_970_BITMASK \
+ "\20" \
+ "\040ONEPPC\037SINGLE\036ISYNCSC\035SERGP\031DEEPNAP\030DOZE" \
+ "\027NAP\025DPM\023TG\022HANGDETECT\021NHR\020INORDER" \
+ "\016TBCTRL\015TBEN\012CIABREN\011HDICEEN\001ENATTN"
+
+#define HID0_E500MC_BITMASK \
+ "\20" \
+ "\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \
+ "\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \
+ "\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \
+ "\010EN_MAS7_UPDATE\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI"
+
+#define HID0_E5500_BITMASK \
+ "\20" \
+ "\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \
+ "\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \
+ "\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \
+ "\010b24\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI"
+
+/*
+ * HID0 bit definitions per cpu model
+ *
+ * bit 603 604 750 7400 7410 7450 7457 e500
+ * 0 EMCP EMCP EMCP EMCP EMCP - - EMCP
+ * 1 - ECP DBP - - - - -
+ * 2 EBA EBA EBA EBA EDA - - -
+ * 3 EBD EBD EBD EBD EBD - - -
+ * 4 SBCLK - BCLK BCKL BCLK - - -
+ * 5 EICE - - - - TBEN TBEN -
+ * 6 ECLK - ECLK ECLK ECLK - - -
+ * 7 PAR PAR PAR PAR PAR STEN STEN -
+ * 8 DOZE - DOZE DOZE DOZE - HBATEN DOZE
+ * 9 NAP - NAP NAP NAP NAP NAP NAP
+ * 10 SLEEP - SLEEP SLEEP SLEEP SLEEP SLEEP SLEEP
+ * 11 DPM - DPM DPM DPM DPM DPM -
+ * 12 RISEG - - RISEG - - - -
+ * 13 - - - EIEC EIEC BHTCLR BHTCLR -
+ * 14 - - - - - XAEN XAEN -
+ * 15 - NHR NHR NHR NHR NHR NHR -
+ * 16 ICE ICE ICE ICE ICE ICE ICE -
+ * 17 DCE DCE DCE DCE DCE DCE DCE TBEN
+ * 18 ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK SEL_TBCLK
+ * 19 DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK -
+ * 20 ICFI ICFI ICFI ICFI ICFI ICFI ICFI -
+ * 21 DCFI DCFI DCFI DCFI DCFI DCFI DCFI -
+ * 22 - - SPD SPD SPG SPD SPD -
+ * 23 - - IFEM IFTT IFTT - XBSEN -
+ * 24 - SIE SGE SGE SGE SGE SGE EN_MAS7_UPDATE
+ * 25 - - DCFA DCFA DCFA - - DCFA
+ * 26 - - BTIC BTIC BTIC BTIC BTIC -
+ * 27 FBIOB - - - - LRSTK LRSTK -
+ * 28 - - ABE - - FOLD FOLD -
+ * 29 - BHT BHT BHT BHT BHT BHT -
+ * 30 - - - NOPDST NOPDST NOPDST NOPDST -
+ * 31 NOOPTI - NOOPTI NOPTI NOPTI NOPTI NOPTI NOPTI
+ *
+ * bit e500mc e5500
+ * 0 EMCP EMCP
+ * 1 EN_L2MMU_MHD EN_L2MMU_MHD
+ * 2 - -
+ * 3 - -
+ * 4 - -
+ * 5 - -
+ * 6 - -
+ * 7 - -
+ * 8 - -
+ * 9 - -
+ * 10 - -
+ * 11 - -
+ * 12 - -
+ * 13 - -
+ * 14 - -
+ * 15 - -
+ * 16 - -
+ * 17 - -
+ * 18 - -
+ * 19 - -
+ * 20 - -
+ * 21 - -
+ * 22 - -
+ * 23 - -
+ * 24 EN_MAS7_UPDATE -
+ * 25 DCFA DCFA
+ * 26 - -
+ * 27 CIGLSO CIGLSO
+ * 28 - -
+ * 29 - -
+ * 30 - -
+ * 31 NOPTI NOPTI
+ *
+ * 604: ECP = Enable cache parity checking
+ * 604: SIE = Serial instruction execution disable
+ * 7450: TBEN = Time Base Enable
+ * 7450: STEN = Software table lookup enable
+ * 7450: BHTCLR = Branch history clear
+ * 7450: XAEN = Extended Addressing Enabled
+ * 7450: LRSTK = Link Register Stack Enable
+ * 7450: FOLD = Branch folding enable
+ * 7457: HBATEN = High BAT Enable
+ * 7457: XBSEN = Extended BAT Block Size Enable
+ */
+
+#define HID1_E500_ABE 0x00001000 /* Address broadcast enable */
+#define HID1_E500_ASTME 0x00002000 /* Address bus streaming mode enable */
+#define HID1_E500_RFXE 0x00020000 /* Read fault exception enable */
+
+#define HID0_E500_DEFAULT_SET (HID0_EMCP | HID0_E500_TBEN | \
+ HID0_E500_MAS7UPDEN)
+#define HID1_E500_DEFAULT_SET (HID1_E500_ABE | HID1_E500_ASTME)
+#define HID0_E500MC_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD | \
+ HID0_E500_MAS7UPDEN)
+#define HID0_E5500_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD)
+
+#define HID5_970_DCBZ_SIZE_HI 0x00000080UL /* dcbz does a 32-byte store */
+#define HID4_970_DISABLE_LG_PG 0x00000004ULL /* disables large pages */
+
+#endif /* _POWERPC_HID_H_ */
diff --git a/freebsd/sys/powerpc/include/machine/pio.h b/freebsd/sys/powerpc/include/machine/pio.h
new file mode 100644
index 00000000..a4d9b327
--- /dev/null
+++ b/freebsd/sys/powerpc/include/machine/pio.h
@@ -0,0 +1,306 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed under OpenBSD by
+ * Per Fogelstrom Opsycon AB for RTMX Inc, North Carolina, USA.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ * $NetBSD: pio.h,v 1.1 1998/05/15 10:15:54 tsubai Exp $
+ * $OpenBSD: pio.h,v 1.1 1997/10/13 10:53:47 pefo Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_PIO_H_
+#define _MACHINE_PIO_H_
+/*
+ * I/O macros.
+ */
+
+/*
+ * Use sync so that bus space operations cannot sneak out the bottom of
+ * mutex-protected sections (mutex release does not guarantee completion of
+ * accesses to caching-inhibited memory on some systems)
+ */
+#define powerpc_iomb() __asm __volatile("sync" : : : "memory")
+
+static __inline void
+__outb(volatile u_int8_t *a, u_int8_t v)
+{
+ *a = v;
+ powerpc_iomb();
+}
+
+static __inline void
+__outw(volatile u_int16_t *a, u_int16_t v)
+{
+ *a = v;
+ powerpc_iomb();
+}
+
+static __inline void
+__outl(volatile u_int32_t *a, u_int32_t v)
+{
+ *a = v;
+ powerpc_iomb();
+}
+
+static __inline void
+__outll(volatile u_int64_t *a, u_int64_t v)
+{
+ *a = v;
+ powerpc_iomb();
+}
+
+static __inline void
+__outwrb(volatile u_int16_t *a, u_int16_t v)
+{
+ __asm__ volatile("sthbrx %0, 0, %1" :: "r"(v), "r"(a));
+ powerpc_iomb();
+}
+
+static __inline void
+__outlrb(volatile u_int32_t *a, u_int32_t v)
+{
+ __asm__ volatile("stwbrx %0, 0, %1" :: "r"(v), "r"(a));
+ powerpc_iomb();
+}
+
+static __inline u_int8_t
+__inb(volatile u_int8_t *a)
+{
+ u_int8_t _v_;
+
+ _v_ = *a;
+ powerpc_iomb();
+ return _v_;
+}
+
+static __inline u_int16_t
+__inw(volatile u_int16_t *a)
+{
+ u_int16_t _v_;
+
+ _v_ = *a;
+ powerpc_iomb();
+ return _v_;
+}
+
+static __inline u_int32_t
+__inl(volatile u_int32_t *a)
+{
+ u_int32_t _v_;
+
+ _v_ = *a;
+ powerpc_iomb();
+ return _v_;
+}
+
+static __inline u_int64_t
+__inll(volatile u_int64_t *a)
+{
+ u_int64_t _v_;
+
+ _v_ = *a;
+ powerpc_iomb();
+ return _v_;
+}
+
+static __inline u_int16_t
+__inwrb(volatile u_int16_t *a)
+{
+ u_int16_t _v_;
+
+ __asm__ volatile("lhbrx %0, 0, %1" : "=r"(_v_) : "r"(a));
+ powerpc_iomb();
+ return _v_;
+}
+
+static __inline u_int32_t
+__inlrb(volatile u_int32_t *a)
+{
+ u_int32_t _v_;
+
+ __asm__ volatile("lwbrx %0, 0, %1" : "=r"(_v_) : "r"(a));
+ powerpc_iomb();
+ return _v_;
+}
+
+#define outb(a,v) (__outb((volatile u_int8_t *)(a), v))
+#define out8(a,v) outb(a,v)
+#define outw(a,v) (__outw((volatile u_int16_t *)(a), v))
+#define out16(a,v) outw(a,v)
+#define outl(a,v) (__outl((volatile u_int32_t *)(a), v))
+#define out32(a,v) outl(a,v)
+#define outll(a,v) (__outll((volatile u_int64_t *)(a), v))
+#define out64(a,v) outll(a,v)
+#define inb(a) (__inb((volatile u_int8_t *)(a)))
+#define in8(a) inb(a)
+#define inw(a) (__inw((volatile u_int16_t *)(a)))
+#define in16(a) inw(a)
+#define inl(a) (__inl((volatile u_int32_t *)(a)))
+#define in32(a) inl(a)
+#define inll(a) (__inll((volatile u_int64_t *)(a)))
+#define in64(a) inll(a)
+
+#define out8rb(a,v) outb(a,v)
+#define outwrb(a,v) (__outwrb((volatile u_int16_t *)(a), v))
+#define out16rb(a,v) outwrb(a,v)
+#define outlrb(a,v) (__outlrb((volatile u_int32_t *)(a), v))
+#define out32rb(a,v) outlrb(a,v)
+#define in8rb(a) inb(a)
+#define inwrb(a) (__inwrb((volatile u_int16_t *)(a)))
+#define in16rb(a) inwrb(a)
+#define inlrb(a) (__inlrb((volatile u_int32_t *)(a)))
+#define in32rb(a) inlrb(a)
+
+
+static __inline void
+__outsb(volatile u_int8_t *a, const u_int8_t *s, size_t c)
+{
+ while (c--)
+ *a = *s++;
+ powerpc_iomb();
+}
+
+static __inline void
+__outsw(volatile u_int16_t *a, const u_int16_t *s, size_t c)
+{
+ while (c--)
+ *a = *s++;
+ powerpc_iomb();
+}
+
+static __inline void
+__outsl(volatile u_int32_t *a, const u_int32_t *s, size_t c)
+{
+ while (c--)
+ *a = *s++;
+ powerpc_iomb();
+}
+
+static __inline void
+__outsll(volatile u_int64_t *a, const u_int64_t *s, size_t c)
+{
+ while (c--)
+ *a = *s++;
+ powerpc_iomb();
+}
+
+static __inline void
+__outswrb(volatile u_int16_t *a, const u_int16_t *s, size_t c)
+{
+ while (c--)
+ __asm__ volatile("sthbrx %0, 0, %1" :: "r"(*s++), "r"(a));
+ powerpc_iomb();
+}
+
+static __inline void
+__outslrb(volatile u_int32_t *a, const u_int32_t *s, size_t c)
+{
+ while (c--)
+ __asm__ volatile("stwbrx %0, 0, %1" :: "r"(*s++), "r"(a));
+ powerpc_iomb();
+}
+
+static __inline void
+__insb(volatile u_int8_t *a, u_int8_t *d, size_t c)
+{
+ while (c--)
+ *d++ = *a;
+ powerpc_iomb();
+}
+
+static __inline void
+__insw(volatile u_int16_t *a, u_int16_t *d, size_t c)
+{
+ while (c--)
+ *d++ = *a;
+ powerpc_iomb();
+}
+
+static __inline void
+__insl(volatile u_int32_t *a, u_int32_t *d, size_t c)
+{
+ while (c--)
+ *d++ = *a;
+ powerpc_iomb();
+}
+
+static __inline void
+__insll(volatile u_int64_t *a, u_int64_t *d, size_t c)
+{
+ while (c--)
+ *d++ = *a;
+ powerpc_iomb();
+}
+
+static __inline void
+__inswrb(volatile u_int16_t *a, u_int16_t *d, size_t c)
+{
+ while (c--)
+ __asm__ volatile("lhbrx %0, 0, %1" : "=r"(*d++) : "r"(a));
+ powerpc_iomb();
+}
+
+static __inline void
+__inslrb(volatile u_int32_t *a, u_int32_t *d, size_t c)
+{
+ while (c--)
+ __asm__ volatile("lwbrx %0, 0, %1" : "=r"(*d++) : "r"(a));
+ powerpc_iomb();
+}
+
+#define outsb(a,s,c) (__outsb((volatile u_int8_t *)(a), s, c))
+#define outs8(a,s,c) outsb(a,s,c)
+#define outsw(a,s,c) (__outsw((volatile u_int16_t *)(a), s, c))
+#define outs16(a,s,c) outsw(a,s,c)
+#define outsl(a,s,c) (__outsl((volatile u_int32_t *)(a), s, c))
+#define outs32(a,s,c) outsl(a,s,c)
+#define outsll(a,s,c) (__outsll((volatile u_int64_t *)(a), s, c))
+#define outs64(a,s,c) outsll(a,s,c)
+#define insb(a,d,c) (__insb((volatile u_int8_t *)(a), d, c))
+#define ins8(a,d,c) insb(a,d,c)
+#define insw(a,d,c) (__insw((volatile u_int16_t *)(a), d, c))
+#define ins16(a,d,c) insw(a,d,c)
+#define insl(a,d,c) (__insl((volatile u_int32_t *)(a), d, c))
+#define ins32(a,d,c) insl(a,d,c)
+#define insll(a,d,c) (__insll((volatile u_int64_t *)(a), d, c))
+#define ins64(a,d,c) insll(a,d,c)
+
+#define outs8rb(a,s,c) outsb(a,s,c)
+#define outswrb(a,s,c) (__outswrb((volatile u_int16_t *)(a), s, c))
+#define outs16rb(a,s,c) outswrb(a,s,c)
+#define outslrb(a,s,c) (__outslrb((volatile u_int32_t *)(a), s, c))
+#define outs32rb(a,s,c) outslrb(a,s,c)
+#define ins8rb(a,d,c) insb(a,d,c)
+#define inswrb(a,d,c) (__inswrb((volatile u_int16_t *)(a), d, c))
+#define ins16rb(a,d,c) inswrb(a,d,c)
+#define inslrb(a,d,c) (__inslrb((volatile u_int32_t *)(a), d, c))
+#define ins32rb(a,d,c) inslrb(a,d,c)
+
+#endif /*_MACHINE_PIO_H_*/
diff --git a/freebsd/sys/powerpc/include/machine/platformvar.h b/freebsd/sys/powerpc/include/machine/platformvar.h
new file mode 100644
index 00000000..d5928e72
--- /dev/null
+++ b/freebsd/sys/powerpc/include/machine/platformvar.h
@@ -0,0 +1,91 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2005 Peter Grehan
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
+ */
+
+#ifndef _MACHINE_PLATFORMVAR_H_
+#define _MACHINE_PLATFORMVAR_H_
+
+/*
+ * A PowerPC platform implementation is declared with a kernel object and
+ * an associated method table, similar to a device driver.
+ *
+ * e.g.
+ *
+ * static platform_method_t chrp_methods[] = {
+ * PLATFORMMETHOD(platform_probe, chrp_probe),
+ * PLATFORMMETHOD(platform_mem_regions, ofw_mem_regions),
+ * ...
+ * PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
+ * { 0, 0 }
+ * };
+ *
+ * static platform_def_t chrp_platform = {
+ * "chrp",
+ * chrp_methods,
+ * sizeof(chrp_platform_softc), // or 0 if no softc
+ * };
+ *
+ * PLATFORM_DEF(chrp_platform);
+ */
+
+#include <sys/kobj.h>
+
+struct platform_kobj {
+ /*
+ * A platform instance is a kernel object
+ */
+ KOBJ_FIELDS;
+
+ /*
+ * Utility elements that an instance may use
+ */
+ struct mtx platform_mtx; /* available for instance use */
+ void *platform_iptr; /* instance data pointer */
+
+ /*
+ * Opaque data that can be overlaid with an instance-private
+ * structure. Platform code can test that this is large enough at
+ * compile time with a sizeof() test againt it's softc. There
+ * is also a run-time test when the platform kernel object is
+ * registered.
+ */
+#define PLATFORM_OPAQUESZ 64
+ u_int platform_opaque[PLATFORM_OPAQUESZ];
+};
+
+typedef struct platform_kobj *platform_t;
+typedef struct kobj_class platform_def_t;
+#define platform_method_t kobj_method_t
+
+#define PLATFORMMETHOD KOBJMETHOD
+#define PLATFORMMETHOD_END KOBJMETHOD_END
+
+#define PLATFORM_DEF(name) DATA_SET(platform_set, name)
+
+#endif /* _MACHINE_PLATFORMVAR_H_ */
diff --git a/freebsd/sys/powerpc/include/machine/spr.h b/freebsd/sys/powerpc/include/machine/spr.h
index 228e3955..4d108593 100644
--- a/freebsd/sys/powerpc/include/machine/spr.h
+++ b/freebsd/sys/powerpc/include/machine/spr.h
@@ -31,6 +31,9 @@
#ifndef _POWERPC_SPR_H_
#define _POWERPC_SPR_H_
+#ifdef __rtems__
+#define BOOKE
+#endif /* __rtems__ */
#ifndef _LOCORE
#define mtspr(reg, val) \
__asm __volatile("mtspr %0,%1" : : "K"(reg), "r"(val))
diff --git a/freebsd/sys/powerpc/mpc85xx/mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/mpc85xx.c
new file mode 100644
index 00000000..7f3df540
--- /dev/null
+++ b/freebsd/sys/powerpc/mpc85xx/mpc85xx.c
@@ -0,0 +1,403 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2008 Semihalf, Rafal Jaworowski
+ * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/reboot.h>
+#include <sys/rman.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#ifndef __rtems__
+#include <machine/machdep.h>
+#endif /* __rtems__ */
+#include <machine/pio.h>
+#include <machine/spr.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+#include <powerpc/mpc85xx/mpc85xx.h>
+
+
+/*
+ * MPC85xx system specific routines
+ */
+
+uint32_t
+ccsr_read4(uintptr_t addr)
+{
+ volatile uint32_t *ptr = (void *)addr;
+
+ return (*ptr);
+}
+
+void
+ccsr_write4(uintptr_t addr, uint32_t val)
+{
+ volatile uint32_t *ptr = (void *)addr;
+
+ *ptr = val;
+ powerpc_iomb();
+}
+
+static int
+mpc85xx_is_p20xx(void)
+{
+ uint32_t ver;
+ int is_p20xx;
+
+ ver = SVR_VER(mfspr(SPR_SVR));
+ switch (ver) {
+ case SVR_P2010:
+ case SVR_P2010E:
+ case SVR_P2020:
+ case SVR_P2020E:
+ case SVR_P2041:
+ case SVR_P2041E:
+ is_p20xx = 1;
+ break;
+ default:
+ is_p20xx = 0;
+ }
+
+ return (is_p20xx);
+}
+
+int
+law_getmax(void)
+{
+ uint32_t ver;
+ int law_max;
+
+ ver = SVR_VER(mfspr(SPR_SVR));
+ switch (ver) {
+ case SVR_MPC8555:
+ case SVR_MPC8555E:
+ law_max = 8;
+ break;
+ case SVR_MPC8533:
+ case SVR_MPC8533E:
+ case SVR_MPC8548:
+ case SVR_MPC8548E:
+ law_max = 10;
+ break;
+ case SVR_P2010:
+ case SVR_P2010E:
+ case SVR_P2020:
+ case SVR_P2020E:
+ case SVR_P2041:
+ case SVR_P2041E:
+ law_max = 12;
+ break;
+ case SVR_P5020:
+ case SVR_P5020E:
+ case SVR_P5021:
+ case SVR_P5021E:
+ case SVR_P5040:
+ case SVR_P5040E:
+ law_max = 32;
+ break;
+ default:
+ law_max = 8;
+ }
+
+ return (law_max);
+}
+
+static inline void
+law_write(uint32_t n, uint64_t bar, uint32_t sr)
+{
+
+ if (mpc85xx_is_qoriq()) {
+ ccsr_write4(OCP85XX_LAWBARH(n), bar >> 32);
+ ccsr_write4(OCP85XX_LAWBARL(n), bar);
+ ccsr_write4(OCP85XX_LAWSR_QORIQ(n), sr);
+ ccsr_read4(OCP85XX_LAWSR_QORIQ(n));
+ } else if (mpc85xx_is_p20xx()) {
+ ccsr_write4(OCP85XX_LAWBAR_P20XX(n), bar >> 12);
+ ccsr_write4(OCP85XX_LAWAR(n), sr);
+ ccsr_read4(OCP85XX_LAWAR(n));
+ } else {
+ ccsr_write4(OCP85XX_LAWBAR(n), bar >> 12);
+ ccsr_write4(OCP85XX_LAWSR_85XX(n), sr);
+ ccsr_read4(OCP85XX_LAWSR_85XX(n));
+ }
+
+ /*
+ * The last write to LAWAR should be followed by a read
+ * of LAWAR before any device try to use any of windows.
+ * What more the read of LAWAR should be followed by isync
+ * instruction.
+ */
+
+ isync();
+}
+
+static inline void
+law_read(uint32_t n, uint64_t *bar, uint32_t *sr)
+{
+
+ if (mpc85xx_is_qoriq()) {
+ *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBARH(n)) << 32 |
+ ccsr_read4(OCP85XX_LAWBARL(n));
+ *sr = ccsr_read4(OCP85XX_LAWSR_QORIQ(n));
+ } else if (mpc85xx_is_p20xx()) {
+ *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBAR_P20XX(n)) << 12;
+ *sr = ccsr_read4(OCP85XX_LAWAR(n));
+ } else {
+ *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBAR(n)) << 12;
+ *sr = ccsr_read4(OCP85XX_LAWSR_85XX(n));
+ }
+}
+
+static int
+law_find_free(void)
+{
+ uint32_t i,sr;
+ uint64_t bar;
+ int law_max;
+
+ law_max = law_getmax();
+ /* Find free LAW */
+ for (i = 0; i < law_max; i++) {
+ law_read(i, &bar, &sr);
+ if ((sr & 0x80000000) == 0)
+ break;
+ }
+
+ return (i);
+}
+
+#define _LAW_SR(trgt,size) (0x80000000 | (trgt << 20) | \
+ (flsl(size + (size - 1)) - 2))
+
+int
+law_enable(int trgt, uint64_t bar, uint32_t size)
+{
+ uint64_t bar_tmp;
+ uint32_t sr, sr_tmp;
+ int i, law_max;
+
+ if (size == 0)
+ return (0);
+
+ law_max = law_getmax();
+ sr = _LAW_SR(trgt, size);
+
+ /* Bail if already programmed. */
+ for (i = 0; i < law_max; i++) {
+ law_read(i, &bar_tmp, &sr_tmp);
+ if (sr == sr_tmp && bar == bar_tmp)
+ return (0);
+ }
+
+ /* Find an unused access window. */
+ i = law_find_free();
+
+ if (i == law_max)
+ return (ENOSPC);
+
+ law_write(i, bar, sr);
+ return (0);
+}
+
+int
+law_disable(int trgt, uint64_t bar, uint32_t size)
+{
+ uint64_t bar_tmp;
+ uint32_t sr, sr_tmp;
+ int i, law_max;
+
+ law_max = law_getmax();
+ sr = _LAW_SR(trgt, size);
+
+ /* Find and disable requested LAW. */
+ for (i = 0; i < law_max; i++) {
+ law_read(i, &bar_tmp, &sr_tmp);
+ if (sr == sr_tmp && bar == bar_tmp) {
+ law_write(i, 0, 0);
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+int
+law_pci_target(struct resource *res, int *trgt_mem, int *trgt_io)
+{
+ u_long start;
+ uint32_t ver;
+ int trgt, rv;
+
+ ver = SVR_VER(mfspr(SPR_SVR));
+
+ start = rman_get_start(res) & 0xf000;
+
+ rv = 0;
+ trgt = -1;
+ switch (start) {
+ case 0x0000:
+ case 0x8000:
+ trgt = 0;
+ break;
+ case 0x1000:
+ case 0x9000:
+ trgt = 1;
+ break;
+ case 0x2000:
+ case 0xa000:
+ if (ver == SVR_MPC8548E || ver == SVR_MPC8548)
+ trgt = 3;
+ else
+ trgt = 2;
+ break;
+ case 0x3000:
+ case 0xb000:
+ if (ver == SVR_MPC8548E || ver == SVR_MPC8548)
+ rv = EINVAL;
+ else
+ trgt = 3;
+ break;
+ default:
+ rv = ENXIO;
+ }
+ if (rv == 0) {
+ *trgt_mem = trgt;
+ *trgt_io = trgt;
+ }
+ return (rv);
+}
+
+#ifndef __rtems__
+static void
+l3cache_inval(void)
+{
+
+ /* Flash invalidate the CPC and clear all the locks */
+ ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_FI |
+ OCP85XX_CPC_CSR0_LFC);
+ while (ccsr_read4(OCP85XX_CPC_CSR0) & (OCP85XX_CPC_CSR0_FI |
+ OCP85XX_CPC_CSR0_LFC))
+ ;
+}
+
+static void
+l3cache_enable(void)
+{
+
+ ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_CE |
+ OCP85XX_CPC_CSR0_PE);
+ /* Read back to sync write */
+ ccsr_read4(OCP85XX_CPC_CSR0);
+}
+
+void
+mpc85xx_enable_l3_cache(void)
+{
+ uint32_t csr, size, ver;
+
+ /* Enable L3 CoreNet Platform Cache (CPC) */
+ ver = SVR_VER(mfspr(SPR_SVR));
+ if (ver == SVR_P2041 || ver == SVR_P2041E || ver == SVR_P3041 ||
+ ver == SVR_P3041E || ver == SVR_P5020 || ver == SVR_P5020E) {
+ csr = ccsr_read4(OCP85XX_CPC_CSR0);
+ if ((csr & OCP85XX_CPC_CSR0_CE) == 0) {
+ l3cache_inval();
+ l3cache_enable();
+ }
+
+ csr = ccsr_read4(OCP85XX_CPC_CSR0);
+ if ((boothowto & RB_VERBOSE) != 0 ||
+ (csr & OCP85XX_CPC_CSR0_CE) == 0) {
+ size = OCP85XX_CPC_CFG0_SZ_K(ccsr_read4(OCP85XX_CPC_CFG0));
+ printf("L3 Corenet Platform Cache: %d KB %sabled\n",
+ size, (csr & OCP85XX_CPC_CSR0_CE) == 0 ?
+ "dis" : "en");
+ }
+ }
+}
+#endif /* __rtems__ */
+
+int
+mpc85xx_is_qoriq(void)
+{
+ uint16_t pvr = mfpvr() >> 16;
+
+ /* QorIQ register set is only in e500mc and derivative core based SoCs. */
+ if (pvr == FSL_E500mc || pvr == FSL_E5500 || pvr == FSL_E6500)
+ return (1);
+
+ return (0);
+}
+
+#ifndef __rtems__
+uint32_t
+mpc85xx_get_platform_clock(void)
+{
+ phandle_t soc;
+ static uint32_t freq;
+
+ if (freq != 0)
+ return (freq);
+
+ soc = OF_finddevice("/soc");
+
+ /* freq isn't modified on error. */
+ OF_getencprop(soc, "bus-frequency", (void *)&freq, sizeof(freq));
+
+ return (freq);
+}
+
+uint32_t
+mpc85xx_get_system_clock(void)
+{
+ uint32_t freq;
+
+ freq = mpc85xx_get_platform_clock();
+
+ return (freq / 2);
+}
+#endif /* __rtems__ */
diff --git a/freebsd/sys/powerpc/mpc85xx/mpc85xx.h b/freebsd/sys/powerpc/mpc85xx/mpc85xx.h
new file mode 100644
index 00000000..0f1ee5cc
--- /dev/null
+++ b/freebsd/sys/powerpc/mpc85xx/mpc85xx.h
@@ -0,0 +1,179 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2008 Semihalf, Rafal Jaworowski
+ * Copyright 2006 by Juniper Networks.
+ * 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 AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
+ */
+
+#ifndef _MPC85XX_H_
+#define _MPC85XX_H_
+
+#include <machine/platformvar.h>
+
+/*
+ * Configuration control and status registers
+ */
+extern vm_offset_t ccsrbar_va;
+extern vm_paddr_t ccsrbar_pa;
+extern vm_size_t ccsrbar_size;
+#define CCSRBAR_VA ccsrbar_va
+#define OCP85XX_CCSRBAR (CCSRBAR_VA + 0x0)
+#define OCP85XX_BPTR (CCSRBAR_VA + 0x20)
+
+#define OCP85XX_BSTRH (CCSRBAR_VA + 0x20)
+#define OCP85XX_BSTRL (CCSRBAR_VA + 0x24)
+#define OCP85XX_BSTAR (CCSRBAR_VA + 0x28)
+
+#define OCP85XX_COREDISR (CCSRBAR_VA + 0xE0094)
+#define OCP85XX_BRR (CCSRBAR_VA + 0xE00E4)
+
+/*
+ * Run Control and Power Management registers
+ */
+#define CCSR_CTBENR (CCSRBAR_VA + 0xE2084)
+#define CCSR_CTBCKSELR (CCSRBAR_VA + 0xE208C)
+#define CCSR_CTBCHLTCR (CCSRBAR_VA + 0xE2094)
+
+/*
+ * DDR Memory controller.
+ */
+#define OCP85XX_DDR1_CS0_CONFIG (CCSRBAR_VA + 0x8080)
+
+/*
+ * E500 Coherency Module registers
+ */
+#define OCP85XX_EEBPCR (CCSRBAR_VA + 0x1010)
+
+/*
+ * Local access registers
+ */
+/* Write order: OCP_LAWBARH -> OCP_LAWBARL -> OCP_LAWSR */
+#define OCP85XX_LAWBARH(n) (CCSRBAR_VA + 0xc00 + 0x10 * (n))
+#define OCP85XX_LAWBARL(n) (CCSRBAR_VA + 0xc04 + 0x10 * (n))
+#define OCP85XX_LAWSR_QORIQ(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n))
+#define OCP85XX_LAWBAR(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n))
+#define OCP85XX_LAWSR_85XX(n) (CCSRBAR_VA + 0xc10 + 0x10 * (n))
+#define OCP85XX_LAWSR(n) (mpc85xx_is_qoriq() ? OCP85XX_LAWSR_QORIQ(n) : \
+ OCP85XX_LAWSR_85XX(n))
+#define OCP85XX_LAWBAR_P20XX(n) (CCSRBAR_VA + 0xc08 + 0x20 * (n))
+#define OCP85XX_LAWAR(n) (CCSRBAR_VA + 0xc10 + 0x20 * (n))
+
+/* Attribute register */
+#define OCP85XX_ENA_MASK 0x80000000
+#define OCP85XX_DIS_MASK 0x7fffffff
+
+#define OCP85XX_TGTIF_LBC_QORIQ 0x1f
+#define OCP85XX_TGTIF_RAM_INTL_QORIQ 0x14
+#define OCP85XX_TGTIF_RAM1_QORIQ 0x10
+#define OCP85XX_TGTIF_RAM2_QORIQ 0x11
+#define OCP85XX_TGTIF_BMAN 0x18
+#define OCP85XX_TGTIF_DCSR 0x1D
+#define OCP85XX_TGTIF_QMAN 0x3C
+#define OCP85XX_TRGT_SHIFT_QORIQ 20
+
+#define OCP85XX_TGTIF_LBC_85XX 0x04
+#define OCP85XX_TGTIF_RAM_INTL_85XX 0x0b
+#define OCP85XX_TGTIF_RIO_85XX 0x0c
+#define OCP85XX_TGTIF_RAM1_85XX 0x0f
+#define OCP85XX_TGTIF_RAM2_85XX 0x16
+
+#define OCP85XX_TGTIF_LBC \
+ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_LBC_QORIQ : OCP85XX_TGTIF_LBC_85XX)
+#define OCP85XX_TGTIF_RAM_INTL \
+ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM_INTL_QORIQ : OCP85XX_TGTIF_RAM_INTL_85XX)
+#define OCP85XX_TGTIF_RIO \
+ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RIO_QORIQ : OCP85XX_TGTIF_RIO_85XX)
+#define OCP85XX_TGTIF_RAM1 \
+ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM1_QORIQ : OCP85XX_TGTIF_RAM1_85XX)
+#define OCP85XX_TGTIF_RAM2 \
+ (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM2_QORIQ : OCP85XX_TGTIF_RAM2_85XX)
+
+/*
+ * L2 cache registers
+ */
+#define OCP85XX_L2CTL (CCSRBAR_VA + 0x20000)
+
+/*
+ * L3 CoreNet platform cache (CPC) registers
+ */
+#define OCP85XX_CPC_CSR0 (CCSRBAR_VA + 0x10000)
+#define OCP85XX_CPC_CSR0_CE 0x80000000
+#define OCP85XX_CPC_CSR0_PE 0x40000000
+#define OCP85XX_CPC_CSR0_FI 0x00200000
+#define OCP85XX_CPC_CSR0_WT 0x00080000
+#define OCP85XX_CPC_CSR0_FL 0x00000800
+#define OCP85XX_CPC_CSR0_LFC 0x00000400
+#define OCP85XX_CPC_CFG0 (CCSRBAR_VA + 0x10008)
+#define OCP85XX_CPC_CFG_SZ_MASK 0x00003fff
+#define OCP85XX_CPC_CFG0_SZ_K(x) (((x) & OCP85XX_CPC_CFG_SZ_MASK) << 6)
+
+/*
+ * Power-On Reset configuration
+ */
+#define OCP85XX_PORDEVSR (CCSRBAR_VA + 0xe000c)
+#define OCP85XX_PORDEVSR_IO_SEL 0x00780000
+#define OCP85XX_PORDEVSR_IO_SEL_SHIFT 19
+
+#define OCP85XX_PORDEVSR2 (CCSRBAR_VA + 0xe0014)
+
+/*
+ * Status Registers.
+ */
+#define OCP85XX_RSTCR (CCSRBAR_VA + 0xe00b0)
+
+#define OCP85XX_CLKDVDR (CCSRBAR_VA + 0xe0800)
+#define OCP85XX_CLKDVDR_PXCKEN 0x80000000
+#define OCP85XX_CLKDVDR_SSICKEN 0x20000000
+#define OCP85XX_CLKDVDR_PXCKINV 0x10000000
+#define OCP85XX_CLKDVDR_PXCLK_MASK 0x00FF0000
+#define OCP85XX_CLKDVDR_SSICLK_MASK 0x000000FF
+
+/*
+ * Run Control/Power Management Registers.
+ */
+#define OCP85XX_RCPM_CDOZSR (CCSRBAR_VA + 0xe2004)
+#define OCP85XX_RCPM_CDOZCR (CCSRBAR_VA + 0xe200c)
+
+/*
+ * Prototypes.
+ */
+uint32_t ccsr_read4(uintptr_t addr);
+void ccsr_write4(uintptr_t addr, uint32_t val);
+int law_enable(int trgt, uint64_t bar, uint32_t size);
+int law_disable(int trgt, uint64_t bar, uint32_t size);
+int law_getmax(void);
+int law_pci_target(struct resource *, int *, int *);
+
+DECLARE_CLASS(mpc85xx_platform);
+int mpc85xx_attach(platform_t);
+
+void mpc85xx_enable_l3_cache(void);
+int mpc85xx_is_qoriq(void);
+uint32_t mpc85xx_get_platform_clock(void);
+uint32_t mpc85xx_get_system_clock(void);
+
+#endif /* _MPC85XX_H_ */
diff --git a/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c
new file mode 100644
index 00000000..b479eb33
--- /dev/null
+++ b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c
@@ -0,0 +1,996 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2006-2007 by Juniper Networks.
+ * Copyright 2008 Semihalf.
+ * Copyright 2010 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Semihalf
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ktr.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/vmem.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofwpci.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <rtems/bsd/local/ofw_bus_if.h>
+#include <rtems/bsd/local/pcib_if.h>
+#include <rtems/bsd/local/pic_if.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
+
+#include <powerpc/mpc85xx/mpc85xx.h>
+#ifdef __rtems__
+#include <rtems/score/memory.h>
+#include <libcpu/powerpc-utility.h>
+#endif /* __rtems__ */
+
+#define REG_CFG_ADDR 0x0000
+#define CONFIG_ACCESS_ENABLE 0x80000000
+
+#define REG_CFG_DATA 0x0004
+#define REG_INT_ACK 0x0008
+
+#define REG_PEX_IP_BLK_REV1 0x0bf8
+#define IP_MJ_M 0x0000ff00
+#define IP_MJ_S 8
+#define IP_MN_M 0x000000ff
+#define IP_MN_S 0
+
+#define REG_POTAR(n) (0x0c00 + 0x20 * (n))
+#define REG_POTEAR(n) (0x0c04 + 0x20 * (n))
+#define REG_POWBAR(n) (0x0c08 + 0x20 * (n))
+#define REG_POWAR(n) (0x0c10 + 0x20 * (n))
+
+#define REG_PITAR(n) (0x0e00 - 0x20 * (n))
+#define REG_PIWBAR(n) (0x0e08 - 0x20 * (n))
+#define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n))
+#define REG_PIWAR(n) (0x0e10 - 0x20 * (n))
+#define PIWAR_EN 0x80000000
+#define PIWAR_PF 0x40000000
+#define PIWAR_TRGT_M 0x00f00000
+#define PIWAR_TRGT_S 20
+#define PIWAR_TRGT_CCSR 0xe
+#define PIWAR_TRGT_LOCAL 0xf
+
+#define REG_PEX_MES_DR 0x0020
+#define REG_PEX_MES_IER 0x0028
+#define REG_PEX_ERR_DR 0x0e00
+#define REG_PEX_ERR_EN 0x0e08
+
+#define REG_PEX_ERR_DR 0x0e00
+#define REG_PEX_ERR_DR_ME 0x80000000
+#define REG_PEX_ERR_DR_PCT 0x800000
+#define REG_PEX_ERR_DR_PAT 0x400000
+#define REG_PEX_ERR_DR_PCAC 0x200000
+#define REG_PEX_ERR_DR_PNM 0x100000
+#define REG_PEX_ERR_DR_CDNSC 0x80000
+#define REG_PEX_ERR_DR_CRSNC 0x40000
+#define REG_PEX_ERR_DR_ICCA 0x20000
+#define REG_PEX_ERR_DR_IACA 0x10000
+#define REG_PEX_ERR_DR_CRST 0x8000
+#define REG_PEX_ERR_DR_MIS 0x4000
+#define REG_PEX_ERR_DR_IOIS 0x2000
+#define REG_PEX_ERR_DR_CIS 0x1000
+#define REG_PEX_ERR_DR_CIEP 0x800
+#define REG_PEX_ERR_DR_IOIEP 0x400
+#define REG_PEX_ERR_DR_OAC 0x200
+#define REG_PEX_ERR_DR_IOIA 0x100
+#define REG_PEX_ERR_DR_IMBA 0x80
+#define REG_PEX_ERR_DR_IIOBA 0x40
+#define REG_PEX_ERR_DR_LDDE 0x20
+#define REG_PEX_ERR_EN 0x0e08
+
+#define PCIR_LTSSM 0x404
+#define LTSSM_STAT_L0 0x16
+
+#define DEVFN(b, s, f) ((b << 16) | (s << 8) | f)
+
+#define FSL_NUM_MSIS 256 /* 8 registers of 32 bits (8 hardware IRQs) */
+
+struct fsl_pcib_softc {
+ struct ofw_pci_softc pci_sc;
+ device_t sc_dev;
+ struct mtx sc_cfg_mtx;
+ int sc_ip_maj;
+ int sc_ip_min;
+
+ int sc_iomem_target;
+ bus_addr_t sc_iomem_start, sc_iomem_end;
+ int sc_ioport_target;
+ bus_addr_t sc_ioport_start, sc_ioport_end;
+
+ struct resource *sc_res;
+ bus_space_handle_t sc_bsh;
+ bus_space_tag_t sc_bst;
+ int sc_rid;
+
+ struct resource *sc_irq_res;
+ void *sc_ih;
+
+ int sc_busnr;
+ int sc_pcie;
+ uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */
+};
+
+struct fsl_pcib_err_dr {
+ const char *msg;
+ uint32_t err_dr_mask;
+};
+
+struct fsl_msi_map {
+ SLIST_ENTRY(fsl_msi_map) slist;
+ uint32_t irq_base;
+ bus_addr_t target;
+};
+
+SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head);
+
+static const struct fsl_pcib_err_dr pci_err[] = {
+ {"ME", REG_PEX_ERR_DR_ME},
+ {"PCT", REG_PEX_ERR_DR_PCT},
+ {"PAT", REG_PEX_ERR_DR_PAT},
+ {"PCAC", REG_PEX_ERR_DR_PCAC},
+ {"PNM", REG_PEX_ERR_DR_PNM},
+ {"CDNSC", REG_PEX_ERR_DR_CDNSC},
+ {"CRSNC", REG_PEX_ERR_DR_CRSNC},
+ {"ICCA", REG_PEX_ERR_DR_ICCA},
+ {"IACA", REG_PEX_ERR_DR_IACA},
+ {"CRST", REG_PEX_ERR_DR_CRST},
+ {"MIS", REG_PEX_ERR_DR_MIS},
+ {"IOIS", REG_PEX_ERR_DR_IOIS},
+ {"CIS", REG_PEX_ERR_DR_CIS},
+ {"CIEP", REG_PEX_ERR_DR_CIEP},
+ {"IOIEP", REG_PEX_ERR_DR_IOIEP},
+ {"OAC", REG_PEX_ERR_DR_OAC},
+ {"IOIA", REG_PEX_ERR_DR_IOIA},
+ {"IMBA", REG_PEX_ERR_DR_IMBA},
+ {"IIOBA", REG_PEX_ERR_DR_IIOBA},
+ {"LDDE", REG_PEX_ERR_DR_LDDE}
+};
+
+/* Local forward declerations. */
+static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int,
+ u_int, int);
+static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int,
+ u_int, uint32_t, int);
+static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *);
+static void fsl_pcib_err_init(device_t);
+static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t,
+ uint64_t, uint64_t);
+static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t,
+ uint64_t, uint64_t);
+
+/* Forward declerations. */
+static int fsl_pcib_attach(device_t);
+static int fsl_pcib_detach(device_t);
+static int fsl_pcib_probe(device_t);
+
+static int fsl_pcib_maxslots(device_t);
+static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int);
+static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
+ uint32_t, int);
+static int fsl_pcib_alloc_msi(device_t dev, device_t child,
+ int count, int maxcount, int *irqs);
+static int fsl_pcib_release_msi(device_t dev, device_t child,
+ int count, int *irqs);
+static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq);
+static int fsl_pcib_release_msix(device_t dev, device_t child, int irq);
+static int fsl_pcib_map_msi(device_t dev, device_t child,
+ int irq, uint64_t *addr, uint32_t *data);
+
+#ifndef __rtems__
+static vmem_t *msi_vmem; /* Global MSI vmem, holds all MSI ranges. */
+#endif /* __rtems__ */
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t fsl_pcib_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, fsl_pcib_probe),
+ DEVMETHOD(device_attach, fsl_pcib_attach),
+ DEVMETHOD(device_detach, fsl_pcib_detach),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots),
+ DEVMETHOD(pcib_read_config, fsl_pcib_read_config),
+ DEVMETHOD(pcib_write_config, fsl_pcib_write_config),
+ DEVMETHOD(pcib_alloc_msi, fsl_pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, fsl_pcib_release_msi),
+ DEVMETHOD(pcib_alloc_msix, fsl_pcib_alloc_msix),
+ DEVMETHOD(pcib_release_msix, fsl_pcib_release_msix),
+ DEVMETHOD(pcib_map_msi, fsl_pcib_map_msi),
+
+ DEVMETHOD_END
+};
+
+static devclass_t fsl_pcib_devclass;
+
+DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods,
+ sizeof(struct fsl_pcib_softc), ofw_pci_driver);
+EARLY_DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0,
+ BUS_PASS_BUS);
+
+static void
+fsl_pcib_err_intr(void *v)
+{
+ struct fsl_pcib_softc *sc;
+ device_t dev;
+ uint32_t err_reg, clear_reg;
+ uint8_t i;
+
+ dev = (device_t)v;
+ sc = device_get_softc(dev);
+
+ clear_reg = 0;
+ err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR);
+
+ /* Check which one error occurred */
+ for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) {
+ if (err_reg & pci_err[i].err_dr_mask) {
+ device_printf(dev, "PCI %d: report %s error\n",
+ device_get_unit(dev), pci_err[i].msg);
+ clear_reg |= pci_err[i].err_dr_mask;
+ }
+ }
+
+ /* Clear pending errors */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg);
+}
+
+static int
+fsl_pcib_probe(device_t dev)
+{
+
+ if (ofw_bus_get_type(dev) == NULL ||
+ strcmp(ofw_bus_get_type(dev), "pci") != 0)
+ return (ENXIO);
+
+ if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") ||
+ ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") ||
+ ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") ||
+ ofw_bus_is_compatible(dev, "fsl,p5020-pcie") ||
+ ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") ||
+ ofw_bus_is_compatible(dev, "fsl,qoriq-pcie")))
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fsl_pcib_attach(device_t dev)
+{
+ struct fsl_pcib_softc *sc;
+ phandle_t node;
+ uint32_t cfgreg, brctl, ipreg;
+ int error, rid;
+ uint8_t ltssm, capptr;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->sc_rid = 0;
+ sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
+ RF_ACTIVE);
+ if (sc->sc_res == NULL) {
+ device_printf(dev, "could not map I/O memory\n");
+ return (ENXIO);
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_res);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res);
+ sc->sc_busnr = 0;
+
+ ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1);
+ sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S;
+ sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S;
+ mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN);
+
+ cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2);
+ if (cfgreg != 0x1057 && cfgreg != 0x1957)
+ goto err;
+
+ capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1);
+ while (capptr != 0) {
+ cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2);
+ switch (cfgreg & 0xff) {
+ case PCIY_PCIX:
+ break;
+ case PCIY_EXPRESS:
+ sc->sc_pcie = 1;
+ sc->sc_pcie_capreg = capptr;
+ break;
+ }
+ capptr = (cfgreg >> 8) & 0xff;
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ /*
+ * Initialize generic OF PCI interface (ranges, etc.)
+ */
+
+ error = ofw_pci_init(dev);
+ if (error)
+ return (error);
+
+ /*
+ * Configure decode windows for PCI(E) access.
+ */
+ if (fsl_pcib_decode_win(node, sc) != 0)
+ goto err;
+
+ cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2);
+ cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
+ PCIM_CMD_PORTEN;
+ fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2);
+
+#ifndef __rtems__
+ /* Reset the bus. Needed for Radeon video cards. */
+ brctl = fsl_pcib_read_config(sc->sc_dev, 0, 0, 0,
+ PCIR_BRIDGECTL_1, 1);
+ brctl |= PCIB_BCR_SECBUS_RESET;
+ fsl_pcib_write_config(sc->sc_dev, 0, 0, 0,
+ PCIR_BRIDGECTL_1, brctl, 1);
+ DELAY(100000);
+ brctl &= ~PCIB_BCR_SECBUS_RESET;
+ fsl_pcib_write_config(sc->sc_dev, 0, 0, 0,
+ PCIR_BRIDGECTL_1, brctl, 1);
+ DELAY(100000);
+#endif /* __rtems__ */
+
+ if (sc->sc_pcie) {
+ ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1);
+ if (ltssm < LTSSM_STAT_L0) {
+ if (bootverbose)
+ printf("PCI %d: no PCIE link, skipping\n",
+ device_get_unit(dev));
+ return (0);
+ }
+ }
+
+ /* Allocate irq */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL) {
+ error = fsl_pcib_detach(dev);
+ if (error != 0) {
+ device_printf(dev,
+ "Detach of the driver failed with error %d\n",
+ error);
+ }
+ return (ENXIO);
+ }
+
+ fsl_pcib_err_init(dev);
+
+ /* Setup interrupt handler */
+ error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, fsl_pcib_err_intr, dev, &sc->sc_ih);
+ if (error != 0) {
+ device_printf(dev, "Could not setup irq, %d\n", error);
+ sc->sc_ih = NULL;
+ error = fsl_pcib_detach(dev);
+ if (error != 0) {
+ device_printf(dev,
+ "Detach of the driver failed with error %d\n",
+ error);
+ }
+ return (ENXIO);
+ }
+
+ return (ofw_pci_attach(dev));
+
+err:
+ return (ENXIO);
+}
+
+static uint32_t
+fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ uint32_t addr, data;
+
+ addr = CONFIG_ACCESS_ENABLE;
+ addr |= (bus & 0xff) << 16;
+ addr |= (slot & 0x1f) << 11;
+ addr |= (func & 0x7) << 8;
+ addr |= reg & 0xfc;
+ if (sc->sc_pcie)
+ addr |= (reg & 0xf00) << 16;
+
+ mtx_lock_spin(&sc->sc_cfg_mtx);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
+#ifdef __rtems__
+ ppc_enforce_in_order_execution_of_io();
+#endif
+
+ switch (bytes) {
+ case 1:
+ data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA + (reg & 3));
+ break;
+ case 2:
+ data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA + (reg & 2)));
+ break;
+ case 4:
+ data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA));
+ break;
+ default:
+ data = ~0;
+ break;
+ }
+ mtx_unlock_spin(&sc->sc_cfg_mtx);
+ return (data);
+}
+
+static void
+fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t data, int bytes)
+{
+ uint32_t addr;
+
+ addr = CONFIG_ACCESS_ENABLE;
+ addr |= (bus & 0xff) << 16;
+ addr |= (slot & 0x1f) << 11;
+ addr |= (func & 0x7) << 8;
+ addr |= reg & 0xfc;
+ if (sc->sc_pcie)
+ addr |= (reg & 0xf00) << 16;
+
+ mtx_lock_spin(&sc->sc_cfg_mtx);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
+#ifdef __rtems__
+ ppc_enforce_in_order_execution_of_io();
+#endif
+
+ switch (bytes) {
+ case 1:
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA + (reg & 3), data);
+ break;
+ case 2:
+ bus_space_write_2(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA + (reg & 2), htole16(data));
+ break;
+ case 4:
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ REG_CFG_DATA, htole32(data));
+ break;
+ }
+ mtx_unlock_spin(&sc->sc_cfg_mtx);
+}
+
+#if 0
+static void
+dump(struct fsl_pcib_softc *sc)
+{
+ unsigned int i;
+
+#define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o)
+ for (i = 0; i < 5; i++) {
+ printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i)));
+ printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i)));
+ printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i)));
+ printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i)));
+ }
+ printf("\n");
+ for (i = 1; i < 4; i++) {
+ printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i)));
+ printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i)));
+ printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i)));
+ printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i)));
+ }
+ printf("\n");
+#undef RD
+
+ for (i = 0; i < 0x48; i += 4) {
+ printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0,
+ i, 4));
+ }
+}
+#endif
+
+static int
+fsl_pcib_maxslots(device_t dev)
+{
+ struct fsl_pcib_softc *sc = device_get_softc(dev);
+
+ return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX);
+}
+
+static uint32_t
+fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct fsl_pcib_softc *sc = device_get_softc(dev);
+ u_int devfn;
+
+ if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
+ return (~0);
+ devfn = DEVFN(bus, slot, func);
+
+ return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes));
+}
+
+static void
+fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ struct fsl_pcib_softc *sc = device_get_softc(dev);
+
+ if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
+ return;
+ fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes);
+}
+
+static void
+fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start,
+ uint64_t size, uint64_t pci_start)
+{
+ uint32_t attr, bar, tar;
+
+ KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__));
+
+ attr = PIWAR_EN;
+
+ switch (tgt) {
+ case -1:
+ attr &= ~PIWAR_EN;
+ break;
+ case PIWAR_TRGT_LOCAL:
+ attr |= (ffsl(size) - 2);
+ default:
+ attr |= (tgt << PIWAR_TRGT_S);
+ break;
+ }
+ tar = start >> 12;
+ bar = pci_start >> 12;
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr);
+}
+
+static void
+fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start,
+ uint64_t size, uint64_t pci_start)
+{
+ uint32_t attr, bar, tar;
+
+ switch (res) {
+ case SYS_RES_MEMORY:
+ attr = 0x80044000 | (ffsll(size) - 2);
+ break;
+ case SYS_RES_IOPORT:
+ attr = 0x80088000 | (ffsll(size) - 2);
+ break;
+ default:
+ attr = 0x0004401f;
+ break;
+ }
+ bar = start >> 12;
+ tar = pci_start >> 12;
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr);
+}
+
+
+static void
+fsl_pcib_err_init(device_t dev)
+{
+ struct fsl_pcib_softc *sc;
+ uint16_t sec_stat, dsr;
+ uint32_t dcr, err_en;
+
+ sc = device_get_softc(dev);
+
+ sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2);
+ if (sec_stat)
+ fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2);
+ if (sc->sc_pcie) {
+ /* Clear error bits */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER,
+ 0xffffffff);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR,
+ 0xffffffff);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR,
+ 0xffffffff);
+
+ dsr = fsl_pcib_cfgread(sc, 0, 0, 0,
+ sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2);
+ if (dsr)
+ fsl_pcib_cfgwrite(sc, 0, 0, 0,
+ sc->sc_pcie_capreg + PCIER_DEVICE_STA,
+ 0xffff, 2);
+
+ /* Enable all errors reporting */
+ err_en = 0x00bfff00;
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN,
+ err_en);
+
+ /* Enable error reporting: URR, FER, NFER */
+ dcr = fsl_pcib_cfgread(sc, 0, 0, 0,
+ sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4);
+ dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE |
+ PCIEM_CTL_NFER_ENABLE;
+ fsl_pcib_cfgwrite(sc, 0, 0, 0,
+ sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4);
+ }
+}
+
+static int
+fsl_pcib_detach(device_t dev)
+{
+ struct fsl_pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_destroy(&sc->sc_cfg_mtx);
+
+ return (bus_generic_detach(dev));
+}
+
+static int
+fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc)
+{
+ device_t dev;
+ int error, i, trgt;
+
+ dev = sc->sc_dev;
+
+ fsl_pcib_outbound(sc, 0, -1, 0, 0, 0);
+
+ /*
+ * Configure LAW decode windows.
+ */
+ error = law_pci_target(sc->sc_res, &sc->sc_iomem_target,
+ &sc->sc_ioport_target);
+ if (error != 0) {
+ device_printf(dev, "could not retrieve PCI LAW target info\n");
+ return (error);
+ }
+
+ for (i = 0; i < sc->pci_sc.sc_nrange; i++) {
+ switch (sc->pci_sc.sc_range[i].pci_hi &
+ OFW_PCI_PHYS_HI_SPACEMASK) {
+ case OFW_PCI_PHYS_HI_SPACE_CONFIG:
+ continue;
+ case OFW_PCI_PHYS_HI_SPACE_IO:
+ trgt = sc->sc_ioport_target;
+ fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT,
+ sc->pci_sc.sc_range[i].host,
+ sc->pci_sc.sc_range[i].size,
+ sc->pci_sc.sc_range[i].pci);
+ sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci;
+ sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci +
+ sc->pci_sc.sc_range[i].size - 1;
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_MEM32:
+ case OFW_PCI_PHYS_HI_SPACE_MEM64:
+ trgt = sc->sc_iomem_target;
+ fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY,
+ sc->pci_sc.sc_range[i].host,
+ sc->pci_sc.sc_range[i].size,
+ sc->pci_sc.sc_range[i].pci);
+ sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci;
+ sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci +
+ sc->pci_sc.sc_range[i].size - 1;
+ break;
+ default:
+ panic("Unknown range type %#x\n",
+ sc->pci_sc.sc_range[i].pci_hi &
+ OFW_PCI_PHYS_HI_SPACEMASK);
+ }
+ error = law_enable(trgt, sc->pci_sc.sc_range[i].host,
+ sc->pci_sc.sc_range[i].size);
+ if (error != 0) {
+ device_printf(dev, "could not program LAW for range "
+ "%d\n", i);
+ return (error);
+ }
+ }
+
+ /*
+ * Set outbout and inbound windows.
+ */
+ fsl_pcib_outbound(sc, 3, -1, 0, 0, 0);
+ fsl_pcib_outbound(sc, 4, -1, 0, 0, 0);
+
+ fsl_pcib_inbound(sc, 1, -1, 0, 0, 0);
+ fsl_pcib_inbound(sc, 2, -1, 0, 0, 0);
+#ifndef __rtems__
+ fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0,
+ ptoa(Maxmem), 0);
+#else /* __rtems__ */
+ const Memory_Information *mem = _Memory_Get();
+ const Memory_Area *area = _Memory_Get_area( mem, 0 );
+ fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0,
+ (uintptr_t)_Memory_Get_end(area), 0);
+#endif /* __rtems__ */
+
+ /* Direct-map the CCSR for MSIs. */
+ /* Freescale PCIe 2.x has a dedicated MSI window. */
+ /* inbound window 8 makes it hit 0xD00 offset, the MSI window. */
+ if (sc->sc_ip_maj >= 2)
+ fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa,
+ ccsrbar_size, ccsrbar_pa);
+ else
+ fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa,
+ ccsrbar_size, ccsrbar_pa);
+
+ return (0);
+}
+
+static int fsl_pcib_alloc_msi(device_t dev, device_t child,
+ int count, int maxcount, int *irqs)
+{
+#ifndef __rtems__
+ struct fsl_pcib_softc *sc;
+ vmem_addr_t start;
+ int err, i;
+
+ sc = device_get_softc(dev);
+ if (msi_vmem == NULL)
+ return (ENODEV);
+
+ err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0,
+ VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start);
+
+ if (err)
+ return (err);
+
+ for (i = 0; i < count; i++)
+ irqs[i] = start + i;
+#else /* __rtems__ */
+ BSD_ASSERT(0);
+#endif /* __rtems__ */
+
+ return (0);
+}
+
+static int fsl_pcib_release_msi(device_t dev, device_t child,
+ int count, int *irqs)
+{
+#ifndef __rtems__
+ if (msi_vmem == NULL)
+ return (ENODEV);
+
+ vmem_xfree(msi_vmem, irqs[0], count);
+#else /* __rtems__ */
+ BSD_ASSERT(0);
+#endif /* __rtems__ */
+ return (0);
+}
+
+static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq)
+{
+ return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq));
+}
+
+static int fsl_pcib_release_msix(device_t dev, device_t child, int irq)
+{
+ return (fsl_pcib_release_msi(dev, child, 1, &irq));
+}
+
+static int fsl_pcib_map_msi(device_t dev, device_t child,
+ int irq, uint64_t *addr, uint32_t *data)
+{
+ struct fsl_msi_map *mp;
+
+ SLIST_FOREACH(mp, &fsl_msis, slist) {
+ if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS)
+ break;
+ }
+
+ if (mp == NULL)
+ return (ENODEV);
+
+ *data = (irq & 255);
+ *addr = ccsrbar_pa + mp->target;
+
+ return (0);
+}
+
+
+/*
+ * Linux device trees put the msi@<x> as children of the SoC, with ranges based
+ * on the CCSR. Since rman doesn't permit overlapping or sub-ranges between
+ * devices (bus_space_subregion(9) could do it, but let's not touch the PIC
+ * driver just to allocate a subregion for a sibling driver). This driver will
+ * use ccsr_write() and ccsr_read() instead.
+ */
+
+#define FSL_NUM_IRQS 8
+#define FSL_NUM_MSI_PER_IRQ 32
+#define FSL_MSI_TARGET 0x140
+
+struct fsl_msi_softc {
+ vm_offset_t sc_base;
+ vm_offset_t sc_target;
+ int sc_msi_base_irq;
+ struct fsl_msi_map sc_map;
+ struct fsl_msi_irq {
+ /* This struct gets passed as the filter private data. */
+ struct fsl_msi_softc *sc_ptr; /* Pointer back to softc. */
+ struct resource *res;
+ int irq;
+ void *cookie;
+ int vectors[FSL_NUM_MSI_PER_IRQ];
+ vm_offset_t reg;
+ } sc_msi_irq[FSL_NUM_IRQS];
+};
+
+static int
+fsl_msi_intr_filter(void *priv)
+{
+ struct fsl_msi_irq *data = priv;
+ uint32_t reg;
+ int i;
+
+ reg = ccsr_read4(ccsrbar_va + data->reg);
+ i = 0;
+ while (reg != 0) {
+ if (reg & 1)
+#ifndef __rtems__
+ powerpc_dispatch_intr(data->vectors[i], NULL);
+#else /* __rtems__ */
+ BSD_ASSERT(0);
+#endif /* __rtems__ */
+ reg >>= 1;
+ i++;
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+fsl_msi_probe(device_t dev)
+{
+ if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale MSI");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fsl_msi_attach(device_t dev)
+{
+ struct fsl_msi_softc *sc;
+ struct fsl_msi_irq *irq;
+ int i;
+
+ sc = device_get_softc(dev);
+
+#ifndef __rtems__
+ if (msi_vmem == NULL)
+ msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 0, M_BESTFIT | M_WAITOK);
+#endif /* __rtems__ */
+
+ /* Manually play with resource entries. */
+ sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
+ sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1);
+
+ if (sc->sc_map.target == 0)
+ sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET;
+
+ for (i = 0; i < FSL_NUM_IRQS; i++) {
+ irq = &sc->sc_msi_irq[i];
+ irq->irq = i;
+ irq->reg = sc->sc_base + 16 * i;
+ irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &irq->irq, RF_ACTIVE);
+ bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE,
+ fsl_msi_intr_filter, NULL, irq, &irq->cookie);
+ }
+#ifndef __rtems__
+ sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev),
+ FSL_NUM_MSIS, 0, 0);
+
+ /* Let vmem and the IRQ subsystem work their magic for allocations. */
+ vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK);
+#endif /* __rtems__ */
+
+ SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist);
+
+ return (0);
+}
+
+static void
+fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv)
+{
+ struct fsl_msi_softc *sc;
+ struct fsl_msi_irq *irqd;
+
+ sc = device_get_softc(dev);
+
+ irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ];
+ irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector;
+}
+
+static device_method_t fsl_msi_methods[] = {
+ DEVMETHOD(device_probe, fsl_msi_probe),
+ DEVMETHOD(device_attach, fsl_msi_attach),
+
+ DEVMETHOD(pic_enable, fsl_msi_enable),
+ DEVMETHOD_END
+};
+
+static devclass_t fsl_msi_devclass;
+
+static driver_t fsl_msi_driver = {
+ "fsl_msi",
+ fsl_msi_methods,
+ sizeof(struct fsl_msi_softc)
+};
+
+EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + 1);
diff --git a/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c
new file mode 100644
index 00000000..74a580f5
--- /dev/null
+++ b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c
@@ -0,0 +1,111 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright 2015 Justin Hibbits
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ktr.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/intr_machdep.h>
+
+#include <rtems/bsd/local/pcib_if.h>
+
+DECLARE_CLASS(ofw_pcib_pci_driver);
+
+struct fsl_pcib_softc {
+ /*
+ * This is here so that we can use pci bridge methods, too - the
+ * generic routines only need the dev, secbus and subbus members
+ * filled.
+ *
+ * XXX: This should be extracted from ofw_pcib_pci.c, and shared in a
+ * header.
+ */
+ struct pcib_softc ops_pcib_sc;
+ phandle_t ops_node;
+ struct ofw_bus_iinfo ops_iinfo;
+};
+
+static int
+fsl_pcib_rc_probe(device_t dev)
+{
+
+ if (pci_get_vendor(dev) != 0x1957)
+ return (ENXIO);
+ if (pci_get_progif(dev) != 0)
+ return (ENXIO);
+ if (pci_get_class(dev) != PCIC_PROCESSOR)
+ return (ENXIO);
+ if (pci_get_subclass(dev) != PCIS_PROCESSOR_POWERPC)
+ return (ENXIO);
+
+ device_set_desc(dev, "MPC85xx Root Complex bridge");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t fsl_pcib_rc_methods[] = {
+ DEVMETHOD(device_probe, fsl_pcib_rc_probe),
+ DEVMETHOD_END
+};
+
+static devclass_t fsl_pcib_rc_devclass;
+DEFINE_CLASS_1(pcib, fsl_pcib_rc_driver, fsl_pcib_rc_methods,
+ sizeof(struct fsl_pcib_softc), ofw_pcib_pci_driver);
+EARLY_DRIVER_MODULE(rcpcib, pci, fsl_pcib_rc_driver, fsl_pcib_rc_devclass, 0, 0,
+ BUS_PASS_BUS);
diff --git a/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c
new file mode 100644
index 00000000..1d806970
--- /dev/null
+++ b/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c
@@ -0,0 +1,710 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008-2012 Semihalf.
+ * 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.
+ */
+
+#include <rtems/bsd/local/opt_platform.h>
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/smp.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/hid.h>
+#include <machine/_inttypes.h>
+#include <machine/machdep.h>
+#include <machine/md_var.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+#include <machine/spr.h>
+#include <machine/vmparam.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+#include <powerpc/mpc85xx/mpc85xx.h>
+
+#include <rtems/bsd/local/platform_if.h>
+
+#ifdef SMP
+extern void *ap_pcpu;
+extern vm_paddr_t kernload; /* Kernel physical load address */
+extern uint8_t __boot_page[]; /* Boot page body */
+extern uint32_t bp_kernload;
+extern vm_offset_t __startkernel;
+
+struct cpu_release {
+ uint32_t entry_h;
+ uint32_t entry_l;
+ uint32_t r3_h;
+ uint32_t r3_l;
+ uint32_t reserved;
+ uint32_t pir;
+};
+#endif
+
+extern uint32_t *bootinfo;
+vm_paddr_t ccsrbar_pa;
+vm_offset_t ccsrbar_va;
+vm_size_t ccsrbar_size;
+
+static int cpu, maxcpu;
+
+static device_t rcpm_dev;
+static void dummy_freeze(device_t, bool);
+
+static void (*freeze_timebase)(device_t, bool) = dummy_freeze;
+
+static int mpc85xx_probe(platform_t);
+static void mpc85xx_mem_regions(platform_t, struct mem_region *phys,
+ int *physsz, struct mem_region *avail, int *availsz);
+static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref);
+static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref);
+static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref);
+static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref);
+static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu);
+static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap);
+
+static void mpc85xx_reset(platform_t);
+
+static platform_method_t mpc85xx_methods[] = {
+ PLATFORMMETHOD(platform_probe, mpc85xx_probe),
+ PLATFORMMETHOD(platform_attach, mpc85xx_attach),
+ PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions),
+ PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq),
+
+ PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu),
+ PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu),
+ PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp),
+ PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu),
+ PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync),
+
+ PLATFORMMETHOD(platform_reset, mpc85xx_reset),
+
+ PLATFORMMETHOD_END
+};
+
+DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0);
+
+PLATFORM_DEF(mpc85xx_platform);
+
+static int
+mpc85xx_probe(platform_t plat)
+{
+ u_int pvr = (mfpvr() >> 16) & 0xFFFF;
+
+ switch (pvr) {
+ case FSL_E500v1:
+ case FSL_E500v2:
+ case FSL_E500mc:
+ case FSL_E5500:
+ case FSL_E6500:
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+int
+mpc85xx_attach(platform_t plat)
+{
+ phandle_t cpus, child, ccsr;
+ const char *soc_name_guesses[] = {"/soc", "soc", NULL};
+ const char **name;
+ pcell_t ranges[6], acells, pacells, scells;
+ uint64_t ccsrbar, ccsrsize;
+ int i;
+
+ if ((cpus = OF_finddevice("/cpus")) != -1) {
+ for (maxcpu = 0, child = OF_child(cpus); child != 0;
+ child = OF_peer(child), maxcpu++)
+ ;
+ } else
+ maxcpu = 1;
+
+ /*
+ * Locate CCSR region. Irritatingly, there is no way to find it
+ * unless you already know where it is. Try to infer its location
+ * from the device tree.
+ */
+
+ ccsr = -1;
+ for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++)
+ ccsr = OF_finddevice(*name);
+ if (ccsr == -1) {
+ char type[64];
+
+ /* That didn't work. Search for devices of type "soc" */
+ child = OF_child(OF_peer(0));
+ for (OF_child(child); child != 0; child = OF_peer(child)) {
+ if (OF_getprop(child, "device_type", type, sizeof(type))
+ <= 0)
+ continue;
+
+ if (strcmp(type, "soc") == 0) {
+ ccsr = child;
+ break;
+ }
+ }
+ }
+
+ if (ccsr == -1)
+ panic("Could not locate CCSR window!");
+
+ OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells));
+ OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells));
+ OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells,
+ sizeof(pacells));
+ OF_getprop(ccsr, "ranges", ranges, sizeof(ranges));
+ ccsrbar = ccsrsize = 0;
+ for (i = acells; i < acells + pacells; i++) {
+ ccsrbar <<= 32;
+ ccsrbar |= ranges[i];
+ }
+ for (i = acells + pacells; i < acells + pacells + scells; i++) {
+ ccsrsize <<= 32;
+ ccsrsize |= ranges[i];
+ }
+ ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize);
+ ccsrbar_pa = ccsrbar;
+ ccsrbar_size = ccsrsize;
+
+ mpc85xx_enable_l3_cache();
+
+ return (0);
+}
+
+void
+mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
+ struct mem_region *avail, int *availsz)
+{
+
+ ofw_mem_regions(phys, physsz, avail, availsz);
+}
+
+static u_long
+mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref)
+{
+ u_long ticks;
+ phandle_t cpus, child;
+ pcell_t freq;
+
+ if (bootinfo != NULL) {
+ if (bootinfo[0] == 1) {
+ /* Backward compatibility. See 8-STABLE. */
+ ticks = bootinfo[3] >> 3;
+ } else {
+ /* Compatibility with Juniper's loader. */
+ ticks = bootinfo[5] >> 3;
+ }
+ } else
+ ticks = 0;
+
+ if ((cpus = OF_finddevice("/cpus")) == -1)
+ goto out;
+
+ if ((child = OF_child(cpus)) == 0)
+ goto out;
+
+ switch (OF_getproplen(child, "timebase-frequency")) {
+ case 4:
+ {
+ uint32_t tbase;
+ OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));
+ ticks = tbase;
+ return (ticks);
+ }
+ case 8:
+ {
+ uint64_t tbase;
+ OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));
+ ticks = tbase;
+ return (ticks);
+ }
+ default:
+ break;
+ }
+
+ freq = 0;
+ if (OF_getprop(child, "bus-frequency", (void *)&freq,
+ sizeof(freq)) <= 0)
+ goto out;
+
+ if (freq == 0)
+ goto out;
+
+ /*
+ * Time Base and Decrementer are updated every 8 CCB bus clocks.
+ * HID0[SEL_TBCLK] = 0
+ */
+ if (mpc85xx_is_qoriq())
+ ticks = freq / 32;
+ else
+ ticks = freq / 8;
+
+out:
+ if (ticks <= 0)
+ panic("Unable to determine timebase frequency!");
+
+ return (ticks);
+}
+
+static int
+mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
+{
+
+ cpu = 0;
+ cpuref->cr_cpuid = cpu;
+ cpuref->cr_hwref = cpuref->cr_cpuid;
+ if (bootverbose)
+ printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid);
+ cpu++;
+
+ return (0);
+}
+
+static int
+mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
+{
+
+ if (cpu >= maxcpu)
+ return (ENOENT);
+
+ cpuref->cr_cpuid = cpu++;
+ cpuref->cr_hwref = cpuref->cr_cpuid;
+ if (bootverbose)
+ printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid);
+
+ return (0);
+}
+
+static int
+mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
+{
+
+ cpuref->cr_cpuid = mfspr(SPR_PIR);
+ cpuref->cr_hwref = cpuref->cr_cpuid;
+
+ return (0);
+}
+
+#ifdef SMP
+static int
+mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc)
+{
+ vm_paddr_t rel_pa, bptr;
+ volatile struct cpu_release *rel;
+ vm_offset_t rel_va, rel_page;
+ phandle_t node;
+ int i;
+
+ /* If we're calling this, the node already exists. */
+ node = OF_finddevice("/cpus");
+ for (i = 0, node = OF_child(node); i < pc->pc_cpuid;
+ i++, node = OF_peer(node))
+ ;
+ if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa,
+ sizeof(rel_pa)) == -1) {
+ return (ENOENT);
+ }
+
+ rel_page = kva_alloc(PAGE_SIZE);
+ if (rel_page == 0)
+ return (ENOMEM);
+
+ critical_enter();
+ rel_va = rel_page + (rel_pa & PAGE_MASK);
+ pmap_kenter(rel_page, rel_pa & ~PAGE_MASK);
+ rel = (struct cpu_release *)rel_va;
+ bptr = pmap_kextract((uintptr_t)__boot_page);
+ cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
+ rel->pir = pc->pc_cpuid; __asm __volatile("sync");
+ rel->entry_h = (bptr >> 32);
+ rel->entry_l = bptr; __asm __volatile("sync");
+ cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
+ if (bootverbose)
+ printf("Waking up CPU %d via CPU release page %p\n",
+ pc->pc_cpuid, rel);
+ critical_exit();
+ pmap_kremove(rel_page);
+ kva_free(rel_page, PAGE_SIZE);
+
+ return (0);
+}
+#endif
+
+static int
+mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
+{
+#ifdef SMP
+ vm_paddr_t bptr;
+ uint32_t reg;
+ int timeout;
+ uintptr_t brr;
+ int cpuid;
+ int epapr_boot = 0;
+ uint32_t tgt;
+
+ if (mpc85xx_is_qoriq()) {
+ reg = ccsr_read4(OCP85XX_COREDISR);
+ cpuid = pc->pc_cpuid;
+
+ if ((reg & (1 << cpuid)) != 0) {
+ printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid);
+ return (-1);
+ }
+
+ brr = OCP85XX_BRR;
+ } else {
+ brr = OCP85XX_EEBPCR;
+ cpuid = pc->pc_cpuid + 24;
+ }
+ bp_kernload = kernload;
+ /*
+ * bp_kernload is in the boot page. Sync the cache because ePAPR
+ * booting has the other core(s) already running.
+ */
+ cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload));
+
+ ap_pcpu = pc;
+ __asm __volatile("msync; isync");
+
+ /* First try the ePAPR way. */
+ if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) {
+ epapr_boot = 1;
+ goto spin_wait;
+ }
+
+ reg = ccsr_read4(brr);
+ if ((reg & (1 << cpuid)) != 0) {
+ printf("SMP: CPU %d already out of hold-off state!\n",
+ pc->pc_cpuid);
+ return (ENXIO);
+ }
+
+ /* Flush caches to have our changes hit DRAM. */
+ cpu_flush_dcache(__boot_page, 4096);
+
+ bptr = pmap_kextract((uintptr_t)__boot_page);
+ KASSERT((bptr & 0xfff) == 0,
+ ("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr));
+ if (mpc85xx_is_qoriq()) {
+ /*
+ * Read DDR controller configuration to select proper BPTR target ID.
+ *
+ * On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers
+ * interleaving. If this bit is set, we have to use
+ * OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs,
+ * this bit is reserved and always 0.
+ */
+
+ reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG);
+ if (reg & (1 << 29))
+ tgt = OCP85XX_TGTIF_RAM_INTL;
+ else
+ tgt = OCP85XX_TGTIF_RAM1;
+
+ /*
+ * Set BSTR to the physical address of the boot page
+ */
+ ccsr_write4(OCP85XX_BSTRH, bptr >> 32);
+ ccsr_write4(OCP85XX_BSTRL, bptr);
+ ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK |
+ (tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2));
+
+ /* Read back OCP85XX_BSTAR to synchronize write */
+ ccsr_read4(OCP85XX_BSTAR);
+
+ /*
+ * Enable and configure time base on new CPU.
+ */
+
+ /* Set TB clock source to platform clock / 32 */
+ reg = ccsr_read4(CCSR_CTBCKSELR);
+ ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid));
+
+ /* Enable TB */
+ reg = ccsr_read4(CCSR_CTBENR);
+ ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid));
+ } else {
+ /*
+ * Set BPTR to the physical address of the boot page
+ */
+ bptr = (bptr >> 12) | 0x80000000u;
+ ccsr_write4(OCP85XX_BPTR, bptr);
+ __asm __volatile("isync; msync");
+ }
+
+ /*
+ * Release AP from hold-off state
+ */
+ reg = ccsr_read4(brr);
+ ccsr_write4(brr, reg | (1 << cpuid));
+ __asm __volatile("isync; msync");
+
+spin_wait:
+ timeout = 500;
+ while (!pc->pc_awake && timeout--)
+ DELAY(1000); /* wait 1ms */
+
+ /*
+ * Disable boot page translation so that the 4K page at the default
+ * address (= 0xfffff000) isn't permanently remapped and thus not
+ * usable otherwise.
+ */
+ if (!epapr_boot) {
+ if (mpc85xx_is_qoriq())
+ ccsr_write4(OCP85XX_BSTAR, 0);
+ else
+ ccsr_write4(OCP85XX_BPTR, 0);
+ __asm __volatile("isync; msync");
+ }
+
+ if (!pc->pc_awake)
+ panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid);
+ return ((pc->pc_awake) ? 0 : EBUSY);
+#else
+ /* No SMP support */
+ return (ENXIO);
+#endif
+}
+
+static void
+mpc85xx_reset(platform_t plat)
+{
+
+ /*
+ * Try the dedicated reset register first.
+ * If the SoC doesn't have one, we'll fall
+ * back to using the debug control register.
+ */
+ ccsr_write4(OCP85XX_RSTCR, 2);
+
+ /* Clear DBCR0, disables debug interrupts and events. */
+ mtspr(SPR_DBCR0, 0);
+ __asm __volatile("isync");
+
+ /* Enable Debug Interrupts in MSR. */
+ mtmsr(mfmsr() | PSL_DE);
+
+ /* Enable debug interrupts and issue reset. */
+ mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM);
+
+ printf("Reset failed...\n");
+ while (1)
+ ;
+}
+
+static void
+mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap)
+{
+ static volatile bool tb_ready;
+ static volatile int cpu_done;
+
+ if (ap) {
+ /* APs. Hold off until we get a stable timebase. */
+ while (!tb_ready)
+ atomic_thread_fence_seq_cst();
+ mttb(tb);
+ atomic_add_int(&cpu_done, 1);
+ while (cpu_done < mp_ncpus)
+ atomic_thread_fence_seq_cst();
+ } else {
+ /* BSP */
+ freeze_timebase(rcpm_dev, true);
+ tb_ready = true;
+ mttb(tb);
+ atomic_add_int(&cpu_done, 1);
+ while (cpu_done < mp_ncpus)
+ atomic_thread_fence_seq_cst();
+ freeze_timebase(rcpm_dev, false);
+ }
+}
+
+/* Fallback freeze. In case no real handler is found in the device tree. */
+static void
+dummy_freeze(device_t dev, bool freeze)
+{
+ /* Nothing to do here, move along. */
+}
+
+
+/* QorIQ Run control/power management timebase management. */
+
+#define RCPM_CTBENR 0x00000084
+struct mpc85xx_rcpm_softc {
+ struct resource *sc_mem;
+};
+
+static void
+mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze)
+{
+ struct mpc85xx_rcpm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (freeze)
+ bus_write_4(sc->sc_mem, RCPM_CTBENR, 0);
+ else
+ bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1);
+}
+
+static int
+mpc85xx_rcpm_probe(device_t dev)
+{
+ if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0"))
+ return (ENXIO);
+
+ device_set_desc(dev, "QorIQ Run control and power management");
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+mpc85xx_rcpm_attach(device_t dev)
+{
+ struct mpc85xx_rcpm_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ freeze_timebase = mpc85xx_rcpm_freeze_timebase;
+ rcpm_dev = dev;
+
+ rid = 0;
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ return (0);
+}
+
+static device_method_t mpc85xx_rcpm_methods[] = {
+ DEVMETHOD(device_probe, mpc85xx_rcpm_probe),
+ DEVMETHOD(device_attach, mpc85xx_rcpm_attach),
+ DEVMETHOD_END
+};
+
+static devclass_t mpc85xx_rcpm_devclass;
+
+static driver_t mpc85xx_rcpm_driver = {
+ "rcpm",
+ mpc85xx_rcpm_methods,
+ sizeof(struct mpc85xx_rcpm_softc)
+};
+
+EARLY_DRIVER_MODULE(mpc85xx_rcpm, simplebus, mpc85xx_rcpm_driver,
+ mpc85xx_rcpm_devclass, 0, 0, BUS_PASS_BUS);
+
+
+/* "Global utilities" power management/Timebase management. */
+
+#define GUTS_DEVDISR 0x00000070
+#define DEVDISR_TB0 0x00004000
+#define DEVDISR_TB1 0x00001000
+
+struct mpc85xx_guts_softc {
+ struct resource *sc_mem;
+};
+
+static void
+mpc85xx_guts_freeze_timebase(device_t dev, bool freeze)
+{
+ struct mpc85xx_guts_softc *sc;
+ uint32_t devdisr;
+
+ sc = device_get_softc(dev);
+
+ devdisr = bus_read_4(sc->sc_mem, GUTS_DEVDISR);
+ if (freeze)
+ bus_write_4(sc->sc_mem, GUTS_DEVDISR,
+ devdisr | (DEVDISR_TB0 | DEVDISR_TB1));
+ else
+ bus_write_4(sc->sc_mem, GUTS_DEVDISR,
+ devdisr & ~(DEVDISR_TB0 | DEVDISR_TB1));
+}
+
+static int
+mpc85xx_guts_probe(device_t dev)
+{
+ if (!ofw_bus_is_compatible(dev, "fsl,mpc8572-guts") &&
+ !ofw_bus_is_compatible(dev, "fsl,p1020-guts") &&
+ !ofw_bus_is_compatible(dev, "fsl,p1021-guts") &&
+ !ofw_bus_is_compatible(dev, "fsl,p1022-guts") &&
+ !ofw_bus_is_compatible(dev, "fsl,p1023-guts") &&
+ !ofw_bus_is_compatible(dev, "fsl,p2020-guts"))
+ return (ENXIO);
+
+ device_set_desc(dev, "MPC85xx Global Utilities");
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+mpc85xx_guts_attach(device_t dev)
+{
+ struct mpc85xx_rcpm_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ freeze_timebase = mpc85xx_guts_freeze_timebase;
+ rcpm_dev = dev;
+
+ rid = 0;
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ return (0);
+}
+
+static device_method_t mpc85xx_guts_methods[] = {
+ DEVMETHOD(device_probe, mpc85xx_guts_probe),
+ DEVMETHOD(device_attach, mpc85xx_guts_attach),
+ DEVMETHOD_END
+};
+
+static driver_t mpc85xx_guts_driver = {
+ "guts",
+ mpc85xx_guts_methods,
+ sizeof(struct mpc85xx_guts_softc)
+};
+
+static devclass_t mpc85xx_guts_devclass;
+
+EARLY_DRIVER_MODULE(mpc85xx_guts, simplebus, mpc85xx_guts_driver,
+ mpc85xx_guts_devclass, 0, 0, BUS_PASS_BUS);
diff --git a/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c b/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c
new file mode 100644
index 00000000..26dd52ba
--- /dev/null
+++ b/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c
@@ -0,0 +1,178 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/intr_machdep.h>
+
+#include <rtems/bsd/local/pcib_if.h>
+
+static int ofw_pcib_pci_probe(device_t bus);
+static int ofw_pcib_pci_attach(device_t bus);
+static phandle_t ofw_pcib_pci_get_node(device_t bus, device_t dev);
+static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev,
+ int intpin);
+
+static device_method_t ofw_pcib_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ofw_pcib_pci_probe),
+ DEVMETHOD(device_attach, ofw_pcib_pci_attach),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t pcib_devclass;
+
+struct ofw_pcib_softc {
+ /*
+ * This is here so that we can use pci bridge methods, too - the
+ * generic routines only need the dev, secbus and subbus members
+ * filled.
+ */
+ struct pcib_softc ops_pcib_sc;
+ phandle_t ops_node;
+ struct ofw_bus_iinfo ops_iinfo;
+};
+
+DEFINE_CLASS_1(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods,
+ sizeof(struct ofw_pcib_softc), pcib_driver);
+EARLY_DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_pci_driver, pcib_devclass, 0, 0,
+ BUS_PASS_BUS);
+
+static int
+ofw_pcib_pci_probe(device_t dev)
+{
+
+ if ((pci_get_class(dev) != PCIC_BRIDGE) ||
+ (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) {
+ return (ENXIO);
+ }
+
+ if (ofw_bus_get_node(dev) == -1)
+ return (ENXIO);
+
+ device_set_desc(dev, "OFW PCI-PCI bridge");
+ return (0);
+}
+
+static int
+ofw_pcib_pci_attach(device_t dev)
+{
+ struct ofw_pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->ops_pcib_sc.dev = dev;
+ sc->ops_node = ofw_bus_get_node(dev);
+
+ ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo,
+ sizeof(cell_t));
+
+ pcib_attach_common(dev);
+ return (pcib_attach_child(dev));
+}
+
+static phandle_t
+ofw_pcib_pci_get_node(device_t bridge, device_t dev)
+{
+ /* We have only one child, the PCI bus, so pass it our node */
+
+ return (ofw_bus_get_node(bridge));
+}
+
+static int
+ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin)
+{
+ struct ofw_pcib_softc *sc;
+ struct ofw_bus_iinfo *ii;
+ struct ofw_pci_register reg;
+ cell_t pintr, mintr[2];
+ int intrcells;
+ phandle_t iparent;
+
+ sc = device_get_softc(bridge);
+ ii = &sc->ops_iinfo;
+ if (ii->opi_imapsz > 0) {
+ pintr = intpin;
+
+ /* Fabricate imap information if this isn't an OFW device */
+ bzero(&reg, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, &reg,
+ sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
+ &iparent);
+ if (intrcells) {
+ /*
+ * If we've found a mapping, return it and don't map
+ * it again on higher levels - that causes problems
+ * in some cases, and never seems to be required.
+ */
+ mintr[0] = ofw_bus_map_intr(dev, iparent, intrcells,
+ mintr);
+ return (mintr[0]);
+ }
+ } else if (intpin >= 1 && intpin <= 4) {
+ /*
+ * When an interrupt map is missing, we need to do the
+ * standard PCI swizzle and continue mapping at the parent.
+ */
+ return (pcib_route_interrupt(bridge, dev, intpin));
+ }
+ return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent(
+ bridge)), bridge, intpin));
+}
+
diff --git a/freebsd/sys/rpc/svc.c b/freebsd/sys/rpc/svc.c
index 1f32ad30..e93c3f2b 100644
--- a/freebsd/sys/rpc/svc.c
+++ b/freebsd/sys/rpc/svc.c
@@ -1379,7 +1379,6 @@ svc_run(SVCPOOL *pool)
#else /* __rtems__ */
pool->sp_proc = NULL;
#endif /* __rtems__ */
-
/* Choose group count based on number of threads and CPUs. */
pool->sp_groupcount = max(1, min(SVC_MAXGROUPS,
min(pool->sp_maxthreads / 2, mp_ncpus) / 6));
diff --git a/freebsd/sys/rpc/svc_auth.c b/freebsd/sys/rpc/svc_auth.c
index 77d767ca..a8956aef 100644
--- a/freebsd/sys/rpc/svc_auth.c
+++ b/freebsd/sys/rpc/svc_auth.c
@@ -199,3 +199,4 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
return (FALSE);
}
}
+
diff --git a/freebsd/sys/sys/_domainset.h b/freebsd/sys/sys/_domainset.h
index 5685d532..443c68fd 100644
--- a/freebsd/sys/sys/_domainset.h
+++ b/freebsd/sys/sys/_domainset.h
@@ -43,7 +43,7 @@
#define DOMAINSET_SETSIZE DOMAINSET_MAXSIZE
#endif
-BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE);
+__BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE);
typedef struct _domainset domainset_t;
/*
diff --git a/freebsd/sys/sys/buf.h b/freebsd/sys/sys/buf.h
index 209174b4..dfe3eaa6 100644
--- a/freebsd/sys/sys/buf.h
+++ b/freebsd/sys/sys/buf.h
@@ -497,7 +497,11 @@ extern int cluster_pbuf_freecnt; /* Number of pbufs for clusters */
extern int vnode_pbuf_freecnt; /* Number of pbufs for vnode pager */
extern int vnode_async_pbuf_freecnt; /* Number of pbufs for vnode pager,
asynchronous reads */
+#ifndef __rtems__
extern caddr_t unmapped_buf; /* Data address for unmapped buffers. */
+#else /* __rtems__ */
+extern caddr_t __read_mostly unmapped_buf;
+#endif /* __rtems__ */
static inline int
buf_mapped(struct buf *bp)
diff --git a/freebsd/sys/sys/domainset.h b/freebsd/sys/sys/domainset.h
index 5a00347f..e028f3e9 100644
--- a/freebsd/sys/sys/domainset.h
+++ b/freebsd/sys/sys/domainset.h
@@ -44,34 +44,34 @@
sizeof(__XSTRING(MAXMEMDOM)))
-#define DOMAINSET_CLR(n, p) BIT_CLR(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_COPY(f, t) BIT_COPY(DOMAINSET_SETSIZE, f, t)
-#define DOMAINSET_ISSET(n, p) BIT_ISSET(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_SET(n, p) BIT_SET(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_ZERO(p) BIT_ZERO(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FILL(p) BIT_FILL(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_SETOF(n, p) BIT_SETOF(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_EMPTY(p) BIT_EMPTY(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_ISFULLSET(p) BIT_ISFULLSET(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_SUBSET(p, c) BIT_SUBSET(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_OVERLAP(p, c) BIT_OVERLAP(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_CMP(p, c) BIT_CMP(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_OR(d, s) BIT_OR(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_AND(d, s) BIT_AND(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_NAND(d, s) BIT_NAND(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_SET_ATOMIC(n, p) BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_CLR(n, p) __BIT_CLR(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_COPY(f, t) __BIT_COPY(DOMAINSET_SETSIZE, f, t)
+#define DOMAINSET_ISSET(n, p) __BIT_ISSET(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_SET(n, p) __BIT_SET(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_ZERO(p) __BIT_ZERO(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FILL(p) __BIT_FILL(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_SETOF(n, p) __BIT_SETOF(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_EMPTY(p) __BIT_EMPTY(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_ISFULLSET(p) __BIT_ISFULLSET(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_SUBSET(p, c) __BIT_SUBSET(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_OVERLAP(p, c) __BIT_OVERLAP(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_CMP(p, c) __BIT_CMP(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_OR(d, s) __BIT_OR(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_AND(d, s) __BIT_AND(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_NAND(d, s) __BIT_NAND(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p)
#define DOMAINSET_SET_ATOMIC_ACQ(n, p) \
- BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_AND_ATOMIC(n, p) BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_OR_ATOMIC(d, s) BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s)
+ __BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s)
#define DOMAINSET_COPY_STORE_REL(f, t) \
- BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t)
-#define DOMAINSET_FFS(p) BIT_FFS(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FLS(p) BIT_FLS(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_COUNT(p) BIT_COUNT(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FSET BITSET_FSET(_NDOMAINSETWORDS)
-#define DOMAINSET_T_INITIALIZER BITSET_T_INITIALIZER
+ __BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t)
+#define DOMAINSET_FFS(p) __BIT_FFS(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FLS(p) __BIT_FLS(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_COUNT(p) __BIT_COUNT(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FSET __BITSET_FSET(_NDOMAINSETWORDS)
+#define DOMAINSET_T_INITIALIZER __BITSET_T_INITIALIZER
#define DOMAINSET_POLICY_INVALID 0
#define DOMAINSET_POLICY_ROUNDROBIN 1
diff --git a/freebsd/sys/sys/vnode.h b/freebsd/sys/sys/vnode.h
index d297c931..ddb9ac30 100644
--- a/freebsd/sys/sys/vnode.h
+++ b/freebsd/sys/sys/vnode.h
@@ -266,8 +266,12 @@ struct xvnode {
*/
struct vattr {
enum vtype va_type; /* vnode type (for create) */
+#ifndef __rtems__
u_short va_mode; /* files access mode and type */
u_short va_padding0;
+#else /* __rtems__ */
+ mode_t va_mode; /* files access mode and type */
+#endif /* __rtems__ */
uid_t va_uid; /* owner user id */
gid_t va_gid; /* owner group id */
nlink_t va_nlink; /* number of references to file */
diff --git a/freebsd/sys/vm/uma_core.c b/freebsd/sys/vm/uma_core.c
index a1ea3497..8448bd77 100644
--- a/freebsd/sys/vm/uma_core.c
+++ b/freebsd/sys/vm/uma_core.c
@@ -3924,6 +3924,8 @@ uma_prealloc(uma_zone_t zone, int items)
#ifndef __rtems__
vm_domainset_iter_policy_ref_init(&di, &keg->uk_dr, &domain,
&aflags);
+#else /* __rtems__ */
+ domain = 0;
#endif /* __rtems__ */
for (;;) {
slab = keg_alloc_slab(keg, zone, domain, M_WAITOK,
diff --git a/freebsd/tools/tools/net80211/wlanstats/main.c b/freebsd/tools/tools/net80211/wlanstats/main.c
index 3a6fd204..ef2caf56 100644
--- a/freebsd/tools/tools/net80211/wlanstats/main.c
+++ b/freebsd/tools/tools/net80211/wlanstats/main.c
@@ -43,6 +43,7 @@
#ifdef __rtems__
#define __need_getopt_newlib
#include <getopt.h>
+#include <string.h>
#include <machine/rtems-bsd-program.h>
#include <machine/rtems-bsd-commands.h>
#endif /* __rtems__ */
@@ -96,6 +97,7 @@ getfmt(const char *tag)
return tag;
}
+#ifndef __rtems__
static int signalled;
static void
@@ -103,6 +105,7 @@ catchalarm(int signo __unused)
{
signalled = 1;
}
+#endif /* __rtems__ */
#if 0
static void
@@ -262,6 +265,7 @@ main(int argc, char *argv[])
wf->setstamac(wf, mac);
if (argc > 0) {
+#ifndef __rtems__
u_long interval = strtoul(argv[0], NULL, 0);
int line, omask;
@@ -283,24 +287,10 @@ main(int argc, char *argv[])
wf->print_total(wf, stdout);
}
fflush(stdout);
-#ifndef __rtems__
omask = sigblock(sigmask(SIGALRM));
if (!signalled)
sigpause(0);
sigsetmask(omask);
-#else /* __rtems__ */
- {
- sigset_t oldmask, desired, empty;
-
- sigemptyset(&empty);
- sigemptyset(&desired);
- sigaddset(&desired, SIGALRM);
- sigprocmask(SIG_BLOCK, &desired, &oldmask);
- while (!signalled)
- sigsuspend(&desired);
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- }
-#endif /* __rtems__ */
signalled = 0;
alarm(interval);
line++;
@@ -346,6 +336,10 @@ main(int argc, char *argv[])
} while (len >= sizeof(struct ieee80211req_sta_info));
}
#endif
+#else /* __rtems__ */
+ (void)mode;
+ printf("wlanstats: not implemented\n");
+#endif /* __rtems__ */
} else {
wf->collect_tot(wf);
wf->print_verbose(wf, stdout);
diff --git a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
index f88c5834..afb145be 100644
--- a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
+++ b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
@@ -1,5 +1,3 @@
/* generated by userspace-header-gen.py */
#include <rtems/linkersets.h>
#include "rtems-bsd-wlanstats-data.h"
-/* main.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_wlanstats, static int signalled);
diff --git a/freebsd/usr.bin/netstat/if.c b/freebsd/usr.bin/netstat/if.c
index c578629e..ffb639c6 100644
--- a/freebsd/usr.bin/netstat/if.c
+++ b/freebsd/usr.bin/netstat/if.c
@@ -512,6 +512,7 @@ intpr(void (*pfunc)(char *), int af)
freeifmaddrs(ifmap);
}
+#ifndef __rtems__
struct iftot {
u_long ift_ip; /* input packets */
u_long ift_ie; /* input errors */
@@ -575,6 +576,7 @@ catchalarm(int signo __unused)
{
signalled = true;
}
+#endif /* __rtems__ */
/*
* Print a running summary of interface statistics.
@@ -585,6 +587,7 @@ catchalarm(int signo __unused)
static void
sidewaysintpr(void)
{
+#ifndef __rtems__
struct iftot ift[2], *new, *old;
struct itimerval interval_it;
int oldmask, line;
@@ -619,26 +622,11 @@ loop:
xo_close_list("interface-statistics");
return;
}
-#ifdef __rtems__
- {
- sigset_t oldmask, desired, empty;
-
- sigemptyset(&empty);
- sigemptyset(&desired);
- sigaddset(&desired, SIGALRM);
- sigprocmask(SIG_BLOCK, &desired, &oldmask);
- while (!signalled)
- sigsuspend(&desired);
- signalled = false;
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- }
-#else /* __rtems__ */
oldmask = sigblock(sigmask(SIGALRM));
while (!signalled)
sigpause(0);
signalled = false;
sigsetmask(oldmask);
-#endif /* __rtems__ */
line++;
fill_iftot(new);
@@ -681,4 +669,5 @@ loop:
goto loop;
/* NOTREACHED */
+#endif /* __rtems__ */
}
diff --git a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
index 9e12b65e..9a685f33 100644
--- a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
+++ b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
@@ -1,5 +1,3 @@
/* generated by userspace-header-gen.py */
#include <rtems/linkersets.h>
#include "rtems-bsd-netstat-data.h"
-/* if.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_netstat, static sig_atomic_t signalled);
diff --git a/ipsec-tools/src/libipsec/pfkey.c b/ipsec-tools/src/libipsec/pfkey.c
index 385a21a9..cc6ad816 100644
--- a/ipsec-tools/src/libipsec/pfkey.c
+++ b/ipsec-tools/src/libipsec/pfkey.c
@@ -1836,8 +1836,18 @@ pfkey_open(void)
(void)setsockopt(so, SOL_SOCKET, SO_SNDBUF,
&bufsiz_wanted, sizeof(bufsiz_wanted));
+#ifndef __rtems__
/* Try to have have at least 2MB. If we have more, do not lower it. */
bufsiz_wanted = 2 * 1024 * 1024;
+#else /* __rtems__ */
+ /*
+ * The bufsize_wanted has an influence on the maximum number of SPDs. We
+ * don't really need that much of them on an embedded system. If some
+ * application really needs it, this can be overwritten with the
+ * pfkey_buffer option in the config file.
+ */
+ bufsiz_wanted = 128 * 1024;
+#endif /* __rtems__ */
len = sizeof(bufsiz_current);
ret = getsockopt(so, SOL_SOCKET, SO_RCVBUF,
&bufsiz_current, &len);
diff --git a/ipsec-tools/src/racoon/privsep.c b/ipsec-tools/src/racoon/privsep.c
index 8efdae84..914d8a44 100644
--- a/ipsec-tools/src/racoon/privsep.c
+++ b/ipsec-tools/src/racoon/privsep.c
@@ -80,7 +80,9 @@ static int privsep_sock[2] = { -1, -1 };
static int privsep_recv(int, struct privsep_com_msg **, size_t *);
static int privsep_send(int, struct privsep_com_msg *, size_t);
static int safety_check(struct privsep_com_msg *, int i);
+#ifndef __rtems__
static int port_check(int);
+#endif /* __rtems__ */
static int unsafe_env(char *const *);
static int unknown_name(int);
static int unsafe_path(char *, int);
@@ -321,7 +323,6 @@ privsep_init(void)
#if defined(__NetBSD__) || defined(__FreeBSD__)
setproctitle("[priv]");
#endif
-#endif /* __rtems__ */
/*
* Don't catch any signal
@@ -334,13 +335,16 @@ privsep_init(void)
signal(SIGUSR1, SIG_DFL);
signal(SIGUSR2, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
+#endif /* __rtems__ */
while (1) {
size_t len;
struct privsep_com_msg *combuf;
struct privsep_com_msg *reply;
char *data;
+#ifndef __rtems__
size_t *buflen;
+#endif /* __rtems__ */
size_t totallen;
char *bufs[PRIVSEP_NBUF_MAX];
int i;
@@ -1067,7 +1071,9 @@ privsep_getpsk(str, keylen)
vchar_t *psk;
struct privsep_com_msg *msg;
size_t len;
+#ifndef __rtems__
int *keylenp;
+#endif /* __rtems__ */
char *data;
if (geteuid() == 0)
@@ -1129,7 +1135,11 @@ privsep_socket(domain, type, protocol)
size_t len;
char *data;
struct socket_args socket_args;
+#ifndef __rtems__
int s, saved_errno = 0;
+#else /* __rtems__ */
+ int s;
+#endif /* __rtems__ */
if (geteuid() == 0)
return socket(domain, type, protocol);
diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
index 7f1e9247..6867269e 100644
--- a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
+++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
@@ -56,7 +56,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern struct dhgroup dh_modp8192);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern char *pname);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int f_foreground);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int print_location);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern u_int32_t loglevel);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern __uint32_t loglevel);
/* policy.c */
/* privsep.c */
/* proposal.c */
diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
index cdf8a74d..51b67d2e 100644
--- a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
+++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
@@ -5,8 +5,5 @@
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_active_mask);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_preset_mask);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int nfds);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int signals[]);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static sig_atomic_t volatile volatile sigreq[]);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor *allocated_fd_monitors);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor_list fd_monitor_tree[]);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct sched scflushsa);
diff --git a/ipsec-tools/src/racoon/session.c b/ipsec-tools/src/racoon/session.c
index 90120c76..bd2bd316 100644
--- a/ipsec-tools/src/racoon/session.c
+++ b/ipsec-tools/src/racoon/session.c
@@ -119,6 +119,7 @@ struct fd_monitor {
#define NUM_PRIORITIES 2
+#ifndef __rtems__
static void close_session __P((void));
static void initfds __P((void));
static void init_signal __P((void));
@@ -126,6 +127,7 @@ static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int))));
static void check_sigreq __P((void));
static void check_flushsa __P((void));
static int close_sockets __P((void));
+#endif /* __rtems__ */
#ifndef __rtems__
static fd_set preset_mask, active_mask;
@@ -140,8 +142,10 @@ static struct fd_monitor *allocated_fd_monitors;
static TAILQ_HEAD(fd_monitor_list, fd_monitor) fd_monitor_tree[NUM_PRIORITIES];
static int nfds = 0;
+#ifndef __rtems__
static volatile sig_atomic_t sigreq[NSIG + 1];
static struct sched scflushsa = SCHED_INITIALIZER();
+#endif /* __rtems__ */
void
monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority)
@@ -199,9 +203,11 @@ session(void)
{
struct timeval *timeout;
int error;
+#ifndef __rtems__
char pid_file[MAXPATHLEN];
FILE *fp;
pid_t racoon_pid = 0;
+#endif /* __rtems__ */
int i, count;
struct fd_monitor *fdm;
@@ -209,6 +215,8 @@ session(void)
#ifndef __rtems__
FD_ZERO(&preset_mask);
#else /* __rtems__ */
+ size_t allocated_mask_size = sizeof(fd_set) *
+ howmany(rtems_libio_number_iops, sizeof(fd_set) * 8);
allocated_preset_mask = calloc(sizeof(fd_set),
howmany(rtems_libio_number_iops, sizeof(fd_set) * 8));
if (allocated_preset_mask == NULL)
@@ -228,7 +236,9 @@ session(void)
/* initialize schedular */
sched_init();
+#ifndef __rtems__
init_signal();
+#endif /* __rtems__ */
if (pfkey_init() < 0)
errx(1, "failed to initialize pfkey socket");
@@ -325,24 +335,31 @@ session(void)
racoon_pid = getpid();
fprintf(fp, "%ld\n", (long)racoon_pid);
fclose(fp);
-#endif /* __rtems__ */
for (i = 0; i <= NSIG; i++)
sigreq[i] = 0;
+#endif /* __rtems__ */
while (1) {
+#ifndef __rtems__
/*
* asynchronous requests via signal.
* make sure to reset sigreq to 0.
*/
check_sigreq();
+#endif /* __rtems__ */
/* scheduling */
timeout = schedular();
/* schedular can change select() mask, so we reset
* the working copy here */
+#ifndef __rtems__
active_mask = preset_mask;
+#else /* __rtems__ */
+ memcpy(allocated_active_mask, allocated_preset_mask,
+ allocated_mask_size);
+#endif /* __rtems__ */
error = select(nfds + 1, &active_mask, NULL, NULL, timeout);
if (error < 0) {
@@ -379,6 +396,7 @@ session(void)
}
}
+#ifndef __rtems__
/* clear all status and exit program. */
static void
close_session()
@@ -391,11 +409,6 @@ close_session()
flushsainfo();
close_sockets();
backupsa_clean();
-#ifdef __rtems__
- free(allocated_preset_mask); allocated_preset_mask = NULL;
- free(allocated_active_mask); allocated_active_mask = NULL;
- free(allocated_fd_monitors); allocated_fd_monitors = NULL;
-#endif /* __rtems__ */
plog(LLV_INFO, LOCATION, NULL, "racoon process %d shutdown\n", getpid());
@@ -565,11 +578,7 @@ set_signal(sig, func)
memset((caddr_t)&sa, 0, sizeof(sa));
sa.sa_handler = func;
-#ifndef __rtems__
sa.sa_flags = SA_RESTART;
-#else /* __rtems__ */
- sa.sa_flags = 0;
-#endif /* __rtems__ */
if (sigemptyset(&sa.sa_mask) < 0)
return -1;
@@ -590,6 +599,7 @@ close_sockets()
#endif
return 0;
}
+#endif /* __rtems__ */
#ifdef __rtems__
#include "rtems-bsd-racoon-session-data.h"
diff --git a/libbsd.py b/libbsd.py
index 7593e9ba..3fa2c8f3 100644
--- a/libbsd.py
+++ b/libbsd.py
@@ -177,6 +177,8 @@ class rtems(builder.Module):
'local/ofw_if.c',
'local/pcib_if.c',
'local/pci_if.c',
+ 'local/pic_if.c',
+ 'local/xdma_if.c',
'local/usb_if.c',
'local/mmcbus_if.c',
'local/mmcbr_if.c',
@@ -817,6 +819,24 @@ class evdev(builder.Module):
)
#
+# MV643XX Ethernet driver
+#
+class if_mve(builder.Module):
+
+ def __init__(self, manager):
+ super(if_mve, self).__init__(manager, type(self).__name__)
+
+ def generate(self):
+ mm = self.manager
+ self.addRTEMSKernelSourceFiles(
+ [
+ 'sys/dev/mve/if_mve.c',
+ 'sys/dev/mve/if_mve_nexus.c',
+ ],
+ mm.generator['source']()
+ )
+
+#
# USB
#
class dev_usb(builder.Module):
@@ -1643,6 +1663,39 @@ class dev_nic_e1000(builder.Module):
)
#
+# NIC xilinx
+#
+class dev_nic_xilinx(builder.Module):
+
+ def __init__(self, manager):
+ super(dev_nic_xilinx, self).__init__(manager, type(self).__name__)
+
+ def generate(self):
+ mm = self.manager
+ self.addKernelSpaceHeaderFiles(
+ [
+ 'sys/dev/xilinx/if_xaereg.h',
+ 'sys/dev/xilinx/if_xaevar.h',
+ 'sys/dev/mii/tiphy.h',
+ 'sys/dev/xdma/xdma.h',
+ 'sys/dev/xilinx/axidma.h',
+ ]
+ )
+ self.addKernelSpaceSourceFiles(
+ [
+ 'sys/dev/xilinx/if_xae.c',
+ 'sys/dev/xdma/xdma.c',
+ 'sys/dev/xdma/xdma_mbuf.c',
+ 'sys/dev/xdma/xdma_queue.c',
+ 'sys/dev/xdma/xdma_sg.c',
+ 'sys/dev/xdma/xdma_bank.c',
+ 'sys/dev/xdma/xdma_sglist.c',
+ 'sys/dev/xilinx/axidma.c',
+ ],
+ mm.generator['source']()
+ )
+
+#
# DEC Tulip aka Intel 21143
#
class dev_nic_dc(builder.Module):
@@ -3032,17 +3085,21 @@ class pci(builder.Module):
'sys/dev/pci/pcib_support.c',
'sys/dev/pci/pci.c',
'sys/dev/pci/pci_pci.c',
+ 'sys/dev/pci/pci_subr.c',
'sys/dev/pci/pci_user.c',
+ 'sys/dev/ofw/ofwpci.c',
],
mm.generator['source']()
)
self.addKernelSpaceHeaderFiles(
[
+ 'sys/dev/ofw/ofwpci.h',
'sys/dev/pci/pcib_private.h',
'sys/dev/pci/pci_private.h',
'sys/dev/pci/pcireg.h',
'sys/dev/pci/pcivar.h',
'sys/dev/pci/pcivar.h',
+ 'sys/powerpc/mpc85xx/mpc85xx.h',
]
)
self.addCPUDependentFreeBSDHeaderFiles(
@@ -3051,6 +3108,9 @@ class pci(builder.Module):
'sys/x86/include/legacyvar.h',
'sys/x86/include/bus.h',
'sys/x86/include/pci_cfgreg.h',
+ 'sys/powerpc/include/platformvar.h',
+ 'sys/powerpc/include/hid.h',
+ 'sys/powerpc/include/pio.h',
]
)
self.addCPUDependentFreeBSDSourceFiles(
@@ -3061,7 +3121,41 @@ class pci(builder.Module):
],
mm.generator['source']()
)
+ self.addCPUDependentFreeBSDSourceFiles(
+ [ 'powerpc' ],
+ [
+ 'sys/powerpc/mpc85xx/mpc85xx.c',
+ 'sys/powerpc/mpc85xx/pci_mpc85xx.c',
+ 'sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c',
+ 'sys/powerpc/ofw/ofw_pcib_pci.c',
+ ],
+ mm.generator['source']()
+ )
+ self.addCPUDependentRTEMSSourceFiles(
+ [ 'powerpc' ],
+ [
+ 'sys/powerpc/platform_mpc85xx.c',
+ ],
+ mm.generator['source']()
+ )
+#
+# TSI148
+#
+class tsi148(builder.Module):
+
+ def __init__(self, manager):
+ super(tsi148, self).__init__(manager, type(self).__name__)
+
+ def generate(self):
+ mm = self.manager
+ self.addRTEMSKernelSourceFiles(
+ [
+ 'sys/dev/vme/tsi148.c',
+ 'sys/dev/vme/vme-rtems-compat.c',
+ ],
+ mm.generator['source']()
+ )
#
# User space
@@ -5186,7 +5280,7 @@ class in_cksum(builder.Module):
)
self.addTargetSourceCPUDependentHeaderFiles(
[ 'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k',
- 'nios2', 'sh', 'sparc', 'v850' ],
+ 'microblaze', 'nios2', 'sh', 'sparc', 'v850' ],
'mips',
[
'sys/mips/include/in_cksum.h',
@@ -5223,8 +5317,8 @@ class in_cksum(builder.Module):
self.addCPUDependentFreeBSDSourceFiles(
[
'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k',
- 'mips', 'moxie', 'nios2', 'or1k', 'riscv', 'sh', 'sparc',
- 'v850'
+ 'microblaze', 'mips', 'moxie', 'nios2', 'or1k', 'riscv', 'sh',
+ 'sparc', 'v850'
],
[
'sys/mips/mips/in_cksum.c',
@@ -5494,7 +5588,7 @@ class tests(builder.Module):
self.addTest(mm.generator['test']('smp01', ['test_main'], extraLibs = ['rtemstest']))
self.addTest(mm.generator['test']('media01', ['test_main', 'pattern-test'],
runTest = False,
- extraLibs = ['ftpd', 'telnetd']))
+ extraLibs = ['tftpfs', 'ftpd', 'telnetd']))
self.addTest(mm.generator['test']('mcast01', ['test_main']))
self.addTest(mm.generator['test']('vlan01', ['test_main'], netTest = True))
self.addTest(mm.generator['test']('lagg01', ['test_main'], netTest = True))
@@ -5536,6 +5630,9 @@ class tests(builder.Module):
self.addTest(mm.generator['test']('ipsec01', ['test_main']))
self.addTest(mm.generator['test']('openssl01', ['test_main']))
self.addTest(mm.generator['test']('openssl02', ['test_main']))
+ self.addTest(mm.generator['test']('tcpdump01', ['test_main']))
+ self.addTest(mm.generator['test']('vme01', ['test_main'],
+ runTest = False))
def load(mm):
@@ -5551,6 +5648,7 @@ def load(mm):
mm.addModule(mmc_ti(mm))
mm.addModule(dev_input(mm))
mm.addModule(evdev(mm))
+ mm.addModule(if_mve(mm))
mm.addModule(dev_usb(mm))
mm.addModule(dev_usb_controller(mm))
@@ -5587,12 +5685,14 @@ def load(mm):
# Add PCI
mm.addModule(pci(mm))
+ mm.addModule(tsi148(mm))
# Add NIC devices
mm.addModule(dev_nic(mm))
mm.addModule(dev_nic_re(mm))
mm.addModule(dev_nic_fxp(mm))
mm.addModule(dev_nic_e1000(mm))
+ mm.addModule(dev_nic_xilinx(mm))
mm.addModule(dev_nic_dc(mm))
mm.addModule(dev_nic_smc(mm))
mm.addModule(dev_nic_broadcomm(mm))
diff --git a/libbsd.txt b/libbsd.txt
deleted file mode 100644
index 3d665500..00000000
--- a/libbsd.txt
+++ /dev/null
@@ -1,1331 +0,0 @@
-RTEMS BSD Library Guide
-=======================
-:toc:
-:icons:
-:numbered:
-:website: http://www.rtems.org/
-
-The libbsd makes FreeBSD subsystems like TCP/IP, USB, SD and some more usable
-for RTEMS. It tries to follow the FreeBSD development as close as possible and
-therefore is updated to the latest FreeBSD HEAD revision from time to time.
-To find out which version of FreeBSD is currently used as the base version for
-libbsd please take a look at the
-https://git.rtems.org/rtems-libbsd/log/freebsd-org[freebsd-org] submodule.
-
-This is a guide which captures information on the
-process of merging code from FreeBSD, building this library,
-RTEMS specific support files, and general guidelines on what
-modifications to the FreeBSD source are permitted.
-
-Goals of this effort are
-
-* update TCP/IP and provide USB in RTEMS,
-* ease updating to future FreeBSD versions,
-* ease tracking changes in FreeBSD code,
-* minimize manual changes in FreeBSD code, and
-* define stable kernel/device driver API which is implemented
-by both RTEMS and FreeBSD. This is the foundation of the port.
-
-We will work to push our changes upstream to the FreeBSD Project
-and minimize changes required at each update point.
-
-*******************************************************************************
-This is a work in progress and is very likely to be incomplete.
-Please help by adding to it.
-*******************************************************************************
-
-== Getting Started
-
-=== Tool Chain ===
-
-You need a tool chain for RTEMS based on the latest RTEMS Source Builder (RSB).
-
-=== Installation Overview ===
-
-. You must configure your BSP with the +--disable-networking+ option to disable
-the old network stack. Make sure no header files of the old network stack are
-installed.
-
-. Clone the Git repository +git clone git://git.rtems.org/rtems-libbsd.git+.
-. Change into the RTEMS BSD library root directory.
-. If you want to run tests with a custom IP configuration instead of the default
- one you can use an adjusted `config.inc` configuration file.
-. Run +waf configure ...+.
-. Run +waf+.
-. Run +waf install+.
-
-Refer to the README.waf for Waf building instructions.
-
-Make sure the submodules have been initialised and are updated. If a 'git
-status' says `rtems_waf` need updating run the submodule update command:
-
- $ git submodule sync
- $ git submodule rtems_waf update
-
-=== Board Support Package Requirements ===
-
-You need the latest RTEMS version to build the libbsd master. The Board
-Support Package (BSP) must support the
-http://www.rtems.org/onlinedocs/doxygen/cpukit/html/group\__rtems\__interrupt__extension.html[Interrupt Manager Extension]
-// The first underscores have to be masked to stop asciidoc interpreting them
-to make use of generic FreeBSD based drivers.
-
-=== Board Support Package Configuration and Build ===
-
-You need to configure RTEMS for the desired BSP and install it. The BSP must
-be configured with a disabled network stack. The BSD library containing the
-new network stack is a separate package. Using a BSP installation containing
-the old network stack may lead to confusion and unpredictable results.
-
-The following script is used to build the `arm/xilinx_zynq_a9_qemu` BSP for
-our internal testing purposes:
-
--------------------------------------------------------------------------------
-#!/bin/sh
-
-cd ${HOME}/sandbox
-rm -rf b-xilinx_zynq_a9_qemu
-mkdir b-xilinx_zynq_a9_qemu
-cd b-xilinx_zynq_a9_qemu
-${HOME}/git-rtems/configure \
- --prefix=${HOME}/sandbox/install \
- --target=arm-rtems5 \
- --enable-rtemsbsp=xilinx_zynq_a9_qemu \
- --disable-networking && \
- make && \
- make install
--------------------------------------------------------------------------------
-
-The `arm/xilinx_zynq_a9_qemu` BSP running on the Qemu simulator has some
-benefits for development and test of the BSD library
-
-* it offers a NULL pointer read and write protection,
-* Qemu is a fast simulator,
-* Qemu provides support for GDB watchpoints,
-* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge
-devices (you can run multiple test instances on one virtual network).
-
-=== BSD Library Configuration and Build ===
-
-The build system based on the Waf build system. To build with Waf please refer
-to the README.waf file.
-
-Note that the libbsd supports different buildsets. These can be selected with
-the `--buildset=xxx.ini` option during the configure phase. Take a look at the
-comments in `buildset/*.ini` to see which build sets are officially supported.
-
-You can also create and provide your own buildset configuration. But remember
-that it's quite easy to break something by disabling the wrong modules. Only the
-configurations in the `buildset` directory are officially maintained.
-
-===== Example Configuration for Network Tests =====
-
-If you need some other IP configuration for the network tests that use a fixed
-IP config you can copy `config.inc` to a location outside to the source tree and
-adapt it. Then use the option `--net-test-config=NET_CONFIG` to pass the file to
-waf's configure command.
-
--------------------------------------------------------------------------------
-NET_CFG_SELF_IP = 10.0.0.2
-NET_CFG_NETMASK = 255.255.0.0
-NET_CFG_PEER_IP = 10.0.0.1
-NET_CFG_GATEWAY_IP = 10.0.0.1
--------------------------------------------------------------------------------
-
-=== BSD Library Initialization ===
-
-To initialise the BSD Library create a suitable rc.conf file. The FreeBSD man
-page rc.conf(5) provides the details needed to create a suitable format file:
-
- https://www.freebsd.org/cgi/man.cgi?rc.conf
-
-You can call one of three functions to run the initialisation once BSD has
-initialised:
-
- - rtems_bsd_run_etc_rc_conf: Run /etc/rc.conf.
- - rtems_bsd_run_rc_conf: Run a user supplied file.
- - rtems_bsd_run_rc_conf_script: Run the in memory line feed separated text string.
-
-For exapmle:
-
- void
- network_init(void)
- {
- rtems_status_code sc;
-
- sc = rtems_bsd_initialize();
- assert(sc == RTEMS_SUCCESSFUL);
-
- rtems_bsd_run_etc_rc_conf(true); /* verbose = true */
-
-}
-
-By default the networking support is builtin. Other directives can be added and
-are found in 'machine/rtems-bsd-rc-conf-directives.h'. Please check the file
-for the list.
-
-The following network names are supported:
-
- cloned_interfaces
- ifconfig_'interface'
- defaultrouter
- hostname
-
-For example:
-
- #
- # My BSD initialisation.
- #
- hostname="myhost"
- cloned_interfaces="vlan0 vlan1"
- ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0"
- fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0"
- defaultrouter="10.10.10.1"
-
-You can also intialise the BSD library using code. The following code to
-initialize the BSD library:
-
--------------------------------------------------------------------------------
-#include <assert.h>
-#include <sysexits.h>
-
-#include <rtems/bsd/bsd.h>
-
-void
-network_init(void)
-{
- rtems_status_code sc;
- int exit_code;
-
- sc = rtems_bsd_initialize();
- assert(sc == RTEMS_SUCCESSFUL);
-
- exit_code = rtems_bsd_ifconfig_lo0();
- assert(exit_code == EX_OK);
-}
--------------------------------------------------------------------------------
-
-This performs the basic network stack initialization with a loopback interface.
-Further initialization must be done using the standard BSD network
-configuration commands
-http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)]
-using `rtems_bsd_command_ifconfig()` and
-http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)]
-using `rtems_bsd_command_route()`. For an example please have a look at
-`testsuite/include/rtems/bsd/test/default-network-init.h`.
-
-=== Task Priorities and Stack Size ===
-
-The default task priority is 96 for the interrupt server task (name "IRQS"), 98
-for the timer server task (name "TIME") and 100 for all other tasks. The
-application may provide their own implementation of the
-`rtems_bsd_get_task_priority()` function (for example in the module which calls
-`rtems_bsd_initialize()`) if different values are desired.
-
-The task stack size is determined by the `rtems_bsd_get_task_stack_size()`
-function which may be provided by the application in case the default is not
-appropriate.
-
-=== Size for Allocator Domains ===
-
-The size for an allocator domain can be specified via the
-`rtems_bsd_get_allocator_domain_size()` function. The application may provide
-their own implementation of the `rtems_bsd_get_allocator_domain_size()`
-function (for example in the module which calls `rtems_bsd_initialize()`) if
-different values are desired. The default size is 8MiB for all domains.
-
-=== Redirecting or Disabling the Output ===
-
-A lot of system messages are printed to the stdout by default. If you want to
-redirect them you can overwrite the default print handler. That can even be done
-before the libbsd initialization to catch all messages. An example would look
-like follows:
-
--------------------------------------------------------------------------------
-int my_vprintf_handler(int level, const char *fmt, va_list ap) {
- /* Do something with the messages. */
-
- return number_of_printed_chars;
-}
-
-...
- /* In your initialization: */
- rtems_bsd_vprintf_handler old;
- old = rtems_bsd_set_vprintf_handler(my_vprintf_handler);
-...
--------------------------------------------------------------------------------
-
-As a special case, you can set the `rtems_bsd_vprintf_handler_mute(...)`
-provided by libbsd to suppress all output.
-
-== Network Stack Features
-
-http://roy.marples.name/projects/dhcpcd/index[DHCPCD(8)]:: DHCP client
-
-https://developer.apple.com/library/mac/documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html[dns_sd.h]:: DNS Service Discovery
-
-http://www.opensource.apple.com/source/mDNSResponder/mDNSResponder-320.10/mDNSCore/mDNSEmbeddedAPI.h[mDNS]:: Multi-Cast DNS
-
-http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4[UNIX(4)]:: UNIX-domain protocol family
-
-http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4[INET(4)]:: Internet protocol family
-
-http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4[INET6(4)]:: Internet protocol version 6 family
-
-http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4[TCP(4)]:: Internet Transmission Control Protocol
-
-http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4[UDP(4)]:: Internet User Datagram Protocol
-
-http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4[ROUTE(4)]:: Kernel packet forwarding database
-
-http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4[BPF(4)]:: Berkeley Packet Filter
-
-http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2[SOCKET(2)]:: Create an endpoint for communication
-
-http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2[KQUEUE(2)]:: Kernel event notification mechanism
-
-http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2[SELECT(2)]:: Synchronous I/O multiplexing
-
-http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2[POLL(2)]:: Synchronous I/O multiplexing
-
-http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)]:: Manually manipulate the routing tables
-
-http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)]:: Configure network interface parameters
-
-http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1[NETSTAT(1)]:: Show network status
-
-http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8[PING(8)]:: Send ICMP ECHO_REQUEST packets to network hosts
-
-http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8[PING6(8)]:: Send ICMPv6 ECHO_REQUEST packets to network hosts
-
-http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3[SYSCTL(3)]:: Get or set system information
-
-http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3[RESOLVER(3)]:: Resolver routines
-
-http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3[GETHOSTBYNAME(3)]:: Get network host entry
-
-== Network Interface Drivers
-
-=== Link Up/Down Events
-
-You can notifiy the application space of link up/down events in your network
-interface driver via the if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN)
-function. The DHCPCD(8) client is a consumer of these events for example.
-Make sure that the interface flag IFF_UP and the interface driver flag
-IFF_DRV_RUNNING is set in case the link is up, otherwise ether_output() will
-return the error status ENETDOWN.
-
-== Shell Commands
-
-=== HOSTNAME(1)
-
-In addition to the standard options the RTEMS version of the HOSTNAME(1)
-command supports the -m flag to set/get the multicast hostname of the
-mDNS resolver instance. See also rtems_mdns_sethostname() and
-rtems_mdns_gethostname().
-
-== Qemu
-
-Use the following script to set up a virtual network with three tap devices
-connected via one bridge device.
-
--------------------------------------------------------------------------------
-#!/bin/sh -x
-
-user=`whoami`
-interfaces=(1 2 3)
-
-tap=qtap
-bri=qbri
-
-case $1 in
- up)
- sudo -i brctl addbr $bri
- for i in ${interfaces[@]} ; do
- sudo -i tunctl -t $tap$i -u $user ;
- sudo -i ifconfig $tap$i up ;
- sudo -i brctl addif $bri $tap$i ;
- done
- sudo -i ifconfig $bri up
- ;;
- down)
- for i in ${interfaces[@]} ; do
- sudo -i ifconfig $tap$i down ;
- sudo -i tunctl -d $tap$i ;
- done
- sudo -i ifconfig $bri down
- sudo -i brctl delbr $bri
- ;;
-esac
--------------------------------------------------------------------------------
-
-Connect your Qemu instance to one of the tap devices, e.g.
-
--------------------------------------------------------------------------------
-qemu-system-i386 -m 512 -boot a -cpu pentium3 \
- -drive file=$HOME/qemu/pc386_fda,index=0,if=floppy,format=raw \
- -drive file=fat:$HOME/qemu/hd,format=raw \
- -net nic,model=e1000,macaddr=0e:b0:ba:5e:ba:11 \
- -net tap,ifname=qtap1,script=no,downscript=no \
- -nodefaults -nographic -serial stdio
--------------------------------------------------------------------------------
-
--------------------------------------------------------------------------------
-qemu-system-arm \
- -serial null \
- -serial mon:stdio \
- -nographic \
- -M xilinx-zynq-a9 \
- -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \
- -net tap,ifname=qtap1,script=no,downscript=no \
- -m 256M \
- -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/media01.exe
--------------------------------------------------------------------------------
-
-Make sure that each Qemu instance uses its own MAC address to avoid an address
-conflict (or otherwise use it as a test).
-
-To connect the Qemu instances with your local network use the following
-(replace 'eth0' with the network interface of your host).
-
--------------------------------------------------------------------------------
-ifconfig eth0 0.0.0.0
-brctl addif qbri eth0
-dhclient qbri
--------------------------------------------------------------------------------
-
-=== VDE and QEMU
-
-On FreeBSD you can create VDE or the Virtual Distributed Ethernet to create a
-network environment that does not need to run qemu as root or needing to drop
-the tap's privileges to run qemu.
-
-VDE creates a software switch with a default of 32 ports which means a single
-kernel tap can support 32 qemu networking sessions.
-
-To use VDE you need to build qemu with VDE support. The RSB can detect a VDE
-plug and enable VDE support in qemu when building. On FreeBSD install the VDE
-support with:
-
- # pkg install -u vde2
-
-Build qemu with the RSB.
-
-To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD
-add to your /etc/rc.conf:
-
- cloned_interfaces="bridge0 tap0"
- autobridge_interfaces="bridge0"
- autobridge_bridge0="re0 tap0"
- ifconfig_re0="up"
- ifconfig_tap0="up"
- ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0"
- defaultrouter="10.10.1.1"
-
-Start the VDE switch as root:
-
- # sysctl net.link.tap.user_open=1
- # sysctl net.link.tap.up_on_open=1
- # vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660
- # chmod 660 /dev/tap0
-
-You can connect to the VDE switch's management channel using:
-
- $ vdeterm /tmp/mgmt1
-
-To run qemu:
-
- $ qemu-system-arm \
- -serial null \
- -serial mon:stdio \
- -nographic \
- -M xilinx-zynq-a9 \
- -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \
- -net vde,id=vde0,sock=/tmp/vde1
- -m 256M \
- -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/rcconf02.exe
-
-== Issues and TODO
-
-* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map().
-
-* Priority queues are broken with clustered scheduling.
-
-* Per-CPU data should be enabled once the new stack is ready for SMP.
-
-* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP.
-
-* Multiple routing tables are not supported. Every FIB value is set to zero
- (= BSD_DEFAULT_FIB).
-
-* Process identifiers are not supported. Every PID value is set to zero
- (= BSD_DEFAULT_PID).
-
-* User credentials are not supported. The following functions allow the
- operation for everyone
- - prison_equal_ip4(),
- - chgsbsize(),
- - cr_cansee(),
- - cr_canseesocket() and
- - cr_canseeinpcb().
-
-* A basic USB functionality test that is known to work on Qemu is desirable.
-
-* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model
- so that those architectures can use new TCP/IP and USB code.
-
-* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old
- RTEMS TCP/IP stack. For some reason, the __printflike markers do not
- compile in this environment. We may want to use the FreeBSD syslog.h
- and get this addressed.
-
-* in_cksum implementations for architectures not supported by FreeBSD.
- This will require figuring out where to put implementations that do
- not originate from FreeBSD and are populated via the script.
-
-* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c").
-
-* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML
- parser and mmap().
-
-* get_cyclecount(): The implementation is a security problem.
-
-* What to do with the priority parameter present in the FreeBSD synchronization
- primitives and the thread creation functions?
-
-* TASKQUEUE(9): Support spin mutexes.
-
-* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c.
-
-* KQUEUE(2): Choose proper lock for global kqueue list.
-
-* TIMEOUT(9): Maybe use special task instead of timer server to call
- callout_tick().
-
-* sysctl_handle_opaque(): Implement reliable snapshots.
-
-* PING6(8): What to do with SIGALARM?
-
-* <sys/param.h>: Update Newlib to use a MSIZE of 256.
-
-* BPF(4): Add support for zero-copy buffers.
-
-* UNIX(4): Fix race conditions in the area of socket object and file node
- destruction. Add support for file descriptor transmission via control
- messages.
-
-* PRINTF(9): Add support for log(), the %D format specifier is missing in the
- normal printf() family.
-
-* Why is the interrupt server used? The BSD interrupt handlers can block on
-synchronization primitives like mutexes. This is in contrast to RTEMS
-interrupt service routines. The BSPs using the generic interrupt support must
-implement the `bsp_interrupt_vector_enable()` and
-`bsp_interrupt_vector_disable()` routines. They normally enable/disable a
-particular interrupt source at the interrupt controller. This can be used to
-implement the interrupt server. The interrupt server is a task that wakes-up
-in case an associated interrupt happens. The interrupt source is disabled in
-a generic interrupt handler that wakes-up the interrupt server task. Once the
-postponed interrupt processing is performed in the interrupt server the
-interrupt source is enabled again.
-
-* Convert all BSP linkcmds to use a linkcmds.base so the sections are
-easier to insert.
-
-* NIC Device Drivers
-- Only common PCI NIC drivers have been included in the initial set. These
-do not include any system on chip or ISA drivers.
-- PCI configuration probe does not appear to happen to determine if a
-NIC is in I/O or memory space. We have worked around this by using a
-static hint to tell the fxp driver the correct mode. But this needs to
-be addressed.
-- The ISA drivers require more BSD infrastructure to be addressed. This was
-outside the scope of the initial porting effort.
-
-== FreeBSD Source
-
-You should be able to rely on FreebSD manual pages and documentation
-for details on the code itself.
-
-== BSD Library Source
-
-== Initialization of the BSD Library
-
-The initialization of the BSD library is based on the FreeBSD SYSINIT(9)
-infrastructure. The key to initializing a system is to ensure that the desired
-device drivers are explicitly pulled into the linked application. This plus
-linking against the BSD library (`libbsd.a`) will pull in the necessary FreeBSD
-infrastructure.
-
-The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of
-object files linked together. If we have a library, then creating the
-executable is simple. We begin with a start symbol and recursively resolve all
-references. With a bunch of object files linked together we need a different
-mechanism. Most object files don't know each other. Lets say we have a driver
-module. The rest of the system has no references to this driver module. The
-driver module needs a way to tell the rest of the system: Hey, kernel I am
-here, please use my services!
-
-This registration of independent components is performed by SYSINIT(9) and
-specializations:
-
-http://www.freebsd.org/cgi/man.cgi?query=SYSINIT
-
-The SYSINIT(9) uses some global data structures that are placed in a certain
-section. In the linker command file we need this:
-
--------------------------------------------------------------------------------
-.rtemsroset : {
- KEEP (*(SORT(.rtemsroset.*)))
-}
-
-.rtemsrwset : {
- KEEP (*(SORT(.rtemsrwset.*)))
-}
--------------------------------------------------------------------------------
-
-This results for example in this executable layout:
-
--------------------------------------------------------------------------------
-[...]
- *(SORT(.rtemsroset.*))
- .rtemsroset.bsd.modmetadata_set.begin
- 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o)
- 0x000000000025fe00 _bsd__start_set_modmetadata_set
- .rtemsroset.bsd.modmetadata_set.content
- 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o)
- .rtemsroset.bsd.modmetadata_set.content
- 0x000000000025fe08 0x4 libbsd.a(kern_module.o)
-[...]
- .rtemsroset.bsd.modmetadata_set.content
- 0x000000000025fe68 0x4 libbsd.a(mii.o)
- .rtemsroset.bsd.modmetadata_set.content
- 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o)
- .rtemsroset.bsd.modmetadata_set.end
- 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o)
- 0x000000000025fe70 _bsd__stop_set_modmetadata_set
-[...]
-.rtemsrwset 0x000000000030bad0 0x290
- *(SORT(.rtemsrwset.*))
- .rtemsrwset.bsd.sysinit_set.begin
- 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o)
- 0x000000000030bad0 _bsd__start_set_sysinit_set
- .rtemsrwset.bsd.sysinit_set.content
- 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o)
- .rtemsrwset.bsd.sysinit_set.content
- 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o)
- .rtemsrwset.bsd.sysinit_set.content
- 0x000000000030badc 0x4 libbsd.a(init_main.o)
-[...]
- .rtemsrwset.bsd.sysinit_set.content
- 0x000000000030bd54 0x4 libbsd.a(frag6.o)
- .rtemsrwset.bsd.sysinit_set.content
- 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o)
- .rtemsrwset.bsd.sysinit_set.end
- 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o)
- 0x000000000030bd60 _bsd__stop_set_sysinit_set
-[...]
--------------------------------------------------------------------------------
-
-Here you can see, that some global data structures are collected into
-continuous memory areas. This memory area can be identified by start and stop
-symbols. This constructs a table of uniform items.
-
-The low level FreeBSD code calls at some time during the initialization the
-mi_startup() function (machine independent startup). This function will sort
-the SYSINIT(9) set and call handler functions which perform further
-initialization. The last step is the scheduler invocation.
-
-The SYSINIT(9) routines are run in mi_startup() which is called by
-rtems_bsd_initialize().
-
-This is also explained in "The Design and Implementation of the FreeBSD
-Operating System" section 14.3 "Kernel Initialization".
-
-In RTEMS we have a library and not a bunch of object files. Thus we need a way
-to pull-in the desired services out of the libbsd. Here the
-`rtems-bsd-sysinit.h` comes into play. The SYSINIT(9) macros have been
-modified and extended for RTEMS in `<sys/kernel.h>`:
-
--------------------------------------------------------------------------------
-#ifndef __rtems__
-#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
- static struct sysinit uniquifier ## _sys_init = { \
- subsystem, \
- order, \
- func, \
- (ident) \
- }; \
- DATA_SET(sysinit_set,uniquifier ## _sys_init)
-#else /* __rtems__ */
-#define SYSINIT_ENTRY_NAME(uniquifier) \
- _bsd_ ## uniquifier ## _sys_init
-#define SYSINIT_REFERENCE_NAME(uniquifier) \
- _bsd_ ## uniquifier ## _sys_init_ref
-#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
- struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \
- subsystem, \
- order, \
- func, \
- (ident) \
- }; \
- RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier))
-#define SYSINIT_REFERENCE(uniquifier) \
- extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \
- static struct sysinit const * const \
- SYSINIT_REFERENCE_NAME(uniquifier) __used \
- = &SYSINIT_ENTRY_NAME(uniquifier)
-#define SYSINIT_MODULE_REFERENCE(mod) \
- SYSINIT_REFERENCE(mod ## module)
-#define SYSINIT_DRIVER_REFERENCE(driver, bus) \
- SYSINIT_MODULE_REFERENCE(driver ## _ ## bus)
-#define SYSINIT_DOMAIN_REFERENCE(dom) \
- SYSINIT_REFERENCE(domain_add_ ## dom)
-#endif /* __rtems__ */
--------------------------------------------------------------------------------
-
-Here you see that the SYSINIT(9) entries are no longer static. The
-\*_REFERENCE() macros will create references to the corresponding modules which
-are later resolved by the linker. The application has to provide an object
-file with references to all required FreeBSD modules.
-
-The FreeBSD device model is quite elaborated (with follow-ups):
-
-http://www.freebsd.org/cgi/man.cgi?query=driver
-
-The devices form a tree with the Nexus device at a high-level. This Nexus
-device is architecture specific in FreeBSD. In RTEMS we have our own Nexus
-device, see `rtemsbsd/bsp/bsp-bsd-nexus-devices.c`.
-
-=== SYSCTL_NODE Example
-
-During development, we had an undefined reference to
-_bsd_sysctl__net_children that we had trouble tracking down. Thanks to
-Chris Johns, we located it. He explained how to read SYSCTL_NODE
-definitions. This line from freebsd/netinet/in_proto.c is attempting
-to add the "inet" node to the parent node "_net".
-
-----
-SYSCTL_NODE(_net, PF_INET, inet, CTLFLAG_RW, 0,
- "Internet Family");
-----
-
-Our problem was that we could not find where _bsd_sysctl__net_children
-was defined. Chris suggested that when in doubt compile with -save-temps
-and look at the preprocessed .i files. But he did not need that. He
-explained that this the symbol name _bsd_sysctl__net_children was
-automatically generated by a SYSCTL_NODE as follows:
-
-* _bsd_ - added by RTEMS modifications to SYSCTL_NODE macro
-* sysctl_ - boilerplace added by SYSCTL_NODE macro
-* "" - empty string for parent node
-* net - name of SYSCTL_NODE
-* children - added by SYSCTL macros
-
-This was all generated by a support macro declaring the node as this:
-
-----
-struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name);
-----
-
-Given this information, we located this SYSCTL_NODE declaration in
-kern/kern_mib.c
-
-----
-SYSCTL_NODE(, CTL_KERN, kern, CTLFLAG_RW, 0,
- "High kernel, proc, limits &c");
-----
-
-== Core FreeBSD APIs and RTEMS Replacements ==
-
-=== SX(9) (Shared/exclusive locks) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=sx
-
-Binary semaphores (this neglects the ability to allow shared access).
-
-=== MUTEX(9) (Mutual exclusion) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=mutex
-
-Binary semaphores (not recursive mutexes are not supported this way).
-
-=== RWLOCK(9) (Reader/writer lock) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=rwlock
-
-POSIX r/w lock.
-
-=== RMLOCK(9) (Reader/writer lock optimized for mostly read access patterns) ===
-
-Note: This object was implemented as a wrapper for RWLOCK in the rm_lock header file.
-
-http://www.freebsd.org/cgi/man.cgi?query=rmlock
-
-POSIX r/w lock.
-
-=== CONDVAR(9) (Condition variables) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=condvar
-
-POSIX condition variables with modifications (hack).
-
-=== CALLOUT(9) (Timer functions) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=callout
-
-Timer server.
-
-=== TASKQUEUE(9) (Asynchronous task execution) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=taskqueue
-
-TBD.
-
-=== KTHREAD(9), KPROC(9) (Tasks) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=kthread
-
-http://www.freebsd.org/cgi/man.cgi?query=kproc
-
-Tasks.
-
-=== ZONE(9) (Zone allocator) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=zone
-
-TBD.
-
-=== devfs (Device file system) ===
-
-There is a minimal implementation based on IMFS. The mount point is fixed to
-"/dev". Note that the devfs is only used by the cdev subsystem. cdev has been
-adapted so that the full path (including the leading "/dev") is given to devfs.
-This saves some copy operations.
-
-devfs_create() first creates the full path and then creates an IMFS generic node
-for the device.
-
-TBD: remove empty paths on devfs_destroy().
-
-=== psignal (Signals) ===
-
-TBD. Seems to be not needed.
-
-=== poll, select ===
-
-TBD. Seems to be not needed.
-
-=== RMAN(9) (Resource management) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=rman
-
-TBD. Seems to be not needed.
-
-=== DEVCLASS(9), DEVICE(9), DRIVER(9), MAKE_DEV(9) (Device management) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=devclass
-
-http://www.freebsd.org/cgi/man.cgi?query=device
-
-http://www.freebsd.org/cgi/man.cgi?query=driver
-
-http://www.freebsd.org/cgi/man.cgi?query=make_dev
-
-Use FreeBSD implementation as far as possible. FreeBSD has a nice API for
-dynamic device handling. It may be interesting for RTEMS to use this API
-internally in the future.
-
-=== BUS_SPACE(9), BUS_DMA(9) (Bus and DMA access) ===
-
-http://www.freebsd.org/cgi/man.cgi?query=bus_space
-
-http://www.freebsd.org/cgi/man.cgi?query=bus_dma
-
-Likely BSP dependent. A default implementation for memory mapped linear access
-is easy to provide. The current heap implementation supports all properties
-demanded by bus_dma (including the boundary constraint).
-
-== RTEMS Replacements by File Description ==
-
-Note: Files with a status of USB are used by the USB test and have at least
-been partially tested. If they contain both USB and Nic, then they are used
-by both and MAY contain methods that have not been tested yet. Files that
-are only used by the Nic test are the most suspect.
-
-----
-rtems-libbsd File: rtems-bsd-assert.c
-FreeBSD File: rtems-bsd-config.h redefines BSD_ASSERT.
-Description: This file contains the support method rtems_bsd_assert_func().
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-autoconf.c
-FreeBSD File: FreeBSD has BSP specific autoconf.c
-Description: This file contains configuration methods that are used to setup the system.
-Status: USB
-
-rtems-libbsd File: rtems-bsd-bus-dma.c
-FreeBSD File: FreeBSD has BSP specific busdma_machdep.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-bus-dma-mbuf.c
-FreeBSD File: FreeBSD has BSP specific busdma_machdep.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-callout.c
-FreeBSD File: kern/kern_timeout.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-cam.c
-FreeBSD File: cam/cam_sim.c
-Description:
-Status: USB
-
-rtems-libbsd File: rtems-bsd-condvar.c
-FreeBSD File: kern/kern_condvar.c
-Description:
-Status: USB
-
-rtems-libbsd File: rtems-bsd-copyinout.c
-FreeBSD File: bsp specific copyinout.c )
-Description: Note: The FreeBSD file is split with some methods being in rtems-bsd-support
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-delay.c
-FreeBSD File: bsp specific file with multiple names
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-descrip.c
-FreeBSD File: kern/kern_descrip.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-generic.c
-FreeBSD File: kern/sys_generic.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-init.c
-FreeBSD File: N/A
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-init-with-irq.c
-FreeBSD File: N/A
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-jail.c
-FreeBSD File: kern/kern_jail.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-lock.c
-FreeBSD File: kern/subr_lock.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-log.c
-FreeBSD File: kern/subr_prf.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-malloc.c
-FreeBSD File: kern/kern_malloc.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-mutex.c
-FreeBSD File: kern/kern_mutex.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-newproc.c
-FreeBSD File: N/A
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-nexus.c
-FreeBSD File: bsp specific nexus.c
-Description:
-Status: USB
-
-rtems-libbsd File: rtems-bsd-panic.c
-FreeBSD File: boot/common/panic.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-rwlock.c
-FreeBSD File: kern_rwlock.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-shell.c
-FreeBSD File: N/A
-Description:
-Status: USB
-
-rtems-libbsd File: rtems-bsd-signal.c
-FreeBSD File: kern/kern_sig.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-smp.c
-FreeBSD File: N/A
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-support.c
-FreeBSD File: bsp specific copyinout.c
-Description: Note: the FreeBSD file is split with some methods being in rtems-bsd-copyinout.
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-sx.c
-FreeBSD File: kern/kern_sx.c
-Description: Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-synch.c
-FreeBSD File: kern/kern_synch.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-syscalls.c
-FreeBSD File: User API for kern/uipc_syscalls.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-sysctlbyname.c
-FreeBSD File: User API for sysctlbyname(3)
-Description:
-Status:
-
-rtems-libbsd File: rtems-bsd-sysctl.c
-FreeBSD File: User API for sysctl(8)
-Description:
-Status:
-
-rtems-libbsd File: rtems-bsd-sysctlnametomib.c
-FreeBSD File: User API for sysctlnametomib
-Description:
-Status:
-
-rtems-libbsd File: rtems-bsd-taskqueue.c
-FreeBSD File: kern/subr_taskqueue.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-thread.c
-FreeBSD File: kern/kern_kthread.c
-Description:
-Status: USB, Nic
-
-rtems-libbsd File: rtems-bsd-timeout.c
-FreeBSD File: kern/kern_timeout.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-timesupport.c
-FreeBSD File: kern/kern_clock.c
-Description:
-Status: Nic
-
-rtems-libbsd File: rtems-bsd-vm_glue.c
-FreeBSD File: vm/vm_glue.c
-Description:
-Status: USB, Nic
-----
-
-== Notes by File ==
-
-altq_subr.c - Arbitrary choices were made in this file that RTEMS would
-not support tsc frequency change. Additionally, the clock frequency
-for machclk_freq is always measured for RTEMS.
-
-conf.h - In order to add make_dev and destroy_dev, variables in the cdev
-structure that were not being used were conditionally compiled out. The
-capability of supporting children did not appear to be needed and was
-not implemented in the rtems version of these routines.
-
-== NICs Status ==
-
-----
-Driver Symbol Status
-====== ====== ======
-RealTek _bsd_re_pcimodule_sys_init Links
-EtherExpress _bsd_fxp_pcimodule_sys_init Links
-DEC tulip _bsd_dc_pcimodule_sys_init Links
-Broadcom BCM57xxx _bsd_bce_pcimodule_sys_init Links
-Broadcom BCM4401 _bsd_bfe_pcimodule_sys_init Links
-Broadcom BCM570x _bsd_bge_pcimodule_sys_init Needs Symbols (A)
-E1000 IGB _bsd_igb_pcimodule_sys_init Links
-E1000 EM _bsd_em_pcimodule_sys_init Links
-Cadence ? Links, works.
-----
-
-To add a NIC edit rtemsbsd/include/bsp/nexus-devices.h and add the driver
-reference to the architecture and/or BSP. For example to add the RealTek driver
-add:
-
-SYSINIT_DRIVER_REFERENCE(re, pci);
-
-and to add the MII PHY driver add:
-
-SYSINIT_DRIVER_REFERENCE(rge, miibus);
-
-The PC BSP has these entries.
-
-Symbols (A)
- pci_get_vpd_ident
-
-=== Cadence ===
-
-The cadence driver works on the Xilinx Zynq platform. The hardware checksum
-support works on real hardware but does not seem to be supported on qemu
-therefore the default state is to disable TXCSUM and RXCSUM and this can be
-enabled from the shell with:
-
- # ifconfig cgem0 rxcsum txcsum
-
-or with an ioctl call to the network interface driver with SIOCSIFCAP and the
-mask IFCAP_TXCSUM and IFCAP_RXCSUM set.
-
-== PF (Firewall) ==
-
-It is possible to use PF as a firewall. See
-[https://www.freebsd.org/doc/handbook/firewalls-pf.html] for details on the
-range of functions and for how to configure the firewall.
-
-The following is necessary to use PF on RTEMS:
-
-- You have to provide a +/etc/pf.os+ file. The firewall can use it for passive
- OS fingerprinting. If you don't want to use this feature, the file may contain
- nothing except a line of comment (for example "# empty").
-
-- If some filters use protocol names (like tcp or udp) you have to provide a
- +/etc/protocols+ file.
-
-- If some filters use service names (like ssh or http) you have to provide a
- +/etc/services+ file.
-
-- Create a rule file (normally +/etc/pf.conf+). See the FreeBSD manual for the
- syntax.
-
-- Load the rule file using the pfctl command and enable pf. Please note that the
- pfctl command needs a lot of stack. You should use at least
- RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of stack. An example initialisation can
- look like follows:
-
-----
- int exit_code;
- char *params[] = {
- "pfctl",
- "-f",
- "/etc/pf.conf",
- "-e",
- NULL
- };
-
- exit_code = rtems_bsd_command_pfctl(ARGC(params), params);
- assert(exit_code == EXIT_SUCCSESS);
-----
-
-=== Known restrictions ===
-
-- Currently PF on RTEMS always uses the configuration for memory restricted
- systems (on FreeBSD that means systems with less than 100 MB RAM). This is
- fixed in +pfctl_init_options()+.
-
-== Wireless Network (WLAN) ==
-
-The libbsd provides a basic support for WLAN. Note that currently this support
-is still in an early state. The WLAN support is _not_ enabled in the default
-buildset. You have to configure libbsd with the
-`--buildset=buildset/everything.ini` to enable that feature.
-
-The following gives a rough overview over the necessary steps to connect to an
-encrypted network with an RTL8188EU based WiFi dongle:
-
-- Reference all necessary module for your BSP. For some BSPs this is already
- done in the nexus-devices.h:
-
-----
- SYSINIT_MODULE_REFERENCE(wlan_ratectl_none);
- SYSINIT_MODULE_REFERENCE(wlan_sta);
- SYSINIT_MODULE_REFERENCE(wlan_amrr);
- SYSINIT_MODULE_REFERENCE(wlan_wep);
- SYSINIT_MODULE_REFERENCE(wlan_tkip);
- SYSINIT_MODULE_REFERENCE(wlan_ccmp);
- SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub);
- SYSINIT_REFERENCE(rtwn_rtl8188eufw);
-----
-
-- Create your wlan device using ifconfig:
- +ifconfig wlan0 create wlandev rtwn0 up+
-
-- Start a wpa_supplicant instance for that device:
- + wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf+
-
-Note that the wpa_supplicant will only be active till the device goes down. A
-workaround is to just restart it every time it exits.
-
-=== Known restrictions ===
-
-- The network interface (e.g. wlan0) is currently not automatically created. It
- would be nice, if some service would create it as soon as for example a USB
- device is connected. In FreeBSD the names are assigned via rc.conf with lines
- like +wlans_rtwn0="wlan0"+.
-
-- wpa_supplicant hast to be started after the device is created. It has to be
- restarted every time the connection goes down. Instead of this behaviour,
- there should be some service that starts and restarts wpa_supplicant
- automatically if a interface is ready. Probably the dhcpcd hooks could be used
- for that.
-
-- The current wpa_supplicant implementation is protected with a lock so it can't
- be started more than one time. If multiple interface should be used, all have
- to be handled by that single instance. That makes it hard to add interfaces
- dynamically. wpa_supplicant should be reviewed thoroughly whether multiple
- instances could be started in parallel.
-
-- The control interface of wpa_supplicant most likely doesn't work. The wpa_cli
- application is not ported.
-
-== IPSec ==
-
-The IPSec support is optional in libbsd. It is disabled in the default build
-set. Please make sure to use a build set with +netipsec = on+.
-
-To use IPSec the following configuration is necessary:
-
-----
-SYSINIT_MODULE_REFERENCE(if_gif);
-SYSINIT_MODULE_REFERENCE(cryptodev);
-RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec)
-RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL);
-----
-
-Alternatively you can use the `RTEMS_BSD_CONFIG_IPSEC` which also includes the
-rc.conf support for ipsec. It's still necessary to include a crypto device in
-your config (`cryptosoft` in the above sample).
-
-The necessary initialization steps for a IPSec connection are similar to the
-steps on a FreeBSD-System. The example assumes the following setup:
-
-- RTEMS external IP: 192.168.10.1/24
-- RTEMS internal IP: 10.10.1.1/24
-- remote external IP: 192.168.10.10/24
-- remote internal IP: 172.24.0.1/24
-- shared key: "mysecretkey"
-
-With this the following steps are necessary:
-
-- Create a gif0 device:
-
-----
-SHLL [/] # ifconfig gif0 create
-----
-
-- Configure the gif0 device:
-
-----
-SHLL [/] # ifconfig gif0 10.10.1.1 172.24.0.1
-SHLL [/] # ifconfig gif0 tunnel 192.168.10.1 192.168.10.10
-----
-
-- Add a route to the remote net via the remote IP:
-
-----
-SHLL [/] # route add 172.24.0.0/24 172.24.0.1
-----
-
-- Call `setkey` with a correct rule set:
-
-----
-SHLL [/] # cat /etc/setkey.conf
-flush;
-spdflush;
-spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use;
-spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use;
-SHLL [/] # setkey -f /etc/setkey.conf
-----
-
-- Start a ike-daemon (racoon) with a correct configuration.
-----
-SHLL [/] # cat /etc/racoon.conf
-path pre_shared_key "/etc/racoon_psk.txt";
-log info;
-
-padding # options are not to be changed
-{
- maximum_length 20;
- randomize off;
- strict_check off;
- exclusive_tail off;
-}
-
-listen # address [port] that racoon will listen on
-{
- isakmp 192.168.10.1[500];
-}
-
-remote 192.168.10.10 [500]
-{
- exchange_mode main;
- my_identifier address 192.168.10.1;
- peers_identifier address 192.168.10.10;
- proposal_check obey;
-
- proposal {
- encryption_algorithm 3des;
- hash_algorithm md5;
- authentication_method pre_shared_key;
- lifetime time 3600 sec;
- dh_group 2;
- }
-}
-
-sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any)
-{
- pfs_group 2;
- lifetime time 28800 sec;
- encryption_algorithm 3des;
- authentication_algorithm hmac_md5;
- compression_algorithm deflate;
-}
-SHLL [/] # cat /etc/racoon_psk.txt
-192.168.10.10 mysecretkey
-SHLL [/] # racoon -F -f /etc/racoon.conf
-----
-
-All commands can be called via the respective API functions. For racoon there is
-a `rtems_bsd_racoon_daemon()` function that forks of racoon as a task.
-
-Alternatively IPSec can also be configured via rc.conf entries:
-
-----
-cloned_interfaces="gif0"
-ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10"
-ike_enable="YES"
-ike_program="racoon"
-ike_flags="-F -f /etc/racoon.conf"
-ike_priority="250"
-
-ipsec_enable="YES"
-ipsec_file="/etc/setkey.conf"
-----
-
-ATTENTION: It is possible that the first packets slip through the tunnel without
-encryption (true for FreeBSD as well as RTEMS). You might want to set up a
-firewall rule to prevent that.
-
-== Problems to report to FreeBSD ==
-
-The MMAP_NOT_AVAILABLE define is inverted on its usage. When it is
-defined the mmap method is called. Additionally, it is not used
-thoroughly. It is not used in the unmap portion of the source.
-The file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap
-the call to mmap and file rec_close.c uses the munmap method.
diff --git a/rtemsbsd/arm/include/arm/lpc/probe.h b/rtemsbsd/arm/include/arm/lpc/probe.h
new file mode 100644
index 00000000..54f9ebda
--- /dev/null
+++ b/rtemsbsd/arm/include/arm/lpc/probe.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef _ARM_LPC_PROBE_H
+#define _ARM_LPC_PROBE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int lpc_eth_probe(int unit);
+
+int lpc_ohci_probe(int unit);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ARM_LPC_PROBE_H */
diff --git a/rtemsbsd/fs/nfsclient/nfs.c b/rtemsbsd/fs/nfsclient/nfs.c
index ffbf7f4d..a3a29b62 100644
--- a/rtemsbsd/fs/nfsclient/nfs.c
+++ b/rtemsbsd/fs/nfsclient/nfs.c
@@ -830,8 +830,10 @@ rtems_nfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry, const void *data)
{
struct nfs_args args;
- const char *fspath = NULL;
+ static int mount_counter;
+ char fspath[NAME_MAX + 1];
char *at;
+ int len;
int error;
if (RTEMS_DEBUG) {
@@ -923,15 +925,30 @@ rtems_nfs_initialize(
rtems_bsd_vfs_mount_init(mt_entry);
- fspath = mt_entry->target;
- if (*fspath == '/') {
- ++fspath;
+ at = mt_entry->target + strlen(mt_entry->target);
+ len = 0;
+ while (at != mt_entry->target && !rtems_filesystem_is_delimiter(at[-1])) {
+ at--;
+ len++;
}
- if (strchr(fspath, '/') != 0) {
+
+ /*
+ * Account for the mount number and leading `/`
+ */
+ if (len >= sizeof(fspath) - (6 + 2)) {
error = EINVAL;
goto out;
}
+ /*
+ * Append a unique number to the end of the path created in
+ * the FreeBSD root file system. All mounts are in the root
+ * directory of the root file system and so flat. A user could
+ * use different mount paths with the same end name. Without
+ * the counter appended they would clash.
+ */
+ snprintf(fspath, sizeof(fspath), "/%s-%d", at, mount_counter++);
+
if (getnfsargs(mt_entry->dev, &args) < 0) {
if (RTEMS_DEBUG)
printf(
@@ -949,7 +966,7 @@ rtems_nfs_initialize(
* export then find the vnode and hold it. Make sure we find the root
* node of the NFS share and the not the root file system's mount node.
*/
- error = rtems_bsd_rootfs_mkdir(fspath);
+ error = rtems_bsd_rootfs_mkdir(fspath + 1);
if (error == 0) {
struct addrinfo *ai;
enum tryret tryret;
@@ -964,7 +981,7 @@ rtems_nfs_initialize(
if (tryret == TRYRET_SUCCESS) {
error = nfs_trymount(mt_entry, ai, &args, fspath, data);
if (RTEMS_DEBUG)
- printf("nfs: mount: (%d) %s\n", error, strerror(error));
+ printf("nfs: mount: (%d) %s\n", error, strerror(error));
break;
} else {
error = EIO;
diff --git a/rtemsbsd/include/bsp/mv643xx_eth.h b/rtemsbsd/include/bsp/mv643xx_eth.h
new file mode 100644
index 00000000..7d122cd6
--- /dev/null
+++ b/rtemsbsd/include/bsp/mv643xx_eth.h
@@ -0,0 +1,397 @@
+/* Acknowledgement:
+ *
+ * Valuable information for developing this driver was obtained
+ * from the linux open-source driver mv643xx_eth.c which was written
+ * by the following people and organizations:
+ *
+ * Matthew Dharm <mdharm@momenco.com>
+ * rabeeh@galileo.co.il
+ * PMC-Sierra, Inc., Manish Lachwani
+ * Ralf Baechle <ralf@linux-mips.org>
+ * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org>
+ * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com>
+ *
+ * Note however, that in spite of the identical name of this file
+ * (and some of the symbols used herein) this file provides a
+ * new implementation and is the original work by the author.
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (mv643xx ethernet driver for RTEMS) was
+ * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The 'mv643xx ethernet driver for RTEMS' was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#ifndef MV643XX_LOWLEVEL_DRIVER_H
+#define MV643XX_LOWLEVEL_DRIVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MV643XXETH_NUM_DRIVER_SLOTS 2
+
+struct mveth_private;
+
+/* Create interface.
+ * Allocates resources for descriptor rings and sets up the driver software structure.
+ *
+ * Arguments:
+ * unit:
+ * interface # (1..2). The interface must not be attached to BSD.
+ *
+ * driver_tid:
+ * optional driver task-id (can be retrieved with BSP_mve_get_tid()).
+ * Not used by the low-level driver.
+ *
+ * isr(isr_arg):
+ * user ISR.
+ *
+ * void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred):
+ * Pointer to user-supplied callback to release a buffer that had been sent
+ * by BSP_mve_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg'
+ * and a flag indicating whether the send had been successful.
+ * The driver no longer accesses 'user_buf' after invoking this callback.
+ * CONTEXT: This callback is executed either by BSP_mve_swipe_tx() or
+ * BSP_mve_send_buf(), BSP_mve_init_hw(), BSP_mve_stop_hw() (the latter
+ * ones calling BSP_mve_swipe_tx()).
+ * void *cleanup_txbuf_arg:
+ * Closure argument that is passed on to 'cleanup_txbuf()' callback;
+ *
+ * void *(*alloc_rxbuf)(int *p_size, unsigned long *p_data_addr),
+ * Pointer to user-supplied callback to allocate a buffer for subsequent
+ * insertion into the RX ring by the driver.
+ * RETURNS: opaque handle to the buffer (which may be a more complex object
+ * such as an 'mbuf'). The handle is not used by the driver directly
+ * but passed back to the 'consume_rxbuf()' callback.
+ * Size of the available data area and pointer to buffer's data area
+ * in '*psize' and '*p_data_area', respectively.
+ * If no buffer is available, this routine should return NULL in which
+ * case the driver drops the last packet and re-uses the last buffer
+ * instead of handing it out to 'consume_rxbuf()'.
+ * CONTEXT: Called when initializing the RX ring (BSP_mve_init_hw()) or when
+ * swiping it (BSP_mve_swipe_rx()).
+ *
+ *
+ * void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len);
+ * Pointer to user-supplied callback to pass a received buffer back to
+ * the user. The driver no longer accesses the buffer after invoking this
+ * callback (with 'len'>0, see below). 'user_buf' is the buffer handle
+ * previously generated by 'alloc_rxbuf()'.
+ * The callback is passed 'cleanup_rxbuf_arg' and a 'len'
+ * argument giving the number of bytes that were received.
+ * 'len' may be <=0 in which case the 'user_buf' argument is NULL.
+ * 'len' == 0 means that the last 'alloc_rxbuf()' had failed,
+ * 'len' < 0 indicates a receiver error. In both cases, the last packet
+ * was dropped/missed and the last buffer will be re-used by the driver.
+ * NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header
+ * is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes)
+ * is appended. 'len' accounts for both.
+ * CONTEXT: Called from BSP_mve_swipe_rx().
+ * void *cleanup_rxbuf_arg:
+ * Closure argument that is passed on to 'consume_rxbuf()' callback;
+ *
+ * rx_ring_size, tx_ring_size:
+ * How many big to make the RX and TX descriptor rings. Note that the sizes
+ * may be 0 in which case a reasonable default will be used.
+ * If either ring size is < 0 then the RX or TX will be disabled.
+ * Note that it is illegal in this case to use BSP_mve_swipe_rx() or
+ * BSP_mve_swipe_tx(), respectively.
+ *
+ * irq_mask:
+ * Interrupts to enable. OR of flags from above.
+ *
+ */
+
+/* Direct assignment of MVE flags to user API relies on irqs and x-irqs not overlapping */
+#define MV643XX_ETH_IRQ_RX_DONE (1<<2)
+#define MV643XX_ETH_EXT_IRQ_TX_DONE (1<<0)
+#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16)
+
+struct mveth_private *
+BSP_mve_create(
+ int unit,
+ rtems_id tid,
+ void (*isr)(void*isr_arg),
+ void *isr_arg,
+ void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred),
+ void *cleanup_txbuf_arg,
+ void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+ void (*consume_rxbuf)(void *user_buf, void *closure, int len),
+ void *consume_rxbuf_arg,
+ int rx_ring_size,
+ int tx_ring_size,
+ int irq_mask
+);
+
+/*
+ * Clear multicast hash filter. No multicast frames are accepted
+ * after executing this routine (unless the hardware was initialized
+ * in 'promiscuous' mode).
+ */
+void
+BSP_mve_mcast_filter_clear(struct mveth_private *mp);
+
+/*
+ * Program multicast filter to accept all multicast frames
+ */
+void
+BSP_mve_mcast_filter_accept_all(struct mveth_private *mp);
+
+/*
+ * Add a MAC address to the multicast filter.
+ * Existing entries are not changed but note that
+ * the filter is imperfect, i.e., multiple MAC addresses
+ * may alias to a single filter entry. Hence software
+ * filtering must still be performed.
+ *
+ * If a higher-level driver implements IP multicasting
+ * then multiple IP addresses may alias to the same MAC
+ * address. This driver maintains a 'reference-count'
+ * which is incremented every time the same MAC-address
+ * is passed to this routine; the address is only removed
+ * from the filter if BSP_mve_mcast_filter_accept_del()
+ * is called the same number of times (or by BSP_mve_mcast_filter_clear).
+ */
+void
+BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr);
+
+/*
+ * Remove a MAC address from the multicast filter.
+ * This routine decrements the reference count of the given
+ * MAC-address and removes it from the filter once the
+ * count reaches zero.
+ */
+void
+BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr);
+
+
+/* Enable/disable promiscuous mode */
+void
+BSP_mve_promisc_set(struct mveth_private *mp, int promisc);
+
+/* calls BSP_mve_stop_hw(), releases all resources and marks the interface
+ * as unused.
+ * RETURNS 0 on success, nonzero on failure.
+ * NOTE: the handle MUST NOT be used after successful execution of this
+ * routine.
+ */
+int
+BSP_mve_detach(struct mveth_private *mp);
+
+/* Enqueue a buffer chain for transmission.
+ *
+ * RETURN: #bytes sent or -1 if there are not enough descriptors
+ * -2 is returned if the caller should attempt to
+ * repackage the chain into a smaller one.
+ *
+ * Comments: software cache-flushing incurs a penalty if the
+ * packet cannot be queued since it is flushed anyways.
+ * The algorithm is slightly more efficient in the normal
+ * case, though.
+ */
+
+typedef struct MveEthBufIter {
+ void *hdl; /* opaque handle for the iterator implementor */
+ void *data; /* data to be sent */
+ size_t len; /* data size (in octets) */
+ void *uptr; /* user-pointer to go into the descriptor;
+ note: cleanup_txbuf is only called for desriptors
+ where this field is non-NULL (for historical
+ reasons) */
+} MveEthBufIter;
+
+typedef MveEthBufIter *(*MveEthBufIterNext)(MveEthBufIter*);
+
+int
+BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it);
+
+
+/* Legacy entry point to send a header + a buffer */
+int
+BSP_mve_send_buf_raw(struct mveth_private *mp, void *head_p, int h_len, void *data_p, int d_len);
+
+/* Descriptor scavenger; cleanup the TX ring, passing all buffers
+ * that have been sent to the cleanup_tx() callback.
+ * This routine is called from BSP_mve_send_buf(), BSP_mve_init_hw(),
+ * BSP_mve_stop_hw().
+ *
+ * RETURNS: number of buffers processed.
+ */
+int
+BSP_mve_swipe_tx(struct mveth_private *mp);
+
+/* Retrieve all received buffers from the RX ring, replacing them
+ * by fresh ones (obtained from the alloc_rxbuf() callback). The
+ * received buffers are passed to consume_rxbuf().
+ *
+ * RETURNS: number of buffers processed.
+ */
+int
+BSP_mve_swipe_rx(struct mveth_private *mp);
+
+/* read ethernet address from hw to buffer */
+void
+BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr);
+
+/* Interrupt related routines */
+
+/* Note: the BSP_mve_enable/disable/ack_irqs() entry points
+ * are deprecated.
+ * The newer API where the user passes a mask allows
+ * for more selective control.
+ */
+
+/* Enable all supported interrupts at device */
+void
+BSP_mve_enable_irqs(struct mveth_private *mp);
+
+/* Disable all supported interrupts at device */
+void
+BSP_mve_disable_irqs(struct mveth_private *mp);
+
+/* Acknowledge (and clear) all supported interrupts.
+ * RETURNS: interrupts that were raised.
+ */
+uint32_t
+BSP_mve_ack_irqs(struct mveth_private *mp);
+
+/* Enable interrupts included in 'mask' (leaving
+ * already enabled interrupts on). If the mask
+ * includes bits that were not passed to the 'setup'
+ * routine then the behavior is undefined.
+ */
+void
+BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t irq_mask);
+
+/* Disable interrupts included in 'mask' (leaving
+ * other ones that are currently enabled on). If the
+ * mask includes bits that were not passed to the 'setup'
+ * routine then the behavior is undefined.
+ *
+ * RETURNS: Bitmask of interrupts that were enabled upon entry
+ * into this routine. This can be used to restore the
+ * previous state.
+ */
+uint32_t
+BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t irq_mask);
+
+/* Acknowledge and clear selected interrupts.
+ *
+ * RETURNS: All pending interrupts.
+ *
+ * NOTE: Only pending interrupts contained in 'mask'
+ * are cleared. Others are left pending.
+ *
+ * This routine can be used to check for pending
+ * interrupts (pass mask == 0) or to clear all
+ * interrupts (pass mask == -1).
+ */
+uint32_t
+BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask);
+
+/* Retrieve the driver daemon TID that was passed to
+ * BSP_mve_setup().
+ */
+
+rtems_id
+BSP_mve_get_tid(struct mveth_private *mp);
+
+/* Dump statistics to file (stdout if NULL)
+ *
+ * NOTE: this routine is not thread safe
+ */
+void
+BSP_mve_dump_stats(struct mveth_private *mp, FILE *f);
+
+#define MV643XX_MEDIA_LINK (1<<0)
+#define MV643XX_MEDIA_FD (1<<1)
+#define MV643XX_MEDIA_10 (1<<8)
+#define MV643XX_MEDIA_100 (2<<8)
+#define MV643XX_MEDIA_1000 (3<<8)
+#define MV643XX_MEDIA_SPEED_MSK (0xff00)
+
+/*
+ * Initialize interface hardware
+ *
+ * 'mp' handle obtained by from BSP_mve_setup().
+ * 'promisc' whether to set promiscuous flag.
+ * 'enaddr' pointer to six bytes with MAC address. Read
+ * from the device if NULL.
+ * 'speed' current speed and link status as read from the PHY.
+ *
+ * Note: Multicast filters are cleared by this routine.
+ * However, in promiscuous mode the mcast filters
+ * are programmed to accept all multicast frames.
+ */
+void
+BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int speed);
+
+/*
+ * Update the serial port to a new speed (e.g., result of a PHY IRQ)
+ */
+void
+BSP_mve_update_serial_port(struct mveth_private *mp, int speed);
+
+/*
+ * Shutdown hardware and clean out the rings
+ */
+void
+BSP_mve_stop_hw(struct mveth_private *mp);
+
+unsigned
+BSP_mve_mii_read(struct mveth_private *mp, unsigned addr);
+
+unsigned
+BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v);
+
+unsigned
+BSP_mve_mii_read_early(int port, unsigned addr);
+
+unsigned
+BSP_mve_mii_write_early(int port, unsigned addr, unsigned v);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h
index 46df17b4..ac4a52a1 100644
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -107,18 +107,14 @@ RTEMS_BSD_DRIVER_MMC;
#include <bsp/irq.h>
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus);
RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SLCR;
/* Qemu only applies user-mode networking to the first interface by default, so
* all 4 CGEM instances must be configured in the Qemu arguments using
- * "-nic user,model=cadence_gem" for each nic.
- *
- * CGEM3 is used for LibBSD because all Zynq Ultrascale+ MPSoC dev boards treat
- * the highest-mapped CGEM as the primary interface.
+ * "-nic user,model=cadence_gem" for each desired nic.
*/
-RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0(ZYNQMP_IRQ_ETHERNET_0);
-RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1(ZYNQMP_IRQ_ETHERNET_1);
-RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2(ZYNQMP_IRQ_ETHERNET_2);
-RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3(ZYNQMP_IRQ_ETHERNET_3);
+SYSINIT_DRIVER_REFERENCE(cgem, simplebus);
RTEMS_BSD_DRIVER_E1000PHY;
RTEMS_BSD_DRIVER_UKPHY;
@@ -135,6 +131,10 @@ RTEMS_BSD_DRIVER_XILINX_VERSAL_CGEM0(VERSAL_IRQ_ETHERNET_0);
RTEMS_BSD_DRIVER_XILINX_VERSAL_CGEM1(VERSAL_IRQ_ETHERNET_1);
RTEMS_BSD_DRIVER_UKPHY;
+RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI0;
+RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI1;
+RTEMS_BSD_DRIVER_MMC;
+
#elif defined(LIBBSP_ARM_ATSAM_BSP_H)
RTEMS_BSD_DRIVER_USB;
@@ -255,10 +255,22 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
#include <bsp/irq.h>
RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL);
+
SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus);
SYSINIT_DRIVER_REFERENCE(tsec, simplebus);
SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+#ifdef RTEMS_BSD_MODULE_PCI
+SYSINIT_DRIVER_REFERENCE(pcib, ofwbus);
+SYSINIT_DRIVER_REFERENCE(pci, pcib);
+SYSINIT_DRIVER_REFERENCE(pcib, pci);
+SYSINIT_DRIVER_REFERENCE(rcpcib, pci);
+#endif
+
+#ifdef RTEMS_BSD_MODULE_TSI148
+SYSINIT_DRIVER_REFERENCE(tsi148, pci);
+#endif
+
#endif /* QORIQ_CHIP_IS_T_VARIANT(QORIQ_CHIP_VARIANT) */
#elif defined(LIBBSP_POWERPC_TQM8XX_BSP_H)
@@ -266,12 +278,25 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL);
SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+#elif defined(LIBBSP_BEATNIK_BSP_H)
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(mve, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+
#elif defined(LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H)
RTEMS_BSD_DRIVER_PC_LEGACY;
RTEMS_BSD_DRIVER_PCI_DC;
RTEMS_BSD_DRIVER_UKPHY;
-#endif /* LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H */
+#elif defined(LIBBSP_MICROBLAZE_FPGA_BSP_H)
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus);
+SYSINIT_DRIVER_REFERENCE(xae, simplebus);
+SYSINIT_DRIVER_REFERENCE(axidma, simplebus);
+RTEMS_BSD_DRIVER_E1000PHY;
+
+#endif /* LIBBSP_MICROBLAZE_FPGA_BSP_H */
#endif
diff --git a/rtemsbsd/include/machine/_kernel_if.h b/rtemsbsd/include/machine/_kernel_if.h
index 08086658..16733fe3 100644
--- a/rtemsbsd/include/machine/_kernel_if.h
+++ b/rtemsbsd/include/machine/_kernel_if.h
@@ -41,6 +41,20 @@ MALLOC_DECLARE(M_IFADDR);
MALLOC_DECLARE(M_IFMADDR);
#endif
+extern struct sx ifnet_detach_sxlock;
+
+struct nvlist;
+struct ifcap_nv_bit_name;
+int if_capnv_to_capint(const struct nvlist *nv, int *old_cap,
+ const struct ifcap_nv_bit_name *nn, bool all);
+void if_capint_to_capnv(struct nvlist *nv,
+ const struct ifcap_nv_bit_name *nn, int ifr_cap, int ifr_req);
+struct siocsifcapnv_driver_data {
+ int reqcap;
+ int reqcap2;
+ struct nvlist *nvcap;
+};
+
#define ifr_buffer ifr_ifru.ifru_buffer /* user supplied buffer with its length */
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
diff --git a/rtemsbsd/include/machine/_kernel_socket.h b/rtemsbsd/include/machine/_kernel_socket.h
index e9acc744..3acee460 100644
--- a/rtemsbsd/include/machine/_kernel_socket.h
+++ b/rtemsbsd/include/machine/_kernel_socket.h
@@ -46,6 +46,7 @@
#define MSG_SOCALLBCK 0x00010000 /* for use by socket callbacks - soreceive (TCP) */
#define MSG_MORETOCOME 0x00100000 /* additional data pending */
+#define MSG_TLSAPPDATA 0x00200000 /* do not soreceive() alert rec. (TLS) */
#define CMSG_ALIGN(n) _ALIGN(n)
diff --git a/rtemsbsd/include/machine/bus.h b/rtemsbsd/include/machine/bus.h
index a0c3d63a..8a61a7d0 100644
--- a/rtemsbsd/include/machine/bus.h
+++ b/rtemsbsd/include/machine/bus.h
@@ -168,6 +168,15 @@
#endif /* BSP_HAS_PC_PCI */
/*
+ * Provide a memory tag for the DMA bus interface
+ */
+#ifdef BSP_BUS_SPACE_MEM
+#define BUS_SPACE_MEM BSP_BUS_SPACE_MEM
+#else
+#define BUS_SPACE_MEM 1
+#endif
+
+/*
* Bus address alignment.
*/
#define BUS_SPACE_ALIGNED_POINTER(p, t) ALIGNED_POINTER(p, t)
diff --git a/rtemsbsd/include/machine/resource.h b/rtemsbsd/include/machine/resource.h
index ad4f0f29..babc1ae6 100644
--- a/rtemsbsd/include/machine/resource.h
+++ b/rtemsbsd/include/machine/resource.h
@@ -45,5 +45,6 @@
#define SYS_RES_MEMORY 3
#define SYS_RES_IOPORT 4
#define SYS_RES_GPIO 5
+#define PCI_RES_BUS 6
#endif /* _RTEMS_BSD_MACHINE_RESOURCE_H_ */
diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
index 94e0d56f..c74eadbc 100644
--- a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
+++ b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
@@ -3847,6 +3847,7 @@
#define pci_find_cap_method _bsd_pci_find_cap_method
#define pci_find_class _bsd_pci_find_class
#define pci_find_dbsf _bsd_pci_find_dbsf
+#define pci_find_device _bsd_pci_find_device
#define pci_find_extcap_method _bsd_pci_find_extcap_method
#define pci_find_htcap_method _bsd_pci_find_htcap_method
#define pci_find_next_cap_method _bsd_pci_find_next_cap_method
diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
index 37bd701d..2b45c2db 100644
--- a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
+++ b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
@@ -98,6 +98,9 @@ extern "C" {
/* General define to activate BSD kernel parts */
#define _KERNEL 1
+/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */
+#define IN_HISTORICAL_NETS
+
/*
* Various developer tracing options. See waf --help and --freebsd-options.
*/
@@ -244,6 +247,14 @@ dev_t rtems_bsd__makedev(int _M, int _m);
struct dirent;
void dirent_terminate(struct dirent *dp);
+/*
+ * Enable the "new" PCI-PCI bridge driver, since this is going to be the future
+ * FreeBSD driver:
+ *
+ * https://reviews.freebsd.org/D32954
+ */
+#define NEW_PCIB 1
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/rtemsbsd/include/machine/rtems-bsd-libio.h b/rtemsbsd/include/machine/rtems-bsd-libio.h
index e662a9ec..8cc67ae3 100644
--- a/rtemsbsd/include/machine/rtems-bsd-libio.h
+++ b/rtemsbsd/include/machine/rtems-bsd-libio.h
@@ -228,7 +228,7 @@ rtems_bsd_libio_iop_hold(int fd, rtems_libio_t **iopp)
rtems_libio_t *iop = NULL;
unsigned int flags = 0;
int ffd = -1;
- if (fd < rtems_libio_number_iops) {
+ if (fd >= 0 && fd < rtems_libio_number_iops) {
iop = rtems_libio_iop(fd);
flags = rtems_libio_iop_hold(iop);
if ((flags & LIBIO_FLAGS_OPEN) != 0) {
@@ -249,7 +249,9 @@ rtems_bsd_libio_iop_hold(int fd, rtems_libio_t **iopp)
if (RTEMS_BSD_DESCRIP_TRACE)
flags = iop->flags;
} else {
- *iopp = NULL;
+ if (iopp != NULL) {
+ *iopp = NULL;
+ }
}
if (RTEMS_BSD_DESCRIP_TRACE)
printf("bsd: iop: hold: fd=%d ffd=%d refs=%d iop=%p by %p\n",
diff --git a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
index 9e1e725a..9481375b 100644
--- a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
+++ b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
@@ -160,10 +160,10 @@ extern "C" {
#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_SLCR */
/*
- * Xilinx ZynqMP Arasan SDIO Driver.
+ * Xilinx Arasan SDIO Driver.
*/
-#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI)
- #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI(_num, _base, _irq) \
+#if !defined(RTEMS_BSD_DRIVER_ARASAN_SDHCI)
+ #define RTEMS_BSD_DRIVER_ARASAN_SDHCI(_num, _base, _irq) \
static const rtems_bsd_device_resource arasan_sdhci ## _num ## _res[] = { \
{ \
.type = RTEMS_BSD_RES_MEMORY, \
@@ -178,45 +178,34 @@ extern "C" {
RTEMS_BSD_DEFINE_NEXUS_DEVICE(arasan_sdhci, _num, \
RTEMS_ARRAY_SIZE(arasan_sdhci ## _num ## _res), \
&arasan_sdhci ## _num ## _res[0])
-#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI */
+#endif /* RTEMS_BSD_DRIVER_ARASAN_SDHCI */
#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI0)
#define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI0 \
- RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI(0, 0xFF160000, 80)
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(0, 0xFF160000, 80)
#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI0 */
#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI1)
#define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI1 \
- RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI(1, 0xFF170000, 81)
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(1, 0xFF170000, 81)
#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_SDHCI1 */
-/*
- * Xilinx Zynq Arasan SDIO Driver.
- */
-#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI)
- #define RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI(_num, _base, _irq) \
- static const rtems_bsd_device_resource arasan_sdhci ## _num ## _res[] = { \
- { \
- .type = RTEMS_BSD_RES_MEMORY, \
- .start_request = 0, \
- .start_actual = (_base) \
- }, { \
- .type = RTEMS_BSD_RES_IRQ, \
- .start_request = 0, \
- .start_actual = (_irq) \
- } \
- }; \
- RTEMS_BSD_DEFINE_NEXUS_DEVICE(arasan_sdhci, _num, \
- RTEMS_ARRAY_SIZE(arasan_sdhci ## _num ## _res), \
- &arasan_sdhci ## _num ## _res[0])
-#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI */
#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI0)
#define RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI0 \
- RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI(0, 0xE0100000, 56)
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(0, 0xE0100000, 56)
#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI0 */
#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI1)
#define RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI1 \
- RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI(1, 0xE0101000, 79)
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(1, 0xE0101000, 79)
#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQ_SDHCI1 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI0)
+ #define RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI0 \
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(0, 0xF1040000, 158)
+#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI0 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI1)
+ #define RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI1 \
+ RTEMS_BSD_DRIVER_ARASAN_SDHCI(1, 0xF1050000, 160)
+#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_SDHCI1 */
+
/*
* LPC32XX Power Control (PWR).
*/
diff --git a/rtemsbsd/include/machine/rtems-bsd-program.h b/rtemsbsd/include/machine/rtems-bsd-program.h
index f71ac9cd..3062c1a2 100644
--- a/rtemsbsd/include/machine/rtems-bsd-program.h
+++ b/rtemsbsd/include/machine/rtems-bsd-program.h
@@ -60,6 +60,12 @@ rtems_bsd_program_call_main_with_data_restore(const char *name,
int (*main)(int, char **), int argc, char **argv,
void *data_buf, const size_t data_size);
+void *
+rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg);
+
+void
+rtems_bsd_program_remove_destructor(void *cookie, bool call);
+
void
rtems_bsd_program_exit(int exit_code) __dead2;
@@ -198,7 +204,7 @@ rtems_bsd_program_free(void *ptr);
#endif
#ifndef RTEMS_BSD_PROGRAM_NO_FREE_WRAP
- #define free(ptr) rtems_bsd_program_free(ptr);
+ #define free(ptr) rtems_bsd_program_free(ptr)
#endif
__END_DECLS
diff --git a/rtemsbsd/include/machine/rtems-bsd-user-space.h b/rtemsbsd/include/machine/rtems-bsd-user-space.h
index 67326a0f..a84690f3 100644
--- a/rtemsbsd/include/machine/rtems-bsd-user-space.h
+++ b/rtemsbsd/include/machine/rtems-bsd-user-space.h
@@ -41,6 +41,7 @@
#define _RTEMS_BSD_MACHINE_RTEMS_BSD_USER_SPACE_H_
#define __FreeBSD__ 1
+#define _WANT_FREEBSD_BITSET
#include <rtems/bsd/local/opt_inet6.h>
#include <machine/rtems-bsd-version.h>
diff --git a/rtemsbsd/include/rtems/bsd/bsd.h b/rtemsbsd/include/rtems/bsd/bsd.h
index d5decbf2..7d79e547 100755
--- a/rtemsbsd/include/rtems/bsd/bsd.h
+++ b/rtemsbsd/include/rtems/bsd/bsd.h
@@ -98,28 +98,6 @@ typedef struct {
rtems_status_code rtems_bsd_initialize(void);
/**
- * @brief Initializes the libbsd and starts a DHCPCD task.
- *
- * The libbsd is initialized via rtems_bsd_initialize(). If this is
- * successful, then the loop back interfaces are created. If this is
- * successful, then a DHCPCD task is started at the least important priority.
- *
- * The default devices of the BSP are initialized. Support for
- * - IF_BRIDGE(4),
- * - LAGG(4),
- * - multicast routing,
- * - UNIX(4), and
- * - VLAN(4),
- * is enabled.
- *
- * No RTEMS shell commands are registered.
- *
- * @retval RTEMS_SUCCESSFUL Successful operation.
- * @retval otherwise An error occurred.
- */
-rtems_status_code rtems_bsd_initialize_dhcp(void);
-
-/**
* @brief Configures the lo0 (loopback) interface.
*
* @return Returns an exit code, see also <sysexits.h>.
diff --git a/rtemsbsd/include/rtems/bsd/local/pic_if.h b/rtemsbsd/include/rtems/bsd/local/pic_if.h
new file mode 100644
index 00000000..a903a25d
--- /dev/null
+++ b/rtemsbsd/include/rtems/bsd/local/pic_if.h
@@ -0,0 +1,133 @@
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from source file
+ * freebsd-org/sys/powerpc/powerpc/pic_if.m
+ * with
+ * makeobjops.awk
+ *
+ * See the source file for legal information
+ */
+
+
+#ifndef _pic_if_h_
+#define _pic_if_h_
+
+/** @brief Unique descriptor for the PIC_BIND() method */
+extern struct kobjop_desc pic_bind_desc;
+/** @brief A function implementing the PIC_BIND() method */
+typedef void pic_bind_t(device_t dev, u_int irq, cpuset_t cpumask, void **priv);
+
+static __inline void PIC_BIND(device_t dev, u_int irq, cpuset_t cpumask,
+ void **priv)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_bind);
+ ((pic_bind_t *) _m)(dev, irq, cpumask, priv);
+}
+
+/** @brief Unique descriptor for the PIC_TRANSLATE_CODE() method */
+extern struct kobjop_desc pic_translate_code_desc;
+/** @brief A function implementing the PIC_TRANSLATE_CODE() method */
+typedef void pic_translate_code_t(device_t dev, u_int irq, int code,
+ enum intr_trigger *trig,
+ enum intr_polarity *pol);
+
+static __inline void PIC_TRANSLATE_CODE(device_t dev, u_int irq, int code,
+ enum intr_trigger *trig,
+ enum intr_polarity *pol)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_translate_code);
+ ((pic_translate_code_t *) _m)(dev, irq, code, trig, pol);
+}
+
+/** @brief Unique descriptor for the PIC_CONFIG() method */
+extern struct kobjop_desc pic_config_desc;
+/** @brief A function implementing the PIC_CONFIG() method */
+typedef void pic_config_t(device_t dev, u_int irq, enum intr_trigger trig,
+ enum intr_polarity pol);
+
+static __inline void PIC_CONFIG(device_t dev, u_int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_config);
+ ((pic_config_t *) _m)(dev, irq, trig, pol);
+}
+
+/** @brief Unique descriptor for the PIC_DISPATCH() method */
+extern struct kobjop_desc pic_dispatch_desc;
+/** @brief A function implementing the PIC_DISPATCH() method */
+typedef void pic_dispatch_t(device_t dev, struct trapframe *tf);
+
+static __inline void PIC_DISPATCH(device_t dev, struct trapframe *tf)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_dispatch);
+ ((pic_dispatch_t *) _m)(dev, tf);
+}
+
+/** @brief Unique descriptor for the PIC_ENABLE() method */
+extern struct kobjop_desc pic_enable_desc;
+/** @brief A function implementing the PIC_ENABLE() method */
+typedef void pic_enable_t(device_t dev, u_int irq, u_int vector, void **priv);
+
+static __inline void PIC_ENABLE(device_t dev, u_int irq, u_int vector,
+ void **priv)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_enable);
+ ((pic_enable_t *) _m)(dev, irq, vector, priv);
+}
+
+/** @brief Unique descriptor for the PIC_EOI() method */
+extern struct kobjop_desc pic_eoi_desc;
+/** @brief A function implementing the PIC_EOI() method */
+typedef void pic_eoi_t(device_t dev, u_int irq, void *priv);
+
+static __inline void PIC_EOI(device_t dev, u_int irq, void *priv)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_eoi);
+ ((pic_eoi_t *) _m)(dev, irq, priv);
+}
+
+/** @brief Unique descriptor for the PIC_IPI() method */
+extern struct kobjop_desc pic_ipi_desc;
+/** @brief A function implementing the PIC_IPI() method */
+typedef void pic_ipi_t(device_t dev, u_int cpu);
+
+static __inline void PIC_IPI(device_t dev, u_int cpu)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_ipi);
+ ((pic_ipi_t *) _m)(dev, cpu);
+}
+
+/** @brief Unique descriptor for the PIC_MASK() method */
+extern struct kobjop_desc pic_mask_desc;
+/** @brief A function implementing the PIC_MASK() method */
+typedef void pic_mask_t(device_t dev, u_int irq, void *priv);
+
+static __inline void PIC_MASK(device_t dev, u_int irq, void *priv)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_mask);
+ ((pic_mask_t *) _m)(dev, irq, priv);
+}
+
+/** @brief Unique descriptor for the PIC_UNMASK() method */
+extern struct kobjop_desc pic_unmask_desc;
+/** @brief A function implementing the PIC_UNMASK() method */
+typedef void pic_unmask_t(device_t dev, u_int irq, void *priv);
+
+static __inline void PIC_UNMASK(device_t dev, u_int irq, void *priv)
+{
+ kobjop_t _m;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,pic_unmask);
+ ((pic_unmask_t *) _m)(dev, irq, priv);
+}
+
+#endif /* _pic_if_h_ */
diff --git a/rtemsbsd/include/rtems/bsd/local/xdma_if.h b/rtemsbsd/include/rtems/bsd/local/xdma_if.h
new file mode 100644
index 00000000..e5271f60
--- /dev/null
+++ b/rtemsbsd/include/rtems/bsd/local/xdma_if.h
@@ -0,0 +1,144 @@
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from source file
+ * xdma_if.m
+ * with
+ * makeobjops.awk
+ *
+ * See the source file for legal information
+ */
+
+
+#ifndef _xdma_if_h_
+#define _xdma_if_h_
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_REQUEST() method */
+extern struct kobjop_desc xdma_channel_request_desc;
+/** @brief A function implementing the XDMA_CHANNEL_REQUEST() method */
+typedef int xdma_channel_request_t(device_t dev, struct xdma_channel *xchan,
+ struct xdma_request *req);
+
+static __inline int XDMA_CHANNEL_REQUEST(device_t dev,
+ struct xdma_channel *xchan,
+ struct xdma_request *req)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_request);
+ rc = ((xdma_channel_request_t *) _m)(dev, xchan, req);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_PREP_SG() method */
+extern struct kobjop_desc xdma_channel_prep_sg_desc;
+/** @brief A function implementing the XDMA_CHANNEL_PREP_SG() method */
+typedef int xdma_channel_prep_sg_t(device_t dev, struct xdma_channel *xchan);
+
+static __inline int XDMA_CHANNEL_PREP_SG(device_t dev,
+ struct xdma_channel *xchan)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_prep_sg);
+ rc = ((xdma_channel_prep_sg_t *) _m)(dev, xchan);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_CAPACITY() method */
+extern struct kobjop_desc xdma_channel_capacity_desc;
+/** @brief A function implementing the XDMA_CHANNEL_CAPACITY() method */
+typedef int xdma_channel_capacity_t(device_t dev, struct xdma_channel *xchan,
+ uint32_t *capacity);
+
+static __inline int XDMA_CHANNEL_CAPACITY(device_t dev,
+ struct xdma_channel *xchan,
+ uint32_t *capacity)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_capacity);
+ rc = ((xdma_channel_capacity_t *) _m)(dev, xchan, capacity);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_SUBMIT_SG() method */
+extern struct kobjop_desc xdma_channel_submit_sg_desc;
+/** @brief A function implementing the XDMA_CHANNEL_SUBMIT_SG() method */
+typedef int xdma_channel_submit_sg_t(device_t dev, struct xdma_channel *xchan,
+ struct xdma_sglist *sg, uint32_t sg_n);
+
+static __inline int XDMA_CHANNEL_SUBMIT_SG(device_t dev,
+ struct xdma_channel *xchan,
+ struct xdma_sglist *sg,
+ uint32_t sg_n)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_submit_sg);
+ rc = ((xdma_channel_submit_sg_t *) _m)(dev, xchan, sg, sg_n);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_OFW_MD_DATA() method */
+extern struct kobjop_desc xdma_ofw_md_data_desc;
+/** @brief A function implementing the XDMA_OFW_MD_DATA() method */
+typedef int xdma_ofw_md_data_t(device_t dev, pcell_t *cells, int ncells,
+ void **data);
+
+static __inline int XDMA_OFW_MD_DATA(device_t dev, pcell_t *cells, int ncells,
+ void **data)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_ofw_md_data);
+ rc = ((xdma_ofw_md_data_t *) _m)(dev, cells, ncells, data);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_ALLOC() method */
+extern struct kobjop_desc xdma_channel_alloc_desc;
+/** @brief A function implementing the XDMA_CHANNEL_ALLOC() method */
+typedef int xdma_channel_alloc_t(device_t dev, struct xdma_channel *xchan);
+
+static __inline int XDMA_CHANNEL_ALLOC(device_t dev, struct xdma_channel *xchan)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_alloc);
+ rc = ((xdma_channel_alloc_t *) _m)(dev, xchan);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_FREE() method */
+extern struct kobjop_desc xdma_channel_free_desc;
+/** @brief A function implementing the XDMA_CHANNEL_FREE() method */
+typedef int xdma_channel_free_t(device_t dev, struct xdma_channel *xchan);
+
+static __inline int XDMA_CHANNEL_FREE(device_t dev, struct xdma_channel *xchan)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_free);
+ rc = ((xdma_channel_free_t *) _m)(dev, xchan);
+ return (rc);
+}
+
+/** @brief Unique descriptor for the XDMA_CHANNEL_CONTROL() method */
+extern struct kobjop_desc xdma_channel_control_desc;
+/** @brief A function implementing the XDMA_CHANNEL_CONTROL() method */
+typedef int xdma_channel_control_t(device_t dev, struct xdma_channel *xchan,
+ int cmd);
+
+static __inline int XDMA_CHANNEL_CONTROL(device_t dev,
+ struct xdma_channel *xchan, int cmd)
+{
+ kobjop_t _m;
+ int rc;
+ KOBJOPLOOKUP(((kobj_t)dev)->ops,xdma_channel_control);
+ rc = ((xdma_channel_control_t *) _m)(dev, xchan, cmd);
+ return (rc);
+}
+
+#endif /* _xdma_if_h_ */
diff --git a/rtemsbsd/local/pic_if.c b/rtemsbsd/local/pic_if.c
new file mode 100644
index 00000000..6fa600bb
--- /dev/null
+++ b/rtemsbsd/local/pic_if.c
@@ -0,0 +1,69 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from source file
+ * freebsd-org/sys/powerpc/powerpc/pic_if.m
+ * with
+ * makeobjops.awk
+ *
+ * See the source file for legal information
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/bus.h>
+#include <sys/cpuset.h>
+#include <machine/frame.h>
+#include <rtems/bsd/local/pic_if.h>
+
+
+static pic_translate_code_t pic_translate_code_default;
+
+static void pic_translate_code_default(device_t dev, u_int irq,
+ int code, enum intr_trigger *trig, enum intr_polarity *pol)
+{
+ *trig = INTR_TRIGGER_CONFORM;
+ *pol = INTR_POLARITY_CONFORM;
+}
+
+struct kobjop_desc pic_bind_desc = {
+ 0, { &pic_bind_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_translate_code_desc = {
+ 0, { &pic_translate_code_desc, (kobjop_t)pic_translate_code_default }
+};
+
+struct kobjop_desc pic_config_desc = {
+ 0, { &pic_config_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_dispatch_desc = {
+ 0, { &pic_dispatch_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_enable_desc = {
+ 0, { &pic_enable_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_eoi_desc = {
+ 0, { &pic_eoi_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_ipi_desc = {
+ 0, { &pic_ipi_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_mask_desc = {
+ 0, { &pic_mask_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc pic_unmask_desc = {
+ 0, { &pic_unmask_desc, (kobjop_t)kobj_error_method }
+};
+
diff --git a/rtemsbsd/local/xdma_if.c b/rtemsbsd/local/xdma_if.c
new file mode 100644
index 00000000..9b3c86a5
--- /dev/null
+++ b/rtemsbsd/local/xdma_if.c
@@ -0,0 +1,56 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*
+ * This file is produced automatically.
+ * Do not modify anything in here by hand.
+ *
+ * Created from source file
+ * xdma_if.m
+ * with
+ * makeobjops.awk
+ *
+ * See the source file for legal information
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <machine/bus.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/xdma/xdma.h>
+#include <rtems/bsd/local/xdma_if.h>
+
+struct kobjop_desc xdma_channel_request_desc = {
+ 0, { &xdma_channel_request_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_prep_sg_desc = {
+ 0, { &xdma_channel_prep_sg_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_capacity_desc = {
+ 0, { &xdma_channel_capacity_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_submit_sg_desc = {
+ 0, { &xdma_channel_submit_sg_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_ofw_md_data_desc = {
+ 0, { &xdma_ofw_md_data_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_alloc_desc = {
+ 0, { &xdma_channel_alloc_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_free_desc = {
+ 0, { &xdma_channel_free_desc, (kobjop_t)kobj_error_method }
+};
+
+struct kobjop_desc xdma_channel_control_desc = {
+ 0, { &xdma_channel_control_desc, (kobjop_t)kobj_error_method }
+};
diff --git a/rtemsbsd/nfsclient/nfs.c b/rtemsbsd/nfsclient/nfs.c
index baada6ce..e9e83abb 100644
--- a/rtemsbsd/nfsclient/nfs.c
+++ b/rtemsbsd/nfsclient/nfs.c
@@ -3114,7 +3114,7 @@ rtems_filesystem_location_info_t old;
rtems_filesystem_current->location = old;
}
rtems_semaphore_release(rpa->sync);
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
diff --git a/rtemsbsd/pppd/rtemspppd.c b/rtemsbsd/pppd/rtemspppd.c
index cf237a81..c001eff5 100644
--- a/rtemsbsd/pppd/rtemspppd.c
+++ b/rtemsbsd/pppd/rtemspppd.c
@@ -71,7 +71,7 @@ static rtems_task pppTask(rtems_task_argument arg)
/* terminate myself */
rtems_pppd_taskid = 0;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
int rtems_pppd_initialize(void)
diff --git a/rtemsbsd/rtems/program-internal.h b/rtemsbsd/rtems/program-internal.h
index da817130..2104c064 100644
--- a/rtemsbsd/rtems/program-internal.h
+++ b/rtemsbsd/rtems/program-internal.h
@@ -60,6 +60,12 @@ struct program_allocmem_item {
LIST_ENTRY(program_allocmem_item) entries;
};
+struct program_destructor {
+ void (*destructor)(void *);
+ void *arg;
+ LIST_ENTRY(program_destructor) link;
+};
+
struct rtems_bsd_program_control {
void *context;
int exit_code;
@@ -68,6 +74,7 @@ struct rtems_bsd_program_control {
LIST_HEAD(, program_fd_item) open_fd;
LIST_HEAD(, program_file_item) open_file;
LIST_HEAD(, program_allocmem_item) allocated_mem;
+ LIST_HEAD(, program_destructor) destructors;
};
struct rtems_bsd_program_control *rtems_bsd_program_get_control_or_null(void);
diff --git a/rtemsbsd/rtems/rtems-bsd-mountroot.c b/rtemsbsd/rtems/rtems-bsd-mountroot.c
index 132a08ff..d913e12f 100644
--- a/rtemsbsd/rtems/rtems-bsd-mountroot.c
+++ b/rtemsbsd/rtems/rtems-bsd-mountroot.c
@@ -94,8 +94,6 @@ bsd_mountroot(const char *fstype)
if (vfsp != NULL) {
mp = vfs_mount_alloc(NULLVP, vfsp, "/", cred);
- crfree(cred);
-
error = VFS_MOUNT(mp);
if (error != 0)
panic("Cannot mount root file system: %d", error);
@@ -114,6 +112,8 @@ bsd_mountroot(const char *fstype)
set_rootvnode(mp);
}
+
+ crfree(cred);
}
static void
diff --git a/rtemsbsd/rtems/rtems-bsd-racoon.c b/rtemsbsd/rtems/rtems-bsd-racoon.c
index c7ea3594..e6e6205c 100644
--- a/rtemsbsd/rtems/rtems-bsd-racoon.c
+++ b/rtemsbsd/rtems/rtems-bsd-racoon.c
@@ -75,7 +75,7 @@ racoon_task(rtems_task_argument arg)
}
clean_up_args(args);
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
rtems_status_code
diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
index 23ee15db..8ffaa914 100644
--- a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
+++ b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
@@ -103,7 +103,9 @@ cloned_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa)
"ifconfig", aa->argv[arg], "create", NULL
};
rtems_bsd_rc_conf_print_cmd(rc_conf, "cloning_interfaces", 3, ifconfg_args);
- rtems_bsd_command_ifconfig(3, (char**) ifconfg_args);
+ if (rtems_bsd_command_ifconfig(3, (char**) ifconfg_args)) {
+ return -1;
+ }
}
return 0;
@@ -377,7 +379,7 @@ defaultrouter(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, bool
memset(&sin, 0, sizeof(sin));
memset(&rti_info[0], 0, sizeof(rti_info));
sin.sin_family = AF_INET;
- inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr);
+ (void) inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr);
r = rtems_get_route(&sin, rti_info);
if (r == 0 && rti_info[RTAX_GATEWAY] != NULL) {
@@ -710,9 +712,9 @@ run_dhcp(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa)
}
dd->config.priority = priority;
- rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv);
+ r = rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv);
- if (dd->argc_argv->argc > 0) {
+ if (r == 0 && dd->argc_argv->argc > 0) {
dd->config.argc = dd->argc_argv->argc;
dd->config.argv = dd->argc_argv->argv;
}
diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf.c b/rtemsbsd/rtems/rtems-bsd-rc-conf.c
index 36f90a1d..88d98c3e 100644
--- a/rtemsbsd/rtems/rtems-bsd-rc-conf.c
+++ b/rtemsbsd/rtems/rtems-bsd-rc-conf.c
@@ -260,12 +260,14 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
*/
length = strnlen(text, RTEMS_BSD_RC_CONF_MAX_SIZE);
if (length == RTEMS_BSD_RC_CONF_MAX_SIZE) {
+ free(_rc_conf);
errno = E2BIG;
return -1;
}
copy = strdup(text);
if (copy == NULL) {
+ free(_rc_conf);
errno = ENOMEM;
return -1;
}
@@ -286,6 +288,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
lines = malloc(sizeof(char*) * line_count);
if (lines == NULL) {
free(copy);
+ free(_rc_conf);
errno = ENOMEM;
return -1;
}
@@ -335,6 +338,13 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
if (timeout >= 0)
_rc_conf->waiter = rtems_task_self();
+ if (_rc_conf->name == NULL) {
+ free((void*) _rc_conf->lines);
+ free((void*) _rc_conf->data);
+ free(_rc_conf);
+ return -1;
+ }
+
/*
* Create the lock.
*/
@@ -343,6 +353,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
free((void*) _rc_conf->name);
free((void*) _rc_conf->lines);
free((void*) _rc_conf->data);
+ free(_rc_conf);
return -1;
}
@@ -714,6 +725,7 @@ rc_conf_worker(rtems_task_argument task_argument)
rtems_chain_node* node = rtems_chain_first(&services);
int r = 0;
int error;
+ bool rc_conf_verbose;
/*
* Check for a syslog priority before any services are run.
@@ -748,6 +760,8 @@ rc_conf_worker(rtems_task_argument task_argument)
if (r < 0)
rc_conf->error_code = error;
+ rc_conf_verbose = rc_conf->verbose;
+
/*
* If there is a waiter signal else clean up because the waiter has gone.
*/
@@ -760,10 +774,10 @@ rc_conf_worker(rtems_task_argument task_argument)
rc_conf_destroy(rc_conf);
}
- if (rc_conf->verbose)
+ if (rc_conf_verbose)
printf("rc.conf: finished\n");
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
int
@@ -793,6 +807,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
if (sc != RTEMS_SUCCESSFUL) {
fprintf(stderr, "error: %s: get priority: %s\n",
name, rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return -1;
}
@@ -805,6 +820,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
&worker);
if (sc != RTEMS_SUCCESSFUL) {
fprintf (stderr, "error: worker create: %s", rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return -1;
}
@@ -814,6 +830,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
(rtems_task_argument) rc_conf);
if (sc != RTEMS_SUCCESSFUL) {
fprintf (stderr, "error: worker start: %s", rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return - 1;
}
@@ -869,7 +886,7 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose)
if (r < 0)
return r;
- rc_conf = malloc(sb.st_size);
+ rc_conf = malloc(sb.st_size + 1);
if (rc_conf == NULL) {
errno = ENOMEM;
return -1;
@@ -892,6 +909,8 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose)
fclose(file);
+ rc_conf[sb.st_size] = '\0';
+
r = rtems_bsd_run_rc_conf_script(name, rc_conf, timeout, verbose);
free(rc_conf);
diff --git a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
index 4af789cc..3f705975 100644
--- a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
+++ b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
@@ -53,7 +53,7 @@ new_wpa_supplicant_task(rtems_task_argument arg)
free(params->argv);
free(params);
- rtems_task_delete( RTEMS_SELF );
+ rtems_task_exit();
}
int rtems_bsd_command_wpa_supplicant_fork(int argc, char **argv)
diff --git a/rtemsbsd/rtems/rtems-bsd-syscall-api.c b/rtemsbsd/rtems/rtems-bsd-syscall-api.c
index 76fc8ad7..bec03c57 100644
--- a/rtemsbsd/rtems/rtems-bsd-syscall-api.c
+++ b/rtemsbsd/rtems/rtems-bsd-syscall-api.c
@@ -889,6 +889,7 @@ rtems_bsd_sysgen_open_node(
struct vnode *cdir;
struct vnode *rdir;
const char *opath;
+ rtems_filesystem_location_info_t *rootloc;
int opathlen;
int fd;
int error;
@@ -902,6 +903,8 @@ rtems_bsd_sysgen_open_node(
fdp = td->td_proc->p_fd;
+ rootloc = &iop->pathinfo.mt_entry->mt_fs_root->location;
+
/*
* There is no easy or clean means to open a vnode and follow the
* POSIX open semantics. See `kern_openat`. You can open a vnode but
@@ -912,11 +915,17 @@ rtems_bsd_sysgen_open_node(
* parent directory vnode to position ourselves in the parent
* directory. The pathloc vnode points to the '.' or '..' directory.
*/
- opath = path + strlen(path);
- opathlen = 0;
- while (opath != path && !rtems_filesystem_is_delimiter(opath[-1])) {
- opath--;
- opathlen++;
+ if (rtems_bsd_libio_loc_to_vnode(&iop->pathinfo) ==
+ rtems_bsd_libio_loc_to_vnode(rootloc)) {
+ opath = ".";
+ opathlen = 1;
+ } else {
+ opath = path + strlen(path);
+ opathlen = 0;
+ while (opath != path && !rtems_filesystem_is_delimiter(opath[-1])) {
+ opath--;
+ opathlen++;
+ }
}
if (rtems_filesystem_is_current_directory(opath, opathlen) ||
rtems_filesystem_is_parent_directory(opath, opathlen)) {
@@ -929,23 +938,18 @@ rtems_bsd_sysgen_open_node(
opath = ".";
cdir = rtems_bsd_libio_loc_to_vnode(&iop->pathinfo);
} else {
- rtems_filesystem_location_info_t *rootloc =
- &iop->pathinfo.mt_entry->mt_fs_root->location;
+ /*
+ * We need the parent directory so open can find the
+ * entry. If we are creating the file the pathinfo
+ * vnode entry is the directory open uses to create
+ * the file in.
+ */
cdir = rtems_bsd_libio_loc_to_vnode_dir(&iop->pathinfo);
+ if (cdir == NULL || creat) {
+ cdir = rtems_bsd_libio_loc_to_vnode(&iop->pathinfo);
+ }
if (fdp->fd_cdir == NULL) {
- cdir = rtems_bsd_libio_loc_to_vnode(rootloc);
- } else if (rtems_bsd_libio_loc_to_vnode(&iop->pathinfo) ==
- rtems_bsd_libio_loc_to_vnode(rootloc)) {
- /*
- * If this is a directory and this is the root node of
- * the mounted file system we need to move up the
- * hidden pseudo file system node.
- */
- if (isdir) {
- cdir = rootvnode;
- } else {
- cdir = rtems_bsd_libio_loc_to_vnode(rootloc);
- }
+ cdir = rtems_bsd_libio_loc_to_vnode_dir(rootloc);
}
}
@@ -958,11 +962,14 @@ rtems_bsd_sysgen_open_node(
FILEDESC_XUNLOCK(fdp);
if (RTEMS_BSD_SYSCALL_TRACE) {
- printf("bsd: sys: open: path=%s opath=%s vn=%p cwd=%p"
- " flags=%08x mode=%08x isdir=%s\n",
- path, opath,
- creat ? NULL : rtems_bsd_libio_loc_to_vnode(&iop->pathinfo),
- fdp->fd_cdir, oflag, mode, isdir ? "yes" : "no");
+ struct vnode* _vn = rtems_bsd_libio_loc_to_vnode(&iop->pathinfo);
+ struct vnode* _dvn = rtems_bsd_libio_loc_to_vnode_dir(&iop->pathinfo);
+ printf("bsd: sys: open: path=%s opath=%s vn=%p (%c) dvn=%p (%c) cwd=%p"
+ " flags=%08x mode=%o isdir=%s\n",
+ path, opath,
+ _vn, creat ? 'c' : _vn ? (_vn->v_type == VDIR ? 'd' : 'r') : 'n',
+ _dvn, _dvn ? (_dvn->v_type == VDIR ? 'd' : 'r') : 'n',
+ fdp->fd_cdir, oflag, mode, isdir ? "yes" : "no");
}
VREF(fdp->fd_cdir);
@@ -1315,9 +1322,12 @@ rtems_bsd_sysgen_vnstat(
if (vp == NULL)
error = EFAULT;
else {
- VOP_LOCK(vp, LK_SHARED);
- error = vn_stat(vp, buf, td->td_ucred, NOCRED, td);
- VOP_UNLOCK(vp, 0);
+ if (VOP_LOCK(vp, LK_SHARED) == 0) {
+ error = vn_stat(vp, buf, td->td_ucred, NOCRED, td);
+ VOP_UNLOCK(vp, 0);
+ } else {
+ error = ENOLCK;
+ }
}
if (RTEMS_BSD_SYSCALL_TRACE) {
printf("bsd: sys: vnstat: exit %p\n", vp);
diff --git a/rtemsbsd/rtems/rtems-kernel-bus-dma.c b/rtemsbsd/rtems/rtems-kernel-bus-dma.c
index 1d28f62c..9c9194b0 100644
--- a/rtemsbsd/rtems/rtems-kernel-bus-dma.c
+++ b/rtemsbsd/rtems/rtems-kernel-bus-dma.c
@@ -63,6 +63,10 @@
#include <bsp/linker-symbols.h>
#endif
+#ifdef X86_BUS_SPACE_MEM
+#define BUS_SPACE_MEM X86_BUS_SPACE_MEM
+#endif
+
/*
* Convenience function for manipulating driver locks from busdma (during
* busdma_swi, for example). Drivers that don't provide their own locks
@@ -261,7 +265,7 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
unsigned char* mem = *vaddr;
int len = dmat->maxsize;
while (len-- > 0) {
- bsp_bus_space_write_1(mem, 0);
+ bus_space_write_1(BUS_SPACE_MEM, mem, 0, 0);
mem++;
}
}
diff --git a/rtemsbsd/rtems/rtems-kernel-init.c b/rtemsbsd/rtems/rtems-kernel-init.c
index 90a9c809..8ac2f59e 100644
--- a/rtemsbsd/rtems/rtems-kernel-init.c
+++ b/rtemsbsd/rtems/rtems-kernel-init.c
@@ -223,7 +223,9 @@ rtems_bsd_initialize(void)
return RTEMS_UNSATISFIED;
}
- mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO);
+ if (mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
+ return RTEMS_UNSATISFIED;
+ }
sc = rtems_timer_initiate_server(rtems_bsd_get_task_priority(name),
rtems_bsd_get_task_stack_size(name), RTEMS_DEFAULT_ATTRIBUTES);
diff --git a/rtemsbsd/rtems/rtems-kernel-lockmgr.c b/rtemsbsd/rtems/rtems-kernel-lockmgr.c
index 518f6831..de5d9d8a 100644
--- a/rtemsbsd/rtems/rtems-kernel-lockmgr.c
+++ b/rtemsbsd/rtems/rtems-kernel-lockmgr.c
@@ -207,13 +207,11 @@ lockmgr_lock_fast_path(struct lock *lk, u_int flags, struct lock_object *ilk,
{
uintptr_t x, tid;
u_int op;
- bool locked;
if (__predict_false(panicstr != NULL))
return (0);
op = flags & LK_TYPE_MASK;
- locked = false;
switch (op) {
case LK_SHARED:
if (!__predict_false(lk->lock_object.lo_flags & LK_NOSHARE))
diff --git a/rtemsbsd/rtems/rtems-kernel-nexus.c b/rtemsbsd/rtems/rtems-kernel-nexus.c
index 8fc879fe..c5ee33d8 100644
--- a/rtemsbsd/rtems/rtems-kernel-nexus.c
+++ b/rtemsbsd/rtems/rtems-kernel-nexus.c
@@ -57,6 +57,7 @@
#endif
#include <rtems/bsd/bsd.h>
+#include <rtems/bsd/modules.h>
#include <rtems/irq-extension.h>
#include <bsp.h>
@@ -103,6 +104,10 @@ static struct rman mem_rman;
static struct rman irq_rman;
+#ifdef RTEMS_BSD_MODULE_PCI
+static struct rman pci_rman;
+#endif
+
#if defined(RTEMS_BSP_PCI_IO_REGION_BASE)
static struct rman port_rman;
#endif
@@ -137,6 +142,17 @@ nexus_probe(device_t dev)
err = rman_manage_region(&irq_rman, irq_rman.rm_start, irq_rman.rm_end);
BSD_ASSERT(err == 0);
+#ifdef RTEMS_BSD_MODULE_PCI
+ pci_rman.rm_start = 0;
+ pci_rman.rm_end = ~0UL;
+ pci_rman.rm_type = RMAN_ARRAY;
+ pci_rman.rm_descr = "PCI bus";
+ err = rman_init(&pci_rman) != 0;
+ BSD_ASSERT(err == 0);
+ err = rman_manage_region(&pci_rman, pci_rman.rm_start, pci_rman.rm_end);
+ BSD_ASSERT(err == 0);
+#endif
+
#if defined(RTEMS_BSP_PCI_IO_REGION_BASE)
port_rman.rm_start = 0;
port_rman.rm_end = ~0UL;
@@ -191,6 +207,11 @@ nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
case SYS_RES_IRQ:
rm = &irq_rman;
break;
+#ifdef RTEMS_BSD_MODULE_PCI
+ case PCI_RES_BUS:
+ rm = &pci_rman;
+ break;
+#endif
#if defined(RTEMS_BSP_PCI_IO_REGION_BASE)
case SYS_RES_IOPORT:
rm = &port_rman;
diff --git a/rtemsbsd/rtems/rtems-kernel-pager.c b/rtemsbsd/rtems/rtems-kernel-pager.c
index 5a48c2e8..d8febb03 100644
--- a/rtemsbsd/rtems/rtems-kernel-pager.c
+++ b/rtemsbsd/rtems/rtems-kernel-pager.c
@@ -85,7 +85,9 @@ pbuf_ctor(void *mem, int size, void *arg, int flags)
bp->b_ioflags = 0;
bp->b_iodone = NULL;
bp->b_error = 0;
- BUF_LOCK(bp, LK_EXCLUSIVE, NULL);
+ if (BUF_LOCK(bp, LK_EXCLUSIVE, NULL) != 0) {
+ return -1;
+ }
return (0);
}
diff --git a/rtemsbsd/rtems/rtems-kernel-pci_bus.c b/rtemsbsd/rtems/rtems-kernel-pci_bus.c
index d344e7a3..6cbae125 100644
--- a/rtemsbsd/rtems/rtems-kernel-pci_bus.c
+++ b/rtemsbsd/rtems/rtems-kernel-pci_bus.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
+#include <sys/rman.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
@@ -51,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <machine/resource.h>
#include <rtems/bsd/local/pcib_if.h>
+#undef pci_find_device
#define pci_find_device rtems_pci_find_device
#if HAVE_RTEMS_PCI_H
#include <rtems/pci.h>
diff --git a/rtemsbsd/rtems/rtems-kernel-thread.c b/rtemsbsd/rtems/rtems-kernel-thread.c
index 49ec6df7..c52f9408 100644
--- a/rtemsbsd/rtems/rtems-kernel-thread.c
+++ b/rtemsbsd/rtems/rtems-kernel-thread.c
@@ -288,13 +288,6 @@ rtems_bsd_thread_start(struct thread **td_ptr, void (*func)(void *), void *arg,
return eno;
}
-static __dead2 void
-rtems_bsd_thread_delete(void)
-{
- rtems_task_delete(RTEMS_SELF);
- BSD_PANIC("delete self failed");
-}
-
void
kproc_start(const void *udata)
{
@@ -320,7 +313,7 @@ kproc_create(void (*func)(void *), void *arg, struct proc **newpp, int flags, in
void
kproc_exit(int ecode)
{
- rtems_bsd_thread_delete();
+ rtems_task_exit();
}
void
@@ -350,7 +343,7 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, struct thread **new
void
kthread_exit(void)
{
- rtems_bsd_thread_delete();
+ rtems_task_exit();
}
int
diff --git a/rtemsbsd/rtems/rtems-kernel-vfs.c b/rtemsbsd/rtems/rtems-kernel-vfs.c
index 2f4d009b..69c9ba56 100644
--- a/rtemsbsd/rtems/rtems-kernel-vfs.c
+++ b/rtemsbsd/rtems/rtems-kernel-vfs.c
@@ -490,7 +490,7 @@ rtems_bsd_vfs_fchmod(const rtems_filesystem_location_info_t *loc, mode_t mode)
}
return rtems_bsd_error_to_status_and_errno(ENOMEM);
}
- error = setfmode(td, NULL, vp, mode);
+ error = setfmode(td, td->td_ucred, vp, mode);
return rtems_bsd_error_to_status_and_errno(error);
}
@@ -511,7 +511,7 @@ rtems_bsd_vfs_chown(
}
return rtems_bsd_error_to_status_and_errno(ENOMEM);
}
- error = setfown(td, NULL, vp, owner, group);
+ error = setfown(td, td->td_ucred, vp, owner, group);
return rtems_bsd_error_to_status_and_errno(error);
}
@@ -679,7 +679,11 @@ restart:
goto restart;
}
vfs_notify_upper(vp, VFS_NOTIFY_UPPER_UNLINK);
- error = VOP_RMDIR(dvp, vp, &cn);
+ if (vp->v_type == VDIR) {
+ error = VOP_RMDIR(dvp, vp, &cn);
+ } else {
+ error = VOP_REMOVE(dvp, vp, &cn);
+ }
vn_finished_write(mp);
out:
return rtems_bsd_error_to_status_and_errno(error);
diff --git a/rtemsbsd/rtems/rtems-program.c b/rtemsbsd/rtems/rtems-program.c
index 370609d4..4d2ce6ef 100644
--- a/rtemsbsd/rtems/rtems-program.c
+++ b/rtemsbsd/rtems/rtems-program.c
@@ -224,6 +224,18 @@ allocmem_free_all(struct rtems_bsd_program_control *prog_ctrl)
}
}
+static void
+call_destructors(struct rtems_bsd_program_control *prog_ctrl)
+{
+ struct program_destructor *node;
+ struct program_destructor *tmp;
+
+ LIST_FOREACH_SAFE(node, &prog_ctrl->destructors, link, tmp) {
+ (*node->destructor)(node->arg);
+ free(node);
+ }
+}
+
int
rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context)
{
@@ -251,6 +263,7 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context)
LIST_INIT(&prog_ctrl->open_fd);
LIST_INIT(&prog_ctrl->open_file);
LIST_INIT(&prog_ctrl->allocated_mem);
+ LIST_INIT(&prog_ctrl->destructors);
if (setjmp(prog_ctrl->return_context) == 0) {
exit_code = (*prog)(context);
@@ -262,10 +275,48 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context)
fd_close_all(prog_ctrl);
file_close_all(prog_ctrl);
allocmem_free_all(prog_ctrl);
+ call_destructors(prog_ctrl);
free(prog_ctrl);
return (exit_code);
}
+void *
+rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg)
+{
+ struct rtems_bsd_program_control *prog_ctrl;
+ struct program_destructor *node;
+
+ prog_ctrl = rtems_bsd_program_get_control_or_null();
+ if (prog_ctrl == NULL) {
+ return (NULL);
+ }
+
+ node = malloc(sizeof(*node));
+ if (node == NULL) {
+ return (NULL);
+ }
+
+ node->destructor = destructor;
+ node->arg = arg;
+ LIST_INSERT_HEAD(&prog_ctrl->destructors, node, link);
+ return (node);
+}
+
+void
+rtems_bsd_program_remove_destructor(void *cookie, bool call)
+{
+ struct program_destructor *node;
+
+ node = cookie;
+ LIST_REMOVE(node, link);
+
+ if (call) {
+ (*node->destructor)(node->arg);
+ }
+
+ free(node);
+}
+
void
rtems_bsd_program_exit(int exit_code)
{
diff --git a/rtemsbsd/rtems/rtems-routes.c b/rtemsbsd/rtems/rtems-routes.c
index 6663e8d4..0b5250f0 100644
--- a/rtemsbsd/rtems/rtems-routes.c
+++ b/rtemsbsd/rtems/rtems-routes.c
@@ -85,8 +85,10 @@ int rtems_get_route(const struct sockaddr_in* sin, struct sockaddr** rti_info)
}
s = socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC);
- if (s < 0)
+ if (s < 0) {
+ free(buf);
return -1;
+ }
rtm = (struct rt_msghdr *) buf;
rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
diff --git a/rtemsbsd/sys/arm/lpc/if_lpe.c b/rtemsbsd/sys/arm/lpc/if_lpe.c
index 40ac162e..87ca9ff7 100755
--- a/rtemsbsd/sys/arm/lpc/if_lpe.c
+++ b/rtemsbsd/sys/arm/lpc/if_lpe.c
@@ -1,1428 +1,1769 @@
-#include <machine/rtems-bsd-kernel-space.h>
-
-/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+/**
+ * @file
*
- * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
- * All rights reserved.
+ * @ingroup lpc_eth
*
- * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ * @brief Ethernet driver.
+ */
+
+/*
+ * Copyright (C) 2009, 2022 embedded brains GmbH
*
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <bsp.h>
+
+#if defined(LIBBSP_ARM_LPC24XX_BSP_H) || defined(LIBBSP_ARM_LPC32XX_BSP_H)
#include <sys/param.h>
-#include <sys/endian.h>
-#include <sys/systm.h>
-#include <sys/sockio.h>
-#include <sys/mbuf.h>
-#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/module.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/rman.h>
-#include <sys/bus.h>
#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <sys/bus.h>
#include <machine/bus.h>
-#ifndef __rtems__
-#include <machine/intr.h>
-#endif /* __rtems__ */
#include <net/if.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
+#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
-#include <net/bpf.h>
+#include <dev/mii/mii.h>
-#ifndef __rtems__
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-#endif /* __rtems__ */
+#include <rtems/bsd/bsd.h>
-#include <dev/mii/mii.h>
-#include <dev/mii/miivar.h>
+#include <arm/lpc/probe.h>
-#include <arm/lpc/lpcreg.h>
-#include <arm/lpc/lpcvar.h>
-#include <arm/lpc/if_lpereg.h>
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/lpc-ethernet-config.h>
+#include <bsp/utility.h>
-#include <rtems/bsd/local/miibus_if.h>
-#ifdef __rtems__
-#include <machine/rtems-bsd-cache.h>
-#include <rtems/bsd/bsd.h>
-#endif /* __rtems__ */
+#if MCLBYTES > (2 * 1024)
+ #error "MCLBYTES to large"
+#endif
-#ifdef DEBUG
-#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
- printf(fmt,##args); } while (0)
+#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ #define LPC_ETH_CONFIG_TX_BUF_SIZE sizeof(struct mbuf *)
#else
-#define debugf(fmt, args...)
+ #define LPC_ETH_CONFIG_TX_BUF_SIZE 1518U
#endif
-struct lpe_dmamap_arg {
- bus_addr_t lpe_dma_busaddr;
-};
+#define DEFAULT_PHY 0
+#define WATCHDOG_TIMEOUT 5
+
+typedef struct {
+ uint32_t start;
+ uint32_t control;
+} lpc_eth_transfer_descriptor;
+
+typedef struct {
+ uint32_t info;
+ uint32_t hash_crc;
+} lpc_eth_receive_status;
+
+typedef struct {
+ uint32_t mac1;
+ uint32_t mac2;
+ uint32_t ipgt;
+ uint32_t ipgr;
+ uint32_t clrt;
+ uint32_t maxf;
+ uint32_t supp;
+ uint32_t test;
+ uint32_t mcfg;
+ uint32_t mcmd;
+ uint32_t madr;
+ uint32_t mwtd;
+ uint32_t mrdd;
+ uint32_t mind;
+ uint32_t reserved_0 [2];
+ uint32_t sa0;
+ uint32_t sa1;
+ uint32_t sa2;
+ uint32_t reserved_1 [45];
+ uint32_t command;
+ uint32_t status;
+ uint32_t rxdescriptor;
+ uint32_t rxstatus;
+ uint32_t rxdescriptornum;
+ uint32_t rxproduceindex;
+ uint32_t rxconsumeindex;
+ uint32_t txdescriptor;
+ uint32_t txstatus;
+ uint32_t txdescriptornum;
+ uint32_t txproduceindex;
+ uint32_t txconsumeindex;
+ uint32_t reserved_2 [10];
+ uint32_t tsv0;
+ uint32_t tsv1;
+ uint32_t rsv;
+ uint32_t reserved_3 [3];
+ uint32_t flowcontrolcnt;
+ uint32_t flowcontrolsts;
+ uint32_t reserved_4 [34];
+ uint32_t rxfilterctrl;
+ uint32_t rxfilterwolsts;
+ uint32_t rxfilterwolclr;
+ uint32_t reserved_5 [1];
+ uint32_t hashfilterl;
+ uint32_t hashfilterh;
+ uint32_t reserved_6 [882];
+ uint32_t intstatus;
+ uint32_t intenable;
+ uint32_t intclear;
+ uint32_t intset;
+ uint32_t reserved_7 [1];
+ uint32_t powerdown;
+} lpc_eth_controller;
+
+#define LPE_LOCK(e) mtx_lock(&(e)->mtx)
+
+#define LPE_UNLOCK(e) mtx_unlock(&(e)->mtx)
+
+static volatile lpc_eth_controller *const lpc_eth =
+ (volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE;
+
+/* ETH_RX_CTRL */
+
+#define ETH_RX_CTRL_SIZE_MASK 0x000007ffU
+#define ETH_RX_CTRL_INTERRUPT 0x80000000U
+
+/* ETH_RX_STAT */
+
+#define ETH_RX_STAT_RXSIZE_MASK 0x000007ffU
+#define ETH_RX_STAT_BYTES 0x00000100U
+#define ETH_RX_STAT_CONTROL_FRAME 0x00040000U
+#define ETH_RX_STAT_VLAN 0x00080000U
+#define ETH_RX_STAT_FAIL_FILTER 0x00100000U
+#define ETH_RX_STAT_MULTICAST 0x00200000U
+#define ETH_RX_STAT_BROADCAST 0x00400000U
+#define ETH_RX_STAT_CRC_ERROR 0x00800000U
+#define ETH_RX_STAT_SYMBOL_ERROR 0x01000000U
+#define ETH_RX_STAT_LENGTH_ERROR 0x02000000U
+#define ETH_RX_STAT_RANGE_ERROR 0x04000000U
+#define ETH_RX_STAT_ALIGNMENT_ERROR 0x08000000U
+#define ETH_RX_STAT_OVERRUN 0x10000000U
+#define ETH_RX_STAT_NO_DESCRIPTOR 0x20000000U
+#define ETH_RX_STAT_LAST_FLAG 0x40000000U
+#define ETH_RX_STAT_ERROR 0x80000000U
+
+/* ETH_TX_CTRL */
+
+#define ETH_TX_CTRL_SIZE_MASK 0x7ffU
+#define ETH_TX_CTRL_SIZE_SHIFT 0
+#define ETH_TX_CTRL_OVERRIDE 0x04000000U
+#define ETH_TX_CTRL_HUGE 0x08000000U
+#define ETH_TX_CTRL_PAD 0x10000000U
+#define ETH_TX_CTRL_CRC 0x20000000U
+#define ETH_TX_CTRL_LAST 0x40000000U
+#define ETH_TX_CTRL_INTERRUPT 0x80000000U
+
+/* ETH_TX_STAT */
+
+#define ETH_TX_STAT_COLLISION_COUNT_MASK 0x01e00000U
+#define ETH_TX_STAT_DEFER 0x02000000U
+#define ETH_TX_STAT_EXCESSIVE_DEFER 0x04000000U
+#define ETH_TX_STAT_EXCESSIVE_COLLISION 0x08000000U
+#define ETH_TX_STAT_LATE_COLLISION 0x10000000U
+#define ETH_TX_STAT_UNDERRUN 0x20000000U
+#define ETH_TX_STAT_NO_DESCRIPTOR 0x40000000U
+#define ETH_TX_STAT_ERROR 0x80000000U
+
+/* ETH_INT */
+
+#define ETH_INT_RX_OVERRUN 0x00000001U
+#define ETH_INT_RX_ERROR 0x00000002U
+#define ETH_INT_RX_FINISHED 0x00000004U
+#define ETH_INT_RX_DONE 0x00000008U
+#define ETH_INT_TX_UNDERRUN 0x00000010U
+#define ETH_INT_TX_ERROR 0x00000020U
+#define ETH_INT_TX_FINISHED 0x00000040U
+#define ETH_INT_TX_DONE 0x00000080U
+#define ETH_INT_SOFT 0x00001000U
+#define ETH_INT_WAKEUP 0x00002000U
+
+/* ETH_RX_FIL_CTRL */
+
+#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST 0x00000001U
+#define ETH_RX_FIL_CTRL_ACCEPT_BROADCAST 0x00000002U
+#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST 0x00000004U
+#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST_HASH 0x00000008U
+#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH 0x00000010U
+#define ETH_RX_FIL_CTRL_ACCEPT_PERFECT 0x00000020U
+#define ETH_RX_FIL_CTRL_MAGIC_PACKET_WOL 0x00001000U
+#define ETH_RX_FIL_CTRL_RX_FILTER_WOL 0x00002000U
+
+/* ETH_CMD */
+
+#define ETH_CMD_RX_ENABLE 0x00000001U
+#define ETH_CMD_TX_ENABLE 0x00000002U
+#define ETH_CMD_REG_RESET 0x00000008U
+#define ETH_CMD_TX_RESET 0x00000010U
+#define ETH_CMD_RX_RESET 0x00000020U
+#define ETH_CMD_PASS_RUNT_FRAME 0x00000040U
+#define ETH_CMD_PASS_RX_FILTER 0X00000080U
+#define ETH_CMD_TX_FLOW_CONTROL 0x00000100U
+#define ETH_CMD_RMII 0x00000200U
+#define ETH_CMD_FULL_DUPLEX 0x00000400U
-struct lpe_rxdesc {
- struct mbuf * lpe_rxdesc_mbuf;
-#ifndef __rtems__
- bus_dmamap_t lpe_rxdesc_dmamap;
-#endif /* __rtems__ */
-};
+/* ETH_STAT */
-struct lpe_txdesc {
- int lpe_txdesc_first;
- struct mbuf * lpe_txdesc_mbuf;
-#ifndef __rtems__
- bus_dmamap_t lpe_txdesc_dmamap;
-#endif /* __rtems__ */
-};
+#define ETH_STAT_RX_ACTIVE 0x00000001U
+#define ETH_STAT_TX_ACTIVE 0x00000002U
-struct lpe_chain_data {
- bus_dma_tag_t lpe_parent_tag;
- bus_dma_tag_t lpe_tx_ring_tag;
- bus_dmamap_t lpe_tx_ring_map;
- bus_dma_tag_t lpe_tx_status_tag;
- bus_dmamap_t lpe_tx_status_map;
- bus_dma_tag_t lpe_tx_buf_tag;
- bus_dma_tag_t lpe_rx_ring_tag;
- bus_dmamap_t lpe_rx_ring_map;
- bus_dma_tag_t lpe_rx_status_tag;
- bus_dmamap_t lpe_rx_status_map;
- bus_dma_tag_t lpe_rx_buf_tag;
- struct lpe_rxdesc lpe_rx_desc[LPE_RXDESC_NUM];
- struct lpe_txdesc lpe_tx_desc[LPE_TXDESC_NUM];
- int lpe_tx_prod;
- int lpe_tx_last;
- int lpe_tx_used;
-};
+/* ETH_MAC2 */
-struct lpe_ring_data {
- struct lpe_hwdesc * lpe_rx_ring;
- struct lpe_hwstatus * lpe_rx_status;
- bus_addr_t lpe_rx_ring_phys;
- bus_addr_t lpe_rx_status_phys;
- struct lpe_hwdesc * lpe_tx_ring;
- struct lpe_hwstatus * lpe_tx_status;
- bus_addr_t lpe_tx_ring_phys;
- bus_addr_t lpe_tx_status_phys;
-};
+#define ETH_MAC2_FULL_DUPLEX BSP_BIT32(8)
-struct lpe_softc {
- struct ifnet * lpe_ifp;
- struct mtx lpe_mtx;
-#ifndef __rtems__
- phandle_t lpe_ofw;
-#endif /* __rtems__ */
- device_t lpe_dev;
- device_t lpe_miibus;
- uint8_t lpe_enaddr[6];
- struct resource * lpe_mem_res;
- struct resource * lpe_irq_res;
- void * lpe_intrhand;
- bus_space_tag_t lpe_bst;
- bus_space_handle_t lpe_bsh;
-#define LPE_FLAG_LINK (1 << 0)
- uint32_t lpe_flags;
- int lpe_watchdog_timer;
- struct callout lpe_tick;
- struct lpe_chain_data lpe_cdata;
- struct lpe_ring_data lpe_rdata;
-};
+/* ETH_SUPP */
-static int lpe_probe(device_t);
-static int lpe_attach(device_t);
-static int lpe_detach(device_t);
-static int lpe_miibus_readreg(device_t, int, int);
-static int lpe_miibus_writereg(device_t, int, int, int);
-static void lpe_miibus_statchg(device_t);
-
-static void lpe_reset(struct lpe_softc *);
-static void lpe_init(void *);
-static void lpe_init_locked(struct lpe_softc *);
-static void lpe_start(struct ifnet *);
-static void lpe_start_locked(struct ifnet *);
-static void lpe_stop(struct lpe_softc *);
-static void lpe_stop_locked(struct lpe_softc *);
-static int lpe_ioctl(struct ifnet *, u_long, caddr_t);
-static void lpe_set_rxmode(struct lpe_softc *);
-static void lpe_set_rxfilter(struct lpe_softc *);
-static void lpe_intr(void *);
-static void lpe_rxintr(struct lpe_softc *);
-static void lpe_txintr(struct lpe_softc *);
-static void lpe_tick(void *);
-static void lpe_watchdog(struct lpe_softc *);
-static int lpe_encap(struct lpe_softc *, struct mbuf **);
-static int lpe_dma_alloc(struct lpe_softc *);
-static int lpe_dma_alloc_rx(struct lpe_softc *);
-static int lpe_dma_alloc_tx(struct lpe_softc *);
-static int lpe_init_rx(struct lpe_softc *);
-static int lpe_init_rxbuf(struct lpe_softc *, int);
-static void lpe_discard_rxbuf(struct lpe_softc *, int);
-static void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int);
-static int lpe_ifmedia_upd(struct ifnet *);
-static void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
-
-#define lpe_lock(_sc) mtx_lock(&(_sc)->lpe_mtx)
-#define lpe_unlock(_sc) mtx_unlock(&(_sc)->lpe_mtx)
-#define lpe_lock_assert(_sc) mtx_assert(&(_sc)->lpe_mtx, MA_OWNED)
-
-#define lpe_read_4(_sc, _reg) \
- bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg))
-#define lpe_write_4(_sc, _reg, _val) \
- bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val))
-
-#define LPE_HWDESC_RXERRS (LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \
- LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \
- LPE_HWDESC_RXNODESCR)
-
-#define LPE_HWDESC_TXERRS (LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \
- LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR)
-
-static int
-lpe_probe(device_t dev)
-{
-
-#ifndef __rtems__
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
+#define ETH_SUPP_SPEED BSP_BIT32(8)
- if (!ofw_bus_is_compatible(dev, "lpc,ethernet"))
- return (ENXIO);
-#endif /* __rtems__ */
+/* ETH_MCFG */
- device_set_desc(dev, "LPC32x0 10/100 Ethernet");
- return (BUS_PROBE_DEFAULT);
-}
+#define ETH_MCFG_CLOCK_SELECT(val) BSP_FLD32(val, 2, 4)
-static int
-lpe_attach(device_t dev)
-{
- struct lpe_softc *sc = device_get_softc(dev);
- struct ifnet *ifp;
-#ifndef __rtems__
- int rid, i;
- uint32_t val;
-#else /* __rtems__ */
- int rid;
-#endif /* __rtems__ */
-
- sc->lpe_dev = dev;
-#ifndef __rtems__
- sc->lpe_ofw = ofw_bus_get_node(dev);
-
- i = OF_getprop(sc->lpe_ofw, "local-mac-address", (void *)&sc->lpe_enaddr, 6);
- if (i != 6) {
- sc->lpe_enaddr[0] = 0x00;
- sc->lpe_enaddr[1] = 0x11;
- sc->lpe_enaddr[2] = 0x22;
- sc->lpe_enaddr[3] = 0x33;
- sc->lpe_enaddr[4] = 0x44;
- sc->lpe_enaddr[5] = 0x55;
- }
-#else /* __rtems__ */
- rtems_bsd_get_mac_address(device_get_name(sc->lpe_dev), device_get_unit(sc->lpe_dev), sc->lpe_enaddr);
-#endif /* __rtems__ */
-
- mtx_init(&sc->lpe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
- MTX_DEF);
-
- callout_init_mtx(&sc->lpe_tick, &sc->lpe_mtx, 0);
-
- rid = 0;
- sc->lpe_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
- RF_ACTIVE);
- if (!sc->lpe_mem_res) {
- device_printf(dev, "cannot allocate memory window\n");
- goto fail;
- }
-
- sc->lpe_bst = rman_get_bustag(sc->lpe_mem_res);
- sc->lpe_bsh = rman_get_bushandle(sc->lpe_mem_res);
-
- rid = 0;
- sc->lpe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
- RF_ACTIVE);
- if (!sc->lpe_irq_res) {
- device_printf(dev, "cannot allocate interrupt\n");
- goto fail;
- }
-
- sc->lpe_ifp = if_alloc(IFT_ETHER);
- if (!sc->lpe_ifp) {
- device_printf(dev, "cannot allocated ifnet\n");
- goto fail;
- }
-
- ifp = sc->lpe_ifp;
-
- if_initname(ifp, device_get_name(dev), device_get_unit(dev));
- ifp->if_softc = sc;
- ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
- ifp->if_start = lpe_start;
- ifp->if_ioctl = lpe_ioctl;
- ifp->if_init = lpe_init;
- IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
- ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
- IFQ_SET_READY(&ifp->if_snd);
-
- ether_ifattach(ifp, sc->lpe_enaddr);
-
- if (bus_setup_intr(dev, sc->lpe_irq_res, INTR_TYPE_NET, NULL,
- lpe_intr, sc, &sc->lpe_intrhand)) {
- device_printf(dev, "cannot establish interrupt handler\n");
- ether_ifdetach(ifp);
- goto fail;
- }
-
- /* Enable Ethernet clock */
-#ifndef __rtems__
- lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL,
- LPC_CLKPWR_MACCLK_CTRL_REG |
- LPC_CLKPWR_MACCLK_CTRL_SLAVE |
- LPC_CLKPWR_MACCLK_CTRL_MASTER |
- LPC_CLKPWR_MACCLK_CTRL_HDWINF(3));
-#else /* __rtems__ */
-#ifdef LPC32XX_ETHERNET_RMII
- lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL,
- LPC_CLKPWR_MACCLK_CTRL_REG |
- LPC_CLKPWR_MACCLK_CTRL_SLAVE |
- LPC_CLKPWR_MACCLK_CTRL_MASTER |
- LPC_CLKPWR_MACCLK_CTRL_HDWINF(3));
-#else
- lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL,
- LPC_CLKPWR_MACCLK_CTRL_REG |
- LPC_CLKPWR_MACCLK_CTRL_SLAVE |
- LPC_CLKPWR_MACCLK_CTRL_MASTER |
- LPC_CLKPWR_MACCLK_CTRL_HDWINF(1));
-#endif
-#endif /* __rtems__ */
-
- /* Reset chip */
- lpe_reset(sc);
-
- /* Initialize MII */
-#ifndef __rtems__
- val = lpe_read_4(sc, LPE_COMMAND);
- lpe_write_4(sc, LPE_COMMAND, val | LPE_COMMAND_RMII);
-
- if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd,
- lpe_ifmedia_sts, BMSR_DEFCAPMASK, 0x01,
- MII_OFFSET_ANY, 0)) {
- device_printf(dev, "cannot find PHY\n");
- goto fail;
- }
-#else /* __rtems__ */
- if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd,
- lpe_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY,
- MII_OFFSET_ANY, 0)) {
- device_printf(dev, "cannot find PHY\n");
- goto fail;
- }
-#endif /* __rtems__ */
-
- lpe_dma_alloc(sc);
-
- return (0);
-
-fail:
- if (sc->lpe_ifp)
- if_free(sc->lpe_ifp);
- if (sc->lpe_intrhand)
- bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
- if (sc->lpe_irq_res)
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
- if (sc->lpe_mem_res)
- bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
- return (ENXIO);
-}
+#define ETH_MCFG_RESETMIIMGMT BSP_BIT32(15)
-static int
-lpe_detach(device_t dev)
-{
- struct lpe_softc *sc = device_get_softc(dev);
+/* ETH_MCMD */
- lpe_stop(sc);
+#define ETH_MCMD_READ BSP_BIT32(0)
+#define ETH_MCMD_SCAN BSP_BIT32(1)
- if_free(sc->lpe_ifp);
- bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
- bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
+/* ETH_MADR */
- return (0);
-}
+#define ETH_MADR_REG(val) BSP_FLD32(val, 0, 4)
+#define ETH_MADR_PHY(val) BSP_FLD32(val, 8, 12)
-static int
-lpe_miibus_readreg(device_t dev, int phy, int reg)
-{
- struct lpe_softc *sc = device_get_softc(dev);
- uint32_t val;
- int result;
+/* ETH_MIND */
- lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ);
- lpe_write_4(sc, LPE_MADR,
- (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
- (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
+#define ETH_MIND_BUSY BSP_BIT32(0)
+#define ETH_MIND_SCANNING BSP_BIT32(1)
+#define ETH_MIND_NOT_VALID BSP_BIT32(2)
+#define ETH_MIND_MII_LINK_FAIL BSP_BIT32(3)
- val = lpe_read_4(sc, LPE_MIND);
+/* Events */
- /* Wait until request is completed */
- while (val & LPE_MIND_BUSY) {
- val = lpe_read_4(sc, LPE_MIND);
- DELAY(10);
- }
+#define LPC_ETH_EVENT_INIT_RX RTEMS_EVENT_0
- if (val & LPE_MIND_INVALID)
- return (0);
+#define LPC_ETH_EVENT_INIT_TX RTEMS_EVENT_1
- lpe_write_4(sc, LPE_MCMD, 0);
- result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
- debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result);
+#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3
- return (result);
-}
+#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4
-static int
-lpe_miibus_writereg(device_t dev, int phy, int reg, int data)
-{
- struct lpe_softc *sc = device_get_softc(dev);
- uint32_t val;
+/* Status */
- debugf("phy=%d reg=%d data=0x%04x\n", phy, reg, data);
+#define LPC_ETH_INTERRUPT_RECEIVE \
+ (ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE)
- lpe_write_4(sc, LPE_MCMD, LPE_MCMD_WRITE);
- lpe_write_4(sc, LPE_MADR,
- (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
- (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
+#define LPC_ETH_RX_STAT_ERRORS \
+ (ETH_RX_STAT_CRC_ERROR \
+ | ETH_RX_STAT_SYMBOL_ERROR \
+ | ETH_RX_STAT_LENGTH_ERROR \
+ | ETH_RX_STAT_ALIGNMENT_ERROR \
+ | ETH_RX_STAT_OVERRUN \
+ | ETH_RX_STAT_NO_DESCRIPTOR)
- lpe_write_4(sc, LPE_MWTD, (data & LPE_MWTD_DATAMASK));
+#define LPC_ETH_LAST_FRAGMENT_FLAGS \
+ (ETH_TX_CTRL_OVERRIDE \
+ | ETH_TX_CTRL_PAD \
+ | ETH_TX_CTRL_CRC \
+ | ETH_TX_CTRL_INTERRUPT \
+ | ETH_TX_CTRL_LAST)
- val = lpe_read_4(sc, LPE_MIND);
+/* Debug */
- /* Wait until request is completed */
- while (val & LPE_MIND_BUSY) {
- val = lpe_read_4(sc, LPE_MIND);
- DELAY(10);
- }
+#ifdef DEBUG
+ #define LPC_ETH_PRINTF(...) printf(__VA_ARGS__)
+ #define LPC_ETH_PRINTK(...) printk(__VA_ARGS__)
+#else
+ #define LPC_ETH_PRINTF(...)
+ #define LPC_ETH_PRINTK(...)
+#endif
- return (0);
+typedef enum {
+ LPC_ETH_STATE_NOT_INITIALIZED = 0,
+ LPC_ETH_STATE_DOWN,
+ LPC_ETH_STATE_UP
+} lpc_eth_state;
+
+typedef struct {
+ device_t dev;
+ struct ifnet *ifp;
+ struct mtx mtx;
+ lpc_eth_state state;
+ uint32_t anlpar;
+ struct callout watchdog_callout;
+ rtems_id receive_task;
+ unsigned rx_unit_count;
+ unsigned tx_unit_count;
+ volatile lpc_eth_transfer_descriptor *rx_desc_table;
+ volatile lpc_eth_receive_status *rx_status_table;
+ struct mbuf **rx_mbuf_table;
+ volatile lpc_eth_transfer_descriptor *tx_desc_table;
+ volatile uint32_t *tx_status_table;
+ void *tx_buf_table;
+ uint32_t tx_produce_index;
+ uint32_t tx_consume_index;
+ unsigned received_frames;
+ unsigned receive_interrupts;
+ unsigned transmitted_frames;
+ unsigned receive_drop_errors;
+ unsigned receive_overrun_errors;
+ unsigned receive_fragment_errors;
+ unsigned receive_crc_errors;
+ unsigned receive_symbol_errors;
+ unsigned receive_length_errors;
+ unsigned receive_alignment_errors;
+ unsigned receive_no_descriptor_errors;
+ unsigned receive_fatal_errors;
+ unsigned transmit_underrun_errors;
+ unsigned transmit_late_collision_errors;
+ unsigned transmit_excessive_collision_errors;
+ unsigned transmit_excessive_defer_errors;
+ unsigned transmit_no_descriptor_errors;
+ unsigned transmit_overflow_errors;
+ unsigned transmit_fatal_errors;
+ uint32_t phy_id;
+ int phy;
+ rtems_vector_number interrupt_number;
+ rtems_id control_task;
+ int if_flags;
+ struct ifmedia ifmedia;
+} lpc_eth_driver_entry;
+
+static void lpc_eth_interface_watchdog(void *arg);
+
+static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e);
+
+static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e)
+{
+ rtems_status_code sc = rtems_event_transient_send(e->control_task);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
}
-static void
-lpe_miibus_statchg(device_t dev)
+static void lpc_eth_control_request(
+ lpc_eth_driver_entry *e,
+ rtems_id task,
+ rtems_event_set event
+)
{
- struct lpe_softc *sc = device_get_softc(dev);
- struct mii_data *mii = device_get_softc(sc->lpe_miibus);
-
-#ifndef __rtems__
- lpe_lock(sc);
-#endif /* __rtems__ */
-
- if ((mii->mii_media_status & IFM_ACTIVE) &&
- (mii->mii_media_status & IFM_AVALID))
- sc->lpe_flags |= LPE_FLAG_LINK;
- else
- sc->lpe_flags &= ~LPE_FLAG_LINK;
-
-#ifndef __rtems__
- lpe_unlock(sc);
-#endif /* __rtems__ */
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ e->control_task = rtems_task_self();
+
+ sc = rtems_event_send(task, event);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ e->control_task = 0;
}
-static void
-lpe_reset(struct lpe_softc *sc)
+static inline uint32_t lpc_eth_increment(
+ uint32_t value,
+ uint32_t cycle
+)
{
- uint32_t mac1;
-
-#ifndef __rtems__
- /* Enter soft reset mode */
- mac1 = lpe_read_4(sc, LPE_MAC1);
- lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
- LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX);
-
- /* Reset registers, Tx path and Rx path */
- lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET |
- LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
-
- /* Set station address */
- lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
- lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
- lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
-
- /* Leave soft reset mode */
- mac1 = lpe_read_4(sc, LPE_MAC1);
- lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
- LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX));
-#else /* __rtems__ */
- /* Reset registers, Tx path and Rx path */
- lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET | LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
-
- /* Enter soft reset mode */
- mac1 = lpe_read_4(sc, LPE_MAC1);
- lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
- LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX);
-
- /* Leave soft reset mode */
- mac1 = lpe_read_4(sc, LPE_MAC1);
- lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
- LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX));
-
- /* Reinitialize registers */
- lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(0x7));
- lpe_write_4(sc, LPE_MAC2, LPE_MAC2_PADCRCENABLE | LPE_MAC2_CRCENABLE | LPE_MAC2_FULLDUPLEX);
- lpe_write_4(sc, LPE_IPGT, 0x15);
- lpe_write_4(sc, LPE_IPGR, 0x12);
- lpe_write_4(sc, LPE_CLRT, 0x370f);
- lpe_write_4(sc, LPE_MAXF, 0x0600);
- lpe_write_4(sc, LPE_SUPP, LPE_SUPP_SPEED);
- lpe_write_4(sc, LPE_TEST, 0x0);
-#ifdef LPC32XX_ETHERNET_RMII
- lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX | LPE_COMMAND_RMII);
-#else
- lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX);
-#endif
- lpe_write_4(sc, LPE_INTENABLE, 0x0);
- lpe_write_4(sc, LPE_INTCLEAR, 0x30ff);
- lpe_write_4(sc, LPE_POWERDOWN, 0x0);
-
- /* Set station address */
- lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
- lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
- lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
-#endif /* __rtems__ */
+ if (value < cycle) {
+ return ++value;
+ } else {
+ return 0;
+ }
}
-static void
-lpe_init(void *arg)
+static void lpc_eth_enable_promiscous_mode(bool enable)
{
- struct lpe_softc *sc = (struct lpe_softc *)arg;
-
- lpe_lock(sc);
- lpe_init_locked(sc);
- lpe_unlock(sc);
+ if (enable) {
+ lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_UNICAST
+ | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST
+ | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST;
+ } else {
+ lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_PERFECT
+ | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH
+ | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST;
+ }
}
-static void
-lpe_init_locked(struct lpe_softc *sc)
+static void lpc_eth_interrupt_handler(void *arg)
{
- struct ifnet *ifp = sc->lpe_ifp;
- uint32_t cmd, mac1;
-
- lpe_lock_assert(sc);
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- return;
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
+ rtems_event_set re = 0;
+ rtems_event_set te = 0;
+ uint32_t ie = 0;
+
+ /* Get interrupt status */
+ uint32_t im = lpc_eth->intenable;
+ uint32_t is = lpc_eth->intstatus & im;
+
+ /* Check receive interrupts */
+ if ((is & (ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN)) != 0) {
+ if ((is & ETH_INT_RX_OVERRUN) != 0) {
+ re = LPC_ETH_EVENT_INIT_RX;
+ ++e->receive_fatal_errors;
+ }
+
+ if ((is & ETH_INT_TX_UNDERRUN) != 0) {
+ re = LPC_ETH_EVENT_INIT_TX;
+ ++e->transmit_fatal_errors;
+ }
+ } else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) {
+ re = LPC_ETH_EVENT_INTERRUPT;
+ ie |= LPC_ETH_INTERRUPT_RECEIVE;
+ ++e->receive_interrupts;
+ }
+
+ /* Send events to receive task */
+ if (re != 0) {
+ (void) rtems_event_send(e->receive_task, re);
+ }
+
+ LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te);
+
+ /* Update interrupt mask */
+ lpc_eth->intenable = im & ~ie;
+
+ /* Clear interrupts */
+ lpc_eth->intclear = is;
+}
- /* Enable Tx and Rx */
- cmd = lpe_read_4(sc, LPE_COMMAND);
- lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE |
- LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME);
+static void lpc_eth_enable_receive_interrupts(void)
+{
+ rtems_interrupt_level level;
- /* Enable receive */
- mac1 = lpe_read_4(sc, LPE_MAC1);
-#ifdef __rtems__
- (void)mac1;
-#endif /* __rtems__ */
- lpe_write_4(sc, LPE_MAC1, /*mac1 |*/ LPE_MAC1_RXENABLE | LPE_MAC1_PASSALL);
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable |= LPC_ETH_INTERRUPT_RECEIVE;
+ rtems_interrupt_enable(level);
+}
- lpe_write_4(sc, LPE_MAC2, LPE_MAC2_CRCENABLE | LPE_MAC2_PADCRCENABLE |
- LPE_MAC2_FULLDUPLEX);
+static void lpc_eth_disable_receive_interrupts(void)
+{
+ rtems_interrupt_level level;
- lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7));
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_RECEIVE;
+ rtems_interrupt_enable(level);
+}
- /* Set up Rx filter */
- lpe_set_rxmode(sc);
+static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
+{
+ volatile uint32_t *const status = e->tx_status_table;
+ uint32_t const index_max = e->tx_unit_count - 1;
+ volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ struct mbuf **const mbufs = e->tx_buf_table;
+ #else
+ char *const buf = e->tx_buf_table;
+ #endif
+ uint32_t produce_index;
+
+ /* Disable transmitter */
+ lpc_eth->command &= ~ETH_CMD_TX_ENABLE;
+
+ /* Wait for inactive status */
+ while ((lpc_eth->status & ETH_STAT_TX_ACTIVE) != 0) {
+ /* Wait */
+ }
+
+ /* Reset */
+ lpc_eth->command |= ETH_CMD_TX_RESET;
+
+ /* Transmit descriptors */
+ lpc_eth->txdescriptornum = index_max;
+ lpc_eth->txdescriptor = (uint32_t) desc;
+ lpc_eth->txstatus = (uint32_t) status;
+
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Discard outstanding fragments (= data loss) */
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ m_freem(mbufs [produce_index]);
+ mbufs [produce_index] = NULL;
+ }
+ #else
+ /* Initialize descriptor table */
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ desc [produce_index].start =
+ (uint32_t) (buf + produce_index * LPC_ETH_CONFIG_TX_BUF_SIZE);
+ }
+ #endif
+
+ /* Initialize indices */
+ e->tx_produce_index = lpc_eth->txproduceindex;
+ e->tx_consume_index = lpc_eth->txconsumeindex;
+
+ /* Enable transmitter */
+ lpc_eth->command |= ETH_CMD_TX_ENABLE;
+}
- /* Enable interrupts */
- lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR |
- LPE_INT_RXFINISH | LPE_INT_RXDONE | LPE_INT_TXUNDERRUN |
- LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE);
+#define LPC_ETH_RX_DATA_OFFSET 2
- sc->lpe_cdata.lpe_tx_prod = 0;
- sc->lpe_cdata.lpe_tx_last = 0;
- sc->lpe_cdata.lpe_tx_used = 0;
+static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait)
+{
+ struct mbuf *m = NULL;
+ int mw = wait ? M_WAITOK : M_NOWAIT;
+
+ MGETHDR(m, mw, MT_DATA);
+ if (m != NULL) {
+ MCLGET(m, mw);
+ if ((m->m_flags & M_EXT) != 0) {
+ /* Set receive interface */
+ m->m_pkthdr.rcvif = ifp;
+
+ /* Adjust by two bytes for proper IP header alignment */
+ m->m_data = mtod(m, char *) + LPC_ETH_RX_DATA_OFFSET;
+
+ return m;
+ } else {
+ m_free(m);
+ }
+ }
+
+ return NULL;
+}
- lpe_init_rx(sc);
+static bool lpc_eth_add_new_mbuf(
+ struct ifnet *ifp,
+ volatile lpc_eth_transfer_descriptor *desc,
+ struct mbuf **mbufs,
+ uint32_t i,
+ bool wait
+)
+{
+ /* New mbuf */
+ struct mbuf *m = lpc_eth_new_mbuf(ifp, wait);
+
+ /* Check mbuf */
+ if (m != NULL) {
+ /* Cache invalidate */
+ rtems_cache_invalidate_multiple_data_lines(
+ mtod(m, void *),
+ MCLBYTES - LPC_ETH_RX_DATA_OFFSET
+ );
+
+ /* Add mbuf to queue */
+ desc [i].start = mtod(m, uint32_t);
+ desc [i].control = (MCLBYTES - LPC_ETH_RX_DATA_OFFSET - 1)
+ | ETH_RX_CTRL_INTERRUPT;
+
+ /* Cache flush of descriptor */
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [i],
+ sizeof(desc [0])
+ );
+
+ /* Add mbuf to table */
+ mbufs [i] = m;
+
+ return true;
+ } else {
+ return false;
+ }
+}
- /* Initialize Rx packet and status descriptor heads */
- lpe_write_4(sc, LPE_RXDESC, sc->lpe_rdata.lpe_rx_ring_phys);
- lpe_write_4(sc, LPE_RXSTATUS, sc->lpe_rdata.lpe_rx_status_phys);
- lpe_write_4(sc, LPE_RXDESC_NUMBER, LPE_RXDESC_NUM - 1);
- lpe_write_4(sc, LPE_RXDESC_CONS, 0);
+static void lpc_eth_receive_task(rtems_task_argument arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_event_set events = 0;
+ lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg;
+ struct ifnet *const ifp = e->ifp;
+ volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table;
+ volatile lpc_eth_receive_status *const status = e->rx_status_table;
+ struct mbuf **const mbufs = e->rx_mbuf_table;
+ uint32_t const index_max = e->rx_unit_count - 1;
+ uint32_t produce_index = 0;
+ uint32_t consume_index = 0;
+
+ LPC_ETH_PRINTF("%s\n", __func__);
+
+ /* Main event loop */
+ while (true) {
+ /* Wait for events */
+ sc = rtems_event_receive(
+ LPC_ETH_EVENT_INIT_RX
+ | LPC_ETH_EVENT_INIT_TX
+ | LPC_ETH_EVENT_STOP
+ | LPC_ETH_EVENT_INTERRUPT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events);
+
+ /* Stop receiver? */
+ if ((events & LPC_ETH_EVENT_STOP) != 0) {
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
+ /* Initialize receiver or transmitter? */
+ if ((events & (LPC_ETH_EVENT_INIT_RX | LPC_ETH_EVENT_INIT_TX)) != 0) {
+ if ((events & LPC_ETH_EVENT_INIT_RX) != 0) {
+ /* Disable receive interrupts */
+ lpc_eth_disable_receive_interrupts();
+
+ /* Disable receiver */
+ lpc_eth->command &= ~ETH_CMD_RX_ENABLE;
+
+ /* Wait for inactive status */
+ while ((lpc_eth->status & ETH_STAT_RX_ACTIVE) != 0) {
+ /* Wait */
+ }
+
+ /* Reset */
+ lpc_eth->command |= ETH_CMD_RX_RESET;
+
+ /* Clear receive interrupts */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE;
+
+ /* Move existing mbufs to the front */
+ consume_index = 0;
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ if (mbufs [produce_index] != NULL) {
+ mbufs [consume_index] = mbufs [produce_index];
+ ++consume_index;
+ }
+ }
+
+ /* Fill receive queue */
+ for (
+ produce_index = consume_index;
+ produce_index <= index_max;
+ ++produce_index
+ ) {
+ lpc_eth_add_new_mbuf(ifp, desc, mbufs, produce_index, true);
+ }
+
+ /* Receive descriptor table */
+ lpc_eth->rxdescriptornum = index_max;
+ lpc_eth->rxdescriptor = (uint32_t) desc;
+ lpc_eth->rxstatus = (uint32_t) status;
+
+ /* Initialize indices */
+ produce_index = lpc_eth->rxproduceindex;
+ consume_index = lpc_eth->rxconsumeindex;
+
+ /* Enable receiver */
+ lpc_eth->command |= ETH_CMD_RX_ENABLE;
+
+ /* Enable receive interrupts */
+ lpc_eth_enable_receive_interrupts();
+
+ lpc_eth_control_request_complete(e);
+ }
+
+ if ((events & LPC_ETH_EVENT_INIT_TX) != 0) {
+ LPE_LOCK(e);
+ lpc_eth_initialize_transmit(e);
+ LPE_UNLOCK(e);
+ }
+
+ /* Wait for events */
+ continue;
+ }
+
+ while (true) {
+ /* Clear receive interrupt status */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE;
+
+ /* Get current produce index */
+ produce_index = lpc_eth->rxproduceindex;
+
+ if (consume_index != produce_index) {
+ uint32_t stat = 0;
+
+ /* Fragment status */
+ rtems_cache_invalidate_multiple_data_lines(
+ (void *) &status [consume_index],
+ sizeof(status [0])
+ );
+ stat = status [consume_index].info;
+
+ if (
+ (stat & ETH_RX_STAT_LAST_FLAG) != 0
+ && (stat & LPC_ETH_RX_STAT_ERRORS) == 0
+ ) {
+ /* Received mbuf */
+ struct mbuf *m = mbufs [consume_index];
+
+ if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) {
+ /* Discard Ethernet CRC */
+ int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1 - ETHER_CRC_LEN;
+
+ /* Update mbuf */
+ m->m_len = sz;
+ m->m_pkthdr.len = sz;
+
+ LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz);
+
+ /* Hand over */
+ (*ifp->if_input)(ifp, m);
+
+ /* Increment received frames counter */
+ ++e->received_frames;
+ } else {
+ ++e->receive_drop_errors;
+ }
+ } else {
+ /* Update error counters */
+ if ((stat & ETH_RX_STAT_OVERRUN) != 0) {
+ ++e->receive_overrun_errors;
+ }
+ if ((stat & ETH_RX_STAT_LAST_FLAG) == 0) {
+ ++e->receive_fragment_errors;
+ }
+ if ((stat & ETH_RX_STAT_CRC_ERROR) != 0) {
+ ++e->receive_crc_errors;
+ }
+ if ((stat & ETH_RX_STAT_SYMBOL_ERROR) != 0) {
+ ++e->receive_symbol_errors;
+ }
+ if ((stat & ETH_RX_STAT_LENGTH_ERROR) != 0) {
+ ++e->receive_length_errors;
+ }
+ if ((stat & ETH_RX_STAT_ALIGNMENT_ERROR) != 0) {
+ ++e->receive_alignment_errors;
+ }
+ if ((stat & ETH_RX_STAT_NO_DESCRIPTOR) != 0) {
+ ++e->receive_no_descriptor_errors;
+ }
+ }
+
+ /* Increment and update consume index */
+ consume_index = lpc_eth_increment(consume_index, index_max);
+ lpc_eth->rxconsumeindex = consume_index;
+ } else {
+ /* Nothing to do, enable receive interrupts */
+ lpc_eth_enable_receive_interrupts();
+ break;
+ }
+ }
+ }
+}
- /* Initialize Tx packet and status descriptor heads */
- lpe_write_4(sc, LPE_TXDESC, sc->lpe_rdata.lpe_tx_ring_phys);
- lpe_write_4(sc, LPE_TXSTATUS, sc->lpe_rdata.lpe_tx_status_phys);
- lpe_write_4(sc, LPE_TXDESC_NUMBER, LPE_TXDESC_NUM - 1);
- lpe_write_4(sc, LPE_TXDESC_PROD, 0);
+static struct mbuf *lpc_eth_next_fragment(
+ struct ifnet *ifp,
+ struct mbuf *m,
+ uint32_t *ctrl
+)
+{
+ struct mbuf *n;
+ int size;
+
+ while (true) {
+ /* Get fragment size */
+ size = m->m_len;
+
+ if (size > 0) {
+ /* Now we have a not empty fragment */
+ break;
+ } else {
+ /* Skip empty fragments */
+ m = m->m_next;
+
+ if (m == NULL) {
+ return NULL;
+ }
+ }
+ }
+
+ /* Set fragment size */
+ *ctrl = (uint32_t) (size - 1);
+
+ /* Discard empty successive fragments */
+ n = m->m_next;
+ while (n != NULL && n->m_len <= 0) {
+ n = m_free(n);
+ }
+ m->m_next = n;
+
+ /* Is our fragment the last in the frame? */
+ if (n == NULL) {
+ *ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
+ }
+
+ return m;
+}
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+static void lpc_eth_tx_reclaim(lpc_eth_driver_entry *e, struct ifnet *ifp)
+{
+ volatile uint32_t *const status = e->tx_status_table;
+ volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ struct mbuf **const mbufs = e->tx_buf_table;
+ #else
+ char *const buf = e->tx_buf_table;
+ #endif
+ uint32_t const index_max = e->tx_unit_count - 1;
+ uint32_t consume_index = e->tx_consume_index;
+
+ /* Free consumed fragments */
+ while (true) {
+ /* Save last known consume index */
+ uint32_t c = consume_index;
+
+ /* Get new consume index */
+ consume_index = lpc_eth->txconsumeindex;
+
+ /* Nothing consumed in the meantime? */
+ if (c == consume_index) {
+ break;
+ }
+
+ while (c != consume_index) {
+ uint32_t s = status [c];
+
+ /* Update error counters */
+ if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) {
+ if ((s & ETH_TX_STAT_UNDERRUN) != 0) {
+ ++e->transmit_underrun_errors;
+ }
+ if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) {
+ ++e->transmit_late_collision_errors;
+ }
+ if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) {
+ ++e->transmit_excessive_collision_errors;
+ }
+ if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) {
+ ++e->transmit_excessive_defer_errors;
+ }
+ if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) {
+ ++e->transmit_no_descriptor_errors;
+ }
+ }
+
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Release mbuf */
+ m_freem(mbufs [c]);
+ mbufs [c] = NULL;
+ #endif
+
+ /* Next consume index */
+ c = lpc_eth_increment(c, index_max);
+ }
+ }
+
+ e->tx_consume_index = consume_index;
+}
- callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
+static int lpc_eth_tx_enqueue(
+ lpc_eth_driver_entry *e,
+ struct ifnet *ifp,
+ struct mbuf *m0
+)
+{
+ volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ struct mbuf **const mbufs = e->tx_buf_table;
+ #else
+ char *const buf = e->tx_buf_table;
+ uint32_t frame_length;
+ char *frame_buffer;
+ #endif
+ uint32_t const index_max = e->tx_unit_count - 1;
+ uint32_t produce_index = e->tx_produce_index;
+ uint32_t consume_index = e->tx_consume_index;
+ struct mbuf *m = m0;
+
+ while (true) {
+ uint32_t ctrl;
+
+ /* Compute next produce index */
+ uint32_t p = lpc_eth_increment(produce_index, index_max);
+
+ /* Queue full? */
+ if (p == consume_index) {
+ LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m);
+
+ /* The queue is full */
+ return ENOBUFS;
+ }
+
+ /* Get next fragment and control value */
+ m = lpc_eth_next_fragment(ifp, m, &ctrl);
+
+ /* New fragment? */
+ if (m != NULL) {
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Set the transfer data */
+ rtems_cache_flush_multiple_data_lines(
+ mtod(m, const void *),
+ (size_t) m->m_len
+ );
+ desc [produce_index].start = mtod(m, uint32_t);
+ desc [produce_index].control = ctrl;
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [produce_index],
+ sizeof(desc [0])
+ );
+
+ LPC_ETH_PRINTF(
+ "tx: %02" PRIu32 ": %u %s\n",
+ produce_index, m->m_len,
+ (ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : ""
+ );
+
+ /* Next produce index */
+ produce_index = p;
+
+ /* Last fragment of a frame? */
+ if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
+ /* Update the produce index */
+ lpc_eth->txproduceindex = produce_index;
+ e->tx_produce_index = produce_index;
+
+ mbufs [produce_index] = m0;
+
+ /* Increment transmitted frames counter */
+ ++e->transmitted_frames;
+
+ return 0;
+ }
+
+ /* Next fragment of the frame */
+ m = m->m_next;
+ #else
+ size_t fragment_length = (size_t) m->m_len;
+ void *fragment_start = mtod(m, void *);
+ uint32_t new_frame_length = frame_length + fragment_length;
+
+ /* Check buffer size */
+ if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) {
+ LPC_ETH_PRINTF("tx: overflow\n");
+
+ /* Discard overflow data */
+ new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE;
+ fragment_length = new_frame_length - frame_length;
+
+ /* Finalize frame */
+ ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
+
+ /* Update error counter */
+ ++e->transmit_overflow_errors;
+ }
+
+ LPC_ETH_PRINTF(
+ "tx: copy: %" PRIu32 "%s%s\n",
+ fragment_length,
+ (m->m_flags & M_EXT) != 0 ? ", E" : "",
+ (m->m_flags & M_PKTHDR) != 0 ? ", H" : ""
+ );
+
+ /* Copy fragment to buffer in Ethernet RAM */
+ memcpy(frame_buffer, fragment_start, fragment_length);
+
+ if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
+ /* Finalize descriptor */
+ desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK)
+ | (new_frame_length - 1);
+
+ LPC_ETH_PRINTF(
+ "tx: %02" PRIu32 ": %" PRIu32 "\n",
+ produce_index,
+ new_frame_length
+ );
+
+ /* Cache flush of data */
+ rtems_cache_flush_multiple_data_lines(
+ (const void *) desc [produce_index].start,
+ new_frame_length
+ );
+
+ /* Cache flush of descriptor */
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [produce_index],
+ sizeof(desc [0])
+ );
+
+ /* Next produce index */
+ produce_index = p;
+
+ /* Update the produce index */
+ lpc_eth->txproduceindex = produce_index;
+
+ /* Fresh frame length and buffer start */
+ frame_length = 0;
+ frame_buffer = (char *) desc [produce_index].start;
+
+ /* Increment transmitted frames counter */
+ ++e->transmitted_frames;
+ } else {
+ /* New frame length */
+ frame_length = new_frame_length;
+
+ /* Update current frame buffer start */
+ frame_buffer += fragment_length;
+ }
+
+ /* Free mbuf and get next */
+ m = m_free(m);
+ #endif
+ } else {
+ /* Nothing to transmit */
+ m_freem(m0);
+ return 0;
+ }
+ }
}
-static void
-lpe_start(struct ifnet *ifp)
+static int lpc_eth_mdio_wait_for_not_busy(void)
{
- struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
+ rtems_interval one_second = rtems_clock_get_ticks_per_second();
+ rtems_interval i = 0;
- lpe_lock(sc);
- lpe_start_locked(ifp);
- lpe_unlock(sc);
+ while ((lpc_eth->mind & ETH_MIND_BUSY) != 0 && i < one_second) {
+ rtems_task_wake_after(1);
+ ++i;
+ }
+
+ LPC_ETH_PRINTK("tx: lpc_eth_mdio_wait %s after %d\n",
+ i != one_second? "succeed": "timeout", i);
+
+ return i != one_second ? 0 : ETIMEDOUT;
}
-static void
-lpe_start_locked(struct ifnet *ifp)
+static uint32_t lpc_eth_mdio_read_anlpar(int phy)
{
- struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
- struct mbuf *m_head;
- int encap = 0;
+ uint32_t madr = ETH_MADR_REG(MII_ANLPAR) | ETH_MADR_PHY(phy);
+ uint32_t anlpar = 0;
+ int eno = 0;
- lpe_lock_assert(sc);
+ if (lpc_eth->madr != madr) {
+ lpc_eth->madr = madr;
+ }
- while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
- if (lpe_read_4(sc, LPE_TXDESC_PROD) ==
- lpe_read_4(sc, LPE_TXDESC_CONS) - 5)
- break;
+ if (lpc_eth->mcmd != ETH_MCMD_READ) {
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
+ }
- /* Dequeue first packet */
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
- if (!m_head)
- break;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+ if (eno == 0) {
+ anlpar = lpc_eth->mrdd;
+ }
- lpe_encap(sc, &m_head);
+ /* Start next read */
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
- encap++;
- }
-
- /* Submit new descriptor list */
- if (encap) {
- lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod);
- sc->lpe_watchdog_timer = 5;
- }
-
+ return anlpar;
}
-#ifdef __rtems__
-static int
-lpe_get_segs_for_tx(struct mbuf *m, bus_dma_segment_t segs[LPE_MAXFRAGS],
- int *nsegs)
+static int lpc_eth_mdio_read(
+ int phy,
+ void *arg RTEMS_UNUSED,
+ unsigned reg,
+ uint32_t *val
+)
{
- int i = 0;
-
- do {
- if (m->m_len > 0) {
- segs[i].ds_addr = mtod(m, bus_addr_t);
- segs[i].ds_len = m->m_len;
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_flush_multiple_data_lines(m->m_data, m->m_len);
-#endif
- ++i;
- }
- m = m->m_next;
- if (m == NULL) {
- *nsegs = i;
- return (0);
- }
- } while (i < LPE_MAXFRAGS);
- return (EFBIG);
+ int eno = 0;
+
+ if (0 <= phy && phy <= 31) {
+ lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy);
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+
+ if (eno == 0) {
+ *val = lpc_eth->mrdd;
+ }
+ } else {
+ eno = EINVAL;
+ }
+
+ return eno;
}
-#endif /* __rtems__ */
-static int
-lpe_encap(struct lpe_softc *sc, struct mbuf **m_head)
-{
- struct lpe_txdesc *txd;
- struct lpe_hwdesc *hwd;
- bus_dma_segment_t segs[LPE_MAXFRAGS];
- int i, err, nsegs, prod;
-
- lpe_lock_assert(sc);
- M_ASSERTPKTHDR((*m_head));
-
- prod = sc->lpe_cdata.lpe_tx_prod;
- txd = &sc->lpe_cdata.lpe_tx_desc[prod];
-
- debugf("starting with prod=%d\n", prod);
-
-#ifndef __rtems__
- err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag,
- txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
-#else /* __rtems__ */
- err = lpe_get_segs_for_tx(*m_head, segs, &nsegs);
-#endif /* __rtems__ */
-
- if (err)
- return (err);
-
- if (nsegs == 0) {
- m_freem(*m_head);
- *m_head = NULL;
- return (EIO);
- }
-
-#ifndef __rtems__
- bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap,
- BUS_DMASYNC_PREREAD);
-#endif /* __rtems__ */
- bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
- BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
-
- txd->lpe_txdesc_first = 1;
- txd->lpe_txdesc_mbuf = *m_head;
-
- for (i = 0; i < nsegs; i++) {
- hwd = &sc->lpe_rdata.lpe_tx_ring[prod];
- hwd->lhr_data = segs[i].ds_addr;
- hwd->lhr_control = segs[i].ds_len - 1;
-
- if (i == nsegs - 1) {
- hwd->lhr_control |= LPE_HWDESC_LASTFLAG;
- hwd->lhr_control |= LPE_HWDESC_INTERRUPT;
- hwd->lhr_control |= LPE_HWDESC_CRC;
- hwd->lhr_control |= LPE_HWDESC_PAD;
- }
-
-#ifdef __rtems__
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd));
-#endif
-#endif /* __rtems__ */
- LPE_INC(prod, LPE_TXDESC_NUM);
- }
- bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
- BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+static int lpc_eth_mdio_write(
+ int phy,
+ void *arg RTEMS_UNUSED,
+ unsigned reg,
+ uint32_t val
+)
+{
+ int eno = 0;
- sc->lpe_cdata.lpe_tx_used += nsegs;
- sc->lpe_cdata.lpe_tx_prod = prod;
+ if (0 <= phy && phy <= 31) {
+ lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy);
+ lpc_eth->mwtd = val;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+ } else {
+ eno = EINVAL;
+ }
- return (0);
+ return eno;
}
-static void
-lpe_stop(struct lpe_softc *sc)
+static int lpc_eth_phy_get_id(int phy, uint32_t *id)
{
- lpe_lock(sc);
- lpe_stop_locked(sc);
- lpe_unlock(sc);
-}
+ uint32_t id1 = 0;
+ int eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR1, &id1);
-static void
-lpe_stop_locked(struct lpe_softc *sc)
-{
- lpe_lock_assert(sc);
+ if (eno == 0) {
+ uint32_t id2 = 0;
- callout_stop(&sc->lpe_tick);
+ eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR2, &id2);
+ if (eno == 0) {
+ *id = (id1 << 16) | (id2 & 0xfff0);
+ }
+ }
- /* Disable interrupts */
- lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff);
+ return eno;
+}
- /* Stop EMAC */
- lpe_write_4(sc, LPE_MAC1, 0);
- lpe_write_4(sc, LPE_MAC2, 0);
- lpe_write_4(sc, LPE_COMMAND, 0);
+#define PHY_KSZ80X1RNL 0x221550
+#define PHY_DP83848 0x20005c90
- sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
-}
+typedef struct {
+ unsigned reg;
+ uint32_t set;
+ uint32_t clear;
+} lpc_eth_phy_action;
-static int
-lpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+static int lpc_eth_phy_set_and_clear(
+ lpc_eth_driver_entry *e,
+ const lpc_eth_phy_action *actions,
+ size_t n
+)
{
- struct lpe_softc *sc = ifp->if_softc;
- struct mii_data *mii = device_get_softc(sc->lpe_miibus);
- struct ifreq *ifr = (struct ifreq *)data;
- int err = 0;
-
- switch (cmd) {
- case SIOCSIFFLAGS:
- lpe_lock(sc);
- if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- lpe_set_rxmode(sc);
- lpe_set_rxfilter(sc);
- } else
- lpe_init_locked(sc);
- } else
- lpe_stop(sc);
- lpe_unlock(sc);
- break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- lpe_lock(sc);
- lpe_set_rxfilter(sc);
- lpe_unlock(sc);
- }
- break;
- case SIOCGIFMEDIA:
- case SIOCSIFMEDIA:
- err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
- break;
- default:
- err = ether_ioctl(ifp, cmd, data);
- break;
- }
-
- return (err);
+ int eno = 0;
+ size_t i;
+
+ for (i = 0; eno == 0 && i < n; ++i) {
+ const lpc_eth_phy_action *action = &actions [i];
+ uint32_t val;
+
+ eno = lpc_eth_mdio_read(e->phy, NULL, action->reg, &val);
+ if (eno == 0) {
+ val |= action->set;
+ val &= ~action->clear;
+ eno = lpc_eth_mdio_write(e->phy, NULL, action->reg, val);
+ }
+ }
+
+ return eno;
}
-static void lpe_set_rxmode(struct lpe_softc *sc)
-{
- struct ifnet *ifp = sc->lpe_ifp;
- uint32_t rxfilt;
+static const lpc_eth_phy_action lpc_eth_phy_up_action_default [] = {
+ { MII_BMCR, 0, BMCR_PDOWN },
+ { MII_BMCR, BMCR_RESET, 0 },
+ { MII_BMCR, BMCR_AUTOEN, 0 }
+};
- rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT;
+static const lpc_eth_phy_action lpc_eth_phy_up_pre_action_KSZ80X1RNL [] = {
+ /* Disable slow oscillator mode */
+ { 0x11, 0, 0x10 }
+};
- if (ifp->if_flags & IFF_BROADCAST)
- rxfilt |= LPE_RXFILTER_BROADCAST;
+static const lpc_eth_phy_action lpc_eth_phy_up_post_action_KSZ80X1RNL [] = {
+ /* Enable energy detect power down (EDPD) mode */
+ { 0x18, 0x0800, 0 },
+ /* Turn PLL of automatically in EDPD mode */
+ { 0x10, 0x10, 0 }
+};
- if (ifp->if_flags & IFF_PROMISC)
- rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST;
+static int lpc_eth_phy_up(lpc_eth_driver_entry *e)
+{
+ int eno;
+ int retries = 64;
+ uint32_t val;
+
+ e->phy = DEFAULT_PHY - 1;
+ while (true) {
+ e->phy = (e->phy + 1) % 32;
+
+ --retries;
+ eno = lpc_eth_phy_get_id(e->phy, &e->phy_id);
+ if (
+ (eno == 0 && e->phy_id != 0xfffffff0 && e->phy_id != 0)
+ || retries <= 0
+ ) {
+ break;
+ }
+
+ rtems_task_wake_after(1);
+ }
+
+ LPC_ETH_PRINTF("lpc_eth_phy_get_id: 0x%08" PRIx32 " from phy %d retries %d\n",
+ e->phy_id, e->phy, retries);
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_pre_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_pre_action_KSZ80X1RNL)
+ );
+ break;
+ case PHY_DP83848:
+ eno = lpc_eth_mdio_read(e->phy, NULL, 0x17, &val);
+ LPC_ETH_PRINTF("phy PHY_DP83848 RBR 0x%08" PRIx32 "\n", val);
+ /* val = 0x21; */
+ val = 0x32 ;
+ eno = lpc_eth_mdio_write(e->phy, NULL, 0x17, val);
+ break;
+ case 0:
+ case 0xfffffff0:
+ eno = EIO;
+ e->phy = DEFAULT_PHY;
+ break;
+ default:
+ break;
+ }
+
+ if (eno == 0) {
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_action_default)
+ );
+ }
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ e->phy_id = 0;
+ }
+
+ return eno;
+}
- if (ifp->if_flags & IFF_ALLMULTI)
- rxfilt |= LPE_RXFILTER_MULTICAST;
+static const lpc_eth_phy_action lpc_eth_phy_down_action_default [] = {
+ { MII_BMCR, BMCR_PDOWN, 0 }
+};
- lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt);
-}
+static const lpc_eth_phy_action lpc_eth_phy_down_post_action_KSZ80X1RNL [] = {
+ /* Enable slow oscillator mode */
+ { 0x11, 0x10, 0 }
+};
-static void lpe_set_rxfilter(struct lpe_softc *sc)
+static void lpc_eth_phy_down(lpc_eth_driver_entry *e)
{
- struct ifnet *ifp = sc->lpe_ifp;
- struct ifmultiaddr *ifma;
- int index;
- uint32_t hashl, hashh;
-
- hashl = 0;
- hashh = 0;
-
- if_maddr_rlock(ifp);
- CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_LINK)
- continue;
-
- index = ether_crc32_be(LLADDR((struct sockaddr_dl *)
- ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f;
-
- if (index > 31)
- hashh |= (1 << (index - 32));
- else
- hashl |= (1 << index);
- }
- if_maddr_runlock(ifp);
-
- /* Program new hash filter */
- lpe_write_4(sc, LPE_HASHFILTER_L, hashl);
- lpe_write_4(sc, LPE_HASHFILTER_H, hashh);
+ int eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_down_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_action_default)
+ );
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_down_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
}
-static void
-lpe_intr(void *arg)
+static void lpc_eth_soft_reset(void)
{
- struct lpe_softc *sc = (struct lpe_softc *)arg;
- uint32_t intstatus;
-
- debugf("status=0x%08x\n", lpe_read_4(sc, LPE_INTSTATUS));
-
- lpe_lock(sc);
-
- while ((intstatus = lpe_read_4(sc, LPE_INTSTATUS))) {
- if (intstatus & LPE_INT_RXDONE)
- lpe_rxintr(sc);
-
-#ifndef __rtems__
- if (intstatus & LPE_INT_TXDONE)
- lpe_txintr(sc);
-
-#else /* __rtems__ */
- if (intstatus & LPE_INT_TXUNDERRUN) {
- if_inc_counter(sc->lpe_ifp, IFCOUNTER_OERRORS, 1);
- lpe_stop_locked(sc);
- lpe_init_locked(sc);
- }
- else if (intstatus & (LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE))
- lpe_txintr(sc);
-#endif /* __rtems__ */
- lpe_write_4(sc, LPE_INTCLEAR, 0xffff);
- }
-
- lpe_unlock(sc);
+ lpc_eth->command = 0x38;
+ lpc_eth->mac1 = 0xcf00;
+ lpc_eth->mac1 = 0x0;
}
-static void
-lpe_rxintr(struct lpe_softc *sc)
+static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
{
- struct ifnet *ifp = sc->lpe_ifp;
- struct lpe_hwdesc *hwd;
- struct lpe_hwstatus *hws;
- struct lpe_rxdesc *rxd;
- struct mbuf *m;
- int prod, cons;
-
- for (;;) {
- prod = lpe_read_4(sc, LPE_RXDESC_PROD);
- cons = lpe_read_4(sc, LPE_RXDESC_CONS);
-
- if (prod == cons)
- break;
-
- rxd = &sc->lpe_cdata.lpe_rx_desc[cons];
- hwd = &sc->lpe_rdata.lpe_rx_ring[cons];
- hws = &sc->lpe_rdata.lpe_rx_status[cons];
-#ifdef __rtems__
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_invalidate_multiple_data_lines(rxd, sizeof(*rxd));
- rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd));
- rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws));
-#endif
-#endif /* __rtems__ */
-
- /* Check received frame for errors */
- if (hws->lhs_info & LPE_HWDESC_RXERRS) {
- if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
- lpe_discard_rxbuf(sc, cons);
- lpe_init_rxbuf(sc, cons);
- goto skip;
- }
-
- m = rxd->lpe_rxdesc_mbuf;
-#ifdef __rtems__
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
-#endif
-#endif /* __rtems__ */
- m->m_pkthdr.rcvif = ifp;
- m->m_data += 2;
-
- if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
-
- lpe_unlock(sc);
- (*ifp->if_input)(ifp, m);
- lpe_lock(sc);
-
- lpe_init_rxbuf(sc, cons);
-skip:
- LPE_INC(cons, LPE_RXDESC_NUM);
- lpe_write_4(sc, LPE_RXDESC_CONS, cons);
- }
+ int eno = 0;
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ struct ifnet *ifp = e->ifp;
+
+ if (up && e->state == LPC_ETH_STATE_DOWN) {
+ lpc_eth_config_module_enable();
+
+ /* Enable RX/TX reset and disable soft reset */
+ lpc_eth->mac1 = 0xf00;
+
+ /* Initialize PHY */
+ /* Clock value 10 (divide by 44 ) is safe on LPC178x up to 100 MHz AHB clock */
+ lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10) | ETH_MCFG_RESETMIIMGMT;
+ rtems_task_wake_after(1);
+ lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10);
+ rtems_task_wake_after(1);
+ eno = lpc_eth_phy_up(e);
+
+ if (eno == 0) {
+ const uint8_t *eaddr;
+
+ /*
+ * We must have a valid external clock from the PHY at this point,
+ * otherwise the system bus hangs and only a watchdog reset helps.
+ */
+ lpc_eth_soft_reset();
+
+ /* Reinitialize registers */
+ lpc_eth->mac2 = 0x31;
+ lpc_eth->ipgt = 0x15;
+ lpc_eth->ipgr = 0x12;
+ lpc_eth->clrt = 0x370f;
+ lpc_eth->maxf = 0x0600;
+ lpc_eth->supp = ETH_SUPP_SPEED;
+ lpc_eth->test = 0;
+ #ifdef LPC_ETH_CONFIG_RMII
+ lpc_eth->command = 0x0600;
+ #else
+ lpc_eth->command = 0x0400;
+ #endif
+ lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN;
+ lpc_eth->intclear = 0x30ff;
+ lpc_eth->powerdown = 0;
+
+ /* MAC address */
+ eaddr = IF_LLADDR(e->ifp);
+ lpc_eth->sa0 = ((uint32_t) eaddr [5] << 8) | (uint32_t) eaddr [4];
+ lpc_eth->sa1 = ((uint32_t) eaddr [3] << 8) | (uint32_t) eaddr [2];
+ lpc_eth->sa2 = ((uint32_t) eaddr [1] << 8) | (uint32_t) eaddr [0];
+
+ lpc_eth_setup_rxfilter(e);
+
+ /* Enable receiver */
+ lpc_eth->mac1 = 0x03;
+
+ /* Initialize tasks */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_INIT_RX);
+ lpc_eth_initialize_transmit(e);
+
+ /* Install interrupt handler */
+ sc = rtems_interrupt_handler_install(
+ e->interrupt_number,
+ "Ethernet",
+ RTEMS_INTERRUPT_UNIQUE,
+ lpc_eth_interrupt_handler,
+ e
+ );
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ /* Start watchdog timer */
+ callout_reset(&e->watchdog_callout, hz, lpc_eth_interface_watchdog, e);
+
+ /* Change state */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ e->state = LPC_ETH_STATE_UP;
+ }
+ } else if (!up && e->state == LPC_ETH_STATE_UP) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /* Remove interrupt handler */
+ sc = rtems_interrupt_handler_remove(
+ e->interrupt_number,
+ lpc_eth_interrupt_handler,
+ e
+ );
+ BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
+
+ /* Stop task */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
+
+ lpc_eth_soft_reset();
+ lpc_eth_phy_down(e);
+ lpc_eth_config_module_disable();
+
+ /* Stop watchdog timer */
+ callout_stop(&e->watchdog_callout);
+
+ /* Change state */
+ e->state = LPC_ETH_STATE_DOWN;
+ }
+
+ return eno;
}
-static void
-lpe_txintr(struct lpe_softc *sc)
+static void lpc_eth_interface_init(void *arg)
{
- struct ifnet *ifp = sc->lpe_ifp;
- struct lpe_hwdesc *hwd;
- struct lpe_hwstatus *hws;
- struct lpe_txdesc *txd;
- int cons, last;
-
- for (;;) {
- cons = lpe_read_4(sc, LPE_TXDESC_CONS);
- last = sc->lpe_cdata.lpe_tx_last;
-
- if (cons == last)
- break;
-
- txd = &sc->lpe_cdata.lpe_tx_desc[last];
- hwd = &sc->lpe_rdata.lpe_tx_ring[last];
- hws = &sc->lpe_rdata.lpe_tx_status[last];
-
-#ifndef __rtems__
- bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag,
- txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE);
-#else /* __rtems__ */
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_invalidate_multiple_data_lines(txd, sizeof(*txd));
- rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd));
- rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws));
-#endif
-#endif /* __rtems__ */
-
- if_inc_counter(ifp, IFCOUNTER_COLLISIONS, LPE_HWDESC_COLLISIONS(hws->lhs_info));
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
- if (hws->lhs_info & LPE_HWDESC_TXERRS)
- if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
- else
- if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
-
- if (txd->lpe_txdesc_first) {
-#ifndef __rtems__
- bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag,
- txd->lpe_txdesc_dmamap);
-#endif /* __rtems__ */
-
- m_freem(txd->lpe_txdesc_mbuf);
- txd->lpe_txdesc_mbuf = NULL;
- txd->lpe_txdesc_first = 0;
- }
+ (void) lpc_eth_up_or_down(e, true);
+}
- sc->lpe_cdata.lpe_tx_used--;
- LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM);
- }
+static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e)
+{
+ struct ifnet *ifp = e->ifp;
+
+ lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0);
+
+ if ((ifp->if_flags & IFF_ALLMULTI)) {
+ lpc_eth->hashfilterl = 0xffffffff;
+ lpc_eth->hashfilterh = 0xffffffff;
+ } else {
+ struct ifmultiaddr *ifma;
+
+ lpc_eth->hashfilterl = 0x0;
+ lpc_eth->hashfilterh = 0x0;
+
+ if_maddr_rlock(ifp);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ uint32_t crc;
+ uint32_t index;
+
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ /* XXX: ether_crc32_le() does not work, why? */
+ crc = ether_crc32_be(
+ LLADDR((struct sockaddr_dl *) ifma->ifma_addr),
+ ETHER_ADDR_LEN
+ );
+ index = (crc >> 23) & 0x3f;
+
+ if (index < 32) {
+ lpc_eth->hashfilterl |= 1U << index;
+ } else {
+ lpc_eth->hashfilterh |= 1U << (index - 32);
+ }
+ }
+ if_maddr_runlock(ifp);
+ }
+}
- if (!sc->lpe_cdata.lpe_tx_used)
- sc->lpe_watchdog_timer = 0;
+static int lpc_eth_interface_ioctl(
+ struct ifnet *ifp,
+ ioctl_command_t cmd,
+ caddr_t data
+)
+{
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int eno = 0;
+
+ LPC_ETH_PRINTF("%s\n", __func__);
+
+ switch (cmd) {
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ eno = ifmedia_ioctl(ifp, ifr, &e->ifmedia, cmd);
+ break;
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl(ifp, cmd, data);
+ break;
+ case SIOCSIFFLAGS:
+ LPE_LOCK(e);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ e->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) {
+ lpc_eth_setup_rxfilter(e);
+ }
+ } else {
+ eno = lpc_eth_up_or_down(e, true);
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ eno = lpc_eth_up_or_down(e, false);
+ }
+ }
+ e->if_flags = ifp->if_flags;
+ LPE_UNLOCK(e);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ LPE_LOCK(e);
+ lpc_eth_setup_rxfilter(e);
+ LPE_UNLOCK(e);
+ }
+ break;
+ default:
+ eno = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return eno;
}
-static void
-lpe_tick(void *arg)
+static int lpc_eth_interface_transmit(struct ifnet *ifp, struct mbuf *m)
{
- struct lpe_softc *sc = (struct lpe_softc *)arg;
- struct mii_data *mii = device_get_softc(sc->lpe_miibus);
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
+ int eno;
- lpe_lock_assert(sc);
-
- mii_tick(mii);
- lpe_watchdog(sc);
+ LPE_LOCK(e);
- callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
-}
+ if (e->state == LPC_ETH_STATE_UP) {
+ eno = lpc_eth_tx_enqueue(e, ifp, m);
+ lpc_eth_tx_reclaim(e, ifp);
-static void
-lpe_watchdog(struct lpe_softc *sc)
-{
- struct ifnet *ifp = sc->lpe_ifp;
+ if (__predict_false(eno != 0)) {
+ struct mbuf *n;
- lpe_lock_assert(sc);
+ n = m_defrag(m, M_NOWAIT);
+ if (n != NULL) {
+ m = n;
+ }
- if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--)
- return;
+ eno = lpc_eth_tx_enqueue(e, ifp, m);
+ }
+ } else {
+ eno = ENETDOWN;
+ }
- /* Chip has stopped responding */
- device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n");
- lpe_stop_locked(sc);
- lpe_init_locked(sc);
+ if (eno != 0) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
+ }
- /* Try to resend packets */
- if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- lpe_start_locked(ifp);
+ LPE_UNLOCK(e);
+ return eno;
}
-static int
-lpe_dma_alloc(struct lpe_softc *sc)
+static void lpc_eth_interface_watchdog(void *arg)
{
- int err;
-
- /* Create parent DMA tag */
- err = bus_dma_tag_create(
- bus_get_dma_tag(sc->lpe_dev),
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_parent_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create parent DMA tag\n");
- return (err);
- }
-
- err = lpe_dma_alloc_rx(sc);
- if (err)
- return (err);
-
- err = lpe_dma_alloc_tx(sc);
- if (err)
- return (err);
-
- return (0);
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
+
+ if (e->state == LPC_ETH_STATE_UP) {
+ uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy);
+
+ if (e->anlpar != anlpar) {
+ bool full_duplex = false;
+ bool speed = false;
+
+ e->anlpar = anlpar;
+
+ if ((anlpar & ANLPAR_TX_FD) != 0) {
+ full_duplex = true;
+ speed = true;
+ } else if ((anlpar & ANLPAR_T4) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_TX) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_10_FD) != 0) {
+ full_duplex = true;
+ }
+
+ if (full_duplex) {
+ lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX;
+ } else {
+ lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX;
+ }
+
+ if (speed) {
+ lpc_eth->supp |= ETH_SUPP_SPEED;
+ } else {
+ lpc_eth->supp &= ~ETH_SUPP_SPEED;
+ }
+ }
+
+ callout_reset(&e->watchdog_callout, WATCHDOG_TIMEOUT * hz, lpc_eth_interface_watchdog, e);
+ }
}
-static int
-lpe_dma_alloc_rx(struct lpe_softc *sc)
+static int lpc_eth_media_change(struct ifnet *ifp)
{
- struct lpe_rxdesc *rxd;
- struct lpe_dmamap_arg ctx;
- int err, i;
-
- /* Create tag for Rx ring */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- LPE_RXDESC_SIZE, 1, /* maxsize, nsegments */
- LPE_RXDESC_SIZE, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_rx_ring_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Rx ring DMA tag\n");
- goto fail;
- }
-
- /* Create tag for Rx status ring */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- LPE_RXSTATUS_SIZE, 1, /* maxsize, nsegments */
- LPE_RXSTATUS_SIZE, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_rx_status_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Rx status ring DMA tag\n");
- goto fail;
- }
-
- /* Create tag for Rx buffers */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- MCLBYTES * LPE_RXDESC_NUM, /* maxsize */
- LPE_RXDESC_NUM, /* segments */
- MCLBYTES, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_rx_buf_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Rx buffers DMA tag\n");
- goto fail;
- }
-
- /* Allocate Rx DMA ring */
- err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_ring_tag,
- (void **)&sc->lpe_rdata.lpe_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
- BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_ring_map);
-
- err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_ring_tag,
- sc->lpe_cdata.lpe_rx_ring_map, sc->lpe_rdata.lpe_rx_ring,
- LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
-
- sc->lpe_rdata.lpe_rx_ring_phys = ctx.lpe_dma_busaddr;
-
- /* Allocate Rx status ring */
- err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_status_tag,
- (void **)&sc->lpe_rdata.lpe_rx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
- BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_status_map);
-
- err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_status_tag,
- sc->lpe_cdata.lpe_rx_status_map, sc->lpe_rdata.lpe_rx_status,
- LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
-
- sc->lpe_rdata.lpe_rx_status_phys = ctx.lpe_dma_busaddr;
-
-
- /* Create Rx buffers DMA map */
- for (i = 0; i < LPE_RXDESC_NUM; i++) {
- rxd = &sc->lpe_cdata.lpe_rx_desc[i];
- rxd->lpe_rxdesc_mbuf = NULL;
-#ifndef __rtems__
- rxd->lpe_rxdesc_dmamap = NULL;
-
- err = bus_dmamap_create(sc->lpe_cdata.lpe_rx_buf_tag, 0,
- &rxd->lpe_rxdesc_dmamap);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Rx DMA map\n");
- return (err);
- }
-#endif /* __rtems__ */
- }
-
- return (0);
-fail:
- return (err);
+ (void) ifp;
+ return EINVAL;
}
-static int
-lpe_dma_alloc_tx(struct lpe_softc *sc)
+static void lpc_eth_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct lpe_txdesc *txd;
- struct lpe_dmamap_arg ctx;
- int err, i;
-
- /* Create tag for Tx ring */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- LPE_TXDESC_SIZE, 1, /* maxsize, nsegments */
- LPE_TXDESC_SIZE, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_tx_ring_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Tx ring DMA tag\n");
- goto fail;
- }
-
- /* Create tag for Tx status ring */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- LPE_TXSTATUS_SIZE, 1, /* maxsize, nsegments */
- LPE_TXSTATUS_SIZE, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_tx_status_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Tx status ring DMA tag\n");
- goto fail;
- }
-
- /* Create tag for Tx buffers */
- err = bus_dma_tag_create(
- sc->lpe_cdata.lpe_parent_tag,
- LPE_DESC_ALIGN, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- MCLBYTES * LPE_TXDESC_NUM, /* maxsize */
- LPE_TXDESC_NUM, /* segments */
- MCLBYTES, 0, /* maxsegsize, flags */
- NULL, NULL, /* lockfunc, lockarg */
- &sc->lpe_cdata.lpe_tx_buf_tag);
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Tx buffers DMA tag\n");
- goto fail;
- }
-
- /* Allocate Tx DMA ring */
- err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_ring_tag,
- (void **)&sc->lpe_rdata.lpe_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
- BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_ring_map);
-
- err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_ring_tag,
- sc->lpe_cdata.lpe_tx_ring_map, sc->lpe_rdata.lpe_tx_ring,
- LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
-
- sc->lpe_rdata.lpe_tx_ring_phys = ctx.lpe_dma_busaddr;
-
- /* Allocate Tx status ring */
- err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_status_tag,
- (void **)&sc->lpe_rdata.lpe_tx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
- BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_status_map);
-
- err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_status_tag,
- sc->lpe_cdata.lpe_tx_status_map, sc->lpe_rdata.lpe_tx_status,
- LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
-
- sc->lpe_rdata.lpe_tx_status_phys = ctx.lpe_dma_busaddr;
-
-
- /* Create Tx buffers DMA map */
- for (i = 0; i < LPE_TXDESC_NUM; i++) {
- txd = &sc->lpe_cdata.lpe_tx_desc[i];
- txd->lpe_txdesc_mbuf = NULL;
-#ifndef __rtems__
- txd->lpe_txdesc_dmamap = NULL;
-#endif /* __rtems__ */
- txd->lpe_txdesc_first = 0;
-
-#ifndef __rtems__
- err = bus_dmamap_create(sc->lpe_cdata.lpe_tx_buf_tag, 0,
- &txd->lpe_txdesc_dmamap);
-#endif /* __rtems__ */
-
- if (err) {
- device_printf(sc->lpe_dev, "cannot create Tx DMA map\n");
- return (err);
- }
- }
-
- return (0);
-fail:
- return (err);
+ (void) ifp;
+
+ imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+ imr->ifm_active = IFM_ETHER;
+
+ if ((lpc_eth->supp & ETH_SUPP_SPEED) != 0) {
+ imr->ifm_active |= IFM_100_TX;
+ } else {
+ imr->ifm_active |= IFM_10_T;
+ }
+
+ if ((lpc_eth->mac2 & ETH_MAC2_FULL_DUPLEX) != 0) {
+ imr->ifm_active |= IFM_FDX;
+ } else {
+ imr->ifm_active |= IFM_HDX;
+ }
}
-static int
-lpe_init_rx(struct lpe_softc *sc)
+__weak_symbol int lpc_eth_probe(int unit)
{
- int i, err;
+ if (unit != 0) {
+ return ENXIO;
+ }
- for (i = 0; i < LPE_RXDESC_NUM; i++) {
- err = lpe_init_rxbuf(sc, i);
- if (err)
- return (err);
- }
-
- return (0);
+ return BUS_PROBE_DEFAULT;
}
-static int
-lpe_init_rxbuf(struct lpe_softc *sc, int n)
+static int lpc_eth_do_probe(device_t dev)
{
- struct lpe_rxdesc *rxd;
- struct lpe_hwdesc *hwd;
-#ifndef __rtems__
- struct lpe_hwstatus *hws;
-#endif /* __rtems__ */
- struct mbuf *m;
- bus_dma_segment_t segs[1];
-#ifndef __rtems__
- int nsegs;
-#endif /* __rtems__ */
-
- rxd = &sc->lpe_cdata.lpe_rx_desc[n];
- hwd = &sc->lpe_rdata.lpe_rx_ring[n];
-#ifndef __rtems__
- hws = &sc->lpe_rdata.lpe_rx_status[n];
-#endif /* __rtems__ */
- m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
-
- if (!m) {
- device_printf(sc->lpe_dev, "WARNING: mbufs exhausted!\n");
- return (ENOBUFS);
- }
-
- m->m_len = m->m_pkthdr.len = MCLBYTES;
-
-#ifndef __rtems__
- bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
-
- if (bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_rx_buf_tag,
- rxd->lpe_rxdesc_dmamap, m, segs, &nsegs, 0)) {
- m_freem(m);
- return (ENOBUFS);
- }
-
- bus_dmamap_sync(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap,
- BUS_DMASYNC_PREREAD);
-#else /* __rtems__ */
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
-#endif
- segs[0].ds_addr = mtod(m, bus_addr_t);
-#endif /* __rtems__ */
-
- rxd->lpe_rxdesc_mbuf = m;
- hwd->lhr_data = segs[0].ds_addr + 2;
- hwd->lhr_control = (segs[0].ds_len - 1) | LPE_HWDESC_INTERRUPT;
-#ifdef __rtems__
-#ifdef CPU_DATA_CACHE_ALIGNMENT
- rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd));
-#endif
-#endif /* __rtems__ */
-
- return (0);
+ device_set_desc(dev, "LPC32x0 Ethernet controller");
+ return lpc_eth_probe(device_get_unit(dev));
}
-static void
-lpe_discard_rxbuf(struct lpe_softc *sc, int n)
+static int lpc_eth_attach(device_t dev)
{
- struct lpe_rxdesc *rxd;
- struct lpe_hwdesc *hwd;
-
- rxd = &sc->lpe_cdata.lpe_rx_desc[n];
- hwd = &sc->lpe_rdata.lpe_rx_ring[n];
-
-#ifndef __rtems__
- bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
-#endif /* __rtems__ */
-
- hwd->lhr_data = 0;
- hwd->lhr_control = 0;
-
- if (rxd->lpe_rxdesc_mbuf) {
- m_freem(rxd->lpe_rxdesc_mbuf);
- rxd->lpe_rxdesc_mbuf = NULL;
- }
+ lpc_eth_driver_entry *e = device_get_softc(dev);
+ struct ifnet *ifp = NULL;
+ char *unit_name = NULL;
+ int unit_index = device_get_unit(dev);
+ size_t table_area_size = 0;
+ char *table_area = NULL;
+ char *table_location = NULL;
+ rtems_status_code status;
+ uint8_t eaddr[ETHER_ADDR_LEN];
+
+ BSD_ASSERT(e->state == LPC_ETH_STATE_NOT_INITIALIZED);
+
+ mtx_init(&e->mtx, device_get_nameunit(e->dev), MTX_NETWORK_LOCK, MTX_DEF);
+
+ ifmedia_init(&e->ifmedia, 0, lpc_eth_media_change, lpc_eth_media_status);
+ ifmedia_add(&e->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&e->ifmedia, IFM_ETHER | IFM_AUTO);
+
+ callout_init_mtx(&e->watchdog_callout, &e->mtx, 0);
+
+ /* Receive unit count */
+ e->rx_unit_count = LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT;
+
+ /* Transmit unit count */
+ e->tx_unit_count = LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT;
+
+ /* Remember interrupt number */
+ e->interrupt_number = LPC_ETH_CONFIG_INTERRUPT;
+
+ /* Allocate and clear table area */
+ table_area_size =
+ e->rx_unit_count
+ * (sizeof(lpc_eth_transfer_descriptor)
+ + sizeof(lpc_eth_receive_status)
+ + sizeof(struct mbuf *))
+ + e->tx_unit_count
+ * (sizeof(lpc_eth_transfer_descriptor)
+ + sizeof(uint32_t)
+ + LPC_ETH_CONFIG_TX_BUF_SIZE);
+ table_area = lpc_eth_config_alloc_table_area(table_area_size);
+ if (table_area == NULL) {
+ return ENOMEM;
+ }
+ memset(table_area, 0, table_area_size);
+
+ table_location = table_area;
+
+ /*
+ * The receive status table must be the first one since it has the strictest
+ * alignment requirements.
+ */
+ e->rx_status_table = (volatile lpc_eth_receive_status *) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_status_table [0]);
+
+ e->rx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_desc_table [0]);
+
+ e->rx_mbuf_table = (struct mbuf **) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_mbuf_table [0]);
+
+ e->tx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location;
+ table_location += e->tx_unit_count * sizeof(e->tx_desc_table [0]);
+
+ e->tx_status_table = (volatile uint32_t *) table_location;
+ table_location += e->tx_unit_count * sizeof(e->tx_status_table [0]);
+
+ e->tx_buf_table = table_location;
+
+ /* Set interface data */
+ e->dev = dev;
+ e->ifp = ifp = if_alloc(IFT_ETHER);
+ ifp->if_softc = e;
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_init = lpc_eth_interface_init;
+ ifp->if_ioctl = lpc_eth_interface_ioctl;
+ ifp->if_transmit = lpc_eth_interface_transmit;
+ ifp->if_qflush = if_qflush;
+ ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
+ IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1);
+ ifp->if_snd.ifq_drv_maxlen = LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1;
+ IFQ_SET_READY(&ifp->if_snd);
+ ifp->if_hdrlen = sizeof(struct ether_header);
+
+ rtems_bsd_get_mac_address(device_get_name(e->dev), unit_index, eaddr);
+
+ /* Create tasks */
+ status = rtems_task_create(
+ rtems_build_name('n', 't', 'r', 'x'),
+ rtems_bsd_get_task_priority(device_get_name(e->dev)),
+ 4096,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &e->receive_task
+ );
+ BSD_ASSERT(status == RTEMS_SUCCESSFUL);
+ status = rtems_task_start(
+ e->receive_task,
+ lpc_eth_receive_task,
+ (rtems_task_argument)e
+ );
+ BSD_ASSERT(status == RTEMS_SUCCESSFUL);
+
+ if_link_state_change(e->ifp, LINK_STATE_UP);
+
+ /* Change status */
+ e->state = LPC_ETH_STATE_DOWN;
+
+ /* Attach the interface */
+ ether_ifattach(ifp, eaddr);
+
+ return 0;
}
-static void
-lpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+static int lpc_eth_detach(device_t dev)
{
- struct lpe_dmamap_arg *ctx;
+ /* FIXME: Detach the interface from the upper layers? */
- if (error)
- return;
+ /* Module soft reset */
+ lpc_eth->command = 0x38;
+ lpc_eth->mac1 = 0xcf00;
- ctx = (struct lpe_dmamap_arg *)arg;
- ctx->lpe_dma_busaddr = segs[0].ds_addr;
-}
+ /* FIXME: More cleanup */
-static int
-lpe_ifmedia_upd(struct ifnet *ifp)
-{
- return (0);
-}
-
-static void
-lpe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
-{
- struct lpe_softc *sc = ifp->if_softc;
- struct mii_data *mii = device_get_softc(sc->lpe_miibus);
-
- lpe_lock(sc);
- mii_pollstat(mii);
- ifmr->ifm_active = mii->mii_media_active;
- ifmr->ifm_status = mii->mii_media_status;
- lpe_unlock(sc);
+ return ENXIO;
}
static device_method_t lpe_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, lpe_probe),
- DEVMETHOD(device_attach, lpe_attach),
- DEVMETHOD(device_detach, lpe_detach),
-
- /* Bus interface */
- DEVMETHOD(bus_print_child, bus_generic_print_child),
-
- /* MII interface */
- DEVMETHOD(miibus_readreg, lpe_miibus_readreg),
- DEVMETHOD(miibus_writereg, lpe_miibus_writereg),
- DEVMETHOD(miibus_statchg, lpe_miibus_statchg),
- { 0, 0 }
+ DEVMETHOD(device_probe, lpc_eth_do_probe),
+ DEVMETHOD(device_attach, lpc_eth_attach),
+ DEVMETHOD(device_detach, lpc_eth_detach),
+ DEVMETHOD_END
};
-static driver_t lpe_driver = {
- "lpe",
- lpe_methods,
- sizeof(struct lpe_softc),
+static driver_t lpe_nexus_driver = {
+ "lpe",
+ lpe_methods,
+ sizeof(lpc_eth_driver_entry)
};
static devclass_t lpe_devclass;
-
-#ifndef __rtems__
-DRIVER_MODULE(lpe, simplebus, lpe_driver, lpe_devclass, 0, 0);
-#else /* __rtems__ */
-DRIVER_MODULE(lpe, nexus, lpe_driver, lpe_devclass, 0, 0);
-#endif /* __rtems__ */
-DRIVER_MODULE(miibus, lpe, miibus_driver, miibus_devclass, 0, 0);
-MODULE_DEPEND(lpe, obio, 1, 1, 1);
-MODULE_DEPEND(lpe, miibus, 1, 1, 1);
+DRIVER_MODULE(lpe, nexus, lpe_nexus_driver, lpe_devclass, 0, 0);
+MODULE_DEPEND(lpe, nexus, 1, 1, 1);
MODULE_DEPEND(lpe, ether, 1, 1, 1);
+
+#endif /* LIBBSP_ARM_LPC24XX_BSP_H || LIBBSP_ARM_LPC32XX_BSP_H */
diff --git a/rtemsbsd/sys/arm/lpc/if_lpereg.h b/rtemsbsd/sys/arm/lpc/if_lpereg.h
deleted file mode 100644
index a40bf8b5..00000000
--- a/rtemsbsd/sys/arm/lpc/if_lpereg.h
+++ /dev/null
@@ -1,210 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
- *
- * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
- * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$
- */
-
-#ifndef _ARM_LPC_IF_LPEREG_H
-#define _ARM_LPC_IF_LPEREG_H
-
-#define LPE_MAC1 0x000
-#define LPE_MAC1_RXENABLE (1 << 0)
-#define LPE_MAC1_PASSALL (1 << 1)
-#define LPE_MAC1_RXFLOWCTRL (1 << 2)
-#define LPE_MAC1_TXFLOWCTRL (1 << 3)
-#define LPE_MAC1_LOOPBACK (1 << 4)
-#define LPE_MAC1_RESETTX (1 << 8)
-#define LPE_MAC1_RESETMCSTX (1 << 9)
-#define LPE_MAC1_RESETRX (1 << 10)
-#define LPE_MAC1_RESETMCSRX (1 << 11)
-#define LPE_MAC1_SIMRESET (1 << 14)
-#define LPE_MAC1_SOFTRESET (1 << 15)
-#define LPE_MAC2 0x004
-#define LPE_MAC2_FULLDUPLEX (1 << 0)
-#define LPE_MAC2_FRAMELENCHECK (1 << 1)
-#define LPE_MAC2_HUGEFRAME (1 << 2)
-#define LPE_MAC2_DELAYEDCRC (1 << 3)
-#define LPE_MAC2_CRCENABLE (1 << 4)
-#define LPE_MAC2_PADCRCENABLE (1 << 5)
-#define LPE_MAC2_VLANPADENABLE (1 << 6)
-#define LPE_MAC2_AUTOPADENABLE (1 << 7)
-#define LPE_MAC2_PUREPREAMBLE (1 << 8)
-#define LPE_MAC2_LONGPREAMBLE (1 << 9)
-#define LPE_MAC2_NOBACKOFF (1 << 12)
-#define LPE_MAC2_BACKPRESSURE (1 << 13)
-#define LPE_MAC2_EXCESSDEFER (1 << 14)
-#define LPE_IPGT 0x008
-#define LPE_IPGR 0x00c
-#define LPE_CLRT 0x010
-#define LPE_MAXF 0x014
-#define LPE_SUPP 0x018
-#define LPE_SUPP_SPEED (1 << 8)
-#define LPE_TEST 0x01c
-#define LPE_MCFG 0x020
-#define LPE_MCFG_SCANINCR (1 << 0)
-#define LPE_MCFG_SUPPREAMBLE (1 << 1)
-#define LPE_MCFG_CLKSEL(_n) ((_n & 0x7) << 2)
-#define LPC_MCFG_RESETMII (1 << 15)
-#define LPE_MCMD 0x024
-#define LPE_MCMD_READ (1 << 0)
-#define LPE_MCMD_WRITE (0 << 0)
-#define LPE_MCMD_SCAN (1 << 1)
-#define LPE_MADR 0x028
-#define LPE_MADR_REGMASK 0x1f
-#define LPE_MADR_REGSHIFT 0
-#define LPE_MADR_PHYMASK 0x1f
-#define LPE_MADR_PHYSHIFT 8
-#define LPE_MWTD 0x02c
-#define LPE_MWTD_DATAMASK 0xffff
-#define LPE_MRDD 0x030
-#define LPE_MRDD_DATAMASK 0xffff
-#define LPE_MIND 0x034
-#define LPE_MIND_BUSY (1 << 0)
-#define LPE_MIND_SCANNING (1 << 1)
-#define LPE_MIND_INVALID (1 << 2)
-#define LPE_MIND_MIIFAIL (1 << 3)
-#define LPE_SA0 0x040
-#define LPE_SA1 0x044
-#define LPE_SA2 0x048
-#define LPE_COMMAND 0x100
-#define LPE_COMMAND_RXENABLE (1 << 0)
-#define LPE_COMMAND_TXENABLE (1 << 1)
-#define LPE_COMMAND_REGRESET (1 << 3)
-#define LPE_COMMAND_TXRESET (1 << 4)
-#define LPE_COMMAND_RXRESET (1 << 5)
-#define LPE_COMMAND_PASSRUNTFRAME (1 << 6)
-#define LPE_COMMAND_PASSRXFILTER (1 << 7)
-#define LPE_COMMAND_TXFLOWCTL (1 << 8)
-#define LPE_COMMAND_RMII (1 << 9)
-#define LPE_COMMAND_FULLDUPLEX (1 << 10)
-#define LPE_STATUS 0x104
-#define LPE_STATUS_RXACTIVE (1 << 0)
-#define LPE_STATUS_TXACTIVE (1 << 1)
-#define LPE_RXDESC 0x108
-#define LPE_RXSTATUS 0x10c
-#define LPE_RXDESC_NUMBER 0x110
-#define LPE_RXDESC_PROD 0x114
-#define LPE_RXDESC_CONS 0x118
-#define LPE_TXDESC 0x11c
-#define LPE_TXSTATUS 0x120
-#define LPE_TXDESC_NUMBER 0x124
-#define LPE_TXDESC_PROD 0x128
-#define LPE_TXDESC_CONS 0x12c
-#define LPE_TSV0 0x158
-#define LPE_TSV1 0x15c
-#define LPE_RSV 0x160
-#define LPE_FLOWCONTROL_COUNTER 0x170
-#define LPE_FLOWCONTROL_STATUS 0x174
-#define LPE_RXFILTER_CTRL 0x200
-#define LPE_RXFILTER_UNICAST (1 << 0)
-#define LPE_RXFILTER_BROADCAST (1 << 1)
-#define LPE_RXFILTER_MULTICAST (1 << 2)
-#define LPE_RXFILTER_UNIHASH (1 << 3)
-#define LPE_RXFILTER_MULTIHASH (1 << 4)
-#define LPE_RXFILTER_PERFECT (1 << 5)
-#define LPE_RXFILTER_WOL (1 << 12)
-#define LPE_RXFILTER_FILTWOL (1 << 13)
-#define LPE_RXFILTER_WOL_STATUS 0x204
-#define LPE_RXFILTER_WOL_CLEAR 0x208
-#define LPE_HASHFILTER_L 0x210
-#define LPE_HASHFILTER_H 0x214
-#define LPE_INTSTATUS 0xfe0
-#define LPE_INTENABLE 0xfe4
-#define LPE_INTCLEAR 0xfe8
-#define LPE_INTSET 0xfec
-#define LPE_INT_RXOVERRUN (1 << 0)
-#define LPE_INT_RXERROR (1 << 1)
-#define LPE_INT_RXFINISH (1 << 2)
-#define LPE_INT_RXDONE (1 << 3)
-#define LPE_INT_TXUNDERRUN (1 << 4)
-#define LPE_INT_TXERROR (1 << 5)
-#define LPE_INT_TXFINISH (1 << 6)
-#define LPE_INT_TXDONE (1 << 7)
-#define LPE_INT_SOFTINT (1 << 12)
-#define LPE_INTWAKEUPINT (1 << 13)
-#define LPE_POWERDOWN 0xff4
-
-#define LPE_DESC_ALIGN 8
-#define LPE_TXDESC_NUM 128
-#define LPE_RXDESC_NUM 128
-#define LPE_TXDESC_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwdesc))
-#define LPE_RXDESC_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwdesc))
-#define LPE_TXSTATUS_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwstatus))
-#define LPE_RXSTATUS_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwstatus))
-#define LPE_MAXFRAGS 8
-
-struct lpe_hwdesc {
- uint32_t lhr_data;
- uint32_t lhr_control;
-};
-
-struct lpe_hwstatus {
- uint32_t lhs_info;
- uint32_t lhs_crc;
-};
-
-#define LPE_INC(x, y) (x) = ((x) == ((y)-1)) ? 0 : (x)+1
-
-/* These are valid for both Rx and Tx descriptors */
-#define LPE_HWDESC_SIZE_MASK (1 << 10)
-#define LPE_HWDESC_INTERRUPT (1U << 31)
-
-/* These are valid for Tx descriptors */
-#define LPE_HWDESC_LAST (1 << 30)
-#define LPE_HWDESC_CRC (1 << 29)
-#define LPE_HWDESC_PAD (1 << 28)
-#define LPE_HWDESC_HUGE (1 << 27)
-#define LPE_HWDESC_OVERRIDE (1 << 26)
-
-/* These are valid for Tx status descriptors */
-#define LPE_HWDESC_COLLISIONS(_n) (((_n) >> 21) & 0x7)
-#define LPE_HWDESC_DEFER (1 << 25)
-#define LPE_HWDESC_EXCDEFER (1 << 26)
-#define LPE_HWDESC_EXCCOLL (1 << 27)
-#define LPE_HWDESC_LATECOLL (1 << 28)
-#define LPE_HWDESC_UNDERRUN (1 << 29)
-#define LPE_HWDESC_TXNODESCR (1 << 30)
-#define LPE_HWDESC_ERROR (1U << 31)
-
-/* These are valid for Rx status descriptors */
-#define LPE_HWDESC_CONTROL (1 << 18)
-#define LPE_HWDESC_VLAN (1 << 19)
-#define LPE_HWDESC_FAILFILTER (1 << 20)
-#define LPE_HWDESC_MULTICAST (1 << 21)
-#define LPE_HWDESC_BROADCAST (1 << 22)
-#define LPE_HWDESC_CRCERROR (1 << 23)
-#define LPE_HWDESC_SYMBOLERROR (1 << 24)
-#define LPE_HWDESC_LENGTHERROR (1 << 25)
-#define LPE_HWDESC_RANGEERROR (1 << 26)
-#define LPE_HWDESC_ALIGNERROR (1 << 27)
-#define LPE_HWDESC_OVERRUN (1 << 28)
-#define LPE_HWDESC_RXNODESCR (1 << 29)
-#define LPE_HWDESC_LASTFLAG (1 << 30)
-#define LPE_HWDESC_ERROR (1U << 31)
-
-
-#endif /* _ARM_LPC_IF_LPEREG_H */
diff --git a/rtemsbsd/sys/arm64/xilinx/versal_slcr.c b/rtemsbsd/sys/arm64/xilinx/versal_slcr.c
index 74ebde91..fea3abab 100644
--- a/rtemsbsd/sys/arm64/xilinx/versal_slcr.c
+++ b/rtemsbsd/sys/arm64/xilinx/versal_slcr.c
@@ -80,8 +80,9 @@ cgem_set_ref_clk(int unit, int frequency)
{
struct versal_slcr_softc *sc = versal_slcr_softc_p;
int div, last_error = 0;
- uint64_t clk_ctrl, pll_ctrl;
+ uint64_t clk_ctrl, pll_ctrl, to_xpd_ctrl;
uint32_t clk_ctrl_val, pll_ctrl_val, pll_freq, pll_reset, pll_bypass;
+ uint32_t clk_src_sel, to_xpd_ctrl_val, to_xpd_div, to_xpd_freq;
if (!sc)
return (-1);
@@ -126,15 +127,36 @@ cgem_set_ref_clk(int unit, int frequency)
}
/* Apply divider */
- pll_freq >>= (pll_ctrl_val & VERSAL_SLCR_PLL_CTRL_DIV_MASK) >> VERSAL_SLCR_PLL_CTRL_DIV_SHIFT;
+ pll_freq >>= (pll_ctrl_val & VERSAL_SLCR_PLL_CTRL_DIV_MASK) >> VERSAL_SLCR_PLL_CTRL_DIV_SHIFT;
+
+ /* Check if routed through {X}PLL_TO_XPD_CLK to GEM{unit}_REF_CLK and adjust */
+ clk_src_sel = (clk_ctrl_val & VERSAL_SLCR_GEM_CLK_CTRL_SRCSEL_MASK);
+ to_xpd_ctrl = 0;
+ if (clk_src_sel == VERSAL_SLCR_GEM_CLK_CTRL_SRCSEL_P_PLL) {
+ to_xpd_ctrl = VERSAL_SLCR_PPLL_TO_XPD_CTRL;
+ } else if (clk_src_sel == VERSAL_SLCR_GEM_CLK_CTRL_SRCSEL_N_PLL) {
+ to_xpd_ctrl = VERSAL_SLCR_NPLL_TO_XPD_CTRL;
+ }
+
+ if (to_xpd_ctrl != 0) {
+ to_xpd_ctrl_val = RD4(sc, to_xpd_ctrl);
+ to_xpd_div = (to_xpd_ctrl_val & VERSAL_SLCR_XPD_CLK_CTRL_DIVISOR_MASK);
+ to_xpd_div = to_xpd_div >> VERSAL_SLCR_XPD_CTRL_DIV_SHIFT;
+ if (to_xpd_div == 0) {
+ to_xpd_div = 1;
+ }
+ to_xpd_freq = pll_freq / to_xpd_div;
+ } else {
+ to_xpd_freq = pll_freq;
+ }
/* Find suitable divisor. Linear search, not the fastest method but hey.
*/
for (div = 1; div <= VERSAL_SLCR_GEM_CLK_CTRL_DIVISOR_MAX; div++) {
- int div_freq = pll_freq / div;
+ int div_freq = to_xpd_freq / div;
int error = abs(frequency - div_freq);
if (error >= last_error && last_error != 0) {
- div--;
+ div--;
break;
}
last_error = error;
diff --git a/rtemsbsd/sys/arm64/xilinx/versal_slcr.h b/rtemsbsd/sys/arm64/xilinx/versal_slcr.h
index e1c967ac..121c1e0a 100644
--- a/rtemsbsd/sys/arm64/xilinx/versal_slcr.h
+++ b/rtemsbsd/sys/arm64/xilinx/versal_slcr.h
@@ -78,6 +78,12 @@
#define VERSAL_SLCR_GEM_CLK_CTRL_SRCSEL_R_PLL (1<<0)
#define VERSAL_SLCR_GEM_CLK_CTRL_SRCSEL_N_PLL (3<<0)
+#define VERSAL_SLCR_PPLL_TO_XPD_CTRL (VERSAL_SLCR_CRF_OFFSET + 0x100)
+#define VERSAL_SLCR_NPLL_TO_XPD_CTRL (VERSAL_SLCR_CRF_OFFSET + 0x104)
+#define VERSAL_SLCR_XPD_CLK_CTRL_DIVISOR_MAX 0x3ff
+#define VERSAL_SLCR_XPD_CLK_CTRL_DIVISOR_MASK (VERSAL_SLCR_XPD_CLK_CTRL_DIVISOR_MAX<<8)
+#define VERSAL_SLCR_XPD_CTRL_DIV_SHIFT 8
+
#define VERSAL_DEFAULT_PS_CLK_FREQUENCY 33333333
#ifdef _KERNEL
diff --git a/rtemsbsd/sys/dev/atsam/if_atsam.c b/rtemsbsd/sys/dev/atsam/if_atsam.c
index ff8219f4..21a28fcd 100644
--- a/rtemsbsd/sys/dev/atsam/if_atsam.c
+++ b/rtemsbsd/sys/dev/atsam/if_atsam.c
@@ -42,6 +42,7 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mbuf.h>
+#include <sys/sbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/kernel.h>
@@ -49,7 +50,9 @@
#include <sys/bus.h>
#include <sys/sysctl.h>
+#include <net/bpf.h>
#include <net/if.h>
+#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_media.h>
@@ -66,6 +69,7 @@
#include <rtems/bsd/local/miibus_if.h>
#include <rtems/bsd/if_atsam.h>
+#include <rtems/bsd/bsd.h>
/*
* Number of interfaces supported by the driver
@@ -90,46 +94,40 @@
/** The runtime pin configure list for GMAC */
#define BOARD_GMAC_RUN_PINS BOARD_GMAC_PINS
-/** Multicast Enable */
-#define GMAC_MC_ENABLE (1u << 6)
-#define HASH_INDEX_AMOUNT 6
-#define HASH_ELEMENTS_PER_INDEX 8
-#define MAC_ADDR_MASK 0x0000FFFFFFFFFFFF
-#define MAC_IDX_MASK (1u << 0)
-
-/** Promiscuous Mode Enable */
-#define GMAC_PROM_ENABLE (1u << 4)
-
/** RX Defines */
#define GMAC_RX_BUFFER_SIZE 1536
#define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC
-#define GMAC_RX_SET_OFFSET (1u << 15)
-#define GMAC_RX_SET_USED_WRAP ((1u << 1) | (1u << 0))
-#define GMAC_RX_SET_WRAP (1u << 1)
-#define GMAC_RX_SET_USED (1u << 0)
-/** TX Defines */
-#define GMAC_TX_SET_EOF (1u << 15)
-#define GMAC_TX_SET_WRAP (1u << 30)
-#define GMAC_TX_SET_USED (1u << 31)
#define GMAC_DESCRIPTOR_ALIGNMENT 8
/** Events */
#define ATSAMV7_ETH_RX_EVENT_INTERRUPT RTEMS_EVENT_1
-#define ATSAMV7_ETH_TX_EVENT_INTERRUPT RTEMS_EVENT_2
-#define ATSAMV7_ETH_START_TRANSMIT_EVENT RTEMS_EVENT_3
-
-#define ATSAMV7_ETH_RX_DATA_OFFSET 2
-
-#define WATCHDOG_TIMEOUT 5
/* FIXME: Make these configurable */
#define MDIO_RETRIES 10
#define MDIO_PHY MII_PHY_ANY
-#define RXBUF_COUNT 8
-#define TXBUF_COUNT 64
#define IGNORE_RX_ERR false
+#define RX_INTERRUPTS (GMAC_ISR_RCOMP | GMAC_ISR_RXUBR | GMAC_ISR_ROVR)
+
+#define RX_DESC_LOG2 3
+#define RX_DESC_COUNT (1U << RX_DESC_LOG2)
+#define RX_DESC_WRAP(idx) \
+ ((((idx) + 1) & RX_DESC_COUNT) >> (RX_DESC_LOG2 - 1))
+RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 1) ==
+ GMAC_RX_WRAP_BIT, rx_desc_wrap);
+RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 2) ==
+ 0, rx_desc_no_wrap);
+
+#define TX_DESC_LOG2 6
+#define TX_DESC_COUNT (1U << TX_DESC_LOG2)
+#define TX_DESC_WRAP(idx) \
+ ((((idx) + 1) & TX_DESC_COUNT) << (30 - TX_DESC_LOG2))
+RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 1) ==
+ GMAC_TX_WRAP_BIT, tx_desc_wrap);
+RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 2) ==
+ 0, tx_desc_no_wrap);
+
/** The PINs for GMAC */
static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS };
@@ -139,11 +137,13 @@ typedef struct if_atsam_gmac {
uint32_t retries;
} if_atsam_gmac;
-typedef struct ring_buffer {
- unsigned tx_bd_used;
- unsigned tx_bd_free;
- size_t length;
-} ring_buffer;
+struct if_atsam_tx_bds {
+ volatile sGmacTxDescriptor bds[TX_DESC_COUNT];
+};
+
+struct if_atsam_rx_bds {
+ volatile sGmacRxDescriptor bds[RX_DESC_COUNT];
+};
/*
* Per-device data
@@ -156,17 +156,16 @@ typedef struct if_atsam_softc {
struct ifnet *ifp;
struct mtx mtx;
if_atsam_gmac Gmac_inst;
+ size_t tx_idx_head;
+ size_t tx_idx_tail;
+ struct if_atsam_tx_bds *tx;
+ struct mbuf *tx_mbufs[TX_DESC_COUNT];
+ size_t rx_idx_head;
+ struct if_atsam_rx_bds *rx;
+ struct mbuf *rx_mbufs[RX_DESC_COUNT];
uint8_t GMacAddress[6];
rtems_id rx_daemon_tid;
- rtems_id tx_daemon_tid;
rtems_vector_number interrupt_number;
- struct mbuf **rx_mbuf;
- struct mbuf **tx_mbuf;
- volatile sGmacTxDescriptor *tx_bd_base;
- size_t rx_bd_fill_idx;
- size_t amount_rx_buf;
- size_t amount_tx_buf;
- ring_buffer tx_ring;
struct callout tick_ch;
/*
@@ -190,13 +189,12 @@ typedef struct if_atsam_softc {
struct if_atsam_stats {
/* Software */
uint32_t rx_overrun_errors;
+ uint32_t rx_used_bit_reads;
uint32_t rx_interrupts;
- uint32_t tx_complete_int;
uint32_t tx_tur_errors;
uint32_t tx_rlex_errors;
uint32_t tx_tfc_errors;
uint32_t tx_hresp_errors;
- uint32_t tx_interrupts;
/* Hardware */
uint64_t octets_transm;
@@ -243,6 +241,8 @@ typedef struct if_atsam_softc {
uint32_t tcp_checksum_errors;
uint32_t udp_checksum_errors;
} stats;
+
+ int if_flags;
} if_atsam_softc;
static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc);
@@ -251,27 +251,6 @@ static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc);
#define IF_ATSAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
-static void if_atsam_event_send(rtems_id task, rtems_event_set event)
-{
- rtems_event_send(task, event);
-}
-
-
-static void if_atsam_event_receive(if_atsam_softc *sc, rtems_event_set in)
-{
- rtems_event_set out;
-
- IF_ATSAM_UNLOCK(sc);
- rtems_event_receive(
- in,
- RTEMS_EVENT_ANY | RTEMS_WAIT,
- RTEMS_NO_TIMEOUT,
- &out
- );
- IF_ATSAM_LOCK(sc);
-}
-
-
static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp)
{
struct mbuf *m;
@@ -356,11 +335,10 @@ if_atsam_miibus_readreg(device_t dev, int phy, int reg)
static int
if_atsam_miibus_writereg(device_t dev, int phy, int reg, int data)
{
- uint8_t err;
if_atsam_softc *sc = device_get_softc(dev);
IF_ATSAM_LOCK(sc);
- err = if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw,
+ (void)if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw,
(uint8_t)phy, (uint8_t)reg, data, sc->Gmac_inst.retries);
IF_ATSAM_UNLOCK(sc);
@@ -403,63 +381,53 @@ if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck,
static void if_atsam_interrupt_handler(void *arg)
{
if_atsam_softc *sc = (if_atsam_softc *)arg;
- uint32_t irq_status_val;
- rtems_event_set rx_event = 0;
- rtems_event_set tx_event = 0;
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ uint32_t is;
/* Get interrupt status */
- irq_status_val = GMAC_GetItStatus(pHw, 0);
+ is = pHw->GMAC_ISR;
- /* Check receive interrupts */
- if ((irq_status_val & GMAC_IER_ROVR) != 0) {
- ++sc->stats.rx_overrun_errors;
- rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
- }
- if ((irq_status_val & GMAC_IER_RCOMP) != 0) {
- rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
+ if (__predict_false((is & GMAC_TX_ERR_BIT) != 0)) {
+ if ((is & GMAC_IER_TUR) != 0) {
+ ++sc->stats.tx_tur_errors;
+ }
+ if ((is & GMAC_IER_RLEX) != 0) {
+ ++sc->stats.tx_rlex_errors;
+ }
+ if ((is & GMAC_IER_TFC) != 0) {
+ ++sc->stats.tx_tfc_errors;
+ }
+ if ((is & GMAC_IER_HRESP) != 0) {
+ ++sc->stats.tx_hresp_errors;
+ }
}
- /* Send events to receive task and switch off rx interrupts */
- if (rx_event != 0) {
+
+ /* Check receive interrupts */
+ if (__predict_true((is & RX_INTERRUPTS) != 0)) {
+ if (__predict_false((is & GMAC_ISR_RXUBR) != 0)) {
+ ++sc->stats.rx_used_bit_reads;
+ }
+
+ if (__predict_false((is & GMAC_ISR_ROVR) != 0)) {
+ ++sc->stats.rx_overrun_errors;
+ }
+
++sc->stats.rx_interrupts;
- /* Erase the interrupts for RX completion and errors */
- GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
- (void)if_atsam_event_send(sc->rx_daemon_tid, rx_event);
- }
- if ((irq_status_val & GMAC_IER_TUR) != 0) {
- ++sc->stats.tx_tur_errors;
- tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
- }
- if ((irq_status_val & GMAC_IER_RLEX) != 0) {
- ++sc->stats.tx_rlex_errors;
- tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
- }
- if ((irq_status_val & GMAC_IER_TFC) != 0) {
- ++sc->stats.tx_tfc_errors;
- tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
- }
- if ((irq_status_val & GMAC_IER_HRESP) != 0) {
- ++sc->stats.tx_hresp_errors;
- tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
- }
- if ((irq_status_val & GMAC_IER_TCOMP) != 0) {
- ++sc->stats.tx_complete_int;
- tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
- }
- /* Send events to transmit task and switch off tx interrupts */
- if (tx_event != 0) {
- ++sc->stats.tx_interrupts;
- /* Erase the interrupts for TX completion and errors */
- GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0);
- (void)if_atsam_event_send(sc->tx_daemon_tid, tx_event);
+
+ /* Disable RX interrupts */
+ pHw->GMAC_IDR = RX_INTERRUPTS;
+
+ (void)rtems_event_send(sc->rx_daemon_tid,
+ ATSAMV7_ETH_RX_EVENT_INTERRUPT);
}
}
-static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc)
+static void
+if_atsam_rx_update_mbuf(struct mbuf *m, uint32_t status)
{
int frame_len;
- frame_len = (int) (buffer_desc->status.bm.len);
+ frame_len = (int)(status & GMAC_LENGTH_FRAME);
m->m_data = mtod(m, char*)+ETHER_ALIGN;
m->m_len = frame_len;
@@ -467,7 +435,7 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc)
/* check checksum offload result */
m->m_pkthdr.csum_flags = 0;
- switch (buffer_desc->status.bm.typeIDMatchOrCksumResult) {
+ switch ((status >> 22) & 0x3) {
case GMAC_RXDESC_ST_CKSUM_RESULT_IP_CHECKED:
m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID;
m->m_pkthdr.csum_data = 0xffff;
@@ -481,21 +449,17 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc)
}
}
-/*
- * Receive daemon
- */
-static void if_atsam_rx_daemon(void *arg)
+static void
+if_atsam_rx_daemon(rtems_task_argument arg)
{
if_atsam_softc *sc = (if_atsam_softc *)arg;
struct ifnet *ifp = sc->ifp;
- rtems_event_set events = 0;
- void *rx_bd_base;
- struct mbuf *m;
- struct mbuf *n;
- volatile sGmacRxDescriptor *buffer_desc;
- uint32_t tmp_rx_bd_address;
- size_t i;
+ volatile sGmacRxDescriptor *base;
+ struct if_atsam_rx_bds *rx;
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ size_t idx;
+ struct mbuf **mbufs;
+ struct mbuf *m;
IF_ATSAM_LOCK(sc);
@@ -506,47 +470,37 @@ static void if_atsam_rx_daemon(void *arg)
}
/* Allocate memory space for priority queue descriptor list */
- rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor),
+ base = rtems_cache_coherent_allocate(sizeof(*base),
GMAC_DESCRIPTOR_ALIGNMENT, 0);
- assert(rx_bd_base != NULL);
+ assert(base != NULL);
- buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
- buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP;
- buffer_desc->status.val = 0;
+ base->addr.val = GMAC_RX_OWNERSHIP_BIT | GMAC_RX_WRAP_BIT;
+ base->status.val = 0;
- GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1);
- GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2);
+ GMAC_SetRxQueue(pHw, (uint32_t)base, 1);
+ GMAC_SetRxQueue(pHw, (uint32_t)base, 2);
/* Allocate memory space for buffer descriptor list */
- rx_bd_base = rtems_cache_coherent_allocate(
- sc->amount_rx_buf * sizeof(sGmacRxDescriptor),
+ rx = rtems_cache_coherent_allocate(sizeof(*rx),
GMAC_DESCRIPTOR_ALIGNMENT, 0);
- assert(rx_bd_base != NULL);
- buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
+ assert(rx != NULL);
+ sc->rx = rx;
+ mbufs = &sc->rx_mbufs[0];
/* Create descriptor list and mark as empty */
- for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < sc->amount_rx_buf;
- ++sc->rx_bd_fill_idx) {
+ for (idx = 0; idx < RX_DESC_COUNT; ++idx) {
m = if_atsam_new_mbuf(ifp);
assert(m != NULL);
- sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
- buffer_desc->addr.val = ((uint32_t)m->m_data) &
- GMAC_RX_BUF_DESC_ADDR_MASK;
- buffer_desc->status.val = 0;
- if (sc->rx_bd_fill_idx == (sc->amount_rx_buf - 1)) {
- buffer_desc->addr.bm.bWrap = 1;
- } else {
- buffer_desc++;
- }
+ mbufs[idx] = m;
+ rx->bds[idx].addr.val = mtod(m, uint32_t) | RX_DESC_WRAP(idx);
}
- buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
/* Set 2 Byte Receive Buffer Offset */
- pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET;
+ pHw->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO(2);
/* Write Buffer Queue Base Address Register */
GMAC_ReceiveEnable(pHw, 0);
- GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0);
+ GMAC_SetRxQueue(pHw, (uint32_t)&rx->bds[0], 0);
/* Set address for address matching */
GMAC_SetAddress(pHw, 0, sc->GMacAddress);
@@ -554,306 +508,230 @@ static void if_atsam_rx_daemon(void *arg)
/* Enable Receiving of data */
GMAC_ReceiveEnable(pHw, 1);
- /* Setup the interrupts for RX completion and errors */
- GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
+ IF_ATSAM_UNLOCK(sc);
- sc->rx_bd_fill_idx = 0;
+ idx = 0;
while (true) {
- /* Wait for events */
- if_atsam_event_receive(sc, ATSAMV7_ETH_RX_EVENT_INTERRUPT);
+ rtems_event_set out;
- /*
- * Check for all packets with a set ownership bit
- */
- while (buffer_desc->addr.bm.bOwnership == 1) {
- if (buffer_desc->status.bm.bEof == 1) {
- m = sc->rx_mbuf[sc->rx_bd_fill_idx];
+ sc->rx_idx_head = idx;
- /* New mbuf for desc */
- n = if_atsam_new_mbuf(ifp);
- if (n != NULL) {
- rx_update_mbuf(m, buffer_desc);
+ /* Enable RX interrupts */
+ pHw->GMAC_IER = RX_INTERRUPTS;
- IF_ATSAM_UNLOCK(sc);
- sc->ifp->if_input(ifp, m);
- IF_ATSAM_LOCK(sc);
- m = n;
- } else {
- (void)if_atsam_event_send(
- sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT);
- }
- sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
- tmp_rx_bd_address = (uint32_t)m->m_data &
- GMAC_RX_BUF_DESC_ADDR_MASK;
-
- /* Switch pointer to next buffer descriptor */
- if (sc->rx_bd_fill_idx ==
- (sc->amount_rx_buf - 1)) {
- tmp_rx_bd_address |= GMAC_RX_SET_WRAP;
- sc->rx_bd_fill_idx = 0;
- } else {
- ++sc->rx_bd_fill_idx;
- }
+ (void) rtems_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT,
+ RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out);
- /*
- * Give ownership to GMAC for further processing
- */
- tmp_rx_bd_address &= ~GMAC_RX_SET_USED;
- _ARM_Data_synchronization_barrier();
- buffer_desc->addr.val = tmp_rx_bd_address;
+ while (true) {
+ uint32_t addr;
+ uint32_t status;
- buffer_desc = (sGmacRxDescriptor *)rx_bd_base
- + sc->rx_bd_fill_idx;
+ addr = rx->bds[idx].addr.val;
+ if ((addr & GMAC_RX_OWNERSHIP_BIT) == 0) {
+ break;
}
- }
- /* Setup the interrupts for RX completion and errors */
- GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
- }
-}
-/*
- * Update of current transmit buffer position.
- */
-static void if_atsam_tx_bd_pos_update(size_t *pos, size_t amount_tx_buf)
-{
- *pos = (*pos + 1) % amount_tx_buf;
-}
+ status = rx->bds[idx].status.val;
+ m = mbufs[idx];
-/*
- * Is RingBuffer empty
- */
-static bool if_atsam_ring_buffer_empty(ring_buffer *ring_buffer)
-{
- return (ring_buffer->tx_bd_used == ring_buffer->tx_bd_free);
-}
+ if (__predict_true((status & GMAC_RX_EOF_BIT) != 0)) {
+ struct mbuf *n;
-/*
- * Is RingBuffer full
- */
-static bool if_atsam_ring_buffer_full(ring_buffer *ring_buffer)
-{
- size_t tx_bd_used_next = ring_buffer->tx_bd_used;
+ n = if_atsam_new_mbuf(ifp);
+ if (n != NULL) {
+ if_atsam_rx_update_mbuf(m, status);
+ (*ifp->if_input)(ifp, m);
+ m = n;
+ }
+ } else {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ }
- if_atsam_tx_bd_pos_update(&tx_bd_used_next, ring_buffer->length);
- return (tx_bd_used_next == ring_buffer->tx_bd_free);
-}
+ mbufs[idx] = m;
+ rx->bds[idx].addr.val = mtod(m, uint32_t) |
+ RX_DESC_WRAP(idx);
-/*
- * Cleanup transmit file descriptors by freeing mbufs which are not needed any
- * longer due to correct transmission.
- */
-static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc)
-{
- struct mbuf *m;
- volatile sGmacTxDescriptor *cur;
- bool eof_needed = false;
-
- while (!if_atsam_ring_buffer_empty(&sc->tx_ring)){
- cur = sc->tx_bd_base + sc->tx_ring.tx_bd_free;
- if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) {
- eof_needed = true;
- cur->status.val |= GMAC_TX_SET_USED;
- m = sc->tx_mbuf[sc->tx_ring.tx_bd_free];
- m_free(m);
- sc->tx_mbuf[sc->tx_ring.tx_bd_free] = 0;
- if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_free,
- sc->tx_ring.length);
- if (cur->status.bm.bLastBuffer) {
- eof_needed = false;
- }
- } else {
- break;
+ idx = (idx + 1) % RX_DESC_COUNT;
}
}
}
-/*
- * Prepare Ethernet frame to start transmission.
- */
-static bool if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m)
+static void
+if_atsam_tx_reclaim(struct if_atsam_softc *sc, struct ifnet *ifp)
{
- volatile sGmacTxDescriptor *cur;
- volatile sGmacTxDescriptor *start_packet_tx_bd = 0;
- int pos = 0;
- uint32_t tmp_val = 0;
- Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
- bool success;
- int csum_flags = m->m_pkthdr.csum_flags;
+ uint32_t head_idx;
+ uint32_t tail_idx;
+ volatile sGmacTxDescriptor *base;
- if_atsam_tx_bd_cleanup(sc);
- /* Wait for interrupt in case no buffer descriptors are available */
- /* Wait for events */
- while (true) {
- if (if_atsam_ring_buffer_full(&sc->tx_ring)) {
- /* Setup the interrupts for TX completion and errors */
- GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0);
- success = false;
+ head_idx = sc->tx_idx_head;
+ tail_idx = sc->tx_idx_tail;
+ base = &sc->tx->bds[0];
+
+ while (head_idx != tail_idx) {
+ uint32_t status;
+ ift_counter cnt;
+ struct mbuf *m;
+
+ status = base[tail_idx].status.val;
+
+ if ((status & GMAC_TX_USED_BIT) == 0) {
break;
}
- /*
- * Get current mbuf for data fill
- */
- cur = &sc->tx_bd_base[sc->tx_ring.tx_bd_used];
- /* Set the transfer data */
- if (m->m_len) {
- uintptr_t cache_adjustment = mtod(m, uintptr_t) % 32;
-
- rtems_cache_flush_multiple_data_lines(
- mtod(m, const char *) - cache_adjustment,
- (size_t)(m->m_len + cache_adjustment));
-
- cur->addr = mtod(m, uint32_t);
- tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED;
- if (sc->tx_ring.tx_bd_used == (sc->tx_ring.length - 1)) {
- tmp_val |= GMAC_TX_SET_WRAP;
- }
- if (pos == 0) {
- start_packet_tx_bd = cur;
- }
- sc->tx_mbuf[sc->tx_ring.tx_bd_used] = m;
- m = m->m_next;
- if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_used,
- sc->tx_ring.length);
+ if (__predict_true((status & GMAC_TX_ERR_BITS) == 0)) {
+ cnt = IFCOUNTER_OPACKETS;
} else {
- /* Discard empty mbufs */
- m = m_free(m);
+ cnt = IFCOUNTER_OERRORS;
}
- /*
- * Send out the buffer once the complete mbuf_chain has been
- * processed
- */
- if (m == NULL) {
- tmp_val |= GMAC_TX_SET_EOF;
- tmp_val &= ~GMAC_TX_SET_USED;
- if ((csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP |
- CSUM_TCP_IPV6 | CSUM_UDP_IPV6)) != 0) {
- start_packet_tx_bd->status.bm.bNoCRC = 0;
- } else {
- start_packet_tx_bd->status.bm.bNoCRC = 1;
- }
- _ARM_Data_synchronization_barrier();
- cur->status.val = tmp_val;
- start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED;
- _ARM_Data_synchronization_barrier();
- GMAC_TransmissionStart(pHw);
- success = true;
- break;
- } else {
- if (pos > 0) {
- tmp_val &= ~GMAC_TX_SET_USED;
+ while ((m = sc->tx_mbufs[tail_idx]) == NULL ) {
+ base[tail_idx].status.val = status | GMAC_TX_USED_BIT;
+ tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
+ status = base[tail_idx].status.val;
+
+ if (__predict_false((status & GMAC_TX_ERR_BITS) != 0)) {
+ cnt = IFCOUNTER_OERRORS;
}
- pos++;
- cur->status.val = tmp_val;
}
+
+ base[tail_idx].status.val = status | GMAC_TX_USED_BIT;
+ if_inc_counter(ifp, cnt, 1);
+ sc->tx_mbufs[tail_idx] = NULL;
+ m_freem(m);
+
+ tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
}
- return success;
-}
+ sc->tx_idx_tail = tail_idx;
+}
-/*
- * Transmit daemon
- */
-static void if_atsam_tx_daemon(void *arg)
+static void
+if_atsam_cache_flush(uintptr_t begin, uintptr_t size)
{
- if_atsam_softc *sc = (if_atsam_softc *)arg;
- rtems_event_set events = 0;
- sGmacTxDescriptor *buffer_desc;
- int bd_number;
- void *tx_bd_base;
- struct mbuf *m;
- bool success;
+ uintptr_t end;
+ uintptr_t mask;
+
+ /* Align begin and end of the data to a cache line */
+ end = begin + size;
+ mask = CPU_CACHE_LINE_BYTES - 1;
+ begin &= ~mask;
+ end = (end + mask) & ~mask;
+ rtems_cache_flush_multiple_data_lines((void *)begin, end - begin);
+}
- IF_ATSAM_LOCK(sc);
+static int
+if_atsam_tx_enqueue(struct if_atsam_softc *sc, struct ifnet *ifp, struct mbuf *m)
+{
+ size_t head_idx;
+ size_t tail_idx;
+ size_t capacity;
+ size_t idx;
+ volatile sGmacTxDescriptor *base;
+ volatile sGmacTxDescriptor *desc;
+ size_t bufs;
+ uint32_t status;
+ struct mbuf *n;
- Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
- struct ifnet *ifp = sc->ifp;
+ head_idx = sc->tx_idx_head;
+ tail_idx = sc->tx_idx_tail;
+ capacity = (tail_idx - head_idx - 1) % TX_DESC_COUNT;
- GMAC_TransmitEnable(pHw, 0);
+ idx = head_idx;
+ base = &sc->tx->bds[0];
+ bufs = 0;
+ n = m;
- /* Allocate memory space for priority queue descriptor list */
- tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor),
- GMAC_DESCRIPTOR_ALIGNMENT, 0);
- assert(tx_bd_base != NULL);
+ do {
+ uint32_t size;
- buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
- buffer_desc->addr = 0;
- buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP;
+ desc = &base[idx];
- GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1);
- GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2);
+ size = (uint32_t)n->m_len;
+ if (__predict_true(size > 0)) {
+ uintptr_t begin;
- /* Allocate memory space for buffer descriptor list */
- tx_bd_base = rtems_cache_coherent_allocate(
- sc->amount_tx_buf * sizeof(sGmacTxDescriptor),
- GMAC_DESCRIPTOR_ALIGNMENT, 0);
- assert(tx_bd_base != NULL);
- buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
+ ++bufs;
+ if (__predict_false(bufs > capacity)) {
+ return (ENOBUFS);
+ }
- /* Create descriptor list and mark as empty */
- for (bd_number = 0; bd_number < sc->amount_tx_buf; bd_number++) {
- buffer_desc->addr = 0;
- buffer_desc->status.val = GMAC_TX_SET_USED;
- if (bd_number == (sc->amount_tx_buf - 1)) {
- buffer_desc->status.bm.bWrap = 1;
- } else {
- buffer_desc++;
+ begin = mtod(n, uintptr_t);
+ desc->addr = (uint32_t)begin;
+ status = GMAC_TX_USED_BIT | TX_DESC_WRAP(idx) | size;
+ desc->status.val = status;
+ if_atsam_cache_flush(begin, size);
+ idx = (idx + 1) % TX_DESC_COUNT;
}
+
+ n = n->m_next;
+ } while (n != NULL);
+
+ sc->tx_idx_head = idx;
+
+ idx = (idx - 1) % TX_DESC_COUNT;
+ desc = &base[idx];
+ sc->tx_mbufs[idx] = m;
+ status = GMAC_TX_LAST_BUFFER_BIT;
+
+ while (idx != head_idx) {
+ desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) |
+ status;
+ status = 0;
+
+ idx = (idx - 1) % TX_DESC_COUNT;
+ desc = &base[idx];
}
- buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
- /* Write Buffer Queue Base Address Register */
- GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0);
+ desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | status;
+ _ARM_Data_synchronization_barrier();
+ sc->Gmac_inst.gGmacd.pHw->GMAC_NCR |= GMAC_NCR_TSTART;
+ ETHER_BPF_MTAP(ifp, m);
+ return (0);
+}
- /* Enable Transmission of data */
- GMAC_TransmitEnable(pHw, 1);
+static int
+if_atsam_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct if_atsam_softc *sc;
+ int error;
- /* Set variables in context */
- sc->tx_bd_base = tx_bd_base;
+ if (__predict_false((m->m_flags & M_VLANTAG) != 0)) {
+ struct mbuf *n;
- while (true) {
- /* Wait for events */
- if_atsam_event_receive(sc,
- ATSAMV7_ETH_START_TRANSMIT_EVENT |
- ATSAMV7_ETH_TX_EVENT_INTERRUPT);
- //printf("TX Transmit Event received\n");
-
- /*
- * Send packets till queue is empty
- */
- while (true) {
- /*
- * Get the mbuf chain to transmit
- */
- if_atsam_tx_bd_cleanup(sc);
- IF_DEQUEUE(&ifp->if_snd, m);
- if (!m) {
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- break;
- }
- success = if_atsam_send_packet(sc, m);
- if (!success){
- break;
- }
+ n = ether_vlanencap(m, m->m_pkthdr.ether_vtag);
+ if (n == NULL) {
+ m_freem(m);
+ return (ENOBUFS);
}
+
+ m = n;
}
-}
+ sc = ifp->if_softc;
+ IF_ATSAM_LOCK(sc);
-/*
- * Send packet (caller provides header).
- */
-static void if_atsam_enet_start(struct ifnet *ifp)
-{
- if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
+ error = if_atsam_tx_enqueue(sc, ifp, m);
+ if_atsam_tx_reclaim(sc, ifp);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- if_atsam_event_send(sc->tx_daemon_tid,
- ATSAMV7_ETH_START_TRANSMIT_EVENT);
-}
+ if (__predict_false(error != 0)) {
+ struct mbuf *n;
+ n = m_defrag(m, M_NOWAIT);
+ if (n != NULL) {
+ m = n;
+ }
+
+ error = if_atsam_tx_enqueue(sc, ifp, m);
+ if (error != 0) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
+ }
+ }
+
+ IF_ATSAM_UNLOCK(sc);
+ return (error);
+}
static uint8_t if_atsam_get_gmac_linkspeed_from_media(uint32_t media_subtype)
{
@@ -975,23 +853,55 @@ if_atsam_tick(void *context)
callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc);
}
+static void
+if_atsam_setup_tx(struct if_atsam_softc *sc)
+{
+ sGmacTxDescriptor *base;
+ struct if_atsam_tx_bds *tx;
+ size_t i;
+ Gmac *pHw;
-/*
- * Sets up the hardware and chooses the interface to be used
- */
-static void if_atsam_init(void *arg)
+ pHw = sc->Gmac_inst.gGmacd.pHw;
+ GMAC_TransmitEnable(pHw, 0);
+
+ /* Allocate memory space for priority queue descriptor list */
+ base = rtems_cache_coherent_allocate(sizeof(base),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(base != NULL);
+
+ base->addr = 0;
+ base->status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT;
+
+ GMAC_SetTxQueue(pHw, (uint32_t)base, 1);
+ GMAC_SetTxQueue(pHw, (uint32_t)base, 2);
+
+ /* Allocate memory space for buffer descriptor list */
+ tx = rtems_cache_coherent_allocate(sizeof(*sc->tx),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(tx != NULL);
+
+ /* Set variables in context */
+ sc->tx = tx;
+
+ /* Create descriptor list and mark as empty */
+ for (i = 0; i < TX_DESC_COUNT; ++i) {
+ tx->bds[i].addr = 0;
+ tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i);
+ }
+
+ /* Write Buffer Queue Base Address Register */
+ GMAC_SetTxQueue(pHw, (uint32_t)&tx->bds[0], 0);
+
+ /* Enable Transmission of data */
+ GMAC_TransmitEnable(pHw, 1);
+}
+
+static void
+if_atsam_init(if_atsam_softc *sc)
{
rtems_status_code status;
-
- if_atsam_softc *sc = (if_atsam_softc *)arg;
- struct ifnet *ifp = sc->ifp;
uint32_t dmac_cfg = 0;
- uint32_t gmii_val = 0;
- if (ifp->if_flags & IFF_DRV_RUNNING) {
- return;
- }
- ifp->if_flags |= IFF_DRV_RUNNING;
sc->interrupt_number = GMAC_IRQn;
/* Enable Peripheral Clock */
@@ -1000,7 +910,6 @@ static void if_atsam_init(void *arg)
}
/* Setup interrupts */
NVIC_ClearPendingIRQ(GMAC_IRQn);
- NVIC_EnableIRQ(GMAC_IRQn);
/* Configuration of DMAC */
dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) |
@@ -1011,20 +920,17 @@ static void if_atsam_init(void *arg)
/* Enable hardware checksum offload for receive */
sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_RXCOEN;
+ /* Use Multicast Hash Filter */
+ sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN;
+ sc->Gmac_inst.gGmacd.pHw->GMAC_HRB = 0;
+ sc->Gmac_inst.gGmacd.pHw->GMAC_HRT = 0;
+
/* Shut down Transmit and Receive */
GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0);
GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0);
GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1);
- /*
- * Allocate mbuf pointers
- */
- sc->rx_mbuf = malloc(sc->amount_rx_buf * sizeof *sc->rx_mbuf,
- M_TEMP, M_NOWAIT);
- sc->tx_mbuf = malloc(sc->amount_tx_buf * sizeof *sc->tx_mbuf,
- M_TEMP, M_NOWAIT);
-
/* Install interrupt handler */
status = rtems_interrupt_handler_install(sc->interrupt_number,
"Ethernet",
@@ -1036,30 +942,143 @@ static void if_atsam_init(void *arg)
/*
* Start driver tasks
*/
- sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096,
- if_atsam_rx_daemon, sc);
- sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096,
- if_atsam_tx_daemon, sc);
+
+ status = rtems_task_create(rtems_build_name('S', 'C', 'r', 'x'),
+ rtems_bsd_get_task_priority(device_get_name(sc->dev)), 4096,
+ RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_MODES, &sc->rx_daemon_tid);
+ assert(status == RTEMS_SUCCESSFUL);
+
+ status = rtems_task_start(sc->rx_daemon_tid, if_atsam_rx_daemon,
+ (rtems_task_argument)sc);
+ assert(status == RTEMS_SUCCESSFUL);
callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc);
+ if_atsam_setup_tx(sc);
+}
+
+static int
+if_atsam_get_hash_index(const uint8_t *eaddr)
+{
+ uint64_t eaddr64;
+ int index;
+ int i;
+
+ eaddr64 = eaddr[5];
+
+ for (i = 4; i >= 0; --i) {
+ eaddr64 <<= 8;
+ eaddr64 |= eaddr[i];
+ }
+
+ index = 0;
+
+ for (i = 0; i < 6; ++i) {
+ uint64_t bits;
+ int j;
+ int hash;
+
+ bits = eaddr64 >> i;
+ hash = bits & 1;
+
+ for (j = 1; j < 8; ++j) {
+ bits >>= 6;
+ hash ^= bits & 1;
+ }
+
+ index |= hash << i;
+ }
+
+ return index;
+}
+
+static void
+if_atsam_setup_rxfilter(struct if_atsam_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ uint64_t mhash;
+ Gmac *pHw;
+
+ pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ if ((sc->ifp->if_flags & IFF_PROMISC) != 0) {
+ pHw->GMAC_NCFGR |= GMAC_NCFGR_CAF;
+ } else {
+ pHw->GMAC_NCFGR &= ~GMAC_NCFGR_CAF;
+ }
+
+ ifp = sc->ifp;
+
+ if ((ifp->if_flags & IFF_ALLMULTI))
+ mhash = 0xffffffffffffffffLLU;
+ else {
+ mhash = 0;
+ if_maddr_rlock(ifp);
+ CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ mhash |= 1LLU << if_atsam_get_hash_index(
+ LLADDR((struct sockaddr_dl *) ifma->ifma_addr));
+ }
+ if_maddr_runlock(ifp);
+ }
+
+ pHw->GMAC_HRB = (uint32_t)mhash;
+ pHw->GMAC_HRT = (uint32_t)(mhash >> 32);
+}
+
+static void
+if_atsam_start_locked(struct if_atsam_softc *sc)
+{
+ struct ifnet *ifp = sc->ifp;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ return;
+ }
+
ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ if_atsam_setup_rxfilter(sc);
+
+ /* Enable TX/RX */
+ pHw->GMAC_NCR |= GMAC_NCR_RXEN | GMAC_NCR_TXEN;
}
+static void
+if_atsam_start(void *arg)
+{
+ struct if_atsam_softc *sc = arg;
-/*
- * Stop the device
- */
-static void if_atsam_stop(struct if_atsam_softc *sc)
+ IF_ATSAM_LOCK(sc);
+ if_atsam_start_locked(sc);
+ IF_ATSAM_UNLOCK(sc);
+}
+
+static void
+if_atsam_stop_locked(struct if_atsam_softc *sc)
{
struct ifnet *ifp = sc->ifp;
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ size_t i;
- ifp->if_flags &= ~IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
- /* Disable MDIO interface and TX/RX */
+ /* Disable TX/RX */
pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN);
- pHw->GMAC_NCR &= ~GMAC_NCR_MPE;
+
+ /* Reinitialize the TX descriptors */
+
+ sc->tx_idx_head = 0;
+ sc->tx_idx_tail = 0;
+
+ for (i = 0; i < TX_DESC_COUNT; ++i) {
+ sc->tx->bds[i].addr = 0;
+ sc->tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i);
+ m_freem(sc->tx_mbufs[i]);
+ sc->tx_mbufs[i] = NULL;
+ }
}
@@ -1070,7 +1089,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc)
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
octets = pHw->GMAC_OTLO;
- octets |= pHw->GMAC_OTHI << 32;
+ octets |= (uint64_t)pHw->GMAC_OTHI << 32;
sc->stats.octets_transm += octets;
sc->stats.frames_transm += pHw->GMAC_FT;
sc->stats.broadcast_frames_transm += pHw->GMAC_BCFT;
@@ -1092,7 +1111,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc)
sc->stats.carrier_sense_errors += pHw->GMAC_CSE;
octets = pHw->GMAC_ORLO;
- octets |= pHw->GMAC_ORHI << 32;
+ octets |= (uint64_t)pHw->GMAC_ORHI << 32;
sc->stats.octets_rec += octets;
sc->stats.frames_rec += pHw->GMAC_FR;
sc->stats.broadcast_frames_rec += pHw->GMAC_BCFR;
@@ -1120,24 +1139,159 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc)
sc->stats.udp_checksum_errors += pHw->GMAC_UCE;
}
+static int
+if_atsam_stats_reset(SYSCTL_HANDLER_ARGS)
+{
+ struct if_atsam_softc *sc = arg1;
+ int value;
+ int error;
+
+ value = 0;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error != 0 || req->newptr == NULL) {
+ return (error);
+ }
+
+ if (value != 0) {
+ IF_ATSAM_LOCK(sc);
+ if_atsam_poll_hw_stats(sc);
+ memset(&sc->stats, 0, sizeof(sc->stats));
+ IF_ATSAM_UNLOCK(sc);
+ }
+
+ return (0);
+}
+
+static int
+if_atsam_sysctl_reg(SYSCTL_HANDLER_ARGS)
+{
+ u_int value;
+
+ value = *(uint32_t *)arg1;
+ return (sysctl_handle_int(oidp, &value, 0, req));
+}
+
+static int
+if_atsam_sysctl_tx_desc(SYSCTL_HANDLER_ARGS)
+{
+ struct if_atsam_softc *sc = arg1;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ struct sbuf *sb;
+ int error;
+ size_t i;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0) {
+ return (error);
+ }
+
+ sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
+ if (sb == NULL) {
+ return (ENOMEM);
+ }
+
+ sbuf_printf(sb, "\n\tHead %u\n", sc->tx_idx_head);
+ sbuf_printf(sb, "\tTail %u\n", sc->tx_idx_tail);
+ sbuf_printf(sb, "\tDMA %u\n",
+ (pHw->GMAC_TBQB - (uintptr_t)&sc->tx->bds[0]) / 8);
+
+ for (i = 0; i < TX_DESC_COUNT; ++i) {
+ sbuf_printf(sb, "\t[%2u] %08x %08x\n", i,
+ sc->tx->bds[i].status.val, sc->tx->bds[i].addr);
+ }
+
+ error = sbuf_finish(sb);
+ sbuf_delete(sb);
+ return (error);
+}
+
+static int
+if_atsam_sysctl_rx_desc(SYSCTL_HANDLER_ARGS)
+{
+ struct if_atsam_softc *sc = arg1;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ struct sbuf *sb;
+ int error;
+ size_t i;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0) {
+ return (error);
+ }
+
+ sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
+ if (sb == NULL) {
+ return (ENOMEM);
+ }
+
+ sbuf_printf(sb, "\n\tHead %u\n", sc->rx_idx_head);
+ sbuf_printf(sb, "\tDMA %u\n",
+ (pHw->GMAC_RBQB - (uintptr_t)&sc->rx->bds[0]) / 8);
+
+ for (i = 0; i < RX_DESC_COUNT; ++i) {
+ sbuf_printf(sb, "\t[%2u] %08x %08x\n", i,
+ sc->rx->bds[i].status.val, sc->rx->bds[i].addr);
+ }
+
+ error = sbuf_finish(sb);
+ sbuf_delete(sb);
+ return (error);
+}
static void
if_atsam_add_sysctls(device_t dev)
{
struct if_atsam_softc *sc = device_get_softc(dev);
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *base;
struct sysctl_oid_list *statsnode;
struct sysctl_oid_list *hwstatsnode;
struct sysctl_oid_list *child;
struct sysctl_oid *tree;
ctx = device_get_sysctl_ctx(dev);
- child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ base = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
- tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
+ tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "regs", CTLFLAG_RD,
+ NULL, "if_atsam registers");
+ child = SYSCTL_CHILDREN(tree);
+
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdesc", CTLTYPE_STRING |
+ CTLFLAG_RD, sc, 0, if_atsam_sysctl_tx_desc, "A",
+ "Transmit Descriptors");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxdesc", CTLTYPE_STRING |
+ CTLFLAG_RD, sc, 0, if_atsam_sysctl_rx_desc, "A",
+ "Receive Descriptors");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "imr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_IMR), 0,
+ if_atsam_sysctl_reg, "I", "IMR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "isr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_ISR), 0,
+ if_atsam_sysctl_reg, "I", "ISR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_RSR), 0,
+ if_atsam_sysctl_reg, "I", "RSR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tsr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_TSR), 0,
+ if_atsam_sysctl_reg, "I", "TSR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "nsr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NSR), 0,
+ if_atsam_sysctl_reg, "I", "NSR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncfgr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCFGR), 0,
+ if_atsam_sysctl_reg, "I", "NCFGR");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncr", CTLTYPE_UINT |
+ CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCR), 0,
+ if_atsam_sysctl_reg, "I", "NCR");
+
+ tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "stats", CTLFLAG_RD,
NULL, "if_atsam statistics");
statsnode = SYSCTL_CHILDREN(tree);
+ SYSCTL_ADD_PROC(ctx, statsnode, OID_AUTO, "reset", CTLTYPE_INT |
+ CTLFLAG_WR, sc, 0, if_atsam_stats_reset, "I", "Reset");
+
tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "sw", CTLFLAG_RD,
NULL, "if_atsam software statistics");
child = SYSCTL_CHILDREN(tree);
@@ -1145,12 +1299,12 @@ if_atsam_add_sysctls(device_t dev)
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overrun_errors",
CTLFLAG_RD, &sc->stats.rx_overrun_errors, 0,
"RX overrun errors");
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_used_bit_reads",
+ CTLFLAG_RD, &sc->stats.rx_used_bit_reads, 0,
+ "RX used bit reads");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_interrupts",
CTLFLAG_RD, &sc->stats.rx_interrupts, 0,
"Rx interrupts");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_complete_int",
- CTLFLAG_RD, &sc->stats.tx_complete_int, 0,
- "Tx complete interrupts");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_tur_errors",
CTLFLAG_RD, &sc->stats.tx_tur_errors, 0,
"Error Tur Tx interrupts");
@@ -1163,9 +1317,6 @@ if_atsam_add_sysctls(device_t dev)
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_hresp_errors",
CTLFLAG_RD, &sc->stats.tx_hresp_errors, 0,
"Error Hresp Tx interrupts");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_interrupts",
- CTLFLAG_RD, &sc->stats.tx_interrupts, 0,
- "Tx interrupts");
tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "hw", CTLFLAG_RD,
NULL, "if_atsam hardware statistics");
@@ -1175,40 +1326,40 @@ if_atsam_add_sysctls(device_t dev)
NULL, "if_atsam hardware transmit statistics");
child = SYSCTL_CHILDREN(tree);
- SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_transm",
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets",
CTLFLAG_RD, &sc->stats.octets_transm,
"Octets Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames",
CTLFLAG_RD, &sc->stats.frames_transm, 0,
"Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames",
CTLFLAG_RD, &sc->stats.broadcast_frames_transm, 0,
"Broadcast Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames",
CTLFLAG_RD, &sc->stats.multicast_frames_transm, 0,
"Multicast Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames",
CTLFLAG_RD, &sc->stats.pause_frames_transm, 0,
"Pause Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes",
CTLFLAG_RD, &sc->stats.frames_64_byte_transm, 0,
"64 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes",
CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_transm, 0,
"65 to 127 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes",
CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_transm, 0,
"128 to 255 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes",
CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_transm, 0,
"256 to 511 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes",
CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_transm, 0,
"512 to 1023 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes",
CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_transm, 0,
"1024 to 1518 Byte Frames Transmitted");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_greater_1518_byte_transm",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes",
CTLFLAG_RD, &sc->stats.frames_greater_1518_byte_transm, 0,
"Greater Than 1518 Byte Frames Transmitted");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "transmit_underruns",
@@ -1237,49 +1388,49 @@ if_atsam_add_sysctls(device_t dev)
NULL, "if_atsam hardware receive statistics");
child = SYSCTL_CHILDREN(tree);
- SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_rec",
+ SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets",
CTLFLAG_RD, &sc->stats.octets_rec,
"Octets Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames",
CTLFLAG_RD, &sc->stats.frames_rec, 0,
"Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames",
CTLFLAG_RD, &sc->stats.broadcast_frames_rec, 0,
"Broadcast Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames",
CTLFLAG_RD, &sc->stats.multicast_frames_rec, 0,
"Multicast Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames",
CTLFLAG_RD, &sc->stats.pause_frames_rec, 0,
"Pause Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes",
CTLFLAG_RD, &sc->stats.frames_64_byte_rec, 0,
"64 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes",
CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_rec, 0,
"65 to 127 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes",
CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_rec, 0,
"128 to 255 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes",
CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_rec, 0,
"256 to 511 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes",
CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_rec, 0,
"512 to 1023 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes",
CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_rec, 0,
"1024 to 1518 Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_byte_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes",
CTLFLAG_RD, &sc->stats.frames_1519_to_maximum_byte_rec, 0,
"1519 to Maximum Byte Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames",
CTLFLAG_RD, &sc->stats.undersize_frames_rec, 0,
"Undersize Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames",
CTLFLAG_RD, &sc->stats.oversize_frames_rec, 0,
"Oversize Frames Received");
- SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers_rec",
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers",
CTLFLAG_RD, &sc->stats.jabbers_rec, 0,
"Jabbers Received");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frame_check_sequence_errors",
@@ -1311,49 +1462,6 @@ if_atsam_add_sysctls(device_t dev)
"UDP Checksum Errors");
}
-
-/*
- * Calculates the index that is to be sent into the hash registers
- */
-static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val)
-{
- uint64_t tmp_val;
- uint8_t i, j;
- uint64_t idx;
- int offset = 0;
-
- addr &= MAC_ADDR_MASK;
-
- for (i = 0; i < HASH_INDEX_AMOUNT; ++i) {
- tmp_val = 0;
- offset = 0;
- for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) {
- idx = (addr >> (offset + i)) & MAC_IDX_MASK;
- tmp_val ^= idx;
- offset += HASH_INDEX_AMOUNT;
- }
- if (tmp_val > 0) {
- *val |= (1u << i);
- }
- }
-}
-
-
-/*
- * Dis/Enable promiscuous Mode
- */
-static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable)
-{
- Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
-
- if (enable) {
- pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE;
- } else {
- pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE;
- }
-}
-
-
static int
if_atsam_mediaioctl(if_atsam_softc *sc, struct ifreq *ifr, u_long command)
{
@@ -1380,8 +1488,6 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int rv = 0;
- bool prom_enable;
- struct mii_data *mii;
switch (command) {
case SIOCGIFMEDIA:
@@ -1389,17 +1495,31 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
rv = if_atsam_mediaioctl(sc, ifr, command);
break;
case SIOCSIFFLAGS:
+ IF_ATSAM_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- if_atsam_init(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI)) {
+ if_atsam_setup_rxfilter(sc);
+ }
+ } else {
+ if_atsam_start_locked(sc);
}
- prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0);
- if_atsam_promiscuous_mode(sc, prom_enable);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if_atsam_stop(sc);
+ if_atsam_stop_locked(sc);
}
}
+ sc->if_flags = ifp->if_flags;
+ IF_ATSAM_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ IF_ATSAM_LOCK(sc);
+ if_atsam_setup_rxfilter(sc);
+ IF_ATSAM_UNLOCK(sc);
+ }
break;
default:
rv = ether_ioctl(ifp, command, data);
@@ -1416,7 +1536,6 @@ static int if_atsam_driver_attach(device_t dev)
if_atsam_softc *sc;
struct ifnet *ifp;
int unit;
- char *unitName;
uint8_t eaddr[ETHER_ADDR_LEN];
sc = device_get_softc(dev);
@@ -1437,13 +1556,6 @@ static int if_atsam_driver_attach(device_t dev)
memcpy(sc->GMacAddress, eaddr, ETHER_ADDR_LEN);
- sc->amount_rx_buf = RXBUF_COUNT;
- sc->amount_tx_buf = TXBUF_COUNT;
-
- sc->tx_ring.tx_bd_used = 0;
- sc->tx_ring.tx_bd_free = 0;
- sc->tx_ring.length = sc->amount_tx_buf;
-
/* Set Initial Link Speed */
sc->link_speed = GMAC_SPEED_100M;
sc->link_duplex = GMAC_DUPLEX_FULL;
@@ -1486,17 +1598,20 @@ static int if_atsam_driver_attach(device_t dev)
*/
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
- ifp->if_init = if_atsam_init;
+ ifp->if_init = if_atsam_start;
ifp->if_ioctl = if_atsam_ioctl;
- ifp->if_start = if_atsam_enet_start;
- ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ ifp->if_transmit = if_atsam_transmit;
+ ifp->if_qflush = if_qflush;
+ ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 |
- IFCAP_VLAN_HWCSUM;
+ IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTAGGING;
+ ifp->if_capenable = ifp->if_capabilities;
ifp->if_hwassist = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP |
CSUM_IP6_UDP | CSUM_IP6_TCP;
- IFQ_SET_MAXLEN(&ifp->if_snd, TXBUF_COUNT - 1);
- ifp->if_snd.ifq_drv_maxlen = TXBUF_COUNT - 1;
+ IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
+ ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
IFQ_SET_READY(&ifp->if_snd);
+ ifp->if_hdrlen = sizeof(struct ether_vlan_header);
/*
* Attach the interface
@@ -1504,6 +1619,7 @@ static int if_atsam_driver_attach(device_t dev)
ether_ifattach(ifp, eaddr);
if_atsam_add_sysctls(dev);
+ if_atsam_init(sc);
return (0);
}
diff --git a/rtemsbsd/sys/dev/mve/if_mve.c b/rtemsbsd/sys/dev/mve/if_mve.c
new file mode 100644
index 00000000..517484ee
--- /dev/null
+++ b/rtemsbsd/sys/dev/mve/if_mve.c
@@ -0,0 +1,2389 @@
+/* RTEMS driver for the mv643xx gigabit ethernet chip */
+
+/* Acknowledgement:
+ *
+ * Valuable information for developing this driver was obtained
+ * from the linux open-source driver mv643xx_eth.c which was written
+ * by the following people and organizations:
+ *
+ * Matthew Dharm <mdharm@momenco.com>
+ * rabeeh@galileo.co.il
+ * PMC-Sierra, Inc., Manish Lachwani
+ * Ralf Baechle <ralf@linux-mips.org>
+ * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org>
+ * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com>
+ *
+ * Note however, that in spite of the identical name of this file
+ * (and some of the symbols used herein) this file provides a
+ * new implementation and is the original work by the author.
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (mv643xx ethernet driver for RTEMS) was
+ * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The 'mv643xx ethernet driver for RTEMS' was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+/*
+ * NOTE: Some register (e.g., the SMI register) are SHARED among the
+ * three devices. Concurrent access protection is provided by
+ * the global networking semaphore.
+ * If other drivers are running on a subset of IFs then proper
+ * locking of all shared registers must be implemented!
+ *
+ * Some things I learned about this hardware can be found
+ * further down...
+ */
+
+/*
+#ifndef KERNEL
+#define KERNEL
+#endif
+#ifndef _KERNEL
+#define _KERNEL
+#endif
+*/
+
+#include <bsp.h>
+
+#ifdef LIBBSP_BEATNIK_BSP_H
+
+#include <rtems/bspIo.h>
+#include <rtems/error.h>
+#include <bsp/irq.h>
+#include <bsp/gtreg.h>
+#include <libcpu/byteorder.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <bsp/mv643xx_eth.h>
+
+/* CONFIGURABLE PARAMETERS */
+
+/* Enable Hardware Snooping; if this is disabled (undefined),
+ * cache coherency is maintained by software.
+ */
+#undef ENABLE_HW_SNOOPING
+
+/* Compile-time debugging features */
+
+/* Enable paranoia assertions and checks; reduce # of descriptors to minimum for stressing */
+#define MVETH_TESTING
+
+/* Enable debugging messages and some support routines (dump rings etc.) */
+#undef MVETH_DEBUG
+
+#define TX_NUM_TAG_SLOTS 1 /* leave room for tag; must not be 0 */
+
+/* This is REAL; chip reads from 64-bit down-aligned buffer
+ * if the buffer size is < 8 !!! for buffer sizes 8 and upwards
+ * alignment is not an issue. This was verified using the
+ * 'mve_smallbuf_test.c'
+ */
+#define ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM
+
+/* Chip register configuration values */
+#define MVETH_PORT_CONFIG_VAL (0 \
+ | MV643XX_ETH_DFLT_RX_Q(0) \
+ | MV643XX_ETH_DFLT_RX_ARP_Q(0) \
+ | MV643XX_ETH_DFLT_RX_TCP_Q(0) \
+ | MV643XX_ETH_DFLT_RX_UDP_Q(0) \
+ | MV643XX_ETH_DFLT_RX_BPDU_Q(0) \
+ )
+
+
+#define MVETH_PORT_XTEND_CONFIG_VAL 0
+
+#ifdef OLDCONFIGVAL
+#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \
+ | MV643XX_ETH_FORCE_LINK_PASS \
+ | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \
+ | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \
+ | MV643XX_ETH_BIT9_UNKNOWN \
+ | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \
+ | MV643XX_ETH_SC_MAX_RX_1552 \
+ | MV643XX_ETH_SET_FULL_DUPLEX \
+ | MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD \
+ )
+#endif
+/* If we enable autoneg (duplex, speed, ...) then it seems
+ * that the chip automatically updates link settings
+ * (correct link settings are reflected in PORT_STATUS_R).
+ * However, when we disable aneg in the PHY then things
+ * can get messed up and the port doesn't work anymore.
+ * => we follow the linux driver in disabling all aneg
+ * in the serial config reg. and manually updating the
+ * speed & duplex bits when the phy link status changes.
+ * FIXME: don't know what to do about pause/flow-ctrl.
+ * It is best to just use ANEG anyways!!!
+ */
+#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \
+ | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX \
+ | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \
+ | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \
+ | MV643XX_ETH_BIT9_UNKNOWN \
+ | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \
+ | MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII \
+ | MV643XX_ETH_SC_MAX_RX_1552 \
+ )
+
+#define MVETH_SERIAL_CTRL_CONFIG_MSK (0 \
+ | MV643XX_ETH_SERIAL_PORT_ENBL \
+ | MV643XX_ETH_FORCE_LINK_PASS \
+ | MV643XX_ETH_SC_MAX_RX_MASK \
+ )
+
+
+#ifdef __PPC__
+#define MVETH_SDMA_CONFIG_VAL (0 \
+ | MV643XX_ETH_RX_BURST_SZ_4_64BIT \
+ | MV643XX_ETH_TX_BURST_SZ_4_64BIT \
+ )
+#else
+#define MVETH_SDMA_CONFIG_VAL (0 \
+ | MV643XX_ETH_RX_BURST_SZ_16_64BIT \
+ | MV643XX_ETH_TX_BURST_SZ_16_64BIT \
+ )
+#endif
+
+/* minimal frame size we accept */
+#define MVETH_MIN_FRAMSZ_CONFIG_VAL 40
+
+/* END OF CONFIGURABLE SECTION */
+
+/*
+ * Here's stuff I learned about this chip:
+ *
+ *
+ * RX interrupt flags:
+ *
+ * broadcast packet RX: 0x00000005
+ * last buf: 0x00000c05
+ * overrun: 0x00000c00
+ * unicast packet RX: 0x00000005
+ * bad CRC received: 0x00000005
+ *
+ * clearing 0x00000004 -> clears 0x00000001
+ * clearing 0x00000400 -> clears 0x00000800
+ *
+ * --> 0x0801 are probably some sort of summary bits.
+ *
+ * TX interrupt flags:
+ *
+ * broadcast packet in 1 buf: xcause: 0x00000001 (cause 0x00080000)
+ * into disconn. link: " "
+ *
+ * in some cases, I observed xcause: 0x00000101 (reason for 0x100 unknown
+ * but the linux driver accepts it also).
+ *
+ *
+ * Here a few more ugly things about this piece of hardware I learned
+ * (painfully, painfully; spending many many hours & nights :-()
+ *
+ * a) Especially in the case of 'chained' descriptors, the DMA keeps
+ * clobbering 'cmd_sts' long after it cleared the OWNership flag!!!
+ * Only after the whole chain is processed (OWN cleared on the
+ * last descriptor) it is safe to change cmd_sts.
+ * However, in the case of hardware snooping I found that the
+ * last descriptor in chain has its cmd_sts still clobbered *after*
+ * checking ownership!, I.e.,
+ * if ( ! OWN & cmd_sts ) {
+ * cmd_sts = 0;
+ * }
+ * --> sometimes, cmd_sts is STILL != 0 here
+ *
+ * b) Sometimes, the OWNership flag is *not cleared*.
+ *
+ * c) Weird things happen if the chip finds a descriptor with 'OWN'
+ * still set (i.e., not properly loaded), i.e., corrupted packets
+ * are sent [with OK checksum since the chip calculates it].
+ *
+ * Combine a+b+c and we end up with a real mess.
+ *
+ * The fact that the chip doesn't reliably reset OWN and that OTOH,
+ * it can't be reliably reset by the driver and still, the chip needs
+ * it for proper communication doesn't make things easy...
+ *
+ * Here the basic workarounds:
+ *
+ * - In addition to check OWN, the scavenger compares the "currently
+ * served desc" register to the descriptor it tries to recover and
+ * ignores OWN if they do not match. Hope this is OK.
+ * Otherwise, we could scan the list of used descriptors and proceed
+ * recycling descriptors if we find a !OWNed one behind the target...
+ *
+ * - Always keep an empty slot around to mark the end of the list of
+ * jobs. The driver clears the descriptor ahead when enqueueing a new
+ * packet.
+ */
+
+#define DRVNAME "mve"
+#define MAX_NUM_SLOTS 3
+
+#if MV643XXETH_NUM_DRIVER_SLOTS > MAX_NUM_SLOTS
+#error "mv643xxeth: only MAX_NUM_SLOTS supported"
+#endif
+
+#ifdef NDEBUG
+#error "Driver uses assert() statements with side-effects; MUST NOT define NDEBUG"
+#endif
+
+#ifdef MVETH_DEBUG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+#define TX_AVAILABLE_RING_SIZE(mp) ((mp)->xbuf_count - (TX_NUM_TAG_SLOTS))
+
+/* macros for ring alignment; proper alignment is a hardware req; . */
+
+#ifdef ENABLE_HW_SNOOPING
+
+#define RING_ALIGNMENT 16
+/* rx buffers must be 64-bit aligned (chip requirement) */
+#define RX_BUF_ALIGNMENT 8
+
+#else /* ENABLE_HW_SNOOPING */
+
+/* Software cache management */
+
+#ifndef __PPC__
+#error "Dont' know how to deal with cache on this CPU architecture"
+#endif
+
+/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency
+ * management works for cache line sizes of 16 and 32 bytes only. If the line size
+ * is bigger, the descriptors could be padded...
+ */
+#if PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32
+#error "Cache line size must be 16 or 32"
+#else
+#define RING_ALIGNMENT PPC_CACHE_ALIGNMENT
+#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT
+#endif
+
+#endif /* ENABLE_HW_SNOOPING */
+
+
+/* HELPER MACROS */
+
+/* Align base to alignment 'a' */
+#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1)))
+
+#define NOOP() do {} while(0)
+
+/* Function like macros */
+#define MV_READ(off) \
+ ld_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off)))
+#define MV_WRITE(off, data) \
+ st_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off)), ((unsigned)data))
+
+
+/* ENET window mapped 1:1 to CPU addresses by our BSP/MotLoad
+ * -- if this is changed, we should think about caching the 'next' and 'buf' pointers.
+ */
+#define CPUADDR2ENET(a) ((Dma_addr_t)(a))
+#define ENET2CPUADDR(a) (a)
+
+#if 1 /* Whether to automatically try to reclaim descriptors when enqueueing new packets */
+#define MVETH_CLEAN_ON_SEND(mp) (BSP_mve_swipe_tx(mp))
+#else
+#define MVETH_CLEAN_ON_SEND(mp) (-1)
+#endif
+
+#define NEXT_TXD(d) (d)->next
+#define NEXT_RXD(d) (d)->next
+
+/* REGISTER AND DESCRIPTOR OFFSET AND BIT DEFINITIONS */
+
+/* Descriptor Definitions */
+/* Rx descriptor */
+#define RDESC_ERROR (1<< 0) /* Error summary */
+
+/* Error code (bit 1&2) is only valid if summary bit is set */
+#define RDESC_CRC_ERROR ( 1)
+#define RDESC_OVERRUN_ERROR ( 3)
+#define RDESC_MAX_FRAMELENGTH_ERROR ( 5)
+#define RDESC_RESOURCE_ERROR ( 7)
+
+#define RDESC_LAST (1<<26) /* Last Descriptor */
+#define RDESC_FRST (1<<27) /* First Descriptor */
+#define RDESC_INT_ENA (1<<29) /* Enable Interrupts */
+#define RDESC_DMA_OWNED (1<<31)
+
+/* Tx descriptor */
+#define TDESC_ERROR (1<< 0) /* Error summary */
+#define TDESC_ZERO_PAD (1<<19)
+#define TDESC_LAST (1<<20) /* Last Descriptor */
+#define TDESC_FRST (1<<21) /* First Descriptor */
+#define TDESC_GEN_CRC (1<<22)
+#define TDESC_INT_ENA (1<<23) /* Enable Interrupts */
+#define TDESC_DMA_OWNED (1<<31)
+
+
+
+/* Register Definitions */
+#define MV643XX_ETH_PHY_ADDR_R (0x2000)
+#define MV643XX_ETH_SMI_R (0x2004)
+#define MV643XX_ETH_SMI_BUSY (1<<28)
+#define MV643XX_ETH_SMI_VALID (1<<27)
+#define MV643XX_ETH_SMI_OP_WR (0<<26)
+#define MV643XX_ETH_SMI_OP_RD (1<<26)
+
+#define MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port) (0x2448 + ((port)<<10))
+#define MV643XX_ETH_TX_START(queue) (0x0001<<(queue))
+#define MV643XX_ETH_TX_STOP(queue) (0x0100<<(queue))
+#define MV643XX_ETH_TX_START_M(queues) ((queues)&0xff)
+#define MV643XX_ETH_TX_STOP_M(queues) (((queues)&0xff)<<8)
+#define MV643XX_ETH_TX_STOP_ALL (0xff00)
+#define MV643XX_ETH_TX_ANY_RUNNING (0x00ff)
+
+#define MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(port) (0x2680 + ((port)<<10))
+#define MV643XX_ETH_RX_START(queue) (0x0001<<(queue))
+#define MV643XX_ETH_RX_STOP(queue) (0x0100<<(queue))
+#define MV643XX_ETH_RX_STOP_ALL (0xff00)
+#define MV643XX_ETH_RX_ANY_RUNNING (0x00ff)
+
+#define MV643XX_ETH_CURRENT_SERVED_TX_DESC(port) (0x2684 + ((port)<<10))
+
+/* The chip puts the ethernet header at offset 2 into the buffer so
+ * that the payload is aligned
+ */
+#define ETH_RX_OFFSET 2
+#define ETH_CRC_LEN 4 /* strip FCS at end of packet */
+
+
+#define MV643XX_ETH_INTERRUPT_CAUSE_R(port) (0x2460 + ((port)<<10))
+/* not fully understood; RX seems to raise 0x0005 or 0x0c05 if last buffer is filled and 0x0c00
+ * if there are no buffers
+ */
+#define MV643XX_ETH_ALL_IRQS (0x07ffffff)
+#define MV643XX_ETH_KNOWN_IRQS (0x00080c07)
+#define MV643XX_ETH_IRQ_EXT_ENA (1<<1)
+/* defined in public header
+#define MV643XX_ETH_IRQ_RX_DONE (1<<2)
+ */
+#define MV643XX_ETH_IRQ_RX_NO_DESC (1<<10)
+#define MV643XX_ETH_TX_Q_N_END(n) (1<<((n)+19))
+/* We just use queue 0 */
+#define MV643XX_ETH_TX_Q_END MV643XX_ETH_TX_Q_N_END(0)
+
+#define MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(port) (0x2464 + ((port)<<10))
+/* not fully understood; TX seems to raise 0x0001 and link change is 0x00010000
+ * if there are no buffers
+ */
+#define MV643XX_ETH_ALL_EXT_IRQS (0x0011ffff)
+/* Recent (2013) linux driver mentions both bits 0x00110000 as 'link change' causes */
+#define MV643XX_ETH_KNOWN_EXT_IRQS (0x00110101)
+/* TX queues 0..7 */
+#define MV643XX_ETH_EXT_IRQ_TXN_DONE(n) (1<<(n))
+/* We just use queue 0 */
+/* defined in public header
+#define MV643XX_ETH_EXT_IRQ_TX_DONE MV643XX_ETH_EXT_IRQ_TXN_DONE(0)
+#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16)
+ */
+#define MV643XX_ETH_INTERRUPT_ENBL_R(port) (0x2468 + ((port)<<10))
+#define MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(port) (0x246c + ((port)<<10))
+
+/* port configuration */
+#define MV643XX_ETH_PORT_CONFIG_R(port) (0x2400 + ((port)<<10))
+#define MV643XX_ETH_UNICAST_PROMISC_MODE (1<<0)
+#define MV643XX_ETH_DFLT_RX_Q(q) ((q)<<1)
+#define MV643XX_ETH_DFLT_RX_ARP_Q(q) ((q)<<4)
+#define MV643XX_ETH_REJ_BCAST_IF_NOT_IP_OR_ARP (1<<7)
+#define MV643XX_ETH_REJ_BCAST_IF_IP (1<<8)
+#define MV643XX_ETH_REJ_BCAST_IF_ARP (1<<9)
+#define MV643XX_ETH_TX_AM_NO_UPDATE_ERR_SUMMARY (1<<12)
+#define MV643XX_ETH_CAPTURE_TCP_FRAMES_ENBL (1<<14)
+#define MV643XX_ETH_CAPTURE_UDP_FRAMES_ENBL (1<<15)
+#define MV643XX_ETH_DFLT_RX_TCP_Q(q) ((q)<<16)
+#define MV643XX_ETH_DFLT_RX_UDP_Q(q) ((q)<<19)
+#define MV643XX_ETH_DFLT_RX_BPDU_Q(q) ((q)<<22)
+
+
+
+#define MV643XX_ETH_PORT_CONFIG_XTEND_R(port) (0x2404 + ((port)<<10))
+#define MV643XX_ETH_CLASSIFY_ENBL (1<<0)
+#define MV643XX_ETH_SPAN_BPDU_PACKETS_AS_NORMAL (0<<1)
+#define MV643XX_ETH_SPAN_BPDU_PACKETS_2_Q7 (1<<1)
+#define MV643XX_ETH_PARTITION_DISBL (0<<2)
+#define MV643XX_ETH_PARTITION_ENBL (1<<2)
+
+#define MV643XX_ETH_SDMA_CONFIG_R(port) (0x241c + ((port)<<10))
+#define MV643XX_ETH_SDMA_RIFB (1<<0)
+#define MV643XX_ETH_RX_BURST_SZ_1_64BIT (0<<1)
+#define MV643XX_ETH_RX_BURST_SZ_2_64BIT (1<<1)
+#define MV643XX_ETH_RX_BURST_SZ_4_64BIT (2<<1)
+#define MV643XX_ETH_RX_BURST_SZ_8_64BIT (3<<1)
+#define MV643XX_ETH_RX_BURST_SZ_16_64BIT (4<<1)
+#define MV643XX_ETH_SMDA_BLM_RX_NO_SWAP (1<<4)
+#define MV643XX_ETH_SMDA_BLM_TX_NO_SWAP (1<<5)
+#define MV643XX_ETH_SMDA_DESC_BYTE_SWAP (1<<6)
+#define MV643XX_ETH_TX_BURST_SZ_1_64BIT (0<<22)
+#define MV643XX_ETH_TX_BURST_SZ_2_64BIT (1<<22)
+#define MV643XX_ETH_TX_BURST_SZ_4_64BIT (2<<22)
+#define MV643XX_ETH_TX_BURST_SZ_8_64BIT (3<<22)
+#define MV643XX_ETH_TX_BURST_SZ_16_64BIT (4<<22)
+
+#define MV643XX_ETH_RX_MIN_FRAME_SIZE_R(port) (0x247c + ((port)<<10))
+
+
+#define MV643XX_ETH_SERIAL_CONTROL_R(port) (0x243c + ((port)<<10))
+#define MV643XX_ETH_SERIAL_PORT_ENBL (1<<0) /* Enable serial port */
+#define MV643XX_ETH_FORCE_LINK_PASS (1<<1)
+#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX (1<<2)
+#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL (1<<3)
+#define MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL (1<<4)
+#define MV643XX_ETH_FORCE_FC_MODE_TX_PAUSE_DIS (1<<5)
+#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX (1<<7)
+#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1<<8)
+#define MV643XX_ETH_BIT9_UNKNOWN (1<<9) /* unknown purpose; linux sets this */
+#define MV643XX_ETH_FORCE_LINK_FAIL_DISABLE (1<<10)
+#define MV643XX_ETH_RETRANSMIT_FOREVER (1<<11) /* limit to 16 attempts if clear */
+#define MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII (1<<13)
+#define MV643XX_ETH_DTE_ADV_1 (1<<14)
+#define MV643XX_ETH_AUTO_NEG_BYPASS_ENBL (1<<15)
+#define MV643XX_ETH_RESTART_AUTO_NEG (1<<16)
+#define MV643XX_ETH_SC_MAX_RX_1518 (0<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_1522 (1<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_1552 (2<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_9022 (3<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_9192 (4<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_9700 (5<<17) /* Limit RX packet size */
+#define MV643XX_ETH_SC_MAX_RX_MASK (7<<17) /* bitmask */
+#define MV643XX_ETH_SET_EXT_LOOPBACK (1<<20)
+#define MV643XX_ETH_SET_FULL_DUPLEX (1<<21)
+#define MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD (1<<22) /* enable flow ctrl on rx and tx in full-duplex */
+#define MV643XX_ETH_SET_GMII_SPEED_1000 (1<<23) /* 10/100 if clear */
+#define MV643XX_ETH_SET_MII_SPEED_100 (1<<24) /* 10 if clear */
+
+#define MV643XX_ETH_PORT_STATUS_R(port) (0x2444 + ((port)<<10))
+
+#define MV643XX_ETH_PORT_STATUS_MODE_10_BIT (1<<0)
+#define MV643XX_ETH_PORT_STATUS_LINK_UP (1<<1)
+#define MV643XX_ETH_PORT_STATUS_FDX (1<<2)
+#define MV643XX_ETH_PORT_STATUS_FC (1<<3)
+#define MV643XX_ETH_PORT_STATUS_1000 (1<<4)
+#define MV643XX_ETH_PORT_STATUS_100 (1<<5)
+/* PSR bit 6 unknown */
+#define MV643XX_ETH_PORT_STATUS_TX_IN_PROGRESS (1<<7)
+#define MV643XX_ETH_PORT_STATUS_ANEG_BYPASSED (1<<8)
+#define MV643XX_ETH_PORT_STATUS_PARTITION (1<<9)
+#define MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY (1<<10)
+
+#define MV643XX_ETH_MIB_COUNTERS(port) (0x3000 + ((port)<<7))
+#define MV643XX_ETH_NUM_MIB_COUNTERS 32
+
+#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO (0)
+#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_HI (1<<2)
+#define MV643XX_ETH_MIB_BAD_OCTS_RCVD (2<<2)
+#define MV643XX_ETH_MIB_INTERNAL_MAC_TX_ERR (3<<2)
+#define MV643XX_ETH_MIB_GOOD_FRAMES_RCVD (4<<2)
+#define MV643XX_ETH_MIB_BAD_FRAMES_RCVD (5<<2)
+#define MV643XX_ETH_MIB_BCAST_FRAMES_RCVD (6<<2)
+#define MV643XX_ETH_MIB_MCAST_FRAMES_RCVD (7<<2)
+#define MV643XX_ETH_MIB_FRAMES_64_OCTS (8<<2)
+#define MV643XX_ETH_MIB_FRAMES_65_127_OCTS (9<<2)
+#define MV643XX_ETH_MIB_FRAMES_128_255_OCTS (10<<2)
+#define MV643XX_ETH_MIB_FRAMES_256_511_OCTS (11<<2)
+#define MV643XX_ETH_MIB_FRAMES_512_1023_OCTS (12<<2)
+#define MV643XX_ETH_MIB_FRAMES_1024_MAX_OCTS (13<<2)
+#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO (14<<2)
+#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_HI (15<<2)
+#define MV643XX_ETH_MIB_GOOD_FRAMES_SENT (16<<2)
+#define MV643XX_ETH_MIB_EXCESSIVE_COLL (17<<2)
+#define MV643XX_ETH_MIB_MCAST_FRAMES_SENT (18<<2)
+#define MV643XX_ETH_MIB_BCAST_FRAMES_SENT (19<<2)
+#define MV643XX_ETH_MIB_UNREC_MAC_CTRL_RCVD (20<<2)
+#define MV643XX_ETH_MIB_FC_SENT (21<<2)
+#define MV643XX_ETH_MIB_GOOD_FC_RCVD (22<<2)
+#define MV643XX_ETH_MIB_BAD_FC_RCVD (23<<2)
+#define MV643XX_ETH_MIB_UNDERSIZE_RCVD (24<<2)
+#define MV643XX_ETH_MIB_FRAGMENTS_RCVD (25<<2)
+#define MV643XX_ETH_MIB_OVERSIZE_RCVD (26<<2)
+#define MV643XX_ETH_MIB_JABBER_RCVD (27<<2)
+#define MV643XX_ETH_MIB_MAC_RX_ERR (28<<2)
+#define MV643XX_ETH_MIB_BAD_CRC_EVENT (29<<2)
+#define MV643XX_ETH_MIB_COLL (30<<2)
+#define MV643XX_ETH_MIB_LATE_COLL (31<<2)
+
+#define MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(port) (0x3400+((port)<<10))
+#define MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(port) (0x3500+((port)<<10))
+#define MV643XX_ETH_DA_FILTER_UNICAST_TBL(port) (0x3600+((port)<<10))
+#define MV643XX_ETH_NUM_MCAST_ENTRIES 64
+#define MV643XX_ETH_NUM_UNICAST_ENTRIES 4
+
+#define MV643XX_ETH_BAR_0 (0x2200)
+#define MV643XX_ETH_SIZE_R_0 (0x2204)
+#define MV643XX_ETH_BAR_1 (0x2208)
+#define MV643XX_ETH_SIZE_R_1 (0x220c)
+#define MV643XX_ETH_BAR_2 (0x2210)
+#define MV643XX_ETH_SIZE_R_2 (0x2214)
+#define MV643XX_ETH_BAR_3 (0x2218)
+#define MV643XX_ETH_SIZE_R_3 (0x221c)
+#define MV643XX_ETH_BAR_4 (0x2220)
+#define MV643XX_ETH_SIZE_R_4 (0x2224)
+#define MV643XX_ETH_BAR_5 (0x2228)
+#define MV643XX_ETH_SIZE_R_5 (0x222c)
+#define MV643XX_ETH_NUM_BARS 6
+
+/* Bits in the BAR reg to program cache snooping */
+#define MV64360_ENET2MEM_SNOOP_NONE 0x0000
+#define MV64360_ENET2MEM_SNOOP_WT 0x1000
+#define MV64360_ENET2MEM_SNOOP_WB 0x2000
+#define MV64360_ENET2MEM_SNOOP_MSK 0x3000
+
+
+#define MV643XX_ETH_BAR_ENBL_R (0x2290)
+#define MV643XX_ETH_BAR_DISABLE(bar) (1<<(bar))
+#define MV643XX_ETH_BAR_DISBL_ALL 0x3f
+
+#define MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(port) (0x260c+((port)<<10))
+#define MV643XX_ETH_RX_Q1_CURRENT_DESC_PTR(port) (0x261c+((port)<<10))
+#define MV643XX_ETH_RX_Q2_CURRENT_DESC_PTR(port) (0x262c+((port)<<10))
+#define MV643XX_ETH_RX_Q3_CURRENT_DESC_PTR(port) (0x263c+((port)<<10))
+#define MV643XX_ETH_RX_Q4_CURRENT_DESC_PTR(port) (0x264c+((port)<<10))
+#define MV643XX_ETH_RX_Q5_CURRENT_DESC_PTR(port) (0x265c+((port)<<10))
+#define MV643XX_ETH_RX_Q6_CURRENT_DESC_PTR(port) (0x266c+((port)<<10))
+#define MV643XX_ETH_RX_Q7_CURRENT_DESC_PTR(port) (0x267c+((port)<<10))
+
+#define MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(port) (0x26c0+((port)<<10))
+#define MV643XX_ETH_TX_Q1_CURRENT_DESC_PTR(port) (0x26c4+((port)<<10))
+#define MV643XX_ETH_TX_Q2_CURRENT_DESC_PTR(port) (0x26c8+((port)<<10))
+#define MV643XX_ETH_TX_Q3_CURRENT_DESC_PTR(port) (0x26cc+((port)<<10))
+#define MV643XX_ETH_TX_Q4_CURRENT_DESC_PTR(port) (0x26d0+((port)<<10))
+#define MV643XX_ETH_TX_Q5_CURRENT_DESC_PTR(port) (0x26d4+((port)<<10))
+#define MV643XX_ETH_TX_Q6_CURRENT_DESC_PTR(port) (0x26d8+((port)<<10))
+#define MV643XX_ETH_TX_Q7_CURRENT_DESC_PTR(port) (0x26dc+((port)<<10))
+
+#define MV643XX_ETH_MAC_ADDR_LO(port) (0x2414+((port)<<10))
+#define MV643XX_ETH_MAC_ADDR_HI(port) (0x2418+((port)<<10))
+
+/* TYPE DEFINITIONS */
+
+/* just to make the purpose explicit; vars of this
+ * type may need CPU-dependent address translation,
+ * endian conversion etc.
+ */
+typedef uint32_t Dma_addr_t;
+
+typedef volatile struct mveth_rx_desc {
+#ifndef __BIG_ENDIAN__
+#error "descriptor declaration not implemented for little endian machines"
+#endif
+ uint16_t byte_cnt;
+ uint16_t buf_size;
+ uint32_t cmd_sts; /* control and status */
+ Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */
+ Dma_addr_t buf_ptr;
+ /* fields below here are not used by the chip */
+ void *u_buf; /* user buffer */
+ volatile struct mveth_rx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */
+ uint32_t pad[2];
+} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthRxDescRec, *MvEthRxDesc;
+
+typedef volatile struct mveth_tx_desc {
+#ifndef __BIG_ENDIAN__
+#error "descriptor declaration not implemented for little endian machines"
+#endif
+ uint16_t byte_cnt;
+ uint16_t l4i_chk;
+ uint32_t cmd_sts; /* control and status */
+ Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */
+ Dma_addr_t buf_ptr;
+ /* fields below here are not used by the chip */
+ uint32_t workaround[2]; /* use this space to work around the 8byte problem (is this real?) */
+ void *u_buf; /* user buffer */
+ volatile struct mveth_tx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */
+} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthTxDescRec, *MvEthTxDesc;
+
+/* Assume there are never more then 64k aliasing entries */
+typedef uint16_t Mc_Refcnt[MV643XX_ETH_NUM_MCAST_ENTRIES*4];
+
+/* driver private data and bsdnet interface structure */
+struct mveth_private {
+ MvEthRxDesc rx_ring; /* pointers to aligned ring area */
+ MvEthTxDesc tx_ring; /* pointers to aligned ring area */
+ MvEthRxDesc ring_area; /* allocated ring area */
+ int rbuf_count, xbuf_count; /* saved ring sizes from ifconfig */
+ int port_num;
+ int phy;
+ MvEthRxDesc d_rx_t; /* tail of the RX ring; next received packet */
+ MvEthTxDesc d_tx_t, d_tx_h;
+ uint32_t rx_desc_dma, tx_desc_dma; /* ring address as seen by DMA; (1:1 on this BSP) */
+ int avail;
+ void (*isr)(void*);
+ void *isr_arg;
+ /* Callbacks to handle buffers */
+ void (*cleanup_txbuf)(void*, void*, int); /* callback to cleanup TX buffer */
+ void *cleanup_txbuf_arg;
+ void *(*alloc_rxbuf)(int *psize, uintptr_t *paddr); /* allocate RX buffer */
+ void (*consume_rxbuf)(void*, void*, int); /* callback to consume RX buffer */
+ void *consume_rxbuf_arg;
+ rtems_id tid;
+ uint32_t irq_mask; /* IRQs we use */
+ uint32_t xirq_mask;
+ int promisc;
+ struct {
+ unsigned irqs;
+ unsigned maxchain;
+ unsigned repack;
+ unsigned packet;
+ unsigned idrops; /* no counter in core code */
+ struct {
+ uint64_t good_octs_rcvd; /* 64-bit */
+ uint32_t bad_octs_rcvd;
+ uint32_t internal_mac_tx_err;
+ uint32_t good_frames_rcvd;
+ uint32_t bad_frames_rcvd;
+ uint32_t bcast_frames_rcvd;
+ uint32_t mcast_frames_rcvd;
+ uint32_t frames_64_octs;
+ uint32_t frames_65_127_octs;
+ uint32_t frames_128_255_octs;
+ uint32_t frames_256_511_octs;
+ uint32_t frames_512_1023_octs;
+ uint32_t frames_1024_max_octs;
+ uint64_t good_octs_sent; /* 64-bit */
+ uint32_t good_frames_sent;
+ uint32_t excessive_coll;
+ uint32_t mcast_frames_sent;
+ uint32_t bcast_frames_sent;
+ uint32_t unrec_mac_ctrl_rcvd;
+ uint32_t fc_sent;
+ uint32_t good_fc_rcvd;
+ uint32_t bad_fc_rcvd;
+ uint32_t undersize_rcvd;
+ uint32_t fragments_rcvd;
+ uint32_t oversize_rcvd;
+ uint32_t jabber_rcvd;
+ uint32_t mac_rx_err;
+ uint32_t bad_crc_event;
+ uint32_t coll;
+ uint32_t late_coll;
+ } mib;
+ } stats;
+ struct {
+ Mc_Refcnt specl, other;
+ } mc_refcnt;
+};
+
+/* GLOBAL VARIABLES */
+
+/* Format strings for statistics messages */
+static const char *mibfmt[] = {
+ " GOOD_OCTS_RCVD: %"PRIu64"\n",
+ 0,
+ " BAD_OCTS_RCVD: %"PRIu32"\n",
+ " INTERNAL_MAC_TX_ERR: %"PRIu32"\n",
+ " GOOD_FRAMES_RCVD: %"PRIu32"\n",
+ " BAD_FRAMES_RCVD: %"PRIu32"\n",
+ " BCAST_FRAMES_RCVD: %"PRIu32"\n",
+ " MCAST_FRAMES_RCVD: %"PRIu32"\n",
+ " FRAMES_64_OCTS: %"PRIu32"\n",
+ " FRAMES_65_127_OCTS: %"PRIu32"\n",
+ " FRAMES_128_255_OCTS: %"PRIu32"\n",
+ " FRAMES_256_511_OCTS: %"PRIu32"\n",
+ " FRAMES_512_1023_OCTS:%"PRIu32"\n",
+ " FRAMES_1024_MAX_OCTS:%"PRIu32"\n",
+ " GOOD_OCTS_SENT: %"PRIu64"\n",
+ 0,
+ " GOOD_FRAMES_SENT: %"PRIu32"\n",
+ " EXCESSIVE_COLL: %"PRIu32"\n",
+ " MCAST_FRAMES_SENT: %"PRIu32"\n",
+ " BCAST_FRAMES_SENT: %"PRIu32"\n",
+ " UNREC_MAC_CTRL_RCVD: %"PRIu32"\n",
+ " FC_SENT: %"PRIu32"\n",
+ " GOOD_FC_RCVD: %"PRIu32"\n",
+ " BAD_FC_RCVD: %"PRIu32"\n",
+ " UNDERSIZE_RCVD: %"PRIu32"\n",
+ " FRAGMENTS_RCVD: %"PRIu32"\n",
+ " OVERSIZE_RCVD: %"PRIu32"\n",
+ " JABBER_RCVD: %"PRIu32"\n",
+ " MAC_RX_ERR: %"PRIu32"\n",
+ " BAD_CRC_EVENT: %"PRIu32"\n",
+ " COLL: %"PRIu32"\n",
+ " LATE_COLL: %"PRIu32"\n",
+};
+
+/* Interrupt Handler Connection */
+
+/* forward decls + implementation for IRQ API funcs */
+
+STATIC int
+mveth_init_rx_desc_ring(struct mveth_private *mp);
+
+STATIC int
+mveth_init_tx_desc_ring(struct mveth_private *mp);
+
+int
+BSP_mve_dring_nonsync(struct mveth_private *mp);
+
+static void mveth_isr(rtems_irq_hdl_param unit);
+static void noop(const rtems_irq_connect_data *unused) {}
+static int noop1(const rtems_irq_connect_data *unused) { return 0; }
+
+static rtems_irq_connect_data irq_data[MAX_NUM_SLOTS] = {
+ {
+ BSP_IRQ_ETH0,
+ 0,
+ (rtems_irq_hdl_param)0,
+ noop,
+ noop,
+ noop1
+ },
+ {
+ BSP_IRQ_ETH1,
+ 0,
+ (rtems_irq_hdl_param)1,
+ noop,
+ noop,
+ noop1
+ },
+ {
+ BSP_IRQ_ETH2,
+ 0,
+ (rtems_irq_hdl_param)2,
+ noop,
+ noop,
+ noop1
+ },
+};
+
+/* LOW LEVEL SUPPORT ROUTINES */
+
+/* Software Cache Coherency */
+#ifndef ENABLE_HW_SNOOPING
+#ifndef __PPC__
+#error "Software cache coherency maintenance is not implemented for your CPU architecture"
+#endif
+
+static inline unsigned INVAL_DESC(volatile void *d)
+{
+typedef const char cache_line[PPC_CACHE_ALIGNMENT];
+ asm volatile("dcbi 0, %1":"=m"(*(cache_line*)d):"r"(d));
+ return (unsigned)d; /* so this can be used in comma expression */
+}
+
+static inline void FLUSH_DESC(volatile void *d)
+{
+typedef const char cache_line[PPC_CACHE_ALIGNMENT];
+ asm volatile("dcbf 0, %0"::"r"(d),"m"(*(cache_line*)d));
+}
+
+static inline void FLUSH_BARRIER(void)
+{
+ asm volatile("eieio");
+}
+
+/* RX buffers are always cache-line aligned
+ * ASSUMPTIONS:
+ * - 'addr' is cache aligned
+ * - len is a multiple >0 of cache lines
+ */
+static inline void INVAL_BUF(register uintptr_t addr, register int len)
+{
+typedef char maxbuf[2048]; /* more than an ethernet packet */
+ do {
+ len -= RX_BUF_ALIGNMENT;
+ asm volatile("dcbi %0, %1"::"b"(addr),"r"(len));
+ } while (len > 0);
+ asm volatile("":"=m"(*(maxbuf*)addr));
+}
+
+/* Flushing TX buffers is a little bit trickier; we don't really know their
+ * alignment but *assume* adjacent addresses are covering 'ordinary' memory
+ * so that flushing them does no harm!
+ */
+static inline void FLUSH_BUF(register uintptr_t addr, register int len)
+{
+ asm volatile("":::"memory");
+ len = MV643XX_ALIGN(len, RX_BUF_ALIGNMENT);
+ do {
+ asm volatile("dcbf %0, %1"::"b"(addr),"r"(len));
+ len -= RX_BUF_ALIGNMENT;
+ } while ( len >= 0 );
+}
+
+#else /* hardware snooping enabled */
+
+/* inline this to silence compiler warnings */
+static inline int INVAL_DESC(volatile void *d)
+{ return 0; }
+
+#define FLUSH_DESC(d) NOOP()
+#define INVAL_BUF(b,l) NOOP()
+#define FLUSH_BUF(b,l) NOOP()
+#define FLUSH_BARRIER() NOOP()
+
+#endif /* cache coherency support */
+
+/* Synchronize memory access */
+#ifdef __PPC__
+static inline void membarrier(void)
+{
+ asm volatile("sync":::"memory");
+}
+#else
+#error "memory barrier instruction not defined (yet) for this CPU"
+#endif
+
+/* Enable and disable interrupts at the device */
+static inline void
+mveth_enable_irqs(struct mveth_private *mp, uint32_t mask)
+{
+rtems_interrupt_level l;
+uint32_t val;
+ rtems_interrupt_disable(l);
+
+ val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num));
+ val = (val | mask | MV643XX_ETH_IRQ_EXT_ENA) & mp->irq_mask;
+
+ MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), val);
+
+ val = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num));
+ val = (val | mask) & mp->xirq_mask;
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), val);
+
+ rtems_interrupt_enable(l);
+}
+
+static inline uint32_t
+mveth_disable_irqs(struct mveth_private *mp, uint32_t mask)
+{
+rtems_interrupt_level l;
+uint32_t val,xval,tmp;
+ rtems_interrupt_disable(l);
+
+ val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num));
+ tmp = ( (val & ~mask) | MV643XX_ETH_IRQ_EXT_ENA ) & mp->irq_mask;
+ MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), tmp);
+
+ xval = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num));
+ tmp = (xval & ~mask) & mp->xirq_mask;
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), tmp);
+
+ rtems_interrupt_enable(l);
+
+ return (val | xval);
+}
+
+/* This should be safe even w/o turning off interrupts if multiple
+ * threads ack different bits in the cause register (and ignore
+ * other ones) since writing 'ones' into the cause register doesn't
+ * 'stick'.
+ */
+
+static inline uint32_t
+mveth_ack_irqs(struct mveth_private *mp, uint32_t mask)
+{
+register uint32_t x,xe,p;
+register uint32_t rval;
+
+ p = mp->port_num;
+ /* Get cause */
+ x = MV_READ(MV643XX_ETH_INTERRUPT_CAUSE_R(p));
+
+ /* Ack interrupts filtering the ones we're interested in */
+
+ /* Note: EXT_IRQ bit clears by itself if EXT interrupts are cleared */
+ MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(p), ~ (x & mp->irq_mask & mask));
+
+ /* linux driver tests 1<<1 as a summary bit for extended interrupts;
+ * the mv64360 seems to use 1<<19 for that purpose; for the moment,
+ * I just check both.
+ * Update: link status irq (1<<16 in xe) doesn't set (1<<19) in x!
+ */
+ if ( 1 /* x & 2 */ )
+ {
+ xe = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p));
+
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p), ~ (xe & mp->xirq_mask & mask));
+ } else {
+ xe = 0;
+ }
+#ifdef MVETH_TESTING
+ if ( ((x & MV643XX_ETH_ALL_IRQS) & ~MV643XX_ETH_KNOWN_IRQS)
+ || ((xe & MV643XX_ETH_ALL_EXT_IRQS) & ~MV643XX_ETH_KNOWN_EXT_IRQS) ) {
+ fprintf(stderr, "Unknown IRQs detected; leaving all disabled for debugging:\n");
+ fprintf(stderr, "Cause reg was 0x%08x, ext cause 0x%08x\n", x, xe);
+/*
+ mp->irq_mask = 0;
+ mp->xirq_mask = 0;
+*/
+ }
+#endif
+ /* luckily, the extended and 'normal' interrupts we use don't overlap so
+ * we can just OR them into a single word
+ */
+ rval = (xe & mp->xirq_mask) | (x & mp->irq_mask);
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"%i: mveth_ack_irqs 0x%08x\n", rval);
+#endif
+ return rval;
+}
+
+static void mveth_isr(rtems_irq_hdl_param arg)
+{
+struct mveth_private *mp = (struct mveth_private*) arg;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mveth_isr\n");
+#endif
+ mp->stats.irqs++;
+ mp->isr(mp->isr_arg);
+}
+
+static void
+mveth_clear_mib_counters(struct mveth_private *mp)
+{
+register int i;
+register uint32_t b;
+ /* reading the counters resets them */
+ b = MV643XX_ETH_MIB_COUNTERS(mp->port_num);
+ for (i=0; i< MV643XX_ETH_NUM_MIB_COUNTERS; i++, b+=4)
+ (void)MV_READ(b);
+}
+
+/* Reading a MIB register also clears it. Hence we read the lo
+ * register first, then the hi one. Correct reading is guaranteed since
+ * the 'lo' register cannot overflow after it is read since it had
+ * been reset to 0.
+ */
+static unsigned long long
+read_long_mib_counter(int port_num, int idx)
+{
+unsigned long lo;
+unsigned long long hi;
+ lo = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2));
+ idx++;
+ hi = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2));
+ return (hi<<32) | lo;
+}
+
+static inline unsigned long
+read_mib_counter(int port_num, int idx)
+{
+ return MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2));
+}
+
+
+/* write ethernet address from buffer to hardware (need to change unicast filter after this) */
+static void
+mveth_write_eaddr(struct mveth_private *mp, unsigned char *eaddr)
+{
+int i;
+uint32_t x;
+
+ /* build hi word */
+ for (i=4,x=0; i; i--, eaddr++) {
+ x = (x<<8) | *eaddr;
+ }
+ MV_WRITE(MV643XX_ETH_MAC_ADDR_HI(mp->port_num), x);
+
+ /* build lo word */
+ for (i=2,x=0; i; i--, eaddr++) {
+ x = (x<<8) | *eaddr;
+ }
+ MV_WRITE(MV643XX_ETH_MAC_ADDR_LO(mp->port_num), x);
+}
+
+static inline int
+port2phy(int port)
+{
+ port &= 0x1f;
+ /* during early init we may not know the phy and we are given a port number instead! */
+ return ( (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*port)) & 0x1f );
+}
+
+/* PHY/MII Interface
+ *
+ * Read/write a PHY register;
+ *
+ * NOTE: The SMI register is shared among the three devices.
+ * Protection is provided by the global networking semaphore.
+ * If non-bsd drivers are running on a subset of IFs proper
+ * locking of all shared registers must be implemented!
+ */
+static unsigned
+do_mii_read(int phy, unsigned addr)
+{
+unsigned v;
+unsigned wc = 0;
+
+ addr &= 0x1f;
+
+ /* wait until not busy */
+ do {
+ v = MV_READ(MV643XX_ETH_SMI_R);
+ wc++;
+ } while ( MV643XX_ETH_SMI_BUSY & v );
+
+ MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_RD );
+
+ do {
+ v = MV_READ(MV643XX_ETH_SMI_R);
+ wc++;
+ } while ( MV643XX_ETH_SMI_BUSY & v );
+
+ if (wc>0xffff)
+ wc = 0xffff;
+ return (wc<<16) | (v & 0xffff);
+}
+
+unsigned
+BSP_mve_mii_read(struct mveth_private *mp, unsigned addr)
+{
+unsigned rval = do_mii_read(mp->phy, addr);
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": BSP_mve_mii_read(%d): 0x%08x\n", addr, rval);
+#endif
+ return rval;
+}
+
+unsigned
+BSP_mve_mii_read_early(int port, unsigned addr)
+{
+ return do_mii_read(port2phy(port), addr);
+}
+
+static unsigned
+do_mii_write(int phy, unsigned addr, unsigned v)
+{
+unsigned wc = 0;
+
+ addr &= 0x1f;
+ v &= 0xffff;
+
+ /* busywait is ugly but not preventing ISRs or high priority tasks from
+ * preempting us
+ */
+
+ /* wait until not busy */
+ while ( MV643XX_ETH_SMI_BUSY & MV_READ(MV643XX_ETH_SMI_R) )
+ wc++ /* wait */;
+
+ MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_WR | v );
+
+ return wc;
+}
+
+unsigned
+BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v)
+{
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": BSP_mve_mii_write(%d): 0x%08x\n", addr, v);
+#endif
+ return do_mii_write( mp->phy, addr, v );
+}
+
+unsigned
+BSP_mve_mii_write_early(int port, unsigned addr, unsigned v)
+{
+ return do_mii_write(port2phy(port), addr, v);
+}
+
+
+/* MID-LAYER SUPPORT ROUTINES */
+
+/* Start TX if descriptors are exhausted */
+static __inline__ void
+mveth_start_tx(struct mveth_private *mp)
+{
+uint32_t running;
+ if ( mp->avail <= 0 ) {
+ running = MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num));
+ if ( ! (running & MV643XX_ETH_TX_START(0)) ) {
+ MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0));
+ }
+ }
+}
+
+/* Stop TX and wait for the command queues to stop and the fifo to drain */
+static uint32_t
+mveth_stop_tx(int port)
+{
+uint32_t active_q;
+
+ active_q = (MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) & MV643XX_ETH_TX_ANY_RUNNING);
+
+ if ( active_q ) {
+ /* Halt TX and wait for activity to stop */
+ MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port), MV643XX_ETH_TX_STOP_ALL);
+ while ( MV643XX_ETH_TX_ANY_RUNNING & MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) )
+ /* poll-wait */;
+ /* Wait for Tx FIFO to drain */
+ while ( ! (MV643XX_ETH_PORT_STATUS_R(port) & MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY) )
+ /* poll-wait */;
+ }
+
+ return active_q;
+}
+
+void
+BSP_mve_promisc_set(struct mveth_private *mp, int promisc)
+{
+uint32_t v;
+
+ v = MV_READ(MV643XX_ETH_PORT_CONFIG_R(mp->port_num));
+ if ( (mp->promisc = promisc) )
+ v |= MV643XX_ETH_UNICAST_PROMISC_MODE;
+ else
+ v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE;
+ MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), v);
+}
+
+/* update serial port settings from current link status */
+
+void
+BSP_mve_mcast_filter_clear(struct mveth_private *mp)
+{
+int i;
+register uint32_t s,o;
+uint32_t v = mp->promisc ? 0x01010101 : 0x00000000;
+ s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num);
+ o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num);
+ for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) {
+ MV_WRITE(s,v);
+ MV_WRITE(o,v);
+ s+=4;
+ o+=4;
+ }
+ for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) {
+ mp->mc_refcnt.specl[i] = 0;
+ mp->mc_refcnt.other[i] = 0;
+ }
+}
+
+void
+BSP_mve_mcast_filter_accept_all(struct mveth_private *mp)
+{
+int i;
+register uint32_t s,o;
+ s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num);
+ o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num);
+ for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) {
+ MV_WRITE(s,0x01010101);
+ MV_WRITE(o,0x01010101);
+ s+=4;
+ o+=4;
+ /* Not clear what we should do with the reference count.
+ * For now just increment it.
+ */
+ for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) {
+ mp->mc_refcnt.specl[i]++;
+ mp->mc_refcnt.other[i]++;
+ }
+ }
+}
+
+static void add_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt)
+{
+uint32_t val;
+uint32_t slot = hash & 0xfc;
+
+ if ( 0 == (*refcnt)[hash]++ ) {
+ val = MV_READ(off+slot) | ( 1 << ((hash&3)<<3) );
+ MV_WRITE(off+slot, val);
+ }
+}
+
+static void del_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt)
+{
+uint32_t val;
+uint32_t slot = hash & 0xfc;
+
+ if ( (*refcnt)[hash] > 0 && 0 == --(*refcnt)[hash] ) {
+ val = MV_READ(off+slot) & ~( 1 << ((hash&3)<<3) );
+ MV_WRITE(off+slot, val);
+ }
+}
+
+void
+BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr)
+{
+uint32_t hash;
+static const char spec[]={0x01,0x00,0x5e,0x00,0x00};
+static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff};
+uint32_t tabl;
+Mc_Refcnt *refcnt;
+
+ if ( ! (0x01 & enaddr[0]) ) {
+ /* not a multicast address; ignore */
+ return;
+ }
+
+ if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) {
+ /* broadcast address; ignore */
+ return;
+ }
+
+ if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) {
+ hash = enaddr[5];
+ tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num);
+ refcnt = &mp->mc_refcnt.specl;
+ } else {
+ uint32_t test, mask;
+ int i;
+ /* algorithm used by linux driver */
+ for ( hash=0, i=0; i<6; i++ ) {
+ hash = (hash ^ enaddr[i]) << 8;
+ for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) {
+ if ( hash & test )
+ hash ^= mask;
+ }
+ }
+ tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num);
+ refcnt = &mp->mc_refcnt.other;
+ }
+ add_entry(tabl, hash, refcnt);
+}
+
+void
+BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr)
+{
+uint32_t hash;
+static const char spec[]={0x01,0x00,0x5e,0x00,0x00};
+static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff};
+uint32_t tabl;
+Mc_Refcnt *refcnt;
+
+ if ( ! (0x01 & enaddr[0]) ) {
+ /* not a multicast address; ignore */
+ return;
+ }
+
+ if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) {
+ /* broadcast address; ignore */
+ return;
+ }
+
+ if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) {
+ hash = enaddr[5];
+ tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num);
+ refcnt = &mp->mc_refcnt.specl;
+ } else {
+ uint32_t test, mask;
+ int i;
+ /* algorithm used by linux driver */
+ for ( hash=0, i=0; i<6; i++ ) {
+ hash = (hash ^ enaddr[i]) << 8;
+ for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) {
+ if ( hash & test )
+ hash ^= mask;
+ }
+ }
+ tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num);
+ refcnt = &mp->mc_refcnt.other;
+ }
+ del_entry(tabl, hash, refcnt);
+}
+
+/* Clear all address filters (multi- and unicast) */
+static void
+mveth_clear_addr_filters(struct mveth_private *mp)
+{
+register int i;
+register uint32_t u;
+ u = MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num);
+ for (i=0; i<MV643XX_ETH_NUM_UNICAST_ENTRIES; i++) {
+ MV_WRITE(u,0);
+ u+=4;
+ }
+ BSP_mve_mcast_filter_clear(mp);
+}
+
+/* Setup unicast filter for a given MAC address (least significant nibble) */
+static void
+mveth_ucfilter(struct mveth_private *mp, unsigned char mac_lsbyte, int accept)
+{
+unsigned nib, slot, bit;
+uint32_t val;
+ /* compute slot in table */
+ nib = mac_lsbyte & 0xf; /* strip nibble */
+ slot = nib & ~3; /* (nibble/4)*4 */
+ bit = (nib & 3)<<3; /* 8*(nibble % 4) */
+ val = MV_READ(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot);
+ if ( accept ) {
+ val |= 0x01 << bit;
+ } else {
+ val &= 0x0e << bit;
+ }
+ MV_WRITE(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot, val);
+}
+
+#if defined( ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM ) && 0
+/* Currently unused; small unaligned buffers seem to be rare
+ * so we just use memcpy()...
+ */
+
+/* memcpy for 0..7 bytes; arranged so that gcc
+ * optimizes for powerpc...
+ */
+
+static inline void memcpy8(void *to, void *fr, unsigned x)
+{
+register uint8_t *d = to, *s = fro;
+
+ d+=l; s+=l;
+ if ( l & 1 ) {
+ *--d=*--s;
+ }
+ if ( l & 2 ) {
+ /* pre-decrementing causes gcc to use auto-decrementing
+ * PPC instructions (lhzu rx, -2(ry))
+ */
+ d-=2; s-=2;
+ /* use memcpy; don't cast to short -- accessing
+ * misaligned data as short is not portable
+ * (but it works on PPC).
+ */
+ __builtin_memcpy(d,s,2);
+ }
+ if ( l & 4 ) {
+ d-=4; s-=4;
+ /* see above */
+ __builtin_memcpy(d,s,4);
+ }
+}
+#endif
+
+static int
+mveth_assign_desc_raw(MvEthTxDesc d, void *buf, int len, void *uptr, unsigned long extra)
+{
+int rval = (d->byte_cnt = len);
+
+#ifdef MVETH_TESTING
+ assert( !d->u_buf );
+ assert( len );
+#endif
+
+ /* set CRC on all descriptors; seems to be necessary */
+ d->cmd_sts = extra | (TDESC_GEN_CRC | TDESC_ZERO_PAD);
+
+#ifdef ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM
+ /* The buffer must be 64bit aligned if the payload is <8 (??) */
+ if ( rval < 8 && ( ((uintptr_t)buf) & 7) ) {
+ d->buf_ptr = CPUADDR2ENET( d->workaround );
+ memcpy((void*)d->workaround, buf, rval);
+ } else
+#endif
+ {
+ d->buf_ptr = CPUADDR2ENET( (unsigned long)buf );
+ }
+ d->u_buf = uptr;
+ d->l4i_chk = 0;
+ return rval;
+}
+
+/*
+ * Ring Initialization
+ *
+ * ENDIAN ASSUMPTION: DMA engine matches CPU endianness (???)
+ *
+ * Linux driver discriminates __LITTLE and __BIG endian for re-arranging
+ * the u16 fields in the descriptor structs. However, no endian conversion
+ * is done on the individual fields (SDMA byte swapping is disabled on LE).
+ */
+
+STATIC int
+mveth_init_rx_desc_ring(struct mveth_private *mp)
+{
+int i,sz;
+MvEthRxDesc d;
+uintptr_t baddr;
+
+ memset((void*)mp->rx_ring, 0, sizeof(*mp->rx_ring)*mp->rbuf_count);
+
+ mp->rx_desc_dma = CPUADDR2ENET(mp->rx_ring);
+
+ for ( i=0, d = mp->rx_ring; i<mp->rbuf_count; i++, d++ ) {
+ d->u_buf = mp->alloc_rxbuf(&sz, &baddr);
+ assert( d->u_buf );
+
+#ifndef ENABLE_HW_SNOOPING
+ /* could reduce the area to max. ethernet packet size */
+ INVAL_BUF(baddr, sz);
+#endif
+
+ d->buf_size = sz;
+ d->byte_cnt = 0;
+ d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA;
+ d->next = mp->rx_ring + (i+1) % mp->rbuf_count;
+
+ d->buf_ptr = CPUADDR2ENET( baddr );
+ d->next_desc_ptr = CPUADDR2ENET(d->next);
+ FLUSH_DESC(d);
+ }
+ FLUSH_BARRIER();
+
+ mp->d_rx_t = mp->rx_ring;
+
+ /* point the chip to the start of the ring */
+ MV_WRITE(MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->rx_desc_dma);
+
+
+ return i;
+}
+
+STATIC int
+mveth_init_tx_desc_ring(struct mveth_private *mp)
+{
+int i;
+MvEthTxDesc d;
+
+ memset((void*)mp->tx_ring, 0, sizeof(*mp->tx_ring)*mp->xbuf_count);
+
+ /* DMA and CPU live in the same address space (rtems) */
+ mp->tx_desc_dma = CPUADDR2ENET(mp->tx_ring);
+ mp->avail = TX_AVAILABLE_RING_SIZE(mp);
+
+ for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++,d++ ) {
+ d->l4i_chk = 0;
+ d->byte_cnt = 0;
+ d->cmd_sts = 0;
+ d->buf_ptr = 0;
+
+ d->next = mp->tx_ring + (i+1) % mp->xbuf_count;
+ d->next_desc_ptr = CPUADDR2ENET(d->next);
+ FLUSH_DESC(d);
+ }
+ FLUSH_BARRIER();
+
+ mp->d_tx_h = mp->d_tx_t = mp->tx_ring;
+
+ /* point the chip to the start of the ring */
+ MV_WRITE(MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->tx_desc_dma);
+
+ return i;
+}
+
+/* PUBLIC LOW-LEVEL DRIVER ACCESS */
+
+struct mveth_private *
+BSP_mve_create(
+ int unit,
+ rtems_id tid,
+ void (*isr)(void*isr_arg),
+ void *isr_arg,
+ void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred),
+ void *cleanup_txbuf_arg,
+ void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr),
+ void (*consume_rxbuf)(void *user_buf, void *closure, int len),
+ void *consume_rxbuf_arg,
+ int rx_ring_size,
+ int tx_ring_size,
+ int irq_mask
+)
+{
+struct mveth_private *mp;
+int InstallISRSuccessful;
+
+ if ( unit <= 0 || unit > MV643XXETH_NUM_DRIVER_SLOTS ) {
+ printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, MV643XXETH_NUM_DRIVER_SLOTS);
+ return 0;
+ }
+
+ if ( rx_ring_size < 0 && tx_ring_size < 0 )
+ return 0;
+
+ if ( MV_64360 != BSP_getDiscoveryVersion(0) ) {
+ printk(DRVNAME": not mv64360 chip\n");
+ return 0;
+ }
+
+ if ( tx_ring_size < 1 ) {
+ printk(DRVNAME": tx ring size must not be zero (networking configuration issue?)\n");
+ return 0;
+ }
+
+ if ( rx_ring_size < 1 ) {
+ printk(DRVNAME": rx ring size must not be zero (networking configuration issue?)\n");
+ return 0;
+ }
+
+ mp = calloc( 1, sizeof *mp );
+
+ mp->port_num = unit-1;
+ mp->phy = port2phy(mp->port_num);
+
+ mp->tid = tid;
+ mp->isr = isr;
+ mp->isr_arg = isr_arg;
+
+ mp->cleanup_txbuf = cleanup_txbuf;
+ mp->cleanup_txbuf_arg = cleanup_txbuf_arg;
+ mp->alloc_rxbuf = alloc_rxbuf;
+ mp->consume_rxbuf = consume_rxbuf;
+ mp->consume_rxbuf_arg = consume_rxbuf_arg;
+
+ mp->rbuf_count = rx_ring_size;
+ mp->xbuf_count = tx_ring_size;
+
+ if ( mp->xbuf_count > 0 )
+ mp->xbuf_count += TX_NUM_TAG_SLOTS;
+
+ if ( mp->rbuf_count < 0 )
+ mp->rbuf_count = 0;
+ if ( mp->xbuf_count < 0 )
+ mp->xbuf_count = 0;
+
+ /* allocate ring area; add 1 entry -- room for alignment */
+ assert( !mp->ring_area );
+ mp->ring_area = malloc( sizeof(*mp->ring_area) * (mp->rbuf_count + mp->xbuf_count + 1) );
+ assert( mp->ring_area );
+
+ BSP_mve_stop_hw(mp);
+
+ if ( irq_mask ) {
+ irq_data[mp->port_num].hdl = mveth_isr;
+ irq_data[mp->port_num].handle = (rtems_irq_hdl_param)mp;
+ InstallISRSuccessful = BSP_install_rtems_irq_handler( &irq_data[mp->port_num] );
+ assert( InstallISRSuccessful );
+ }
+
+ if ( rx_ring_size < 0 )
+ irq_mask &= ~ MV643XX_ETH_IRQ_RX_DONE;
+ if ( tx_ring_size < 0 )
+ irq_mask &= ~ MV643XX_ETH_EXT_IRQ_TX_DONE;
+
+ mp->irq_mask = (irq_mask & MV643XX_ETH_IRQ_RX_DONE);
+ if ( (irq_mask &= (MV643XX_ETH_EXT_IRQ_TX_DONE | MV643XX_ETH_EXT_IRQ_LINK_CHG)) ) {
+ mp->irq_mask |= MV643XX_ETH_IRQ_EXT_ENA;
+ mp->xirq_mask = irq_mask;
+ } else {
+ mp->xirq_mask = 0;
+ }
+
+ return mp;
+}
+
+void
+BSP_mve_update_serial_port(struct mveth_private *mp, int media)
+{
+int port = mp->port_num;
+uint32_t old, new;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": Entering BSP_mve_update_serial_port()\n");
+#endif
+
+ new = old = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(port));
+
+ /* mask speed and duplex settings */
+ new &= ~( MV643XX_ETH_SET_GMII_SPEED_1000
+ | MV643XX_ETH_SET_MII_SPEED_100
+ | MV643XX_ETH_SET_FULL_DUPLEX );
+
+ if ( (MV643XX_MEDIA_FD & media) )
+ new |= MV643XX_ETH_SET_FULL_DUPLEX;
+
+ switch ( (media & MV643XX_MEDIA_SPEED_MSK) ) {
+ default: /* treat as 10 */
+ break;
+ case MV643XX_MEDIA_100:
+ new |= MV643XX_ETH_SET_MII_SPEED_100;
+ break;
+ case MV643XX_MEDIA_1000:
+ new |= MV643XX_ETH_SET_GMII_SPEED_1000;
+ break;
+ }
+
+
+ if ( new != old ) {
+ if ( ! (MV643XX_ETH_SERIAL_PORT_ENBL & new) ) {
+ /* just write */
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new);
+ } else {
+ uint32_t were_running;
+
+ were_running = mveth_stop_tx(port);
+
+ old &= ~MV643XX_ETH_SERIAL_PORT_ENBL;
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), old);
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new);
+ /* linux driver writes twice... */
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new);
+
+ if ( were_running ) {
+ MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0));
+ }
+ }
+ }
+ /* If TX stalled because there was no buffer then whack it */
+ mveth_start_tx(mp);
+}
+
+rtems_id
+BSP_mve_get_tid(struct mveth_private *mp)
+{
+ return mp->tid;
+}
+
+int
+BSP_mve_detach(struct mveth_private *mp)
+{
+ BSP_mve_stop_hw(mp);
+ if ( mp->irq_mask || mp->xirq_mask ) {
+ if ( !BSP_remove_rtems_irq_handler( &irq_data[mp->port_num] ) )
+ return -1;
+ }
+ free( (void*)mp->ring_area );
+ memset(mp, 0, sizeof(*mp));
+ __asm__ __volatile__("":::"memory");
+ return 0;
+}
+
+/* MAIN RX-TX ROUTINES
+ *
+ * BSP_mve_swipe_tx(): descriptor scavenger; releases mbufs
+ * BSP_mve_send_buf(): xfer mbufs from IF to chip
+ * BSP_mve_swipe_rx(): enqueue received mbufs to interface
+ * allocate new ones and yield them to the
+ * chip.
+ */
+
+/* clean up the TX ring freeing up buffers */
+int
+BSP_mve_swipe_tx(struct mveth_private *mp)
+{
+int rval = 0;
+register MvEthTxDesc d;
+
+ for ( d = mp->d_tx_t; d->buf_ptr; d = NEXT_TXD(d) ) {
+
+ INVAL_DESC(d);
+
+ if ( (TDESC_DMA_OWNED & d->cmd_sts)
+ && (uint32_t)d == MV_READ(MV643XX_ETH_CURRENT_SERVED_TX_DESC(mp->port_num)) )
+ break;
+
+ /* d->u_buf is only set on the last descriptor in a chain;
+ * we only count errors in the last descriptor;
+ */
+ if ( d->u_buf ) {
+ mp->cleanup_txbuf(d->u_buf, mp->cleanup_txbuf_arg, (d->cmd_sts & TDESC_ERROR) ? 1 : 0);
+ d->u_buf = 0;
+ }
+
+ d->buf_ptr = 0;
+
+ rval++;
+ }
+ mp->d_tx_t = d;
+ mp->avail += rval;
+
+ return mp->avail;
+}
+
+int
+BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it)
+{
+int rval;
+register MvEthTxDesc l,d,h;
+int nmbs;
+MveEthBufIter head = *it;
+
+ rval = 0;
+
+ /* if no descriptor is available; try to wipe the queue */
+ if ( (mp->avail < 1) && MVETH_CLEAN_ON_SEND(mp)<=0 ) {
+ /* Maybe TX is stalled and needs to be restarted */
+ mveth_start_tx(mp);
+ return -1;
+ }
+
+ h = mp->d_tx_h;
+
+#ifdef MVETH_TESTING
+ assert( !h->buf_ptr );
+ assert( !h->u_buf );
+#endif
+
+ /* Don't use the first descriptor yet because BSP_mve_swipe_tx()
+ * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we
+ * start with the second mbuf and fill the first descriptor
+ * last.
+ */
+
+ l = h;
+ d = NEXT_TXD(h);
+
+ mp->avail--;
+
+ nmbs = 1;
+ while ( (it = next(it)) ) {
+ if ( 0 == it->len )
+ continue; /* skip empty mbufs */
+
+ nmbs++;
+
+ if ( mp->avail < 1 && MVETH_CLEAN_ON_SEND(mp)<=0 ) {
+ /* Maybe TX was stalled - try to restart */
+ mveth_start_tx(mp);
+
+ /* not enough descriptors; cleanup...
+ * the first slot was never used, so we start
+ * at mp->d_tx_h->next;
+ */
+ for ( l = NEXT_TXD(h); l!=d; l=NEXT_TXD(l) ) {
+#ifdef MVETH_TESTING
+ assert( l->u_buf == 0 );
+#endif
+ l->buf_ptr = 0;
+ l->cmd_sts = 0;
+ mp->avail++;
+ }
+ mp->avail++;
+ if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) {
+ /* this chain will never fit into the ring */
+ if ( nmbs > mp->stats.maxchain )
+ mp->stats.maxchain = nmbs;
+ mp->stats.repack++;
+ /* caller may reorganize chain */
+ return -2;
+ }
+ return -1;
+ }
+
+ mp->avail--;
+
+#ifdef MVETH_TESTING
+ assert( d != h );
+ assert( !d->buf_ptr );
+#endif
+
+ /* fill this slot */
+ rval += mveth_assign_desc_raw(d, it->data, it->len, it->uptr, TDESC_DMA_OWNED);
+
+ FLUSH_BUF( (uint32_t)it->data, it->len );
+
+ l = d;
+ d = NEXT_TXD(d);
+
+ FLUSH_DESC(l);
+ }
+
+ /* fill first slot - don't release to DMA yet */
+ rval += mveth_assign_desc_raw(h, head.data, head.len, head.uptr, TDESC_FRST);
+
+
+ FLUSH_BUF((uint32_t)head.data, head.len);
+
+
+ /* tag last slot; this covers the case where 1st==last */
+ l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA;
+
+ FLUSH_DESC(l);
+
+ /* Tag end; make sure chip doesn't try to read ahead of here! */
+ l->next->cmd_sts = 0;
+ FLUSH_DESC(l->next);
+
+ membarrier();
+
+ /* turn over the whole chain by flipping ownership of the first desc */
+ h->cmd_sts |= TDESC_DMA_OWNED;
+
+ FLUSH_DESC(h);
+
+ membarrier();
+
+ /* notify the device */
+ MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0));
+
+ /* Update softc */
+ mp->stats.packet++;
+ if ( nmbs > mp->stats.maxchain )
+ mp->stats.maxchain = nmbs;
+
+ /* remember new head */
+ mp->d_tx_h = d;
+
+ return rval; /* #bytes sent */
+}
+
+int
+BSP_mve_send_buf_raw(
+ struct mveth_private *mp,
+ void *head_p,
+ int h_len,
+ void *data_p,
+ int d_len)
+{
+int rval;
+register MvEthTxDesc l,d,h;
+int needed;
+void *frst_buf;
+int frst_len;
+void *uarg;
+
+ rval = 0;
+
+#ifdef MVETH_TESTING
+ assert(head_p || data_p);
+#endif
+
+ needed = head_p && data_p ? 2 : 1;
+
+ /* if no descriptor is available; try to wipe the queue */
+ if ( ( mp->avail < needed )
+ && ( MVETH_CLEAN_ON_SEND(mp) <= 0 || mp->avail < needed ) ) {
+ /* Maybe TX was stalled and needs a restart */
+ mveth_start_tx(mp);
+ return -1;
+ }
+
+ h = mp->d_tx_h;
+
+#ifdef MVETH_TESTING
+ assert( !h->buf_ptr );
+ assert( !h->u_buf );
+#endif
+
+ /* find the 'first' user buffer */
+ if ( (frst_buf = head_p) && (h_len > 0) ) {
+ frst_len = h_len;
+ } else {
+ frst_buf = data_p;
+ frst_len = d_len;
+ }
+
+ uarg = (head_p && ! h_len) ? head_p : frst_buf;
+
+ /* Legacy: if h_len == 0 but head_p is not then use that for the user arg */
+
+ /* Don't use the first descriptor yet because BSP_mve_swipe_tx()
+ * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we
+ * start with the second (optional) slot and fill the first
+ * descriptor last.
+ */
+
+ l = h;
+ d = NEXT_TXD(h);
+
+ mp->avail--;
+
+ if ( needed > 1 ) {
+ mp->avail--;
+#ifdef MVETH_TESTING
+ assert( d != h );
+ assert( !d->buf_ptr );
+#endif
+ rval += mveth_assign_desc_raw(d, data_p, d_len, 0, TDESC_DMA_OWNED);
+ FLUSH_BUF( (uint32_t)data_p, d_len );
+ d->u_buf = data_p;
+
+ l = d;
+ d = NEXT_TXD(d);
+
+ FLUSH_DESC(l);
+ }
+
+ /* fill first slot with raw buffer - don't release to DMA yet */
+ rval += mveth_assign_desc_raw(h, frst_buf, frst_len, 0, TDESC_FRST);
+
+ FLUSH_BUF( (uint32_t)frst_buf, frst_len);
+
+ /* tag last slot; this covers the case where 1st==last */
+ l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA;
+
+ /* first buffer of 'chain' goes into last desc */
+ l->u_buf = uarg;
+
+ FLUSH_DESC(l);
+
+ /* Tag end; make sure chip doesn't try to read ahead of here! */
+ l->next->cmd_sts = 0;
+ FLUSH_DESC(l->next);
+
+ membarrier();
+
+ /* turn over the whole chain by flipping ownership of the first desc */
+ h->cmd_sts |= TDESC_DMA_OWNED;
+
+ FLUSH_DESC(h);
+
+ membarrier();
+
+ /* notify the device */
+ MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0));
+
+ /* Update softc */
+ mp->stats.packet++;
+ if ( needed > mp->stats.maxchain )
+ mp->stats.maxchain = needed;
+
+ /* remember new head */
+ mp->d_tx_h = d;
+
+ return rval; /* #bytes sent */
+}
+
+/* send received buffers upwards and replace them
+ * with freshly allocated ones;
+ * ASSUMPTION: buffer length NEVER changes and is set
+ * when the ring is initialized.
+ * TS 20060727: not sure if this assumption is still necessary - I believe it isn't.
+ */
+
+int
+BSP_mve_swipe_rx(struct mveth_private *mp)
+{
+int rval = 0, err;
+register MvEthRxDesc d;
+void *newbuf;
+int sz;
+uintptr_t baddr;
+
+ for ( d = mp->d_rx_t; ! (INVAL_DESC(d), (RDESC_DMA_OWNED & d->cmd_sts)); d=NEXT_RXD(d) ) {
+
+#ifdef MVETH_TESTING
+ assert(d->u_buf);
+#endif
+
+ err = (RDESC_ERROR & d->cmd_sts);
+
+ if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) {
+ /* drop packet and recycle buffer */
+ newbuf = d->u_buf;
+ mp->stats.idrops++;
+ } else {
+#ifdef MVETH_TESTING
+ assert( d->byte_cnt > 0 );
+#endif
+ mp->consume_rxbuf(d->u_buf, mp->consume_rxbuf_arg, d->byte_cnt);
+
+#ifndef ENABLE_HW_SNOOPING
+ /* could reduce the area to max. ethernet packet size */
+ INVAL_BUF(baddr, sz);
+#endif
+ d->u_buf = newbuf;
+ d->buf_ptr = CPUADDR2ENET(baddr);
+ d->buf_size = sz;
+ FLUSH_DESC(d);
+ }
+
+ membarrier();
+
+ d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA;
+
+ FLUSH_DESC(d);
+
+ rval++;
+ }
+ MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0));
+ mp->d_rx_t = d;
+ return rval;
+}
+
+/* Stop hardware and clean out the rings */
+void
+BSP_mve_stop_hw(struct mveth_private *mp)
+{
+MvEthTxDesc d;
+MvEthRxDesc r;
+int i;
+
+ mveth_disable_irqs(mp, -1);
+
+ mveth_stop_tx(mp->port_num);
+
+ /* cleanup TX rings */
+ if (mp->d_tx_t) { /* maybe ring isn't initialized yet */
+ for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) {
+ /* should be safe to clear ownership */
+ d->cmd_sts &= ~TDESC_DMA_OWNED;
+ FLUSH_DESC(d);
+ }
+ FLUSH_BARRIER();
+
+ BSP_mve_swipe_tx(mp);
+
+#ifdef MVETH_TESTING
+ assert( mp->d_tx_h == mp->d_tx_t );
+ for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) {
+ assert( !d->buf_ptr );
+ }
+#endif
+ }
+
+ MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_STOP_ALL);
+ while ( MV643XX_ETH_RX_ANY_RUNNING & MV_READ(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num)) )
+ /* poll-wait */;
+
+ /* stop serial port */
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num),
+ MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num))
+ & ~( MV643XX_ETH_SERIAL_PORT_ENBL | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE | MV643XX_ETH_FORCE_LINK_PASS)
+ );
+
+ /* clear pending interrupts */
+ MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0);
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0);
+
+ /* cleanup RX rings */
+ if ( mp->rx_ring ) {
+ for ( i=0, r=mp->rx_ring; i<mp->rbuf_count; i++, r++ ) {
+ /* should be OK to clear ownership flag */
+ r->cmd_sts = 0;
+ FLUSH_DESC(r);
+ mp->consume_rxbuf(r->u_buf, mp->consume_rxbuf_arg, 0);
+ r->u_buf = 0;
+ }
+ FLUSH_BARRIER();
+ }
+
+
+}
+
+uint32_t mveth_serial_ctrl_config_val = MVETH_SERIAL_CTRL_CONFIG_VAL;
+
+/* Fire up the low-level driver
+ *
+ * - make sure hardware is halted
+ * - enable cache snooping
+ * - clear address filters
+ * - clear mib counters
+ * - reset phy
+ * - initialize (or reinitialize) descriptor rings
+ * - check that the firmware has set up a reasonable mac address.
+ * - generate unicast filter entry for our mac address
+ * - write register config values to the chip
+ * - start hardware (serial port and SDMA)
+ */
+
+void
+BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int media)
+{
+int i;
+uint32_t v;
+static int inited = 0;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"%i: Entering BSP_mve_init_hw()\n", mp->port_num+1);
+#endif
+
+ /* since enable/disable IRQ routine only operate on select bitsets
+ * we must make sure everything is masked initially.
+ */
+ MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), 0);
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), 0);
+
+ BSP_mve_stop_hw(mp);
+
+ memset(&mp->stats, 0, sizeof(mp->stats));
+
+ mp->promisc = promisc;
+
+ /* MotLoad has cache snooping disabled on the ENET2MEM windows.
+ * Some comments in (linux) indicate that there are errata
+ * which cause problems which would be a real bummer.
+ * We try it anyways...
+ */
+ if ( !inited ) {
+ unsigned long disbl, bar;
+ inited = 1; /* FIXME: non-thread safe lazy init */
+ disbl = MV_READ(MV643XX_ETH_BAR_ENBL_R);
+ /* disable all 6 windows */
+ MV_WRITE(MV643XX_ETH_BAR_ENBL_R, MV643XX_ETH_BAR_DISBL_ALL);
+ /* set WB snooping on enabled bars */
+ for ( i=0; i<MV643XX_ETH_NUM_BARS*8; i+=8 ) {
+ if ( (bar = MV_READ(MV643XX_ETH_BAR_0 + i)) && MV_READ(MV643XX_ETH_SIZE_R_0 + i) ) {
+#ifdef ENABLE_HW_SNOOPING
+ MV_WRITE(MV643XX_ETH_BAR_0 + i, bar | MV64360_ENET2MEM_SNOOP_WB);
+#else
+ MV_WRITE(MV643XX_ETH_BAR_0 + i, bar & ~MV64360_ENET2MEM_SNOOP_MSK);
+#endif
+ /* read back to flush fifo [linux comment] */
+ (void)MV_READ(MV643XX_ETH_BAR_0 + i);
+ }
+ }
+ /* restore/re-enable */
+ MV_WRITE(MV643XX_ETH_BAR_ENBL_R, disbl);
+ }
+
+ mveth_clear_mib_counters(mp);
+ mveth_clear_addr_filters(mp);
+
+/* Just leave it alone...
+ reset_phy();
+*/
+
+ if ( mp->rbuf_count > 0 ) {
+ mp->rx_ring = (MvEthRxDesc)MV643XX_ALIGN(mp->ring_area, RING_ALIGNMENT);
+ mveth_init_rx_desc_ring(mp);
+ }
+
+ if ( mp->xbuf_count > 0 ) {
+ mp->tx_ring = (MvEthTxDesc)mp->rx_ring + mp->rbuf_count;
+ mveth_init_tx_desc_ring(mp);
+ }
+
+ if ( enaddr ) {
+ /* set ethernet address from arpcom struct */
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"%i: Writing MAC addr ", mp->port_num+1);
+ for (i=5; i>=0; i--) {
+ printk("%02X%c", enaddr[i], i?':':'\n');
+ }
+#endif
+ mveth_write_eaddr(mp, enaddr);
+ }
+
+ /* set mac address and unicast filter */
+
+ {
+ uint32_t machi, maclo;
+ maclo = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num));
+ machi = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num));
+ /* ASSUME: firmware has set the mac address for us
+ * - if assertion fails, we have to do more work...
+ */
+ assert( maclo && machi && maclo != 0xffffffff && machi != 0xffffffff );
+ mveth_ucfilter(mp, maclo&0xff, 1/* accept */);
+ }
+
+ /* port, serial and sdma configuration */
+ v = MVETH_PORT_CONFIG_VAL;
+ if ( promisc ) {
+ /* multicast filters were already set up to
+ * accept everything (mveth_clear_addr_filters())
+ */
+ v |= MV643XX_ETH_UNICAST_PROMISC_MODE;
+ } else {
+ v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE;
+ }
+ MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num),
+ v);
+ MV_WRITE(MV643XX_ETH_PORT_CONFIG_XTEND_R(mp->port_num),
+ MVETH_PORT_XTEND_CONFIG_VAL);
+
+ v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num));
+ v &= ~(MVETH_SERIAL_CTRL_CONFIG_MSK);
+ v |= mveth_serial_ctrl_config_val;
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), v);
+
+ if ( (MV643XX_MEDIA_LINK & media) ) {
+ BSP_mve_update_serial_port(mp, media);
+ }
+
+ /* enable serial port */
+ v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num));
+ MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num),
+ v | MV643XX_ETH_SERIAL_PORT_ENBL);
+
+#ifndef __BIG_ENDIAN__
+#error "byte swapping needs to be disabled for little endian machines"
+#endif
+ MV_WRITE(MV643XX_ETH_SDMA_CONFIG_R(mp->port_num), MVETH_SDMA_CONFIG_VAL);
+
+ /* allow short frames */
+ MV_WRITE(MV643XX_ETH_RX_MIN_FRAME_SIZE_R(mp->port_num), MVETH_MIN_FRAMSZ_CONFIG_VAL);
+
+ MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0);
+ MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0);
+ /* TODO: set irq coalescing */
+
+ /* enable Rx */
+ if ( mp->rbuf_count > 0 ) {
+ MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0));
+ }
+
+ mveth_enable_irqs(mp, -1);
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"%i: Leaving BSP_mve_init_hw()\n", mp->port_num+1);
+#endif
+}
+
+/* read ethernet address from hw to buffer */
+void
+BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr)
+{
+int i;
+uint32_t x;
+unsigned char buf[6], *eaddr;
+
+ eaddr = oeaddr ? oeaddr : buf;
+
+ eaddr += 5;
+ x = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num));
+
+ /* lo word */
+ for (i=2; i; i--, eaddr--) {
+ *eaddr = (unsigned char)(x & 0xff);
+ x>>=8;
+ }
+
+ x = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num));
+ /* hi word */
+ for (i=4; i; i--, eaddr--) {
+ *eaddr = (unsigned char)(x & 0xff);
+ x>>=8;
+ }
+
+ if ( !oeaddr ) {
+ printf("%02X",buf[0]);
+ for (i=1; i<sizeof(buf); i++)
+ printf(":%02X",buf[i]);
+ printf("\n");
+ }
+}
+
+void
+BSP_mve_enable_irqs(struct mveth_private *mp)
+{
+ mveth_enable_irqs(mp, -1);
+}
+
+void
+BSP_mve_disable_irqs(struct mveth_private *mp)
+{
+ mveth_disable_irqs(mp, -1);
+}
+
+uint32_t
+BSP_mve_ack_irqs(struct mveth_private *mp)
+{
+ return mveth_ack_irqs(mp, -1);
+}
+
+
+void
+BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t mask)
+{
+ mveth_enable_irqs(mp, mask);
+}
+
+uint32_t
+BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t mask)
+{
+ return mveth_disable_irqs(mp, mask);
+}
+
+uint32_t
+BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask)
+{
+ return mveth_ack_irqs(mp, mask);
+}
+
+void
+BSP_mve_dump_stats(struct mveth_private *mp, FILE *f)
+{
+int p = mp->port_num;
+int idx;
+uint32_t v;
+
+ if ( !f )
+ f = stdout;
+
+ fprintf(f, DRVNAME"%i Statistics:\n", mp->port_num + 1);
+ fprintf(f, " # IRQS: %i\n", mp->stats.irqs);
+ fprintf(f, " Max. mbuf chain length: %i\n", mp->stats.maxchain);
+ fprintf(f, " # repacketed: %i\n", mp->stats.repack);
+ fprintf(f, " # packets: %i\n", mp->stats.packet);
+ fprintf(f, " # buffer alloc failed: %i\n", mp->stats.idrops);
+ fprintf(f, "MIB Counters:\n");
+ for ( idx = MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2;
+ idx < MV643XX_ETH_NUM_MIB_COUNTERS;
+ idx++ ) {
+ switch ( idx ) {
+ case MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2:
+ mp->stats.mib.good_octs_rcvd += read_long_mib_counter(p, idx);
+ fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_rcvd);
+ idx++;
+ break;
+
+ case MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO>>2:
+ mp->stats.mib.good_octs_sent += read_long_mib_counter(p, idx);
+ fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_sent);
+ idx++;
+ break;
+
+ default:
+ v = ((uint32_t*)&mp->stats.mib)[idx] += read_mib_counter(p, idx);
+ fprintf(f, mibfmt[idx], v);
+ break;
+ }
+ }
+ fprintf(f, "\n");
+}
+
+#ifdef MVETH_DEBUG
+/* Display/dump descriptor rings */
+
+/* These low-level routines need to be synchronized with
+ * any Tx/Rx threads!
+ */
+int
+BSP_mve_dring_nonsync(struct mveth_private *mp)
+{
+int i;
+if (1) {
+MvEthRxDesc pr;
+printf("RX:\n");
+
+ for (i=0, pr=mp->rx_ring; i<mp->rbuf_count; i++, pr++) {
+ printf("cnt: 0x%04x, size: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n",
+ pr->byte_cnt, pr->buf_size, pr->cmd_sts, (uint32_t)pr->next_desc_ptr, pr->buf_ptr);
+ }
+}
+if (1) {
+MvEthTxDesc pt;
+printf("TX:\n");
+ for (i=0, pt=mp->tx_ring; i<mp->xbuf_count; i++, pt++) {
+ printf("cnt: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x, mb: 0x%08x\n",
+ pt->byte_cnt, pt->cmd_sts, (uint32_t)pt->next_desc_ptr, pt->buf_ptr,
+ (uint32_t)pt->u_buf);
+ }
+}
+ return 0;
+}
+#endif
+
+#endif /* LIBBSP_BEATNIK_BSP_H */
diff --git a/rtemsbsd/sys/dev/mve/if_mve_nexus.c b/rtemsbsd/sys/dev/mve/if_mve_nexus.c
new file mode 100644
index 00000000..be9433da
--- /dev/null
+++ b/rtemsbsd/sys/dev/mve/if_mve_nexus.c
@@ -0,0 +1,935 @@
+/* RTEMS driver for the mv643xx gigabit ethernet chip */
+
+/* Acknowledgement:
+ *
+ * Valuable information for developing this driver was obtained
+ * from the linux open-source driver mv643xx_eth.c which was written
+ * by the following people and organizations:
+ *
+ * Matthew Dharm <mdharm@momenco.com>
+ * rabeeh@galileo.co.il
+ * PMC-Sierra, Inc., Manish Lachwani
+ * Ralf Baechle <ralf@linux-mips.org>
+ * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org>
+ * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com>
+ *
+ * Note however, that in spite of the identical name of this file
+ * (and some of the symbols used herein) this file provides a
+ * new implementation and is the original work by the author.
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software (mv643xx ethernet driver for RTEMS) was
+ * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The 'mv643xx ethernet driver for RTEMS' was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+/* Nexus port by Till Straumann, <till.straumann@psi.ch>, 3/2021 */
+
+#include <machine/rtems-bsd-kernel-space.h>
+#include <bsp.h>
+
+#ifdef LIBBSP_BEATNIK_BSP_H
+
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <rtems/bsd/local/miibus_if.h>
+#include <stdio.h>
+#include <bsp/mv643xx_eth.h>
+
+#define DRVNAME "mv63xx_nexus"
+
+#undef MVETH_DEBUG
+
+/* Define default ring sizes */
+
+#undef MVETH_TESTING
+
+#ifdef MVETH_TESTING
+/* hard and small defaults */
+#define MV643XX_RX_RING_SIZE 2
+#define MV643XX_TX_QUEUE_SIZE 4
+#define MV643XX_BD_PER_PACKET 1
+#define TX_LOWWATER 1
+
+#else /* MVETH_TESTING */
+
+#define MV643XX_RX_RING_SIZE 40 /* attached buffers are always 2k clusters, i.e., this
+ * driver - with a configured ring size of 40 - constantly
+ * locks 80k of cluster memory - your app config better
+ * provides enough space!
+ */
+#define MV643XX_TX_QUEUE_SIZE 40
+#define MV643XX_BD_PER_PACKET 10
+#define TX_LOWWATER (4*(MV643XX_BD_PER_PACKET))
+#endif /* MVETH_TESTING */
+
+/* NOTE: tx ring size MUST be > max. # of fragments / mbufs in a chain;
+ * I observed chains of >17 entries regularly!
+ */
+#define MV643XX_TX_RING_SIZE ((MV643XX_TX_QUEUE_SIZE) * (MV643XX_BD_PER_PACKET))
+
+/* The chip puts the ethernet header at offset 2 into the buffer so
+ * that the payload is aligned
+ */
+#define ETH_RX_OFFSET 2
+#define ETH_CRC_LEN 4 /* strip FCS at end of packet */
+
+#ifndef __PPC__
+#error "Dont' know how to deal with cache on this CPU architecture"
+#endif
+
+/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency
+ * management works for cache line sizes of 16 and 32 bytes only. If the line size
+ * is bigger, the descriptors could be padded...
+ */
+#if !defined(PPC_CACHE_ALIGNMENT)
+#error "PPC_CACHE_ALIGNMENT not defined"
+#elif PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32
+#error "Cache line size must be 16 or 32"
+#else
+#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT
+#endif
+
+/* HELPER MACROS */
+
+/* Align base to alignment 'a' */
+#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1)))
+
+
+#define IRQ_EVENT RTEMS_EVENT_0
+#define TX_EVENT RTEMS_EVENT_1
+
+/* Hacks -- FIXME */
+rtems_id
+rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg);
+#define SIO_RTEMS_SHOW_STATS _IO('i', 250)
+
+#define MVE643XX_DUMMY_PHY 0 /* phy is defined by low-level driver */
+
+struct mve_enet_softc {
+ device_t dev;
+ struct ifnet *ifp;
+ device_t miibus;
+ struct mii_data *mii_softc;
+ struct mveth_private *mp;
+ struct mtx mtx;
+ struct callout wdCallout;
+ rtems_id daemonTid;
+ int oif_flags;
+};
+
+static struct mve_enet_softc * ifaces[MV643XXETH_NUM_DRIVER_SLOTS] = { 0 };
+
+typedef struct MveMbufIter {
+ MveEthBufIter it;
+ struct mbuf *next;
+ struct mbuf *head;
+} MveMbufIter;
+
+/* Forward Declarations */
+struct mve_enet_softc;
+
+static void
+mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
+
+static int
+mve_media_change(struct ifnet *ifp);
+
+static void
+mve_set_filters(struct ifnet *ifp);
+
+static int
+xlateMediaFlags(const struct mii_data *mid);
+
+static void
+mve_ack_link_change(struct mve_enet_softc *sc);
+
+static __inline__ void
+mve_send_event(struct mve_enet_softc *sc, rtems_event_set ev)
+{
+rtems_status_code st;
+ if ( RTEMS_SUCCESSFUL != (st = rtems_event_send(sc->daemonTid, ev)) ) {
+ printk(DRVNAME": rtems_event_send returned 0x%08x (TID: 0x%08x, sc: 0x%08x)\n", st, sc->daemonTid, sc);
+ rtems_panic(DRVNAME": rtems_event_send() failed!\n");
+ }
+}
+
+static __inline__ void
+mve_lock(struct mve_enet_softc *sc, const char *from)
+{
+ mtx_lock( & sc->mtx );
+/*printk("L V %s\n", from);*/
+}
+
+static __inline__ void
+mve_unlock(struct mve_enet_softc *sc, const char *from)
+{
+/*printk("L ^ %s\n", from);*/
+ mtx_unlock( & sc->mtx );
+}
+
+static int
+mve_probe(device_t dev)
+{
+ int unit = device_get_unit(dev);
+ int err;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_probe (entering)\n");
+#endif
+
+ if ( unit >= 0 && unit < MV643XXETH_NUM_DRIVER_SLOTS ) {
+ err = BUS_PROBE_DEFAULT;
+ } else {
+ err = ENXIO;
+ }
+
+ return err;
+}
+
+/*
+ * starting at 'm' scan the buffer chain until we
+ * find a non-empty buffer (which we return)
+ */
+static __inline__ struct mbuf *
+skipEmpty(struct mbuf *m)
+{
+ while ( m && ( 0 == m->m_len ) ) {
+ m = m->m_next;
+ }
+ return m;
+}
+
+/*
+ * Record a buffer's info in the low-leve driver 'iterator' struct.
+ * Also scan ahead to find the next non-empty buffer (store it in
+ * the iterator's 'next' field). This info is needed because we
+ * want to know if 'this' buffer is the last (non-empty!) one
+ * in a chain.
+ *
+ * On entry 'it->next' identifies 'this' buffer and on return
+ * 'it->next' points to the next non-empty buffer.
+ */
+static MveEthBufIter *
+nextBuf(MveEthBufIter *arg)
+{
+MveMbufIter *it = (MveMbufIter*)arg;
+struct mbuf *m;
+ /* If 'this' buffer is non-null */
+ if ( (m = it->next) ) {
+ /* find next non-empty buffer */
+ it->next = skipEmpty( m->m_next );
+ /* record 'this' buffer's info */
+ it->it.data = mtod(m, void*);
+ it->it.len = m->m_len;
+ /* if there is a non-empty buffer after 'this' uptr is NULL
+ * if this is tha last buffer in a chain then record the
+ * head of the chain in the uptr (for eventual cleanup
+ * by release_tx_mbuf()).
+ */
+ it->it.uptr = it->next ? 0 : it->head;
+ return (MveEthBufIter*)it;
+ }
+ return 0;
+}
+
+/*
+ * Initialize the iterator struct
+ */
+static MveEthBufIter *
+initIter(MveMbufIter *it, struct mbuf *m)
+{
+ /* record the head of the chain */
+ it->head = m;
+ /* initialize 'next' field to the first non-empty buffer.
+ * This may be NULL if the chain is entirely empty but
+ * that is handled correctly.
+ */
+ it->next = skipEmpty( m );
+ /* Fill with first buf info */
+ return nextBuf( &it->it );
+}
+
+static int
+mve_send_mbuf( struct mve_enet_softc *sc, struct mbuf *m_head )
+{
+MveMbufIter iter;
+int rval;
+
+ if ( ! m_head ) {
+ return 0;
+ }
+
+ if ( ! initIter( &iter, m_head ) ) {
+ /* completely empty chain */
+ m_freem( m_head );
+ return 0;
+ }
+
+ rval = BSP_mve_send_buf_chain( sc->mp, nextBuf, &iter.it );
+
+ return rval;
+}
+
+static void
+mve_isr(void *closure)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*)closure;
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_isr; posting event to %x\n", sc->daemonTid);
+#endif
+ BSP_mve_disable_irqs( sc->mp );
+ mve_send_event( sc, IRQ_EVENT );
+}
+
+static void
+mve_stop(struct mve_enet_softc *sc)
+{
+ BSP_mve_stop_hw( sc->mp );
+ /* clear IF flags */
+ if_setdrvflagbits(sc->ifp, 0, (IFF_DRV_OACTIVE | IFF_DRV_RUNNING));
+}
+
+static void
+mve_set_filters(struct ifnet *ifp)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp );
+int iff = if_getflags(ifp);
+struct ifmultiaddr *ifma;
+unsigned char *lladdr;
+
+ BSP_mve_promisc_set( sc->mp, !!(iff & IFF_PROMISC));
+
+ if ( iff & (IFF_PROMISC | IFF_ALLMULTI) ) {
+ BSP_mve_mcast_filter_accept_all(sc->mp);
+ } else {
+ BSP_mve_mcast_filter_clear( sc->mp );
+
+ if_maddr_rlock( ifp );
+
+ CK_STAILQ_FOREACH( ifma, &ifp->if_multiaddrs, ifma_link ) {
+
+ if ( ifma->ifma_addr->sa_family != AF_LINK ) {
+ continue;
+ }
+
+ lladdr = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
+
+ BSP_mve_mcast_filter_accept_add( sc->mp, lladdr );
+
+ }
+
+ if_maddr_runlock( ifp );
+ }
+}
+
+/* Daemon task does all the 'interrupt' work */
+static void
+mve_daemon(void *arg)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) arg;
+struct ifnet *ifp = sc->ifp;
+rtems_event_set evs;
+struct mbuf *m;
+int avail;
+int sndStat;
+uint32_t irqstat;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": bsdnet mveth_daemon started\n");
+#endif
+
+ mve_lock( sc, "daemon" );
+
+ for (;;) {
+
+ mve_unlock( sc, "daemon" );
+ if ( RTEMS_SUCCESSFUL != rtems_event_receive( (IRQ_EVENT | TX_EVENT), (RTEMS_WAIT | RTEMS_EVENT_ANY), RTEMS_NO_TIMEOUT, &evs ) ) {
+ rtems_panic(DRVNAME": rtems_event_receive() failed!\n");
+ }
+ mve_lock( sc, "daemon" );
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": bsdnet mveth_daemon event received 0x%x\n", evs);
+#endif
+
+ if ( !(if_getflags(ifp) & IFF_UP) ) {
+ mve_stop(sc);
+ /* clear flag */
+ if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING);
+ continue;
+ }
+
+ if ( ! (if_getdrvflags(ifp) & IFF_DRV_RUNNING) ) {
+ /* event could have been pending at the time hw was stopped;
+ * just ignore...
+ */
+ continue;
+ }
+
+ if ( (evs & IRQ_EVENT) ) {
+ irqstat = BSP_mve_ack_irqs(sc->mp);
+ } else {
+ irqstat = 0;
+ }
+
+ if ( (MV643XX_ETH_EXT_IRQ_LINK_CHG & irqstat) && sc->mii_softc ) {
+ /* phy status changed */
+ mii_pollstat( sc->mii_softc );
+ }
+
+ /* free tx chain and send */
+ if ( (evs & TX_EVENT) || (MV643XX_ETH_EXT_IRQ_TX_DONE & irqstat) ) {
+ while ( (avail = BSP_mve_swipe_tx( sc->mp )) > TX_LOWWATER ) {
+ IF_DEQUEUE( &ifp->if_snd, m );
+ if ( ! m ) {
+ /* clear active bit */
+ if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
+ break;
+ }
+ sndStat = mve_send_mbuf( sc, m );
+ if ( sndStat < 0 ) {
+ /* maybe not enough space right now; requeue and wait for next IRQ */
+ IF_PREPEND( &ifp->if_snd, m );
+ break;
+ }
+ }
+ }
+ if ( (MV643XX_ETH_IRQ_RX_DONE & irqstat) ) {
+ BSP_mve_swipe_rx(sc->mp);
+ }
+
+ BSP_mve_enable_irqs(sc->mp);
+ }
+
+ mve_unlock( sc, "daemon (xit)" );
+}
+
+static void
+release_tx_mbuf(void *user_buf, void *closure, int error_on_tx_occurred)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*)closure;
+struct ifnet *ifp = sc->ifp;
+struct mbuf *mb = (struct mbuf*)user_buf;
+
+ if ( error_on_tx_occurred ) {
+ if_inc_counter( ifp, IFCOUNTER_OERRORS, 1 );
+ } else {
+ if_inc_counter( ifp, IFCOUNTER_OPACKETS, 1 );
+ if_inc_counter( ifp, IFCOUNTER_OBYTES, mb->m_pkthdr.len );
+ }
+ m_freem(mb);
+}
+
+static void *
+alloc_rx_mbuf(int *p_size, uintptr_t *p_data)
+{
+struct mbuf *m;
+unsigned long l,o;
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+
+ if ( m ) {
+
+ o = mtod(m, unsigned long);
+ l = MV643XX_ALIGN(o, RX_BUF_ALIGNMENT) - o;
+
+ /* align start of buffer */
+ m->m_data += l;
+
+ /* reduced length */
+ l = MCLBYTES - l;
+
+ m->m_len = m->m_pkthdr.len = l;
+ *p_size = m->m_len;
+ *p_data = mtod(m, uintptr_t);
+ }
+
+ return (void*) m;
+}
+
+
+static void
+consume_rx_mbuf(void *user_buf, void *closure, int len)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*)closure;
+struct ifnet *ifp = sc->ifp;
+struct mbuf *m = (struct mbuf*)user_buf;
+
+ if ( len <= 0 ) {
+ if_inc_counter( ifp, IFCOUNTER_IQDROPS, 1 );
+ if ( len < 0 ) {
+ if_inc_counter( ifp, IFCOUNTER_IERRORS, 1 );
+ }
+ m_freem(m);
+ } else {
+ m->m_len = m->m_pkthdr.len = len - ETH_RX_OFFSET - ETH_CRC_LEN;
+ m->m_data += ETH_RX_OFFSET;
+ m->m_pkthdr.rcvif = ifp;
+
+ if_inc_counter( ifp, IFCOUNTER_IPACKETS, 1 );
+ if_inc_counter( ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len );
+
+ if (0) {
+ /* Low-level debugging */
+ int i;
+ for (i=0; i<m->m_len; i++) {
+ if ( !(i&15) )
+ printk("\n");
+ printk("0x%02x ",mtod(m,char*)[i]);
+ }
+ printk("\n");
+ }
+
+ mve_unlock( sc, "rx_cleanup" );
+ (*ifp->if_input)(ifp, m);
+ mve_lock( sc, "rx_cleanup" );
+ }
+}
+
+/* Translate IFFLAGS to low-level driver representation */
+static int
+xlateMediaFlags(const struct mii_data *mid)
+{
+int lowLevelFlags = 0;
+int msk = IFM_AVALID | IFM_ACTIVE;
+
+ if ( (mid->mii_media_status & msk) == msk ) {
+ lowLevelFlags |= MV643XX_MEDIA_LINK;
+
+ if ( IFM_OPTIONS( mid->mii_media_active ) & IFM_FDX ) {
+ lowLevelFlags |= MV643XX_MEDIA_FD;
+ }
+
+ switch ( IFM_ETHER_SUBTYPE_GET( mid->mii_media_active ) ) {
+ default:
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"xlateMediaFlags: UNKNOWN SPEED\n");
+#endif
+ break; /* UNKNOWN -- FIXME */
+ case IFM_10_T:
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"xlateMediaFlags: 10baseT\n");
+#endif
+ lowLevelFlags |= MV643XX_MEDIA_10;
+ break;
+ case IFM_100_TX:
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"xlateMediaFlags: 100baseT\n");
+#endif
+ lowLevelFlags |= MV643XX_MEDIA_100;
+ break;
+ case IFM_1000_T:
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"xlateMediaFlags: 1000baseT\n");
+#endif
+ lowLevelFlags |= MV643XX_MEDIA_1000;
+ break;
+ }
+ } else {
+#ifdef MVETH_DEBUG
+ printk(DRVNAME"xlateMediaFlags: NO LINK\n");
+#endif
+ }
+ return lowLevelFlags;
+}
+
+static void
+mve_init(void *arg)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*)arg;
+struct ifnet *ifp = sc->ifp;
+int lowLevelMediaStatus = 0;
+int promisc;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_init (entering)\n");
+#endif
+
+ if ( sc->mii_softc ) {
+ mii_pollstat( sc->mii_softc );
+ lowLevelMediaStatus = xlateMediaFlags( sc->mii_softc );
+ if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) {
+ if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
+ } else {
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+ }
+ }
+
+ promisc = !! (if_getdrvflags(ifp) & IFF_PROMISC);
+
+ BSP_mve_init_hw(sc->mp, promisc, if_getlladdr(ifp), lowLevelMediaStatus);
+
+ /* if promiscuous then there is no need to change */
+ if ( ! promisc ) {
+ mve_set_filters(ifp);
+ }
+
+
+ if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
+}
+
+static void
+mve_start(struct ifnet *ifp)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp );
+ mve_lock( sc, "mve_start" );
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+ mve_unlock( sc, "mve_start" );
+ mve_send_event( sc, TX_EVENT );
+}
+
+static int
+mve_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp );
+struct ifreq *ifr = (struct ifreq *)data;
+int err = 0;
+int f, df;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_ioctl (entering)\n");
+#endif
+
+ mve_lock( sc, "mve_ioctl" );
+
+ switch ( cmd ) {
+ case SIOCSIFFLAGS:
+ f = if_getflags( ifp );
+ df = if_getdrvflags( ifp );
+ if ( (f & IFF_UP) ) {
+ if ( ! ( df & IFF_DRV_RUNNING ) ) {
+ mve_init( (void*)sc );
+ } else {
+ if ( (f & IFF_PROMISC) != (sc->oif_flags & IFF_PROMISC) ) {
+ mve_set_filters(ifp);
+ }
+ /* FIXME: other flag changes are ignored/unimplemented */
+ }
+ } else {
+ if ( df & IFF_DRV_RUNNING ) {
+ mve_stop(sc);
+ }
+ }
+ sc->oif_flags = f;
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ if ( sc->mii_softc ) {
+ err = ifmedia_ioctl( ifp, ifr, &sc->mii_softc->mii_media, cmd );
+ } else {
+ err = EINVAL;
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if ( if_getdrvflags( ifp ) & IFF_DRV_RUNNING ) {
+ mve_set_filters(ifp);
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ BSP_mve_dump_stats(sc->mp, stdout);
+ break;
+
+ default:
+ err = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ mve_unlock( sc, "mve_ioctl" );
+
+ return err;
+}
+
+/* SIO RTEMS_SHOW_STATS is too cumbersome to use -- for debugging, provide direct hack */
+int
+mv643xx_nexus_dump_stats(int unit, FILE *f)
+{
+ if ( unit < 0 || unit >= MV643XXETH_NUM_DRIVER_SLOTS || ! ifaces[unit] )
+ return -EINVAL;
+ if ( ! f )
+ f = stdout;
+ BSP_mve_dump_stats(ifaces[unit]->mp, f);
+ return 0;
+}
+
+/*
+ * Used to update speed settings in the hardware
+ * when the phy setup changes.
+ *
+ * ASSUME: caller holds lock
+ */
+static void
+mve_ack_link_change(struct mve_enet_softc *sc)
+{
+struct mii_data *mii = sc->mii_softc;
+int lowLevelMediaStatus;
+
+ if ( !mii )
+ return;
+
+ lowLevelMediaStatus = xlateMediaFlags( mii );
+
+ if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) {
+ BSP_mve_update_serial_port( sc->mp, lowLevelMediaStatus );
+ if_setdrvflagbits( sc->ifp, 0, IFF_DRV_OACTIVE );
+ mve_start( sc->ifp );
+ } else {
+ if_setdrvflagbits( sc->ifp, IFF_DRV_OACTIVE, 0 );
+ }
+}
+
+/* Callback from ifmedia_ioctl()
+ *
+ * Caller probably holds the lock already but
+ * since it is recursive we may as well make sure
+ * in case there are other possible execution paths.
+ */
+static int
+mve_media_change(struct ifnet *ifp)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp );
+struct mii_data *mii = sc->mii_softc;
+int err;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_media_change\n");
+#endif
+
+ if ( ! mii ) {
+ return ENXIO;
+ }
+
+ err = mii_mediachg( mii );
+
+ return err;
+}
+
+static void
+mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp );
+struct mii_data *mii = sc->mii_softc;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_media_status\n");
+#endif
+
+ if ( mii ) {
+ mii_pollstat( mii );
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ }
+}
+
+static int
+mve_attach(device_t dev)
+{
+struct mve_enet_softc *sc;
+struct ifnet *ifp;
+uint8_t hwaddr[ETHER_ADDR_LEN];
+struct mveth_private *mp;
+int unit = device_get_unit(dev);
+int tx_ring_size = MV643XX_TX_RING_SIZE;
+int rx_ring_size = MV643XX_RX_RING_SIZE;
+int tx_q_size = MV643XX_TX_QUEUE_SIZE;
+
+ sc = device_get_softc( dev );
+ sc->dev = dev;
+ sc->ifp = ifp = if_alloc(IFT_ETHER);
+ sc->daemonTid = 0;
+ sc->mii_softc = 0;
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_attach (entering)\n");
+#endif
+
+ mtx_init( &sc->mtx, device_get_nameunit( sc->dev ), MTX_NETWORK_LOCK, MTX_RECURSE );
+ callout_init_mtx( &sc->wdCallout, &sc->mtx, 0 );
+
+ if_setsoftc ( ifp, sc );
+ if_initname ( ifp, device_get_name(dev), unit);
+ if_setinitfn ( ifp, mve_init );
+ if_setioctlfn ( ifp, mve_ioctl );
+ if_setstartfn ( ifp, mve_start );
+ if_setflags ( ifp, (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX) );
+ sc->oif_flags = if_getflags( ifp );
+
+ if_setsendqlen ( ifp, tx_q_size );
+ if_setsendqready( ifp );
+
+ mp = BSP_mve_create(
+ unit + 1, /* low-level driver' unit numbers are 1-based */
+ 0,
+ mve_isr, (void*)sc,
+ release_tx_mbuf, (void*)sc,
+ alloc_rx_mbuf,
+ consume_rx_mbuf, (void*)sc,
+ rx_ring_size,
+ tx_ring_size,
+ ( MV643XX_ETH_IRQ_RX_DONE
+ | MV643XX_ETH_EXT_IRQ_TX_DONE
+ | MV643XX_ETH_EXT_IRQ_LINK_CHG));
+
+ if ( ! mp ) {
+ rtems_panic("Unable to create mv643xx low-level driver");
+ }
+
+ sc->mp = mp;
+
+ BSP_mve_read_eaddr( mp, hwaddr );
+
+ if ( 0 == mii_attach( sc->dev,
+ &sc->miibus,
+ ifp,
+ mve_media_change,
+ mve_media_status,
+ BMSR_DEFCAPMASK,
+ MVE643XX_DUMMY_PHY,
+ MII_OFFSET_ANY,
+ 0 ) ) {
+ sc->mii_softc = device_get_softc( sc->miibus );
+ }
+
+ sc->daemonTid = rtems_bsdnet_newproc("MVE", 4096, mve_daemon, (void*)sc);
+
+ ether_ifattach( ifp, hwaddr );
+
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_attach (leaving)\n");
+#endif
+
+ ifaces[unit] = sc;
+
+ return 0;
+}
+
+static int
+mve_miibus_read_reg(device_t dev, int phy, int reg)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev);
+
+ /* low-level driver knows what phy to use; ignore arg */
+ return (int) BSP_mve_mii_read( sc->mp, reg );
+}
+
+static int
+mve_miibus_write_reg(device_t dev, int phy, int reg, int val)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev);
+
+ /* low-level driver knows what phy to use; ignore arg */
+ BSP_mve_mii_write( sc->mp, reg, val );
+ return 0;
+}
+
+static void
+mve_miibus_statchg(device_t dev)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev);
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_miibus_statchg\n");
+#endif
+ /* assume this ends up being called either from the ioctl or the driver
+ * task -- either of which holds the lock.
+ */
+ mve_ack_link_change( sc );
+}
+
+static void
+mve_miibus_linkchg(device_t dev)
+{
+struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev);
+#ifdef MVETH_DEBUG
+ printk(DRVNAME": mve_miibus_linkchg\n");
+#endif
+ /* assume this ends up being called either from the ioctl or the driver
+ * task -- either of which holds the lock.
+ */
+ mve_ack_link_change( sc );
+}
+
+
+static device_method_t mve_methods[] = {
+ DEVMETHOD(device_probe, mve_probe ),
+ DEVMETHOD(device_attach, mve_attach),
+
+ DEVMETHOD(miibus_readreg, mve_miibus_read_reg ),
+ DEVMETHOD(miibus_writereg, mve_miibus_write_reg),
+ DEVMETHOD(miibus_statchg , mve_miibus_statchg ),
+ DEVMETHOD(miibus_linkchg , mve_miibus_linkchg ),
+
+ DEVMETHOD_END
+};
+
+static driver_t mve_nexus_driver = {
+ "mve",
+ mve_methods,
+ sizeof( struct mve_enet_softc )
+};
+
+static devclass_t mve_devclass;
+
+DRIVER_MODULE(mve, nexus, mve_nexus_driver, mve_devclass, 0, 0);
+DRIVER_MODULE(miibus, mve, miibus_driver, miibus_devclass, 0, 0);
+
+MODULE_DEPEND(mve, nexus, 1, 1, 1);
+MODULE_DEPEND(mve, ether, 1, 1, 1);
+
+#endif /* LIBBSP_BEATNIK_BSP_H */
diff --git a/rtemsbsd/sys/dev/sdhci/arasan_sdhci.c b/rtemsbsd/sys/dev/sdhci/arasan_sdhci.c
index ccdcb09b..017f4acb 100644
--- a/rtemsbsd/sys/dev/sdhci/arasan_sdhci.c
+++ b/rtemsbsd/sys/dev/sdhci/arasan_sdhci.c
@@ -123,16 +123,8 @@ static uint32_t
arasan_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
{
struct arasan_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32, wrk32;
- val32 = RD4(sc, off);
-
- if (off == SDHCI_CAPABILITIES) {
- val32 &= ~SDHCI_CAN_DO_8BITBUS;
- return (val32);
- }
-
- return val32;
+ return (RD4(sc, off));
}
static void
@@ -203,6 +195,27 @@ arasan_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot)
{
struct arasan_sdhci_softc *sc = device_get_softc(dev);
+ // wait a maximum of 1 second for card stable to settle
+ const unsigned int max_tries = 20;
+ const rtems_interval sleep_ticks =
+ rtems_clock_get_ticks_per_second() / max_tries;
+
+ unsigned int count = 0;
+ while (!(RD4(sc, SDHCI_PRESENT_STATE) & SDHCI_CARD_STABLE) &&
+ (count < max_tries))
+ {
+ rtems_task_wake_after(sleep_ticks);
+ ++count;
+ }
+
+ if (!(RD4(sc, SDHCI_PRESENT_STATE) & SDHCI_CARD_STABLE))
+ {
+ device_printf(dev,
+ "Card Detect failed to stabilize,"
+ " setting to not present.\n");
+ return false;
+ }
+
return (RD4(sc, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
}
@@ -234,6 +247,12 @@ arasan_sdhci_attach(device_t dev)
{
struct arasan_sdhci_softc *sc = device_get_softc(dev);
int rid, err;
+#if defined(LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_H)
+ volatile uint32_t *RST_LPD_IOU2_ptr = (uint32_t*)0xFF5E0238;
+ uint32_t RST_LPD_IOU2 = *RST_LPD_IOU2_ptr;
+ uint32_t SDIO0_disabled = RST_LPD_IOU2 & (1 << 5);
+ uint32_t SDIO1_disabled = RST_LPD_IOU2 & (1 << 6);
+#endif
sc->dev = dev;
@@ -246,6 +265,27 @@ arasan_sdhci_attach(device_t dev)
goto fail;
}
+ /*
+ * These devices may be disabled by being held in reset. If this is the
+ * case, a read attempt in its register range will result in a CPU hang.
+ * Detect this situation and avoid probing the device in this situation.
+ */
+#if defined(LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_H)
+ if ( sc->mem_res == 0xFF160000 ) {
+ if ( SDIO0_disabled != 0 ) {
+ device_printf(dev, "SDIO0 disabled\n");
+ err = ENXIO;
+ goto fail;
+ }
+ } else {
+ if ( SDIO1_disabled != 0 ) {
+ device_printf(dev, "SDIO1 disabled\n");
+ err = ENXIO;
+ goto fail;
+ }
+ }
+#endif
+
rid = 0;
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE);
@@ -262,16 +302,18 @@ arasan_sdhci_attach(device_t dev)
goto fail;
}
- sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
- sc->slot.quirks |= SDHCI_QUIRK_BROKEN_AUTO_STOP;
+ /*
+ * There are some combinations of board routing and eMMC memory that are
+ * not compatible with the HISPD mode. This disables HISPD mode for
+ * compatibility.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_DONT_SET_HISPD_BIT;
/*
* DMA is not really broken, it just isn't implemented yet.
*/
sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
- sc->slot.max_clk = 50000000;
-
sdhci_init_slot(dev, &sc->slot, 0);
sc->slot_init_done = true;
@@ -290,7 +332,7 @@ fail:
static int
arasan_sdhci_probe(device_t dev)
{
- device_set_desc(dev, "Zynq-7000 SDHCI");
+ device_set_desc(dev, "Arasan SDIO");
return (0);
}
diff --git a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c
index c7c09bb4..d02bba98 100755
--- a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c
+++ b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <dev/usb/controller/ohci.h>
#include <dev/usb/controller/ohcireg.h>
+#include <arm/lpc/probe.h>
#include <arm/lpc/lpcreg.h>
#include <arm/lpc/lpcvar.h>
@@ -90,7 +91,7 @@ __FBSDID("$FreeBSD$");
while ((lpc_otg_read_4(_sc, _sreg) & _value) != _value); \
} while (0);
-static int lpc_ohci_probe(device_t dev);
+static int lpc_ohci_do_probe(device_t dev);
static int lpc_ohci_attach(device_t dev);
static int lpc_ohci_detach(device_t dev);
@@ -107,12 +108,20 @@ static int lpc_otg_i2c_wait_for_transaction_done(struct ohci_softc *sc);
static int lpc_otg_i2c_read(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t *value);
static int lpc_otg_i2c_write(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t value);
+__weak_symbol int
+lpc_ohci_probe(int unit)
+{
+
+ (void)unit;
+ return (BUS_PROBE_DEFAULT);
+}
+
static int
-lpc_ohci_probe(device_t dev)
+lpc_ohci_do_probe(device_t dev)
{
device_set_desc(dev, "LPC32x0 USB OHCI controller");
- return (BUS_PROBE_DEFAULT);
+ return (lpc_ohci_probe(device_get_unit(dev)));
}
static int
@@ -473,7 +482,7 @@ lpc_ohci_resume(device_t dev)
static device_method_t lpc_ohci_methods[] = {
/* Device interface */
- DEVMETHOD(device_probe, lpc_ohci_probe),
+ DEVMETHOD(device_probe, lpc_ohci_do_probe),
DEVMETHOD(device_attach, lpc_ohci_attach),
DEVMETHOD(device_detach, lpc_ohci_detach),
DEVMETHOD(device_suspend, bus_generic_suspend),
diff --git a/rtemsbsd/sys/dev/vme/tsi148.c b/rtemsbsd/sys/dev/vme/tsi148.c
new file mode 100644
index 00000000..99c3cfcd
--- /dev/null
+++ b/rtemsbsd/sys/dev/vme/tsi148.c
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <bsp.h>
+#ifdef LIBBSP_POWERPC_QORIQ_BSP_H
+#include <bsp/VMEConfig.h>
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <stdio.h>
+
+struct tsi148 {
+ device_t dev;
+ int mem_rid;
+ struct resource *mem_res;
+ int irq_rid;
+ struct resource *irq_res;
+ void *irq_cookie;
+};
+
+static void
+tsi148_intr(void *arg)
+{
+ struct tsi148 *sc;
+
+ sc = arg;
+
+ /*
+ * Note: This interrupt is never used. It would be used in case of MSIs.
+ * But the Tsi148 can't generate them. So this interrupt must never
+ * happen.
+ */
+ puts("tsi148_intr: Unexpected interrupt. Should never happen.\n");
+}
+
+static int
+tsi148_probe(device_t dev)
+{
+
+ if (pci_get_devid(dev) == 0x014810e3) {
+ device_set_desc(dev, "Tundra Tsi148 PCI-VME bridge");
+ return (BUS_PROBE_GENERIC);
+ }
+
+ return (ENXIO);
+}
+
+static int
+tsi148_attach(device_t dev)
+{
+ struct tsi148 *sc;
+
+ if (bsp_vme_pcie_base_address != 0) {
+ puts("tsi148: Another instance is already attached.\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ sc->mem_rid = PCIR_BAR(0);
+ sc->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
+ &sc->mem_rid, RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ return (ENOMEM);
+ }
+
+ sc->irq_rid = 0;
+ sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
+ &sc->irq_rid, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ return (ENOMEM);
+ }
+
+ bus_setup_intr(sc->dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, tsi148_intr, sc, &sc->irq_cookie);
+ if (sc->irq_cookie == NULL) {
+ return (ENOMEM);
+ }
+
+ bsp_vme_pcie_base_address = sc->mem_res->r_bushandle;
+ BSP_vme_config();
+
+ return 0;
+}
+
+static int
+tsi148_detach(device_t dev)
+{
+ BSD_ASSERT(0);
+ return (ENXIO);
+}
+
+static device_method_t tsi148_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tsi148_probe),
+ DEVMETHOD(device_attach, tsi148_attach),
+ DEVMETHOD(device_detach, tsi148_detach),
+ DEVMETHOD_END
+};
+
+static driver_t tsi148_driver = {
+ "tsi148",
+ tsi148_methods,
+ sizeof(struct tsi148),
+};
+
+static devclass_t tsi148_devclass;
+
+DRIVER_MODULE(tsi148, pci, tsi148_driver, tsi148_devclass, NULL, 0);
+#endif /* LIBBSP_POWERPC_QORIQ_BSP_H */
diff --git a/rtemsbsd/sys/dev/vme/vme-rtems-compat.c b/rtemsbsd/sys/dev/vme/vme-rtems-compat.c
new file mode 100644
index 00000000..c5ce8d84
--- /dev/null
+++ b/rtemsbsd/sys/dev/vme/vme-rtems-compat.c
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2023 embedded brains GmbH
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+/*
+ * This file is a glue layer that allows the TSI148 RTEMS VME driver to use the
+ * libbsd PCI support.
+ */
+
+typedef void FILE;
+
+#define __INSIDE_RTEMS_BSP__
+
+#include <rtems/pci.h>
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/pciio.h>
+#include <dev/pci/pcivar.h>
+
+#undef pci_find_device
+
+static device_t dev;
+
+static int
+read_config_byte(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint8_t *val)
+{
+ *val = (uint8_t)pci_read_config(dev, reg, 1);
+ return 0;
+}
+
+static int
+read_config_word(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint16_t *val)
+{
+ *val = (uint16_t)pci_read_config(dev, reg, 2);
+ return 0;
+}
+
+static int
+read_config_dword(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint32_t *val)
+{
+ *val = pci_read_config(dev, reg, 4);
+ return 0;
+}
+
+static int
+write_config_byte(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint8_t val)
+{
+ pci_write_config(dev, reg, val, 1);
+ return 0;
+}
+
+static int
+write_config_word(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint16_t val)
+{
+ pci_write_config(dev, reg, val, 2);
+ return 0;
+}
+
+static int
+write_config_dword(unsigned char bus, unsigned char slot, unsigned char func,
+ unsigned char reg, uint32_t val)
+{
+ pci_write_config(dev, reg, val, 4);
+ return 0;
+}
+
+static const pci_config_access_functions pci_functions = {
+ .read_config_byte = read_config_byte,
+ .read_config_word = read_config_word,
+ .read_config_dword = read_config_dword,
+ .write_config_byte = write_config_byte,
+ .write_config_word = write_config_word,
+ .write_config_dword = write_config_dword
+};
+
+rtems_pci_config_t BSP_pci_configuration = {
+ .pci_functions = &pci_functions
+};
+
+int
+pci_find_device(unsigned short vendorid, unsigned short deviceid,
+ int instance, int *pbus, int *pdev, int *pfun)
+{
+ struct pci_devinfo *dinfo;
+
+ if (instance != 0) {
+ return -1;
+ }
+
+ if (dev != NULL) {
+ return -1;
+ }
+
+ dev = _bsd_pci_find_device(vendorid, deviceid);
+ if (dev == NULL) {
+ return -1;
+ }
+
+ dinfo = device_get_ivars(dev);
+ *pbus = dinfo->conf.pc_sel.pc_bus;
+ *pdev = dinfo->conf.pc_sel.pc_dev;
+ *pfun = dinfo->conf.pc_sel.pc_func;
+ return 0;
+}
+
+#include <bsp.h>
+#ifdef LIBBSP_POWERPC_QORIQ_BSP_H
+#include <bsp/VMEConfig.h>
+
+uintptr_t bsp_vme_pcie_base_address = 0;
+unsigned short (*_BSP_clear_vmebridge_errors)(int) = NULL;
+#endif
diff --git a/rtemsbsd/sys/net/if_ppp.c b/rtemsbsd/sys/net/if_ppp.c
index 709f13e0..e134dc76 100644
--- a/rtemsbsd/sys/net/if_ppp.c
+++ b/rtemsbsd/sys/net/if_ppp.c
@@ -313,11 +313,12 @@ static rtems_task ppp_txdaemon(rtems_task_argument arg)
frag=0;
/* initialize output values */
- sc->sc_outfcs = PPP_INITFCS;
- sc->sc_outbuf = (u_char *)0;
- sc->sc_outlen = (short )0;
- sc->sc_outoff = (short )0;
- sc->sc_outfcslen = (short )0;
+ sc->sc_outfcs = PPP_INITFCS;
+ sc->sc_outbuf = (u_char *)0;
+ sc->sc_outlen = (short )0;
+ sc->sc_outoff = (short )0;
+ sc->sc_outoff_update = false;
+ sc->sc_outfcslen = (short )0;
/* printf("Start Transmit Packet..\n"); */
diff --git a/rtemsbsd/sys/net/if_pppvar.h b/rtemsbsd/sys/net/if_pppvar.h
index fdfb56df..bd11bcbc 100644
--- a/rtemsbsd/sys/net/if_pppvar.h
+++ b/rtemsbsd/sys/net/if_pppvar.h
@@ -117,6 +117,7 @@ struct ppp_softc {
struct ifqueue sc_freeq; /* free packets */
short sc_outoff; /* output packet byte offset */
+ bool sc_outoff_update; /* outoff needs update in pppstart */
short sc_outflag; /* output status flag */
short sc_outlen; /* length of output packet */
short sc_outfcslen; /* length of output fcs data */
diff --git a/rtemsbsd/sys/net/ppp_tty.c b/rtemsbsd/sys/net/ppp_tty.c
index 80d4fee1..2e850dc7 100644
--- a/rtemsbsd/sys/net/ppp_tty.c
+++ b/rtemsbsd/sys/net/ppp_tty.c
@@ -124,7 +124,7 @@ int pppread(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args);
int pppwrite(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args);
int ppptioctl(struct rtems_termios_tty *tty, rtems_libio_ioctl_args_t *args);
int pppinput(int c, struct rtems_termios_tty *tty);
-int pppstart(struct rtems_termios_tty *tp);
+int pppstart(struct rtems_termios_tty *tp, int len);
u_short pppfcs(u_short fcs, u_char *cp, int len);
void pppallocmbuf(struct ppp_softc *sc, struct mbuf **mp);
@@ -557,7 +557,7 @@ pppasyncctlp(
* Called at spltty or higher.
*/
int
-pppstart(struct rtems_termios_tty *tp)
+pppstart(struct rtems_termios_tty *tp, int len)
{
u_char *sendBegin;
u_long ioffset = (u_long )0;
@@ -567,6 +567,13 @@ pppstart(struct rtems_termios_tty *tp)
/* ensure input is valid and we are busy */
if (( sc != NULL ) && ( sc->sc_outflag & SC_TX_BUSY )) {
+ /* Adapt offsets if necessary */
+ if ( sc->sc_outoff_update ) {
+ sc->sc_stats.ppp_obytes += len;
+ sc->sc_outoff += len;
+ sc->sc_outoff_update = false;
+ }
+
/* check to see if we need to get the next buffer */
/* Ready with PPP_FLAG Character ? */
@@ -644,8 +651,25 @@ pppstart(struct rtems_termios_tty *tp)
/* write out the character(s) and update the stats */
(*tp->handler.write)(ctx, (char *)sendBegin, (ioffset > 0) ? ioffset : 1);
- sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1;
- sc->sc_outoff += ioffset;
+ /*
+ * In case of polled drivers, everything is sent here. So adapt the
+ * offsets. In case of interrupt or task driven drivers, we don't know
+ * whether all characters have been sent. We only get feedback via
+ * rtems_termios_dequeue_characters() function which is the one that is
+ * calling us.
+ */
+ if (tp->handler.mode == TERMIOS_POLLED) {
+ sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1;
+ sc->sc_outoff += ioffset;
+ sc->sc_outoff_update = false;
+ } else {
+ if (ioffset > 0) {
+ sc->sc_outoff_update = true;
+ } else {
+ sc->sc_outoff_update = false;
+ sc->sc_stats.ppp_obytes += 1;
+ }
+ }
return (0);
}
diff --git a/rtemsbsd/sys/powerpc/platform_mpc85xx.c b/rtemsbsd/sys/powerpc/platform_mpc85xx.c
new file mode 100644
index 00000000..8c034eb3
--- /dev/null
+++ b/rtemsbsd/sys/powerpc/platform_mpc85xx.c
@@ -0,0 +1,48 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008-2012 Semihalf.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+
+#include <powerpc/mpc85xx/mpc85xx.h>
+
+#include <bsp.h>
+
+#ifdef LIBBSP_POWERPC_QORIQ_BSP_H
+
+#include <bsp/qoriq.h>
+
+vm_paddr_t ccsrbar_pa = (vm_paddr_t)&qoriq;
+vm_offset_t ccsrbar_va = (vm_offset_t)&qoriq;
+vm_size_t ccsrbar_size = sizeof(qoriq);
+
+#endif /* LIBBSP_POWERPC_QORIQ_BSP_H */
diff --git a/rtemsbsd/ttcp/ttcp.c b/rtemsbsd/ttcp/ttcp.c
index d361fece..fe71844f 100644
--- a/rtemsbsd/ttcp/ttcp.c
+++ b/rtemsbsd/ttcp/ttcp.c
@@ -169,7 +169,7 @@ static void initialize_vars(void)
addr = NULL;
}
-char Usage[] = "\
+static const char Usage[] = "\
Usage: ttcp -t [-options] host [ < in ]\n\
ttcp -r [-options > out]\n\
Common options:\n\
@@ -193,28 +193,23 @@ Options specific to -r:\n\
-m ## delay for specified milliseconds between each write\n\
";
-char stats[128];
-double nbytes; /* bytes on net */
-unsigned long numCalls; /* # of I/O system calls */
-double cput, realt; /* user, real time (seconds) */
-
-void err();
-void mes();
-void pattern();
-void prep_timer();
-double read_timer();
-int Nread();
-int Nwrite();
-void delay();
-int mread();
-char *outfmt();
-
-void
-sigpipe()
-{
-}
-
-void millisleep(long msec)
+static char stats[128];
+static double nbytes; /* bytes on net */
+static unsigned long numCalls; /* # of I/O system calls */
+static double cput, realt; /* user, real time (seconds) */
+
+static void err();
+static void mes();
+static void pattern();
+static void prep_timer();
+static double read_timer();
+static int Nread();
+static int Nwrite();
+static void delay();
+static int mread();
+static char *outfmt();
+
+static void millisleep(long msec)
{
#if defined(ENABLE_NANOSLEEP_DELAY)
struct timespec req;
diff --git a/testsuite/dhcpcd01/test_main.c b/testsuite/dhcpcd01/test_main.c
index c1d97a3f..b982bfc9 100644
--- a/testsuite/dhcpcd01/test_main.c
+++ b/testsuite/dhcpcd01/test_main.c
@@ -44,6 +44,7 @@
#include <rtems/dhcpcd.h>
#define TEST_NAME "LIBBSD DHCPCD 1"
+#define TEST_STATE_USER_INPUT 1
static void
dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env)
@@ -67,9 +68,7 @@ test_main(void)
{
rtems_dhcpcd_add_hook(&dhcpcd_hook);
-
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/testsuite/dhcpcd02/test_main.c b/testsuite/dhcpcd02/test_main.c
index 221b096f..cbfd0846 100644
--- a/testsuite/dhcpcd02/test_main.c
+++ b/testsuite/dhcpcd02/test_main.c
@@ -40,12 +40,13 @@
#include <rtems.h>
#define TEST_NAME "LIBBSD DHCPCD 2"
+#define TEST_STATE_USER_INPUT 1
static void
test_main(void)
{
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/testsuite/evdev01/init.c b/testsuite/evdev01/init.c
index fe588ff4..5a8b0beb 100644
--- a/testsuite/evdev01/init.c
+++ b/testsuite/evdev01/init.c
@@ -341,7 +341,7 @@ evdev_scan_task(rtems_task_argument arg)
}
}
otask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
@@ -401,7 +401,7 @@ err:
}
}
ktask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
@@ -484,7 +484,7 @@ err:
}
}
mtask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
@@ -561,7 +561,7 @@ err:
}
}
ttask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
diff --git a/testsuite/foobarclient/test_main.c b/testsuite/foobarclient/test_main.c
index d55b55c6..71b4774d 100644
--- a/testsuite/foobarclient/test_main.c
+++ b/testsuite/foobarclient/test_main.c
@@ -272,8 +272,7 @@ test_main(void)
foobar_register(&question);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/testsuite/ftpd01/test_main.c b/testsuite/ftpd01/test_main.c
index 7ec17b96..cc7e8a00 100644
--- a/testsuite/ftpd01/test_main.c
+++ b/testsuite/ftpd01/test_main.c
@@ -79,8 +79,7 @@ test_main(void)
rv = rtems_initialize_ftpd();
assert(rv == 0);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/testsuite/include/rtems/bsd/test/network-config.h.in b/testsuite/include/rtems/bsd/test/network-config.h.in
index fd63eded..69609854 100755
--- a/testsuite/include/rtems/bsd/test/network-config.h.in
+++ b/testsuite/include/rtems/bsd/test/network-config.h.in
@@ -52,6 +52,8 @@
#endif
#elif defined(LIBBSP_ARM_ATSAM_BSP_H)
#define NET_CFG_INTERFACE_0 "if_atsam0"
+#elif defined(LIBBSP_MICROBLAZE_FPGA_BSP_H)
+ #define NET_CFG_INTERFACE_0 "xae0"
#else
#define NET_CFG_INTERFACE_0 "@NET_CFG_INTERFACE_0@"
#endif
diff --git a/testsuite/loopback01/test_main.c b/testsuite/loopback01/test_main.c
index 1b5c0064..51d5e5b2 100644
--- a/testsuite/loopback01/test_main.c
+++ b/testsuite/loopback01/test_main.c
@@ -133,7 +133,7 @@ static rtems_task workerTask(rtems_task_argument arg)
if (close(s) < 0)
printf("Can't close worker task socket: %s\n", strerror(errno));
printf("Worker task terminating.\n");
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
/*
@@ -230,7 +230,7 @@ static rtems_task clientTask(rtems_task_argument arg)
clientWorker(arg);
sendClientEventToMasterTask(arg);
printf("Client task terminating.\n");
- rtems_task_delete( RTEMS_SELF );
+ rtems_task_exit();
}
/*
diff --git a/testsuite/media01/test_main.c b/testsuite/media01/test_main.c
index 2a1c9aae..001c80a8 100644
--- a/testsuite/media01/test_main.c
+++ b/testsuite/media01/test_main.c
@@ -201,6 +201,8 @@ early_initialization(void)
#define CONFIGURE_FILESYSTEM_DOSFS
+#define CONFIGURE_FILESYSTEM_TFTPFS
+
#define CONFIGURE_MAXIMUM_PROCESSORS 32
#define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 4096
diff --git a/testsuite/nfs01/test_main.c b/testsuite/nfs01/test_main.c
index d0642630..a3d75ddd 100644
--- a/testsuite/nfs01/test_main.c
+++ b/testsuite/nfs01/test_main.c
@@ -46,18 +46,27 @@
#include <rtems/console.h>
#include <rtems/shell.h>
-#include <rtems/telnetd.h>
#include <librtemsNfs.h>
#include <rtems/bsd/test/network-config.h>
+#define END_TEST_IN_SHELL 0
+
#define TEST_NAME "LIBBSD NFS 1"
#define TEST_STATE_USER_INPUT 1
#define TEST_WAIT_FOR_LINK NET_CFG_INTERFACE_0
static const char *test_top = "test-nfs01";
+#define rtems_test_assert(__exp) \
+ do { \
+ if (!(__exp)) { \
+ printf( "%s: %d %s\n", __FILE__, __LINE__, #__exp ); \
+ assert(1 == 0); \
+ } \
+ } while (0)
+
#define rtems_test_errno_assert(__exp) \
do { \
if (!(__exp)) { \
@@ -145,7 +154,7 @@ test_walk_tree(const char *start, walk_tree_callout callout, void *data)
while (dir != NULL && active) {
test_dir *tmp_dir;
if (active && dir->dirs == NULL && dir->indir == NULL) {
- struct DIR *ddir;
+ DIR *ddir;
rtems_test_errno_assert((ddir = opendir(".")) != NULL);
while (active) {
struct dirent *dp;
@@ -209,6 +218,7 @@ test_walk_tree(const char *start, walk_tree_callout callout, void *data)
}
}
+#if NFS_TREE_WALK
typedef struct test_printer_data {
char path[MAXPATHLEN];
int count;
@@ -256,6 +266,7 @@ test_walk_tree_printer(walk_tree_dir state,
}
return true;
}
+#endif
static bool
test_walk_tree_unlink(walk_tree_dir state,
@@ -280,7 +291,7 @@ test_walk_tree_unlink(walk_tree_dir state,
static void
test_setup(const char *base)
{
- struct DIR *ddir;
+ DIR *ddir;
printf("test: nfs: setup\n");
printf("test: nfs: opendir: %s\n", base);
rtems_test_errno_assert((ddir = opendir(base)) != NULL);
@@ -306,30 +317,73 @@ static void
test_path_eval(const char *base, int depth)
{
char path[MAXPATHLEN];
+ char curpath[MAXPATHLEN];
+ char getpath[MAXPATHLEN];
int l;
- printf("test path eval\n");
+ printf("test path eval: %s\n", base);
test_setup(base);
+ sprintf(curpath, "%s/%s", base, test_top);
+
for (l = 1; l <= depth; ++l) {
snprintf(path, sizeof(path), "%d", l);
- printf("test: nfs: mkdir: %s\n", path);
+ strcat(curpath, "/");
+ strcat(curpath, path);
+ printf("test: nfs: mkdir: %s (%s)\n", path, curpath);
rtems_test_errno_assert(mkdir(path, 0777) == 0);
- printf("test: nfs: chdir: %s\n", path);
+ printf("test: nfs: chdir: %s (%s)\n", path, curpath);
rtems_test_errno_assert(chdir(path) == 0);
- printf("test: nfs: getcwd: %s\n", path);
- assert(getcwd(path, sizeof(path)) != NULL);
- printf("test: nfs: getcwd: %s\n", path);
+ printf("test: nfs: getcwd: %s (%s)\n", path, curpath);
+ assert(getcwd(getpath, sizeof(getpath)) != NULL);
+ printf("test: nfs: getcwd: %s (want: %s)\n", getpath, curpath);
+ assert(strcmp(curpath, getpath) == 0);
}
test_cleanup(base);
}
static void
+test_path_file_copy(const char *base, int depth)
+{
+ char path[MAXPATHLEN];
+ struct stat sb;
+ FILE* f;
+ int l;
+
+ printf("test path eval\n");
+
+ test_setup(base);
+
+ memset(path, 0, sizeof(path));
+
+ for (l = 1; l <= depth; ++l) {
+ char* p = path + strlen(path);
+ if (l > 1) {
+ *p++ = '/';
+ }
+ snprintf(p, sizeof(path), "%d", l);
+ printf("test: nfs: mkdir: %s\n", path);
+ rtems_test_errno_assert(mkdir(path, 0777) == 0);
+ }
+
+ strlcat(path, "/test-file.txt", sizeof(path));
+ printf("Create file %s\n", path);
+ rtems_test_errno_assert((f = fopen(path, "w")) != NULL);
+ rtems_test_errno_assert(fprintf(f, "The contents of %s\nNFS test\n", path) > 0);
+ rtems_test_errno_assert(fclose(f) == 0);
+ printf("Checking %s has been copied\n", path);
+ rtems_test_errno_assert(stat(path, &sb) == 0);
+
+ test_cleanup(base);
+}
+
+static void
test_nfs(const char *base)
{
test_path_eval(base, 5);
+ test_path_file_copy(base, 3);
#if NFS_TREE_WALK
test_printer_data pd;
memset(&pd, 0, sizeof(pd));
@@ -338,30 +392,6 @@ test_nfs(const char *base)
}
static void
-telnet_shell(char *name, void *arg)
-{
- rtems_shell_env_t env;
-
- rtems_shell_dup_current_env(&env);
-
- env.devname = name;
- env.taskname = "TLNT";
- env.login_check = NULL;
- env.forever = false;
-
- rtems_shell_main_loop(&env);
-}
-
-rtems_telnetd_config_table rtems_telnetd_config = {
- .command = telnet_shell,
- .arg = NULL,
- .priority = 0,
- .stack_size = 0,
- .login_check = NULL,
- .keep_stdio = false
-};
-
-static void
test_main(void)
{
const char remote_target[] = NET_CFG_NFS_MOUNT_PATH;
@@ -371,8 +401,6 @@ test_main(void)
int retries = 0;
int rv;
- assert(rtems_telnetd_initialize() == RTEMS_SUCCESSFUL);
-
if (strlen(options) != 0) {
mount_options = options;
}
@@ -397,12 +425,17 @@ test_main(void)
test_nfs(mount_point);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+#if END_TEST_IN_SHELL
+ rtems_task_exit();
+#else
+ exit(0);
+#endif
}
#define CONFIGURE_SHELL_COMMANDS_ALL
-#define DEFAULT_NETWORK_SHELL
+#if END_TEST_IN_SHELL
+ #define DEFAULT_NETWORK_SHELL */
+#endif
#define CONFIGURE_FILESYSTEM_NFS
diff --git a/testsuite/openssl02/test_main.c b/testsuite/openssl02/test_main.c
index 32e9c03a..11229e58 100644
--- a/testsuite/openssl02/test_main.c
+++ b/testsuite/openssl02/test_main.c
@@ -46,6 +46,8 @@
#include <sysexits.h>
#include <unistd.h>
+#include <rtems/bsd/modules.h>
+
#include <machine/rtems-bsd-commands.h>
#define TEST_NAME "LIBBSD OPENSSL 2"
diff --git a/testsuite/pf02/test_main.c b/testsuite/pf02/test_main.c
index 5941ea5a..dfb9847b 100644
--- a/testsuite/pf02/test_main.c
+++ b/testsuite/pf02/test_main.c
@@ -167,6 +167,7 @@ test_main(void)
{
rtems_status_code sc;
int rv;
+ rtems_shell_env_t env;
prepare_files();
@@ -176,9 +177,7 @@ test_main(void)
rv = rtems_initialize_ftpd();
assert(rv == 0);
- rtems_shell_env_t env;
-
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
rtems_shell_main_loop( &env );
exit(0);
diff --git a/testsuite/ppp01/test_main.c b/testsuite/ppp01/test_main.c
index d4baf5db..b6e9d4d7 100644
--- a/testsuite/ppp01/test_main.c
+++ b/testsuite/ppp01/test_main.c
@@ -272,8 +272,7 @@ test_main(void)
rv = rtems_pppd_connect();
assert(rv == 0);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
RTEMS_BSD_DEFINE_NEXUS_DEVICE(ppp, 0, 0, NULL);
diff --git a/testsuite/tcpdump01/test_main.c b/testsuite/tcpdump01/test_main.c
new file mode 100644
index 00000000..0f31cdde
--- /dev/null
+++ b/testsuite/tcpdump01/test_main.c
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @brief Tests the tcpdump command.
+ */
+
+/*
+ * Copyright (C) 2022 embedded brains GmbH
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <vm/uma.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+
+#include <assert.h>
+#include <ck_epoch.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rtems.h>
+#include <rtems/bsd/bsd.h>
+#include <rtems/console.h>
+#include <rtems/libcsupport.h>
+
+#include <machine/rtems-bsd-commands.h>
+
+#define TEST_NAME "LIBBSD TCPDUMP 1"
+
+typedef struct {
+ rtems_id main_id;
+ int sp[2];
+} test_context;
+
+static test_context test_instance;
+
+static void
+epoch_cleanup(void)
+{
+ rtems_status_code sc;
+
+ sc = rtems_task_wake_after(CK_EPOCH_LENGTH);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void
+init_addr(struct sockaddr_in *addr)
+{
+ int ok;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(1234);
+ ok = inet_aton("127.0.0.1", &addr->sin_addr);
+ assert(ok != 0);
+}
+
+static rtems_id
+start_task(rtems_task_entry entry, void *arg)
+{
+ rtems_task_priority prio;
+ rtems_id id;
+ rtems_status_code sc;
+
+ sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_create(rtems_build_name('T', 'E', 'S', 'T' ), prio - 1,
+ 32 * 1024, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(id, entry, (rtems_task_argument)arg);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ return id;
+}
+
+static void
+test_tcpdump_help(void)
+{
+ char *argv[] = {
+ "tcpdump",
+ "-h",
+ NULL
+ };
+ rtems_resource_snapshot snapshot;
+ int exit_code;
+
+ rtems_resource_snapshot_take(&snapshot);
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ assert(rtems_resource_snapshot_check(&snapshot));
+}
+
+static const char raw_packets_file[] = "packets.bin";
+
+static void
+run_tcpdump_write_raw_packets(rtems_task_argument arg)
+{
+ test_context *ctx;
+ FILE *file;
+ FILE *saved_stdin;
+ FILE *saved_stdout;
+ FILE *saved_stderr;
+ int rv;
+ char *argv[] = {
+ "tcpdump",
+ "-n",
+ "-i",
+ "lo0",
+ "-w",
+ RTEMS_DECONST(char *, raw_packets_file),
+ NULL
+ };
+ int exit_code;
+ rtems_status_code sc;
+
+ ctx = (test_context *)arg;
+
+ saved_stdin = stdin;
+ saved_stdout = stdout;
+ saved_stderr = stderr;
+
+ file = fdopen(ctx->sp[1], "r+");
+ assert(file != NULL);
+
+ stdin = file;
+ stdout = file;
+ stderr = file;
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ stdin = saved_stdin;
+ stdout = saved_stdout;
+ stderr = saved_stderr;
+
+ rv = fclose(file);
+ assert(rv == 0);
+
+ sc = rtems_event_transient_send(ctx->main_id);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rtems_task_exit();
+}
+
+static void
+test_tcpdump_write_raw_packets(test_context *ctx)
+{
+ char *argv[] = {
+ "tcpdump",
+ "-n",
+ "-r",
+ RTEMS_DECONST(char *, raw_packets_file),
+ NULL
+ };
+ rtems_resource_snapshot snapshot;
+ int in;
+ int out;
+ int rv;
+ char c;
+ ssize_t n;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ rtems_status_code sc;
+ int exit_code;
+
+ rtems_resource_snapshot_take(&snapshot);
+
+ rv = socketpair(PF_UNIX, SOCK_STREAM, 0, ctx->sp);
+ assert(rv == 0);
+
+ start_task(run_tcpdump_write_raw_packets, ctx);
+
+ init_addr(&addr);
+
+ in = socket(PF_INET, SOCK_DGRAM, 0);
+ assert(out >= 0);
+
+ rv = bind(in, (const struct sockaddr *) &addr, sizeof(addr));
+ assert(rv == 0);
+
+ out = socket(PF_INET, SOCK_DGRAM, 0);
+ assert(out >= 0);
+
+ c = 'x';
+ n = sendto(out, &c, sizeof(c), 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ assert(n == 1);
+
+ c = 'y';
+ addr_len = sizeof(addr);
+ n = recvfrom(in, &c, sizeof(c), 0,
+ (struct sockaddr *) &addr, &addr_len);
+ assert(n == 1);
+ assert(c == 'x');
+
+ /* The tcpdump pcap read timeout is 1000ms */
+ sc = rtems_task_wake_after(rtems_clock_get_ticks_per_second());
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ c = 'q';
+ n = write(ctx->sp[0], &c, sizeof(c));
+ assert(n == 1);
+
+ rv = close(out);
+ assert(rv == 0);
+
+ rv = close(in);
+ assert(rv == 0);
+
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rv = close(ctx->sp[0]);
+ assert(rv == 0);
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ rv = unlink(raw_packets_file);
+ assert(rv == 0);
+
+ epoch_cleanup();
+ assert(rtems_resource_snapshot_check(&snapshot));
+}
+
+static void
+test_main(void)
+{
+ test_context *ctx;
+ FILE *file;
+ int rv;
+
+ ctx = &test_instance;
+ ctx->main_id = rtems_task_self();
+
+ /* Fill dynamic file pool in Newlib */
+ file = fopen(CONSOLE_DEVICE_NAME, "r+");
+ assert(file != NULL);
+ rv = fclose(file);
+ assert(rv == 0);
+
+ /*
+ * Stop interferences of uma_timeout() which may need some dynamic
+ * memory.
+ */
+ rtems_uma_drain_timeout();
+
+ rtems_bsd_ifconfig_lo0();
+ epoch_cleanup();
+
+ test_tcpdump_help();
+ test_tcpdump_write_raw_packets(ctx);
+
+ exit(0);
+}
+
+#include <rtems/bsd/test/default-init.h>
diff --git a/testsuite/telnetd01/test_main.c b/testsuite/telnetd01/test_main.c
index 73d19c4e..d7e50a97 100644
--- a/testsuite/telnetd01/test_main.c
+++ b/testsuite/telnetd01/test_main.c
@@ -75,8 +75,7 @@ test_main(void)
rtems_status_code sc = rtems_telnetd_initialize();
assert(sc == RTEMS_SUCCESSFUL);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_SHELL
diff --git a/testsuite/thread01/test_main.c b/testsuite/thread01/test_main.c
index 8dd77e88..d1bbecbe 100644
--- a/testsuite/thread01/test_main.c
+++ b/testsuite/thread01/test_main.c
@@ -109,13 +109,10 @@ wait_for_worker_thread(void)
static void
non_bsd_thread(rtems_task_argument arg)
{
- rtems_status_code sc;
test_curthread("");
wake_up_main_thread();
-
- sc = rtems_task_delete(RTEMS_SELF);
- assert(sc == RTEMS_SUCCESSFUL);
+ rtems_task_exit();
}
static void
diff --git a/testsuite/ttcpshell01/test_main.c b/testsuite/ttcpshell01/test_main.c
index c7631d14..f7fdf36e 100644
--- a/testsuite/ttcpshell01/test_main.c
+++ b/testsuite/ttcpshell01/test_main.c
@@ -32,8 +32,6 @@
* SUCH DAMAGE.
*/
-#include <assert.h>
-
#include <rtems.h>
#include <rtems/shell.h>
#include <rtems/console.h>
@@ -44,8 +42,7 @@
static void
test_main(void)
{
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define SHELL_TTCP_COMMAND_ENABLE
diff --git a/testsuite/usbkbd01/init.c b/testsuite/usbkbd01/init.c
index 0ea2d2b7..9800b871 100644
--- a/testsuite/usbkbd01/init.c
+++ b/testsuite/usbkbd01/init.c
@@ -101,7 +101,7 @@ usb_keyboard_read_task(rtems_task_argument arg)
rtems_message_queue_send(omid, &msg, sizeof(msg));
}
rtask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
@@ -141,7 +141,7 @@ usb_keyboard_open_task(rtems_task_argument arg)
printf("keyboard device closed\n");
}
otask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
diff --git a/testsuite/usbmouse01/init.c b/testsuite/usbmouse01/init.c
index 5bf732d8..56518d67 100644
--- a/testsuite/usbmouse01/init.c
+++ b/testsuite/usbmouse01/init.c
@@ -103,7 +103,7 @@ usb_mouse_read_task(rtems_task_argument arg)
}
rtask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
@@ -143,7 +143,7 @@ usb_mouse_open_task(rtems_task_argument arg)
printf("mouse device closed\n");
}
otask_active = false;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
static void
diff --git a/testsuite/vme01/test_main.c b/testsuite/vme01/test_main.c
new file mode 100644
index 00000000..fe6c1072
--- /dev/null
+++ b/testsuite/vme01/test_main.c
@@ -0,0 +1,80 @@
+/**
+ * @file
+ *
+ * @brief A test for VME interrupts.
+ */
+
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2023 embedded brains GmbH & Co. KG
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#define TEST_NAME "LIBBSD VME 1"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <bsp.h>
+#include <rtems/bsd/modules.h>
+#if defined(RTEMS_BSD_MODULE_TSI148) && defined(LIBBSP_POWERPC_QORIQ_BSP_H)
+
+#include <bsp/vmeTsi148.h>
+
+static void
+test_main(void)
+{
+ int error = 0;
+
+ for (int vec = 42; vec < 43; ++vec) {
+ for (int lvl = 7; lvl > 0; --lvl) {
+ int x = vmeTsi148IntLoopbackTst(lvl, vec);
+ printf("Tsi Interrupt test lvl %d, vec %d: %d\n",
+ lvl, vec, x);
+ if (x != 0) {
+ error = x;
+ }
+ }
+ }
+
+ exit(error);
+}
+
+#else /* RTEMS_BSD_MODULE_TSI148 */
+
+static void
+test_main(void)
+{
+ puts("VME not enabled in the current build set or not available on this BSP.");
+ exit(0);
+}
+
+#endif /* RTEMS_BSD_MODULE_TSI148 */
+
+#define RTEMS_BSD_CONFIG_BSP_CONFIG
+
+#include <rtems/bsd/test/default-init.h>
+
diff --git a/testsuite/zerocopy01/test_main.c b/testsuite/zerocopy01/test_main.c
index 1be546e4..3a03c12c 100644
--- a/testsuite/zerocopy01/test_main.c
+++ b/testsuite/zerocopy01/test_main.c
@@ -240,8 +240,7 @@ test_main(void)
sc = rtems_task_start(id, network_flood_task, (rtems_task_argument) bc);
assert(sc == RTEMS_SUCCESSFUL);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/waf b/waf
index 7ceee167..3099c10e 100755
--- a/waf
+++ b/waf
@@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE.
import os, sys, inspect
-VERSION="2.0.19"
-REVISION="1f3c580272b15a03d2566843c5fe872a"
-GIT="61ee22b598cf80e260beb64e475966f58b304d0d"
+VERSION="2.0.25"
+REVISION="767522112be77f8585812fcfaa08e805"
+GIT="39ef33e48380a2db38a0eae40a3a4a2c954f4450"
INSTALL=''
-C1='#6'
-C2='#.'
-C3='#%'
+C1='#+'
+C2='#*'
+C3='#)'
cwd = os.getcwd()
join = os.path.join
@@ -168,6 +168,6 @@ if __name__ == '__main__':
Scripting.waf_entry_point(cwd, VERSION, wafdir)
#==>
-#BZh91AY&SY9 ½´\¥ÿÿÿ³DPÿÿÿÿÿÿÿÿÿÿÿm (¬#%00e€(b÷/mЀ#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%öÞúzómmkîÖZžÞæ—²U#.´[iÚ¾îõ¹±µ–ú:¥'kd‹î7{ÛïoºµÛ¶ÖµK“¹]Øvµ'ÛßO¾ö¸gv÷s½÷¶N)#.Q+Ù½íp·µÞ]/{mš»<{guÒ‹@Ýó{§«vo;¾ø;ŸvÎiîáÉî44lºï{ß|ûëËßÚU9íõÝŸÝšî·¯y½÷½x#%#%@(}ìh#%` ï²€=âwfí˜:4§[aÓ¹½ y©³A¦€‡¸ê»´ö4=¦öìd+Ö¨ª)ífš0#6 *”¢”{ƒ’Q$#%#6#%YJzÚV½ƒ%çÍíîæ}»j½êºa’ïjÚØá3TTf˦Iv×]s}6óu7¾÷z:#.ÎxûÝW_=³ÛzÖ­åZ;Û»ÍÝÛ}Þsy¾¯³»ß{¸ûížÖîiÉÔ÷Ëzõ¶§rç>^æû}ñ¾íóÛa zúz{x¹-ìz̀ݳ­Ö²4÷³¼ÛÞ÷fÞÏs¬ô`u¡ÒãF°Û"’EE7w¼#%(J*’OCÀ#%lîè•=÷n{Ywº¹½»ÐdÜ öï>}Ü[vÇGÐ`>†ÛEmk®¥ÑMãW`×LæyÛãÚ^u½#%}÷aŽît;o¶û}|{mçO—vpòǼ΀Šo·*WµíƘܶ2>öí®®òÏŸwÖõ»]´î;ºjîí9QÚt×·Zt“ž>÷»Ü®¨ÜÙu÷‡ß|}í­«×ܬ‰&ÞíN«îq\îû½:¾Ã.ñŽ^ÙéyaC§¶Ó¶Û›—³®÷Ð¥&nCzî¤|öilöͶíÝîвë[¸˜÷»³Ûºû5ÖkoX«èqØ—nöÐl­îõÞÞ/`{´žó½wÌ>ï†Ðê¥PJªPTT´jB¹7uÐv&i¶v­Üèº]vú‰Ý›:kÛkÔØëUUܘ{x÷š)º¼»HЃºÚɯfw€#%7ZI#%#%;Ü^êåÉÎó²û3ÊF-Ÿm½ÜözÔíÝ™Üì_U.us[›¶ õv]]ó¸|˜Hû†,ÍqÑ»ÝÁæñ{c;úŒçvåJº{+¹éÔ²õ§l÷+ªWg¡»`úùÛÙ;{n#î²Ç]÷}çww»{»»xîvÛ#.¾Øú½mÝEfù‡"sž¼ï¯¼ùÚÀ°ËlÐú$/g¬íÑ”x<ïÜë°VØ6EÇ‘µÞ®ÚGxO{×>öø½ïwÞ´#%À*×Û;Fªµíí7¶;g<ÂÀëu´oSk´Ý­ëÓ­mï 9.ó’®Ú×ݬ» ݾçwÅówwtåvë¥Þç:#.[.ëwc Ó‚àéÕhÁ”gu“½Þ^ïŸnØu¼ÓÐŒ…/=ÝÚ4#%•c¹éî½Mö{ÜàìJ…U )½{xW»´×`ï{®ñÝÊäÅÖÃV¶•U[}÷IzovƒuÇH*Ší·u­$ëIÒÖªîúãÞncÚ[ÓµŽÛ× &Æ»»}öøø‡½óí·³acÜ| ìÁ“G»îñÌͼÜ&:·»}½í6µîÐíç;ï¹ßvÛRÀîï·Ÿw}-âòó[ãnh€ &€#%&€ £CBa0š#.#%‰ä™PÏTz†Ðš#%õ§’{SÔõA) B @†@©©á$zzI☠é4#%#%#%#%#%#%#%‚D!  M5TÿMŠoU<ÔôzTý(òÔõ)é¨mG¨#%#%Ñ£@#%#%'ªRD!4dÐODõ$ÙOFž¡£õM#.€ò@#.Sj#%#%#%#%#%#%$!#%&@bh&M#%M#.4SÓiˆŒ4 ¢ #%h#%#%#%I¨ˆ L˜#Až€&Œ¦=&”õmêˆ{SIé” h#%d4#%È#%—ÿÓ?£Uirê\ÕQWwk¹úµZvhʃ>5Zu!LA³ ,L•Q*"€)æ°Pcõ§óü Zü“•5OùÓ¦¸ üæJt¦î£Ä‘8•WoT"¢]^SÌÆ´_3ý†os2ì ÀlDs‚4;m»qE6­›®ñ‘ÎÊ­´ÚMí˜ÖØ`Vd…â]jíâ—˜•5o‚# åñ÷UŒI7wD¼áÌKUå¦/žÝ~«oÔBäE"ªSº»U¦ÖÖ5k3khÈ‹ €7#%H*-E Õ:¥”$HŠ& ˆ" HåP° !ß#%hŠ–‚¡#%ª$@d PÉ@mªfL…’ŒÍCLÓd@M$Rj6Ñ35–25)F”Sm&‰š$ÐJ2ZšQ°h[Fi,ši¢Z!#.F)iM€F¥ #.1eM£DRl–¨Š–ZSMhˆYi#.š ƒ(ÌÌcRF£Qµ&ƒJl†BjcI@˜hÒËHÆR›F¦›"[M¶³U´i˜’ÆfLÈM& ²m¦ÛM65%)-5±–¦Û3fZL“1‘f´ÄÑd¢‚™l‰B!Qf‘´R`4TH!`ؤ©„f•Jb0lB ‘XhdFIR&B4±ClÍ ’&)BFRÊËf$@ÒYŠY5“ccE#6‘d†K) -)¦ ±%&EDš2hhɉIF‘($@VAMEˆ¢fRÒ¦`›É ؉1M Í‚ab6¤Ø–V‹$”›‚$´”PRD•’%¤Q0EaÆIL# ”&RJME™±£XHÒjH’b iM$"H²[bË2ŠFÌ’‰²™3bdE(Í‘˜&U4Ä‹)A³Q`,i¦†Æ"5+%’6(”R"šIˆ&¤a³LcI¡ &¤–Q”Ôi3HÚ("šh¢kP,&YA¤²2‹%&DÐM2‘*4f6”)1¨6ÄÊA! "“1F4,Ë ŠÙ&TÌÕ˜£l¥±(‰™#.H¦!HŒØСYM$QF¤É&ÉØÆJšF¦h±b2’™˜²2³i„¬D`ˆ¢JM52i™!Œ¢)#.š1Sb‹5*R’š)#.‘“2)´…HÅ(ÊHR"‹E$˜’M)´i5DŠFÀ˜Œh&Ti¦DÚ#.ƒ,„Òe‹DÈY‘¦ÉJb”D!f™ k6lmd°$4–Ld5EPZ XR$Ècc1 Â2ƒRXÔY¤J&ŒE©M(Z˜hI™IŠ$2ɲ#)‚E,Ñ¥1„5„²e"$4M¶¦¶´`™BjfŒ¦F&”RLȈ­¥6À¥š,Å&‘”%–TÙ¢-L¬-”bC؈RˆšD#HJm~Ãk¢¦©aˆÍ¢ŒlVƶ*6L¥4ÔR¤š44´…¨ØFÉ5Y†¡¦c(ÉS*H¶D‰™¦[%´Q‰IKQµ…£&˜D±5˜±acR#6e¥™2eI´*Ò4TÙB+dË"©TÒT›63š¶,Y­“L¬‰S,¦ÖJ²ÙJSLl’‘µ)¬±%¤2RÔZ ªŒ!¨Õ£%AEVMd£jŠÅEH”kDÑ$m‹hÅŠÔl2ѵ€´™0°U@i€”R £F2™&•¦±Ʀ&±‹d‹i Õª–µ–* ”ÍKY4IR1!M¤lY"¶¥Y¶1Jm*–R¦YaZšDF¶•K"e5MM¶„”Êk,Xl²k+,…lÓ  ¨ÒD…bÃ4˜‰!´…-£T%¢†KQY&š6LšJ,ˆ&ËŠl¤#."+,‚E1”ÌJ”lÓ4 AE¢Ó$&Fm6“F6ÆÈS¦™cE%bHÌÔ#!4LÔQ¢JdR–P5BTÀ4˜Ó J™²Bj-#636£)F̤’Œ£!HbÊkÃi4F5&Éa $YP…-4) Äš4Y”©””²•˜d¬aš,‘± jE¨Õ5£4ECF¥)6b¥fQj4  2ÆA¦&Ä•%I”1Ršcd¤Ía)ME`Û0Ù¤²lf“E˜‰CJ2Zfµ‘hTÍ2–H’l£0D2hªih¬Òl”Ä,LDÒRBAh´[ Q¨ÔQŠÆ-–ÃI†’M#6LŒRˆ#b-f›C2ˆ6*2%,¤©‘Ñ´k£TI¢„Ë5EŠ$Ôš£PdðŒj”¡kA¥4Ë!E$Š¦Ih‚5Eˆ±JÛ"¥F±¤ÁdÑÌ¡jQHfJ$©±(‰•j#V#.2,Y*6Œ)‹4¢Ù2V-­&)+)¶D¶M¡¢T’™R¢Âƒccd£dÑ&#¶HÔRLÁ¬ÉƒA#6)³6&"šÆ¨©•“Y‘¤)¤šŠ¢Š&›RV-”’ÆɈ£F‚4˜2JÔˆÑie£he¨’ÑjŠ±”£i5² …Q¶5%)ЙÈ̌ƚ‘LÙ&kDlU%‹hÔ•,«&´LÚ1lj £cc$UE2©¥XÚ6¶1mI˜ÊQ f²±-F+iQµ´©QJV”f*šB (4X"J&ØÖ,Ê’+I[M²“+FbbÑIE[l¶¬R&T*&$¢1PÀ"CFŠi&ÔÃ%XÛØ´Í[F5¬´•42ÖÊY6ÔÔÛ!mI¦¬ME26’QA°ÖjMI™²Êˆ B¢LI&RLˆÈÄd"Ù-¢Ñ‚±“þn··ýÔ¥C)þB51jÄÿTqôe+(a(Æh”‡÷!þ§ ûbåÕ LÿL$hÒlùNzí¿Îó±½9¿‹øºô·¦SUIPª?ë[ŒlÈŸÎÿ·ÖYLâsÈ †ßùë;L(0\:8À¥‰pÁI äˆd˜*ƒ‰Ãß±¢rý*¤çÿœ3ÿ³ôZÑÿÊI(R×+ˆ½”g™,$b/­Í0ÿ-‹npz%ZM‡¼íõâ8ßwB¹ÁŒ¨d~Ë Õ‹NØœ“ÆZþ,3cRR(l“ŠrrXˆþúݳf¤áV…cÈÌ`I¥Û"lÆLhƒcôzç““G]“¼î˜Þ¹w§¥^¥ã\³Ë/âuçåu™Í\‚LŠëP¦"W‹4LªvcK™@¥JAF&Ä“M‚ê×bÈ‹Ò3ˆ(êñç]¨eñnY½74h6 m `Ù—(c2@sªªzoþÝ(‘ 7p‡ Êc#6Gv…)†ì,`¼rÂü®R[ŒR}ßÒëdzçgsÅéšÐN×VÄQL0¦Ð¤Y;ùd«Ïô8$ÚÍÏ£˜ØÐØžj¤Ï(%?·râƒþ°Ãµ “ºÀ¡…†JÅxžß÷k¶Œ¶cÂx-›2KËw%€•¬C°UyáyÜ–íÞw^QŽr車esn–CQW6éçqllO²ëå5¼Z4EA¯»ºŸ?_ÆW‹ò-ȦWÅȶÆåÈIºÕø¼u‘¨Û?Go*„Èyùæ|m<™b#4L9cuPEX°ž«©#.¹t@#6A!´ä#ñÆ#.¯)ÎãÂHˆÃz”^+›Ý\Ѩ8šë•ÞwcrŽ•Ë™(Ë®é$s™5öø‘ZA„ÿhÿŒJrÈ.YÚlCÙ’,0!M ðÛtÉÈe«#6ýR­‹ÁÃ79ô,ýפ¾ìÉþ˜FtÃÏxV¬@Gô¡€dž%lþ «p’—<kìÄ9 c£ðÐÃ|¶Ç™“#tE]H P0îT[æÈ؃Ù_#¨½—zírH È¦wâÔŠŽÍW†7mxþVYê? )Ärvj³oQžwE¦m„e#.¿ÂdÔ‚™ç™×¶b²àþN!ïe §&¢”øuû/ÑÏKà`ö§nínœšùT)"*¤#6d9 #c*w&éƒbÛ†ÉðfR6zš®:'ï.àûì¥k,YL#6N*}±ÆyÞ>ÇãŠUË"õÖ“UO«pi)8ª—â”+ñ §Ó¬,‰±Èj[þv¼‹lQôÇôa™1Kèë¯Ç×FÌ÷¿SªÄ7Ím‰Z½&#6”*‰£NŒ¼Pâ-#6¬AË@‰*•{iX<ÙZøW÷»(Éðâ»EEE¼nY5ÆÕ͵þ—ìw’>ÇS6½•ß‹«¦öøÿM‡fhŠF¨%2+zmýW!†ŒbSQIðîÜ®÷uVé(i%"÷@Xš'+¬¥&D§)J²‹šå¢¶/]öï^¯"R¬•k¹bÑ_·-×»±öõÆ*Š|ý{ü=o÷îèë˜ØÔl›|ÏÀÞ1¨4–üŽ‹> õÝŒ>¬žýóL—ÖȲ"4£~"Uä¨ræPaŒLÖ¶jϹ$Â![ÙùzÛ²†F²lWßî«âøùÑÊW=ì¼4D‰Áa«l—AHÒPŤ'Ò'mBµ§…C×Gƒ¶éE2„R]U¥"»wÕ¬ü*ÔðХ߲œœ²ôî²Á‹¨ÀSE#6lÃQ MZH"JuœëJ» *”Ó#6I¢L²íãe—¥Â„S²ŠŸ$+ b¨¬N¬<ÙkH](©É#6<*žoЗçAJõ ü/•;q°óap×^Ro^X„jÅÄ"_gñ‡Ë<TC—ª¼s×y›¬Xq&ýqK\:05a–áeF1PXäiMö÷3g{O4Ýur™gúkßÙóô€oÄï3à )#åªiÔö¢'”־ʣôwr³éº8;+¹+Ÿ†oìÑ®Há m^L¯q#.1¼öÊIr%ÉnCMÏ}‡×–ìƒ6q¸v~„ØÆ5.›|båÙåA„Å#ƒ™S—<*Jtªð¡áô\äáDäÑö%*Éú9ß<}/²Á"#.“L3¦ÙúÀýv½ëåŽ+’ù§T-±·,»í¶g)õïDš!;SñŵaÊà=±3œÏDKA´](´ ¸åHvÈÍRß#v/6P½E#Ç…‡8qêΰâKè(q<åÅð¹’ý>¼ê÷Œ:+#%ñ²(\HÎpÊ6ç–´zmï*'Ü©×1Ù»á?_kŒ#.êV£„ÊFyûeo–Ðæ”$ê?•ßjfA›­vß¾ÚœNü£ƒz…$…!¨.fä³Ü¡Ï\ˆiÌ©Ê#.#¥ŠÉE0F:E&©¯}Ùž…µAîg]°éˆÃp?X7d1€á\NB=}ÙILƘ#6P#6íç;¢gä˜ý=«»†"ž<ú¹áïïRî:I2Éðˆ\õ­q.Vž)gÉ…,Õ8`¬=jPŽûí‰ÞèqëZ®#.þø¯,nM´B8÷8»è¸×µÛº†ä1ž‡i‹l÷õ m±Ï׺|š5Ì=Zí鵦üLuÙ $‘(w0©WX uÒ‘Bd˜N>ù¢Ü¸I¸ì¾½i“íE•éËÒêwë+Õ^{å5ÁZU{Š)ŠãÉÂé¨c¼T˜ì˜í§ @^†Ç¡=4´îç ʉZÛoý4^Û1¶Ã‰±öíªÆË ˜ò-—îûâû™§õoÉÜn¬æU>õéºiúK|e˨†J/±ßåÇ}m}w*IeQ92e@êÛ íhQAA9¼CrºõÝO¡¸Ëðuu5¤&÷JÃF}w/Å¡j©jŠÿ}…(© Ÿ+þ«¯½û¿éºÚ|~þëÇT•öµö°ù¥±G‘R{¨«EŠ ¢€úªS>,õ‰÷öæß»åj»çíj܇±†¿•™Yõ¿¯œõâ‚ú·+$‰þ.hÚ>n·‚ªôj£]–P‚ê©¨Æ ×¶{âÝûïg˜œå mk4µ“O§k{Ñ”ÇÛEË£õ;n”dm¥AyÑQŸ#6ÚäúÓ«f‰Í²­ãX—8T¾NL\ð—ôP苘1 øˆ63œYî y²úUM3¦ùÝ#.X˜|è¤TWù“7ìÍÇΊD^jÀEŒR,Œ)9Õß¹ÊÈ·)óÜÑŒûúuO³~™¾$_¶ÏŠ ÊsãÆ4?ŒZu>žì]1yÅøÚüžkÓEµÛX)>óOÖâm8s´Ì‚ëéÞO–z\àÚÄÚ¸;â¦R¸Á1ŠŠ~FS_tí“Rn(‘ØPñ¼b˜¤Rf“WYüÊ¡êô„cì×ùúù«ÜËÎP¼B´ ú’Y×…é¤DQQe0¨x~‚Û{*©nf £ªwiX‡T¯)œ’6-Ï;Ë£êw³$3Ê¡¶Ö}•TÛÉ|-ºz²8ï5›`BüéÝââêÖh¢’…ƒÒ¡¸É~¦™J‘‹Î€¤Z+¯¦9r¬èœlÿV´xð¨#6h6ÀêD¶x0¥Ë)AX›³£*0²¹ë¯_#.Ç·†È÷zìe¥HÙÓ,›¿µñïñ²5AòõÁÞŸŠ{žeÔË7Sú­þ¡&yÖYñ¯¥³LÅSÂœ¹¾Ù?•”¾ŽÙƒÊ-„y¯69^èŽ}ž¦pà†BI‘iK‡&±nÎǨŸþ³…éÝs»Î¹ßŽe²‚ð,¼&2:LäßÏå[‹«¸óxHÇ®*–Ø×ÒU9„åé1䌟 DL5;þ²ÏiîÐå‚„QT nUÙ‘`ÒŸ#ÍoJò4†1Sºëïv馫ÚÕéa離Mô4O|®­–I$ {U½órà¼0‡;0©*ÃzG/½7ƒ+– #%Qb ]’öÙK\.ª¸­ÌÖ‡÷) I9q×ëa¾¡—¼òó#.ÑýýïÞ5К¾ä¬×w*8ÙA—'<ß:¥˜bÊï¬2×ÂxÕQn:ú,öTl8:<ôáyï9ëCÛ¢M\¡"#.JvX¢=Êóðw¹Ô%g±œCÍ•P´GhaÑû£™ªºÅH…”ö)¬¾U.ÎõÛ™Âî‰A=†”s È«Ôs›H]#.¤ú$¢8.‹\Ó³Mìòõò³Þ› TÑTaÓMš}ª P‚ «½È¥>?‚¿½¨èØkÕborR#Rñ/ŸÌç*3ÔÆѦø–Ú4u©ð±o‰c‡ÈÞéñÇ#.vnV»pÁáã)r‹³Z”,ª£Ž.O€êÀ’·}zwîÝlnª¥Ÿ.LQøy×@EHiwýÐçç=Q›q§"%V2&0+VŒq¬¨w=Pë¦uÁ7ºV^-Eu©QQ'çU0D}ÏéÞ™Ni×ôñ$¤úÊ'1ÎK¯U=_­“lS­#6´íŠ0ã×D­1zßÆǾ+âdpÚˆô±æZ?wéžx½\`<+½—f—Jøã×~WKËÕ¸îáÝ8n©$z«G»à<c½Ì/Y€á÷ˆÓ vfˆz3ó¹L´cz7k­ù%ûÚEΛY¥ñ¹-J`]ÞBvP\ÃÚïŸHm2LˆMßwðrÈ÷4›:ðÅ’µ¼`¡‚¯\Ô½u}˜<(ñf`¨Åˆ³÷ôͽ‰UQ9c:ªO+%‰ŽöPiP/º°<H¬§`±È6,I<Y–e"4ECØ@f‹zÝ«šÉbFŽõ™G¡B>q¡z<À®÷±¢—Ù è‘ì#Œá?Ÿ—™}ûI”¡ÓL»_KÃ'ë—B¶Øÿ#É+ù¢ËÖò„|ÑúåµömŽ±‹ת©ü°Œá˜Ç’7û"ònþ½bôŠ#sh¡›¶¢~¥?~´›V]6k)0úô£ ¦ÙUBÕñ °Uˆ¢,õ'«@ž5ûÜ+ÏÕlŸ±X6µÉ nŒ.ö{pùèü}´:ÑÚð†y¥ªŸbsf19%¢ØñZ³æ#Ô#6 ‚ ­ê,&Ñž3|#6y€"ú.$#. #.æuµÅ.÷ éÙד»*{gd¾Š­evKfûQοí¶ÒÌð7N ›hÄe hP"1b±êL`ÅMm¬"ã-ÅX]#6=™©Õ)˜«Î#.äÓ2¿+]øºtÉg“:Ô00Y½UDà’ÆŠÛHœIå#LÇÍ’6\j¾}›Þ#.‚Û»Æ6Ñ=U S¿ÑÏÞštÌ4så Ùº"ïg³.¾‰t(ÄÉ:qþ²c1û¦³^¹ÙÈÕýÉn*fb’¢fƒÛŠãš;èXhêöÛ÷Ç1•£a¼s>¬=N#.ä÷Áý(ûqÃÙ†fT`ÒalÚÞœ~2å8ŒÀr¥‘Ÿ¦!:Y@àe ¨Öj¡Á¥N¬Íec±;8`i‰j%ÉÓcW?äDM¶@0L‚¸¤\Þ'k‘KŽ«®NºËϧcV*±FÖ˜Òßãñð³Ô9O—äÇ»éìåôÁõ*5,<:Ø’|EI$¹$G4¡Õ{âS§#%bŸeÙ‚‡TöŽ¯äìÚÈQ·Ø!û€PveòóêÞ6‚^—ø\F´÷fX= ”*´nïú>Q/ãnNQŠž#64…¯¦E¼×^Æ~ú“’`GÈ'0’õÍ\P”DØLÍÕ7؃Ku„ÑohsyÛÝQ¼ã*Sšçsƒ;5XvPÍf8uŒÁ®[˜ÝÒS¬ÝÏ’³•ÔÍ4”¹²†s?…'ÚÝ­i Î;¾n)tYG.å¯K|íMÆ×6AâÖuü×8'¶¿UÊÒ»*se¦Ü™L)e‹8«|Šãý×W#%}O`×|AÊ>ƒbm€fò†\sIÑO##y3£Bé#.⊠}ÏÕ¡n«r£CDb+ÂÛ` ì™­úÛn¨>O½Õqòh‹¼’×59·b¢ fŒ~~˜V#+bç±^Ug•:^óŽ×ÿxºTq(Ū¿1™y{T‘áâzÒÔ²?[좧s ˆÔ5YÿÆgk’ —qjûWXvã_WmMÆñuÍZ3¥Q_{=’žìã³—¦(5#<IÛD9Ü÷3Ï^—’y%0I†jÂÉ[J3NMíT ¦nPÿG†4„–_oñï=BÚÓK¤JÍ¿‹˜ï¹¹oŒÕa^ݶxe@eˆëªã8GHºê½ÙGÄÆŠÀú"ûÞ° £‘’wñ†ýž~Sa¢œv®4L§Uz°ÎÞÛß²+f”y´Þ¿#./wWdË«^úPÄ´¤áYKAq:;åG2jT wÌCˆ{¿‡ÚœC¥™k5PuÝrª0àÒs¢l+«3Û¬#Û8]Å‹‘Tœˆò»Ó:—úûâ$KÈr™‰0Êâ(\ñ>ˆ‡„Âæ©CÞØ ¥Î]«m9¹ÙãHˆ X1ºÈ½7¸ÙŸdÉ-”K†OèÜû¿—ŽqÛDË‹âíÜ×s²žó#%$ÌØ@²š%"£ÏiñÁGn¦· —›šÿ¦åg­sáºÖÉÑosã¢Ñ+-¯m%b”"¹mofŽŠÁfm{pYpÐØoÆ­Z”̸ Â4¸b³Øúxì^S™ÎuÜÛzVþîlbݵ Ï°POæÀŠ:›»ˆ¾Zn(Ô#{›7)×¹L ùσãÕñÁ”€÷YÓ¡á‡õ"6zXø£°ébc™G“ë-þ7J-YSÕ–#.™[òw_#6}P$RFÂ/þª:üó²§HèCþCo:“Të–=49yz¿ÃÆÊræÄ•{´R4IÖÌom¨3 ]•F“m;Àuc£tÇloz‰Á`(]ô¿5Œ¨å¥÷!œ.º¿»ZUǤe(®™¯ù‚Ê<V+ÎŽ3¾Òÿ™Ó²uèæ:»bà-¯°F–|¦W“ìºS·§Œµ‚]ëÑöá« Ø·M(ŒW–ù¯:ŒýuÊ:jÝÚÌ£H¹EÑ£8`ñÅتá¨à³ýk £Ã7c¤ü¶<Î’W‚=ëS¼qé4§5#.-ڃ߾›öÝE3ÂcÈär¨Äžù§L¢^3ýÞ®§r"ßM¼}1(gËD­ôÑ%¯PÄIƒ¶ôÄ`ƒ³”CȲ-G%nG5—lV°‰À(#6QEc—ærÚéͬ١C8’z³‡`Cß½uˆ.7ªY`ÞD,U GÊRÌÑvp«œOøg[\þ–]EÅš@Bcvß8èˆÕñ]Ñ·7ˆŒBov?Uëì”?ë†oŒ;2ëðÿ…¾>^ŽR÷ F““Ö\«ÈÞ¬Ãô²†Ct½FŽ«X 8Ž,Ò;hŸ˜Xj¾$8ÍûYùüxpå»È6püà ·Ý¤$ÀA7ýc€%#%%"A/üÕê¢c–å9Xãßá]5GóÚY"E5É#ÁÛ/*9/„QÉ?Bu˜jÑm-Ã&é;ýz|ž¯‚££Õ¨l@'ñ(¢¹*öúi|GÀS«êÓ©ÝœKð!7ÕmVÐÒ×>ƒø³9€&u ŽÈ„ƒ#% ’ûÿÕ»Ñþ-¢îÕáø­ÇîäévÞ»¥ #.cc¢¼¢ ÎD×Ì@?Ç4#6†¡o轶j0–ê**ÂŽ¸ÙsóîÅÇ#.„¾º"MŒ\îª"ˆ¤‘ò©¹#%gíÞœá4yR™b(Ñ>@€òtÆ¡R¿šÒ]eÑ·äsvÓ Én(+VpäÿE®‘+ü'6¹†ðN°¢3¤Ä[(ZŠaÉ»I{–Þa½x=™Ž9"1ˆ[ëJ¯l ê1­H#.ªñŒŠ%OÙŒ#%”ì#%ÄcÏm¾ÿ¶ç•aùã¤þÑI‡ç³ñåú#.5™ðׯ_ó~µ?èÌO“HÅGTUÙÀM¿ƒðý¶y´»õ¨5ц6¥—Ãcj™b²ø§¹ƒƒ$|_Cºuž0€^~c_ñd° "wƒ#%°>בj>ðõ—ER=Sþñ¶s|º5ŸÛå€Ãm¶fÕ¿.Õ4|œåÝj]“³‘¨:;£únŠ>’Ÿñ:ÿn@ é )ª×fËw#.·l¾¡ßþ0}÷îâ£(WÏÎüoNßÛès’ {G4ÆÞ|ö0t9×bbÎ?GÝ÷k_ÁÉfª ž"@îkò•X/E÷;¥fL¡¦¿qößÕ5¼8±“d¢*¡â{/Ìë¿D4áÅtÉšûàûoötgS³wÓ>¬XB§<ÈIÁM•mGý—ùu…9£Š>¿zsáþ¸'^Í¡c®¶7B ªƒ+Ã…À3ïr˜\«poª_P–ÜâÛô=‚TÁÖ“æõ§º»¶4ZMI:îäÙœ³[Ê+YòÈ{Q”Óho³…`#67]±áÄRd1ÌU…²[d"ˆ—”b$#6Jk!¬gÛªLk Š$,¸ûÌ£Ö[ËúžP&¶Ï•¢U´’†ð¼U1#ÅË;§¿&b±A…1dy%*üüóŒî€¨ "/Z7—cûÿuËBˆsrœ™§¿-‘œ$ýþ~…mLÀ:z®X»U ÄDq#6“Ïß©s <=Uƒ¨~L …¯R›5¸}Š ÛVIé­³íLgï› ¦aóÓáåvº˜ÿ_E—F–– -™áG8˜Ÿ“?ûmRGÒ=bSfqÖ7¶ÆAi+sèü"Ê|;O¦$÷#I±aiñÓíÁº×®[Hå[#.º“œu€ÆÆŽ²X[È—¡J×<óÙT¢ Þº™ÆÕQjª@J’ßü.Å#%…W&û9œo…èîD@¾Àß#.âë…cû§˜A,BCG:l¡úkLuÐ#6”‘Õž ?; éõæk¬‡©÷‘ž#.qÌvÊÐ3:^LŸUÈk4Ž#6`#.~•H‰5ðÏÙž>Ãõ›÷þ}¾Ó¯‘[ŠhyѺBJ2\¡~¬dâe#/¯ÿa7·§4ãYUáCÇMs÷þ{¿ÍcB'$Å;Ç©·8¦ø¦#.{,µj€weß#.røâ^˾<0fxþîÍÎ5&Ī¦š6P·cQǘ"5Uô†÷êŸE7¥ã˜y–l)p™Ú7úµ!$—Ó8>ž{K2®rãyòUçD#¯lüw¯åz€ã=wÊ‹Ý#6Dn…¸}^æ¡}ªé«)D¥ÐŠ”‚]âuq·à¿çÛ³2±³…®\ÚS{ûìSXãÅY% qßÀn©ÇA#°óÑUá]=@tÔ³žÅj&†Â»Æ5&fîŒðpK‰ ¶˜Sd&vEÚ}´zGYÌ*B#6 ìÃQ>Gêp¬vþ—Û…m}M‚ææ‰ï¤É *Ý0ƒÞþÓpì÷Æûè|A²vB0;½–k[Èé#6¦h˜"# _ ßzéòøÍ3ã 2&ˆBžÑ…5Â6‡…ܦ2,,»¹Ç|زØR¢"ÈQˆæY¼° ÆÒƒ •ÇLkG>'ôjŽ1* Èéô‹uüi‚S1ëŒu\SdIiÆTˆ°Š²O¨6ƘڱÔá‘¢»µázl_gêÿ8hx”MöX0-ôî ê#ôéŠsüKˆòÿ5ö¨»?#%Ýyx®)–˜À’7 y#%í2òw‰ ÐPc±   Æ#%tÀ`=!#.fîÁN¯%Ò[^v…'k³Q÷Þ´R“̧`ÆzÆ) û¼nuí¶#ÎÃí?.ÒIpJ[¥iB¡×*,ÂÒÅ š=ÕÕ`„ìQ×pgc¼Iâèez«ôC,Á†f{Ê2ѪRuš„Æú 빈7©Óyå_æ>£vöt…µÆ|ú9´VKœâR˜;#6(<äwãC)ª((F‚Ïv0ñ2hÊЭŒ–ˆDÊ#0+C“9°z“˜¼•]>ÄlÑJYb¦„ÒTœ-¨¦Å¾bàèÁFrÐpõŽ*(~¨Ñäû£sÐÌꆄÊ4 u»y!jŽÐ#8±sy#.bƒn=>tzËÌh†Á¡ÈÌ\åê¦å­…bPõYÄËþ:¹Â—nT ÝÐ`¨é“Õñ턱ֽnz1»DR‘Å+‡GèobŽÊØÛÑ£ŒU$2ÖåŸ#tóëñØó^¸émÖ½¹ªO8«,›}YEω{^ÜIfxOÐpLÂÅG•#.pßú¿‡¸ÊYE³>vKUÓç¼çûˆ} šgا©Ú§^Ï­ÜøÌÍ+#%®[Ÿaœ:9Þšœ9ÉÃd×+àB~e.wµ†¼qalñwE#%{j›,dº*1—®íwyæ]®îëO‘˜ÀFŽn‘snZ*4x’m1|F¢ ˆªÝ4s¡ÊŒýÖž{Ζ¹,äl D {#%ÓDhM´ÔD)$CTÅVƨʣ}íÁ14LT.%"¸ã #%l@'D188Ö÷#.A³èòñ®$Õ^¾0Ʊ³\´h$‡{Þ®#.NUzJ);I?×$è±ØíJö»‹? v#.ê‹Ùö‰T«T¾î°¦‡ãaÆ»EÀø5vÈbãlyA}·É¹»Ròš¢C÷)K.Ç(…ösN\0p¨¨S"Ðoœ*ñ“!WTðCyp2b2„ˆè/mû¬kÙÓY¢Kn9‚†¦µJ:¸Ü­³\ü{oM—h]¥ÂŠfŒXn#6àÛ¯d Ó™Â/QƒèØ…E®‘KNÀá®WöÙ=þ= ^u¶Ã?×›íÆÊ“5çãœ+8~Ñ…ZF0oÑ›48Ã@òð_¾*Çc0Z4ÛþþJ˜°q …áF‹Ã=’tÌÐÔ%Îln¶b–Š†æ˜¦Àé˜JÞ(BwyÉ.ÅBjܘx†HÃQ•TÊ8¦4c#6Å|mñd@ñ˜#%†ŠÒ奉c‹6PºËÈaFĈ`Ÿ]ÿw&ÅççqÕ2ü2GµÐQˆ¯ŠÛš1R^+¦¯LZ¾•yïÛn@m2(ʼn£-eFK`²(euDÕ 8IwN`;ª–4,œ:$VbV66ÄPEBD 2ÙN—ßYö;üó6é}rÛ=ùk²#6_³S0¢³*¸‰B¬¥yÔy¥,ôS°X¾.‡9€¶¤Ëuª‘ë@+ë#%)|ðÆÔÔ@zæX"% F #%Às´õ‹&Á;Æzüiûç|ú¶&í•Ÿïöíˆ0t 6‚5à1”L Þ=Ùôãõ¹H 9ív ¥RÜð¿4‡Þ{m›ÇnpíÔ+ãÍ=ŒSº!tî#6oÅgcì—tn€…™©t,–Ülu„VFÆ”±#.Ù%Æ·©ƒ_NÃÕsËÏù*\¶µ8r&Aö„²ÕÚ¨N6˜‘÷KY¾#¥ö’ZÔŸŽÌì:‚w¬9§ƒUf ‰Þj  Íñ0“ÀF•‡+IŽuœ,—<H¿ý…ÍF;ù]õ½ =ci?kÓ]w›©XNÚ“ÃC$#."oáo”+wÖÓý6Ì<ärÿ‡gŽ·²ln{²ïé£ú/ä#%Ưۡ¬Ï‰”ªÜåŒô¥ázY].Lè®k¥èÐkš#.Ï·…g/!­¡5ªJ ±R%KA¾Ç³8Ávœk#.î#ƒßxÄEûÈ¿•÷þÎÈ•Úìv`‡‡ÁÕåEjÜ¡ˆøØ¢Öˆö%ˆ#d¶{äs1Ü“‚QãËðÓø—É­Ïe·­’&¸r¦¶Äj¡ýÓ;• ¤5wÙ1#.Ä)r­Á·÷ íÌiò/:'D\ÿl–Ì€m#.¦2!Š1Mäñ&¬ˆ¬ƒF)š˜èdXUK¤@md”2,ÈtëE„4'ÖFÁ°5’‘F:Ep[ÌÙRMÔ0õoïº5"H­J)#.’í¤#6)$Õ¤o.Ø…)ÚŠ"0Ñ¥bÈèQ„Ѹ&bÄÌ´‰cˆu·^†ØŒj5 ÔF=3#. M‰ C˜]˜o/Õïâ÷~ÞaÆ„DbšÃ'™¢Y鱂‡˜ÍCB&g×LF[o×QLi ‚†•Â4ŽòìÏ˃GdÛ78¬Hý]Ñãˆg(q5ÆL¯;•ÆÎEêRiü\©Dyb9iȦaš<µL£Õö½w±Í£ÅBM"1¸È¥—Yi]Ã\†¶…‚Ýb»/•Ì]Nç†úsé Ç p#fâ‘ ›Œz܈òëÑFß‰Ä å .¡‚IÛµË#.oQævyÙŠxq=L”°¤XÁMÀ7E+_î9x:ãOƒF#äøT`¡’œÜ³µq›„ùCåÊjm·}#6±;¤½d‘JHµ!MSxžLÍÁÁ­ˆàé#6\³•*"ÿ©©eã‡;ߎÏxý#.–q#Ö§i¸>NcUGEæÿÏ^ۤɎà˜4ó$„Á;>)Í™n$"˃ýȵ7æÍ–Ñ ÷šÈÙf1¡ýê(v Ž h«éC5ל`ò ã{Ô R#ƒ7(HTZ'ÖcJ­:7 Zf6NGÌQä8@ª‘sIÊۤ܎jì’Ú#%;qŒÂnwjÙ˜Q±¡3¿Áaªê©i·Š(Vj¡Š(bõëVðÖ\ÓâM´0kD>§¶U‰„zpNzb#.ý:ë8á“âJï}ŸC•ñ•Ov(ÀäOË ¦Ãÿ¡˜6ˆ8H^&ì×¥±Ó³?òtÇy™ë°#%#ûý¶¼·6é³]Vâ¡3‰ée›Á×GY`IÁ´Ú·%®Š ¼ï| HsϺš˜ÛE“H:u™Å) ²,Fj0ËsVºY#W6é­щ(4ÐÅ"´4Yˆ„aŒdtªÙ#6‚#}´‹Ü¶`Ø5ŒMõøf¤ëÀºfmj Î(#.árµ¯ VŸ¢¿í;'\ãS—´)=ǸöòÒ#%'Ü€”#%›½ƒzÜ\‹n@|ʼîmÛ¿6ôÙ ²êö¶¾7Ç¿ eÊçó)vaÒ¡ÃHF‚Á‰ XÏÛwêÖ?!¿òÑŸŸ©›Ââýj—Ìžx‹OuƒÖwð4r }ðÛãæáŸ7>ñ§Æ(8Ñ_Ì]`#÷˜úv#%ƒHÑ=©“UŠ°Q0…þrm¾H%x‘; Öõ‹™ý¹….?½VI|gxûœˆq¨[$›î}PT=]‘#6 Lk+ƒì ™•Ïãý¯²K»d9r_ Ÿ¹bD³åAˆÍÝðü(òìù· #6#føåðÝÿ¯c»ì²BŒÜÖÝÕÏtê~jðêêçßlúü3~i»Ónˆn>ûy¾{çñ~ùë]—‹ Þn¨`"1€Ø{ô°õ¯ƒ©Y.HGs$‘å<šÆë½\GŸ<ŸDºáëߧ=~ÏÕþÓCrqy!áã^H³U$ÅÕ2Änà cèúÔb3AÙîù½ùa.ÁGc@õ¼#6\Q\RÒ9¼ôUäÎ)u”Û¯ëqV RVÜ°¶·úûJ#%Gh¡ïÿÒ#%f—÷,ÛQ¾ß·èøvKx#% œè¡”#6€”»—wE·B¸cÖáÄîâ§Ï—¹øj<}Åz¤4:CíÖï÷7¥d€B‹b3Ôíh"™µ%“D˜€Í•,J/Îã14i±Wö ÑÛv¢e»véRŒIKûR…IXñ¥·õ‹jý_È^Ö„ÒVÅ&.Ìýü²Ü2SQui¶ø]ˆ!C†û^~7²^4snë«u*䔩󫺱5øuåw?çÔÕû%`¢åÑ©ÉãüjRD>=>ÿòu>œi>ÊÊ:##69dc|›ŒVþé­nmü/®ž~çú•´J:3#Þ'$!éþÝPY]oR®ˆm½$­òZ¯&Ö¯SQUQF@ö#Ûá aøè}ΑÙCui±œb)f¢y&¨ƒŠ•D éƒ*þn&NSìÓ°%ö$¨áTò¹^%\…™ýÖwbIlI{VTe—ùä§îjÅ‹çƒÞ•¥Ô:±…Ø1†’µ&óWR÷ñþÑùùÜ>2…ÿ#.#ú6q4¿~¿•ù·X30~>êí{P?œëF(¢,Ö>_#6à3~ž‘­Úˆ#6 ïž0\îr›fžÁ«#6C õÃü j1T,jƒ¥q¾#6Û6Od'@h'Á¤¢$„K“ð²åª¯1¶ œ ½šô† ±ùË=rÿˆ2·æðWÕPÅŒùw©1P°æ(óÚ‰ùPŸ™´†îΔVåRh!0ÁV“ñJ@-I[ú¤\T(–#6N=>$oŠ¿ƒïŸ-#ëüþoÌ}÷~?©¿SÄ>áŸçú}+ãä™SÖyà-[O¯?8Ñ[úF©î»ÏDÐÏcÒÿ½tz/Œƒº~˜ùÓïá7|çé6`¶œ¹‚ð‡ÍVúö«Ãñ˹G8©ç°híýqpä"#.ó$`{z—EÂêVsãbÆG˜¹ÄL¯“<„OeпõIGá=}}›’ô,k±Þá<öv}²#.¹—iÒå•ýº{ã¶+¦p±ß³0éý¹@ÒÍîÎÃ{¶uÁ#uwDa n'ª@ÆpTÏÝc#æP”OR˜1ß@y÷^¥ïPÊí8jßeÖÑnvµåô¸aËA¸cüU&dã#6V]#ÛêG²S "‡ Í÷ÙKÎw-ÃÑ4¾Pdé9ïÏåp.Q¯„Ÿ~9r—a#.xR^ö s›eH»q(â>ƒýºë9ÕÜ{Ϭý°±øï½áäÞQEV§ì±p2”/Xu|¶]æ¢c=!U%äi?Cù… CØcX¦ËìÆýýøPRv²Ä…¥24q`Þam;“ÿƒ ŸÕÛ¾×Î_\P Wë¾ëÝ2ÒÓÄ«>îþ ë¤îº~š¤>¶–=TM@òjj*©¢³ÒB''%=-–uûûHkû8ܪpÿXŽËؼ‡¡&>]°CÒp}!é8‘BvþZ>Ö-øÛ꘶*äĄǸ„$M®pƒ9@øI‡Hg­2q#.SýuFfŸÑýÿ›3ñyXx{Pî-ô2Ñ°oõIöO¶ž–$˜¬'Ñ¿ÆŠâ4ÊјŠÂCcíÔÓ×å óí5±y¿oœü|zÚ1QÈøÎóK,,$o_»Cd œÌˆbWά¯Ÿ–¥éŠÕ áZ¿V3XÀŸÓÐôüŠ¹CŠE.}•<åÔtÖšžv¹W‹:ìN#K»aÝÙ<ïíáËXÿZ0þ:4°4n'ô)¿©cç£*(ÑZ1F © ûÍIpŒ[Å ]E}¹ýæ™æzï ‘½Ô‰ˆÈ‡@x#Muaͧøô÷{»þyú¾ošÓé6låÕTÏG7°èôÓ6”Õò}Y]ûü¤~zú›)ùí`¯]š$Æ‚–×=ù\¹µYÖïS"Ëàîý­ßê#õoÛ}¶?=º;~Îícvφ\·X!kÊôCÍ~8ED´Õí«ù¿]mRáã>^Õçá&÷_#68êpbo×—›;ëvدµZSqŽ§iûöÇ÷oöjíôgËÈm‡ysrËG÷t{œvǃƒh²à¢á`×æÒúôÏG7…m óSžÜpa»£:H~ êþRöÄj9\RAï·î~^ÌîéçÇ]%=†"Ëù™é»‡íxâ¦É hæ^†LG’ëe#.·s-”ëÝÑ3Ú÷ÙÌêÏæ|Û%”n³¬|ðjãWuzµ}F×péÌœ°çþS¶1†š¶¯‘ãLSèyµìÑ­}˜í{ù`-¼-§z9Ðó%[¶5¡v»º½2Î;§'D‹?/Ã?À؃}öÛfª]wT¸(9=E)#.]ÙYY‰xôpaòÆÁ¿E3Æ£Ü0¹ÏѼF%Ì.lAÀ>2.sºUÍ?¯Öç½FŽ6e2w?.ì·Å¿–=ÛüÇn¢n–;å"^¯Nù]¦ëûi=Ü[ºó´<Js‘CÆÉÝác!»…-9ý6GŸ£M\RD‡8½;ä¬Dèà¨Xhÿk¬1ÛÝîöj|G#àef®zu?|{?ˆb죩]÷<qáó„Ø2óyúW»î4öùüãØì˜sfö_IÄdaÀíÔ2²#í‘·Ù#.ˆªú«¾û+ôçñé¾ÎïÒ-«alÙÞ¿(Œô/£#.lŸ<tìß+{t¤3.—üÂó/°Ú 2´@ÎôcL|>y8bºwc|{a²rÜÞÁÐ#šŸgŽwùv[…qýª$R¿S›ú>]ÚwæÿÔtf¶³×ÖžOåð{£<î¾ßÇKjØtcú ®ê]·yïìDZ¡¯Ú’ÿ°Ëà” F«àËÕ¤pøzŽì¡Aó:Ð}Ù"¼p+ˆQÕ‡“ß_«ÐÃ^‰/±m&­îù<‡7;›(|]o”ÎÇO%~Wû™ß2lSœ–7µ|r¯ýæ†ï»,èá†vENJÞá^[#=6{}ŸÂ¶uýŽm™`žóùÌ~ÃýîÑÛŸ]nåÌ)ÖÿîËÕè¯m¶ÿ£Ïª£ò©#6ù¿GFqŸÂ^FÌÒìò7ðü4µ?w·ö¡(?1œ½è„ŒŒ®ßçõ`¶úO·¯¯Ûï)ÍÓ["†ï ÆŽH#%õ"ÕËÆÐ2Æ P~kZ#%RE¦)#%éŒ:³»“ÿ£O2ƒŸìô„ý½£•<Ý~¯Ïî·•üsŸ?·åõuåÚ%÷‹›WwPì»4ô¿IÛø×~÷%IôiÓtêüÚoæòšº³¢qüµiAŸ£ªÇçÃ@ïîøgÕÓùj¸aõv{À>ÑÀu.Þ?üB…Í´t¶¿®gºç*¯õhÛDAþ óSö䓈yGÇbeÎ>Í_ÝøþÌ)Ón¢¹8h¾>#.[¿g®øÔjý ¿º;¾œœ0v½=†áÑ›Éñ¯Ÿìá¡>1û4Šðx÷ gP#÷h÷óñÙÀpý_Yõ¥©Ü;ü|PåVˆQó•ñ=€ê?~‡u±ßåmAì#6`¿Ç™«–+¿÷¶ÔºÇ%ð#.ý‰Ã“ ¬óS³÷wt÷/vÎná#Ͷ?!b¿ƒpv ùlîâ¹ù;òžêÌ™/Ð|S¶ž9¢ ãþ?» "(H;G¾÷*Œt<&/»éÖ†ûçë’GK÷Dj`’à–õç9K#%#61ÜT'&D¯žñª¤½Œ[-2è+˜`†IBfH&ùÚ’~;†A΋wù´Åð8,†È¿{#Y^‰ýÙátÁôM ÏŽý®7Ü}·»,°§EÜÜpà…#.±J‘ÔÁ€ÐúìŽR#ÕÃîá4¶LÜhâ+µw».©™À’Xê%äqh– !ô¦ÏÇ~žIèù}A7Œ®«wèüº!À²h;ÐÁà· -˜>ðÐB)Ø ¼³»¥JÒˆ®Ç“>;ÚëZÁZ;ž|%9µdC–PSx½óƒsZËËgDås£wr×+)°±¶1/1B+SéÏkÍÞ<k«ïQ}=:ÕѧBxóÃ&ýÇï"÷´¹8KÎ.óÉÞiYÉê¤kÍÍê” BI×eŸWRÍ “÷zž4åµ%fˆjŽ|x¥¹‚ê΃0Ϩ]¸RÃbo£<±Ã<âñrgóa^ç£Ç¼;y-¾çYð‰&aÈI$¤í"…:¶¾ 8w”Ôu—™²æ\ËÆv³i¯ožM°Õ¥ž„|Yïæ1xÑÀ{]°‚2Çã×^—þa»›Pëø-z×ã¾4-‘]»l¥_3T¿˜*|õ„)8óÌõ:„aΓÉ>·_!†(£«F€+~›4sgì¶W×M«°ìÆç/Ïö´©ݎ߶£Ní3¡hžl‰“3û‹Ý-ÌØm=O‹£Ûóp>Ø#&þQ¬½0â/Sß3 þ>mM©‡ñw“÷ì\•ý\} ÿ“—$Ùwü÷y¡ÔR‰$ìØú|¡ô_ÓIØÈDí'¶1ulu³LׄãÏú[=8á'x¿ƒôúè3ú†A¼#%Éé/SØC¡I%yb1ÖFÉ¥Góü™*”å˜bG‹ÑºüCª öIO4|žÿHï½c ÀøòŽ»ÚcY|uŠ·‹>Sþÿ—æ#`ó²{ÑÊÌáþBÔfeÌ©cv²Á¨9G…?G¥F5¨4iTlTŽ Ô¢¥nØ€Ðæ7#6ªÒƒÀ¢¢,tþÍÚ›HÁ¶¸}=3†´Ö¤On `˜a´ŽÅ-ŽÀ¦°Aȱ¬a¦ILA«"hhn„)"–4±[VWŒc€ì'Ë#.Ss¬6ÐÚ]&6)":ýº[ÅÖ¡É \’6ö‹B{qZ8dÚu‰ýÿ¶ø[Ñã"?›9ÌgBPP}‡¨üÖÊS<¦³ =a‹5þrÐÍAÉ]E··cˆXw\Š1´™ac0]WëøoýOSø\ýVæœ#¯#±E4DCÄHèé@ŒÖbï¡i7¨1lXT¥Q&FÈCV ±6Æ›f’J¤ƒÿj*P´Y¶Oà9þ¬éšq·ß›åñãrÌӛܙïëûpì—A¾öFþÞŒi|œ-'Ôl³D±€œçj·UuwåËu¾_…ãŸÉfºòöWV¯Ùãõlò:­ŸŸëôeñÖ1ýèæÕ»ê(¦áLS·ñþ>ܬù±ý9Hþÿ<ƒ·ˆjÄU±Ýmù}Z bçvù–U²«&¸ê²·ü³×†íú¡[GmC……fŸj#6#Ò$pçwÔ]uGÜ<Ý~ßÓø¯Ó§Ù£O«¸¢Z„xûþ#%8g#65U6ü;ܶ>®aL~ØY¿w?VPèÁwÙu~Uwö¹\ç3Ü®“šÀÂÐÃÉÑkøÿ.¬&5)n—|?±½¿>¯&¿án®nàM>äM¹¬ý“ûôwL[% û"ûBßæ<ëά#6)_@è¸à\S¼|8¿ÁéÎØÄÑ›^F+rEµh‘[J%c§l”õX*5¢gtõu¼^´yçœÛÆ9ÍfRÉ¢((Œ©[\0°Ä£#6’ œÙiéÈ¢ Ôch¥b…€È£°µÂÂ44W VR²ÙÿÒ£FÁ÷R­\æŒmFAKT­Q eBÑlB†#.°Q’ ƒtêMæîy3‘wä%r(*`¥2ƒshÙÀXœ;Ýg6®¶JhÇTChÆ%™ 8Ì+:ª-!ñiÜtu,¥µÌà’‡#.óÅ!ÚÐ@AFšþ§YÙ¬†žuD³Žmé¾#.äQ¡±‰‹$I•8‰ì ~UÒI‘#ü,D¢Ö–%\ȪrF‚(3š*ˆÂ®ÔÊŒp#®²R1Œlž°b¹ÓòJ®x3¸ø!U†$‚’IsJCÙõC‹ID&é½’ôAŒô;/ôôÆNô˜}€Ë ´íäë-ûLH ¬^Þ@ u3ÇD>Õ÷z“´ö#.QùUôhÉz°÷ý”OT=¾÷góxáˆ$g±S—óPü-ΊÃáÉj»#6ý£×£ŸLPL*ø–ý§f¶£Ô§ü!½ëÉ3§F9–û§ùj~¸îîh¨xÊìƒÍcÔºåøËãïÏ›""'NõOU87«6« üVûú„ÖY´¹ÄÞÏÇñ}NO#ûjÒõ@Tõ¹€â9Øs ‘•ûÊvA#%_œï†${Ó«”ÔŽVì6TÖÙõ¯’=óø~ç–{àäm·l’_/ÊÙµÇÒBÐ’Ä–'Ÿp€âqCó9Ë‹è‡æ÷¯!üyI£×<³ì©«.V|¾ŸÇÕ!‰æª£#6u¬êyÄC¸î'·>Åþ¹õüܯ±­öÝAe;6LèǨ4«9ÎCH¡QH2t2˜wT9"+1r_uLUv¹ä“h—ì¯F¬ÿÂÅê‘€wf\„F±>ž™[î÷QâZ%T»…#6ÏÕ¬ªŠÿM[hÅe”£$#ˆÜ‚±#.D¿ºÅlÅ˦ómòØÊŠŠB‚YQYCFF¶Ï7®_ËäÇÝã³áê_{HUuí;wùüÿ§OÉ“A󭬺!BAT‹‚1ž­sa虑sÊ,¿] WÚh&6p¦41-6—·n›ÑHÚ)¬1=j ±o\ËA„y©Y¦=Zﶊ°Ì#6Ô«ÉgAèpE30ËBƒH¨Á‰”V¢À*ø¹¢hâÒñ/mˆŒM}RdCÝ7ˆÌy#6Ú´s¶jdßå$ – 8ÛAmb{€„&‚ãì͆¬o*ž ©H¥›·86AUƒ„Œ‹³àrYb‘ÍZ¨¬ÛÕ§hCZ­¸ÆÛ…‘2SS­DÍ#.H#6J lXdL1aˆ4KDã#.­‹G‚í3³u#.Ðá1¶“­¡×l°lôÝX Ê-“m(Uz§ûÿñhÉäŽø1†µ—š(UX¬0âØ”©DƒlópÈ#.Ìi@¢OlƒÂÕ·åtS¼'Þ30<sÁ„l˜Pt⢲oŸ»ø¿“Õæ#6” !B#6‚-ê»o.O‹„6öÚº<AÿlqgºŠØÏwìèõfñf4ÙËÆbýÖ}æSuƒT0Ÿ‰’‡44V\áÿwáîÞ:ŸÙìÛé{yзc•(ž~Qïɨ<Þ O û‡Š¹ñÓͫчJt‚zAíÜ'ku#%ùšÇ"#,,‚6űaiUSõÒªˆ4ã÷]áv,ÄDjGH¸¸ÂðâÒŒi§O®|´°Ud—N¤G#"DÔÂfýV5[ÒDê*#.m4&’Wç§(¡ßÅÆ_ˆt5³…‡¥ˆ3MéСÅA!ðy½ÀÐ’ÑK¥¢#.Tˆ£¡·ÆóÌU:¼]*«EPˆTå·—ôùdì×k»¤kmó0¦T¨J 5Ël$2~ßØ?sÏêê¿g¯Ì:?.\`<<øâï‹«@»6žñ™>\G¯øøJ½µ`CT<ʤ2+r‚i÷MŸ„š -+A‘¸·Tc£´øìz^Ƕò—ÅG=Úæ4Rë«Ùî65ÚaŽtHÇ‹µ0 óF‡ÞG[q·º¨ÊN𮋳 þü¥Å Ÿ™ˆ¶<ª¢‘¨6Óˆ®Ç!#6^¹"XÞ<(²ÀÐœk]o3ÆÌÀãH›5KœK¸´pLÊ,°¡+&°,”Êp#.#Za¿*V>#%ÐÎýhÂ8p™˜Í†K§9æe¼‹õ².l‘m¬8ÁV6>d4#±C§›à­¶——ÔAܺÉ&Fèé ᦠ¦€¬ÒKbíV4˜Î Ø=ÕÝ ¸Døn$DN÷»A£PÓŒ+gb†XÌ‘Œ®çFPá2-îc¶+Pƒ¥ò)2ÉÂ2™+ç*³v»ö—w?Ãzwò_Ûü9ô[ß™£oƒ×ûiþnþAý— èyÔÆÓýb'j¼ç3Xtß1ám¦,¼3êtäzÉù~îìa#.6uqöMÛ‰ó¦q9°gì,ªÄÊ;÷ ÁÍS¹(v-ª ­ù`kb—ô»­6ßV۾ϱ£)‹`åÈf=NÜRäòAUHtF¨ÚE’ëž1’œÕOSxWxe¶ãjh¨ˆô©Siyiºf6äÜ‘R®©n1v6«D˜E¼ Œqì’T'wÜÏ€.›gÊh#i™£^ÞM¼h½óé¶3`ìQ±q’&Úri3haÔ”9†ÙÆbX±AO}`PwÍUí«3Û¡~ÕoÒ_ÇúFÅš°¤<N=¦,Ü9}Ð'pø”F_smŒÕA—#6#.¬:M³±¸Œ-µÉ°àzÌ! 1IË(Dâ8ìMpî@Τ2ìl+ñ"V½†Üq·ž*t;$î§iœŽ¨Åo›;n3†Ù†-ÏŒu#.põQ…boyN×?u°ƒL…#.¹}#.IŽƒM¤$Á„ x¶ï‹)—Ï®v¼îÑŽ±­nú¼…Ë@:kï¯#.K¨iccndë‹ÔªÞAåj«[É•P]ÃK©)pU7ŽUØ"wW;´Ç ƒˆÁ’ß’ÛÄ!ÜÔr7 •JQoùŲmÛZé\¨ÂIA­Æ†àˆQÞ#6:26#.Š]î;ñ´ïC®â¶£¨ë³º ¬›ÌK%c¸Tnq°QÍ峺mñÙºâã!X ÷Ðk¶Ö·ÚåÇF=ˤV¬v„NŒSuÕÑÆs'&vl©”4µrÍb< vj—N›19ÆbÉÃ"°Ë0NrcZ6ŒßW$œãdŠB\ÜÖŠëwl #6Ó¨cÇ.Ûldå¤É6µ&†Dm<ÍŽc/³Ür±±5ß ÕoÓ:Ù›D$âoµ}ä«‚ÅÇy溽<¸üþY·íhÕŠ@óμžKaNâÒW•GiÝ/_³ym§,­qGr0÷GÂÜœFŒDdªõç›V׆¾†’¿òÜdIÄäF6$ˆ«,Ÿ©…5·\m4üŠb-ßöÐTM^í¶"R§".f哽ÿ¾ºáwu…œ¹RM|an‰¸ê!Ô¥•“ÄAl©·KÆ<F–°´‘¼)ÁF§P6ø¤,½¬-®eß4ð¡“RàoNYyÉ\#6wæïÆ·¶¨Å€ÎînŠ,ÿ…ÒÛ§Gt•ïŸJ“L˜L™ kÞ8!ÉLº÷m?oõxtîåöÅa^øñ×4@üÃe¥ŠnÓ8¹búÇUÖµÕim;'Âyƒhz7,íÎ+¿‘öÉ«þÇyDCá÷˜šT–ToƒÁ89I°šY¾ç´<„ß\±`%dn½;,ù²BÂå68ãu6«7“Ž'}ÆôÛ^÷–YtNÃWdÓ;=ä/fRͲºòc p®FiùUóískˆ[áü3¦†¯²ŠŸ77И{¬1F.™LÍÔ4éç¤<³§n#%·SœáGFÓƒ9“fài©CÓzÒ¯,6õbi#6‹aÝì®îÊómàò7ù¾ÅÚOf#.5Í`ùfz×¥q„aôËÊǵ¦ô»u× [AŒ(g×zÿ”dÅGߨrt‡5:Ú'à®üc´vP¢¨$‹ÞìÛ³­Fáàtx‚‘Ãù]L@¢Ùù›¨öVA¬^–yw†17sŽGqì€AÙƒ™ÉdfÒÒ_—Ä£¯Ãó7n·hÓ'²8u,Ü.¤ñyƒ¿0t¾óO`šD…Tò#6 3 ɤ@ºÃjúP3‚<#.þ”‹‘FRØ/(?€7óGŠá:ix¤ú{¹D€ç#%²yþ¼#âôØž¢€8‹=lè{ý}å¥r¸¸éñ¾Šáç\Íï>’ï ø2Ý䀹ÐiߥþCÒKíWnר>áüpbx_9³å¬×WÎŽ@žTÛBðÑŠZ·¼Bì–”b>ß_§çcOVz|ymx#6fǼYJ˜…²-™Ê  0#6@ŸŠwßwvOóÓ®ËÒëÎ!‘Þöƒ‚xnpŒF€°Q¨#%Š²ÝjŠ«ÀÎÇ·e#1T˜´ËRV¡Ö1(3‡È40° cpç¯T‡ô沪ÎA›œÏ¨9zâ"3áÅ‚NÇíí<·¹“ycÐœr—v>óZªQà:¹ý탂%ÖД~ND|²EÊÉ´à÷Ô%¸< &ÙÚq4öÁ …)UyøèÌùÿ_ó–Æu«޼?ßàzg nõ¼;qËô;剚ރ†Ò„&LÅSå1ºúeÃyg²)IVì–\—.B„m0J°þ’[qƒkdÅ°ó#´-©`®Rb]­8ð3ŠÏcÝÇÊûO¥Ÿ]ÝB“$ÄKÁÇ|\ÀÈ›|jÌÅg}'ò$j­•a±áêȵ¿<4Æ=6=&¹|§‹ð»®6…]ºÔOvé¸pX»Ü=ôTŸÖV*yumžì…+¾â1e¼#µöÚgDtž,DãFQiwqlÞŸäÀþµ#.س^O¢ƒàô.­é¤îÝϪ’‡-ÇIÐ÷]ÔAõõ…îÆQ72„I8€ïxL’joLk.ËŽˆ¢MDz=ýtï&½ó:oŸ$48;!Cªpóa}j‹ bÝ›94gÖ|«¯3­ß½›µ4#.ÒOè%èÛÔØ¡ñ­–ÓÞ:Î<4ÅôE#.54òì3R‘¬úTW-Çðk¥³øyçÈcUéN-<#.'çik“é;!:-DŒ¤§ 7)%#.M;î·$®ƒÂ÷h.Ø–CpíJi“˜5Ðéï¬Zñ™9þ®ß#%ÿ‡7±CðÉêpC ‚Ï#ò‰Á´ð ü¹“ßrÞÓ¬Grɧ嶖“9¶šE„“J¥™Í•aÕ…ë¶ú`xf¤-0è´%ùk‚ö2#Ž¼üx#.|^š:+Њ „ÒÉù­ÌÛS9lVŠo•azâ3!m ²<…¦y©CÞo{vþ|eŽûüÿdQ茌¶F÷T½Ä½Ïá~;¬wÚjl .r<0XîÍ1`àžœ1ß¹iê1æйºKj„¶Ó§\ž¾¯‡å;åd“ý|†:ó»OeÑ¡ÌìÉâcs§ãò²Íc↛Zøô<«LÁ“QÖE-¿”Æa H •àòµ0·Nõ3\JܘrSs t UýÒ>\}[ˆìäåúžŽÞc‡ríàXZ.ê,¥¯Ô#Îfi?ú'î¤è—DÁµÊŠ©æÔ÷½`£Ñ@ˆ¨úã´#6ˆoÍÇ~ÏhÊ0*Ü×ã:Ö hïÿKC¼i´Î|½_ðÍÓÃ_.¼71¼§½Âu"˜¹r—Fxô⠘Šç#LþÒ¹Æky™—óé“>Q†9¬€Àȳ÷›#%hÍÊÈ(²…Ÿ¨ˆž+fLmìßãh”}ƒãÄuÚŽŽBN[ëR fJé{ 2ñªA·Pjפ/ñté‚£YÚÚ$ùyPñŽÖ–øêú뢕‹@…)Õ§)O”~U$BO3âud¾‰Û1/5¿<’k«æ‡U«xd›í¬½ìu¶ø¦ta>y6œ­¡™û5œÍNîQáp­NHèºeò¸è>–·D.•÷*EdÌn2,Y—(迃f—ñ/¥sµÒ› ñ˜D‰µÒ‹Óô’ÌØ—›Ð¦wN—¯®ÖIóíîç#.¤PBbÛV¦u]í‡Un=Eœlã‹·ï¿>JªE·":Ç”Ì[~ÊÚ‹Ä&ÂOš¨ÁÄ­)ƒ%&UrfIJNgq¾aCF¦p—zP‡™†vß·SC…:l#ƒ:=ɪ/¢6<ç0DU­¼F9‘Š¦îÅR3–}m¡ð6‚£y)¤8Õáà7Õ¿QúÜñeìî#$†ã\¦Æ§ò^q1®J=f3£ÕÍkÆÝ$·LùÎã{ˆ–¦Ö=Jì‹mŸ&@׆vµÑ|^m÷"Xî<ºãcèšQÕ¨ŠR„­HyrÕòá#6̪\hlæÏ„„#6Qɺ*h9·¹Œ52g(eG=¦!ƒB-½FäÈ= *\¨½[=Ù4³Ê…ܧIm¯ƒÐŠã¹Fo%ë*œ¡açgþsé7s”¾›ð¤ß>­üç:¶è$qCñntl ûÜÂŒ=³D”OåùÅ@à}Äýó;Â_+'ç» EFžÍÇD_½@Ê´Î/HÌ(¹%“üñãÖÛ~!ÞʨÁõsÉMžªG4E8ŸˆyKåÒØÚ„¾ôc6Ãä´ÜàÎû6vâv¥l½Ë#.‡g“t>";¢ïm¼Ùu¼,ÉvõutÁ=;2hä!0ßH¿(âÞ}´s^G ap%N=¬c¥çµQ»<dZf˜ÛêïÞWˆå—=¾ïy±[g|̉¥“ÿi¼å¤2ÖÉüLøÅ7Š¯#.¿n˜ßF0‰r ðI8T­ÝÌŒ­·6µD$‰M/#6oñ@üÑ ˜HÜ¡—ÈYe—V×ÛrÁ‘D¨ïDÔ_‰MºpàütC¯‹y°ý:Ç1_G’ ¦h.:üzÑÓ«‚“&~Ðbª ÒqëM—N6kÕmcí99º×ļkg‡X3s­S§¶‡–„ûtc˜¼/+ÍìF!ä÷øŸ†2xìåX?]¤BM#%èìùRÔöÁ3. s®)©³ù3F~~!­Ä i9þ×8ÁIütgHC©B³Ê @é›Fº×E¸éÆÉÔt ÎxêýbT{ÈIZkŽšÎŸér­Î[J(7ÕZÇפґ–é½¥“°ô4%ngW³U¡p‰zÜÆš. D…c·Fó\Ïå¢ê ÑsT»f«ø†W#6­/á9=jÑ´S¹e‹EØàö¹ÑãÈB#%ÈX±u­ÏÊÖŠ¬N,&6#6B‹õ=p]Ù#6fˆ§|:×D¹a­êF/þ¿©+6Í|Õ‡,2Ø'7;5mÌ©ÙÂû]Ü`x¿2°²kŒ\U_v‹¬X)æûêëò°,àµ{wï…ñYžÞ%åq\jµ|ßxèªÌÕ,`œRWn²Æ°øÙÍËM0º‰ <X5…U’Ûo6<-¨Ç)d^÷Ýb#%ûðçÚ°Šò”®[£±õ¾·>Iz]¸lð«°S·#6eŽh´³$'°´¬çíHf#6^Uî…Ѓt.‡.k„“¤ö#.п4âî6 zÛXÛPÅ¥½“\9šÛ1mðR´t­}yUQ¯}[;î¨Æûáožs„˜-÷À•<¤¦TvZ§•H¤sÄ•éïã寻Ë&?Áß™Æf}ôèÁ>WÞ³ÅøÕO‡Ùwbe'½øëåÆqžçŒtÕÑ_I&5q¶–"æ¹Öçgõj2àE°™©£•½"*[WÕn¬2¾ëAá¦ÐM*’aFkmö0*áe"ë²°I@.µq|ž%hy±gt>›ûõGÌ0ÍxQ“¨ZQ]~¹„…LfÏ9ÖÑhjÀ’®“'»YžtX?PØçÜôTðQäj?¬½Èo]YQµfÐ[DÔ³áGçtP?ÒóèþØÎeϤ;ûmð÷\ã4•§á˜‘x§5¨íÍKèÒ@¥ÛY ü– —°¡•³([¥uã`£„"ñ%K ¢%b5j!®#TRé,Ы©’ªö`A¬5w,åu1ž…¶K#6¯uÌÍ+;¼òzÜÇEºâko¡.¦ùÕÏVÍ߇Ç>ëä(úXòV÷9Cg–­Œ ôuª<²w]ÇÞ7ÆðÛþë+™Þ#6–~J¢ÖÞož8Ð#!žõþ³PJ†#%)W&8Ø9¢/ï•®Îo7v°Ðv¸- õ 6âm¾Žæ¤C+«WA’z)zÄêéVµ{„Ñuå|NÉK›¦Ò ¥tßËòܹÆ"‘½E¦/séÑWa›Æº\T:Ü©ÝÆðñ#%̹ÖovJ±$o¨e›@„¦ëtLE#.õ Há­Ð­’æxizÎ @pžßyƒŠ¦M>úgŒV0P´¡ã/ïÚWFŒê ªÆê°ÊgR0y¹ö\÷› Ü+nS×GJÊÕ–^I¸ºY©}ªëÜ轄BÎYC™;-|ŽCÄDfVšsÖ#%Û5·\a?Æ ÖÖv•s>×_Òë5Fó¨÷ypfÝuÓÊ#-/Í\¿Tz>#»¸dq ÄËÍKÎ{e¿"ϽFòæŒÁ³Áµ@œ}#6#6pÂi†1šIc¯1Ž¡òª¯ ÇÃ|èŽIm'T\&IËlgï™´µp/jº~?†5óÎvÝ ææܲÓ5‘bŒ7fÓnº™N3[al@}sÙ…_ XƒFÉÆÛ„ß'mÂ6)k4ïÞ',Ö #6Ö•KdÒUÅCÞî… £¬ô#6F6¡H‰Dp¢JÊ÷ÖÚWS€“ä²R²sýÙí~ ¥axÆ«aázß5Ä7¾m.mí*E¨$ CAlæ¦BIm*øÒ¡ò˳5®Ø45ÖJ·jºï9}æžÖÏxôHü5Z¸QŒ<Wž÷¾»N³VDÕhU¡|ãf+ÏÆ<é¹v „€^-~]ðBäA`Äæ‡n˜ð¬W…éÒô>·W«¯ê‚ñ‘B¢(PŽ%¶ÃD7K#..mØÎLjئ+Ô,#e3m{­wlˆ4º/}úî}¼ÎYðsEÞC9H2ƒ¶±ƒtflkA²´ÕM1‹C,áî…`âl„™Ø±„ëkù´OIiN•}¥áù#6nÆ#%ààÍA^†³(ÆÀl˜!Ê.±o7Bù[8fX»HíÚ€Wzò2ÔÕså@ç¬É“¯ËjÎc¯oN=,qkóPxÙ¡ÀË·qÊX8~ =Þ¡O½íÏ’‹Ó¯tÌqÁ8=þø´¼Âžûx–’ÔE†@é#%Åmp‰•‘ÀO˜NÐ’³Ïaj¬ ôe“³ÊÈT†I*ƒxzÊÔ¢ˆŽ4äãlûGüGþÞtОq͇¯5K¯ìëâì·VžÉxdÔÊ*Wž6—XËSm³¾‘$ðH/᦯°FŽì¤øS‰Ùà‚<?z̈GP-’;yZæOµD5Ë·gºë«iT ÖêœÏéÄ2 Á¿ryº~NKûôøÌGˆâkgÑêv|¿Wç—¿êþÆú~xøLη²1«¨\8û}Ñ Ó®ŠÌW¯Î6—¼jS…;TǯOmæÜuW<ß±©38z†%~çrý·–›ÙÄ¢3̦ۤïàåq^˜—úðN¥gÑ'u±k//u65—>Q²{Æh‰e!wº(¦±Ïš]ºÈ—c!‡ïƒÂëOZÏ_…ÍÓy©1L6ðéšP¾·«>ºÃezÑa¦ö_j¸ï©~Z^¢iJ­áÝtQaè[›9ò-QÍïäzí$o“‰±ýL— ÷æsaÕHR1€ÒTÕ ×¡Áâè*aæu®|b¶ž‡µïææyÌ„…Ä“{Sê-p5cÞ§wsÕ_L›ÜÂG‚›ËÖôöû©åŽ¿¦àc}±Œ —«ûû5I³©jJ#fع¾”ÎM$rÂKLîg¡sß9a--JÉ–,°ã¾›õ½`¤Ç|ír]xä+n/ÑÖÕœELm¹‡˜‰‰›Èb3ÍO]a­Ó(£ì!^\C᛽¦Õ-(r"„ÇFgRÒ¸nXم꣥ü1½„+/!“ÆšÃ6d#6P ÕdØIá‚ÇT”ÁHS{ÚÔbÿ3Ÿ1棆…N¬ù \¤ë€`- xŠf<Ž3·¡Ù#6  g î®Þ|Ûc$âSâ¹@ág"Ä ÀÝ:9Òà»Ý.mÚ†—¾|×[6âjžg5sÍ¢ðºàÙ½KÚ=WæˆÐ#%U)Aµý³MU7/”Îà³E³V¶û³õb>|ÔÔsDD°#6T2À¦y Áq¢Ð²#6èü¥ŸÒk1~6ñ,ŸÑå%Øâèûgû—K°¶ˆ"hÔi2KúŸ;e#6é f¼¬T¡ ÅX]Ó¦h”vÍÖ°NZPqîWP¦õáÐä²I¼|Æ ág¬í÷ži ã¢\ÒªÊaîa·Øé8ºibP» ”Jr¬ÑÑkLØ0óöÖë¢vÝ\ê£q!ö•ytŸØ :÷Ôܵo‚û<£gŒÏ=Éo6¼IÞbì·låHÜCZýn±ù¯Ö7Fn²“žd»Ê¿_¦Ö„VM‚ü£cîƒ:v¹+F;³Ü¬)&#.M—'ÁÂ#.›Ë‚¯£¶Œb»ÀYÊw:$ƒ†±å½À=ë Z×û°' &hhmZžñX\Ë,€ÊŠÙ9CÆçò¤!9¹­0rÊÜ =ÞµmŒ©3¾¶[…ƒ¿ è‰ZÁW©TƒŒ·ÈÞÝ+(a 19Å‚‘Ìáë<¥Ñéæ¡ÜvÅF³Ôè©xñ‚è~ÅGÁB UU(;}ný½º¯œà•Þ¹™Áµzoô]4Œ‰B:ž­Ð=.®atÏ$³ôÒeJûœä™í` r=q`ÉiC J¿ç¼ø”D¡ùâÓÁ#.BY`œ‘åfYB¢òó¿Ay ±U /v¶ /—C—3¯¿±òÔ$¬sék:!ßr=®.ê«Y8=}«œ[SdІ*„¼¡÷M¹õ(œö´¯RÔ˜xÁ5&œ¾GÔ†¢¨)´èÉÄp¼Ió{ ã™.xGtÆ¡žÿ',©iAxdÙt½×ôY»7ÓFšs<dçWÙÕÈߣlõúÅ`)N}ÊèÍ™fK¸H™}öC¡ó"qÝQ†™/Džq×hÞ%Y“NuðùËûaØÓüf6¦­œ‘O}_+Ës$õÞ;œöçzìm$$L·LÑáz¯bòþøËžš*'%$i:tÙ™cŒ£yªâs‚–Qi':-'‰™·(Þ`„b6†<-‹ã8ž¨ó.£uÐé¶ïËmòÚ]{¿ÜÞg·j9 ¨ š^æJª'­ÎæÀo}Ò‡¬ÑÐñD-‘‹W8g{Èm´“ˆqná†êkÏZ9æË9ò›ïŒùÖ, CŒü`ÅNŒ`[£½Â™y#6Z=Ôs'´WïÏ'dë¤\¬ù¦¯ÇUÖ|MÛµÃÑŠé{ùËzürE÷õn­tœwšæi­0²J:Ìñímã¢û åO äzã‰-¹|o)#XÔW·—>ÅÓÐs=o¼[DØЂ unÄ2¨æƒû‹`Ü1˜ÛNAö;ñoó$³¶‹›¸x‹<¡³EüqÊíêK‘­/–¬ÙßbÐK¬„/aÑt÷h$&c„%n×çl#«'¬s¹â™(é­T¿·iاۤªËò¡^v*ø1Ö.)òFÅ<eZÝØxA)³NTÿ(ÃöAБ?”>?NŠá_§X#6ãûbo1QìÐb¼DzÞÒ¸Û¾nB¬htRߌM~Þ¹Ÿ¼çhæ>(â= 6º†6Mº1@ì|]½fÏ.×ösÛž(õ::l¬züø³jîOÁk)¾pVzקk÷û±jÔ3pV×>IÎf¥$™Jý€?LV2e*¤Xpå¹³‹nžÈ(#2òY¸zk0Ö9êŸ<õ-w¨õ°u¡Íˆ¶lÂnIv(˜"`‘îTaŠÙߦ:b×däB®zí†GmÚå»#6õÈyh3å„Œ³#.¡™Œæó‡vÏ=ýB´Ùîïj¸+8;å>N¼Ù8›dÃøݺÌu]:D£~·/Ò®*©z½pIs>•Ó¾Ä¶7¢ð¾ìæ|¡Ð.ÝC×o“ü<©Oz¬Û5*ôÚÐŒ}&E§xãÑŠ»ó!½SbGeœžFI-ÉU#%¦$Ô³I Ù™lM,–01Œ*Fe`÷ S~S G>»2@×ã¾~ï3¬ ZÂË/ÒsÛ¤¸zµkÓÆ.xï|~ñÍ‘•D¤#%¼Û鮈u™¯ÍÐ*±§Z#.ßËn¾‹€P>¡…uxi¬ëÍÔ¨&„'a¼ S9—2)igkHÏíð|·¯`çÛÜ¡à‚iÖÉÛS{Æ`'Œ+æU<:êI,öæÙ7¿_ǃVñ5î‡O/Iú~!®ìàœZ§ºÜÁáÈR¸÷pùqyàì^Km›ž$?«ÿr¢­‡u“à;46CR;,VÒ¤èøu'Ž/•0Î?­í5¯{íÀ™iÎÚÞÜsü"Vîu¾ jOð3ˆýKÉOäýN“w™³ª¯ÂÛ¦/½6¨ .¡#6×X”rpÑoHì~!ÑRKz¹£%dn(è)!ëßžnÈ€r†lšÔˆ£§‹G’ÜŸ#D’ÎŽ¸N5›çþwP$È~žåtÔŸNBeåkµîM„ ‚ÏÙèn æ²1Â7_êÝóÞ¹¾?5õü;{Óº'àãÕøÏŒ¥¡{ùq(#%`@)ïõ(õ§áôáÔŠç°Ö9MvŽ#Õ(JL+G;I@À€ðèz¼xÔ¾(H)­æ„gëxTWÆ®òØ] —÷ào»ýEkù©\ë¹lIi?K¶Wò¨Ø”‹Rm“cÚ-«®¯ñþ5ù³Lˆ–ˆ…>­(2Í›ÊþbFmp–ß‹gË~:×ô@ß4>$Ë_#p¼$’ ŠÅ"‘AƒY ä&©×‡™z½:œžÛ¬-&OŸÈÌySµgÎ]–ÜžlÈþÔÃfÁõ¤™^¥«Û¾÷Ê“Ao¼?“ñxüGŠ5‘ý7B÷ØH‡Ó\™ü¯íN9†1‚¤IY‘[Gtþ#%²©‚¨qHTi*7+¡þÜõÿ—‰Ëò?¯YC‹‰xþöaÏ(¾zÿ~[Ûá_Ú Ï]>Nt©"rNK(ö·ËçzÔöKíˆd¿»æïžðù´!Ú‡Eá¹tî›V}ëªÚÃÞíèÝnCiü˜]÷ùY??ÎýŽ…ªz¯nÇN¶:*F`6$´t%5®Ææ󳃊\ª#%±|´ãÓ¹ƒþ1“ì?£¾í/ÒÊY¡°ê8KË,H|ñ“‰"~¯Ð©¡ýj!Š#.K©wk|Y ž™²äèYû=v&‚Sú7åí}f“›¥h¯P#6ªÌ·Š Ϩ:ý_Býš>¼•s<­7E‰a§VNKÎõú]›(ú¶ÅÆQØ/ð~¶w+E@âõL›0½Æ\üƒª"XÉBÌÜ!œ?_þG7‹L˜zß„çmßÂÇ>¯+e§m¿—Á÷¡Øf c(QÆŠ#6‹"šýu9¡Ñ#.P?KóO¹ êL¥09]#%n‡?Ÿøí®@¡/T¨#%ž½t xÎè†"RµœKš Tg¹‘L°ÇJæhZ‡\j ¨µ+r¼ Lt#Èæ"#%oŽŠ®¡òËÒ(~†—ª#%¾± ´é§ëãcßÁmÃfÿU#%èõ@ÝEôÿR ðºØŒÄŸxi¯Ðþ•ëôBañŠ¾`Ù8ú&ØH¨Ù6óòP¿Ÿˆ€XøcÚi®à†í´ ÇK™÷$qý4¹a‹ß-)Á>ÏN99¨“Õ‰5v8ñ:je`a"ÅeOXï2€ƒ~ƒZhCq‹ yžÏWiƺ(T¾¦Û¿ol|ïøH,ézþë–(`L$¼{ †9gÕWR|1€¿{ÕŸ¹Ýø™@Äyi<×Þ€ÿ$ï è LJòS(ò‚ýäOgÏIëŽÕ¥OSIïô–õ#Ñzüco*(B¢Êvæý?Ít,¡xë©TÏ[]3«X(~Š¤ž©R”#6¥`UMKõ·;ã6Á¢(ܣ؊y·½?=Æ Ýа섀>Š©˜ aI\öñ}V$øÁ4º­2qÇ m¿Êe#.fqpÚi”ÌkZÔ·ü(b§RfÌ%ݵEÔR1#µ¤Ûp2„‚\4SywE9gŸ+ìôŸÐmÒü!Œô¯#À耠(&ë]Ì#%¹Öië…uQºâý:vêÙ®¨ÙJË5Iší4ôýn½2‘×m’pŒ­è«¡ön³DAñ|],ʈ‰[#6Øy`ïÄ#6€ŒPIµoßÕwÊîŽ,ßlN¹¬ç·Ü"‡c;þú†Y³:=Ÿ±Z&W¨å#6‚Çf½Y¦ÈÈ`¢ÃÏÅ3×8ÈŠ7K±´-•T"EÃvÉFQ`¹Š\ÏP.Dp1C¯f¶Öoe¹_}g¶âúà«âc'‰ŸKš³’T¢©Ö¨`¢XD$¡›\æ#6P8ÌüøÀ·Ì£€¾ý—tb•ÓÎOIan0aÓÖ€LŠ bBç#.°‚ ¶ö]\ñ²òBET dØÇ(°so¾)g®Ñ à#.4FÌ·e¦`1ÜL`òyJœIÅG§³Â¥ô ï#?øYÖzKéÄ2 ÷#.ÍÔþ:·Ü7ÃßüÕaÖNQżÿi˃2}}Jo£Èú¯.þ³ÊÍýÇÐïL_fs@ˆvô#6€Ä¥Í7–;Î_{Éž‘Fy"]yXºmý~,@yÆNr+(kîØ®™ñ3zL;,Ÿ" ì¼BN3$ü÷>åq6׿yðö]|'<@¶HZ°¦ „Ýè[ï*¹süÚÍæðX¤ßï”@¤U-0žàeœ‹{¶Ú3ÀÂÓ“ƒíTÆií’7q¢‘I¼y»Î…—'íýVç?rÕëHƒŒõ!d=1#3tÿe–®ÎÝ~AN{º4N ¡úÓ‡× ‚I5Ù(åtD# øŒ uÕ™Ü`ÔQP+&Ö—á`KH³ZŒhð!É î¾ÉA ê(‘e<ýd’¼þ½eý÷ímÊ5^äÞ¢)?̸Úþñçš–6`éØÂàäâ{î£Æák¼dVÔ™2S„Ä;¬7ÊN¤@‘`wdÃ@à¢`s¤˜á59—_E·OR« _ªzŽq…›Ks^GlÞ<ЈŠ¹\¡±BÖ³žBdF&!Úë\[S®~zÞd'¼ˆ’¢$6›ÒÙ¢½ÉŸ6NX |¼¡AÀ(噘øïÃrã;XËq-î¨ùöˆÎrI{@Î{"{íLÕźÞœ“5y\ˆJ‹Eã›%9×!}ÞCøI(ès=>§X.~Xp^‰éó{ì¨k±âøa§Ë%–&â’’Ñþ„Ww,/òmì¥z¹€Nãõ‡5ÇŽ/mKŸ¦$jìá +ÓK6L²8ª~í1‚o'š\8¸`À-€ #!C<×® lˆá2ô×8ëõÍÙXÃN¾eÌü®R«•b/!ö¥E7Â5ƒˆpdp«¬BÜqÖ;»8]c5 Ù½= L=«ô7#%í¾XžïÃ%3#6‰¥,4«ã¤"»¹¼±¦ÿò€uoŠ8‘KÔ<׬(¸§E¤;UZ ÙÃoXÍ:I¬‚ßMejyyBþBœT¥îú#6=‘q¨<ô,;¢ qÌ·¡Lá-á¨rjß~Ä‚=%³Š¿uû:RcŒ’x+#.ÃVe(Jź•¨ C°×¦m"¯ƒ‘1IÃÉ2*˜Ü¼ùÎÔ‡FÆ2­…?Ù0¨k˳ߔïÚ?Éðý\ðßCc£{ ž±¥õ‚>Ô儯p/UM†óè<g;e#6,Û¡¬…(àÆz¶bâJ ü¼¢‰®:…Öò7ã œlG1ÑŸát¸ù|±<ŸXÛ'M1çIñà /T½]‘)œÏo9ìð_Urb,ˆÝOèL{Sá_mTܶ§ã³ZY:Tú9°Ïo¿¥ÁÆš‚³(u™l;g}šm@óß‘¿²YµCfMjdÕó<#.x³:O)ºm‘÷/·ú4@[Zô’; Š÷hÙ‹hhP ¯;wœìÁÒ‚Vñ¸¥_F”m¥?ºø2_P|M9Ü©|r]¼­½–ko {†<x–rÎdLf¡œ,z7±¿ßg>îPxÝ,lû5 =¹‡™ËJÔé{CTj‚Çh÷ñ•bìãšc°«¾DƒiÊî:L™$eR‹I¦ÆÚtî×ãÄtç)†¬ëÈß™·žÜŽÑ~“tÔÆã‚Ãœç£#.p²†}wÍ)@))ë 2d¨…t‘`!. 3ØYbô(%œD®‡#%ûtdb#ŠÐ7B™Ê©—Ÿ'Ø«ÁYúcþõCµg›ï‰îé¬ÔMRaÿ2[þ4¤ ]g†bÒ•»woÕNÝ«Ï,.1D¦•XËûØö¾ÛTÓ<’€5ÿ'ƒË.¸¿¢ª×?Øä*£b¯iöÅW-«cµ@UTÙ #%AýJÌ#%î? øó>ÝÚô¯á[íê{}¾¤aªô·ï«éE „aûÄEÃû(ÈõÍüÏŽðÝøÉöý–Áä?²ÔðNšxè` ß·¥"lÆP Þ·G?`lû=Úø ®j¹Ë—\ॣ4ʤˆAàP(AÍ&…à¾Ê0Ax2E¼«¸Móˆr“¹Ù2I#6Q0’D%9èÀ÷¯0(^Cg~‚ÁÛ•RT:F¯gŽw÷Ù}}v®‰ÄT$/d*>Q¾•4C´ãÝÞ¨¦ÙˆÇVÍT«Å„Kv#.ï­ÙÊ[Tú_‘üúl6q™½„ Ì:ÑAéé-‘¾ìE.ôñ x4‚à‘†ŽÎ2²#.±ñÞ‹|Â¥ÀÿóääqÚ¡K¥Èj#Åìðý3˜© 2r·2¢ «êÈš#6Óù¬<7ÐêžY’Ö1Ö‘$“â‚Íh>GÛTÌx~2‹ããéŸÊô@‰î*%Ô39h­þZš[ÃÏù`tgø·Ø’j«Úcü© Àïð÷Ãz¾ô! °,¹f°Ç­Ù”èd"Ô@„2 v¨0é³k(!ïh[YÙR |"€h/ #.( ³(00Ýú¸ …˜Mm”°¶>˜+dʈ¦²’ÆDTKƒ¥ÊGE´@ˆÅ2†. ¨&c¯u…ù8 #a j¥6MIF›EÜ.–3+ˆ;bÁ–$K*˜Ù’”³T”HBn|4D3Š¿Á#6Ù½3¡ç$ú{•OôÙ«ÂÿHô³ €i"Q`UK‹È–…s;$>ª¾5ñÌP({èlpùÈ?)ã¨,ý¾r˜b"Zô§KA±Æ’;5WÚ˜ øO´~‹Ë~ËýOî„Nâ¡{Zc#1¼¦Ìň…P'OäŠ)Ï­·ž±ŠÎ£¼M!+½E>÷TöƒÃßô9¶s¨ÎšÞ?— ä#%“`cnh?½öÝ+ŸBGG%om_}Žþ ¤M¼Q' ÀwG«ÏwE&h¢æÝnï%óªpmœÜ"&ñõëw&©$·)¢rÛu©í³Ùí«ûÉíööñp)\uÆUüdá{‘KãqÚÛ ãE¦é]RÎ*8#.ñ6lTxÌ2Œi†˜§DéÃL©Ýåj22JË‘ïuPݹš3Tj²‡©ÿ :§Å¦%tCAç0Û³ô=f[ŽªŠ®Û/0Mk­svLoTõw e1ð C¹{…ÉR‰â_qý“Zk‹ác>Õaü¬ûìfоáÜlJéæBBé€r'·;<[ŒKå€îñÛuïºä±f’È«%3J„a1b‘{Ûì=G\‹ULj#ERX城ncsÁÜr†sVÓmš;ÃX;MÆV Š]±#.Z²·›mÔ2$ †g-$F„ÄÆñ žLŠ%ð.ª_–L©§Ç6íßQSÔxÝš#.I»!¨œM#.”F×Ϫ‡MC€êvB¶ð#%æ! qŽI*™Øt‘§m­Œå´Æ¡t:CÖ\$×@0!ª†#6%ÀB‰eV‰7vd”‚À°ò®cÀAÖ©Fvt×FýeÐm6Û¨ëô_^qöÀ©3_éävjᙎ:¤ö7T%â#.aû ŸŽÈ3ÅÀã#’Dõ<d!'š=Ws=Ñ á"ƒ{Üð±ür3‚»xXããÑèL“#Ì{ë€FÅ0ñœ“¹<ït²'ö(v²¢Uº¥\±ªÞËs[%k·ÁÖU’ÖÅZåµsl›UÊ®’@ÁK¢¼›ªÆ!E T$UhÁ^³¢“eeÍq0Q›j„¹»6x©$ëÛAICt“×M4iÁ‚šŽ‘@ƒš\!£Ž{í!Êxtš®á™,ØècÄß“³¡x ltH±h<û“yî̯wy£¥|®3¼¥ðìp§Õ+4 ðŸ‚)ÒIÍ ŒHŒ‚ÂE‘@;Ð4['H!³¡ljÁQÎ^58uä9^çSi|£E_¬ŠPE‹Üš4æÃixj’–Rd’F[ÏI)-|݇E#.ÉBœ–ÿJÖ¢}•÷Ø ˆ“ÊS0Ž+Ÿ–Zc8VêrØQ­„5†TQ¨[u-¶*£gu­ï«^2÷4¯yt£K»kvË_\h¼•G}þ÷{ÇÎNÍߨV I ’tU¯Ìb*ÂäÚ§’Œ‡VKðfá,#†œl ìÇ Žs·©¿®üwB1‹óQ”"¬ÝØ ÊN¿]S}|x×­?¿ç=,%xo:!ÀI*såÑoÉ‹1¬J:ò¤´¡eÒB Û#.#%,þ$÷ü9g»ŸnòyϨ7#%&êuÆ$3â6;ˆ7ØR71)™¥‡Tð$Ž¸¤¶§#6ø¹!ÅHTZaH $´"%;úQҬ߃(Z„©"²(M­PB0ÛËŽw@NÕeóñÙ{]=Üd[»ÁrÈnÀÇVoÔR@<†‹$„D DÉG׶·-"+joaÖà eSUXò'Ò‚ˆòB‚«ÐÆ,ÚºK•Ô¸g¯/7vj»ºRK]#.Ìeâ+µžÿ‡|½xzª”p®ބERÇ“Piƒg>á,üPCª†Ä{Æ–ò½˜½«XÜ©¦_=Þq¤¡A g6ОO¢¦‰_*’P!6E†3Ë87Xd]ä#6­æmóŽ±2M#%áF³™k#U@T¢yÂý3Ø›÷o#ÖÉÐÁfÞî,Ü ›;èΗm0T&â&4÷5I$ÂpŽº~¶4Æ1ržÍ/€‡ë›#6íÙj¼wüšXëËh:nx–fø|ñLJgXC§ÒPt/yÒŸ¿áìÍørŠ#6°\ÈÆØÍ(‰< UTȵ\[#%^™H#.}qš¨ÛVFX+1á÷D¨ûºÕÓ­Bà„#..”rªü‚˜m¾È½Í—¡—½Y]ŽZ† I„¸8ÓLÓA5À×ÏN·Ó0$ÑŽ–mÅ#6æ#.€Xõæa÷îÙâ.êôå“\ºHÜŽ9T¥w_–¥v¢E”F`0HƒÊ¡E%xÜÂ+º¸öî2<õç<Œ-“#.áê}›ϤÛy£¶™ÙÜT[§Ï%)Ý7°40¶ãòâæÙO:§dXoY£t¢«º{{»õÆU;ªR %£D P–ÈÌ0ì¼Y3#%€Û*V ÃØq@iãAF®L`àîë´'©!ç#´ ÏAVì#º(®RÐBVcÙ3±EÊí’ a „›¶)ÙXª`_€žôÉ ­X¸h£Dì;£Ûe¼ô‰ŒŽ'¡Ž4QÁJ#6 Q`¹”(T"’@ çK¨ ®¬`²ÇînÆ´Ý ïZ:uY¹í©ŸÆtFýð›ìÂ-¢˜RTˆÐfµs™K>÷Bo¾Hç¼~YÅuúý­sPÓÊÑ¿±ï ˜9:ô–Y†&Û˜:ö cU:çÙ›ëBûrÎ0º>È^i…[d6p;÷Š6¡ ’O‹ðƒ#.¿c>ëU o*&L:ÜÁÀŒ–.$ó³ôLx㤴»§Õ~àÂïÉ9Àë'tldæ…!¤1#.—@Ìäu*n&H÷QT*ÖËCî.졉=9"E‹.Ôœ+u˜e×̪¤HÈA$}á{;~æJ²\¨MâÔDF`aÝGnJB†PÁŒ=­Ù#.“ݱâcL¸¡Bž"Yáî¢ýYÏÒj@=½Ï<èq¾í!É‹ùÆÞf—l¹c\ƒü1GVø¦l.o6ÈêûlinDµÄ ®ÌCÆD ¬ïÁKøµˆÃÖ)Uf[(8?ìBª± §^fÀ] tJëM/œBóC]™éƒ¯…è×»i¡^|ÇE7u[d¥g¥ÊZ;KÚ`¤ ÆóÈ3;†óæï`n… 節qi‹8šöדOB!2ÌØCP;Èä{Ž]¡GÊ‚¢¼È0†QvDÝ9¼‘¥ºËY=†s³¤íEÃBî4¶:˜vAèC2.û\•|ÓioÝv®ZFQ4ZOí²i2e67­î1kÓkð/™#.§]Ž6@‘³4xÊ3töÕÑøželG•Í†²ÍÑ’@R† ]ˆ)˜NýþüueVL5š‡Ú)·é.´É©ü sà7§@4/:UÛ‘Ë]u‹C<äR\€wÂ'UWÞ:çQ“vbª§F†H RÉS©Ð,wÀ×ß=uÃYƒV\<wWa‘"ä¢wÕ÷€Èߤ #ö ˜’®qœ”¿£å„.›#8%€vÏÏíωIúu-?µ÷ðž·8>ë2‡“Rª™MÄ£8{ôíåŽíxÍ]z¨Bì<bc«öz:yÿfáÃçG•ŸËhäŸbV…©÷b‰ ÒÖéZ‰Ù‹ÙUð%ž¯óè™Q>V'׿_ÝSÈ2È6½–_g¸ñ¢Öåä#%˜Æð§:¬É©£_ÑÆxÖyßÔ#%ŒÒéDÙ‡¯Š~ªJ†ó$ƒ?Ù~~é†8f>†îÑ=G/‰…Þe%A7Üüq,[Y§â­¿o¹€b΢oªd¡ÝŠãà¶4€CÄ™À?Æaûý?ËÉoôÝÝã‰è”Ÿßþ$Ý6aH*2AHe,-3ôWðþ>—|ðoÃpûgŠrñßã‹jËntKç;ÓøÂß­%ÂòJŸMY¨TßzÊüúQ÷óÌ<u`ÔáXXCWKqŠ_æA?žÛÇoMWœŒ*ôˤ,«éýŸ£õÿ/7ö0oöÿ­‹Ûöþ±/Ý8üq¤¬”I&Éêe…UšÕωk›ôR2ˆXIlW+YsÎC?Û äÚ¡Gþ€€ŠeÝžYg ÜÕ|ÆÚ#%$Ç¡µ¨Ãhý››“[<cOgöNÚk¼Mbf"\ 0íÝ{PÖõÏ?oÈGðª¡)©>p?îüy÷?=BVüsɼcýNG8(´5Y‰yPYú¾ÎÞξ½¢jö«vÚÙÜ1¸ýË}+sk…6édص2½yÛù¯$Ü¡i)_?ï_Œ3÷ÓÇíüûàüéC6ª?·LG÷³û_Àȯ«ï5‹Mu¹=!£~*+LOú¤k2¨‘bÄ2ðÏVÞ 7üDÏDa{ª';âûƒ (#jº£ª€%3t»¬i×¥¦•A‰QÇŽ®¬2ÕYhqßž´w/ù‡øƒã¦Z7¤Åö#6uÑiM«ZÎ0—XL¡ëŒ!…9¼zóü€ª ²zYÏ¿hnÎHr`§fá5©¯N$fá"ßæßV Þ±Æeî>¥?ë.3/«ð>KÔ6½ŸM(Ÿ9ž—Ož‰ ÊK#6=mcÒï¾´'ª¬€§J¦1EæwŸ³g¶ËO… õR—¿Õó °"ûå m"À„Œ@¦D¡È_ßØ_éú«ÕÈqè4é|Ÿ`òßR쎉+´j`ŠQ‰(‰âL”&‡ПK#.FM=‡3–ÿ§úñ3£I¢Sø¦Ø¥cÒš ÈȹÄþƒ~î>ÜþËq•U×-Ã^Ë­uèàH19ôêaY?¸NN·Ñ¯¨]ó#.—y·Ûßù `2É/ÈDÏà‰ê>Ÿ #6m*€ÕHç<\2ê=|.mcÅwž~÷‡w§`PòMêÆ9^Þj¼=ÐXÂŽfulDRžÀç,±þBp­ˆRH‚ú‡Ð݉™îµ˜)_Å®ðߦ.üîÏÏôúýà|Iõü¸“ò» ãšMJîë áEŸO×ñ"aÚO‰A$ÉF‚ï5ýéúxqçÐû¦„Fwd;zçСËÓ².'œgòêç©äâ|9Êlrå€ó×ê¯l¢:vGÐi2] ™³ @ñ¦ Š ®3×#M»½‹.À;Gâ¿9BG8ý¯ý\žŽý»D=®h¯^þòÖØøÉýò=k‹â<RÂÐäõ†©‹€DD` _êåî‚"xKyy‘+·75¼â]*ß:Îp#%"t-8¹j°ä €®Ä»ÓëRiö4úi÷>áú¿ÀØñ<ÎzüãËçò%LÑQû>\ÿ3pÓ°êvÇ1gío ¡¨Ócj‹ÛÚï^ÿø½\¯:³Hr!i ~ZT×°-î(ÿL½þ›rø†'›™@Ù·Uì:k¶+cJ ©Òò]ô8<Ãr¾ɹÐ#6È„Ô˜²¤ƒìüØõ^ˆcÕgqÁ”oTÔL#6$8ˆ=Ìò<Ë1c„‹Š4+?G£âòó¿x#.âÎ\ÝZs ÜŸ™ÀàF’)¡EŽhsŽ•ƒ.²`.z´ñò„Ù0öªÞÒʹQR~m+í»0†Ç._¯ÛË—ÑåÇC«nè5 ÏutßWv#60蔪Z.0òë𷵬žËðSŠF§NOƒµN®#.ÐûEÁ¤Ÿe˜ûøÑ3¦2J¦|;ìïMmQ³ ïºvj†x¼rïJœ¹¾Ý‘~“Ѐpr)‡c§~¢;wo«j£ï‹ïÎö1žsÑõñTh#0Ò!ãóR³UW•×}Úh¥bÍ exƒËá "æ|PH[ǧ:àJžäô/°qŒDC“×I¦™u„ÃGµ_z§ó8Xzgü‰Ï—N×È:ÎÝÓ¿¿x£aF÷›Ó¤ïÅø̺€["·½ò½ejøM†§Û™AéA&¶²¶®6)BЉˆ;XG꧕('ꢵ¬IB¬×(~ a{ŸÕpß5×.êµ 55‡½.>#b·—´Þç=ÿ©]oðÃG§Tmü#%AÁP1@zsÍnál?]ÞªzxkÝ@ˆ2 eó*+hÛ:¬¿w›ìݩΆÜ~‚ TJ–^9¿ Í‘4hB&¨ï¿#.xbðPýÀ;Ô¢Ÿ^Æýú}¡‡›¥Ø€K“Ú}´!IŸ+ê@=ªçŸÏ@- êá)"„-±ñ³êa”å7&#.J+Ç¢ñå™ÏL½pæ#.uÚ…ýŸÏó,fƒò¼5à‡(ÌTMB³î9ál¡0á5ŠÕ’:¦s‡V6z=oÝür¼úÙ,åƯ÷~\Ý¿£Œm½ñW#úLv‘ÅÙÏ#.‚{£PP·6Ú).zEËÃÂ{ï»ý›Üan!Ì@ï\Nø¯*}Wÿ†±›þÉ3ÄÒS+ÏPhÅu·¯cãßm’Ù Ó‡Ú]’<ÑÒõyÅÕ'ëïztW{ÛÚ³—êøÞ`Ì«í- XLm#¸ ‡F—u¢ÓZ±ä¾µïŒÿ9S9œí¶}È8ß+äªnæÓ^ßn$ÊdE[Ó'}ìt”¯íøѵK¥Q´Ûmï·“mˆs³‡ñVhïU¹á _@6½o‡ÞíöO¢êCU˜sª†V“Ãú«:ÇJM,²tnOƒäÛ伎r'Jh±Ï“ªŒÁ¬ˆW•è*˜• IJŽTy`¸­ñ´,xÂÆPèL*3(#.ÛàsÇÍ(Ç5Ã.wà~>>j; ›A^ZÕŠÂ0MÆÇ¿š*vˆaFš¸7Ï#%÷Ðgˆ}lÌÒÌÁì-a”Ôƒz3urî#.œ‚÷?̶›#6!î8P¹òˆ-ã¡lÕ]EïÇ®ÎZØð™[¥•ðRӇºÝâ“H‘53û5qµ‘ÓÝ¥ƒ¡ƒ(¨*‚—Sv†Akë«Ü´iXE¦ë%qaÅn_–#%ÑEò¸ÙeÑ.’g}‚H#lkÓ>§.¿ÙÔ¯-º(ž2}T|pðÔ=ˆæ ÊÓm.C:)]úÝ«ËÛyï§ÌåÝÖÇð …0«¥7§aÝçYјóÉî® úÁž™ÛEg'>±Ež®LNïˆzûë1Åè§âßÒ(íÓ\x¬ëµÎ©ê wƒõÆiYئóãÊe0wóÔ‡%<Gœz¦÷Z¿­ ±a”úÝÊéFK^Ôœ%zd¿M½5:îRª®cw×{Wô©hØOú“¨—o¯œ¾½löyšâÍ$®¨¯zÀ 0§mCÓgÀpP®¥h îÒöUA| ÊÀÝ!=yy©DzäsÎ9UI#ºêI¹#6ÉWeý¯gÍóz8V“)tÇáFöƒ®úBÜXöãfñÎþG|Âøoí.lì&)ÄpæôëøMSÓŸô5#.2ŠsQ"„—Þw"×ju÷5ªNþº7Îåí}uKfs=}éØŠzÚ_CŽ¿dlO¶¹²ªrú%Ÿ§Øçß#¤¿|*¦›góßsg~®ÉD\͈zw”¼Ö~h˜é¬»{ˆxHgPî„6ßÂ÷¬8¹ãŠß³Þt[iÖ(N…ÒIÝô—‰Ð‘Œ¿ÅÜSm©Áw—tî‹sÄ:·'³?‘¬g6ムÉÄz)÷=n™çЯ[J DqD QJhµŸ<Â^·gPáñ%mÝäú»·n·=3¼ítd2ïäËÉAíTdýúÌù^sŸÙo–R×j=µì\å,c¾-#%¯Ã³Žx©©; WÍxÙzë†X\µÜéó™3Õ] ΰ»«—K^ûÕGª1Õª÷ć¡–k{¦1Bϧñ êœY±£]é~·<ëí¬‹«%HºîmÆ”2¡™ ´¸U\ E\¨¥D#6Ö©ýè¥=ÿE̒㶉ÛËrëïÇ‘Yòûáã%»¾f=ÞéMŸÞ³žKÚtZÓÈ'l8•¿8{žß6TF=Eò|"#.ßßUGÙVmÏ‘iûY'.þ÷Æ ´n¤(¦Ôh³ä\ŠjdäW¹MØ£Ç×G¦XÒ|^–uS‚&Æ ú]^Ñ]s}§GPW齑ɵùÛ]¹>íîæ˜~çÚã¯+}ß  Ùî"•6ÃõµÁU =c¥Ú8j~>Ï?sÓ×Òs…Å"Cqv…?·å N=3 ÏDü™ƒÆs\ùÿ‚¤­õ¤=§¤y-&*ŽJý†öãÃ[ÔÛw ™bÄØñéûDfNÖHZZÛóJQù«¿¯bÑÔ|¥¨Áv¼êšŠ±¤z(ó±V‹ÖÁtAµêq¶#.èÄ‘ø6ÔÔrÑþú#¸\ØÇÖgÞü},#6ÛNjBÅp®~ÇBYÅ7ŸàÅË;æ­ßO¶ï‡Ãß«…Ä* Â=µ³\F’˜v©ểÁRÚMܯ©”øeàE\G)×ÈLÅ­<ýŸ™];8iÉú‘Æ££í –]Ä ËĺÏYŽ\6Çg#%U¤{’Ø[†»¦.Iœ<:Øg‹#6¨Q.AQÁƒ´7漯#üá#.¬h;û!ƒ]žX–0ÇÏçò¸m‡gÝš1Ž9ÍÿžèmúH¾_H‡–GYÓ¼Ï?N××wíQ:)þ$ø/KyÎi6¡+žvŽJŠ¢ÕE‰B¹@¸ÊƉðˆP¢úz…¢,Š¡–ªÙm–¿¢„ÛiKˆ»eïAxý1_®ùS4`Š÷³…¿wÙ“»N¶{PÔôÖ÷ÅTXPD]Î*çDïCEžüYc¤ô¤ãð^FO¸Žëz]ÕÏéæ’cpÈÚUÊUÐ@œ,#%ŒÂpHmUWùa±ƒµ®*|ÛH¦Îˆ}Úu÷'Ø2›Ô¤æŸŒ¼…ìGC9BSu&3æÖ5ñh÷=3m"±|Ø\!€XPŠ‘.{ØyÝH¸›¹[¹BJ+ÅGPÍS#.O2,,<Ó¿f;r‘÷ú½M`ª)…®¢Ñ@9b\m@ü @@]¶ÐئD #_K®cR(ä?µ‰#QQÄpFñ#%¨ªêªÄÄ:UÙ#%œ¨"ñÓmÝ„²#.ž·4맯É(¾Î^¬(üEJBë…‹2 3º ¬W¾\îp®4«ÎJê#.¬ËqÂ&zÐB/ò‰!<Ë9#.ølc´“Àw£ôMzME‚×®#.CÁEEÁEà#%‚<så£ñhõyp³R¸„øðá#6ÌÞO}Ž¢„åE§=•ï½ï–8Å»—ðàÙø{q!† ‘¯ÑÅ=ÜÆ¢Û©ˆ)êCöà?Ës_îì‡]#6„ŒêT?ˆ€–@-!öðó÷ÎÌ/™¾¥™Mþ¢Jk覱{Z-@?÷U§œÕë¹™üGê-›ÇŸ·Õ@{ª¼cnÎL‡§Q-ðÿúöòÎÿ”ÛeÐFçÛAùk9ý3xÇx/‡À4¾ÔPÅH DÀÒ¯ §¼z@ìÓüUÿ‰¼x´#%žo2‡2iíé…¶z—® kÒ$~ÇÙcÒ5õ«ŸoÕöü}œù°RH1蹈çè Ú4£}çKêçÎ L¥Ÿ­vJjþ©J%ÿñ ìøÐ}ÝAbAr€ðQ1«˜o£Ú·X„#66ÅH.>qÈ|ñ$T½‘Š"~ßÀ$ééí#.YqrA@f#%ûW°@Cë:ÑЊi&s7Ïôì¾&BOCüf+uÙòj” Ù$ßœº×Ïíz³¾EêOæÚ$>ùhÕn¡á™t÷HŒ†ú§¸!#*1"Ú.Nç¸Û;]\xVÙöùú¤šd^ ƒwÏ|gO6ÃϺí;ŒJæ0&é5Ó#%}¡8Ur/$€ß…3‘ì„>Ü­x¿,CŠq¨;™†•@v/#%å@]¹C9P¤grÏþ¸(9Á Èv kš$§9T¬Æ4˜h ÷~ÿm*ñ8꘻ˆÈ` #%X} ¹lJ‚‡^¾ü. #.JCVgˆ‚5t^‚ˆ7!߆÷ eøÆe#%~zT)AY`ʼNµÂù íUö)>Sæ׋rYXP\¨¢!Å&‚NŽ0#6±ƒzY*¥MtÛ ìt‰šÊl,tgÎçg2ˆj—bOåN@–Âq7´ñ&¿FŠ)¶¦=((œ{;›‚P'+ª×Ãw”V_ðÀëá?tf“¸Ÿ@¢ÛnóX.Ž3Q%x]]Ïv[ÒÉk¥סTÔ4¢¦ª%…e^ROkl(FX8g&øw†å!£?géâÊ#.#íYÍïj‡~†âi›e•Ä×u'³Ý˜e;g•ÂTüLìq1Ý ¯#¨(òáá#ŒÉ,p†“çt‡Aè•5+ÙldèwäÕÒ{øÞv‡åÆ;Úõm”P•'@î}ˆço=pÊ2H#6O G™ê_™0P&Ú‰J#%íËTO¦\ýæÀ¢j›hDp¿É“ꡧКy©9Sr³xêÚ-ÇhvÔdŠ#.Y|ÓãÃPmSÀˆ_¶V?L_"'(PvÖR«6ã$¾Z'Žxõ×=÷#.A='áTP‘)þ%¦}ýjW¿Íþ\èô´Ò¨õÑ3Ê¡â̸|ÅZ~c¡ÞFÊ0‡V‡(·ÙÛiúÿ+Þíôüt|~!s#.Ë@Å¥ÑZÃñxä·˜aƒ……%»©±ªþ½GŠK1à*ÒpÁþ'Â=6ŸqK½•¶±/öþ ®x ©®óÒ½±dRl/U¬ôdÂAÆr³#6+óÁ…G‹Ì&IŸòê“nçôBs\g"ìé _žKâéœ?ŠŸ§èMað­{4•„ÃCɸü¤ý³þ~öÜ…ÚázNö}Ëê˜9„)˜Jë³ÒïIÆ€f¸<Êw²Á»¤‹Æ=,µ¶&M•!.üpVí±ðNtÞe°Ø²”Ó¦p„1•ИàƒøôžzrżùX—wÇ+8So£°y1Ï,ð#.›0#йò½ÇÈB9:ƒ-{~Ã1[rµã[öÁå/{Ã"“ V«Ñ*&dÛëqì¶Y-Ï.õâÒÝpœO5Æw‹;-¹’¸ò ¦JÅff Ç»¼Á¤à°Õç–H‰|WÉ¡œ!I¥Æý1eEÖ¿Ö9†AŸtP_ªÈåÐêk‹WHV#6%±¥ì嬭1^ˆ´#0Ó)åªùº°E­óg*ímŲÜë èûßÁV–Bo^Eæñ4ûy­Ö°‡. éÄFŸ±¾z4÷ö`€¡(Ù§¢œW'¨f鶹Jb•‚®òÑ;¥:ZÑ;n‘»Íçe¤°,5÷¿_„¡==:©8¼á*è›çñŽ–_ozâ|qä“5v]QsÚö.2ß#6ïÓœ‡Ó~ýá*ÜEÛ‘"cƒ¬|T=ù<³#€‚ˆ#6àœÆ`—Ñ-¶™ŠP¦#.…ÀLï6– ÏÜѺ Gà¨ëÔTt6k®^m¾×¡Îu¾íˆr%ÄD>Ú‡òQŠMŸã~pÝŒ, ¬&»œ_Ag¨šŸ²„Naæ{¼'##6ØÊ°àY•€È‡#søýø²\¿ú-´Ø~mÞÂɱ*ƒ6i¨I@­¡¯PUG¶/G$YYzÑÎR0úgÏüŸ—î–å¯Cæ“ô~6·ÌS™PÔŸ^ÎÓ¤é#.Ózý¯kHmiÛm.•3Ìx‡K¦ÜÈÿ~®%Ä"Þ+G;L¡ïncï–53áõ>¬pxq¸dê}ƒŽ †¬=ÓÓ·Uý öA¶Ÿ—vr„v¤’9’“4‹>ü‘L¸½p¯´°©gL{Ùþµt6É{<]^ÒÞœ‚™Ó X ÈÁ\€‚ ¸­ALäo¢—Ïóü¢’Ôð8ºÜP6;Ak…Ù#.†›njú«xõm©ÁäXµEÕ-— ">/Ñ£§5ô¦`åä[R÷6qì./Ó#%®Prö9¤¡ÏTXŒù8H^ôѬPòÖ~•Zª¨‚S~§!¡™<פýUîO'›Uú¤–0°*û¡Â!5Éåúõ…`ØY—³¿ÂÁà$µD£#%¡<矘 A8¤y¦ç½â±F*$‚P’IåeÉ#%úZ;dž>žý{/ó n\ö»A8*Ž#vw3…*ü5`,rØ5¦#./¾ºËÜŽ 4W‘i•Z¢‰œG˜é­Ÿ!uá&(ÒÇ_q”Då®gˆ·3$g²a:¬ï,ÕMˆ{ÂkÌû!†ÚR [€#.Mßâ–i a½6`3­š"»—½§ïØÈNÐ+Ùbv™‡§ˆêëdë—ŠZO9ª$-]»°Û–ü"üfÕÃõ¸p‚ELÂçìîiUHšE¹é±e.™ÂC¼â‡gA¬6y¢gÃë]±éwÁ÷U îåã½}¿)¯#.A²ÌëðÀ`lAºÁ×dí9Èãjá*Ù݆ 渻²§°ñ,‘ƒ«hï Ü£dwÌ.°‚»d€åšy „r ‹%(sãeäFzeÚ­Xí¼#.Kó¿i†CÜÚ'~Z6´–# ±sY‘”JÉaL”Jƒ ˜¦#%39F¸¨™Q" ^Då6{PSS3yÝL†Ý'n˜'´sûõ*ãšz‚¶ƒ«cT¸ˆ#. 2!r(PhPÚë37nPᙌF;E6‹ƒ#%ò-®}“˜]…âRkß ×ø°Œ‘ç>Ðt©Ž[â¾Ú²Ù¯kYëõÉ×,oÅѲZf°Øâ'̆ü¼üFæ"_ê•ßv8í<[8›¢XæË«É;ýˆÚru06fåÚ¹$£”ìH½_¿ÅÌÖ_ÈB'ÑsÁ¾Wì$Š“³m#6E]}Ñ#.~j4aÙ£ Mfù‡ ‘À\ö=‚°¶²„„ÔJqå΂{ŠôÅO ª÷Üþ§Òj›AéTPº¦Êƒæ}Ëã1¨ò%¦ÿ£Ì÷"‡ÛÍ¢1#6HsMžÇ¡±Q¯5óßâ¬Ì½9ÆÂ>uRÂ#}ì3va¤gìvŽ‘m¹€ZUÀ#ª³qÆîëƒVØX®H2 ÚÏßÙ#.ÁÖ›@¨$7›°­íc<ƒŠ!"†·ô2¯È.îöö !Ò‡5‰ÐÀ„¡LŒ÷³á±Ú¶æ…™Ÿ`ÆVØäºÑb#6#6õϧë(¢Ý‹a'9ú#6«`Ž‰„4Z†¢ >P4i á~q\KAÔ!nAÌ‘)™ÁÑYÅɼLèÈÉ6Þ¼44mŠ§3r !±é郓Z-*5Þ/!*kâÔ €VäD­—K¸ù°;"Ön[z€2bŒˆèé}ˆÚÖ½ÖJ"ÃX¢žÌvh{X£è¹Øf¬b¡Z–ëó‰Šè¾´ò+AŽ­u~üâOÚ°Éqm©Þ„:¤Œ·C®aïÄ,«&q\È™A̶¿n<l4(ÉÙÛÄW†ŸΔ"oí?wŠ©zg\.Äf¾]%k€Ù’¢t<æÐ#.c–Êá tgß›r#%s-¤  ÷çmhÅ»]§ˆN4(ãZs´cÎ.ßx*sŸgi5‘ÃÌJI@íod/(&_ÊX·éåøC¥¿ZO¹V?0Ìd<—‰Ö‘¼ÌĉË0æ–•;bù¶ëðzàUö‡Kd3‹³ Ê— áh@Ù€Qm´c`‰ñíYÀíÌ ÀNò‰1ª8Ø”´œ+n5µ#˜ðF¼]²"†RO]WQ5#.ê/Ùªß~”aêÔàí#%鉣µ âÓ˜ê(-ˆ”+ƒGcÒÐÝ`|^D]ÿ¤¿³ÄœÁ#6El\q´`!z I¡¼<)MG~©oÖxEß¿øÍF'­Qn#%xTL9ó¸- 4‚€ÜÜÜÝOÂxp8Yx¶@Ø‘o\D÷Níuœ§WL7COÀ»îZ¨#6iY|ŸÊäVú—AÓ]&[óêæ¥À_5·i>sE/#6¸¹€%5ÿ/ÃN¿ÃÝìï(öþlâò9Ðs7Îåú§óˆh£ógTH‚€ŸðYg}ç_Aò0¢VÅSNÄ‚HQ–'Äá×õÀ£U GJ­ÙB3qØ÷dZšÞ gÅÒЇq÷q¥¢×‹_aÜ8#%Iü~JÚž79ÀëÒ6ò`á|û6¯/ YÚr´hÄú §]Ù3óþKÕ⻳«—Ä|3ü|cÈužyb3f‡ïíÃø‡Ý Cx-®–X3ÐPX‰ú†t?Qà œˆL#6Ô28~±s;{þ|%UVvþ,¿Æp‚gºÿ…ÀY²Tì*”Br)SŽ0e–Çûï¯Éþp>VÇù¸îü¡ü_ášbÖ?©BÈrøð½C9%_ý¹$TÅIÿUP©þ·÷êYE»üZÕôAêa#°^ˆØ”Xá©8Š1#%hÿ]¡ 0;5 gûYý¼³Ö÷vØà|ávçv…†ßë›ûîšÖáþйގc€Ë«±_wNóx©©A3CaÚ— ï:Æ]Û¸…xn4©‰ã×Ìê{3 ®íH¸éÍßYTBºâüT£Ca²lÀ–ºª<ƒý¼ƒ;ª§`xµÍG°{‘Ú€ÀÞõ{ƒ¯œaô{ý.Aô¯³í4A#.PÈû=¨íŠÚ+þ˜ªYÄ; ]ÃäNÁ5%–‘Ñ…î\þK¿ÞÍ5”9€Í=¶Ïâ}™ F²†¡®B Ötüû™§ }ËÔ:Ð$A7ýß¾ºÏîû]MéÎÉ㈠D|_÷*ØUÙý./Žœó,Õ!nÿP=`dÄ_¼çœâÜ&\’B7dªõ{ ²Aåý—ˆŽ qÈP#%)X†u„¬¯# #\>Ïæþ¯G¿å+÷[q7¡ˆç˜b<œ*„™Ïw÷ý=OŸâG­ÔûôŸH|ï=áãøN®A ÊL’ª´RÅßzáóHò?yˆCÔ”“àVøþí¼…ò>!”0нÈ^‡ûÆÁ„.4ñ6×å#.ý?ipvóÙ7ìj¢Ñ X…˜%.S–7pÂM”ÞbÀò6£ƒ4¡ÖyÂCaΚ#.Ì·¨¬Qv ´†&ËPÿ—<#6ÃvT‡«±}ž‚ ûXðÒ¥W–‡Ò`_Ýä }ÝâÄì9Åwœû[wmZ?¼“C`ðã tÌ{àsõ›Û?OqÂöžVÆȨ7RÁ°Æ.¸È’(H0µ˜p½#%o{‹‚íx-)F,=žÂÙ²"œÃÌ„ä—°íÀÚæ9Níw.i…c’vL0ìòÆ‚Ð*ÚÃz}pAZ€sŸmy^®šROÅö…îNÿïòðY GÔT%X›|ËBáá5¦ý4 ¨TÎ(ÔXÐ# ýeµeþô=]Hê#%ºwZÁÀH“_ž6£áúl¯ïwç£èÃ0%ï»=¡iìÁ'åƒÇ`w‚Ž%#{†ì;r6,#.øÖ,ć¤zdx`¿‡ý9p©CYÐÒTe*ÞiD¢-&¶Ö H<>Rg#65Y$îŸ?.ÔäxRR°Áîú=T²U}ø@ú1ME~ kmmÙG“åô¨óƒ&FØèÃefæj²’~G³M¬2ëôê‚YAÁwYÃâÖ÷ ™#%y M©o®#̉ôlBÉÕ@"Ð$eæ ˆÄ|³5:IF~Œ—¸"Ì-¨É>¤÷x›¸ºŽ"#.?â#6ª[pÆñ”·æ4ï* *˜D¨ uåsØmos¬!ÖÃfM€ð*fÀo5йdãÆ¿­ç=1…”V·ñÚÀÓömçÊkû8…6ú„l£\ðgŒbÝ’K!UU;¤’I#6ðïlJüý¸f¶­˜Çí86#îu︔öoz©žàÖó×„É „à<è:YhÜðyfÊ–çñΙSlK6ÉÆd–.Õåè‡SøÌöü½Aô–~Ã!#6ü§ù‘|N3ôÿ›»òï;§ÔO‘Úy K~ô¡…×Ä°´—SêœúfÄ=ºÔ#.óévdÖõò?IÆÉõáUê6ü‰“ƒôû”[~@ðˆ@X#6‚Ä`ÈÃ/õ{ü>Ÿ¨ #6èQÚ@Äöl„²Φ§\Ã)%±·P7À`«³¹ð×…÷C.ŽÐup È`gü­ŠûV}¹Î•|¥F1°bcC„2æ²Ëœæ‡0ž}"£‡A°o‚#. '#%ØÐhü÷‚ìÚ¹&F÷7yGÃÌ2M†Š:Œ„ÖúD(3ü_‹ý}Hlû·§qƒE#.2+Ï­M¦äûøæƒÖ039!×ÚFå—9-Eö V#6„ ‡`d’€êxïk #%ÔOÁ-=fŒŠq/¾Êƒõ¥*`k|X}yÅ^Eí“!l<—FDYºÓA ±¨TD â)#–¢š3S :€™™š‘i°q`wr$îï+à™±›">,Œòq$D©L#6„3ÎÏõk’NðÍÔk‡Ä^!rÈÌC¹éi Ñ93C<µ™½Ø2.¦„#.Y$ˆà ¸ûŸÙÓñŸoXÿËw^º#%r€ÿ™úõˆUv­Aýù+ŽRWáóÆVoë¸q®ž½v¤ÞŒ”™zíÛ$cÁú»e’XjÿÇý1ž-.õ„XÊf‰]dmffžµ¨Tä!Ô }™§§Å´5N*näl³+”¤CEš‘×’I$™ª5ŠLTË­MIikm§™«š²Öjãzef׌º†:õ„ '«^kA«ÐÖ·¢šféu†G¼ÇV:ŒÒÄeA)2¬gøY(Î:ü¾Ù#6ŒX#6ª4YH¹„ŸŽ²¨é™ô0 ˜A‘P~_bÑjTmå»2sxqnaÑX„î;ÚŸ%³ïöÞÙýµ£ƒ˜t€Gí^^ÈžÂÆÛýÁ éŒó˼ÉI~}~ÝÕ¼QRü¾ÔúÑ÷}‚q§‡#%è!À´>Ò^„¶Cwl9¨; Œ„‚6ð›IÝ·;¶¯¬ó·Þd]¶d>a؈ZjH M)ßQl‰ÐªïVY¬$ˆ €r¶PRm82d?ðÛc;ŽÆxézZªÎÀ«àU¶#AIE5º&W¾f^%àù§Sš«-5RkÓ„¾]ZÍÌ¿ŸÂ19l=1IAJ~ËöêuEð4+Õ.^‡¿%6‰è ßرfÆó£æŽg §-Ô¦ÝɽŠI"H±’~Å÷WN3v¥7(Š)sp‚ƒ*ˆKûû;®Î϶þÒÀû“´ÛïŸ3Ž£ßødc_dDôvö´€’»~¢µQ‹ý![~Æ1m—ؾñ!túíÈÿ©©¿)èlHÅ3ê+ì:ŸöK<¾¢ªQRR”Š"›äÙL„Ù°LÔËsMw¶`¡º îÍieBñ@0bRòK:R¢d|~“n|šâšWnƒGô „ݽ}‘8_¨à<'¯aòV¼Qîó5¯·o\W‹÷Û^0'h7 2u%‘¸Fmîò f±0/ÍÊÀ·ió¸ò@£P.£ùbúÈ+‹?NÎTáõPäDs#%>!ø½Þ@Y68þçxÉ\ż.Ü?€ú-¸QÙþ*°™D­jIôßç½a-êzÄRÏ¢ˆ*%ö›Q<ìr0}y,ýÂz ú>Ç[„žäv€ãòƒ`õ#.€²Â½ú&Ì2(Abã:’6ƒ¿ô9œv¾ðû`ç¾É±9ßÙú_¨ì>†Ê§8»ÀþÉù€ö»6x'ã7ˆu'€{Å;H©A5twµñíPI”É(Ö?xyX"DNþ}+»ÅÞy‡sF±èæÆÀY—€3m;0#6ŒJŸÊÌ#%âaAŒö÷D<…‚É„Y(‡Ìò ¯À.pW÷Îapõ÷lòÕs½8¾?~†ç3 PGÌpC© :¨3yC³ŠÐÕ?P«¯™»/id+ä?ÇGÞÙúÉõ­õɬ÷V7LUQX•®4=áù2»Ý–£¯ë‚ø|áçð*ú­Ä ‚êkCÔ¦j˜„‡£’I'±â©æ>·Ò »ø5UŠÈA°4d€>Ÿ/ЯãH¤ŸWb)ÙZ]CÝZ{Ž_\£ßö¨0G±Lùru‹pÖÖÆZžÿG}6\ÒšT…Éá žåõú÷£°20®UöÊ’°8MAÕñ#.7û;³ ƒ¬{Š¯Ïàtc$Ÿ:F†Þ,!î:€ìÎ:`âÁbq e¨s꾸äædQÒPÁ“NêƒËJCì2U‘ ÕZˆ*>RQÀÑИ†©üèeLæHãc;väáÁbNˆTm÷T†Í&†t”K£Pññø©†tóUŒ‚Hª%HYÇW¯>nÛC-Žmu‹r6ÑêE±½„¹Þô+šn}|=i³Ÿ'2)˜E^úÏS<a‘$Ed@¡$´ZЫFþÎ)Ìí76,BĤJ(~¾'QvVôb•JÁ­øN|À=ɯ»ß<OZ'ˆIâh<Þ°ö#%`íßÂs#.dòUî6šûcÁ-£È3#%§Mª¥¹ãïù¼_Øû«óåïè/˜_Çk/qcu¨²{¤·´?×?ž#%#49'öJŠ<ªJÿ¡Ä†Ã”5m᥈8`«:m _ \‘¸Í©R•JJ­Q¸kúºu÷OIÝ'lòµ°~3áGb˜â=hòäÒøŸj=ußËã$½!­°ÔÃïLý®äwšžò!³Ý(TöC’ÇÙDåÄã÷~C—Ï¡\Èৠ‹T £T¦!S䧊r>Ð2üY"æC}Àí•iòJ;)œ.Ù¹ž½ð˜ h_Ÿ™é籄¾üåw¼!,Œ#6|õ»ùN}Cƒà=åË)BB¡háô"þã3ì• kEµ,ã‚e”'?`vðßù\à£öíç¶>ã‰(}M|)kì`éþ¹­áaª]Ë•àa½E½h#ãòF%LæV #%ÚI0%‰¾ Z\Baнìh%R­À2` øÐlHvE6Œ˜‚(¤\D½€d󈸂HÅH"8‰ERG5Ö ™½Áù²AC€ DÖ55-~£¿Àú}ý#.>(#.ØÈðòÅÓ<³1™Z*¦°°'Qb¨r#%*ãIé}¨kOH~)Û´ÑLAG§QbY()9gb4 ÁˆGô~×ôs­¢×3Ü“×fH„z3J³íü`‘œV߶PÁý+_>q°Ù#64X”Ì¢3|»Í¯’r«Ò¸n‘[]0ÙP˜‚²ÙmÝyæ]­<(©h–‘ïô›“Wóúý?f’þÐ3MãÊ ³ìóžAì};`%õñºéîf ú‰#%–½'¼ŠYÍõù½HÃ[±#hqŽÃkóÔ6CôƒH&·j ˜ªY1#€(#.D†B2`ëJhL€é·E(ˆÒŽ*XZÌO/'œ ;+Ó³xBJ<ò9œ/;3§¨^-ûi^Cµ#6@Õ£wëÜŸ¢}”RN5 7ö†n9ÐõD#%ØpHo(ë u}›J6öÄ+pòi:ºØç¡<½s¶Üíö[c˜Û’.Ó à49°¶¨ <ïªÊ<Ïgã³ÙÛƒ; Âä=Åa¨]lùŒ‰úÇ#.ÒÒá)Š#6(£ß½d©ý`~¯YíˆIæIRJìóî9\§ÃÞúîŸ8ÿ‰#6Ù6zC¬÷|ý“>4Ò~Á1ØŠŸÂ¡ð&Il™LatɆ¼,#ZÅò4|˜kf_Ñ‚½š ´¸¹K†\ UÔ0½—¼$…´UO°ßÈùq¢úHhG®Ž¬’:Š#žY9!|úÿróŒ=HÍŠ@p2¡ «B#؉ë·×îüèt*KHÍÁÙ·Z<ƒ°ïC$"4˜ëêëç–?}z¢3ŒÔŸx'îM'oÝé#%ø÷ÿ´‹;)âB0®ƒôo%dLƒ›,6f(·—Öߤ¯J”¼u.î¯[ž<r±!‚J‘™´Sâ_í#6Ïíªãø ùßïÅ#%¯P–C2?¢?½öýfYÌ1?I#6¯Úz³:ÃÀÉå”Öh¢ˆ"ƒ’•VŠ£ÌwŒg#6 „þ¬)ʲüöTÜ0¯Á=iÚ ÏŽ‰™…Bi0‡Ö“%ö§ gpõ c%ÌU!ˆ1¤1*D\?S:PzΟ ®þçæ–Ë—ÉHJMŠ´ŠRÁd¸¸1?;‚ÁcÀÌÝ°.<Îb§ ‡Ö;nçÎN[»›Ñê Ýžõ{Cízƒ!RسãõÙ. S#6 žŠê=‡,”è>½¾zDZN°ëÀàô·•NV’2KÀ.*Ð~› àGX5ºž!¶Å/K›¬Û…Ø©Ó€yâzK¨Š*üÌ`…–êAn߉lä¦ï/g«”*|[•B‰³‰¿âUBP„ Ÿ£âÍù·4„¸Å„á8'Pf,ÍŠ¥d‹Ä1éÿk‰üš;Ô¾Ö#Õ„žQÒRƒQŠñ{ùmzIIJ…0êĉ´Ã–Å…ãd…å1 *ç0%v_η?nTÖtÎ#lš1绸UgP=`Š‡*•IùI²8¢º'ʨ´§F¥‰ëPØ A¶_NáAJŽ`µ}ÿ¨|ýÅÓÚ/O€>9X<ü|z`î7Éíö¤Š»ÍýVèŸt¹ úd¨HÁ^mQÔЯ€ŒÒkK™ÖH¾‚ƒºëÓ.pâìDéGb\3>:ZA*à¡=^Wû< eÀ|ä 9 âp]û-³•¦hêØyƒš¡¥ÌŠšÁÐðN£{q¤bB:8jð´AK F#6#}븟O`y½JÝü‰Ñ <÷ à]ÈÈJ¬Êª%¤[9‡_Ôöì;#.HÂ.Ò‚PŽó÷áùOžÜ¶ÕÜ’“ÌúˆQœóμ8*eåàx _Xú(ÉA=ÐIÓ퇌É\½èÖÈ,Råí <nÝÔn3Ûò'ÙÙéóøö–_gùUJè â¯Wô‹(X¨7>àý<ž?ÔÕ =>Âcp`¾Æø™ÏðAv}Ï°¦òO"¾€<ªŠƒR†{Ë€¦8r?ŠOësðo¶S·à~4Îj·¹£0gþðˆ¢¢™+dã(€Z‹@·˜A}î­lí…rÔåŸØޟ̤]×Ý®¥éÎ#%5I#%M$—ee5hPmØ؇5o¢àLÝpxûÂoJ#.0I Ò,6©‚BÈXõZªPýÑ÷w˜*÷o6`>Ϲ#.¨,~謙ðˆ!"Æ "?»  Þ`]Q{“jòNo.ä˾‡0,bd% ¯pA°kó:Î.îz³h*;¾ŸÏ»kõê'©uÑÔ9V/N|Ž¢½%œbªdÏ¡‘?*=…ym+Pw!Ýd#.Œ[*›p… ½ÈÁa°ln7â^€öÂ9„íLÀйp´4#6ú}>¿|{Vuï-¯MŠ«¼ô©)mï¥Ývó·n°­EÿQõ½Ä3t6ËnáññõÙºÈÀ®Þn]¯C9Q›pë’]q¾#.ãvË´[ÒQ¦ßô‡ßõ‡…ÌšD÷Fˆ§œ-hja°»^¹B†‡VͶl؈Å#%PX°V0PPÓ®¹ø:ä nî¡zgžxDeñŠÝÚb‡Ÿ³ÇÐ>ˆyÊék¦ô>z{h}Ás1óAˆdÑä–H>Ôéf X}„Ô¾îÍžÌ#¼ð8&`ÛwÐ6pŸÑF¾ý…BBM¬#6I#6çá~­Èëž H7V%íü/Äÿxên=@çE¶¡ï‚=jÛQQEV$[jUUD:ü‹•z⤊^*eV“Ø”!ªú¿¨Ì&&r¶l|ô—‡D\Üç´X†€Öi¡š?«Xiê%x¯<íæ ®ézîÒfSo—[Ö¨ ¬’ s4üåú¿ä'÷åcyÜÕÕáP”Ðv‚SÓú0Ñ"#ò ÈðÍ‘~éº{ç´×Dª_*9äÕ¸S^t œ§(MÉg#ärö=“§ßÆò}²„)NVú¿A>?Òe ³èÎýPã/#.Yl{d+×V=µpø£µ0têÔ¹2ý0‘‡â{Qnw™vï÷ê8KÎÛ¨àzÊk0Ó2¡#%óØéüú»#.qÉTÕK£ë‡±+WU‚шù÷ÜxÉ°ý *(D¾Ý!Q4ãH“À¢¤çæT,P@X$àY´5eHr€ëþ;lýÐèllWTKw{ºZÄš¬û?‘ü£øN=‰ªg±‹\ļ3à1ê¢Il“¸ù݃ã'0´`Œ°+Ãm÷@#.ÉY†Çî½Éc÷µóy§#êú’éàãœ/ê#%B§ôëé._0þjà%Æ/ò9Q»ür#6Wúÿ=…rý™¥ˆ“âÀ¡(‰#6`1—M3-“ÊS¼ómÛÛËÄï)s ª[!÷Œ™D–TL­¡Y¬ß¨è™Žhq9"ºT"Õ#6+èAá©…¤©˜¥q„¬©æŸøÚŸàúY¡š…ûfgñ ²¤(kf:ñ£ðåØŒÅCù¬”#. çXÑa¤Å¨klmYçqÎ3Ö~73™®Ô.¦Æ%½Q²’: v-°—r· ê0#6ÄlÖ¯H`³}gÏ¡‘t¼¨Lýïä|~Gôÿ‡ìÛnîv<ÁÌ!ìÊù #.‹ÓÕwÊŸ†,ý†Ã°õ*ŒUŠ0BA_Üá÷KÒ¡‰(æI:§*•‚þM?µ™Í®ßÅÁ5›ä=½ÖN}¼NÍ\ë׳ž&‡í?z Ð¤´Y-…„¤*¨õÃ~‘#%·žöµ¦ÚSŸõ’ Qk„#%Z…z³#%åÝÈW;ÀÏ„§m`ŽðŒÝ%#%&„âUêï1ó~{Žýq×ÙúùT{,eÃMÙ"bª!œÖŸÍÌ"qùz„Nº|úw¿1ˆÔe”É»D@’ å€Ày#6#°2;×₾÷%þ÷2zTXAÄ#%2€8Æð*5@åï'»ˆn„Êl„’V#6~Ž\bHQ^ÿ½èž¿,[>G;H=iêPÈJÞ§b¨$ ¦ð¯ñ”LÒú˜.½ÕÎ`ð4ƒÑm°É(4^?b——S*èdðpéÿ7f§Mó ÜÖñ?A‰£7DË$Ë—tÆûëÌ›dÞ>o#%dˆ„*6<¾Þ<Î= Í¸_èyËa°aú ðLfë9›ßm‚ÚíŸãÆxjt3û±]]î~N.5¿U4†K.w¤Í…ÒG…QëæìøŠæ°ÞŠi}‚kCeúÉî.§ZÆ8=6t™¸Yr#6„%Ý7"—sPDÖL1ܱ þ’‡^'y·=Ÿë;â*Slb¬lˆ{¼*þR{ „\2ˆî@¯çk·¨˜ªØPîᬅâðDçG½#6@<0GzÂò£|~Hø¤þ‡zN† ˜ØûcGÿÍý]ŽbïÓcy×÷Ò…Ø!éÅ!ù‰Æ?ƒí·oÙÜÙ?ÉåTQŽÜ)ÞQñ6ÓÒáÞú_7Ûƒàò‹à#.èðüª½ÚŠpÉê7?v¿~óAÞJî„îç'bœ—û,íÜz@®‘”^í@q(M¶9T*²õ†Ã.Évm‡¶Âºˆ9ψY t Æ"ñÌ&÷Ä;:„×ì<݇Lò0âÃñ{ü×R¬þ/$œúý3Ò#6?¿>þ_Óu,š6æšsl»sÄl˩Ň0â•Õ躨ƒsŸæG#ä.ñuÁgg¹mPÿk¼§l$XóüE‘æv˜Q ?™â[òÏÞßÆIGG”HNåwÒSƒîý çâ-‚¹³\Áx„µ”9ù¯ˆs£†{©¤`öù¤J÷ªÕ:^¶‰B4žœË?ÕzfêÞÿ•§„¥¢®¿)#.!£“†a’€û1¬KÚëõÌó€ê†2_‘ã[¬{™lAnµLL#6#.µ› qÍ¡S'[o”½ÂÓGf¬(`ƒ{JÂñw–óç¦$ÛÇRuœŽ:g<®ÏðÛ] 3êød¼çŒCpêÖ“4¦8óe2övÏw]Lâ0W½k³8h[¡åÁ,¶ †”{ö}/l\_G–H½£¤á¦#6ù^'…òlŸ­æ¢å®#6—Ê Ù.rƒ‚w ë0Þáü~-¼ï>]¸ã›ÉÀswsÜ&…ÑÜÝÛe‰ìü§]È]2ý3„Ë4Ù—–£UtÏ[žmjpªž¤9-ÐÁ@AbŒ#6™£AkãÕÓlÔ•ùúøoTqŽ˜4°—gÒ3,Θw<vâ‘/£Ã±ü—3#6¤39ߊ \ëa 7gÖâ¸< Ž«I…º×$ #%‚!¼|_á1wç—£6ôtŠ‹3kì'6¥Œe¶>É1~¯&#Ùî+ïUÜ¿îÌ×}.µŒŠðƳӦ³K7wMïTD_UŒÒr{RniÐU!Üñȸ>mòi?]“Ðúsyü®ç:áéõ#6Œõ›3Pÿ[~´ì—ù¯§¹OšÏ.pìà?h‹^UÚfÞ|ð‰ 7䆨 DN&¥6iQ-9z-¶bêwþ{£K–Ý4d@òuL:Ø€ÝrŽËç}ÖÓ¶æ8\à4›Ýœ&dOË‹x”tð£ÕŠ1ÕoêïSßþþt¾þßã}¿FZ÷|iwïòÃ\EÂ^ÃÑq¼.Îúy­µŒß³Ì#F×›šcró°Ê 1J?„yŸU`< zŒôê­ 9Ÿ=(ÇV#%Û»#%øÓY]¦˜ÆÉNIör+Í0‰Õ‡”#%)aN^/X#÷$ 2ÂOaA~eœoa#%†aÓÈÅNÍ¿GõŽ‡ðãηÁ8•†ÝóÎ$÷ÍKs{üœtJä^ôeìQú¤ê(LÌ#.!Ž~ž=.gm “iijÞ]¨¦b¸MÐ6Eî@ýŒ´‡Þv`­w‡aY<<ëà›;% ‚Ó3!.áé»IÕ35Ç3å·®N†úÇÓ (¯§ëƒù6‰c9vòD[µ4;ȘQT 4¢Æ’R.²aV&¤BœÓÕ¯Nh¬Fz *åQH¼Õj´$fU"Â67U’<y„b ןžb® »T†#.‹Zd˜‹K©ƒ¨å9Ì‘‘?¼’TRëøq©û߯:ÌÑÜYE‚zh€À‘O¥òúÑ'Â}ßϯú¶´ã´!hRéÿgÉ8_‡ú+ƒvá4¼þxÓËDçèûý]g#.ZŠgùPô˜}šý;ñšlÈ°†± IôKû«ôlpýŸHN#öþ?bpà€~á¹#%—$!!ûß#%Ü•Ul?Úz˜Søól]¸~·a¶Ä­Ûɘkä öjk#6vè¼]ý#.çˆÓHIH‘Šÿ{µ?¢ËÀ;uM·YMQÃÈÓ[q“Ƀ[N¹´ƒ€&ÚôunµÝW€x¾0óÉ籈×p4-œçPÖ\IÁ¸7\¶Öò¨ªñô¦Ü‚ Béǽÿ9Á¸t<#%<‡3§ê#. 2¿ÛÐs½k•!(Ú*]𒪨°©A+hX™˜’Æ?“Úvÿ)Èi0h~~ë[v[‘¹a,îÍìw ÚÌÖM€N0Ï|¼Ùª<ÝД'Qä{jWÛö[¾¿‰ 9`±Mà®P ³(yÝ9qõEX‡—ós!GWQ®Ú-ñg4'™io/R„ á—«®Pï¡ØùØk„•¬ÈŽBé;"kƒ£s_¤‡}5fŠcfS.ê²ëƒ€Ã2 `D  §ÂSIˆ0&„b1"áJ5ih¤˜>fRàH¢Í«‡N¶s8„ˆ¬ Fe…qWžäz’§d¾5àÖ…!;xÀÞ>ï†ç†eÕeB-!Øœïm§0M.Î"Éô¤à7ñ¡NõbÈ>}¾X3ýä…:²íäÖNÅÍ÷ü7º[Sò„aéߦ#HáòÇéwo¦¢Ì³ÙÛ´!€;Z-"¡ø‰è’zbS Á#6ª…°ÐÕ’–²A±¨ÛAp¥i²e¢TA Ð@h=BµËŒº¿ f?´5û†»›q&­}Ûrd^Wð?üßk{K5™ Ú,_Öï{¬/+Yüt1Q!%¥*‰d`ü¿­ÚŸ@R¾Ä9AžäF"ÕÝ·fÛžPàw†£|}¯ôh„ {·g =×·½¬HER!AÓË´Ößè,?»ßØ=†ËȪŠÄDy_7xÿ<íî,ö‡+ɲ­.eñÖàÈÔhX؉‚+VBG=«ÝŠÛV =¼R7–Áý.ÎùrõT'(f¡Úh»V7Ê‘¤–sW´Ð’Ž¦À&’áõQN!ëMpqZ%·d岘—nƒ¶;±'Ú%1†LhˆUQú#6l}i Ã ó"™e¿¦<BˆÈ|½†3®Ü<<t¹Rð`6¸Ç&ÀvîÛv ŸyÂ8Û`«pBº¶DÆd“kOVR\:¤+‰;ï#¡s°îÝÈãLºÅ€xó rÇúÝkå.̇xžÔ©¸ë„4Úgu“"0úË!we*ñt5æ—:ÝÎ&e%•ßãD‹¬sì·.¬é5Ĭ^r)Ãg[Äb.;a“3¤ðžœX8:^Ó·¿Ÿ¡Û&Éë“Ïs^½ÛÃõwG•$fž>šWâw}wλw̸á‘ŶòÕš˜Y$6ÎÖa7Åç4E#v8œ–¹omæf¸À¯C'am²H|MÜ4!vkÿ!VBÞ†ðÙ:Æ Å]å&@Éî[ÝÈù1àô#.sžµ3/MC4XÅ#6ÅE)ë‹nØáã´'a‚Ú[–\†£Àº½ŽfÖ@ƒ¬næ¨|?¢©£_w³Ý¨Ï¦ÐàY”ÎÀlô2K™‡?*#%a8xIéç×¾=¾øs¿AÜrø€‡‡fA#6¤–±á ·GÃM§o#.F¾™Üϵš*wQ¥íè¥T—ˆ”ñ˜›`Ñö7€õF4kÈAËð#.2ìª0hXøT¬ZÔß´åb µØ=H<àXoA#.!rqíg#%[:yléTËs©ÓT#q¿`Z×Í“^!|´Bl¶Ç¸ò´©Ê=1Ž#ÖÑ8šÆέ$'`äQ.º%ì+¶&†À÷{¡èzy…‰5 °ÔmyÁ,Tí6¾$pd‡—ø(Q13(%&ŤÂBß²°Ôla¥²HrÏ#.æcÎ<BÙɤôÏÔÞ»«[°ËZPX·wouMÁÈ̪÷LÎi‚]©C£0=ƒ°X¨îuà]ˆñ»$8dï;ˆu!‚ÉÒ@0äPT®a¬xoà„fv‡fåWl×€Œ G–¿àªˆQp@©Öö›¡’”MÇ“qG{tØ&ðºSlƒ* z½39Ã=NùÌÌÈðØy"¥ˆw'ÐUÒq5¶Æé¹v/¡’Ëà24@·øl'âlȳ§w@'©gyÂvk–Ìùµ‹œÒ¢¬ d¤Í„4ôÎ¥ë|í#6£Ðà«5t¶õMÚ]î‘<#6 i5À{WÓêB<ᤜ‡”16;€¤ ²D`ª#6†ÍɈšN^D­YuQX’¥ŽÚhgLm¤»2îxNKaÊ£R+q õ(Xø8œLÎÎoE”¤hÂ.JhÚBHN_C]àYÑÉ.ƒžÅuáŽ-,\à@ Ø+ǯ53¢,UæeÇ™™2æffXÛ1¬«2ÌÉ%Xì<õcˆûªjñl$0n˜Æ&z¸í ü'qÚÌQ¶*WÕÜFÞz>7³wœ&–¥éÖû½C›9Û#%!zˆlžP?V[¸·=¾Û»ÊÄsÍÑÖ™Ò„&w·áÁ"TK¯=k9çÓäôzp¼m™)ðièÖšÜæÄÙ3=êFC¾hYCS8G+}9•­ÇÏ5®ÍGÞ˜sÔÁÐi“·d%(ô#6×nÜÉBiÄhŸáIw°P¨¡%Õ,±h¨¢ ^4˜{ûw+Ñ#6Æ]Án™³ÍÊÔJ,!25.!bÌÎ’[úsHƶg6|kë‡P奕èŽÐÐɪ×e‚ìëÁéë#.dÓ’C¸ÌG)¢”AkotcC~[^²Ù#%D¹ÞìÎBI(×¼®Ò´U6U;â´øÀ¸}=‡ »Ï|#%Ö«Õ¬´”ÑÂ7:ðäUC#6ŠS¥Áµ;’ȉèáÎ\6Fj«U*F7Û`žr¹á˜t¯äXÈ‚ë@¾v…^µš­G1)ã^ï4¡N)LvàYÎò`']ÑL„ª5m²Ý3ã}´Y¼5ÞB@-º· ¦ü¦ÏFµ&¶€á®) l‚ÐÑÛµŽm,¹r,±aòãH }3V%O2‘(ö›–„Ðà£J(‹ÐâHk2fdçqªºDôòÚy"èN‰5«Û¹ˆ]Æ#.¡¬Ï7Á£0¡ :þÿç‡06î쪻BÌÙµRÉÊ$Ç‘‡cHÃáŠÂûJ$ètU&WD9æÏ‚š*Ë!çÈÜåÞb<BÑ#.#.¤E¡Fy ha½¶E· ­38aú-á'/JZ*šV¨i4œ`hRq!]#6ÐΡ«0Q¢e.‘ÿ_B†"ûÉ¥#:+R*‰F䨎ãºq=r@õœ¾_ 9Û!ß/¦MHYˆLÓÓ‘gq.Ôt=vü7]Xu&I¯Ù„(Cy¨¼°jÏ4±K ¨d;Ä¢¥Ú…©#.üJ”$E1ΦÚ1‘d‚€k±€×´†³[&ᨡÁÄrGÌXÐ!™"|&“4уIŒƒr`ô<4˜ÒËMkW€gÅ7&»Ó6ínS./ÌçÇÚÖÞ°‡F2õN;@ÕDf¹â“»ãUëÐûŽÀ%NÐn_~L•À ‚DH²Óf©˜¤ß |oŒšR½oªó æ85IF+:ƒ5_`d{ÎuȦ¾¿\–·\g#_-7‘èõ–ì{Œèìî"æ³#YCi#.‹×ØÊW¬¹7R¹ÉØÎ!‡‹±E¹—*.I¦›#6’‚Éw×¢½³áp$Ç‹$‹#.Îxy|FÀPL’Z#.š°’(Ì—‡nÛm&C¶#.F´"뼡%Ï·z9¼ÒjÄ›6¦„™Þ0ð¾oHCøŒÇÇš‰Š¥7L!3 „0q‚Ä#%Æ;{‚[‚þ@o $ÜÙVI6“-Ãp%A<劂O××'ŸP£ÿ¯¹Mu#%†I|E>½Êõž…8q¼D¨-ê½0ÒƘ»²ô:¢äåê=¦‘™&-O醥éÏÛgÆ úîí²]+<GHk%Ù¢˜*>ñ¬K‡¿·M>i#.$×ûwã_ÜŸóÐÑŒ‰¸$ëÌg­ñ\W«§Ñ3nWVGM§OUªy#.ñ”ÜY7#.ï·»·=h=BÐR´”‡gÌß»´_t=V[(@²2N. c ›!áHôØ>((Q6ÿÅ7Ý*Š…=Š°eHÐÿoµéÅr.#6ë\HîÚæzyŸðCÿ?²e˜³ý“€‡BÔVKÓÝò<`PA>B¢*£!>Ÿy5©÷Tõ.¥(¶fÉ(ε<U”vöú|G¼z¨lô!á×gï"ûN`´é~aõúCäD#6F ‰[Ûº‰7÷«nÓ6R[êž ‚P!½ÊPE.xùš®5 †#.+i}˜ 2AA‚ˆûw­ R@ŠH¡A)Ò¢+­4Õ#6˜ÚÊ fkÙƒ:·oNqï$$$¥¯b¸Îô”yØöpÕ$µ@© ‚Ò(øì¡ Þ§’pm`<lr°#.ú¯±òÁY±W,ï$ˆÍø àž ””ôŠ]CP÷À;?8¤D™v#6#bBÙ-’hÐ I÷ì{ÄB"@ˆæ|§®\¦€ú¢Æ*X4GZ‘A#.ʛÙx‚{i#.¤Pd ‚##%=ÀA`|x_ä CR³¥éó4èÔ£`$‰kB1KºíÁ#6Xƨ1tI‡ÅÊ»{m|®Õé¶#%¬Pº5ƒ80.˜HÜÌö¶D[¡·¯¸ê’{-Ïþ0~ðî6#.ÐÀÛœuHKÓÁȬ;‡Æè+ù(ª‡—°0ˆ#6AÍ£3¬%„_²Ÿ\$Í•>‚&@!åÌh!7#%Ü#.[Ó]ì¸F/ˆ5û"•µöi^øUÍÔdwéÖn­\¶qüŠ/  ‚êÌpá<` bˆé·¤Éê5e6Û}åcj墩i,ÚAE`d;ˆá;`‚þèˆ ˆè«UÛ4ÖÖéW¼ªÙ¬”àc‹ò z9$ž»u[}’ÌnS(î—ÁG¦" uÆäMö#%²¶×/lM²`Î 3 ˆ™ €Iˆ‡BÅ…‘ÆêÜ£ÉPbî 0*z*RI‹z»·M·7©½MÍv%»´^uå»ùÞy·‹›¦;¹ÕÍË%ewh䉽#.„ºEhÒÐ% Ô¦¯>6õ|,µ•ŸÁ}lƒ@›$~×Ú#%Üà=Ó$.4¡°†eÏE‡Y $$gñ¸|o'¯Øÿ&_xr¤¹ße{ÚƒŒ²qõøç#Øéêô/Í9xóÏsLXËü‚ðDo™Ã7 æzF%€A\Gv‚íg–}¡õ¨ÄhKmBˆx#6I!yb°hÚùëí*×O–é¦Ïî§{h“Vø­¶¹[sU>þ³Ö2šf00$Y§Ó‘Ä·+?߈ó÷ã–ì#%OPÐ?ÂUHïjÍgy诨·«f#.¼ç• ™r£k¸,“‹CÇ•sŒ(lÈd×XT ‘˜„¡…#.-¡¦êTÞè`¦–—`0¹þf( äC.¸hïâÜ_ <rµXzèý¦.[÷ƒ9WlÑþDÝÇ~++yK”ÜD„Ýèó5Û­=ÇÝåó†Ýæ°‡_uZ¬ÎûÛItï ½Œìßi!,òîh~a­ÑCåð#.ïvœ±ß“!@»‡#.W8Gµä´®M[q‘³ó'ôÿ5çc‰yΉ˜èóó\s‚ÚU’Á´ü³1,;næ)šÈ£D£lè¢{Â#6üýE„ª•RŠNÞϤ:oN‹EŽµbz©5RöóžnJ¯\E  k}ð„Gp0³5+"¾•N¤zŒ¥|JNq±z©$MÅkÔÃâÁ/H…çƒx8˜ˆSø{Qy´¶"ØØ›}¹ú+<·‹1™#k²*âªh–ü·ÖÐñÓãïí„()de1*#úå¢#ј{<¤©#6B~´0„úÑPPa­¶ÉV‹d¢6jÆ’l¢JŒV++-«M²ZI[Ò[YQ¦ÌÐ Š@ŒAdDõ”ü8üú—ÒZ§U¾¹G]WHsÞ"zådHª2#%È õúÐú „˜à×ùuÜßÄgõ¯"ÝV7Õ6‡?e¹¡€ðÐØ늣šˆ5 ŒhXdi½ú#.ˆ«,Š_Â*†ƒ›”é’i›â{ÒWÍí ï—BI*@<&k(}¼Hšt¯•MÍ#.%€hkI”‚/¤Ò²fIš¯©ÓrÝw‚§º*(»w*R Z(íxÇÕ˜N{Òup£&šÝ¨$[j]8l JÄ*ʽ‘mî°uA}žCË•¬ ê•##%©QM¢:öï¦ ¤*$g¸À½3ÂtBE ÁP‰'H¤!‰-EjæÜÙ+šÜ¦l)S(ªf#.EI­¤)¨ˆ‚€¢“ç™÷O°h!ØÜÓ&²Ð8@#% HAdA•NÔUÔ£Êúø>|Çí½÷z»Õ‹r¦š7ËHdaÅÂ&ï’€fA=/Q…yžwßv) ß#%ÖHˆ}0!ÂÓã!ì?çîÿ—ðûoçôŸü6*‡œ9Ã}ŸâHß $Áßég‹/ƒŠb¬5B^!Õ i >ÐŒüÝhFŠD"°¢#誨d|M×½÷ÇNV2]Q5J…:4¡x¡²¢(.QÒHMŸnš]E>¶,xC6tŒ‰h/לá†É»²Ô{hÍDhËþ8_£÷ùóÄ“õÒ+µ˜A#.¬Zb ‚¡žÿiÞ0"$œqÝ»1ñ;“£n²èÓxIx5S²ù2ûõ#.Ou‚ *}–À©9,Ê1üKûEåž­è')®Á²#% ÈB £H싶0›º=IÐøõœþÚ;B ³q@lŠi8"d3¿!û±Üx#.ARúì:ýØp´xÔ…ˆ$‘¶¯SÓÖû·¿„'ˆCµ¥ß£sƒì¡ßlQñ»aúpð:r‰S²^uÍz놳ÃéÊdT$Ö`ý²À ”Œ>·†D=‡ÕNž3%c£xIîÝøµÏʼiă=ÏI#.-3}ûüÉr¯ÏõÉÀÃÄ#%ÿ4vvu„#%ÍN‘‘vº÷n¼Bq †ˆYƒ¼ê…(›lÊSiLúÚ¥Äcí&b‰3ãOx±“»#.k#6P2C+Ž´ãLjAçXô9¢”pªq¯ïf÷³®zçíblS¡-§œÚá&+šíXRka\¹ÙrÇŒ`„X#%ˆŒ žâ’¢¡ë ò£À;Ÿx‹¦h}§œ6uò:«1[w~ÿòÑ ³?Ä7¯(iHPjÊj¨¨4£QPuQöt?_‡ïbɱù*îíw(Qa–DçéýM ™ªEbyf:öõ¯ŒìôeÕË8C8èPX‰{il[ÍI~#6!OLcìš¾––@#9OÓçVvÐøX79° #¡Ž@úA¢•V/5MP4€0#.»Bgð¸k7ŸÃ¿ß^×ο}Ò‡¯Êùñ‹(=–côôþÁ~‡'‹¢!ÈD£v^ÿŽ#6îóÊ@²²ZÐXzñÍ}fT°Þ.Rá>çæÞ™¤SÚ¶8‘ècs(Ÿ:óž‹Á¿>z œ,>cº¸½¾1Þt*U¶¼G¡ÔL c3úZo¥î«ÂÁÞµÑáè㥷;WeÓÇc¶¹åpçqF;jÌt,½ÇÙ¤mÖÙ*a\£·¶|kžµÐ/ç¦ÅоÍhŒ¯›ËDP$S ¡x7ãmš£"äŠ[lmp`¯h8x–ÁÁ}7:ÙדlíA¦ÕÃÃ|v#6õw;60ný1ljmß>øµ;Q‰šcµnÊ$[ž9µœÚŒ5)Ü‹QžaH$ì£h8ŠâæfÏ{xÅlÔÝ.£Á°_+À£ÐÑðøbsaצóëÇ#%YbdSÎdÆ#.pûŸs{¼Il0ÜûÙR&ÌêAÇ#6ñÿBHjëP‘¹A골)œHTøL3 #%!+R¨Ï¢VAH©JÙêÄcv­>8}¥hà÷·2Ö|ÊöçÛ!ˆ¿#%ù˜ì÷;vÀ+…Ã"x%õéÑ×Ûc@­¤«x@¥Ñ ºï¡Ÿ8`Ŷoê¡ùÁS’'+ä9Ùýâ6*êvòñ‘­š¹lØ6‹Ÿ×ÆÛywPõ˜¤/ÊfïWÇ Œ„¬ï¡ëµ#.W#žÃòú¾ ædôn;vêÝFl[Ú ÷†w6%‰9‘ªM–ÛpZód½™òK[?i`ë¿WúX†šAžÔ„†Ó}{–n#.˹ÏeA–Ç=ÎB:£‰ îÎhá#¯$ïR¥ˆ"Ã!ÙÙã>|÷É8Žf#.(¤SôܾöÒcC|¸á6RGdõLÅõý}Œô=H )G¯†÷&r ‚ 0Á˜ô€z•s:ñ—•wdêßË-㡧"Ö$3!~ I$ß¿Ð4!•#(í¿‹ Ó]àôö klÑü™1v.ÂŽsϺ&®]Cj¤„Jöm7†ÊöÚ´õŠšúAôöh†)(r¡ÊV‡T’u©£Ý55#hý~=Yäk©"N(Dï·Ká%OÔŽœLzJ“"À^q\øëÑ•X0¸°&ÃaÅ?Õ³h„FE5|øeëëܼó¨_ ë'_·&µîP5' ;ûè`õVSªS¼Ž– ªˆ­*·Us²43d¶º\ÙIÝ]Mœ‹’ÆÚš[]¾TŒR5 ‚²0ƒ¹`¨ÈX¬%JBŒ¡i)¹d¥ÖpìnæDp¹”f²áݳ ²XÒŠÄé½Vù—µôh´55Šf¬Ê‘E$M¯ê ï!åÄ£c#6låÑ1¥gkÏ~ÓcíÝèginÿu[ÇPüúᆤ„ ×#6Ê´—é+¥dÖm¿JâÐÂO}Žˆgº„#%¨‚#%ÆÙCÜ#6„tü¥È|lûžäðv+ªÁÂ"®Ý ÙS)D¨­ìr«– 4óI£#gm‡±°æ´Sà¨P©Í×p„ã²µma·Ø¢žÏÎu„mwñž`W¹ ¤vëC0‚Ù_·«#%b ž˜n*F¢[föUŽŽÃÛ¦š¸í<¶‰ÞNHO–Nù'ùîHÙ¥#6¬…#.¬meQ€Fˆªd°QB$J”Œ`ÈÓD©*X²R’ A¡iP‰bB‹>&í'5mÌSÀ‡aõP#.ž3¼ù­«¯Uõ_\ÊƃnëµXÚ€Iét÷ÄAÓ¹jr‰ir%§Ã^[šõpÉ!1ßй×ÑXZÚ§TÙóYüÕfǦ³¶Kò`^üBþ“™:â"€^%GPß­:D×-nóD¿\“iKÛ6ªª§£Ú!ÚE$H#%;±Ä+†¦ûãÛþ:Á‰Ù(gœëw'`v2›OA¸ºÚ¡bˆb$(cÕ¨‡Bô6H Ðçªõ&™:å„Á[ýBðn‘6šLÙY¢’&oáoÃa gZæ4ÅS$.vøü}^Gwxù?LD(:íÔl†Ú£Ø|û»^ys*½ oèo’­è¡ýþ{o´Ò)”4±"ˆ‚EƒÖŠ"Îg™ÑŒf¯:ºJåM.î›8ˆgÕô8è$€ÍI¬´­e¨Ùòˆ”7Ú!ôäþF›­9Îòué¬ ÌV°WT…vgˆ÷ÙîNž^»Þ×·t) Õc=#j7®çkÏgÔKm&ðŠßÌåܹÁ5.”SêÔÆɸŸk4 JApN:lŠ§eÏÛÆ­‡m2LÆ?¯]°ËYØ¡¶“¯Áª¤úCÝÂûªC<sLTšCvê‚­ÃA ´»†Ê/>!!hl5ç©\¬Ð™J\ ¯×¥ dHÂdê43d=BÂÁH#¢‘¸pK5Qz 5ÑZt%Î^E†A‚@–@Èœ_àæ÷e{>ñ¢ ³<Ô ‰²Æw+¸s, "õ€5#%B «Piuô+55ƒÁ‚Ú4ÍÖo.Æ#*H) ŒBˆFõ‘ˆ®¦`Ș`¡âÏY¥­Âz¢ØÃɘj8MË!KI} ‹A#.``(3ŠU€Àõ¶­Ô&¢M•·ÊêëlU”j0À%Ô¥†-Ù_›œá烞;^Q=õ\¿)ŸI#6w¬©”H¹`Íöò€¨Åïå‘|ÙF×mÐA;8ck³èÚ{Î “{44‡ ”JàÉÜ™ÕÏ€}LPCæyBM±dY¾‡«²›ábyOÄ’ýäµ’ý™ˆƒ~šÉ¹úšØà›Õ#.¼C"¶‡I<Û;úÏÕ‹{FÊinlü~}|ßáñBûÅÉ{øºF¾ dìàÉ#6çîà±!!1134ã¹c÷t³Ê'HœC…˜-&Å#6°ªhvÏWqþD€ÈAˆèªt@‹QÙÃÇD¹Ö ‚[!ä åRÉÒs'tÃ-‚T#%g£è雕*ª“ÒY“Kt¥ÅF"(«UV#6Ñ0&(÷&T¼Ç0}zšƒŸO­‡²‰Qj#Æ%…ŒN¼ƒ±€¤nˆ²Ÿ™(VMBòÑsýà ¬¬ãb¨LcC@! ƒ.¤ð¥t˜1'?]‡ˆŠ& Õã¶ØÀÀ¨‘i‹®(DŽ÷¨æçÌâŽõy¤Z¬Ý*BèÀ½ej·–Mdø¨Fí¢FtHFØÑ}ÃZ0À"'•¦?ÑâÊõŠhØÒ;ðdYC*Ï;C¿ ­P…Ct_ À#%2Nÿµª©°¢dÔÔH<ü%ÝqHMÝI\uª´’ß ßΠŒäLĦæêpÚMÒ<ëç#.­†ÚÊ8ôã%˜fL‰QÇ&јÅ\‰*…å‹01ñxaj€ ;‰”ž‹ùžª{è™<(…pÑŽ® =¬¢ÿHy!ŽwD0íµÞÇ•˜ÔAc&™´²–b‚˜£Š¤Ø.5sSIý›1ôj2¶ŠEH¢ƒ¥·ÆÏ,k¬Õ=­ÍñÚÍÍ2äÁEœ ¸¨(ʽˆ„ :¡9ˆZkµ‘©´öO<שMFØÚË!Lc`0Ò3s@ï}Y÷N#6late<˜pÖ0÷J"DzØØÓˆßyCr6±Ë#%=l+§j¹0 jÒOþ¯|×f>-=q¦ûÊß:Û¿ƒ×¢ó1Èø¹¨õ’’ ^¼P“\aa£Oö¶³r@^õéu4¨’ãº} ˜xÚc4uÆÙàâ×Xgds°•;îæ"ÆÖ,_ãMþºl„áÇtï¡“ÛKV¬7LÅ¡8ƒ 9ŒÀ„²T(5T‰H†„™ ‘¨Œp­_;KŠ¦ùeÉYJ9-Uà AˆìXÛIò–.#.áï#bì’ lÃIm£€õ˜U±­ØdáA´”Ã)jѹ ÒMF…Ý4m¦¤’j!6I&̓³¦&1>ì!áÞ¦°ãm.[¼R>Ø]™Ã]+ÙÒí.ì]¥ñ•éx·´®jñašJ¥-#.ÐÑÇ#6†ƒ%±dA‹af“ÂÈ­[&fVî£ãøIå.Û¹ð‡Ç9ÝML™Í5#›Ô’#|¼™[A‹Úæèu˜‰_kØAÆ:œQB3³eÀÔ/ð°ÎÊF ws+¡-+¬œpä.Q­‚S&h%mK‡ƒOÖ¨fö¬¾ ÒŠ‡·šñßÝä:yGÈ`ÓÂ$F…½"šˆ„ˆ»|hÙàP;#.aHê‰Å4¦”K_kà@Çip[6‰6y3÷ˆóxpBÿFýfÔLâ[6ãknk׫‰™®TãÝ¥¹„ÊÊ—ÎúNº¬Ó 2I+FÂmá‚ØÙkå>)4+.b‹$馬ú‰¨Ë¶?m…ÃS>^O¹ðã³*0üÉÍhæŸ'£~;j–0æÕº5-Nîše¶F¯Á³9HßGIÚÉr,=ä°4!{nî8nØýÐë’Ùë œ~Æí:gg¿˜Ðƒ`È-@Tùÿ/¯Þ?D/«¹ÕíاÁB °!D±¡DÐE‰Ï% #.&3í÷R’ŽÖâƒ6æ¦÷0Ü Éu*:hë$‘ÌM4h@I&…M/WX(,¹§fh#6l°#%„ÌSÎ`°£X n¦yÓzÚ¹‹©i¥RJùkïVr2L”7käÜr˜E¤¼Ö¦EÇRJðÉ·ÂwXU :½–c7ãweû‰ÚŠ(AÓ×Lo®ÜXçû¡¸×-•=®1&òXËÙL9Éž‰! ’Ù}Á¹IǾä\y$þÉW¦G†~ÃE4ªm©•ëS_ÉþßÌ G®#%ˆ£sºvYBog•?’­~ËA%*j×Ú¹âÛˆ,#6ѵ¨¶¼TjæÕ¶5­¨ÖÕÍmͶ-V6«¥¥ÖévCÙç.'Q ÷¸ÁE5bõG9;#.¶ïY4¥•(ö±Ô÷u;ƒk®ŽÛœ+¦¤ùï4šÖ¿™D’4$‚S&e3JÓ,É)I¦)5E¤ÍAúΤÂZÒ1Q4Ô¡’š"P¢´±¯~Ü¡“ZLY%™[4)“4Ì‘˜hEQC”M½ÝDFÒY)˜¤”–PÃT%¢Ò£EL¦FhÆL•¦)¬T„Ê&ƒ%!MA”¨ÌA¦ƒLÚcH` ‹4ÑÜS¹&óg¶§äe/è#6b|[|X&Aï~l­U_SBݘ×Çøf̈þ¢˜;708ëY!,”Ìm¯Òfvòús†Á©Ù9`ÄÞ ¬ü8Î#.©‹4czKÖãj\ ‚0Ž¬Î]iÙ¼ ¼Ú(’5÷Ðz—àmÚn.Ö›¨—,mDñs0³Ž¥xÂxâN;Z-_ßK¡­# ý‰}úYJÒ»0teŒ^ÎÝfw†’~âÅçãîùA{*ÂïÃñŒŽƒbm°Ç Œªd© ­…ïúûZø^Ûo³_‘bc5 £#f²V*(“i–`!Faå³><9ôí0ݽò¬X ›#6"ƒtXaÛô3³ÙŸyèNUMæçaëÔY„b’™Â†+ÏNbC 4®}û0_ê®[ç/c´y‡OÀôW¿—¼‡}#êk#6xn4ëO–åûÒ3Ï=GQá’íPœê‹+™|ÎàÉ ÒV_£ãfåÄw&}z^Ë –p{"r>Mõ£ÓG]‡eUY´#.q¨ E)¢AT‘$/rdÇ¥oKý}eÂGÝ2û¶ÛùåßXÇÆiKôôˆ£¬Ê%x4ûmÁ÷cÇ—$I*#1â8-¹¬-ߣ¼8ó#+ígÇö£‡dÎ CìÖ“÷µ« ªIm¦úõöÃ{N¹”`g"â–PR¨EΨ¸)µ›xãcB?+ç#6cguVÖˬ!çE®PÓŠ#6Ú+G0Âè'­#66[ïG8 ápI5ö,nm_&·É‚Ôè2…ò˜í¶£·ŽWÂc.[õ"^7L³§<1YCŒu-Í4î'¼Ñ¯$º41² Úîg’Œ ®„Ä©OSååšä‡½ðæ^R°öœA虚O?M"… CòqôÂdìNIÈDBª‹´ÊxŒK'²Ùª ÕµÞŽ¢gd6Eõ)×FÛÀ¹m¹™—¼2ÙIb=OiÑ·,(ŸŸÕ5X@ý\µêž{w÷7g FÊHC–ï‰:ڷשÔ'®½]Æðëì±õz¼†— Ãíúëïúv“¢B¤†xà÷Ç}IÁ\³Âw¢â‡vÏ·§Ó–æÖ_¯¥ø? 畧ЪX‰@w Üóßœž5Ìíí†á#6áí)~¿.wðzŒàDà ¢Ÿn”GkBkjl8ògâm»]š•%}; àf.äIG^fP–så­3 jÓ5'B;Cq¬ ™™#6;뀛<rÝ &\?#€E„C8(p+x#.jÝ_âN®ú9pÜh>?/æåüÛ>é5ú³0ù²Yþ–¥7Q6È£Šö0¦Ä¦a i™ªF—˜»ÔÎELš¢ƒÖ¡•ÂŒœ‘u§÷5˜Ú30ÞW>ØÙ‹šÓ©œê<Ô4̵¶Í<¼ØÍ3‘©t,ņq4hÔQSRJbã5¬D‹(E…Æð¶ÒçVL›WUޜŵ¬ æß´¥(%©ñ?”ÕKľ$–.›¥âÃ1^{¶S{ÛeóL:Ø"ža‚ *cŠ“óuo\ a\MLËhz²IÆ´lÆV#6¶Â'¿S³Œ;ÙŒï¬îç;WGÚv Òá8ƒ acgµ9t!éî^ Ñ´åd»Ìê5+6É 5#.ÂÉ,0&àbÛNJÒ:¤é°~­g–]øì`ɽ/Ü,7#6ÁŒR[Ì&*_¨€ÊÐõ¡‰êìí§ž4ÍTo#.ª?02!ðnÐ6#.ÂΩª²æÑóÄï4Á&:ÏJFìÀ9]Ѥ]Ò¬ua®y:†š0jæó2w67¶ŒLªL¹‚$ôgç}õ‹´n£ð@ußpBƒŒí…´ñÖ=,æYí?9Š@í´ÓRP±áɬ@ºZßlAsø;oˆ‚Nñ©¦™«¬•qbÐ×gÉÙã6ô¯fl-&¢<ºF!ÏOB|VvÌïDDÆÇr¯/“,¸Ú‹z"VnöÍfrg£ÑñS­ëqH·:Óº¶6´=V_è-³eßÇ®ÚƱã‚æ·mˆO¸Œè¢õ¸t“¤÷)0–ešÔj§=1åŠÎØÐà’F§1žÜWVÕ£i–7îæ)¬Rãžw¾_<ã0Ý®È^ÏP•Üd>ÃÜllg:Žûà5Úã%¬GQÄî²a…3™§¦è:Ó;1¢dÆ9Ž°„LJ¡n5´š£Íš']ºß—ÊßQt×\PÄ##.'yJX5Ñ\F™Ö¸2L‘/Fò.ZX]ÔG¡‚Œ(¸jS }5#6jåVͶã]*—ïXTäÊ•Š°­óqdßûªÚöFSB™cÕ\llÏ^sÇ9*Në¡Ü,ßž]Ì•öO,ºK+çThu¿(vÎ[‚=e´Æ™™¹CAib u†S"“q0V^£ Zk¿kO+gX›]Ž°ÎNe6¼™_y#”n˜uÁÕÎkƒ¦©Éct9)¯Y6ª9àZ€nl8\e¢­‘±¿#ƒ¿˜A#M|fS®±*CP7ð˜vB¦šEVöU;"áÖ“€X.\†[DY\('¹Ä>›;Öï8‚óghh:yYz™ä×&YNÚ‹0,¶iQ!³é¥ØXlhÍÍÌpÑÓNVŸNÚ]#. ºÑH.q¼†Å:¦ÌR‚IFÈ¡“bÓ<P¹ƒ‰Ô™ÂÀµ)>Û4=¸ÍÃ#6Y¬ŽÛëEÎ&)Ä+æaõ—Æ0[3A”@¡®bXßQÝëí*1‚¤X‚1qI—N~.2ߩݨÛháÛá£Ô7á‰)” ¬neïpOVêÑÙRQ9B&PÆ7Ês‹iõnc­ºÍ7Ûƒu³U·6vu†blâÂçìw‡Ûsðüa½evâæfcyž5Ù9ݬe|[Èu`z§ Û™ L>8¦²‡·¹O/ ¦ÅR=:„%›)Ç’:Ó{¸ÜÖZÍQëF72Ìf2û"t[¸—LLLb|¾ZÏÔáFc™êEæðó8™—‡S7G”´ÖQ²´ƒÓgórâ’Êܨ;ç¿©Ç1™£Wœ•· i©#\Z%±pƒˆð²ÌÝUX6嵡s2úBº9‰“†H4qÆ:jn¬21É O)&PmV†Ìâë\—[™lÖfõŽ™5,yU–LbÇ0vBŒ ¢&¨©ÒªC¤‰r# šSH‹^éi!°Ó×ÆÊ»I>ívÃ8YÙªüµW›a²EZN©Øχ–+aì'gj0šŽ²ÕUÎ☙ìrÓŠÖ*L¾#6$§Ê 3vðä{Á¨·É"aNû+â(0àe#6Bu öó›Rô`h©µÀŠp PI©š,#. f˜F’èã#6ëÚ°äÚ¸c¸M­™©_{RÆ£eé\½1åf2åR;ÎZ©a®šf%‡Ã±Âb"âzÿºïÍ·9V¶ßé2#.¾¦¡#º@DE»ÎuBI‘@aàÞÔs‰¯#]Öhè P h“}”’‚•èuz1‘Uõç €tÍ®NŒzzOQ(‹¶Twqh÷Ä঴üšÄõRù` »pZPLÒ;Ì7CN]ƒ"ÌŽÍ€äü#6.s׫ZÃ’‰4ý½Ø'»õâΠшä^î‹le,Tm®t• ‘쉌SR™ 3› @±0îÃt¬y\)&Œ#.:b8ô“!ËK@õ´]NÍc4Ü‚Ó@rÖàš²e53Al2ȈbiA-’™µR‘rîšvÖºS%#6#¥ÍÜ ¶ói"2ÐÀ…°œõ­3Sd”ÂÅtvNI-"ÚS#.ó['£‘2ä`éRqf à˜d5eëRªŒ#.XsL%q®i¶L§¤n2éÍc*±<.Gâ (N¶çc²ž¥i¶m£¦ TéÂ&pL*ˆ[#%ŒXŒ*tv™‰‚mœÀåŽÈB $I”3¥/ObYÚD³»w\ñ#.»dÃ펋xå/ºï#.Ó‰\lÜêÎ#6eÊ47Uç3}Ç/™ÒÙlpNZÖ)£¢Ájx#u±¡öMXNRÎÒ¬˜³ˆÎ ÔÁ|`5Œ`pËè²3Pò†à‚f—8¬&„7PÐÖÆn°Öj–›;Ã'žCSž…™_¿&™ÓŒPiIÛ+]#%ODö®÷Â,zÜÎùèe̲¤9i 7R#6tÈðS0@e°usÇùM׉#6KÕ5B˜E ¥!UAI HX.Ëb004’ÚÏêÀóbŒ;àG±.NâˆÑÔGI–F1T*©ÒNï_»;QwW‹˜m3ŠoüòÊA8ÐTX$GaJt®Þ†Á˜`g£é®á™ N Ž¡}[aY¢¹I‡°êˆÒB5RÙÂ[#%†—l8Øqˆ+CŽûÈ¡¢#.F+2fisÄØÜ l³3Y†ÅÖ¸©Hˆu#.Íî‘„ÌI‘awmwš#6nò“aæqÁÊŒWÃ;‘ÂeU ô88JD61C#%CGfú1Ãg­Æ$˜±ÆNØÃɆµ#.fŠšåî äi ’q¥º£ Ã0é#. (à›™œ hLÉåJII4ƒdpjŠì•ËrÁ·i3fÕ̺ÇEÐÙ™9—Fm0²jJG8ÒgtÓ^Rg~V@.·uXiØl4I˜%ÝlK9Ã00¡Ü[Xn&uA#%Š0ŒIF ÄúÈ%#6¯qCÿ$R5Á@w³;ÀiSh¦²…Ê#┈1Aÿ6¾*?g€|zœ(† íW¹ *I"Hƒ#$d‚#%‚|jÇØ?´È3$䈌@q:áÔ:7H¤ýhuF¤ˆ@„&È4€¨±TC8Ì •î#%ƒ»-=Ùk<Œ÷˜º›~¸X$Ÿ*vʪL>ô ÷ó¼JÓhiÁ×/‰Ô'‰Ø³‡—7®R™­ÆM߬·YEr,;Bó®_}®P«4ó¸ëá›&A±´Æ\©s'M•.áiR¸k5†…ÜÖBh7„nÂ20+aÀ7èu%¶ eŽxn„X!ÑÕöõ#%c=¤×Àæ^çõµ’§4pu\0R ÈpBÿev#6h•"J¨”ÔËkñ¦[m&òRjµ*ÕᚈPÙŒR€² q¶˜X¡1H_§‡D‰˜#.o=#6“JAµµ‚ÍBp5㊡Œš\¡íö÷¬C$;×á8·SoWÇ4íUú‚*ˆ#%nùnR 6._¡S—WwUÙn»³.nºn¨-sd¯å¯5<¿Fîȵ¾*ä}î³ÓÁAáÖ<‰`P¡ý÷©¬ŽÙLv*Žôr†ð8«#6#%„†|ž(u/¼ˆqœz0g-ZŠ"½ (àŠ{̸°'ìò@7>™GÔ»ìo?6©2.œþ“0Vè<œŸ­²¾Ü?Ø|~ rÚ=¹—ÈFÙÙwE¹µÈM 44¨ÈÀ,ˆœ;Si#67‹ –P¼o„Æn F¿½µ°bhÐ1D)¦ ‹õªS@£¦n0—.Ö”™Ò.eÆùA2Ac¿´z¸äf$YÞЂQ#.cG÷±3þ!¬:?+xø…Ë–%êá¹A.‡¦9FòKPí?Vþ?_¶×äUi´&RkF±£Q[FÔIDL“`Í3-‚‹lXÅ|ÿZ0Pˆì´O€ïö¸ñÒ´TîRºÔ᳧zä™K¼º:ŽØk5vˆz€4DŸ+ZÂ"kÂÑ’ÄDŒFÓI0Ž#%¬D‘¨í #”E¶V-ÄÞƒ‡ âêxÝïf§PD`@Ë[΋Iß3_Ùk̹<æÑÛ ÕŠ~«Qb*H²j}šJ²a_x;wÙ$”œŽ›l[^•ÖAõ4ÑLÔdÂËp‘çbˆ7úN(ŠÒcKi"ÈF'¸„Üf5jå]*¾M½KÓoB5L­âÕÙ1‹fR–@l@ÓE`›Œ+K†³D#0ŽQÆR&lcƒ8®åB+ Ö×ËHŸyÂ˱žJEƒ†ˆ­HÐq ²@Q¶“Uú©oƒ#.ê;˜‚eK¾ü'Ÿô(ï‰éz–”#.s’ddÞí¨»‹¼è‘%Ï$¡ˆïÏë&ðÙ»#.†PÔ…HåïT¥Èõ¢®î°]Æc(i“°·Ej#.^&Ž²Ð†åZHšp0…“8‹ˆW‹‹ª.¢cc#%mÆ LN¨–L…uÈÍY^sŲñüÞaÅ#%ò=§w‘Kbm´’lc+A)aLDHÀ Æ-Ÿ°5Iq0]i>â k…@ /_¢Àmˆ!š*  I,Ëñ  ‡Ý›¼;âdç¬D©™_&,ŠúMŽ“õÊŠ6Á­ ý{ˆ8QÌ`o¨¹U¹¾NJÚ## ‘qöe]²gÞRšrE0;>Ç/¼Ú#Ú:í¦Æ>µîÅÇmcz"4ˆŠïU#.è§ 2†]õÈl0ÒjøáiiZµ}†køw 3†FC“®•zª‰¶i!KábÇgØÌ:„'5˜’bCèVmÓc{”^ZÖH8 “‡ ÉË¿+#%­Ó¾æÖZ ­øA”ËIÃ4Áùz¶^ym£Á¬ÂÉ'Gi±†êØá9E$åPš«%*‰ ÀUŒeYNÜBº1B)#6×6¦Ô9sypYµ.ݘUfylM‘¥ç—x¸‡kÈDÔëi¦lïÊ‹]ôŽ†ÁúÑ ¡C‡|ÐÁÅF–1#6hÊ7n®ÆbC#%ktE.J Ô˜DbTJ¨š Q$Ø£J#.L„@Àc¤œ#%-P$PRÄ[¼ t`luqlg¨LÍ #6Ð?Æ@  ®åB<5í Ÿ¨;+Æ>ER+ýÜÉŸz¹M8¤Qb{(=Gø+t Ãï ~L>±ÿWTŸª/‚¢ÒkºÓÏ ó<1G¦A¢|A]fsÕ10#!ëGšú™"‰‚d„’FHÅ"½†«bƨÚɶ¬ZÚM«Š„ "BCÒ÷©š5 Ï?=x•T\½KŸJåÈûŸ ó[ʾ§ôÀikE/³Šâk|t¶u+W…þ n.¬­…¨ê¬ÞÔrØr­$®E#.“{:iÃYÞ%lšeäʵS0:2¥KŠYk8ißjÓm&ìÙ&èd­ñv5-zÔbŠºSc³AÎ##6pšHÒ.™KÄӃƶð¤¶Ê>-ÌÞI†SŒ1d¼­ÔèËÍ<»SâKÛ¦3‡Û’$•Šiȉ3̸jMÙsy†ô@#DmãŽü¼Õ‹Á(ÑŒíju¾N&H†mŠr¦=fÎþW˜ÑÇàËNÔë‡5ÀÈå‡Ë­µÝÀÆi>ŠSæ)1jnÀI™Ž‘®$ÌÜC#:¨y>ÖÞ*¨i½ðyXÛz›Mp“\ÎJùš}e)œãï3Ã&=¾¢¸É&B°‚âðÇ"íž–7•5¬´ÝÎ+c¤ØàdÌrpQ© »M=íW0ƒ arr1Æ@Æõ«t¥XE€D‹LXÄ%‚ѵeÌôö²ÒBÌ´¼›Ë•ôÖkUã\§›©­ê^y^¦¶¼¼âƺÜÛiÖíª ,ŠÜÔsK¡B4ªä!_ÁšhR‡M¢†:—«#ÿRDõìÍ”¤Dkm^| œ•ÝÅUÚì›"hƒI°’1Ž<¥jÆY*dÂK èíƒ0š]#6»jí&°n•ÝuÝÅ&ñ\µëµÉ–§›®ó*òlLi[©­Ãc]¦£hÒT¼e?äÐÉH‘Ã…(¦Ñ³R¤me-¦U$…”™f±¶5¦¤S5ÔµÒ´²–™5¥™SUFŸ6¾^xMEª *f‹VЕlÒ„T„7–öy™¼túccQ”Õ©£â™`IPÀ€¡Q6››@‚—bQ‘Q‘P´X!º+çÚD='qí°z³ÄõŒ.‡kÓWéaKÞ9S@Ð×î6ýy‡†´Oƒ;‘Øngq  ¾ Oz³,§E™ÀËÂqhöëÏKS-Àº#%DŒFßÝOXÅ¥*jXÃɪÓm'ÓO#»;ÎLÀMÉÞ¦l1JœSí "[ÕKŒR¥U(ÑJ01!¤Uý¨¦ÐŠa•#%¨Çj#%¨)"ìØn¢¶Í¸—eµsÆòèéf´Í:ð²ÿfÂs©÷ pÝ#%H…5¦µ.Gf` #6 ò®µ2™ñÞ‰ eRD£T5ïÓ×ûûyk#.Žæñ#.ãÀÇ=AõÕê4\¸Twíé©Øm?2s¦O^ßn’õU „\ˆ«#%rãÑâuJÿo‘¢Æéˆ%òÅ€´ AS¶(†L!¯$yB—`w?6Èl„¨íx3ÝŸb…ÍÁ‡L8o\šårEœÎD¹S¤,`&Q¶7ühÄ´t{Î+Áoœn/1~»bµxÙ#%à.jßÞ¡ö ~Ɉ0Tˆ„ªþ—¯æƹ¾óíèlodð8YGçÙ|fQ¯ãûô0*mùÊ‚¨<„ h%µj6¢ùýÕ²Eâì(–’eQ‰’MÍmr,¡–‰¬“Þûµk“M¥c)m&Ê6-")¦Y,¦JUÅ,µ”gå]aM«)˜lÚ1&ÑFÚ¦µMÓŠ+"µ+ºéµ }í®Öí^Ý»*b(2#6em¥­)­IkjhÆ¥Wµó¹Ư~í’lŠ-cd¶¶ÙH›e­n]ihÒeRMKmçáM‰´Úl¡3#6š°ÛilÚMìê­KlcãnŠÍ–ó«¯;’R­2hk–êÍRW‚ë5x®Ø”ĪÂFÛÂk±ŠË[L7ô®ßõLUšq¼ó¾T•ÔÇŽîRÈ´­,£›^›?/Ëá>Ïw>Bmôs'`˜ÆèOeÅ3$˜SF¯<XqIƒFÎzŸTX@÷Äñ˜…ª’×æmãœ÷_NñW{<{1’MEœC=”#%.b¤3ª#‚E À‰$T B 6Ø£Ç^–S² ¤"-³JÙ¦Ú÷mªé¶±oƒ²³KÍÚå5so›º×”ª¢#6²QdFA­ôR*fSJl)«iJ)’‘Ô,¢…E’Ö–ÍFÌRÓ3ÚE E¥&©Kmi¶i›Y5°Òš)R›|Vå (“-EeQ-%£(ÓJHQ¡´Û)M"jLÑ°Ó1”b‚Äa£+eØÙ2IdhXÔš±j”ª#6Š”ÙR”ÉT•¢’© Y#jQ#.¨µ›M !BJLX)2“ ¦I¦J–j¦ØƨŠ²$mE,LÚ“$‹6Ö¥šÉ“F”’Ò›l²¶¤­Z÷Õk»VÒ²ÛM¤²Ô†’ÞòÚ×M›6µ)ª²V­¯f¯6{*¹­zÍV-¶R–ÁmFÕ¤J­*mm£ZúáÖÞcí³€^ù6g]!‡ n5Æx§“{á·ç5ª÷è`fþÆpb‚›Ézì7ùWkN-ÝðÞW ¸ˆˆîmE<³¤•!‘à#%¤yFäoDz¥ª$„>Xqå×⢵žåE÷Äô^éè†øì\$1gÁÚaž>Ž®ìk£g]7bÙ 3meÌz4º:¶š.L@ݤ¡áÈ"¥+´Ä»/šö7ñ_‡K3ü½©=Úßn9=½™C­#6?q°Å‘@P \cha!¼†„ )Ìæ]28ÐƘ‘›à¡ª ƒ(ÁW_@pÄ$•ûŽ -΄¸>õÒlÕsuæ°ÙåÈ)P;+ôø ž:ó‚lŽPù À‚.3ôу¿ŽI÷¾×‰ÅLFz%;8ã»m§f¸ñ¼õЫ´Ô¬ovÄÅ.šy‡Ëg ØPÝM±"Ê~¤}þ…b\åö2eÏàØ¥WCó|såÇéл\Lu#6“8TóT>µK‘ƒîªtJZ~ÙVêøPŸvÙÂíͳ ÏHdÖ°éè#ÅÄØÚ =õ øs«~O†Øõl£qGFù¬Öû#6¤Èƺj–µ¤7Ò6Wi0½«#._Èé}/[onÓ€‹ @òî#%#%‹lj¨Ê²N"Q@R+¥rÔ'8#6Èo(Õàž ‚"2! ÖDëê¥Ñxšíb¹Õu^¬U˜°‰=³¾_vóÒ…‘Xdé[ž&ya–&\T•CA¸œ!Š$Œ*¨c#.ª “SLÖ`O¼I.!&*x³VÏz§@â]7ù=ý«¨á×èv#.çWµ±ç]{|)LcÜšÃPó €fÛÊÖs²šD\OúÙÀ«–‚Îx…#6((S)øbî#úÛËR’dA] %Ù@ÿgìºùaZÞåe±F6ÐÊÊH‘F–Œæª&cD2LŒ#l#6Ðh{ÄC5U.mÈÁ¶“ ÄH)fáu'6ÑÆ¡‰†±ÅXƒVFA4^Z®¨ÆÎj v–©g6&  b S´·»RÅF[¢ÃK#6ª‘@Q®—I&#6–­ŠL*cð¨Râ‘¿J“T#.t#%6:^ôtqs•èÕ(ñQ €jÔÎQ§×hXÔz´A;Pƒl|߶¨4"Ñ JÔL©ª‰‹hJ¸XÌîÁò€›`‘8ŤMýàž5çñæk±—°÷§ŒÁÜæ" k;CXx†j/zø/#£Pì|…4ÉL`ZhÚ-AXAKëí­ÚÛ­[EìÔ×9b$>#%ý0¸ Ìèª=V.ná·kmÿ>sê ý¹gźÆS®i{HÔ·t0Jcˆ*ßÁ$çL¼•·ŸZ«ùß$!›Ã¾$ë”ÚwËIƒ;àtn”l øÇ-i¥!I¡#.njßRY²’VTÊÁ[õðð™l…c0¶õ«ìXlA‘4„(Fë¹ËÈ/n•÷Odçèʃô½teú‹&YùA16ºa2‚e8 ÔÁõQv!h+åËbêÅó¿r@Ã…/¥)Æ®)iQÒÆâO±¹9&íBø>ÿ|­€É­p©*ÚYKmj”i!´ƒ:I$š””žm­DVCCpš€P±Èc&P1èó€´âÚs½s¯gÙ™² .Û!›H #6áPªR3ÂxQȵßxÇ™Š¥ƒcQ¢Á:÷Ts‹ hiß5¶JÆKF¤„ÉÃ'3ÔGÃœyÍãsKMûé:Ÿ]û`‚ª‡§ ÃÂ2‰UKÕ•JâòÌ«ŒÏZ§N7~R±î(: @"¡ÌȡⶶQfíf´Ä‘ÒiDÓ´ThAŒ´~Œù~ÿNè>‡—èëTšPå›úÇQpº¢ˆóG‚Çšpó ¥€CaFE<Q#%,ˆ2ªœä°®Gíâ&±û¼á9Ťð;õ1A¾¼uŒÈŒhœ)¹¢`Ûk­„NÑD’ =ó§#.ùüq ¹ Ž[tcv¦¢â#.iÉÆLjÆD.Ã1ì˜ÆVí¡¶&q±"J5†Òë`̆/¸:ž”¯l’F#6¾”R¯OQz‹#%ñ=;NÝÖo+fy*wЙ¹çF|û Å"TX¥BSM4…DB —¥×*¯rïšJZÀeE–¤É@òrÎf§„PÜ\/èÆQø#%»¶’(tWx.ǨÚP QÕ‚¤BÉUN#("»ðX¨2#%›LâF›M`”'°fT(#.à2–pâ§.=ZpË×v3œ{×raY &&´Ú²4î¤G] :\„clç¹¾,u/Ëß xµî¹anGÇå¬$46×PŠÁDùkŠ‰’It¼îÂ2žv¹ÍÄÀŽ+­¹,Òçžu ñ<xϹ½eäŒmŽ "#%$lŒá­`Ü1R Œü{n×z¨¢3D¦ë2Ó™#.äÅž†êí¤¯dò“ê߆¹nŒh/‘!Ý­]ñžåîî¶ØnéŸ-ü:»+~A8uˆëÈN3‹5åŠP«Ã p¼Cmí$/R«4²¦ÆîÁ,í1=;ÓPp¥'#.¾>Œú UGLc0õâu{2=AÑ+½ëÜ('_¹HCX=˜DàmÆq–zEn€ÆÅ$ONÃ-Ž’¥¯Ë¡e¤šÈ#6¤ÍSY}Ÿ—ƒ“¹‚YîêúÑ#6I£åíª† ¸%Ý£HÐÍ?]‰$E­/]jôåå˼n¤ÛìnÈ™”HNõæ½Z½jé%»mj©RC¤Š@…àmQcš°^:wn) «—1KÙ)yš#6†­QŠÒ€4šlß·ºw7¥´D9Š;¢¾âênïÕ¢ìÑꙧëÛÄtN(Àºü5šîIåÖïãrBÌ©[æq‚_S¾®¦Rg¿s%_Æ—¥àM~%Db+)'æhwJM¬0…«ö5¯YFqtªÁÏé‚ "懲‚:Ê­•½Õ4Ñ´L¢‹Z®Q•M4šjW]6èrbK$ج[FßVäbÄVkãJ*¯KÒõ-kÆ嬳oßýþñ¶Éki¶[V½*ÝrttÊ7ĸeÀ ®h 'kåŠг)ôãK —]¬Îe´"„¶JJˆPÉB7&®r°„2ý(-„Øo)’€<(˜ û7 Îu¡¬²h%,3al!™â ¦±hv>¹3Ù]´l4‚„}»ÒX(¸ÐÞì½qOt”T5(¿åiÀ’¤(‰º#8+!iH@ÛaSòÕ*ŒµVzPT*éd“W˜fTp9°ÌPð5 ëÖàtïôQìꪅ¨…E#5ÝvÆÓ"£I´Ul­#%&°ÝíÝ+Èt00p‘²Fù`Åì&á—,Ü}C…pÀ×t,Våp¨CšA2 ¬ ÛÑo¢|¸¾ÀÞï'´Pg=b,‹q¤  B€=cik=A’~x#6gLüqöÿË€.×HšÂà\‹h]IÕíø1…Œ‘#%¨%扨&?¿dÒI¡ÏÊçÕ~¶ÔØ;Îáâ¨Ë#.ÃÀa a‘Qúùh€ŸFÍpA24‘» Mšjÿ-ŸF?=+áÿ‡ûRâl~C~˜œƒ$4Kç‚^ˆn«&߃Byï‹(íê`=4ñ…x(«C’~¿´ŽA@TTвÏa81#6êå{œ êÒB|<Luß»¤/6ìÙýžðéÙÔ;ÂŒûî„€pt6žøSTSbGz¿Cé¿Êw.;=#%žd#%RIí #6J¥*#6¡DYX@¨©PPfŽôlY‚!x¥EdBD “b[’Š3\0ù_a»ÌË÷¡ƒôq¸Á™+£b‚ÓIbÁNQ²Õ~ƒ«0ŽGqÍ9Ÿe±ìSQù~ý£Ëj± DŸPõ$›SÒžÔ£C N/P¼läV_Òþ íUAd‚:ƒ C—â[!Üak%ªE IH®^E¿Wcâ|s ¼R¨ÄÅgßêóåÅ„àX!`≫r'ÈàPë;OyÜbPWGçÄ>'¯—¯ï`Ãæy~6×Üß=a§,»rÞtï£ÔŸHÄT̉I9¨™¦¤¯V–¾T­ª‹SRÙÍTþ ËÁ˜I¦ƒúËúPцjˆ,TU#6©3û®ÜSE,#6@¼•I#6e,%Žì†€Ze¡Cð@ÃQ>9yñþ_›¼ï€Õ-’­6#ÆD“ca]‚ªñË<>ÙZcm¦É\ïg:F0¶ 0#BÌ £&D®¤ÍeµÄ±¡Gf–4'ÔF4ÆÒ„qI‰Žlb-ªÍå†LQ”X(‰‹J¶™*™Ef°‹-œH#.Ô’™a4J@fµ@¤ ±I2ÈPµ›„´Ù’œbƒb$]’ÌÐLïR`MS$’•‹ µZLibm-äB1ŒŒŒç+–=B`HVZM¨“F`L%¥ª¤mV>Lä±¼xa¶EXVÆ¢ÄÞª²¬ ÕÖAÉF¶áÃ4gJ’Z¢‹,j¨j¥0¤…'6Ñ´®è±\·6Årܾ#.ãc^»½.4<ºí¾-“¹1Šç4\3•¨lêApÌjÓ´KŒ&D`:3±ók0Ó,$i¾K†µbc‡,P¸yA®5&Ψ hÕQ.šHº5@€ák[R²°ðÕ5O<­#.ŽC:Q¨€6%Üe{îË]ƒúÓN×Ò!£ Ñ uP2Ëñ»h|€úim²O:“4MiãÁÖÆïØøÈxÕ,#>¸Òc!I¬ÆñÛmwºòeºÍ7YºÐ «áùqüô”Â1Ó ÍÍÓ+p8ñö¼þ#.áߣ .H(ô MTØØ?9ø Ñ 0€À«¢¯`1 «þ8÷‘A%Q#FååljT²j,ªê£TI’ñn’ª0±¬QrÕ]JƬQ¶ÜÝm’ÓDbd”@b -eAÔn*¬Z•$A$TɸúÓøC )È°íDÔ@QHXÄHE¤Ǽð?ÙÔX#.Gꂤ@ƒÆYÁáõ×g¿Â¬{S(±ƒk—Ùæf¹!¡çò«W²®]¦Ûnºë[ÎerZOºÂ¡.TP¤IËúìÀ&#ƒŒé¹‰ÍuÕñVã1 Ng°þ["ALàlBƒêŽPoÕTBD‘WáU‚š¼ZƒPø¯4²mdÛÎÝJT²«šæ·¯¥Vò̈¢Ù+Xl…TzšÅŠê»ª-zjö»×‘/]nË\Õ|š×šZƯ7uY‰…$¶¤Õ©3J’ö÷í¶ØšA¦@¨¢G¶2<㥩Xý|º!ÔÑò‘kk™E{”\±È5¨p¯Ê‘•E.I#%}3@¡„ ‘„Ë Á±“,€e rIcÇQ4×´Õ0NK2#.É#%½ Žçì”è¤0#q ÒòœMŽ›ÇnXq[‘l'µ}Ò@aš%Ç¿¯3Ëš‡74Â&iGÊ<Ø!DW‚bUTŠUðË›Â[åHŒ=TvU Äh¸¨Z7aq“P׈ñ0»¹aÌ°oËŽuÍ>¨½ ¥ECOˆbÈ€(¿ˆ|³”Mî®d=` T6qÓr.À;Q$!îa^ Ò¥#.-T””iKJTÕcj´×¼®ØÓUšk)êWQ´ŒE%D/å‰Ðñ9™‘çÙê°Ä@º5]°;c{nÑ÷µJ*ÎdBdñaöü’6µñ³'¤.ÿ¤ˆý¾v™íFqp£FC²êNº–3ìBKŸQé£4Ó]®“#6¤Ó‹‡ý#.ËÌg8¨¼±†bCÖ°¡ :ëŒÃPó¨êÎÉ&&42i•´PËZÇ0Û#%G{ª" M18=ɉÝGÜ`‡+hƒ}ñ¡È*M bð]þáñOC×ÝXd’I"BMçQýQ¹“‘ì<ÏÖônå¦F^Ϻú¹!}zæ±Ë`™ÉûdšWß#"‡ %54I7L0µš…°3'øþ|ˆ@å¹ÉY¶çA#6?KämɃ:Ž’®MœÓûlp(¡©ÐÌ“Ò¸×V¹ "ÄëˆÔ§VlÃ@,×j°nÅF•@ˆ$+û™RMŠ6SHDÀ˜B0 D¢¥Ø#P³N5X©Õ4O4D1öÍ—á=ü'§1ÀÁ5š5Ò¤”{Û:ðeQÃýi`êñ×K”ã;4Ï×ú´1$ÐÑеÊc£çF^Ó#Ž¼ô=¥ØΤú¡©“§A>iéTSíMZʸk2Pù|Tà€‘¨ðÛ‡’„‰Â5ˆ¶PajJL–i˜¶¦¶–¤ØÖŒŒe5ù¥tA&‰•2ÒÏØjܶ¦e©²-,Í`•³LKlÛl­6³Lµ™m–$µ‹J"©µ3f«4M5M±UQli$’1H³$#.GäiGåL"±£æÙ8î¸l–Ɖš¸Èéáü)D M´VµÔåV¢ÚÅ®¦µ®kvo^aQƒoѴצÕû»ËA'"vC¿¯†€œ#%#%ÛFÈD*TK[›R]Kí´·µUøˆm~B ²#ùKÐ=Q_fÚÈH†EQ ÆGúZ#>Õ†–aõ­óØø„á¼ådzApQÙ¸»h¥âB9¨!áOGŠk§!€' %JŠ¾ØÞ!¡ìNHlá`G´>ÒB ‰zAm¨#%û¢Ü‚%íKtŠD©ìByìçx‹Æqä;ÏÓ×8Ï<Iµ#% CÁÕPÌÅŒ¼Ð“yzÔ´àµ!¶7$¹=ˆÉ6Ý*a x—2š^³30xã—è—pþ#6XXPµÈ…p>W·ÑëU8;€¡¤g‘ê&L‘x‹Ñ;ˆQÄ7V«ZÖ‹œKš“è6˜x#6ÅéØ¡›¼ƒ¸€p[1v›*®š@âƒ7̨‡·Ý£Bò8#. Ò\Ð;Ÿ¤:háŸÜz°žp!¬a#.DV„††‰#„mbL¿ê¯(Ó4tì!#6°I£±…Oú;N¦Ï”Rbå.¡÷Ã-+?£VF0¥VªêãìŠ~}мáö™mFÂiîAÁÆQ:ïÄ•ƒ#%>°Ñ#&’!ƒ¦-ŒcöðtL”‚‰!/Ù¯9ãvþWà¼è¥æÔö€ršÍù[ë 9+ÁS:~“r˜âúýt&ø-tœõw–¼¤¦åÆåÒñL ,˜SÝðð4Ç]«Pø¦äº‘C#6(,H„QX€ŒPí‹‚ÇéÓ9À“óx§諒#%2O‡ùæ&ѬH›€ø8Õ–Y!D1ž4P)#.h¤4–ÉÓL*vƒ#6HÛLMûþ¯ŸéIJB#.™žüX11¾"lN9anTG§d#6숒6«Æ%Œ­  `–6‰ØÄ´wc&µi︷l¼C#.@6dÏZ«Hfh…ãëU)%E`Ò‰„c JO´'ñTŠ¨â*cm¬hæF‰&(}q#%çDBŸ ;s­R; SÎTyzg?~—<s/T– œ#.\/lëã:Þo\™qŠFˆ•(P¹¨µî€„âð8' ¬J‰Œô*Æ›‹›%S ×õ-ªl;w£DÑ@äàPƒ@4ÆB ˆ‚¤PEÐBÈ”ÅPe'‚Î#6zx¨†vi=>~¼® ­QÐu€}Þ&`†]|Ñ÷N^@ÃÕUBÅf€ÔUЪ`EZó}MI¢#ðGðGù$=è‘ŽÞ;}%¡&Zi‚Á]ä4¸¸fÇfÙ«EC–n¢ÉBvP§`¾Cqh—„Š\5&+&ÉWsLŠjFà|Ûùfwâ{àrTPœÙ#6`R#.Õ!æÊ%•‰r)(#6a<,<Ï[Þ‰Kž:MåÊd¤.óÏ!Y;0•UQT»¸Y“™¡õñ²‹"‡½®$D0øBR0Š!ÔÜD½.Øbo’ìÀ#Š]œÌš T`Áѱ†É"žˆ.)¢†èÉd­rf5Âó"^¬ ž¿îi‰hˆžòœŠÝÝ%|Á#6‡ÓeOuB¥´àÈ©PÚW.T²¹[#.½i?†îÍL#%Ez¤#6©36¶~ôâüÞ*Ž”(›Âý4Ƕ¿Í»CVnÙ=è¾»VâHfmÚ¨·aYeÖ” je 5VWE·[Ñ$F¥”¤ŽÂB°ƒNÄy3ó, /¼ŒXŸ¼™½p †8¬§gdpŒ¨:©´>j"ìO=H£EëeäÎh ïË‹“§ÊgؤHÞ’–%?^uÝF§·ÙéV§mTS±ÄºÕ+nj„ÜBðôÕ#6´×9Íâ„BröÓ,à¾81rÂsÚU:âæìbúékÿgÖË:¼µÜ‹|ÖƲ:Š#6K%$—,è`˜#%*¨#%A DP€ÐÈŠÒ¥h€#ͨ¨I n1€l+p<;ŠTÈ)í ˜©Ù¿Ó:þ°UyónêïæÔe³#6ÔeëÇ·VÜÞQöº1ÈßÌÕ×ë2Q#%dd!C‹ÇBŽbuðE!¤G³v#6Ð($«U‰`ѽ­„ÀsƒqKòƒÙï Ñ£šÄv\5Ê ÎÐHÒž%A#%‚‰Î|É:LòOB§¯ÆSÌ‚N¦#64 / LdM´¼íÍr¼ó¦ëÏ5wŽÚëm¦ÖJ­I¶ÒËÇ’›BHTˆƒ*‰F2ÀD#6 üÿ_¸÷J(-ƒ!þÚnêÖwÁrPl ´ W1¢Šª`Gñúnî¿«Âb£ Œb´Fϸ•­:Ä6Ö4kR&'Ó&{‹_¿´)·w¶õÚV7t\-2á˜Ö5bB<* ˆ\K‰lH2Ym@¡&©€ª"Ó-PÒËf{½•nçˆØé¦{ÚG.®ÊÅz¢ç±¦&#.œ¼zPšÕ1`W¨¬PÙ²‡2¸ÛO!-Ý0éà›È”fÙF¢D$ #fKÀ0‡#.`—ömZhmmèØ•áͳŒX"bͲ9Pí( ¼X†m–ÌG0Õ.à£MË쀿˜â§iÅLüI!1²Éb;EP DüÀSqS¿•c²vwñÄä'Ñ祥ÎUñ<þÞzªßòNu ؈›'úû¤ÓIŒáÂfÊbWGq'NÄöP”!×#%ÄI¾åmøÛ^ef’Êd­‰–’ÒjØÕ)­}ʾÖÚL®»U?OÇ—ŒkÆÛ•Êæ°µ0}½ÃÞ^“xÇëaÎ6<îXJ.mèÚ¦Òv¨"«Om¬Õ¨}Pr)N=\w†žü²Ëˆ«Ÿ(ײ|J+±±òT-ªä`Uí èvÙïIðƒ{¨óÎ!WžâÛoUx$˜§áUýÇêYÐYy¾áa©<–·ØÍn6˜I6=Î q.ê`˜Â÷ývÇ]tú¦øo¹ƒ +œM`sZõQ4$n@âtÂpÅ&/m…ÇãîHF1מwë߸Æ,bÅè¬ÞBðì&Ù3‘°kŒÕ&˜†72^A#% •·4ß¹çLÌÑL´K3Jj í>E/Œ»ÛêÛçÌÚA!~ï,v7ÞÛ¯é#sµzø%ªì2t‹·‘ƶ€BʵŽ -úð*¨ð ¹FÇò,¬ðç" sž^ÓÅC^´GÌñUGÝ‚!p6m=ßmÃÝŒ#6NÓ0¨^b!”d%]±‚-Ê.ÛJZÊ0O ¾^!‰¡ÖÂ0ןbí‹-s¦µy䃉Õe‹´SZ·°òüX›K¾fsMÖï%ç8Tb»üè\²?}{Þ>%ËÂça‹8ð‰øqgêd;1Cð™ )æÁ… €ªT'çd%íBnpŸêá·>Ž¡˜¼»f>¥ó‰èÄyKp#ÃææG³´ÕsmGyç…cQ®»·¦Ûš%¶Ì\U#.¡$‰HH0‘Q·™KAŠCo·UïУç»Rˆƒ*·µsà$.?çû‹õà¶Kª bÓ\µ…‚ň„aÇY2J2ÄTX’ (·VyÛ¹ÖîuÞªZÛ¦Û#.1A¼A(ˆ‰ð ¬ ^3@¤dâÑnÀÕÁ£ò—ðt£àyžˆ“Åù¿r=‚÷‹·EI&þŸö01ü›„$Õ¥³º[›Ðö"û·'Ã4Š|·låËMÓGÎòçQÝOg”C_˜ù´4èÓwA0Cf±eÂEÃpÑIå¨GGè ö’à$ëªU˜èt§ÝÅÈòZ­ˆ¿0óhäk>1"°Õ—,”W%ëFþä;ùq56z{þÎ@ @qôÙ(”yCScpô_{J!0£:tª4‘“™QZ$†¸ê×\×v庺ͩ¦¬¦ÛNݵu&ÄÚÛ4¶«ªØÑ\¦^.^]ÚVòµ¾l²„@ Å#%C@US_B„Ä«ËÚ”­5ê)ALjºr·S9¬¹† 5öTX“lzï€ÀÊM‡†u>#.3ïk¤ÁÖøûí#ÂÈÉ j÷â ¿«G„í#. Ã0¾Ãn”º·JYUiKí€:ÍËažÛp¶<h6Ú`g5!‘5‘È0°ØzùpÕŒH¥²”!ÍíÀuþÁë¾pS-VÞM¹”dmÐL¯Ü†á¸6uð|#.ÊäÊÉÜ @à{&ÿ}v›uˆˆÄE'™sj>¦þZ2nÇË׬-å®Wߢæ #%ß`+Âގɬí'm'­·õ#.|¹_úuØÑTF#6Ì5å*oµµX~ÍóÂ\èÄgi[á4Gëk®“]ä9*éitQ™0#6ñ'êwÉúgúÿ§îoÛ`§ñ!(RÄæY} _’bû.“»áÁ5Å(,D‡ÙéPºRð¹ßSX§%DAÀµ¶&Œ‚¤?3Y{ÀZ$*Áöm™˜k_ªèzÔ.¹#mäÐl¨AAì[œv,€aÈ$ºvÀ¥‚+ã'C‡-®H4(an~#.ÐÅ¡´ÁëX> 7¼Pˆ“Å£Ÿ›ã“Fs¼ðE’Ó£pè&¦‘—K¨€ü~iÿKn<£ª{6±€g™TSE_çõð¾ÌÏžRI'EÔ,Ìï‰#.™–ümQºC^[ò†#6Èì<¶’èÝPi¥ÀûqL…ñ =Lo³#%{BhÙë Ò¬Ü¿¶lÁs-†…¯¢jgÎ0‰‡€xAËÓ¥¶ßgLeXd¬b‘…û`Y)0(ÏQ3‡I!…áô®ÔùašÃŒý¡ø̽™:ТIÅÔeÁÙ)ýÕ‚Ô<=±®’áÐÁá†c),·[I _òÀ3r^”B8iý5³V-êWBÆCGµ÷cHgM‰ardæ7ÝÍO1U;žæf,Ý1!„Èk q.‚ìaì£P0ëO¯võѤÈg õá§NJ±ÐèO5Ĩw¿€y ‡ˆr²þÊçõm|µåê#%ĨƒÂ#%p+ÓUC'‘ÕG Ìñä¦j&¨ª™ßË– ¡!ÞÀ[©#%¦B‡½‘OE«¨¶Ö+Q´Z5mdÆ©5£Y,mb¶ @¢²*TEMŒ2…Èì5W‡Ùn—¹\ê4—"Ô–* AR‡æÀá¹Uz,.ã`!i ÅòìÚp:6ßvýøg7nYÁÞúËÝ;«Üd(]Š)5«=:™šC÷«‚Œ7ã„-\õûn}ªcâÀuÓWCÈ2ùmFm’ CEli î°2d–S ~Õê(‡åíOyÖ!¤ØwF¿?´"„Œ5jùíÌ<ÁCi²”R‘ÚPl.@ä#6ê%ºéçÓ5„ÊU¨ü=¹Ñ®%O™×?#6\¢p`v㋜NJɆ@;$™Ðs]‹_2B‘ªPôÐöVâ¢ßÌ0`Cm¶¬×7¶Ú9FD‡·7|Y´&©ªVÍ,[þ}¯]tÁ(L:’"W. {Ž§¶@,ϬGüÛo­ÖÍ ½”(TÐJÀÕs’ª¤ ó £äI/#6½y³ªîfx²&qK¨*.‘qß<¡ˆ¥¥LÒ®%–Å#6eq}Kw4(#ÔæH€ Ÿò*EZ#66°Ö‰Ÿ-5¸éW¾`[É@DêƒFq#.በ†ÌªMZ¡Pwgåõ{ö²Ÿ7>Gcúìcšò0Ä5™µÕÔâ»×võo1“kpÄ&Á|ÙôO«Y…ñ–¦ãQè‡î>Ž#.zÞ“®ùŇ'èkj—©a¨‰„²ÐÈTʥȔläÏ$Í íB…mÊ=·e×àšY bø¿]ع`±#%˜ ª£‹.êLãžÅÓ?ɧtÐnòu#%žNÐê„Ø€L8»1²üÁð¢zÏpÝ#.ž½…Ôêôn•‘êÌÇʪKq·t/Bª+5Ö˜kYfŒ…2ÉUBÆÐÀù¾Cˆ®YIô ÙMIÀǵÜ„$“Á¨˜Ø:¤Â*š›ËuÖηL«w]¥#jff±PÑh×]uôÕ<®óõ§½æèQfðÀÆÀnBhT¬ci0xÈ`hpTZÔJÄ0`Ä28Ü«hãÕJ!ÁÑ­S£‘ ÉLkLD²üaÐ6™Þ(Ú߃pHìÃWÖ™û%°Dûo!H¤HI¬j¬¦ºUÍôMÌ‹6J´¥\é23Í×Y2¤ …r»ˆàd­¨Å ¢‹Š#6&†Û¤ 3PE Ðî÷ZH ¨ÌÝZ@Œë ²Ä0ÂíV›52‘ ¥l¥f†Sµ$‘e6›,Û3$ÈÛe-/]Û˵riÝË›rAfë®[sNݽåÍy¼ï"h¡RÔ3–ùýw³Ù› šf¡å輸÷íï¼öÕæ"¢FJ°ë¦2†–šÙˆ¬ˆËAÁ\µh@Ò4EÙcÄôõf1ºÔ6î“#.j*Ócq¡¨ÑÈ0À×ÙUkH°[yÐ÷´IòãÁ¤óßQÆM¦Òp&äbÓ\3 PÍÖ†úÀ4Õ½1ΙóEåêÞnÕŠ ­zÚéEF¾ÊËIµð[¤ö#6<qêšTU¶ÓuÛ5!(£ƒdbå´ ±#k7 ‘+®6½ª\®¦øµ]ï™\†÷¾’¥Y•â²"°£i1 yŒ ic$I˜Ùi%ehCÑV‚Žc"sz(ˆ¤ˆÝT‚&0ÝÜD1§~/êˆÔaáˆenÃ)±²á™i©Ä3aCwc®<¥j[J\Mb0s.#.ç¶ÖÆÖ¤2Eõ5Ÿß`i’l(A£§‡£‰G½„MÙ·¥2AžTÍn¦©Üž­]ÏDO•µ3JiAÓ«Ê_¬­Ý»8Ÿ+ɳskhÁÇ gjF— V4Ò.#%ÖcKlÆöõT•MžX…,A(SæX‘ôšçzGí ºó2Ìÿ#FœÜ4ĺf#.„ndÇÕ|PÇí÷k#zLö1Âf˜Ý—Öd6róo?v:WGÆŽ.Š5Fcmäå…tÈWÓÇ°…š1¯‰ÝqƒÆŒM!j´•M¶Vo‰¥Øz¦øã5 ¤§d†„˜vÎUjhÙµ†•#.\"ª9lT¡µ=³C#-‹ÃqÓéîó}&°Æ0#.7B¬¡û÷ Bì¤]”¬‹ˆ&Áê$åÜ,eeU»cõ ¹¸GãO8a¤)¡ ä¥P4/—–ººëtÖ›{¯5E]5fTŠƒ@Â& Â6: é `"0lŒU,P]§DÂQ %Ħÿ¸ „tàtë=A×n¸Ø nìÁìüô³,8¤è×æ¤EþÕiqu#%­T#.Pæûqb¾e4~¹(ÄHægÞ·›fŽ='³¦ÓO¾ï×q6ø¿yÃ&ÒVý?²ç‹ª+lMHŸÜúõCÔÞ±\öÍ#.hÅ#3ÚÚmáîÝÔÉK#{{ºNÅËÖåyºjð=šy½\ÖM\l¹Ù‘K*̵ðòJpð÷uÐôÐïÛjØÍ-¿kÙ»£qFb!$‡-ÌÌÛ“¨Ù¼è®¹ãÌÛ„˜F6qÇ8hPhÂ,CöºÂ7ÌÕãµCdÌqÎám#±•M¥„ÙBº¯¹yÐà½ëš|÷3¿y‚!È7稺(4Q®ÛX0“wù¾ƒ,h#6?äè‰!“‰¸\£Êo/£÷õgg^i¹ã‚íÓj#.M,=6ͪ뚷ê³xFpSÑ4¦ì¨‰–€FÛ,„š~¦JôÖ±ÝrðÓ+°iá³Ýdý_>÷Ž„ÖéM¨#6kf©l#6Ì놡Å)”êýáˆ{Á„=óùœû£‚{ vÜé „R $T@›‡Ð# *Ì…°´%Ôl–#%ú H‚š›*GåB XtL„,³:jU(älŠFÒªŽ‚® Õ`]›[†ÖéµIµwŽ‹b×r髵·6îÜÈrêñµðË%‹H´ŒT©«œwUÝÚ±Z•¦¾+Uã[É[Iº@•%rØ¡#6BKI€`‡šW(0ú6nün°££a‘I}ø«Jì¹X½9*–Šae(¡Q#P­1EŠZÀôÉŒ.p»Rշӛ͗Æ×ZïrÐÕâ¢%&±’X‚Ñ %¨OsË}2†±P”BTBˆe„EªR–¤_ÂëQM”ZÅf&i¥E±¶5™ŠE¨¶6³+Ó5I²Y”LɉTÑDmLɲlklÕçjº7ैŠ9Ÿ? ÉïÐÐ>ߺDE‰OŸjÚóÔMª+ RRËð9ì¿âŸ‹ÆýÝ^lx­êþeËŸÝåEÓ¸°zÃÅ8ÓÕÝóa‡T9—ùhìž !¥ŠÖüíM6´”ÊѾ‡7ѽ]Bͨûf·äV·#6µâ×OÜêê F™æî¢6Ó»³®µÝ¤¥­¹ŠŠ«Knk—5]¥+ZV²½vìÖj(ÄJE¦®ÏøHf;ÙYÈŒI–b2_l(†‚Ñ$˜‚D@ Ä:o',À’ÕPBMˆÈ4p#6¡a#.§gW¶¤¡Pm+œÄ4ÚÁ €™—Nκk‘fñÖ¶!4ž€õ…>ûµ>ÖùñkÑ6wć– äÁí0Ϻ¼è¨…Ì]hʈ°Ÿ,Ó`ûO]Œ‰ÿ3]¸™·7µLÌf7?,lc]Ì“MBbAŒ1H¬µ4Ýi†d8w&ßNÓ)Ä”v»Æ#.Áàu?²æñçãµì! 5éá1°<¦ˆt2b²tÙðVþ²ðæ8ÀU;¬"Ç‘¶C’}åtÈŽÏë“|w½µ^]™3aÌ8fùã—=ù6´ì§Í„%µ ~¹åšÑ¶»zÊW\=ÙXÛÔZh¸Ïa†°Ó(±Æ™må÷<H°UýÒvЬbE“È#6NÁÛ±=ôL~+"‡õ†È„eÛ)·‚È~ÏðV<0T‡&*ž“2ÊuJÚ©X¡à†÷BxQGš`…×Øhœ Ÿ-“T–­ŠÑªKo•/©ZºˆB2$"cü„\¯IÛ\â£rh¶·’{:²Ulm¨Ú¹Uˆ#Ÿ”U#%Ÿiy?Ò3vµÇ‡$eù»#%ˆb Zü¬ßàTy„/o\½Õ.@Úº[%´×]§·-lfmBÑl@Eá1Á¢#DBùRÈ8$j©À\0z¦Fü¡€¸ á[oZµ¾‚[[fIŒl•m\ÓX¾>Ù*¼î«ˆžË¼UÚDFÒæ»É×>µž¹éw“W–Ù’€“€œãKÁhã*7j*”™A#6´¶³V¥(1Ä*ÛAÍXU¬!R#. aS#6$D•Q#6#6ŠÆ,ÊŽXI˜…vU‡ï€DdЄÂ%¢¤€äÄã0õ‡ázâhÌ RƒLcAA‡¬–T#.9?³¿Ïïç*®·€(øhyÒ]aöàŸsÖÐa`ªsêpÄ%X¬gÕ™3nš#.Ç#.³ÈÂêH9F1˜ÖKQ·nžíùîÑ™ÛÌ mµ¶¾©kU­~[Â` #.Ä;@¸ˆâ@HŠnüÝ¡ã¦Qð!¼*‰ï'æ£[£Cõmþè¡/C ;¡ø&©“òƽq‡j¯ù#%AD^G9ŸÐÀÓz*‘J#.Š¬¤,XcÍ#6ŽÇ+Ìf¡„zœ#%fθåC8>yóvoÖ¢ŽJ¹ÛïFØØé”8ꦈbCDÐ#%ÅEœ7áÃGÐ*‰¨á%ÎÞÇ ¢tºÄ÷Å4Èn#6½¡1*ÃM63âqMí€!œý!ȵ¥ð¨W¬|TaøµX#»# hLCZ¢«xbi(6³‚Š±tŠ\°èYl…ˆEX#%$™—p‰4b9&K“`È°y~5D¼_¼ #%¡ôˆ^3Äê;HÓ× XïYU[ï"zGEM“pùC›Ý‘¶Íêц|J§NqÂIß.k6#.éï?XC߶ óð&‚©UMÛWJ(Õ]Ûeº»±š¿ºÄB¬€%Ÿò\;é(B¢ÔÈîLgÇõÙüÞ­ôÛWÎôNÝWu\v¶Âegd!Q‹ò –Bt?g> YA h7vgyÝ\º2嫆”uÝâòœÜËÍÅfË"i$l&Ê–£ÆÛ´Û&‹S60xÜMæÜ®UÍÜw^vo.Üu×l£ •ÝÛ¤W/ñäª9¦Ëy<Ëurîk2Ƈnój•hÈó¶Úæå®›VKhÔ¥ŠÎ·)³,›'ŠÝÝݧ5wft×$RÑœáÊ»eγQhÑÊÔX¥"µDh‚´m› ¹¸#6XTbzà„vA؉CH#%ömõoÊ´;TÄ< 8#%îGû;ƒ¾Š #P„ ¡A;X‰alExýª*ŠoN™¨xþÉÂ{S´pAÀúJA¸!áE ±#%ã›êwŒ'¬+Ó}†Ïu¯‰¥hÂ%öz3…#6ËïÜŸ›´æm:Ïr#%r"1Œ’ ”ÌÒùíêæÛVükZæ+kº¬ÿ¤¹gú£ #6Æ#%™Îžî6S!5²jÅjÐ¥¡–·½5{Ì|‹1À P÷€A!µ%AbÄB’VçjœuÝÔ²±c#.ݵêZðc^úªli¤#. ÷„5w!,8‘*àN߉D l&’PƒtI¨ÅiéÓ&Iš#.ˆ½Ö­|ý·\­{úµ¯UÑæÝÛ½(ÒDH"^‚b#6#.)k‹`‰E•Tb¤"M(0'ñn\ÄÈ)OŠŠVuç#6'÷ð=•øØ 1†„HA„cŠQ Aû†j=•€Ôáa­3‹¼D#%;" T€ÒÍ–ÖiµJ¢¶•fËMô¡ˆ!·/¤¸Ž‡Y@sÑV2#Í©ZdÍQ¶Ú…­DüÏ&1‰½#.@ §Ë†HH"Zf0šÚÿmFkB¦km…ˆ7€ÉLÍv¦vÓÆk‹±_T,£T ÂÊ$6#%þDûOPp%*¼CrW¿ªúÖó6óô÷}w~›¢|—ïd`ŸB2¿˜¦gËñꟙ´Cå#%a‚aÌHôÿ¹Xˆ4)RkU‘»M¶ ÈfÈOÆÑv`iñÎŒlD‘HI'2's¤Ñ’õ¡²yˆ?Àáýb€@cÈoSÑ5·câX¢^ü-ëpqû˜ZÇ#%‰ÄÄ#.IÇY%¢ˆiQ8ð$MÊ$4ªB£ŠQ!.pK#.dÔþÕâòºmsWI*å^ï&Å­Ä*6VjBTBW@#6Ö#.c0LK½àŒ1†åËBá-$‹ok#%´Â "JM!hKEÌ©#~NÍŠªð»/8Áo:¶f¾$öû^à쪰pÐÖ„ 331(Ä`m¥ÁØx]âO,’½).àdË0L\Ò² u"Áb…0«‹ÔÁh BX’T°€²+"¢7*© H4uÍ’it52LòêUÖRnݸ6 hüt#ùÓw2¢H×`ôÂò*ù{Ntp~m£fE…DAÙ‰`=aPP¬&i»PE±*Ð#6E#KT-ѺZÆž^Šv0ù lûa£ï€ƒÄ‘Œ$µb ÉS¯t·\ªvݪ–Ê­š[§.ê¾»mµõR¶¿’¶×ºØ+ìŠf‘MÄ”± f#.‘¨-RHE9À³­‰ŠÐÂѹÑxOd#.Ášyþg2Å>?åDÚñç—ì[Ýa3FÖAÊR±1m6¥ÒqŸÏ#.¾r¯©†×PäqŸPì†4`!‘ÄF˜ÁDm6›¬‰¶òÉ’c‚„$‘¦ÖcÁ¨ñ©˜ÛvHEJèGŒ+ú2LÌRØ&Ç.-S9;7¶£´3P–—´¹”-2µ‹–š}¦Ó’òxPëצè!àDûšOË[ˆm4;CìAÓA?{@~¨ìFêøʈ;gèª#. #°êÞ#6&àÍdT#%¤‚‚O]#.0UXû%H€ð+Qè¨÷#6'dÑg"æ”Ø;¾#%âßh§#%–6‡ÕáüÓ†#.š¶ö!þ]ßzLÉ¿¿¯¥$уHX*fO‚'Æ"°›[>PAÍ#%GÉQMAû={”}ᧃvY¬¯ÑõÖ)Ñu©A_°d FÛϲ½¿†Šc[Ü%•—oEÓ,y† ÷¢ˆJ2&óo†tÜÖ‚;¤¼6^o´uç‚A›(77ü“)– µ¶÷Y…ÄLx‹l+Ûš¨SÃù5ä4wòò$;8SV›âT:—<­‘t(ñH˜Èª†',ð³™RÚ€ÒâRgpo¤ˆŒÂÉ2"ZBÇË0/‚x†ã~æŸÇTCîûÖþIõ‚h&"´#6bƒéC;¾¯3Ž¯«ÚÉÖÙ’>úWš‰ûc~Ó ÊqÖ'lcØÆöœ·0´U>’L#»²8US›3H—¸¨wâ® B?‹ï‡kzþˆú&)¸E–m[p•´!oqÎJЗYÅ{™ÙL7£L€ÁA€Œ¸¦ÓŽ V0>²BG.­#ÄDDJª® )æŒM+I’¦Ík¥¦=i4b¦—sìž ‹'4ƒ³>é}2ksá²HÎó|DaXéw‡kÎú}e°&f´R‹˜ƒÚyiÚΔóKÛù=iæÙ° y/âu+‰`Çxö$ÝÀû0}]‚ÐJ3ñdáË-^Ýø϶Û9@Ѽ#.3]7üºÊê@óÀ¶ñXTÕ˜¬Fôõ{¹Œë²›Q…2fqLd–¦ñ!»o³!‡ ÄtÞ8<µ†çÂôÈ6c8Cg¥B~óQMãøÙKºŒËù®ÇC‰mª¹"9n¨¢ H´<J•#Öر–#%׋1Mn ¥ÊéÝÔxaöŸOÚŸÖNÿuQá´ÿb¨Ìñz` œ74*¨9‡N¾›tÌ Sõ–z¡Ö…Ë8 zJãì;EøxW´‘±3Cd¦””6JöˆHŒ å¼Òa¯`&C! íŸó<µ,]I˜ZðÇá€Òß:}P#6f27¢– T‘Í6Eún§m¯FØØ™áŽOu—m­×êoÙœºSV#6ÌÔýìÞ×éÀÔ’¶I&³#ø}3 lýCÖå¿YãšµÏ]Ú6·©d·‹j ­sÝ…M)gV\ÕÛÒÕÞ,¯ßë'õ‰r8ÃÒPòC5…U4v!yc:Îpôk¼¶••ÿd^קÃD~„ »÷??[TôšðŽE¶»š’wª•;ÀkŽ½WÆÎÊ]!A†ùp£Îrm°d+Û #L`„Œ± ´| P Hšƹ†ç—_®gXo,›¹iž¹ªË\ XyËú)ò¦á4·†§`ž8ÏkÆïgBŽC¹HÎçÕåNw3‰Uv0/ÏnñÑ8ª¡3'»=·„1Mw™É™1‚Ù}ûÒ2xÃER…¦§‘±PL¿@ã×¢LQRI5=û¼x_ZC‡#.eÒG§ï‘üØke(¤ÞàÒ„ F@½Ù¹€ŸuôWn qÈËÙþ/£ò色§Ù(oÒ`Ä‚H¿I/#% $;ÁðH|‘ë ü!ô€ÿ8¬¾$DYDˆ‡vâõtŠŸFþX*G²»ÏÅ^“Ò™ ÌÝ@E8*mì81P}°‡f>ÃÞ²âôž“s#.Anã"Ý3-½GCÔtä.9#¿å¼:öŸ´Ô>‰ÄÇpwÇÉÄP +m&™#%f¶FÖ´ÑÛd92hÅŸ$ÑEy#6,:@ä¡í’(:xÔw‡u#6Š©6Ï쯟T®Î®£Æ>ÞHˆ{:¨óÚrz¨öY5zº¹[­(úù³¨å•gõ 8Àu¢ÚO…±Ú¨¦ ¢0ôV#% FB$š‡¯°Þå<jØÀOfçDšŽ¡Ð+ö8ÍÆ#%àA)hBÀÆ+ Ä®qk_g?ë?¯à̬¤‹ÝOÞS/P@JFÒQ?µ•ªù±À2:‡ß·gˆdªH5R#£Ê‡ˆ;ãM羄Á#%óë‚x N_¼n1¬ÙlX+¤Ž g¹·ù_•4k3>÷Vgl6éÎ×Ó©ñCqš×ÐEúW9³‘v@ÓѨa´Nø¥7 ì⇲ñvVù¦…é ì€?ÔTÆ'»Ÿe¿Ü»Ù;}`£9úì=§„¦ò•…A+©/þnw‹®B’v>û¶çU;QUΆ¤”RtÐ:`#A‹'BÑ1Œ«¾ùa+Ð1 MpAH!ŸþO«mmCPç¢6îÐ*Á#ñÈr=ñ?£?ùûKÑTHØ?ÚáUŠ¦7ÊIÈ—Û—X”ÇrxiÞ7•ÎåÆš˜:tà€^³Í£Á!žKòó©,8AüÏzßóÄw(I ìäh·ÑQúöùw,ù.™1øµZw®íb!&t4!Ç.ü–ú2EŽ”)OkR¶Ä4³¬?eBF1³öeÑ$í8xnÕnùx-ˆPúÉŒé^û,ÇãXFKP­Í<œÇ+•wOÞ×j¤Ñ¬E¶Ëf³MRÍbZRµMR(gfí–sʵ$‘Èu.kOU;ÏïHÝEUõÔ«@È¥~Ïíýש?£ú~dm”eI£0h šm‰¨ŠLBLh”b#M*FJ&…!&Í“a#6ˆ±Šü$åÅögŒxɳ'€«:û“7Ü›o'6úÝyŸ”R’M›vÌ ÜÆ0Øh4MÌÝ4¢¤aKæÞ7t¶zh%*éMð·åÆ^&y}Gå1ÑÔ¦׃M6`¥·m—8™@¨…{*E#R«5sQƒí‹“øø˜ÞŽ¨Ã&D ¡+fCÐÄÆÛxÎ&cz\/Ó†À[]4šªœ„>n¨¤ôÜŸh#.ØL“ ïÖ9obkMNËÒuÕ.Âjõ!2̺ju^ëØÖ8ì3ŸÛë¨fèÎ[ŽG —ç_¸3ýO8†ž°Õ Úm𦶰9ãT’/è„ ™á­ž3°ûç™#6ƒRi¾m#%F÷"¢Ö[©Œ”¥m¦œiG#%0NPÈ©-D‚„#61u2H€âˆë—QUË;®JnVÝ®ésuu®ÈïkÎòYšÛ‰×·‰7®«— +lÆ4›hÆ@î’1ˆAš *ˆ‚•º%*»@Â’Žë&M’æ˘ª#%¦_å¬2H¢ÈAB,$Ö™WŠ¸rérÎësk$¢3—*çFד2 ËLEø:UÊA-H¡K29ÇXar„—ŽR×eFÉ‘?6¢teÌeÄÑs4Ñø©…~÷ð/c…ž ò—hâÓX!˜Ë#6Tr!žS“Ñ©/Þ:ãk‚Ãz)\Q„Ñ!TÁ`U«Š¢7*â͈Òll«/c\(­›Ž!¡6BÒˆ’¼«³5A”«¥¨Šž;Í·rÊ.»xÖúÇ®î×uQ”õíQ°i…¢I¥Âħ%4Ъ!h¨&MŽ9Q%Œ²ÛSM©·LÆ<D†”ÓÄÜ$‰AƒVeCbz’Ū‰×*­:R\£³Û+­µ‚(m„UÅ¡:‰WTŠ7"B¡\+-£u´6›­6R†Ê–ó Uj…¦<«•5C`À5·—&Á¼éåŽnãÀnUŠ.à»*.ÔÝTZ%ÚÔ&#.*A±Ñ©§¼Å¶e™ÕÞŒøÁŒÆ1š'3²9Daƒ[#.iH­H7SlYe «UÆ«A–@¬’; )ucÔN }fÍ=,$‚î|ùWoŽÃAŽR<®Ó&ÂQAg–Û#©Â‹\\CÑz!¦©¦GK1dR0q­¨²yܦ8B Ù¼1’EâMjJ#.ÁŽB•mÌKH'…p¬ƒJ¼—šŒŽá*8À­pìŒ#0o”àÅ-ZN5F†A4¨·B戠2+±Ðj!B ›SR1F/4Ìâ†0Ê#.býßR ÷Øsȱ˜Tnͯ›7†nnø÷Xµä¢ÉʾNLâ­!ILs­C#&D™VE ¶Fi›„‚¨(a S#6Hƺ-¡2@aZM'"1A‚Æî<j¨í˧¥ÁlUòÃÃÛæM8K‰˜¤A³¡ˆØ¡)‘"ÞZ9fñJ6Þ˜ º‡ÐÑœê äd'̓4Ǹj¨ºN0ü˜fâÆiûi—©²™°°(0ÓbÉQ”ÑHD5qîe‘éån#.nèÀ™O\m Õà*%oŸC#.«ÊŒM’ï‹ìu¦÷Â…wÀjïˆT™•¯¿&Cr—@Þ±¨Š¨„ÄpŸ•6Êäøz$×&p²±@‹V[i¡YDæË(F )96pÒ¡& 4©J¸(©0Ö\±‚’…0¸ †(ƒæ!B†ÐQ!bJŠ£"¤Á€4‰Š ­Õƒ´‰ÈÂÓGâMI)Ǹ9pd!U!Ì[¼;†îíâ+Îòé‹Q’×b‚„ŸžŠQÁHµCTˆE­ÍIÀ;¤’‚Ú‹mŠ¦Í¦l²Œ"µj …”Cñ`§ë.ÿ3#6ä4êJðüû¯Ì("Å#6ˆˆH°=ùÀ=Rc·.£÷e‹}·ªŠ>üR˜ÕDôÑF¿œ•Y—Ú>REB#%!‰ŒT£AÜ‘Båµé{T}µ*Àì=UíŒ&„æë§ÎÎ00Ò °W!P‹'Û:D¯µg<¼ÕF r†™„O#.4P÷¨ì¬p£þÛ2FÄÙÝÛn®®›[{k®”;¸[ºU0Ir… Y…ƒvÊk^(¦µR˜¡LdÁ’ RËuB¦æ»³qP Ù(f3Т#‰omB‹7û¾¥°·°ø\¼ÌÚÒ㌊L6-Öýš"ò»-ëÛ¹Vçt$YÐI€ÛâóÝkXäœ$Y¬Ùæj±ˆtª/œvA»ˆ>#.¬—½HíÓÒ1ø¶GÛ«ÃD¯P‚VÑõ(þ¯àˆ© N½¤€L¯ÈÏa‚“‰‰r×(‰œ$ìÝÉÐ’³àw ïRb4w‡^×»C¾/…ìX,^¨bK”\«Ù½-!®ÓlßMvª|ºõö!üȦ7@Ï_]ßÌØuåÜ(7Ñúþ©ØÁ#%Ë·I#ú;}+¾?}œ­n‡*œ>—Û±ÏÒ[ì×{oѤvÒBÅÛ ³É~¹¼È?ÈùŒc(šñG‘© ÔU#6„+¿æ‚d礇¼ì½¦ÐðÉßz†R‰Ê'j[br¬#.v#%ôηUŠšûʸT71wV­fºËmŽEßÒX J§^v×# ï EC°yƱ,• ìA2±A„ETù°’á™òNÊR¨QeTÎÞ‰rá˜\ö9Ú5‘jªË#.&t0ßá‰cZb8z¤0À—mE„þf³ÈßçHœŽ°Mq4› ¤Š9 #.þš ÃUHr¢’\ªévÜV^ßÈò¼QkÚr¶Kh·²ÖìKrîêÛôK\6¼UÆŽnVéµ·MY#.cnm&Úæ²k—-µÍÞír,m“j Qª+—‹;«î-Ã#.âw²Ë»$çžv÷07Šü<¢ŒÍ}Zk¾˜•˜zÓo´=H^Êî"Øκ’!7@A"ä#6´Þ”=¯íÂäD ´‘ƒ»¿vú®_†o™ªƒ‹¾ªýQy>ð)Gä#6çøÑá"É4ÖÂ=$±òSè2w@d#%“ Gßè—q¢1¤(š‰IBHå%ŠJ*¨Nîòá^T#6Z\# x Ù b¡†*©""(¤›[u­#.mt›¦µø~{oF®Ü¨Rd%ÛÝäT]Ñ *½Q@–Q¤ÖHѶÛ%[¿™~k÷!D,#6 éB?õ@Ô#%šÿ$CÏ¥'¾ûHjÆMöFÒ¶FñÇÙ{ZPuðP甼P#%;²f_Œî@O÷A$‘;ý&Ô¬›¡Ôu`¯ª¤ª£Ù¨3`Ð: H#"&Q)òT(uqe-LÍ’IJ1idŒ´i66hmIEbÆÊRRJdÚ4)µ´Eª*ØÖÔkjZV™jZ52¥bÖ‹ci5³[ϺÏn·$cu¬È²#.Ç"6W"Dûne6˜ÛaPÊAÇ$r®tëÓ£Ò®Ï=tªò®<ìiq¨ò¤©¥’D£O c!&$Ü*+hlŒc ¬H˜É-a"#6@£`èÁi†“#.‰H… 4ECJ¤P*( B*Zë0$(¾B¡ Ô#6Y2?Íü5°Á#%™ô© DØ% %¦îéݘÜ:ÓõÚô·W®ìÛVÀ(@d‘‚$–f­2Óȸ@M°%Ä^û³„B3ã`ûÇNÙ"vÔôôm÷wc¾²³Ù”„…QRN {ABH,O½¸VÆÈŒÜÐö5OÈ¡äØf2vp®§W_—[—,1Z%ê/-l@Ò¨ˆŠ±±Ü¨Mâ xFß—¤jœ¯§1p‰V!TÌH!PáJu”0UR8Gõa£ÜœµõN¦’„µFénãɼ•ÖòË©R™”/Ú]êµsQµ±hÕZŽê®Á¥±ìóp–9‡ávº?®HEÊoz¤ÑïpÛ2#6e_ìÓjÎ`»8C®+dßX{Ap–Â!ê>ÿï(ü±YD©Ë³eJ[LTz÷F"Ë`ç`΃՚‚RúÕ ÿ™ùiÏJ—Šê%áMY¢ªS|š²|‰š®#6‘u¡‘àÖ½usFpÙ"}rC¼"Él³Êª<ÂoבøÏM™Ïr§ý–æØè„#Æ X}¨vµºt¡;?;#'•ËRùi@Ü¡ÍM¸Ôã|]?ÙÒί^¡ÔB}¨#.#%ù`ø€¨ÖîAå—Íã°Ï/Ñžÿ|hÒl\1º**;­»3Ú*¥â‘TþÈñÅ4¢ØìçÓ~ö3aƒ¦)¸÷GZ³*¥›z—ÁÑÇUƶÜ` ÂpÞ]ÎÚ£]u\Ê­Jã‰Q¢ë‡òUÓ3÷˜‘ðÁÆ&Òe¥”ÌI#‰ÆåÛJ{ií'[lüÇ¿Üo5ÕÛÄîÙ¥º'Q#%ä3!O¨"e¨BG½V×ÒH#´'53×ÕîZ×#.ÃP„˜ñçRn tÈCñžxm÷¦‰¾Ù¶Uúûïy/‘ãV°A„<(}ZÒÙ81K²‰$=·E4œ¢Bl‚:E’æÊSlµÉÆW_û­×¹/#.ÑÛ‡QuÔîí®:väì³G¸\§”µí{KXî{îV¾¬#%¤LÒ#6Bk¬¨7â¶(/%ÀèÞà÷Ô¦Çb-¬Ä¢õÂÕÄÀÂFÅÆixå$Z³™F®;ûó–©vÂqÙÔ!Äg ÷7tVBZÇS¼•/ yÀo#ß«½Þ%™&‡s©X½*77/²cMž±Ár˜øW^3Ý$"“Q?:yÛ„6"£ŠéÅƺ–{åøÉÐ#.ôŠ¡¡”ÌVË~ügƒÉg‰smm9)¸®jWMöÓÃm‹f±“ 虘·â®8kÃÓ[é‘þ]»±6årXöN”VRTâÌYÍZª¥;¢øùžgrª³s BÄyo…,pѾa& q<£“jf)‰ÐÙnÆÛ›ÐÓE¥Å—•›f5Šw©± ŠÑÄâV\º)2›Æƒl`œqÑáDë¾x¶ŒÅf^–a›TûåkĆŠWnëh6“³m<pæyßý_ZÝ>Ø—[ÑÆçhNI/$“ËÆfš7Ž)¤ÖŸô•öÍGŒb$C¥×ÃŒêC–C:ÚÒsŠ/}Ñw§Ê€ò¶DÉJŒnÛTóQíjP÷xÍ÷‡“bëZ:>춖Êü¹Ÿ9õÌW_J×°¦ŠéyƒiÄÆx’»uƒw}²™×:üç´ç”¼Žî'÷u{õõŒa*Ž½^W‹W0ïoèÌÐâò¹˜ô³8ÎÜVx˜«­¶®œ§|ʼntªCVo…Ž†#6åUvga*Fñêòª‹­éïlg¢/Ç=˜’Ã5ÅéüÔÂln÷.úþöb¢</DeìóÕ«ô<¥sïŸ>NΠlr²¶AÅ׬Æ<EúûcÛÔÑìôþÝ!˜ ‚"D^æy¾8Ü£ŽËB6KÛ­8£,g ‘:d©D÷˜ÓìLíoY°¼0PÚŒ/iÈÚyÔs dÛ)Ž…7°²›žTîÃ÷ä&ªhdrå#6%#.-í/Š×Cð&d«©.]ê™'O8çmÃ7£žUãŒIð,¦`œW£2êçµ8ÏNÀÅî$`.èXÌ@ð=Z¢Ã„ì÷m„R†˜m³³£f3ƒg#„RcÀÞQä¼b3x0óš˜ð½0„_”$gB†È›0íÚº‰9¥#6¨ÜÑ)¯± Ò'¬Ï•Ç– keØ yŒc0À)Õ€t™±…(ßÛíÖ Ù­dJݧÍ-®¯®ŽAåÖÇ“¯0É<¼–^ G¨&ê @1[õ c®UNÃcAÈn>Рo#.°†(ÐÈ#×LC÷ªó#%€@:ó š#6`FãaÀP™˜‡a»ô:£L6 ñÕýxõ»Æ`vŒž¥›¤•`qG=æ26ð•9è•ÖkSAC«#%6ª„sÊ^æz±¿v‰•‘ Z3Kˆ—3.ªÞ&˘sxÃ\ã#6?#6«_Dd\¨¡PʸG`{»u½§D¯OÆ.þîO'6÷ûÞ [ž‡“°ìkÅÖÈ,½ÓÀý* “„GÄÂg ¾T©œ"VsïÎ ´##ËlºNÁƹÓkFÒéaùC·”úÏòghæz¢:§ç™ŽŠ–”žç—>ýcšŠETÚQSSNFüb((±SöÛœM—Ä e²Ú{Að]‘Ák]zVÔ4o““Œ{#.”Uû»ð*#%Å9ÇGÖ“ôÚÏ6(¯Û#.oÏ϶;3\4‘Š¡³G>FÎD(Öѽò‹çÇÑp²9–žfª7 ­Zæ0ÙºÞÝCë¡ã’šÖ‹NÚ21D‘ª!ƒŸ.e-lZ'ÚUbÕõQÃØ¡™ç~%‰@½vÝiVkQ-29¾ysæ83DñÅì^[)X×ÕÚÉš" Dv:e£;F#6Ç~›•Ù¸°ëíÓÓwÃŒãWoÕqÜÌ‹²¿';¢Cs:ö#.a º:ë¶Y¹Tû›èœ ¯[–ãÔÇ4v4n s©»x÷ÑÏ;èÝ 2÷~öç‰sw~/´j¦<“£\W‰t·Œåóm‚åG‘^5&ŽÉoä wªŸ7 AÍÄ%å#¾Þ0Må8pqäñ<³~7SÛ»å߃Ló‘Î¥mZ¡\dõQ†Ð{x09hRÄ¥ˆµ¥«#%}¥ë„ô—‰”D4·–bÍ…èŒ3¨ç’š®E’=¸¼ó‡ ;Hœðà>.Ãx[CUZØ=íô7,Èh¨·tz=<7~†O›œ í†fBñEo7ê9q À°ï‹ˆLBõ«•³üñ6°§«Y܉±Y…„;W¸Žœ –Îå›\¹Þq.ï6… )sf\=®Ë#ÕƒV4(êvµÐ<³Ê@Ç”,,¸Œg ¢(Ôy zw-•HÕ]B¡¡¯E´öq3¤:f,öÒ­'!Ñð ì@â†1HóFÔó vM\;€© É3@Úõì.Úg$$VtIm-äSLÛ0w0ÈÁ'‰#%ÕY"aÃßôèU×]õûC#.˜`fá˜+u¸mæò?‰N®°Cw"³0ñ²)qO‘n"xXŽ ‚í¸NÛ&Äy~‘å(ä·°ÇèpÅ`\r ‡Pž <ß‚ð(;ïÍVßáL{U#.>¿ãxKf °ÂwÀõüƒå'™y;`kàýpÃhST«CÙç2°gÏ¥_V«GªG±8v'$ODMáÀÖN}®;O‘[Tb,ŠÈÃäÒ¡E€~ ¯Á°e…D–aì»Ímlš…s˜Ô¸ª’“#I#6±Kº’Ì0ªm&@•<Æ…(Ó`YF91!²²"¨DÈ¢–‚“EXB!$JÆÕ½+F·5¨­nZÊèZ©”‹"(ØÁci¬¢ÅFPTÒŠˆ¢aQFR¥‚0ø!\Þg¬âþ&xØ…µ± Î@LñÜî#…# ¹vÐ…á¤é$c9ÍbŠž”Õé¼mãnEɱ­*µ¯¹m¶‹V®UQUUé¹UײóÇÐÜŒÍýÐçP:þ~–µÑøC5÷]cÇ-–§AKxæ*Æ1ƒK+udˆ×/¨ÒÁí¸i¶í®o Ü#.hÇHiþc£f-+°£W ¾ù¥SáîÀkN4bJ³ÄòÖ:¸`@¥,×–cÉ –(2ƒtHqbePDiòãOR;Æ]ÓxÖa%‚dH°!V“7V˜Á·Ù (š&ÓicRE,%ÝEcbi¦4C®Z*=Ê[ÌgA§Âu‹wL¤t’d½>ùlÀö›L!P9l›ª#6@Y0 šÂfœ‘L7zUàÅ3R·X›€QŽÆ6ëØËÖ¥4ÊÑÛŽv²ÍîFjh£MÔU¬zmkZ¦A¸Lk.<“µ†µcÔèç9«eo†LÑ$D”MqP\`SZ¨X³LÐ*±c‚ñcix1”2ëB‚ÕC$Î¥¯Õ¾5©Ï9;·hÓaY ÎS¼JÎ!ª#6A,ØTP”2„ÇcLhÇ»*ëµs½µPAôãn²‡U"^Ck*œêæVú†•“%+XIw­Ü&]J&0ghnÀot‚Çßh­siH“°N'a§ïšiÏ]P \‰ªR8ÙFö0TjŒ?ªãÞºÓÖ“cSˆ‚ò£œµ.¨CZF“Làèd¬q¨Ô"Ú7*ª#6”*à Ѹ”Ðá¤0m0m15+‹‹FÞn-¼™,²»zça›[ ¹tùÐC 2´CdT¨£]ž&09ÜEZÙ*å#.hFe¡›±—mÝF?Sã5@‚ŠLãRùg%¥¶È|4k ¸Ô €ˆÚhŽ¦ª~¬¶ÆŠä$¼*7Ä®é@ì€zi¤„<˜D¨°]8&è .ï®r#.V†FÚqÉ#——B–¡ZYm r˜m†2Lä­„ÊlbWfJÌæÚ¢&n\c§ˆF±V=¼§^1Q¼ÁèÞi•®›m·Xˆ9¢VßãÞŸßÞŠ= ­GÕy˜úe”}#611(÷"Ñ„Ž·e0Qsâš z±ƒXr8c™MAø¹¢@‚œ=6ñalÊSŽ!vH#.0ÕŒÏ ¶–‹€û0­\d#3ÉÜ$d9ö¥›:é¨WbTF¡£†MP1#BÌðCV#6æe´Î-ͳZ"Û­hDF ¤¨b0fµ8Ò†¸Â±¶FxØ7‘¨Ôƒ{ÔË ¦A¡6Á&'"Ë9#.+µU°ÍNß!UíÏ*ª™Û2‡s—£Û1ó•M`ÁC!0çr‡c$’Ûã½n‹ç€¿¦Ì+¾îÝiL™5–MÓõ6¯ÚÙH•$ä”´•¤$ˆz—O™®;ÅÝóèºbCóHwÀ©ÚªwUŒØÖ­À4†*SmÙüçƒ U+ {}E*ûXSÔÓ^#.“ñqº²±eÓ¦3™~sÈ+?2CÁûàÿ›Ð9¯¥Ag*Î*JAFä#.o³·Ä³Bx«ÇzQ€#È›»û<*±¿®væA§ ­öœšÁ›Kˆ?~Cóyßì¨×\Ubƒeª•Áå˜ó>\f÷={p1¼$a¦î“»¹0ˆ¿{w@y9;šbƪ¦[ƒ'º0’ÇotÜòä<×Ñ(õÈø¦k´à­µÕ÷ ¤qÁΑHû®øŽ›ü!‘¬í£Q:ǧ å@Hµð†ç© !Í’5iŠ Wj™KV——\Õ‹m]7‹hlPPH¯Ð†¬»öÐÖiä#ÔD~èì9ߥ}~•Œvý"°R(±H#%V ZÛ#61´Œ˜Æ&1¤ D©(Z¦UŠil[%¤¶¬RQ¶$¬š4(¦Ê£Í¡M5#JM’Q³JHIQÓhD¤KFHS4T¦È¥SF’a¶lÂ%(I1«X!#%øÞ:úx—ôÌÔžg´ê9šÉSÛŒº¬[Ç4UyÜÐ~ðWæòs5þW‘$$g1$8)×ÃÂÚµâW#o®å$î—VÐElO"¨>œ€#.¶ƒ˜4~‘ ¼Hga¡„’hf6ؽØLKn£’R& ¾~§Ûzk³pCõ%ßÚ(2ÆH\Àù9q]‡=½haѹS(ÂBY‰èâ°ª¦$ã…ß´ùû½šÂùïâsþŒCJ‘FÃÅqy§üÚµù¯×ÑÖÇ‹{!Ãë: ûo?“WÝjßµ¨’Û&ÑXªKXÑ÷Ö­si±©B“Q£±õÝ«™C!b”R©bXK2ïÈ0'uàEŠ$fPÉ#6>ÿ£"Ù›‹)ƒí¬÷ó.#.ƒ>+„¡Q…¡¦b0ÜürèÕcD>n.ž5ª(àÉX¦¤ŽRb"©¢˜ªŠD.éÊf’¡”+@6lxœËRXÐV,Iˆ.¾Þ1Ó{¯.íГ³šöó«ÌÑŒ¦»6ö•å&ñsD¬ÊÜ·/×–³PQÀº. a#%­ÅÝÞ!”`ŠÐ¡GLŒŠÄˆÄ½G¾ôÓIà{è5O‹†EÉŸ'& u1p6e¤J'LHQb`àËVAd¦¢ ŒbŒ¤…(Œó©#.LáGô`6  kgì›μÞ6ór1J¤NŽ2,=¤4,°pˆœ¦˜„| d<ÀÂÛ«6ìjHÚÛElmŠ#6­#%ÚÔ(ƒ"3@pBh_²*ÁUY‘U¦#6!ìßÕºLŽ@ ¦‰@¥+†Å-= #%½]­”ô¿~7c¿#´…`>ϾÈ÷÷ò3Œþ¨ž‰Cft½B$#!¿ö?‹"dê…„|êÙ¾~h%¡Ð{Ñæ#.‚’ Ÿ,(`šä塤|ÈØ”n2¦‰”­­LÓÈ3ÏÐ@„>€ã7ïØV†Æ’iy¼Â°÷ýÞ=ßµöR|ª2Zûf‹5ïðñÆUo¿[52UQJ–m¯ZÔÆÊJýKï*G;ó&¦¥„u Êqµm¢ò êg™*ñ4-o^…°n2nF-tiªD§7C5Æ6Ó&TS`Ò &Œ!*Q›±A÷Ä rê]uâ+NA–«B/¯Ô{,¢tòOئÊúÓ’rµÉF¯Aï J+«)è(° Éë;øNsê>«íó]ÊMË°#.ŠšL†|ÊÛéRæ!Á)’Bª9£^¤ÿjfÅ<Ì;¬#6Aî0xw;Þõ†HA)4g˜fzúœqŽòF­õcÏ á„ØE¡kõ|¦!»°uªMG&~逸²#%Q;úŒ¿ÜtŠ@d‰"Ȉˆ*¦#.>¸ô/}RŽâb_cóÖìT#D¥:Ζ£Ê±rÖîõý“'5>‹ç|â²âè}.šàý_Tl×=ÎÜÔ²]4X`C$0ÇVêã9'HØv'F³ñIÚj &¥g™õ@>3¹“ŽÐí€#%pàJrê`{ä7s@òq{ÊA/˜”UÒ\zge)^±²=sDþXÜÆ1×Æ£ÔŒå°nd[wof²XŒ‘²¾çg)P’6B·Þ\+ÉÚEµÜ,3âÍ„JS»uUtDÀý19Wž53ÂpTlÐÈÓ#.¤c€h‚` ñ‘0pP#.@ŸÐnªÁ`È%ÿV TU*2ìÈ/&YÛr-Þ¨»5!€PÔÊ0E#Q¤H›a¸’6P";^-ˆš@â–YÇ1Ìnì5Í—™¡#%1” ,½!‡sˆYÚŒñ¾¤flzÙ¹Ôé¥ÁciDã&‘Ho?/3²Ð½X2Ž(uÇÌ°´ÃdÝ­zYg$äÝ&Á5åD ˆEPÚ¥HŒA±è~“3#%‚ˆ Lˆ™ªÀC}ŠYf‚-"¼¤}q¥£ÌàIQ%¼†Ø^FZTz³µšHÅKÅ"@Sp¤u^<o.ÈB3@Dmï½àâ…×j”ta¦lrÞX@z‡QfkQ ‚Á¨‚À½Aƒ(Ö÷Šóò¯n7ÛžW üï¬"ù:ÈÏÓ7Ò`ÀR7{ƒ¦BdXg&ðS¨f%¤Š‰” É0ð»,í‹ýî“•¶ÜD¶uS2ºcre38ºt#%™aÁ%ç­»#6Lp²O§pß’X9 ¤à»WD¢ƒvkb”Ô+LÄ#.–܈#.»³BñC7<è‰D©#6H2#%ë¸ïšµ ‹!£!PÅ X#6ÉŠH žºÄyŽ-•_F¨é<´­ŒöÝaš•˜ñ¿n«ÍÛµ±S†o1ï[ÍœVWÁ¦e 2äptc¯,ã2®i²‹혎s¤°ð­Ž4F!ÇöR¨±AV,ñ³á‡‰$Eïæm;ÖcHo!Ó@3k9LJ_™'LÁ›YÓB¯iè1@'*kMž.³ˆÆ£6™‰6“ A»m¶ÒÁL\RE΢ŒFe¥%o˜Ý“*—,už›`Á´¾Ñ‘9¥É1260ìo)¯i#.&”÷Ã@V«¦Ä$l‚tšÊvÛck¦—r˜F)œÀKE ÙM‰,vLí.\º{Õ—[>!m´Áiå¬Þc#4Î!­äkû}av˜ÚߤœljW¸ä'7qÖ$[¬vŠÚÜßLU»ÆD<´ÑjlìïôH=fUÌœL3™aè’'£>4‹O¬ÈÐŒ!Zõ‰S5{ÓX¦¹‡—CÎ&—;±8k¦îŸ\@r¯2n&‘='I©Zã¤êBÅ FÕ—„,¹\¬*ÍV׳p™`—\ñ©HfJ•Y#%êë㚨‡"ß‘kkÌ›%ñO+vv&º?grà{Þºç(äUÐä×I¤  X¬E#65³6É›!ÅdhmÌóÀé­^]04©qs:ÙÁÇ0@ëæE}h·¨nœLeÁÃ}8 Üƒ^-1³ñ|2£Ãµ÷@¸p´V×›;"'›‚å¡„±•X%ôŒ–ºí#%í5`+IB27©t§#.NZÐÅ©º†B¸–°Aø±b+m:+¤‚BN\¯…mgY¬ñ[6L†ÎÀVælåŒa²DgÑ@iµDбP<ŽýÿB㌱§4ü lÔ?nŠMÖÎð„àM$ÕY'¯BÐòtoi´\Ûñ¥Ø+~8ᥢ]-YÃ[Ôtͤp‰?UÿÂQm—2áLØqEÍ#.q™ƒíñØåÓ<<4x¨1 (3Hr›ŽÞ<9²óëktò ¦Æ ‡r³òëz]YÒ1Íñ,ÌŠsÕ¦)³åÈFµ.ÉzˆÂ—y|²ÂÒ +d׳„ÛFó~BÀ™aud¤Ö †l”ŇÇ-Å…µ¾ù‚3XSC´=‹‰±ùq«óëth:j&#61iu]†Šp3B„Àtž§¯_ m}i¢2HÛ bvvÏxâ«$3@ä½Ì·gnªÕ ³ ŠP—JPÍ HÆÜq!¦YÛ%j®`Þˆ"–#I¢#P/¿=¹fgØMœÁC´;,LMµM4Yp'Ò*Úúµ˜·”ÉSìÎxÔEë¹Y¦§vM€Y@ôðSäÌŽœ™è‰åå#6ݹpf#%ÊÏÚ;tíyA&w©b¥‡OLìO#.&Âé?8L89ÞÝ“M7ã%†8Ž9Ê¡Ø-u#. ³/L]Ÿ¯\Õ¿%‚Ú.­cxŽ¨b‰4&µ!Ò`,fÄlLM‚á#6­˜‰Ó£p:5ÒHÄÄÌü¤Žd‘A¶¢„Hø0Ì‘Áö 4EqT¡ªk&§xÝÛ3ÅÖè)ïe£(Ã1YÚ`¢ÇŽ6! Ý]ÛÑ.$#.  €àT–0%DH!¹±Z¸å9Ç´ÒQ PP4 ˆwŒšÈi8,ÑHŠê%Fl!6aeÈ0à\*hÛiD5Üè8hé™–*¢P ƒK#6¦æÃ@ÀÀALCÄD‘6D“áS*¸è†wB]èÊ4,¨6"ŠwA³½ò3bà‡"¨)ØÙ˜Òi½‘ƒ†ûøU‘’« Hªå¶ÎMlEÜ 1 ˆÃØ#6š6v4#60gSP#.F†¸H¶WU%0MEó& 83ÐíÄ„3œmÂt대ÉHrºâÄ¢ œBB¢ê™• ¯3[Ò^6¼¯_#.óˆ±c%%¾²yyôŸCk£U@Ê(¨ˆ°צŽ½Èš“n‡£¤ž±°f÷ò C°5#6,"B@MbªPê ‡á¿öôl½€Èë:½u‹yÑ\ƒ²íšeX§—ëïk—°Õy+'J#.?Ö>£MÃ܇)üŽøx¨âÙàm&-£™×B¸kMµ7ⓃIf ŒsQ‡fE°Šë»ºf$‘$#6DXÍf¿šY¯§·š#%ó×W%U®ÔEüðMÑÎ^hÂò¨I#%ê¯JS°x|k0ÂJh(¨Gf‚uaÐÆzífˆ?“nôÓ'àÒ«„¨“¹*3#¼¹R'W((q#HªÕÝe’iŽè3³%<ªÙ 쪌JÅrØãz#ÈÄ6¹–7oSñkJêF9ÌTÍ@Z¿0TV(NAqKìÍËVè+ÈämÎÜa·4}o{ž«;Ù¬å¦66”˜µ²¶[[:ÁÖ®¿¢>¡DHª{hˇà}*s9e¾Dʳ:¯«d{÷ÅT°3ñD#.¶CvN]GÃL—@SÞ¡#%4„Ž©C»¶Çá^Wf“·DîΗZ²º÷áâÔRñ7p#6ªVŒˆÚX"kÕáË»[Í$2%ñ5;Îéo7v†È®òï^vÉåÒ¹Å×RÉ#6A‘Ü0@4Ћ5*ˆF ´8iQ¡øé3 iŽöGZK(4‡]-ÌUDÖ7v)<´MF¢¡ÇgVƒ#34u*ÌÙbQ „GFùèë쯷»È!ð†<ýZ÷ H|ÁpDäA#%¦Ï¡¦3ƒõ†j‘¬¼•dú.ÖÚ1<#P„$xîamˆ…ÁV†ŠÄ®ÉÉL+ÃàÀØšÁ$p ¶”(“Ÿ(7'§¨&µ9w²#6nc±ì³êžlæ!ÜúìçÙ#.:ª&Œª§­LYUå™ ùû¦ƒ÷×!ŸG´šÈ(|#%ÉÂmr,A-Ùa…NKÎMN8ÓR0;»n[5'Ã)Lª×³¨^b>%¹C3é8]6±uLDÔ`X€ B!:©O¬i悘ç³n%Ú’"öa\ÐØq¥ÑÃK×è‘£ñLôQ#.i‹1Ç¿†0Ú É 0µö~tm6V,ŒV"$ú‰¬á†sÈkÐÒŒ˜Ê¸ öò…0'º)HH2DO¢"öµ”ZÍ­iHJÔߪXDVBEGE„ìE—„0cUÆ„e5@Àm©n\0P‰`„M`¯8#%ì#%IîËÑ•…*Bé—øahš<2­½Æ¦Z\ÜìŠÈ¤°ƒ² H’€:¯ƒäßbš•X1¿ 8Íõ #6 #%HŠ’*¾ß–[·|4ÅO4oÕå}#›9†Ñth¼g#.`±Þ»Ž´Å¸3©Õñ*\žY9)h„¢‡#65½P4a£ePM£2X’l+%Ê«EE¨íD‰‘·LNA‰GUÕÂë×yåmÊ^¶²VÌÝv¬V`•%šQV@IÅN†ýWPË3:È$#.EG^2S4#%Ü¥…¤M—2,Às4µ§âPNÞ0:=G§‹Í,0!&*Ç·§ä=ÔÌÃaÂօз;sHc²iÞfÍ ŸŽtê1ëa°SàßÚ½&ñØÒ 7!,HÈ#%—5ëÃ@ä%é© ¾EsRÏr¢Íýï_kWÏ:ó¶¶*'õÍn¥‰†šHƒ[ïh4×·¢`wAu­uÚá#.,‡q89 ‰JþÏáÃ;«¯³‡3€¯ÅO8~kÔ1 Ü$9Ê%¯«y£6ŠZhI¥EG!‘+Ž]£fvßæoíÕ˜8”™`±Òùn¸¿¾¶^Óötš†Y[ÕôãBa»Ö›iAà~_X>ÈH‚&³ì‚‰ÔXb„#.4Ñ©î„5ë%ïë!rKïl}9úÓì='p>ýÉ­Ô(Ž^7/$‰ˆ¤´š’jYiŒVIJ#6«bÖa´&ÄŠ*kLmµ&ߺÑZ¸Îk¡£Y³k‡s¼ßÛ<“ÕXªå‘˜ûµ£aÞ¢þs”Ôæ: ÇÅ#.#.¾®¾Š+Ÿ˜±;öòÀMI@\Ùô‘²z°/‡aÙ°nâ#%žÕNÑ=ǾꉑäKÁŸFzùÂFC?6ëñ(ŽukµÃ Ž…XE{šJ•ƒ9&ärÔP|üi¶ŠöÔä4Ø-J[hyªôƒmše;“;ð´#.MÓIÓqA’q«)\»’ÄùÑe®qÄAäÒå˜CDƒ­ 8:ß 7$™l·}uf¶™ÛŽ6c„6ZAI¡%!pj[´-ÑÅ[åÍü ¬+cTdë‹ÔçL¨Í¶§fˆÜ€¤#%PYl±’Ú!VYCrhM ­#.Ñ­ÞîôÅN‚¡êÐ0LoäÅ£JÔ1”ÊȲnÅF´ýJÖ† bÕk#.¥ø?{5²b]%OZZl}Æ0 ¸äE!òó÷΀Ù,"÷•Ég4 )@w}* uAÔ ²$ALpIF wIÏž/ƒ©‰tÊð¨A°O€!##Æ Žj‚ U6¼ãÕt±ð<4O‡ ¢+ÙÚt÷ a‚­ŠÛ ‹L;ÏX#6ɨF §ßæý#%oÙ”1d”@Ç!”ƒ Tñ {õŸWnÿWòmî'y‰üµÍŒó6@gö8A°ÄZ‘´³0?yüä<vuøl¼ôªÄfPx"%7³0 Ü'´ws7uÿê"çð’Žš¾ÞöÝ—fÇÓ500.=Ô–l×Àþê:;Xæ¾·ÞQPñã@?LSa#%G1×#6Ü ÕAf˧f¡€Xùè£C&3#ÅÔlDhÒKºº #.‘¦WÑÞaû]Û\«›œõæóÏ]ų„†É”Sd5Q‰y7s]¼‹&ÆѶñs\·Æ+»·‹yåv•¤å[y“oÑ[¶Ís!ÅSõ$S`6™mÙó÷þŠ#6*¾%|}¶<L³=AÞIYªa<Í>›é#%Å<Ô®Êû>Ê,U!¬Ûf,ÚÍ´õµ¿5÷­øµj÷û"Ñ-62)E’4²’©³jü¿tj7ßÕ~îú#. £D1SQ¶*¥)JÚüz§h’—µ¼'Ì‹x„"#%Z"göBRÖÃÖ1ȸ0ćÐ0–HrHNTŠ$è>ä#.D1¦î¹²)±¬-f¬Q¬lÈÅc+™E²Á‰ëÎò*ˆ„(@Š'](Š]ê4{qM_îòýé©çÜOsèùfaב$ðWÀ šõ‡#%£'¸}—ËeL|ó*j¹ #6ý{.³ƒæɃxv÷’,!Ú°3œ…ó˜ÈIÀôªyæ8öÑUãoË{Ø,E˜E'F™ñßt €ž&ny¤À˜ÞB Q¶Äb‘püÈt‡î"€Û@ÖôTÖ¡ódH˜E9§¹Z€¡ ½B!JŠS˜Újh(‘"jKNíÒkTÛRiIª[R’T šl‘‘@njm¯a0¨ ÿNR‚(”+Ô\/*SÞR¬‹e¬Ë/n£.žÃgUPû£ãÓ dtÎ=v£9é.k ¯Ni`MÄljh™ú([;³RÑÖ*È5õXV²Oµ8Ð"Å…k°ÞÚ4ì#.y@^K?ÞÑïW-ï%ë¼lcúé#nìˆzK™š!ö¦ ¸X÷¢Ї§øþàêüûo–—çãvMÅ)¦%I²5¶1R`ÔFÕ&Û#6Z7æm¿+W‰°ÑZ(F AUdQ$ïî9ú»,ì–i!`¡P*…ˆû¤6+°0rBF"EHÒ(ÄÉ…Š)¢hlÚQ¬,#%”†ýh—Æ}qÏzíÙ/uÝJQþÀ!"kWš±mF- Ú”5F„«6X‹X«QšZÊßÓ[¯3†:zº›üYݽ´ªË%$ÜkìÇègÍ#6Ñü5–#êŒ1KŠL5VÊå˜F·ÓLðкùÀÆ—àIô÷$È«miGlý/$²ÙÛ3"‹ *hc<jÖ†Óƒý90Š(„8A K"…Ð2Ú·Én³I©¢•-ë6ÛNìµÝÙ^5æòµÕ6‹&·¥\•ówY›³2«®nÚŠ¹Ú‚KdYµywcMk»«»­¤ÙRTÈ”ØÖóº·škΨ¬$IS‚•a!ƒHˆ6Ôb£cÞMi„^›×—j”ÒÙMI–UéZêÞuuçj¼lm(Êe¬¶R×Ü·k»tYc)&A¢ aˆ£ÑN™¡àΓ#%É#6ÛÐqV„m —3ø{äš‚è5H±dÜ>²øQ¶EF*‚d‚ÈÇ#%ª@ Sã’¡¬þ¤Ê[Fh¥È“(1P¹{äÁ5×\É&§4)DÚ›7Õ¥GôØ46àYêÓ’äö¢ìþ3OêÁ `>¸ˆ¯pÏ?~ðøfŒEº#6ݧ¾÷#R÷fìˆfKËÇXZ¦#.Yë9¾îã~07"ß^GÏõ–™“¤M#.UJœmG/¤lír$a®îB™1 ßèSJë@ë#³o(ƒ$QGÊ)ļ.#.õêLo.Eä`%Áæ1‰¢GP5¦Õñóç/ml·p†B#%|²$Ÿxk‘(#6™(iÎ ¹8>J¦ç¸iߎ„>ÔÒ9åÈt(a…-Q¢Þ–3oÐÕùUbÙ›õ`|¢‡B hÚ)I¨i¦×£|#.âöA¯qØÔùKŒ˜5Ó"÷gëtód›±BKûòVµw¬¢ö|jë‚P¤Ot~ÃìJ'`O8)¹A°.F´­'RK`¨¤,n#¨f4äÈlƽQÁ€ÑÔÂ!³n¶`¢úC#’ØÇwšƽ!ȱ|*é €ŠDŘaM ‚¶”FŠ›ÕÕšWáœuzC<óËû²œkGG ŽÄêîéÕ‚]Üãjþ?70ÚEy£ÉZ$£Y¨Ï¨ÑÐǽÁäuMmÐ/÷Ð…ÄZè8@¾?jã‚8“ù‹ç¯fƒYÁêF4dýŒ—-DEC=µvNÅ|ªÖÞm¨:ƒ´Pi\Xì"ð#.%t¢#‹Ì¸ÝѨU׎1Qߪ…@ãƒ[8AÀÓ Êö;÷—çKŽ¤B4Dbžƒ:[nXŠ#.édRS(´@Ír:†‘ä|Xr×c'pždl€A±ï47mà¶Ô~¤…f1Þ“‚BG¸Ô_h½GCˆô"W¾s;/~†D#.îfYÚÛÊÚ{aµÝ×oÊ÷}þëiäk×í¸*‰>ÜD-(?„LþdÓ* z©Q£fÜ@ ݨþQD¢¸ß³Ñ2Tm`ô=åWì#%ÇßwïØÁ²>u­Lä‘™› ™˜ÿl#%#%b#%?›Ùÿs¿ñõÿ¿ü¿éýŸôÿ¹íÿóÿûýíÿóîÿ/óÿëþ_òÿHþ]º>_¿ýÙ|¾ß÷Oþÿëëÿ‡þ?ðøÿãáÿ~#ü¿ãÿ/óþïü?ùÙÿ‡ú|åÿ/ôÿ=¸G£üúÿÓËæùGô_ôêüß_Õ¥P÷SÿLBÄ?iþ,ϳüêÈDÊy;"™e\?·ûäE7¨;ˆ‹‡©sÁþ±L@­ZüÄm4 þË#%öªŠÂ)40)*ÿšþþǹÝÔI I™ŸF·ÊÙVôh #%âQ³ßÌôjB »M¢Ï¡E˜SYˆ8ˆÛ,×g Ø+,€ƒþûö¦C¦Ò‚æ¨x¶æñ#.Öâ×ýÀä÷² ƒ¢îÇ‘^A‰:Õó»0ÉæfØÝ@„#Ì%*ãþU•Êlé·üiÙ\¾ÅŸÙ»Æøyö¡ÃÉvǧ‡¿„eÔ«jÿ݆떅 ¥&P"ƾrÿïUËrž#.ð7Ï_û«u”a:X˜„Ès+u‘ž#6Ù™ýþ.ÀÀ7µ*jH†þ”¦xii”Qš]d*ši‚—eaËœTÁŒ0ÙÿŠeTñhÞ¶ñïÕ¥ƒpvŒîm©TœYnœ44¼BÒÙl`…ÕÁËsO6e7ª¨5mÚ8øq훓Mmƶ9T¨¨ëÌFdMmT˜I†qx“Ý,ØY.䃆5¨mõyõx*ŧ7ŠFÎîs¢kCºFêâ ¬#y{¢ï4êÈh2ùÃá’f´Øäì¹a²…qS1a ïH²F#¸yí‹ÈdŒ¸ÐŽH?F:oÌÌü4ÃÃ)×Ц­ë^EúîÞK¨ŒëN‘±£\-˜ÑNàS4 '>:`«é”ßw´ÿJ±·ý4#.pó‹À@ÄžGßF;tk5SÞ’æ ш넞‰‡ gà w#6Hp,†#6¹^>`L³¬5¬R Eò™„ "IEq¸Ov˜]œ.fB¦æ<õ Ù3PEATV*AB“9wt¸¦cß·Y˜ÌÊ2 ¯kº\69‰¤U¥€ >È!<NÁ(¿K61$@ 0P’2îHqw”;ó\¨xƒH1Mï}Ÿlı38bÿûKuÊÉ€ž–¬Æ\¬îÙ ,F#6"`–ºT70]#.ú wó­bs„ ‰‚$LjÄÔJƒlkY¥3[FÔm’¨ÌÒDÅ…‘S_EÛWZªø|:Ö¾¸‚:?àE3€ÿt#%¿]Tµi¸#I(¼co¦üÛªözÜíSlÖÕïéñ#%Mþ}OÒÂFA~©o;|mq>§^²æF4?ícS9öɬצåU1Š"ü«Ç»­'_û6Ä+W] 扄8uîKºƒxteæ߶}C3¿¾z„g,r.;À}²“½©½Ç0À×–f­ÜT9†P^]*zgâ_§Æ³ò8ÎŽQHÔ¼=‰ #6@C&9›Ò’#.³QÁOª‹#.D'ÒáG½\áUò»µŠšP®›CGݼòöœ²áž˜¾¦ÕÍ-ø­¾v챯¤ƒ[êk˜Ø´|nmcãm½(ÕìµoSEªMAj½óZåUìÞÝy»¶ë€]#6.æäQ‰#. ©‘’| P?ç!xnãdN2Apã'PQ±Ôí±s¼p]º¡f!‘OVݤ—¿š7áUÁbÇá*J9…¨H{ÿî°÷cÍÔ¼(5É@$fáÌR¿æû'wØÊ=<ÑÜŽ§ÛÈ_.EúØqPôÇX¡ÿ’#"‹(]¨î;µ#D”)Qb(ÁmLÍtÖêÕÙjûËf¤ÅÍh é! H¤€ £þßÃ3ÏëÿÏئ¨¤"Žþ~˜™áã(ø{fYr0Ûm‹CòNÀ.!xN¦ çå,A‘<?à“‡áÿ¨  —ýþ­&^â|YY?÷ùB-Þ6áÍtçüŠ¤ÿÿ&EÐý hNí'»ÿ›cÿ`[Óúl¿‰·@³¼%ËAú¼Oìÿ»æÿÜíe÷øCâ.ÃG éʽ1ÍÀ‡ê'¼waÕBžÏ+“Üã!Éä,—yý>^#y;³9ø¾Šÿѵú½gþs(ž?l-A!ñÿÔ¥ÝóG#%‰#%(ö#6”CÛ¢ OùI[¥BY=‹”+¨›4ÃÔkéýÕsø]¯üžB_Fœ*ÀN#.ÿ6pŸñäÑÎJíâqTE3‚o¦«…ÆY̘wÈâfÓ§m3 ÊÊÇðç,Má†/-„±¿Ñ8¨–¼­§0aw<}8fJÃ$É o§‚ì]aðx`HŠ$Y@zõnÒ=®Ù i„ÿ~éŽ1éÿµ^#.Jz(ÎLPÛæcè–ªÆ"ÿ“â|xã‡ü†œgê?I¼Ëò~ÿð"‰ÿü]ÉáB@ä&öÐ
+#BZh91AY&SY#*s†#)Êwÿÿ°#)Pÿÿÿÿÿÿÿÿÿÿÿeà(Â(€0Í0çŒb¼{¤Æ@#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)÷Ý×}=y”£îâìl=½Á½µ«C(4šÓ··ÞÓÞ³ è>ò·›lÙb#îÒi÷·5ï;µ‘zë´hm2_<>ûåÁ;íëo9tz—QRQ^îë{­†íÞÖZS»vÏvn«Vá¤|óÏKz×Ùï–÷G)Ú¦=Û×;ï¯{<w`dñï‹koÃvànû¼âïsœ`Ð#):ÐÐs»#)<t#+0ßf‚x¶{·°z{w´½Üô·ßo°i #)ãZ=2zŠ‡ªÐR‚Û QJR°wc€PP¡TR‚÷¸)QU"-€4S×@êUBŠéZì¶óæö÷]ÕÙöÜojÏoxÛÍÕ©3#*TThm2*Uš·Üõ;h÷Þäðè;ÍÙvڵϷ^½Ý‡<9›»½Þ³íóæö=ؾ¾µõíŸv·sm»]Mï­é=ªc¹»åîÞ÷×ÑヒÚöÎŽì¦J%FP!RAÖ´ÞÍÛ+nºÝŽžïw¢öí¼ö÷½ç­ÊëƒZWMÆN°íœÙH’Švó”ÊzÐ #+UD½Øg è¼Ü%/lï{sÕØ[m{ƒJ>Ü#*ß{_ÜÏ}Þ”H#)´´Åmk®úoEË#)%Þ°rM€o¼xyæôìo‘ͽìô-nŸ}ç½ç_uÁêñק ›íÊ£Ó^ÊÔpiÒù›¸w‹>o¾¼ënîÜÛ—tÕÝÚnúÎÞåØúæ÷%³çjuÚî^Ý^¯=Ã=·×wΥؒlÕ4k#*mõîôï°û³Å×-³n肵Ñé«fî¸ïmÛçÔR›^îí\kS7Ϲ¾¼ï¶;ìÛžöí÷==k{‰½—R¾÷uëc_YGgŽÛÝ×uõÏ=é¾D#)7Ó—Ï;Ð.|ÃîðhX:©E€Û!°³#+æËvrT[뒽ݗ`vèí= ¼Ç»Téו{¶Ø®•SŽºžÞ=æR-×m½GBë5¦ÛÛ#)#)žç]¨#)[Ü^¯sÝz­yìönñ¤æ½ëÞ ^Eî;Ó²Þöˆò½ÝÒjtºŸ{¸|˜öΈ…O‰®:"öàóÞÇ›^ûg¹¹ÓÀ)W£Ò•íÝ0÷·W¨™ƒ¯rxiôú£ï§AÝk뵇>ÛžÇÞ³3înï7ß;›í»»»xît°Ûíƒî÷yîínŽÇ½»ŽàæÛb³×E“`Ø3EhÞÏYÛ£)꧞¶®‚¶Á²©Ïê•Øì/<7}µÚÞ}ÎÀ­^ÕªŸ\zR}ÝöôèéÝÑÆ•ÝmÆ#+»MÚÕÑÙÕ÷îw9ÚÏs#+ûk/a½w»¹Åëvî4×J¬”èÐíã2]ØÞ¼èåLêðn ·T;PãG{Îò÷Ÿ}®À{[ÈÈ#* ŽYµm¡::#)J¸ò·›]z›ì÷¹ÀØP(¥Ù·{”ó»[lì\nòu—[P¦UU–÷M—wb–ëÀH¢ÝµÔífí¥F»¶éî4÷¹.Æ4î7zu"ïXÛzÒ9s×Öí|CÞùöÛÖ`OcàB=± š=×Ýâ'·›´P¶½í½™uÝÏ}Ï}t:§Ïg5í¥`wwn:xº½áo;ÎŽZ¾5ñw#*4@#)™#)@M#)&L#)O%4Ò4ÓÒ#)hõFj=Ó) B&4Äñ1SÄô“SÙ&SÒz™='¨4#)#)Ð#)#)#)H„!4C@#+i¦$Ÿê¦ñ$üG¢žž¨õÊF#*¨Ó@#)#)h#)#)#)IꔑÒF“MOj&¦ôô‡¨mOSj#)é@úˆ=@#)#)#)#)#)’€ #)L€˜M ¦2##)Ô4É¢z&(Ð#)#)4#)#*#)ÐI¨ˆ  ‚hhCL©²O%<ÔOjŸ”ÊhõzOG¨€2#)#)#*#)òÿúF«]rOã9µ©7ôjµ×ñÕkõêµÞÙ‰%ˆ0S=êµ×dÕi“yU®»h­¬ª“ᶒ¢ £""à1=gÍ$=¿1ðÔc#+{Ø xƒñ™‡):µ*±„©æe[Ý\Uz™VXÅbŲ ‘Cóš¼é^õµp® :Á]ªðˆ„‚4‰½¡PÝ*_vü6¶:U4ËÇ\\¬=:|\QurõŒ<c ª-ÞžbqX|DÝã =à”à &ýýºþb'¸¶Óm–Ó*¬®î¥U¥µX¶™­V5U‹jŠ¨ÛkXѵE¥­µ½÷Y5µW³Z¢ñ #)‘±=‘Rˆ©a*( XYJ#)H•ÕV›kj«~#*m[j™“!d£3PÓ4ÙF •h™š„K#)š”£J)¶“aDÍdš FKSQª6#* hÍ%“I¢Z!#*FJDÚSA Ñ©cCLYShÑ›%¢ÔE K-4ÒÚ bZCf‚`Ê33Ô‘¨ÔZƒ$ÐiMÂ(Mš4” †‹,´Œ`E)´ji²%´Ûk+[F˜ÑK™3!4D˜2ɶ›m4ÚÔ”l¦Zkc-M¶eJZL“1‘f´ÄÑd¢‚™l‰B!Qf‘´R`4TH!`ؤلf•Jb1„($D‘- ˆÉ#jDÈF–"fhd‘1J0Ì‚–VX31"’ÈÄRɬ›,Q0‹"É –R4m)¦ ±%&EDš2hi™1 ©(Ò%¤+ ¦Æ‰"ÄQ3)iS0Jd†lD˜¦†fÁ0±ÒlK+E€I)65ZK‘&¢D£AŠ& ÀJ d”Â0™Be$¤ÔY›5„‚“RD“ˆKJl!E‚Ø€“Y”R6d”M”È؉›")FlŒÁ2©¦$„YJa³Q`,i¦†Æ"5+%’6(”R"šIˆ&¤a³LcIˆBI©%”e5LÒ6Š¦š(šÔ ‰–Pi6FB1d¤Èš ¦R%FŒÂfÒ…&5Á™H$$Rf0"Å‚%’a1A$Ê™š³m”¶%3!©Ä)›+(QP‘E"“$›$cc*i™¢ÅˆÊHRf`SŒ¬Úa-ˆŒII¦¦M3 ¤1”E!³DTØ¢ÍJ”¤¦ŠCddÆLŠm!E’1J2’ˆ¢ÅQI&‰$Ò›F“Q¤H¡¦6ÄcA2¢Í2›A£c0ešL£cH™ 2 ”Ù)LRˆ„,Ó!-fË ¶JÉ`2Hi,˜ÈjŠ ¨$MaH“!ŒÄƒÊ#*IcQf‘(š1¥4 Qja¡&e&(Ë&ÈŒ¦ ³F”ÆÔÉ”ˆÑ*´ÛkF ”&¦hÊdbiE$̈ŠÐjSl#+Y¢ÌRiH–YSfˆ¨´ÊÂÙF$(¦ÄB”DÒ!BSkùM®ˆJ›ÒÛ¢ Œ1´QŠØ­Š“)M5©&#*-!j6²MFÖDa¨i˜Ê2FÊ`%Iȃ134Ëd¶Š1))j6¢´dÓ ©¬Å‹ S-,É•&ÐTl«HÑSe­“,Š¥RÉS#+lØÌfjØÑdElšedJ™e6²U–ÊR–d[dh ”¨±Me‰-!’–¢Õª1!¨Õ£%AF«&²QZŠÅEH”kDÑ$m‹´bÅj6hÚÆÀZL˜Q‚ªL¢M1´É4­-ˆ¶4m15Œ[$XÛDµRÖ²ÅC!2™©­“D•#4ÚFÅ’+jU›c¦Ò©e*fË#+ÔÒ"5´ªY”µ54lBÚS)¬±a²É¬¬²²ÌPPÊ$HE"ņi01$Ch4&1#+[F¨J%E š¢²M4l™4”Y#*–)ÙH6DVYJQ”ÌJ”lÓ4 AE¢Ó$&Fm6“ccd)„SL‰#+1¢’‹#*1$dÔ#!4LÔQ¢JdR–P5BTÀ4˜³ J™²Bj-"ŒÍ¨ÊQ³)$£ hÈR²›b"˜m&ˆÆ¤Ù,"‚ÉXBŒ–Š †bM,ÊTÊJYJÌ2V0‚ËH؆5"ÔjšÑš"¡£R”›#*1RÆÙ”–£@‘“ LdÄØ’¤©2…)¦6I ‰šÂRš`Û0Ù¤²lf“E˜‰¦”RZdmY¦…LÑ)aD‰!Ê2‚!“F©¤lQ¢³I²S±1II £h¶£Q¨£ŒZ3-†“#*$š,™¥FÅZ#+Í6,†e$1²RÊJ™m@F±ª5Dš(L³TPX ‚MIª5L1 Æ©JXÚ#*)¦Y#+(Ñ$U2KDª,EˆÊVÙ(ª-&‹&ˆ¦e RŠC2Q%M‰DL«Q°h©‘±dÔmSiE²d¬[ZLRVSl‰l›CD©%2 ¥E„ ƒcQ²Q²h“ˆÛ$Hj)&`ƒVdÁ …F”Ù›Mcj*edÛ24…4“QHEQ(Z’±l¤–6LE(#Iƒ$­H–Z6†Z‰-¨«J6“["Z±©(±H¦„ȦFdf4Ԉġ©›$ÍhŠ¤±m’¥•dÖ‰›R-DlldŠ¨¦U4­£hØÛc©3J!,ÖV%¨ÅbÍ*6¢6•*)JÒŒÅB4ÒY@Ñ¢ÁTi6ƱfT‘ZJزm”™Z4kÓŠLhÚ6¶ÙmX¤L¨TLIDb¡€D†ÒM©†2M´m‹lZf­£ÖZJške,›jjm¶¤ÓV&Œ¢ˆHJ(6ÍI©™›,ÔEbI2’dFF#!d¶‹2I4ßû¿ý€¾ï•ÿÇ[›¿í½3þS܈ê²*Jaêý¨‡’R,õ±DB{$ e±j©'†X;ë#+ÿ¶ƒ#¾?ÀÂð $I6|'=vßÐó±½9¿OéëÒÞ™MÜrª=ÖÛ2'ù_ñäYLâväPCo²³¸E ‡G±.)$M·¹‰Ã>f‰ÏñU#ÿGùƒ?ù~{Z?òJµÊâ/e¸+(‘ ¾Ç4Ñþ{ÜàõJ°Ãàvûño»¡\àÆT5¶ÃÃ5bÓ¶'$ñ–¿‹#*R(¢†É8¡·'%ˆÁýõ»fÍ……£ÈÌ`I¥Û"lÆLhƒcõhÉÉ£®ÉÞwHLo\»ÓÒ¯Rñ®YÎå—ùŽ¼üŽ³9«I±u¨S «Åš&U;1¥Ì¡LU)„¤ŠI1H°Þíº P®‘€‰‡[•xó®ØeñnTŒÞ›š4V´"ÁS8͆@Ö÷.9_ûXk#)àá#*ó)ŒX)Ú¦ºòhñË ïv¹IlF1IùÿޢܼYÜñzf´µÕ±S )´)þY*̶Ñ"Ìi^­©@Ã"‘ÆnDçD”á÷éWŠ÷†¨XœÐ¶ (D%òþgËý×F[1ÝD!²Ð9³$¼nä ÛâÄÕ­–F4›iêY#+“qÆA³¸¦W6éd5sn¼v$ØØŸ]×ÊmxÚ,EAoÏÝO§¯ÖW‹ì[‘L¯‹‘mËÿ9 ƒòxë#Q¶~=¼¨"!çç˜Añ´òe¨Rh˜rÆê Š°XOUÔ†Ž\º2MÊM£;®QéÊ7Õß~z»»\¯nò^+›Ý\Ѩ8šë•ÞwcrŽ•Ë™(Ë®é$s™cé¢K ÿTþÒS–ArÈ6ÓbÈD‘a#+hg†ØËËV,öQJ>´š~-Ò¶í.¾ofd4x ‰_îQI»)饖«PÅCüè`ìÀQ³ûVá%.x×Ûˆs@ÇG㡆Î[cÌÉ‘º!$Āšê‹|Ù{kädu¶ï\’(3)£^··vj¼1»kÇóÙg¡øaN#“³U›x“:8XMÙ†ØFPÑÉýðû&¤Ï<§^¹ŠÍwÔPCàÊANME)ðëöߣž—ÀÁîNÝÚÝ95ó¨RDUH„ä,Œ©Ü›¦tj#+M4+É1e)Õ–Ód|h*ô¡ã¡e³,YI#+N*}±ÆyÞ>×åŠUË"õÖ“UO¯pi)O$WàÊø„ÓéûrH›†å¿ÜÃ××oL[1þ[¬3&)}]uøúèÈÇCßAbæ¶Ä­^¨ŒJFmǶ®Aâp¶˜=8 fwŠ^w0ø²µð¯õ{(ÊšÒ…A@P#+m–™5ÆÕ͵þ‡ì÷’>·S6½•^41XýÞ?¶Ã³4E#T™Š¾çÇû/6½5ŒcšŠO‡væn#)(5Qžë#)£6Ïi‘ÞÒç²à”\×-±zï˽z¼ˆFiVJ‡µÜ±h¯Ü–ëÝØü½qQ¡7Ó׿ÃÖñO~‹FÉ·ÐûÍãƒIo±Ð¨°R:§eè!íÅx鵘OrTÅB™)¦jKGñ¨ræPaŒLÖ¶jϽ$ô¥+?+³òu·e dÛð÷DvU»½Ð´’ºp²ðÑ$XS†Í²]#ICœ›HÕsÖžM]·J)”"’ê­)Ù¥®úµ‘¿Zž»øVS“–^ÖX1a#+p(¡M˜j!i«L‚$¦õœï0J» *‘£ÃDbá‹MZûÒ—ucg²~,'~6ÅQXXy²Ö"<4º:QS’xU<ßb_3²Šå|ìá®Ñ 3VoÎMëËX¸„³ú¡ó犈rõWŽ`cï su‹$ß²)kG›AÃFš5Ú”ØdÌ(Ì)¿/s1õ÷´óMƒLOýæoÛòõ€m¡¹ÄîÃÏ$|2Ì¥@Œ¡ïj"*i_mQû{»{p}X‡aùRi“Ç¿WìѤ-´#*©ÉÍ{ˆiçºRJñ¥ÉnCMÏ…‡×–ìƒ1nÇ#+ÈÁ61K¦Â¹vB9&Ò#*Xe5úèÙÆÔyÕ:”éUáCÃÙs“…“GÚ…$ä¾»Z×qZ‡#+9‘JQo`·ÐéX¹O q\‹åN¨Yœñ'mg2||Ý#ïӌ֒ÈþöÜ\KÉŒP6@{"i:O8–ƒhºQXPüŽÑlŒÕ-ù‰±y²…èú)<!ÌÉŽ“©1¥óN”8žrâ÷ÜÉ~NZ½ãpödn®$g ¸esËZ=M]¨ò}Ê‘¡ýs‘ë¾ð ý®07¨UX¾k”‰Ó¾­vT9¥ 0àD*Ó?<cµ4fé\[Š¥FyÆõ#+I#+CPXÍÉg½Cž¹Ó›O~™/gÊÊÉE0F:E&©¯}Ùž•Èk^œáÓ†à~NX#*É `8FW_vSr (´è=Fj/~wfgÔÑù²þ[TÎÅ<ytr ÃÛ´iK¸é$Ë'¾!réE·ã s7ïÂ41¯Å¢6¸gl&?H Ç×\â÷[ ¸áÕ­Wÿ|-“–F·&Ú!z]ô\kÜíÝÆÃrÉêvµmŸ cmŽ~Óçh×0ôzíëµø˜ë²I",P>îaS×¥)F´ÚÇãš6¢ #*΃`w©Dï ”T ÝfĈÎ(»W\ÞN0›“í!oǺêŽ3»º„hö4{:Œ#*ñ#+¸øM-;¹È2°âV¶ã[ÿr‹Ýf6Øq6>ݵXÙdȶ_»ï‹îfŸÕ¿'q¸v³™­NúøS#Yöbùd6CTEMØßÛ'Õã×è×k7éÑT^D¤AèëEõtE¾§Ü®½wSæÜe÷º(A¢P¦3ë¹~- UKTWúXRŠ™ò¿éºü¿ý¶Ÿ/úñÕ%}ÍE}Ì?Bñ‰ó»o·®ñ`¢*~§no“=b~¹·ïùÚ­ô¨ð»šUÈgóLZ{ŸvØìÅkêܬ’'ù5·Xýð黄—n#†9ìÄ<$’0 1,áÂ|Ý®>¸ÙL¶T¼å0$Õ4ÔºÓÒùÔBQ³”‰ü\†‡?‚л´£#m* ÒŠŒøÖ×'Ñ; 0ÔO'IXÏÂb«´W·°äÅÏ ‚„|‚.aûâðXŒËO˜àLTÍéC]Íø­ Â`áGÙE"¢¿«4dfýš\}(¤Eàf¬F"‘daIήü´©¥Yç¥dÂ|wjŸfýs|4H¿mž#*A”çÇŒ0h´ë-+‡mènnΛÔrÛòy¯=×m`p¦;öš~—iÄë–Ó2 §Ÿi>5Îe 2}–¥Ö™;v¦dMŸ¥¨ù&;ÝÅááÛã¸D;/[×0Êe¨ÌõÇàg#š<Ð/¶ŠGƒ5¸SÓ³k˜~ {bÂõ¢Ø ú’Y×…é¤DQQe0¨x~r‚Û{*©nf Øøg·sèÉð„kÃ#bÜ󼺟S½™!žP­ m¶³ìª¦ÞHóæ´éêÈã¼ÔVm ò§w‹ˆ~W«5FA´?8.†¥úš6e*DF/:‘h®¾˜åʳ¢q³ý½hñáPÐmÔ‰lðaK–R‚±7gFTaes×^¾o#*‘Ò÷àbì±,¥7KªÊß\Ï«fÜËFá5L´*éù“÷9æ]L³u·óôŒ,aï?æÐÎÊÙu9s}r~û)|ݳ‹Ñ-Áð‡7ÒïŸgÖ"+EÅ3|E£[³±è'ÿyÂôî¹nó®[ñÊ[(/ËÂc#¤ÎMü¾5¸º;7„Œuv†kBoy ùqòª#Š¤:¤*!šãü…žãß¡Ë¢¨@ÜÚì…2,Sæ\&¹X±@•E©¢$@Ájô°Œ÷SÑ ¦ú%ѯ9Ar*¨[Ú­ï›—á„9Ù…H±T›Ò9ÿ#*|PוÖ+ŽJÔemA×ÂŽûis…õWô9šÂÐä^å!UdT±Ä ¨q‘¹pöæ÷¹ûÔ¡)M¡_DÏO߇7•ª9;¤Ò™&)¡/)ãUE¸ëêYùL6”î8:<öp½wœõ¡íÑ+‰q°.~ŽÇäô°ô?­“á߯•Æ´üwZ·ÙQgí¾Y­goKR!e=Šcë—ê¥ÕÞºø“ŸÛ›oä¶Ú`èéΓq©+¥µ\”GÓkšvji7XÁÀœÔH0U4UtÁSfŸrƒD^8Ç·™üß˧oy±áóã=˜‚ÍO*÷9ßú1‰btE†WZ»»#*&5Oçׇ‡NzŽåÜ\패gÆv“Ç“:T‹³Z”()ÇÃ'Àu€®•\ð><¼û:è<&.Ò#*iEíƒÙϪEÙöP~úáPØë9*±‘18pÎóM‡µú0ôÞ¸Ätëm«Ž²HøŠ][ÑŸVµu”·ì©üûS)¡#*:þI)D}èá‘ÈU}g]_­“lS­#+´íŠ0ã×D¶ v>wNšÛ­b8mDzØÆó-¿òžx½8À}¦^©i»~8õß•ÒÁrõn;¸wCéÖƒ‹•O'Dô׶øæ1;ÜÂô˜x< •”փܛ÷*E''Ni<·HôD}¡*¼Áq½Él‚SìòÁ°Ýç¥víãsx±-¼kÁÈ##ÜÒlëËJÖñ‚†#+½sRõþö`ò4£Å™‚£"Ïæé›{ª¢rÆ6uTú®¸¾•ïÈnä²°<H¨ËÈvA²„‘ÂË»å6<8b?©š-íts¬PZk%‰|e… øçÆ…éJÓüj6J¸É8Mü„ÃËêëóñåf_~²eèt³"߈d¢/ø ¦¸ ÆF=ªDŠaø3eëyB>Hý2Úú¶ÇHÅ‚”Î~·\¸n‰GKùTä·÷ç²¥C­rh¥fí¨Ÿ,ù8Œæ`Û{åÌÂ3Ѹc0qÖ£QËãAdXŠ"ÏDôÐ+–j_Á¢Ü}• º¯ÅÐYŒ‚ØÏžÜ~Ú?OmŽ´v¼ážij§ØœÙŒNIiÏ_qßη÷§ow…Ò¡¿G8K‘åU¦ý€Ž›?dÈ~ó:Úâ—k†³Ržú¨v æ´•ÜÕ  ì®Élßj9µú»¶Û0üÞ#)†éÁ3mpŒ¡-#+F,V!ý Œ©­µ„\bŸ« ¡G·5:¥3yÁ¢šfXøµÐO‹§LÜòbÛE\É:b£Em¤MÉå#LÄã¢Çtˆ”бÞîDk³Æ6Ñ<ë¤8™äû!û¬7ÍWËÝRçz-s½žÜºú%У$éÇü„Æc÷Íf"6½’.u¡ËùÙ^E¤J)*&h=Ø®9£¾…†Ž®ŠøÓXKa i†±å›C«Bâ¼hJüB¬lßÙlĨÁ¤Âè黌?bª<M`¨¨šÿI0^T€ðj#*Dæ¤GN6Ï6³YXÅlGÓg ¤Ff¤¡J¢P±F^>¼Râ€ÉQUÕ)YSWŽÝÛ›Äír)qÕuÉ×YyõljÀbkxs# <ý°Ø ÀêòMüy°w2Ô¨tÔ°õõ±$ù#+’IrHŽR‡Uï‰NœŠ|ÿž. @˜ËîÕbX÷Ô}Á¾Ð#)A0#)A©»ù²¸n½/ò\FÄ÷ä°z(T 9hÝßçù„¿}¹¹F*y”i>+_\‹z.½Œþz™Òñ‡“Ò>“J÷j«Ä3ŠžPS„}4›&Q¶Ïhsy[ÝQ|£*S&; Ù4 ÙöP¡!šÌpëƒ\·„iÛrjÕø¶×‹£Œêh=­~í9ÛqmõÞ"1R;ëj–QùkÎß;FfãPÙ@·OXý°Û#eÙ~E‚‰`©iF!Å1!ä8¥–,â­ò«ø]\ö=ƒ]ñ‡(úŒa‰¶›ÎqÊNŠy™Í¡HÀi/U#)Ãó|7?^•º­Ê#*0qˆ®ûm€|42eoæmÚàù>÷UÇͦ.óKdÔ}&ݪˆ%”côõB±Û=Šò«<©Ô÷œw?ú‹¥GŒZ«ô—‘g¹PD†ù¬da´ÌÍÈGŠÐ´#Næ¨j²=¾ù¬pH.]Ũsë]!ÛŒD}µ7Å×*´gJ¢ðýÞ)ÆÉãHg#U(+mV¼)¦UÎñÛl\*!"·Ž¬,•´¡c8ÔäÞÕ@á›^¾e\7ErûvóÐ-­4ºD¬Û÷¹ŽÜšHü– `6ãˆn—#)ÛQ¡v“Øýå¥ÚOËqñ1¢°>ˆ¾÷¬Óº÷âI7ïÏ“ô£*a#>êfÜ0“Õ†v÷^øÍ)X+4£Í¦õøéxÓº¸*aO‰¿tl1Vc;ee`àã=xwËc£8É™A nËÌ‚NˆPÊNKYªƒnÃôÞÌŽZnt¥vföíöÒbäU'A#+ã“ÑK—£s3…ü‡%ʘ“, æþ"…ž'¸‘ðƒƒQ¼Õ({Û¹eÚ¶Ó‘K–ÏD@JÁÖEé½Æ̱#+hYʆS¹-Ø.6-sÊØá0ç(<1ÄìqÛ‹‚‚ȤÕ&X ³5lS‰û9¿ž”;õµ¸\½Ü×ý—+=hË‘~-lÝù6ˆòYn{j(ŠRdW=Íå†QÓ1Ü+–Í׬¹´60æÕ«H’“¦Y.!\1Yƒ©m…„aÇk¹n²Ã¨íŮԸmÇ&mŠHIõ 'ó"š¿7îÄ”Ôh©2¬ŒªÅç ªÁÀÄŽ ¨ÆíŽ0€ù,êÐðÃûÈ…Í,|QØu11åGÕürßñߦZ]SÆ´Æ#*y[òöã¥O¦d‚@ˆÀøP%ÿÕGW–vT鎉?å‚z(x&gAaÉBC‡?öñæÙN\ø’¯vŸï#LlÇ܃Bí»@BØt´ÞRµùü2~WjÝÜÕ½›gºú@ßÆŽšÆTrÒûJ&ùæs/³KH–}ÒöDdü#+W•g}¥ÿQϪuææ:;bà-—Ô#K/+Ãì.tíçß­`—zó}¸jÂv-ÓJ#ã|וF~šäŽz·EõÚ„ÝÏC›œñ´¯¬‚œõMUÃYÁ´}Ë £Ë–çp Ð.p~A¶™º­f—<ºO†–ëAí¾›õÝEã;4JbNC•F$öštÊ%Ú_6ŽÈÆ!š 4«qŒ¡£=2´ ÓL”:½c&Ü8Sm€¼¢E‘j9+r9¬»jµ„N@RfV9ý’ü¦ºìm\½¼Ü‰O†þ'ª*±Å?¸·íÕÛŽŠ±T19b7;·š®q?ÛBÚçõ0Öê+ýùHLoÝ鿤Dk÷Ý×»/!œpäE‰åzùÀð#þp$œ×#*[ÿºþïw¿¹Æˆ½ÓÈÀW§º®Ïæº.:üƒª=–°(q™¬wQ>±aUÞÀÝb‹}÷ão#)#)‡¿¼¾½‘r ŸŸõ`ÁM3#*¿»]#û\·)ÍlqŠðñ®ª£ú-,‘"»$‘æ.ÛqQÈq|"ŽIú“¬|üüã^›h‘nlÛ¨ðø5y½Ÿ#*GO³XÚ!ÝÅW·Õ;¢=¥:¾Í:Ñaˆ¼:ðA¹TX…ä^¡þ#*NàÁµîBrAH€ 'Óú®ãøZ.žïKbúá“¥ÝzÅñ×@;@2Ùì-` üþ¼¯‡rö/öåïѤ_R4#)ýôþÕ¼Ú¿ú>×!Öÿ‚‰fnÈB$Ãxw Hƒó>u7##) ýÛÖ0o2ÒíÚp½_Ùû#*Ïç„DuH??=¤»·Êï•ÝίH=²àPYc¸ ô?ösÆËZkûÞÿVåþ3¸£]êd¹訉.ò¦(æS#*ùòô³íÈסؔÌ5íM#*áÂ:“U¥Ø@´¶8~xŠf„—Ö#)4§_Li€ÆçyÁpŠÛ«à‡Éí•o¥—ü¿2 ÿ<B‹@ÄTUÙĎ΃ÛõdðFõ\1cW6Ú–_#*ªeŠËâžæä„É×þ)û}Ëçc0t?I¯üÂL–d@ƒ¼\€€¹ðF¼†µx‚˦©.º-.xÀÛ9¾}:ÇÕñyà0Ým™ÏñoÓçÀÚ¦ƒÓ›œ¢;íK³v‚5‡G|]ÑGÒSþggðÌè!…5Ñû,Û`æèþm×m¾¡ß(þ}÷ïâ£8U}=Æôîþ§9 „­­z#+è³q»WöÈÛïõúõj÷wÑuÆ ˆ;Zü¥VÂiõcœæ֣܈mÃë?L|‡Ö2l”EIHà4ŠÙé1ÄR¤ÁáëäaÑ-È¥ÔA%Y¢êÅ„*sÈ„œÙVÔ×FS›¡{üçÝËoîç³==œ¢Ž(ì6™$,Ñ‹Ì-'òd{¹Vàïd¿0–íÛõM‚TÁÖ“q{Ì*H(Ñé©"'CýÜ›3–kyEk?ÂÊ•ô½¬}¾’ÿ“ëÞ a.|ý¨”m°ù;wÞ#*§Û>Æ2a™º&„0Ö›:|–½Ç—¿×0·ÈÎdYq÷™G¤·õéÁ0…¶xÔ«%[I(në½T$~W”ëãâ!>ë·hjI6õâ·Èò›7Ã#ôo.ÇöþË–…æå93O~6Fp“ö÷ñø+jf_´4 eÝd(K….ë?§2Íàóî«—åÚhfDjë0é[‡Ô ½µd˜šÛ>±TÆoí¬X[0ùçïñvý?ÅÌèî¶}QUÊ 6”®ùq—>¥?½kÐ#Ìv%6g#{`,d’·>oÂ,§Ã´ùâOrbè5)ïÏëÁº×¦[Hä‡ØmÔ¯¤†°°ÐÃýôt’ÂÞD¼Í·òÏ–û6.ë¸w8wk3{eùÿ£–º¦ Ýûu“UÇÒWKèÓõ"ÕpGÅxºáXÿ ƒÒÄ$Tèf~ÉË’e$uño†ï½„õ|cŸýÑ.9®ë߯Ìï0zr2}yÍ#‚˜}Åä#)8Ot¹ Q[ñ]ñYÔ.…´*}`#)ƒ™ÎHYrØ-ÞŠdpó¢:Ë_êíRzNša£ ä¡#+#+Ï×øÝþk9&)Þ=#*¹c„CWdÑƬ!TÎYÜ—Çö^ópÁ™áûxFv<–ºGtIPï°EG`ˆÕWÕ˾íÓq—9á´‡'¸Líý’Kçƒ:(äBûd¡4Z L1á-õ®`,•õ‘hTƒƒ;±ß›‰Öø›ÍW÷½ék”AÁr4‘#©ß¬uš3_ÓÆùËéIÀííÛb˜ÊÀèæŽýêÉ([ŽýÃuN: ‡_5ï/ Z#*a{ÿ¶±c±Ä¯QLšÄ69øGׂtToËN­7}F"í^ê=#°âˆ*B#+ íÃY9ŽõñÃàZX`¼P!¾N`XžÐPŠL“P°"­Ó=ŸÖnžøß}ˆ6NÈFaתÍb(VÇBœiÅÂÂwÞº|þSLøÁ( ‰ „)í#‰®ÉÖ»ÂF‘j4ˆ™¦Š[W~³bËaJˆ‹!F#™fWAB-(0¹\tÁÖ±¤s ÕñÅöëTäŽnšSû3 ‡–ºõyfl+¼F2¤E€ÄI&fûƒ3s—ž†Õ”'¾6‹Éü‡ù@Ú¤ßaEƒßNê¢o–³ãü+´ý_íWÅéö¹ÓÏæGßàT¦xìÆÞ^¸´‘òb‡À'ÝÀGp Æ b@1@00{Ëa(zÂÍÝ¢”âkˈ‰t†öÛ¡¡IÚì¨øbÂ÷‡—#*ê€FW´HqFYT_…"Ú,P~ÕµZI.3©Ku-(T:åE˜ZX¡2}uØ!;vÜØæïHÝ ïU®˜ça“=åjÖ†¡1‚sŽ©3#+½ÓQ}²•þcáJ7oQ÷HR'Eã¿9jàøÆâ÷y?šî’¸»…(=$yq¡”Õ#Qg»(^ɧ;B¶2Z!(ŒÀ­læÁêNEäªé¹ö Õf˜ÚRμÛÑ:]yìë‡é£™8·<”X‡Hç΢‡ètny™PЙF”:ݼ!ª#*œ‡lÆB¥œI:¥š:è˦Â849‹œ½BÃœ‘ºØV%UfeÔ±oߣœ)vä fìƒG<ž\%Ž•ésÍÚ"”„Ž)Xæì:?õ(çØ­¼Ú8ÅQ²C-nP°7/.#*KÓÕ/.—tÍ•ëÈtnª5<±VY6ú²‹žòö½x“ ð¹Â0±Q↸ñý¾¦Â)™ñ£<¾?~BóüÈ} •3ìSÑ;TëÕõ£ùöÒÓoA ü|*Ã(tô=5¸ƒ›¶Ís¾Ä'Ö¢…Îæ(#W2Ì4U>ÿjWzòòmç]„K×v»¼ó.×wu§ŽÈÌ`#G7²™)  ,ѯ-q¡æКUtÚc{ù^}œßv3—ÊúN^÷’Ê–Â+H…$ƒ`Õ1U…1ƒlj'XÐ×Ë^¸,[fˆ©‘¦ n8Ã#)H³ƒèrdCÀ5Û–âG·.+õ#*×J lÇ-$ #aÞ÷«ƒS—£‚MÝ8°Ê^M^ŸÌ÷Úïy³òȃ¾´^Ï´J¥Z¥òÀšH(\Xb·0í`CÔ[!‹±â ë¾N'—¿r`?S…Ú¡Í-÷óÍ *#)ªÈ´ç#+¼dÈUÕ<ÞD\ ˜Œ¡ý¢í¡ßu{:k4ImÇ0PÔâFÇÃÁΉΧ—ÃÖï–ùE¬©¨úUÓäL#áÒêeVßI6µNu­£°ìûúùÎJZv#*r_#c¾àó¡ó˜E±÷Û .Š  ?FVÄÀX¸7é@ˆŸ!#)F¾8ÊF´rê͵óh^ øEXìf£Mºâb£.Ôh»gºN^Þ¶5#*9ºÎëf)d¨n)Šl™„­â„'xÐñXX’)œ²Þk0˜™‘¤$`Q1¬aX£@ÞѤK"ŒÀ4V—-,KX!3e ¬° e…0Hóãö'aÇÇKêÇé±|»7è<à¤6M¹£%쮚½1jûŠóß­¹¶4È£&ŒÚ(ÞÁ4d¶ "†XTMPÄ—tæÝT±¡dáÑ"°+j±°1¶"Q„$@‘C(çmõ—c¿Ï3f—Ö-¯ßÓݶ#+_»[0¢³*¸ B¬¥yÖz%,è¦5`±|]“mH!–ëU#Ú€WÛ#)Rùᩬ€õÉ`ˆ–0ì@í~Ám'c۳̟ÆxO²ô߶ sþ‹tDèქ´{£^aˆ„¢eóoÑ©?Ÿ7srtZ&ìR¥º 5ýr§z­´}~[to¡_(æ{4ö°qNø…Õ¼)½JÎÓí–Ún€…™©t,–ÜXë¬)b²KoS¾‡¥Ï/?ÓR嵩Ñ2´%–®Õ@*q°ÄÀÔ²ZÍñ﬒֤ûög`ùT½`‘Í=µVbž¡„$§ÞÂO<Â+v“ 8Y,á݇ß~¢&#+?½]°Dé©—„á¤Ú í7R0°µ'v†HDߺß(9Öï­§øÛ0ó‘Ëý½^:^ɱ¹îË¿žá5~¼ÍfbU©Ü`z;Þl®—&z+šézµ#*s@¼û¸¬i£ÌkxMª’ˆ,V A…’ÐKsÄí#nø©.ÝŒ‘‰aÛ÷Žß±oý»0ðÆé¶;pCÇ`ìyQZ·(A+#)”ZÑı`Œ–ÏqþS™Žä œ._«OñÉ­Ï^BvB";3Ö)á¡HÏï|a¶Î¾šŠFâ¹VàÛû—Ù˜Óä^TNˆ¹ýÞ¹-˜%#)ÚLdCŽ&òx“VDVA£N”ÍL@ô2,*¥Ò 6²Jd :u¢Â€ë#`Ø)c¤QG¼É#*•$ÝCMþBà&¤Ò+J‡,µÆˆ£áÆ;§Î"7bøõÕ3^îÙïué{¼ÒöÆZU‘,q¶àëÐÐ;F¤ˆÇ¦a¢A‰±!ˆbÓ%Ù†òý/wîæ¡™¡7¸†ý±…×@ä¡»iÈ[ dikðÛÂݼm¿ME1¤w.#+WÒ;K³<8G'Ž©¶nX¬HýÑ߈g(q5ÆL¯+•ÆÎEêRiü©4xÄriȦa‚<j™F?«ëzíc;›G·U¶‘ÜdRˬ´¶<>ôá Bâ±£s¹‹©Áâ|ðß>~ÌpÇ6n)ɱèÇ­È.œÔmø@°nP’è$ºÜ°ÖõgW˜§€GÐÉK#+EŒÜpäRµþÛ–`é>#*L…F#+)Èý‹:×I±ˆO”>\¤zR«0«ñCµÊKÖI¤‹R×7‰ÙçɹœØ”äÛ›êT¨‹þ–¥—Žêí}ú½ãô6YÄŒOJ¦à÷¹9Uדÿ,}:î“&;`ÓÌ’ìø§6d]¸‹,öâÔß|?H-£AìuȹHƇ÷ª4¡Ú‚:¢¬ts¥ lV¹âé+Té‡F:Ä}W”½v}›ü˜Ût<ÅÎÂÖ™“‘ô”y#)Ð*¤\Òr¶ù7#•vÉm#)ü~¼ts»V̶4&`à×ùl5]UL«IŠ(Vj¡Š”1zõ«xk.iò&Û0âúžÙTn&éÁ9ëƒioé×XqÃÔxQ½Ÿ3’øJ§»`r'ã~Saÿ«0mp¼J%ŸlÅ//Lg•û>¶„#) “*Ä#ìM4×5~ø(LÄ´²Íà룫šÀ“siµ·&ëæ§ ªÚËA7cÎ¥7þÐÕœ‹šN.-R6‰6£ ·5k¥’5phˆ’ QVˆ©#*RK܉jºQc*¶CÊÁPDo°[H½Ëf#*„Ò†K_vjN¼ ¦fÖ¡Ìâ€Þ5¨5–iUÂeúNQ×8Ôèö'´öž°ž@ûP€w—AoÉ[¬ÌN=û媼ô[¿´¿ìòñJü¹Ñûž¯klñ|{øî>PÃ@¿K³J5Óth,šŒþ+¿^Ñû‡ݧGGZ 6¨¤!òªZS<ÐêYßÀÑÈ%øÃo_.ûú8#*^"ƒñíÅÖ?‰«j€þ©ð¦u·+•C ²q›_S ~ܾ¹Þa?¿*—ÙVI|gâ­^H¿žãñ–øó­ØܧŸq\f#*ep}‚”2¹ð<cùöCÍb´G7úA÷,ŽywŒ@–]ð_Ëð.ߟzuj#)Q!–^ñ¿Ãõíw…–HQ›žÛºú.OÍ^n¾·ýü-ŸgŽ_TÀßëÒýçå«GÓ³iûü~í®Í¢È7£®Œ`6Ÿ#*L=‹âêVAÀEƒ‹Â;Ü’G<Ûû½œ}'DŸDºáü'u±îúð CRzi æ#+¤#™T£ 0d býÿZŒ0 ÷|ŸOÑ–ìv4[Æ#*?d˜ÔqXW7ž¡™œRë)·_Ôâ­„F¤­¹alÿgø»2#)Ghøí#)F±éñ2À¢“O?×ÛîÝ?˜#)R•PˆJ@J_ÏÕmϦö8q{Ǽ¨ñí;¦7õ…ç$HfG §3¢**9S°¨QlFzZ¦mIdÑ& 3eK‹ó8ÌMlUýjôvݨ£X5RŠh€TI\Ki_ب뷇¸Ý³ŸÞuêMæÜõ›“#*¿­ü8å¸d¦¢èÓmïv …,ûï²æPXÁTR™*Š @I)R çWt/bkñéÊþþ¿SWì•r‹46„§lç·øìÔ„_—óq<ï“ð¬Bdšëêö™|›ŒVþå­nV5õ¿7øRÏÔÉ¢PÑ™.ð)92«ýšeA­êUÑ#*·¤‘µÒŽò® (aÁTUS!<añëÐ?Ä~­½Âó;hn£m6#ŒE,Â/‹8h+‘H(#*ŸÙêmmþÎ~ óºaÿ:Eë„úɃ*ÛW—÷î77£,Òüu/óÙïR”Î4ÿ!DºZÒ[0ÒV¤Þjê^þ?¨ãs8kV¾åýÇlV#*Ï)í,®GG%Äâ@?ÖŒQDY¬|¾4pǧï¿ì¾9XdRÎÛ(ñ Y¥•Tsg ŽCV"†ééKQˆ*¡cT+÷ýµÉâü‘ð @ý²¡C`ŽäÓzÃ"Š„MD¾ íÁaƒ,~räd¿Ú[óxOÊ#(×ãîlYCq!7iCôD~Éh&©¤ÊŠ¼“÷Rä@pÁV“ôJd–͹ÿ±³†ŠÛe/oÊÂ7ǃáügËók£çúÏwåúþç~¸û¼¾¯«ë|=kæçLéí=0‡-§Û§¤jŒ­ýCdÇøÝê¢igOŠöCñ]^¼%0î·¶5#WÐÆ núÚlÁm9ó…æ‡ÓVû·+ÃñÏÁG@R-Ñ`ÓÝû"à:D& ^ßé¸]JÎ|lXÈó—8‰•óè‰íºþ¹(ý3ÙÙÓy¹/BÆ»]ò è³ðˆ]Ì»N—,fïðÓá#*ÒmT®ý¹¯ßœ#*-àá] 8;od7W|F†òp*°9jÊž6Û’Ã@T_½0c¾€òí½KÞ¡¼QiKû1*ÒÃâ£Moäà#*‡-à@;¯¹ y—ÄB"˜ñÑŒ÷Ӕ˯xÿ_¸Ÿ)™úi£þ>™¥òƒ'QÑ~;rœÒ}øçÊ]¤5áH@U|8Lf-È€ð7J‘ÞñŽ#ë?¾‹‘˜v0ûað ÷^ðño(¢¿hVø(¸Z£0yØöþ¯o¯¤Ìøžœ6ºTrÿØl9ç—DÉåÔÞußÇÇ#)j#+NöX´¦S#*n…Æ#+ŠÆè©ãû¼7:Mv­õ° Wí¾ëÝÈ­-5#+ÃNt |`™2žJ¤Ò¤£ÕDÔ6°æ¢ªš+0mD(pRsSÖÙè_ÑÜ¢B~û*õõÀëC„y+ðˆeyPd+Ê‚J´§ôÓø0ÓoÈßšbÝkµ'oÞ‘Mï¦#*/½SÁ5ᢠÊn䟦¨ÌÓú·ñÁ“?!•‡‡±Ü_¾± Iz¯²}´õ±$Åa>þTW¦Vˆ4Æ(H"¨€4š¼¿µBïUWÀ%Ägrõù¶4#+b£‘ó–XX Hà¿Ž–ÉA9Ùį©X“¯TCA˯צkûÝODZW(qH¡O€#*ÒP- Àn¡H‘¢¢¢=©9ñ=<§•ýœ9kìFÇF–Äþ•7ô Ñ*å %¢T-¢#*DK)·œ:T—żÔWןÜižiÕ®ú Ýs­²?¬À<†)—]üÿÏ«¿Ùáôï槳èú.>³”¶ò늪o«ŸÚuúé£bgòýVY\8yÈûëìlç鵂½vé|ÓJ[]çrå®ÎÇ{_àÞÃúøn¾Û¢Ý=ßw~Á¿oÅ.[ìµåzaè¿"¢Zªö×ýÏ×cT¸yOŸ¹z9¤×{áGo Mû3ôhrŽÝ÷+Joï"ÑÖí_ŽèÿÝ}Þ­ù¢ðï>\³Óþ=>÷Ñæpm6\\,'vz5>½SÓÏã[h=è·‹ØoéÐ’¡=ŸÒ^èƒîG!#)ËŠ@i>þÂ+Û¡Ý]줧´ÄY!ô=7ãñ~wŽ*l–™qçédÄy®¶PÝw:ÙNÍñm8ó½¯}›.dé§^ŸCæÙ¬£uƒé€×ÆŒ4õû5ý¦×qêÉ9ðéþ“¶1†ª¶¿•ãTÏ¡å¯fˆÝkè4ÇkßÇmëiÞŽt= Vîh]²î†¯L´ùÉÑ"ÆÑÇôhø[oîÂë­Ù[ï”­Á€ÛÈ¥(ý}ùÙJ‰ytö°ù±ÁñóÛËŸÖ;uç4kÏâc#+`|LvR®–TÌýÏ-?Íð9ïQ§™ãÇ ;›ÓË›–ù8rÃnïAÛ¨›c†çyÈ—³×¾VéôÞ{©=ü[Û44<T0è",‡“»›ŠÆC5-:=vGŸ£U\RD‡Ð8½<$¬DéæT,4ÿÒë w{ýºÄø˜×W=:Ÿ¾ù}þt4«¿ŽþšÆ^OBöþžïGœ|.͇,†—Òq˜p;E5ŒìˆÃ·lmöÃj*¾ªïÆÊýš<š¯³£Kûµ jØ[6w¯Ê#=+èÃc'Ó!Ïîá;»^Éu?è™}ÆÕ•¢‡£cäúdኪjßñîŽÙK{{HŽTû¼³¿Í¶Ü(£Gïa2–|v'FKõ|¼5ógÿAÕ˜½¶›ÿ7jy¿§·âŒô:û-M¯qÓüò”½p?¼+»W}þ¬;òïhmø€óLa÷û§8·2÷l¾>ͼ4Bƒæu ûô"¼q+ˆQÙ‡£óWìÀu0Ù¦Kð­£å×Ìï•óÁØtt¹³‡¿ªMóØéæ¯Îÿ‘׌›èAåí_,«þ‘¡¿ðÏB8a¡‘S’€GŽ`x8W–ØÏUŸ·ùVÎϾÑÏ·<è?¸ýIûù]§»FÊÝÊ p§cÿÇ?gª½ÖÛüÊN¸~ŸÜG£ô…•®wN£Æ^fÉ¥Ûæ#*ü¿F¦§ð÷~ô%¸PŒ=î1B,Xןúß —˜aúyyiŠÿЩ´Þ¢WÞ39Ôéw_/+<Ä~_ AõÕE€(Xb˜Ã¯C¹?òúµs¨:~ïXOßÜ9SÑÒ0ùõýÿ7Éw>´ŸWÅ/gf}Â_ˆ±™µ÷õŽÛ±ôOSõߠߧÔݹsD[lêÙ²ÕÙ·ëÙG Ô…Û©—è^ô厮Ë?»–‘äðø§ÛÙûµÜ0û{þîœs±èßÍåP¹nM³óLÏ.ûáý,gëÕºÄAþKôS÷Ü“yÇËbhèvÏñü¿nê·O×\Ü4Ž/­ß·à¾5ÅFÏRpçŽÿ·7 ³Wi¸tÃéó|Ëéû¹´§È?oðÅGæÑ€#ùL8|‚Ƕl?-ŸO8ßÈ”~ϼü)byŸœkè€G*´B¬¯•öy-Ëc»ÂÚ‚¸h)€uÿ&f®X®ÞCõ6Ô¥ApoïNnvÙé§ðòuù˜]·Ÿ¦ù.}Òù‹ý-ÌìôÛßÅ[O'þéï¬I—ÖÄù'm<óEX#Ëüÿ†DPw/„)ò*Œtœ<f/»í؆ûçÍÙΑÔýñØ Ÿ°¨§Ã¢@æm,#)(ÇyPœ™¾šƪ’÷0alµK¤®C2œu®8ek—hltx׺bô+Ör4.l?¤"r:J알렦¢i^ŒxiŸ’Ì°ù|w—»FŒiÕG\8©CnR¤Dv0`5>»°r‘Î?‹¸É-©—(¸Šï^g`EÃÙ38KA¼ŽC¨nÔ>ÔÛúøiÛ螟œ'Ïöoü…ÿ~˜sØMƒºX<æ±ÒÚì/#*" ËÊå˜+]ò­iDWsÌdãѲְVŽèù)óÛOÆQ/ÜÆþ/ŒààÖ²òÙÄQ9\ônîZæ²› có"µ1þ´ö¼Ýãƺ>õÍóÏ¥]päÛÔæÇì?!O]PB:®ús…G}ëeaµP¥u²™Ñ­3eˆ¤Ò*ú¹qþc‡/`¯øÿ“'–{ÒViÎô„µÏ· ºô ÈhÖ.à-¼ÞœÀTh–8hœ^"®Mœ+àñ§ãxúõ7žÛîužù&C„”ÄP¢G×ßX ?=ß Uê<ˆÍ®pBç/ÞÍæ½~Y6Ã>f4W‚7¢Ã˜ y‡Æí¤A–?.Šõ?ìùõŽÏŠÑaǤQ=º<!·JÙÚºÊUó5Kù§ÕXIÒ“9ïS¬A Qí„›þ9ö:ù 1Ezt ­ú¬ÓÏ£¶Ù_]UþÎ÷œ¿WàÒ¤ríw#*Õ·ê DóæL™ŸÞXùofÃqë|\í?ÓÌ~(#&q°Á0â/]¾:êôlmŒ<Ç‹¼ßÇréu¼¯íåêgüÙüúuܯôÝè„`=Šv¤“v×ÓáŪþªNÆB'i=ш{«c­˜ó•á8ô~N­ž¼p“©äþPÕðThö ÃxDä½oa•$•åˆÇaü¶Í*?»ädªS–C<ž­÷âQ²JyGÍó÷X7¯–QÌ#).ymÁ¨6—Ç`+wéÑ?òüßA†lŸ:9Oò*?pf@ç9œå%”› Y`Ô‚#ƒŸ­F5¨4iTlTŽÔ¢¥nØ€Ðæ7#+ªÒƒÀ¢¢,tÿíM¤`Vjîå5fYš¨èЈD0QJ…ÑŠ[M`ƒ‘cXÃL’˜ƒVDÐÐÝRE,h5b¶¬®9ÇØOŸ#*Ss¬6ÐÚ]&6) ºýº[xºÔ90$½¢ÐžìVŽ6bí¾ôxÈÙœæ3¡((>¿ÉÔ~kÖS<¦³ =a‹5ÿ$´3QÉ]E··cˆXwBñR‘dK¢Â`z¿¯òÖqõÿ |&àÏ ôò"4i[F2DHèé@ŒÖbï¡i7¢C$M¨R%dl!#*YRM±¦Ù…¤’©$sþlˆm»gí:>Ý ”ãoÏ—ÍåÆ噧?ÆšþÁ?χÉ>“}ìþ8ÒØ$2p´iŸY²Í2ÆsªÝu×äÏ–û}ÇGžÍ•åð×^¿Ûåø6‰yÇ©VÏ¿óz³xìF_·ª.êèÙÃí2N¹'åü±}º,ú²ýZ$ªAÜÂ1lx[~{4˜ÅÎîô,«eVMq×eoùç²9ñ5Ç0¶Žë„J0Ê}È(H’AáߘºêÀz;=ß«ò_·W·N¯gyD´A˜¯Þ8s8#)á /ÔQMŠ¦ï™À]q×ðt#+ed,á¿£¯8táÁ¬‰:ן÷YþG½ì÷¬a75¡…Á‡›ªçòþ—_0©­Ku;âý­îüz5ù¶+uój»˜š~›²³öÏñÓß1l–@L/í‹í   ô¿J°(¤}Ý 8=u¢ãýQNñðâþÏ^vÆ&ŒÚò1[ª-«DŠÚPÈ9+¥®–#+Œ0‡q,j‡Ó[»¦KAi¦ˆ$J‘D›·||×£^·^¡­|š©­R§§"ˆƒPlchŠ1J1BÀdQØZáa++)YlÿWÖ£F¡ã*­\æŒmFAKT£Q[*‹£86ÁFH2#*Ó©7›¹äÌ#+F5ß”MÈAS(Á„˜0£FÌ&bpïuœÚºÙ) lcQ#*¤c̉†fUø‚4î::RÚæpI C†ù₉mh  £üͧ`2ãzXUÖ»]b¥0Q‰‹$I•8‰ì ~UÒIGù E­,I´W2*œ„ÐEsEQUÚ™QŽde¤cÙ=€ÓDérf†Î Ü|ªÃAI$¨åJCÙo!AŠ¼–„Ý"÷²^ˆ1ž—báÞ¾¨ÉÞ³¸a/úNîN²ßÎdjEböó­ž:aù×ßìNãÚ5ÇæTRÝ:s^¼>º)쇻çvà ÕSž‰º¹"°÷?ä±]u>ñìÏ–ˆ ˜UÅÉâ0H#Ç°=z™6Ê)R#k“¡Þ©»”BƒžÏÝšV=A{®_y}~m‰²""tçñ^©ë§3zò×aŸ…¿6ÁÐôþãU¦Žq8³óüŸiÒù?¾¡ýoQdOeÏp‡K‚Ar2ÉNâ 50Èhû6tMHåçnãeD ~µé¹µ÷z‹Ôª¥ÈI%™UU®ðQ8$whM 9,HÙbzy¼‚B>SÄâ‡èþéË‹è—åó¯!üz=‚÷Øþ#+‘-Õc®>M¨É¬’[ 5þÔ<SÐ÷Ûd$Ü>’]þ¬ù¿L5òøo¶ê )Ù²gF1ý#*/¾²XnFDy»:|V51ï°9$, ½0¾¦*»œòI¸HKöŠ×§#*740±zä`ÛŸ!°O¿¯®zï÷ûÔkF …Tf@ ’žÙ"ªáï&ˉw\í{÷[ÎÓu_×¥]`OóåÓy¶‰yteŠ(È7b–¤dkîô{eý>\ÿËç·ßÚ4þ˜UÃ!”ûûþhyž Å“Q#+#+¤XŒŒõë›RfDQÌr¡Kò„ØOÐlfrñ¦41-6—·n›ÑHÚ)¬1=j ±o\ËA„y©Y¦=Zﺊ°Ì#+ÑFy# â¨=¦fhPi12ŠÔX_;š&Ž-/ö؈Ä××&D+‚剕.œ!&ˆíš™7ùI%‚Î6Ðmlst+¥#*ß¼Œ†hŒ5`£yTðeLzDm,ݹÁ²#+¬ä$d]Ÿ#)Ë’ËtŽjÐøÕEfÞ­;BÕmÆ6Ü,‰’šÝëq3cRbˆlfÐrP¡™&%‚L"#)´nåµG[#Hê¡Âcm'[C®Ù`Ø2麰!05i™ÈgNªéÿ=Pp…©$Xj§0C^ž¼eÌ]w î:õÔw¦P(“Û Ö­¿+¢á8Fñ™ãž )JÁ`Ù­ÂÒ´ÛãûÏâØþOØTáÀ1ÞË·räø¸Cw€G”;ð† ÷MIg瘸(!Áàƒ‹1¦È&^3òÙ÷MÌ8aŒýæˆ=AÀ¯/qÿ~3Aм}ÛåÌqµBÝŽt¢zømG¿6 ôûA>Ãò#+èÇW-~¬:S¤Òh v¸-ž #*áî..T–AbŒX°´ª¯Ï“ U44ã÷Þ1nEtˆHé7^ZQ"TéöO£KVItêDqH2$HÝMCŒÁß®ÍEĉÔTíšÃ~¡£DžÝÞg‹¶ l=½±zM7§B‡v<„WÁæôk@KEÓÈ´A¢#+‚q64(¸ÃXĸÜæÕÒª´U…N3éý”uÆb!П:¦”ŠM¸Û(ŽoÊÛ†V~[ÿ¼ŸÙ×~ßgœtþþ\`<}âï^‘vZ¼Ióâ<=ŸÏÆUî« ¢‰ýП‰T§ÞÂ#*Óî›? 4.ZVƒ#qnÎ&:Ç”øíxöö›ÚÀgNŽ|æ4Rë«Úî½æé˘cŽmcÒ´Â@ƒÌ}äu·{ªŒ¤ï#+è»2ïÊXHÜQàÈØÙùÌE±å\b0­A¶œF:7„)zÑ6ñáE–„ãZëyž6fDÙª\â]Å£‚`ÆQe…AY5d¦C`1T‚ùAX„oª$e*QM5CEZ¬-Ý#*ÿH®YÚÃŒccæCB;:y¾#+ÛiypMDˬ’dnŽ’`ºh#+Í$¶.ÕcIŒçš6Œ]Ú „O–âDDï{´5#*8±¦w(eŒÉÈÚèèÊ&E½À¬vÅ`j0t¡E&Y#FS%|åFX3äÐg~v™nÔ÷tË„.1z~8æ[Ê3ʦ6˜þ‘ͪóœÍ[¦ù m†²ðÏ©Ó‘é'Çêä™ÁÌŽp8QZãÇmùá¢hø*®åûÐ`æΩܔ;ÕPVü°M!”_ã§[m¾·mŸcFSÁÉÈf=¼Rà¸@“mˆóc®1´¬òÌѤ_C¸5-Æ«¯:úÙ¶ØÆ?8¢ðÎ<o¦j6äÜ‘R®©n1v6«D˜E¼ Œƒ$M±ÕBws=ÀºmŸ(#* ¦fzømãEï˜×=¸ça¢‘¼—43“¦—B#JHçXÌX ()ð¬ -­óU{jÌÞÜ5¶ßÍBôÁ±ý¦Åš°¤<Ci‹7t Ü>—ÜÛc5P ‘0Ú±ÇI¶q·kYäp8Íé0„$Å',¡ˆã±5ùC:˱°`¬Åü‰X~ÖqÆÞX¨aÐì’yØiœŽˆÅ{q¬¸Ã}Cç¹ÃÎFÜõ£#+ÄÞòî~ëa™]]Íú’7›HIƒñmßS/—LíyÝ£#ZÝõy –€t×ß^—@ÒÆÆÜ>QÒcVò+UZÞLª‚î]IK‚´*ùNþjì;«Îºc…ÁÄ`ÉoȶÃ@ŒA‚Í@G!¸xL¨ÚR‹x·Î-“nÚ×:ä¢>é%·‚!Dvx(æÈØ6)v¸íÆÓ½»#+6ÚŽƒ®®ê#+ɼIJV;…Fç¯-ÓoŽ¨¥Ó#+ÀO¾ƒX°Ü#*¶µ¾×.:1ì¹ÅjÇhDèÅ7M]g2r3³eI(ijäÍb<@ìÕ.6bsŒÅ“†6Ea–`0œäcZ6ƒ7ÑÉ'8Ù¢—+šÑ].íaZu `Øäí¶ÆNM žIµ©42 Ãiæls}žã’ÆÄ×ncV¿<ëfm“‰¾µö’® ÐÌ!(nÇ,~>éµáÜ)2pç:䤡KÄ9S!mDø¨ë;¥éí¼¶Ó–#*ÖŽ)¸‘‡º=öäâ0„b#%To¯,Ú¶¼5ó4•ÿŽã"N'"1±$@Xùdý )­ºci§ä)ˆx·×AQ5{¶Ø‰Tˆ‹™‡ygWþK®gQYË•$×½F蛎‚JYY1 D1–Ê›t»ã¼ik IÂœTi#+uoŠBËÚÁñÚæ]óO#+3¥"‹Û0EPû)<TG›ê$&I7~WEÏÓIÑÏ#*|qƒhÁŒPšp¾%ц=;fõÓýw;¹}1XW¾;Å÷Í?(l´±MÖgW,_1øèºVº--§`„øO0mB&å¹b°ñ©lš¿ëq÷”D>y‰¥IeFø;“ƒ<™A¦²?Ïh&‰ ¾¹âÀJÈÝzvØ#*ò-d…„ Êl#26«7“Ž'}ÆôÛ_–YtNÃWdÓ;=ä/fRͲºòcRá\ŒÓò«åÚæ×·Ãù³¦†£²ŠŸ'7И{ì1î5HtΚáØž~¶Ç¥_iM[AÍJ6E;àñ¬Ü#*1µ(zïZUå†Þ¬M1¡¶2„’ƒ. – ¬±¡°DûN‹A2Dàá½\69Ø/ZãÂ%ë—•ŽkMév뮶ƒ8ux¼üá#+‹ÂÖð-2‰ºtl_¸Æ.Ž#*€­A$Xð¯fß¡j7§È<YGé| N€QJ}æ—ûJ‰ÜÚŠ§–a"âõØo… ƒ³g'Õl¨éì/ÓïQÝæ£wív­r{#€‡bÍÁ‚ëO@wÔ/ÄÓÚ&‘!EU<â‚̆I¨@»kúÐ3‡ã÷9ÝHÚ bÅz2Ç‘8z³óGËV¾¼õ{•ï½D€è#)²z¾Ü#ãì°"#È  EÌèüÿi\£v.:¼¯¢¸zW&Àø®Íx|nó@\è4ïÔÿ1i%÷+·kÔxß蘞ÎACíùë5Æ•öQÈʃ„—È0›FB뙌01Ú2ÏŸ¿ñÁ¿ÃmþîßÞ¾"™åà,¥#)Ì…LôÍÊ  0~Iß}÷÷©ÓýÚ—e—¥÷œC#¾vñÞá!`£X#)$;¤#¾×6C¿ÈU®*?#*±£vÑÉg~­ GT$Ä'ÏðƒC #)–7ŠõÈQnVC¹@°9È2è3ë^ȈŒpùÀIÔýcg“{™7Œy“ŽIvcí5ª§ô#)s£ŸÐ.¸1x"]m GääGÇ$\¬›Nj‚¥­ÁàI6ÌÓ‰§¶)J¨{ÏÃF`?Úý?Æ[…\åÝþmÑÂD yÁÚ·ãñ¥Å)¢‰xØ0L™Š§ÊcuóʆñžÈ¥%[²YpF\¹#+Q´Á*ÃøÛŒ[&-‡™¡h­KrI‰pCŒ…G©îãã}OÂϦî¡I’b%àã¶.`ä&ß³1CÀ™ßIü5WÕ[Ãæ‘Ÿ#+áÝ®ºIHB¥§Ù4,²tJÄzÅÂqæØÜUÛ]nk»tà0X»Ü=ôTŸÔV*y:¶ÏvB•ˆ_aG˜²ÝÑÖúí3¢9Ï#+–"q£$@‘LT&¯¾!u½“<ÖaàoWƒzi™UK‘O­IC–ã¤è{Œ®Ê úý!{±”MÁÑÇM¤"ˆ9æµå2I©¬#+Xv\sG™'ÙÝtï&½Aé’’yê½Y±GfXFÞ©ˆèì:f ‡94gÒ|«§)ÖïÚÍÚšç'ôômê^èÑ8ßéO¶zßLãÚwæLY¬ÔÈ„´ðV}Url„¯DÚk$ÿ4óÈJhTψƒ‚ž‡eçik“è;#ŠþIqÏ1þ#6úÈÛO´:U$øUü’ørðÆŠæ8ðü/v‚í‰d7'hèSK…Ì'p»k㔜-÷a¹ôt(Ä$x°2 ˜1åÈ~HœO#+?½p™= Î[ÖuˆáÎLš~;ii1#›aAI¤XI1áAóΛ*ã Ól9óÀðÍHZaÑhJò×ìDG9|8#*õÓžŽjô"ˆ!4²~U¹›cö‘•3–Áôh¦øÖ¦#2к£À´Ï5(r¹ëìÝd#µzü¬ñ¤‰4"·T½Ä½Óã~;D¬xZjm 9ËQï…ðäÞí§ÆˆG±2¹'¨tBw#+Ï¥þ‹Ïb_É;ä‚2Iÿ#᎜ ·iÀÜŒ¢"´B¬È樻³„㼄t&§p­Ã7Í1Ñ$RÛðùLf†2TOsÅ©…ºw§™®)nL9)ˆÄÈÈ_ÎGË«q\œ¿CÍÛºlpî]¼ EÝE”µß¹øˆò£ÕšO¼ãýã÷RsKš`Úå5Tójû^°Qæ DT}1ˆÚ D7æㇶÏhÊ0*Ü×ß:Ö h#)íúæZi4QÃRÆZØR:¯¤À$:h§2€`UéžÄ6ÖeV1É#*vÄYÆ©–ëÇ"ÌýerÌïò!ðþ\ø3â0Ç*Ë+YQ_ÀFéuž-Å”,ýdDó­™±··‡•¢Qö“Ùhtsc%[ìR fŠê{ 3òªA·ÐkÙ¨ªë¢Ó• Œ%d®PñŽ¶–øèú颕‹@…)Õ§)Oˆû¢ªH„žg¼êÉ}¶b^j~8y$*×GÍ«VðÉ7×Y{ØémðLèÂ|ò6œ­¡™úµœ¦§w(î¸V§$s\òù\sK[¢ÑÆ©vÃæ!sYP¢·N+å3Ç¥|Õm¿”sΠáß&×:/OÎK3mäô)Ó¥ééµ’|ºû¹tÀp&`ȃѧ4995‘’…l2 ®„Û™{Ü#*-\ÌædèÛð6VÔ^!6x–óŸUr“í·Xm´üD6sž*kçÓb'&ëx\Ö®]éBfÛŽ¼)ÓaŒÑîMQ|ѱå­¢ÜÝŸxêºÇvb©ƒ»ñTŒå£ci|#* ¨ÆY—<…ü•ƒhYÁo Òá…ÞËÙÜF I#*Ç°”ØÔþKÊ&5È£Òc:=Ö»íÎKtÏœáž7¸‰jmcЮ¨¶ÙøÑ )’*DÜw(:¤‡Š@*…as¯6¾‰©Py#+‰¶Û$ûE)}ë'Æï}'nkcg6|$ PàòŽFè© åor8'jeä;MF‹ílr½FôÐ= *\¨½:ÛEÙ´²8‚بz’i=ÊðŠã±Fo%ë*œ¡açgþ3ç7s”¾{ð¤ß>üg:¶æ$qCñnsl û\ÂŒ=³<U"¸x/ÊO` •,'è{¨´•Y E]*4íöéXóAƒ§ÅÃÆûyæÙÁâ¿n Ç¥·؇{*£Öç…6z)Ñâ~!å/;cjûQŒÛ’ÓrÁölíÄíJÙ{–Ï&èy%ÝØ‹´”Ùu¼,ÉvõutÁ=ZÏjø}¦o¢ÿÀ¿8âÞ}´s^G ap%N4e °iQ‘Ieâ]©„–¢üÈRÆQãËÌØ­³¾fDÒÉá߬FÞRÒkdýæ{⛽Wv߯<o£ ˜D¹qÉÂ¥cŽ‰fFVÛ›[ãhròÄ$Þ”:éùÚÿWÐØæä?ëQÇ÷åVܘ2(ƒu¡ðäÔOLnû‡ߢ|ɇçȶ9E|ÞH1™Í+¯³®N½j",evY®sBSóF:ÓeӇɚôŠH6±öœœï]|ñ×icàÍƵL#+¥ž e¡>ÜØå#)vƒåy=ˆÄ<žàßïÆNý\«ë´ˆI _*ZžÀ"Á&eÁ.Pjâš›?ߟÏÜû¾rÚbgýIX¯È¢ê+äS\Õ}ÞuFŠ/ê«>Í‹¦ìu]dÀë:FNxëýbT{ÈIZkŽªÎŸ$ér­Î[J(7ÕZÊõR:چ齄óv¦„­ÉÕÁl×h[…¤0¢^·1£´ÜD…e-×FóÜÏå¦ú ÓsT»f«ùW#+­/期«šÑLõYf#¡òxÃ#½áåÐ!#)tÎÖ“ïnŽW4Ubsa1´RXŸµèèÙß“¡L¢)áŽÕÕ.Xmz†Q‹ÿ·ÉjJÅÚQÁgÏXoÃ=¢ss²­¹*vÅn± »ËÏâ¬*pƒW[¦Ú¬óþ:]~p6œ áLqi1Þ³={ËÊâ¸Õjù_hæªÌÕ,`œRWsÇÀŠÇü§}²ù*šÅFÀª²[,çÚڌ2–SXBX€>Ǽ9ÁFmd"­ÊR¢Ý-­ó¹òKÐÙk÷#*ž6:#+váLñÊ-,’ØZØtw&Aâ%å^è]7Jéråpu™¦G‹à8Âü§/q°X4VتÆÚ†-. › éÌÖÙ‹EðR´t­}yuQ³…[Cî¨Æûáo¦s„˜-÷°&žRS*:­SʤR9q%yßù£ã¯³ÆL.ܧ™ö§"!ÙÃÊ̦æÐð9B †Õ²üë(ÊâéYµô×åI|m¥ˆ¹\ët3úõ™slLÔÓÊÞ¡ -«ƒk·^ßp´mV‚iT“#+3X»¯±W (ñ]‚J­l_'‰AZŒYÝ/§#*qƳ 2¼(ÍÔ 1¹QŸ†­Ùš8Yž]”Òò{¯>Dob|»ú,Ô÷ïKÓƒýƒÚk"§Š3Qý…îCzìί-%´ÍK>qn‡AåôÂZW\e'(âʺé¿då2^$õ°‡'¨œÛ»*_F’.ÜÈM¿¹¢ä½…Lî¡BÝK²¶#+:x’¥ÐQ±µ’â5Å.’Í#+ºy*¯fÃ_zÎW@]¬ë™†Ñ€¨»ÆÝ33JÎÏ<K˜æ·\Mmó%Öªuqx{6^qŒto¾B¥¯%os”4ìZ°~„t ÇŒ—a÷ñ¼6ÿ²Êå;ÁRÏȪ!mäùã2h½´ÅÔ¡€#+GMÉ q° r¿Âsvƒy»¹†“¹Áhg¬I·“môw="™]Zº “ÓHìØ&Ñ¥)W<0r±Î9çI[ Ädn}ÍèµmKTYyQú ár÷¼:5vyWK«Š‡CŸ™ ñ•;øÞ ù×BÍîÍV$Šp(Ë6€XJo·LÄPßP´Ž#+Ù.w ™Õ9û±Â{}æ*™Ý§Ï·<ñšÆ#+”<eý¶Ï<lÀNª#+¬n«™¼¦ˆóB‘ƒÍϲç¼Ø^[sžÊ:Tlv¬³óMÅÒÊ—Ú®½Î‹ØH!a-”9“¶×Èæ<„Fei«E`#*³[vFü¢#*ma»qX×'ÚëúdÞ+1ªwŽ Û®šyS¤e¥ùUËñµG›â;;†IÙ㉟/E˜7ÎO~‚Ϫ\Ò¬f#*ž-®ãêP Rîl(«TEE&’XìÈë‘ÇŽ‰UWšññ_:#’[‰× ’s¤s¿}vÌÚZ›‘zÜmÏïû±·Ë9Þtƒ•Í¹e¦n"7EoËTv8#+g8 šÛ{fjë¢Ì*øbÄ6hÀDï6Ü&öÝ„,RÖjátq¨¼0¥'HÖ dšJ¸({ÝÒ´u‚>¡HÆÔ)#¨ŽIY_#+ÛJêp|–JBÖNaỞ×á`ÎVŒj¶kÖøÙ²!¾y´\¹piR-A b g=3KiWÆ•–}¹Zí£KQíd«v»®Ðå¬Âï¡óÖÙïiv«W#+1‡ŠíòÞ÷×YÖjÈ—|•C¨_(ÙŠÁ³óG¦›—`ÈH·Œ¯£UÈ\ƒ,œÐíÓŠð½:^‡ÑÕêëõ8àÔŒÊB„q-º]#*òÃS›~4±â3 Sƒ„j²™n{­w46Ä]¾û¶_ bó9hÁÍx• å ÊÜÆ#*Ó“cZ#*µ¦ºjŒZèt+d$ÎÅŒ'[_Èç¦z¨éÑZ°´ÀC1MøÀ¨+ÒÖgØ#*“¤#EÖ-æè_+g –.Ô;· żŒƒµµ@Á\÷#+59ÂBþpœ­!M6i€Pgä,2¢1ýÒT¥ƒ Y)ø€÷z>÷·/#+,:νÓ1Çàöö‹KÈSáoÒ@úˆ°Ì@­¡Î2²8 áÕ)ÜÒVyí-U~œóv‰C#+É%PoYZ”QÆœœ6Ï©¥ÿ¿ÍîšÃŽ9°ãôåQTº~®îËuj™/ šÙEJôFÒëjm£ÒxR$‚ ãd5UáöÑÝ´ƒ_#+q#ƒ<‚<ŠÌˆGX-š;Zä/µD5Ë·Wºé«iT ÒèœÏèÄ2 Á¿bÖÁëh´Cªk3dÖ9ñt{¨±­ëmª!îô…¬ßË|Ìëxú£±Ê…÷ÙÍ:ç/f/Óå/x™N;Tǧ?]æÜuG<Ÿ±©3+#+¡·û^×ìw)ŸÝÊZsÉDmÛy™L?ÝÈ港LKý|©RÔŽ#*¡à]lZËãKÝMÍeÁÏ”lŸ0Ê"DYH]ånª)ßòOë·IáÏÏ: ¿|žZzÛ]~77Mæ¤tÄs%YI QĘN¦u¾T¢¬ÈLÚFÃÌ`o¦ÑµKæ KÔM)U¼;®Š,= xý•ƒŸ“ùmDt¬8upŽq qc%ãMù¹°ì¤)Àl*j„î€Ôàñt0ô:×>1[OSÚ÷ô; d„…Ä“{“79È·ƒß»Ó»žŠùäÞæ;û+o,ÛaéïöÓË0Cuüqoœà¨L½_Øã³T™Û:–¤¢6m‹›á ršE˜ËKÞ_Þsû)ªÖ{gÖ6ß,±e‡>tá±ë&<'k’è+Åœö./ÓÚÕ‚TÃ÷Ûô‘!#y FG{Sà¬6:e#)”}¤+Ë€é|1Ò·´fê—N<ˆ‰1Ó“©i\7¬lÂõQÖþloa#+ËÎdñª°Ë$˜c×Aš |zåÕºt¨äǤjÿR<×íGžÎ{¼ª}~>LRú´,9¡ø³½÷½»óùcÁ ¢C;¯»£-ñ’r)ñÜ©ÆÎrÄ ÀÝ:9Òâ»UÝNmúƧ¾}[6âjž‡5tM¢ð» Ùx/pö_”ft€J6ç÷LY5TÞ¾pGC‚ÍÍ{îÛˆúr¦³”DK#)¡•C,#+h¼:- ð0!CÜ$trŽY¤…øÙÄi°NWœ—a‹°÷Sø-tœnªšµZLš¿±ô×CôÊÝa.Ž›U*BñVÄul˜J;FûX#*Ç -È8÷«¨S‚ót¹,’sšáA‹<EYë{A¾sÏ!L²¸Klª²˜{˜nŶºN.šX”.éB‰NUƒš:­i›þŽêÝ`´BNÁ›«¡Tj#k-RÃEïÎèØ9QÐr>È‹ìòž3<¸{’ÞÝs£3û¿‹ûr÷ë;XÏÄû§zí̽ƒDfáÛ)9æK¼«ïuÑ:mhEdׯÌl}ÐgN×)‚À£„<ÙïV¤˜56\Ÿ6^xü0¥}]´`2+µÌœgs¢@x8lkÜÞ°Å­¿ñp”Aa¥¢–&=}jŽ,ùxâ=xÅ­Þ<K–|+ûÛݶ¦9«µ/+°,÷| ÛYRg…l·†Ð%k^µR0~ï3{µ¬¡„,ÄçR9Ü>Ê]>¾y#+çtTl0-AÒnŠ¼ýCÍ‹>ˆ±”„D’ ƒúÏŸÝü{øúuv&çÉœ_±ÖÿUÕHÈ”#©êÝ#Öêä.Ûæ–Ž“ZL©_~1º/Ûe½úkeÎl}ÚXqaœÿGWð ¹8$ýià†¥,°NHŒs€³<áÓ#+yú¤¼ƒ#+ت„^í@^™.—.N¾þ×ËX’±Ñ©®4釅Èö¸»®­eý#)öw.mM“Bªò‡ß6èÖ¢sÜÒ½KRBaãÖš³ù_Ruþ¢0r 8z\AÝ DŸW°NxïÒº¡i£Uþ~Œ©iAxb@© is‘=ãÀ†G!¹QD¤¹ŽbJ)ËÑ–"é#FB´§W2Ò3fY…RîDHÒ®œwÇÝ{#lü/ëÅúeþë3-vŽ"U™4欤·EPMw­Ž¢‡ìg Nu.¾Çb-ÂÚ¿I!"`}¸’fŽëÑ|«—öÆ~ݼ÷5—Hô^žœw˜ÇlÏDm»ö^Bn±b3F”ÛŸÞ³Š’˜Þðj!l¢ìÒ ö°Þ/¥VÔ¤k$™Ã%²Us±Áâׂ |&i{™)þÔOJ Ìl+ç¬}X}6z^V…¶sk1ƒ†‡¼†ßI0X‡ã>7p¤ôÎbÛ` -ÐèeùÖ, CŒü`Æ×[È©®Ì{¥£ÎŽRzÅ~Üò:§\âågÉ5~:¨e\æ³ÞnÝ®ŒPßUïå-éðÉÛѸFµÎqÚk”ÓZ9ád*”t™ãÖÛ¿5ôƒ’žÈôÇ[r|o)#XÔW·—>‹Ÿ˜æz^x¶‰±¡ê݈eQʃû#*‹`Ü1˜ÛNAõ;ñoò$³®‹›¸x‹<Cf‹øc•×;Ô—#Z_f³g~AÚ-¹ñwÒ·ž¾D‰ ‘·sô6‰×›Ö:ñLÔ]9½Ë¯P_qÒNK>¹¼¬O_3EÅ>HاŒ«\àMœÜ_Ñ™~¨:'ñÝÏ“\•ùó€®?œMæ*=Z WxKÂù×c×ÉÈU‚b-Š[ñƒ ¯×Ó3öœ¶ž`ƒàŽ#Îk¨cdÛ£ÇÁÓHdIZ©Vám…ÊA„‚9ÛW5~!ÛŒä[åg¥yõ¿ov"ÍZ†n#+ÚçÂs”Ô¤‰oéìé‡ÆaÓƒ¡ÑÀÿêøG‘ÁŽzö·ÝòCÛ &™(B~ðž[ÃP:mPžÌG~ýÖÔÓíQ@E#äTa’ÛåÕ#G]›Ñ8s>—_X¡xhs›rëxÐgÆ2Ì4j†f3›™ (¶x!ðê¦Ï{Ux+Y8;å>w^`Óq6ɇï»s%˜è,ºt‰Fý._\T Rôzà’æ |'+Ÿm‰loEá}™Ì÷þ®dxáÐCbù?ÃÊ”ù‹ÕfÜ©W®Ö„cé2-Ixãà‹»ò!½bGeœžÚ(Ô®"LI´'š¶Kbjd±ˆÈ¤d¬ä#+oÃR`äèÙb&d#*}ûgìò:C qÐì!¼eùÎzó—F­y÷Ä7Ž÷ÇãŸѹYTJ@ ;šè‡Xþ]‰M;oþ›¶tÚîúØQ_—UgX¦žµA4!;MàbšĺÇ#OC<_ˇjtnïPàA4‹ëdí)½ã 'Œ+éU<Ê•#*$–‹rÛ7¿g˃^‘ïkÝ®^ÃöûÆË´pejžûrBÇ¿›çÅäwjò m›Þ$?³ÿ‚£™¨;ìŸ0n—f1U#¶#+Ú”?´òÅò£¢¦ò5½¦µìûp&Z~ÚÞÜrý±"ÝΗÄ#*IáÞqŠð§ò~‡9Œ;ÌÙÑWÝmÑÚ›TÆ—B!WÆosÝãËæ}™ì1­+°ð/8ç2·z³RB+×ÃM €tg¡mAH ºY4rЖh|K::Òq¬ß,cøËZ„ &Có÷(;&¤úr/ï°…@tØB˜,ý¾¦æ_@×™Žºÿg? €ð2åòú/¯éîïNøŸ‡WË>2–•ðåÄ #)§ÏìPìOÑõãÖŠç°Ø9Mw#Ù(JL+G;Q@À€ðè{<¸ÖK ! Až½(AF#©õƒãWyl.£%ýç8Ãöÿ¡v¿œ•Î»–Ä–“õ;e6‰HÚ“l›ªå¶åµuÕ§¯@ðbq2"Z"@ú4 Ë6o+ùü,q‰µÂ[~-Ÿ,øk_Ï|Ðù‰–¾FåxI$"‘AƒRIæ&©×‡™z½:›†r:L-&OŸÄÌx§jÎ-œ»-±<Ù‘ýp®™j9I2½KW²}¯& ‚ßh'Ùáó7#+&-žû¡{ì$Cì®È#)GÌþä㘋`ªˆ$-Qµ]Ê¿º8,ªdU,…F’£rºÞ›¿eEž'ÛP Øåo±(Í¡mÿd“^çü€»‚‹ªHœ“’Ê=D-òùÞƒ­=’ûb/çòwÏhYäb›zêÞ96½]µ‡ÎîÝ0nCqýÌ.ü|ìŸÒ#*®…ªzïnbb5i£¦¤dÔÓҔػZCŸÒÎ)rª%‹ça{à®èRˆö9#MLЩ‘; $=‰Bj}¥HøÓCåÃ嬉°‘?GØ©¡ý*!Š#*K©wkSî¨ÅÈ?ƒÜ˜ê„#+}s³˜íÜ"—kU‚€@UVeÀUœ7íËoÔ¿v¿‡ñ%\ÏÄ«B#*Ór\E³¨dн0_±Ùè³|\eÁ‚ÿ'ígsÚ*ƒ“Ôj2l…î2é稈yc% 3p†€ýŸæsy5Iø„ç˜FÛ{ òá k‡çܨ¡XÊr¢’¢È¦~úw@ß8§Ý>öÀ{“)LW@¡Ïñÿ%ðB!àkRw!%"_VºU<'lC&ˆVnÂÎp#+d÷°Õ)4`/Žµ‡\дž,¤†¥Ô“D¡¡°á» †ìxp ƈ #+Tv‘½µ%ƒÙg#)ùL¢#OF³»i“µÒÅ„þˆC®ÀžÏ‹¬„½Öl£öNºJõøÂañŠ¾`Ù8ú&Ø`“Fvk¾}N¿ãÞ =øõšk¸!„zí13H쇶7*¥NëT] ]ê£v̯>[0nƒÝ®…ÎiÑGTqâtÔÊÀÂEŠÊ®Àoj¢Ê#)¤’P Ì°‚ÒL{Ww-%éfá­…ÉíU½íuÏîª%í:ûïFÍÀ`¯o—LïøÕÀ”žØÀ_¹êÏØîŽ]¦P#qŒ´€lÏQ#)}î«"Ýb³“ì ^<#Ìë£{÷o9¬ã*]U×»â_‚ãõßì…‘mdÉÔ(­ß„ ˆ9_7¢¬™J^UÁAÞªBzJ” Pµ+ªj_©¹Üï¶#*FãŸ$z¨Ó‡/[O2F•PÍU3Œ) K—^÷ÑbO|4Òè´ÉÇ1¶ÿ ”5¶q‰ººXG]uÖ/q<2‰´Û=ªc-ƒ­QuŒHí@i6Ī"Àm!¼Š;¢œ³Ï•özè6é~Æ}uç<§VF²&¡«½€:ÍBªë£»##*Z·kÛ²¨Ù¢¼Š\DAN=†W<„1”ˆJ¡`Qf¡YAá¾7MÇñõ°*"%l+a僿 (1A&×Ç]ß3ºhàÂrr^õ{ñ䨡ØÎÿ¡–lÎgîV‰•õÔ#+@žæD* šõf›#!‚‹O‚f[ïÕž×oeùn+݉FS? Ÿ¶qœ@ýÓs#ÎÐàH˜Hj )2q¶Þø|7¤vžoŽ$ìLÇõ“62½×5g$&¨ES­PÁIÉ4J«ÎŽ`¥ŒÏÓŒ }#)z¹…÷í»§®®€ªz‹ qƒ®Ä†„ïd-.Šð/Ôˆ¼úÝyüuåÕI­Q ÏXçnÅ,ø-pÐ#)ÕDl–ìõL‚‚#0®´šq'#+žÎê—΃´ŒÿØΰ›Î_N!^À–Áîì˜\:BnÛè/Š™ •ícÆ<ÂÔ`IßQCmßQóy¿Eã·¤ò@ù¿°ù ç‹êÎi€îäC·˜T%.TÞ1ÚrûÞLóˆîòye› TŸ›DgˆÏF›œÃÎÊû¶«…æ|F‘ŒÞ“Ï7ȃ;#o§HՌʼn?MϹ\E#*«Ï4¥0½B)œhBÐ-L »Ð·àUrçúõ›Íà±I¿á(HªZa<!ÀË94œ.¶Ç #„1§'Ö©ŒÓÚ#*$nãE"#+’ xòw .O×øÛœ¾Å«Ö‘èBÈzbF;¦fçþË-]z|Bœ÷shœCó%§®Bæ[ÝÆqãž ÆJÁÕ)ç¾—Á\l[›†ùst G˹ ‘Ë+Qƒ`-$9$ÙÛ(!=e,£Ë¢÷#ôsÞåÛ\{uÜ£UîAè*Oò.6¿´yåRÆÌN¸ƒ™-ó_•ÔxÞ-w”ŠÚ“³6JsLC¾Ã|¤á*Dª…  pGÍ#*ÃAÎŒòMni×Ío¯B«®zzëÍ2‡@ÂÍ£5¹¯#ºoˆ€ÄE Ü®PØ¡aËaÑ!¶# #*0îu‚.-­×?Eo3à ÄIQÃq½-š,ŒÙñƒf傇ÍÊÁG,™†úØ–¨EKn% vouGˬFs’H3ÖsÕ|k‘mß~ÍØð$䙨 ÊäBTZ/üy(й‹îÌ.D’×SÍcèt‚çã‡ay§§ÍﲡýnÄS‹ß†Ÿ,±7”–ˆ—ó"»9ax§“hU+ÑÌ0ÇékŽü^º2—/ž$jêá +Ýpe™œU?~˜Á7ΓÍ.\0`ÀP#+q7’n%ƒ(B”Rç‰óžŸqTç¸^Ûâý=ßGï^9ºwÆéá\›soã{”I#*;Ï[°ûŽ±ÙÙÂë¨^­ççdym_ƒp®»å‰ìü2S0¨‘ùÒÃJ°N:B+³›Ëoä|`^¿4¤mÑCÍ{‹ŠqtZ@•zöpÛÖ1Œ$Ö@o"Á•0ÞC½Y$S¥¢imIçô(ø²g€{w,Rîú¼Õ#+ZÞ‡­÷ìH#Ñs[9•û¯ÛÒ”98̉'‚°Þ5æ¥ Z·ÒÊ€Ä;š¦Ò*ø9: ¬ ‘TÆåçÎpØtlc*ÑæSüæÑ#*yv{ñûmwTø~Žw_ÄØÂ(ÞÂBg¤zVöÄFërÂWˆ¸ª¦Óyõ€Ža›ÇÊX#+ ·Ãa#+QÁŒõíÅÄ‚€È??8¢lŽ±u¼Ä8é'È.EÝ›8p‹­íI ¦‚Ý-,€£ÈÔ¨CŠ)yOW‚ú+“dFêÇ­>õÕMËj~Û5¨Õ'J‚ßW>-ò·õ¸8ÓJ°V`ųs-‡tï³Hmȯ]pkâÐeØÊfõF©äctÕmèÚFhMÛrúÿ‚ˆ k^`’GQ‘^í#ïm#*#+tå¶3˜:PJÞ7”«èÃ0ò¸§øßKêO‰§C•/Žo·‹oOßšÛºáŽýå‡Ü³”ˆlÔ3‘ã$m¬]Øɘ˜9#+ùæT@æÀã5¢MÌE#+*€CÙÆý\ötÚ(Þ°sÝ°¡Ã=Å% b¦Í(´‘laM§^ý~\GNr˜jμ‹ß‘­nS™Q—O æ¾áIÂjcÁaŽ®sብ»#åéôë¼áÀ8o¿’;êF¼“G“¦‰[¿‚‡Å1)ŒùOç!\Húڎμÿ\ïo$î¥éÍö*Ð’žÄ?ÙPbJk<èG+¢‘D„qþd1üæåhºÎ Å¥+vîß°*Ý…Æ(”ÂR«ü¿Ùí{ò©¦x¥#)iþ>÷Ž]q~º¨LþÏñŠ¨Å„+Ü|Ì“ïôwÖYÂgßAUOv€ þ0OH19ùq<r{×ëöC¡'­Sߟ^X¨ª¤ $ÿ#*ð- C÷È‹‡õP#’òÍüφðÝö?‡×õ[ˆþ«SÀ=iá¡€3~¾t‰³@/zÝô_?^Îb„×*¹ËÍK®p#+Rєʢ2BxÙâ„/öQ€‹»)YÛÆ»„ß8‡1 >ýþ—F:Þ9Ž¿V»ã^’"p…ç¿ãÕ,_©Ñ*",õ{|s¸ç¸ÖËëë°åt N" )!{!Qð*ô©¢§îõE6ÌF:¶j¥a‘Y·‘1Žk{¿„ûÏëۉDZÖyJ GJ €ãÀyùËdo~#)ú‘K›|ûÂ]Í`«éhÃGgYØøïE‹>ARá?´xp˜˜å½B—N#œl#Éí¯êœÅM“•¹Õ_VDñb|ÿ–É×¥wۨݙCƒ ‚°Uûƒê.VÁ÷frõýŠ†;ÿ+Ÿ¡ý_/LþOPçOyQ(¼#)zXÚlD¿×òÿùÿ'ü?Ïþ¯ö}ß÷þÿ«ÿ‡ÿµûsÿgÿoüáøûoûÍõÿ£ý?7«ýÑû}ß÷ÿ·þÿ›ÿú?ÓˆÿÇþÿò¯üñÿ/ú¿ãÛþïõYÿ³þùy>ÃÏôWÏþÿ÷¿ïÿwûáÛÿøÿÇø~`#)ŸÆÿo÷ãþfäþ«XUª?žËSûÌXq|¿ö¦¡ýÿêåÚ¯üˆCû"—ÿQè .Y¬0~t fS¡’£Q#)rPÈ\€ ýª‡D :D,Ãñb¢e:¢97ÖØ¡Ë4°#)H‚ú€Ò€»2‚a¥ˆ é#)Áˆ+#)‰­²–Õð‚¶L¨Šk),dEI¸:\¤tQºPY#ʸ2 ™Ž½Öø\°†5R&¤£M¢îK•ŒÄ±`ŒK%•LlÉJYªFDƒ%ÔPÎ*ÿß¡mŸæžÿ:/úUÿ—ÒÏ/îéÿXÿK8õøÚ,è“=§u* bwòy‰îËüÛÖ7üôl8#+Ùp\†ÿéBÿ¶±§èsx$–š»A¦ÀdCa5±³æÀ!Ù^däîË¢ß÷g¢Še®Ð½­1‘˜ÞSfb¶ºxÞ¨ãžæhÔ>ªo-cGhšBWzŠ}î©íw¿÷\Û9ÔgE»ÇýNÉ&ÀÆܨ>Ô½­¹×/2GGom_fÉ=´Ó3rèÃÂ@z³ ÈH6 i¡°R™(•V ±n‚pmœÜ"&ñõëwa!šH´q™s£·ˆ~þ!°/1üxÛˆ‚¸ëŒ«û¤á{ÑKãqÖëFÞ|!YÓ'¢YÅG¾&ÍŠ†‹ˆe'u×ìᶡÅðõZŒŒ’mè~á–Dw`w³FjVPõ?ÚãΩòi‹¨ö$³ÇgWæv˜ll<Í㢲ë›õÓÌøÃòYDéÝtZ e1ð!!ÇÞ‡ƒ=£ÎBÖšØc™ÿ¥á#*q{ìiÂÚ¬?8k?ã`Ë6…ïîÓbW?"È?ìÀ9Ùž-Æ%ŽâÀvø+FM1f’ËY)šT³ZfJbEïo°õr,r4UU1¤”UBÎÝ}˜j#*ÚO{r;# \C9ìpr.S³p˜7ØÍÉ0GY|è‹HÆȘDˆ°ìHg“$" E=å‘_„M-6iòÇ#*»wÔTõ7fƒRnÆ2AðØÙDmqLú(tÔ8‡d+owZ€KBl‘ ÅS;’4í£±‘þ<¡›'vÐÞd‚Òf„-04 ê È(xÂ#+’mŽ©¤V H…ƒ®·YÖ©Fv5Ñ¿YF´E#*¶è:¼ï¯8û Tšcëàtœ\µSo +!á :,jθ‘­ÚÎ04âàq‘É"z^2“É‹¹žØ†p‘ Á5VD9?øh4šUºòöt¬ŒXži×PMŠ`ã9'rx#ÞédN(ìPíeDT#+H¦­ì·5²V±«|eY*Ø«\«\Û%Zæ«¥µz²/:ïÆó»×­uÊ«èùôØ)4`¯HÅÑI²³ÊâdMñøWÎUt×/°>—§¬È;¾˜â½@A¥£ÎèpC§áŠøðY¿f“…©j4•9<Ìx›òvt/#*Ž‰-Ÿro=ù•ïão"tkçP¡å/‡c€µ>¹Xñ èg„øøÜO¹¶ú–É´VÆÌÛE_N¢Ù9Á#*œËcV#+Žrñ©Ã« Á£ÎY‘_1ôrUñÈ¥X¹ÝÉ£@Ǫ«ƒ–©)e&I$e¼ñT’’ׂ“$ædS’ßçZÁÐO²¾Ûx”Ì#«6>7šÂ+u9l(Öƒ‰BÃ*(ˆÔ ˆÈ튄¨ÙÝ«{×àmk×û·ÀlYHªÔJX%PIR$;ï€<6t:ûý®6÷”[·@<X$&IÐ;¾6ë!Š°¹6©ä£!Õ’Ä<†ø2‹‚ ³2¥çˆgŽu:íß Ž;9£È¡æ£(AVn„Ø ÊN¿]S}|x×­?ãóž–¼7à$•9òè·äŘÖ%yRZP²é!Pm†À€ß'ÃãË=Üûw=Gpn#)MÔêŒI gÄlvo°¤z#)höñ%ˆR³Ó7ÛP5ò†ˆ‘"Ó#+@Y%¡(9ßÒŽ•gLš%¨J’+"„ÚÕ##*¼¸çtì™nð×;2Å`,!†UHwÍÌ œ¾]!¦É!#)Q'rQĵí±ËHŠÚœv8(äSUXò‡Ô‚ˆòB‚«ÐÆ, P—+ו׃<òóq×f«»¥$¨.†æ…â+µžÿ‡|#ì­ ¸ëÑT£€UÖ<Éš•#*A¦#*œ÷Ø{„³ðAªì4·‹Ù‹Øú5ÉH4‚ùní»¡Ä=BœÛBy>Šš%|êIB@D5dXc<³ƒuEÞ@¡:ž“o”u‰’dŽlx"gwÓŠ›äåyNჟ.dzY:,ÛÝÌ%›3g`}Ò〭¦#+„ÜCÊeoP;»Èøc».¨L„!²ÊÊo@ÿ?Êl+ Þ2ÇÁ‚ûR`]Q“‘w4lÉQLHR³%„Âx›Êý~]ØÉãFÀ”KBmTŠ„Ì©7ﲓ»¬ËÎô£¨­¼d=ßfÂÖ4î{÷†‡·ˆCœ>è•gJºu¨K€w—J9*¿L6ßT^æËŽ°Ëƒ^Œ®Ç&¡¨a Dn4Ð(#+;öñž÷!ÅvŒÚÀå‚@ Ay&6c×™‡ß»gˆ»«Ó–Mré#r8ê’©Jî¿-JíD‹(Œ"À`‘—kŠJñ¹„WuqíÜdyëÎx,Ax0—¥‘ïàqÇŽ4ž Ù¼©T94¦„òò)Hþ齡…°ŸŸ‹›e<ê‘`y¼â—FéEWt÷ww댪wT¤KFˆ‘´¼N8½‡#+ KF„d‰&¿d§¹1ƒƒ»®Ðž¤'œŽÒƒ==[¬Ç 7ßœŸ/7Ço³œÞ}²x¾ìPîˆpíŠ|;ªm¬U0/ÄNøz$΄ج\4Q¢v.ŒékyéOCh£‚”*¢Ár2(³ƒ%*øNœãçÛ¥vÿ]pû9äé Õ:¬ÜöWqÙG¾;ÏÍöaÓL(ðûW:궂öÁË}k]®]¿-bz}>kÒ¸¨iä´mê{ÌŽf|å†Ç{=V #*'&#)NÃàÁlbJ§b{³œKåLã «ì…è˜U¶AÓ`wLj£jm 1Åy Ãwÿ&}Öª@ÞTL˜v9ƒ¬\I^‚ý:‚ÿF}rÒîœ_MûC ¿$éÔ&NèØÈ1Ò…@Ù"1#*—@ÌäuàÜà&„{¨ªku¡÷—vPćžœÑ"Å— êN5ºÌQ“\À\ªD¸„A7Þ·»þ¦†')Cqµ#)µ¢r£Ž#+Dh‰žRÖHz4y˜Ó.(P§ˆ–wûè…?kÀVsóšokÏ0 åP懧!3„”@$î‹Q¯Må ZæølŽ¶Æ–äKQƒ.›1 X~kBÖ¹s2ìWyS„V#`¥T:l àüŸ¹#+w›Hë²#+€u¶h~»ÃÂ^æAâ~¸ßs†ß]²•éÑЯ>c¢›Ã:­²R³Òå-¤Â¡Ø MÀSvÆ›ô-@@ânXêÊŒŽÃ>UÕØÓ̈L³6ÔŽòÅùã…×hQÆr ‡(¯IÊ.È›§KÉf$náï´Ö¸6W &¬'²Ä8@í!™}®J»#*!‹’B ŒCL‹I³lšL™MÝmî1–4ÚG±œ6v8ÙFÌÑã(ÍÓÝfçÐèZ烉À¹ˆEP{š% rœ`0 ÛŸá]íì˜j15=´SoÎ]i’?'ñž¹ÚÏ øPÍ#)P×K-<*x,ƒÔ”l÷Õ}C®u7aXª¥:´0!±,xDC˜@ÜÐmì¾·é±F¯§Ã“ú–$Íc y;œùÀÄoΑú†LIW8Î J_δHMúáœ#*¥ÓÔŸû~Xê*ve¡þùéÖ÷Ì\øÙ”<˜zd4y§Óo>ÜúpβNÿÆu#)KˆX?ÊxÄϧþç£@êíÿn°Ùð£¶Çü÷;Ç0>ä­#+'áŠA¥JÝ/qóÎ.H¾²pý_Ž™•åb}[õýµ<€,€k×eñ†qK-n^@ ׌o#+sªÌ&¨˜ûé¸)G<ãñ#)bi´Ç³Ù'\AøÀðÂyŸp¨>˜óö˜cÍó8v‰ìê9¾&y”•ÝÿcñımfŸˆ>–ý¾Ö‹: ¾‰’‡v+‚ØÓ#)ÂÄ™À<Àû¿§÷Ùÿžüùüó5¼fîØÁ þù ºe… ¨É!¢.XZgzþù<îÿõà߆áõOåã¿ËÕ–7:%‡ð;ÿxêÂߣ±!)'_›Î‡\åî|y9ý0#*Q ÿäô̈jçn1Küçþræ…[³ŽÞz¯?¸.wd ö¶¢€'Áÿoú|ÿõûÑÿ‡ýrÿ¶Gþå™QøÿäÐX:´•’m®f•ŒçÊ|"¢¿Š·ê¬éHZ-ÊåjEœíœã_Q §h¬H#+ó#)H( eÙž_*4í3#)GDwœ#)G#)"kíO—®^¸#)C,?=ƒ3”A‡xt|'ñŠ©QêÞÿ¦ˆ@ïÆÃ|}"Ï¢¨•D*ä??oæúº9φ`ŽpFCÇ÷éÑ çþ^G3¤Z¬Èa¤&ny½EÛu®ãkÊ½Û #9˜‚Îrº ÁÔ°}¤%¤'6·ø3n–M‹Qô«×¿œòO7B¤3fâŸä÷{½spõ‚ÀRJ£ïƒ|îø}ÀÞ@¾Ÿy¬Ó]nGêÍñEZbᑯl‡-‹ËÍ¢­À@wv=1…ïˆKxªè"ŽÊ#)”ˤ~£P¦Î!’&ÇÕ-A™Q¢¯ õQâ:^GÜî#)@Ü¿ÛÔûÁ¸7ÏjLjæJŒm¸[Rlvãeˆ(W£n¿pã'y”"¬êƒH¶îÏ߃¦l Ó]úÔoÙÀ„2LδûY~šÅµC375_[PÆøïb l?,ïþ&œGʼ ؽþuÜm{~šaO”ÏeÓÂ'¢CJ2„>—3¶ÖÒ²ä3Š*£ {G·ÊÞ½žË­>Ê#ð*”·ü¿#)Á;9 Éôh`‡6P#+°!#)‘(|³ø6§OýO£#Æò»ýñ:ïBoL½NãÕ:?mwgÕ_«²ÃQ´‘_‹ÓCÝÂq3"åân6êú ¿ùÏ®;âm†P6CöCKÒÆyT¡"Å“VÖtçÙó×ó¾Æª¼[ëÕÂE¤” NZØV@L~q98:ßNαwÐ6Ýçáo‡îÀg›Àly‹Ï¤ò_¶~N³èâ4lmCÕ<î{=²\ÝœíÑ´y-¿Ñêðz,ýˆ•µ#)æ#+¯{™Æ2UûnÿKÌXžçõØ}¼¶œ ½jijY™µôîfs(§ï/6G÷—ݤ4¢¸nÿ¥ÆÉkåyih‹„Pa*††©ò\̨ü™™Å·¹›s RžÀ€ïîé#)u„îì¨O9)W©#+ç]ÜÍEŸgßñ‡"aàO•Š‰s…G³¯²oêøx|D#*–3ÆKÏÝ­üšèêdéU=Ï#*éò”%º†¶ÛåÄuÜt<ŸGYL¸=´èË~ë<‘”‡nðÿš:wÓ¤zÂ#*®#+ ô*–/ÊHœÁç„ÔÛåøÚP‡ú—ì(HëÅÿ³¥èïãÀCäsE{øò§®¼±ûRÿFWâý«ìÏ^D·õºÄ… äH0y1×úAÚv÷W®!ú¶+õîê±Û÷j]óÛÿC+cî†èg’(+îP"@>±Ó#):»uóÉî¶~wÂâ=Lõ#*°_*º«ƒ¡ˆ‰¿ß˜¢y¾a¾`{;÷yG¯ÙéJ™¢§~Qëק-#dy9K‚"€1s8l#*H⺥zÈìÎc „A¬üu¨»pZæQÿ¾¯fíúd>Q…‰éèP›·ë¿hëp;•S®è}½”yšf.Y(øbæ£4¦äEsò©ff#*!¾ê¯±6(»,Éyb'?†ÝjŒ­4põ|ª‚]íLÕ«qÃ~ŽÍ:ÐpO–L#)̃=ÜÛŸ˜òþé:EÍyPŠî9õµ¢W[wAþ,0d(Sy âÏ3Fïf LNÏ£«;y°ù4[q 7ºyôqXjÆ Â]S›v4äHuˆŠ3^·ºQLB"$…rå.kªöíߦ}·®¿vÍ×ØÖª†“M­dž±V­Ëßè3í¾…BADR!æ)FÍ÷ÖSm¶az«”8Ê5èÕ‚"4˜lòùî²F›lx»\C#c`‘YJ=šÞ]™ øA*w'°z>Æ1Om&š´÷œˆ™kØ*Êò§#*Üx‡-ÁÈííöuvNØwÚØèeŒ2óÕN¾@æ@+[ª(M]dð÷ÍAŒ7ÄFI÷ÓΕöA­p­)#)â¬TˆDENø»²Fr‰Ï*s`Ž›…#*Žùe:„DHyj"&]€#óôuý½º¸¢í5ü€;]%†ñ'¿²yfoî¿£Vüÿ¯o5èì~íßmÃXg–b¯@«°CZ 6¯§x ‡?Pÿù¤v_dîÑwjs¡·´ƒ†TT;‚s.l¸ï¿xt]'ŸõmÛeÚ½½¡atÓp.¸C+i‹Á@ ßÈùýŒ,ü77ôŸ¸0ôíu#)%Þãî ùJLùßRîW<þzhøŸW I!mÏŸ™†s”ܘ5(¯«Ç¤`g=Röà5×köÿ‡Ö±š×xkÁQ‘Q5#+зø/+å›Ñ&Ÿ#*PîêÉÌš‡Hìªü¿Õ˜Ã×É/Å<&Ÿì÷j-{cñGlUÈþs¤qvs¼C`pÅÃç,*)G<›nh9YX¶Õ­W•`Ñ5(‹x¢âwÇlÕUySê¸'üŒßõÉž&’™^ZƒF+¥½z6úè„–È^|>Ò褑äŽw«Î.©?Og§Ev½½k9~˜Þ$Ì¢úËCHî!Ñ¥Ùh´×b’úW´gøÈzÌlçXu³îAÆù_Rò˜¹îþË"e2"­é“¾öé)_ÏáFÔa.uDuÓm·µ¼›lC¯o—ÎÊEÄcâïΈµ[±­ðâûÝÂÉôØN¤1µY‰B¨`åa <?¥ºô,u$ÒË'Fäø>MÂKÈè"t¦›ù0a#+¨È5‘#+ò½&æLKÜ MbŽUy`²[âhXñ‚Š„£2€ÝÞS¢>YF9\3è~äã裰¹±´å±X¬##)Ülªúa×¼±àÚ ÓÉᲫcËoÇxʯð¹PrƒÄÔ‹‚3uòï#* ‚õ‰  ¥r,HeƒXËoˆ‚Þ9–ÍUÐ^zÉIÿSÂeN–ÞÏI—ÊÅnñI¤B&¦V× #Ÿ»Ò ¾pæâqy¦æC®zY]³²¯rѤa›¬•Å†7¸M~¦XEÊãe—DºBI¡ö ±¯TúܳýÓqΗu’å·É¤åK9D¹[6eÈgE+¿K¸Uã×yí§ÌåÝÖÇí …0«7ŸQÝçYјòÉî® úAžyÛEg'/H¢ÏG&'wÄ=}µ˜â‹óS‰qoçuç®;ÖuÖçTõ;Aúc4¬ƒêSyïâe0vòÔ‡"ž#Ê=³{­_xÖ¿nX°ÊÝ"ùâ %.Èm™ýÔ_¦þšw)UWG±»ë½*ñÎ%ðûšo>Õég«Ì×h9Ñìòzíú7)½¿’êsÙ>µh îá®ÕA„KÕ‰7ÌSnˆz)D‚Tä^™ÕÒGã¶m¨(y#*©ñþšŠmTZ”“¢‘£ñ£rÐußH[‹ÌlÞ=/ævmo³¦(áPaN#‡7§_¶hâžœÿó¨i”Sš‰$¾ã±ºÓ¨í¹­RwóѾw/ké¨Ò[3™é숧­¥ñC(SåaÒÎ×;`÷If‹uyØu¼)'ÐÆGU*º<x;IÞ&íÞfÆMIûß?S2ùõÌE>rÊØΡÝm¿mïXqrãŠß³Út[)Ö(@ñäßIw ËüÅ6ÑywNè·;²L;\ÄâÒ” X‡Äz©ø=n™èÒ¯^l9$˜d-”ÏwûãÈrÞ]j­[ÓñÝ(Š·ìbeW8…˜s_¦c‘A¶‚‡*$=“³„¥.Ø=¤…èÐ-®zÄ"c´¿Î:"¦¤íX‚=œW+À~Û×d0zÀŠç²çOœæÉ¢¨;)pè;˜ä«îú}±Ö¹véÚìñ½=aɤÄi.œ½#+T¤„#+è!·—ؽêðLJ%Hºî­­Ê”;pwJö88Ö#*šÅJhÑ‹ä=‰œ~‰—¿e̒㶉ÛÆå×ÛgÇÛ-Ýó1åäðYþ|佧A¥­<ðyæVý!ð{}YQxù‡}ò{âÚªª¬Û—‘iûY''~¯ŒÈømsœ&äh³ä\ŠjdäW¹MØ£Çæ£Ó<i>/K:é`g0hâÕíÓ7Ötq~€{Ù¯ÊÚíÇú]ÄÒÜû\uïç¶ûþl¡³ÞE*m‡èk‚ª2#*v#*;ÎjÊúûºx9së6Áx•R \G²Ø ø}bqç™|j'âÌóšt>ï–d='ˆì´˜ª7+óûpÇŸ}ºÞ vî2ʼn±ÛÊ!öˆÌl´µùJ?AøŸ£·òèZ9” ùŽÄHþÁ^…Me?ÚÒ=`ˆ”yÚ«Eì`º ÚÒ,¤E^"ÄGº>æÚšŽ@¿÷¨ŽÁp KcIŸgãêy`P¸†ÚsRŽ(¨úÝ aþôÞ_sCrZãóûeßø{µp¸…A8Cò¶tQ‘5ÄjÄÐ~·«2'uKi7pR¾¶Sáš±™>ß5Gõ ßS>ßÐkÏÙ¦Dç¡mñÕóªo™ ËĺÏIŽNÆØêà#+±´r@ü(ù{¹èæ “8xö0ÑP¢\BàQáÁú[ÄXþ#+§½BˆiLxrDÛ)‡f”³øû¢KA‰‡úM„éås_Ëáp»»··€!)uÓ¼Ï/Ÿ[éƒ;õ¨ÿh/KyNi6¡+ž[G$èoÁܵQbP®p.2±¢|b龞Áh…„ "¨&µjÚþt&ØãJ\EÛ/z ÇåúaÝ•.Q!"ð›_[®ÿ”åï¤$=ØK»$ñø…Î‰Þ †‹<;ù24ÇIëIÇà¿][ý9Ï»Ùo«íôèòÞtÍÒ¯N*º 0æVE´G\.ï©Œ’OgÄ»~#+ÉVƒò³òŽèýf:ê¥'4ø°ôeä(ä‡b<êÊf—=¨PžÖežŒ<\¦¬…¤ö¡x(¦`BB*Dú0aéu"âoåo(e(¯%c:–FЮ‘a`pqæú±Ôp»”ˆôw‰²¨¦²ì;¢€pĸÛ"@üÐ "J9$I¯HQïŠ6½ãT*Âꘊ“,ÈÌ0™ 2Íî´B'}Q„<P.Txé¶îÒYÏÍ:Ä)ìóJ¯¼r¨á„£ñ•) ®,È, Î낱^‰s¹Â¸Ò¯9«¨73-Æ(™æKA¼#ÄI æYÈo·luèQ\ƒµŸ|×DZ.zâÔ>D…#) ý1¿=?°~qìóáfµpÿ辬úú¥jt¥Kd¶‹{ø×ÓÆ™:ŒÄW·—ëÁ³ïö~ÒC #á³ÑÉ=ÝóYë×ð?‡ùìçþ“ŽE"Ãôr,Ÿ´€Bðj~¾ÏÚúd;¿3­4V?2¦?Pµ+î©YÅÚTI#)þÛVžˆÿõö\Ìý‡é-›Ç§Ýê =õ^1‚9a+7>”0âËÿÁüÏþÆׄGÿ3ˆfæ&“÷8L§ûy}ôøêùÿÏ2Wæh´¨† ê¬B?À}ÁÙ§ûÕÿ‘¼xh=„ÿGÁΚû„z†Cãmßñã÷A.zDÿµ÷žêgÔ7v+Ÿæöü•éÐ;I ˨^æ#§¨0WjÔ<bøO«Ÿ8-3”J~ÅÝ)«ú¤H(—üƒÊƒ»äAøvˆÊÁDæ\¬{Ç5^Ö_j(Û•!—¤rLF I$/`#)bˆŸ¿ôœSìϾ\Úóâä‚€Ì4ùÂöˆ#+oükÜÓaFf¹#*/“Ðþãºêù?¾¥(6$§‘…÷¿é÷=Yß*õ«ëÞ$?i×n±ã’é)ï ‘´`9ÊŒH¶‹“¹í6ÎÇW¶}F~@~ô†P`€H‚#*ßMñ= D>ëµmR1+‘7I¬˜ó„檻5R@é#*ú) h”!ùðnÅâüñqN5'…’ç]$î¯"ôKøQkÓõgÛ§?ú=EDõsŽÑmr„‰)ÐD„ÕVcL4{ÿº•xœuÌ]ÄhŽ,` c.[ ¡×¯Ã ˆ#)U¯'ˆ’5ôÞˆ7¡á‡ gùFe#)~z”)Aa`A‰Ö¸_!}ª¾Õ$z§f-ÈnfAaA¾ÉŠ› äAß/p’F `öØcòÌ’ÂÉÎ&k)°°EÑŸ+œÊ!ª]‰?e9#)XÉÄ޳Ěü4P)Mµ1ç@øçDãÕÜÜÛú9]½û¼¢²ÿv^ùû#4ÄúÕJXG2Átñš‰+Àšëï{³à–Ke(Ö½#+¦±©0½Q,+*ÚJé׉b$€XŠ Y-$R%Ë»‹(4­g7½ªøn‰¦m–W]”ÆÏfa”ì8qœuW Sñ>²D1Ê¢+™!ôä:‚ѯ*µ9jW$±Â3VŸf)ƒÑ*lW¶Ø™¹ž(¶ößyÚ“!Œv¬ã¶QBTTr"ÚhŒ£4€¤ðÄz¥ñI‘Í‚€A6Ð4JPoZ 8ë}3èð6#)ý3TÚJ#…þlÞU#*>”ÕÏIÌ{•œ^án;ƒ· {$PkÏáçŸmaµÏ!|"ÙØýQ|ˆœ¡AÝYJ¬ÛÌBùiž:#Ù]Ü4Ü õ‰QG8ïÐËÎ,¨ü¸Ó^žÉû·ÑÚ@´2ªô8äìpš”¯QVŸ”s;HÙFêÐåõú»m?Oßu„`W|ÆýárÖ@#)‚HŵÓWÃòxä¯8Ã%…%¿­±ªýÿŠ$–cÄU¤áƒý^oˆz´î?kÁ•·1sü¿×uŒˆÒ#Ïn:gÏ] Ë5|›üOÁg-¶ËVó©ü“SW3»M­‹+ïê³Ä"4kŒä]]!c+óÉ|]3‡îSóüX{ë^­%a0åàf–x8y =1\`–…ÚázNö}Ë¡ê˜9„)•×h¥Þ²-#)Êàó)ÜF"ªIŒtÀ@Ò*JL:T…ûûqÁ[¶Ç½9Ïy–ÃbÊSN™ÂDÆK*1A`aê»â}×Ü‚#V‹D#*þIeÉiïõ÷8†Ze¦²È Ñè\ù^ãç! î Ïný“†EmÎ׌MoݦéPˆ<&ÖÆG‚·¦jÇŠ&dÛéqê¶Y-Ï«½¥ºá8žkŒïuZ7s$ qäL4•ŠÌÌAv#yƒH%ØXjÇBuãÃ3t·gÏLÅù ‚iq¿TGÛQu¯Ãàã0èD_$k‡¶:zàÔÔ¥‚ˆìgj{9i+ AÇDZ#*ˆÓ)åªMG¢Òé3•}£Þ£Ç¾wUµs¬vöâõK¹y¼M>ÞKu¬!ËÃ:q§ò7Ï6žÞŒ&I£¾®6uÙüTÁ3öÏúpø$e)ŠV#+¸lÏLî”ék3©REUДZ˜•D #*}¯ÓÚPŒž~}œ^p•sMñûã—×Ùq=øƒrIš».¨Ž™ë{?Ž Ë{ß·>Yž0ý»BU¸‹¯!"cƒ¤ZnÏP÷áàíÐ.¥¹fý[è´ú%¶Ó"”)ƒapûÍ¥€tºQªC:ËÅ÷…G3fºäómï.¸ôYÖû´rä&Kˆˆ}µáDJ)6u÷mÃv0° Hpð#*Œÿu-}~ŽáÌü´Bƒ°ô yVÆU‡̬#)ŒE©ìì{ p ]ù|-*u¾Òɵ*ƒ,¦ }…›ðžrÅVì³òY‘‡Ú>Yôÿ±ó\¿l™1è|Ò~Xc}E:‘òE#*ÁÃíô‹;8ؼø‡Ãî{ZCkNØirrTÏ8í—=¹Kýšºš·…#+QËiƒºt"íÌ}’Ʀ{¾§ÍŽî7 Øþ¸›ÔÔ^ŽÙæoæaÛ©å"žç±¤j$ŽrRf‘kÝ¢)—'û } #+–tÇ©#*ŸêWLé¶K¡åŠ«Ú[Ï«S;uåÝ yaqZ‚š¦—Ïïù…%­àqu¸ lv‚<÷ ³d-#*6ÞÕõÖñëÝSƒÈ±j‹®[nD|^ÿNž¬¯¥2PsgäÝ&<à¥W¥./Âc.M;>/déMçî¬y«?R­UT A)ÃSÒÌ—$½tîO“ϪíRJ°¾°0`»í ÖùÀÃ^¢‘²#)m…žŸo‡ƒÄIj(‰úß«õüC}f¿#*ñ‚úäÐNϲeQb©<¬¹ #)‚p¥£ºIåêðÙ¶ÿ@¶åÑk´ƒz¨â7ès8m½uõêq/ÁêH2×>t~6~ÏŧBö‘É*µE@9Õ[>RëÂLQ¨®¾â (‰ËdÏnL‘žÙ„·®ßàFºí@ìd„ßd0ÝJD pæÖÞIe!$ 82È#* f˜®õðiüûYè;€¯mˆ!ÚFL=xíGWc'd¼’ÔzuQ!jî߆ìøaã6¨ñ†±ÃšQI ©`º;{ÚUR&ƒ‘nFº¬YKªpÅÑxTRâ’µáÎÉBê~ÓX:^ðzmVÅýñ’PG<òuø`06 ß`ì²vqµp•lïÃ#*&s\]ÛSÚx–hÁÕ´xïQ¶; f`A]Ò@sÊy‹„s ‹%(tceäFz¥¡ÚíXî¼#*kô¿q†cÞÚg~zw4–# ñsqág ò÷·‡3±Û'ZðhÛ«ç»i9”áÑ Êlö ¦¦Méu3µÚ žáÑóëUÇ)ë#+ÚN½­Râ 4°Ì…Ì¡A¥Cl¬Ìݽ\"S#†ÞáMÂàÀ<‹k£lÀçax”˜^ù¿É„d:0ž“¤`ô¦óÐöÛ-šõµžŸL2ÆüYÌà\Û%¦kccˆœ"òòɪ"Ì5e±î8Õ˜:È"‚—55¬w«„¦«È§'CaunNÕÈ’ŽIØ‘z?oƒ™¬0¿x„OšÓrà|¯ÔIR¤êÛBŸÂ®¾È†¿%rúÆD·Õhr ÏcÚ+ k(HAA¡ÊƒqN<ºOy^¨©æΠ/yß¿£Ù#+ÑSx##*jŠeAú *ù‡{Æm˜r:Ía MÌ€ bàÊ&’ÀifWÍçBùVd½ZÒ>…RÂ#…ì2íÃP.ÑÚí=BÛriW#)l$; .…½ö†¥ª¹ TÈ,{«?Ÿ¶ƒ­6PH½ÆŽÓ‚¾Ó Q 5¿¤A•~Qw|è'·´xÙ”:³µ:J”Ä1Óƒ>k]N¬¡fO°c+lr]h±{'Õ}”GÑnÅ°“œý%U°GD‹B-CQ¨uPs_ WÔÐu[„Ps¤Jdàè¬âäà&.B$M·§#*#*b©ÌÜ‚HlyùàÁá­·=Ýä2M“¼Zpê܈•²êwFçlZÃmËoXB CQ‘O±b×¾ÉDXkSÛŽÝ/kq6Ð#V©P­KtÆùÄÅsúTK+AŽu~ÙÆkÞ÷áñm7©Þ„:¤Œ·Ã²aïÄ,«&q\‘$@µÉ¯×ŽçK#*J2uvïݧ¼3¥›úÇÙùÜî©´H.b2¾e%k€Ûš¢t”=Ð#*cžÚá¤tèá—n^`»¿$ Mu^Qî`£—¾m¶üÍclRŽ5§:Æ<¡Âí÷‚§0yuv“Y<€D¤”ÖøõBñËø–-źy~éoÒ“îUÆÌÂÈó^u¤Do3q"’È ò–¥;Íbù¶ì0zß‚´.žØhÍt¸h BÜ‹m£O—rÎv@0¼ð(“ã‰KI¶ã[R9dkÅÛb(e$òUÕuXà¢ýºàîêF®½nÒ hi¡Ö4 LBc¬ ¶"P¬d#*¯KG`™‡Ãë/íò'8B‘Z®@Ü0ÇÁð঳Ã\¸k#)¼"ˆpáÆ÷à#5žÅE¸ã` sépZ€Ò#+sóóõ¿ áÌp²ñl°(3"ÞȉïÛ*+9O¾¨o†¯…x\µTJjY ³_$#+·Ôºzç2ߟG5.ù-ºÉòœy©xwí,)³û¿F­Ÿ£ßíÿ8÷}zät ço¥ËõOèÑGסQ̉'ü+Nxá;}sé±­Ù¶ªkò>I¡VsåsÝîBŒiT,ˆŽ•[²…g$ã±ïȵ4MFñ`{~> Ž„;Ï¿-¼ZûñÌ'òùkjy\ç³PÝÊN#)B1ùßÛ¹yy‚Ì#*Õ£N'Ôe:ïÍŸ£÷/_’îÞø£ùyG˜ê<¥xÃâ“p¼¼þƒpü!`o²]øEè(,DýcB¬ç†VD#*&j?`ǽŽœþ§ª«ý™aÃï#)JúýؽÀY²Të*”Br)SŽ0e–Ç÷3Ý_‡úÐøÛÝÇwåÙðýsBO܇õ…ô$A€ašúAÃngüÍ¡¤FÿÈÕT*¹ý¤º–@íþ]jùÁêa#«`¼4‰E€.“ˆ£`õÚÀóP¶ÎÏè-ãž··²Çç…Ûººoîºk[‡ûªœ3s¹Ç—GZ0¾î}ÆñSR‚f†Ã±.AÞu »·q#+ïÜiSÙâŒrk¼òÔ8”*¬™ðï„Æ8BWŒ_™J0T6(Ì kª£È?ßÈ3ºªu—ƒ]*=cÚŽÔë÷£Ú]1‡¿Ýèr’úþ³AÐÕ Z;"µÿdT,+Þ.áð'Pš%–‘Ñ…î\þK¿ÚÍ5”9€Í=M¶ÏØú²@e#*C\„™Óù¶73O =‹Ð:Ð$A7Ÿgð.³¿²¬¦äè²wúKõÇÅÿj­…]ŸÞq|tç˜1f© wùƒÕüU„ÿEô]£å¥-–GÃo==Cd¥_C™. Y@#)¥`Ê•ätk‡ÝýÏøý_?ÌWð¶ârõ0kmd#*š£ÙPŠ6}œunÀTí` ie•Ãè ˜‡ñ½Ý  Ði#+vBÛ1>â’m>“StKçKP}Ej¿÷éÖIÖzÃ.Þ^ä/Cý£`Âx„‚kò†þÄ\½;&ýTZ4 °A)rœ±»†l¤nô‹#)Á°Ô#*쥬ò„†Â #*œô™n QX¢í$ òÅÌÝj×<#+ǃ*CÕؾÖÏAþV|4©UÆå¡õå÷ù‡“¨!ÍìNä+¼éímÚšâ»VÀiö’j9(K¦cᇬÞÙü]ÇÔqÆóÎÂØÙêXK" œX°YH—q; ® t¥Áv¼”£H¿YlÙNé¹±©{NÜ#*®c”î×ráÁ6†ŽIÙ0ÁÁű B¯€t‡æ“ø É% w¿¥{qWM)'Ùõ…îNïíñïY @ô• VÛ6ù†¾kMú´ ¨TÎ(ÔXÐ# ýcNù ù O´#)@#+éÜ]k"M~˜ÚŽw겿Åß~Õ´Ä‘û±#ÚžÌ~X;ðux!×÷F2Œï¸n÷#abÀßbÌHzG©FFààŸû#'#+”0E#*%Ažr­æ”J"ÒkmbƒÃôÐÊ×5ˆj"²H§äŸ?br;é)X`öûý4®U{íðïÅ5ù€ÖÿmÙGsãï?yòƒ&G&mfækBôdŸ‹Ù®l2ëì!Ô²ƒ\"ty\ŸŸ·MP¦g7;¾üÏ­_s,ûu†vÉNÿÐà ‚ŒGÚyé6:š$£OnKà,͆$«Ô'¼i=¾'‡YÐ!Óïýaµ¬¶â¿sÈ#+RÜc¸¨$ªa %—•Ëô›`êu)”]#)æS‡ü%ÃSžE›æÝÿĹH„™s™Iàh„½ÖÒÜ1©özÿƒGz)K¶¦Üµ×JŠº™G9Îj•UG,ê²KÏ—fÕɘÇìx6#í.M{ðé=†{ÞŠg¸6=!ØáÔFA„#*Ù‡<üŒ¡°è:L×H;Þ53ijlœfIbíQM²ó‡C÷_oÇÒ"ÏÔd!_”þèIÌÛïÝýÝ<Ÿ`úÎ'ˆ”±'ó% .¾%… ¬ºŸ9ºÿzlCÛ‡`´EzÄ`½|ÀŒcdúìªõ A°ðÿäL¨+˜b['æ#+´‚K¹Æéµ±gë¹?Ùú¾ÙûCcj¯Úz\[B™ÔÔë˜e$¶#6ð)ƒR<AÝychî‹òn´ƒ…€ƒåg+ô‘ý5z›V4iDPHŒŒ(Åã8²ç9¡ÌçŸH¨´µˆ6(#)4N± Ñù÷‚ìÚµ&F÷7yGÃÈ2M†€ºY‰¬!ò Óìû?ÛІߩúùöH4PÓ î¥6›“í㚯PÅÌä‡Waå—9-Eö V#+„û‡Xd’€êxïk #)ÔO½,÷—#*‹!±ßn›“Ý#+TÀÖø°úgy¶L…°ð\9I»ÈAÐÌ)„8€È娦ŒÔÀ&feõ"Ù¸q`vò$íî+à™±š¢þfF{fUXSQ#+D×[ŸÛÁ.7~°Þq9'gœ\²3îz­$:'1æ`¨g–³7¨-£"êaˆ@Ð5’HŽ#+ ™Èþ®¿¸éëÙrÝW£ò@#*¨\?£ðÍUÊJIúpVËšåßËý½ô6ýþk§¯]©7£ #*DEAAëÃUWFoü¿;Æ!¹á’ÒïQ¸EŒ¦h•ÖFÖhÖiëZ…NA²@ÇÙš|nKhjœTÝÈÙfW)H†‹5#¯ $’LÕšÅ&*eÖ¦¤´µ¶ÓÌÕÍYk5q½2³ëÆ]CzÂÕ¯5 Õˆhk[ÑM3tºÃ#Þc«FYF5ÈÍ6Ó¯í[”½½}ßØý­®ÌjrѵDEúóRàL°þ»Ðef“ú‰¨õnv#*Ü·äœþ<[œtÖ!;Æn÷'ËjôfÔwÄQ4@zÀ«á#+î.•*00(E,nʸˆB)ò(@v÷ûéGÛõÆž˜‡RÓô»×hÞ„CŸeK&¡ ÌdTE0Ñï\ ÝÆ%¡ÿYò‡ï,»lÈ|‚&#©´Ô6@šS¾¢Ù¡UÞ¬³X0IA#)ä)l ¤Ú>PdÉ:Û·ÆrhY+{‹„’I¶#Wyô]ãÉÎ:¹e%5º&V‘ŒKDux#*¸w­6Þ¡Ãn®4îàsø”øñ‰ËaèŠJ#+Sõ_³Sª/y¡X>‰p2ó{²Shža›ú‚Ö,ØÞs|‘Ìát应Áö}Zû™´6aYû7箜fíJnP.nS)QMÝ´äÃ&a‡{w†#)xˆ—‘;Gƒ<y¤ÅÞ¿€_ˆWQAgÇâ¢|Q:‡è7¨#*¯ÔŽ6©†é6Oa!8zuî+æpüo¼ k.%GÚ+àp=xâØóúÊ©EIJR(Šo“i#*q⓼o½5ÜBÙ‚†è€»5¥„KÅ#)Á‰JÅ,éH)‘ó|#v#*r#*í+»PÑý õ“äé‰?#©:½Œ“Ì@±Z#+(e,ÄO½H‡óE-í†ôN²²7Î®ï ¶kñ ܬ«pvŸ=À/’uåˆaGé ®,ü¶p§_ʉ Âj#)"ܯéùÏVúü–»›ý ùõöŸæ?§§QB#1ãü9Y» ý•B¿Ž?&Jõ„·©ëK>ŠZ ¨—ÚmDó±ÈÁõd\³ö‰èƒèúÝn>i=Èì9ÇäÁêe„9{ôM˜dP‚Ä"¼èH}#*wçs8í{ÍáöAÏ|?’cr»¯ô?IÖ8CeP}”»XÀýÓóíxîÝàŸyÄC¥<#)#Þ)Ú@‘«£À#*Ïj‚L¦IF±ûƒÊÁŠ zwóë®ÿyæ]ÍÇ£ÔiËhBµqàGŒïâSi*›oôŽ¦ˆ†»N] ²D‘YdR2>X9úÂε€é €w '¸È gŽ«ÉÅðû´7;ø˜Z‚>HtÁ„€ê Íå¾*?©ú€¨}]]%þì½…¯ÿx_dWê'Ô>§Õ&³ÛXÝ1UD bV¸Ð÷åÊïnZŽ¯® ßôCËàUõ[ˆÔÖ‡¥LÔ 1 <FI$ž´ ª§úŸ@&ïáÔ!V+ ]°4d€>Яà‘I>Ž´S®;µº¶´öœ¾™GW  ÁÕ3çäê5…[¥ÚØËSßãÅà_B#*—/¥šT† ßà‚ãôú·£°20®Uõ*ZÉ3#*ûî#*º|½5 ‘œeÙõ•_Á¬ðc$Ÿ=#C‚ïvö#)uç0q`±8…‹IjúoyÇî99™s”0d$Ûz…àòûLàˆjb¢#J””p2nF!ª~ô2¦s$q±»rp‰`±'G/öR¢qÙØÖ¡CˆJOg³îÌ@Ä<ÕDc ’*‰RqÕëÏ›¶ÐËFc›ÄŒnFÚ=¶7°—;žet¦çÕÃÔ›:y9‘LÂ*ðß<5Lñ†$Ed›˜P’Z-hU£_é; ½¹r%"QCôò:K°õ¨Õp »lªV#*pã:t#)÷ Nÿ„ò>0O:ÄÐ>|Þ ë#)ÁÙ¿„éd@<U{M¦¾ÈÿðKhò À)àSj©Eîx{¾w‹úŸm~Œ½ÜÂ¥ó øb#*aÅî,nµOl–öûgóÀÒãDMIþ9QG•IB¿ô8Ør†«xib˜*ΛHÃ#)ob£J”¤ªRUjþŽ}]³ÐvÉÙ<mlqð£­Lq="/דKâ}¨Dô¨–þošIzC[a©†Š?â™ûÈï5=¤Cg¶P©ì‡%®õ—ßøœ§3ìF¹;ÁOA2©AE!P˜…OŠ›|‘õŸÙš.8\XÖ¨ƒW˜ âQÚ$N»¦æzø@mr`%¥~®w§¦Ë}¹Êïx ¢Yùkwòœ:¤èð±í0¡JP‡¨P¨Z8}¿¸ÁŒû%h¢"€9»Bñö¯NÉ°€—?·&ûŽ¢põ5óS*×ØÁÒ1ýsFð¶šÃ7.W†õõ¤F%ÇæF%LæV #)ÚI0*ÔÉœ Kˆ@Ì9ö²mªU¸Q”*Í€¨nÈý#+7Œ™‚(Á“†/`<à® ’1RŽ"QTÑv(&op~9¡Äƒ¢kš–ú±ÌðÉÚ÷õ| 4#c#ÃËcîÔÎ¥h,PMad'Qb¨r@)c)$Sö¡ú6'¨?”îÜjS3XQêÖX–J#+Ndؘ1H„GìG4éÚ-s=é8Ú¯T‰ÊmW¾…/¤4UXS à}w‰Th¥‘YPX-™”Fo—y[ä£\ªô®¤V×L6T& ¬¶ZUq*l¢›ajb†'öáØWÃÓõç<qíŠpˆ2ϳ)âÇÓ¸öPPo_ £§¹˜2é$Zôžò)g7Õäô##*nÄŽA ¦:ͯѨ#+l‡ïH&·j ˜ªX@ŒFDa #+2 €K8.p© ‡#)C¦tRˆ}(⥅¬ÄðRò” :ë¿“³xBJ<²:NyÓÐ/À–ýH´/!Ú… jÑ»‚unOÑ>º)'á›û74=#)6Ê:ˆ#)_fÒ½‘#+Ü<šNŽ€v9èÏTì·M¾»lsrEÚdÍ…´…@Ðaå}VQé=ydëìÁ‰:àO¬¬ÊLIsÜI÷¶— LPQEýë%Oðü™=gÍ€¾áiZ/w¡Û‚žÿsêº|ñþe6z‹è÷ø·¹ë©PüH_Œgü$MŸx´*ÒÓ3Ó&jBð°Œ!†ö(ö,’¬’Hû\ô`ú÷ɺœ;õkRZÀ51 ÐfT´<cl„¹I$}FþGLJÈ6„z¨èÉ#¨¢9å“’Ï«øצ2 ô 6)ÀÊ„8&¬ X>¨ÏW·ó¡Ì©-#7^Ýhò³¹ ˆÒc«£«¦% ~Ú(í !ìIøCäýÉêåñíTõôÿš<eÆ”ð !W1÷ô´“‘“˜wÄ‘‰–w3Ô†%´ UA‰S³³KÈ´††¦ÑO‰|O¬kü€ §Ø#*«ø ùßîÅ#)ïP–C2?½àúñ”»ô‘§úý‡§ì8'Qƒ³Í ÑEE%*­G ïÎ ý8S•uúl¨2/Àa_©=‰Ú ˜óJÀbŠÌ*I€äûR{\©,Æq'TU, •V½Û) B)ñWñH‹‡æ Aõ8Ó¬çò#+îí{ùél¹|”„ tØ«H¥!Bâà ÄûÁÜ æfèEqé:D3O@?¨,_³®}ÂrÝÚlÞA#)Üö«ØcÐ#+FÀªÉpHª˜PLôWQçÖrÉNa“êÛå¬zÔê ë8yTåi#$¼â­ïY:À­Ôñ#*¶)yÜÝa6Ü.É!¿XyâzK¨Š*üÌ`…–ýpçëÿR(ÛÉMގ߃”*|›ÕB‰³‰¿âUBP„ £§âÍòïh q‹ ÂpN°ÌX?ŸJɉ“3î!ñ_Ó£½Kíb=0“Ê:J@j1^/.o *T‘5¨UʇT†dK 9lQˆ^ºõ’‰ˆaW,À”IÕ*ÜýyPr¬éœGéßFÆ|øw#+­mÖ¨r©TŸœ›£•Ñ>UE¥:5,OZ†ÀZ#*²úwâ#+Ts‚Ôa÷þ¡óö%j:¾`|s°x<9xõ`î7˜Éíö¤Š¼#*ý6êO“g"w`¤P°jI&éTp2߬„rs©3:ÉÀWÀ îºó˦]ˆœâ²ìK†gϧ;H%CÂÜ'®§*÷w–Ë€>ùÈ;@ rAÄà»ö[g+LÑÕ°"ôƒš¡¥ÐEM`èw§A½¸Ò1!#*p Õà#h"–H‰@Àäo½tÛ‰óB{ÉèPíîõ½^àøì= [66"N ëiUÀz-ý\N<gCÙ°ë5#»J B;Ïá‡â}rÛTrZO!ØìBŒç–ußÁS(/¼bú‡ÎŒß>ñ¨xÌ•Ëàl‚€¢—/lD$ð»wQ¸ÏlPÊŸ__£Ëæì-:>¿ô 4-Ò€xB½ÔH",¡b Þû€gõrxÿ#T€ôûÉÁ‚¯½YÞDç¡ùì>’8ydTó**n¥ ÷–%,"pä~Ô6(ŸÍϵ¾’¾Óî  å™êlÌ_è¿ÏÎÎŽ7£‡Íã Ãœƒwà~ŠíÿÕoÇá{Þœ³â Óø©uä7k©zs€#*D’D¦’#+˲²š´*mØØæ-ôh ƒà÷`>¤rBÁ#*éA«’#*!óa´#*lu84BÀG :•R‡ì³¸ÁW¸»zY€ú~Ä6¨±û"²g „‹,ˆÿ4¸:€º¢ö¦Õä/.Ô˺‡0,Bd% ¯hA°kò:Ž.îY´wÇónÚà}Z†Éé]ttU‹Â„_ ¯;’Î ±U2H?á¡‘?zÊñÚV íC¶È»Æèì¡,ä`Ž0Ø67ñ#+/@zÃaŒBv%¡EÂÐÐ+ãèõ{ {š³üw¾}²dP¯‚(ŒšA*Š—Jn°­EÿŒúÞâºe·pŒGôñõÙºÈÀ®Þn]¯C9Q›pë’]q¾#*ãvË´[ÒQ¦ßó‡ßõ‡…­1ìÍ<¡kCS#*…ÚõJ0Ä8"ï/ÅåñµzÌU1¡š(¯~ûºñ÷ºä nî¡xÏ<¼"2¤š+wiŠŸ_‡˜ùÃÊW;X}¡òÓÙCí ™’ C ."²À=‰Û='È3àpFP¬}ñ„Ô¾Þ½ž¼#¼ï8&`Ûw¼là?:8z¸”Š/(…AJïõã©8ògà ðI€œˆÊLoü÷â ÔÜz΋mað‚=jÛ$„@Ë;]wwe}ßؼÝçÃ×+!kÓüuü7mQ8ú¿€ÃcÝBaÊñA£ðÃD«BOo­Ý®˜p¤Ã]ŽÐ˜EŸÎ°6ÓÔJ ÓalU6Ù dˆcc.ª @M„[™§æ:ÔOíÅçpOO…BSAÚ]OOçÃDŠÈ.?#Ã0Y?cÍû¬áCU*9äÕ¸S^t œ§$u#*¯´Ûã8ÈoùøÞO®R…)ÊßœÔpîþÓ(Mžÿ®ýRÑŒ¨xóÜöÉW¦¬{*áñGr`ãÓ©rd/ú!"Cò=¨·;Ì»wûu%ç#+¿ñj8²šÌ$4̨g@¸ö:>®Ã\rU7©6Ÿ$ù….ýÖÆw‰[øÞS Z‡Ö……—Üh¹Ò‰§Â&/—ŸÄˆ¢€«#)a`“d<ªào¥Ð NñÛgí‡P±±]Q,EÝîê¶DŽÈôýÇÝcòN_Þjìd!ËLÄ»³ØcÐvfh@À’ƒsÒ{@è¹ë]ÁhDƒ,ŠðÛ}ÐrVa±ûorXýý}/Jr=ßB\~{ÎaõXCò*^Þ¢åôîW.1s•´!É!å [ëõ0 oÂõ,–õ|º]e®jfót̶#*‚#*]É*EE’ËF¬˜eRÙÀC,D%”%DÊÚš®¦å_hq=[VkAU¡E`<50´•3®0••dzØ&ÔþÏ¥š©Qÿ¸f8M•!C[1׈0µÿ€~½ˆÌQ-1ùª *ÄóÀ÷¼°SÃVo¶ „ÏYú\Îf»Pº›–ô#*DÈRHè2ì[h®îVäý&XšÀö`i o¡ úpKÊ÷”ÏßþGâOøþ­–ìè±âdfWòp+jõußó'èÖ¿´ÚwµQ’±FH+ü\"þùzÔ12É'C«dåR°_ÜÓüìÎm¶þN ´ß!îðåüæœûxš¹×¯g<L!å?™PhRZ,–ÁˆBRTzáŽ?P€[Ï‘­khíIÓû ¨¹Â ~§ˆ ‡ÔqåAåÛ:å½´þ¬jrõò4ÉwNÏλÌ|Ÿ—aߦ:x~ž*=V2á¦êŽ1UÖeŽãœïgòcô„N??2°‰×O—N÷æ#ÑŒ²‚9F‚ˆCýØ œ¡;C#¾+ïr_åìžµVPq#)  „1¼#+…AE9{‡×Ä7Be6BIH+Ÿ·ƒœbHÕ|? /ßõçn,únQ<ýX¾†U¯ê¢ÌŒ·‹ðÕD­ñ¿RfïŸîýÞ{ŸîLÁAè/³!¿,ð'ɤóèÔ}èÅ/õL«™’C¹Àø»0|E:o‘#)øìæ·‰ùŒMº&Y&\Ó{í¯"m“|¼<Ș„*6<}|¾ù<Ž=`͸_àÄW&Ã`ÃóܘÍÖr 7´Y¶ h¶‡á©Ó3ûŽÜkÒ=ÏÌâá£[ô¦ÉeÎô™°ºHð¡èuÌX+óŒeÌ º ¦ùŒš„ˆùäD™gZÄx>£ix|šÃõkÈy!Õ->;2 Ä± þ²‡‰^'Ásþ¼"*St^¬lˆoº<¥ ÃÜ3ˆï@Ïèk¸(˜ªØP4îæØBñx"s£Þ 'Àá*7É槒Oéw¬éa* îº4/ÿoölrŽ]Úlo:¾ÚP»=¤?W×nÏ«µ²1ýÜB‡bᤌÈ*£¤¡±fv5§™Ã Ø„èŽtx~J½²íE8dõŸÃ_Ãy ï%wBwsDÀ§#eþÑžNÑ¿PÄh‹Ý8”#+,jí{(*]{aÁçÚñïÈnªöÚWYJ}ù…GJ b0âo~ ôq½±¾ÀcÑØtÁWþæXþšèUŸ¹ä‘r¢Ž}²oëåÑÛãzo¥ƒ&¹DY«-·8B¹u¸¢ñb8§…*ôÝb Þçú#)ÆQÌù‹€üAðÙÛï[T?Üï9Ý =d|í0¢(ºCÄx:'æŸÎßÎIGG”HNåwØS‹îý+éä-‚¹²¸5‚ñk~WÄ9ÑÃEÔÔ0{}#*%{ÕU:V^¶‰B4ž§äÑÕý—®Ž­ïùšxVsÓcßœ† ÑÍà @uTfköºýc·Dà;!Œ—åxØëæ[[±SƒufÈrÒ©›­óÜái£¨ù— Ëƒ¥a€»:»E8W+ç)#+QAE`ÓÁwRwgÑðÉyO†áÕ­&iLq‡fÊeêížÎºÄ`¯e®¬á¡n‡—²Ø€Qí³ézââù¼¡('­ntZa¡´¯âx_&ÍñzŠ V˜*]0{fXå..Ž{·pHëY†÷Ø«ímåyï׎0ì0'w1#*Âh\ÝÍݶXë@þS®Ä.y~yÂešlâ±2o›¦zÜókS…Tõ!Én†" ELÑ µñêé¶jJˆmÜÃz#ŒsÁ¬:êúFe™Óç~¼@Ò%óqìT³l‚) œïÉP.…°†ôlq\g]¤ÀÂÝ_òI#)ˆ#) ˆpååø¾‚ï¾^œ¸# ÈÄTY–ÎÒ^¯ŒgßT‰˜¿G“ëöÆ&Ê“°¿žf»g¥c"¼#±ñºéÉë7wÍïTdFê´œ““Ú“sH†‚©´¿¢Y©/åë˜ËõÐù<ÏŸ;Ïåw9×O¨Tg¬Ù˜z‡úB­| ü#vÂí&w(µQ@ Vh39T,4ć#)á[C !P@ˆØ§°¢&Ýj$EÇ?U×L~¬+äüøJ·­ÚêÈä`P)Ì@ %ÄbxÃ#GR¢6Ù†¨÷iœXOÆ5ñ(ëð£ÕŠ1Ñl}ê{¿áÒ } õþdêí…nïTwñóͲ"á/ié¸Þo5¾¾{mF#øîô.̹朡„#+"ªO€¦cʯMÄB2Éd)°~Yr»Ðrš#)û= ˆ„\ËåÈR&fî°.²A” )@–åäøÁ!ž¸ñL:÷Vúõ³A$)‡ˆr…Rì¡ÍIÇ£ÈzG3öñå[àœJÃnùå‰=¦¥¹Bÿ'¹ ÙF:”~2t&fHG/Ÿ;™ÛBdÇ’P»vâ¦LÈd„Ýd^äÜËHpíå1Ôz<½_Þ›;%¦€€óÌÈK¸yîÒ@”DƒX®vtÕ!p}f¼bbÎ!ü|X[[EÈ%%O‹v¦‡`9ìlÂ=îuôgÎ3‘ÑÏ‹ZbjD)Ï={5eˆÒ/Ca%BqævßÑ`ì5$fªE¤nnL/«$ytįOLÅ\†É ´É1V—S#+ÕñäëˆB>rˆŠ„‡/Íb§Ê{_#)Ÿ Œ¬s,Q`‡è Š|ìËêÿ\|ßöýþŽÏéÿ=iÇhBФ+Õþï8_‡úëƒvá4¼þxÓÆ)ÓèôzºŽ´49¿ÙóEÀÄî…Jûþku¹j‹"&l ƒâZ¤ý_}ÏÝûПú‘óˆ ñMa§¥//íÆE½5UVóž¦ÔÁ„þLųq@sÙ·8@u—1`6ì ð2ߺ›“`S»P#)Qw÷œ#*…¦’•#"1þ×jE—€vj›¢¥Q¯¬Ë;I|r#*nu:æÒ #)\ ›kÑÕº×u^âøñLšC@8j"ÑÜôYq'àÝrÛ[Ê¢«ÃfÑN=Ïø‡3¼¼'>#Ü€<4Ç_<Pwâ¸4£G"HbzÕ#*ê™8i|BšHàÓt×ð\žã·÷v˜Ó‡çïµ·e±¹–ÌT¹aë íW¼ëCX B5–ç6±ƒ›º„è<OgíJû>»wWñ¡g# ,)¼Êˆâ'C·gsˆy~‡œÈQÕÔk¶‹|eB™mc¿ÈȆ‚xeÁèë’ô;+#*pÂ}‹Ö3ez&;1r°íöŒú"•B&ªqdŽ³³ôàð$à Æ#*@ cX`Œ‰¡ŒD€¸RAšZ,¬à<vz  ÁÚ4®úØÝ„} gƒ1V!{QiM¥öŽäà¶5¯v÷iòØî(‡—E‡£*¼• @Œ-iI-Òí,ŸaëNa¼'™#+x+@ õnô@smÑú #+uçÝɬ‹—è÷u^én³û£(¿†¨C›èæww®¢ÌôYݸA€q*„$‡ÖC¶ Ûv¢:àURZ&FqiÅꉶ"áJÓdËD¨‚A €Ðz#+ÔC.2êþ “0ü¿pÀ‚D(ÜšµßÃæÞ&Eå~ÿù?§ÚÞÒÍfh6‹ú½ïu‰yXÚÏâó¡¥yÒQAú¨–FÇúÝ©ï#+WÖ‡(@ôH1®í»6Üò„¼5ãîÔ ÑPÇ»vpÁ“Ý{{5€I°*D(9øìbõ–ôþƒÙØl¼Š¨¬DG•ówï½ÅŸ0íÂØ•åÊU.J`¿pÎËFÎŽCšÁ¶"`ŠÃ•‘År÷â¶Õˆw…<˃ú]Òåê¨$NPÍ!C´Ñv¬o•$¨4òmú›‡¡È Ú¨ü¡!ÚÎ0îúcKNÄ5CSXª©È–voMT0|D¢æ!ÉJª?9Myšh0È<ˆ¦@™oçŽð¢25¡Œë¯Æn@"j^ À·äØÝÛnÁsð8Gln+»‹ ê+ÊTîÑp*v ÙÓ ðîhýGvîG-2ëàgç scüýkã.̇xžÔ©¸ãh)´Îë&D0aõBîÊUâèj#Ò—:Î&e%•ÝáD‹¬së·.¬é5Ĭ^r)Ãg[Äb.;a“3¤ðžœX8: 7ƒo<û(‡yú»ó·®·…Óv9@îŠR´’¿»ë¾uÞ÷ÇŽy[o-Y©…’Clíf|^sDR7c‰Ék–öÞfkŒ#+ô2pöÛ$‡ÀÝÃBV¿òiÔ-áøn퓤`œUÞPòaD žÕ½ÜË,x<Ã\éئ…é¡èf‹¡X¨¥=qmË`áã´& hl#*nX r:Žòêõ¹›XXM±»š¡ðþŠ¦}¾¿n£>­¡À³)€Ùæd—3#)a8wIçåÓ¶=Þý{ùoÌw¾ !ÇaÖ*’ZÇ|.Ýý6œ5ùçs<JÖh©ÛFfÞŠQEAyx‰O‰¶#*Sw@ä#*£^j _€iŸeQƒBÇÀ ÍbÖ¦æËH¥®ÁêAçÃzi “k5„lLç㳜H ES-ΧMPÆýakœ)5âËD&Ëaœpë+Jœ£×â=‘+‰¬lêÒBuŽEâë¢^»bhlo¶o? ±&¡6¯LÀENÃ`á0òI“D¬W­¶ñxÆÂa;´X(‚`YXj60ÒƒÕ$9gvò1åNž!läÒzg¼Uû‰Q#+ P6Í{Ú"!W¬ÄfáDõ 'a¡Ôf°v Žœ ©÷c‡ ƒ=§ˆ„t —¡"™šZ[}áÀzu€ÇRÓ˘#)rxdˆ3·‡ôU0P“$#+Oaº)DÜpá™7w7M‚o ¥6È-ÁKáܘ–®‡†äSP¶È¸BHï$ùǶ»Úòˆ=ßaɳ~#+4ÈÎŽŠ<}|¹M™tîèà5,ï8NÍrÙŸ6±sšTUY) sa#*=3©zß;B¨Æ8<*Í]-½Sv—{¤O#BƒMp/‰ ¹2]¤Ú†ÆhtPEQ‚¨*4po&&’†rò%j˪ŠÄ”…0ì7—)‡#*ײ=§sF!ÀÞg&ø(Pò(Xø8œLÎÎoE”¤u3XÉMHI Ëæ×xªF…¥zy™ëÊ´]N‡:9p û~o-fSlUæeÇ™™2æffXÛ1¬«2ÌÉ%Xí™è.Åü5[ZöÝm#*ˆ;´qÆg§]¼ÝówNÖb´iR¾ÿqD¥’“<8šZ—ŸKìõ#+lä7¶#)Bô6;ì®±ãCÄóõô/†¼ê°ë|ùé“®e((Œª*qå@¤©—^zÖsϦ&#ÉèôáxÛ2SàÒæžñ#*ÈÄÙ3=ªFC¶hYCS8G%¾œÊÖãç•k«QÇ·vfº92uìE±£Ð+]»s% ¤¢v|»…%Üq¢¥Ýt·Æò:ìJcè¼¾ç»JñÔú¸‹AX˸-Ó#Vy9Z‘©E„&F ÅÄ,Y™ÒKb_NiÖÌæÏaÝcê´¹{* ØŠ­q¦X.μž°ÖM9$;ŒÄrš)AD¶÷F47åµì Q—þ`PNs¤ãª**Ñá^bsð›É!Êéá§ÂÃå‘îÏð¾#*€kUèÖZJ÷háœËwò*¡ˆÈE#+©‡ RàÚÉ ˜>ŸZ“Ûw}ß"tvé˃^YxòÆZ‰Û¯½°èã˜_€»IW­f«QßÌJx×£{Ëq#t:eƒ‹9o&uÍ-ÈL!ñZ£ZNV¼xn4: ( [ÍæF¦:©s¬ÚVðá(¼#+#*daÃ…ÓZ#‹.\‹,X|üiö.d_1úΊÂhpQ¥Eèq$5À™32s¸Õ]"zuÂhšuÂL‡Z+[^Âèì"H‘½÷ÌÍ¡…_E{_çÑæ^ßY-aLËn1¦Ã¹6W&¤Ì’Í šc9ê0IÐèªL®hs£>#+`HçAëÁ£²Ü-ÐÚDZa“À#*Â\'Œœ#)×RŠËÈ4ÎòPj…†ž‹xIËÒ–Š¦• i‹kn»°XèÜa’±Û»€ÀšG-¯9!üré‘úÖ÷äÓ=ÇlërTÇqÝ8ž¹ v;= × QÅL_n+5Eªè!‰‰Tß`ºèæyùXwò4ÜAuaÕ#*!ÃäÀD¢Cs#*†àƒ5ÖT‰¥ ‡p”T»P¢t!¿‰Ma!}Ôé”b›I#*€qÉÆpã#*f¶MÃQCƒˆäJoH^Œé™–X°ÄK‘a#+ Ô©ƒÐðÒcHs-5­^Á¯ƒ:F¹õuF¡‰ nÜ·ù'b<,!ÑŒ½SŽÐ5Q‘®\Rw|j½#€ZqØ;v#)cDx¢M]ôQ”¥’ÆÑ4Ùªf(G1ÐЄH.ò,‡™Ò85IF+:5_`d{ŽšäS_OªK[ª3‘¯–ˆÈóz‹#+õ½¦tuö¡‘sY‘¬¡´†Åêëe+Ô\›©\äëgÃÅ؇"„2åEÉ3Ý Â–úòÌÞ«ôH…ó†Ê‚O„ÑJ°{ $ˆ÷1íë×ZB!»·B¾ü@òæÒ&9Í—c|<¼Y^~166«l ¨t›V¦„”—™_ÓÂøVN×›òòÕË­<ÞÆÐÄ0\`±#)1PÈCnW¾(#*¤›‹*É&Ò`%·mĨ G”±@PIúyÑ»#ë0Lÿúܦ¼€d—ÁYôܯYèTÊ’˜S²}¬âœkl‚奵¿¸üNZM£lìug膥å×Ûë›5§>›»l—:¹ž#œ'¢ ´‰8*ñxdø¤;D#÷Ϋý#+Äd#+X/ef3Òø®+Ñ6#*‰ÜÂÀé²éÝò¾MZà†8ßg_fzÐy AJÒR]û«ÏEÞDžiÛbÑ¢a€ #*‚;!ÝHóØ=è(Q6ÿ’o!²UJJœP—#+ !`Ê‘ =þßkÓŠyw¸+ñ#+O»×†z”_ü¨ôûfY‡ü?×8t­Ed·rïP#)€%/Ø„ƒ·]µ¿?ë[ZŸž§©v”¢Ù›&Qjþ&Û:û>Ï·õmúƒÌ”#*Cg þ«?iØt‚Ó¥úCéôÄšR“T¶™5JDÄ­íÝòZHÅDBSÃaJ7¹J¥ÏÑUÆ¡pÀ•hÛÂñP¦‰?‘òï{×-X¶+>¹*ˆ®´ÓT*ck(%™¯n xwsèHï`{É!!%-zÕÆw¤£ÊÀ>ÿÕÈõI#*P*A¨ ´Š>;iDG¥èN-¬Ž‹#)ߦñ;,—û•rÎòIêÖo^¨x¤)…="%ÔšÃã#)ìaIf]‚ˆØ¶Kdš4~;H‰#™òž¹r‚€ú"Æ*X4GZ#*ʛäȼA=´†Ò0"A€àà °>|/ò R³¥éŸ´z5(Ø‘-hF)w]¸¤c ŠTI‡ÅÊ»{m|®Õé¶#%«X¡tkp`]0‘¹˜ò¶D[¡¯¯´áYQá—¾ÇÖ‡3dvxHIÕÐnbtÑ:2WÄŠ¨y{C#)X€¤0ô zLΠ–~ª}P“6T÷¨à<¹&à«zk½À7Åñ¿¾ÈV××¥w›áEÍÔdwiÔn\¶iüŠ/˜EÕ˜áÂ(x@*Ä #)4ÛÎdô2›m¾¥cj墩i,ÚT„Qõ†C¸Ž² /킀ȊH""«" ˆ#)±)ÀÇâóä’z­ÑmöKnS#+;eðQè‰B6ÒÕE“ê KVlâUÝFÖ‚nbåŒ%È°†#)Ü Ý­u«ãy^YëäWøäùŠe‘5êx×¢¥$˜·«»xôÛp½Mênk±(ÝÝ ’ðë¨×6ïñ<óo7Lwr®nY++»G$Mèh”%Ò+F–À”2 ©Lÿ‡>6õ|,ÚÊÏâ¾rlƒ@›$~ÕØÜà=³$.4¡°†eœÏ ÄET`{ æOfãòŸÕ“·í'‡!KöQÁ¨8Ë!G_–r=Žžž¥ù§ ïyîi‹ˆb^‘ð-ó8fá|ÏHHÁä°+ˆïÒ]§TóѤt¾µ)m¨Q#)ðI$/,V#*_=}…ZéñÝ4ÙþŠw¶€90Uo‹mµÊÛš¶4ûºÏXÊi˜ÀÀ‘fŸ,Ž&t9YþؼG¨#)N~캷¤`?¬ª‘ÞÕšÊóξ‚Þœ9˜ÛÓ<h\Ë•]ÁawD$ ‘-¶·1( éÁžhY<!Pp2DVb†4¶†›©S{¡‚šXR\l€Âçø±@1"u@Ø;Gâ÷á㕪ÃÕGñ¹oß å]“GõùÍÜwâ°â·”¹MÁ¤HMÞ~F»u'´üž1?°Û¸Öà«í«Uƒµß{`K /p]ëg^ûI g—kCó†·EŒW¼;÷Ÿ Ý«£.üÙ#+ýüxê¹Â=ï%¥rjÛˆ<Íž?Ÿúnó±Ä©ç¢'z<¼—°[J²A6ƒ?–I‰aÛw1KcŒ(ãQ(Û9„¨ž³ï‚¿?¯¢ˆBUJ©E'g_È9ïNƒEŽ¡bzi5RötÏ'!Wª" Ð5 º";…™©Y‘Eô*t+Ðe+æ):cbõQXs-PóBNÚ„éÁUšýÝð½­-ˆ¶6&ÁŸn~5ž[Ř̑µÙqU DŽü·ÖÐðÓæ÷vB4²2˜•ý2Ñßnµ¥*ø@¸]t$ 5Š´Û%Z-’ˆÙ«I²‰*1X¬¬¶¬M6Éi%lkImdeF‹À‚)#)V1‘Ò~_¿ÏйmnžëüÚ<j¼¿¡}P;XI ‚W©|#)°ƒ“#)‚`;Ð>öøŒãàµãd[ªÁú°&ÐÿUvæB†ÃCc®*Žj Ô21¡aY¤Fñ6(¬°©~ø#)˜VlFTÛHm­kŽÜ¢÷VˆüñHªcQÎ$Ê4¶h›x&‘4é_*›šJ2-#)Á0±_@/¥dÌ “5^=žåºïOlQQv›•)-v‚ÎÆ|5#)éÏzN®dÓ[µ‹mK§#*” ˜‰I…Uü¨{X—áõ ×ÖζÅÓŒ¶Ú¹Í¯žÖú>s²ÎÕÉE,÷#)X~6$#*ñH’@`Å~yk•ŒIj+W6æÉ\Öå3aJ™ES0j*Mlm!ÎÉ(¨‹~mû{ëw«ÞWÞ–rÁš†Ð8@#) HAdA•NÀ#)5(ò¾¾—Hýw¾ïIù=8·*i£|´†F\"nø¨dÐôxW‘Þ'h=×b’€@O¶°ùB4‡OšCÖÍÝÿ¿ëþ/ÍèÿñØ€žp|ç#*ôB">–! ŽŽÛÛÏ]£O\À©»­zjûíÓm¼#)÷çH0còu!h QŠÂˆœ‘›?9ã3<‰î÷zÓitÑÓŒ‹• iyþ¸ ¹D9!6}Zit¨GøýM¯û¿ÆÃó0ð›É?ZOve%â°ôn®ÈeÑKâ”ë!oÝIÓÎvî9‰'ïÒ+µ˜A#*¬Zb ‚¡žÿià1픉Œ;+.ýú‘©9¶ê!N}„—ƒŒPá×æÐ_Û®ÍoŠÉ•>å¸*NK2Œ~ò¿‘G[ P>Xë Š¢QC ­.ØÂoêzS¨ùuœÿ%¡Ù¸ 6E4œ2T†]%rNEX7³8­û#+^´'øI” ëb¬½ý“yáóŸg@ü"O#)‡bÿc¿Fçʇ}±GÆí‡çÃÀæUÊ|œ>/×ëütt#+ª ðƒÄ˜?|°#¡1÷­¨Añ>çWæÔ<óQ(hvïÅ®_ï§#*9W#*gN¥ٳȽËŸôÜÚ0Ç­#)?¾"»:ú‚#)f§8È»]H{w^ ì P,\ï:¡J fÛ2”ÚS>â„×âbÛãFûz›ãbrÛáãØÇZq¦5 §#+¥Pžˆ NC>+ü3wf³¬âí!’t2å´ó›BüÐ"b±ÅÇ33Š·u‹(7êCÄ€È#)BÀ" í)**¡ñ;Àµ÷ºf'¾”òˆ†Î®GEf«nß×üÔC¾D„,Ïç?£¿2¨Æšª*#*(ÔDæª>Þ‡ˆü‌ÇÙWwk¹CE†X“_‡íþÃtP ™ªEbxæ:¶õ/„ëóË£–pMP×b‚ƽ¯7îqÖBèˆUjúÚY#)Œ<äE?‹Î¬í¡‡p¡äaîLxGCôE#)¥d8H#)Ä™E&ŸF|ë°²ö¯ÐÊ@ÃrÙŒ5@5™7uß2Õ² ʇT;wÄ?u¤edÅ °wûûwéňîíÂè!KÉÕ/ì®ÿï„Þµ±ÄC!Ú¡×…mÑ0ÁÑ1Y6&#*#+Y½Î²3 //­23q0-ŒsÏèi¾toº®èeРíZÀèîôqÎÛŽ}kªçߎ‘×\¹.ãŽ(Ç]YŽe—¸û4ºÛ%B0.HëÃížúåÒ¹…ñ¼óغ:qîf‡£¡´EE0º7ƒ~6Ù®2.H¥¶Æ×#+ôa`)“’'óÜégNFÙÚƒM«‡ 3†ølèî#+vl`ÝùâØÔÛ¾}¢ÔíF&hŽµ»(tدýË¿ª†µ;Ñj4L)‚>Š0ƒˆ®.fm¦Q}ét*;›#*|–!G™£ßïÄæç=çÓŽ#)²Äȇ”Éà7÷>ç÷w“¸a¹{3¶N8ˆN\uŸÙ×í *ôCÀ÷{#+‡»Ê~Yù¤r€BYa b3ê–H)¡}¾ÌF7kÕ¥Çbþ›8 JÇa^XòRìž ÷—ãèšr@­vLæxztu÷DÔØ+Aé*Þ)ghŠ&™íÜ•œ0bÛ7ôPü°Tä‰ÊùFËØFÆ™²ÛQ8o%“ƵÅ>kÚÌv(M!ê€;2#+!·Ÿ#)ˆFB@Öw ÐõZ†«‘Ó°øúk2û¯p#*Û4n;uèÝlKÚ ö îlKrÕ&„JfÀš¹²v5¡w&¸Ý8W¤à€moˆmR"Lytì6Y¸7,Lnç.ªƒ-Ž]ŽA}"…C¶PdÜä§^DZª•,AFÎÏösß$Lâ9˜4¢‘O¥âüVD`Œvi¢´,ªn¨OmcÝîàcÐõ"€¥úõYq‚/7í@îŠæuc/í>©ÑÓ·N„ØÛ´»ÔLuŒztú‰i9M“®ýí§L!v†óõüÌklÑûÙ1,Š%ç…òôa¿oq.ª¾\Ž€üMµø¦qš(|<¥ÜiF#+¨r¾n:¤“¨Õé©©GéíèȎ漉qB'}s¾TïËx%'›4ôf뮹gw³ ‹l6Yøý;6ˆDdSWÏÃ/WVåé΢A|Cª^Ìš×¹@þ½iÐ}ÌšÎtµY½…—T`„ #+b‚ÈÐÍ’Úése'uu6r.KiKk·êyrÝÍ×uÚ¼Q„ËFBÅX㈆†Q(ŠnY)v{¹‘.ce¬¸wlÈ,–E´„"±#)Â:7\0mI’jkʬÊ-¶|÷õÏÉZà1›#)¢¨B£$4 |–,ãÛ`ªuÛÉ“nìVá;~ïâJëp”üK”‹(ÐÔ… XŒ#$õPz¢ª¼n› žp#+@"¥Ð<²#)¢&éø—!ñ³î{S½Ø®¨Š»t$Nî­ I‡@g“Ð.ˆ/Õ#*i™AÙòã/caÍh¦1ÁP¡SšÍŠ×„»™BˆMe†º>5ªåHìÛ}àW¹ ¤vëC0‚Ù_³«#)b žˆn*F¢[föUŽnÃÛ¦š¸í<v‰Ü‘NHO–•uô¾ˆ2Æ]k§†xnëE«šêó.ó­È€¢"T¤c£M7BJ b”‘#)Š#* @$K#+QcÖj•#*ÒKBCœÁâ{¨Ic©¸–ÖˆQpätL¬X6î»U¨­¾¿Zþ’h:Ö-´WƒEŸFþ®éÍzpÉ!1¿˜pÛ>«Bíe›Ù¡ì˜ü–åÍŽMgl—äÀ½ø…ýF2uÄE#)¼K:”ÜüšSç-Þb‚Wê’m)c[bfÕUTù7¨Áê&a&"A#)íÇ®¦ûãÙû«'\¢= Yx‡)´ôÛ‹­¨Ã0(…FB¤grB˜ó ª'9=Þzìë}]Ý¢oìÅX±dM¦†ÇS6Vh¤‰›ø[ðØhÖ¹1T„á¯?³hN ä(Kd*EŠ£¤xìÖ™Ã1Uï§Ñhå1EúzíóÇ#fCDÚňˆ$X=h¡,æyÆj󫤮U1ULå§ ú¾gÄ’©5–•¬µ >"qIñ·£y¼ëš9ñÁÖúo©$¬ÔªB»3Ä{ì÷§@Ï]ïkÀá[ºj‚‘=cj7®çkÏgÔKm&ðŠßÌåܹÀ¸.”SëÔÆɸŸs4„ JApN:l™ÍEÏ×Æ­‡m2LÆ?¯]pËYÔ¡¶“§½Á‰”•R}Aïá}Õ!ž9Φ(šCvê‚ÚÃA ´»†Ê/>!!hl5ç©Vê‚d)p.¿N”E# “¨ к XX)sC‚ ðnÍ@Ôd„#*sV rË (°È îHÈ‹ûœÞì¯WÃ>4Dg‚1 •Ö$~ÑÌ°$‹Ô#)Ô#+‚­A¥×̬ÔÖ hÒk7Y¼»Œ© ¥¨LBˆFõ‘¤WS#*#*0dL0Pqg°ÒÖˆá="ØÃɘj8NÆXRÒz‹A#*``(3ŠS@F“Q ¦ÊÛåuu¶»Ë¯„Æ.¥,0ðÙnäy~Îs‡žxížB&}ØŸ¾$qìM¶EädÜN#*†°€°æûy@TbørȾL£k¶è š3›O—3àpd›Ù¡¤9#)üâPNäή|ëb„yÖ‹’ÈȲ:²;¸Ô¶»ë6âæ]ÅÝ Wd…ÙǦûOºi@,òIÆÎÖ#*•[ä6q>rÉ.0å\3kÛá¯Âzûõ(d½Üf£_°màÉ~úéöðX˜˜™š#*)³Qc÷ô³Ê'HœC…˜-&Å#+©TDhvÏOiþd€ÈAˆæªsF¶¼ÞZ%αÙ0g,#+–N£’wÁÌ3Ú%@…î{nÝÝËòo,¾n<âó ’ Ï€«h˜{Ó*^#+c˜>½MÿÃóaë¢TZˆñ‰ac$œ/ è\)¢(DOÖ”#+« ¦¡yh¹‚©~ ¬¬ãb©#+¦1¡ #*ÐA+$ð¥t˜1'?]‡ˆŠ& ÕçÎó‘P#*"Ó\P‰ïAÒçÒq·z½(Ò“…ÌlÄ&7eú¶¦×É =‚ÅZFtHFØÑ}ãZ0À"'•¦?Æ=Ä4Ä·“QC¸¼ôl©çV}Gd ¡GH¾Ã#)âT“Ãîjªn(šÂ2§’^„ÝÔ•ÇZ«@y­ðÍüªÎF‘)†ù:œ6“t:ùGka¶²Ž=8ÉfY’]•ürmŒUÈ’¨^X³à>w†¨#+¼™Iêõ½T,øQ2xQ#+á£\z›¬¢þÆHy!ŽwD0íµÞÇ•š™-겑µ#M$g!S•`Ób™¥>Êb1èÔX2¶ŠEH¢ƒ¥·ÆÏ,ã…Ãc«¬õkmy3OF£‚kÙصj#+2¯j!FN¨C˜…¦±ûY†žÉç–õ)hÛ²ÆÈS`6ÒcHÍͼôϺpTƒc £*òaÃXÀ[Ý*Ee;±±¦ ¾ò†ämc–#){Zv‹“#)Æ­$ñŸÑß5Ù‹WL–ï f­I Ñ õÙ¹’ÇwX‰§U0Hzq"LYq…†?͵›’÷¯;©¥D—“ìéÃÆÓ£¦6ϺC;#–ÂBTï&ï嘌#*¬Ø¿Æ›ý„Ù ÃŽ&çÛC'¶–­Xn™‹@vEÜE#*b6H’ª¨B  2&.A#P;áZ¾v—MòË’²”rZ«Á@ƒر¶“ä,\/ËÄ¥ qJª“D¢MC°ÁsA›¦ŒÐáA´¢a‚% f´nh4“Q(Q#*ÓFÚa#+a&¢d˜lØ;7-2Ëtp£#+ ©1h ¥ÖÒE“„Ú2V“ƒ"""ÈÃX´0dLÒU)hn†Ž8Q4-‹" X 1f“ÂÈ­[&fVî£á÷Iâ]·sߎYÝML™Í5#•ê I¾^LÆ­ŠÎ ÄÞÏ7C¬ÄJ=èÖ9‘°ƒŒt8¢„gf ˨_ØÃ;*1h¤ 'æbÇãÓG—”+Æ\{HƉ#*“* @›RáÁ Óôª#)Ù°í„k/ I0—GåÇØÙ̺>|ä†Æ¥½hYJˆÑª)r! .ï6øÓXD.š=ZΈ´À6ïá8#× >ÇeÑ€šÈó44Ó-”›åÏÏWßüút^aª¯gÂtš“×z‰dMtSŒ[w–é ••/ž4©ívÌf©]€Î6„0…•ÀàðÔÉTSâ“B²æ(²N»5gØAF]±ú!¶&¦|¼‹#+ˆ.{+Empµó½d6&ºMæ+&Rhú‘9jwpD Ó-²0>-™ÊD†ú9ÎÖK‘gÀº#*¡B;üànèý°ê’Ùë œ~mÆí9™ggähA°dN9X¦ß›ÑÔN„|ö•ß£±ÝB °!D±¡DÐE‰Ï% #*&3ï÷Ò’¯ŠÁC…‡K0ÒÂ#)•F$ÔI “:É$sMÐeXŒ<5\ldµ¾µ°aÑ#)!yåÍ6:à1@iØ5‰i²$#)„ jMÁ„Œ%#*Úù7‡è»›&&À­Y—¤=˜5{‹Q#À{þ ³¿»/ÞNäR@I¶u®Ìô•ç¤ß9‘ YŠ^ï|•6¤b]*#)4&¥Ì¢ðÕÕ‹ÁøÜ&Z+NòNr?#+¹¨e)Á‰ù™,̸¨bœ¥ú;õ}Àƒ<Ð Œ@TnwÎÒÊxÓ<éì>x’HB¦­~eÏÜA`V­Eµâ£W-[cZÚŠÚ¹­¹²È€2(A!CBì<5_œ¸œˆ_¹Æ#+)«ª:¤ë6Û¹W*¦@š33Òp:K<C<¦šu¾ç ø6HEDê!‰$hI¦LÊf•¦Y’R“LRj‹IšƒùN¤ÂZÒ1Q4Ô¡’š"P£ZX׿nPÉ­&,’‰L­šɉšfHÂTPÄ¥Cêî¢#i,Š‰†)%%”0ÕÉA¨´¨ÑD”Â(ÍÉ’´Å5Š™DÐd¤)¨2•ˆ4Ði›LcIMïîî)Ü“y³ÛSˆò²—ì#+’²{'t{?++UWÐÆ(}Ù|?uÙ‘Ò;!·äÈ;8H¨Ü*9>ã•á†s³¬#*ôç#*ƒS²rÁ‰¼YúqœS(hÆô–Þ™å #)àŒ#«3—7ZuïïKEF£>ê6%øv„Û‹‡m¦ê%ËA<Ì,ã©]‚õ\vidZ¿Æ—CZFú“|wë‚.µ­vc±êÌ núðµÒ6—óˆö—_`?ø¡¿YBß~+˜Èè6&Û pÀÈʦAáJšØýZø^Ûoů±bc- £1³Y6ŠŠ#d´Ë(„a;3ãçŸQ†íè·bÀLØQ×4àíø3³ÙŸcÌÍ7›œa‡¯AfŠJg9 W–œ8Ć62i\û®[é®å/c°zCŸÄH `MþÒ”›WS·aèéOŽÕûóÇ=G"Ã!v¨NŠ¢ÀJè/™Ö4JËô|,ܸŽäÏž†›rÅ%œ¨†Ü‹}bóÑ×D!×UVm\jQJA(U$I#)‹Ø™1ç[ÒÿWW×p‘öL£~ݶþYwÖ1ðšRüüâ(é2‰F^#*>Ûp}1ŒcÇ"$•o7Û|^ZàèÜžrXL´g/.Û>^?Î#+ø‰³ƒû5¤þ#*jè*’[i¾½°ÞÓ‡îe¸¥…†*t*.#+mfà8ÅXèPÎù†úVÚó¦Ç(iÅl£˜_€€zРQ›g¾ôrÁ ‚Iú¯›WÅ­ò`µº L¡|dŽ»j:÷ä½órß¡ñºe8yaŠÊc¡ni§q=æxK›C Í®Æyapü& EJzŸ3\ˆ{ßeá%+iÄi™¤òï¤Ò(P”?KÇÕ GdêpÚNB"T]¦SÄbY=¶ÍP¡#)Æ­®ôu;¡²/©Nº6ÞËmÌ̽á–Ê"lG¡ì9 ve…óúf«§–½SËnþÖìîäÈÙIrÝð'SVú5:„õW§´Þ]v>OˆÁãA8}_Eq½ÿAÓi9¤*HgŽtwÔœË<!öyÜC¬íÙõsùe°ÇïZËôs ßâ9Z{ÕK±(Ñ;^ùÉ禷;{¡¸B¸{Š_§—;ø½Fp"aQO»J RGkBkbl8ògâm»]š•%|¶;€Ì]È’*ŽŽ¼&P–så­3 jÓ5'2;Cq¬üŽù™™#+;«€›<2Ý 'Ê÷ÞÂX°ˆgE%p­@ûkùS§¾ŽŽ̇§«õlÏ5|*³öã=©W_ÜʳK…h•#“ëhŽŒc#+ãZ‘Ž7•™o´6Ìš¢ƒÖ¡•ÂŒœ‘u§÷5˜6™óq™†ò¹÷FÌXÜÔnLçQ桦e­¶iåæÆiœK¡d´Å âhѨ¢¦¥0ŠáW­b$YB,.7…¶—:²fÚŠ‡zsÖ°'›|JÒ” –¦wÄþSU/ø“éº^,3ç±ûe7½±ÖQ~ɇ[#)Dgá¦ø Á2¦8©?7VõÀ–ÄÔ̶‡«9Ñ©¨¶pi•‚­°„ÉðÔÇÎì§y+;ö“ðCµs}§j#*.Sˆ0¶6{P#“¤éOrðf§+%ÞgQzœ ÔC#*Cp²K f¸¶Ä“’´Ž©:l¦³Ë.üv0dÕA:oØ,œÄ7#+ÁŒUEÅAbåú ­YžŽÎiç–™ªáµGßÆAËœ>#*ÚÁ¸YÑ5V\Ú0>x’æ˜$ÇIçHݘ5êΙwJ±Õ†B¹äêh`Á«›ÌX;!÷67¶ŒLjL¾ =ù_mbí¨Ç.›îPq°¶ž#ZÐ'¥œË=§å˜¤ :ØÑ5% ÜšÀ„ ­öÄ0ÿ{¶ñ01€l´“¼G¯ji¦jë%\X´5Ùòvxãͽ4V 4ØZMDxç‡<üÉïYÛ0gµ;iʼ¼bL²ãj-è‰Y»Û5™Éœ^ŒW7ÅN·­Å#:LèNèvØÚÐõYšÛ6Pñ<zm¬kHüpbo†Ü„üÎÅŸ¥Ã¤¹C¨ÆD¤ÊY}Ö«hÕNyãÆ(#;cC‚HMœÆzñ\¤j9¼ow1Mb—ò½òùåŒÃsz»!z½BT;AÆJÓì>#ccYÐÔw߬n×(}q|oWÄî²a…3™§¦è:Ó;1¢dÆ9Ž°„LJ©n5´š£Íš']ºß—ÊßQt×\PÄ##)^Nò”:±¡µÑ82%­pd™"^­!ä\´°»¨(2CAl(¸jS }5\7š¢¨HÂIÓlª^mK‘ a+a[æâ*ɾ÷UµìŒ¦„!2Æ(1ª¸ ØÙž¼§ŽY5‡«òíd 7ç—s%}“Ë.’ÊùÕoÉͧ)8ÇRÚcLÌÜÐcZXƒ!”ŤÜL—¨Ç2@ª&ßhƒ”Cà†–I¶5$äËÁmÉ·wŽHÝ0냦`á\ö§%‹C’šõ“j£–3fà HâA–ÌÃìí ˆÐ—#üÁÄ5ï™NºD©#*@\Ü@;ÂaÙ#+ši[ÙT싇ZN#)]`¹rmep œnçô‘¼%–Ì>ÎPiI#+4óÉ6I‡ƒjvÅ0,¶iQ!³é¥ØXlhÍÍÌpÑÏNVŸNÚ]#* ºQH.q¼†Æ.¶bÔJ2¦E ›6™äz…Èr'i3…jR}¶huhfá…,ˆÖGmõ¢çHBºfY|c90ÌÒB P×±!¾j1»½ý%EàÖ‡N74BbeÓŸŽK„Œ„7èîŒÔm´píƒpRj¯âã#*'Œ5xîs/ps[Dìr·‘1„Qvkwg·JÃ]Þq•ÛÂEBIìÙB –HÄ9ú”amoaq&õ•ØS‹™™ŒLMæxÔ;dçv±•ð#*o!Õé8fÜÈm¢qãKf‰¹¼s%O6;ª‘éÔ!,ÙH¹D»¨#+¤®%'©ˆE@ª‰IæQ(îšQnâ\ñ11ˆyññÖxƧ#+3§¡[›ÃÌáæTB¥Uqx‡Úk ¨ÙZGžÏä‡-é,­ÊƒyóÓã.‰vÆX¨Ì¼!$œª‹UH–ÔÑ-‹„ÌG€5–fêJ½1·[ƒs2ú™Bº9‰“†H4qÆ:jn¬21É O)&PmV†Ìâë\—[™lÖfõŽ™5,yU–LbÇ0vB‰ÓW5£YÓÖ™fBÞ0Ì £Ý+.ì2çÎêç#+ªAâÎ1¬“BåPÜÒ Õi:§c=ÞX­‡°¨Âj8rËUW8Šbvg±ËN+X©0Nø4a©Ë 9Þåqä{Á¨·É"aNû+â(0àe#+Bu öó›Rõ`h©µÄE8($ÔÍN–3L#Itq…FõîXrm\1Ü&ÖÌÔ¯½©cQ²ê¤EÒÂ%*‘ÚrÕK#*tÓ1#>"<4[»K5ýl^«¼;6ÜåZÛ|g®È95{ÛdæŠmP)MÂÝç#)º¡$Ì 0ñoj9ÆÄÙ‘²‹ë4tP)Q´IÂÊI@ìÍæxúªÄ°… iÙ4J¥L©Ù‹¶TwdZ>h€!#)4Ãß“B£Xž¡#+_,nÛ¦Pí„&R Ìá‰Ë°dY‘Ù°Ž¥ܱÑ>á!ÿÐá‘W<ÖDwÀ6é"ðd^»åÉDk¡H¤Š1Ršd-œXZ‰‡vCϤ0é‹C€ÊÇgys( AÍ1š® Âej‰–³7Õ)©š a–D@ÈÓJ l”ͪ”‹—viÛZéN”(XŽ”S7p‚ÛͤˆËC a9ëZf¦É)!…Šè윒ZE´¦æ¶O7"eÈÁÒ¤âÌ<Á0ÈjËÖ¥:3T#*°æ˜Jã\ÙÁsC…U¶ xò¬eV'—'qøƒ#+­¹X¡àèÑ¢Ó@¥Nœ"g¨…ØÊ ãc8zŒöf\»#)CÔPà†È1Œ6`ÚuFòjlAé;H–wnË—Û¶L>Ð8è·ŽHy}×hn|Jãfå«n#+eɪó™>ã—Êt¶C›Ê¡WŽ4TÎ…õJ.ô½ßc¢vk\8RÎÒ¬˜³ˆÎ ÔÁ|`5Œ`pËî¸ÅG5D´Lbð¥ñ®¬]CC[¸.Œa½n–œ!“ŠÏ!©ÏaA¦WíÉ„˜™ÓŒPiIÛ+] gw6÷¾ü2Ç­Ìï—æf ²„9i 7U@88û±³0…6šD¼rî+„Ýx¤½WÁ\Ö+Ùr»º¹kǦ©-Nô\)–Ä``i’ÚÏêÀè’âãA.NâˆÑÔGI–F1¶ÂHÀcfÒíåxüÚæË•c¬ik#¿í*F ï%‚Dv ‡JíèlËRQ]+y¦—"€9 aÔ4ë8†æy¬r¡˜2„6(kX›mlá.@!µÛlyˆkCŽûÈ¡¢#*F+2fisÄØÜ l³3Y†ÅÖ…²U)¡ÎQÓ#)e2É•%d#)rá¹™#+9ŽÅ‡32£ðÆŽäp‚UC=N A‚#*ŒBÀÑÙ¾ŒpÙÂ+q‰%ÛʈÝÒÐÎhÎ2\fÐÜ$#*†DN4”;œÄÅ1Í‹ FÃ9²4SlÁdÌž)Iy#pt( q“3a4ìnf:Í«™t5Ž‹³aÁ¢ê23i…“RR8Æ‹Æv´±={›f¤Í¨Ûss!€¦D‰w[ÎpÌ #)ÅC¨@Àî#)bú†âgT£Ä‘„bõ€% °‚P¡ú·?ÈI„MDÐìÎðTÚ)¬¡rˆûR‘(#_é×ÅGǼ=ý …ÔŠö´I$IddŒ’BO}Q¸úöcœ€Q€"'T:F韡ˆÔ‘Blƒ@Q(#+‹@D3ŒÀ™#)ÀP.àH;²Óß–³ÈÏ‹ i°p‡é…‚IñNÙUI‡Ã>”ÜâV›Cûuœü®Ëâu âv,áÅ¥ÍÁ«”¦kFñ“wë-ÖQEÜ‹h^uËïµÄL«4ó¸ëá›&Rc'j_ÃXGåxÎœk—’ÛW g`ÐÉÍ`f!æƒxFì##¶<fó¡-°[,pCÃp„"ÁŽŒo· ë úÍŵ²Rän„¹ÂK” A#ÈpBÿ:ì‚ 4Ñ*D•Q*4Ëkñ¦j¬›ÉIªÔÚÕåðQ#+"ïðŠPT!Ó Ô&) óïæ‘2­éЩ 4¤B¨,Ô'^8ª(É¥ÊÏgpÄ2C¹~ò‹u6ô|Ù§b¯À ‚*¹EUÝónR Z¾•Ë÷Õ9uwu]˜Öë»2æë¦ê‚×6Jþ槗ïîì‹[äÕìvˆê¡àK…ï½#)\vÊc±Tw ;7Àüø#)ä$3äñC¡}ÄCŒãÌ¡ƒ8‘jÔQæaGSÜ`=¥?Htsáßב´}+¾ÆóðÕ&EÃò‡ô™‚·Aääý-•ö`Ùþ'Íðc–Ñì̾B6ÊzîçfiZLPOB0@cK.ªõ6£xº e ÀöøLfà´kü;[&ÐdBšb±oQ*¥4#+:\sq„¹v´¤Î‘s.7Ê ’ ýƒÑÇ#4"Î愈k;ϵ‰˜‡ë"Ûñ·‡€\¹b^®”èz#”o$±‘57Îý‹ù=~ã_b«M¢IL¶*±¢Ñµ£j$Æ¢&I°f™–DQm‹¯§ã©$Be¢|±Ç†•¢§h‚•Ô§#*œû—$È2]åÑÔv@Y«°CÒæ‘¢#÷Ý؈šð´Ad±#´ÂL#À+$Ej;Håm U‹qy€ÍÂx:žosשÔ2ÁÔôÑi;¦kþD{Œ5î|¨á™)ƒKõXQ¤‰s>5a¼$ô€'-ZDŒW‰@Hm<ùä®2¼3+¬ƒêi¢™¨0É…–á#ÎÅH7üGEi1¥Ž´‘d#ÜBn^šÕʺU|›z—¦Þ„j™#+°.ÉŒ[2”²bš+ÜaZ\34B1c@ÔsùÎ2‘3cÅw,!hF¶¾X#+Dü]ˆðªDˆÁÇÆäh8Y (ÛIƒ@U~ªXž8ªM;¹ˆ&T»ïÂyþš;byÞ¥¥\ä™7»j.â"­ ††ôC‰µË}ë Úc* 6È5$*G/z¥.G­5ÝàÛÜD¢Ñ; tfõ$^&Ž’ІäŒ6ÚfjÃLâ*qLuquEÔLhF1IoÄGTK&Bºäf¬¯:ql¼? ã#)=¡ìIæz{J˜(À*À-!VD˜S04ªýÓx#F%ûÆ…¬*’ðU÷Ø#*±D3‚0cº#*ƒ4þ$À!°|µ÷KxÅz[úÌÇòý4g<0æFÖ:N%E`Ö™ú7p£˜ÀßQr0«s|œªÚ## ‘qöe]²cûÊCNH¦§gØå÷›DÒ˜oÃ8ªßÂæÃwhº"4ˆŠïTØuñ£L4úãAÈÑ´-«ã…¥¥jÖAl"¿–á$á‡2k³B¨TLí³H#+g‘NÇ6:Ç™ÍÅ®(láã¡Y·MŒ{”^ZÖH8[΃3™<Ÿ#)÷¾æÖU¸ehÃå ¨ýúN¦Ã¥‘œ¤Í¹=†Œ’(Jv›n­N<mmƒåPš«%*‰ ÀUŒeYNÜ9#+)áÕaŒ:°Ö@mQõ'u¡=p®pì’ëSɱ4\CbF”­ñqÖ=‰©}lô‹7Ë;VôÆ„ub‚ÂŽl El0,±ˆSFQ»uv0È3[¢)e x#*À„aL*˜d@!L@£*#* Ì#)¸_zÀ-$BE,$˜: bmã7ì–k¸B㬠ŠÐ?ä PWr¡ö…ÏÒuà×ÔH…£2gÞ®SN)XžÊQþ#+Ý4@õº°.yø³¡±_#+zÛf¸c±Néç‹Çû& @Qéh‡ßWYœõLL#)ˆÈ{澦H¢`™!$‘’1Mõk^6¶þzŶ‹Uj-m "#+H$‰ Cܦh|êž^Zðܹz—>#+•Ë‘ö>!ã5¼¡ïè#*-h¥õq\MoŽvÎ^øA¸ÕY[ QÑY½¨äØr­$®E#*“{9éÃYÞ%lšeäʵS0:2¥K‹ßl4ïµi¶“vl“t2Vø»–¯V1FÛÜlc6h9Äq£šHÒ.™KÄӃƶð¤¶Ê> s7’a”ã 6ÍWIT%³/4ò嫈’öçŒáöäD´¬SNDIžRá©7DMÌ—C€é‡ÚsF7Êš€ˆ¼Œ1­N·ÉÄÉÍ£qNv˧º£~Q&˜Ç؈ƒh5)ßa=`C§‡8·Š´Û§ E2±J|Å&-MØ 31Î5Ä™›µ3žµfgS´œb¨i½ðyXÛz›Mp“\ÎJùš}d£3œ}æxdÇ·ÔW!2„dž9lð4(ÚwM‡ƒc&úU/“›dØ¢™­¨sLêgïgjRP@Æc$"1a¹9ã czÕºR‚,"ˆÁ`‹L1É`´mYGË]¾qµK¥ŒŠ¥%!Ð##+\¨ŒP ´0hH¥ƒhmQª’†” "D‹"·5œÒèP@@`Œ’~äË V¬,0ÜqJÓʹ¢ƒÔƒ€ØØ"í²8ÆqUv‡;bì×MŠŽî¯I#ÊV¬e’¦HÜ!²ŽæVÁ‰­ÒÍ75t*ê×i5ƒt®ë®î)7Šå¯]®L´í<Ýw™W“bcJÝMní4H¤‰,ÊÍø)80¥Ú6x¯m¸­·Œ¥´Ê¤²“"lÖ6Æ´ÔŠfº–ºV–RÒÉm,Êšª4ú5òóÂj6¨0X©š-[IŠ± B*BËzüŒÞH:#+ü£cV©@YKj@æÁD8¦EØd0 (TM¦æÐ ¥Ø”D ACœAìnŠùDv‘AÚ{,œñ=$# ¡Øóý²–œr,¦¡¯Úmúsýj‡œ'>bŠ è ï@¶a€´èµ¬¸Á‡fn¼´±°51bÜ  HÈhûìê$Ì•5,aâÕi¶“ŽiŠ§‰ÛæØÜCЩ6¥O‚}oßd@êŠ\b•*©Fˆ„‚ÔBàÅ,þ Z2"S ¨F;¤†ÒïÜò„ðüiâw®œo.Ž‘†kGìÔeÃ]‹}yº,n§å#)ת 2!Mi­K‘Ù˜ˆ<«…­L¦Ez鄤0©"@ŠÑª÷éêýŽý¼µ†Çsdx†ñàc‰ž újõ.\*;öóÔì6Ÿzi¿p¿C~–g%éT2r "¬ËŽ-Ž#r­ý9™,æå!3`ZI!æÈ!“kÉ*ð—¤*“ k„˜8噧&¶(\¨Ž :aÂë%e²;N_#6Yð f”Ký[Z‰­8&›ajt'áwM¬íÈ 3 ¤š·÷(}JªD#)Œ"¡Þøú¾v5Òû¯™±¼“¼áež*²øÌ£_¯ôä](’:~B’IBv{[Vñ¥ã²Z¨€þÿº£VH¼]…ÒLª12I¹­®E”2Ñ5’b·¾íZäÓi´e-¤ÙFŤE4Ë%”ÉJ¢˜¥›P)F~EÖÚ³LÃfÐÌi‰6Š*ÔÖ©¢zqEdV¥w]6¡/«iÚÍ^Ý»*b(2#+f¶ÒŒÖ”¶¤ÚÄš1iUû«UÖ¾—"XÕïݲM‘E¬l•­¶D’&Ùµ­Ë­-LªI©m¼ó¯)Q-6›(L¦¬6Ú[6“{:µ©mŒb¢<mÑY²ÞuuçrJU¦M#*rÝYªJð]f¯Û˜•XHÛxMvM 0E#$Æÿ“—¦eViËç|©+­-ýÀ°­,£œèÈø|;+ÑÕ¦á’j4ªÖÝ í¸¦d“¡×Ùª-Dhå2ãîé|R"bOk”º¨¤=¬–ÓN‰ÒZe·,XˆŒ€e×#)jID‡tËKM¥”ÒÚ6Ô¤ÕHr²o#*®CÉ€¤QE$Œ’$TÊ*Qs”ÄbBÍ%@jæßGu¯)ªém±W65¨k}…”Š™‡1Ò›#+jÚRŠdéjß+e·6ª‹%­-š˜ÒÓ3ÚE E¥&©Kmi¶TͬšØiM©M¾+r†I–¢²¨›IA(Ê4Ò’hm6ÊSHš“4l4Ìe ±hVÊ)±²bKBƤÚÆÚ”ª#*EJl©JdªJÑIT–’6¥Ú‹Y´Ð²2RcAI”˜Jdšd©fªmˆÕVD¨¥‰›Rd‘fµ©f²dÑ¥$´¦Û,¬„ ™ %R#+@`©„4†’ÞòÚ×M›-ZSUe‘ ,Ä#)¨‰v 2"±ªRØ-¨Ú´‰U¦¥€"´mæ?Puœ÷Ìèq°‹:é 2:B}ÛÍŠ÷ÃoË5ªöÐÀÍýöpb‚›¦uëòÎ9J&£'ºÊôq¹ÃM¨§–t’¤Èï#*G‡xR<£r7‰á×RÕB,8ñ¿ïø ­g¸E}ñ<ïtó†øìT$1g½¸ ƒ’ÈÅ~Žý˜×6ιîų@fÚÊ ˜óivz.•Mù”£v’‡‡ Š”®Óì¾H+ѽ7Àe§ý¬†ß;Ž³ºëÒsq!~FDÉ2lÀm \cha!¼†„#)S™ÌóÙsšL–oðõt˜%)ºõu«¼‘B +÷Ê y€T·E p}ª™³UÍÔtÍa²)ËR u#+*Wèî)A=|5Hqf‰÷ŒÀ‚.3î£#*L“ð}Î6dXšs1š"„$]º‰nû{Ó&¼ÍolË¡=8¼½7¬]Î(³€Üêp׳]Hû<ÊĹÉö2R­ÃeÝùŸ·èšìßçdBS1Ô*LáPT_×PúÕ.F¾©Ñ)iûYá`'ëqŽg#*¶`E’FtªM*CzµX^7:µ½ß£ª£›º±jSI£|Ök}„ˆÙ®ÑFrp[ö‡œB¢\†™<äH¥ ÑÓLÔUU"„€!˸#)#)V-±ª£Ûé•w]·!êpövÈVyF¨·Tô„‘ Z‹ÜÉ´ÉØœpc±¾üÝ÷iVUÆD`üßSŽ½‡¾ÂHƒ#)Y;+s™…8e‰†’‘Çfw†„m}Ù²¯weŒp¶tìÆcoM>ƒWÌhJ~=;6û„@=ã¿ÅïçÚºÎ6óv#*çG±±Ý][{éLcÚšÓPô„P,Àý;nÉÞù »!&'ù¿ÅÀ«–‚Îx…Q®\‰úþ»ÌŸÌyìíË^›)6†#*¥iÿÖþ¾XV·¹YlQ´2²’$QŒhÑœÕRhÖ4C DÈÂ6À­‡¼D5$Öê-pÁ´#*‘‚9m&ˆRÍ(¦7#JhÃ\Ã#*cŠ±¬Œ‚h¼°Š³†7#*»KTŠ3› ˆl LÐÝeq‹±Å,TCD.ÚXUTŠƺ]$ ˜*Z´<R)0©Î¡HkŠFý*LàH’·€í—MÜ\åz5J<BGàµó”B)Á5Ú5NÄ Ú×ÒƒÏÄÈÒƒ4çCÜR4j’±Ì#+pvàøÄ]©Æ-"jí8s*Wg¯qž†'Ó¦Ná\„$€Ãw0'M;}§£z·"˜Ø·7;†±#+²•@TFñAj#+Àº#+_gmoÐ?«Z¶‹Ù©®rÄH|ú~p¸ ÌêU‹7pÛµ¶ÿŸ¦}ÒNg•B ¤Ožø·>÷è¨1£ñMnx$œé˜W’¶âÓâëUc^o#ŽMÉ! y§h^õ Q‰¡²ÒÛˆoŠkÙŒ»±“4!¸L «}IfÈzIYS*#oÓ½#*àþœ›ãCòü¸{öcÂô2>þÅHT#áÓJ)Xùö˜`üh˜øæb¾Ú!ºE›¡X R$ÕÒS$™>]ˆZ¾<¶.¬_;ö¤ 8RñN0$ ‡™µœÅýS䘵 àü>¶&µÂ¶ïm%ãü¼¦šm£jõiå&4jRRy´zj"²„Ô…Ž@£2>8#*¸>Öw8æ篌ì’ä1vb]00ŽÌÍ…PȵHÏ à=G"Ö÷’3Tm´âp Ëéí©Ü¡\}js¢f‹F¤„ÉÃ'`²Ãa¨‰«Zô­t0Q¥ii¿}'Sé~éUŒDì=9”Jª^¬¨‚P'–e\fzÕ:q»ò•yì°êD#)€É ÉÁê÷Nbœ¥CŽ“J&˜õ & @C8„D»Ï¾½¿w~è>g—áÚ©4¡Ë7õŽ¢áuEÕËiÃÎÒ`€sCgÀ|#*#)Q0jHõ¢Â¹»ˆšÇïó„瓘ú­(7׎±™…74Lmu¢‰Ú#¨€MCgÓ—­@îíËïÄ&ä¶æÆíMD#*ÄÓ’5Œ˜ÕŒ-8ŠvÄÏI±Ô9:ÌbÚË‘(ÖK­€!¨™1ÎÓ€8rUb*ú¨¥^ž²õâzvœ3»¬ÞVÌòT.4ï;¡3sÎŒø9ö Ðb‘*,R¡)Œ`f‹"DB —¥×*¯zïšIÚÚJXB“%ÉË9˜FœCqp¿£GàîÚH¡Í]ໃiH¢tGV#+‘ $iU8Œ@ ŠïÁb È# ꢱ˜$¢»™‹„ \Yu«JIK‹NzîÆszîLo;®îõ›ØƒœSÍ>­÷5&#*æÁH©µR*Šª†ÌZô­D´†‰I4ªuÔ›3‘¦†ÚêXˆÚ|µÅDÉ$ Ë"de<ís›‰.W[rY¥Ï<ëAâxñŸkzËÎä' "#)$lŒá­`Ü1 çéÛv»ÕEš%0ÐXpe§2Q0{º–Ý„'Cü[Ñ›cÛ[J;¥PaŸ*C·Z&»ã=ËÚ#Ûm°ÝÏ>[øtuÖ­ ë࣯ Y8Î,×–)B¯ -ÂñXµLTªÍ,©±»°K¯áÔ˜Šé¨8RŽ„6øyçͨ:c¯£Ù‘éi]ÏP¦áA:¸´íÊBúëÂ'n3Œ|sÒ+tÄF,Âzvlt•-~} -$Ö@U&j˜*ÈÃíü°h9;˜%žþ©ëD(YzwÜ°À—€jN,0â©‚e캒EÖ—®µzròåÞ7Rmõ·dLÊ$'zó^­^µt“c7m«ww@5*Ha(’™´Qcš°O;·„Õ˘¥ì”¼ËUCV(ÅiH@M6ïÛß;›ÒÚ"Žè¯´º›»µh»4z&cèúvñŠD §¬¹½‹íðœú³¹RãMpÜuH§ÞwÞêf“=û™5~´½/kë]f"±‘‚’~¶‡t¤ÚÃZ¿kZõhÕ“j¬ŒÃý©ÁP:U2dfÃ`mEouM4m(¢Ö«”iTÓI¥¥ºè¹·C“Y‹hÛó*ÜŒXŠÑ­|iEUéz^¦Ö¼nZË6þóûÞñ¶É«i¶km¥é::byôÐÅ) *p%—Î\TâMHùÍÃ#*Kƒ8 –BЊRâ2DZq1'+C oЂØM†ñ2P‡rËHD}¯…ç:ÈÐÖY4‰–°´š¤•wfa#*r½5ølmØ›=›œ €IŸ>l#*“¼Ühov^¸§¶J*”_ô„rÖ-0¢v&èŒà¬…¥!m…OÊ©Teª³Ò‚¡WK$š¼Ã2£Í†YÖp€ëÖàtîó£!ÙÓU#+#Q#+ŠD‰UKLŠ&ÑTju¶Ûªµôïfé^#¡„k„*7Ë/a7 ¹fä{ª+†»ab·(ôÁ ”‚dYQ·½óãÅõ†÷y8ൢƒ:uˆ²-Ɔ€‰²#)ó,¥¬ôI÷íà)œM3ðÇ×þü#)vºDÖä[Bê‚NgÀÙŒ(4dˆA(þèš¿ë°ä³CŸ•Ï®ým©°wÃÅQ–‡€¢,#*„Aéä ¢{ök‚ ‘ªCb%ò58þ\4ùj™m¯³ü÷¢9üGákè D‡T]°õdÞˆn»&ߥ¡=ŇœwuŒðºyB¼U¡Í?o“ÜG àìì}/Ž?Š$D‡'&\^¾ˆo¯Ì£¦ûºBónÍŸÕðC¼! ˆÏ¼äP:Í€ç¾Õ؃â#µ_›Íô_å;^Hž$PRI‘Dë #+J¥*#+¡DUYXD¨©PPfŽälY€¡x@ ‘,‚ä‰%f¸añ¾¤Nìk3.?Þ†=Ž·3Rº0V(,i,X)ÀÊ6Bý¹î\r;”é>«SìSQù~Ý«Ëj± DŸ@ô$›SОģC˜N/P¼läV_Ôþ€å$¤ ÂPÀ‡eø–ÈwGÜÉj‘BÒR+†—‘D‡o×ØøŸ,Âïª11Yøz¼ùqa8X8¢j܉ó8:ÎÓœdŠÓaí¨#u›¼…AÚ5m,WÀ»5¦ XiË.Ì·œû¨ô§Ü¤NÒ*^D’"tÈa±æ5ðÙ°¾¤—TY*¥6È.j§ó³,?6a&šòìuCFª ±QV$6kù­y ´ÐDj6€dÀîÈaX¡†Z? °QÖú){~å]j¬iRD@‚›]C–*H¢†0PžrÜ5uÉ8b¤™EX¤®wγ##*ÛŽ‰fQ“"#+×Rf²ÚâXÄj°+HìÍ46„úˆÀf˜ÚPŽ)11Ò#*ŒµYÆTdÅE‚ˆ˜´«a)’©”Vk²ÙÁ”€ÝBS ,†‰H춨„)&Y#+³p–›2SŒPa D‹²X„Æõ!l –’•‹#*m&4‚±6–ò!ÀÆFFs•ËÚ!0! «-&‰•hÌ „´µƒtªÒÁ ‘¼–7 6È«#+Ãp´X›Õ¬«L°rt5¶ŽÞÝ{{öÛÁŠòwvwnk–¹sV6•Ý+–åQ\·/ƒxĘ׮ïK.»o‹dÁ®IŠç4\3•¨lêApÌjÓ´KŒ&Di:3±öu˜i–4ßÃZ±1£–(\'(2ƤÙÕ#*ª0%ÓI@ 8ZÖÔ¬¬<5MSÏXÛ3¥‰b]ÆW¾ìµØ?±4ì}0œÆA,¾çmˆ*[l“ʤÀ…ÍZxpu±»õ>KϦ4ÀˆšÌoªÝçn¼™n³MÖn²‹«áùqý”Â1Ó ÍÇžVàqãìz~#*áÝ£ .H(ó MTÖÔ?<û Ñ 0€À«Š¯X1 «ûãÜE•DˆFåæ¶5*Y-UvØÆ•d¼[¤ªŒ,k\µWR±«m·7[d´ÓX™%ƒ YPuŠ«#)–¥II¼²n>”ýaäXv¨j ¤,b$"Ò?»yÞ}_A`5~ôUXŒ³ƒ¿é®¿w}XõŽ¦Qc×/«ÈÍrCCËPbP¤Š­PBªT`T"¯ÆáP—*(R$åþ 0 …ˆàã:njÄ€çºêù+q˜„§3Ú‡-£›ˆ’" <MˆP}¥!ñ½UD$I~X)¡ Ũ5#*!å,•²mçn¥*YUÍs[Æ×Üm¯,ÈŠ-’Ŷ†ÈUG©¬X®«º¢×¦¯k½yõÖìµÍWÉ­y¥¬jówQE˜˜RMjMZ“4ÚKÛß°Û`i˜I#)#+Dt dyÇKR±û9uC©¢ÞE­˜$Ê+Ü¢åˆìƒQJW#+ü©….I#)}@!x!ˆâ&X‹ˆ b6 íƒb"Ù™ R¼€;›ŽÙ#)&¥@¶ÊŸGðQì#b0d•0›AþÂîmù½ôžCrÊؤ‹a=‹í’#)ë Ñ.=ÝYž=ô• oi„L¥8ô`…«Á1*ªE*ègÏÆá/nÔˆÃÕS²¨N#EÅBØÝ…ÆMC^$Ø`1v rÿCòdg W+×0úbõAJŠ†¯ˆbÊ#)h¿ˆ|¯¢&÷W2°ª8êÜ‹°ÔCÉ{†ÁØWƒ´©Fí»ŠJ4¥¥*j±µ ÚkÞWliªÍ5”õ+¨¬(±F0XDd•¿¯¡âs2Ó·}ƒèÕv@ìí»GÞÕ(P]«šŒ `îÐ>^å8ʵáQíû‹¿Õá ~'ÇG<n¸Ñ캓®¥Œûƒ’ÃåÔzhÍ4›h¦ATšqpÿ­á¹<ÆsŠ‹Ëf$1#*k#+CT¦ï®0fŽ—#*î¸I#¬¥ª(e­cƒm€#½Õ&˜ŽäÄŒî£î0aC•´A¾øÐ䱇½wû„Á<ޮتÀê$’IhLUG÷£s'#Öy­ç»–™zþÛêä…õëšÇ-‚c¾œW*ùÖ,®ð©NPYT.ÂÔZjBÀÌŸëý¼‚@å¹ÉY¶çA?Bô3Ãuå\›9¦#õØàQCS¡™&>^ÌJ8×V¹ "ÒuˆÄjS«6a k‚907ÓM‡ƒrb&'üD¦Å0Ù#*d†IPŠ0¡l0F¡fœj±RF¨¦y#+!®l¿ îäi=ŽÍÈ5Ï$w¡¾L±Ã\jð…‚f÷bäv¬C<¡ÿŸ÷òZK&$HànÙdÁá&l½¡‘Ç^zÂŒìgR}ÔÉϘŸ:z#)CÒûV€r®Ì”>?#)Õ8 $j<#)¶Æáãj4[Ö"Ùƒ RRd³Lŵ5´µ&Æ´d˜Êkõev “DÊ™igìµnm©™jl‹E 3X%lÅSÆÛ5T­6²Ìµ™m–"Ö-(Š¦ÔÌMš¬Ñ4Õ6ÅUE°¥ Í¦Ï}WÊþfêÛöû&¿Nãç~EÓ–üÙlµ$"jp+#ÕãúÛ¤µ¹¶ŠÖºœÛjµ‹]Mk\ÖìÞ#+¼Â£ßÁ´×¨¡µ°IÁiÌîêá '@6Ѳ6æ®s·6(¤º—å´·¶Ùê’ ø]‘Ê^”èŠúöÐFBD2$N8À¤‡ÒÑ0)ö¨ 0üÙ‡“‚|Ù1q@a>ÎVGœ{æÅ1jéC¿ž~ þ‡N€'( %J€² whzÓ¡#*úØ‘óÐQ!•(dñI-$$)#+@ý‘nAö¥ºE"#+Tõ¡<Bös¼E‹cï>3Ë=»¶\ׂÁ‹#)%ªÇ S">³¿Ê¥§m©#*±¹%Éê"¶éMà\˨¹”Òõ™™ƒÀß¿<º«€ÏïRÂÀк…®D+ñ½½þJ§'`4Œñ=#¤VNÂOy„£°9×Wwj2jÃéï6˜x#)!ÅçÖ¡›¼ƒ¸€p[5v›»ËAÕ‚o/ “Ç•7# Hp¡’m`q>»#*Sø9m+¥.â!Fb+BCCD‘Â6°L¿Ð¼£LÑÓ°„,ÜXšË•;Þçì:>1J=h}¿”º‡Û ¹œ~n¶kIêµP¿²)øIötSBó‡Úeµ §¹'s«ý|a®#)p® ¬-Aç®×_Ï‘ÑŠQ’÷ýÚðãž7oåñ¡çE/6¡ p)¬ß‘µ¾°³’¼3§ñäÀŸ'×ë )&©#+§Y A””²’„´ ,˜Sßñð4Ç]«Pù&云kÑe•ˆeIŠß…׬z _;ª‘#*ûxƒoMt#)‡Oñÿ»È ²å˜­µ1¦¬²É‚ ‘Œò¢´Ž!n[$#M0©Ø )#m$_/w·ìÁ£ Ý)œùb`H‹¥E)ƒ—°·*#Ó²vDIUãÆVŠ´@Z-QzÖal¢ ¸£Á5«OšâÞv=òñ 5#)Ù“=UZH_#)ÁÛæÙ,H ™B™ˆʆðŒPjp´ ûjETq1¶Ö4s#D‰>¸€i‚‘@ ¯L`)ªv@§G—­çðÒçžeê’Àá5ÀÕÂöΰŽ3­æñ¡µÉ—I#+5ˆàÂ3‹Œ@!ádêuhA ë†ð-íÐÍ-ç-1¡‡4X…”þñ dîxžža,H2:#+0ˆÒ ‘0…B’†Å4#*K …‘)Š¡ ÊNMœõx¨†vi=>~½Ö(è:À>ß0C.¾jú]½¾$Núª$dG ”É!ETB›G|¶«bªŽúOb~)¦]ÈÁ­àfÑ·ôÄ!,ƒç4FDZŸ&NAóŸBÜ`5±Ý®lÔtfë,”'e[_e·ç·¶ÛÛƽ#já©0‘QØSAˆSR0×èßÓ3Ãå’¢„æÈRBn©FPÉ,¬KØ­¢¢±­õù^g­ïD¥ÓÇI¼¹L”…Þyå#o¼é"½ $M¶[QR]Áªo(²(|Íq"!‡Äª‚‘¢(‡YÐÆ]ÚÐi¦u¢Ó#|½-0"`ÁÓdƒKùÈbz ¸¦Š£$mŠq£IÎ×I2íâ05“ÙâÍ3±-±äS‘.î’¿80ˆûiÝqâÐÛ*JåÊ–W"€Ka·­'óník#)þ„‚D‰•º¨EOy©$($4#*²¨q[ñ¶BqK â¼]FžCl Áã–&M›eÖ” je 5VWE·[Ñ$F¥”¬ŽÂB q‡’?5î‘À2·wbÄý¤Íè€hGràE<3ƒ¢§U6‡ÍD]‰ç_Û1"bvÁ'P e;dÙeƒgÍÙÒRħêλ¨Ð#4öû=*ÔíªŠv8—Z¥mÍP›ˆ^OMWñ«MsœÞ(@d'/m1òÎ ãƒ,'=¥QC®0òÖ³´B×oô~ÔéÕÓå®ä[æ¶5‘ÔPPÒZ³cyq»UÝÕR‘Eµ^²Ýu4»^3bÑ(Þ–)²DTqŒa[áÜPxê¦AOi#),ÀH®Íþ™ÕõŠ#)vgœoˆ€åþm½0€WöWÛ®0Ýçà5¹°ÈßÒjêõ™( 2#)²ˆ!Åã¡GHGR:Dz÷`­‚JµX–#*ÚØ@\#)q8—¼Rü`õúh#*}]^HjÛ¼íÓ2jÜYÖ“8›‹ê}Þ—V¹žIæ©êð”Äò “¡ŠMè È’B$ÆÆ’b…°j •Äˆ&äÛM¬›mRm´²ñä£ZT…Hˆ2¨”c,@ ÏÏú=ç¼ðB°d?è¡vî­i|%À›Bn%I ¸û-³ú½ë[Cb¶CCàU³-¤0 3U;¥ià]¾=¡M»½·®Ò±»¢ái— Ʊª4 öš¹I^eæ^5%ªæÞ^;WKj˜#+¢-2Ð0å­,¶f«(E¶›»ÎŽšg½¤rêé/óåEÎ!cLL9xô¡5ªbÀ¯P#X¡²ed9diä%»¦<yŒÛ#hÔH„$lÉx0á¬þ½«@#*­½¼9 öñ‹B,\*…°ii@eàÄ3l¶b8†©wn_\þ\ç;*gâjI –KÚUŠ8è#)ʘˊž|ë³° ¿ 6'!>ŸM-.r­Þïg¨^ÀCôÎu ؈›'ù·;I¦“Ãá8š'½ÜIÏ­#)}t%u@1$D8 ×y•šK*5’¶&Z6KI«cT¦µùÕ~VÚL®»U=ÖÙlX[$¤)#+a 0?OBz ‹ÃòïK˜Øò¹e(¹·›j˜8 ã´UXz;-f¬…Cß5"”ãÌå×ËFžì²ËŠ«Ÿ×®|ÅÖØøª7ׂ02¶Wº¨;nõ²| âê<³ˆU¶åÛoUx$˜§Á#*ýk¢È¶‚ËÍðŸ #*Iä·Ø¹¨Úa$Ñ8UA‰vSÀ&·Ólw×?™Í¹ï†û)ôÄÖH"Ð7ªˆ ¡#rž†)1{l.¸ßop0ˆ²B1Ž¼ó¿VýÉFyä#c¼ù½ð7ÄCD‰¤0Iƒ®%¬:EGs“|AAÓo-/Ù3¤m]'щ)MA=gÁKá.öú¶Å¹ò6H_³Æ:›ïmÓô¹ÖŽ=;’Õv9ÅÛÈìÖÐÈ@ëóxâFýØ?™åJª@(/#+6¿‘eg‡9ë máÔ7¢õ¢>G‚ª>Üé ³iëíúîؼ`Rv…@ñï £!*íŒnQvÚRÕ»f…ï‚ÄMü–O6Bdm@8LÊŒÒZ¼òAÄê²ÅÚ)­ ÛØy~LM¥÷Á¶²"&V¶Ð\s!JD»‚„H@zp.¥É†çÂÓ‘çÀ†‡.äǬJ¾ÁÙ Í[Õ#+y°aB`*• û {P›œ'û¼6àGÑÔ1…åÛ09ô_(žŒG”·1Œ>Na{{D-¨ï<ð¬j5ÝÛÓmÍ:mVÌ\U#*¡$‰HH0‘Q·‘KAŠCo³UïÌ£éݨ#*Á•([¼Ú¹„õÇüÿy~¼ÉuAb ,Zk€¶°º,D#&:É’Q–"¢ÄH0n°lP±Hà¤p˜”M$…PofK>E„ AÃ!š#'‹v®#*”¿sóÈóDïÁéôcÚ/yê»tT’a¿èÿ_ÎÜ 9&­-ÒV)7‰ëEöîO†iøîÙË–8›¦•å΃¶ž=q#*~ÁóhiѦî‚`†Ì5bË$ &ᢓËPŽoÍAë%ÀIÓTª)1¡Ð`Fœ#w#ÈIj¶"ü’óhäkÝ‘XHš²Œå’Šä½HßÚ‡XŸ&¡ÏO‡ÛÈ>«%2jlqî‡ÁìqŘƒ‡N•F’#rs*+A°×ZëšîÜ·WYµ4Õ”µgnÚº“d•¶Í5ªê¶4W)—‹——v•¼­o£,Ñd#)ˆ #)d›:0Ô¦ªlê(† }~z¥+MzŠPS†nœ­ÔÎk.Ea„ŠQµöÐ,#+I¾=—Å”›¾¸?ø5Ò`ë|~‘ádd„µ|Ø…‚_Û“”œƒcPÔÇ–Õ&üÚ‘Aª#+Ú¤“sc£ ðÿ^álxÐl6%´ÀÎjC"j“PÈ4FXl=œ‡ƒ¸jÎÆ$RÈZ±`„#4 g·Õú7¨Nè9S-VÞM¹”dmÐL¯Oj†³¯ƒÞnW&VBÞ~ÐlÈõÎ>êî7í Qˆ#)Ï2æô}M÷ïBÆMØð=Z‚¼uJøtyÜÁ€ëwÛÏ©ày•'¥·õ|ù_ûºìhª#f€òQuÌ:eaú·Ï s£¥lá1Gèk}8¯ª¨«¥¥ÐIDdPÀ+ÄŸ©ß/ëý¿×ÿWðo²À-OæDàÎSßRú¿$Åö]'v5ÂkŠPX‰·çˆ±²ð¹ßSX§%DAÀµ¶&Œ‚¤?9¬½à-`û6ÌÌ5¯ãºµ „.HÛy4#)jP{çÆÝ‹ B˜oQp<AuòÃÌñ#*­áN‡PûŸŠó1hm0zÖ¨#*ÀïV¢ÙáÑ¿â±’‰ÎóÁKNØššF\ì‚îù'íJ›^ƒ¦¡EI…P¨¡]ãæ½Õˆñ$ÉÍuK39gpÖ³fÝx¶ÈQ-žÈcB’b8L6dfêƒM.ÛŠ`D/ˆaí#+bð@!Kž4UÌtóxäÁ¦Ðbñ´7âÄa‡€xAËÓ¥¶ßgLeXPó.„ÄzàY)0(Ï€¢g’C »è>|¥ƒ.œ× Øþ!õ·;ô‹Â‰+"dÌŠ¡òÈÿšaB;)™ç=‡^yÌàö¤‘ÀcK-ÒÒLÅþü7"ðŒ¢ÃOè­š´xÞ¥s,LWÇëÑÓkŒC;”ò9y^ýáÖy[u¬Øvªdl‰pIÀf–—mAk<hÌŒ:ÓëÞòÜ݉(6ëÊcz0èRR5|m¼µV]áÒ2ÉRÈýûÊéú6¾:òô bÐA„a¨‚•èª„!“Èè£fxrS4ST#)jëÛqÈS¦’KRPNŸ…±xÚ-X­FÛX­FÑhÕ¶4[ ‹RkF²XÚƵ*´«^5mÊ£msY#ºfÙƒ]ÎŽíúe÷æ݈Á v"• Œ)CñÀá¹Uz,.ã`!i Ä<zöœm·Ý¿vÄMÄD–pw>¢Å7NÚö™#+bŠMjÏF¥nYÇLgöQ³nÔ-Tôúî}jcàÀtÓW3Àd7ñµM¶H0`¹yào#*Ðíu#ÑÌ1ç'qB~ï8}“aÛüÞÀŠ0Ôy+å·0ò#*¦ÊHHˆ^Ìz„döŽü"÷{õ`{rÃø}¸sdÃ¯Ò m“gø;6Q80;qÅÎc½dà ’Ìæ9‹Ç¦Å¯‘!HÕ(zh{+qQ@ïˆf?¬!¶ÛVb£9µHC!•?E) ªj•³Kÿ~·®º`”&#*I·€Žy$ ­Œw»mõºÙ¡—®…#+ƒº X®™*ªJ Ê>$‘ñï«×“:.æg‹"g±ª‚¢éMñÆŠZTÍ âYlP¦WÒuÜ Ò ŒWS™ X(‚‚ U £a[#*aA˜†°þN{-{æg#)]!7#)‰Õ8 ŒâÃ2A#*™U š*µB 8îÏÍõ{ÝïK=yz¸Ñtg™ƒf#!A@Ò…STU7I20Š9Áð‡uxâ÷/Œµ7D?qôpkØô“’Añ û»k ï—TæÝâ×ik"9ŽqE8QŒs,86iÁ-2wC³c8wÇF¼‹€‹àý6bå‚Äb‚ªŽ,x»©3ÏžÚ êŸßm\ë¯YáôûAú~{ì#)¹^‡DÛàÅQ ê=£t8üx˜î÷ž3jÐøjgïªo²½ ˜ªQW<èÒ!¦uˆå¨¥Eª¢FD€ óNCˆ®YIô ÙMIÀÓÚîBU„Á¨˜Ø<aRa¦¢¢„v®«w]¥#jff±hhÚ!#*‹Зòʃz”Y¼ ( TP$ÆIe¢#p”`48*j¥â0bnVÑÆj £HptkFTĨÂ$HrEiˆ–A” КÓ;Å[ð$‡1¯dOÊ®è‡!únfUDTD1j²šéW7©¹‘fÉV”«&Fyºë&T…(´#+åv7ÀÉ[Q‹AD#%£[( LÑ Üo##+a‚#+ŠË+&†Y¶©FÑkA4š”#)¨5+e+4%1±‹RI&…6›,Û3$ÈÛe+/]ÛË­riÝËš¹ ³u×-¹§nÞòæ¼Þw‘4P©jË}>»Ùì͆ ÀE³#+YK¥’óâŠ"”¡é¼Ðr#*£uœSl5X<'­Z4A vXñ==YŒcnµ#*»§dÃZŠ´Çƒ #*}•V±„‹·‘jÚ$ùqàÒyð¨ã&Ói@¸r1i®†(fëC}`j˜ÓqÒŸD^^­æíXª #+×­®”QH×ùYi6¾ tžÕäôÓ›3,–«ÛºÍUY)¡Ò.Y A@Ë1³pÉ°Úá£!*‚›$#+Ò1#+E¬45-—w%¦WŠÈŠÂ¤Ä1än0-¥Œ‘&`Ùi%ehCÑV‚“SšØÆ1±&;"ÐÆf;jc ß».qÂ#Q†7†!•» ¤n¸LÉšœ@ã67v:ãÊV¥´¥ÄÐW ˉ¼÷ZØÚÔ†H¾¦£Óûì#*2M…4tðõq(÷°‰»6ô¦CÈ NyVëu5Hÿ*ÕÜó@qø¶¦iM(9ôyCKô€u»¶âgåbY#ÃVnmm8álíHÀá€jÆšEÃ`ÌhÛ1½½U”ÄÙç¤)b#*¡M$}&¹Þ ƒv] ö1̳?ÄÑ„§7#*0á.™ƒa™1ô_#)Ç©úþs¥²÷ÓSb<4$²àwOÄô°¿.y™~nžl¶Ç\èë‘I–XåW²ÙŠ-ôÅd1ì!fŒkâD7\``ñ£HZ­%Sm•›âir=S|qš„ÒSŠ^5© aÛ9U¨E£fÖT5pŠ©#¶*PŽMOF,ÐÈÁ‹bðÃÜG¬ùû¼ŸI¬1ŒDPÅ#*Ы(é»)e ë â °z‰9R· A™UnØÆýnnÆQøÓÎh ¡ ä¥P4-¼µÕ×[¦´ÛÝy¨Ú*é«6¤TC H˜ƒØè0c¤,%€ˆÁ²B1T±Av D$—›þЀrÓϨô‡T}šàC`»¯¯å¥™aÅ'4î¿4H‹ýjÒâêZ¨¡Ì1õâÄ?„Êhý4Qˆ‘Ì>Ͻoz4që=½6›Ÿuß¡ÄÛâýç ›I[ù]ÏTáX‹bjDþçפ=Ö+žé¡á£F' ÷6#*¼=ûº™)doowIعqû¯7M^³O7«šÉ«—;2)eY–¾INþššúí[¥·ë{7tn(ÌD$å¹™›ru7•Ó<yp“ÆÎ3¸ãG#*#+#*Eˆ~×XFùš¼p6¢À¨b ™Ž9ÝÒ;TÚXM‘$+ªî[— Ùr§ÁÛs;÷ö0D9üº šƒB£]v°a&íò}˜Ð&~ÒŒn^ Œ>‡ÒÎG‘Xg~{–õÏϬ©Oƒ9ñÁ¯..Ýš„’Å>òÿt’L˜õa#Ü)»*"e ¶Ë!†æŸŸ†©’½5©ï Ç)N‡K¨Ÿl®ò7ðü<-ä6lC¤eÀ~홦¶j–À¬Î¸jR™N¯àùÀCá?SŸ|pO|Â{ÀÝ!0ŠAdŠˆ ÷ˆÈŠ³aDl- u%‘>ˆ¢š›*GãB Y#)Ý! Κyw“¸»wM;Æòy[ÇJ⫳kp­ºmRm]ã±b¬my×.šºµ;s!ίQO ²X´‹HÅJš¹Çu]Ý«Ôª4×ÅZ¼[y6´›¥[ªëÏ7ŒV4mmâÚ5^¬|Y\ ÃèÙ»ó4ÝaF=FÂ9"’õ]­+®åbôä*Z(Q…”"…4Éu¶ìÚ©µçš©ö‹f^ª.!{,4„¬¤H…¡ ÍkµíµäÍÙ^–«Öµy«¢÷–úe#*b¡)„© …@ËTj¥)jEú]j)²‹X¬ÆfšT[cQ©˜¨ØÔZ‹ck2°hÑ3T›%™D̘•J(©™6@Ímš¼íWK;)@¢"¤gÙá™ ~Ý÷‘G!O§Uµç¨4•¨¬%I"H)eø;/öO³Âý½Lx­êþ~»·?ÑãEÓ´°õ‡Šp8#§;«»æè(s/ò£²xI%µ¥ŠÖüÍM6´”Íhß77Ïzº…š5Qùf·ØÖ·#+µâ×Oézº‚Ѧžnê#m;»:êÝÚJZÛ˜¨ª±¤¶æ¹sYÕÚnέiZÊõÛ³Y¨‰#)#+R-4}6Û!˜î7eg"1&Yˆ~%Å_t(†‚Ñ$˜‚D`b#*‹€‰7“–`Ij¨ ‰#*È´p#+¡a#*§_G¶¤¡Pm+½Êlr„50^5+´¹†)Â6!4žoª@§Ýv§¼u¾\Zó›;¢HCÇqÉó3¾¼è¨…Ì]hʈ°Ÿ<Ó`û6l<ê£ú™ÃZÆ ºK1Œ&~VX8ØÆ2»™&š„ă#+b‘X3jiº8$Ó ÉpîM¾¦S‰(íQ‰qàu?ªæñåß­ì! 5éá1°<¦ˆt2b²sÙðVþ’ðæ8ÀU;¬"Ç‘¶C’}¥s—ceòw¼o0<&…1d¼ØeÌ"ò9Æ.{òmiÙO“P–Öû'–kFÚíì)[hítKEH³ *Ó¸Áƒ† ¥“#*1.ïg¹àšE‚¯ð“¶…c,žDèÀRs†Îû…fGaºò²ˆ#+&Õ6g-Ÿ³îñ¡þšÇ†*C“bô™”SªVÕK­5¼î„ï5QG’P…Àõ†š'7í5¬›dµlmF©-¾TûêÁDB‘!÷0?ÌAÊô‘Î#+>§Åmo$öu¤ªØÛQµpA,[w±€+oeªåi÷«Ä8coEò¤ @Ã×åfÿ Éð…íê—º¥äm]-’Úk®ÓÛ‹¶3#+"…¢Ø€‹Âb-‚8#DFˆ-37lZRE ÀÑ÷=šüA€!·Â¶ÞªÖù’ÚÛ2LFÉ«jæšÅñöÉUçu\Dö]â®Ò"6—5ÞN¹øzç¥ÞM^[f2@IÀ@Îq¥à´q•µHÌ …FÚ#*[FÙ«R”âm ˆf¬*Ö© ÆÐ0©…#+’"J¨…#+…#+ÅcÈeG,¤ÌBÞ0«ßDdЄÂ%¢€ƒ“®aìÎõÄј@¤'˜Æ ‚ƒxÐ8 _ ùxõåõgb1ˆ¤/+=]çè?'Üõ´X*œúŽx„«Œú3&mÓYƒ·vgÒd…$£Ìk%¨Û·Onü÷hÈèUN$E#)‘;KHFI$„$ h‡`UH OÇ°<3Ëè> t…PúGì£;Q‘÷éþ? <!l‹Å5@üÓTÁùF½Q‡…ÓþxEäs™ý¬#*7„Œd<–2qO_»}ÏÍ“OŽ¡N?,ß|Ç:Áë9hTOç@Z‚u:ˆ¤d.Hв€(?IF¸[.^2#)1Qg#*øpÁÑô#+¢j8Is·¶³µ‚ó³ /º)¦CpQÿ.Š˜–0ÑŠ'¡µšè€A7öÄÎg\#¾n=±´Û#+xŒ8°ÙÊåØ­7Ì¢€l]"•Pô)ZE&Û#) Š¸yZÂÂiÄrL—&Á‘`òû•!†OÚ #)à“3R½ûö¾±<þž ¶7÷ÖÅS‰ãÖ©ô>Ê›&áò‡6»#m›Ñ£ ø•Nœâ3„“½*z€QCøƒLà_!¤òeAws®m«¥j®í²Ý]ÚF!Ò”1«" gü×êJ¨µr;Sñý6|.;Å#*’òš¨#+’‘b£D!Q‹ó –Bt?w>,Yi^½çžygyÝ\º2嫆”uÝâòœÜËÍÅfË"i$l&Ê–£ÆÛ´Û%(¦l`ñ¸š %͹\«›¸î¼æ^MÇ]vÊ9й]ݺErñoJ£šl·“Ì·W.æ³,hvï6©VŒ;m®nZéµd¶JX¬ër›2Édñ[»»£tæ®ìΚäŠZ3œ9Wl¹ÖTZ4rµ»i¶åÝ-·_áy^j#)ÜÜ,*­=P #*Â; ìD€!¤#)zöúwƒ@åØg¦ b0#)v£û»Cº€‹#P„ ¡A;‰al@ExýjŠoNy¨x~©Â{AS°qè)à‡| ‚ÄŽo¦=¾ž ¯Eö=¶¾&• —Ø!çŸp¤û(P ü¾íÉøö&Ó¨öƒ`#)7 2#Õ¨Êfi}6õr«[ñ­b£ )TØ\³ýQEc#)Lç?o&¶½”–5²jÅjÐ¥¡„&€ÀÐañ>IŽ‡À #*©* +—âW›ÇjœuÝÔÙX¨²ªC"Bб‹—c 1<BŒÞä%‡%ZëøBY(0vm¾‰ôé“$ÍÄ^äâ!#x#)° ÝJ#*éL`Ú)#+5ézk[ÔÕz‘V·‘@Ò‘f¸¶eÈQ@´ƒLF* †£œý›—12#+SæAJμ¡DþÞ¬2¿€¦0ЉŒb±J!ƒÞëš]`5¸C¤5¦qwŠ#)\Kb¦«+RÍ–ÖiµJ¢”€1`‘Þ†"&ܾEÄt:ˆ‚­ñÛf5“fÔ-2f¨Ê¡$Q #)éò<XÆ&ô5#)$vŸj(¤Ô–™Œ&¶¿ÀÛQˆ’@#X„.8Ü"þ¨:dkµ3²ž3T]`˜ƒ#NqS]ŠÐ÷Ìw’M@˜QÏÙ“¦Q3eËgm–Ñ=ï@ƒÁÖ†0Oz3?^÷é÷Wã}wŠ¢Õ¤#)áXq„ÔýŒðT6ÚcÈ3‰4 ;YÏ04¤'åh»04øÈgF6"H¤$“ƒ‡8èç‡ÇCè]ñ‡ðüÃa~!òy,Òr<À-%ÇØCM>ZÇ#)‰ÄÄ#*IÇY%¢–#+U„"mA#‰#)l(DÓtRŒ„]0KDdK#+”ÐAŒ`FÝi&Å­Ä*6VjBTE+ k±˜3¦ÙPƆ٘î\´.ÐRH¶ö° AL R$¤Ð‰! @’XÂ(&eIòvlI$–ÑTÉ8ŠÂ‚«ÞâOoµáPîÊ«3h}Á“” H\U3‚Ô*”\ÍcÂï@zyd•ëõŠÔ4Ö #*7¨ÛÚ<í'ŒW5ÞRŽ¼ø^¯²#*ˆ- ȈH€£qR¡@”¨(!´Üþ]!³lnqÒèîŠë)7nÜá4}Ô#ù“wIRBB@Ž»¢‡‰WËØtÑÁùÛFЦå’× Àz ¡XLÓv iQÊÀˆHm-P·FékxyÓ±‡Î°óÃG݉#Ihä©Õº#*#)%- X#)± Ñ*@t*©È ©øCm{­ˆÒ¾¸¦i]½ZvÍ­ÁÌ#PZ¡ŠtÀ³­‰ŠÐÂÐE¹ÍxO\MÁšy'KŒ(HÈzŠÈ(‰µãÎ?ܶn°™£kª–ª¬¢Ø˜¶ˆ@sjÝ'ü°Ðkå*ú˜mpEg/Í<î½5ê´¹ÚæJÔ·y›o!,™&8(BIa]f<™·d„T®„’ÿL1‹þ}†Ù#)â®0‡'ŽîF¼>ãò-A*ËÊ«m°¯+X¹i§Öm9/'…½zaa¢áHÄî> ñÖÔ6šõ é‚ Ÿ¿ ?Dv#u|%D³óÕØtop ãdPª.Òµiúýnʪ¦þK«Kò„¨ˆsT{B®h³‘sJloÛ#)qo­€KSè;äþiÃÍ[zÐÿNïµ&GcÔßÖiI4A¤,3EŸ³·Í‘E„ÚÙñˆŽ`€ê>"ŠjÔñëÜ£í=<²Ì}#+ý¿JÅ:.µ(+÷ ”¶ÇEòì¯GoᢘÖ÷ eeÛÑtËaƒ>¢Œ‰¼Ûã75‡ Žé/#*—›íà†PfÊ#*ÍÿI”ËZ[{¬Ââ&6ŶíÍT)ˆáˆüÍy#*ü¼‰ÆNÕñ* žVÆȺx$LdUC#*–xY‰L©m@iq)3Š¸7Ò#*D#)ÆadÆ-!cã˜?—½;ÄCq¿sb‰Útd‡äûVÞ¬WÜÈ ·d”’ óS»°Ùœîòc­³$}´®%9õ<Æýf‰ÇH±SÚrÜÂÑT`ùÉ0Œ>îÈáUNlÌ "^â¡ßŠ¸%ýϾ­ëú#昦>ÁYµmÂVÐ…½Ç,¡.³Š÷²þ]²˜oF™‚ƒ3q‹N8L- a}d,„ŽYÍ<cÆ2H#*#*‚0oPŹ°ÑÍk¥¦=i4b¦—sìž ‹'4ƒ‡Ÿ£Çe*­ý,sNèÞñ#Hè„,I8£h«Óë-0ŠQszÏ&¬çO4½{Öžm›#)·’þB¸– vRMѼ°ÇÑØ-£?N™jöíÆ}vØ)Êàišç¼o㤮„< nõMÖ^YEû}\5Œ¤eÆ@æjmÑ!Ù%©¼HnÛíÈaê17Ž-a¹ó^™Ìgló¨OÚb*)»þë ©wQ™%Ôæq-µW$G-Âu4B!‰@6‡‰R¤z›"Àñf)¡#*ı¹];zë>³àÉúÓúC‰ßìª1ûvŸëUÞ/OßȆæ…U ïçÕÏn™*~’ÏD"ºÐ¹gA\}y·È¿®óŠöñ6&hl€sÒ’‚& ^Ñ |·šL5íI2Ðùlÿož¥‹¢É3 Aà\0CÝçOªLÀŒè¥¨$D3M‡AFþ{©Ûkä ›<3±Éî£ríµº?¿nrè}l<X+5 Çð`~ö¸÷Τ•²I5½j¿›é˜·ƒíJIõ¶ÅÃ⪠!0$D–‰eh#žü*h$m®¬¹«·¥«¼Y ϨãøH—#Œ=%$3XUSGRgŒgYÏF»Ë`IY_øâ÷=>#õ eß½ùû¤ï¤×„r-µÜÐ “½T©Ø¾a®:ô¾6p&Pƒ{ wÇh|QØcÑÏ! ™Åi¼@Rö¡dN¢íAV)‰GJ-ùtúfu†ñ‘ów-*zf§-paäG'óS⛄ÒÝÓ>Ì?Ÿ#+R” ÜÜѹñåmçQ•v0/ÏnñÑ8ª¡3'»=·„1Kø)ê–`Ėȃ§¶Æ§N9ß#*Tò´Öô!†1]OCã´ÑJ®óÕÏÙ× zð1SGÐ#+·,62”RopiB#0^ì¹ÀŸ…ô³áÆÁÌé:L½Ëïüºbl©õJÃò °bA$_‘ ¼#)$PîG½!ñG¨/ð‡ÈùÅ`Ýð Š$€$""@ C»qz¸ŽÄ~þX*G®»²½¡2™º€Špzຎ cÐñØC¯QîNÉq#)ùƒs#*Aná#ó-¼GCÒsä.6c’;þ7­ÇñÂǾq1Ú qñq.ùt–rîÀ SîÕµmó‰h<4¶ÓkñfÐDW‘@¢Ã~9({¤Š¾5ÝB¢ªMóû«ììJíçÜu;×ǶH@ùwQÍ`ÄDÉGK Ÿ>X¶ ÅG›¬ä1‚ÇÊ¢ci>Çb¢˜Šƒ"vɤ`¹‡Uš›*Ó^y…ø»³àHàà0×ê‚^:=¡#)¥ìIKB#)Æ1XH ås‹Zû9ÿ+úþfee$^ú~ò˜Ñz‚Åd—z[-Úé@Ðî'Ó—`i! Jª‘ PÔ<Aßo=Ô&»Åë#ßç9™àq¼Ùçð£.J¦„O%~ËÒš5™›ufvÃnœí}:ŸrCq3Cú¢‚ÿus›8d#*1úa(«GÐÒ"Àú°tý’€Ýù¬BÉ.Á; è!†Wóµ¿<û:+_5#*<­GIµk+Ø´#+‚WB_ûºo\„7$ë}×lΊv6¢«;;Ë…(:Ùç°óÀNb™XÅêÁŒ‹¾ùð•èÐ&¸ ¤Ïϳõxžt.9B¿?!(6¿>ųøš?·–}‰‘°µÂ«Ln3”’9‘/·.±)Žä÷ïÜ;¦õŒªÌ©Dæýúà³{%à˜Úû÷Ù[™¡óÑŸ¿åˆîP’AÙÈÑoª¢_˜]HÄúwL˜üZ­;×u‰±55(øtЇ»°;èÉR:R¥:íe­T‘@ÙÖª¡#Ùú²è’vœ<7eú1*2‡Í,˜Î•ë;rÌ~jÂ2Z…J‰,JŒr¹Wtýê»U&b-¶[5šj–kÒ•ªj6©³ñîÙd#*Ï*Ô’D7!й¬\»ºîêøuŸ(Fè¢(z¨‡®¥Z#)°Eîü9Ì`gú?O‡™#)›FeRDhÌš(&›D¢j"““%ˆÓJ‘’‰¡HI³dØP‘£$é]»'Ž6 'È‹¤^d’;Èzâúã·‡6ú]y”R’M›vÌ ÜÆ0Øh5$˜l²AšQR0%óoº[=4È ˆ®Ú8o‰‹ùñ•€ü‡“‚’G4ÓÁ߇+kí÷›0‰9ùvg\vruÙ{R·ŸÉû½2÷ÑæÎL£(JÙÀô11¶Ýó‰˜Þ— ïÃ`-®šMU_@µi–0bVÇ ‰šÇ-ìMci©ãŠ‡ÙU'ß߀nk±Ù¤ec‚á¬j˜TC§Ûë¨fèÎ[‰‘À ËóÏÚróˆië©ß#+kkž5I"þX@É—ýoò猉Þðù‘T“Mópµ$ -e±*˜ÉJVÚiÆ(âKBjEIj0 Q‹¬i’@´FÝQUË;®JW+n×t¹ºº…µ’êÅd#IJ4fÑŠ ÅÄC#+Û1&Ú1;¤Œ`f¢#+€âà¥n‰J„.Ð0¤£¶É”´×6\ÅP2ÿÂ#)(²PˆÂMi•x«W.—,î·5d”Frã>òÖÅ\èÚòfT#+ÌEùHSËH"Og¹£°i-±¼knâq¶H܃oͨspÅ!4\Í4Eý´Â¿ƒù‹Øágˆ<„eÄÚ8tÖHL%ÀB„•ˆg”äôFªKýã®6¸*:„mä1›bCb%mä„`tDy¶>ñm#I°y²¬`½­ppl¢¶n8„F„Ú11!9¦ÞZ4ÉcŠ#*‚'Žómܲ‹®Þ5¿#)õÝv»¨Œ§³jƒHÌ-M#*Œ @áüJrSM#+¢Š‚a´ØãŽ(’ÆYm©¦ÔÛ‰¦c"ÃJibnD Á«²¡±=#*ÉbƒUDë•V).QÙ„m•ÖÚÁ6Â*âÐaD«ªE‘!P®•Ú1§CiºÓe(l©o0•Q†¨ZcÂaUƒJŒ0lC­ezïOv9»*A)V(»‚쨻SEÝÄ´¹j @űѩ§¼Å¶e™ÕÞŒøÁc®„¬—µvCg@TF4±pÖ”ŠÔƒqs”¥`«UÆ«A–@–Úì2¦AÕŒ#V„‡ «M3OK »Ÿ.UÑ×€ÀÀÊAÇ•Úbb¤ØJ ˆ,à’ºÒ|¢µÀuÄ=7¢jšdt³EÁƶ¢Éçpf˜á3fðÆIkRPn r¨ãnbZ@Á<(Ó„}dU¬—šŒŽá*8˜ ` wÈÂ3ùN XÕ¤ãT`hdJ‹t.hŠ"³d‚j!B!›SUPj8ŽöÎŒ@ÉŒ3ƒ@F¿oЈ/¢‹”à#XšGdÕm5œÊÐ&ÄQ‘@ݤÎ*Ò”ÁW:Ô22dI•@PËdI¦™¸H*‚†Š(ÑšhË#+Й °£B©¤äA(0XѽÀGUý¹tôÅnÒ\LA]ª²Ðà†¤‚IqЩDÚYcŽY¼R±éŠ ¨}#*œ¸ƒ‘<Ÿ6 `Ó᪢é8ÃðA6á(¥â =M„8@ ¤†’£)¢ ˆµqîe‘éån#*nèÀ™OdnDœD­óêaµyQ‰²]ò}Ž´ÞâXP¯¬Xˆ–”ãîÑÜ¥Ð7¬j"ªB$5çf‰lŸ7ªMrg +µe°FÓ"&˜Ï&©8±xtí¸Œ¶ª$ˆ’Z!LìaMHÒZ#*Š(ƒæ!B†ÐQ!bJŠ£"¤Á€524 ÚK7 )"#)8ZhýpÞ PŸÅÌ;zÒR%R”Å%ZÕ+wvñ?›çytŨÉk½ut·ïuÄõr;§rV;å|-ôWàÚJ j-¶(Z›6™²É“Vú6ÝÐBÊ!÷X§é.ÿGšnÛ45*êv‹²ÍÂüÀ2‚,P¨ „‹ÝœÔ Yé’n‡Lß銤€Œ¦jC;Ðü(£‡âµZ˜r'¹Y "#)P€Â1©F‚¹"…ËkÒörAöÔ¤¨‡y±‡tÏ/}ˆ^T$IB\Œ*dû§H•÷0,ç—š¨Á.PÓ0‰â¦’#+;+$(ÿÓ`fHØ›;»b1±­ÕÕÓkomuÒ‡w `ÂwJ¢Æ .P¡‹0°nÙ@b˃¥ÀÖªQ³)Œ€ø2CöŠYrT PjnkºeËF()fÉC ×BˆŽ$p½µ#+.<ßîûÂÞÃápd͵61.ÎòX× QÞÈnO¶ÍŠño#*o» ‹;˜#*¾/=”ÍIÂEJÍžf«üA¨¨ ½Ùî ÷¶²^õ#·L_HÄpæm:/§W~‰^‘­£éP5ýÃR@[EÒrŸy¯%C°Ë‚ðPÃTP‡‘‡'BJÏ܃¼MIˆÑÚ}ý[^Ýè½÷±`±z¡‰.Qr¯f,C^&Ù¾šë9TøõkëCøO‰LK g¯ªïâÔueÚ‚7Ñú~¥ØÁ#)Ë×)%í·½·•ç#)Ó–«Ç2Ðai¾§Äþÿ¼™ñ|DG¾˜Þ™†–ÞAÆÝKÇÉîeÅød"I¯yé’#*EP¨@B»þtìÛHûÏY…À|,fcœ¿n³& N.È=ÌFÕåX,Ù:V*mî(zà PTÜÅÝZµšë-¶:#+@'?‘`{ñ*yÛ\ŒP„èȇ`ò;b\”Ø‚eb‚ Š©ö0’á™óNÊR¨QmTÎÞ¤¹Q¤Uñs´k"ÕU– Lèa¿ÃÆ´Èj樤0À—mE„ýMg‘¾#*Α9H<\f·µÉ±´&Ä›#m¿è1Éóë\’åWH»¶â²öþŸÊñFɽ§+d¶‹{5·b[—wVßÁ-pÚñTk9¹[¦ÖÝ5d5¹´š·5“\¹U¹S»Ý®E²mAŠ5¼W"ÒÒ5AüKpÃD8ì²îÉ7cu=#*Q_€g”@âQ™Zú4׌œÄ™‡©6ûÌÉ Ù]Ä[Ð7RD"†èŠ#D@<QV›Ò‡±þ,D@»I;»·oªå÷æù¨8»ê /Ñ#î~ ®|÷}ȇë"Щ6ðŸÏI,|DÂ:Á†“ª#+E#)ªÁv<ùUéL) Q‚"FÒP’74‡ Ÿè0'Õ"Òà0XŽÈdk 0#)6¨µm¤­·jÐÖ×I°X€œö yíÀŠ…&B]½Þ‚¢îˆªªü-ªYF“Y#FÛl  "³ÀùÑD,(Ž çB?Ø#)Ÿô€&¿ËîçIÕn’ŒZ¡X0™/ÉCÑE!+Ê•<ïkJ®#+1 KÄQз&e÷ ‰ÿ$ƒ §w Ú•“t:ì•ùÒ‡u¸ùnÆ¢ä„XKݮߛm]m_ÛÙKS3d’R…ŒZY#-MšRQX±²””’™6#+VÚ#j[Ú¶¥¥i–¥£S*V-h¶2ГB¿aŸ*Ý$‚ cnµ™¶C\ÑÏM®/Û]öùêõzò‘TyC)Ü`G po2ä7b4À¸ÔyR@ˆTÒ€É"Q§1Ž“n‰±6FÆAX”‘1¨åŒ$AB¨l-0ÃRÔBŠV!"Œ¤¤ P!#)‚-u4_!P‘j,`ÅŸ_êÎÁpAÇm)h@'.ºjnîÙí>Õv½A ½Sm("!bB ch‰C¬@M°%Ô^별B3ã`û‡NÉ"vTôsmöö㺲³×”„…QRN {AD‘!ì Û‡ôllˆÍÍkTþE&˜SG]Ÿ¡º0±öô††€–\Z#\b»‰Œl2× ÷(–!„mùq€Q#+@†©Áú|ük.ë1ìb>f‘€ÆlÑú¬QjªGþœ4{S‚v!Ü©ÐÉ#*Îk¦Ü;Ç™¼•ÖòË©R™”/Ü]êµrѵ±hÖ„$ª@©!pil@züœ%Žü3¡í×Gù€Q‚¦1TšUw¸p̤B™GÓùtÙê˜K*Pt—Êo¤= ¸Kaõo÷~X¬¢TåÙ²¥-¦?§dW<`ã•Ìõ‰ºx¹¤Ý7Þ)ÿKóÕ¢4•/!Õª¡0#+Yš*¥07É«'È™ªà«Œò*Ðû÷üÛÎþÞ'#*’'×$;Â,–Ë<ª£™βë‰ídçr§–þ«slsB߬>Ô;ZîãuÄt~—Ǻ)ã‚øaº õ=â¿òigW¯Pê!>æ#)샭±š^(quÔ¨ryqTOŸ›´i6.Ý–ÝYíRñH„ªTwâšQlvróßµŒØ`çŠn=ÑÒ¬ÁŠ©fÞ¥ðsqÕq­·0œ7%Üëª5ÓXô̪qĨÑuÃøUc2íÐ|oq@²°™‰$q8ܳ=U2Ím³òoq¼×Gnó»f–èœ<r¢!ÈfBž%PDËP„e[_9 Ž°œÔÏOG¹k\7#*BþU&à—<„? 29æìÙM|®%l«õw^ò._­`‚N"Dh}ZÒÜœ¥ŠYD’뢚M¬Ò:@$¹°¥6Á\œeuÂÝ[’QÉ7ÕnJèvv× G»rvY£Ü.Iå-@ûBŽHQ¶3üØHœh‘`1ÚÂ4]…A¼ÚwíÑA.Ü:.}Jmv"ÚÌJ/\-\L !$aŒ\f—ŸVXã«ëI§~Ûûg-Rí„㳨CˆÏ1î9ªn欄µŽ‡i*^ó€ÞG¿G{¼K2MçB±zT.W7/²cMž‘ÁrŽã˜÷×N|óÝ$"“Q?*y[„6"£ŠçÅƺ{Kñ“˜èô873C(!˜­–ý¸Ï…ž%͵´äL¦â¹T®>{í§†ÛÍc&AÍ31oÄ#*\pׇ¦·ÒiÈþÿz„¦Fx ]YsCÊ—C¶é¦ žžI$;º/€O™å;•U›#ÆøQaÌ"Ç#*è`—cÄr6¦b˜#*•ì;w;è\kg#*Û/+6ÌkïSb1£‰Ä¬¹tRrÎËÛÂAÛŽõ2yÊîuëž-£1Y—¥˜fÕ>ùZï!¢•ÛºÚ#*¤êÛO9ž[ÿŸÒ·O¶%Öôq¹Ö’KÉ$òxÌÓFñÅ4šÓâ^’¾¹¨ïŒDˆtºwqHrd3­­'8¢÷Ýz|¨6È™)QÛmî¹\zá@êAqÙ=ˆ0zˆ¯Jè­1žKRØ_—)îóŸLÅtï;RzŠâ»[ám8ƒ8ÏÕפ6îûe3®Züç´ç‰yÜOÏÜžý=#J£§G•ÞÕº›6de6 k›L£*YYã#+b®´JÚºró%Ò©#*YÕìð¼àywyï=pô¡'Fñèòª‹­éïlgš/Ç=˜‘ašâô‡~U0›½Ë¾¿¹˜…æŽëÍ™{0àÞµ«ðyJçÚ|¹]‘—%•².IŒw‹ôõǯ ú;ö#*ü[–qØwb÷3ÍñÆåvXÂq²XÆÝiÅc8LŒiÓ%J'¼ÅŸ3­Ís°ß4.¯ Ör6žDtÃ6Êc™Mê,¦å³ÊØ~܈ðÕM ŽNP¢PÒÞ²ø­s1¿fJºàeÞ©’tóŽ[hƼÜ|òTlŽ1'¼¶)˜'<u,áGf(kA2Ê”¸;¸[x„¸pņåÚjç’^Ä#*æ¤u鮡­4Á³‘Â)1Üoáw$ÄfðV"³c³RëÏ!Î=Ôìb#6.†º¡»WQ G4¡Uš%5ãØ!šCäözà½t ;@©l¼ƒF1˜`èÀ:LØ”oìökAa†Ü+a ˜2pu姃œ§è©<9Àzp±6Ìö¼Wœ:FÅ®]šp#+Aà pL·é@ÇM*ÆÆ7ƒpÜ}¡@Ü$É$gL"kØÎLCø*ó#)€@>#+  )„@#„Aµ#—-ãôqØlߣúqéwŒÁˆa¸s¸ã3¨iaHN(ç¼ÆFÞ§N‰]Fµ4:0ƒaš (@ˆ<eîg«÷h™YPš´º s2ê­âl¹‡7Œ5Î0£ðªµôFA5ÁèŠ  ëDv·³[ØsJô|ÒríÜtm”NÜïí-Ï3ðìk½ÖÈ,½ÓÀüê2p‰øLäWÉJ™Â%g>ÙÁv„dym—9Ø8Õ³IÛ6£”˜­¤*fíN²#+á*5®¼†¼­¶¹®/32{ž\ûôŽU"Š©´¢*¦¦œøÄPQb§ë·,M—Ä e²ÚzÁï]QÁk]9ÖÔ4nû““Œz‚FQWîíÀ¨LçßZKµ´óBŠý°Öüüû`c³5ÃIª4q“ç;B×u´o|€ï|»ñ- #™iå5Q½j×(Ãdvéxwu®g~E5­´db‰#T@Ç/¥-lZð¶é/yÂŨáìPÌó¿Ä ^›4n´«5¨–™ß¼¹‹sN™¢xâö/-”¬kèíd͈¢:œòÑ£c·=ÊêÜXtõç牻\!œjãmú.;‘uWã´ ˜ÁÓ¨k vGMuË7%O¹¾‰Àºt¹n= r£©£u ý˜«Ç¸Žny_6æYí»­Îòæîü_XÕL0xNq]åÒÞ3—Ͷ • ßx™“GT·Ÿ#¼Tù8bW—‰öï‚o)Ä{ƒÉ›îº.<»ðr žr9Ô­«T+OC*0Ñðúw-#+X”±÷4µ`#)Ï´½pžƒRñ˜Â¨†–ñ˜³ay£ ê9r)ªàtY#Û‹Êp¨Ò§<8‹°ÞÐãUV¶{@þå™#*î7£'víÌÉòsƒð:á™»ÐüÍúŽ\h0,;¢â£}jålÿ<M¬)èÖv¢lC–aaÅí#§å„3¹f×.wK»Í¡C#+\Ù—c²Èô`Õ¤#*#+:­o,ò1å .#ÂhŠ5.W!N岩«¨T0kÁG‘m=œL陋=Ô«I°j:º HJh¼²›Ó&·”Ø…—3˜ëP"ºÀ9O‹$åQìŒ]i-¥¼S¹ÐÓ6Ì 2¨I#)š2·ˆ=¥÷Ï´Ý\IK#)}M@§£Ìþ%:û#*ÞŠÌÃÊÈ¥Å>e¸ˆAáb: ·`PnQšßÚVù€¼t#*~Ê#*s¨òæ!tÛV\Äò~ À g6û;Ã4q[1êUT4úŽuÝ-˜)1„ïëù‡Ì2òvÀ×ÁúC#*¡MR­—_’°gÓί«U£Ñ#)£¨=idO8›Ã¬œû\vŸ2¶¨ÄY‡Í¥B,‹#)ü_ƒ`Õ…D–aí»Ímlš…s˜Ô±U%&F’b—uBDá Ø¢d SÌhXЂ6”c#I#*•‚U@’ ³x"–‚s@ˆACUkzVnkQZܵ•ÓlÖ(v¦R,ˆ ‡u‚­¦¨5*2‚ Æ”TE#+Š2•($…FñÊ3='ð3ÆÄ-¬!™È#)É€ýÛ„p°R.Ú¼4$‚¢,g9¨¢§§+ÓxÛÆÜ‹“cm*µ¯Î¶[E«W-TkU^›•]{2×ô7#3t9Ô¿—…¥­t~Í}×XñËe©ÐRÞ9Š±Œc`ÒÅ#+ÝD"5Ëçj4°{nmŽÚæðÍÀÖŒt†Ÿç1h‘]…2¸™÷Í*ŸvlëZq£U˜Ž'–±ÕÃ)f¼³M±A¤« ÊC‹*‚#H¯—z‘Þ2î›Æ³‘,"E#+´™º´Æ#*¸æÈaDÔi6›K’)a.ê+M1¢@%rÑQîSüO1#+|'X·u²¡ÒHW£>úlÀö›L#¢Ù7T€²`&5„Í81"Ìvî\¦jVã p#+1ØÆÝ{zÔ¦™Z;ñÎÖY½ÈÍMi¢ŠµM­kTÈ7 glÓÔ–ÃZ±êts‹œÕ²·Ã ‚6Ò ¸È†<Á³‰`"Í3@ªÅŽ Å¥àÄVPË­+m†IK_¦øÖ§<äîÝ£L…d39Nñ+;rDŽHcB÷Aê„@Ô ˜ãŒi÷cÅ]c®w¢ªC1¶ã!sR%à4²©Æ®eo˜iY2R³ .õ³7 —R‰ŒÚ›°Ý ±Ç·ÔV¹´¤IØ'tRÙ«#*wÍ4ç‹®¨.DÕ)l£|˜Dä4< ÿRãÞºÓÖ“cSˆ‚ò£œµ.¨CZF“Làèd¬q¨Ô"Ú7*ª#+”*à Ѹ”Ðá¤0m0m15+‹‹FÞn-¼™,²»zça›[ ¹tùÐC 2´CdT¨£]ž&09ÜEZÙ*å#*hFe¡›±—mÝF=]q›„DËš&Zþ㙢ðà•¬½ÍˆòÞа¨iSq—W²©U[UO$¼*7Ä®ôéif›:˜pÌw`ôäcL…"½®ûb†[¥cMUSW³E¥…(V›UÖ‚ÇZ1ŒÐµ¢r3LäÅ=š&g6Õ3rã<@Ê5Š±ÑÅ›òıqɦ3ËN•Um!C4@ŠÛý]éþ®ôQíj>«ÌÇÓ( £è Qˆ±¯sA_-š$u»)‚‹¯(h1êÄn#*`9ÐáŽe4<iäæ‰#+pôÛÅ…³)L8…Ù 4ÃV3<“m-÷aZ¸2ÈF;g›¸HÈmá&47,ÍÜJˆÔ4pɪ$bˆYžjÁ\̬Mnñ¢g%MfHT06™ %‰Tø¦r±$!htq`8XqƒFŠ\ŠX¬ï¬Ä5–tc`ÎÜqœ˜RcõÖ®“éxú’I/’>.¢v“O«æçñj¥,Hˆ,F#+Q؆ˆv2I-¾;Vè¾\üóþaüÝœae2m–MÓíÚ¿oe&¸}ývÝ®HIô®Ÿ9®;ÅÍóÂÕ'Øb ÒNÕSº¬fƵn¤1R˜ËnÏÞq(0‘UQ²»ÔR¯¹…=M5àÙ?G²5{©•ðšîà’O{Öx§9òPÕöv‡5ô b¨,åVÈe&8O—Ÿ°'êØ}`oìé#+2;GŸ«Ë×Užž/ždJpšÛ×'iäõµ±®‚¦ôh?wÆÿœöd£¬PlµR¸<³gÏÆos×n7„Œm#*H1É"a~öî€òrwnÍ›»žÿUdöÆXãá\÷<¹BùÊ= r;àƺi![k«ð„Hãƒ"ûðûŸÒÌge‰Ô<øOE¨÷7=ˆ`–•ènb¶ìÛIIWj™JBHF!6$‚#*V’`@H”*#+{ÐÕ€7~ÊÉ<Dz‡èân·m{»kׯ¿ùá¢ÄbÕ5CLµ¶ci1ŒLcH‰RPµL«ÒضKImX¤£lIY4hQM•F!›BšjF”›$£f”’¢41¦Ñ‰H–Œ¦h©M‘J¦Œ-$Ãl0Ù„JP’cV°AUoî7Þùôð/虨Ng°è:MHd)ì‹F]o9ÚÎÇÝ4Uy\ÐvðWçy9šÿ+È’3‡‡#)œêáßmzæ%r6ú®RNÙum´ƒ/Ràzñ@#+ mC À½jŒp%ÁV[1¶ÅîÂb[pÝÌ”‚Ù0eô}0~1xʲ@ûáks9QBF+ ¯nà>.AÀÜQÖtíêCÈ¢™FÌO>!¶òI!pNƒ]­ä{ý9¢¿WQ¹éíŒ ÕF©©ˆNÜÙ~ʈ\ÛóÑ«µØæ§P #+š?ˆ3Vüm¨ÕD–Ù6ˆŠÀ˜RmcGÝZµÍ¦Æ¥#+MFŒkb3ð]«™VZ•ŠJ¤}ia, Ë¿À·€ÈÈB 3(d…‡³"Ù›”Á÷V tö`ÏŠá(TGaE¨i˜Œ7?,º5XÑ“‹§CªŠ82V)©#”˜ˆ¢Ûf#*UE"#+tå³FÉÐË! `#+@ÐqÅÜ“ I‰çÉ%±jouåݺvs^Þuyš1”×FLˆX#-(`£Ä%$¦ýÔ&j#+8EÁጠ¸»»Ä2‚lZ(à‚ ‘‘X‘—ªèøC¾÷µï>ê#*F“äá‘`rgÅäÁn¦.Ì´‰Jû©EˆÜ×I‘j#L!(ÊHRˆÏ:ÔÎn`ºÐü«b9×›1·›‘ŠU"tq‘aî!¡o@ƒ„Då0„Ä#ß!éon¬ #±©#kmA±¶(*¶kH‚"Àpä#+K†Hä[Å’D’I#€U†*!ëßѺLŽ@i¢d)Já£qKO5„ˆ^žÆÊz·±Ý‘ØB°WÛ#)ä{»¹8Æ~´¨žr†ÌçzdHFBÿn/ë~̉“ª:zjÙ¾^H%¡Ì{‘Ò‰ª¡ÓuËÖ:DŠx µ%BÉ„bce+kS4ñ óó Bðæ›÷ì+C",dæö¢Ðñørðý«Òå1*í጗Yøxxã*Š7ß­š™*¨¥K6×­jãe%=WÞTŽwçLMKê”ãjÛD½CHÄŽºŒšW°Ø»øäûo#ÌÒ`=¦"Þ¦©œÝ[S[ÆÚdÊŠlA`‰£5%4ÝŠº [—Bë¯yZr µZ}^“×`ÕŸˆz8®Å6WÒœ“•®J5yžê¢Žìš<§ ¢ÀHƒ'¬ïàYÃN}GÕ}¾k±éøɹv±SIÏœ­¾….b'zFIJ¡S‘ê5êOöflSÌÁ°ãºÀ¤ãcµØï/¬2B I£<ƒ3ÕÌTãŒwj7éV<ñ@M„Z¨þ?œ¨nìj“2`ÃîÝô#qõÀÊ%»¡ÐA÷{NqH R,’""#+©ƒO¤z¾©Gq1/±û5´mƒPq#à{ì=ØJaoÏ÷þǵÂGæÎ3†’x%Éôºkƒøþ¨Ù®{¹©d*ºh°À†U0ÚÝ\C’t€‡btÅ^~I;Lƒ ©XÕï=¡½;éòî/Ú#)'‡‰?e‰óh·ÂÙO;ʤÂA/˜”UÒ\yge)^±²=sDiù~{ÜãŒuñ¨õ#9d,™ÝÛÙ¬–"]Ò!ntr• #d+}å»»H¶»…†|Y°‰Jwa¢*®ˆ˜ž'*óƦxN#+š„G x„(€` Y#)wÄfAƒ‚€€jþSuV A/úB¢ hœäI†ù9Qw.äÖì뀰8e˜$R5)&Øn$”ŽÅpÉc#*Ò„ÚQF´¡0&¹9팈@ÂËÒp,@2ÎÔgõ,3cÎÍÊvÒAÓK‚Æ4‰"q“H¤·Ÿ—™Údœ:äK'[àY†˜l›µ¯K,䜒¡ãaF¼¨q¢*T©€p(6<ÏÚf`P1)‘5B08o±lÐE¤W”®<mlçaÁœ¤•\e!…‹uLîÖîFaÁ #*‰ X'W»¸ÁÇ$DHÉawÃn:vèïÁÅ ®Õ(èÃLØå¼°€õ¢Éªt2BC¢ #)bõ £[Þ+ËÅ>7Û—%È–úÁ’/‘ÒF~y¸v“‘»ÜE2"Ã97€š@³1-$TL NI‡…Õgl_éÿ’NVÛqÙÕLÊçÉ”ÌâçÌ|e‡—–¶ê)1ÂÉ>}ƒ~D°r ¤àº×4¢ƒvkcõ#+Ä1e·2áƒnìмPÍÏ:"PÔ…$€uÜwÍZ…ƒ"À4d)/BV²b’ °Šg®q'^c«¹š5@·Iå¥lgºë Ô¬Çûµ^nÝ­Šœ3yzÞhõâ²¾#*3(I—#ƒ£ìíy`•p“HöXÈßlÄs 5‡…vA[ÄâñèØé­Ÿ§— ¾ü:fÓ½f4ˆÖò0æmg8pâÂpëàÓ$é˜3k:{OAŠ9 ­6q8v5´ÌNÂRd7m¶ÚX)‹‘Ê’(Lè®ZŠ1–”•¾cvLª4f\±ÖyíÃdɽ>ñ¡9²äLG&FæŽ%5í!¤Òžù8ÐdZ®{‘² Òvk)Ûm®š]ÊHŠg0ÑH6SDâK“;K—.žõeÖψ[m0Zyk.eŠF«—Mý:’-“_g|X™á[§pBsqáWbEºÁÇX­­Íñ4Æ´Nòò2bš-Mâ>‰£ŒÊ²ÂY“‰†s-š00¹æ§ ¬S®pUœ1ízD©š½é¬S\¡åÐó‰¥Ëv'#*tÝ“ëˆJñs&âiÒtîÔ‹\sH`Za#*YxBË•ÉaVj¶»&àdKŒØZ{§pwreè¨âÈP·_.u«aw<‡×móMs±\œjJ݉€®oÕÞà{Þºg(ä*ær5Îih+BFìIJfÁÈ8¬#*¹ž\šÕåÓJ—)ÖÎ9‚Xw0rô :^¡¹ñ1–;õNå5âÓ!?'Ã*8;_}®fà´Žøk(D$N 6)ËC f•`ãf(i®»@;MX#+ÄRP„M#)Æ“¼)ÃS–´1jn¡®å·öÁºZHº+¤‚BN\¯…mgY¬ù%C]ÞÅV…@§„#*4ÑœèLkå#)¼»"È@x;vüe9§àcf¡øétR^›=k4Q&ªÉ=š‡“£{M¢æߊm‚ãb5éhÚÎÜæ.ƒ¦m#„Iøßíû¥öÙs.›"—!­ybi¤ü|òqζò°ø#+ƒ„˜l×Ƨwu#)i×ÁÖ¯®ó\uK!®~~·¥Õ#ßrÌÈNz´ÉXÍQLÍ;T_!5qUŠÔ¾®èj齜&Ú7›ð, –FJMjfÁ‰LX|2ÜX[[ï˜#5…1 !#+ÆÃعb£á«0tÔLb 6Òê#+»#*àf… €é\&8æ¹>LJd†½áõÒw+mÆ4,¶(»ÜËuvè­Z 0¡ÉÄ (K(f…¤cn8é>{rk­o)Þ¨ †˜ij!¡A¡¦ ñÅàÇQ4ZsжÖ&&Ú¦‚ ,¸ÓÖß™®`—s¢ÙÅD^±Û•‘Êk;¦¸Ë ©MNNpŽËW4W*aÛ“€ëPÐ&~±×ŸX¼ £;ï¬Qažœv3†“atŸ¤&ïnɦ›ñ’ÃGå Ð캆„‹Yƒ®.ÏÙ®jß’Á máÌé1ðà ÞÅÄS{qßb: !0dG4lä]+T£p:5Ò‰‰™ù¤Žd‘A¶¢„HòCªhEáAFH"¸ªPÕ5“S¼–”ºÄ8:ðšÑ”a˜¬áÍ0QcLjNî ºà<ÙSlð°;*0%DH!¹¡.æ¢ì+TК÷™‚Y0#*#*L#)ŒN’.jdë2#*¹¢‘ÔJŒØC SÕâ„5&²Ìš,•œMÃV6feŠ¨”`àQ®ç›ã:š½V™iE®Û]\Q$ÅÇxTÊ®:!Зz2 *#*ˆ¢ÐlïC |ŒØ¸#aȪ#+v6f4šodADàġ¾þdd„jÂҥ͕MŒ„P6' HD† €6NÃ4#+’0`éFp†Í$Á50J€uÑœQ´3#)€¦ˆÜ`Îq·é:Á# 2:ÎÚ@Æñ…¨Ø#+S&pÃI`³Øâˆ^0Àgi¢¢‘H±DŸByyôŸÆÖ9Fª”QQ`1¯Má#*áËcêð_PØ3 {¹!Ö! &°U#+‚(tCïßú‹yì½€ñ:ŽUbÞTW òKóvÒ³S%Éùú¥gy3>Fø[JÞPÇðE£MÃ܇)üç|=º¾6¥MmºåTÞchS”uá÷H…s#3A7bÙÒÃ$†8lªŠ\¹$ r ‘¯çK5òìé^zê䪠õZˆ¿š º2˹DJöR* UzRƒÃåY†SAEB;4«€ö0ã×k4AüvïM2p#**¸J‰;’£2;Ë•"påp2‚‡24Š­P}öQ-çµ#*{4GâV‘}’&™2xäïvÇàÄms,:nÞ8§èÖ•ÔŒ8"s˜¨!š€´#*~°¨¬Pœ‚â—Ù›–Í,%ì5J¾Ø’ÓÑòWoÒxzœ¦BBLÉTEÅ £ÃÆ u/¨UL}*ˆ‘TöQ—_Àù)ÌèÏ„‰htß^ØìCÝ#)Àϲ m²²rè>d¼‚žå¤$uJ%U+'L,P`Æ¥#+5Q¡(ßÀäNN5J¦dÝÀ*v©Z0b#i`ˆŒ+q“µ¼ÒC&J-âjwÒÞní6ÒfH –$ÇH0Ž6BÉ#+AvaˆHX€i #Ôª i Z4(Ðû©3 IŽæG\“H2?=Ãjæ¸ï§(b¹«Â4jÂ%Ö…#34u*ÌÙbQ „GFùçÕ×;goˆCá wÚµîùј"rN€€YÈúc/è#*XA‘¬·“ŸEÚÛF'„j#)ðƒ¤Ì5™‘Í#+´4V%vH˜†Ja^0kbk‘‚ÚPƒ< ÜPžŽ€šÔäqÞÈ)¹ŽÁw²Ó¹óg1ç×g>ÈhÑÕQ4eU=jbʯ,È_Ýäô¸r²ÉÂ}¼C8~è¾&à·e†9/958ãMHÀîí¹lÔŸeV¸¥˜…Bóïq-ÊŸ(Ó…ÓkTÄMAÔˆÎ!²¢‘÷z†ž‘ÇN͸<WjH@ˆ ×…sCaÆ—G#*5|zg¢ˆkLYŒ}ý»±†Ð^H`1…¯«ó£-²±db±'±(ùÎg<†½#*(É+€ZS{b”„ƒ$D÷ÁZE%³kZRmM÷z‘XDVBEGE„VìE—!F[L!‹3p„¸(·.(D€°B&´W¦#);#){róÈ¢‘¥H]2ÿ"ìCg~UËÐÞ!Õ‡vØ9Î,‘a0‘"HBê½ü;þˆt¾µ5*±c~qšï+‚‚ šþ8Û·áž*xp®ù¾¼o¤p€›6t†Ñth¼g#*`¡Þ»Ž¤Å½û9†³{[#‰AŽZHSÌ0Ѳ¨ ¦ÑÀ™,I6’åU¢›kÉo'žk».™gt¶ç;x«…×®óÊÛ”½ZÉY¦ëµd,Á*K4¢¬€“ŠœÍú®¡–fuHŠŽ¼d¦l‚®å,­"l¹‘f™¥­>Åìã›Ðz8½)a 1V=œÿÛKà6-h] s·1´†;&ælÐÉùgN¢ó#*‚˜ÏþÀìæÙ7ŽÀ6‘¹âQW"#)‘16ìã sôÔ…ß2½©g½Qfúö08D$Pu¨S @‘!2)#)Î&½Ü!0PGkT<†ìUZž,Ö–C¸œ„ÄŒ¥?ßãÃ;«¯»›h Wä§Ã0ü¯PÄ'xè(–¼e[Ì…´ÒУÆo8œJ-9—‡—:—ÂTãî8ùõ9ac©¡JñÝq›å.ÇòæBÀØhÓÇ=ÜD¡6“½ºgÑê× Ö}PQ: P€A©R†IJך'Œ|DÀ¸é,ü5øÃõóÑ_väŒVÈ éG/ —’DˆÁR)›I©%¥–˜Åd” ª¶-f&Л(©­1jÒmû­«Œæº5‹¡Üï7Ä6ÏôÇ*¹df¾ÝhØw¨‡»¦oñšœÇD´ùÐÛéêæ#+¹ù ­Ïo,8Ù‘dEÍŸ"6ONïãÒ{¶î( ìTìÚ{®™NB¼ÛfA$ŒÆŽ}÷âQê×s‡I*°Š74” +–Ô´½A#)ñ7ËáM´W®§!¦ÁjRÛCÍQÇH6Ù¦S¹3¿ @0ÔÝ47 G²•Ë¹,O6PjàaWDÙ6fÑ ëEƒ·ÂDÉ&FZеŒ)­ƒäuã˜á#*¤ÀÖRhIHnbí tqVùsF?+#+ØÕ:âõ9Ê\1¶ÔìÀÑ€#+ -–2[BU‹(nC- ¡u¡±ùõ»ÝÞˆ©ÁïT>1`!}î¦L˸Á,Å¥LPæìTkA¿ãV´0X³«Xpm/À°ûÙ­“c"úYXS“ù2áÐèÆÄ`:û=/0l–Q{Šä³»¥#+P]¿%¸ï,‰B#ÚC¿‘„æ²tôâø:—L¯#+‰Ðð0"0!Ò1sUb©Ó¸èºXøú'ÃÌ¡¯FÿAÈ Ä’K@9& *'IÞF/X¢£C^­óXIÑ{ÒˆPE¤ä2t#+ž!o~Ãêíßêý;{‰ÞbG?>£9±žfÈìó¾Ó‡"v‹R6–fï=aE½ž;o=J±(#+<Q ¿ŸÛ Þ'¸wó·öC{÷ÉG=__sn»³c蚀‡U…ËÛIvÍ|í¡£©ÜÇ5ô…¾ÒŠ‡ˆ7ùE6sq@ ]à½4l±:»5Š¯/¦Š0$2c2<]FÄF&]ÕÐHl™¦WϼÃöû¶¹W79ëÍçž»³„‘²RŠl†¢ònæºyM£mãs\·Æ+»·‹yåv•¤æ­¼É·ðV¡•v*Ÿ¼‘M‚@PÚe·gÏï;ü%U|Jøûlx˜/fzƒ¸’³TÂyšyßH)éP::ß4d#)„‘«(ÅIu´Þ=b!ŸQ ’A"ÈÂ)E’4²’©³jõùãQ¾î«÷wÌÒ#+4Cš¢¨Úi¦cm¯Çjvˆˆ&Il?“ `l!¯êF¡+aêä\X]O#aM°GmBB.ò}ª*øJõ¦î¹²)±¬-f¬QQ£fF+X©”[,ž¼ïm&µ*mJmcª” E.õ#)½¸¢&¯÷øþüÔôö“Úùüs0ëÈ’w«ÞMzÀQ“Ú>gÇeL|ù•5\……~—YˆAòdż:Ä{‰ì#*XÎH.yÌ„Ê/yèòÌq좫Ù»°±…!"ŠN3ä?Ít €ž&ny¤À˜ÞB Q¶Äb‘pû oMýw#+‚€Û@ÖóAÖ¡ñÉ‘0Št§µZ€¡ ½!BŠS¼|·WFšÔ–Û¤Ö©¶¥"”’ ÐdÓdù騠85¶Ù´˜U«)#*aJ€ë.•)ï)VE‡²Öe—Cý†¾Š¡÷GÇž4Æ!ûøkáaÃûŒ5†W§4°&âcÄ´LüVuÅ3Wch« ×ÒµÚ}èÁÆ€›M¢%Í 9ßjõ-òÀß&ÄÕ£è{Æ÷>rõÚ61ýT‘Ç+õ`ËÁ„{ ½?áWû@ ðWRC¤i„IŠSLJ“dj)lb¤Á¨ªM¶´oÕm¿#W‰°ÑZ(ÚM2€Ø•,ôôÛç[(ËD$#+B©‘„<ÔЫ |æØ @è*F…F&L(÷ÑMCfÒa`¡7ë@ïô}0ÏzíÙ/uÝ[imoçÔkWš±mF- Ú”5F„«6X‹X«RÆ „ø’3†:t6ù3·kiU–JHœËÆÿl}ð(€!£õÖX¦0Å.)E4¤»ÂƒžQFzžQ%Û馗àIôx$È«miGlþ'‡šYl홑#*€Å…„«‰9æí‚ÇûŸ= ‡ECˆ&íÝWMã­ô-Öi54R ±4›a‰ˆ’&¢ª€4‚É­éW%E¼ÝÖfì̪뛶¢®v ’Ùm^]ØÓDÚîêîêÒl©*dJlhV@U#*V$©ÉJ°ÁÀ·k¡¹o"o%i„^•ëˈµJil¦¤Ë*ô­uo:º¼ó­WŠŠÊ3LÛe²–¼îå»]ۢ˭—Mtšå®Üö©Ùš ì˜HVØF€íÄZ´`&ÍOÜ5åUY¡t¤X²nBøQ’ñ°˜){Z6gª TÛW9d¢ck?žô™KhÍ¡ø¤Ê AT.F¨’oÅ#)9 vŽÝÎpéÓ\MÎp,ôi‚Ér{Qv~×/มpõBK°‘îˆyü7‡Ç0ôźsÛéŒ I¼ž†Ú¶éƒ•)¶uF±±á¼·âBÝÚñË‘ñüȦF$Ç©CUR§WÊtÎÑoÎ÷¼QŒ-õx™˜zä)ØaöíådŠ(â¹E#8—…Á¾½Iâå¯â½„#)5:‘0î¡5$ÑÇÃ3U‘wà°þ̤—øæÍ 8;x_°jùœþ©+.ëIé­ìcìE±<@yr#+RÕ!®*F<¾ÀÖ±ÛU‹foÕñŠÈ%£h¥G|6×U:žóPj” ~VoÜv•#*ñÞpÅ`×L‹ÝŸ¦žl“v›³ôhœKxP¼¿„³4pcoGê>ÖìâÌ ç7$ä5¦-XêI0TR'·!À4¶ª/pÖšÄëj hêa×»[0Qüá‘Élc·É#+Ù]©´±':µ@¶¤YzêÛÓ\ñJmå5ÓuÖÜŽWC%Û‡Ôq ä<e~äe"hóƒÉK©8>YãÉÈ[9Ï|Çîù¹‚Öè|l‡pYyL(7Æ|5NˆÞöÁò<-šX ýî 8 M[‡Ñó.8#‰1YÚˆËÁäŒpÃ÷2\µ†{h$ìŠùÕ­¼›Pnh Òþ¨6±ØEì’ºQÅæFÜn‹hÔ*ëǨïÆ…@ãƒ[8AÀÓ Êö;÷—çAÇÒ!"1OAªs¶Ü±2ÒȤ¦Qhšät Á‡-u1ßp–dl0Ày±»o¶£ï=(ð1Žäœl=¦¢ûEè9œG™È"¸=г™Úxû´2 os+:ίmåm=ÚŒí¯Êöý¾Ûgâç«TO®àd ¤úñ´ ýñ3ød„L¨-é¡ršCp…@C·Qü…Šã~Ï4ÉPUµƒÌóOftë#)OfYvë35Ãǵ^úUsb¡2ýë#) ?ýÉ$ÿâR†V¼wÎÕާцRÄÜá™íúN±LL§#)¤p`óõHŠo!Pv‘#*%η°S+VEÀö ´2²Z¿@CbŽïQnóí¿—ŸØ÷;º‰$ 33ç­ò¶U¿ÕwUðï$rφB@;3Õ©.í6ˆkC’2tBÚ9DmžG}ü/dp¬°‚uÇfý©é´ ¹ª¡mÒñ#*ÖâƒÕè'º/ÐŒÊ{†qäW„bA€µ|îÌ3ñ‹À;:hž÷d\:_ìÈ`Ï6l€Ctè~+—Ú³õ_Ëðò=íC‡’íLáCáÂ2éJêóøCxŠ#+,]d†'ŠD$í¯”‡oóUËrOü#*Ê7ãKýšÝe¥‰ˆL‡1Ò·Yàþs#5¯×Íà {R¦¤ˆo½)ž:Zef—Y#+¡¦†#+]”#*‡—šTÁŒ0ÙÿŠeTò´o[x÷ìÒÁ§îS¯sÅ#;µ^ûlÝÃL++UŒº¢X9aðiæ̦õ@£µ­»G‡Ù¹4Ñ#)fÜkc•AšŠŽ°lÄa¦DÑ!vÕI„œ^$ö–l,—rA‚C šÔ6ú¼ú¼bÓ›Å#gw9h‚Ð„8ƒ+Þ^è†{Í:‡² ¾~YŒ£ø&l&Ï6ì¹°ÙB¸©˜°w‰$Y#‹\ž{bò#)c.4£™èÇB#*ùLÏÇL<2t)«zׂƒ}vo  …iÒ64k…³)Ü#+fì‰ÓÇL}2›îöB±³è h3‡œ^i IäqÝôac·F³U1]é.`AHŒIàˆ˜z†|ØK¸RCd0UÊñóüy=ûŸ#*¸ax1¥wfLò¹ƒ¶à ‹ÏÛ³¯]×8óÔ,glÍ+P=X¢#+• ðÝÓÂâ™vÝf``3(HÈ&½®épØæ&‘V† ÷‹ „Pð:¢üìØPÄ‘@$HÁBHË´e!ÅÞAyð–á(8caÆ—Ð0û¿ ¸ŒÌáÿ{M Âüì;Œ¹Yݲ@XŒ DÀ'-t¨n`ºô6îé­btÂ#+D„Õ´¤LjÄÔJƒlkY¥3[FÔm’¨ÌÒDÅ…‘S_;¶®Ú¨Ï::"füO˜Še#)úȇß#)/ΪZŠ·,Rزs=r€Ë‰M@±=ý?ÝãÉùÏÍ##*"¿L·¾k]O¥×¬Á¡ðcS9öɬצåU1Š#€•sóèèéüÍäÛ­\ç•pè-Ü—uñËþëû¨mŸPÌïð€^¡Ë‹Žðl¤ïjoEwÊ [å%®ð†=Ç•N›æ7©¥<X?Íí럨N#+3ÉšgY,ØÕL#+š<³}<*¶j8# ©‹#*D'ÒáG½D᡽ÑqIâJò5–¿=7§ÈÙmJS{¦8r›W4 °RlÉ8²¢E…¡E]o¾×1±hø6Ü­ŒW¶ô£W²Õ½MTš6‚Õ{æµÌ@iªß ¦­ë³°X6*<0Ý0HF ¤22OŒ#+÷¼7q²§ ¸q“¨(ÖêvعÜ8.ÝP³#+ЩÝ˸ǚ7áUÁbÇã*J9…¨DôÿÞ‹öA]Kƒ\‘dFnÅ+óýS·»êez=(îGSìä/"Œ ýl¸¨yͬPþÈŒŠ,X mv£¸íÔP§6kk%µ2šé­Õ«³jú–&ÍI¯WѪ×Û$ѨªRÀäw³ÝðÌòúPÿ­õ)ª)£¿§ÑÑ;ü%c¦¦eò²Ó÷=g0@ÂfóxÈ{˜}íˆÈÿgÞ—‡ßýÀ€í €âïÂ&¼l=PH,Š#+,Ÿçò„Zÿ·žœÿ¾U'ú?¯"‡=d12„÷ÿÇô½>í·ñ6éx¹h?ÃÈþßëô~§iô/Ï㌻#*<Ú+:³qM:ãÉïøuЧ·Îä÷„ùG²y‹%ÞŸ_ŸˆàNøŒ÷§þV'—ˆÕûÄOA Qä $»ùiwDüeàp#+?dYðêÉ×ÿ6•occ-3{(WQ6éÿ›Ø#³ÞUßæf'üuâ¥Ä”ù6Qû*Qþå³Ò»xœULà›çªáq–s¦ò8‡ÇÕ´éÛD è2²±ûye‰¼#pÅå°–7áá'ו´æ .ÇŸ ÁȬ2LMf¬yÙpx`HŠ$Y@zõïÔ=²Ù j‰ýÞÍÏ«_ŸûŸ©®E=Tg6(‡ÚgÝ-UŒF7°;·m¾Ïö™m÷Ž_—øy ¨ÿÿrE8P#*s†
#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAl3aW3cACgkQSbTGfAUn\neqruqA//Y9oJ46ZR8W7YB/e45bfrYxGbN7NnkvkwSPNziObYur+n1QpQEOaPTn/U\n5kFtPWHXRJzaG/A9poKn7pl1Xd7Edcu1aalfoEazZbuD37VOxIp9lnrefCAeICqj\nGv0SD96Zac91CbA+b20Q4xnqxKMi3LSI4NPjfFGy62FkSk3MS4p6Rdp0/WAKwwNj\nw7WEjQCNmLb37z+FGSzXg28aljYeteBZEthsVmGJ5QqVwMBwgj2+y5FOTzFfxmqB\nrWgjFYS0l85kgYRZv9yzdNmFs5SScwafwpT8Xmdr49tFn/+0LxXyRxX+rdODgrpV\nY4EOiQz0fd6mMMnaTDXlLSXls3JyVYmbTjeNL/9gcHmnStzJ851CJQfyQg7A+JoC\nc7nz0HbiFyTgB+PUZr1OhGj3A7287o8XQ0tqR3oa7jXIOX0OynrGplMQKr++0jE1\nBgKzjLoE9CTbjkQfICLG+aUy3S1ZyDk/BcO+5+Ytbru+qXuDsIgAdVosMfNSv9jJ\nXvOINsbRMekdejYMZv8fIkn5OEjCFHVhNpobEsCb768bjB3p7alQGECBvjHCm6dy\nXZPzl9cBMWIXcBjPTS+GZj+PIXGcu76pbsx6HBHWf+uJ+4xgOsUCVu//0AV09jvA\n0MjtLWwQ8mdRH6Wt4hsp4HKtSvQrhmljf2OnuYBgaFmcdJkN1zI=\n=C0oT\n-----END PGP SIGNATURE-----\n
+#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECzlystnjLqtCPS4PIr4MYv+/pUgFAmOxj14ACgkQIr4MYv+/\npUgg7A//X6agCAo9x3REEDFk4GTbW8nMkH7gD+ixNnMNcDF96pmp1f3Qdkm4DU19\nscpa1IPN5ik9xeU4Xs7+SifSlJrT1h9gj2bCFIPWWVlXjCXbn52mU4lijQ4iyaV7\nwLuD4ebG8UU9QK5gP1U2hERdTbkuOxuhQijTijDpfOuUnR1N5YoZmONszgYvYmAL\nr7Zr+Cuc+0HiEdsgMUabp1LTyY+urkZJoHcOTMe0QoyrTvM/CFgB4V3ppw9006VQ\nV4XdoAXEkNNfdJUE1s+4SrQxWT2AC2n9X7vjxbqNwfglzrujf862JrloOjybffdg\ngMPXwLpfT8TrVHFSs5aQK++8YhgwcgDz4Biqt/aq68iM424XnUYSwkzZTowGM/A7\nhyM7Jh+Mp/Of10pIx6PBbjjhbh3dH4OUtFv48uYLh1V95ICQ538UypMfBOK9mEPL\n8zpyTbQ8U873Ri+awOcpoJ4738gZNHAbEHZ3Ctr+g7eoayNlFMENG8bMrLwOkiUQ\nLZVXzNPtPs/OFe0rYu6okVONs8MLmuArnIy7v21Ti73Dtc4ABP6V4CFfc3AGqO+l\n3SmEb5r31h+iXvc4vdp33tESohxUFosIgMGjD8ZMyVFWy78wgp5I9qSbSPdVhl60\nF8WGX/ydvcf6D8NDZqjrsRyFtxKn6xLRQgUI+6DKsNLwRzJBVas=\n=mYk7\n-----END PGP SIGNATURE-----\n
diff --git a/waf_libbsd.py b/waf_libbsd.py
index dd391191..81818956 100644
--- a/waf_libbsd.py
+++ b/waf_libbsd.py
@@ -2,7 +2,7 @@
"""LibBSD build configuration to waf integration module.
"""
-# Copyright (c) 2015, 2021 Chris Johns <chrisj@rtems.org>. All rights reserved.
+# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. All rights reserved.
#
# Copyright (c) 2009, 2015 embedded brains GmbH. All rights reserved.
#
@@ -62,13 +62,6 @@ def _add_flags_if_not_present(current_flags, addional_flags):
if flag not in current_flags:
current_flags.append(flag)
-def _remove_bsp_include_path(bsp_include_path, current_flags):
- # this does not handle quted strings; maybe needed
- for bsp_path in bsp_include_path:
- if bsp_path in current_flags:
- current_flags = [flag for flag in current_flags if flag != bsp_path]
- return current_flags
-
#
# The waf builder for libbsd.
#
@@ -194,16 +187,10 @@ class Builder(builder.ModuleManager):
conf.env['HAVE_%s' % l.upper()] = True
else:
bld.fatal('invalid config test: %s' % (configTest))
- section_flags = ["-fdata-sections", "-ffunction-sections"]
- _add_flags_if_not_present(conf.env.CFLAGS, section_flags)
- _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags)
- _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"])
- conf.env.CFLAGS = _remove_bsp_include_path(conf.env.IFLAGS,
- conf.env.CFLAGS)
- conf.env.CXXFLAGS = _remove_bsp_include_path(conf.env.IFLAGS,
- conf.env.CXXFLAGS)
- conf.env.LINKFLAGS = _remove_bsp_include_path(conf.env.IFLAGS,
- conf.env.LINKFLAGS)
+ section_flags = ["-fdata-sections", "-ffunction-sections"]
+ _add_flags_if_not_present(conf.env.CFLAGS, section_flags)
+ _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags)
+ _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"])
def build(self, bld):
#
@@ -250,7 +237,7 @@ class Builder(builder.ModuleManager):
inc_paths = sorted(include_paths)
inc_paths.remove('build')
inc_paths.remove('cpu')
- includes = { 'bsp': [p[2:] for p in bld.env.IFLAGS] }
+ includes = {}
for inc in inc_paths:
includes[inc] = include_paths[inc]
# cpu include paths must be the first searched
@@ -445,7 +432,7 @@ class Builder(builder.ModuleManager):
bld.objects(target='kvmsymbols',
features='c',
cflags=cflags,
- includes=kvmsymbols_includes + includes['kernel'] + includes['bsp'],
+ includes=kvmsymbols_includes + includes['kernel'],
source=kvmsymbols['files']['all']['default'][0])
libbsd_use += ["kvmsymbols"]
@@ -500,7 +487,7 @@ class Builder(builder.ModuleManager):
bld.objects(target='lex_%s' % (lex['sym']),
features='c',
cflags=cflags,
- includes=lexIncludes + includes['user'] + includes['bsp'],
+ includes=lexIncludes + includes['user'],
defines=defines + lexDefines,
source=lex['file'][:-2] + '.c')
libbsd_use += ['lex_%s' % (lex['sym'])]
@@ -540,7 +527,7 @@ class Builder(builder.ModuleManager):
bld.objects(target='yacc_%s' % (yaccSym),
features='c',
cflags=cflags,
- includes=yaccIncludes + includes['user'] + includes['bsp'],
+ includes=yaccIncludes + includes['user'],
defines=defines + yaccDefines,
source=yaccFile[:-2] + '.c')
libbsd_use += ['yacc_%s' % (yaccSym)]
@@ -574,7 +561,7 @@ class Builder(builder.ModuleManager):
cflags=cflags + bld_cflags,
cxxflags=cxxflags,
includes=sorted(build.get('includes', [])) +
- includes[space] + includes['bsp'],
+ includes[space],
defines=defines,
source=bld_sources)
libbsd_use += [target]
@@ -594,7 +581,7 @@ class Builder(builder.ModuleManager):
features='c cxx',
cflags=cflags,
cxxflags=cxxflags,
- includes=includes['kernel'] + includes['bsp'],
+ includes=includes['kernel'],
defines=defines,
source=bld_sources,
use=libbsd_use)
@@ -671,7 +658,7 @@ class Builder(builder.ModuleManager):
bld.program(target='%s.exe' % (testName),
features='cprogram',
cflags=cflags,
- includes=includes['user'] + includes['bsp'],
+ includes=includes['user'],
source=test_sources,
use=['bsd'],
lib=libs,
diff --git a/wscript b/wscript
index bfdcc91c..7b8f4d31 100644
--- a/wscript
+++ b/wscript
@@ -170,10 +170,6 @@ def options(opt):
def bsp_configure(conf, arch_bsp):
conf.check(header_name="dlfcn.h", features="c")
conf.check(header_name="rtems/pci.h", features="c", mandatory=False)
- if not rtems.check_posix(conf):
- conf.fatal(
- "RTEMS kernel POSIX support is disabled; configure RTEMS with --enable-posix"
- )
if rtems.check_networking(conf):
conf.fatal(
"RTEMS kernel contains the old network support;" \