summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md327
-rw-r--r--CONTRIBUTING.rst736
-rw-r--r--Makefile.todo1
-rw-r--r--README.md294
-rw-r--r--README.rst853
-rwxr-xr-xbuilder.py870
-rw-r--r--buildset/default.ini3
-rw-r--r--dhcpcd/dhcpcd.c2
m---------freebsd-org0
-rwxr-xr-xfreebsd-to-rtems.py53
-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/crypto/md5.h4
-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_parser-data.h1
-rw-r--r--freebsd/sbin/ping/ping.c8
-rw-r--r--freebsd/sbin/ping6/ping6.c27
-rw-r--r--freebsd/sys/arm/freescale/imx/imx6_ccm.c2
-rw-r--r--freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c305
-rw-r--r--freebsd/sys/arm/ti/ti_pinmux.c463
-rw-r--r--freebsd/sys/arm/ti/ti_pinmux.h80
-rw-r--r--freebsd/sys/arm64/arm64/in_cksum.c243
-rw-r--r--freebsd/sys/arm64/include/machine/armreg.h665
-rw-r--r--freebsd/sys/arm64/include/machine/cpu.h203
-rw-r--r--freebsd/sys/arm64/include/machine/cpufunc.h153
-rw-r--r--freebsd/sys/arm64/include/machine/in_cksum.h52
-rw-r--r--freebsd/sys/contrib/ck/include/ck_pr.h2
-rw-r--r--freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h352
-rw-r--r--freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h298
-rw-r--r--freebsd/sys/dev/e1000/if_em.c6
-rw-r--r--freebsd/sys/dev/ffec/if_ffec.c139
-rw-r--r--freebsd/sys/dev/mmc/mmcsd.c10
-rw-r--r--freebsd/sys/dev/usb/controller/dwc_otg.c64
-rw-r--r--freebsd/sys/dev/usb/controller/dwc_otg.h3
-rw-r--r--freebsd/sys/dev/usb/controller/ehci_pci.c593
-rw-r--r--freebsd/sys/dev/usb/usb_pci.h (renamed from freebsd/sys/arm/ti/am335x/am335x_scm_padconf.h)36
-rw-r--r--freebsd/sys/i386/include/machine/bus.h6
-rw-r--r--freebsd/sys/i386/include/machine/intr_machdep.h6
-rw-r--r--freebsd/sys/i386/include/machine/legacyvar.h63
-rw-r--r--freebsd/sys/i386/include/machine/specialreg.h6
-rw-r--r--freebsd/sys/moxie/include/machine/in_cksum.h83
-rw-r--r--freebsd/sys/net/iflib.c6827
-rw-r--r--freebsd/sys/net/iflib_private.h70
-rw-r--r--freebsd/sys/net/mp_ring.c554
-rw-r--r--freebsd/sys/net/mp_ring.h75
-rw-r--r--freebsd/sys/or1k/include/machine/in_cksum.h83
-rw-r--r--freebsd/sys/sys/_domainset.h2
-rw-r--r--freebsd/sys/sys/callout.h6
-rw-r--r--freebsd/sys/sys/domainset.h52
-rw-r--r--freebsd/sys/x86/include/machine/specialreg.h (renamed from rtemsbsd/include/x86/specialreg.h)0
-rw-r--r--freebsd/sys/x86/include/machine/x86_var.h145
-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--freebsd/usr.sbin/ifmcstat/ifmcstat.c1295
-rw-r--r--freebsd/usr.sbin/ifmcstat/printb.c75
-rw-r--r--freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h4
-rw-r--r--freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h7
-rw-r--r--freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h5
-rw-r--r--freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h4
-rw-r--r--ipsec-tools/src/libipsec/pfkey.c17
-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.h9
-rw-r--r--ipsec-tools/src/racoon/session.c60
-rw-r--r--libbsd.py662
-rw-r--r--libbsd.txt1331
-rw-r--r--rtemsbsd/arm/include/arm/lpc/probe.h43
-rw-r--r--rtemsbsd/include/bsp/mv643xx_eth.h397
-rw-r--r--rtemsbsd/include/bsp/nexus-devices.h67
-rw-r--r--rtemsbsd/include/bsp/st-sdmmc-config.h71
-rw-r--r--rtemsbsd/include/machine/_kernel_if.h14
-rw-r--r--rtemsbsd/include/machine/_kernel_socket.h1
-rw-r--r--rtemsbsd/include/machine/frame.h1
-rwxr-xr-xrtemsbsd/include/machine/rtems-bsd-cache.h2
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-commands.h2
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h5
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-space.h6
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-nexus-bus.h49
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-program.h8
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-user-space.h4
-rwxr-xr-xrtemsbsd/include/rtems/bsd/bsd.h22
-rw-r--r--rtemsbsd/include/rtems/netcmds-config.h4
-rw-r--r--rtemsbsd/include/x86/bus.h1109
-rw-r--r--rtemsbsd/include/x86/x86_var.h146
-rw-r--r--rtemsbsd/nfsclient/nfs.c141
-rw-r--r--rtemsbsd/powerpc/include/linux/crc32.h2
-rw-r--r--rtemsbsd/powerpc/include/linux/genalloc.h2
-rw-r--r--rtemsbsd/powerpc/include/machine/legacyvar.h2
-rw-r--r--rtemsbsd/powerpc/include/machine/pci_cfgreg.h2
-rw-r--r--rtemsbsd/pppd/rtemspppd.c2
-rw-r--r--rtemsbsd/rtems/program-internal.h7
-rw-r--r--rtemsbsd/rtems/rtems-bsd-cxx.cc1
-rw-r--r--rtemsbsd/rtems/rtems-bsd-init-dhcp.c5
-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-ifmcstat.c36
-rw-r--r--rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c2
-rw-r--r--rtemsbsd/rtems/rtems-kernel-epoch.c43
-rw-r--r--rtemsbsd/rtems/rtems-kernel-init.c4
-rw-r--r--rtemsbsd/rtems/rtems-kernel-thread.c11
-rw-r--r--rtemsbsd/rtems/rtems-program.c51
-rw-r--r--rtemsbsd/rtems/rtems-routes.c4
-rw-r--r--rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c4
-rw-r--r--rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c227
-rwxr-xr-xrtemsbsd/sys/arm/lpc/if_lpe.c2855
-rw-r--r--rtemsbsd/sys/arm/lpc/if_lpereg.h210
-rw-r--r--rtemsbsd/sys/dev/atsam/if_atsam.c1170
-rw-r--r--rtemsbsd/sys/dev/iicbus/rtems-i2c.c8
-rw-r--r--rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c12
-rw-r--r--rtemsbsd/sys/dev/mmc/st-sdmmc-config.c52
-rw-r--r--rtemsbsd/sys/dev/mmc/st-sdmmc.c859
-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/nvd/nvd.c37
-rw-r--r--rtemsbsd/sys/dev/stmac/if_stmac.c1009
-rw-r--r--rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c166
-rw-r--r--rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c88
-rwxr-xr-xrtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c17
-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--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
-rw-r--r--testsuite/include/rtems/bsd/test/default-network-init.h4
-rw-r--r--testsuite/loopback01/test_main.c4
-rw-r--r--testsuite/mcast01/test_main.c286
-rw-r--r--testsuite/media01/pattern-test.c337
-rw-r--r--testsuite/media01/pattern-test.h44
-rw-r--r--testsuite/media01/test_main.c11
-rw-r--r--testsuite/netshell01/test_main.c2
-rw-r--r--testsuite/nfs01/test_main.c3
-rw-r--r--testsuite/openssl02/test_main.c2
-rw-r--r--testsuite/pf02/test_main.c7
-rw-r--r--testsuite/ppp01/test_main.c5
-rw-r--r--testsuite/tcpdump01/test_main.c292
-rw-r--r--testsuite/telnetd01/test_main.c5
-rw-r--r--testsuite/thread01/test_main.c5
-rw-r--r--testsuite/usbkbd01/init.c4
-rw-r--r--testsuite/usbmouse01/init.c4
-rw-r--r--testsuite/zerocopy01/test_main.c5
-rwxr-xr-xwaf16
-rw-r--r--waf_libbsd.py445
-rw-r--r--wscript217
159 files changed, 25207 insertions, 7408 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index f2e5da21..00000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,327 +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.
-
-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 `./create-kernel-namespace.sh` if you imported kernel space headers. Add only your new defines via `git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h`.
-* 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.
- `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:
- ```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..0b6fc7a0
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,736 @@
+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.
+
+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 ``./create-kernel-namespace.sh`` if you imported kernel space headers. Add only your new defines via ``git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h``.
+* 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 6822e812..9e6084ed 100644
--- a/Makefile.todo
+++ b/Makefile.todo
@@ -95,6 +95,7 @@ program-header:
cd freebsd/tools/tools/net80211/wlanstats && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/tools/tools/net80211/wlanstats/*.o -p wlanstats
cd freebsd/usr.bin/netstat && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.bin/netstat/*.o -p netstat
cd freebsd/usr.sbin/arp && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.sbin/arp/*.o -p arp
+ cd freebsd/usr.sbin/ifmcstat && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.sbin/ifmcstat/*.o -p ifmcstat
cd ipsec-tools/src/racoon && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/ipsec-tools/src/racoon/*.o -p racoon
cd ipsec-tools/src/setkey && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/ipsec-tools/src/setkey/*.o -p setkey
diff --git a/README.md b/README.md
deleted file mode 100644
index 7794b12b..00000000
--- a/README.md
+++ /dev/null
@@ -1,294 +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-default/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-default/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.
-
-Branches
---------
-
-* master - branch 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 - branch 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 - 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 - branch belongs to the RTEMS 5 release. It is based on a FreeBSD
- development version.
-
-* freebsd-9.3 - 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 - branch for the RTEMS 4.11 release series. This branch is
- unmaintained. It is recommended to update to RTEMS 5 or 6.
-
-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.
-
-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:
-
-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
-```
-
-To enable kernel internal consistency checking use:
-
-```
---freebsd-options=invariants,invariant_support
-```
-
-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. You have to create a TAP virtual Ethernet
-interface for this:
-
-```
-sudo tunctl -p -t qtap -u $(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:
-
-```
-$ 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:
-
-```
-qemu-system-arm -serial null -serial mon:stdio -nographic \
- -M xilinx-zynq-a9 -m 256M \
- -net tap,ifname=qtap,script=no,downscript=no \
- -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:12 \
- -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/media01.exe
-```
-
-After some seconds it will acquire a IPv4 link-local address, e.g.
-
-```
-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:
-
-```
-$ 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 [/] #
-```
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..9b328078
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,853 @@
+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
+
+To enable kernel internal consistency checking use:
+
+.. code-block:: none
+
+ --freebsd-options=invariants,invariant_support
+
+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 0eda461f..b43eb618 100755
--- a/builder.py
+++ b/builder.py
@@ -1,7 +1,10 @@
+# SPDX-License-Identifier: BSD-2-Clause
+"""Manage the libbsd build configuration data.
+"""
+
+# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. All rights reserved.
#
-# Copyright (c) 2015-2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
-#
-# Copyright (c) 2009, 2017 embedded brains GmbH. All rights reserved.
+# Copyright (c) 2009, 2017 embedded brains GmbH. All rights reserved.
#
# embedded brains GmbH
# Dornierstr. 4
@@ -9,42 +12,40 @@
# Germany
# <info@embedded-brains.de>
#
-# Copyright (c) 2012 OAR Corporation. 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.
#
-# 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.
-
-# FreeBSD: http://svn.freebsd.org/base/releng/8.2/sys (revision 222485)
+# 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.
from __future__ import print_function
-import shutil
+import codecs
+import copy
+import difflib
import os
import re
import sys
-import getopt
-import filecmp
-import difflib
-import codecs
-import copy
+
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
#
# Global controls.
@@ -60,31 +61,22 @@ filesTotal = 0
filesTotalLines = 0
filesTotalInserts = 0
filesTotalDeletes = 0
-diffDetails = { }
+diffDetails = {}
verboseInfo = 1
verboseDetail = 2
verboseMoreDetail = 3
verboseDebug = 4
-def _cflagsIncludes(cflags, includes):
- if type(cflags) is not list:
- if cflags is not None:
- _cflags = cflags.split(' ')
- else:
- _cflags = [None]
- else:
- _cflags = cflags
- if type(includes) is not list:
- _includes = [includes]
- else:
- _includes = includes
- return _cflags, _includes
-
-def verbose(level = verboseInfo):
+BUILDSET_DIR = "buildset"
+BUILDSET_DEFAULT = "buildset/default.ini"
+
+
+def verbose(level=verboseInfo):
return verboseLevel >= level
-def changedFileSummary(statsReport = False):
+
+def changedFileSummary(statsReport=False):
global filesTotal, filesTotalLines, filesTotalInserts, filesTotalDeletes
@@ -107,18 +99,25 @@ def changedFileSummary(statsReport = False):
#
# Sort by opacity.
#
- ordered_diffs = sorted(diffDetails.items(), key = lambda diff: diff[1].opacity, reverse = True)
+ ordered_diffs = sorted(diffDetails.items(),
+ key=lambda diff: diff[1].opacity,
+ reverse=True)
for f in ordered_diffs:
print(' %s' % (diffDetails[f[0]].status()))
+
def readFile(name):
try:
- contents = codecs.open(name, mode = 'r', encoding = 'utf-8', errors = 'ignore').read()
+ contents = codecs.open(name,
+ mode='r',
+ encoding='utf-8',
+ errors='ignore').read()
except UnicodeDecodeError as ude:
print('error: reading: %s: %s' % (name, ude))
sys.exit(1)
return contents
+
def writeFile(name, contents):
path = os.path.dirname(name)
if not os.path.exists(path):
@@ -128,11 +127,13 @@ def writeFile(name, contents):
print('error: cannot create directory: %s: %s' % (path, oe))
sys.exit(1)
try:
- codecs.open(name, mode = 'w', encoding = 'utf-8', errors = 'ignore').write(contents)
+ codecs.open(name, mode='w', encoding='utf-8',
+ errors='ignore').write(contents)
except UnicodeDecodeError as ude:
print('error: write: %s: %s' % (name, ude))
sys.exit(1)
+
#
# A builder error.
#
@@ -140,11 +141,14 @@ class error(Exception):
"""Base class for exceptions."""
def __init__(self, msg):
self.msg = 'error: %s' % (msg)
+
def set_output(self, msg):
self.msg = msg
+
def __str__(self):
return self.msg
+
#
# Diff Record
#
@@ -158,7 +162,8 @@ class diffRecord:
self.inserts = inserts
self.deletes = deletes
self.changes = inserts + deletes
- self.opacity = (float(self.changes) / (self.lines + self.changes)) * 100.0
+ self.opacity = (float(self.changes) /
+ (self.lines + self.changes)) * 100.0
def __repr__(self):
return self.src
@@ -167,36 +172,50 @@ class diffRecord:
return 'opacity:%5.1f%% edits:%4d (+):%-4d (-):%-4d %s' % \
(self.opacity, self.changes, self.inserts, self.deletes, self.src)
+
#
# This stuff needs to move to libbsd.py.
#
+
# Move target dependent files under a machine directory
def mapCPUDependentPath(path):
- return path.replace("include/", "include/machine/")
+ return path.replace("include/", "include/machine/")
+
def fixIncludes(data):
- data = re.sub('#include <sys/resource.h>', '#include <rtems/bsd/sys/resource.h>', data)
- data = re.sub('#include <sys/unistd.h>', '#include <rtems/bsd/sys/unistd.h>', data)
+ data = re.sub('#include <sys/resource.h>',
+ '#include <rtems/bsd/sys/resource.h>', data)
+ data = re.sub('#include <sys/unistd.h>',
+ '#include <rtems/bsd/sys/unistd.h>', data)
return data
+
# revert fixing the include paths inside a C or .h file
def revertFixIncludes(data):
- data = re.sub('#include <rtems/bsd/', '#include <', data)
- data = re.sub('#include <util.h>', '#include <rtems/bsd/util.h>', data)
- data = re.sub('#include <bsd.h>', '#include <rtems/bsd/bsd.h>', data)
- data = re.sub('#include <zerocopy.h>', '#include <rtems/bsd/zerocopy.h>', data)
- data = re.sub('#include <modules.h>', '#include <rtems/bsd/modules.h>', data)
+ data = re.sub('#include <rtems/bsd/', '#include <', data)
+ data = re.sub('#include <util.h>', '#include <rtems/bsd/util.h>', data)
+ data = re.sub('#include <bsd.h>', '#include <rtems/bsd/bsd.h>', data)
+ data = re.sub('#include <zerocopy.h>', '#include <rtems/bsd/zerocopy.h>',
+ data)
+ data = re.sub('#include <modules.h>', '#include <rtems/bsd/modules.h>',
+ data)
return data
+
# fix include paths inside a C or .h file
def fixLocalIncludes(data):
- data = re.sub('#include "opt_([^"]*)"', '#include <rtems/bsd/local/opt_\\1>', data)
- data = re.sub('#include "([^"]*)_if.h"', '#include <rtems/bsd/local/\\1_if.h>', data)
- data = re.sub('#include "miidevs([^"]*)"', '#include <rtems/bsd/local/miidevs\\1>', data)
- data = re.sub('#include "usbdevs([^"]*)"', '#include <rtems/bsd/local/usbdevs\\1>', data)
+ data = re.sub('#include "opt_([^"]*)"',
+ '#include <rtems/bsd/local/opt_\\1>', data)
+ data = re.sub('#include "([^"]*)_if.h"',
+ '#include <rtems/bsd/local/\\1_if.h>', data)
+ data = re.sub('#include "miidevs([^"]*)"',
+ '#include <rtems/bsd/local/miidevs\\1>', data)
+ data = re.sub('#include "usbdevs([^"]*)"',
+ '#include <rtems/bsd/local/usbdevs\\1>', data)
return data
+
# revert fixing the include paths inside a C or .h file
def revertFixLocalIncludes(data):
data = re.sub('#include <rtems/bsd/local/([^>]*)>', '#include "\\1"', data)
@@ -211,42 +230,48 @@ def assertHeaderFile(path):
print("*** Move it to a C source file list")
sys.exit(2)
+
def assertSourceFile(path):
if path[-2:] != '.c' and path[-2:] != '.S' and path[-3:] != '.cc':
print("*** " + path + " does not end in .c, .cc or .S")
print("*** Move it to a header file list")
sys.exit(2)
+
def assertHeaderOrSourceFile(path):
if path[-2] != '.' or (path[-1] != 'h' and path[-1] != 'c'):
print("*** " + path + " does not end in .h or .c")
print("*** Move it to another list")
sys.exit(2)
+
def diffSource(dstLines, srcLines, src, dst):
global filesTotal, filesTotalLines, filesTotalInserts, filesTotalDeletes
#
# Diff, note there is no line termination on each string. Expand the
# generator to list because the generator is not reusable.
#
- diff = list(difflib.unified_diff(dstLines,
- srcLines,
- fromfile = src,
- tofile = dst,
- n = 5,
- lineterm = ''))
+ diff = list(
+ difflib.unified_diff(dstLines,
+ srcLines,
+ fromfile=src,
+ tofile=dst,
+ n=5,
+ lineterm=''))
inserts = 0
deletes = 0
if len(diff) > 0:
if src in diffDetails and \
diffDetails[src].dst != dst and diffDetails[src].diff != diff:
- raise error('repeated diff of file different: src:%s dst:%s' % (src, dst))
+ raise error('repeated diff of file different: src:%s dst:%s' %
+ (src, dst))
for l in diff:
if l[0] == '-':
deletes += 1
elif l[0] == '+':
inserts += 1
- diffDetails[src] = diffRecord(src, dst, srcLines, diff, inserts, deletes)
+ diffDetails[src] = diffRecord(src, dst, srcLines, diff, inserts,
+ deletes)
#
# Count the total files, lines and the level of changes.
@@ -258,6 +283,7 @@ def diffSource(dstLines, srcLines, src, dst):
return diff
+
#
# Converters provide a way to alter the various types of code. The conversion
# process filters a file as it is copies from the source path to the
@@ -265,8 +291,12 @@ def diffSource(dstLines, srcLines, src, dst):
# source.
#
class Converter(object):
-
- def convert(self, src, dst, hasSource = True, sourceFilter = None, srcContents = None):
+ def convert(self,
+ src,
+ dst,
+ hasSource=True,
+ sourceFilter=None,
+ srcContents=None):
global filesProcessed, filesProcessedCount
@@ -337,10 +367,12 @@ class Converter(object):
for l in diff:
print(l)
+
class NoConverter(Converter):
- def convert(self, src, dst, hasSource = True, sourceFilter = None):
+ def convert(self, src, dst, hasSource=True, sourceFilter=None):
return '/* EMPTY */\n'
+
class FromFreeBSDToRTEMSHeaderConverter(Converter):
def sourceFilter(self, data):
data = fixLocalIncludes(data)
@@ -349,7 +381,8 @@ class FromFreeBSDToRTEMSHeaderConverter(Converter):
def convert(self, src, dst):
sconverter = super(FromFreeBSDToRTEMSHeaderConverter, self)
- sconverter.convert(src, dst, sourceFilter = self.sourceFilter)
+ sconverter.convert(src, dst, sourceFilter=self.sourceFilter)
+
class FromFreeBSDToRTEMSUserSpaceHeaderConverter(Converter):
def sourceFilter(self, data):
@@ -358,7 +391,8 @@ class FromFreeBSDToRTEMSUserSpaceHeaderConverter(Converter):
def convert(self, src, dst):
sconverter = super(FromFreeBSDToRTEMSUserSpaceHeaderConverter, self)
- sconverter.convert(src, dst, sourceFilter = self.sourceFilter)
+ sconverter.convert(src, dst, sourceFilter=self.sourceFilter)
+
class FromFreeBSDToRTEMSSourceConverter(Converter):
def sourceFilter(self, data):
@@ -369,7 +403,8 @@ class FromFreeBSDToRTEMSSourceConverter(Converter):
def convert(self, src, dst):
sconverter = super(FromFreeBSDToRTEMSSourceConverter, self)
- sconverter.convert(src, dst, sourceFilter = self.sourceFilter)
+ sconverter.convert(src, dst, sourceFilter=self.sourceFilter)
+
class FromFreeBSDToRTEMSUserSpaceSourceConverter(Converter):
def sourceFilter(self, data):
@@ -379,7 +414,8 @@ class FromFreeBSDToRTEMSUserSpaceSourceConverter(Converter):
def convert(self, src, dst):
sconverter = super(FromFreeBSDToRTEMSUserSpaceSourceConverter, self)
- sconverter.convert(src, dst, sourceFilter = self.sourceFilter)
+ sconverter.convert(src, dst, sourceFilter=self.sourceFilter)
+
class FromRTEMSToFreeBSDHeaderConverter(Converter):
def sourceFilter(self, data):
@@ -389,19 +425,29 @@ class FromRTEMSToFreeBSDHeaderConverter(Converter):
def convert(self, src, dst):
sconverter = super(FromRTEMSToFreeBSDHeaderConverter, self)
- sconverter.convert(src, dst, hasSource = False, sourceFilter = self.sourceFilter)
+ sconverter.convert(src,
+ dst,
+ hasSource=False,
+ sourceFilter=self.sourceFilter)
+
class FromRTEMSToFreeBSDSourceConverter(Converter):
def sourceFilter(self, data):
- data = re.sub('#include <machine/rtems-bsd-kernel-space.h>\n\n', '', data)
- data = re.sub('#include <machine/rtems-bsd-user-space.h>\n\n', '', data)
+ data = re.sub('#include <machine/rtems-bsd-kernel-space.h>\n\n', '',
+ data)
+ data = re.sub('#include <machine/rtems-bsd-user-space.h>\n\n', '',
+ data)
data = revertFixLocalIncludes(data)
data = revertFixIncludes(data)
return data
def convert(self, src, dst):
sconverter = super(FromRTEMSToFreeBSDSourceConverter, self)
- sconverter.convert(src, dst, hasSource = False, sourceFilter = self.sourceFilter)
+ sconverter.convert(src,
+ dst,
+ hasSource=False,
+ sourceFilter=self.sourceFilter)
+
#
# Compose a path based for the various parts of the source tree.
@@ -413,6 +459,7 @@ class PathComposer(object):
def composeLibBSDPath(self, path, prefix):
return os.path.join(prefix, path)
+
class FreeBSDPathComposer(PathComposer):
def composeOriginPath(self, path):
return os.path.join(FreeBSD_DIR, path)
@@ -420,6 +467,7 @@ class FreeBSDPathComposer(PathComposer):
def composeLibBSDPath(self, path, prefix):
return os.path.join(prefix, 'freebsd', path)
+
class RTEMSPathComposer(PathComposer):
def composeOriginPath(self, path):
return path
@@ -427,6 +475,7 @@ class RTEMSPathComposer(PathComposer):
def composeLibBSDPath(self, path, prefix):
return os.path.join(prefix, 'rtemsbsd', path)
+
class LinuxPathComposer(PathComposer):
def composeOriginPath(self, path):
return path
@@ -434,236 +483,375 @@ class LinuxPathComposer(PathComposer):
def composeLibBSDPath(self, path, prefix):
return os.path.join(prefix, 'linux', path)
+
class CPUDependentFreeBSDPathComposer(FreeBSDPathComposer):
def composeLibBSDPath(self, path, prefix):
- path = super(CPUDependentFreeBSDPathComposer, self).composeLibBSDPath(path, prefix)
+ path = super(CPUDependentFreeBSDPathComposer,
+ self).composeLibBSDPath(path, prefix)
path = mapCPUDependentPath(path)
return path
+
class CPUDependentRTEMSPathComposer(RTEMSPathComposer):
def composeLibBSDPath(self, path, prefix):
- path = super(CPUDependentRTEMSPathComposer, self).composeLibBSDPath(path, prefix)
+ path = super(CPUDependentRTEMSPathComposer,
+ self).composeLibBSDPath(path, prefix)
path = mapCPUDependentPath(path)
return path
+
class CPUDependentLinuxPathComposer(LinuxPathComposer):
def composeLibBSDPath(self, path, prefix):
- path = super(CPUDependentLinuxPathComposer, self).composeLibBSDPath(path, prefix)
+ path = super(CPUDependentLinuxPathComposer,
+ self).composeLibBSDPath(path, prefix)
path = mapCPUDependentPath(path)
return path
+
class TargetSourceCPUDependentPathComposer(CPUDependentFreeBSDPathComposer):
def __init__(self, targetCPU, sourceCPU):
self.targetCPU = targetCPU
self.sourceCPU = sourceCPU
def composeLibBSDPath(self, path, prefix):
- path = super(TargetSourceCPUDependentPathComposer, self).composeLibBSDPath(path, prefix)
+ path = super(TargetSourceCPUDependentPathComposer,
+ self).composeLibBSDPath(path, prefix)
path = path.replace(self.sourceCPU, self.targetCPU)
return path
-class BuildSystemFragmentComposer(object):
- def __init__(self, includes = None):
+
+class BuildSystemComposer(object):
+ def __init__(self, includes=None):
if type(includes) is not list:
self.includes = [includes]
else:
self.includes = includes
- def compose(self, path):
+ def __str__(self):
return ''
-class SourceFileFragmentComposer(BuildSystemFragmentComposer):
-
- def __init__(self, cflags = "default", includes = None):
- self.cflags, self.includes = _cflagsIncludes(cflags, includes)
+ def getIncludes(self):
+ if None in self.includes:
+ incs = []
+ else:
+ incs = self.includes
+ return incs
def compose(self, path):
- if None in self.includes:
- flags = self.cflags
+ """A None result means there is nothing to build."""
+ return None
+
+ @staticmethod
+ def filesAsDefines(files):
+ define_keys = ''
+ for f in files:
+ f = f.upper()
+ for c in '\/-.':
+ f = f.replace(c, '_')
+ define_keys += ' ' + f
+ return define_keys.strip()
+
+ @staticmethod
+ def cflagsIncludes(cflags, includes):
+ if type(cflags) is not list:
+ if cflags is not None:
+ _cflags = cflags.split(' ')
+ else:
+ _cflags = [None]
+ else:
+ _cflags = cflags
+ if type(includes) is not list:
+ _includes = [includes]
else:
- flags = self.cflags + self.includes
- return ['sources', flags, ('default', None)], [path], self.cflags, self.includes
+ _includes = includes
+ return _cflags, _includes
+
+
+class SourceFileBuildComposer(BuildSystemComposer):
+ def __init__(self, cflags="default", includes=None):
+ self.cflags, self.includes = self.cflagsIncludes(cflags, includes)
+
+ def __str__(self):
+ return 'SF: ' + ' '.join(self.getFlags())
+
+ def compose(self, path):
+ flags = self.getFlags()
+ return ['sources', flags, ('default', None)], \
+ [path], self.cflags, self.includes
+
+ def getFlags(self):
+ return self.cflags + self.getIncludes()
-class SourceFileIfHeaderComposer(SourceFileFragmentComposer):
- def __init__(self, headers, cflags = "default", includes = None):
+class SourceFileIfHeaderComposer(SourceFileBuildComposer):
+ def __init__(self, headers, cflags="default", includes=None):
if headers is not list:
headers = [headers]
self.headers = headers
- super(SourceFileIfHeaderComposer, self).__init__(cflags = cflags, includes = includes)
+ super(SourceFileIfHeaderComposer, self).__init__(cflags=cflags,
+ includes=includes)
+
+ def __str__(self):
+ return 'SFIH:' + ' '.join(self.getFlags()) + \
+ ' ' + self.filesAsDefines(self.headers)
def compose(self, path):
- r = SourceFileFragmentComposer.compose(self, path)
- define_keys = ''
- for h in self.headers:
- h = h.upper()
- for c in '\/-.':
- h = h.replace(c, '_')
- define_keys += ' ' + h
- r[0][2] = (define_keys.strip(), self.headers)
+ r = SourceFileBuildComposer.compose(self, path)
+ r[0][2] = (self.filesAsDefines(self.headers), self.headers)
return r
-class TestFragementComposer(BuildSystemFragmentComposer):
- def __init__(self, testName, fileFragments, configTest = None, runTest = True, netTest = False, extraLibs = []):
+class TestFragementComposer(BuildSystemComposer):
+ def __init__(self,
+ testName,
+ fileFragments,
+ configTest=None,
+ runTest=True,
+ netTest=False,
+ extraLibs=[],
+ modules=[]):
self.testName = testName
self.fileFragments = fileFragments
self.configTest = configTest
self.runTest = runTest
self.netTest = netTest
self.extraLibs = extraLibs
+ self.modules = modules
+
+ def __str__(self):
+ return 'TEST: ' + self.testName
def compose(self, path):
- return ['tests', self.testName, ('default', None)], { 'configTest': self.configTest,
- 'files': self.fileFragments,
- 'run': self.runTest,
- 'net': self.netTest,
- 'libs': self.extraLibs}
+ return ['tests', self.testName, ('default', None)], {
+ 'configTest': self.configTest,
+ 'files': self.fileFragments,
+ 'run': self.runTest,
+ 'net': self.netTest,
+ 'libs': self.extraLibs,
+ 'modules': self.modules,
+ }
-class TestIfHeaderComposer(TestFragementComposer):
- def __init__(self, testName, headers, fileFragments, runTest = True, netTest = False, extraLibs = []):
+class TestIfHeaderComposer(TestFragementComposer):
+ def __init__(self,
+ testName,
+ headers,
+ fileFragments,
+ runTest=True,
+ netTest=False,
+ extraLibs=[],
+ modules=[]):
if headers is not list:
headers = [headers]
self.headers = headers
- super(TestIfHeaderComposer, self).__init__(testName, fileFragments, 'header',
- runTest = runTest, netTest = netTest,
- extraLibs = extraLibs)
+ super(TestIfHeaderComposer, self).__init__(testName,
+ fileFragments,
+ 'header',
+ runTest=runTest,
+ netTest=netTest,
+ extraLibs=extraLibs,
+ modules=modules)
def compose(self, path):
r = TestFragementComposer.compose(self, path)
- define_keys = ''
- for h in self.headers:
- h = h.upper()
- for c in '\/-.':
- h = h.replace(c, '_')
- define_keys += ' ' + h
- r[0][2] = (define_keys.strip(), self.headers)
+ r[0][2] = (self.filesAsDefines(self.headers), self.headers)
return r
-class TestIfLibraryComposer(TestFragementComposer):
- def __init__(self, testName, libraries, fileFragments, runTest = True, netTest = False, extraLibs = []):
+class TestIfLibraryComposer(TestFragementComposer):
+ def __init__(self,
+ testName,
+ libraries,
+ fileFragments,
+ runTest=True,
+ netTest=False,
+ extraLibs=[],
+ modules=[]):
if libraries is not list:
libraries = [libraries]
self.libraries = libraries
- super(TestIfLibraryComposer, self).__init__(testName, fileFragments, 'library',
- runTest = runTest, netTest = netTest,
- extraLibs = extraLibs)
+ super(TestIfLibraryComposer, self).__init__(testName,
+ fileFragments,
+ 'library',
+ runTest=runTest,
+ netTest=netTest,
+ extraLibs=extraLibs,
+ modules=modules)
def compose(self, path):
r = TestFragementComposer.compose(self, path)
- define_keys = ''
- for l in self.libraries:
- l = l.upper()
- for c in '\/-.':
- l = l.replace(c, '_')
- define_keys += ' ' + l
- r[0][2] = (define_keys.strip(), self.libraries)
+ r[0][2] = (self.filesAsDefines(self.libraries), self.libraries)
return r
-class KVMSymbolsFragmentComposer(BuildSystemFragmentComposer):
+class KVMSymbolsBuildComposer(BuildSystemComposer):
def compose(self, path):
- return ['KVMSymbols', 'files', ('default', None)], [path], self.includes
+ return ['KVMSymbols', 'files', ('default', None)], \
+ [path], self.includes
-class RPCGENFragmentComposer(BuildSystemFragmentComposer):
+class RPCGENBuildComposer(BuildSystemComposer):
def compose(self, path):
- return ['RPCGen', 'files', ('default', None)], [path]
+ return ['RPCGen', 'files', ('default', None)], \
+ [path]
-class RouteKeywordsFragmentComposer(BuildSystemFragmentComposer):
+class RouteKeywordsBuildComposer(BuildSystemComposer):
def compose(self, path):
- return ['RouteKeywords', 'files', ('default', None)], [path]
+ return ['RouteKeywords', 'files', ('default', None)], \
+ [path]
-class LexFragmentComposer(BuildSystemFragmentComposer):
- def __init__(self, sym, dep, cflags = None, includes = None, build = True):
+class LexBuildComposer(BuildSystemComposer):
+ def __init__(self, sym, dep, cflags=None, includes=None, build=True):
self.sym = sym
self.dep = dep
- self.cflags, self.includes = _cflagsIncludes(cflags, includes)
+ self.cflags, self.includes = self.cflagsIncludes(cflags, includes)
self.build = build
def compose(self, path):
- d = { 'file': path,
- 'sym': self.sym,
- 'dep': self.dep,
- 'build': self.build }
+ d = {
+ 'file': path,
+ 'sym': self.sym,
+ 'dep': self.dep,
+ 'build': self.build
+ }
if None not in self.cflags:
d['cflags'] = self.cflags
if None not in self.includes:
d['includes'] = self.includes
return ['lex', path, ('default', None)], d
-class YaccFragmentComposer(BuildSystemFragmentComposer):
- def __init__(self, sym, header, cflags = None, includes = None, build = True):
+class YaccBuildComposer(BuildSystemComposer):
+ def __init__(self, sym, header, cflags=None, includes=None, build=True):
self.sym = sym
self.header = header
- self.cflags, self.includes = _cflagsIncludes(cflags, includes)
+ self.cflags, self.includes = self.cflagsIncludes(cflags, includes)
self.build = build
def compose(self, path):
- d = { 'file': path,
- 'sym': self.sym,
- 'header': self.header,
- 'build': self.build }
+ d = {
+ 'file': path,
+ 'sym': self.sym,
+ 'header': self.header,
+ 'build': self.build
+ }
if None not in self.cflags:
d['cflags'] = self.cflags
if None not in self.includes:
d['includes'] = self.includes
return ['yacc', path, ('default', None)], d
-#
-# File - a file in the source we move backwards and forwards.
-#
+
class File(object):
- def __init__(self, path, pathComposer,
- forwardConverter, reverseConverter, buildSystemComposer):
+ '''A file of source we move backwards and forwards and build.'''
+ def __init__(self, space, path, pathComposer, forwardConverter,
+ reverseConverter, buildSystemComposer):
if verbose(verboseMoreDetail):
- print("FILE: %-50s F:%-45s R:%-45s" % \
- (path,
+ print("FILE: %-6s %-50s F:%-45s R:%-45s" % \
+ (space, path,
forwardConverter.__class__.__name__,
reverseConverter.__class__.__name__))
+ self.space = space
self.path = path
self.pathComposer = pathComposer
self.originPath = self.pathComposer.composeOriginPath(self.path)
- self.libbsdPath = self.pathComposer.composeLibBSDPath(self.path, LIBBSD_DIR)
+ self.libbsdPath = self.pathComposer.composeLibBSDPath(
+ self.path, LIBBSD_DIR)
self.forwardConverter = forwardConverter
self.reverseConverter = reverseConverter
self.buildSystemComposer = buildSystemComposer
+ def __str__(self):
+ out = self.space[0].upper() + ' ' + self.path
+ bsc = str(self.buildSystemComposer)
+ if len(bsc) > 0:
+ out += ' (' + bsc + ')'
+ return out
+
+ def __eq__(self, other):
+ state = self.space == other.space
+ state = state and (self.path == self.path)
+ state = state and (self.pathComposer == self.pathComposer)
+ state = state and (self.originPath == self.originPath)
+ state = state and (self.forwardConverter == self.forwardConverter)
+ state = state and (self.self.reverseConverter
+ == self.self.reverseConverter)
+ state = state and (self.buildSystemComposer
+ == self.buildSystemComposer)
+ return state
+
def processSource(self, forward):
if forward:
if verbose(verboseDetail):
- print("process source: %s => %s" % (self.originPath, self.libbsdPath))
+ print("process source: %s => %s" %
+ (self.originPath, self.libbsdPath))
self.forwardConverter.convert(self.originPath, self.libbsdPath)
else:
if verbose(verboseDetail):
print("process source: %s => %s converter:%s" % \
- (self.libbsdPath, self.originPath, self.reverseConverter.__class__.__name__))
+ (self.libbsdPath, self.originPath,
+ self.reverseConverter.__class__.__name__))
self.reverseConverter.convert(self.libbsdPath, self.originPath)
def getFragment(self):
- return self.buildSystemComposer.compose(self.pathComposer.composeLibBSDPath(self.path, ''))
+ return self.buildSystemComposer.compose(
+ self.pathComposer.composeLibBSDPath(self.path, ''))
+
+ def getPath(self):
+ return self.path
+
+ def getSpace(self):
+ return self.space
+
-#
-# Module - logical group of related files we can perform actions on
-#
class Module(object):
- def __init__(self, manager, name, enabled = True):
+ '''Logical group of related files we can perform actions on'''
+ def __init__(self, manager, name, enabled=True):
self.manager = manager
self.name = name
- self.conditionalOn = "none"
self.files = []
self.cpuDependentSourceFiles = {}
self.dependencies = []
+ def __str__(self):
+ out = [self.name + ':']
+ if len(self.dependencies) > 0:
+ out += [' Deps: ' + str(len(self.dependencies))]
+ out += [' ' + type(d).__name__ for d in self.dependencies]
+ if len(self.files) > 0:
+ counts = {}
+ for f in self.files:
+ space = f.getSpace()
+ if space not in counts:
+ counts[space] = 0
+ counts[space] += 1
+ count_str = ''
+ for space in sorted(counts.keys()):
+ count_str += '%s:%d ' % (space[0].upper(), counts[space])
+ count_str = count_str[:-1]
+ out += [' Files: %d (%s)' % (len(self.files), count_str)]
+ out += [' ' + str(f) for f in self.files]
+ if len(self.cpuDependentSourceFiles) > 0:
+ out += [' CPU Dep: ' + str(len(self.cpuDependentSourceFiles))]
+ for cpu in self.cpuDependentSourceFiles:
+ out += [' ' + cpu + ':']
+ out += [
+ ' ' + str(f) for f in self.cpuDependentSourceFiles[cpu]
+ ]
+ return os.linesep.join(out)
+
def initCPUDependencies(self, cpu):
if cpu not in self.cpuDependentSourceFiles:
self.cpuDependentSourceFiles[cpu] = []
+ def getName(self):
+ return self.name
+
+ def getFiles(self):
+ return (f for f in self.files)
+
def processSource(self, direction):
if verbose(verboseDetail):
print("process module: %s" % (self.name))
@@ -673,135 +861,156 @@ class Module(object):
for f in files:
f.processSource(direction)
- def addFiles(self, newFiles, buildSystemComposer = BuildSystemFragmentComposer()):
- files = []
- for newFile in newFiles:
- assertFile(newFile)
- files += [File(newFile, composers, buildSystemComposer)]
- return files
-
def addFile(self, f):
+ if not isinstance(f, File):
+ raise TypeError('invalid type for addFiles: %s' % (type(f)))
self.files += [f]
- def addFiles(self, newFiles,
- pathComposer, forwardConverter, reverseConverter,
- assertFile, buildSystemComposer = BuildSystemFragmentComposer()):
+ def addFiles(self,
+ space,
+ newFiles,
+ pathComposer,
+ forwardConverter,
+ reverseConverter,
+ assertFile,
+ buildSystemComposer=BuildSystemComposer()):
files = []
for newFile in newFiles:
assertFile(newFile)
- files += [File(newFile, pathComposer, forwardConverter,
- reverseConverter, buildSystemComposer)]
+ files += [
+ File(space, newFile, pathComposer, forwardConverter,
+ reverseConverter, buildSystemComposer)
+ ]
return files
- def addPlainTextFile(self, files):
- self.files += self.addFiles(files,
+ def addPlainTextFiles(self, files):
+ self.files += self.addFiles('user', files,
FreeBSDPathComposer(), Converter(),
Converter(), assertNothing)
def addKernelSpaceHeaderFiles(self, files):
- self.files += self.addFiles(files,
- FreeBSDPathComposer(), FromFreeBSDToRTEMSHeaderConverter(),
- FromRTEMSToFreeBSDHeaderConverter(), assertHeaderOrSourceFile)
+ self.files += self.addFiles('kernel', files, FreeBSDPathComposer(),
+ FromFreeBSDToRTEMSHeaderConverter(),
+ FromRTEMSToFreeBSDHeaderConverter(),
+ assertHeaderOrSourceFile)
def addUserSpaceHeaderFiles(self, files):
- self.files += self.addFiles(files,
- FreeBSDPathComposer(), FromFreeBSDToRTEMSUserSpaceHeaderConverter(),
- FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile)
+ self.files += self.addFiles(
+ 'user', files, FreeBSDPathComposer(),
+ FromFreeBSDToRTEMSUserSpaceHeaderConverter(),
+ FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile)
def addRTEMSHeaderFiles(self, files):
- self.files += self.addFiles(files, RTEMSPathComposer(),
- NoConverter(), NoConverter(), assertHeaderFile)
+ self.files += self.addFiles('user', files, RTEMSPathComposer(),
+ NoConverter(), NoConverter(),
+ assertHeaderFile)
def addLinuxHeaderFiles(self, files):
- self.files += self.addFiles(files,
- PathComposer(), NoConverter(),
- NoConverter(), assertHeaderFile)
+ self.files += self.addFiles('kernel', files, PathComposer(),
+ NoConverter(), NoConverter(),
+ assertHeaderFile)
def addCPUDependentFreeBSDHeaderFiles(self, files):
- self.files += self.addFiles(files,
- CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSHeaderConverter(),
- FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile)
+ self.files += self.addFiles('kernel', files,
+ CPUDependentFreeBSDPathComposer(),
+ FromFreeBSDToRTEMSHeaderConverter(),
+ FromRTEMSToFreeBSDHeaderConverter(),
+ assertHeaderFile)
def addCPUDependentLinuxHeaderFiles(self, files):
- self.files += self.addFiles(files,
- CPUDependentLinuxPathComposer(), NoConverter(),
- NoConverter(), assertHeaderFile)
+ self.files += self.addFiles('kernel', files,
+ CPUDependentLinuxPathComposer(),
+ NoConverter(), NoConverter(),
+ assertHeaderFile)
- def addTargetSourceCPUDependentHeaderFiles(self, targetCPUs, sourceCPU, files):
+ def addTargetSourceCPUDependentHeaderFiles(self, targetCPUs, sourceCPU,
+ files):
for cpu in targetCPUs:
- self.files += self.addFiles(files,
- TargetSourceCPUDependentPathComposer(cpu, sourceCPU),
- FromFreeBSDToRTEMSHeaderConverter(),
- NoConverter(), assertHeaderFile)
-
- def addSourceFiles(self, files, sourceFileFragmentComposer):
- self.files += self.addFiles(files,
- PathComposer(), NoConverter(), NoConverter(), assertSourceFile,
- sourceFileFragmentComposer)
-
- def addKernelSpaceSourceFiles(self, files, sourceFileFragmentComposer):
- self.files += self.addFiles(files,
- FreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(),
- FromRTEMSToFreeBSDSourceConverter(), assertSourceFile,
- sourceFileFragmentComposer)
-
- def addUserSpaceSourceFiles(self, files, sourceFileFragmentComposer):
- self.files += self.addFiles(files,
- FreeBSDPathComposer(),
- FromFreeBSDToRTEMSUserSpaceSourceConverter(),
- FromRTEMSToFreeBSDSourceConverter(), assertSourceFile,
- sourceFileFragmentComposer)
-
- def addRTEMSSourceFiles(self, files, sourceFileFragmentComposer):
- self.files += self.addFiles(files,
- RTEMSPathComposer(), NoConverter(), NoConverter(),
- assertSourceFile, sourceFileFragmentComposer)
-
- def addLinuxSourceFiles(self, files, sourceFileFragmentComposer):
- self.files += self.addFiles(files,
- PathComposer(), NoConverter(),
+ self.files += self.addFiles(
+ 'kernel', files,
+ TargetSourceCPUDependentPathComposer(cpu, sourceCPU),
+ FromFreeBSDToRTEMSHeaderConverter(), NoConverter(),
+ assertHeaderFile)
+
+ def addSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles('user',
+ files, PathComposer(), NoConverter(),
NoConverter(), assertSourceFile,
- sourceFileFragmentComposer)
-
- def addCPUDependentFreeBSDSourceFiles(self, cpus, files, sourceFileFragmentComposer):
+ sourceFileBuildComposer)
+
+ def addKernelSpaceSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles('kernel', files, FreeBSDPathComposer(),
+ FromFreeBSDToRTEMSSourceConverter(),
+ FromRTEMSToFreeBSDSourceConverter(),
+ assertSourceFile, sourceFileBuildComposer)
+
+ def addUserSpaceSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles(
+ 'user', files, FreeBSDPathComposer(),
+ FromFreeBSDToRTEMSUserSpaceSourceConverter(),
+ FromRTEMSToFreeBSDSourceConverter(), assertSourceFile,
+ sourceFileBuildComposer)
+
+ def addRTEMSKernelSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles('kernel', files, RTEMSPathComposer(),
+ NoConverter(), NoConverter(),
+ assertSourceFile, sourceFileBuildComposer)
+
+ def addRTEMSUserSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles('user', files, RTEMSPathComposer(),
+ NoConverter(), NoConverter(),
+ assertSourceFile, sourceFileBuildComposer)
+
+ def addLinuxSourceFiles(self, files, sourceFileBuildComposer):
+ self.files += self.addFiles('kernel', files, PathComposer(),
+ NoConverter(), NoConverter(),
+ assertSourceFile, sourceFileBuildComposer)
+
+ def addCPUDependentFreeBSDSourceFiles(self, cpus, files,
+ sourceFileBuildComposer):
for cpu in cpus:
self.initCPUDependencies(cpu)
self.cpuDependentSourceFiles[cpu] += \
- self.addFiles(files,
- CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(),
- FromRTEMSToFreeBSDSourceConverter(), assertSourceFile,
- sourceFileFragmentComposer)
-
- def addCPUDependentRTEMSSourceFiles(self, cpus, files, sourceFileFragmentComposer):
+ self.addFiles(
+ 'kernel', files,
+ CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(),
+ FromRTEMSToFreeBSDSourceConverter(), assertSourceFile,
+ sourceFileBuildComposer)
+
+ def addCPUDependentRTEMSSourceFiles(self, cpus, files,
+ sourceFileBuildComposer):
for cpu in cpus:
self.initCPUDependencies(cpu)
self.cpuDependentSourceFiles[cpu] += \
- self.addFiles(files,
+ self.addFiles('kernel', files,
CPUDependentRTEMSPathComposer(), NoConverter(),
NoConverter(), assertSourceFile,
- sourceFileFragmentComposer)
+ sourceFileBuildComposer)
- def addCPUDependentLinuxSourceFiles(self, cpus, files, sourceFileFragmentComposer):
+ def addCPUDependentLinuxSourceFiles(self, cpus, files,
+ sourceFileBuildComposer):
for cpu in cpus:
self.initCPUDependencies(cpu)
self.cpuDependentSourceFiles[cpu] += \
- self.addFiles(files,
+ self.addFiles('kernel', files,
CPUDependentLinuxPathComposer(), NoConverter(),
NoConverter(), assertSourceFile,
- sourceFileFragmentComposer)
+ sourceFileBuildComposer)
def addTest(self, testFragementComposer):
- self.files += [File(testFragementComposer.testName,
- PathComposer(), NoConverter(), NoConverter(),
- testFragementComposer)]
+ self.files += [
+ File('user', testFragementComposer.testName, PathComposer(),
+ NoConverter(), NoConverter(), testFragementComposer)
+ ]
def addDependency(self, dep):
+ if not isinstance(dep, str):
+ raise TypeError('dependencies are a string: %s' % (self.name))
self.dependencies += [dep]
-#
-# Manager - a collection of modules.
-#
+
class ModuleManager(object):
+ '''A manager for a collection of modules.'''
def __init__(self):
self.modules = {}
self.generator = {}
@@ -813,18 +1022,82 @@ class ModuleManager(object):
raise KeyError('module %s not found' % (key))
return self.modules[key]
+ def __str__(self):
+ out = ['Modules: ' + str(len(self.modules)), '']
+ for m in sorted(self.modules):
+ out += [str(self.modules[m]), '']
+ return os.linesep.join(out)
+
+ def _loadIni(self, ini_file):
+ if not os.path.exists(ini_file):
+ raise FileNotFoundError('file not found: %s' % (ini_file))
+ ini = configparser.ConfigParser()
+ ini.read(ini_file)
+ if not ini.has_section('general'):
+ raise Exception(
+ "'{}' is missing a general section.".format(ini_file))
+ if not ini.has_option('general', 'name'):
+ raise Exception("'{}' is missing a general/name.".format(ini_file))
+ if ini.has_option('general', 'extends'):
+ extends = ini.get('general', 'extends')
+ extendfile = None
+ basepath = os.path.dirname(ini_file)
+ if os.path.isfile(os.path.join(basepath, extends)):
+ extendfile = os.path.join(basepath, extends)
+ elif os.path.isfile(os.path.join(BUILDSET_DIR, extends)):
+ extendfile = os.path.join(BUILDSET_DIR, extends)
+ else:
+ raise Exception(
+ "'{}': Invalid file given for general/extends:'{}'".format(
+ ini_file, extends))
+ base = self._loadIni(extendfile)
+ for s in ini.sections():
+ if not base.has_section(s):
+ base.add_section(s)
+ for o in ini.options(s):
+ val = ini.get(s, o)
+ base.set(s, o, val)
+ ini = base
+ return ini
+
+ def _checkDependencies(self):
+ enabled_modules = self.getEnabledModules()
+ if 'tests' in enabled_modules:
+ enabled_modules.remove('tests')
+ for mod in enabled_modules:
+ if mod not in self.modules:
+ raise KeyError('enabled module not found: %s' % (mod))
+ for dep in self.modules[mod].dependencies:
+ if dep not in self.modules:
+ print(type(dep))
+ raise KeyError('dependent module not found: %s' % (dep))
+ if dep not in enabled_modules:
+ raise Exception('module "%s" dependency "%s" not enabled' %
+ (mod, dep))
+
def getAllModules(self):
if 'modules' in self.configuration:
- return self.configuration['modules']
+ return sorted(self.configuration['modules'])
return []
def getEnabledModules(self):
if 'modules-enabled' in self.configuration:
- return self.configuration['modules-enabled']
+ return sorted(self.configuration['modules-enabled'])
return []
def addModule(self, module):
- self.modules[module.name] = module
+ name = module.name
+ if name in self.modules:
+ raise KeyError('module already added: %' % (name))
+ self.modules[name] = module
+ if 'modules' not in self.configuration:
+ self.configuration['modules'] = []
+ if 'modules-enabled' not in self.configuration:
+ self.configuration['modules-enabled'] = []
+ self.configuration['modules'] += [name]
+ self.configuration['modules-enabled'] += [name]
+ self.configuration['modules'].sort()
+ self.configuration['modules-enabled'].sort
def processSource(self, direction):
if verbose(verboseDetail):
@@ -836,16 +1109,7 @@ class ModuleManager(object):
self.configuration = copy.deepcopy(config)
def getConfiguration(self):
- return self.configuration
-
- def updateConfiguration(self, config):
- self.configuration.update(config)
-
- def setModuleConfigiuration(self):
- mods = sorted(self.modules.keys())
- self.configuration['modules'] = mods
- # Enabled modules are overwritten by config file. Default to all.
- self.configuration['modules-enabled'] = mods
+ return copy.deepcopy(self.configuration)
def generateBuild(self, only_enabled=True):
modules_to_process = self.getEnabledModules()
@@ -853,14 +1117,53 @@ class ModuleManager(object):
if only_enabled == False:
modules_to_process = self.getAllModules()
for m in modules_to_process:
+ if m not in self.modules:
+ raise KeyError('enabled module not registered: %s' % (m))
self.modules[m].generate()
+ self._checkDependencies()
+
+ def duplicateCheck(self):
+ dups = []
+ modules_to_check = sorted(self.getAllModules(), reverse=True)
+ while len(modules_to_check) > 1:
+ mod = modules_to_check.pop()
+ for m in modules_to_check:
+ if m not in self.modules:
+ raise KeyError('enabled module not registered: %s' % (m))
+ for fmod in self.modules[mod].getFiles():
+ for fm in self.modules[m].getFiles():
+ if fmod.getPath() == fm.getPath():
+ dups += [(m, mod, fm.getPath(), fm.getSpace())]
+ return dups
+
+ def loadConfig(self, config=BUILDSET_DEFAULT):
+ if 'name' in self.configuration:
+ raise KeyError('configuration already loaded: %s (%s)' % \
+ (self.configuration['name'], config))
+ ini = self._loadIni(config)
+ self.configuration['name'] = ini.get('general', 'name')
+ self.configuration['modules-enabled'] = []
+ mods = []
+ if ini.has_section('modules'):
+ mods = ini.options('modules')
+ for mod in mods:
+ if ini.getboolean('modules', mod):
+ self.configuration['modules-enabled'].append(mod)
+
+ def getName(self):
+ if 'name' not in self.configuration:
+ raise KeyError('configuration not loaded')
+ return self.configuration['name']
def setGenerators(self):
self.generator['convert'] = Converter
self.generator['no-convert'] = NoConverter
- self.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'] = FromFreeBSDToRTEMSUserSpaceSourceConverter
- self.generator['from-RTEMS-To-FreeBSD-SourceConverter'] = FromRTEMSToFreeBSDSourceConverter
- self.generator['buildSystemFragmentComposer'] = BuildSystemFragmentComposer
+ self.generator[
+ 'from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'] = \
+ FromFreeBSDToRTEMSUserSpaceSourceConverter
+ self.generator[
+ 'from-RTEMS-To-FreeBSD-SourceConverter'] = FromRTEMSToFreeBSDSourceConverter
+ self.generator['buildSystemComposer'] = BuildSystemComposer
self.generator['file'] = File
@@ -868,15 +1171,16 @@ class ModuleManager(object):
self.generator['freebsd-path'] = FreeBSDPathComposer
self.generator['rtems-path'] = RTEMSPathComposer
self.generator['cpu-path'] = CPUDependentFreeBSDPathComposer
- self.generator['target-src-cpu--path'] = TargetSourceCPUDependentPathComposer
+ self.generator[
+ 'target-src-cpu--path'] = TargetSourceCPUDependentPathComposer
- self.generator['source'] = SourceFileFragmentComposer
+ self.generator['source'] = SourceFileBuildComposer
self.generator['test'] = TestFragementComposer
- self.generator['kvm-symbols'] = KVMSymbolsFragmentComposer
- self.generator['rpc-gen'] = RPCGENFragmentComposer
- self.generator['route-keywords'] = RouteKeywordsFragmentComposer
- self.generator['lex'] = LexFragmentComposer
- self.generator['yacc'] = YaccFragmentComposer
+ self.generator['kvm-symbols'] = KVMSymbolsBuildComposer
+ self.generator['rpc-gen'] = RPCGENBuildComposer
+ self.generator['route-keywords'] = RouteKeywordsBuildComposer
+ self.generator['lex'] = LexBuildComposer
+ self.generator['yacc'] = YaccBuildComposer
self.generator['source-if-header'] = SourceFileIfHeaderComposer
self.generator['test-if-header'] = TestIfHeaderComposer
diff --git a/buildset/default.ini b/buildset/default.ini
index 741357fb..88379a96 100644
--- a/buildset/default.ini
+++ b/buildset/default.ini
@@ -43,6 +43,7 @@ dhcpcd = on
dpaa = on
evdev = on
fdt = on
+if_mve = on
imx = on
in_cksum = on
mdnsresponder = on
@@ -53,11 +54,13 @@ net80211 = off
netinet = on
netinet6 = on
netipsec = off
+nfsv2 = on
nvme = on
opencrypto = on
pci = on
pf = on
regulator = on
+rpc_user = on
rtems = on
tests = on
tty = 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 6b0307a0a5184339393f555d5d424190d8a8277
+Subproject 5d85e12f44ccb0e5728344a002c108ddc105e03
diff --git a/freebsd-to-rtems.py b/freebsd-to-rtems.py
index 3f06bdf9..cda3ff3a 100755
--- a/freebsd-to-rtems.py
+++ b/freebsd-to-rtems.py
@@ -47,6 +47,7 @@ import libbsd
isForward = True
isEarlyExit = False
statsReport = False
+isConfig = False
def usage():
print("freebsd-to-rtems.py [args]")
@@ -59,14 +60,15 @@ def usage():
print(" -R|--reverse default origin -> LibBSD, reverse that")
print(" -r|--rtems LibBSD directory (default: '.')")
print(" -f|--freebsd FreeBSD origin directory (default: 'freebsd-org')")
+ print(" -c|--config Output the configuration then exit")
print(" -v|--verbose enable verbose output mode")
# Parse the arguments
def parseArguments():
- global isForward, isEarlyExit, statsReport
+ global isForward, isEarlyExit, statsReport, isConfig
try:
opts, args = getopt.getopt(sys.argv[1:],
- "?hdDembSRr:f:v",
+ "?hdDembSRr:f:cv",
[ "help",
"help",
"dry-run"
@@ -78,6 +80,7 @@ def parseArguments():
"stats"
"rtems="
"freebsd="
+ "config"
"verbose" ])
except getopt.GetoptError as err:
# print help information and exit:
@@ -104,6 +107,8 @@ def parseArguments():
builder.LIBBSD_DIR = a
elif o in ("-f", "--freebsd"):
builder.FreeBSD_DIR = a
+ elif o in ("-c", "--config"):
+ isConfig = True
else:
assert False, "unhandled option"
@@ -127,24 +132,44 @@ def wasDirectorySet(desc, path):
print("error:" + desc + " Directory (" + path + ") does not exist")
sys.exit(2)
-# Were directories specified?
-wasDirectorySet( "LibBSD", builder.LIBBSD_DIR )
-wasDirectorySet( "FreeBSD", builder.FreeBSD_DIR )
+try:
+ if not isConfig:
+ # Were directories specified?
+ wasDirectorySet( "LibBSD", builder.LIBBSD_DIR )
+ wasDirectorySet( "FreeBSD", builder.FreeBSD_DIR )
-# Are we generating or reverting?
-if isForward == True:
- print("Forward from", builder.FreeBSD_DIR, "into", builder.LIBBSD_DIR)
-else:
- print("Reverting from", builder.LIBBSD_DIR)
+ # Are we generating or reverting?
+ if isForward == True:
+ print("Forward from", builder.FreeBSD_DIR, "into", builder.LIBBSD_DIR)
+ else:
+ print("Reverting from", builder.LIBBSD_DIR)
-if isEarlyExit == True:
- print("Early exit at user request")
- sys.exit(0)
+ if isEarlyExit == True:
+ print("Early exit at user request")
+ sys.exit(0)
-try:
build = builder.ModuleManager()
libbsd.load(build)
+ build.loadConfig()
build.generateBuild(only_enabled=False)
+
+ dups = build.duplicateCheck()
+ if len(dups) > 0:
+ print()
+ print('Duplicates: %d' % (len(dups)))
+ mods = list(set([dup[0] for dup in dups]))
+ max_mod_len = max(len(dup[1]) for dup in dups)
+ for mod in mods:
+ print(' %s:' % (mod))
+ for dup in [dup for dup in dups if dup[0] == mod]:
+ print(' %-*s %s %s' % (max_mod_len, dup[1], dup[3][0].upper(), dup[2]))
+ print()
+
+ if isConfig:
+ print()
+ print(build)
+ sys.exit(0)
+
build.processSource(isForward)
builder.changedFileSummary(statsReport)
except IOError as ioe:
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 3b68ed51..24c6a29c 100644
--- a/freebsd/contrib/tcpdump/tcpdump.c
+++ b/freebsd/contrib/tcpdump/tcpdump.c
@@ -143,6 +143,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)
@@ -210,8 +211,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;
@@ -223,6 +226,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
@@ -236,6 +240,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;
@@ -627,6 +632,7 @@ static const struct option longopts[] = {
{ NULL, 0, NULL, 0 }
};
+#ifndef __rtems__
#ifndef _WIN32
/* Drop root privileges and chroot if necessary */
static void
@@ -659,7 +665,6 @@ droproot(const char *username, const char *chroot_dir)
}
}
#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",
@@ -672,7 +677,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 {
@@ -694,6 +698,7 @@ droproot(const char *username, const char *chroot_dir)
}
#endif /* _WIN32 */
+#endif /* __rtems__ */
static int
getWflagChars(int x)
@@ -1219,26 +1224,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);
@@ -1247,19 +1248,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);
@@ -1269,27 +1273,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__
@@ -1306,15 +1321,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
@@ -1686,9 +1705,11 @@ main(int argc, char **argv)
zflag = optarg;
break;
+#ifndef __rtems__
case 'Z':
username = optarg;
break;
+#endif /* __rtems__ */
case '#':
ndo->ndo_packet_number = 1;
@@ -1985,6 +2006,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);
@@ -2052,6 +2074,7 @@ main(int argc, char **argv)
}
#endif /* _WIN32 */
+#endif /* __rtems__ */
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
@@ -2146,6 +2169,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);
@@ -2153,6 +2182,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
@@ -2177,6 +2207,7 @@ main(int argc, char **argv)
alarm(1);
#endif
}
+#endif /* __rtems__ */
if (RFileName == NULL) {
/*
@@ -2219,7 +2250,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,
@@ -2338,6 +2381,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_)
@@ -2390,6 +2434,7 @@ child_cleanup(int signo _U_)
wait(NULL);
}
#endif /* HAVE_FORK && HAVE_VFORK */
+#endif /* __rtems__ */
static void
info(register int verbose)
@@ -2750,6 +2795,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_)
{
@@ -2778,6 +2824,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/crypto/md5.h b/freebsd/contrib/wpa/src/crypto/md5.h
index 33f8426c..48afb955 100644
--- a/freebsd/contrib/wpa/src/crypto/md5.h
+++ b/freebsd/contrib/wpa/src/crypto/md5.h
@@ -11,6 +11,10 @@
#define MD5_MAC_LEN 16
+#ifdef __rtems__
+#define hmac_md5_vector wpa_hmac_md5_vector
+#define hmac_md5 wpa_hmac_md5
+#endif /* __rtems__ */
int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac);
int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
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 f339d972..9d752d26 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 1712b9e6..2f7fb828 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_parser-data.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
index bb8832ac..9bbec579 100644
--- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
+++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h
@@ -3,3 +3,4 @@
#include "rtems-bsd-pfctl-data.h"
/* pfctl_parser.c */
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 e31941b8..fae24e53 100644
--- a/freebsd/sbin/ping/ping.c
+++ b/freebsd/sbin/ping/ping.c
@@ -307,7 +307,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,
@@ -572,6 +576,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))
@@ -582,6 +587,7 @@ main(int argc, char *const *argv)
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
+#endif /* __rtems__ */
case 'v':
options |= F_VERBOSE;
break;
@@ -1518,8 +1524,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 a56aae1d..921797d0 100644
--- a/freebsd/sbin/ping6/ping6.c
+++ b/freebsd/sbin/ping6/ping6.c
@@ -115,6 +115,7 @@ __FBSDID("$FreeBSD$");
#include <getopt.h>
#include <machine/rtems-bsd-program.h>
#include <machine/rtems-bsd-commands.h>
+#include <rtems/libio_.h>
#endif /* __rtems__ */
#include <sys/param.h>
#include <sys/capsicum.h>
@@ -286,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);
@@ -346,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;
@@ -373,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;
@@ -399,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
@@ -627,6 +636,7 @@ main(int argc, char *argv[])
options |= F_WAITTIME;
waittime = (int)t;
break;
+#ifndef __rtems__
case 't':
alarmtimeout = strtoul(optarg, &e, 0);
if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
@@ -637,6 +647,7 @@ main(int argc, char *argv[])
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
+#endif /* __rtems__ */
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
case 'P':
@@ -1173,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;
@@ -1188,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;
@@ -1198,7 +1211,13 @@ main(int argc, char *argv[])
struct timespec now, timeout;
struct msghdr m;
struct iovec iov[2];
+#ifndef __rtems__
fd_set rfds;
+#else /* __rtems__ */
+ fd_set big_enough_rfds[howmany(rtems_libio_number_iops,
+ sizeof(fd_set) * 8)];
+#define rfds (*(fd_set *)(&big_enough_rfds[0]))
+#endif /* __rtems__ */
int n;
/* signal handling */
@@ -1211,7 +1230,11 @@ main(int argc, char *argv[])
continue;
}
#endif
+#ifndef __rtems__
FD_ZERO(&rfds);
+#else /* __rtems__ */
+ memset(big_enough_rfds, 0, sizeof(big_enough_rfds));
+#endif /* __rtems__ */
FD_SET(srecv, &rfds);
clock_gettime(CLOCK_MONOTONIC, &now);
timespecadd(&last, &intvl, &timeout);
@@ -1299,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)
@@ -1312,6 +1337,7 @@ main(int argc, char *argv[])
exit(nreceived == 0 ? 2 : 0);
}
+#ifndef __rtems__
static void
onsignal(int sig)
{
@@ -1328,6 +1354,7 @@ onsignal(int sig)
#endif
}
}
+#endif /* __rtems__ */
/*
* pinger --
diff --git a/freebsd/sys/arm/freescale/imx/imx6_ccm.c b/freebsd/sys/arm/freescale/imx/imx6_ccm.c
index 78bbd5c1..7fdb69b8 100644
--- a/freebsd/sys/arm/freescale/imx/imx6_ccm.c
+++ b/freebsd/sys/arm/freescale/imx/imx6_ccm.c
@@ -368,6 +368,7 @@ imx6_ccm_sata_enable(void)
return 0;
}
+#ifndef __rtems__
uint32_t
imx_ccm_ecspi_hz(void)
{
@@ -408,6 +409,7 @@ imx_ccm_ahb_hz(void)
{
return (132000000);
}
+#endif /* __rtems__ */
void
imx_ccm_ipu_enable(int ipu)
diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c b/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c
deleted file mode 100644
index 8823b6af..00000000
--- a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c
+++ /dev/null
@@ -1,305 +0,0 @@
-#include <machine/rtems-bsd-kernel-space.h>
-
-/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
- *
- * Copyright (c) 2012 Damjan Marion <dmarion@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$");
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/bus.h>
-#include <rtems/bsd/sys/resource.h>
-#include <sys/rman.h>
-#include <sys/lock.h>
-#include <sys/malloc.h>
-
-#include <machine/bus.h>
-#include <machine/resource.h>
-#include <machine/intr.h>
-#include <sys/gpio.h>
-
-#include <arm/ti/tivar.h>
-#include <arm/ti/ti_pinmux.h>
-
-#include <arm/ti/am335x/am335x_scm_padconf.h>
-
-#define _PIN(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \
- { .reg_off = r, \
- .gpio_pin = gp, \
- .gpio_mode = gm, \
- .ballname = b, \
- .muxmodes[0] = m0, \
- .muxmodes[1] = m1, \
- .muxmodes[2] = m2, \
- .muxmodes[3] = m3, \
- .muxmodes[4] = m4, \
- .muxmodes[5] = m5, \
- .muxmodes[6] = m6, \
- .muxmodes[7] = m7, \
- }
-
-const static struct ti_pinmux_padstate ti_padstate_devmap[] = {
- {"output", PADCONF_OUTPUT },
- {"output_pullup", PADCONF_OUTPUT_PULLUP },
- {"input", PADCONF_INPUT },
- {"input_pulldown", PADCONF_INPUT_PULLDOWN },
- {"input_pullup", PADCONF_INPUT_PULLUP },
- {"i2c", PADCONF_INPUT_PULLUP_SLOW },
- { .state = NULL }
-};
-
-const static struct ti_pinmux_padconf ti_padconf_devmap[] = {
- _PIN(0x000, "GPMC_AD0", 32, 7,"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"),
- _PIN(0x004, "GPMC_AD1", 33, 7,"gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"),
- _PIN(0x008, "GPMC_AD2", 34, 7,"gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"),
- _PIN(0x00C, "GPMC_AD3", 35, 7,"gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"),
- _PIN(0x010, "GPMC_AD4", 36, 7,"gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"),
- _PIN(0x014, "GPMC_AD5", 37, 7,"gpmc_ad5", "mmc1_dat5", NULL, NULL, NULL, NULL, NULL, "gpio1_5"),
- _PIN(0x018, "GPMC_AD6", 38, 7,"gpmc_ad6", "mmc1_dat6", NULL, NULL, NULL, NULL, NULL, "gpio1_6"),
- _PIN(0x01C, "GPMC_AD7", 39, 7,"gpmc_ad7", "mmc1_dat7", NULL, NULL, NULL, NULL, NULL, "gpio1_7"),
- _PIN(0x020, "GPMC_AD8", 22, 7, "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4", "ehrpwm2A", NULL, NULL, "gpio0_22"),
- _PIN(0x024, "GPMC_AD9", 23, 7, "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5", "ehrpwm2B", NULL, NULL, "gpio0_23"),
- _PIN(0x028, "GPMC_AD10", 26, 7, "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6", "ehrpwm2_tripzone_in", NULL, NULL, "gpio0_26"),
- _PIN(0x02C, "GPMC_AD11", 27, 7, "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7", "ehrpwm0_synco", NULL, NULL, "gpio0_27"),
- _PIN(0x030, "GPMC_AD12", 44, 7, "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0", "eQEP2A_in", "pr1_mii0_txd2", "pr1_pru0_pru_r30_14", "gpio1_12"),
- _PIN(0x034, "GPMC_AD13", 45, 7, "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1", "eQEP2B_in", "pr1_mii0_txd1", "pr1_pru0_pru_r30_15", "gpio1_13"),
- _PIN(0x038, "GPMC_AD14", 46, 7, "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2", "eQEP2_index", "pr1_mii0_txd0", "pr1_pru0_pru_r31_14", "gpio1_14"),
- _PIN(0x03C, "GPMC_AD15", 47, 7, "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3", "eQEP2_strobe", "pr1_ecap0_ecap_capin_apwm_o", "pr1_pru0_pru_r31_15", "gpio1_15"),
- _PIN(0x040, "GPMC_A0", 48, 7, "gpmc_a0", "gmii2_txen", "rgmii2_tctl", "rmii2_txen", "gpmc_a16", "pr1_mii_mt1_clk", "ehrpwm1_tripzone_input", "gpio1_16"),
- _PIN(0x044, "GPMC_A1", 49, 7, "gpmc_a1", "gmii2_rxdv", "rgmii2_rctl", "mmc2_dat0", "gpmc_a17", "pr1_mii1_txd3", "ehrpwm0_synco", "gpio1_17"),
- _PIN(0x048, "GPMC_A2", 50, 7, "gpmc_a2", "gmii2_txd3", "rgmii2_td3", "mmc2_dat1", "gpmc_a18", "pr1_mii1_txd2", "ehrpwm1A", "gpio1_18"),
- _PIN(0x04C, "GPMC_A3", 51, 7, "gpmc_a3", "gmii2_txd2", "rgmii2_td2", "mmc2_dat2", "gpmc_a19", "pr1_mii1_txd1", "ehrpwm1B", "gpio1_19"),
- _PIN(0x050, "GPMC_A4", 52, 7, "gpmc_a4", "gmii2_txd1", "rgmii2_td1", "rmii2_tdx1", "gpmc_a20", "pr1_mii1_txd0", "eQEP1A_in", "gpio1_20"),
- _PIN(0x054, "GPMC_A5", 53, 7, "gpmc_a5", "gmii2_txd0", "rgmii2_td0", "rmii2_txd0", "gpmc_a21", "pr1_mii1_rxd3", "eQEP1B_in", "gpio1_21"),
- _PIN(0x058, "GPMC_A6", 54, 7, "gpmc_a6", "gmii2_txclk", "rgmii2_tclk", "mmc2_dat4", "gpmc_a22", "pr1_mii1_rxd2", "eQEP1_index", "gpio1_22"),
- _PIN(0x05C, "GPMC_A7", 55, 7, "gpmc_a7", "gmii2_rxclk", "rgmii2_rclk", "mmc2_dat5", "gpmc_a23", "pr1_mii1_rxd1", "eQEP1_strobe", "gpio1_23"),
- _PIN(0x060, "GPMC_A8", 56, 7, "gpmc_a8", "gmii2_rxd3", "rgmii2_rd3", "mmc2_dat6", "gpmc_a24", "pr1_mii1_rxd0", "mcasp0_aclkx", "gpio1_24"),
- _PIN(0x064, "GPMC_A9", 57, 7, "gmpc_a9", "gmii2_rxd2", "rgmii2_rd2", "mmc2_dat7 / rmii2_crs_dv", "gpmc_a25", "pr1_mii_mr1_clk", "mcasp0_fsx", "gpio1_25"),
- _PIN(0x068, "GPMC_A10", 58, 7, "gmpc_a10", "gmii2_rxd1", "rgmii2_rd1", "rmii2_rxd1", "gpmc_a26", "pr1_mii1_rxdv", "mcasp0_arx0", "gpio1_26"),
- _PIN(0x06C, "GPMC_A11", 59, 7, "gmpc_a11", "gmii2_rxd0", "rgmii2_rd0", "rmii2_rxd0", "gpmc_a27", "pr1_mii1_rxer", "mcasp0_axr1", "gpio1_27"),
- _PIN(0x070, "GPMC_WAIT0", 30, 7, "gpmc_wait0", "gmii2_crs", "gpmc_csn4", "rmii2_crs_dv", "mmc1_sdcd", "pr1_mii1_col", "uart4_rxd", "gpio0_30"),
- _PIN(0x074, "GPMC_WPn", 31, 7, "gpmc_wpn", "gmii2_rxerr", "gpmc_csn5", "rmii2_rxerr", "mmc2_sdcd", "pr1_mii1_txen", "uart4_txd", "gpio0_31"),
- _PIN(0x078, "GPMC_BEn1", 60, 7, "gpmc_be1n", "gmii2_col", "gmpc_csn6","mmc2_dat3", "gpmc_dir", "pr1_mii1_rxlink", "mcasp0_aclkr", "gpio1_28"),
- _PIN(0x07c, "GPMC_CSn0", 61, 7, "gpmc_csn0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio1_29"),
- _PIN(0x080, "GPMC_CSn1", 62, 7, "gpmc_csn1", "gpmc_clk", "mmc1_clk", "pr1_edio_data_in6", "pr1_edio_data_out6", "pr1_pru1_pru_r30_12", "pr1_pru1_pru_r31_12", "gpio1_30"),
- _PIN(0x084, "GPMC_CSn2", 63, 7, "gpmc_csn2", "gpmc_be1n", "mmc1_cmd", "pr1_edio_data_in7", "pr1_edio_data_out7", "pr1_pru1_pru_r30_13", "pr1_pru1_pru_r31_13", "gpio1_31"),
- _PIN(0x088, "GPMC_CSn3", 64, 7, "gpmc_csn3", "gpmc_a3", "rmii2_crs_dv", "mmc2_cmd", "pr1_mii0_crs", "pr1_mdio_data", "EMU4", "gpio2_0"),
- _PIN(0x08c, "GPMC_CLK", 65, 7, "gpmc_clk", "lcd_memory_clk", "gpmc_wait1", "mmc2_clk", "pr1_mii1_crs", "pr1_mdio_mdclk", "mcasp0_fsr", "gpio2_1"),
- _PIN(0x090, "GPMC_ADVn_ALE", 66, 7, "gpmc_advn_ale", NULL, "timer4", NULL, NULL, NULL, NULL, "gpio2_2"),
- _PIN(0x094, "GPMC_OEn_REn", 67, 7, "gpmc_oen_ren", NULL, "timer7", NULL, NULL, NULL, NULL, "gpio2_3"),
- _PIN(0x098, "GPMC_WEn", 68, 7, "gpmc_wen", NULL, "timer6", NULL, NULL, NULL, NULL, "gpio2_4"),
- _PIN(0x09c, "GPMC_BEn0_CLE", 67, 7, "gpmc_ben0_cle", NULL, "timer5", NULL, NULL, NULL, NULL, "gpio2_5"),
- _PIN(0x0a0, "LCD_DATA0", 68, 7, "lcd_data0", "gpmc_a0", "pr1_mii_mt0_clk", "ehrpwm2A", NULL, "pr1_pru1_pru_r30_0", "pr1_pru1_pru_r31_0", "gpio2_6"),
- _PIN(0x0a4, "LCD_DATA1", 69, 7, "lcd_data1", "gpmc_a1", "pr1_mii0_txen", "ehrpwm2B", NULL, "pr1_pru1_pru_r30_1", "pr1_pru1_pru_r31_1", "gpio2_7"),
- _PIN(0x0a8, "LCD_DATA2", 70, 7, "lcd_data2", "gpmc_a2", "pr1_mii0_txd3", "ehrpwm2_tripzone_input", NULL, "pr1_pru1_pru_r30_2", "pr1_pru1_pru_r31_2", "gpio2_8"),
- _PIN(0x0ac, "LCD_DATA3", 71, 7, "lcd_data3", "gpmc_a3", "pr1_mii0_txd2", "ehrpwm0_synco", NULL, "pr1_pru1_pru_r30_3", "pr1_pru1_pru_r31_3", "gpio2_9"),
- _PIN(0x0b0, "LCD_DATA4", 72, 7, "lcd_data4", "gpmc_a4", "pr1_mii0_txd1", "eQEP2A_in", NULL, "pr1_pru1_pru_r30_4", "pr1_pru1_pru_r31_4", "gpio2_10"),
- _PIN(0x0b4, "LCD_DATA5", 73, 7, "lcd_data5", "gpmc_a5", "pr1_mii0_txd0", "eQEP2B_in", NULL, "pr1_pru1_pru_r30_5", "pr1_pru1_pru_r31_5", "gpio2_11"),
- _PIN(0x0b8, "LCD_DATA6", 74, 7, "lcd_data6", "gpmc_a6", "pr1_edio_data_in6", "eQEP2_index", "pr1_edio_data_out6", "pr1_pru1_pru_r30_6", "pr1_pru1_pru_r31_6", "gpio2_12"),
- _PIN(0x0bc, "LCD_DATA7", 75, 7, "lcd_data7", "gpmc_a7", "pr1_edio_data_in7", "eQEP2_strobe", "pr1_edio_data_out7", "pr1_pru1_pru_r30_7", "pr1_pru1_pru_r31_7", "gpio2_13"),
- _PIN(0x0c0, "LCD_DATA8", 76, 7, "lcd_data8", "gpmc_a12", "ehrpwm1_tripzone_input", "mcasp0_aclkx", "uart5_txd", "pr1_mii0_rxd3", "uart2_ctsn", "gpio2_14"),
- _PIN(0x0c4, "LCD_DATA9", 76, 7, "lcd_data9", "gpmc_a13", "ehrpwm0_synco", "mcasp0_fsx", "uart5_rxd", "pr1_mii0_rxd2", "uart2_rtsn", "gpio2_15"),
- _PIN(0x0c8, "LCD_DATA10", 77, 7, "lcd_data10", "gpmc_a14", "ehrpwm1A", "mcasp0_axr0", NULL, "pr1_mii0_rxd1", "uart3_ctsn", "gpio2_16"),
- _PIN(0x0cc, "LCD_DATA11", 78, 7, "lcd_data11", "gpmc_a15", "ehrpwm1B", "mcasp0_ahclkr", "mcasp0_axr2", "pr1_mii0_rxd0", "uart3_rtsn", "gpio2_17"),
- _PIN(0x0d0, "LCD_DATA12", 8, 7, "lcd_data12", "gpmc_a16", "eQEP1A_in", "mcasp0_aclkr", "mcasp0_axr2", "pr1_mii0_rxlink", "uart4_ctsn", "gpio0_8"),
- _PIN(0x0d4, "LCD_DATA13", 9, 7, "lcd_data13", "gpmc_a17", "eQEP1B_in", "mcasp0_fsr", "mcasp0_axr3", "pr1_mii0_rxer", "uart4_rtsn", "gpio0_9"),
- _PIN(0x0d8, "LCD_DATA14", 10, 7, "lcd_data14", "gpmc_a18", "eQEP1_index", "mcasp0_axr1", "uart5_rxd", "pr1_mii_mr0_clk", "uart5_ctsn", "gpio0_10"),
- _PIN(0x0dc, "LCD_DATA15", 11, 7, "lcd_data15", "gpmc_a19", "eQEP1_strobe", "mcasp0_ahclkx", "mcasp0_axr3", "pr1_mii0_rxdv", "uart5_rtsn", "gpio0_11"),
- _PIN(0x0e0, "LCD_VSYNC", 86, 7, "lcd_vsync", "gpmc_a8", "gpmc_a1", "pr1_edio_data_in2", "pr1_edio_data_out2", "pr1_pru1_pru_r30_8", "pr1_pru1_pru_r31_8", "gpio2_22"),
- _PIN(0x0e4, "LCD_HSYNC", 87, 7, "lcd_hsync", "gmpc_a9", "gpmc_a2", "pr1_edio_data_in3", "pr1_edio_data_out3", "pr1_pru1_pru_r30_9", "pr1_pru1_pru_r31_9", "gpio2_23"),
- _PIN(0x0e8, "LCD_PCLK", 88, 7, "lcd_pclk", "gpmc_a10", "pr1_mii0_crs", "pr1_edio_data_in4", "pr1_edio_data_out4", "pr1_pru1_pru_r30_10", "pr1_pru1_pru_r31_10", "gpio2_24"),
- _PIN(0x0ec, "LCD_AC_BIAS_EN", 89, 7, "lcd_ac_bias_en", "gpmc_a11", "pr1_mii1_crs", "pr1_edio_data_in5", "pr1_edio_data_out5", "pr1_pru1_pru_r30_11", "pr1_pru1_pru_r31_11", "gpio2_25"),
- _PIN(0x0f0, "MMC0_DAT3", 90, 7, "mmc0_dat3", "gpmc_a20", "uart4_ctsn", "timer5", "uart1_dcdn", "pr1_pru0_pru_r30_8", "pr1_pru0_pru_r31_8", "gpio2_26"),
- _PIN(0x0f4, "MMC0_DAT2", 91, 7, "mmc0_dat2", "gpmc_a21", "uart4_rtsn", "timer6", "uart1_dsrn", "pr1_pru0_pru_r30_9", "pr1_pru0_pru_r31_9", "gpio2_27"),
- _PIN(0x0f8, "MMC0_DAT1", 92, 7, "mmc0_dat1", "gpmc_a22", "uart5_ctsn", "uart3_rxd", "uart1_dtrn", "pr1_pru0_pru_r30_10", "pr1_pru0_pru_r31_10", "gpio2_28"),
- _PIN(0x0fc, "MMC0_DAT0", 93, 7, "mmc0_dat0", "gpmc_a23", "uart5_rtsn", "uart3_txd", "uart1_rin", "pr1_pru0_pru_r30_11", "pr1_pru0_pru_r31_11", "gpio2_29"),
- _PIN(0x100, "MMC0_CLK", 94, 7, "mmc0_clk", "gpmc_a24", "uart3_ctsn", "uart2_rxd", "dcan1_tx", "pr1_pru0_pru_r30_12", "pr1_pru0_pru_r31_12", "gpio2_30"),
- _PIN(0x104, "MMC0_CMD", 95, 7, "mmc0_cmd", "gpmc_a25", "uart3_rtsn", "uart2_txd", "dcan1_rx", "pr1_pru0_pru_r30_13", "pr1_pru0_pru_r31_13", "gpio2_31"),
- _PIN(0x108, "MII1_COL", 96, 7, "gmii1_col", "rmii2_refclk", "spi1_sclk", "uart5_rxd", "mcasp1_axr2", "mmc2_dat3", "mcasp0_axr2", "gpio3_0"),
- _PIN(0x10c, "MII1_CRS", 97, 7, "gmii1_crs", "rmii1_crs_dv", "spi1_d0", "I2C1_SDA", "mcasp1_aclkx", "uart5_ctsn", "uart2_rxd", "gpio3_1"),
- _PIN(0x110, "MII1_RX_ER", 98, 7, "gmii1_rxerr", "rmii1_rxerr", "spi1_d1", "I2C1_SCL", "mcasp1_fsx", "uart5_rtsn", "uart2_txd", "gpio3_2"),
- _PIN(0x114, "MII1_TX_EN", 99, 7, "gmii1_txen", "rmii1_txen", "rgmii1_tctl", "timer4", "mcasp1_axr0", "eQEP0_index", "mmc2_cmd", "gpio3_3"),
- _PIN(0x118, "MII1_RX_DV", 100, 7, "gmii1_rxdv", "cd_memory_clk", "rgmii1_rctl", "uart5_txd", "mcasp1_aclkx", "mmc2_dat0", "mcasp0_aclkr", "gpio3_4"),
- _PIN(0x11c, "MII1_TXD3", 16, 7, "gmii1_txd3", "dcan0_tx", "rgmii1_td3", "uart4_rxd", "mcasp1_fsx", "mmc2_dat1", "mcasp0_fsr", "gpio0_16"),
- _PIN(0x120, "MII1_TXD2", 17, 7, "gmii1_txd2", "dcan0_rx", "rgmii1_td2", "uart4_txd", "mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"),
- _PIN(0x124, "MII1_TXD1", 21, 7, "gmii1_txd1", "rmii1_txd1", "rgmii1_td1", "mcasp1_fsr", "mcasp1_axr1", "eQEP0A_in", "mmc1_cmd", "gpio0_21"),
- _PIN(0x128, "MII1_TXD0", 28, 7, "gmii1_txd0", "rmii1_txd0", "rgmii1_td0", "mcasp1_axr2", "mcasp1_aclkr", "eQEP0B_in", "mmc1_clk", "gpio0_28"),
- _PIN(0x12c, "MII1_TX_CLK", 105, 7, "gmii1_txclk", "uart2_rxd", "rgmii1_tclk", "mmc0_dat7", "mmc1_dat0", "uart1_dcdn", "mcasp0_aclkx", "gpio3_9"),
- _PIN(0x130, "MII1_RX_CLK", 106, 7, "gmii1_rxclk", "uart2_txd", "rgmii1_rclk", "mmc0_dat6", "mmc1_dat1", "uart1_dsrn", "mcasp0_fsx", "gpio3_10"),
- _PIN(0x134, "MII1_RXD3", 82, 7, "gmii1_rxd3", "uart3_rxd", "rgmii1_rd3", "mmc0_dat5", "mmc1_dat2", "uart1_dtrn", "mcasp0_axr0", "gpio2_18"),
- _PIN(0x138, "MII1_RXD2", 83, 7, "gmii1_rxd2", "uart3_txd", "rgmii1_rd2", "mmc0_dat4", "mmc1_dat3", "uart1_rin", "mcasp0_axr1", "gpio2_19"),
- _PIN(0x13c, "MII1_RXD1", 84, 7, "gmii1_rxd1", "rmii1_rxd1", "rgmii1_rd1", "mcasp1_axr3", "mcasp1_fsr", "eQEP0_strobe", "mmc2_clk", "gpio2_20"),
- _PIN(0x140, "MII1_RXD0", 85, 7, "gmii1_rxd0", "rmii1_rxd0", "rgmii1_rd0", "mcasp1_ahclkx", "mcasp1_ahclkr", "mcasp1_aclkr", "mcasp0_axr3", "gpio2_21"),
- _PIN(0x144, "RMII1_REF_CLK", 29, 7, "rmii1_refclk", "xdma_event_intr2", "spi1_cs0", "uart5_txd", "mcasp1_axr3", "mmc0_pow", "mcasp1_ahclkx", "gpio0_29"),
- _PIN(0x148, "MDIO", 0, 7, "mdio_data", "timer6", "uart5_rxd", "uart3_ctsn", "mmc0_sdcd","mmc1_cmd", "mmc2_cmd","gpio0_0"),
- _PIN(0x14c, "MDC", 1, 7, "mdio_clk", "timer5", "uart5_txd", "uart3_rtsn", "mmc0_sdwp", "mmc1_clk", "mmc2_clk", "gpio0_1"),
- _PIN(0x150, "SPI0_SCLK", 2, 7, "spi0_sclk", "uart2_rxd", "I2C2_SDA", "ehrpwm0A", "pr1_uart0_cts_n", "pr1_edio_sof", "EMU2", "gpio0_2"),
- _PIN(0x154, "SPI0_D0", 3, 7, "spi0_d0", "uart2_txd", "I2C2_SCL", "ehrpwm0B", "pr1_uart0_rts_n", "pr1_edio_latch_in", "EMU3", "gpio0_3"),
- _PIN(0x158, "SPI0_D1", 4, 7, "spi0_d1", "mmc1_sdwp", "I2C1_SDA", "ehrpwm0_tripzone_input", "pr1_uart0_rxd", "pr1_edio_data_in0", "pr1_edio_data_out0", "gpio0_4"),
- _PIN(0x15c, "SPI0_CS0", 5, 7, "spi0_cs0", "mmc2_sdwp", "I2C1_SCL", "ehrpwm0_synci", "pr1_uart0_txd", "pr1_edio_data_in1", "pr1_edio_data_out1", "gpio0_5"),
- _PIN(0x160, "SPI0_CS1", 6, 7, "spi0_cs1", "uart3_rxd", "eCAP1_in_PWM1_out", "mcc0_pow", "xdm_event_intr2", "mmc0_sdcd", "EMU4", "gpio0_6"),
- _PIN(0x164, "ECAP0_IN_PWM0_OUT",7, 7, "eCAP0_in_PWM0_out", "uart3_txd", "spi1_cs1", "pr1_ecap0_ecap_capin_apwm_o", "spi1_sclk", "mmc0_sdwp", "xdma_event_intr2", "gpio0_7"),
- _PIN(0x168, "UART0_CTSn", 40, 7, "uart0_ctsn", "uart4_rxd", "dcan1_tx", "I2C1_SDA", "spi1_d0", "timer7", "pr1_edc_sync0_out", "gpio1_8"),
- _PIN(0x16c, "UART0_RTSn", 41, 7, "uart0_rtsn", "uart4_txd", "dcan1_rx", "I2C1_SCL", "spi1_d1", "spi1_cs0", "pr1_edc_sync1_out", "gpio1_9"),
- _PIN(0x170, "UART0_rxd", 42, 7, "uart0_rxd", "spi1_cs0", "dcan0_tx", "I2C2_SDA", "eCAP2_in_PWM2_out", "pr1_pru1_pru_r30_14", "pr1_pru1_pru_r31_14", "gpio1_10"),
- _PIN(0x174, "UART0_txd", 43, 7, "uart0_txd", "spi1_cs1", "dcan0_rx", "I2C2_SCL", "eCAP1_in_PWM1_out", "pr1_pru1_pru_r30_15", "pr1_pru1_pru_r31_15", "gpio1_11"),
- _PIN(0x178, "UART1_CTSn", 12, 7, "uart1_ctsn", "timer6_mux1", "dcan0_tx", "I2C2_SDA", "spi1_cs0", "pr1_uart0_cts_n", "pr1_edc_latch0_in", "gpio0_12"),
- _PIN(0x17c, "UART1_RTSn", 13, 7, "uart1_rtsn", "timer5_mux1", "dcan0_rx", "I2C2_SCL", "spi1_cs1", "pr1_uart0_rts_n", "pr1_edc_latch1_in", "gpio0_13"),
- _PIN(0x180, "UART1_RXD", 14, 7, "uart1_rxd", "mmc1_sdwp", "dcan1_tx", "I2C1_SDA", NULL, "pr1_uart0_rxd", "pr1_pru1_pru_r31_16", "gpio0_14"),
- _PIN(0x184, "UART1_TXD", 15, 7, "uart1_txd", "mmc2_sdwp", "dcan1_rx", "I2C1_SCL", NULL, "pr1_uart0_txd", "pr1_pru0_pru_r31_16", "gpio0_15"),
- _PIN(0x188, "I2C0_SDA", 101, 7, "I2C0_SDA", "timer4", "uart2_ctsn", "eCAP2_in_PWM2_out", NULL, NULL, NULL, "gpio3_5"),
- _PIN(0x18c, "I2C0_SCL", 102, 7, "I2C0_SCL", "timer7", "uart2_rtsn", "eCAP1_in_PWM1_out", NULL, NULL, NULL, "gpio3_6"),
- _PIN(0x190, "MCASP0_ACLKX", 110, 7, "mcasp0_aclkx", "ehrpwm0A", NULL, "spi1_sclk", "mmc0_sdcd", "pr1_pru0_pru_r30_0", "pr1_pru0_pru_r31_0", "gpio3_14"),
- _PIN(0x194, "MCASP0_FSX", 111, 7, "mcasp0_fsx", "ehrpwm0B", NULL, "spi1_d0", "mmc1_sdcd", "pr1_pru0_pru_r30_1", "pr1_pru0_pru_r31_1", "gpio3_15"),
- _PIN(0x198, "MCASP0_AXR0", 112, 7, "mcasp0_axr0", "ehrpwm0_tripzone_input", NULL, "spi1_d1", "mmc2_sdcd", "pr1_pru0_pru_r30_2", "pr1_pru0_pru_r31_2", "gpio3_16"),
- _PIN(0x19c, "MCASP0_AHCLKR", 113, 7, "mcasp0_ahclkr", "ehrpwm0_synci", "mcasp0_axr2", "spi1_cs0", "eCAP2_in_PWM2_out", "pr1_pru0_pru_r30_3", "pr1_pru0_pru_r31_3", "gpio3_17"),
- _PIN(0x1a0, "MCASP0_ACLKR", 114, 7, "mcasp0_aclkr", "eQEP0A_in", "mcasp0_axr2", "mcasp1_aclkx", "mmc0_sdwp", "pr1_pru0_pru_r30_4", "pr1_pru0_pru_r31_4", "gpio3_18"),
- _PIN(0x1a4, "MCASP0_FSR", 115, 7, "mcasp0_fsr", "eQEP0B_in", "mcasp0_axr3", "mcasp1_fsx", "EMU2", "pr1_pru0_pru_r30_5", "pr1_pru0_pru_r31_5", "gpio3_19"),
- _PIN(0x1a8, "MCASP0_AXR1", 116, 7, "mcasp0_axr1", "eQEP0_index", NULL, "mcasp1_axr0", "EMU3", "pr1_pru0_pru_r30_6", "pr1_pru0_pru_r31_6", "gpio3_20"),
- _PIN(0x1ac, "MCASP0_AHCLKX", 117, 7, "mcasp0_ahclkx", "eQEP0_strobe", "mcasp0_axr3", "mcasp1_axr1", "EMU4", "pr1_pru0_pru_r30_7", "pr1_pru0_pru_r31_7", "gpio3_21"),
- _PIN(0x1b0, "XDMA_EVENT_INTR0", 19, 7, "xdma_event_intr0", NULL, "timer4", "clkout1", "spi1_cs1", "pr1_pru1_pru_r31_16", "EMU2", "gpio0_19"),
- _PIN(0x1b4, "XDMA_EVENT_INTR1", 20, 7, "xdma_event_intr1", NULL, "tclkin", "clkout2", "timer7", "pr1_pru0_pru_r31_16", "EMU3", "gpio0_20"),
-#if 0
- _PIN(0x1b8, "nresetin_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1bc, "porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1c0, "nnmi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1c4, "osc0_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1c8, "osc0_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1cc, "osc0_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1d0, "tms", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1d4, "tdi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1d8, "tdo", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1dc, "tck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1e0, "ntrst", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
-#endif
- _PIN(0x1e4, "EMU0", 103, 7, "EMU0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_7"),
- _PIN(0x1e8, "EMU1", 104, 0, "EMU1", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_8"),
-#if 0
- _PIN(0x1ec, "osc1_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1f0, "osc1_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1f4, "osc1_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1f8, "rtc_porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x1fc, "pmic_power_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x200, "ext_wakeup", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x204, "enz_kaldo_1p8v", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
-#endif
- _PIN(0x208, "USB0_DM", 0, 0, "USB0_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x20c, "USB0_DP", 0, 0, "USB0_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x210, "USB0_CE", 0, 0, "USB0_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x214, "USB0_ID", 0, 0, "USB0_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x218, "USB0_VBUS", 0, 0, "USB0_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x21c, "USB0_DRVVBUS", 18, 7, "USB0_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio0_18"),
- _PIN(0x220, "USB1_DM", 0, 0, "USB1_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x224, "USB1_DP", 0, 0, "USB1_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x228, "USB1_CE", 0, 0, "USB1_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x22c, "USB1_ID", 0, 0, "USB1_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x230, "USB1_VBUS", 0, 0, "USB1_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x234, "USB1_DRVVBUS", 109, 7, "USB1_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_13"),
-#if 0
- _PIN(0x238, "ddr_resetn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x23c, "ddr_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x240, "ddr_cke", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x244, "ddr_ck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x248, "ddr_nck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x24c, "ddr_casn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x250, "ddr_rasn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x254, "ddr_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x258, "ddr_ba0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x25c, "ddr_ba1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x260, "ddr_ba2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x264, "ddr_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x268, "ddr_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x26c, "ddr_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x270, "ddr_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x274, "ddr_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x278, "ddr_a5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x27c, "ddr_a6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x280, "ddr_a7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x284, "ddr_a8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x288, "ddr_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x28c, "ddr_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x290, "ddr_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x294, "ddr_a12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x298, "ddr_a13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x29c, "ddr_a14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2a0, "ddr_a15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2a4, "ddr_odt", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2a8, "ddr_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2ac, "ddr_d1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2b0, "ddr_d2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2b4, "ddr_d3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2b8, "ddr_d4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2bc, "ddr_d5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2c0, "ddr_d6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2c4, "ddr_d7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2c8, "ddr_d8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2cc, "ddr_d9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2d0, "ddr_d10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2d4, "ddr_d11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2d8, "ddr_d12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2dc, "ddr_d13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2e0, "ddr_d14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2e4, "ddr_d15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2e8, "ddr_dqm0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2ec, "ddr_dqm1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2f0, "ddr_dqs0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2f4, "ddr_dqsn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2f8, "ddr_dqs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x2fc, "ddr_dqsn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x300, "ddr_vref", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x304, "ddr_vtp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x308, "ddr_strben0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x30c, "ddr_strben1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x32c, "ain0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x328, "ain1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x324, "ain2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x320, "ain3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x31c, "ain4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x318, "ain5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x314, "ain6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x310, "ain7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x330, "vrefp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x334, "vrefn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x338, "avdd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x33c, "avss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x340, "iforce", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x344, "vsense", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
- _PIN(0x348, "testout", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
-#endif
- { .ballname = NULL },
-};
-
-const struct ti_pinmux_device ti_am335x_pinmux_dev = {
- .padconf_muxmode_mask = 0x7,
- .padconf_sate_mask = 0x78,
- .padstate = ti_padstate_devmap,
- .padconf = ti_padconf_devmap,
-};
diff --git a/freebsd/sys/arm/ti/ti_pinmux.c b/freebsd/sys/arm/ti/ti_pinmux.c
deleted file mode 100644
index af47d074..00000000
--- a/freebsd/sys/arm/ti/ti_pinmux.c
+++ /dev/null
@@ -1,463 +0,0 @@
-#include <machine/rtems-bsd-kernel-space.h>
-
-/*
- * Copyright (c) 2010
- * Ben Gray <ben.r.gray@gmail.com>.
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Ben Gray.
- * 4. The name of the company nor the name of the author may be used to
- * endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
- */
-
-/**
- * Exposes pinmux module to pinctrl-compatible interface
- */
-#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 <rtems/bsd/sys/resource.h>
-#include <sys/rman.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-
-#include <machine/bus.h>
-#include <machine/resource.h>
-
-#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-#include <dev/fdt/fdt_pinctrl.h>
-
-#include <arm/ti/omap4/omap4_scm_padconf.h>
-#include <arm/ti/am335x/am335x_scm_padconf.h>
-#include <arm/ti/ti_cpuid.h>
-#include "ti_pinmux.h"
-
-struct pincfg {
- uint32_t reg;
- uint32_t conf;
-};
-
-static struct resource_spec ti_pinmux_res_spec[] = {
- { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */
- { -1, 0 }
-};
-
-static struct ti_pinmux_softc *ti_pinmux_sc;
-
-#define ti_pinmux_read_2(sc, reg) \
- bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg))
-#define ti_pinmux_write_2(sc, reg, val) \
- bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
-#define ti_pinmux_read_4(sc, reg) \
- bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
-#define ti_pinmux_write_4(sc, reg, val) \
- bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
-
-
-/**
- * ti_padconf_devmap - Array of pins, should be defined one per SoC
- *
- * This array is typically defined in one of the targeted *_scm_pinumx.c
- * files and is specific to the given SoC platform. Each entry in the array
- * corresponds to an individual pin.
- */
-static const struct ti_pinmux_device *ti_pinmux_dev;
-
-
-/**
- * ti_pinmux_padconf_from_name - searches the list of pads and returns entry
- * with matching ball name.
- * @ballname: the name of the ball
- *
- * RETURNS:
- * A pointer to the matching padconf or NULL if the ball wasn't found.
- */
-static const struct ti_pinmux_padconf*
-ti_pinmux_padconf_from_name(const char *ballname)
-{
- const struct ti_pinmux_padconf *padconf;
-
- padconf = ti_pinmux_dev->padconf;
- while (padconf->ballname != NULL) {
- if (strcmp(ballname, padconf->ballname) == 0)
- return(padconf);
- padconf++;
- }
-
- return (NULL);
-}
-
-/**
- * ti_pinmux_padconf_set_internal - sets the muxmode and state for a pad/pin
- * @padconf: pointer to the pad structure
- * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
- * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
- *
- *
- * LOCKING:
- * Internally locks it's own context.
- *
- * RETURNS:
- * 0 on success.
- * EINVAL if pin requested is outside valid range or already in use.
- */
-static int
-ti_pinmux_padconf_set_internal(struct ti_pinmux_softc *sc,
- const struct ti_pinmux_padconf *padconf,
- const char *muxmode, unsigned int state)
-{
- unsigned int mode;
- uint16_t reg_val;
-
- /* populate the new value for the PADCONF register */
- reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask);
-
- /* find the new mode requested */
- for (mode = 0; mode < 8; mode++) {
- if ((padconf->muxmodes[mode] != NULL) &&
- (strcmp(padconf->muxmodes[mode], muxmode) == 0)) {
- break;
- }
- }
-
- /* couldn't find the mux mode */
- if (mode >= 8) {
- printf("Invalid mode \"%s\"\n", muxmode);
- return (EINVAL);
- }
-
- /* set the mux mode */
- reg_val |= (uint16_t)(mode & ti_pinmux_dev->padconf_muxmode_mask);
-
- if (bootverbose)
- device_printf(sc->sc_dev, "setting internal %x for %s\n",
- reg_val, muxmode);
- /* write the register value (16-bit writes) */
- ti_pinmux_write_2(sc, padconf->reg_off, reg_val);
-
- return (0);
-}
-
-/**
- * ti_pinmux_padconf_set - sets the muxmode and state for a pad/pin
- * @padname: the name of the pad, i.e. "c12"
- * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
- * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
- *
- *
- * LOCKING:
- * Internally locks it's own context.
- *
- * RETURNS:
- * 0 on success.
- * EINVAL if pin requested is outside valid range or already in use.
- */
-int
-ti_pinmux_padconf_set(const char *padname, const char *muxmode, unsigned int state)
-{
- const struct ti_pinmux_padconf *padconf;
-
- if (!ti_pinmux_sc)
- return (ENXIO);
-
- /* find the pin in the devmap */
- padconf = ti_pinmux_padconf_from_name(padname);
- if (padconf == NULL)
- return (EINVAL);
-
- return (ti_pinmux_padconf_set_internal(ti_pinmux_sc, padconf, muxmode, state));
-}
-
-/**
- * ti_pinmux_padconf_get - gets the muxmode and state for a pad/pin
- * @padname: the name of the pad, i.e. "c12"
- * @muxmode: upon return will contain the name of the muxmode of the pin
- * @state: upon return will contain the state of the pad/pin
- *
- *
- * LOCKING:
- * Internally locks it's own context.
- *
- * RETURNS:
- * 0 on success.
- * EINVAL if pin requested is outside valid range or already in use.
- */
-int
-ti_pinmux_padconf_get(const char *padname, const char **muxmode,
- unsigned int *state)
-{
- const struct ti_pinmux_padconf *padconf;
- uint16_t reg_val;
-
- if (!ti_pinmux_sc)
- return (ENXIO);
-
- /* find the pin in the devmap */
- padconf = ti_pinmux_padconf_from_name(padname);
- if (padconf == NULL)
- return (EINVAL);
-
- /* read the register value (16-bit reads) */
- reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off);
-
- /* save the state */
- if (state)
- *state = (reg_val & ti_pinmux_dev->padconf_sate_mask);
-
- /* save the mode */
- if (muxmode)
- *muxmode = padconf->muxmodes[(reg_val & ti_pinmux_dev->padconf_muxmode_mask)];
-
- return (0);
-}
-
-/**
- * ti_pinmux_padconf_set_gpiomode - converts a pad to GPIO mode.
- * @gpio: the GPIO pin number (0-195)
- * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
- *
- *
- *
- * LOCKING:
- * Internally locks it's own context.
- *
- * RETURNS:
- * 0 on success.
- * EINVAL if pin requested is outside valid range or already in use.
- */
-int
-ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state)
-{
- const struct ti_pinmux_padconf *padconf;
- uint16_t reg_val;
-
- if (!ti_pinmux_sc)
- return (ENXIO);
-
- /* find the gpio pin in the padconf array */
- padconf = ti_pinmux_dev->padconf;
- while (padconf->ballname != NULL) {
- if (padconf->gpio_pin == gpio)
- break;
- padconf++;
- }
- if (padconf->ballname == NULL)
- return (EINVAL);
-
- /* populate the new value for the PADCONF register */
- reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask);
-
- /* set the mux mode */
- reg_val |= (uint16_t)(padconf->gpio_mode & ti_pinmux_dev->padconf_muxmode_mask);
-
- /* write the register value (16-bit writes) */
- ti_pinmux_write_2(ti_pinmux_sc, padconf->reg_off, reg_val);
-
- return (0);
-}
-
-/**
- * ti_pinmux_padconf_get_gpiomode - gets the current GPIO mode of the pin
- * @gpio: the GPIO pin number (0-195)
- * @state: upon return will contain the state
- *
- *
- *
- * LOCKING:
- * Internally locks it's own context.
- *
- * RETURNS:
- * 0 on success.
- * EINVAL if pin requested is outside valid range or not configured as GPIO.
- */
-int
-ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state)
-{
- const struct ti_pinmux_padconf *padconf;
- uint16_t reg_val;
-
- if (!ti_pinmux_sc)
- return (ENXIO);
-
- /* find the gpio pin in the padconf array */
- padconf = ti_pinmux_dev->padconf;
- while (padconf->ballname != NULL) {
- if (padconf->gpio_pin == gpio)
- break;
- padconf++;
- }
- if (padconf->ballname == NULL)
- return (EINVAL);
-
- /* read the current register settings */
- reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off);
-
- /* check to make sure the pins is configured as GPIO in the first state */
- if ((reg_val & ti_pinmux_dev->padconf_muxmode_mask) != padconf->gpio_mode)
- return (EINVAL);
-
- /* read and store the reset of the state, i.e. pull-up, pull-down, etc */
- if (state)
- *state = (reg_val & ti_pinmux_dev->padconf_sate_mask);
-
- return (0);
-}
-
-static int
-ti_pinmux_configure_pins(device_t dev, phandle_t cfgxref)
-{
- struct pincfg *cfgtuples, *cfg;
- phandle_t cfgnode;
- int i, ntuples;
- static struct ti_pinmux_softc *sc;
-
- sc = device_get_softc(dev);
- cfgnode = OF_node_from_xref(cfgxref);
- ntuples = OF_getencprop_alloc_multi(cfgnode, "pinctrl-single,pins",
- sizeof(*cfgtuples), (void **)&cfgtuples);
-
- if (ntuples < 0)
- return (ENOENT);
-
- if (ntuples == 0)
- return (0); /* Empty property is not an error. */
-
- for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) {
- if (bootverbose) {
- char name[32];
- OF_getprop(cfgnode, "name", &name, sizeof(name));
- printf("%16s: muxreg 0x%04x muxval 0x%02x\n",
- name, cfg->reg, cfg->conf);
- }
-
- /* write the register value (16-bit writes) */
- ti_pinmux_write_2(sc, cfg->reg, cfg->conf);
- }
-
- OF_prop_free(cfgtuples);
-
- return (0);
-}
-
-/*
- * Device part of OMAP SCM driver
- */
-
-static int
-ti_pinmux_probe(device_t dev)
-{
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- if (!ofw_bus_is_compatible(dev, "pinctrl-single"))
- return (ENXIO);
-
- if (ti_pinmux_sc) {
- printf("%s: multiple pinctrl modules in device tree data, ignoring\n",
- __func__);
- return (EEXIST);
- }
- switch (ti_chip()) {
-#ifdef SOC_OMAP4
- case CHIP_OMAP_4:
- ti_pinmux_dev = &omap4_pinmux_dev;
- break;
-#endif
-#ifdef SOC_TI_AM335X
- case CHIP_AM335X:
- ti_pinmux_dev = &ti_am335x_pinmux_dev;
- break;
-#endif
- default:
- printf("Unknown CPU in pinmux\n");
- return (ENXIO);
- }
-
-
- device_set_desc(dev, "TI Pinmux Module");
- return (BUS_PROBE_DEFAULT);
-}
-
-/**
- * ti_pinmux_attach - attaches the pinmux to the simplebus
- * @dev: new device
- *
- * RETURNS
- * Zero on success or ENXIO if an error occuried.
- */
-static int
-ti_pinmux_attach(device_t dev)
-{
- struct ti_pinmux_softc *sc = device_get_softc(dev);
-
-#if 0
- if (ti_pinmux_sc)
- return (ENXIO);
-#endif
-
- sc->sc_dev = dev;
-
- if (bus_alloc_resources(dev, ti_pinmux_res_spec, sc->sc_res)) {
- device_printf(dev, "could not allocate resources\n");
- return (ENXIO);
- }
-
- sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
- sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
-
- if (ti_pinmux_sc == NULL)
- ti_pinmux_sc = sc;
-
- fdt_pinctrl_register(dev, "pinctrl-single,pins");
- fdt_pinctrl_configure_tree(dev);
-
- return (0);
-}
-
-static device_method_t ti_pinmux_methods[] = {
- DEVMETHOD(device_probe, ti_pinmux_probe),
- DEVMETHOD(device_attach, ti_pinmux_attach),
-
- /* fdt_pinctrl interface */
- DEVMETHOD(fdt_pinctrl_configure, ti_pinmux_configure_pins),
- { 0, 0 }
-};
-
-static driver_t ti_pinmux_driver = {
- "ti_pinmux",
- ti_pinmux_methods,
- sizeof(struct ti_pinmux_softc),
-};
-
-static devclass_t ti_pinmux_devclass;
-
-DRIVER_MODULE(ti_pinmux, simplebus, ti_pinmux_driver, ti_pinmux_devclass, 0, 0);
diff --git a/freebsd/sys/arm/ti/ti_pinmux.h b/freebsd/sys/arm/ti/ti_pinmux.h
deleted file mode 100644
index c299c494..00000000
--- a/freebsd/sys/arm/ti/ti_pinmux.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2010
- * Ben Gray <ben.r.gray@gmail.com>.
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Ben Gray.
- * 4. The name of the company nor the name of the author may be used to
- * endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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$
- */
-
-
-/**
- * Functions to configure the PIN multiplexing on the chip.
- *
- * This is different from the GPIO module in that it is used to configure the
- * pins between modules not just GPIO input output.
- *
- */
-#ifndef _TI_PINMUX_H_
-#define _TI_PINMUX_H_
-
-struct ti_pinmux_padconf {
- uint16_t reg_off;
- uint16_t gpio_pin;
- uint16_t gpio_mode;
- const char *ballname;
- const char *muxmodes[8];
-};
-
-struct ti_pinmux_padstate {
- const char *state;
- uint16_t reg;
-};
-
-struct ti_pinmux_device {
- uint16_t padconf_muxmode_mask;
- uint16_t padconf_sate_mask;
- const struct ti_pinmux_padstate *padstate;
- const struct ti_pinmux_padconf *padconf;
-};
-
-struct ti_pinmux_softc {
- device_t sc_dev;
- struct resource * sc_res[4];
- bus_space_tag_t sc_bst;
- bus_space_handle_t sc_bsh;
-};
-
-int ti_pinmux_padconf_set(const char *padname, const char *muxmode,
- unsigned int state);
-int ti_pinmux_padconf_get(const char *padname, const char **muxmode,
- unsigned int *state);
-int ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state);
-int ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state);
-
-#endif /* _TI_SCM_H_ */
diff --git a/freebsd/sys/arm64/arm64/in_cksum.c b/freebsd/sys/arm64/arm64/in_cksum.c
new file mode 100644
index 00000000..a2c6cd1e
--- /dev/null
+++ b/freebsd/sys/arm64/arm64/in_cksum.c
@@ -0,0 +1,243 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1996
+ * Matt Thomas <matt@3am-software.com>
+ *
+ * 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 by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers
+ * (Portable Alpha version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE32 \
+ { \
+ q_util.q = sum; \
+ sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ }
+#define REDUCE16 \
+ { \
+ q_util.q = sum; \
+ l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ sum = l_util.s[0] + l_util.s[1]; \
+ ADDCARRY(sum); \
+ }
+
+static const u_int32_t in_masks[] = {
+ /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/
+ 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */
+ 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */
+ 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */
+ 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */
+};
+
+union l_util {
+ u_int16_t s[2];
+ u_int32_t l;
+};
+union q_util {
+ u_int16_t s[4];
+ u_int32_t l[2];
+ u_int64_t q;
+};
+
+static u_int64_t
+in_cksumdata(const void *buf, int len)
+{
+ const u_int32_t *lw = (const u_int32_t *) buf;
+ u_int64_t sum = 0;
+ u_int64_t prefilled;
+ int offset;
+ union q_util q_util;
+
+ if ((3 & (long) lw) == 0 && len == 20) {
+ sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
+ REDUCE32;
+ return sum;
+ }
+
+ if ((offset = 3 & (long) lw) != 0) {
+ const u_int32_t *masks = in_masks + (offset << 2);
+ lw = (u_int32_t *) (((long) lw) - offset);
+ sum = *lw++ & masks[len >= 3 ? 3 : len];
+ len -= 4 - offset;
+ if (len <= 0) {
+ REDUCE32;
+ return sum;
+ }
+ }
+#if 0
+ /*
+ * Force to cache line boundary.
+ */
+ offset = 32 - (0x1f & (long) lw);
+ if (offset < 32 && len > offset) {
+ len -= offset;
+ if (4 & offset) {
+ sum += (u_int64_t) lw[0];
+ lw += 1;
+ }
+ if (8 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1];
+ lw += 2;
+ }
+ if (16 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ }
+#endif
+ /*
+ * access prefilling to start load of next cache line.
+ * then add current cache line
+ * save result of prefilling for loop iteration.
+ */
+ prefilled = lw[0];
+ while ((len -= 32) >= 4) {
+ u_int64_t prefilling = lw[8];
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ prefilled = prefilling;
+ }
+ if (len >= 0) {
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ } else {
+ len += 32;
+ }
+ while ((len -= 16) >= 0) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ len += 16;
+ while ((len -= 4) >= 0) {
+ sum += (u_int64_t) *lw++;
+ }
+ len += 4;
+ if (len > 0)
+ sum += (u_int64_t) (in_masks[len] & *lw);
+ REDUCE32;
+ return sum;
+}
+
+u_short
+in_addword(u_short a, u_short b)
+{
+ u_int64_t sum = a + b;
+
+ ADDCARRY(sum);
+ return (sum);
+}
+
+u_short
+in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
+{
+ u_int64_t sum;
+ union q_util q_util;
+ union l_util l_util;
+
+ sum = (u_int64_t) a + b + c;
+ REDUCE16;
+ return (sum);
+}
+
+u_short
+in_cksum_skip(struct mbuf *m, int len, int skip)
+{
+ u_int64_t sum = 0;
+ int mlen = 0;
+ int clen = 0;
+ caddr_t addr;
+ union q_util q_util;
+ union l_util l_util;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ addr = mtod(m, caddr_t) + skip;
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (; m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ mlen = m->m_len;
+ addr = mtod(m, caddr_t);
+skip_start:
+ if (len < mlen)
+ mlen = len;
+ if ((clen ^ (long) addr) & 1)
+ sum += in_cksumdata(addr, mlen) << 8;
+ else
+ sum += in_cksumdata(addr, mlen);
+
+ clen += mlen;
+ len -= mlen;
+ }
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+
+u_int in_cksum_hdr(const struct ip *ip)
+{
+ u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
+ union q_util q_util;
+ union l_util l_util;
+ REDUCE16;
+ return (~sum & 0xffff);
+}
diff --git a/freebsd/sys/arm64/include/machine/armreg.h b/freebsd/sys/arm64/include/machine/armreg.h
new file mode 100644
index 00000000..72f48aab
--- /dev/null
+++ b/freebsd/sys/arm64/include/machine/armreg.h
@@ -0,0 +1,665 @@
+/*-
+ * Copyright (c) 2013, 2014 Andrew Turner
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner 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.
+ *
+ * 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_ARMREG_H_
+#define _MACHINE_ARMREG_H_
+
+#define INSN_SIZE 4
+
+#define READ_SPECIALREG(reg) \
+({ uint64_t val; \
+ __asm __volatile("mrs %0, " __STRING(reg) : "=&r" (val)); \
+ val; \
+})
+#define WRITE_SPECIALREG(reg, val) \
+ __asm __volatile("msr " __STRING(reg) ", %0" : : "r"((uint64_t)val))
+
+#define UL(x) UINT64_C(x)
+
+/* CNTHCTL_EL2 - Counter-timer Hypervisor Control register */
+#define CNTHCTL_EVNTI_MASK (0xf << 4) /* Bit to trigger event stream */
+#define CNTHCTL_EVNTDIR (1 << 3) /* Control transition trigger bit */
+#define CNTHCTL_EVNTEN (1 << 2) /* Enable event stream */
+#define CNTHCTL_EL1PCEN (1 << 1) /* Allow EL0/1 physical timer access */
+#define CNTHCTL_EL1PCTEN (1 << 0) /*Allow EL0/1 physical counter access*/
+
+/* CPACR_EL1 */
+#define CPACR_FPEN_MASK (0x3 << 20)
+#define CPACR_FPEN_TRAP_ALL1 (0x0 << 20) /* Traps from EL0 and EL1 */
+#define CPACR_FPEN_TRAP_EL0 (0x1 << 20) /* Traps from EL0 */
+#define CPACR_FPEN_TRAP_ALL2 (0x2 << 20) /* Traps from EL0 and EL1 */
+#define CPACR_FPEN_TRAP_NONE (0x3 << 20) /* No traps */
+#define CPACR_TTA (0x1 << 28)
+
+/* CTR_EL0 - Cache Type Register */
+#define CTR_DLINE_SHIFT 16
+#define CTR_DLINE_MASK (0xf << CTR_DLINE_SHIFT)
+#define CTR_DLINE_SIZE(reg) (((reg) & CTR_DLINE_MASK) >> CTR_DLINE_SHIFT)
+#define CTR_ILINE_SHIFT 0
+#define CTR_ILINE_MASK (0xf << CTR_ILINE_SHIFT)
+#define CTR_ILINE_SIZE(reg) (((reg) & CTR_ILINE_MASK) >> CTR_ILINE_SHIFT)
+
+/* DAIF - Interrupt Mask Bits */
+#define DAIF_D_MASKED (1 << 9)
+#define DAIF_A_MASKED (1 << 8)
+#define DAIF_I_MASKED (1 << 7)
+#define DAIF_F_MASKED (1 << 6)
+
+/* DCZID_EL0 - Data Cache Zero ID register */
+#define DCZID_DZP (1 << 4) /* DC ZVA prohibited if non-0 */
+#define DCZID_BS_SHIFT 0
+#define DCZID_BS_MASK (0xf << DCZID_BS_SHIFT)
+#define DCZID_BS_SIZE(reg) (((reg) & DCZID_BS_MASK) >> DCZID_BS_SHIFT)
+
+/* ESR_ELx */
+#define ESR_ELx_ISS_MASK 0x00ffffff
+#define ISS_INSN_FnV (0x01 << 10)
+#define ISS_INSN_EA (0x01 << 9)
+#define ISS_INSN_S1PTW (0x01 << 7)
+#define ISS_INSN_IFSC_MASK (0x1f << 0)
+#define ISS_DATA_ISV (0x01 << 24)
+#define ISS_DATA_SAS_MASK (0x03 << 22)
+#define ISS_DATA_SSE (0x01 << 21)
+#define ISS_DATA_SRT_MASK (0x1f << 16)
+#define ISS_DATA_SF (0x01 << 15)
+#define ISS_DATA_AR (0x01 << 14)
+#define ISS_DATA_FnV (0x01 << 10)
+#define ISS_DATA_EA (0x01 << 9)
+#define ISS_DATA_CM (0x01 << 8)
+#define ISS_DATA_S1PTW (0x01 << 7)
+#define ISS_DATA_WnR (0x01 << 6)
+#define ISS_DATA_DFSC_MASK (0x3f << 0)
+#define ISS_DATA_DFSC_ASF_L0 (0x00 << 0)
+#define ISS_DATA_DFSC_ASF_L1 (0x01 << 0)
+#define ISS_DATA_DFSC_ASF_L2 (0x02 << 0)
+#define ISS_DATA_DFSC_ASF_L3 (0x03 << 0)
+#define ISS_DATA_DFSC_TF_L0 (0x04 << 0)
+#define ISS_DATA_DFSC_TF_L1 (0x05 << 0)
+#define ISS_DATA_DFSC_TF_L2 (0x06 << 0)
+#define ISS_DATA_DFSC_TF_L3 (0x07 << 0)
+#define ISS_DATA_DFSC_AFF_L1 (0x09 << 0)
+#define ISS_DATA_DFSC_AFF_L2 (0x0a << 0)
+#define ISS_DATA_DFSC_AFF_L3 (0x0b << 0)
+#define ISS_DATA_DFSC_PF_L1 (0x0d << 0)
+#define ISS_DATA_DFSC_PF_L2 (0x0e << 0)
+#define ISS_DATA_DFSC_PF_L3 (0x0f << 0)
+#define ISS_DATA_DFSC_EXT (0x10 << 0)
+#define ISS_DATA_DFSC_EXT_L0 (0x14 << 0)
+#define ISS_DATA_DFSC_EXT_L1 (0x15 << 0)
+#define ISS_DATA_DFSC_EXT_L2 (0x16 << 0)
+#define ISS_DATA_DFSC_EXT_L3 (0x17 << 0)
+#define ISS_DATA_DFSC_ECC (0x18 << 0)
+#define ISS_DATA_DFSC_ECC_L0 (0x1c << 0)
+#define ISS_DATA_DFSC_ECC_L1 (0x1d << 0)
+#define ISS_DATA_DFSC_ECC_L2 (0x1e << 0)
+#define ISS_DATA_DFSC_ECC_L3 (0x1f << 0)
+#define ISS_DATA_DFSC_ALIGN (0x21 << 0)
+#define ISS_DATA_DFSC_TLB_CONFLICT (0x30 << 0)
+#define ESR_ELx_IL (0x01 << 25)
+#define ESR_ELx_EC_SHIFT 26
+#define ESR_ELx_EC_MASK (0x3f << 26)
+#define ESR_ELx_EXCEPTION(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT)
+#define EXCP_UNKNOWN 0x00 /* Unkwn exception */
+#define EXCP_FP_SIMD 0x07 /* VFP/SIMD trap */
+#define EXCP_ILL_STATE 0x0e /* Illegal execution state */
+#define EXCP_SVC32 0x11 /* SVC trap for AArch32 */
+#define EXCP_SVC64 0x15 /* SVC trap for AArch64 */
+#define EXCP_MSR 0x18 /* MSR/MRS trap */
+#define EXCP_INSN_ABORT_L 0x20 /* Instruction abort, from lower EL */
+#define EXCP_INSN_ABORT 0x21 /* Instruction abort, from same EL */
+#define EXCP_PC_ALIGN 0x22 /* PC alignment fault */
+#define EXCP_DATA_ABORT_L 0x24 /* Data abort, from lower EL */
+#define EXCP_DATA_ABORT 0x25 /* Data abort, from same EL */
+#define EXCP_SP_ALIGN 0x26 /* SP slignment fault */
+#define EXCP_TRAP_FP 0x2c /* Trapped FP exception */
+#define EXCP_SERROR 0x2f /* SError interrupt */
+#define EXCP_SOFTSTP_EL0 0x32 /* Software Step, from lower EL */
+#define EXCP_SOFTSTP_EL1 0x33 /* Software Step, from same EL */
+#define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */
+#define EXCP_BRK 0x3c /* Breakpoint */
+
+/* ICC_CTLR_EL1 */
+#define ICC_CTLR_EL1_EOIMODE (1U << 1)
+
+/* ICC_IAR1_EL1 */
+#define ICC_IAR1_EL1_SPUR (0x03ff)
+
+/* ICC_IGRPEN0_EL1 */
+#define ICC_IGRPEN0_EL1_EN (1U << 0)
+
+/* ICC_PMR_EL1 */
+#define ICC_PMR_EL1_PRIO_MASK (0xFFUL)
+
+/* ICC_SGI1R_EL1 */
+#define ICC_SGI1R_EL1_TL_MASK 0xffffUL
+#define ICC_SGI1R_EL1_AFF1_SHIFT 16
+#define ICC_SGI1R_EL1_SGIID_SHIFT 24
+#define ICC_SGI1R_EL1_AFF2_SHIFT 32
+#define ICC_SGI1R_EL1_AFF3_SHIFT 48
+#define ICC_SGI1R_EL1_SGIID_MASK 0xfUL
+#define ICC_SGI1R_EL1_IRM (0x1UL << 40)
+
+/* ICC_SRE_EL1 */
+#define ICC_SRE_EL1_SRE (1U << 0)
+
+/* ICC_SRE_EL2 */
+#define ICC_SRE_EL2_SRE (1U << 0)
+#define ICC_SRE_EL2_EN (1U << 3)
+
+/* ID_AA64DFR0_EL1 */
+#define ID_AA64DFR0_MASK UL(0x0000000ff0f0ffff)
+#define ID_AA64DFR0_DebugVer_SHIFT 0
+#define ID_AA64DFR0_DebugVer_MASK (UL(0xf) << ID_AA64DFR0_DebugVer_SHIFT)
+#define ID_AA64DFR0_DebugVer(x) ((x) & ID_AA64DFR0_DebugVer_MASK)
+#define ID_AA64DFR0_DebugVer_8 (UL(0x6) << ID_AA64DFR0_DebugVer_SHIFT)
+#define ID_AA64DFR0_DebugVer_8_VHE (UL(0x7) << ID_AA64DFR0_DebugVer_SHIFT)
+#define ID_AA64DFR0_DebugVer_8_2 (UL(0x8) << ID_AA64DFR0_DebugVer_SHIFT)
+#define ID_AA64DFR0_TraceVer_SHIFT 4
+#define ID_AA64DFR0_TraceVer_MASK (UL(0xf) << ID_AA64DFR0_TraceVer_SHIFT)
+#define ID_AA64DFR0_TraceVer(x) ((x) & ID_AA64DFR0_TraceVer_MASK)
+#define ID_AA64DFR0_TraceVer_NONE (UL(0x0) << ID_AA64DFR0_TraceVer_SHIFT)
+#define ID_AA64DFR0_TraceVer_IMPL (UL(0x1) << ID_AA64DFR0_TraceVer_SHIFT)
+#define ID_AA64DFR0_PMUVer_SHIFT 8
+#define ID_AA64DFR0_PMUVer_MASK (UL(0xf) << ID_AA64DFR0_PMUVer_SHIFT)
+#define ID_AA64DFR0_PMUVer(x) ((x) & ID_AA64DFR0_PMUVer_MASK)
+#define ID_AA64DFR0_PMUVer_NONE (UL(0x0) << ID_AA64DFR0_PMUVer_SHIFT)
+#define ID_AA64DFR0_PMUVer_3 (UL(0x1) << ID_AA64DFR0_PMUVer_SHIFT)
+#define ID_AA64DFR0_PMUVer_3_1 (UL(0x4) << ID_AA64DFR0_PMUVer_SHIFT)
+#define ID_AA64DFR0_PMUVer_IMPL (UL(0xf) << ID_AA64DFR0_PMUVer_SHIFT)
+#define ID_AA64DFR0_BRPs_SHIFT 12
+#define ID_AA64DFR0_BRPs_MASK (UL(0xf) << ID_AA64DFR0_BRPs_SHIFT)
+#define ID_AA64DFR0_BRPs(x) \
+ ((((x) >> ID_AA64DFR0_BRPs_SHIFT) & 0xf) + 1)
+#define ID_AA64DFR0_WRPs_SHIFT 20
+#define ID_AA64DFR0_WRPs_MASK (UL(0xf) << ID_AA64DFR0_WRPs_SHIFT)
+#define ID_AA64DFR0_WRPs(x) \
+ ((((x) >> ID_AA64DFR0_WRPs_SHIFT) & 0xf) + 1)
+#define ID_AA64DFR0_CTX_CMPs_SHIFT 28
+#define ID_AA64DFR0_CTX_CMPs_MASK (UL(0xf) << ID_AA64DFR0_CTX_CMPs_SHIFT)
+#define ID_AA64DFR0_CTX_CMPs(x) \
+ ((((x) >> ID_AA64DFR0_CTX_CMPs_SHIFT) & 0xf) + 1)
+#define ID_AA64DFR0_PMSVer_SHIFT 32
+#define ID_AA64DFR0_PMSVer_MASK (UL(0xf) << ID_AA64DFR0_PMSVer_SHIFT)
+#define ID_AA64DFR0_PMSVer(x) ((x) & ID_AA64DFR0_PMSVer_MASK)
+#define ID_AA64DFR0_PMSVer_NONE (UL(0x0) << ID_AA64DFR0_PMSVer_SHIFT)
+#define ID_AA64DFR0_PMSVer_V1 (UL(0x1) << ID_AA64DFR0_PMSVer_SHIFT)
+
+/* ID_AA64ISAR0_EL1 */
+#define ID_AA64ISAR0_MASK UL(0x0000fffff0fffff0)
+#define ID_AA64ISAR0_AES_SHIFT 4
+#define ID_AA64ISAR0_AES_MASK (UL(0xf) << ID_AA64ISAR0_AES_SHIFT)
+#define ID_AA64ISAR0_AES(x) ((x) & ID_AA64ISAR0_AES_MASK)
+#define ID_AA64ISAR0_AES_NONE (UL(0x0) << ID_AA64ISAR0_AES_SHIFT)
+#define ID_AA64ISAR0_AES_BASE (UL(0x1) << ID_AA64ISAR0_AES_SHIFT)
+#define ID_AA64ISAR0_AES_PMULL (UL(0x2) << ID_AA64ISAR0_AES_SHIFT)
+#define ID_AA64ISAR0_SHA1_SHIFT 8
+#define ID_AA64ISAR0_SHA1_MASK (UL(0xf) << ID_AA64ISAR0_SHA1_SHIFT)
+#define ID_AA64ISAR0_SHA1(x) ((x) & ID_AA64ISAR0_SHA1_MASK)
+#define ID_AA64ISAR0_SHA1_NONE (UL(0x0) << ID_AA64ISAR0_SHA1_SHIFT)
+#define ID_AA64ISAR0_SHA1_BASE (UL(0x1) << ID_AA64ISAR0_SHA1_SHIFT)
+#define ID_AA64ISAR0_SHA2_SHIFT 12
+#define ID_AA64ISAR0_SHA2_MASK (UL(0xf) << ID_AA64ISAR0_SHA2_SHIFT)
+#define ID_AA64ISAR0_SHA2(x) ((x) & ID_AA64ISAR0_SHA2_MASK)
+#define ID_AA64ISAR0_SHA2_NONE (UL(0x0) << ID_AA64ISAR0_SHA2_SHIFT)
+#define ID_AA64ISAR0_SHA2_BASE (UL(0x1) << ID_AA64ISAR0_SHA2_SHIFT)
+#define ID_AA64ISAR0_SHA2_512 (UL(0x2) << ID_AA64ISAR0_SHA2_SHIFT)
+#define ID_AA64ISAR0_CRC32_SHIFT 16
+#define ID_AA64ISAR0_CRC32_MASK (UL(0xf) << ID_AA64ISAR0_CRC32_SHIFT)
+#define ID_AA64ISAR0_CRC32(x) ((x) & ID_AA64ISAR0_CRC32_MASK)
+#define ID_AA64ISAR0_CRC32_NONE (UL(0x0) << ID_AA64ISAR0_CRC32_SHIFT)
+#define ID_AA64ISAR0_CRC32_BASE (UL(0x1) << ID_AA64ISAR0_CRC32_SHIFT)
+#define ID_AA64ISAR0_Atomic_SHIFT 20
+#define ID_AA64ISAR0_Atomic_MASK (UL(0xf) << ID_AA64ISAR0_Atomic_SHIFT)
+#define ID_AA64ISAR0_Atomic(x) ((x) & ID_AA64ISAR0_Atomic_MASK)
+#define ID_AA64ISAR0_Atomic_NONE (UL(0x0) << ID_AA64ISAR0_Atomic_SHIFT)
+#define ID_AA64ISAR0_Atomic_IMPL (UL(0x2) << ID_AA64ISAR0_Atomic_SHIFT)
+#define ID_AA64ISAR0_RDM_SHIFT 28
+#define ID_AA64ISAR0_RDM_MASK (UL(0xf) << ID_AA64ISAR0_RDM_SHIFT)
+#define ID_AA64ISAR0_RDM(x) ((x) & ID_AA64ISAR0_RDM_MASK)
+#define ID_AA64ISAR0_RDM_NONE (UL(0x0) << ID_AA64ISAR0_RDM_SHIFT)
+#define ID_AA64ISAR0_RDM_IMPL (UL(0x1) << ID_AA64ISAR0_RDM_SHIFT)
+#define ID_AA64ISAR0_SHA3_SHIFT 32
+#define ID_AA64ISAR0_SHA3_MASK (UL(0xf) << ID_AA64ISAR0_SHA3_SHIFT)
+#define ID_AA64ISAR0_SHA3(x) ((x) & ID_AA64ISAR0_SHA3_MASK)
+#define ID_AA64ISAR0_SHA3_NONE (UL(0x0) << ID_AA64ISAR0_SHA3_SHIFT)
+#define ID_AA64ISAR0_SHA3_IMPL (UL(0x1) << ID_AA64ISAR0_SHA3_SHIFT)
+#define ID_AA64ISAR0_SM3_SHIFT 36
+#define ID_AA64ISAR0_SM3_MASK (UL(0xf) << ID_AA64ISAR0_SM3_SHIFT)
+#define ID_AA64ISAR0_SM3(x) ((x) & ID_AA64ISAR0_SM3_MASK)
+#define ID_AA64ISAR0_SM3_NONE (UL(0x0) << ID_AA64ISAR0_SM3_SHIFT)
+#define ID_AA64ISAR0_SM3_IMPL (UL(0x1) << ID_AA64ISAR0_SM3_SHIFT)
+#define ID_AA64ISAR0_SM4_SHIFT 40
+#define ID_AA64ISAR0_SM4_MASK (UL(0xf) << ID_AA64ISAR0_SM4_SHIFT)
+#define ID_AA64ISAR0_SM4(x) ((x) & ID_AA64ISAR0_SM4_MASK)
+#define ID_AA64ISAR0_SM4_NONE (UL(0x0) << ID_AA64ISAR0_SM4_SHIFT)
+#define ID_AA64ISAR0_SM4_IMPL (UL(0x1) << ID_AA64ISAR0_SM4_SHIFT)
+#define ID_AA64ISAR0_DP_SHIFT 44
+#define ID_AA64ISAR0_DP_MASK (UL(0xf) << ID_AA64ISAR0_DP_SHIFT)
+#define ID_AA64ISAR0_DP(x) ((x) & ID_AA64ISAR0_DP_MASK)
+#define ID_AA64ISAR0_DP_NONE (UL(0x0) << ID_AA64ISAR0_DP_SHIFT)
+#define ID_AA64ISAR0_DP_IMPL (UL(0x1) << ID_AA64ISAR0_DP_SHIFT)
+
+/* ID_AA64ISAR1_EL1 */
+#define ID_AA64ISAR1_MASK UL(0x00000000ffffffff)
+#define ID_AA64ISAR1_DPB_SHIFT 0
+#define ID_AA64ISAR1_DPB_MASK (UL(0xf) << ID_AA64ISAR1_DPB_SHIFT)
+#define ID_AA64ISAR1_DPB(x) ((x) & ID_AA64ISAR1_DPB_MASK)
+#define ID_AA64ISAR1_DPB_NONE (UL(0x0) << ID_AA64ISAR1_DPB_SHIFT)
+#define ID_AA64ISAR1_DPB_IMPL (UL(0x1) << ID_AA64ISAR1_DPB_SHIFT)
+#define ID_AA64ISAR1_APA_SHIFT 4
+#define ID_AA64ISAR1_APA_MASK (UL(0xf) << ID_AA64ISAR1_APA_SHIFT)
+#define ID_AA64ISAR1_APA(x) ((x) & ID_AA64ISAR1_APA_MASK)
+#define ID_AA64ISAR1_APA_NONE (UL(0x0) << ID_AA64ISAR1_APA_SHIFT)
+#define ID_AA64ISAR1_APA_IMPL (UL(0x1) << ID_AA64ISAR1_APA_SHIFT)
+#define ID_AA64ISAR1_API_SHIFT 8
+#define ID_AA64ISAR1_API_MASK (UL(0xf) << ID_AA64ISAR1_API_SHIFT)
+#define ID_AA64ISAR1_API(x) ((x) & ID_AA64ISAR1_API_MASK)
+#define ID_AA64ISAR1_API_NONE (UL(0x0) << ID_AA64ISAR1_API_SHIFT)
+#define ID_AA64ISAR1_API_IMPL (UL(0x1) << ID_AA64ISAR1_API_SHIFT)
+#define ID_AA64ISAR1_JSCVT_SHIFT 12
+#define ID_AA64ISAR1_JSCVT_MASK (UL(0xf) << ID_AA64ISAR1_JSCVT_SHIFT)
+#define ID_AA64ISAR1_JSCVT(x) ((x) & ID_AA64ISAR1_JSCVT_MASK)
+#define ID_AA64ISAR1_JSCVT_NONE (UL(0x0) << ID_AA64ISAR1_JSCVT_SHIFT)
+#define ID_AA64ISAR1_JSCVT_IMPL (UL(0x1) << ID_AA64ISAR1_JSCVT_SHIFT)
+#define ID_AA64ISAR1_FCMA_SHIFT 16
+#define ID_AA64ISAR1_FCMA_MASK (UL(0xf) << ID_AA64ISAR1_FCMA_SHIFT)
+#define ID_AA64ISAR1_FCMA(x) ((x) & ID_AA64ISAR1_FCMA_MASK)
+#define ID_AA64ISAR1_FCMA_NONE (UL(0x0) << ID_AA64ISAR1_FCMA_SHIFT)
+#define ID_AA64ISAR1_FCMA_IMPL (UL(0x1) << ID_AA64ISAR1_FCMA_SHIFT)
+#define ID_AA64ISAR1_LRCPC_SHIFT 20
+#define ID_AA64ISAR1_LRCPC_MASK (UL(0xf) << ID_AA64ISAR1_LRCPC_SHIFT)
+#define ID_AA64ISAR1_LRCPC(x) ((x) & ID_AA64ISAR1_LRCPC_MASK)
+#define ID_AA64ISAR1_LRCPC_NONE (UL(0x0) << ID_AA64ISAR1_LRCPC_SHIFT)
+#define ID_AA64ISAR1_LRCPC_IMPL (UL(0x1) << ID_AA64ISAR1_LRCPC_SHIFT)
+#define ID_AA64ISAR1_GPA_SHIFT 24
+#define ID_AA64ISAR1_GPA_MASK (UL(0xf) << ID_AA64ISAR1_GPA_SHIFT)
+#define ID_AA64ISAR1_GPA(x) ((x) & ID_AA64ISAR1_GPA_MASK)
+#define ID_AA64ISAR1_GPA_NONE (UL(0x0) << ID_AA64ISAR1_GPA_SHIFT)
+#define ID_AA64ISAR1_GPA_IMPL (UL(0x1) << ID_AA64ISAR1_GPA_SHIFT)
+#define ID_AA64ISAR1_GPI_SHIFT 28
+#define ID_AA64ISAR1_GPI_MASK (UL(0xf) << ID_AA64ISAR1_GPI_SHIFT)
+#define ID_AA64ISAR1_GPI(x) ((x) & ID_AA64ISAR1_GPI_MASK)
+#define ID_AA64ISAR1_GPI_NONE (UL(0x0) << ID_AA64ISAR1_GPI_SHIFT)
+#define ID_AA64ISAR1_GPI_IMPL (UL(0x1) << ID_AA64ISAR1_GPI_SHIFT)
+
+/* ID_AA64MMFR0_EL1 */
+#define ID_AA64MMFR0_MASK UL(0x00000000ffffffff)
+#define ID_AA64MMFR0_PARange_SHIFT 0
+#define ID_AA64MMFR0_PARange_MASK (UL(0xf) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange(x) ((x) & ID_AA64MMFR0_PARange_MASK)
+#define ID_AA64MMFR0_PARange_4G (UL(0x0) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_64G (UL(0x1) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_1T (UL(0x2) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_4T (UL(0x3) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_16T (UL(0x4) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_256T (UL(0x5) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_PARange_4P (UL(0x6) << ID_AA64MMFR0_PARange_SHIFT)
+#define ID_AA64MMFR0_ASIDBits_SHIFT 4
+#define ID_AA64MMFR0_ASIDBits_MASK (UL(0xf) << ID_AA64MMFR0_ASIDBits_SHIFT)
+#define ID_AA64MMFR0_ASIDBits(x) ((x) & ID_AA64MMFR0_ASIDBits_MASK)
+#define ID_AA64MMFR0_ASIDBits_8 (UL(0x0) << ID_AA64MMFR0_ASIDBits_SHIFT)
+#define ID_AA64MMFR0_ASIDBits_16 (UL(0x2) << ID_AA64MMFR0_ASIDBits_SHIFT)
+#define ID_AA64MMFR0_BigEnd_SHIFT 8
+#define ID_AA64MMFR0_BigEnd_MASK (UL(0xf) << ID_AA64MMFR0_BigEnd_SHIFT)
+#define ID_AA64MMFR0_BigEnd(x) ((x) & ID_AA64MMFR0_BigEnd_MASK)
+#define ID_AA64MMFR0_BigEnd_FIXED (UL(0x0) << ID_AA64MMFR0_BigEnd_SHIFT)
+#define ID_AA64MMFR0_BigEnd_MIXED (UL(0x1) << ID_AA64MMFR0_BigEnd_SHIFT)
+#define ID_AA64MMFR0_SNSMem_SHIFT 12
+#define ID_AA64MMFR0_SNSMem_MASK (UL(0xf) << ID_AA64MMFR0_SNSMem_SHIFT)
+#define ID_AA64MMFR0_SNSMem(x) ((x) & ID_AA64MMFR0_SNSMem_MASK)
+#define ID_AA64MMFR0_SNSMem_NONE (UL(0x0) << ID_AA64MMFR0_SNSMem_SHIFT)
+#define ID_AA64MMFR0_SNSMem_DISTINCT (UL(0x1) << ID_AA64MMFR0_SNSMem_SHIFT)
+#define ID_AA64MMFR0_BigEndEL0_SHIFT 16
+#define ID_AA64MMFR0_BigEndEL0_MASK (UL(0xf) << ID_AA64MMFR0_BigEndEL0_SHIFT)
+#define ID_AA64MMFR0_BigEndEL0(x) ((x) & ID_AA64MMFR0_BigEndEL0_MASK)
+#define ID_AA64MMFR0_BigEndEL0_FIXED (UL(0x0) << ID_AA64MMFR0_BigEndEL0_SHIFT)
+#define ID_AA64MMFR0_BigEndEL0_MIXED (UL(0x1) << ID_AA64MMFR0_BigEndEL0_SHIFT)
+#define ID_AA64MMFR0_TGran16_SHIFT 20
+#define ID_AA64MMFR0_TGran16_MASK (UL(0xf) << ID_AA64MMFR0_TGran16_SHIFT)
+#define ID_AA64MMFR0_TGran16(x) ((x) & ID_AA64MMFR0_TGran16_MASK)
+#define ID_AA64MMFR0_TGran16_NONE (UL(0x0) << ID_AA64MMFR0_TGran16_SHIFT)
+#define ID_AA64MMFR0_TGran16_IMPL (UL(0x1) << ID_AA64MMFR0_TGran16_SHIFT)
+#define ID_AA64MMFR0_TGran64_SHIFT 24
+#define ID_AA64MMFR0_TGran64_MASK (UL(0xf) << ID_AA64MMFR0_TGran64_SHIFT)
+#define ID_AA64MMFR0_TGran64(x) ((x) & ID_AA64MMFR0_TGran64_MASK)
+#define ID_AA64MMFR0_TGran64_IMPL (UL(0x0) << ID_AA64MMFR0_TGran64_SHIFT)
+#define ID_AA64MMFR0_TGran64_NONE (UL(0xf) << ID_AA64MMFR0_TGran64_SHIFT)
+#define ID_AA64MMFR0_TGran4_SHIFT 28
+#define ID_AA64MMFR0_TGran4_MASK (UL(0xf) << ID_AA64MMFR0_TGran4_SHIFT)
+#define ID_AA64MMFR0_TGran4(x) ((x) & ID_AA64MMFR0_TGran4_MASK)
+#define ID_AA64MMFR0_TGran4_IMPL (UL(0x0) << ID_AA64MMFR0_TGran4_SHIFT)
+#define ID_AA64MMFR0_TGran4_NONE (UL(0xf) << ID_AA64MMFR0_TGran4_SHIFT)
+
+/* ID_AA64MMFR1_EL1 */
+#define ID_AA64MMFR1_MASK UL(0x00000000ffffffff)
+#define ID_AA64MMFR1_HAFDBS_SHIFT 0
+#define ID_AA64MMFR1_HAFDBS_MASK (UL(0xf) << ID_AA64MMFR1_HAFDBS_SHIFT)
+#define ID_AA64MMFR1_HAFDBS(x) ((x) & ID_AA64MMFR1_HAFDBS_MASK)
+#define ID_AA64MMFR1_HAFDBS_NONE (UL(0x0) << ID_AA64MMFR1_HAFDBS_SHIFT)
+#define ID_AA64MMFR1_HAFDBS_AF (UL(0x1) << ID_AA64MMFR1_HAFDBS_SHIFT)
+#define ID_AA64MMFR1_HAFDBS_AF_DBS (UL(0x2) << ID_AA64MMFR1_HAFDBS_SHIFT)
+#define ID_AA64MMFR1_VMIDBits_SHIFT 4
+#define ID_AA64MMFR1_VMIDBits_MASK (UL(0xf) << ID_AA64MMFR1_VMIDBits_SHIFT)
+#define ID_AA64MMFR1_VMIDBits(x) ((x) & ID_AA64MMFR1_VMIDBits_MASK)
+#define ID_AA64MMFR1_VMIDBits_8 (UL(0x0) << ID_AA64MMFR1_VMIDBits_SHIFT)
+#define ID_AA64MMFR1_VMIDBits_16 (UL(0x2) << ID_AA64MMFR1_VMIDBits_SHIFT)
+#define ID_AA64MMFR1_VH_SHIFT 8
+#define ID_AA64MMFR1_VH_MASK (UL(0xf) << ID_AA64MMFR1_VH_SHIFT)
+#define ID_AA64MMFR1_VH(x) ((x) & ID_AA64MMFR1_VH_MASK)
+#define ID_AA64MMFR1_VH_NONE (UL(0x0) << ID_AA64MMFR1_VH_SHIFT)
+#define ID_AA64MMFR1_VH_IMPL (UL(0x1) << ID_AA64MMFR1_VH_SHIFT)
+#define ID_AA64MMFR1_HPDS_SHIFT 12
+#define ID_AA64MMFR1_HPDS_MASK (UL(0xf) << ID_AA64MMFR1_HPDS_SHIFT)
+#define ID_AA64MMFR1_HPDS(x) ((x) & ID_AA64MMFR1_HPDS_MASK)
+#define ID_AA64MMFR1_HPDS_NONE (UL(0x0) << ID_AA64MMFR1_HPDS_SHIFT)
+#define ID_AA64MMFR1_HPDS_HPD (UL(0x1) << ID_AA64MMFR1_HPDS_SHIFT)
+#define ID_AA64MMFR1_HPDS_TTPBHA (UL(0x2) << ID_AA64MMFR1_HPDS_SHIFT)
+#define ID_AA64MMFR1_LO_SHIFT 16
+#define ID_AA64MMFR1_LO_MASK (UL(0xf) << ID_AA64MMFR1_LO_SHIFT)
+#define ID_AA64MMFR1_LO(x) ((x) & ID_AA64MMFR1_LO_MASK)
+#define ID_AA64MMFR1_LO_NONE (UL(0x0) << ID_AA64MMFR1_LO_SHIFT)
+#define ID_AA64MMFR1_LO_IMPL (UL(0x1) << ID_AA64MMFR1_LO_SHIFT)
+#define ID_AA64MMFR1_PAN_SHIFT 20
+#define ID_AA64MMFR1_PAN_MASK (UL(0xf) << ID_AA64MMFR1_PAN_SHIFT)
+#define ID_AA64MMFR1_PAN(x) ((x) & ID_AA64MMFR1_PAN_MASK)
+#define ID_AA64MMFR1_PAN_NONE (UL(0x0) << ID_AA64MMFR1_PAN_SHIFT)
+#define ID_AA64MMFR1_PAN_IMPL (UL(0x1) << ID_AA64MMFR1_PAN_SHIFT)
+#define ID_AA64MMFR1_PAN_ATS1E1 (UL(0x2) << ID_AA64MMFR1_PAN_SHIFT)
+#define ID_AA64MMFR1_SpecSEI_SHIFT 24
+#define ID_AA64MMFR1_SpecSEI_MASK (UL(0xf) << ID_AA64MMFR1_SpecSEI_SHIFT)
+#define ID_AA64MMFR1_SpecSEI(x) ((x) & ID_AA64MMFR1_SpecSEI_MASK)
+#define ID_AA64MMFR1_SpecSEI_NONE (UL(0x0) << ID_AA64MMFR1_SpecSEI_SHIFT)
+#define ID_AA64MMFR1_SpecSEI_IMPL (UL(0x1) << ID_AA64MMFR1_SpecSEI_SHIFT)
+#define ID_AA64MMFR1_XNX_SHIFT 28
+#define ID_AA64MMFR1_XNX_MASK (UL(0xf) << ID_AA64MMFR1_XNX_SHIFT)
+#define ID_AA64MMFR1_XNX(x) ((x) & ID_AA64MMFR1_XNX_MASK)
+#define ID_AA64MMFR1_XNX_NONE (UL(0x0) << ID_AA64MMFR1_XNX_SHIFT)
+#define ID_AA64MMFR1_XNX_IMPL (UL(0x1) << ID_AA64MMFR1_XNX_SHIFT)
+
+/* ID_AA64MMFR2_EL1 */
+#define ID_AA64MMFR2_EL1 S3_0_C0_C7_2
+#define ID_AA64MMFR2_MASK UL(0x000000000fffffff)
+#define ID_AA64MMFR2_CnP_SHIFT 0
+#define ID_AA64MMFR2_CnP_MASK (UL(0xf) << ID_AA64MMFR2_CnP_SHIFT)
+#define ID_AA64MMFR2_CnP(x) ((x) & ID_AA64MMFR2_CnP_MASK)
+#define ID_AA64MMFR2_CnP_NONE (UL(0x0) << ID_AA64MMFR2_CnP_SHIFT)
+#define ID_AA64MMFR2_CnP_IMPL (UL(0x1) << ID_AA64MMFR2_CnP_SHIFT)
+#define ID_AA64MMFR2_UAO_SHIFT 4
+#define ID_AA64MMFR2_UAO_MASK (UL(0xf) << ID_AA64MMFR2_UAO_SHIFT)
+#define ID_AA64MMFR2_UAO(x) ((x) & ID_AA64MMFR2_UAO_MASK)
+#define ID_AA64MMFR2_UAO_NONE (UL(0x0) << ID_AA64MMFR2_UAO_SHIFT)
+#define ID_AA64MMFR2_UAO_IMPL (UL(0x1) << ID_AA64MMFR2_UAO_SHIFT)
+#define ID_AA64MMFR2_LSM_SHIFT 8
+#define ID_AA64MMFR2_LSM_MASK (UL(0xf) << ID_AA64MMFR2_LSM_SHIFT)
+#define ID_AA64MMFR2_LSM(x) ((x) & ID_AA64MMFR2_LSM_MASK)
+#define ID_AA64MMFR2_LSM_NONE (UL(0x0) << ID_AA64MMFR2_LSM_SHIFT)
+#define ID_AA64MMFR2_LSM_IMPL (UL(0x1) << ID_AA64MMFR2_LSM_SHIFT)
+#define ID_AA64MMFR2_IESB_SHIFT 12
+#define ID_AA64MMFR2_IESB_MASK (UL(0xf) << ID_AA64MMFR2_IESB_SHIFT)
+#define ID_AA64MMFR2_IESB(x) ((x) & ID_AA64MMFR2_IESB_MASK)
+#define ID_AA64MMFR2_IESB_NONE (UL(0x0) << ID_AA64MMFR2_IESB_SHIFT)
+#define ID_AA64MMFR2_IESB_IMPL (UL(0x1) << ID_AA64MMFR2_IESB_SHIFT)
+#define ID_AA64MMFR2_VARange_SHIFT 16
+#define ID_AA64MMFR2_VARange_MASK (UL(0xf) << ID_AA64MMFR2_VARange_SHIFT)
+#define ID_AA64MMFR2_VARange(x) ((x) & ID_AA64MMFR2_VARange_MASK)
+#define ID_AA64MMFR2_VARange_48 (UL(0x0) << ID_AA64MMFR2_VARange_SHIFT)
+#define ID_AA64MMFR2_VARange_52 (UL(0x1) << ID_AA64MMFR2_VARange_SHIFT)
+#define ID_AA64MMFR2_CCIDX_SHIFT 20
+#define ID_AA64MMFR2_CCIDX_MASK (UL(0xf) << ID_AA64MMFR2_CCIDX_SHIFT)
+#define ID_AA64MMFR2_CCIDX(x) ((x) & ID_AA64MMFR2_CCIDX_MASK)
+#define ID_AA64MMFR2_CCIDX_32 (UL(0x0) << ID_AA64MMFR2_CCIDX_SHIFT)
+#define ID_AA64MMFR2_CCIDX_64 (UL(0x1) << ID_AA64MMFR2_CCIDX_SHIFT)
+#define ID_AA64MMFR2_NV_SHIFT 24
+#define ID_AA64MMFR2_NV_MASK (UL(0xf) << ID_AA64MMFR2_NV_SHIFT)
+#define ID_AA64MMFR2_NV(x) ((x) & ID_AA64MMFR2_NV_MASK)
+#define ID_AA64MMFR2_NV_NONE (UL(0x0) << ID_AA64MMFR2_NV_SHIFT)
+#define ID_AA64MMFR2_NV_IMPL (UL(0x1) << ID_AA64MMFR2_NV_SHIFT)
+
+/* ID_AA64PFR0_EL1 */
+#define ID_AA64PFR0_MASK UL(0x0000000fffffffff)
+#define ID_AA64PFR0_EL0_SHIFT 0
+#define ID_AA64PFR0_EL0_MASK (UL(0xf) << ID_AA64PFR0_EL0_SHIFT)
+#define ID_AA64PFR0_EL0(x) ((x) & ID_AA64PFR0_EL0_MASK)
+#define ID_AA64PFR0_EL0_64 (UL(0x1) << ID_AA64PFR0_EL0_SHIFT)
+#define ID_AA64PFR0_EL0_64_32 (UL(0x2) << ID_AA64PFR0_EL0_SHIFT)
+#define ID_AA64PFR0_EL1_SHIFT 4
+#define ID_AA64PFR0_EL1_MASK (UL(0xf) << ID_AA64PFR0_EL1_SHIFT)
+#define ID_AA64PFR0_EL1(x) ((x) & ID_AA64PFR0_EL1_MASK)
+#define ID_AA64PFR0_EL1_64 (UL(0x1) << ID_AA64PFR0_EL1_SHIFT)
+#define ID_AA64PFR0_EL1_64_32 (UL(0x2) << ID_AA64PFR0_EL1_SHIFT)
+#define ID_AA64PFR0_EL2_SHIFT 8
+#define ID_AA64PFR0_EL2_MASK (UL(0xf) << ID_AA64PFR0_EL2_SHIFT)
+#define ID_AA64PFR0_EL2(x) ((x) & ID_AA64PFR0_EL2_MASK)
+#define ID_AA64PFR0_EL2_NONE (UL(0x0) << ID_AA64PFR0_EL2_SHIFT)
+#define ID_AA64PFR0_EL2_64 (UL(0x1) << ID_AA64PFR0_EL2_SHIFT)
+#define ID_AA64PFR0_EL2_64_32 (UL(0x2) << ID_AA64PFR0_EL2_SHIFT)
+#define ID_AA64PFR0_EL3_SHIFT 12
+#define ID_AA64PFR0_EL3_MASK (UL(0xf) << ID_AA64PFR0_EL3_SHIFT)
+#define ID_AA64PFR0_EL3(x) ((x) & ID_AA64PFR0_EL3_MASK)
+#define ID_AA64PFR0_EL3_NONE (UL(0x0) << ID_AA64PFR0_EL3_SHIFT)
+#define ID_AA64PFR0_EL3_64 (UL(0x1) << ID_AA64PFR0_EL3_SHIFT)
+#define ID_AA64PFR0_EL3_64_32 (UL(0x2) << ID_AA64PFR0_EL3_SHIFT)
+#define ID_AA64PFR0_FP_SHIFT 16
+#define ID_AA64PFR0_FP_MASK (UL(0xf) << ID_AA64PFR0_FP_SHIFT)
+#define ID_AA64PFR0_FP(x) ((x) & ID_AA64PFR0_FP_MASK)
+#define ID_AA64PFR0_FP_IMPL (UL(0x0) << ID_AA64PFR0_FP_SHIFT)
+#define ID_AA64PFR0_FP_HP (UL(0x1) << ID_AA64PFR0_FP_SHIFT)
+#define ID_AA64PFR0_FP_NONE (UL(0xf) << ID_AA64PFR0_FP_SHIFT)
+#define ID_AA64PFR0_AdvSIMD_SHIFT 20
+#define ID_AA64PFR0_AdvSIMD_MASK (UL(0xf) << ID_AA64PFR0_AdvSIMD_SHIFT)
+#define ID_AA64PFR0_AdvSIMD(x) ((x) & ID_AA64PFR0_AdvSIMD_MASK)
+#define ID_AA64PFR0_AdvSIMD_IMPL (UL(0x0) << ID_AA64PFR0_AdvSIMD_SHIFT)
+#define ID_AA64PFR0_AdvSIMD_HP (UL(0x1) << ID_AA64PFR0_AdvSIMD_SHIFT)
+#define ID_AA64PFR0_AdvSIMD_NONE (UL(0xf) << ID_AA64PFR0_AdvSIMD_SHIFT)
+#define ID_AA64PFR0_GIC_BITS 0x4 /* Number of bits in GIC field */
+#define ID_AA64PFR0_GIC_SHIFT 24
+#define ID_AA64PFR0_GIC_MASK (UL(0xf) << ID_AA64PFR0_GIC_SHIFT)
+#define ID_AA64PFR0_GIC(x) ((x) & ID_AA64PFR0_GIC_MASK)
+#define ID_AA64PFR0_GIC_CPUIF_NONE (UL(0x0) << ID_AA64PFR0_GIC_SHIFT)
+#define ID_AA64PFR0_GIC_CPUIF_EN (UL(0x1) << ID_AA64PFR0_GIC_SHIFT)
+#define ID_AA64PFR0_RAS_SHIFT 28
+#define ID_AA64PFR0_RAS_MASK (UL(0xf) << ID_AA64PFR0_RAS_SHIFT)
+#define ID_AA64PFR0_RAS(x) ((x) & ID_AA64PFR0_RAS_MASK)
+#define ID_AA64PFR0_RAS_NONE (UL(0x0) << ID_AA64PFR0_RAS_SHIFT)
+#define ID_AA64PFR0_RAS_V1 (UL(0x1) << ID_AA64PFR0_RAS_SHIFT)
+#define ID_AA64PFR0_SVE_SHIFT 32
+#define ID_AA64PFR0_SVE_MASK (UL(0xf) << ID_AA64PFR0_SVE_SHIFT)
+#define ID_AA64PFR0_SVE(x) ((x) & ID_AA64PFR0_SVE_MASK)
+#define ID_AA64PFR0_SVE_NONE (UL(0x0) << ID_AA64PFR0_SVE_SHIFT)
+#define ID_AA64PFR0_SVE_IMPL (UL(0x1) << ID_AA64PFR0_SVE_SHIFT)
+
+/* MAIR_EL1 - Memory Attribute Indirection Register */
+#define MAIR_ATTR_MASK(idx) (0xff << ((n)* 8))
+#define MAIR_ATTR(attr, idx) ((attr) << ((idx) * 8))
+#define MAIR_DEVICE_nGnRnE 0x00
+#define MAIR_NORMAL_NC 0x44
+#define MAIR_NORMAL_WT 0xbb
+#define MAIR_NORMAL_WB 0xff
+
+/* PAR_EL1 - Physical Address Register */
+#define PAR_F_SHIFT 0
+#define PAR_F (0x1 << PAR_F_SHIFT)
+#define PAR_SUCCESS(x) (((x) & PAR_F) == 0)
+/* When PAR_F == 0 (success) */
+#define PAR_SH_SHIFT 7
+#define PAR_SH_MASK (0x3 << PAR_SH_SHIFT)
+#define PAR_NS_SHIFT 9
+#define PAR_NS_MASK (0x3 << PAR_NS_SHIFT)
+#define PAR_PA_SHIFT 12
+#define PAR_PA_MASK 0x0000fffffffff000
+#define PAR_ATTR_SHIFT 56
+#define PAR_ATTR_MASK (0xff << PAR_ATTR_SHIFT)
+/* When PAR_F == 1 (aborted) */
+#define PAR_FST_SHIFT 1
+#define PAR_FST_MASK (0x3f << PAR_FST_SHIFT)
+#define PAR_PTW_SHIFT 8
+#define PAR_PTW_MASK (0x1 << PAR_PTW_SHIFT)
+#define PAR_S_SHIFT 9
+#define PAR_S_MASK (0x1 << PAR_S_SHIFT)
+
+/* SCTLR_EL1 - System Control Register */
+#define SCTLR_RES0 0xc8222440 /* Reserved ARMv8.0, write 0 */
+#define SCTLR_RES1 0x30d00800 /* Reserved ARMv8.0, write 1 */
+
+#define SCTLR_M 0x00000001
+#define SCTLR_A 0x00000002
+#define SCTLR_C 0x00000004
+#define SCTLR_SA 0x00000008
+#define SCTLR_SA0 0x00000010
+#define SCTLR_CP15BEN 0x00000020
+/* Bit 6 is reserved */
+#define SCTLR_ITD 0x00000080
+#define SCTLR_SED 0x00000100
+#define SCTLR_UMA 0x00000200
+/* Bit 10 is reserved */
+/* Bit 11 is reserved */
+#define SCTLR_I 0x00001000
+#define SCTLR_EnDB 0x00002000 /* ARMv8.3 */
+#define SCTLR_DZE 0x00004000
+#define SCTLR_UCT 0x00008000
+#define SCTLR_nTWI 0x00010000
+/* Bit 17 is reserved */
+#define SCTLR_nTWE 0x00040000
+#define SCTLR_WXN 0x00080000
+/* Bit 20 is reserved */
+#define SCTLR_IESB 0x00200000 /* ARMv8.2 */
+/* Bit 22 is reserved */
+#define SCTLR_SPAN 0x00800000 /* ARMv8.1 */
+#define SCTLR_EOE 0x01000000
+#define SCTLR_EE 0x02000000
+#define SCTLR_UCI 0x04000000
+#define SCTLR_EnDA 0x08000000 /* ARMv8.3 */
+#define SCTLR_nTLSMD 0x10000000 /* ARMv8.2 */
+#define SCTLR_LSMAOE 0x20000000 /* ARMv8.2 */
+#define SCTLR_EnIB 0x40000000 /* ARMv8.3 */
+#define SCTLR_EnIA 0x80000000 /* ARMv8.3 */
+
+/* SPSR_EL1 */
+/*
+ * When the exception is taken in AArch64:
+ * M[3:2] is the exception level
+ * M[1] is unused
+ * M[0] is the SP select:
+ * 0: always SP0
+ * 1: current ELs SP
+ */
+#define PSR_M_EL0t 0x00000000
+#define PSR_M_EL1t 0x00000004
+#define PSR_M_EL1h 0x00000005
+#define PSR_M_EL2t 0x00000008
+#define PSR_M_EL2h 0x00000009
+#define PSR_M_MASK 0x0000000f
+
+#define PSR_AARCH32 0x00000010
+#define PSR_F 0x00000040
+#define PSR_I 0x00000080
+#define PSR_A 0x00000100
+#define PSR_D 0x00000200
+#define PSR_IL 0x00100000
+#define PSR_SS 0x00200000
+#define PSR_V 0x10000000
+#define PSR_C 0x20000000
+#define PSR_Z 0x40000000
+#define PSR_N 0x80000000
+#define PSR_FLAGS 0xf0000000
+
+/* TCR_EL1 - Translation Control Register */
+#define TCR_ASID_16 (1 << 36)
+
+#define TCR_IPS_SHIFT 32
+#define TCR_IPS_32BIT (0 << TCR_IPS_SHIFT)
+#define TCR_IPS_36BIT (1 << TCR_IPS_SHIFT)
+#define TCR_IPS_40BIT (2 << TCR_IPS_SHIFT)
+#define TCR_IPS_42BIT (3 << TCR_IPS_SHIFT)
+#define TCR_IPS_44BIT (4 << TCR_IPS_SHIFT)
+#define TCR_IPS_48BIT (5 << TCR_IPS_SHIFT)
+
+#define TCR_TG1_SHIFT 30
+#define TCR_TG1_16K (1 << TCR_TG1_SHIFT)
+#define TCR_TG1_4K (2 << TCR_TG1_SHIFT)
+#define TCR_TG1_64K (3 << TCR_TG1_SHIFT)
+
+#define TCR_SH1_SHIFT 28
+#define TCR_SH1_IS (0x3UL << TCR_SH1_SHIFT)
+#define TCR_ORGN1_SHIFT 26
+#define TCR_ORGN1_WBWA (0x1UL << TCR_ORGN1_SHIFT)
+#define TCR_IRGN1_SHIFT 24
+#define TCR_IRGN1_WBWA (0x1UL << TCR_IRGN1_SHIFT)
+#define TCR_SH0_SHIFT 12
+#define TCR_SH0_IS (0x3UL << TCR_SH0_SHIFT)
+#define TCR_ORGN0_SHIFT 10
+#define TCR_ORGN0_WBWA (0x1UL << TCR_ORGN0_SHIFT)
+#define TCR_IRGN0_SHIFT 8
+#define TCR_IRGN0_WBWA (0x1UL << TCR_IRGN0_SHIFT)
+
+#define TCR_CACHE_ATTRS ((TCR_IRGN0_WBWA | TCR_IRGN1_WBWA) |\
+ (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA))
+
+#ifdef SMP
+#define TCR_SMP_ATTRS (TCR_SH0_IS | TCR_SH1_IS)
+#else
+#define TCR_SMP_ATTRS 0
+#endif
+
+#define TCR_T1SZ_SHIFT 16
+#define TCR_T0SZ_SHIFT 0
+#define TCR_T1SZ(x) ((x) << TCR_T1SZ_SHIFT)
+#define TCR_T0SZ(x) ((x) << TCR_T0SZ_SHIFT)
+#define TCR_TxSZ(x) (TCR_T1SZ(x) | TCR_T0SZ(x))
+
+/* Saved Program Status Register */
+#define DBG_SPSR_SS (0x1 << 21)
+
+/* Monitor Debug System Control Register */
+#define DBG_MDSCR_SS (0x1 << 0)
+#define DBG_MDSCR_KDE (0x1 << 13)
+#define DBG_MDSCR_MDE (0x1 << 15)
+
+/* Perfomance Monitoring Counters */
+#define PMCR_E (1 << 0) /* Enable all counters */
+#define PMCR_P (1 << 1) /* Reset all counters */
+#define PMCR_C (1 << 2) /* Clock counter reset */
+#define PMCR_D (1 << 3) /* CNTR counts every 64 clk cycles */
+#define PMCR_X (1 << 4) /* Export to ext. monitoring (ETM) */
+#define PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
+#define PMCR_LC (1 << 6) /* Long cycle count enable */
+#define PMCR_IMP_SHIFT 24 /* Implementer code */
+#define PMCR_IMP_MASK (0xff << PMCR_IMP_SHIFT)
+#define PMCR_IDCODE_SHIFT 16 /* Identification code */
+#define PMCR_IDCODE_MASK (0xff << PMCR_IDCODE_SHIFT)
+#define PMCR_IDCODE_CORTEX_A57 0x01
+#define PMCR_IDCODE_CORTEX_A72 0x02
+#define PMCR_IDCODE_CORTEX_A53 0x03
+#define PMCR_N_SHIFT 11 /* Number of counters implemented */
+#define PMCR_N_MASK (0x1f << PMCR_N_SHIFT)
+
+#endif /* !_MACHINE_ARMREG_H_ */
diff --git a/freebsd/sys/arm64/include/machine/cpu.h b/freebsd/sys/arm64/include/machine/cpu.h
new file mode 100644
index 00000000..5663e50e
--- /dev/null
+++ b/freebsd/sys/arm64/include/machine/cpu.h
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 2014-2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Portions of this software were developed by Andrew Turner
+ * 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. 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: @(#)cpu.h 5.4 (Berkeley) 5/9/91
+ * from: FreeBSD: src/sys/i386/include/cpu.h,v 1.62 2001/06/29
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_CPU_H_
+#define _MACHINE_CPU_H_
+
+#include <machine/atomic.h>
+#include <machine/frame.h>
+#include <machine/armreg.h>
+
+#define TRAPF_PC(tfp) ((tfp)->tf_lr)
+#define TRAPF_USERMODE(tfp) (((tfp)->tf_spsr & PSR_M_MASK) == PSR_M_EL0t)
+
+#define cpu_getstack(td) ((td)->td_frame->tf_sp)
+#define cpu_setstack(td, sp) ((td)->td_frame->tf_sp = (sp))
+#define cpu_spinwait() __asm __volatile("yield" ::: "memory")
+#define cpu_lock_delay() DELAY(1)
+
+/* Extract CPU affinity levels 0-3 */
+#define CPU_AFF0(mpidr) (u_int)(((mpidr) >> 0) & 0xff)
+#define CPU_AFF1(mpidr) (u_int)(((mpidr) >> 8) & 0xff)
+#define CPU_AFF2(mpidr) (u_int)(((mpidr) >> 16) & 0xff)
+#define CPU_AFF3(mpidr) (u_int)(((mpidr) >> 32) & 0xff)
+#define CPU_AFF0_MASK 0xffUL
+#define CPU_AFF1_MASK 0xff00UL
+#define CPU_AFF2_MASK 0xff0000UL
+#define CPU_AFF3_MASK 0xff00000000UL
+#define CPU_AFF_MASK (CPU_AFF0_MASK | CPU_AFF1_MASK | \
+ CPU_AFF2_MASK| CPU_AFF3_MASK) /* Mask affinity fields in MPIDR_EL1 */
+
+#ifdef _KERNEL
+
+#define CPU_IMPL_ARM 0x41
+#define CPU_IMPL_BROADCOM 0x42
+#define CPU_IMPL_CAVIUM 0x43
+#define CPU_IMPL_DEC 0x44
+#define CPU_IMPL_INFINEON 0x49
+#define CPU_IMPL_FREESCALE 0x4D
+#define CPU_IMPL_NVIDIA 0x4E
+#define CPU_IMPL_APM 0x50
+#define CPU_IMPL_QUALCOMM 0x51
+#define CPU_IMPL_MARVELL 0x56
+#define CPU_IMPL_INTEL 0x69
+
+/* ARM Part numbers */
+#define CPU_PART_FOUNDATION 0xD00
+#define CPU_PART_CORTEX_A35 0xD04
+#define CPU_PART_CORTEX_A53 0xD03
+#define CPU_PART_CORTEX_A55 0xD05
+#define CPU_PART_CORTEX_A57 0xD07
+#define CPU_PART_CORTEX_A72 0xD08
+#define CPU_PART_CORTEX_A73 0xD09
+#define CPU_PART_CORTEX_A75 0xD0A
+
+/* Cavium Part numbers */
+#define CPU_PART_THUNDERX 0x0A1
+#define CPU_PART_THUNDERX_81XX 0x0A2
+#define CPU_PART_THUNDERX_83XX 0x0A3
+#define CPU_PART_THUNDERX2 0x0AF
+
+#define CPU_REV_THUNDERX_1_0 0x00
+#define CPU_REV_THUNDERX_1_1 0x01
+
+#define CPU_REV_THUNDERX2_0 0x00
+
+/* APM / Ampere Part Number */
+#define CPU_PART_EMAG8180 0x000
+
+#define CPU_IMPL(midr) (((midr) >> 24) & 0xff)
+#define CPU_PART(midr) (((midr) >> 4) & 0xfff)
+#define CPU_VAR(midr) (((midr) >> 20) & 0xf)
+#define CPU_REV(midr) (((midr) >> 0) & 0xf)
+
+#define CPU_IMPL_TO_MIDR(val) (((val) & 0xff) << 24)
+#define CPU_PART_TO_MIDR(val) (((val) & 0xfff) << 4)
+#define CPU_VAR_TO_MIDR(val) (((val) & 0xf) << 20)
+#define CPU_REV_TO_MIDR(val) (((val) & 0xf) << 0)
+
+#define CPU_IMPL_MASK (0xff << 24)
+#define CPU_PART_MASK (0xfff << 4)
+#define CPU_VAR_MASK (0xf << 20)
+#define CPU_REV_MASK (0xf << 0)
+
+#define CPU_ID_RAW(impl, part, var, rev) \
+ (CPU_IMPL_TO_MIDR((impl)) | \
+ CPU_PART_TO_MIDR((part)) | CPU_VAR_TO_MIDR((var)) | \
+ CPU_REV_TO_MIDR((rev)))
+
+#define CPU_MATCH(mask, impl, part, var, rev) \
+ (((mask) & PCPU_GET(midr)) == \
+ ((mask) & CPU_ID_RAW((impl), (part), (var), (rev))))
+
+#define CPU_MATCH_RAW(mask, devid) \
+ (((mask) & PCPU_GET(midr)) == ((mask) & (devid)))
+
+/*
+ * Chip-specific errata. This defines are intended to be
+ * booleans used within if statements. When an appropriate
+ * kernel option is disabled, these defines must be defined
+ * as 0 to allow the compiler to remove a dead code thus
+ * produce better optimized kernel image.
+ */
+/*
+ * Vendor: Cavium
+ * Chip: ThunderX
+ * Revision(s): Pass 1.0, Pass 1.1
+ */
+#ifdef THUNDERX_PASS_1_1_ERRATA
+#define CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1 \
+ (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \
+ CPU_IMPL_CAVIUM, CPU_PART_THUNDERX, 0, CPU_REV_THUNDERX_1_0) || \
+ CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \
+ CPU_IMPL_CAVIUM, CPU_PART_THUNDERX, 0, CPU_REV_THUNDERX_1_1))
+#else
+#define CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1 0
+#endif
+
+
+extern char btext[];
+extern char etext[];
+
+extern uint64_t __cpu_affinity[];
+
+void cpu_halt(void) __dead2;
+void cpu_reset(void) __dead2;
+void fork_trampoline(void);
+void identify_cpu(void);
+void install_cpu_errata(void);
+void print_cpu_features(u_int);
+void swi_vm(void *v);
+
+#define CPU_AFFINITY(cpu) __cpu_affinity[(cpu)]
+#define CPU_CURRENT_SOCKET \
+ (CPU_AFF2(CPU_AFFINITY(PCPU_GET(cpuid))))
+
+static __inline uint64_t
+get_cyclecount(void)
+{
+ uint64_t ret;
+
+ ret = READ_SPECIALREG(cntvct_el0);
+
+ return (ret);
+}
+
+#define ADDRESS_TRANSLATE_FUNC(stage) \
+static inline uint64_t \
+arm64_address_translate_ ##stage (uint64_t addr) \
+{ \
+ uint64_t ret; \
+ \
+ __asm __volatile( \
+ "at " __STRING(stage) ", %1 \n" \
+ "mrs %0, par_el1" : "=r"(ret) : "r"(addr)); \
+ \
+ return (ret); \
+}
+
+ADDRESS_TRANSLATE_FUNC(s1e0r)
+ADDRESS_TRANSLATE_FUNC(s1e0w)
+ADDRESS_TRANSLATE_FUNC(s1e1r)
+ADDRESS_TRANSLATE_FUNC(s1e1w)
+
+#endif
+
+#endif /* !_MACHINE_CPU_H_ */
diff --git a/freebsd/sys/arm64/include/machine/cpufunc.h b/freebsd/sys/arm64/include/machine/cpufunc.h
new file mode 100644
index 00000000..c3e2a36e
--- /dev/null
+++ b/freebsd/sys/arm64/include/machine/cpufunc.h
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 2014 Andrew Turner
+ * 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_CPUFUNC_H_
+#define _MACHINE_CPUFUNC_H_
+
+static __inline void
+breakpoint(void)
+{
+
+ __asm("brk #0");
+}
+
+#ifdef _KERNEL
+
+#include <machine/armreg.h>
+
+void pan_enable(void);
+
+static __inline register_t
+dbg_disable(void)
+{
+ uint32_t ret;
+
+ __asm __volatile(
+ "mrs %x0, daif \n"
+ "msr daifset, #8 \n"
+ : "=&r" (ret));
+
+ return (ret);
+}
+
+static __inline void
+dbg_enable(void)
+{
+
+ __asm __volatile("msr daifclr, #8");
+}
+
+static __inline register_t
+intr_disable(void)
+{
+ /* DAIF is a 32-bit register */
+ uint32_t ret;
+
+ __asm __volatile(
+ "mrs %x0, daif \n"
+ "msr daifset, #2 \n"
+ : "=&r" (ret));
+
+ return (ret);
+}
+
+static __inline void
+intr_restore(register_t s)
+{
+
+ WRITE_SPECIALREG(daif, s);
+}
+
+static __inline void
+intr_enable(void)
+{
+
+ __asm __volatile("msr daifclr, #2");
+}
+
+static __inline register_t
+get_midr(void)
+{
+ uint64_t midr;
+
+ midr = READ_SPECIALREG(midr_el1);
+
+ return (midr);
+}
+
+static __inline register_t
+get_mpidr(void)
+{
+ uint64_t mpidr;
+
+ mpidr = READ_SPECIALREG(mpidr_el1);
+
+ return (mpidr);
+}
+
+static __inline void
+clrex(void)
+{
+
+ /*
+ * Ensure compiler barrier, otherwise the monitor clear might
+ * occur too late for us ?
+ */
+ __asm __volatile("clrex" : : : "memory");
+}
+
+extern int64_t dcache_line_size;
+extern int64_t icache_line_size;
+extern int64_t idcache_line_size;
+extern int64_t dczva_line_size;
+
+#define cpu_nullop() arm64_nullop()
+#define cpufunc_nullop() arm64_nullop()
+#define cpu_setttb(a) arm64_setttb(a)
+
+#define cpu_tlb_flushID() arm64_tlb_flushID()
+
+#define cpu_dcache_wbinv_range(a, s) arm64_dcache_wbinv_range((a), (s))
+#define cpu_dcache_inv_range(a, s) arm64_dcache_inv_range((a), (s))
+#define cpu_dcache_wb_range(a, s) arm64_dcache_wb_range((a), (s))
+
+#define cpu_idcache_wbinv_range(a, s) arm64_idcache_wbinv_range((a), (s))
+#define cpu_icache_sync_range(a, s) arm64_icache_sync_range((a), (s))
+
+void arm64_nullop(void);
+void arm64_setttb(vm_offset_t);
+void arm64_tlb_flushID(void);
+void arm64_tlb_flushID_SE(vm_offset_t);
+void arm64_icache_sync_range(vm_offset_t, vm_size_t);
+void arm64_idcache_wbinv_range(vm_offset_t, vm_size_t);
+void arm64_dcache_wbinv_range(vm_offset_t, vm_size_t);
+void arm64_dcache_inv_range(vm_offset_t, vm_size_t);
+void arm64_dcache_wb_range(vm_offset_t, vm_size_t);
+
+#endif /* _KERNEL */
+#endif /* _MACHINE_CPUFUNC_H_ */
diff --git a/freebsd/sys/arm64/include/machine/in_cksum.h b/freebsd/sys/arm64/include/machine/in_cksum.h
new file mode 100644
index 00000000..522ba005
--- /dev/null
+++ b/freebsd/sys/arm64/include/machine/in_cksum.h
@@ -0,0 +1,52 @@
+/*-
+ * 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
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#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)
+u_int in_cksum_hdr(const struct ip *);
+#endif
+
+u_short in_pseudo(u_int sum, u_int b, u_int c);
+
+#endif /* _KERNEL */
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/freebsd/sys/contrib/ck/include/ck_pr.h b/freebsd/sys/contrib/ck/include/ck_pr.h
index 2a8c5398..d2b53a5c 100644
--- a/freebsd/sys/contrib/ck/include/ck_pr.h
+++ b/freebsd/sys/contrib/ck/include/ck_pr.h
@@ -51,7 +51,9 @@
#if __ARM_ARCH >= 6
#include "gcc/arm/ck_pr.h"
#else
+#ifndef __rtems__
#include "gcc/arm/ck_pr_armv4.h"
+#endif /* __rtems__ */
#endif
#elif defined(__aarch64__)
#include "gcc/aarch64/ck_pr.h"
diff --git a/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h
new file mode 100644
index 00000000..aa4e3090
--- /dev/null
+++ b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2009-2016 Samy Al Bahra.
+ * Copyright 2013-2016 Olivier Houchard.
+ * 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.
+ */
+
+#ifndef CK_PR_AARCH64_LLSC_H
+#define CK_PR_AARCH64_LLSC_H
+
+#ifndef CK_PR_H
+#error Do not include this file directly, use ck_pr.h
+#endif
+
+CK_CC_INLINE static bool
+ck_pr_cas_64_2_value(uint64_t target[2], uint64_t compare[2], uint64_t set[2], uint64_t value[2])
+{
+ uint64_t tmp1, tmp2;
+
+ __asm__ __volatile__("1:"
+ "ldxp %0, %1, [%4];"
+ "mov %2, %0;"
+ "mov %3, %1;"
+ "eor %0, %0, %5;"
+ "eor %1, %1, %6;"
+ "orr %1, %0, %1;"
+ "mov %w0, #0;"
+ "cbnz %1, 2f;"
+ "stxp %w0, %7, %8, [%4];"
+ "cbnz %w0, 1b;"
+ "mov %w0, #1;"
+ "2:"
+ : "=&r" (tmp1), "=&r" (tmp2), "=&r" (value[0]), "=&r" (value[1])
+ : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1])
+ : "cc", "memory");
+
+ return (tmp1);
+}
+
+CK_CC_INLINE static bool
+ck_pr_cas_ptr_2_value(void *target, void *compare, void *set, void *value)
+{
+ return (ck_pr_cas_64_2_value(CK_CPP_CAST(uint64_t *, target),
+ CK_CPP_CAST(uint64_t *, compare),
+ CK_CPP_CAST(uint64_t *, set),
+ CK_CPP_CAST(uint64_t *, value)));
+}
+
+CK_CC_INLINE static bool
+ck_pr_cas_64_2(uint64_t target[2], uint64_t compare[2], uint64_t set[2])
+{
+ uint64_t tmp1, tmp2;
+
+ __asm__ __volatile__("1:"
+ "ldxp %0, %1, [%2];"
+ "eor %0, %0, %3;"
+ "eor %1, %1, %4;"
+ "orr %1, %0, %1;"
+ "mov %w0, #0;"
+ "cbnz %1, 2f;"
+ "stxp %w0, %5, %6, [%2];"
+ "cbnz %w0, 1b;"
+ "mov %w0, #1;"
+ "2:"
+ : "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1])
+ : "cc", "memory");
+
+ return (tmp1);
+}
+CK_CC_INLINE static bool
+ck_pr_cas_ptr_2(void *target, void *compare, void *set)
+{
+ return (ck_pr_cas_64_2(CK_CPP_CAST(uint64_t *, target),
+ CK_CPP_CAST(uint64_t *, compare),
+ CK_CPP_CAST(uint64_t *, set)));
+}
+
+
+#define CK_PR_CAS(N, M, T, W, R) \
+ CK_CC_INLINE static bool \
+ ck_pr_cas_##N##_value(M *target, T compare, T set, M *value) \
+ { \
+ T previous; \
+ T tmp; \
+ __asm__ __volatile__("1:" \
+ "ldxr" W " %" R "0, [%2];" \
+ "cmp %" R "0, %" R "4;" \
+ "b.ne 2f;" \
+ "stxr" W " %w1, %" R "3, [%2];" \
+ "cbnz %w1, 1b;" \
+ "2:" \
+ : "=&r" (previous), \
+ "=&r" (tmp) \
+ : "r" (target), \
+ "r" (set), \
+ "r" (compare) \
+ : "memory", "cc"); \
+ *(T *)value = previous; \
+ return (previous == compare); \
+ } \
+ CK_CC_INLINE static bool \
+ ck_pr_cas_##N(M *target, T compare, T set) \
+ { \
+ T previous; \
+ T tmp; \
+ __asm__ __volatile__( \
+ "1:" \
+ "ldxr" W " %" R "0, [%2];" \
+ "cmp %" R "0, %" R "4;" \
+ "b.ne 2f;" \
+ "stxr" W " %w1, %" R "3, [%2];" \
+ "cbnz %w1, 1b;" \
+ "2:" \
+ : "=&r" (previous), \
+ "=&r" (tmp) \
+ : "r" (target), \
+ "r" (set), \
+ "r" (compare) \
+ : "memory", "cc"); \
+ return (previous == compare); \
+ }
+
+CK_PR_CAS(ptr, void, void *, "", "")
+
+#define CK_PR_CAS_S(N, M, W, R) CK_PR_CAS(N, M, M, W, R)
+CK_PR_CAS_S(64, uint64_t, "", "")
+#ifndef CK_PR_DISABLE_DOUBLE
+CK_PR_CAS_S(double, double, "", "")
+#endif
+CK_PR_CAS_S(32, uint32_t, "", "w")
+CK_PR_CAS_S(uint, unsigned int, "", "w")
+CK_PR_CAS_S(int, int, "", "w")
+CK_PR_CAS_S(16, uint16_t, "h", "w")
+CK_PR_CAS_S(8, uint8_t, "b", "w")
+CK_PR_CAS_S(short, short, "h", "w")
+CK_PR_CAS_S(char, char, "b", "w")
+
+
+#undef CK_PR_CAS_S
+#undef CK_PR_CAS
+
+#define CK_PR_FAS(N, M, T, W, R) \
+ CK_CC_INLINE static T \
+ ck_pr_fas_##N(M *target, T v) \
+ { \
+ T previous; \
+ T tmp; \
+ __asm__ __volatile__("1:" \
+ "ldxr" W " %" R "0, [%2];" \
+ "stxr" W " %w1, %" R "3, [%2];"\
+ "cbnz %w1, 1b;" \
+ : "=&r" (previous), \
+ "=&r" (tmp) \
+ : "r" (target), \
+ "r" (v) \
+ : "memory", "cc"); \
+ return (previous); \
+ }
+
+CK_PR_FAS(64, uint64_t, uint64_t, "", "")
+CK_PR_FAS(32, uint32_t, uint32_t, "", "w")
+CK_PR_FAS(ptr, void, void *, "", "")
+CK_PR_FAS(int, int, int, "", "w")
+CK_PR_FAS(uint, unsigned int, unsigned int, "", "w")
+CK_PR_FAS(16, uint16_t, uint16_t, "h", "w")
+CK_PR_FAS(8, uint8_t, uint8_t, "b", "w")
+CK_PR_FAS(short, short, short, "h", "w")
+CK_PR_FAS(char, char, char, "b", "w")
+
+
+#undef CK_PR_FAS
+
+#define CK_PR_UNARY(O, N, M, T, I, W, R) \
+ CK_CC_INLINE static void \
+ ck_pr_##O##_##N(M *target) \
+ { \
+ T previous = 0; \
+ T tmp = 0; \
+ __asm__ __volatile__("1:" \
+ "ldxr" W " %" R "0, [%2];" \
+ I ";" \
+ "stxr" W " %w1, %" R "0, [%2];" \
+ "cbnz %w1, 1b;" \
+ : "=&r" (previous), \
+ "=&r" (tmp) \
+ : "r" (target) \
+ : "memory", "cc"); \
+ return; \
+ }
+
+CK_PR_UNARY(inc, ptr, void, void *, "add %0, %0, #1", "", "")
+CK_PR_UNARY(dec, ptr, void, void *, "sub %0, %0, #1", "", "")
+CK_PR_UNARY(not, ptr, void, void *, "mvn %0, %0", "", "")
+CK_PR_UNARY(inc, 64, uint64_t, uint64_t, "add %0, %0, #1", "", "")
+CK_PR_UNARY(dec, 64, uint64_t, uint64_t, "sub %0, %0, #1", "", "")
+CK_PR_UNARY(not, 64, uint64_t, uint64_t, "mvn %0, %0", "", "")
+
+#define CK_PR_UNARY_S(S, T, W) \
+ CK_PR_UNARY(inc, S, T, T, "add %w0, %w0, #1", W, "w") \
+ CK_PR_UNARY(dec, S, T, T, "sub %w0, %w0, #1", W, "w") \
+ CK_PR_UNARY(not, S, T, T, "mvn %w0, %w0", W, "w") \
+
+CK_PR_UNARY_S(32, uint32_t, "")
+CK_PR_UNARY_S(uint, unsigned int, "")
+CK_PR_UNARY_S(int, int, "")
+CK_PR_UNARY_S(16, uint16_t, "h")
+CK_PR_UNARY_S(8, uint8_t, "b")
+CK_PR_UNARY_S(short, short, "h")
+CK_PR_UNARY_S(char, char, "b")
+
+#undef CK_PR_UNARY_S
+#undef CK_PR_UNARY
+
+#define CK_PR_BINARY(O, N, M, T, I, W, R) \
+ CK_CC_INLINE static void \
+ ck_pr_##O##_##N(M *target, T delta) \
+ { \
+ T previous; \
+ T tmp; \
+ __asm__ __volatile__("1:" \
+ "ldxr" W " %" R "0, [%2];"\
+ I " %" R "0, %" R "0, %" R "3;" \
+ "stxr" W " %w1, %" R "0, [%2];" \
+ "cbnz %w1, 1b;" \
+ : "=&r" (previous), \
+ "=&r" (tmp) \
+ : "r" (target), \
+ "r" (delta) \
+ : "memory", "cc"); \
+ return; \
+ }
+
+CK_PR_BINARY(and, ptr, void, uintptr_t, "and", "", "")
+CK_PR_BINARY(add, ptr, void, uintptr_t, "add", "", "")
+CK_PR_BINARY(or, ptr, void, uintptr_t, "orr", "", "")
+CK_PR_BINARY(sub, ptr, void, uintptr_t, "sub", "", "")
+CK_PR_BINARY(xor, ptr, void, uintptr_t, "eor", "", "")
+CK_PR_BINARY(and, 64, uint64_t, uint64_t, "and", "", "")
+CK_PR_BINARY(add, 64, uint64_t, uint64_t, "add", "", "")
+CK_PR_BINARY(or, 64, uint64_t, uint64_t, "orr", "", "")
+CK_PR_BINARY(sub, 64, uint64_t, uint64_t, "sub", "", "")
+CK_PR_BINARY(xor, 64, uint64_t, uint64_t, "eor", "", "")
+
+#define CK_PR_BINARY_S(S, T, W) \
+ CK_PR_BINARY(and, S, T, T, "and", W, "w") \
+ CK_PR_BINARY(add, S, T, T, "add", W, "w") \
+ CK_PR_BINARY(or, S, T, T, "orr", W, "w") \
+ CK_PR_BINARY(sub, S, T, T, "sub", W, "w") \
+ CK_PR_BINARY(xor, S, T, T, "eor", W, "w")
+
+CK_PR_BINARY_S(32, uint32_t, "")
+CK_PR_BINARY_S(uint, unsigned int, "")
+CK_PR_BINARY_S(int, int, "")
+CK_PR_BINARY_S(16, uint16_t, "h")
+CK_PR_BINARY_S(8, uint8_t, "b")
+CK_PR_BINARY_S(short, short, "h")
+CK_PR_BINARY_S(char, char, "b")
+
+#undef CK_PR_BINARY_S
+#undef CK_PR_BINARY
+
+CK_CC_INLINE static void *
+ck_pr_faa_ptr(void *target, uintptr_t delta)
+{
+ uintptr_t previous, r, tmp;
+
+ __asm__ __volatile__("1:"
+ "ldxr %0, [%3];"
+ "add %1, %4, %0;"
+ "stxr %w2, %1, [%3];"
+ "cbnz %w2, 1b;"
+ : "=&r" (previous),
+ "=&r" (r),
+ "=&r" (tmp)
+ : "r" (target),
+ "r" (delta)
+ : "memory", "cc");
+
+ return (void *)(previous);
+}
+
+CK_CC_INLINE static uint64_t
+ck_pr_faa_64(uint64_t *target, uint64_t delta)
+{
+ uint64_t previous, r, tmp;
+
+ __asm__ __volatile__("1:"
+ "ldxr %0, [%3];"
+ "add %1, %4, %0;"
+ "stxr %w2, %1, [%3];"
+ "cbnz %w2, 1b;"
+ : "=&r" (previous),
+ "=&r" (r),
+ "=&r" (tmp)
+ : "r" (target),
+ "r" (delta)
+ : "memory", "cc");
+
+ return (previous);
+}
+
+#define CK_PR_FAA(S, T, W) \
+ CK_CC_INLINE static T \
+ ck_pr_faa_##S(T *target, T delta) \
+ { \
+ T previous, r, tmp; \
+ __asm__ __volatile__("1:" \
+ "ldxr" W " %w0, [%3];" \
+ "add %w1, %w4, %w0;" \
+ "stxr" W " %w2, %w1, [%3];" \
+ "cbnz %w2, 1b;" \
+ : "=&r" (previous), \
+ "=&r" (r), \
+ "=&r" (tmp) \
+ : "r" (target), \
+ "r" (delta) \
+ : "memory", "cc"); \
+ return (previous); \
+ }
+
+CK_PR_FAA(32, uint32_t, "")
+CK_PR_FAA(uint, unsigned int, "")
+CK_PR_FAA(int, int, "")
+CK_PR_FAA(16, uint16_t, "h")
+CK_PR_FAA(8, uint8_t, "b")
+CK_PR_FAA(short, short, "h")
+CK_PR_FAA(char, char, "b")
+
+#undef CK_PR_FAA
+
+#endif /* CK_PR_AARCH64_LLSC_H */
diff --git a/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h
new file mode 100644
index 00000000..e2c9554c
--- /dev/null
+++ b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2009-2016 Samy Al Bahra.
+ * Copyright 2013-2016 Olivier Houchard.
+ * Copyright 2016 Alexey Kopytov.
+ * 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.
+ */
+
+#ifndef CK_PR_AARCH64_LSE_H
+#define CK_PR_AARCH64_LSE_H
+
+#ifndef CK_PR_H
+#error Do not include this file directly, use ck_pr.h
+#endif
+
+CK_CC_INLINE static bool
+ck_pr_cas_64_2_value(uint64_t target[2], uint64_t compare[2], uint64_t set[2], uint64_t value[2])
+{
+ uint64_t tmp1;
+ uint64_t tmp2;
+ register uint64_t x0 __asm__ ("x0") = compare[0];
+ register uint64_t x1 __asm__ ("x1") = compare[1];
+ register uint64_t x2 __asm__ ("x2") = set[0];
+ register uint64_t x3 __asm__ ("x3") = set[1];
+
+ __asm__ __volatile__("casp %0, %1, %4, %5, [%6];"
+ "eor %2, %0, %7;"
+ "eor %3, %1, %8;"
+ "orr %2, %2, %3;"
+ : "+&r" (x0), "+&r" (x1), "=&r" (tmp1), "=&r" (tmp2)
+ : "r" (x2), "r" (x3), "r" (target), "r" (compare[0]), "r" (compare[1])
+ : "memory");
+
+ value[0] = x0;
+ value[1] = x1;
+
+ return (!!tmp1);
+}
+
+CK_CC_INLINE static bool
+ck_pr_cas_ptr_2_value(void *target, void *compare, void *set, void *value)
+{
+ return (ck_pr_cas_64_2_value(CK_CPP_CAST(uint64_t *, target),
+ CK_CPP_CAST(uint64_t *, compare),
+ CK_CPP_CAST(uint64_t *, set),
+ CK_CPP_CAST(uint64_t *, value)));
+}
+
+CK_CC_INLINE static bool
+ck_pr_cas_64_2(uint64_t target[2], uint64_t compare[2], uint64_t set[2])
+{
+ register uint64_t x0 __asm__ ("x0") = compare[0];
+ register uint64_t x1 __asm__ ("x1") = compare[1];
+ register uint64_t x2 __asm__ ("x2") = set[0];
+ register uint64_t x3 __asm__ ("x3") = set[1];
+
+ __asm__ __volatile__("casp %0, %1, %2, %3, [%4];"
+ "eor %0, %0, %5;"
+ "eor %1, %1, %6;"
+ "orr %0, %0, %1;"
+ : "+&r" (x0), "+&r" (x1)
+ : "r" (x2), "r" (x3), "r" (target), "r" (compare[0]), "r" (compare[1])
+ : "memory");
+
+ return (!!x0);
+}
+CK_CC_INLINE static bool
+ck_pr_cas_ptr_2(void *target, void *compare, void *set)
+{
+ return (ck_pr_cas_64_2(CK_CPP_CAST(uint64_t *, target),
+ CK_CPP_CAST(uint64_t *, compare),
+ CK_CPP_CAST(uint64_t *, set)));
+}
+
+
+#define CK_PR_CAS(N, M, T, W, R) \
+ CK_CC_INLINE static bool \
+ ck_pr_cas_##N##_value(M *target, T compare, T set, M *value) \
+ { \
+ *(T *)value = compare; \
+ __asm__ __volatile__( \
+ "cas" W " %" R "0, %" R "2, [%1];" \
+ : "+&r" (*(T *)value) \
+ : "r" (target), \
+ "r" (set) \
+ : "memory"); \
+ return (*(T *)value == compare); \
+ } \
+ CK_CC_INLINE static bool \
+ ck_pr_cas_##N(M *target, T compare, T set) \
+ { \
+ T previous = compare; \
+ __asm__ __volatile__( \
+ "cas" W " %" R "0, %" R "2, [%1];" \
+ : "+&r" (previous) \
+ : "r" (target), \
+ "r" (set) \
+ : "memory"); \
+ return (previous == compare); \
+ }
+
+CK_PR_CAS(ptr, void, void *, "", "")
+
+#define CK_PR_CAS_S(N, M, W, R) CK_PR_CAS(N, M, M, W, R)
+CK_PR_CAS_S(64, uint64_t, "", "")
+#ifndef CK_PR_DISABLE_DOUBLE
+CK_PR_CAS_S(double, double, "", "")
+#endif
+CK_PR_CAS_S(32, uint32_t, "", "w")
+CK_PR_CAS_S(uint, unsigned int, "", "w")
+CK_PR_CAS_S(int, int, "", "w")
+CK_PR_CAS_S(16, uint16_t, "h", "w")
+CK_PR_CAS_S(8, uint8_t, "b", "w")
+CK_PR_CAS_S(short, short, "h", "w")
+CK_PR_CAS_S(char, char, "b", "w")
+
+
+#undef CK_PR_CAS_S
+#undef CK_PR_CAS
+
+#define CK_PR_FAS(N, M, T, W, R) \
+ CK_CC_INLINE static T \
+ ck_pr_fas_##N(M *target, T v) \
+ { \
+ T previous; \
+ __asm__ __volatile__( \
+ "swp" W " %" R "2, %" R "0, [%1];" \
+ : "=&r" (previous) \
+ : "r" (target), \
+ "r" (v) \
+ : "memory"); \
+ return (previous); \
+ }
+
+CK_PR_FAS(64, uint64_t, uint64_t, "", "")
+CK_PR_FAS(32, uint32_t, uint32_t, "", "w")
+CK_PR_FAS(ptr, void, void *, "", "")
+CK_PR_FAS(int, int, int, "", "w")
+CK_PR_FAS(uint, unsigned int, unsigned int, "", "w")
+CK_PR_FAS(16, uint16_t, uint16_t, "h", "w")
+CK_PR_FAS(8, uint8_t, uint8_t, "b", "w")
+CK_PR_FAS(short, short, short, "h", "w")
+CK_PR_FAS(char, char, char, "b", "w")
+
+
+#undef CK_PR_FAS
+
+#define CK_PR_UNARY(O, N, M, T, I, W, R, S) \
+ CK_CC_INLINE static void \
+ ck_pr_##O##_##N(M *target) \
+ { \
+ __asm__ __volatile__(I ";" \
+ "st" S W " " R "0, [%0];" \
+ : \
+ : "r" (target) \
+ : "x0", "memory"); \
+ return; \
+ }
+
+CK_PR_UNARY(inc, ptr, void, void *, "mov x0, 1", "", "x", "add")
+CK_PR_UNARY(dec, ptr, void, void *, "mov x0, -1", "", "x", "add")
+CK_PR_UNARY(not, ptr, void, void *, "mov x0, -1", "", "x", "eor")
+CK_PR_UNARY(inc, 64, uint64_t, uint64_t, "mov x0, 1", "", "x", "add")
+CK_PR_UNARY(dec, 64, uint64_t, uint64_t, "mov x0, -1", "", "x", "add")
+CK_PR_UNARY(not, 64, uint64_t, uint64_t, "mov x0, -1", "", "x", "eor")
+
+#define CK_PR_UNARY_S(S, T, W) \
+ CK_PR_UNARY(inc, S, T, T, "mov w0, 1", W, "w", "add") \
+ CK_PR_UNARY(dec, S, T, T, "mov w0, -1", W, "w", "add") \
+ CK_PR_UNARY(not, S, T, T, "mov w0, -1", W, "w", "eor") \
+
+CK_PR_UNARY_S(32, uint32_t, "")
+CK_PR_UNARY_S(uint, unsigned int, "")
+CK_PR_UNARY_S(int, int, "")
+CK_PR_UNARY_S(16, uint16_t, "h")
+CK_PR_UNARY_S(8, uint8_t, "b")
+CK_PR_UNARY_S(short, short, "h")
+CK_PR_UNARY_S(char, char, "b")
+
+#undef CK_PR_UNARY_S
+#undef CK_PR_UNARY
+
+#define CK_PR_BINARY(O, N, M, T, S, W, R, I) \
+ CK_CC_INLINE static void \
+ ck_pr_##O##_##N(M *target, T delta) \
+ { \
+ __asm__ __volatile__(I ";" \
+ "st" S W " %" R "0, [%1];" \
+ : "+&r" (delta) \
+ : "r" (target) \
+ : "memory"); \
+ return; \
+ }
+
+CK_PR_BINARY(and, ptr, void, uintptr_t, "clr", "", "", "mvn %0, %0")
+CK_PR_BINARY(add, ptr, void, uintptr_t, "add", "", "", "")
+CK_PR_BINARY(or, ptr, void, uintptr_t, "set", "", "", "")
+CK_PR_BINARY(sub, ptr, void, uintptr_t, "add", "", "", "neg %0, %0")
+CK_PR_BINARY(xor, ptr, void, uintptr_t, "eor", "", "", "")
+CK_PR_BINARY(and, 64, uint64_t, uint64_t, "clr", "", "", "mvn %0, %0")
+CK_PR_BINARY(add, 64, uint64_t, uint64_t, "add", "", "", "")
+CK_PR_BINARY(or, 64, uint64_t, uint64_t, "set", "", "", "")
+CK_PR_BINARY(sub, 64, uint64_t, uint64_t, "add", "", "", "neg %0, %0")
+CK_PR_BINARY(xor, 64, uint64_t, uint64_t, "eor", "", "", "")
+
+#define CK_PR_BINARY_S(S, T, W) \
+ CK_PR_BINARY(and, S, T, T, "clr", W, "w", "mvn %w0, %w0") \
+ CK_PR_BINARY(add, S, T, T, "add", W, "w", "") \
+ CK_PR_BINARY(or, S, T, T, "set", W, "w", "") \
+ CK_PR_BINARY(sub, S, T, T, "add", W, "w", "neg %w0, %w0") \
+ CK_PR_BINARY(xor, S, T, T, "eor", W, "w", "")
+
+CK_PR_BINARY_S(32, uint32_t, "")
+CK_PR_BINARY_S(uint, unsigned int, "")
+CK_PR_BINARY_S(int, int, "")
+CK_PR_BINARY_S(16, uint16_t, "h")
+CK_PR_BINARY_S(8, uint8_t, "b")
+CK_PR_BINARY_S(short, short, "h")
+CK_PR_BINARY_S(char, char, "b")
+
+#undef CK_PR_BINARY_S
+#undef CK_PR_BINARY
+
+CK_CC_INLINE static void *
+ck_pr_faa_ptr(void *target, uintptr_t delta)
+{
+ uintptr_t previous;
+
+ __asm__ __volatile__(
+ "ldadd %2, %0, [%1];"
+ : "=r" (previous)
+ : "r" (target),
+ "r" (delta)
+ : "memory");
+
+ return (void *)(previous);
+}
+
+CK_CC_INLINE static uint64_t
+ck_pr_faa_64(uint64_t *target, uint64_t delta)
+{
+ uint64_t previous;
+
+ __asm__ __volatile__(
+ "ldadd %2, %0, [%1];"
+ : "=r" (previous)
+ : "r" (target),
+ "r" (delta)
+ : "memory");
+
+ return (previous);
+}
+
+#define CK_PR_FAA(S, T, W) \
+ CK_CC_INLINE static T \
+ ck_pr_faa_##S(T *target, T delta) \
+ { \
+ T previous; \
+ __asm__ __volatile__( \
+ "ldadd" W " %w2, %w0, [%1];" \
+ : "=r" (previous) \
+ : "r" (target), \
+ "r" (delta) \
+ : "memory"); \
+ return (previous); \
+ }
+
+CK_PR_FAA(32, uint32_t, "")
+CK_PR_FAA(uint, unsigned int, "")
+CK_PR_FAA(int, int, "")
+CK_PR_FAA(16, uint16_t, "h")
+CK_PR_FAA(8, uint8_t, "b")
+CK_PR_FAA(short, short, "h")
+CK_PR_FAA(char, char, "b")
+
+#undef CK_PR_FAA
+
+#endif /* CK_PR_AARCH64_LSE_H */
diff --git a/freebsd/sys/dev/e1000/if_em.c b/freebsd/sys/dev/e1000/if_em.c
index 32eb4afe..4fc6e7b6 100644
--- a/freebsd/sys/dev/e1000/if_em.c
+++ b/freebsd/sys/dev/e1000/if_em.c
@@ -1837,8 +1837,14 @@ em_if_update_admin_status(if_ctx_t ctx)
if (adapter->hw.mac.type < em_mac_min)
lem_smartspeed(adapter);
+#ifdef __rtems__
+ else if (hw->mac.type == e1000_82574 &&
+ adapter->intr_type == IFLIB_INTR_MSIX)
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC);
+#else /* __rtems__ */
E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC);
+#endif /* __rtems__ */
}
static void
diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c
index e8287ed2..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);
}
@@ -714,15 +753,16 @@ ffec_encap(struct ifnet *ifp, struct ffec_softc *sc, struct mbuf *m0,
tx_desc->buf_paddr = segs[i].ds_addr;
tx_desc->flags2 = flags2;
#ifdef __rtems__
- uintptr_t addr_flush = (uintptr_t)segs[i].ds_addr;
+ uintptr_t first_flush = (uintptr_t)segs[i].ds_addr;
size_t len_flush = segs[i].ds_len;
#ifdef CPU_CACHE_LINE_BYTES
+ uintptr_t last_flush = first_flush + len_flush;
/* mbufs should be cache line aligned. So we can just round. */
- addr_flush = addr_flush & ~(CPU_CACHE_LINE_BYTES - 1);
- len_flush = (len_flush + (CPU_CACHE_LINE_BYTES - 1)) &
- ~(CPU_CACHE_LINE_BYTES - 1);
+ first_flush = rounddown2(first_flush, CPU_CACHE_LINE_BYTES);
+ last_flush = roundup2(last_flush, CPU_CACHE_LINE_BYTES);
+ len_flush = last_flush - first_flush;
#endif
- rtems_cache_flush_multiple_data_lines((void*)addr_flush,
+ rtems_cache_flush_multiple_data_lines((void*)first_flush,
len_flush);
#endif /* __rtems__ */
@@ -1568,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);
}
@@ -1717,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)
{
@@ -1734,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
@@ -2038,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/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c
index 3960845c..ff517abc 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
@@ -1196,7 +1202,11 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag)
goto out;
}
if (len != 0) {
+#ifndef __rtems__
dp = malloc(len, M_TEMP, M_WAITOK);
+#else /* __rtems__ */
+ dp = rtems_cache_aligned_malloc(len);
+#endif /* __rtems__ */
err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len);
if (err != 0)
goto out;
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.c b/freebsd/sys/dev/usb/controller/dwc_otg.c
index 005c7ece..b75b8872 100644
--- a/freebsd/sys/dev/usb/controller/dwc_otg.c
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.c
@@ -470,38 +470,31 @@ dwc_otg_uses_split(struct usb_device *udev)
static void
dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc)
{
-
- /*
- * Disabled until further. Assuming that the register is already
- * programmed correctly by the boot loader.
- */
-#if 0
+ uint32_t fslpclksel;
+ uint32_t frint;
uint32_t temp;
- /* setup HOST frame interval register, based on existing value */
- temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK;
- if (temp >= 10000)
- temp /= 1000;
- else
- temp /= 125;
-
- /* figure out nearest X-tal value */
- if (temp >= 54)
- temp = 60; /* MHz */
- else if (temp >= 39)
- temp = 48; /* MHz */
- else
- temp = 30; /* MHz */
-
- if (sc->sc_flags.status_high_speed)
- temp *= 125;
- else
- temp *= 1000;
-
- DPRINTF("HFIR=0x%08x\n", temp);
+ if (sc->sc_flags.status_high_speed ||
+ sc->sc_phy_type != DWC_OTG_PHY_INTERNAL) {
+ fslpclksel = 0;
+ frint = 60000;
+ } else if (sc->sc_flags.status_low_speed) {
+ fslpclksel = 2;
+ frint = 6000;
+ } else {
+ fslpclksel = 1;
+ frint = 48000;
+ }
+ temp = DWC_OTG_READ_4(sc, DOTG_HFIR);
+ temp &= ~HFIR_FRINT_MASK;
+ temp |= frint;
DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp);
-#endif
+
+ temp = DWC_OTG_READ_4(sc, DOTG_HCFG);
+ temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK);
+ temp |= (fslpclksel << HCFG_FSLSPCLKSEL_SHIFT);
+ DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp);
}
static void
@@ -3049,8 +3042,10 @@ dwc_otg_interrupt(void *arg)
else
sc->sc_flags.status_high_speed = 0;
- if (hprt & HPRT_PRTCONNDET)
+ if (hprt & HPRT_PRTCONNDET) {
sc->sc_flags.change_connect = 1;
+ dwc_otg_update_host_frame_interval(sc);
+ }
if (hprt & HPRT_PRTSUSP)
dwc_otg_suspend_irq(sc);
@@ -3059,9 +3054,6 @@ dwc_otg_interrupt(void *arg)
/* complete root HUB interrupt endpoint */
dwc_otg_root_intr(sc);
-
- /* update host frame interval */
- dwc_otg_update_host_frame_interval(sc);
}
/*
@@ -4049,14 +4041,6 @@ dwc_otg_init(struct dwc_otg_softc *sc)
DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF);
}
- if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) {
- /* setup clocks */
- temp = DWC_OTG_READ_4(sc, DOTG_HCFG);
- temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK);
- temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT);
- DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp);
- }
-
/* only enable global IRQ */
DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG,
GAHBCFG_GLBLINTRMSK);
diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.h b/freebsd/sys/dev/usb/controller/dwc_otg.h
index 2de0214f..fd8ce96e 100644
--- a/freebsd/sys/dev/usb/controller/dwc_otg.h
+++ b/freebsd/sys/dev/usb/controller/dwc_otg.h
@@ -223,5 +223,8 @@ driver_filter_t dwc_otg_filter_interrupt;
driver_intr_t dwc_otg_interrupt;
int dwc_otg_init(struct dwc_otg_softc *);
void dwc_otg_uninit(struct dwc_otg_softc *);
+#ifdef __rtems__
+void dwc_otg_platform_init(struct dwc_otg_softc *);
+#endif /* __rtems__ */
#endif /* _DWC_OTG_H_ */
diff --git a/freebsd/sys/dev/usb/controller/ehci_pci.c b/freebsd/sys/dev/usb/controller/ehci_pci.c
new file mode 100644
index 00000000..58789221
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/ehci_pci.c
@@ -0,0 +1,593 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * Carlstedt Research & Technology.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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$");
+
+/*
+ * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ */
+
+/* The low level controller code for EHCI has been split into
+ * PCI probes and EHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+#include <rtems/bsd/local/usb_if.h>
+
+#define PCI_EHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_EHCI_VENDORID_AMD 0x1022
+#define PCI_EHCI_VENDORID_APPLE 0x106b
+#define PCI_EHCI_VENDORID_ATI 0x1002
+#define PCI_EHCI_VENDORID_CMDTECH 0x1095
+#define PCI_EHCI_VENDORID_INTEL 0x8086
+#define PCI_EHCI_VENDORID_NEC 0x1033
+#define PCI_EHCI_VENDORID_OPTI 0x1045
+#define PCI_EHCI_VENDORID_PHILIPS 0x1131
+#define PCI_EHCI_VENDORID_SIS 0x1039
+#define PCI_EHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_EHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_EHCI_VENDORID_VIA 0x1106
+
+static device_probe_t ehci_pci_probe;
+static device_attach_t ehci_pci_attach;
+static device_detach_t ehci_pci_detach;
+static usb_take_controller_t ehci_pci_take_controller;
+
+static const char *
+ehci_pci_match(device_t self)
+{
+ uint32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case 0x523910b9:
+ return "ALi M5239 USB 2.0 controller";
+
+ case 0x10227463:
+ return "AMD 8111 USB 2.0 controller";
+
+ case 0x20951022:
+ return ("AMD CS5536 (Geode) USB 2.0 controller");
+ case 0x78081022:
+ return ("AMD FCH USB 2.0 controller");
+
+ case 0x43451002:
+ return "ATI SB200 USB 2.0 controller";
+ case 0x43731002:
+ return "ATI SB400 USB 2.0 controller";
+ case 0x43961002:
+ return ("AMD SB7x0/SB8x0/SB9x0 USB 2.0 controller");
+
+ case 0x0f348086:
+ return ("Intel BayTrail USB 2.0 controller");
+ case 0x1c268086:
+ return ("Intel Cougar Point USB 2.0 controller");
+ case 0x1c2d8086:
+ return ("Intel Cougar Point USB 2.0 controller");
+ case 0x1d268086:
+ return ("Intel Patsburg USB 2.0 controller");
+ case 0x1d2d8086:
+ return ("Intel Patsburg USB 2.0 controller");
+ case 0x1e268086:
+ return ("Intel Panther Point USB 2.0 controller");
+ case 0x1e2d8086:
+ return ("Intel Panther Point USB 2.0 controller");
+ case 0x1f2c8086:
+ return ("Intel Avoton USB 2.0 controller");
+ case 0x25ad8086:
+ return "Intel 6300ESB USB 2.0 controller";
+ case 0x24cd8086:
+ return "Intel 82801DB/L/M (ICH4) USB 2.0 controller";
+ case 0x24dd8086:
+ return "Intel 82801EB/R (ICH5) USB 2.0 controller";
+ case 0x265c8086:
+ return "Intel 82801FB (ICH6) USB 2.0 controller";
+ case 0x268c8086:
+ return ("Intel 63XXESB USB 2.0 controller");
+ case 0x27cc8086:
+ return "Intel 82801GB/R (ICH7) USB 2.0 controller";
+ case 0x28368086:
+ return "Intel 82801H (ICH8) USB 2.0 controller USB2-A";
+ case 0x283a8086:
+ return "Intel 82801H (ICH8) USB 2.0 controller USB2-B";
+ case 0x293a8086:
+ return "Intel 82801I (ICH9) USB 2.0 controller";
+ case 0x293c8086:
+ return "Intel 82801I (ICH9) USB 2.0 controller";
+ case 0x3a3a8086:
+ return "Intel 82801JI (ICH10) USB 2.0 controller USB-A";
+ case 0x3a3c8086:
+ return "Intel 82801JI (ICH10) USB 2.0 controller USB-B";
+ case 0x3b348086:
+ return ("Intel PCH USB 2.0 controller USB-A");
+ case 0x3b3c8086:
+ return ("Intel PCH USB 2.0 controller USB-B");
+ case 0x8c268086:
+ return ("Intel Lynx Point USB 2.0 controller USB-A");
+ case 0x8c2d8086:
+ return ("Intel Lynx Point USB 2.0 controller USB-B");
+ case 0x8ca68086:
+ return ("Intel Wildcat Point USB 2.0 controller USB-A");
+ case 0x8cad8086:
+ return ("Intel Wildcat Point USB 2.0 controller USB-B");
+ case 0x8d268086:
+ return ("Intel Wellsburg USB 2.0 controller");
+ case 0x8d2d8086:
+ return ("Intel Wellsburg USB 2.0 controller");
+ case 0x9c268086:
+ return ("Intel Lynx Point-LP USB 2.0 controller");
+
+ case 0x00e01033:
+ return ("NEC uPD 72010x USB 2.0 controller");
+
+ case 0x006810de:
+ return "NVIDIA nForce2 USB 2.0 controller";
+ case 0x008810de:
+ return "NVIDIA nForce2 Ultra 400 USB 2.0 controller";
+ case 0x00d810de:
+ return "NVIDIA nForce3 USB 2.0 controller";
+ case 0x00e810de:
+ return "NVIDIA nForce3 250 USB 2.0 controller";
+ case 0x005b10de:
+ return "NVIDIA nForce CK804 USB 2.0 controller";
+ case 0x036d10de:
+ return "NVIDIA nForce MCP55 USB 2.0 controller";
+ case 0x03f210de:
+ return "NVIDIA nForce MCP61 USB 2.0 controller";
+ case 0x0aa610de:
+ return "NVIDIA nForce MCP79 USB 2.0 controller";
+ case 0x0aa910de:
+ return "NVIDIA nForce MCP79 USB 2.0 controller";
+ case 0x0aaa10de:
+ return "NVIDIA nForce MCP79 USB 2.0 controller";
+
+ case 0x15621131:
+ return "Philips ISP156x USB 2.0 controller";
+
+ case 0x70021039:
+ return "SiS 968 USB 2.0 controller";
+
+ case 0x31041106:
+ return ("VIA VT6202 USB 2.0 controller");
+
+ default:
+ break;
+ }
+
+ if ((pci_get_class(self) == PCIC_SERIALBUS)
+ && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
+ && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) {
+ return ("EHCI (generic) USB 2.0 controller");
+ }
+ return (NULL); /* dunno */
+}
+
+static int
+ehci_pci_probe(device_t self)
+{
+ const char *desc = ehci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return (BUS_PROBE_DEFAULT);
+ } else {
+ return (ENXIO);
+ }
+}
+
+static void
+ehci_pci_ati_quirk(device_t self, uint8_t is_sb700)
+{
+ device_t smbdev;
+ uint32_t val;
+
+ if (is_sb700) {
+ /* Lookup SMBUS PCI device */
+ smbdev = pci_find_device(PCI_EHCI_VENDORID_ATI, 0x4385);
+ if (smbdev == NULL)
+ return;
+ val = pci_get_revid(smbdev);
+ if (val != 0x3a && val != 0x3b)
+ return;
+ }
+
+ /*
+ * Note: this bit is described as reserved in SB700
+ * Register Reference Guide.
+ */
+ val = pci_read_config(self, 0x53, 1);
+ if (!(val & 0x8)) {
+ val |= 0x8;
+ pci_write_config(self, 0x53, val, 1);
+ device_printf(self, "AMD SB600/700 quirk applied\n");
+ }
+}
+
+static void
+ehci_pci_via_quirk(device_t self)
+{
+ uint32_t val;
+
+ if ((pci_get_device(self) == 0x3104) &&
+ ((pci_get_revid(self) & 0xf0) == 0x60)) {
+ /* Correct schedule sleep time to 10us */
+ val = pci_read_config(self, 0x4b, 1);
+ if (val & 0x20)
+ return;
+ val |= 0x20;
+ pci_write_config(self, 0x4b, val, 1);
+ device_printf(self, "VIA-quirk applied\n");
+ }
+}
+
+static int
+ehci_pci_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ pci_enable_busmaster(self);
+
+ switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) {
+ case PCI_USB_REV_PRE_1_0:
+ case PCI_USB_REV_1_0:
+ case PCI_USB_REV_1_1:
+ /*
+ * NOTE: some EHCI USB controllers have the wrong USB
+ * revision number. It appears those controllers are
+ * fully compliant so we just ignore this value in
+ * some common cases.
+ */
+ device_printf(self, "pre-2.0 USB revision (ignored)\n");
+ /* fallthrough */
+ case PCI_USB_REV_2_0:
+ break;
+ default:
+ /* Quirk for Parallels Desktop 4.0 */
+ device_printf(self, "USB revision is unknown. Assuming v2.0.\n");
+ break;
+ }
+
+ rid = PCI_CBMEM;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /*
+ * ehci_pci_match will never return NULL if ehci_pci_probe
+ * succeeded
+ */
+ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_EHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_EHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_EHCI_VENDORID_ATI:
+ sprintf(sc->sc_vendor, "ATI");
+ break;
+ case PCI_EHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_EHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_EHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_EHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_EHCI_VENDORID_PHILIPS:
+ sprintf(sc->sc_vendor, "Philips");
+ break;
+ case PCI_EHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ case PCI_EHCI_VENDORID_NVIDIA:
+ case PCI_EHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_EHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New EHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+#else
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+#endif
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ ehci_pci_take_controller(self);
+
+ /* Undocumented quirks taken from Linux */
+
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ATI:
+ /* SB600 and SB700 EHCI quirk */
+ switch (pci_get_device(self)) {
+ case 0x4386:
+ ehci_pci_ati_quirk(self, 0);
+ break;
+ case 0x4396:
+ ehci_pci_ati_quirk(self, 1);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case PCI_EHCI_VENDORID_VIA:
+ ehci_pci_via_quirk(self);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Dropped interrupts workaround */
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ATI:
+ case PCI_EHCI_VENDORID_VIA:
+ sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG;
+ if (bootverbose)
+ device_printf(self,
+ "Dropped interrupts workaround enabled\n");
+ break;
+ default:
+ break;
+ }
+
+ /* Doorbell feature workaround */
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_NVIDIA:
+ case PCI_EHCI_VENDORID_NVIDIA2:
+ sc->sc_flags |= EHCI_SCFLG_IAADBUG;
+ if (bootverbose)
+ device_printf(self,
+ "Doorbell workaround enabled\n");
+ break;
+ default:
+ break;
+ }
+
+ err = ehci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ehci_pci_detach(self);
+ return (ENXIO);
+}
+
+static int
+ehci_pci_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(self);
+
+ pci_disable_busmaster(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+static int
+ehci_pci_take_controller(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ uint32_t cparams;
+ uint32_t eec;
+ uint16_t to;
+ uint8_t eecp;
+ uint8_t bios_sem;
+
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+
+ /* Synchronise with the BIOS if it owns the controller. */
+ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+ eecp = EHCI_EECP_NEXT(eec)) {
+ eec = pci_read_config(self, eecp, 4);
+ if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) {
+ continue;
+ }
+ bios_sem = pci_read_config(self, eecp +
+ EHCI_LEGSUP_BIOS_SEM, 1);
+ if (bios_sem == 0) {
+ continue;
+ }
+ device_printf(sc->sc_bus.bdev, "waiting for BIOS "
+ "to give up control\n");
+ pci_write_config(self, eecp +
+ EHCI_LEGSUP_OS_SEM, 1, 1);
+ to = 500;
+ while (1) {
+ bios_sem = pci_read_config(self, eecp +
+ EHCI_LEGSUP_BIOS_SEM, 1);
+ if (bios_sem == 0)
+ break;
+
+ if (--to == 0) {
+ device_printf(sc->sc_bus.bdev,
+ "timed out waiting for BIOS\n");
+ break;
+ }
+ usb_pause_mtx(NULL, hz / 100); /* wait 10ms */
+ }
+ }
+ return (0);
+}
+
+static device_method_t ehci_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_pci_probe),
+ DEVMETHOD(device_attach, ehci_pci_attach),
+ DEVMETHOD(device_detach, ehci_pci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(usb_take_controller, ehci_pci_take_controller),
+
+ DEVMETHOD_END
+};
+
+static driver_t ehci_driver = {
+ .name = "ehci",
+ .methods = ehci_pci_methods,
+ .size = sizeof(struct ehci_softc),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.h b/freebsd/sys/dev/usb/usb_pci.h
index b1fa22ba..86e8cb39 100644
--- a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.h
+++ b/freebsd/sys/dev/usb/usb_pci.h
@@ -1,6 +1,8 @@
+/* $FreeBSD$ */
/*-
- * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org>
- * All rights reserved.
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -22,26 +24,20 @@
* 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 AM335X_SCM_PADCONF_H
-#define AM335X_SCM_PADCONF_H
+#ifndef _USB_PCI_H_
+#define _USB_PCI_H_
-#define SLEWCTRL (0x01 << 6) /* faster(0) or slower(1) slew rate. */
-#define RXACTIVE (0x01 << 5) /* Input enable value for the Pad */
-#define PULLTYPESEL (0x01 << 4) /* Pad pullup/pulldown type selection */
-#define PULLUDEN (0x01 << 3) /* Pullup/pulldown disabled */
-
-#define PADCONF_OUTPUT (PULLUDEN)
-#define PADCONF_OUTPUT_PULLUP (PULLTYPESEL)
-#define PADCONF_OUTPUT_PULLDOWN (0)
-#define PADCONF_INPUT (RXACTIVE | PULLUDEN)
-#define PADCONF_INPUT_PULLUP (RXACTIVE | PULLTYPESEL)
-#define PADCONF_INPUT_PULLDOWN (RXACTIVE)
-#define PADCONF_INPUT_PULLUP_SLOW (PADCONF_INPUT_PULLUP | SLEWCTRL)
+/*
+ * We don't want the following files included everywhere, that's why
+ * they are in a separate file.
+ */
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
-extern const struct ti_pinmux_device ti_am335x_pinmux_dev;
+#include <sys/rman.h>
+#endif
-#endif /* AM335X_SCM_PADCONF_H */
+#endif /* _USB_PCI_H_ */
diff --git a/freebsd/sys/i386/include/machine/bus.h b/freebsd/sys/i386/include/machine/bus.h
deleted file mode 100644
index f1af2cf6..00000000
--- a/freebsd/sys/i386/include/machine/bus.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*-
- * This file is in the public domain.
- */
-/* $FreeBSD$ */
-
-#include <x86/bus.h>
diff --git a/freebsd/sys/i386/include/machine/intr_machdep.h b/freebsd/sys/i386/include/machine/intr_machdep.h
deleted file mode 100644
index a0b28387..00000000
--- a/freebsd/sys/i386/include/machine/intr_machdep.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*-
- * This file is in the public domain.
- */
-/* $FreeBSD$ */
-
-#include <x86/intr_machdep.h>
diff --git a/freebsd/sys/i386/include/machine/legacyvar.h b/freebsd/sys/i386/include/machine/legacyvar.h
deleted file mode 100644
index 14dd9e03..00000000
--- a/freebsd/sys/i386/include/machine/legacyvar.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*-
- * Copyright (c) 2000 Peter Wemm <peter@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 _MACHINE_LEGACYVAR_H_
-#define _MACHINE_LEGACYVAR_H_
-
-enum legacy_device_ivars {
- LEGACY_IVAR_PCIDOMAIN,
- LEGACY_IVAR_PCIBUS,
- LEGACY_IVAR_PCISLOT,
- LEGACY_IVAR_PCIFUNC
-};
-
-#define LEGACY_ACCESSOR(var, ivar, type) \
- __BUS_ACCESSOR(legacy, var, LEGACY, ivar, type)
-
-LEGACY_ACCESSOR(pcidomain, PCIDOMAIN, uint32_t)
-LEGACY_ACCESSOR(pcibus, PCIBUS, uint32_t)
-LEGACY_ACCESSOR(pcislot, PCISLOT, int)
-LEGACY_ACCESSOR(pcifunc, PCIFUNC, int)
-
-#undef LEGACY_ACCESSOR
-
-int legacy_pcib_maxslots(device_t dev);
-uint32_t legacy_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
- u_int reg, int bytes);
-int legacy_pcib_read_ivar(device_t dev, device_t child, int which,
- uintptr_t *result);
-void legacy_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
- u_int reg, u_int32_t data, int bytes);
-int legacy_pcib_write_ivar(device_t dev, device_t child, int which,
- uintptr_t value);
-struct resource *legacy_pcib_alloc_resource(device_t dev, device_t child,
- int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
-int legacy_pcib_map_msi(device_t pcib, device_t dev, int irq,
- uint64_t *addr, uint32_t *data);
-
-#endif /* !_MACHINE_LEGACYVAR_H_ */
diff --git a/freebsd/sys/i386/include/machine/specialreg.h b/freebsd/sys/i386/include/machine/specialreg.h
deleted file mode 100644
index aace4bfd..00000000
--- a/freebsd/sys/i386/include/machine/specialreg.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*-
- * This file is in the public domain.
- */
-/* $FreeBSD$ */
-
-#include <x86/specialreg.h>
diff --git a/freebsd/sys/moxie/include/machine/in_cksum.h b/freebsd/sys/moxie/include/machine/in_cksum.h
new file mode 100644
index 00000000..d55b838b
--- /dev/null
+++ b/freebsd/sys/moxie/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/net/iflib.c b/freebsd/sys/net/iflib.c
new file mode 100644
index 00000000..733172dc
--- /dev/null
+++ b/freebsd/sys/net/iflib.c
@@ -0,0 +1,6827 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014-2018, Matthew Macy <mmacy@mattmacy.io>
+ * 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. Neither the name of Matthew Macy 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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_inet.h>
+#include <rtems/bsd/local/opt_inet6.h>
+#include <rtems/bsd/local/opt_acpi.h>
+#include <rtems/bsd/local/opt_sched.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/kobj.h>
+#include <sys/rman.h>
+#include <sys/sbuf.h>
+#include <sys/smp.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/taskqueue.h>
+#include <sys/limits.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/mp_ring.h>
+#include <net/pfil.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp_lro.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/ip_var.h>
+#include <netinet/netdump/netdump.h>
+#include <netinet6/ip6_var.h>
+
+#include <machine/bus.h>
+#include <machine/in_cksum.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/led/led.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
+
+#include <net/iflib.h>
+#include <net/iflib_private.h>
+
+#include <rtems/bsd/local/ifdi_if.h>
+
+#ifdef PCI_IOV
+#include <dev/pci/pci_iov.h>
+#endif
+
+#include <sys/bitstring.h>
+/*
+ * enable accounting of every mbuf as it comes in to and goes out of
+ * iflib's software descriptor references
+ */
+#define MEMORY_LOGGING 0
+/*
+ * Enable mbuf vectors for compressing long mbuf chains
+ */
+
+/*
+ * NB:
+ * - Prefetching in tx cleaning should perhaps be a tunable. The distance ahead
+ * we prefetch needs to be determined by the time spent in m_free vis a vis
+ * the cost of a prefetch. This will of course vary based on the workload:
+ * - NFLX's m_free path is dominated by vm-based M_EXT manipulation which
+ * is quite expensive, thus suggesting very little prefetch.
+ * - small packet forwarding which is just returning a single mbuf to
+ * UMA will typically be very fast vis a vis the cost of a memory
+ * access.
+ */
+
+
+/*
+ * File organization:
+ * - private structures
+ * - iflib private utility functions
+ * - ifnet functions
+ * - vlan registry and other exported functions
+ * - iflib public core functions
+ *
+ *
+ */
+MALLOC_DEFINE(M_IFLIB, "iflib", "ifnet library");
+
+struct iflib_txq;
+typedef struct iflib_txq *iflib_txq_t;
+struct iflib_rxq;
+typedef struct iflib_rxq *iflib_rxq_t;
+struct iflib_fl;
+typedef struct iflib_fl *iflib_fl_t;
+
+struct iflib_ctx;
+
+static void iru_init(if_rxd_update_t iru, iflib_rxq_t rxq, uint8_t flid);
+static void iflib_timer(void *arg);
+
+typedef struct iflib_filter_info {
+ driver_filter_t *ifi_filter;
+ void *ifi_filter_arg;
+ struct grouptask *ifi_task;
+ void *ifi_ctx;
+} *iflib_filter_info_t;
+
+struct iflib_ctx {
+ KOBJ_FIELDS;
+ /*
+ * Pointer to hardware driver's softc
+ */
+ void *ifc_softc;
+ device_t ifc_dev;
+ if_t ifc_ifp;
+
+#ifndef __rtems__
+ cpuset_t ifc_cpus;
+#endif /* __rtems__ */
+ if_shared_ctx_t ifc_sctx;
+ struct if_softc_ctx ifc_softc_ctx;
+
+ struct sx ifc_ctx_sx;
+ struct mtx ifc_state_mtx;
+
+ iflib_txq_t ifc_txqs;
+ iflib_rxq_t ifc_rxqs;
+ uint32_t ifc_if_flags;
+ uint32_t ifc_flags;
+ uint32_t ifc_max_fl_buf_size;
+ uint32_t ifc_rx_mbuf_sz;
+
+ int ifc_link_state;
+ int ifc_watchdog_events;
+ struct cdev *ifc_led_dev;
+ struct resource *ifc_msix_mem;
+
+ struct if_irq ifc_legacy_irq;
+ struct grouptask ifc_admin_task;
+ struct grouptask ifc_vflr_task;
+ struct iflib_filter_info ifc_filter_info;
+ struct ifmedia ifc_media;
+ struct ifmedia *ifc_mediap;
+
+ struct sysctl_oid *ifc_sysctl_node;
+ uint16_t ifc_sysctl_ntxqs;
+ uint16_t ifc_sysctl_nrxqs;
+ uint16_t ifc_sysctl_qs_eq_override;
+ uint16_t ifc_sysctl_rx_budget;
+ uint16_t ifc_sysctl_tx_abdicate;
+ uint16_t ifc_sysctl_core_offset;
+#define CORE_OFFSET_UNSPECIFIED 0xffff
+ uint8_t ifc_sysctl_separate_txrx;
+
+ qidx_t ifc_sysctl_ntxds[8];
+ qidx_t ifc_sysctl_nrxds[8];
+ struct if_txrx ifc_txrx;
+#define isc_txd_encap ifc_txrx.ift_txd_encap
+#define isc_txd_flush ifc_txrx.ift_txd_flush
+#define isc_txd_credits_update ifc_txrx.ift_txd_credits_update
+#define isc_rxd_available ifc_txrx.ift_rxd_available
+#define isc_rxd_pkt_get ifc_txrx.ift_rxd_pkt_get
+#define isc_rxd_refill ifc_txrx.ift_rxd_refill
+#define isc_rxd_flush ifc_txrx.ift_rxd_flush
+#define isc_rxd_refill ifc_txrx.ift_rxd_refill
+#define isc_rxd_refill ifc_txrx.ift_rxd_refill
+#define isc_legacy_intr ifc_txrx.ift_legacy_intr
+ eventhandler_tag ifc_vlan_attach_event;
+ eventhandler_tag ifc_vlan_detach_event;
+ struct ether_addr ifc_mac;
+};
+
+void *
+iflib_get_softc(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_softc);
+}
+
+device_t
+iflib_get_dev(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_dev);
+}
+
+if_t
+iflib_get_ifp(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_ifp);
+}
+
+struct ifmedia *
+iflib_get_media(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_mediap);
+}
+
+uint32_t
+iflib_get_flags(if_ctx_t ctx)
+{
+ return (ctx->ifc_flags);
+}
+
+void
+iflib_set_mac(if_ctx_t ctx, uint8_t mac[ETHER_ADDR_LEN])
+{
+
+ bcopy(mac, ctx->ifc_mac.octet, ETHER_ADDR_LEN);
+}
+
+if_softc_ctx_t
+iflib_get_softc_ctx(if_ctx_t ctx)
+{
+
+ return (&ctx->ifc_softc_ctx);
+}
+
+if_shared_ctx_t
+iflib_get_sctx(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_sctx);
+}
+
+#define IP_ALIGNED(m) ((((uintptr_t)(m)->m_data) & 0x3) == 0x2)
+#define CACHE_PTR_INCREMENT (CACHE_LINE_SIZE/sizeof(void*))
+#define CACHE_PTR_NEXT(ptr) ((void *)(((uintptr_t)(ptr)+CACHE_LINE_SIZE-1) & (CACHE_LINE_SIZE-1)))
+
+#define LINK_ACTIVE(ctx) ((ctx)->ifc_link_state == LINK_STATE_UP)
+#define CTX_IS_VF(ctx) ((ctx)->ifc_sctx->isc_flags & IFLIB_IS_VF)
+
+typedef struct iflib_sw_rx_desc_array {
+ bus_dmamap_t *ifsd_map; /* bus_dma maps for packet */
+ struct mbuf **ifsd_m; /* pkthdr mbufs */
+ caddr_t *ifsd_cl; /* direct cluster pointer for rx */
+ bus_addr_t *ifsd_ba; /* bus addr of cluster for rx */
+} iflib_rxsd_array_t;
+
+typedef struct iflib_sw_tx_desc_array {
+ bus_dmamap_t *ifsd_map; /* bus_dma maps for packet */
+ bus_dmamap_t *ifsd_tso_map; /* bus_dma maps for TSO packet */
+ struct mbuf **ifsd_m; /* pkthdr mbufs */
+} if_txsd_vec_t;
+
+/* magic number that should be high enough for any hardware */
+#define IFLIB_MAX_TX_SEGS 128
+#define IFLIB_RX_COPY_THRESH 128
+#define IFLIB_MAX_RX_REFRESH 32
+/* The minimum descriptors per second before we start coalescing */
+#define IFLIB_MIN_DESC_SEC 16384
+#define IFLIB_DEFAULT_TX_UPDATE_FREQ 16
+#define IFLIB_QUEUE_IDLE 0
+#define IFLIB_QUEUE_HUNG 1
+#define IFLIB_QUEUE_WORKING 2
+/* maximum number of txqs that can share an rx interrupt */
+#define IFLIB_MAX_TX_SHARED_INTR 4
+
+/* this should really scale with ring size - this is a fairly arbitrary value */
+#define TX_BATCH_SIZE 32
+
+#define IFLIB_RESTART_BUDGET 8
+
+#define CSUM_OFFLOAD (CSUM_IP_TSO|CSUM_IP6_TSO|CSUM_IP| \
+ CSUM_IP_UDP|CSUM_IP_TCP|CSUM_IP_SCTP| \
+ CSUM_IP6_UDP|CSUM_IP6_TCP|CSUM_IP6_SCTP)
+
+struct iflib_txq {
+ qidx_t ift_in_use;
+ qidx_t ift_cidx;
+ qidx_t ift_cidx_processed;
+ qidx_t ift_pidx;
+ uint8_t ift_gen;
+ uint8_t ift_br_offset;
+ uint16_t ift_npending;
+ uint16_t ift_db_pending;
+ uint16_t ift_rs_pending;
+ /* implicit pad */
+ uint8_t ift_txd_size[8];
+ uint64_t ift_processed;
+ uint64_t ift_cleaned;
+ uint64_t ift_cleaned_prev;
+#if MEMORY_LOGGING
+ uint64_t ift_enqueued;
+ uint64_t ift_dequeued;
+#endif
+ uint64_t ift_no_tx_dma_setup;
+ uint64_t ift_no_desc_avail;
+ uint64_t ift_mbuf_defrag_failed;
+ uint64_t ift_mbuf_defrag;
+ uint64_t ift_map_failed;
+ uint64_t ift_txd_encap_efbig;
+ uint64_t ift_pullups;
+ uint64_t ift_last_timer_tick;
+
+ struct mtx ift_mtx;
+ struct mtx ift_db_mtx;
+
+ /* constant values */
+ if_ctx_t ift_ctx;
+ struct ifmp_ring *ift_br;
+ struct grouptask ift_task;
+ qidx_t ift_size;
+ uint16_t ift_id;
+ struct callout ift_timer;
+
+ if_txsd_vec_t ift_sds;
+ uint8_t ift_qstatus;
+ uint8_t ift_closed;
+ uint8_t ift_update_freq;
+ struct iflib_filter_info ift_filter_info;
+ bus_dma_tag_t ift_buf_tag;
+ bus_dma_tag_t ift_tso_buf_tag;
+ iflib_dma_info_t ift_ifdi;
+#define MTX_NAME_LEN 16
+ char ift_mtx_name[MTX_NAME_LEN];
+ bus_dma_segment_t ift_segs[IFLIB_MAX_TX_SEGS] __aligned(CACHE_LINE_SIZE);
+#ifdef IFLIB_DIAGNOSTICS
+ uint64_t ift_cpu_exec_count[256];
+#endif
+} __aligned(CACHE_LINE_SIZE);
+
+struct iflib_fl {
+ qidx_t ifl_cidx;
+ qidx_t ifl_pidx;
+ qidx_t ifl_credits;
+ uint8_t ifl_gen;
+ uint8_t ifl_rxd_size;
+#if MEMORY_LOGGING
+ uint64_t ifl_m_enqueued;
+ uint64_t ifl_m_dequeued;
+ uint64_t ifl_cl_enqueued;
+ uint64_t ifl_cl_dequeued;
+#endif
+ /* implicit pad */
+ bitstr_t *ifl_rx_bitmap;
+ qidx_t ifl_fragidx;
+ /* constant */
+ qidx_t ifl_size;
+ uint16_t ifl_buf_size;
+ uint16_t ifl_cltype;
+ uma_zone_t ifl_zone;
+ iflib_rxsd_array_t ifl_sds;
+ iflib_rxq_t ifl_rxq;
+ uint8_t ifl_id;
+ bus_dma_tag_t ifl_buf_tag;
+ iflib_dma_info_t ifl_ifdi;
+ uint64_t ifl_bus_addrs[IFLIB_MAX_RX_REFRESH] __aligned(CACHE_LINE_SIZE);
+ caddr_t ifl_vm_addrs[IFLIB_MAX_RX_REFRESH];
+ qidx_t ifl_rxd_idxs[IFLIB_MAX_RX_REFRESH];
+} __aligned(CACHE_LINE_SIZE);
+
+static inline qidx_t
+get_inuse(int size, qidx_t cidx, qidx_t pidx, uint8_t gen)
+{
+ qidx_t used;
+
+ if (pidx > cidx)
+ used = pidx - cidx;
+ else if (pidx < cidx)
+ used = size - cidx + pidx;
+ else if (gen == 0 && pidx == cidx)
+ used = 0;
+ else if (gen == 1 && pidx == cidx)
+ used = size;
+ else
+ panic("bad state");
+
+ return (used);
+}
+
+#define TXQ_AVAIL(txq) (txq->ift_size - get_inuse(txq->ift_size, txq->ift_cidx, txq->ift_pidx, txq->ift_gen))
+
+#define IDXDIFF(head, tail, wrap) \
+ ((head) >= (tail) ? (head) - (tail) : (wrap) - (tail) + (head))
+
+struct iflib_rxq {
+ if_ctx_t ifr_ctx;
+ iflib_fl_t ifr_fl;
+ uint64_t ifr_rx_irq;
+ struct pfil_head *pfil;
+ /*
+ * If there is a separate completion queue (IFLIB_HAS_RXCQ), this is
+ * the command queue consumer index. Otherwise it's unused.
+ */
+ qidx_t ifr_cq_cidx;
+ uint16_t ifr_id;
+ uint8_t ifr_nfl;
+ uint8_t ifr_ntxqirq;
+ uint8_t ifr_txqid[IFLIB_MAX_TX_SHARED_INTR];
+ uint8_t ifr_fl_offset;
+ struct lro_ctrl ifr_lc;
+ struct grouptask ifr_task;
+ struct iflib_filter_info ifr_filter_info;
+ iflib_dma_info_t ifr_ifdi;
+
+ /* dynamically allocate if any drivers need a value substantially larger than this */
+ struct if_rxd_frag ifr_frags[IFLIB_MAX_RX_SEGS] __aligned(CACHE_LINE_SIZE);
+#ifdef IFLIB_DIAGNOSTICS
+ uint64_t ifr_cpu_exec_count[256];
+#endif
+} __aligned(CACHE_LINE_SIZE);
+
+typedef struct if_rxsd {
+ caddr_t *ifsd_cl;
+ iflib_fl_t ifsd_fl;
+ qidx_t ifsd_cidx;
+} *if_rxsd_t;
+
+/* multiple of word size */
+#ifdef __LP64__
+#define PKT_INFO_SIZE 6
+#define RXD_INFO_SIZE 5
+#define PKT_TYPE uint64_t
+#else
+#define PKT_INFO_SIZE 11
+#define RXD_INFO_SIZE 8
+#define PKT_TYPE uint32_t
+#endif
+#define PKT_LOOP_BOUND ((PKT_INFO_SIZE/3)*3)
+#define RXD_LOOP_BOUND ((RXD_INFO_SIZE/4)*4)
+
+typedef struct if_pkt_info_pad {
+ PKT_TYPE pkt_val[PKT_INFO_SIZE];
+} *if_pkt_info_pad_t;
+typedef struct if_rxd_info_pad {
+ PKT_TYPE rxd_val[RXD_INFO_SIZE];
+} *if_rxd_info_pad_t;
+
+CTASSERT(sizeof(struct if_pkt_info_pad) == sizeof(struct if_pkt_info));
+CTASSERT(sizeof(struct if_rxd_info_pad) == sizeof(struct if_rxd_info));
+
+
+static inline void
+pkt_info_zero(if_pkt_info_t pi)
+{
+ if_pkt_info_pad_t pi_pad;
+
+ pi_pad = (if_pkt_info_pad_t)pi;
+ pi_pad->pkt_val[0] = 0; pi_pad->pkt_val[1] = 0; pi_pad->pkt_val[2] = 0;
+ pi_pad->pkt_val[3] = 0; pi_pad->pkt_val[4] = 0; pi_pad->pkt_val[5] = 0;
+#ifndef __LP64__
+ pi_pad->pkt_val[6] = 0; pi_pad->pkt_val[7] = 0; pi_pad->pkt_val[8] = 0;
+ pi_pad->pkt_val[9] = 0; pi_pad->pkt_val[10] = 0;
+#endif
+}
+
+static device_method_t iflib_pseudo_methods[] = {
+ DEVMETHOD(device_attach, noop_attach),
+ DEVMETHOD(device_detach, iflib_pseudo_detach),
+ DEVMETHOD_END
+};
+
+driver_t iflib_pseudodriver = {
+ "iflib_pseudo", iflib_pseudo_methods, sizeof(struct iflib_ctx),
+};
+
+static inline void
+rxd_info_zero(if_rxd_info_t ri)
+{
+ if_rxd_info_pad_t ri_pad;
+ int i;
+
+ ri_pad = (if_rxd_info_pad_t)ri;
+ for (i = 0; i < RXD_LOOP_BOUND; i += 4) {
+ ri_pad->rxd_val[i] = 0;
+ ri_pad->rxd_val[i+1] = 0;
+ ri_pad->rxd_val[i+2] = 0;
+ ri_pad->rxd_val[i+3] = 0;
+ }
+#ifdef __LP64__
+ ri_pad->rxd_val[RXD_INFO_SIZE-1] = 0;
+#endif
+}
+
+/*
+ * Only allow a single packet to take up most 1/nth of the tx ring
+ */
+#define MAX_SINGLE_PACKET_FRACTION 12
+#define IF_BAD_DMA (bus_addr_t)-1
+
+#define CTX_ACTIVE(ctx) ((if_getdrvflags((ctx)->ifc_ifp) & IFF_DRV_RUNNING))
+
+#define CTX_LOCK_INIT(_sc) sx_init(&(_sc)->ifc_ctx_sx, "iflib ctx lock")
+#define CTX_LOCK(ctx) sx_xlock(&(ctx)->ifc_ctx_sx)
+#define CTX_UNLOCK(ctx) sx_xunlock(&(ctx)->ifc_ctx_sx)
+#define CTX_LOCK_DESTROY(ctx) sx_destroy(&(ctx)->ifc_ctx_sx)
+
+#define STATE_LOCK_INIT(_sc, _name) mtx_init(&(_sc)->ifc_state_mtx, _name, "iflib state lock", MTX_DEF)
+#define STATE_LOCK(ctx) mtx_lock(&(ctx)->ifc_state_mtx)
+#define STATE_UNLOCK(ctx) mtx_unlock(&(ctx)->ifc_state_mtx)
+#define STATE_LOCK_DESTROY(ctx) mtx_destroy(&(ctx)->ifc_state_mtx)
+
+#define CALLOUT_LOCK(txq) mtx_lock(&txq->ift_mtx)
+#define CALLOUT_UNLOCK(txq) mtx_unlock(&txq->ift_mtx)
+
+void
+iflib_set_detach(if_ctx_t ctx)
+{
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_IN_DETACH;
+ STATE_UNLOCK(ctx);
+}
+
+/* Our boot-time initialization hook */
+static int iflib_module_event_handler(module_t, int, void *);
+
+static moduledata_t iflib_moduledata = {
+ "iflib",
+ iflib_module_event_handler,
+ NULL
+};
+
+DECLARE_MODULE(iflib, iflib_moduledata, SI_SUB_INIT_IF, SI_ORDER_ANY);
+MODULE_VERSION(iflib, 1);
+
+MODULE_DEPEND(iflib, pci, 1, 1, 1);
+MODULE_DEPEND(iflib, ether, 1, 1, 1);
+
+TASKQGROUP_DEFINE(if_io_tqg, mp_ncpus, 1);
+TASKQGROUP_DEFINE(if_config_tqg, 1, 1);
+
+#ifndef IFLIB_DEBUG_COUNTERS
+#ifdef INVARIANTS
+#define IFLIB_DEBUG_COUNTERS 1
+#else
+#define IFLIB_DEBUG_COUNTERS 0
+#endif /* !INVARIANTS */
+#endif
+
+static SYSCTL_NODE(_net, OID_AUTO, iflib, CTLFLAG_RD, 0,
+ "iflib driver parameters");
+
+/*
+ * XXX need to ensure that this can't accidentally cause the head to be moved backwards
+ */
+static int iflib_min_tx_latency = 0;
+SYSCTL_INT(_net_iflib, OID_AUTO, min_tx_latency, CTLFLAG_RW,
+ &iflib_min_tx_latency, 0, "minimize transmit latency at the possible expense of throughput");
+static int iflib_no_tx_batch = 0;
+SYSCTL_INT(_net_iflib, OID_AUTO, no_tx_batch, CTLFLAG_RW,
+ &iflib_no_tx_batch, 0, "minimize transmit latency at the possible expense of throughput");
+
+
+#if IFLIB_DEBUG_COUNTERS
+
+static int iflib_tx_seen;
+static int iflib_tx_sent;
+static int iflib_tx_encap;
+static int iflib_rx_allocs;
+static int iflib_fl_refills;
+static int iflib_fl_refills_large;
+static int iflib_tx_frees;
+
+SYSCTL_INT(_net_iflib, OID_AUTO, tx_seen, CTLFLAG_RD,
+ &iflib_tx_seen, 0, "# TX mbufs seen");
+SYSCTL_INT(_net_iflib, OID_AUTO, tx_sent, CTLFLAG_RD,
+ &iflib_tx_sent, 0, "# TX mbufs sent");
+SYSCTL_INT(_net_iflib, OID_AUTO, tx_encap, CTLFLAG_RD,
+ &iflib_tx_encap, 0, "# TX mbufs encapped");
+SYSCTL_INT(_net_iflib, OID_AUTO, tx_frees, CTLFLAG_RD,
+ &iflib_tx_frees, 0, "# TX frees");
+SYSCTL_INT(_net_iflib, OID_AUTO, rx_allocs, CTLFLAG_RD,
+ &iflib_rx_allocs, 0, "# RX allocations");
+SYSCTL_INT(_net_iflib, OID_AUTO, fl_refills, CTLFLAG_RD,
+ &iflib_fl_refills, 0, "# refills");
+SYSCTL_INT(_net_iflib, OID_AUTO, fl_refills_large, CTLFLAG_RD,
+ &iflib_fl_refills_large, 0, "# large refills");
+
+
+static int iflib_txq_drain_flushing;
+static int iflib_txq_drain_oactive;
+static int iflib_txq_drain_notready;
+
+SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_flushing, CTLFLAG_RD,
+ &iflib_txq_drain_flushing, 0, "# drain flushes");
+SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_oactive, CTLFLAG_RD,
+ &iflib_txq_drain_oactive, 0, "# drain oactives");
+SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_notready, CTLFLAG_RD,
+ &iflib_txq_drain_notready, 0, "# drain notready");
+
+
+static int iflib_encap_load_mbuf_fail;
+static int iflib_encap_pad_mbuf_fail;
+static int iflib_encap_txq_avail_fail;
+static int iflib_encap_txd_encap_fail;
+
+SYSCTL_INT(_net_iflib, OID_AUTO, encap_load_mbuf_fail, CTLFLAG_RD,
+ &iflib_encap_load_mbuf_fail, 0, "# busdma load failures");
+SYSCTL_INT(_net_iflib, OID_AUTO, encap_pad_mbuf_fail, CTLFLAG_RD,
+ &iflib_encap_pad_mbuf_fail, 0, "# runt frame pad failures");
+SYSCTL_INT(_net_iflib, OID_AUTO, encap_txq_avail_fail, CTLFLAG_RD,
+ &iflib_encap_txq_avail_fail, 0, "# txq avail failures");
+SYSCTL_INT(_net_iflib, OID_AUTO, encap_txd_encap_fail, CTLFLAG_RD,
+ &iflib_encap_txd_encap_fail, 0, "# driver encap failures");
+
+static int iflib_task_fn_rxs;
+static int iflib_rx_intr_enables;
+static int iflib_fast_intrs;
+static int iflib_rx_unavail;
+static int iflib_rx_ctx_inactive;
+static int iflib_rx_if_input;
+static int iflib_rxd_flush;
+
+static int iflib_verbose_debug;
+
+SYSCTL_INT(_net_iflib, OID_AUTO, task_fn_rx, CTLFLAG_RD,
+ &iflib_task_fn_rxs, 0, "# task_fn_rx calls");
+SYSCTL_INT(_net_iflib, OID_AUTO, rx_intr_enables, CTLFLAG_RD,
+ &iflib_rx_intr_enables, 0, "# RX intr enables");
+SYSCTL_INT(_net_iflib, OID_AUTO, fast_intrs, CTLFLAG_RD,
+ &iflib_fast_intrs, 0, "# fast_intr calls");
+SYSCTL_INT(_net_iflib, OID_AUTO, rx_unavail, CTLFLAG_RD,
+ &iflib_rx_unavail, 0, "# times rxeof called with no available data");
+SYSCTL_INT(_net_iflib, OID_AUTO, rx_ctx_inactive, CTLFLAG_RD,
+ &iflib_rx_ctx_inactive, 0, "# times rxeof called with inactive context");
+SYSCTL_INT(_net_iflib, OID_AUTO, rx_if_input, CTLFLAG_RD,
+ &iflib_rx_if_input, 0, "# times rxeof called if_input");
+SYSCTL_INT(_net_iflib, OID_AUTO, rxd_flush, CTLFLAG_RD,
+ &iflib_rxd_flush, 0, "# times rxd_flush called");
+SYSCTL_INT(_net_iflib, OID_AUTO, verbose_debug, CTLFLAG_RW,
+ &iflib_verbose_debug, 0, "enable verbose debugging");
+
+#define DBG_COUNTER_INC(name) atomic_add_int(&(iflib_ ## name), 1)
+static void
+iflib_debug_reset(void)
+{
+ iflib_tx_seen = iflib_tx_sent = iflib_tx_encap = iflib_rx_allocs =
+ iflib_fl_refills = iflib_fl_refills_large = iflib_tx_frees =
+ iflib_txq_drain_flushing = iflib_txq_drain_oactive =
+ iflib_txq_drain_notready =
+ iflib_encap_load_mbuf_fail = iflib_encap_pad_mbuf_fail =
+ iflib_encap_txq_avail_fail = iflib_encap_txd_encap_fail =
+ iflib_task_fn_rxs = iflib_rx_intr_enables = iflib_fast_intrs =
+ iflib_rx_unavail =
+ iflib_rx_ctx_inactive = iflib_rx_if_input =
+ iflib_rxd_flush = 0;
+}
+
+#else
+#define DBG_COUNTER_INC(name)
+static void iflib_debug_reset(void) {}
+#endif
+
+#define IFLIB_DEBUG 0
+
+static void iflib_tx_structures_free(if_ctx_t ctx);
+static void iflib_rx_structures_free(if_ctx_t ctx);
+static int iflib_queues_alloc(if_ctx_t ctx);
+static int iflib_tx_credits_update(if_ctx_t ctx, iflib_txq_t txq);
+static int iflib_rxd_avail(if_ctx_t ctx, iflib_rxq_t rxq, qidx_t cidx, qidx_t budget);
+static int iflib_qset_structures_setup(if_ctx_t ctx);
+static int iflib_msix_init(if_ctx_t ctx);
+static int iflib_legacy_setup(if_ctx_t ctx, driver_filter_t filter, void *filterarg, int *rid, const char *str);
+static void iflib_txq_check_drain(iflib_txq_t txq, int budget);
+static uint32_t iflib_txq_can_drain(struct ifmp_ring *);
+#ifdef ALTQ
+static void iflib_altq_if_start(if_t ifp);
+static int iflib_altq_if_transmit(if_t ifp, struct mbuf *m);
+#endif
+static int iflib_register(if_ctx_t);
+static void iflib_deregister(if_ctx_t);
+static void iflib_init_locked(if_ctx_t ctx);
+static void iflib_add_device_sysctl_pre(if_ctx_t ctx);
+static void iflib_add_device_sysctl_post(if_ctx_t ctx);
+static void iflib_ifmp_purge(iflib_txq_t txq);
+static void _iflib_pre_assert(if_softc_ctx_t scctx);
+static void iflib_if_init_locked(if_ctx_t ctx);
+static void iflib_free_intr_mem(if_ctx_t ctx);
+#ifndef __NO_STRICT_ALIGNMENT
+static struct mbuf * iflib_fixup_rx(struct mbuf *m);
+#endif
+
+static SLIST_HEAD(cpu_offset_list, cpu_offset) cpu_offsets =
+ SLIST_HEAD_INITIALIZER(cpu_offsets);
+struct cpu_offset {
+ SLIST_ENTRY(cpu_offset) entries;
+ cpuset_t set;
+ unsigned int refcount;
+ uint16_t offset;
+};
+static struct mtx cpu_offset_mtx;
+MTX_SYSINIT(iflib_cpu_offset, &cpu_offset_mtx, "iflib_cpu_offset lock",
+ MTX_DEF);
+
+NETDUMP_DEFINE(iflib);
+
+#ifdef DEV_NETMAP
+#include <sys/selinfo.h>
+#include <net/netmap.h>
+#include <dev/netmap/netmap_kern.h>
+
+MODULE_DEPEND(iflib, netmap, 1, 1, 1);
+
+static int netmap_fl_refill(iflib_rxq_t rxq, struct netmap_kring *kring, uint32_t nm_i, bool init);
+
+/*
+ * device-specific sysctl variables:
+ *
+ * iflib_crcstrip: 0: keep CRC in rx frames (default), 1: strip it.
+ * During regular operations the CRC is stripped, but on some
+ * hardware reception of frames not multiple of 64 is slower,
+ * so using crcstrip=0 helps in benchmarks.
+ *
+ * iflib_rx_miss, iflib_rx_miss_bufs:
+ * count packets that might be missed due to lost interrupts.
+ */
+SYSCTL_DECL(_dev_netmap);
+/*
+ * The xl driver by default strips CRCs and we do not override it.
+ */
+
+int iflib_crcstrip = 1;
+SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_crcstrip,
+ CTLFLAG_RW, &iflib_crcstrip, 1, "strip CRC on RX frames");
+
+int iflib_rx_miss, iflib_rx_miss_bufs;
+SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_rx_miss,
+ CTLFLAG_RW, &iflib_rx_miss, 0, "potentially missed RX intr");
+SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_rx_miss_bufs,
+ CTLFLAG_RW, &iflib_rx_miss_bufs, 0, "potentially missed RX intr bufs");
+
+/*
+ * Register/unregister. We are already under netmap lock.
+ * Only called on the first register or the last unregister.
+ */
+static int
+iflib_netmap_register(struct netmap_adapter *na, int onoff)
+{
+ if_t ifp = na->ifp;
+ if_ctx_t ctx = ifp->if_softc;
+ int status;
+
+ CTX_LOCK(ctx);
+ IFDI_INTR_DISABLE(ctx);
+
+ /* Tell the stack that the interface is no longer active */
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ if (!CTX_IS_VF(ctx))
+ IFDI_CRCSTRIP_SET(ctx, onoff, iflib_crcstrip);
+
+ /* enable or disable flags and callbacks in na and ifp */
+ if (onoff) {
+ nm_set_native_flags(na);
+ } else {
+ nm_clear_native_flags(na);
+ }
+ iflib_stop(ctx);
+ iflib_init_locked(ctx);
+ IFDI_CRCSTRIP_SET(ctx, onoff, iflib_crcstrip); // XXX why twice ?
+ status = ifp->if_drv_flags & IFF_DRV_RUNNING ? 0 : 1;
+ if (status)
+ nm_clear_native_flags(na);
+ CTX_UNLOCK(ctx);
+ return (status);
+}
+
+static int
+netmap_fl_refill(iflib_rxq_t rxq, struct netmap_kring *kring, uint32_t nm_i, bool init)
+{
+ struct netmap_adapter *na = kring->na;
+ u_int const lim = kring->nkr_num_slots - 1;
+ u_int head = kring->rhead;
+ struct netmap_ring *ring = kring->ring;
+ bus_dmamap_t *map;
+ struct if_rxd_update iru;
+ if_ctx_t ctx = rxq->ifr_ctx;
+ iflib_fl_t fl = &rxq->ifr_fl[0];
+ uint32_t refill_pidx, nic_i;
+#if IFLIB_DEBUG_COUNTERS
+ int rf_count = 0;
+#endif
+
+ if (nm_i == head && __predict_true(!init))
+ return 0;
+ iru_init(&iru, rxq, 0 /* flid */);
+ map = fl->ifl_sds.ifsd_map;
+ refill_pidx = netmap_idx_k2n(kring, nm_i);
+ /*
+ * IMPORTANT: we must leave one free slot in the ring,
+ * so move head back by one unit
+ */
+ head = nm_prev(head, lim);
+ nic_i = UINT_MAX;
+ DBG_COUNTER_INC(fl_refills);
+ while (nm_i != head) {
+#if IFLIB_DEBUG_COUNTERS
+ if (++rf_count == 9)
+ DBG_COUNTER_INC(fl_refills_large);
+#endif
+ for (int tmp_pidx = 0; tmp_pidx < IFLIB_MAX_RX_REFRESH && nm_i != head; tmp_pidx++) {
+ struct netmap_slot *slot = &ring->slot[nm_i];
+ void *addr = PNMB(na, slot, &fl->ifl_bus_addrs[tmp_pidx]);
+ uint32_t nic_i_dma = refill_pidx;
+ nic_i = netmap_idx_k2n(kring, nm_i);
+
+ MPASS(tmp_pidx < IFLIB_MAX_RX_REFRESH);
+
+ if (addr == NETMAP_BUF_BASE(na)) /* bad buf */
+ return netmap_ring_reinit(kring);
+
+ fl->ifl_vm_addrs[tmp_pidx] = addr;
+ if (__predict_false(init)) {
+ netmap_load_map(na, fl->ifl_buf_tag,
+ map[nic_i], addr);
+ } else if (slot->flags & NS_BUF_CHANGED) {
+ /* buffer has changed, reload map */
+ netmap_reload_map(na, fl->ifl_buf_tag,
+ map[nic_i], addr);
+ }
+ slot->flags &= ~NS_BUF_CHANGED;
+
+ nm_i = nm_next(nm_i, lim);
+ fl->ifl_rxd_idxs[tmp_pidx] = nic_i = nm_next(nic_i, lim);
+ if (nm_i != head && tmp_pidx < IFLIB_MAX_RX_REFRESH-1)
+ continue;
+
+ iru.iru_pidx = refill_pidx;
+ iru.iru_count = tmp_pidx+1;
+ ctx->isc_rxd_refill(ctx->ifc_softc, &iru);
+ refill_pidx = nic_i;
+ for (int n = 0; n < iru.iru_count; n++) {
+ bus_dmamap_sync(fl->ifl_buf_tag, map[nic_i_dma],
+ BUS_DMASYNC_PREREAD);
+ /* XXX - change this to not use the netmap func*/
+ nic_i_dma = nm_next(nic_i_dma, lim);
+ }
+ }
+ }
+ kring->nr_hwcur = head;
+
+ bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ if (__predict_true(nic_i != UINT_MAX)) {
+ ctx->isc_rxd_flush(ctx->ifc_softc, rxq->ifr_id, fl->ifl_id, nic_i);
+ DBG_COUNTER_INC(rxd_flush);
+ }
+ return (0);
+}
+
+/*
+ * Reconcile kernel and user view of the transmit ring.
+ *
+ * All information is in the kring.
+ * Userspace wants to send packets up to the one before kring->rhead,
+ * kernel knows kring->nr_hwcur is the first unsent packet.
+ *
+ * Here we push packets out (as many as possible), and possibly
+ * reclaim buffers from previously completed transmission.
+ *
+ * The caller (netmap) guarantees that there is only one instance
+ * running at any time. Any interference with other driver
+ * methods should be handled by the individual drivers.
+ */
+static int
+iflib_netmap_txsync(struct netmap_kring *kring, int flags)
+{
+ struct netmap_adapter *na = kring->na;
+ if_t ifp = na->ifp;
+ struct netmap_ring *ring = kring->ring;
+ u_int nm_i; /* index into the netmap kring */
+ u_int nic_i; /* index into the NIC ring */
+ u_int n;
+ u_int const lim = kring->nkr_num_slots - 1;
+ u_int const head = kring->rhead;
+ struct if_pkt_info pi;
+
+ /*
+ * interrupts on every tx packet are expensive so request
+ * them every half ring, or where NS_REPORT is set
+ */
+ u_int report_frequency = kring->nkr_num_slots >> 1;
+ /* device-specific */
+ if_ctx_t ctx = ifp->if_softc;
+ iflib_txq_t txq = &ctx->ifc_txqs[kring->ring_id];
+
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ /*
+ * First part: process new packets to send.
+ * nm_i is the current index in the netmap kring,
+ * nic_i is the corresponding index in the NIC ring.
+ *
+ * If we have packets to send (nm_i != head)
+ * iterate over the netmap ring, fetch length and update
+ * the corresponding slot in the NIC ring. Some drivers also
+ * need to update the buffer's physical address in the NIC slot
+ * even NS_BUF_CHANGED is not set (PNMB computes the addresses).
+ *
+ * The netmap_reload_map() calls is especially expensive,
+ * even when (as in this case) the tag is 0, so do only
+ * when the buffer has actually changed.
+ *
+ * If possible do not set the report/intr bit on all slots,
+ * but only a few times per ring or when NS_REPORT is set.
+ *
+ * Finally, on 10G and faster drivers, it might be useful
+ * to prefetch the next slot and txr entry.
+ */
+
+ nm_i = kring->nr_hwcur;
+ if (nm_i != head) { /* we have new packets to send */
+ pkt_info_zero(&pi);
+ pi.ipi_segs = txq->ift_segs;
+ pi.ipi_qsidx = kring->ring_id;
+ nic_i = netmap_idx_k2n(kring, nm_i);
+
+ __builtin_prefetch(&ring->slot[nm_i]);
+ __builtin_prefetch(&txq->ift_sds.ifsd_m[nic_i]);
+ __builtin_prefetch(&txq->ift_sds.ifsd_map[nic_i]);
+
+ for (n = 0; nm_i != head; n++) {
+ struct netmap_slot *slot = &ring->slot[nm_i];
+ u_int len = slot->len;
+ uint64_t paddr;
+ void *addr = PNMB(na, slot, &paddr);
+ int flags = (slot->flags & NS_REPORT ||
+ nic_i == 0 || nic_i == report_frequency) ?
+ IPI_TX_INTR : 0;
+
+ /* device-specific */
+ pi.ipi_len = len;
+ pi.ipi_segs[0].ds_addr = paddr;
+ pi.ipi_segs[0].ds_len = len;
+ pi.ipi_nsegs = 1;
+ pi.ipi_ndescs = 0;
+ pi.ipi_pidx = nic_i;
+ pi.ipi_flags = flags;
+
+ /* Fill the slot in the NIC ring. */
+ ctx->isc_txd_encap(ctx->ifc_softc, &pi);
+ DBG_COUNTER_INC(tx_encap);
+
+ /* prefetch for next round */
+ __builtin_prefetch(&ring->slot[nm_i + 1]);
+ __builtin_prefetch(&txq->ift_sds.ifsd_m[nic_i + 1]);
+ __builtin_prefetch(&txq->ift_sds.ifsd_map[nic_i + 1]);
+
+ NM_CHECK_ADDR_LEN(na, addr, len);
+
+ if (slot->flags & NS_BUF_CHANGED) {
+ /* buffer has changed, reload map */
+ netmap_reload_map(na, txq->ift_buf_tag,
+ txq->ift_sds.ifsd_map[nic_i], addr);
+ }
+ /* make sure changes to the buffer are synced */
+ bus_dmamap_sync(txq->ift_buf_tag,
+ txq->ift_sds.ifsd_map[nic_i],
+ BUS_DMASYNC_PREWRITE);
+
+ slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED);
+ nm_i = nm_next(nm_i, lim);
+ nic_i = nm_next(nic_i, lim);
+ }
+ kring->nr_hwcur = nm_i;
+
+ /* synchronize the NIC ring */
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ /* (re)start the tx unit up to slot nic_i (excluded) */
+ ctx->isc_txd_flush(ctx->ifc_softc, txq->ift_id, nic_i);
+ }
+
+ /*
+ * Second part: reclaim buffers for completed transmissions.
+ *
+ * If there are unclaimed buffers, attempt to reclaim them.
+ * If none are reclaimed, and TX IRQs are not in use, do an initial
+ * minimal delay, then trigger the tx handler which will spin in the
+ * group task queue.
+ */
+ if (kring->nr_hwtail != nm_prev(kring->nr_hwcur, lim)) {
+ if (iflib_tx_credits_update(ctx, txq)) {
+ /* some tx completed, increment avail */
+ nic_i = txq->ift_cidx_processed;
+ kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim);
+ }
+ }
+ if (!(ctx->ifc_flags & IFC_NETMAP_TX_IRQ))
+ if (kring->nr_hwtail != nm_prev(kring->nr_hwcur, lim)) {
+ callout_reset_on(&txq->ift_timer, hz < 2000 ? 1 : hz / 1000,
+ iflib_timer, txq, txq->ift_timer.c_cpu);
+ }
+ return (0);
+}
+
+/*
+ * Reconcile kernel and user view of the receive ring.
+ * Same as for the txsync, this routine must be efficient.
+ * The caller guarantees a single invocations, but races against
+ * the rest of the driver should be handled here.
+ *
+ * On call, kring->rhead is the first packet that userspace wants
+ * to keep, and kring->rcur is the wakeup point.
+ * The kernel has previously reported packets up to kring->rtail.
+ *
+ * If (flags & NAF_FORCE_READ) also check for incoming packets irrespective
+ * of whether or not we received an interrupt.
+ */
+static int
+iflib_netmap_rxsync(struct netmap_kring *kring, int flags)
+{
+ struct netmap_adapter *na = kring->na;
+ struct netmap_ring *ring = kring->ring;
+ if_t ifp = na->ifp;
+ iflib_fl_t fl;
+ uint32_t nm_i; /* index into the netmap ring */
+ uint32_t nic_i; /* index into the NIC ring */
+ u_int i, n;
+ u_int const lim = kring->nkr_num_slots - 1;
+ u_int const head = kring->rhead;
+ int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR;
+ struct if_rxd_info ri;
+
+ if_ctx_t ctx = ifp->if_softc;
+ iflib_rxq_t rxq = &ctx->ifc_rxqs[kring->ring_id];
+ if (head > lim)
+ return netmap_ring_reinit(kring);
+
+ /*
+ * XXX netmap_fl_refill() only ever (re)fills free list 0 so far.
+ */
+
+ for (i = 0, fl = rxq->ifr_fl; i < rxq->ifr_nfl; i++, fl++) {
+ bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ }
+
+ /*
+ * First part: import newly received packets.
+ *
+ * nm_i is the index of the next free slot in the netmap ring,
+ * nic_i is the index of the next received packet in the NIC ring,
+ * and they may differ in case if_init() has been called while
+ * in netmap mode. For the receive ring we have
+ *
+ * nic_i = rxr->next_check;
+ * nm_i = kring->nr_hwtail (previous)
+ * and
+ * nm_i == (nic_i + kring->nkr_hwofs) % ring_size
+ *
+ * rxr->next_check is set to 0 on a ring reinit
+ */
+ if (netmap_no_pendintr || force_update) {
+ int crclen = iflib_crcstrip ? 0 : 4;
+ int error, avail;
+
+ for (i = 0; i < rxq->ifr_nfl; i++) {
+ fl = &rxq->ifr_fl[i];
+ nic_i = fl->ifl_cidx;
+ nm_i = netmap_idx_n2k(kring, nic_i);
+ avail = ctx->isc_rxd_available(ctx->ifc_softc,
+ rxq->ifr_id, nic_i, USHRT_MAX);
+ for (n = 0; avail > 0; n++, avail--) {
+ rxd_info_zero(&ri);
+ ri.iri_frags = rxq->ifr_frags;
+ ri.iri_qsidx = kring->ring_id;
+ ri.iri_ifp = ctx->ifc_ifp;
+ ri.iri_cidx = nic_i;
+
+ error = ctx->isc_rxd_pkt_get(ctx->ifc_softc, &ri);
+ ring->slot[nm_i].len = error ? 0 : ri.iri_len - crclen;
+ ring->slot[nm_i].flags = 0;
+ bus_dmamap_sync(fl->ifl_buf_tag,
+ fl->ifl_sds.ifsd_map[nic_i], BUS_DMASYNC_POSTREAD);
+ nm_i = nm_next(nm_i, lim);
+ nic_i = nm_next(nic_i, lim);
+ }
+ if (n) { /* update the state variables */
+ if (netmap_no_pendintr && !force_update) {
+ /* diagnostics */
+ iflib_rx_miss ++;
+ iflib_rx_miss_bufs += n;
+ }
+ fl->ifl_cidx = nic_i;
+ kring->nr_hwtail = nm_i;
+ }
+ kring->nr_kflags &= ~NKR_PENDINTR;
+ }
+ }
+ /*
+ * Second part: skip past packets that userspace has released.
+ * (kring->nr_hwcur to head excluded),
+ * and make the buffers available for reception.
+ * As usual nm_i is the index in the netmap ring,
+ * nic_i is the index in the NIC ring, and
+ * nm_i == (nic_i + kring->nkr_hwofs) % ring_size
+ */
+ /* XXX not sure how this will work with multiple free lists */
+ nm_i = kring->nr_hwcur;
+
+ return (netmap_fl_refill(rxq, kring, nm_i, false));
+}
+
+static void
+iflib_netmap_intr(struct netmap_adapter *na, int onoff)
+{
+ if_ctx_t ctx = na->ifp->if_softc;
+
+ CTX_LOCK(ctx);
+ if (onoff) {
+ IFDI_INTR_ENABLE(ctx);
+ } else {
+ IFDI_INTR_DISABLE(ctx);
+ }
+ CTX_UNLOCK(ctx);
+}
+
+
+static int
+iflib_netmap_attach(if_ctx_t ctx)
+{
+ struct netmap_adapter na;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+
+ bzero(&na, sizeof(na));
+
+ na.ifp = ctx->ifc_ifp;
+ na.na_flags = NAF_BDG_MAYSLEEP;
+ MPASS(ctx->ifc_softc_ctx.isc_ntxqsets);
+ MPASS(ctx->ifc_softc_ctx.isc_nrxqsets);
+
+ na.num_tx_desc = scctx->isc_ntxd[0];
+ na.num_rx_desc = scctx->isc_nrxd[0];
+ na.nm_txsync = iflib_netmap_txsync;
+ na.nm_rxsync = iflib_netmap_rxsync;
+ na.nm_register = iflib_netmap_register;
+ na.nm_intr = iflib_netmap_intr;
+ na.num_tx_rings = ctx->ifc_softc_ctx.isc_ntxqsets;
+ na.num_rx_rings = ctx->ifc_softc_ctx.isc_nrxqsets;
+ return (netmap_attach(&na));
+}
+
+static void
+iflib_netmap_txq_init(if_ctx_t ctx, iflib_txq_t txq)
+{
+ struct netmap_adapter *na = NA(ctx->ifc_ifp);
+ struct netmap_slot *slot;
+
+ slot = netmap_reset(na, NR_TX, txq->ift_id, 0);
+ if (slot == NULL)
+ return;
+ for (int i = 0; i < ctx->ifc_softc_ctx.isc_ntxd[0]; i++) {
+
+ /*
+ * In netmap mode, set the map for the packet buffer.
+ * NOTE: Some drivers (not this one) also need to set
+ * the physical buffer address in the NIC ring.
+ * netmap_idx_n2k() maps a nic index, i, into the corresponding
+ * netmap slot index, si
+ */
+ int si = netmap_idx_n2k(na->tx_rings[txq->ift_id], i);
+ netmap_load_map(na, txq->ift_buf_tag, txq->ift_sds.ifsd_map[i],
+ NMB(na, slot + si));
+ }
+}
+
+static void
+iflib_netmap_rxq_init(if_ctx_t ctx, iflib_rxq_t rxq)
+{
+ struct netmap_adapter *na = NA(ctx->ifc_ifp);
+ struct netmap_kring *kring = na->rx_rings[rxq->ifr_id];
+ struct netmap_slot *slot;
+ uint32_t nm_i;
+
+ slot = netmap_reset(na, NR_RX, rxq->ifr_id, 0);
+ if (slot == NULL)
+ return;
+ nm_i = netmap_idx_n2k(kring, 0);
+ netmap_fl_refill(rxq, kring, nm_i, true);
+}
+
+static void
+iflib_netmap_timer_adjust(if_ctx_t ctx, iflib_txq_t txq, uint32_t *reset_on)
+{
+ struct netmap_kring *kring;
+ uint16_t txqid;
+
+ txqid = txq->ift_id;
+ kring = NA(ctx->ifc_ifp)->tx_rings[txqid];
+
+ if (kring->nr_hwcur != nm_next(kring->nr_hwtail, kring->nkr_num_slots - 1)) {
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD);
+ if (ctx->isc_txd_credits_update(ctx->ifc_softc, txqid, false))
+ netmap_tx_irq(ctx->ifc_ifp, txqid);
+ if (!(ctx->ifc_flags & IFC_NETMAP_TX_IRQ)) {
+ if (hz < 2000)
+ *reset_on = 1;
+ else
+ *reset_on = hz / 1000;
+ }
+ }
+}
+
+#define iflib_netmap_detach(ifp) netmap_detach(ifp)
+
+#else
+#define iflib_netmap_txq_init(ctx, txq)
+#define iflib_netmap_rxq_init(ctx, rxq)
+#define iflib_netmap_detach(ifp)
+
+#define iflib_netmap_attach(ctx) (0)
+#define netmap_rx_irq(ifp, qid, budget) (0)
+#define netmap_tx_irq(ifp, qid) do {} while (0)
+#define iflib_netmap_timer_adjust(ctx, txq, reset_on)
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+static __inline void
+prefetch(void *x)
+{
+ __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x));
+}
+static __inline void
+prefetch2cachelines(void *x)
+{
+ __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x));
+#if (CACHE_LINE_SIZE < 128)
+ __asm volatile("prefetcht0 %0" :: "m" (*(((unsigned long *)x)+CACHE_LINE_SIZE/(sizeof(unsigned long)))));
+#endif
+}
+#else
+#define prefetch(x)
+#define prefetch2cachelines(x)
+#endif
+
+static void
+iru_init(if_rxd_update_t iru, iflib_rxq_t rxq, uint8_t flid)
+{
+ iflib_fl_t fl;
+
+ fl = &rxq->ifr_fl[flid];
+ iru->iru_paddrs = fl->ifl_bus_addrs;
+ iru->iru_vaddrs = &fl->ifl_vm_addrs[0];
+ iru->iru_idxs = fl->ifl_rxd_idxs;
+ iru->iru_qsidx = rxq->ifr_id;
+ iru->iru_buf_size = fl->ifl_buf_size;
+ iru->iru_flidx = fl->ifl_id;
+}
+
+static void
+_iflib_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+ if (err)
+ return;
+ *(bus_addr_t *) arg = segs[0].ds_addr;
+}
+
+int
+iflib_dma_alloc_align(if_ctx_t ctx, int size, int align, iflib_dma_info_t dma, int mapflags)
+{
+ int err;
+ device_t dev = ctx->ifc_dev;
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+ align, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ size, /* maxsize */
+ 1, /* nsegments */
+ size, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &dma->idi_tag);
+ if (err) {
+ device_printf(dev,
+ "%s: bus_dma_tag_create failed: %d\n",
+ __func__, err);
+ goto fail_0;
+ }
+
+ err = bus_dmamem_alloc(dma->idi_tag, (void**) &dma->idi_vaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &dma->idi_map);
+ if (err) {
+ device_printf(dev,
+ "%s: bus_dmamem_alloc(%ju) failed: %d\n",
+ __func__, (uintmax_t)size, err);
+ goto fail_1;
+ }
+
+ dma->idi_paddr = IF_BAD_DMA;
+ err = bus_dmamap_load(dma->idi_tag, dma->idi_map, dma->idi_vaddr,
+ size, _iflib_dmamap_cb, &dma->idi_paddr, mapflags | BUS_DMA_NOWAIT);
+ if (err || dma->idi_paddr == IF_BAD_DMA) {
+ device_printf(dev,
+ "%s: bus_dmamap_load failed: %d\n",
+ __func__, err);
+ goto fail_2;
+ }
+
+ dma->idi_size = size;
+ return (0);
+
+fail_2:
+ bus_dmamem_free(dma->idi_tag, dma->idi_vaddr, dma->idi_map);
+fail_1:
+ bus_dma_tag_destroy(dma->idi_tag);
+fail_0:
+ dma->idi_tag = NULL;
+
+ return (err);
+}
+
+int
+iflib_dma_alloc(if_ctx_t ctx, int size, iflib_dma_info_t dma, int mapflags)
+{
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+
+ KASSERT(sctx->isc_q_align != 0, ("alignment value not initialized"));
+
+ return (iflib_dma_alloc_align(ctx, size, sctx->isc_q_align, dma, mapflags));
+}
+
+int
+iflib_dma_alloc_multi(if_ctx_t ctx, int *sizes, iflib_dma_info_t *dmalist, int mapflags, int count)
+{
+ int i, err;
+ iflib_dma_info_t *dmaiter;
+
+ dmaiter = dmalist;
+ for (i = 0; i < count; i++, dmaiter++) {
+ if ((err = iflib_dma_alloc(ctx, sizes[i], *dmaiter, mapflags)) != 0)
+ break;
+ }
+ if (err)
+ iflib_dma_free_multi(dmalist, i);
+ return (err);
+}
+
+void
+iflib_dma_free(iflib_dma_info_t dma)
+{
+ if (dma->idi_tag == NULL)
+ return;
+ if (dma->idi_paddr != IF_BAD_DMA) {
+ bus_dmamap_sync(dma->idi_tag, dma->idi_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dma->idi_tag, dma->idi_map);
+ dma->idi_paddr = IF_BAD_DMA;
+ }
+ if (dma->idi_vaddr != NULL) {
+ bus_dmamem_free(dma->idi_tag, dma->idi_vaddr, dma->idi_map);
+ dma->idi_vaddr = NULL;
+ }
+ bus_dma_tag_destroy(dma->idi_tag);
+ dma->idi_tag = NULL;
+}
+
+void
+iflib_dma_free_multi(iflib_dma_info_t *dmalist, int count)
+{
+ int i;
+ iflib_dma_info_t *dmaiter = dmalist;
+
+ for (i = 0; i < count; i++, dmaiter++)
+ iflib_dma_free(*dmaiter);
+}
+
+#ifdef EARLY_AP_STARTUP
+static const int iflib_started = 1;
+#else
+/*
+ * We used to abuse the smp_started flag to decide if the queues have been
+ * fully initialized (by late taskqgroup_adjust() calls in a SYSINIT()).
+ * That gave bad races, since the SYSINIT() runs strictly after smp_started
+ * is set. Run a SYSINIT() strictly after that to just set a usable
+ * completion flag.
+ */
+
+static int iflib_started;
+
+static void
+iflib_record_started(void *arg)
+{
+ iflib_started = 1;
+}
+
+SYSINIT(iflib_record_started, SI_SUB_SMP + 1, SI_ORDER_FIRST,
+ iflib_record_started, NULL);
+#endif
+
+static int
+iflib_fast_intr(void *arg)
+{
+ iflib_filter_info_t info = arg;
+ struct grouptask *gtask = info->ifi_task;
+ int result;
+
+ if (!iflib_started)
+ return (FILTER_STRAY);
+
+ DBG_COUNTER_INC(fast_intrs);
+ if (info->ifi_filter != NULL) {
+ result = info->ifi_filter(info->ifi_filter_arg);
+ if ((result & FILTER_SCHEDULE_THREAD) == 0)
+ return (result);
+ }
+
+ GROUPTASK_ENQUEUE(gtask);
+ return (FILTER_HANDLED);
+}
+
+static int
+iflib_fast_intr_rxtx(void *arg)
+{
+ iflib_filter_info_t info = arg;
+ struct grouptask *gtask = info->ifi_task;
+ if_ctx_t ctx;
+ iflib_rxq_t rxq = (iflib_rxq_t)info->ifi_ctx;
+ iflib_txq_t txq;
+ void *sc;
+ int i, cidx, result;
+ qidx_t txqid;
+ bool intr_enable, intr_legacy;
+
+ if (!iflib_started)
+ return (FILTER_STRAY);
+
+ DBG_COUNTER_INC(fast_intrs);
+ if (info->ifi_filter != NULL) {
+ result = info->ifi_filter(info->ifi_filter_arg);
+ if ((result & FILTER_SCHEDULE_THREAD) == 0)
+ return (result);
+ }
+
+ ctx = rxq->ifr_ctx;
+ sc = ctx->ifc_softc;
+ intr_enable = false;
+ intr_legacy = !!(ctx->ifc_flags & IFC_LEGACY);
+ MPASS(rxq->ifr_ntxqirq);
+ for (i = 0; i < rxq->ifr_ntxqirq; i++) {
+ txqid = rxq->ifr_txqid[i];
+ txq = &ctx->ifc_txqs[txqid];
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD);
+ if (!ctx->isc_txd_credits_update(sc, txqid, false)) {
+ if (intr_legacy)
+ intr_enable = true;
+ else
+ IFDI_TX_QUEUE_INTR_ENABLE(ctx, txqid);
+ continue;
+ }
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+ }
+ if (ctx->ifc_sctx->isc_flags & IFLIB_HAS_RXCQ)
+ cidx = rxq->ifr_cq_cidx;
+ else
+ cidx = rxq->ifr_fl[0].ifl_cidx;
+ if (iflib_rxd_avail(ctx, rxq, cidx, 1))
+ GROUPTASK_ENQUEUE(gtask);
+ else {
+ if (intr_legacy)
+ intr_enable = true;
+ else
+ IFDI_RX_QUEUE_INTR_ENABLE(ctx, rxq->ifr_id);
+ DBG_COUNTER_INC(rx_intr_enables);
+ }
+ if (intr_enable)
+ IFDI_INTR_ENABLE(ctx);
+ return (FILTER_HANDLED);
+}
+
+
+static int
+iflib_fast_intr_ctx(void *arg)
+{
+ iflib_filter_info_t info = arg;
+ struct grouptask *gtask = info->ifi_task;
+ int result;
+
+ if (!iflib_started)
+ return (FILTER_STRAY);
+
+ DBG_COUNTER_INC(fast_intrs);
+ if (info->ifi_filter != NULL) {
+ result = info->ifi_filter(info->ifi_filter_arg);
+ if ((result & FILTER_SCHEDULE_THREAD) == 0)
+ return (result);
+ }
+
+ GROUPTASK_ENQUEUE(gtask);
+ return (FILTER_HANDLED);
+}
+
+static int
+_iflib_irq_alloc(if_ctx_t ctx, if_irq_t irq, int rid,
+ driver_filter_t filter, driver_intr_t handler, void *arg,
+ const char *name)
+{
+ struct resource *res;
+ void *tag = NULL;
+ device_t dev = ctx->ifc_dev;
+ int flags, i, rc;
+
+ flags = RF_ACTIVE;
+ if (ctx->ifc_flags & IFC_LEGACY)
+ flags |= RF_SHAREABLE;
+ MPASS(rid < 512);
+ i = rid;
+ res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, flags);
+ if (res == NULL) {
+ device_printf(dev,
+ "failed to allocate IRQ for rid %d, name %s.\n", rid, name);
+ return (ENOMEM);
+ }
+ irq->ii_res = res;
+ KASSERT(filter == NULL || handler == NULL, ("filter and handler can't both be non-NULL"));
+ rc = bus_setup_intr(dev, res, INTR_MPSAFE | INTR_TYPE_NET,
+ filter, handler, arg, &tag);
+ if (rc != 0) {
+ device_printf(dev,
+ "failed to setup interrupt for rid %d, name %s: %d\n",
+ rid, name ? name : "unknown", rc);
+ return (rc);
+ } else if (name)
+ bus_describe_intr(dev, res, tag, "%s", name);
+
+ irq->ii_tag = tag;
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Allocate DMA resources for TX buffers as well as memory for the TX
+ * mbuf map. TX DMA maps (non-TSO/TSO) and TX mbuf map are kept in a
+ * iflib_sw_tx_desc_array structure, storing all the information that
+ * is needed to transmit a packet on the wire. This is called only
+ * once at attach, setup is done every reset.
+ *
+ **********************************************************************/
+static int
+iflib_txsd_alloc(iflib_txq_t txq)
+{
+ if_ctx_t ctx = txq->ift_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ device_t dev = ctx->ifc_dev;
+ bus_size_t tsomaxsize;
+ int err, nsegments, ntsosegments;
+ bool tso;
+
+ nsegments = scctx->isc_tx_nsegments;
+ ntsosegments = scctx->isc_tx_tso_segments_max;
+ tsomaxsize = scctx->isc_tx_tso_size_max;
+ if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_VLAN_MTU)
+ tsomaxsize += sizeof(struct ether_vlan_header);
+ MPASS(scctx->isc_ntxd[0] > 0);
+ MPASS(scctx->isc_ntxd[txq->ift_br_offset] > 0);
+ MPASS(nsegments > 0);
+ if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_TSO) {
+ MPASS(ntsosegments > 0);
+ MPASS(sctx->isc_tso_maxsize >= tsomaxsize);
+ }
+
+ /*
+ * Set up DMA tags for TX buffers.
+ */
+ if ((err = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sctx->isc_tx_maxsize, /* maxsize */
+ nsegments, /* nsegments */
+ sctx->isc_tx_maxsegsize, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &txq->ift_buf_tag))) {
+ device_printf(dev,"Unable to allocate TX DMA tag: %d\n", err);
+ device_printf(dev,"maxsize: %ju nsegments: %d maxsegsize: %ju\n",
+ (uintmax_t)sctx->isc_tx_maxsize, nsegments, (uintmax_t)sctx->isc_tx_maxsegsize);
+ goto fail;
+ }
+ tso = (if_getcapabilities(ctx->ifc_ifp) & IFCAP_TSO) != 0;
+ if (tso && (err = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ tsomaxsize, /* maxsize */
+ ntsosegments, /* nsegments */
+ sctx->isc_tso_maxsegsize,/* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &txq->ift_tso_buf_tag))) {
+ device_printf(dev, "Unable to allocate TSO TX DMA tag: %d\n",
+ err);
+ goto fail;
+ }
+
+ /* Allocate memory for the TX mbuf map. */
+ if (!(txq->ift_sds.ifsd_m =
+ (struct mbuf **) malloc(sizeof(struct mbuf *) *
+ scctx->isc_ntxd[txq->ift_br_offset], M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate TX mbuf map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Create the DMA maps for TX buffers.
+ */
+ if ((txq->ift_sds.ifsd_map = (bus_dmamap_t *)malloc(
+ sizeof(bus_dmamap_t) * scctx->isc_ntxd[txq->ift_br_offset],
+ M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) {
+ device_printf(dev,
+ "Unable to allocate TX buffer DMA map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+ if (tso && (txq->ift_sds.ifsd_tso_map = (bus_dmamap_t *)malloc(
+ sizeof(bus_dmamap_t) * scctx->isc_ntxd[txq->ift_br_offset],
+ M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) {
+ device_printf(dev,
+ "Unable to allocate TSO TX buffer map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+ for (int i = 0; i < scctx->isc_ntxd[txq->ift_br_offset]; i++) {
+ err = bus_dmamap_create(txq->ift_buf_tag, 0,
+ &txq->ift_sds.ifsd_map[i]);
+ if (err != 0) {
+ device_printf(dev, "Unable to create TX DMA map\n");
+ goto fail;
+ }
+ if (!tso)
+ continue;
+ err = bus_dmamap_create(txq->ift_tso_buf_tag, 0,
+ &txq->ift_sds.ifsd_tso_map[i]);
+ if (err != 0) {
+ device_printf(dev, "Unable to create TSO TX DMA map\n");
+ goto fail;
+ }
+ }
+ return (0);
+fail:
+ /* We free all, it handles case where we are in the middle */
+ iflib_tx_structures_free(ctx);
+ return (err);
+}
+
+static void
+iflib_txsd_destroy(if_ctx_t ctx, iflib_txq_t txq, int i)
+{
+ bus_dmamap_t map;
+
+ map = NULL;
+ if (txq->ift_sds.ifsd_map != NULL)
+ map = txq->ift_sds.ifsd_map[i];
+ if (map != NULL) {
+ bus_dmamap_sync(txq->ift_buf_tag, map, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_buf_tag, map);
+ bus_dmamap_destroy(txq->ift_buf_tag, map);
+ txq->ift_sds.ifsd_map[i] = NULL;
+ }
+
+ map = NULL;
+ if (txq->ift_sds.ifsd_tso_map != NULL)
+ map = txq->ift_sds.ifsd_tso_map[i];
+ if (map != NULL) {
+ bus_dmamap_sync(txq->ift_tso_buf_tag, map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_tso_buf_tag, map);
+ bus_dmamap_destroy(txq->ift_tso_buf_tag, map);
+ txq->ift_sds.ifsd_tso_map[i] = NULL;
+ }
+}
+
+static void
+iflib_txq_destroy(iflib_txq_t txq)
+{
+ if_ctx_t ctx = txq->ift_ctx;
+
+ for (int i = 0; i < txq->ift_size; i++)
+ iflib_txsd_destroy(ctx, txq, i);
+ if (txq->ift_sds.ifsd_map != NULL) {
+ free(txq->ift_sds.ifsd_map, M_IFLIB);
+ txq->ift_sds.ifsd_map = NULL;
+ }
+ if (txq->ift_sds.ifsd_tso_map != NULL) {
+ free(txq->ift_sds.ifsd_tso_map, M_IFLIB);
+ txq->ift_sds.ifsd_tso_map = NULL;
+ }
+ if (txq->ift_sds.ifsd_m != NULL) {
+ free(txq->ift_sds.ifsd_m, M_IFLIB);
+ txq->ift_sds.ifsd_m = NULL;
+ }
+ if (txq->ift_buf_tag != NULL) {
+ bus_dma_tag_destroy(txq->ift_buf_tag);
+ txq->ift_buf_tag = NULL;
+ }
+ if (txq->ift_tso_buf_tag != NULL) {
+ bus_dma_tag_destroy(txq->ift_tso_buf_tag);
+ txq->ift_tso_buf_tag = NULL;
+ }
+}
+
+static void
+iflib_txsd_free(if_ctx_t ctx, iflib_txq_t txq, int i)
+{
+ struct mbuf **mp;
+
+ mp = &txq->ift_sds.ifsd_m[i];
+ if (*mp == NULL)
+ return;
+
+ if (txq->ift_sds.ifsd_map != NULL) {
+ bus_dmamap_sync(txq->ift_buf_tag,
+ txq->ift_sds.ifsd_map[i], BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_buf_tag, txq->ift_sds.ifsd_map[i]);
+ }
+ if (txq->ift_sds.ifsd_tso_map != NULL) {
+ bus_dmamap_sync(txq->ift_tso_buf_tag,
+ txq->ift_sds.ifsd_tso_map[i], BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_tso_buf_tag,
+ txq->ift_sds.ifsd_tso_map[i]);
+ }
+ m_free(*mp);
+ DBG_COUNTER_INC(tx_frees);
+ *mp = NULL;
+}
+
+static int
+iflib_txq_setup(iflib_txq_t txq)
+{
+ if_ctx_t ctx = txq->ift_ctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ iflib_dma_info_t di;
+ int i;
+
+ /* Set number of descriptors available */
+ txq->ift_qstatus = IFLIB_QUEUE_IDLE;
+ /* XXX make configurable */
+ txq->ift_update_freq = IFLIB_DEFAULT_TX_UPDATE_FREQ;
+
+ /* Reset indices */
+ txq->ift_cidx_processed = 0;
+ txq->ift_pidx = txq->ift_cidx = txq->ift_npending = 0;
+ txq->ift_size = scctx->isc_ntxd[txq->ift_br_offset];
+
+ for (i = 0, di = txq->ift_ifdi; i < sctx->isc_ntxqs; i++, di++)
+ bzero((void *)di->idi_vaddr, di->idi_size);
+
+ IFDI_TXQ_SETUP(ctx, txq->ift_id);
+ for (i = 0, di = txq->ift_ifdi; i < sctx->isc_ntxqs; i++, di++)
+ bus_dmamap_sync(di->idi_tag, di->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Allocate DMA resources for RX buffers as well as memory for the RX
+ * mbuf map, direct RX cluster pointer map and RX cluster bus address
+ * map. RX DMA map, RX mbuf map, direct RX cluster pointer map and
+ * RX cluster map are kept in a iflib_sw_rx_desc_array structure.
+ * Since we use use one entry in iflib_sw_rx_desc_array per received
+ * packet, the maximum number of entries we'll need is equal to the
+ * number of hardware receive descriptors that we've allocated.
+ *
+ **********************************************************************/
+static int
+iflib_rxsd_alloc(iflib_rxq_t rxq)
+{
+ if_ctx_t ctx = rxq->ifr_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ device_t dev = ctx->ifc_dev;
+ iflib_fl_t fl;
+ int err;
+
+ MPASS(scctx->isc_nrxd[0] > 0);
+ MPASS(scctx->isc_nrxd[rxq->ifr_fl_offset] > 0);
+
+ fl = rxq->ifr_fl;
+ for (int i = 0; i < rxq->ifr_nfl; i++, fl++) {
+ fl->ifl_size = scctx->isc_nrxd[rxq->ifr_fl_offset]; /* this isn't necessarily the same */
+ /* Set up DMA tag for RX buffers. */
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ sctx->isc_rx_maxsize, /* maxsize */
+ sctx->isc_rx_nsegments, /* nsegments */
+ sctx->isc_rx_maxsegsize, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &fl->ifl_buf_tag);
+ if (err) {
+ device_printf(dev,
+ "Unable to allocate RX DMA tag: %d\n", err);
+ goto fail;
+ }
+
+ /* Allocate memory for the RX mbuf map. */
+ if (!(fl->ifl_sds.ifsd_m =
+ (struct mbuf **) malloc(sizeof(struct mbuf *) *
+ scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev,
+ "Unable to allocate RX mbuf map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+
+ /* Allocate memory for the direct RX cluster pointer map. */
+ if (!(fl->ifl_sds.ifsd_cl =
+ (caddr_t *) malloc(sizeof(caddr_t) *
+ scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev,
+ "Unable to allocate RX cluster map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+
+ /* Allocate memory for the RX cluster bus address map. */
+ if (!(fl->ifl_sds.ifsd_ba =
+ (bus_addr_t *) malloc(sizeof(bus_addr_t) *
+ scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev,
+ "Unable to allocate RX bus address map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Create the DMA maps for RX buffers.
+ */
+ if (!(fl->ifl_sds.ifsd_map =
+ (bus_dmamap_t *) malloc(sizeof(bus_dmamap_t) * scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev,
+ "Unable to allocate RX buffer DMA map memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+ for (int i = 0; i < scctx->isc_nrxd[rxq->ifr_fl_offset]; i++) {
+ err = bus_dmamap_create(fl->ifl_buf_tag, 0,
+ &fl->ifl_sds.ifsd_map[i]);
+ if (err != 0) {
+ device_printf(dev, "Unable to create RX buffer DMA map\n");
+ goto fail;
+ }
+ }
+ }
+ return (0);
+
+fail:
+ iflib_rx_structures_free(ctx);
+ return (err);
+}
+
+
+/*
+ * Internal service routines
+ */
+
+struct rxq_refill_cb_arg {
+ int error;
+ bus_dma_segment_t seg;
+ int nseg;
+};
+
+static void
+_rxq_refill_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct rxq_refill_cb_arg *cb_arg = arg;
+
+ cb_arg->error = error;
+ cb_arg->seg = segs[0];
+ cb_arg->nseg = nseg;
+}
+
+/**
+ * _iflib_fl_refill - refill an rxq free-buffer list
+ * @ctx: the iflib context
+ * @fl: the free list to refill
+ * @count: the number of new buffers to allocate
+ *
+ * (Re)populate an rxq free-buffer list with up to @count new packet buffers.
+ * The caller must assure that @count does not exceed the queue's capacity.
+ */
+static void
+_iflib_fl_refill(if_ctx_t ctx, iflib_fl_t fl, int count)
+{
+ struct if_rxd_update iru;
+ struct rxq_refill_cb_arg cb_arg;
+ struct mbuf *m;
+ caddr_t cl, *sd_cl;
+ struct mbuf **sd_m;
+ bus_dmamap_t *sd_map;
+ bus_addr_t bus_addr, *sd_ba;
+ int err, frag_idx, i, idx, n, pidx;
+ qidx_t credits;
+
+ sd_m = fl->ifl_sds.ifsd_m;
+ sd_map = fl->ifl_sds.ifsd_map;
+ sd_cl = fl->ifl_sds.ifsd_cl;
+ sd_ba = fl->ifl_sds.ifsd_ba;
+ pidx = fl->ifl_pidx;
+ idx = pidx;
+ frag_idx = fl->ifl_fragidx;
+ credits = fl->ifl_credits;
+
+ i = 0;
+ n = count;
+ MPASS(n > 0);
+ MPASS(credits + n <= fl->ifl_size);
+
+ if (pidx < fl->ifl_cidx)
+ MPASS(pidx + n <= fl->ifl_cidx);
+ if (pidx == fl->ifl_cidx && (credits < fl->ifl_size))
+ MPASS(fl->ifl_gen == 0);
+ if (pidx > fl->ifl_cidx)
+ MPASS(n <= fl->ifl_size - pidx + fl->ifl_cidx);
+
+ DBG_COUNTER_INC(fl_refills);
+ if (n > 8)
+ DBG_COUNTER_INC(fl_refills_large);
+ iru_init(&iru, fl->ifl_rxq, fl->ifl_id);
+ while (n--) {
+ /*
+ * We allocate an uninitialized mbuf + cluster, mbuf is
+ * initialized after rx.
+ *
+ * If the cluster is still set then we know a minimum sized packet was received
+ */
+ bit_ffc_at(fl->ifl_rx_bitmap, frag_idx, fl->ifl_size,
+ &frag_idx);
+ if (frag_idx < 0)
+ bit_ffc(fl->ifl_rx_bitmap, fl->ifl_size, &frag_idx);
+ MPASS(frag_idx >= 0);
+ if ((cl = sd_cl[frag_idx]) == NULL) {
+ if ((cl = m_cljget(NULL, M_NOWAIT, fl->ifl_buf_size)) == NULL)
+ break;
+
+ cb_arg.error = 0;
+ MPASS(sd_map != NULL);
+ err = bus_dmamap_load(fl->ifl_buf_tag, sd_map[frag_idx],
+ cl, fl->ifl_buf_size, _rxq_refill_cb, &cb_arg,
+ BUS_DMA_NOWAIT);
+ if (err != 0 || cb_arg.error) {
+ /*
+ * !zone_pack ?
+ */
+ if (fl->ifl_zone == zone_pack)
+ uma_zfree(fl->ifl_zone, cl);
+ break;
+ }
+
+ sd_ba[frag_idx] = bus_addr = cb_arg.seg.ds_addr;
+ sd_cl[frag_idx] = cl;
+#if MEMORY_LOGGING
+ fl->ifl_cl_enqueued++;
+#endif
+ } else {
+ bus_addr = sd_ba[frag_idx];
+ }
+ bus_dmamap_sync(fl->ifl_buf_tag, sd_map[frag_idx],
+ BUS_DMASYNC_PREREAD);
+
+ if (sd_m[frag_idx] == NULL) {
+ if ((m = m_gethdr(M_NOWAIT, MT_NOINIT)) == NULL) {
+ break;
+ }
+ sd_m[frag_idx] = m;
+ }
+ bit_set(fl->ifl_rx_bitmap, frag_idx);
+#if MEMORY_LOGGING
+ fl->ifl_m_enqueued++;
+#endif
+
+ DBG_COUNTER_INC(rx_allocs);
+ fl->ifl_rxd_idxs[i] = frag_idx;
+ fl->ifl_bus_addrs[i] = bus_addr;
+ fl->ifl_vm_addrs[i] = cl;
+ credits++;
+ i++;
+ MPASS(credits <= fl->ifl_size);
+ if (++idx == fl->ifl_size) {
+ fl->ifl_gen = 1;
+ idx = 0;
+ }
+ if (n == 0 || i == IFLIB_MAX_RX_REFRESH) {
+ iru.iru_pidx = pidx;
+ iru.iru_count = i;
+ ctx->isc_rxd_refill(ctx->ifc_softc, &iru);
+ i = 0;
+ pidx = idx;
+ fl->ifl_pidx = idx;
+ fl->ifl_credits = credits;
+ }
+ }
+
+ if (i) {
+ iru.iru_pidx = pidx;
+ iru.iru_count = i;
+ ctx->isc_rxd_refill(ctx->ifc_softc, &iru);
+ fl->ifl_pidx = idx;
+ fl->ifl_credits = credits;
+ }
+ DBG_COUNTER_INC(rxd_flush);
+ if (fl->ifl_pidx == 0)
+ pidx = fl->ifl_size - 1;
+ else
+ pidx = fl->ifl_pidx - 1;
+
+ bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ ctx->isc_rxd_flush(ctx->ifc_softc, fl->ifl_rxq->ifr_id, fl->ifl_id, pidx);
+ fl->ifl_fragidx = frag_idx;
+}
+
+static __inline void
+__iflib_fl_refill_lt(if_ctx_t ctx, iflib_fl_t fl, int max)
+{
+ /* we avoid allowing pidx to catch up with cidx as it confuses ixl */
+ int32_t reclaimable = fl->ifl_size - fl->ifl_credits - 1;
+#ifdef INVARIANTS
+ int32_t delta = fl->ifl_size - get_inuse(fl->ifl_size, fl->ifl_cidx, fl->ifl_pidx, fl->ifl_gen) - 1;
+#endif
+
+ MPASS(fl->ifl_credits <= fl->ifl_size);
+ MPASS(reclaimable == delta);
+
+ if (reclaimable > 0)
+ _iflib_fl_refill(ctx, fl, min(max, reclaimable));
+}
+
+uint8_t
+iflib_in_detach(if_ctx_t ctx)
+{
+ bool in_detach;
+
+ STATE_LOCK(ctx);
+ in_detach = !!(ctx->ifc_flags & IFC_IN_DETACH);
+ STATE_UNLOCK(ctx);
+ return (in_detach);
+}
+
+static void
+iflib_fl_bufs_free(iflib_fl_t fl)
+{
+ iflib_dma_info_t idi = fl->ifl_ifdi;
+ bus_dmamap_t sd_map;
+ uint32_t i;
+
+ for (i = 0; i < fl->ifl_size; i++) {
+ struct mbuf **sd_m = &fl->ifl_sds.ifsd_m[i];
+ caddr_t *sd_cl = &fl->ifl_sds.ifsd_cl[i];
+
+ if (*sd_cl != NULL) {
+ sd_map = fl->ifl_sds.ifsd_map[i];
+ bus_dmamap_sync(fl->ifl_buf_tag, sd_map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(fl->ifl_buf_tag, sd_map);
+ if (*sd_cl != NULL)
+ uma_zfree(fl->ifl_zone, *sd_cl);
+ // XXX: Should this get moved out?
+ if (iflib_in_detach(fl->ifl_rxq->ifr_ctx))
+ bus_dmamap_destroy(fl->ifl_buf_tag, sd_map);
+ if (*sd_m != NULL) {
+ m_init(*sd_m, M_NOWAIT, MT_DATA, 0);
+ uma_zfree(zone_mbuf, *sd_m);
+ }
+ } else {
+ MPASS(*sd_cl == NULL);
+ MPASS(*sd_m == NULL);
+ }
+#if MEMORY_LOGGING
+ fl->ifl_m_dequeued++;
+ fl->ifl_cl_dequeued++;
+#endif
+ *sd_cl = NULL;
+ *sd_m = NULL;
+ }
+#ifdef INVARIANTS
+ for (i = 0; i < fl->ifl_size; i++) {
+ MPASS(fl->ifl_sds.ifsd_cl[i] == NULL);
+ MPASS(fl->ifl_sds.ifsd_m[i] == NULL);
+ }
+#endif
+ /*
+ * Reset free list values
+ */
+ fl->ifl_credits = fl->ifl_cidx = fl->ifl_pidx = fl->ifl_gen = fl->ifl_fragidx = 0;
+ bzero(idi->idi_vaddr, idi->idi_size);
+}
+
+/*********************************************************************
+ *
+ * Initialize a free list and its buffers.
+ *
+ **********************************************************************/
+static int
+iflib_fl_setup(iflib_fl_t fl)
+{
+ iflib_rxq_t rxq = fl->ifl_rxq;
+ if_ctx_t ctx = rxq->ifr_ctx;
+
+ bit_nclear(fl->ifl_rx_bitmap, 0, fl->ifl_size - 1);
+ /*
+ ** Free current RX buffer structs and their mbufs
+ */
+ iflib_fl_bufs_free(fl);
+ /* Now replenish the mbufs */
+ MPASS(fl->ifl_credits == 0);
+ fl->ifl_buf_size = ctx->ifc_rx_mbuf_sz;
+ if (fl->ifl_buf_size > ctx->ifc_max_fl_buf_size)
+ ctx->ifc_max_fl_buf_size = fl->ifl_buf_size;
+ fl->ifl_cltype = m_gettype(fl->ifl_buf_size);
+ fl->ifl_zone = m_getzone(fl->ifl_buf_size);
+
+
+ /* avoid pre-allocating zillions of clusters to an idle card
+ * potentially speeding up attach
+ */
+ _iflib_fl_refill(ctx, fl, min(128, fl->ifl_size));
+ MPASS(min(128, fl->ifl_size) == fl->ifl_credits);
+ if (min(128, fl->ifl_size) != fl->ifl_credits)
+ return (ENOBUFS);
+ /*
+ * handle failure
+ */
+ MPASS(rxq != NULL);
+ MPASS(fl->ifl_ifdi != NULL);
+ bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Free receive ring data structures
+ *
+ **********************************************************************/
+static void
+iflib_rx_sds_free(iflib_rxq_t rxq)
+{
+ iflib_fl_t fl;
+ int i, j;
+
+ if (rxq->ifr_fl != NULL) {
+ for (i = 0; i < rxq->ifr_nfl; i++) {
+ fl = &rxq->ifr_fl[i];
+ if (fl->ifl_buf_tag != NULL) {
+ if (fl->ifl_sds.ifsd_map != NULL) {
+ for (j = 0; j < fl->ifl_size; j++) {
+ if (fl->ifl_sds.ifsd_map[j] ==
+ NULL)
+ continue;
+ bus_dmamap_sync(
+ fl->ifl_buf_tag,
+ fl->ifl_sds.ifsd_map[j],
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(
+ fl->ifl_buf_tag,
+ fl->ifl_sds.ifsd_map[j]);
+ }
+ }
+ bus_dma_tag_destroy(fl->ifl_buf_tag);
+ fl->ifl_buf_tag = NULL;
+ }
+ free(fl->ifl_sds.ifsd_m, M_IFLIB);
+ free(fl->ifl_sds.ifsd_cl, M_IFLIB);
+ free(fl->ifl_sds.ifsd_ba, M_IFLIB);
+ free(fl->ifl_sds.ifsd_map, M_IFLIB);
+ fl->ifl_sds.ifsd_m = NULL;
+ fl->ifl_sds.ifsd_cl = NULL;
+ fl->ifl_sds.ifsd_ba = NULL;
+ fl->ifl_sds.ifsd_map = NULL;
+ }
+ free(rxq->ifr_fl, M_IFLIB);
+ rxq->ifr_fl = NULL;
+ rxq->ifr_cq_cidx = 0;
+ }
+}
+
+/*
+ * Timer routine
+ */
+static void
+iflib_timer(void *arg)
+{
+ iflib_txq_t txq = arg;
+ if_ctx_t ctx = txq->ift_ctx;
+ if_softc_ctx_t sctx = &ctx->ifc_softc_ctx;
+ uint64_t this_tick = ticks;
+ uint32_t reset_on = hz / 2;
+
+ if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING))
+ return;
+
+ /*
+ ** Check on the state of the TX queue(s), this
+ ** can be done without the lock because its RO
+ ** and the HUNG state will be static if set.
+ */
+ if (this_tick - txq->ift_last_timer_tick >= hz / 2) {
+ txq->ift_last_timer_tick = this_tick;
+ IFDI_TIMER(ctx, txq->ift_id);
+ if ((txq->ift_qstatus == IFLIB_QUEUE_HUNG) &&
+ ((txq->ift_cleaned_prev == txq->ift_cleaned) ||
+ (sctx->isc_pause_frames == 0)))
+ goto hung;
+
+ if (ifmp_ring_is_stalled(txq->ift_br))
+ txq->ift_qstatus = IFLIB_QUEUE_HUNG;
+ txq->ift_cleaned_prev = txq->ift_cleaned;
+ }
+#ifdef DEV_NETMAP
+ if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP)
+ iflib_netmap_timer_adjust(ctx, txq, &reset_on);
+#endif
+ /* handle any laggards */
+ if (txq->ift_db_pending)
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+
+ sctx->isc_pause_frames = 0;
+ if (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)
+ callout_reset_on(&txq->ift_timer, reset_on, iflib_timer, txq, txq->ift_timer.c_cpu);
+ return;
+
+ hung:
+ device_printf(ctx->ifc_dev,
+ "Watchdog timeout (TX: %d desc avail: %d pidx: %d) -- resetting\n",
+ txq->ift_id, TXQ_AVAIL(txq), txq->ift_pidx);
+ STATE_LOCK(ctx);
+ if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+ ctx->ifc_flags |= (IFC_DO_WATCHDOG|IFC_DO_RESET);
+ iflib_admin_intr_deferred(ctx);
+ STATE_UNLOCK(ctx);
+}
+
+static void
+iflib_calc_rx_mbuf_sz(if_ctx_t ctx)
+{
+ if_softc_ctx_t sctx = &ctx->ifc_softc_ctx;
+
+ /*
+ * XXX don't set the max_frame_size to larger
+ * than the hardware can handle
+ */
+ if (sctx->isc_max_frame_size <= MCLBYTES)
+ ctx->ifc_rx_mbuf_sz = MCLBYTES;
+ else
+ ctx->ifc_rx_mbuf_sz = MJUMPAGESIZE;
+}
+
+uint32_t
+iflib_get_rx_mbuf_sz(if_ctx_t ctx)
+{
+
+ return (ctx->ifc_rx_mbuf_sz);
+}
+
+static void
+iflib_init_locked(if_ctx_t ctx)
+{
+ if_softc_ctx_t sctx = &ctx->ifc_softc_ctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ if_t ifp = ctx->ifc_ifp;
+ iflib_fl_t fl;
+ iflib_txq_t txq;
+ iflib_rxq_t rxq;
+ int i, j, tx_ip_csum_flags, tx_ip6_csum_flags;
+
+ if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+ IFDI_INTR_DISABLE(ctx);
+
+ tx_ip_csum_flags = scctx->isc_tx_csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP);
+ tx_ip6_csum_flags = scctx->isc_tx_csum_flags & (CSUM_IP6_TCP | CSUM_IP6_UDP | CSUM_IP6_SCTP);
+ /* Set hardware offload abilities */
+ if_clearhwassist(ifp);
+ if (if_getcapenable(ifp) & IFCAP_TXCSUM)
+ if_sethwassistbits(ifp, tx_ip_csum_flags, 0);
+ if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
+ if_sethwassistbits(ifp, tx_ip6_csum_flags, 0);
+ if (if_getcapenable(ifp) & IFCAP_TSO4)
+ if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
+ if (if_getcapenable(ifp) & IFCAP_TSO6)
+ if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
+
+ for (i = 0, txq = ctx->ifc_txqs; i < sctx->isc_ntxqsets; i++, txq++) {
+ CALLOUT_LOCK(txq);
+ callout_stop(&txq->ift_timer);
+ CALLOUT_UNLOCK(txq);
+ iflib_netmap_txq_init(ctx, txq);
+ }
+
+ /*
+ * Calculate a suitable Rx mbuf size prior to calling IFDI_INIT, so
+ * that drivers can use the value when setting up the hardware receive
+ * buffers.
+ */
+ iflib_calc_rx_mbuf_sz(ctx);
+
+#ifdef INVARIANTS
+ i = if_getdrvflags(ifp);
+#endif
+ IFDI_INIT(ctx);
+ MPASS(if_getdrvflags(ifp) == i);
+ for (i = 0, rxq = ctx->ifc_rxqs; i < sctx->isc_nrxqsets; i++, rxq++) {
+ /* XXX this should really be done on a per-queue basis */
+ if (if_getcapenable(ifp) & IFCAP_NETMAP) {
+ MPASS(rxq->ifr_id == i);
+ iflib_netmap_rxq_init(ctx, rxq);
+ continue;
+ }
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) {
+ if (iflib_fl_setup(fl)) {
+ device_printf(ctx->ifc_dev,
+ "setting up free list %d failed - "
+ "check cluster settings\n", j);
+ goto done;
+ }
+ }
+ }
+done:
+ if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
+ IFDI_INTR_ENABLE(ctx);
+ txq = ctx->ifc_txqs;
+ for (i = 0; i < sctx->isc_ntxqsets; i++, txq++)
+ callout_reset_on(&txq->ift_timer, hz/2, iflib_timer, txq,
+ txq->ift_timer.c_cpu);
+}
+
+static int
+iflib_media_change(if_t ifp)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+ int err;
+
+ CTX_LOCK(ctx);
+ if ((err = IFDI_MEDIA_CHANGE(ctx)) == 0)
+ iflib_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+ return (err);
+}
+
+static void
+iflib_media_status(if_t ifp, struct ifmediareq *ifmr)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+
+ CTX_LOCK(ctx);
+ IFDI_UPDATE_ADMIN_STATUS(ctx);
+ IFDI_MEDIA_STATUS(ctx, ifmr);
+ CTX_UNLOCK(ctx);
+}
+
+void
+iflib_stop(if_ctx_t ctx)
+{
+ iflib_txq_t txq = ctx->ifc_txqs;
+ iflib_rxq_t rxq = ctx->ifc_rxqs;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ iflib_dma_info_t di;
+ iflib_fl_t fl;
+ int i, j;
+
+ /* Tell the stack that the interface is no longer active */
+ if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+
+ IFDI_INTR_DISABLE(ctx);
+ DELAY(1000);
+ IFDI_STOP(ctx);
+ DELAY(1000);
+
+ iflib_debug_reset();
+ /* Wait for current tx queue users to exit to disarm watchdog timer. */
+ for (i = 0; i < scctx->isc_ntxqsets; i++, txq++) {
+ /* make sure all transmitters have completed before proceeding XXX */
+
+ CALLOUT_LOCK(txq);
+ callout_stop(&txq->ift_timer);
+ CALLOUT_UNLOCK(txq);
+
+ /* clean any enqueued buffers */
+ iflib_ifmp_purge(txq);
+ /* Free any existing tx buffers. */
+ for (j = 0; j < txq->ift_size; j++) {
+ iflib_txsd_free(ctx, txq, j);
+ }
+ txq->ift_processed = txq->ift_cleaned = txq->ift_cidx_processed = 0;
+ txq->ift_in_use = txq->ift_gen = txq->ift_cidx = txq->ift_pidx = txq->ift_no_desc_avail = 0;
+ txq->ift_closed = txq->ift_mbuf_defrag = txq->ift_mbuf_defrag_failed = 0;
+ txq->ift_no_tx_dma_setup = txq->ift_txd_encap_efbig = txq->ift_map_failed = 0;
+ txq->ift_pullups = 0;
+ ifmp_ring_reset_stats(txq->ift_br);
+ for (j = 0, di = txq->ift_ifdi; j < sctx->isc_ntxqs; j++, di++)
+ bzero((void *)di->idi_vaddr, di->idi_size);
+ }
+ for (i = 0; i < scctx->isc_nrxqsets; i++, rxq++) {
+ /* make sure all transmitters have completed before proceeding XXX */
+
+ rxq->ifr_cq_cidx = 0;
+ for (j = 0, di = rxq->ifr_ifdi; j < sctx->isc_nrxqs; j++, di++)
+ bzero((void *)di->idi_vaddr, di->idi_size);
+ /* also resets the free lists pidx/cidx */
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++)
+ iflib_fl_bufs_free(fl);
+ }
+}
+
+static inline caddr_t
+calc_next_rxd(iflib_fl_t fl, int cidx)
+{
+ qidx_t size;
+ int nrxd;
+ caddr_t start, end, cur, next;
+
+ nrxd = fl->ifl_size;
+ size = fl->ifl_rxd_size;
+ start = fl->ifl_ifdi->idi_vaddr;
+
+ if (__predict_false(size == 0))
+ return (start);
+ cur = start + size*cidx;
+ end = start + size*nrxd;
+ next = CACHE_PTR_NEXT(cur);
+ return (next < end ? next : start);
+}
+
+static inline void
+prefetch_pkts(iflib_fl_t fl, int cidx)
+{
+ int nextptr;
+ int nrxd = fl->ifl_size;
+ caddr_t next_rxd;
+
+
+ nextptr = (cidx + CACHE_PTR_INCREMENT) & (nrxd-1);
+ prefetch(&fl->ifl_sds.ifsd_m[nextptr]);
+ prefetch(&fl->ifl_sds.ifsd_cl[nextptr]);
+ next_rxd = calc_next_rxd(fl, cidx);
+ prefetch(next_rxd);
+ prefetch(fl->ifl_sds.ifsd_m[(cidx + 1) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_m[(cidx + 2) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_m[(cidx + 3) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_m[(cidx + 4) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_cl[(cidx + 1) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_cl[(cidx + 2) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_cl[(cidx + 3) & (nrxd-1)]);
+ prefetch(fl->ifl_sds.ifsd_cl[(cidx + 4) & (nrxd-1)]);
+}
+
+static struct mbuf *
+rxd_frag_to_sd(iflib_rxq_t rxq, if_rxd_frag_t irf, bool unload, if_rxsd_t sd,
+ int *pf_rv, if_rxd_info_t ri)
+{
+ bus_dmamap_t map;
+ iflib_fl_t fl;
+ caddr_t payload;
+ struct mbuf *m;
+ int flid, cidx, len, next;
+
+ map = NULL;
+ flid = irf->irf_flid;
+ cidx = irf->irf_idx;
+ fl = &rxq->ifr_fl[flid];
+ sd->ifsd_fl = fl;
+ sd->ifsd_cidx = cidx;
+ m = fl->ifl_sds.ifsd_m[cidx];
+ sd->ifsd_cl = &fl->ifl_sds.ifsd_cl[cidx];
+ fl->ifl_credits--;
+#if MEMORY_LOGGING
+ fl->ifl_m_dequeued++;
+#endif
+ if (rxq->ifr_ctx->ifc_flags & IFC_PREFETCH)
+ prefetch_pkts(fl, cidx);
+ next = (cidx + CACHE_PTR_INCREMENT) & (fl->ifl_size-1);
+ prefetch(&fl->ifl_sds.ifsd_map[next]);
+ map = fl->ifl_sds.ifsd_map[cidx];
+ next = (cidx + CACHE_LINE_SIZE) & (fl->ifl_size-1);
+
+ /* not valid assert if bxe really does SGE from non-contiguous elements */
+ MPASS(fl->ifl_cidx == cidx);
+ bus_dmamap_sync(fl->ifl_buf_tag, map, BUS_DMASYNC_POSTREAD);
+
+ if (rxq->pfil != NULL && PFIL_HOOKED_IN(rxq->pfil) && pf_rv != NULL) {
+ payload = *sd->ifsd_cl;
+ payload += ri->iri_pad;
+ len = ri->iri_len - ri->iri_pad;
+ *pf_rv = pfil_run_hooks(rxq->pfil, payload, ri->iri_ifp,
+ len | PFIL_MEMPTR | PFIL_IN, NULL);
+ switch (*pf_rv) {
+ case PFIL_DROPPED:
+ case PFIL_CONSUMED:
+ /*
+ * The filter ate it. Everything is recycled.
+ */
+ m = NULL;
+ unload = 0;
+ break;
+ case PFIL_REALLOCED:
+ /*
+ * The filter copied it. Everything is recycled.
+ */
+ m = pfil_mem2mbuf(payload);
+ unload = 0;
+ break;
+ case PFIL_PASS:
+ /*
+ * Filter said it was OK, so receive like
+ * normal
+ */
+ fl->ifl_sds.ifsd_m[cidx] = NULL;
+ break;
+ default:
+ MPASS(0);
+ }
+ } else {
+ fl->ifl_sds.ifsd_m[cidx] = NULL;
+ *pf_rv = PFIL_PASS;
+ }
+
+ if (unload)
+ bus_dmamap_unload(fl->ifl_buf_tag, map);
+ fl->ifl_cidx = (fl->ifl_cidx + 1) & (fl->ifl_size-1);
+ if (__predict_false(fl->ifl_cidx == 0))
+ fl->ifl_gen = 0;
+ bit_clear(fl->ifl_rx_bitmap, cidx);
+ return (m);
+}
+
+static struct mbuf *
+assemble_segments(iflib_rxq_t rxq, if_rxd_info_t ri, if_rxsd_t sd, int *pf_rv)
+{
+ struct mbuf *m, *mh, *mt;
+ caddr_t cl;
+ int *pf_rv_ptr, flags, i, padlen;
+ bool consumed;
+
+ i = 0;
+ mh = NULL;
+ consumed = false;
+ *pf_rv = PFIL_PASS;
+ pf_rv_ptr = pf_rv;
+ do {
+ m = rxd_frag_to_sd(rxq, &ri->iri_frags[i], !consumed, sd,
+ pf_rv_ptr, ri);
+
+ MPASS(*sd->ifsd_cl != NULL);
+
+ /*
+ * Exclude zero-length frags & frags from
+ * packets the filter has consumed or dropped
+ */
+ if (ri->iri_frags[i].irf_len == 0 || consumed ||
+ *pf_rv == PFIL_CONSUMED || *pf_rv == PFIL_DROPPED) {
+ if (mh == NULL) {
+ /* everything saved here */
+ consumed = true;
+ pf_rv_ptr = NULL;
+ continue;
+ }
+ /* XXX we can save the cluster here, but not the mbuf */
+ m_init(m, M_NOWAIT, MT_DATA, 0);
+ m_free(m);
+ continue;
+ }
+ if (mh == NULL) {
+ flags = M_PKTHDR|M_EXT;
+ mh = mt = m;
+ padlen = ri->iri_pad;
+ } else {
+ flags = M_EXT;
+ mt->m_next = m;
+ mt = m;
+ /* assuming padding is only on the first fragment */
+ padlen = 0;
+ }
+ cl = *sd->ifsd_cl;
+ *sd->ifsd_cl = NULL;
+
+ /* Can these two be made one ? */
+ m_init(m, M_NOWAIT, MT_DATA, flags);
+ m_cljset(m, cl, sd->ifsd_fl->ifl_cltype);
+ /*
+ * These must follow m_init and m_cljset
+ */
+ m->m_data += padlen;
+ ri->iri_len -= padlen;
+ m->m_len = ri->iri_frags[i].irf_len;
+ } while (++i < ri->iri_nfrags);
+
+ return (mh);
+}
+
+/*
+ * Process one software descriptor
+ */
+static struct mbuf *
+iflib_rxd_pkt_get(iflib_rxq_t rxq, if_rxd_info_t ri)
+{
+ struct if_rxsd sd;
+ struct mbuf *m;
+ int pf_rv;
+
+ /* should I merge this back in now that the two paths are basically duplicated? */
+ if (ri->iri_nfrags == 1 &&
+ ri->iri_frags[0].irf_len <= MIN(IFLIB_RX_COPY_THRESH, MHLEN)) {
+ m = rxd_frag_to_sd(rxq, &ri->iri_frags[0], false, &sd,
+ &pf_rv, ri);
+ if (pf_rv != PFIL_PASS && pf_rv != PFIL_REALLOCED)
+ return (m);
+ if (pf_rv == PFIL_PASS) {
+ m_init(m, M_NOWAIT, MT_DATA, M_PKTHDR);
+#ifndef __NO_STRICT_ALIGNMENT
+ if (!IP_ALIGNED(m))
+ m->m_data += 2;
+#endif
+ memcpy(m->m_data, *sd.ifsd_cl, ri->iri_len);
+ m->m_len = ri->iri_frags[0].irf_len;
+ }
+ } else {
+ m = assemble_segments(rxq, ri, &sd, &pf_rv);
+ if (pf_rv != PFIL_PASS && pf_rv != PFIL_REALLOCED)
+ return (m);
+ }
+ m->m_pkthdr.len = ri->iri_len;
+ m->m_pkthdr.rcvif = ri->iri_ifp;
+ m->m_flags |= ri->iri_flags;
+ m->m_pkthdr.ether_vtag = ri->iri_vtag;
+ m->m_pkthdr.flowid = ri->iri_flowid;
+ M_HASHTYPE_SET(m, ri->iri_rsstype);
+ m->m_pkthdr.csum_flags = ri->iri_csum_flags;
+ m->m_pkthdr.csum_data = ri->iri_csum_data;
+ return (m);
+}
+
+#if defined(INET6) || defined(INET)
+static void
+iflib_get_ip_forwarding(struct lro_ctrl *lc, bool *v4, bool *v6)
+{
+ CURVNET_SET(lc->ifp->if_vnet);
+#if defined(INET6)
+ *v6 = V_ip6_forwarding;
+#endif
+#if defined(INET)
+ *v4 = V_ipforwarding;
+#endif
+ CURVNET_RESTORE();
+}
+
+/*
+ * Returns true if it's possible this packet could be LROed.
+ * if it returns false, it is guaranteed that tcp_lro_rx()
+ * would not return zero.
+ */
+static bool
+iflib_check_lro_possible(struct mbuf *m, bool v4_forwarding, bool v6_forwarding)
+{
+ struct ether_header *eh;
+
+ eh = mtod(m, struct ether_header *);
+ switch (eh->ether_type) {
+#if defined(INET6)
+ case htons(ETHERTYPE_IPV6):
+ return (!v6_forwarding);
+#endif
+#if defined (INET)
+ case htons(ETHERTYPE_IP):
+ return (!v4_forwarding);
+#endif
+ }
+
+ return false;
+}
+#else
+static void
+iflib_get_ip_forwarding(struct lro_ctrl *lc __unused, bool *v4 __unused, bool *v6 __unused)
+{
+}
+#endif
+
+static bool
+iflib_rxeof(iflib_rxq_t rxq, qidx_t budget)
+{
+ if_t ifp;
+ if_ctx_t ctx = rxq->ifr_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ int avail, i;
+ qidx_t *cidxp;
+ struct if_rxd_info ri;
+ int err, budget_left, rx_bytes, rx_pkts;
+ iflib_fl_t fl;
+ int lro_enabled;
+ bool v4_forwarding, v6_forwarding, lro_possible;
+
+ /*
+ * XXX early demux data packets so that if_input processing only handles
+ * acks in interrupt context
+ */
+ struct mbuf *m, *mh, *mt, *mf;
+
+ lro_possible = v4_forwarding = v6_forwarding = false;
+ ifp = ctx->ifc_ifp;
+ mh = mt = NULL;
+ MPASS(budget > 0);
+ rx_pkts = rx_bytes = 0;
+ if (sctx->isc_flags & IFLIB_HAS_RXCQ)
+ cidxp = &rxq->ifr_cq_cidx;
+ else
+ cidxp = &rxq->ifr_fl[0].ifl_cidx;
+ if ((avail = iflib_rxd_avail(ctx, rxq, *cidxp, budget)) == 0) {
+ for (i = 0, fl = &rxq->ifr_fl[0]; i < sctx->isc_nfl; i++, fl++)
+ __iflib_fl_refill_lt(ctx, fl, budget + 8);
+ DBG_COUNTER_INC(rx_unavail);
+ return (false);
+ }
+
+ /* pfil needs the vnet to be set */
+ CURVNET_SET_QUIET(ifp->if_vnet);
+ for (budget_left = budget; budget_left > 0 && avail > 0;) {
+ if (__predict_false(!CTX_ACTIVE(ctx))) {
+ DBG_COUNTER_INC(rx_ctx_inactive);
+ break;
+ }
+ /*
+ * Reset client set fields to their default values
+ */
+ rxd_info_zero(&ri);
+ ri.iri_qsidx = rxq->ifr_id;
+ ri.iri_cidx = *cidxp;
+ ri.iri_ifp = ifp;
+ ri.iri_frags = rxq->ifr_frags;
+ err = ctx->isc_rxd_pkt_get(ctx->ifc_softc, &ri);
+
+ if (err)
+ goto err;
+ rx_pkts += 1;
+ rx_bytes += ri.iri_len;
+ if (sctx->isc_flags & IFLIB_HAS_RXCQ) {
+ *cidxp = ri.iri_cidx;
+ /* Update our consumer index */
+ /* XXX NB: shurd - check if this is still safe */
+ while (rxq->ifr_cq_cidx >= scctx->isc_nrxd[0])
+ rxq->ifr_cq_cidx -= scctx->isc_nrxd[0];
+ /* was this only a completion queue message? */
+ if (__predict_false(ri.iri_nfrags == 0))
+ continue;
+ }
+ MPASS(ri.iri_nfrags != 0);
+ MPASS(ri.iri_len != 0);
+
+ /* will advance the cidx on the corresponding free lists */
+ m = iflib_rxd_pkt_get(rxq, &ri);
+ avail--;
+ budget_left--;
+ if (avail == 0 && budget_left)
+ avail = iflib_rxd_avail(ctx, rxq, *cidxp, budget_left);
+
+ if (__predict_false(m == NULL))
+ continue;
+
+ /* imm_pkt: -- cxgb */
+ if (mh == NULL)
+ mh = mt = m;
+ else {
+ mt->m_nextpkt = m;
+ mt = m;
+ }
+ }
+ CURVNET_RESTORE();
+ /* make sure that we can refill faster than drain */
+ for (i = 0, fl = &rxq->ifr_fl[0]; i < sctx->isc_nfl; i++, fl++)
+ __iflib_fl_refill_lt(ctx, fl, budget + 8);
+
+ lro_enabled = (if_getcapenable(ifp) & IFCAP_LRO);
+ if (lro_enabled)
+ iflib_get_ip_forwarding(&rxq->ifr_lc, &v4_forwarding, &v6_forwarding);
+ mt = mf = NULL;
+ while (mh != NULL) {
+ m = mh;
+ mh = mh->m_nextpkt;
+ m->m_nextpkt = NULL;
+#ifndef __NO_STRICT_ALIGNMENT
+ if (!IP_ALIGNED(m) && (m = iflib_fixup_rx(m)) == NULL)
+ continue;
+#endif
+ rx_bytes += m->m_pkthdr.len;
+ rx_pkts++;
+#if defined(INET6) || defined(INET)
+ if (lro_enabled) {
+ if (!lro_possible) {
+ lro_possible = iflib_check_lro_possible(m, v4_forwarding, v6_forwarding);
+ if (lro_possible && mf != NULL) {
+ ifp->if_input(ifp, mf);
+ DBG_COUNTER_INC(rx_if_input);
+ mt = mf = NULL;
+ }
+ }
+ if ((m->m_pkthdr.csum_flags & (CSUM_L4_CALC|CSUM_L4_VALID)) ==
+ (CSUM_L4_CALC|CSUM_L4_VALID)) {
+ if (lro_possible && tcp_lro_rx(&rxq->ifr_lc, m, 0) == 0)
+ continue;
+ }
+ }
+#endif
+ if (lro_possible) {
+ ifp->if_input(ifp, m);
+ DBG_COUNTER_INC(rx_if_input);
+ continue;
+ }
+
+ if (mf == NULL)
+ mf = m;
+ if (mt != NULL)
+ mt->m_nextpkt = m;
+ mt = m;
+ }
+ if (mf != NULL) {
+ ifp->if_input(ifp, mf);
+ DBG_COUNTER_INC(rx_if_input);
+ }
+
+ if_inc_counter(ifp, IFCOUNTER_IBYTES, rx_bytes);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, rx_pkts);
+
+ /*
+ * Flush any outstanding LRO work
+ */
+#if defined(INET6) || defined(INET)
+ tcp_lro_flush_all(&rxq->ifr_lc);
+#endif
+ if (avail)
+ return true;
+ return (iflib_rxd_avail(ctx, rxq, *cidxp, 1));
+err:
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_DO_RESET;
+ iflib_admin_intr_deferred(ctx);
+ STATE_UNLOCK(ctx);
+ return (false);
+}
+
+#define TXD_NOTIFY_COUNT(txq) (((txq)->ift_size / (txq)->ift_update_freq)-1)
+static inline qidx_t
+txq_max_db_deferred(iflib_txq_t txq, qidx_t in_use)
+{
+ qidx_t notify_count = TXD_NOTIFY_COUNT(txq);
+ qidx_t minthresh = txq->ift_size / 8;
+ if (in_use > 4*minthresh)
+ return (notify_count);
+ if (in_use > 2*minthresh)
+ return (notify_count >> 1);
+ if (in_use > minthresh)
+ return (notify_count >> 3);
+ return (0);
+}
+
+static inline qidx_t
+txq_max_rs_deferred(iflib_txq_t txq)
+{
+ qidx_t notify_count = TXD_NOTIFY_COUNT(txq);
+ qidx_t minthresh = txq->ift_size / 8;
+ if (txq->ift_in_use > 4*minthresh)
+ return (notify_count);
+ if (txq->ift_in_use > 2*minthresh)
+ return (notify_count >> 1);
+ if (txq->ift_in_use > minthresh)
+ return (notify_count >> 2);
+ return (2);
+}
+
+#define M_CSUM_FLAGS(m) ((m)->m_pkthdr.csum_flags)
+#define M_HAS_VLANTAG(m) (m->m_flags & M_VLANTAG)
+
+#define TXQ_MAX_DB_DEFERRED(txq, in_use) txq_max_db_deferred((txq), (in_use))
+#define TXQ_MAX_RS_DEFERRED(txq) txq_max_rs_deferred(txq)
+#define TXQ_MAX_DB_CONSUMED(size) (size >> 4)
+
+/* forward compatibility for cxgb */
+#define FIRST_QSET(ctx) 0
+#define NTXQSETS(ctx) ((ctx)->ifc_softc_ctx.isc_ntxqsets)
+#define NRXQSETS(ctx) ((ctx)->ifc_softc_ctx.isc_nrxqsets)
+#define QIDX(ctx, m) ((((m)->m_pkthdr.flowid & ctx->ifc_softc_ctx.isc_rss_table_mask) % NTXQSETS(ctx)) + FIRST_QSET(ctx))
+#define DESC_RECLAIMABLE(q) ((int)((q)->ift_processed - (q)->ift_cleaned - (q)->ift_ctx->ifc_softc_ctx.isc_tx_nsegments))
+
+/* XXX we should be setting this to something other than zero */
+#define RECLAIM_THRESH(ctx) ((ctx)->ifc_sctx->isc_tx_reclaim_thresh)
+#define MAX_TX_DESC(ctx) max((ctx)->ifc_softc_ctx.isc_tx_tso_segments_max, \
+ (ctx)->ifc_softc_ctx.isc_tx_nsegments)
+
+static inline bool
+iflib_txd_db_check(if_ctx_t ctx, iflib_txq_t txq, int ring, qidx_t in_use)
+{
+ qidx_t dbval, max;
+ bool rang;
+
+ rang = false;
+ max = TXQ_MAX_DB_DEFERRED(txq, in_use);
+ if (ring || txq->ift_db_pending >= max) {
+ dbval = txq->ift_npending ? txq->ift_npending : txq->ift_pidx;
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ ctx->isc_txd_flush(ctx->ifc_softc, txq->ift_id, dbval);
+ txq->ift_db_pending = txq->ift_npending = 0;
+ rang = true;
+ }
+ return (rang);
+}
+
+#ifdef PKT_DEBUG
+static void
+print_pkt(if_pkt_info_t pi)
+{
+ printf("pi len: %d qsidx: %d nsegs: %d ndescs: %d flags: %x pidx: %d\n",
+ pi->ipi_len, pi->ipi_qsidx, pi->ipi_nsegs, pi->ipi_ndescs, pi->ipi_flags, pi->ipi_pidx);
+ printf("pi new_pidx: %d csum_flags: %lx tso_segsz: %d mflags: %x vtag: %d\n",
+ pi->ipi_new_pidx, pi->ipi_csum_flags, pi->ipi_tso_segsz, pi->ipi_mflags, pi->ipi_vtag);
+ printf("pi etype: %d ehdrlen: %d ip_hlen: %d ipproto: %d\n",
+ pi->ipi_etype, pi->ipi_ehdrlen, pi->ipi_ip_hlen, pi->ipi_ipproto);
+}
+#endif
+
+#define IS_TSO4(pi) ((pi)->ipi_csum_flags & CSUM_IP_TSO)
+#define IS_TX_OFFLOAD4(pi) ((pi)->ipi_csum_flags & (CSUM_IP_TCP | CSUM_IP_TSO))
+#define IS_TSO6(pi) ((pi)->ipi_csum_flags & CSUM_IP6_TSO)
+#define IS_TX_OFFLOAD6(pi) ((pi)->ipi_csum_flags & (CSUM_IP6_TCP | CSUM_IP6_TSO))
+
+static int
+iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp)
+{
+ if_shared_ctx_t sctx = txq->ift_ctx->ifc_sctx;
+ struct ether_vlan_header *eh;
+ struct mbuf *m;
+
+ m = *mp;
+ if ((sctx->isc_flags & IFLIB_NEED_SCRATCH) &&
+ M_WRITABLE(m) == 0) {
+ if ((m = m_dup(m, M_NOWAIT)) == NULL) {
+ return (ENOMEM);
+ } else {
+ m_freem(*mp);
+ DBG_COUNTER_INC(tx_frees);
+ *mp = m;
+ }
+ }
+
+ /*
+ * Determine where frame payload starts.
+ * Jump over vlan headers if already present,
+ * helpful for QinQ too.
+ */
+ if (__predict_false(m->m_len < sizeof(*eh))) {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, sizeof(*eh))) == NULL))
+ return (ENOMEM);
+ }
+ eh = mtod(m, struct ether_vlan_header *);
+ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+ pi->ipi_etype = ntohs(eh->evl_proto);
+ pi->ipi_ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ } else {
+ pi->ipi_etype = ntohs(eh->evl_encap_proto);
+ pi->ipi_ehdrlen = ETHER_HDR_LEN;
+ }
+
+ switch (pi->ipi_etype) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ {
+ struct mbuf *n;
+ struct ip *ip = NULL;
+ struct tcphdr *th = NULL;
+ int minthlen;
+
+ minthlen = min(m->m_pkthdr.len, pi->ipi_ehdrlen + sizeof(*ip) + sizeof(*th));
+ if (__predict_false(m->m_len < minthlen)) {
+ /*
+ * if this code bloat is causing too much of a hit
+ * move it to a separate function and mark it noinline
+ */
+ if (m->m_len == pi->ipi_ehdrlen) {
+ n = m->m_next;
+ MPASS(n);
+ if (n->m_len >= sizeof(*ip)) {
+ ip = (struct ip *)n->m_data;
+ if (n->m_len >= (ip->ip_hl << 2) + sizeof(*th))
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ } else {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, minthlen)) == NULL))
+ return (ENOMEM);
+ ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen);
+ }
+ } else {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, minthlen)) == NULL))
+ return (ENOMEM);
+ ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen);
+ if (m->m_len >= (ip->ip_hl << 2) + sizeof(*th))
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ }
+ } else {
+ ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen);
+ if (m->m_len >= (ip->ip_hl << 2) + sizeof(*th))
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ }
+ pi->ipi_ip_hlen = ip->ip_hl << 2;
+ pi->ipi_ipproto = ip->ip_p;
+ pi->ipi_flags |= IPI_TX_IPV4;
+
+ /* TCP checksum offload may require TCP header length */
+ if (IS_TX_OFFLOAD4(pi)) {
+ if (__predict_true(pi->ipi_ipproto == IPPROTO_TCP)) {
+ if (__predict_false(th == NULL)) {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, (ip->ip_hl << 2) + sizeof(*th))) == NULL))
+ return (ENOMEM);
+ th = (struct tcphdr *)((caddr_t)ip + pi->ipi_ip_hlen);
+ }
+ pi->ipi_tcp_hflags = th->th_flags;
+ pi->ipi_tcp_hlen = th->th_off << 2;
+ pi->ipi_tcp_seq = th->th_seq;
+ }
+ if (IS_TSO4(pi)) {
+ if (__predict_false(ip->ip_p != IPPROTO_TCP))
+ return (ENXIO);
+ /*
+ * TSO always requires hardware checksum offload.
+ */
+ pi->ipi_csum_flags |= (CSUM_IP_TCP | CSUM_IP);
+ th->th_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+ pi->ipi_tso_segsz = m->m_pkthdr.tso_segsz;
+ if (sctx->isc_flags & IFLIB_TSO_INIT_IP) {
+ ip->ip_sum = 0;
+ ip->ip_len = htons(pi->ipi_ip_hlen + pi->ipi_tcp_hlen + pi->ipi_tso_segsz);
+ }
+ }
+ }
+ if ((sctx->isc_flags & IFLIB_NEED_ZERO_CSUM) && (pi->ipi_csum_flags & CSUM_IP))
+ ip->ip_sum = 0;
+
+ break;
+ }
+#endif
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ {
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)(m->m_data + pi->ipi_ehdrlen);
+ struct tcphdr *th;
+ pi->ipi_ip_hlen = sizeof(struct ip6_hdr);
+
+ if (__predict_false(m->m_len < pi->ipi_ehdrlen + sizeof(struct ip6_hdr))) {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr))) == NULL))
+ return (ENOMEM);
+ }
+ th = (struct tcphdr *)((caddr_t)ip6 + pi->ipi_ip_hlen);
+
+ /* XXX-BZ this will go badly in case of ext hdrs. */
+ pi->ipi_ipproto = ip6->ip6_nxt;
+ pi->ipi_flags |= IPI_TX_IPV6;
+
+ /* TCP checksum offload may require TCP header length */
+ if (IS_TX_OFFLOAD6(pi)) {
+ if (pi->ipi_ipproto == IPPROTO_TCP) {
+ if (__predict_false(m->m_len < pi->ipi_ehdrlen + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))) {
+ txq->ift_pullups++;
+ if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))) == NULL))
+ return (ENOMEM);
+ }
+ pi->ipi_tcp_hflags = th->th_flags;
+ pi->ipi_tcp_hlen = th->th_off << 2;
+ pi->ipi_tcp_seq = th->th_seq;
+ }
+ if (IS_TSO6(pi)) {
+ if (__predict_false(ip6->ip6_nxt != IPPROTO_TCP))
+ return (ENXIO);
+ /*
+ * TSO always requires hardware checksum offload.
+ */
+ pi->ipi_csum_flags |= CSUM_IP6_TCP;
+ th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
+ pi->ipi_tso_segsz = m->m_pkthdr.tso_segsz;
+ }
+ }
+ break;
+ }
+#endif
+ default:
+ pi->ipi_csum_flags &= ~CSUM_OFFLOAD;
+ pi->ipi_ip_hlen = 0;
+ break;
+ }
+ *mp = m;
+
+ return (0);
+}
+
+/*
+ * If dodgy hardware rejects the scatter gather chain we've handed it
+ * we'll need to remove the mbuf chain from ifsg_m[] before we can add the
+ * m_defrag'd mbufs
+ */
+static __noinline struct mbuf *
+iflib_remove_mbuf(iflib_txq_t txq)
+{
+ int ntxd, pidx;
+ struct mbuf *m, **ifsd_m;
+
+ ifsd_m = txq->ift_sds.ifsd_m;
+ ntxd = txq->ift_size;
+ pidx = txq->ift_pidx & (ntxd - 1);
+ ifsd_m = txq->ift_sds.ifsd_m;
+ m = ifsd_m[pidx];
+ ifsd_m[pidx] = NULL;
+ bus_dmamap_unload(txq->ift_buf_tag, txq->ift_sds.ifsd_map[pidx]);
+ if (txq->ift_sds.ifsd_tso_map != NULL)
+ bus_dmamap_unload(txq->ift_tso_buf_tag,
+ txq->ift_sds.ifsd_tso_map[pidx]);
+#if MEMORY_LOGGING
+ txq->ift_dequeued++;
+#endif
+ return (m);
+}
+
+static inline caddr_t
+calc_next_txd(iflib_txq_t txq, int cidx, uint8_t qid)
+{
+ qidx_t size;
+ int ntxd;
+ caddr_t start, end, cur, next;
+
+ ntxd = txq->ift_size;
+ size = txq->ift_txd_size[qid];
+ start = txq->ift_ifdi[qid].idi_vaddr;
+
+ if (__predict_false(size == 0))
+ return (start);
+ cur = start + size*cidx;
+ end = start + size*ntxd;
+ next = CACHE_PTR_NEXT(cur);
+ return (next < end ? next : start);
+}
+
+/*
+ * Pad an mbuf to ensure a minimum ethernet frame size.
+ * min_frame_size is the frame size (less CRC) to pad the mbuf to
+ */
+static __noinline int
+iflib_ether_pad(device_t dev, struct mbuf **m_head, uint16_t min_frame_size)
+{
+ /*
+ * 18 is enough bytes to pad an ARP packet to 46 bytes, and
+ * and ARP message is the smallest common payload I can think of
+ */
+ static char pad[18]; /* just zeros */
+ int n;
+ struct mbuf *new_head;
+
+ if (!M_WRITABLE(*m_head)) {
+ new_head = m_dup(*m_head, M_NOWAIT);
+ if (new_head == NULL) {
+ m_freem(*m_head);
+ device_printf(dev, "cannot pad short frame, m_dup() failed");
+ DBG_COUNTER_INC(encap_pad_mbuf_fail);
+ DBG_COUNTER_INC(tx_frees);
+ return ENOMEM;
+ }
+ m_freem(*m_head);
+ *m_head = new_head;
+ }
+
+ for (n = min_frame_size - (*m_head)->m_pkthdr.len;
+ n > 0; n -= sizeof(pad))
+ if (!m_append(*m_head, min(n, sizeof(pad)), pad))
+ break;
+
+ if (n > 0) {
+ m_freem(*m_head);
+ device_printf(dev, "cannot pad short frame\n");
+ DBG_COUNTER_INC(encap_pad_mbuf_fail);
+ DBG_COUNTER_INC(tx_frees);
+ return (ENOBUFS);
+ }
+
+ return 0;
+}
+
+static int
+iflib_encap(iflib_txq_t txq, struct mbuf **m_headp)
+{
+ if_ctx_t ctx;
+ if_shared_ctx_t sctx;
+ if_softc_ctx_t scctx;
+ bus_dma_tag_t buf_tag;
+ bus_dma_segment_t *segs;
+ struct mbuf *m_head, **ifsd_m;
+ void *next_txd;
+ bus_dmamap_t map;
+ struct if_pkt_info pi;
+ int remap = 0;
+ int err, nsegs, ndesc, max_segs, pidx, cidx, next, ntxd;
+
+ ctx = txq->ift_ctx;
+ sctx = ctx->ifc_sctx;
+ scctx = &ctx->ifc_softc_ctx;
+ segs = txq->ift_segs;
+ ntxd = txq->ift_size;
+ m_head = *m_headp;
+ map = NULL;
+
+ /*
+ * If we're doing TSO the next descriptor to clean may be quite far ahead
+ */
+ cidx = txq->ift_cidx;
+ pidx = txq->ift_pidx;
+ if (ctx->ifc_flags & IFC_PREFETCH) {
+ next = (cidx + CACHE_PTR_INCREMENT) & (ntxd-1);
+ if (!(ctx->ifc_flags & IFLIB_HAS_TXCQ)) {
+ next_txd = calc_next_txd(txq, cidx, 0);
+ prefetch(next_txd);
+ }
+
+ /* prefetch the next cache line of mbuf pointers and flags */
+ prefetch(&txq->ift_sds.ifsd_m[next]);
+ prefetch(&txq->ift_sds.ifsd_map[next]);
+ next = (cidx + CACHE_LINE_SIZE) & (ntxd-1);
+ }
+ map = txq->ift_sds.ifsd_map[pidx];
+ ifsd_m = txq->ift_sds.ifsd_m;
+
+ if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
+ buf_tag = txq->ift_tso_buf_tag;
+ max_segs = scctx->isc_tx_tso_segments_max;
+ map = txq->ift_sds.ifsd_tso_map[pidx];
+ MPASS(buf_tag != NULL);
+ MPASS(max_segs > 0);
+ } else {
+ buf_tag = txq->ift_buf_tag;
+ max_segs = scctx->isc_tx_nsegments;
+ map = txq->ift_sds.ifsd_map[pidx];
+ }
+ if ((sctx->isc_flags & IFLIB_NEED_ETHER_PAD) &&
+ __predict_false(m_head->m_pkthdr.len < scctx->isc_min_frame_size)) {
+ err = iflib_ether_pad(ctx->ifc_dev, m_headp, scctx->isc_min_frame_size);
+ if (err) {
+ DBG_COUNTER_INC(encap_txd_encap_fail);
+ return err;
+ }
+ }
+ m_head = *m_headp;
+
+ pkt_info_zero(&pi);
+ pi.ipi_mflags = (m_head->m_flags & (M_VLANTAG|M_BCAST|M_MCAST));
+ pi.ipi_pidx = pidx;
+ pi.ipi_qsidx = txq->ift_id;
+ pi.ipi_len = m_head->m_pkthdr.len;
+ pi.ipi_csum_flags = m_head->m_pkthdr.csum_flags;
+ pi.ipi_vtag = M_HAS_VLANTAG(m_head) ? m_head->m_pkthdr.ether_vtag : 0;
+
+ /* deliberate bitwise OR to make one condition */
+ if (__predict_true((pi.ipi_csum_flags | pi.ipi_vtag))) {
+ if (__predict_false((err = iflib_parse_header(txq, &pi, m_headp)) != 0)) {
+ DBG_COUNTER_INC(encap_txd_encap_fail);
+ return (err);
+ }
+ m_head = *m_headp;
+ }
+
+retry:
+ err = bus_dmamap_load_mbuf_sg(buf_tag, map, m_head, segs, &nsegs,
+ BUS_DMA_NOWAIT);
+defrag:
+ if (__predict_false(err)) {
+ switch (err) {
+ case EFBIG:
+ /* try collapse once and defrag once */
+ if (remap == 0) {
+ m_head = m_collapse(*m_headp, M_NOWAIT, max_segs);
+ /* try defrag if collapsing fails */
+ if (m_head == NULL)
+ remap++;
+ }
+ if (remap == 1) {
+ txq->ift_mbuf_defrag++;
+ m_head = m_defrag(*m_headp, M_NOWAIT);
+ }
+ /*
+ * remap should never be >1 unless bus_dmamap_load_mbuf_sg
+ * failed to map an mbuf that was run through m_defrag
+ */
+ MPASS(remap <= 1);
+ if (__predict_false(m_head == NULL || remap > 1))
+ goto defrag_failed;
+ remap++;
+ *m_headp = m_head;
+ goto retry;
+ break;
+ case ENOMEM:
+ txq->ift_no_tx_dma_setup++;
+ break;
+ default:
+ txq->ift_no_tx_dma_setup++;
+ m_freem(*m_headp);
+ DBG_COUNTER_INC(tx_frees);
+ *m_headp = NULL;
+ break;
+ }
+ txq->ift_map_failed++;
+ DBG_COUNTER_INC(encap_load_mbuf_fail);
+ DBG_COUNTER_INC(encap_txd_encap_fail);
+ return (err);
+ }
+ ifsd_m[pidx] = m_head;
+ /*
+ * XXX assumes a 1 to 1 relationship between segments and
+ * descriptors - this does not hold true on all drivers, e.g.
+ * cxgb
+ */
+ if (__predict_false(nsegs + 2 > TXQ_AVAIL(txq))) {
+ txq->ift_no_desc_avail++;
+ bus_dmamap_unload(buf_tag, map);
+ DBG_COUNTER_INC(encap_txq_avail_fail);
+ DBG_COUNTER_INC(encap_txd_encap_fail);
+ if ((txq->ift_task.gt_task.ta_flags & TASK_ENQUEUED) == 0)
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+ return (ENOBUFS);
+ }
+ /*
+ * On Intel cards we can greatly reduce the number of TX interrupts
+ * we see by only setting report status on every Nth descriptor.
+ * However, this also means that the driver will need to keep track
+ * of the descriptors that RS was set on to check them for the DD bit.
+ */
+ txq->ift_rs_pending += nsegs + 1;
+ if (txq->ift_rs_pending > TXQ_MAX_RS_DEFERRED(txq) ||
+ iflib_no_tx_batch || (TXQ_AVAIL(txq) - nsegs) <= MAX_TX_DESC(ctx) + 2) {
+ pi.ipi_flags |= IPI_TX_INTR;
+ txq->ift_rs_pending = 0;
+ }
+
+ pi.ipi_segs = segs;
+ pi.ipi_nsegs = nsegs;
+
+ MPASS(pidx >= 0 && pidx < txq->ift_size);
+#ifdef PKT_DEBUG
+ print_pkt(&pi);
+#endif
+ if ((err = ctx->isc_txd_encap(ctx->ifc_softc, &pi)) == 0) {
+ bus_dmamap_sync(buf_tag, map, BUS_DMASYNC_PREWRITE);
+ DBG_COUNTER_INC(tx_encap);
+ MPASS(pi.ipi_new_pidx < txq->ift_size);
+
+ ndesc = pi.ipi_new_pidx - pi.ipi_pidx;
+ if (pi.ipi_new_pidx < pi.ipi_pidx) {
+ ndesc += txq->ift_size;
+ txq->ift_gen = 1;
+ }
+ /*
+ * drivers can need as many as
+ * two sentinels
+ */
+ MPASS(ndesc <= pi.ipi_nsegs + 2);
+ MPASS(pi.ipi_new_pidx != pidx);
+ MPASS(ndesc > 0);
+ txq->ift_in_use += ndesc;
+
+ /*
+ * We update the last software descriptor again here because there may
+ * be a sentinel and/or there may be more mbufs than segments
+ */
+ txq->ift_pidx = pi.ipi_new_pidx;
+ txq->ift_npending += pi.ipi_ndescs;
+ } else {
+ *m_headp = m_head = iflib_remove_mbuf(txq);
+ if (err == EFBIG) {
+ txq->ift_txd_encap_efbig++;
+ if (remap < 2) {
+ remap = 1;
+ goto defrag;
+ }
+ }
+ goto defrag_failed;
+ }
+ /*
+ * err can't possibly be non-zero here, so we don't neet to test it
+ * to see if we need to DBG_COUNTER_INC(encap_txd_encap_fail).
+ */
+ return (err);
+
+defrag_failed:
+ txq->ift_mbuf_defrag_failed++;
+ txq->ift_map_failed++;
+ m_freem(*m_headp);
+ DBG_COUNTER_INC(tx_frees);
+ *m_headp = NULL;
+ DBG_COUNTER_INC(encap_txd_encap_fail);
+ return (ENOMEM);
+}
+
+static void
+iflib_tx_desc_free(iflib_txq_t txq, int n)
+{
+ uint32_t qsize, cidx, mask, gen;
+ struct mbuf *m, **ifsd_m;
+ bool do_prefetch;
+
+ cidx = txq->ift_cidx;
+ gen = txq->ift_gen;
+ qsize = txq->ift_size;
+ mask = qsize-1;
+ ifsd_m = txq->ift_sds.ifsd_m;
+ do_prefetch = (txq->ift_ctx->ifc_flags & IFC_PREFETCH);
+
+ while (n-- > 0) {
+ if (do_prefetch) {
+ prefetch(ifsd_m[(cidx + 3) & mask]);
+ prefetch(ifsd_m[(cidx + 4) & mask]);
+ }
+ if ((m = ifsd_m[cidx]) != NULL) {
+ prefetch(&ifsd_m[(cidx + CACHE_PTR_INCREMENT) & mask]);
+ if (m->m_pkthdr.csum_flags & CSUM_TSO) {
+ bus_dmamap_sync(txq->ift_tso_buf_tag,
+ txq->ift_sds.ifsd_tso_map[cidx],
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_tso_buf_tag,
+ txq->ift_sds.ifsd_tso_map[cidx]);
+ } else {
+ bus_dmamap_sync(txq->ift_buf_tag,
+ txq->ift_sds.ifsd_map[cidx],
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txq->ift_buf_tag,
+ txq->ift_sds.ifsd_map[cidx]);
+ }
+ /* XXX we don't support any drivers that batch packets yet */
+ MPASS(m->m_nextpkt == NULL);
+ m_freem(m);
+ ifsd_m[cidx] = NULL;
+#if MEMORY_LOGGING
+ txq->ift_dequeued++;
+#endif
+ DBG_COUNTER_INC(tx_frees);
+ }
+ if (__predict_false(++cidx == qsize)) {
+ cidx = 0;
+ gen = 0;
+ }
+ }
+ txq->ift_cidx = cidx;
+ txq->ift_gen = gen;
+}
+
+static __inline int
+iflib_completed_tx_reclaim(iflib_txq_t txq, int thresh)
+{
+ int reclaim;
+ if_ctx_t ctx = txq->ift_ctx;
+
+ KASSERT(thresh >= 0, ("invalid threshold to reclaim"));
+ MPASS(thresh /*+ MAX_TX_DESC(txq->ift_ctx) */ < txq->ift_size);
+
+ /*
+ * Need a rate-limiting check so that this isn't called every time
+ */
+ iflib_tx_credits_update(ctx, txq);
+ reclaim = DESC_RECLAIMABLE(txq);
+
+ if (reclaim <= thresh /* + MAX_TX_DESC(txq->ift_ctx) */) {
+#ifdef INVARIANTS
+ if (iflib_verbose_debug) {
+ printf("%s processed=%ju cleaned=%ju tx_nsegments=%d reclaim=%d thresh=%d\n", __FUNCTION__,
+ txq->ift_processed, txq->ift_cleaned, txq->ift_ctx->ifc_softc_ctx.isc_tx_nsegments,
+ reclaim, thresh);
+
+ }
+#endif
+ return (0);
+ }
+ iflib_tx_desc_free(txq, reclaim);
+ txq->ift_cleaned += reclaim;
+ txq->ift_in_use -= reclaim;
+
+ return (reclaim);
+}
+
+static struct mbuf **
+_ring_peek_one(struct ifmp_ring *r, int cidx, int offset, int remaining)
+{
+ int next, size;
+ struct mbuf **items;
+
+ size = r->size;
+ next = (cidx + CACHE_PTR_INCREMENT) & (size-1);
+ items = __DEVOLATILE(struct mbuf **, &r->items[0]);
+
+ prefetch(items[(cidx + offset) & (size-1)]);
+ if (remaining > 1) {
+ prefetch2cachelines(&items[next]);
+ prefetch2cachelines(items[(cidx + offset + 1) & (size-1)]);
+ prefetch2cachelines(items[(cidx + offset + 2) & (size-1)]);
+ prefetch2cachelines(items[(cidx + offset + 3) & (size-1)]);
+ }
+ return (__DEVOLATILE(struct mbuf **, &r->items[(cidx + offset) & (size-1)]));
+}
+
+static void
+iflib_txq_check_drain(iflib_txq_t txq, int budget)
+{
+
+ ifmp_ring_check_drainage(txq->ift_br, budget);
+}
+
+static uint32_t
+iflib_txq_can_drain(struct ifmp_ring *r)
+{
+ iflib_txq_t txq = r->cookie;
+ if_ctx_t ctx = txq->ift_ctx;
+
+ if (TXQ_AVAIL(txq) > MAX_TX_DESC(ctx) + 2)
+ return (1);
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD);
+ return (ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id,
+ false));
+}
+
+static uint32_t
+iflib_txq_drain(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx)
+{
+ iflib_txq_t txq = r->cookie;
+ if_ctx_t ctx = txq->ift_ctx;
+ if_t ifp = ctx->ifc_ifp;
+ struct mbuf *m, **mp;
+ int avail, bytes_sent, consumed, count, err, i, in_use_prev;
+ int mcast_sent, pkt_sent, reclaimed, txq_avail;
+ bool do_prefetch, rang, ring;
+
+ if (__predict_false(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING) ||
+ !LINK_ACTIVE(ctx))) {
+ DBG_COUNTER_INC(txq_drain_notready);
+ return (0);
+ }
+ reclaimed = iflib_completed_tx_reclaim(txq, RECLAIM_THRESH(ctx));
+ rang = iflib_txd_db_check(ctx, txq, reclaimed, txq->ift_in_use);
+ avail = IDXDIFF(pidx, cidx, r->size);
+ if (__predict_false(ctx->ifc_flags & IFC_QFLUSH)) {
+ DBG_COUNTER_INC(txq_drain_flushing);
+ for (i = 0; i < avail; i++) {
+ if (__predict_true(r->items[(cidx + i) & (r->size-1)] != (void *)txq))
+ m_free(r->items[(cidx + i) & (r->size-1)]);
+ r->items[(cidx + i) & (r->size-1)] = NULL;
+ }
+ return (avail);
+ }
+
+ if (__predict_false(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_OACTIVE)) {
+ txq->ift_qstatus = IFLIB_QUEUE_IDLE;
+ CALLOUT_LOCK(txq);
+ callout_stop(&txq->ift_timer);
+ CALLOUT_UNLOCK(txq);
+ DBG_COUNTER_INC(txq_drain_oactive);
+ return (0);
+ }
+ if (reclaimed)
+ txq->ift_qstatus = IFLIB_QUEUE_IDLE;
+ consumed = mcast_sent = bytes_sent = pkt_sent = 0;
+ count = MIN(avail, TX_BATCH_SIZE);
+#ifdef INVARIANTS
+ if (iflib_verbose_debug)
+ printf("%s avail=%d ifc_flags=%x txq_avail=%d ", __FUNCTION__,
+ avail, ctx->ifc_flags, TXQ_AVAIL(txq));
+#endif
+ do_prefetch = (ctx->ifc_flags & IFC_PREFETCH);
+ txq_avail = TXQ_AVAIL(txq);
+ err = 0;
+ for (i = 0; i < count && txq_avail > MAX_TX_DESC(ctx) + 2; i++) {
+ int rem = do_prefetch ? count - i : 0;
+
+ mp = _ring_peek_one(r, cidx, i, rem);
+ MPASS(mp != NULL && *mp != NULL);
+ if (__predict_false(*mp == (struct mbuf *)txq)) {
+ consumed++;
+ continue;
+ }
+ in_use_prev = txq->ift_in_use;
+ err = iflib_encap(txq, mp);
+ if (__predict_false(err)) {
+ /* no room - bail out */
+ if (err == ENOBUFS)
+ break;
+ consumed++;
+ /* we can't send this packet - skip it */
+ continue;
+ }
+ consumed++;
+ pkt_sent++;
+ m = *mp;
+ DBG_COUNTER_INC(tx_sent);
+ bytes_sent += m->m_pkthdr.len;
+ mcast_sent += !!(m->m_flags & M_MCAST);
+ txq_avail = TXQ_AVAIL(txq);
+
+ txq->ift_db_pending += (txq->ift_in_use - in_use_prev);
+ ETHER_BPF_MTAP(ifp, m);
+ if (__predict_false(!(ifp->if_drv_flags & IFF_DRV_RUNNING)))
+ break;
+ rang = iflib_txd_db_check(ctx, txq, false, in_use_prev);
+ }
+
+ /* deliberate use of bitwise or to avoid gratuitous short-circuit */
+ ring = rang ? false : (iflib_min_tx_latency | err) || (TXQ_AVAIL(txq) < MAX_TX_DESC(ctx));
+ iflib_txd_db_check(ctx, txq, ring, txq->ift_in_use);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, bytes_sent);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, pkt_sent);
+ if (mcast_sent)
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast_sent);
+#ifdef INVARIANTS
+ if (iflib_verbose_debug)
+ printf("consumed=%d\n", consumed);
+#endif
+ return (consumed);
+}
+
+static uint32_t
+iflib_txq_drain_always(struct ifmp_ring *r)
+{
+ return (1);
+}
+
+static uint32_t
+iflib_txq_drain_free(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx)
+{
+ int i, avail;
+ struct mbuf **mp;
+ iflib_txq_t txq;
+
+ txq = r->cookie;
+
+ txq->ift_qstatus = IFLIB_QUEUE_IDLE;
+ CALLOUT_LOCK(txq);
+ callout_stop(&txq->ift_timer);
+ CALLOUT_UNLOCK(txq);
+
+ avail = IDXDIFF(pidx, cidx, r->size);
+ for (i = 0; i < avail; i++) {
+ mp = _ring_peek_one(r, cidx, i, avail - i);
+ if (__predict_false(*mp == (struct mbuf *)txq))
+ continue;
+ m_freem(*mp);
+ DBG_COUNTER_INC(tx_frees);
+ }
+ MPASS(ifmp_ring_is_stalled(r) == 0);
+ return (avail);
+}
+
+static void
+iflib_ifmp_purge(iflib_txq_t txq)
+{
+ struct ifmp_ring *r;
+
+ r = txq->ift_br;
+ r->drain = iflib_txq_drain_free;
+ r->can_drain = iflib_txq_drain_always;
+
+ ifmp_ring_check_drainage(r, r->size);
+
+ r->drain = iflib_txq_drain;
+ r->can_drain = iflib_txq_can_drain;
+}
+
+static void
+_task_fn_tx(void *context)
+{
+ iflib_txq_t txq = context;
+ if_ctx_t ctx = txq->ift_ctx;
+#if defined(ALTQ) || defined(DEV_NETMAP)
+ if_t ifp = ctx->ifc_ifp;
+#endif
+ int abdicate = ctx->ifc_sysctl_tx_abdicate;
+
+#ifdef IFLIB_DIAGNOSTICS
+ txq->ift_cpu_exec_count[curcpu]++;
+#endif
+ if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING))
+ return;
+#ifdef DEV_NETMAP
+ if (if_getcapenable(ifp) & IFCAP_NETMAP) {
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD);
+ if (ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id, false))
+ netmap_tx_irq(ifp, txq->ift_id);
+ if (ctx->ifc_flags & IFC_LEGACY)
+ IFDI_INTR_ENABLE(ctx);
+ else
+ IFDI_TX_QUEUE_INTR_ENABLE(ctx, txq->ift_id);
+ return;
+ }
+#endif
+#ifdef ALTQ
+ if (ALTQ_IS_ENABLED(&ifp->if_snd))
+ iflib_altq_if_start(ifp);
+#endif
+ if (txq->ift_db_pending)
+ ifmp_ring_enqueue(txq->ift_br, (void **)&txq, 1, TX_BATCH_SIZE, abdicate);
+ else if (!abdicate)
+ ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE);
+ /*
+ * When abdicating, we always need to check drainage, not just when we don't enqueue
+ */
+ if (abdicate)
+ ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE);
+ if (ctx->ifc_flags & IFC_LEGACY)
+ IFDI_INTR_ENABLE(ctx);
+ else
+ IFDI_TX_QUEUE_INTR_ENABLE(ctx, txq->ift_id);
+}
+
+static void
+_task_fn_rx(void *context)
+{
+ iflib_rxq_t rxq = context;
+ if_ctx_t ctx = rxq->ifr_ctx;
+ bool more;
+ uint16_t budget;
+
+#ifdef IFLIB_DIAGNOSTICS
+ rxq->ifr_cpu_exec_count[curcpu]++;
+#endif
+ DBG_COUNTER_INC(task_fn_rxs);
+ if (__predict_false(!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)))
+ return;
+ more = true;
+#ifdef DEV_NETMAP
+ if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP) {
+ u_int work = 0;
+ if (netmap_rx_irq(ctx->ifc_ifp, rxq->ifr_id, &work)) {
+ more = false;
+ }
+ }
+#endif
+ budget = ctx->ifc_sysctl_rx_budget;
+ if (budget == 0)
+ budget = 16; /* XXX */
+ if (more == false || (more = iflib_rxeof(rxq, budget)) == false) {
+ if (ctx->ifc_flags & IFC_LEGACY)
+ IFDI_INTR_ENABLE(ctx);
+ else
+ IFDI_RX_QUEUE_INTR_ENABLE(ctx, rxq->ifr_id);
+ DBG_COUNTER_INC(rx_intr_enables);
+ }
+ if (__predict_false(!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)))
+ return;
+ if (more)
+ GROUPTASK_ENQUEUE(&rxq->ifr_task);
+}
+
+static void
+_task_fn_admin(void *context)
+{
+ if_ctx_t ctx = context;
+ if_softc_ctx_t sctx = &ctx->ifc_softc_ctx;
+ iflib_txq_t txq;
+ int i;
+ bool oactive, running, do_reset, do_watchdog, in_detach;
+ uint32_t reset_on = hz / 2;
+
+ STATE_LOCK(ctx);
+ running = (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING);
+ oactive = (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_OACTIVE);
+ do_reset = (ctx->ifc_flags & IFC_DO_RESET);
+ do_watchdog = (ctx->ifc_flags & IFC_DO_WATCHDOG);
+ in_detach = (ctx->ifc_flags & IFC_IN_DETACH);
+ ctx->ifc_flags &= ~(IFC_DO_RESET|IFC_DO_WATCHDOG);
+ STATE_UNLOCK(ctx);
+
+ if ((!running && !oactive) && !(ctx->ifc_sctx->isc_flags & IFLIB_ADMIN_ALWAYS_RUN))
+ return;
+ if (in_detach)
+ return;
+
+ CTX_LOCK(ctx);
+ for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++) {
+ CALLOUT_LOCK(txq);
+ callout_stop(&txq->ift_timer);
+ CALLOUT_UNLOCK(txq);
+ }
+ if (do_watchdog) {
+ ctx->ifc_watchdog_events++;
+ IFDI_WATCHDOG_RESET(ctx);
+ }
+ IFDI_UPDATE_ADMIN_STATUS(ctx);
+ for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++) {
+#ifdef DEV_NETMAP
+ reset_on = hz / 2;
+ if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP)
+ iflib_netmap_timer_adjust(ctx, txq, &reset_on);
+#endif
+ callout_reset_on(&txq->ift_timer, reset_on, iflib_timer, txq, txq->ift_timer.c_cpu);
+ }
+ IFDI_LINK_INTR_ENABLE(ctx);
+ if (do_reset)
+ iflib_if_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+
+ if (LINK_ACTIVE(ctx) == 0)
+ return;
+ for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++)
+ iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET);
+}
+
+
+static void
+_task_fn_iov(void *context)
+{
+ if_ctx_t ctx = context;
+
+ if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING) &&
+ !(ctx->ifc_sctx->isc_flags & IFLIB_ADMIN_ALWAYS_RUN))
+ return;
+
+ CTX_LOCK(ctx);
+ IFDI_VFLR_HANDLE(ctx);
+ CTX_UNLOCK(ctx);
+}
+
+static int
+iflib_sysctl_int_delay(SYSCTL_HANDLER_ARGS)
+{
+ int err;
+ if_int_delay_info_t info;
+ if_ctx_t ctx;
+
+ info = (if_int_delay_info_t)arg1;
+ ctx = info->iidi_ctx;
+ info->iidi_req = req;
+ info->iidi_oidp = oidp;
+ CTX_LOCK(ctx);
+ err = IFDI_SYSCTL_INT_DELAY(ctx, info);
+ CTX_UNLOCK(ctx);
+ return (err);
+}
+
+/*********************************************************************
+ *
+ * IFNET FUNCTIONS
+ *
+ **********************************************************************/
+
+static void
+iflib_if_init_locked(if_ctx_t ctx)
+{
+ iflib_stop(ctx);
+ iflib_init_locked(ctx);
+}
+
+
+static void
+iflib_if_init(void *arg)
+{
+ if_ctx_t ctx = arg;
+
+ CTX_LOCK(ctx);
+ iflib_if_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+}
+
+static int
+iflib_if_transmit(if_t ifp, struct mbuf *m)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+
+ iflib_txq_t txq;
+ int err, qidx;
+ int abdicate = ctx->ifc_sysctl_tx_abdicate;
+
+ if (__predict_false((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || !LINK_ACTIVE(ctx))) {
+ DBG_COUNTER_INC(tx_frees);
+ m_freem(m);
+ return (ENETDOWN);
+ }
+
+ MPASS(m->m_nextpkt == NULL);
+ /* ALTQ-enabled interfaces always use queue 0. */
+ qidx = 0;
+ if ((NTXQSETS(ctx) > 1) && M_HASHTYPE_GET(m) && !ALTQ_IS_ENABLED(&ifp->if_snd))
+ qidx = QIDX(ctx, m);
+ /*
+ * XXX calculate buf_ring based on flowid (divvy up bits?)
+ */
+ txq = &ctx->ifc_txqs[qidx];
+
+#ifdef DRIVER_BACKPRESSURE
+ if (txq->ift_closed) {
+ while (m != NULL) {
+ next = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ m_freem(m);
+ DBG_COUNTER_INC(tx_frees);
+ m = next;
+ }
+ return (ENOBUFS);
+ }
+#endif
+#ifdef notyet
+ qidx = count = 0;
+ mp = marr;
+ next = m;
+ do {
+ count++;
+ next = next->m_nextpkt;
+ } while (next != NULL);
+
+ if (count > nitems(marr))
+ if ((mp = malloc(count*sizeof(struct mbuf *), M_IFLIB, M_NOWAIT)) == NULL) {
+ /* XXX check nextpkt */
+ m_freem(m);
+ /* XXX simplify for now */
+ DBG_COUNTER_INC(tx_frees);
+ return (ENOBUFS);
+ }
+ for (next = m, i = 0; next != NULL; i++) {
+ mp[i] = next;
+ next = next->m_nextpkt;
+ mp[i]->m_nextpkt = NULL;
+ }
+#endif
+ DBG_COUNTER_INC(tx_seen);
+ err = ifmp_ring_enqueue(txq->ift_br, (void **)&m, 1, TX_BATCH_SIZE, abdicate);
+
+ if (abdicate)
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+ if (err) {
+ if (!abdicate)
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+ /* support forthcoming later */
+#ifdef DRIVER_BACKPRESSURE
+ txq->ift_closed = TRUE;
+#endif
+ ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE);
+ m_freem(m);
+ DBG_COUNTER_INC(tx_frees);
+ }
+
+ return (err);
+}
+
+#ifdef ALTQ
+/*
+ * The overall approach to integrating iflib with ALTQ is to continue to use
+ * the iflib mp_ring machinery between the ALTQ queue(s) and the hardware
+ * ring. Technically, when using ALTQ, queueing to an intermediate mp_ring
+ * is redundant/unnecessary, but doing so minimizes the amount of
+ * ALTQ-specific code required in iflib. It is assumed that the overhead of
+ * redundantly queueing to an intermediate mp_ring is swamped by the
+ * performance limitations inherent in using ALTQ.
+ *
+ * When ALTQ support is compiled in, all iflib drivers will use a transmit
+ * routine, iflib_altq_if_transmit(), that checks if ALTQ is enabled for the
+ * given interface. If ALTQ is enabled for an interface, then all
+ * transmitted packets for that interface will be submitted to the ALTQ
+ * subsystem via IFQ_ENQUEUE(). We don't use the legacy if_transmit()
+ * implementation because it uses IFQ_HANDOFF(), which will duplicatively
+ * update stats that the iflib machinery handles, and which is sensitve to
+ * the disused IFF_DRV_OACTIVE flag. Additionally, iflib_altq_if_start()
+ * will be installed as the start routine for use by ALTQ facilities that
+ * need to trigger queue drains on a scheduled basis.
+ *
+ */
+static void
+iflib_altq_if_start(if_t ifp)
+{
+ struct ifaltq *ifq = &ifp->if_snd;
+ struct mbuf *m;
+
+ IFQ_LOCK(ifq);
+ IFQ_DEQUEUE_NOLOCK(ifq, m);
+ while (m != NULL) {
+ iflib_if_transmit(ifp, m);
+ IFQ_DEQUEUE_NOLOCK(ifq, m);
+ }
+ IFQ_UNLOCK(ifq);
+}
+
+static int
+iflib_altq_if_transmit(if_t ifp, struct mbuf *m)
+{
+ int err;
+
+ if (ALTQ_IS_ENABLED(&ifp->if_snd)) {
+ IFQ_ENQUEUE(&ifp->if_snd, m, err);
+ if (err == 0)
+ iflib_altq_if_start(ifp);
+ } else
+ err = iflib_if_transmit(ifp, m);
+
+ return (err);
+}
+#endif /* ALTQ */
+
+static void
+iflib_if_qflush(if_t ifp)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+ iflib_txq_t txq = ctx->ifc_txqs;
+ int i;
+
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_QFLUSH;
+ STATE_UNLOCK(ctx);
+ for (i = 0; i < NTXQSETS(ctx); i++, txq++)
+ while (!(ifmp_ring_is_idle(txq->ift_br) || ifmp_ring_is_stalled(txq->ift_br)))
+ iflib_txq_check_drain(txq, 0);
+ STATE_LOCK(ctx);
+ ctx->ifc_flags &= ~IFC_QFLUSH;
+ STATE_UNLOCK(ctx);
+
+ /*
+ * When ALTQ is enabled, this will also take care of purging the
+ * ALTQ queue(s).
+ */
+ if_qflush(ifp);
+}
+
+
+#define IFCAP_FLAGS (IFCAP_HWCSUM_IPV6 | IFCAP_HWCSUM | IFCAP_LRO | \
+ IFCAP_TSO | IFCAP_VLAN_HWTAGGING | IFCAP_HWSTATS | \
+ IFCAP_VLAN_MTU | IFCAP_VLAN_HWFILTER | \
+ IFCAP_VLAN_HWTSO | IFCAP_VLAN_HWCSUM)
+
+static int
+iflib_if_ioctl(if_t ifp, u_long command, caddr_t data)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+ struct ifreq *ifr = (struct ifreq *)data;
+#if defined(INET) || defined(INET6)
+ struct ifaddr *ifa = (struct ifaddr *)data;
+#endif
+ bool avoid_reset = false;
+ int err = 0, reinit = 0, bits;
+
+ switch (command) {
+ case SIOCSIFADDR:
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ avoid_reset = true;
+#endif
+#ifdef INET6
+ if (ifa->ifa_addr->sa_family == AF_INET6)
+ avoid_reset = true;
+#endif
+ /*
+ ** Calling init results in link renegotiation,
+ ** so we avoid doing it when possible.
+ */
+ if (avoid_reset) {
+ if_setflagbits(ifp, IFF_UP,0);
+ if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
+ reinit = 1;
+#ifdef INET
+ if (!(if_getflags(ifp) & IFF_NOARP))
+ arp_ifinit(ifp, ifa);
+#endif
+ } else
+ err = ether_ioctl(ifp, command, data);
+ break;
+ case SIOCSIFMTU:
+ CTX_LOCK(ctx);
+ if (ifr->ifr_mtu == if_getmtu(ifp)) {
+ CTX_UNLOCK(ctx);
+ break;
+ }
+ bits = if_getdrvflags(ifp);
+ /* stop the driver and free any clusters before proceeding */
+ iflib_stop(ctx);
+
+ if ((err = IFDI_MTU_SET(ctx, ifr->ifr_mtu)) == 0) {
+ STATE_LOCK(ctx);
+ if (ifr->ifr_mtu > ctx->ifc_max_fl_buf_size)
+ ctx->ifc_flags |= IFC_MULTISEG;
+ else
+ ctx->ifc_flags &= ~IFC_MULTISEG;
+ STATE_UNLOCK(ctx);
+ err = if_setmtu(ifp, ifr->ifr_mtu);
+ }
+ iflib_init_locked(ctx);
+ STATE_LOCK(ctx);
+ if_setdrvflags(ifp, bits);
+ STATE_UNLOCK(ctx);
+ CTX_UNLOCK(ctx);
+ break;
+ case SIOCSIFFLAGS:
+ CTX_LOCK(ctx);
+ if (if_getflags(ifp) & IFF_UP) {
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
+ if ((if_getflags(ifp) ^ ctx->ifc_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI)) {
+ err = IFDI_PROMISC_SET(ctx, if_getflags(ifp));
+ }
+ } else
+ reinit = 1;
+ } else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
+ iflib_stop(ctx);
+ }
+ ctx->ifc_if_flags = if_getflags(ifp);
+ CTX_UNLOCK(ctx);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
+ CTX_LOCK(ctx);
+ IFDI_INTR_DISABLE(ctx);
+ IFDI_MULTI_SET(ctx);
+ IFDI_INTR_ENABLE(ctx);
+ CTX_UNLOCK(ctx);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ CTX_LOCK(ctx);
+ IFDI_MEDIA_SET(ctx);
+ CTX_UNLOCK(ctx);
+ /* FALLTHROUGH */
+ case SIOCGIFMEDIA:
+ case SIOCGIFXMEDIA:
+ err = ifmedia_ioctl(ifp, ifr, ctx->ifc_mediap, command);
+ break;
+ case SIOCGI2C:
+ {
+ struct ifi2creq i2c;
+
+ err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
+ if (err != 0)
+ break;
+ if (i2c.dev_addr != 0xA0 && i2c.dev_addr != 0xA2) {
+ err = EINVAL;
+ break;
+ }
+ if (i2c.len > sizeof(i2c.data)) {
+ err = EINVAL;
+ break;
+ }
+
+ if ((err = IFDI_I2C_REQ(ctx, &i2c)) == 0)
+ err = copyout(&i2c, ifr_data_get_ptr(ifr),
+ sizeof(i2c));
+ break;
+ }
+ case SIOCSIFCAP:
+ {
+ int mask, setmask, oldmask;
+
+ oldmask = if_getcapenable(ifp);
+ mask = ifr->ifr_reqcap ^ oldmask;
+ mask &= ctx->ifc_softc_ctx.isc_capabilities;
+ setmask = 0;
+#ifdef TCP_OFFLOAD
+ setmask |= mask & (IFCAP_TOE4|IFCAP_TOE6);
+#endif
+ setmask |= (mask & IFCAP_FLAGS);
+ setmask |= (mask & IFCAP_WOL);
+
+ /*
+ * If any RX csum has changed, change all the ones that
+ * are supported by the driver.
+ */
+ if (setmask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) {
+ setmask |= ctx->ifc_softc_ctx.isc_capabilities &
+ (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6);
+ }
+
+ /*
+ * want to ensure that traffic has stopped before we change any of the flags
+ */
+ if (setmask) {
+ CTX_LOCK(ctx);
+ bits = if_getdrvflags(ifp);
+ if (bits & IFF_DRV_RUNNING && setmask & ~IFCAP_WOL)
+ iflib_stop(ctx);
+ STATE_LOCK(ctx);
+ if_togglecapenable(ifp, setmask);
+ STATE_UNLOCK(ctx);
+ if (bits & IFF_DRV_RUNNING && setmask & ~IFCAP_WOL)
+ iflib_init_locked(ctx);
+ STATE_LOCK(ctx);
+ if_setdrvflags(ifp, bits);
+ STATE_UNLOCK(ctx);
+ CTX_UNLOCK(ctx);
+ }
+ if_vlancap(ifp);
+ break;
+ }
+ case SIOCGPRIVATE_0:
+ case SIOCSDRVSPEC:
+ case SIOCGDRVSPEC:
+ CTX_LOCK(ctx);
+ err = IFDI_PRIV_IOCTL(ctx, command, data);
+ CTX_UNLOCK(ctx);
+ break;
+ default:
+ err = ether_ioctl(ifp, command, data);
+ break;
+ }
+ if (reinit)
+ iflib_if_init(ctx);
+ return (err);
+}
+
+static uint64_t
+iflib_if_get_counter(if_t ifp, ift_counter cnt)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+
+ return (IFDI_GET_COUNTER(ctx, cnt));
+}
+
+/*********************************************************************
+ *
+ * OTHER FUNCTIONS EXPORTED TO THE STACK
+ *
+ **********************************************************************/
+
+static void
+iflib_vlan_register(void *arg, if_t ifp, uint16_t vtag)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+
+ if ((void *)ctx != arg)
+ return;
+
+ if ((vtag == 0) || (vtag > 4095))
+ return;
+
+ CTX_LOCK(ctx);
+ IFDI_VLAN_REGISTER(ctx, vtag);
+ /* Re-init to load the changes */
+ if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER)
+ iflib_if_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+}
+
+static void
+iflib_vlan_unregister(void *arg, if_t ifp, uint16_t vtag)
+{
+ if_ctx_t ctx = if_getsoftc(ifp);
+
+ if ((void *)ctx != arg)
+ return;
+
+ if ((vtag == 0) || (vtag > 4095))
+ return;
+
+ CTX_LOCK(ctx);
+ IFDI_VLAN_UNREGISTER(ctx, vtag);
+ /* Re-init to load the changes */
+ if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER)
+ iflib_if_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+}
+
+static void
+iflib_led_func(void *arg, int onoff)
+{
+ if_ctx_t ctx = arg;
+
+ CTX_LOCK(ctx);
+ IFDI_LED_FUNC(ctx, onoff);
+ CTX_UNLOCK(ctx);
+}
+
+/*********************************************************************
+ *
+ * BUS FUNCTION DEFINITIONS
+ *
+ **********************************************************************/
+
+int
+iflib_device_probe(device_t dev)
+{
+ const pci_vendor_info_t *ent;
+ if_shared_ctx_t sctx;
+ uint16_t pci_device_id, pci_rev_id, pci_subdevice_id, pci_subvendor_id;
+ uint16_t pci_vendor_id;
+
+ if ((sctx = DEVICE_REGISTER(dev)) == NULL || sctx->isc_magic != IFLIB_MAGIC)
+ return (ENOTSUP);
+
+ pci_vendor_id = pci_get_vendor(dev);
+ pci_device_id = pci_get_device(dev);
+ pci_subvendor_id = pci_get_subvendor(dev);
+ pci_subdevice_id = pci_get_subdevice(dev);
+ pci_rev_id = pci_get_revid(dev);
+ if (sctx->isc_parse_devinfo != NULL)
+ sctx->isc_parse_devinfo(&pci_device_id, &pci_subvendor_id, &pci_subdevice_id, &pci_rev_id);
+
+ ent = sctx->isc_vendor_info;
+ while (ent->pvi_vendor_id != 0) {
+ if (pci_vendor_id != ent->pvi_vendor_id) {
+ ent++;
+ continue;
+ }
+ if ((pci_device_id == ent->pvi_device_id) &&
+ ((pci_subvendor_id == ent->pvi_subvendor_id) ||
+ (ent->pvi_subvendor_id == 0)) &&
+ ((pci_subdevice_id == ent->pvi_subdevice_id) ||
+ (ent->pvi_subdevice_id == 0)) &&
+ ((pci_rev_id == ent->pvi_rev_id) ||
+ (ent->pvi_rev_id == 0))) {
+
+ device_set_desc_copy(dev, ent->pvi_name);
+ /* this needs to be changed to zero if the bus probing code
+ * ever stops re-probing on best match because the sctx
+ * may have its values over written by register calls
+ * in subsequent probes
+ */
+ return (BUS_PROBE_DEFAULT);
+ }
+ ent++;
+ }
+ return (ENXIO);
+}
+
+int
+iflib_device_probe_vendor(device_t dev)
+{
+ int probe;
+
+ probe = iflib_device_probe(dev);
+ if (probe == BUS_PROBE_DEFAULT)
+ return (BUS_PROBE_VENDOR);
+ else
+ return (probe);
+}
+
+static void
+iflib_reset_qvalues(if_ctx_t ctx)
+{
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ device_t dev = ctx->ifc_dev;
+ int i;
+
+ if (ctx->ifc_sysctl_ntxqs != 0)
+ scctx->isc_ntxqsets = ctx->ifc_sysctl_ntxqs;
+ if (ctx->ifc_sysctl_nrxqs != 0)
+ scctx->isc_nrxqsets = ctx->ifc_sysctl_nrxqs;
+
+ for (i = 0; i < sctx->isc_ntxqs; i++) {
+ if (ctx->ifc_sysctl_ntxds[i] != 0)
+ scctx->isc_ntxd[i] = ctx->ifc_sysctl_ntxds[i];
+ else
+ scctx->isc_ntxd[i] = sctx->isc_ntxd_default[i];
+ }
+
+ for (i = 0; i < sctx->isc_nrxqs; i++) {
+ if (ctx->ifc_sysctl_nrxds[i] != 0)
+ scctx->isc_nrxd[i] = ctx->ifc_sysctl_nrxds[i];
+ else
+ scctx->isc_nrxd[i] = sctx->isc_nrxd_default[i];
+ }
+
+ for (i = 0; i < sctx->isc_nrxqs; i++) {
+ if (scctx->isc_nrxd[i] < sctx->isc_nrxd_min[i]) {
+ device_printf(dev, "nrxd%d: %d less than nrxd_min %d - resetting to min\n",
+ i, scctx->isc_nrxd[i], sctx->isc_nrxd_min[i]);
+ scctx->isc_nrxd[i] = sctx->isc_nrxd_min[i];
+ }
+ if (scctx->isc_nrxd[i] > sctx->isc_nrxd_max[i]) {
+ device_printf(dev, "nrxd%d: %d greater than nrxd_max %d - resetting to max\n",
+ i, scctx->isc_nrxd[i], sctx->isc_nrxd_max[i]);
+ scctx->isc_nrxd[i] = sctx->isc_nrxd_max[i];
+ }
+ if (!powerof2(scctx->isc_nrxd[i])) {
+ device_printf(dev, "nrxd%d: %d is not a power of 2 - using default value of %d\n",
+ i, scctx->isc_nrxd[i], sctx->isc_nrxd_default[i]);
+ scctx->isc_nrxd[i] = sctx->isc_nrxd_default[i];
+ }
+ }
+
+ for (i = 0; i < sctx->isc_ntxqs; i++) {
+ if (scctx->isc_ntxd[i] < sctx->isc_ntxd_min[i]) {
+ device_printf(dev, "ntxd%d: %d less than ntxd_min %d - resetting to min\n",
+ i, scctx->isc_ntxd[i], sctx->isc_ntxd_min[i]);
+ scctx->isc_ntxd[i] = sctx->isc_ntxd_min[i];
+ }
+ if (scctx->isc_ntxd[i] > sctx->isc_ntxd_max[i]) {
+ device_printf(dev, "ntxd%d: %d greater than ntxd_max %d - resetting to max\n",
+ i, scctx->isc_ntxd[i], sctx->isc_ntxd_max[i]);
+ scctx->isc_ntxd[i] = sctx->isc_ntxd_max[i];
+ }
+ if (!powerof2(scctx->isc_ntxd[i])) {
+ device_printf(dev, "ntxd%d: %d is not a power of 2 - using default value of %d\n",
+ i, scctx->isc_ntxd[i], sctx->isc_ntxd_default[i]);
+ scctx->isc_ntxd[i] = sctx->isc_ntxd_default[i];
+ }
+ }
+}
+
+static void
+iflib_add_pfil(if_ctx_t ctx)
+{
+ struct pfil_head *pfil;
+ struct pfil_head_args pa;
+ iflib_rxq_t rxq;
+ int i;
+
+ pa.pa_version = PFIL_VERSION;
+ pa.pa_flags = PFIL_IN;
+ pa.pa_type = PFIL_TYPE_ETHERNET;
+ pa.pa_headname = ctx->ifc_ifp->if_xname;
+ pfil = pfil_head_register(&pa);
+
+ for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) {
+ rxq->pfil = pfil;
+ }
+}
+
+static void
+iflib_rem_pfil(if_ctx_t ctx)
+{
+ struct pfil_head *pfil;
+ iflib_rxq_t rxq;
+ int i;
+
+ rxq = ctx->ifc_rxqs;
+ pfil = rxq->pfil;
+ for (i = 0; i < NRXQSETS(ctx); i++, rxq++) {
+ rxq->pfil = NULL;
+ }
+ pfil_head_unregister(pfil);
+}
+
+static uint16_t
+get_ctx_core_offset(if_ctx_t ctx)
+{
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ struct cpu_offset *op;
+ uint16_t qc;
+ uint16_t ret = ctx->ifc_sysctl_core_offset;
+
+ if (ret != CORE_OFFSET_UNSPECIFIED)
+ return (ret);
+
+ if (ctx->ifc_sysctl_separate_txrx)
+ qc = scctx->isc_ntxqsets + scctx->isc_nrxqsets;
+ else
+ qc = max(scctx->isc_ntxqsets, scctx->isc_nrxqsets);
+
+ mtx_lock(&cpu_offset_mtx);
+#ifndef __rtems__
+ SLIST_FOREACH(op, &cpu_offsets, entries) {
+ if (CPU_CMP(&ctx->ifc_cpus, &op->set) == 0) {
+ ret = op->offset;
+ op->offset += qc;
+ MPASS(op->refcount < UINT_MAX);
+ op->refcount++;
+ break;
+ }
+ }
+#endif /* __rtems__ */
+ if (ret == CORE_OFFSET_UNSPECIFIED) {
+ ret = 0;
+ op = malloc(sizeof(struct cpu_offset), M_IFLIB,
+ M_NOWAIT | M_ZERO);
+ if (op == NULL) {
+ device_printf(ctx->ifc_dev,
+ "allocation for cpu offset failed.\n");
+ } else {
+ op->offset = qc;
+ op->refcount = 1;
+#ifndef __rtems__
+ CPU_COPY(&ctx->ifc_cpus, &op->set);
+#endif /* __rtems__ */
+ SLIST_INSERT_HEAD(&cpu_offsets, op, entries);
+ }
+ }
+ mtx_unlock(&cpu_offset_mtx);
+
+ return (ret);
+}
+
+static void
+unref_ctx_core_offset(if_ctx_t ctx)
+{
+ struct cpu_offset *op, *top;
+
+ mtx_lock(&cpu_offset_mtx);
+ SLIST_FOREACH_SAFE(op, &cpu_offsets, entries, top) {
+#ifndef __rtems__
+ if (CPU_CMP(&ctx->ifc_cpus, &op->set) == 0) {
+#else /* __rtems__ */
+ {
+#endif /* __rtems__ */
+ MPASS(op->refcount > 0);
+ op->refcount--;
+ if (op->refcount == 0) {
+ SLIST_REMOVE(&cpu_offsets, op, cpu_offset, entries);
+ free(op, M_IFLIB);
+ }
+ break;
+ }
+ }
+ mtx_unlock(&cpu_offset_mtx);
+}
+
+int
+iflib_device_register(device_t dev, void *sc, if_shared_ctx_t sctx, if_ctx_t *ctxp)
+{
+ if_ctx_t ctx;
+ if_t ifp;
+ if_softc_ctx_t scctx;
+ kobjop_desc_t kobj_desc;
+ kobj_method_t *kobj_method;
+ int err, msix, rid;
+ uint16_t main_rxq, main_txq;
+
+ ctx = malloc(sizeof(* ctx), M_IFLIB, M_WAITOK|M_ZERO);
+
+ if (sc == NULL) {
+ sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK|M_ZERO);
+ device_set_softc(dev, ctx);
+ ctx->ifc_flags |= IFC_SC_ALLOCATED;
+ }
+
+ ctx->ifc_sctx = sctx;
+ ctx->ifc_dev = dev;
+ ctx->ifc_softc = sc;
+
+ if ((err = iflib_register(ctx)) != 0) {
+ device_printf(dev, "iflib_register failed %d\n", err);
+ goto fail_ctx_free;
+ }
+ iflib_add_device_sysctl_pre(ctx);
+
+ scctx = &ctx->ifc_softc_ctx;
+ ifp = ctx->ifc_ifp;
+
+ iflib_reset_qvalues(ctx);
+ CTX_LOCK(ctx);
+ if ((err = IFDI_ATTACH_PRE(ctx)) != 0) {
+ device_printf(dev, "IFDI_ATTACH_PRE failed %d\n", err);
+ goto fail_unlock;
+ }
+ _iflib_pre_assert(scctx);
+ ctx->ifc_txrx = *scctx->isc_txrx;
+
+ if (sctx->isc_flags & IFLIB_DRIVER_MEDIA)
+ ctx->ifc_mediap = scctx->isc_media;
+
+#ifdef INVARIANTS
+ if (scctx->isc_capabilities & IFCAP_TXCSUM)
+ MPASS(scctx->isc_tx_csum_flags);
+#endif
+
+ if_setcapabilities(ifp, scctx->isc_capabilities | IFCAP_HWSTATS);
+ if_setcapenable(ifp, scctx->isc_capenable | IFCAP_HWSTATS);
+
+ if (scctx->isc_ntxqsets == 0 || (scctx->isc_ntxqsets_max && scctx->isc_ntxqsets_max < scctx->isc_ntxqsets))
+ scctx->isc_ntxqsets = scctx->isc_ntxqsets_max;
+ if (scctx->isc_nrxqsets == 0 || (scctx->isc_nrxqsets_max && scctx->isc_nrxqsets_max < scctx->isc_nrxqsets))
+ scctx->isc_nrxqsets = scctx->isc_nrxqsets_max;
+
+ main_txq = (sctx->isc_flags & IFLIB_HAS_TXCQ) ? 1 : 0;
+ main_rxq = (sctx->isc_flags & IFLIB_HAS_RXCQ) ? 1 : 0;
+
+ /* XXX change for per-queue sizes */
+ device_printf(dev, "Using %d TX descriptors and %d RX descriptors\n",
+ scctx->isc_ntxd[main_txq], scctx->isc_nrxd[main_rxq]);
+
+ if (scctx->isc_tx_nsegments > scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION)
+ scctx->isc_tx_nsegments = max(1, scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION);
+ if (scctx->isc_tx_tso_segments_max > scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION)
+ scctx->isc_tx_tso_segments_max = max(1,
+ scctx->isc_ntxd[main_txq] / MAX_SINGLE_PACKET_FRACTION);
+
+ /* TSO parameters - dig these out of the data sheet - simply correspond to tag setup */
+ if (if_getcapabilities(ifp) & IFCAP_TSO) {
+ /*
+ * The stack can't handle a TSO size larger than IP_MAXPACKET,
+ * but some MACs do.
+ */
+ if_sethwtsomax(ifp, min(scctx->isc_tx_tso_size_max,
+ IP_MAXPACKET));
+ /*
+ * Take maximum number of m_pullup(9)'s in iflib_parse_header()
+ * into account. In the worst case, each of these calls will
+ * add another mbuf and, thus, the requirement for another DMA
+ * segment. So for best performance, it doesn't make sense to
+ * advertize a maximum of TSO segments that typically will
+ * require defragmentation in iflib_encap().
+ */
+ if_sethwtsomaxsegcount(ifp, scctx->isc_tx_tso_segments_max - 3);
+ if_sethwtsomaxsegsize(ifp, scctx->isc_tx_tso_segsize_max);
+ }
+ if (scctx->isc_rss_table_size == 0)
+ scctx->isc_rss_table_size = 64;
+ scctx->isc_rss_table_mask = scctx->isc_rss_table_size-1;
+
+ GROUPTASK_INIT(&ctx->ifc_admin_task, 0, _task_fn_admin, ctx);
+ /* XXX format name */
+ taskqgroup_attach(qgroup_if_config_tqg, &ctx->ifc_admin_task, ctx,
+ NULL, NULL, "admin");
+
+#ifndef __rtems__
+ /* Set up cpu set. If it fails, use the set of all CPUs. */
+ if (bus_get_cpus(dev, INTR_CPUS, sizeof(ctx->ifc_cpus), &ctx->ifc_cpus) != 0) {
+ device_printf(dev, "Unable to fetch CPU list\n");
+ CPU_COPY(&all_cpus, &ctx->ifc_cpus);
+ }
+ MPASS(CPU_COUNT(&ctx->ifc_cpus) > 0);
+#endif /* __rtems__ */
+
+ /*
+ ** Now set up MSI or MSI-X, should return us the number of supported
+ ** vectors (will be 1 for a legacy interrupt and MSI).
+ */
+ if (sctx->isc_flags & IFLIB_SKIP_MSIX) {
+ msix = scctx->isc_vectors;
+ } else if (scctx->isc_msix_bar != 0)
+ /*
+ * The simple fact that isc_msix_bar is not 0 does not mean we
+ * we have a good value there that is known to work.
+ */
+ msix = iflib_msix_init(ctx);
+ else {
+ scctx->isc_vectors = 1;
+ scctx->isc_ntxqsets = 1;
+ scctx->isc_nrxqsets = 1;
+ scctx->isc_intr = IFLIB_INTR_LEGACY;
+ msix = 0;
+ }
+ /* Get memory for the station queues */
+ if ((err = iflib_queues_alloc(ctx))) {
+ device_printf(dev, "Unable to allocate queue memory\n");
+ goto fail_intr_free;
+ }
+
+ if ((err = iflib_qset_structures_setup(ctx)))
+ goto fail_queues;
+
+ /*
+ * Now that we know how many queues there are, get the core offset.
+ */
+ ctx->ifc_sysctl_core_offset = get_ctx_core_offset(ctx);
+
+ /*
+ * Group taskqueues aren't properly set up until SMP is started,
+ * so we disable interrupts until we can handle them post
+ * SI_SUB_SMP.
+ *
+ * XXX: disabling interrupts doesn't actually work, at least for
+ * the non-MSI case. When they occur before SI_SUB_SMP completes,
+ * we do null handling and depend on this not causing too large an
+ * interrupt storm.
+ */
+ IFDI_INTR_DISABLE(ctx);
+
+ if (msix > 1) {
+ /*
+ * When using MSI-X, ensure that ifdi_{r,t}x_queue_intr_enable
+ * aren't the default NULL implementation.
+ */
+ kobj_desc = &ifdi_rx_queue_intr_enable_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)ctx)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt) {
+ device_printf(dev,
+ "MSI-X requires ifdi_rx_queue_intr_enable method");
+ err = EOPNOTSUPP;
+ goto fail_queues;
+ }
+ kobj_desc = &ifdi_tx_queue_intr_enable_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)ctx)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt) {
+ device_printf(dev,
+ "MSI-X requires ifdi_tx_queue_intr_enable method");
+ err = EOPNOTSUPP;
+ goto fail_queues;
+ }
+
+ /*
+ * Assign the MSI-X vectors.
+ * Note that the default NULL ifdi_msix_intr_assign method will
+ * fail here, too.
+ */
+ err = IFDI_MSIX_INTR_ASSIGN(ctx, msix);
+ if (err != 0) {
+ device_printf(dev, "IFDI_MSIX_INTR_ASSIGN failed %d\n",
+ err);
+ goto fail_queues;
+ }
+ } else if (scctx->isc_intr != IFLIB_INTR_MSIX) {
+ rid = 0;
+ if (scctx->isc_intr == IFLIB_INTR_MSI) {
+ MPASS(msix == 1);
+ rid = 1;
+ }
+ if ((err = iflib_legacy_setup(ctx, ctx->isc_legacy_intr, ctx->ifc_softc, &rid, "irq0")) != 0) {
+ device_printf(dev, "iflib_legacy_setup failed %d\n", err);
+ goto fail_queues;
+ }
+ } else {
+ device_printf(dev,
+ "Cannot use iflib with only 1 MSI-X interrupt!\n");
+ err = ENODEV;
+ goto fail_intr_free;
+ }
+
+ ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet);
+
+ if ((err = IFDI_ATTACH_POST(ctx)) != 0) {
+ device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err);
+ goto fail_detach;
+ }
+
+ /*
+ * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported.
+ * This must appear after the call to ether_ifattach() because
+ * ether_ifattach() sets if_hdrlen to the default value.
+ */
+ if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU)
+ if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
+
+ if ((err = iflib_netmap_attach(ctx))) {
+ device_printf(ctx->ifc_dev, "netmap attach failed: %d\n", err);
+ goto fail_detach;
+ }
+ *ctxp = ctx;
+
+ NETDUMP_SET(ctx->ifc_ifp, iflib);
+
+ if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter);
+ iflib_add_device_sysctl_post(ctx);
+ iflib_add_pfil(ctx);
+ ctx->ifc_flags |= IFC_INIT_DONE;
+ CTX_UNLOCK(ctx);
+
+ return (0);
+
+fail_detach:
+ ether_ifdetach(ctx->ifc_ifp);
+fail_intr_free:
+ iflib_free_intr_mem(ctx);
+fail_queues:
+ iflib_tx_structures_free(ctx);
+ iflib_rx_structures_free(ctx);
+ taskqgroup_detach(qgroup_if_config_tqg, &ctx->ifc_admin_task);
+ IFDI_DETACH(ctx);
+fail_unlock:
+ CTX_UNLOCK(ctx);
+ iflib_deregister(ctx);
+fail_ctx_free:
+ device_set_softc(ctx->ifc_dev, NULL);
+ if (ctx->ifc_flags & IFC_SC_ALLOCATED)
+ free(ctx->ifc_softc, M_IFLIB);
+ free(ctx, M_IFLIB);
+ return (err);
+}
+
+int
+iflib_pseudo_register(device_t dev, if_shared_ctx_t sctx, if_ctx_t *ctxp,
+ struct iflib_cloneattach_ctx *clctx)
+{
+ int err;
+ if_ctx_t ctx;
+ if_t ifp;
+ if_softc_ctx_t scctx;
+ int i;
+ void *sc;
+ uint16_t main_txq;
+ uint16_t main_rxq;
+
+ ctx = malloc(sizeof(*ctx), M_IFLIB, M_WAITOK|M_ZERO);
+ sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK|M_ZERO);
+ ctx->ifc_flags |= IFC_SC_ALLOCATED;
+ if (sctx->isc_flags & (IFLIB_PSEUDO|IFLIB_VIRTUAL))
+ ctx->ifc_flags |= IFC_PSEUDO;
+
+ ctx->ifc_sctx = sctx;
+ ctx->ifc_softc = sc;
+ ctx->ifc_dev = dev;
+
+ if ((err = iflib_register(ctx)) != 0) {
+ device_printf(dev, "%s: iflib_register failed %d\n", __func__, err);
+ goto fail_ctx_free;
+ }
+ iflib_add_device_sysctl_pre(ctx);
+
+ scctx = &ctx->ifc_softc_ctx;
+ ifp = ctx->ifc_ifp;
+
+ iflib_reset_qvalues(ctx);
+ CTX_LOCK(ctx);
+ if ((err = IFDI_ATTACH_PRE(ctx)) != 0) {
+ device_printf(dev, "IFDI_ATTACH_PRE failed %d\n", err);
+ goto fail_unlock;
+ }
+ if (sctx->isc_flags & IFLIB_GEN_MAC)
+ ether_gen_addr(ifp, &ctx->ifc_mac);
+ if ((err = IFDI_CLONEATTACH(ctx, clctx->cc_ifc, clctx->cc_name,
+ clctx->cc_params)) != 0) {
+ device_printf(dev, "IFDI_CLONEATTACH failed %d\n", err);
+ goto fail_ctx_free;
+ }
+ ifmedia_add(ctx->ifc_mediap, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(ctx->ifc_mediap, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(ctx->ifc_mediap, IFM_ETHER | IFM_AUTO);
+
+#ifdef INVARIANTS
+ if (scctx->isc_capabilities & IFCAP_TXCSUM)
+ MPASS(scctx->isc_tx_csum_flags);
+#endif
+
+ if_setcapabilities(ifp, scctx->isc_capabilities | IFCAP_HWSTATS | IFCAP_LINKSTATE);
+ if_setcapenable(ifp, scctx->isc_capenable | IFCAP_HWSTATS | IFCAP_LINKSTATE);
+
+ ifp->if_flags |= IFF_NOGROUP;
+ if (sctx->isc_flags & IFLIB_PSEUDO) {
+ ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet);
+
+ if ((err = IFDI_ATTACH_POST(ctx)) != 0) {
+ device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err);
+ goto fail_detach;
+ }
+ *ctxp = ctx;
+
+ /*
+ * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported.
+ * This must appear after the call to ether_ifattach() because
+ * ether_ifattach() sets if_hdrlen to the default value.
+ */
+ if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU)
+ if_setifheaderlen(ifp,
+ sizeof(struct ether_vlan_header));
+
+ if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter);
+ iflib_add_device_sysctl_post(ctx);
+ ctx->ifc_flags |= IFC_INIT_DONE;
+ return (0);
+ }
+ _iflib_pre_assert(scctx);
+ ctx->ifc_txrx = *scctx->isc_txrx;
+
+ if (scctx->isc_ntxqsets == 0 || (scctx->isc_ntxqsets_max && scctx->isc_ntxqsets_max < scctx->isc_ntxqsets))
+ scctx->isc_ntxqsets = scctx->isc_ntxqsets_max;
+ if (scctx->isc_nrxqsets == 0 || (scctx->isc_nrxqsets_max && scctx->isc_nrxqsets_max < scctx->isc_nrxqsets))
+ scctx->isc_nrxqsets = scctx->isc_nrxqsets_max;
+
+ main_txq = (sctx->isc_flags & IFLIB_HAS_TXCQ) ? 1 : 0;
+ main_rxq = (sctx->isc_flags & IFLIB_HAS_RXCQ) ? 1 : 0;
+
+ /* XXX change for per-queue sizes */
+ device_printf(dev, "Using %d TX descriptors and %d RX descriptors\n",
+ scctx->isc_ntxd[main_txq], scctx->isc_nrxd[main_rxq]);
+
+ if (scctx->isc_tx_nsegments > scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION)
+ scctx->isc_tx_nsegments = max(1, scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION);
+ if (scctx->isc_tx_tso_segments_max > scctx->isc_ntxd[main_txq] /
+ MAX_SINGLE_PACKET_FRACTION)
+ scctx->isc_tx_tso_segments_max = max(1,
+ scctx->isc_ntxd[main_txq] / MAX_SINGLE_PACKET_FRACTION);
+
+ /* TSO parameters - dig these out of the data sheet - simply correspond to tag setup */
+ if (if_getcapabilities(ifp) & IFCAP_TSO) {
+ /*
+ * The stack can't handle a TSO size larger than IP_MAXPACKET,
+ * but some MACs do.
+ */
+ if_sethwtsomax(ifp, min(scctx->isc_tx_tso_size_max,
+ IP_MAXPACKET));
+ /*
+ * Take maximum number of m_pullup(9)'s in iflib_parse_header()
+ * into account. In the worst case, each of these calls will
+ * add another mbuf and, thus, the requirement for another DMA
+ * segment. So for best performance, it doesn't make sense to
+ * advertize a maximum of TSO segments that typically will
+ * require defragmentation in iflib_encap().
+ */
+ if_sethwtsomaxsegcount(ifp, scctx->isc_tx_tso_segments_max - 3);
+ if_sethwtsomaxsegsize(ifp, scctx->isc_tx_tso_segsize_max);
+ }
+ if (scctx->isc_rss_table_size == 0)
+ scctx->isc_rss_table_size = 64;
+ scctx->isc_rss_table_mask = scctx->isc_rss_table_size-1;
+
+ GROUPTASK_INIT(&ctx->ifc_admin_task, 0, _task_fn_admin, ctx);
+ /* XXX format name */
+ taskqgroup_attach(qgroup_if_config_tqg, &ctx->ifc_admin_task, ctx,
+ NULL, NULL, "admin");
+
+ /* XXX --- can support > 1 -- but keep it simple for now */
+ scctx->isc_intr = IFLIB_INTR_LEGACY;
+
+ /* Get memory for the station queues */
+ if ((err = iflib_queues_alloc(ctx))) {
+ device_printf(dev, "Unable to allocate queue memory\n");
+ goto fail_iflib_detach;
+ }
+
+ if ((err = iflib_qset_structures_setup(ctx))) {
+ device_printf(dev, "qset structure setup failed %d\n", err);
+ goto fail_queues;
+ }
+
+ /*
+ * XXX What if anything do we want to do about interrupts?
+ */
+ ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet);
+ if ((err = IFDI_ATTACH_POST(ctx)) != 0) {
+ device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err);
+ goto fail_detach;
+ }
+
+ /*
+ * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported.
+ * This must appear after the call to ether_ifattach() because
+ * ether_ifattach() sets if_hdrlen to the default value.
+ */
+ if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU)
+ if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
+
+ /* XXX handle more than one queue */
+ for (i = 0; i < scctx->isc_nrxqsets; i++)
+ IFDI_RX_CLSET(ctx, 0, i, ctx->ifc_rxqs[i].ifr_fl[0].ifl_sds.ifsd_cl);
+
+ *ctxp = ctx;
+
+ if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter);
+ iflib_add_device_sysctl_post(ctx);
+ ctx->ifc_flags |= IFC_INIT_DONE;
+ CTX_UNLOCK(ctx);
+
+ return (0);
+fail_detach:
+ ether_ifdetach(ctx->ifc_ifp);
+fail_queues:
+ iflib_tx_structures_free(ctx);
+ iflib_rx_structures_free(ctx);
+fail_iflib_detach:
+ IFDI_DETACH(ctx);
+fail_unlock:
+ CTX_UNLOCK(ctx);
+ iflib_deregister(ctx);
+fail_ctx_free:
+ free(ctx->ifc_softc, M_IFLIB);
+ free(ctx, M_IFLIB);
+ return (err);
+}
+
+int
+iflib_pseudo_deregister(if_ctx_t ctx)
+{
+ if_t ifp = ctx->ifc_ifp;
+ iflib_txq_t txq;
+ iflib_rxq_t rxq;
+ int i, j;
+ struct taskqgroup *tqg;
+ iflib_fl_t fl;
+
+ ether_ifdetach(ifp);
+ /* XXX drain any dependent tasks */
+ tqg = qgroup_if_io_tqg;
+ for (txq = ctx->ifc_txqs, i = 0; i < NTXQSETS(ctx); i++, txq++) {
+ callout_drain(&txq->ift_timer);
+ if (txq->ift_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &txq->ift_task);
+ }
+ for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) {
+ if (rxq->ifr_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &rxq->ifr_task);
+
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++)
+ free(fl->ifl_rx_bitmap, M_IFLIB);
+ }
+ tqg = qgroup_if_config_tqg;
+ if (ctx->ifc_admin_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &ctx->ifc_admin_task);
+ if (ctx->ifc_vflr_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &ctx->ifc_vflr_task);
+
+ iflib_tx_structures_free(ctx);
+ iflib_rx_structures_free(ctx);
+
+ iflib_deregister(ctx);
+
+ if (ctx->ifc_flags & IFC_SC_ALLOCATED)
+ free(ctx->ifc_softc, M_IFLIB);
+ free(ctx, M_IFLIB);
+ return (0);
+}
+
+int
+iflib_device_attach(device_t dev)
+{
+ if_ctx_t ctx;
+ if_shared_ctx_t sctx;
+
+ if ((sctx = DEVICE_REGISTER(dev)) == NULL || sctx->isc_magic != IFLIB_MAGIC)
+ return (ENOTSUP);
+
+ pci_enable_busmaster(dev);
+
+ return (iflib_device_register(dev, NULL, sctx, &ctx));
+}
+
+int
+iflib_device_deregister(if_ctx_t ctx)
+{
+ if_t ifp = ctx->ifc_ifp;
+ iflib_txq_t txq;
+ iflib_rxq_t rxq;
+ device_t dev = ctx->ifc_dev;
+ int i, j;
+ struct taskqgroup *tqg;
+ iflib_fl_t fl;
+
+ /* Make sure VLANS are not using driver */
+ if (if_vlantrunkinuse(ifp)) {
+ device_printf(dev, "Vlan in use, detach first\n");
+ return (EBUSY);
+ }
+#ifdef PCI_IOV
+ if (!CTX_IS_VF(ctx) && pci_iov_detach(dev) != 0) {
+ device_printf(dev, "SR-IOV in use; detach first.\n");
+ return (EBUSY);
+ }
+#endif
+
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_IN_DETACH;
+ STATE_UNLOCK(ctx);
+
+ CTX_LOCK(ctx);
+ iflib_stop(ctx);
+ CTX_UNLOCK(ctx);
+
+ /* Unregister VLAN events */
+ if (ctx->ifc_vlan_attach_event != NULL)
+ EVENTHANDLER_DEREGISTER(vlan_config, ctx->ifc_vlan_attach_event);
+ if (ctx->ifc_vlan_detach_event != NULL)
+ EVENTHANDLER_DEREGISTER(vlan_unconfig, ctx->ifc_vlan_detach_event);
+
+ iflib_netmap_detach(ifp);
+ ether_ifdetach(ifp);
+ iflib_rem_pfil(ctx);
+ if (ctx->ifc_led_dev != NULL)
+ led_destroy(ctx->ifc_led_dev);
+ /* XXX drain any dependent tasks */
+ tqg = qgroup_if_io_tqg;
+ for (txq = ctx->ifc_txqs, i = 0; i < NTXQSETS(ctx); i++, txq++) {
+ callout_drain(&txq->ift_timer);
+ if (txq->ift_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &txq->ift_task);
+ }
+ for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) {
+ if (rxq->ifr_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &rxq->ifr_task);
+
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++)
+ free(fl->ifl_rx_bitmap, M_IFLIB);
+ }
+ tqg = qgroup_if_config_tqg;
+ if (ctx->ifc_admin_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &ctx->ifc_admin_task);
+ if (ctx->ifc_vflr_task.gt_uniq != NULL)
+ taskqgroup_detach(tqg, &ctx->ifc_vflr_task);
+ CTX_LOCK(ctx);
+ IFDI_DETACH(ctx);
+ CTX_UNLOCK(ctx);
+
+ /* ether_ifdetach calls if_qflush - lock must be destroy afterwards*/
+ iflib_free_intr_mem(ctx);
+
+ bus_generic_detach(dev);
+
+ iflib_tx_structures_free(ctx);
+ iflib_rx_structures_free(ctx);
+
+ iflib_deregister(ctx);
+
+ device_set_softc(ctx->ifc_dev, NULL);
+ if (ctx->ifc_flags & IFC_SC_ALLOCATED)
+ free(ctx->ifc_softc, M_IFLIB);
+ unref_ctx_core_offset(ctx);
+ free(ctx, M_IFLIB);
+ return (0);
+}
+
+static void
+iflib_free_intr_mem(if_ctx_t ctx)
+{
+
+ if (ctx->ifc_softc_ctx.isc_intr != IFLIB_INTR_MSIX) {
+ iflib_irq_free(ctx, &ctx->ifc_legacy_irq);
+ }
+ if (ctx->ifc_softc_ctx.isc_intr != IFLIB_INTR_LEGACY) {
+ pci_release_msi(ctx->ifc_dev);
+ }
+ if (ctx->ifc_msix_mem != NULL) {
+ bus_release_resource(ctx->ifc_dev, SYS_RES_MEMORY,
+ rman_get_rid(ctx->ifc_msix_mem), ctx->ifc_msix_mem);
+ ctx->ifc_msix_mem = NULL;
+ }
+}
+
+int
+iflib_device_detach(device_t dev)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+
+ return (iflib_device_deregister(ctx));
+}
+
+int
+iflib_device_suspend(device_t dev)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+
+ CTX_LOCK(ctx);
+ IFDI_SUSPEND(ctx);
+ CTX_UNLOCK(ctx);
+
+ return bus_generic_suspend(dev);
+}
+int
+iflib_device_shutdown(device_t dev)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+
+ CTX_LOCK(ctx);
+ IFDI_SHUTDOWN(ctx);
+ CTX_UNLOCK(ctx);
+
+ return bus_generic_suspend(dev);
+}
+
+
+int
+iflib_device_resume(device_t dev)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+ iflib_txq_t txq = ctx->ifc_txqs;
+
+ CTX_LOCK(ctx);
+ IFDI_RESUME(ctx);
+ iflib_if_init_locked(ctx);
+ CTX_UNLOCK(ctx);
+ for (int i = 0; i < NTXQSETS(ctx); i++, txq++)
+ iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET);
+
+ return (bus_generic_resume(dev));
+}
+
+int
+iflib_device_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params)
+{
+ int error;
+ if_ctx_t ctx = device_get_softc(dev);
+
+ CTX_LOCK(ctx);
+ error = IFDI_IOV_INIT(ctx, num_vfs, params);
+ CTX_UNLOCK(ctx);
+
+ return (error);
+}
+
+void
+iflib_device_iov_uninit(device_t dev)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+
+ CTX_LOCK(ctx);
+ IFDI_IOV_UNINIT(ctx);
+ CTX_UNLOCK(ctx);
+}
+
+int
+iflib_device_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params)
+{
+ int error;
+ if_ctx_t ctx = device_get_softc(dev);
+
+ CTX_LOCK(ctx);
+ error = IFDI_IOV_VF_ADD(ctx, vfnum, params);
+ CTX_UNLOCK(ctx);
+
+ return (error);
+}
+
+/*********************************************************************
+ *
+ * MODULE FUNCTION DEFINITIONS
+ *
+ **********************************************************************/
+
+/*
+ * - Start a fast taskqueue thread for each core
+ * - Start a taskqueue for control operations
+ */
+static int
+iflib_module_init(void)
+{
+ return (0);
+}
+
+static int
+iflib_module_event_handler(module_t mod, int what, void *arg)
+{
+ int err;
+
+ switch (what) {
+ case MOD_LOAD:
+ if ((err = iflib_module_init()) != 0)
+ return (err);
+ break;
+ case MOD_UNLOAD:
+ return (EBUSY);
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * PUBLIC FUNCTION DEFINITIONS
+ * ordered as in iflib.h
+ *
+ **********************************************************************/
+
+
+static void
+_iflib_assert(if_shared_ctx_t sctx)
+{
+ int i;
+
+ MPASS(sctx->isc_tx_maxsize);
+ MPASS(sctx->isc_tx_maxsegsize);
+
+ MPASS(sctx->isc_rx_maxsize);
+ MPASS(sctx->isc_rx_nsegments);
+ MPASS(sctx->isc_rx_maxsegsize);
+
+ MPASS(sctx->isc_nrxqs >= 1 && sctx->isc_nrxqs <= 8);
+ for (i = 0; i < sctx->isc_nrxqs; i++) {
+ MPASS(sctx->isc_nrxd_min[i]);
+ MPASS(powerof2(sctx->isc_nrxd_min[i]));
+ MPASS(sctx->isc_nrxd_max[i]);
+ MPASS(powerof2(sctx->isc_nrxd_max[i]));
+ MPASS(sctx->isc_nrxd_default[i]);
+ MPASS(powerof2(sctx->isc_nrxd_default[i]));
+ }
+
+ MPASS(sctx->isc_ntxqs >= 1 && sctx->isc_ntxqs <= 8);
+ for (i = 0; i < sctx->isc_ntxqs; i++) {
+ MPASS(sctx->isc_ntxd_min[i]);
+ MPASS(powerof2(sctx->isc_ntxd_min[i]));
+ MPASS(sctx->isc_ntxd_max[i]);
+ MPASS(powerof2(sctx->isc_ntxd_max[i]));
+ MPASS(sctx->isc_ntxd_default[i]);
+ MPASS(powerof2(sctx->isc_ntxd_default[i]));
+ }
+}
+
+static void
+_iflib_pre_assert(if_softc_ctx_t scctx)
+{
+
+ MPASS(scctx->isc_txrx->ift_txd_encap);
+ MPASS(scctx->isc_txrx->ift_txd_flush);
+ MPASS(scctx->isc_txrx->ift_txd_credits_update);
+ MPASS(scctx->isc_txrx->ift_rxd_available);
+ MPASS(scctx->isc_txrx->ift_rxd_pkt_get);
+ MPASS(scctx->isc_txrx->ift_rxd_refill);
+ MPASS(scctx->isc_txrx->ift_rxd_flush);
+}
+
+static int
+iflib_register(if_ctx_t ctx)
+{
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ driver_t *driver = sctx->isc_driver;
+ device_t dev = ctx->ifc_dev;
+ if_t ifp;
+
+ _iflib_assert(sctx);
+
+ CTX_LOCK_INIT(ctx);
+ STATE_LOCK_INIT(ctx, device_get_nameunit(ctx->ifc_dev));
+ ifp = ctx->ifc_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "can not allocate ifnet structure\n");
+ return (ENOMEM);
+ }
+
+ /*
+ * Initialize our context's device specific methods
+ */
+ kobj_init((kobj_t) ctx, (kobj_class_t) driver);
+ kobj_class_compile((kobj_class_t) driver);
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ if_setsoftc(ifp, ctx);
+ if_setdev(ifp, dev);
+ if_setinitfn(ifp, iflib_if_init);
+ if_setioctlfn(ifp, iflib_if_ioctl);
+#ifdef ALTQ
+ if_setstartfn(ifp, iflib_altq_if_start);
+ if_settransmitfn(ifp, iflib_altq_if_transmit);
+ if_setsendqready(ifp);
+#else
+ if_settransmitfn(ifp, iflib_if_transmit);
+#endif
+ if_setqflushfn(ifp, iflib_if_qflush);
+ if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+
+ ctx->ifc_vlan_attach_event =
+ EVENTHANDLER_REGISTER(vlan_config, iflib_vlan_register, ctx,
+ EVENTHANDLER_PRI_FIRST);
+ ctx->ifc_vlan_detach_event =
+ EVENTHANDLER_REGISTER(vlan_unconfig, iflib_vlan_unregister, ctx,
+ EVENTHANDLER_PRI_FIRST);
+
+ if ((sctx->isc_flags & IFLIB_DRIVER_MEDIA) == 0) {
+ ctx->ifc_mediap = &ctx->ifc_media;
+ ifmedia_init(ctx->ifc_mediap, IFM_IMASK,
+ iflib_media_change, iflib_media_status);
+ }
+ return (0);
+}
+
+static void
+iflib_deregister(if_ctx_t ctx)
+{
+ if_t ifp = ctx->ifc_ifp;
+
+ /* Remove all media */
+ ifmedia_removeall(&ctx->ifc_media);
+
+ /* Unregister VLAN events */
+ if (ctx->ifc_vlan_attach_event != NULL) {
+ EVENTHANDLER_DEREGISTER(vlan_config, ctx->ifc_vlan_attach_event);
+ ctx->ifc_vlan_attach_event = NULL;
+ }
+ if (ctx->ifc_vlan_detach_event != NULL) {
+ EVENTHANDLER_DEREGISTER(vlan_unconfig, ctx->ifc_vlan_detach_event);
+ ctx->ifc_vlan_detach_event = NULL;
+ }
+
+ /* Release kobject reference */
+ kobj_delete((kobj_t) ctx, NULL);
+
+ /* Free the ifnet structure */
+ if_free(ifp);
+
+ STATE_LOCK_DESTROY(ctx);
+
+ /* ether_ifdetach calls if_qflush - lock must be destroy afterwards*/
+ CTX_LOCK_DESTROY(ctx);
+}
+
+static int
+iflib_queues_alloc(if_ctx_t ctx)
+{
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ device_t dev = ctx->ifc_dev;
+ int nrxqsets = scctx->isc_nrxqsets;
+ int ntxqsets = scctx->isc_ntxqsets;
+ iflib_txq_t txq;
+ iflib_rxq_t rxq;
+ iflib_fl_t fl = NULL;
+ int i, j, cpu, err, txconf, rxconf;
+ iflib_dma_info_t ifdip;
+ uint32_t *rxqsizes = scctx->isc_rxqsizes;
+ uint32_t *txqsizes = scctx->isc_txqsizes;
+ uint8_t nrxqs = sctx->isc_nrxqs;
+ uint8_t ntxqs = sctx->isc_ntxqs;
+ int nfree_lists = sctx->isc_nfl ? sctx->isc_nfl : 1;
+ caddr_t *vaddrs;
+ uint64_t *paddrs;
+
+ KASSERT(ntxqs > 0, ("number of queues per qset must be at least 1"));
+ KASSERT(nrxqs > 0, ("number of queues per qset must be at least 1"));
+
+ /* Allocate the TX ring struct memory */
+ if (!(ctx->ifc_txqs =
+ (iflib_txq_t) malloc(sizeof(struct iflib_txq) *
+ ntxqsets, M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate TX ring memory\n");
+ err = ENOMEM;
+ goto fail;
+ }
+
+ /* Now allocate the RX */
+ if (!(ctx->ifc_rxqs =
+ (iflib_rxq_t) malloc(sizeof(struct iflib_rxq) *
+ nrxqsets, M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate RX ring memory\n");
+ err = ENOMEM;
+ goto rx_fail;
+ }
+
+ txq = ctx->ifc_txqs;
+ rxq = ctx->ifc_rxqs;
+
+ /*
+ * XXX handle allocation failure
+ */
+ for (txconf = i = 0, cpu = CPU_FIRST(); i < ntxqsets; i++, txconf++, txq++, cpu = CPU_NEXT(cpu)) {
+ /* Set up some basics */
+
+ if ((ifdip = malloc(sizeof(struct iflib_dma_info) * ntxqs,
+ M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) {
+ device_printf(dev,
+ "Unable to allocate TX DMA info memory\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+ txq->ift_ifdi = ifdip;
+ for (j = 0; j < ntxqs; j++, ifdip++) {
+ if (iflib_dma_alloc(ctx, txqsizes[j], ifdip, 0)) {
+ device_printf(dev,
+ "Unable to allocate TX descriptors\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+ txq->ift_txd_size[j] = scctx->isc_txd_size[j];
+ bzero((void *)ifdip->idi_vaddr, txqsizes[j]);
+ }
+ txq->ift_ctx = ctx;
+ txq->ift_id = i;
+ if (sctx->isc_flags & IFLIB_HAS_TXCQ) {
+ txq->ift_br_offset = 1;
+ } else {
+ txq->ift_br_offset = 0;
+ }
+ /* XXX fix this */
+#ifndef __rtems__
+ txq->ift_timer.c_cpu = cpu;
+#endif /* __rtems__ */
+
+ if (iflib_txsd_alloc(txq)) {
+ device_printf(dev, "Critical Failure setting up TX buffers\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+
+ /* Initialize the TX lock */
+ snprintf(txq->ift_mtx_name, MTX_NAME_LEN, "%s:TX(%d):callout",
+ device_get_nameunit(dev), txq->ift_id);
+ mtx_init(&txq->ift_mtx, txq->ift_mtx_name, NULL, MTX_DEF);
+ callout_init_mtx(&txq->ift_timer, &txq->ift_mtx, 0);
+
+ err = ifmp_ring_alloc(&txq->ift_br, 2048, txq, iflib_txq_drain,
+ iflib_txq_can_drain, M_IFLIB, M_WAITOK);
+ if (err) {
+ /* XXX free any allocated rings */
+ device_printf(dev, "Unable to allocate buf_ring\n");
+ goto err_tx_desc;
+ }
+ }
+
+ for (rxconf = i = 0; i < nrxqsets; i++, rxconf++, rxq++) {
+ /* Set up some basics */
+
+ if ((ifdip = malloc(sizeof(struct iflib_dma_info) * nrxqs,
+ M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) {
+ device_printf(dev,
+ "Unable to allocate RX DMA info memory\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+
+ rxq->ifr_ifdi = ifdip;
+ /* XXX this needs to be changed if #rx queues != #tx queues */
+ rxq->ifr_ntxqirq = 1;
+ rxq->ifr_txqid[0] = i;
+ for (j = 0; j < nrxqs; j++, ifdip++) {
+ if (iflib_dma_alloc(ctx, rxqsizes[j], ifdip, 0)) {
+ device_printf(dev,
+ "Unable to allocate RX descriptors\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+ bzero((void *)ifdip->idi_vaddr, rxqsizes[j]);
+ }
+ rxq->ifr_ctx = ctx;
+ rxq->ifr_id = i;
+ if (sctx->isc_flags & IFLIB_HAS_RXCQ) {
+ rxq->ifr_fl_offset = 1;
+ } else {
+ rxq->ifr_fl_offset = 0;
+ }
+ rxq->ifr_nfl = nfree_lists;
+ if (!(fl =
+ (iflib_fl_t) malloc(sizeof(struct iflib_fl) * nfree_lists, M_IFLIB, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate free list memory\n");
+ err = ENOMEM;
+ goto err_tx_desc;
+ }
+ rxq->ifr_fl = fl;
+ for (j = 0; j < nfree_lists; j++) {
+ fl[j].ifl_rxq = rxq;
+ fl[j].ifl_id = j;
+ fl[j].ifl_ifdi = &rxq->ifr_ifdi[j + rxq->ifr_fl_offset];
+ fl[j].ifl_rxd_size = scctx->isc_rxd_size[j];
+ }
+ /* Allocate receive buffers for the ring */
+ if (iflib_rxsd_alloc(rxq)) {
+ device_printf(dev,
+ "Critical Failure setting up receive buffers\n");
+ err = ENOMEM;
+ goto err_rx_desc;
+ }
+
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++)
+ fl->ifl_rx_bitmap = bit_alloc(fl->ifl_size, M_IFLIB,
+ M_WAITOK);
+ }
+
+ /* TXQs */
+ vaddrs = malloc(sizeof(caddr_t)*ntxqsets*ntxqs, M_IFLIB, M_WAITOK);
+ paddrs = malloc(sizeof(uint64_t)*ntxqsets*ntxqs, M_IFLIB, M_WAITOK);
+ for (i = 0; i < ntxqsets; i++) {
+ iflib_dma_info_t di = ctx->ifc_txqs[i].ift_ifdi;
+
+ for (j = 0; j < ntxqs; j++, di++) {
+ vaddrs[i*ntxqs + j] = di->idi_vaddr;
+ paddrs[i*ntxqs + j] = di->idi_paddr;
+ }
+ }
+ if ((err = IFDI_TX_QUEUES_ALLOC(ctx, vaddrs, paddrs, ntxqs, ntxqsets)) != 0) {
+ device_printf(ctx->ifc_dev,
+ "Unable to allocate device TX queue\n");
+ iflib_tx_structures_free(ctx);
+ free(vaddrs, M_IFLIB);
+ free(paddrs, M_IFLIB);
+ goto err_rx_desc;
+ }
+ free(vaddrs, M_IFLIB);
+ free(paddrs, M_IFLIB);
+
+ /* RXQs */
+ vaddrs = malloc(sizeof(caddr_t)*nrxqsets*nrxqs, M_IFLIB, M_WAITOK);
+ paddrs = malloc(sizeof(uint64_t)*nrxqsets*nrxqs, M_IFLIB, M_WAITOK);
+ for (i = 0; i < nrxqsets; i++) {
+ iflib_dma_info_t di = ctx->ifc_rxqs[i].ifr_ifdi;
+
+ for (j = 0; j < nrxqs; j++, di++) {
+ vaddrs[i*nrxqs + j] = di->idi_vaddr;
+ paddrs[i*nrxqs + j] = di->idi_paddr;
+ }
+ }
+ if ((err = IFDI_RX_QUEUES_ALLOC(ctx, vaddrs, paddrs, nrxqs, nrxqsets)) != 0) {
+ device_printf(ctx->ifc_dev,
+ "Unable to allocate device RX queue\n");
+ iflib_tx_structures_free(ctx);
+ free(vaddrs, M_IFLIB);
+ free(paddrs, M_IFLIB);
+ goto err_rx_desc;
+ }
+ free(vaddrs, M_IFLIB);
+ free(paddrs, M_IFLIB);
+
+ return (0);
+
+/* XXX handle allocation failure changes */
+err_rx_desc:
+err_tx_desc:
+rx_fail:
+ if (ctx->ifc_rxqs != NULL)
+ free(ctx->ifc_rxqs, M_IFLIB);
+ ctx->ifc_rxqs = NULL;
+ if (ctx->ifc_txqs != NULL)
+ free(ctx->ifc_txqs, M_IFLIB);
+ ctx->ifc_txqs = NULL;
+fail:
+ return (err);
+}
+
+static int
+iflib_tx_structures_setup(if_ctx_t ctx)
+{
+ iflib_txq_t txq = ctx->ifc_txqs;
+ int i;
+
+ for (i = 0; i < NTXQSETS(ctx); i++, txq++)
+ iflib_txq_setup(txq);
+
+ return (0);
+}
+
+static void
+iflib_tx_structures_free(if_ctx_t ctx)
+{
+ iflib_txq_t txq = ctx->ifc_txqs;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ int i, j;
+
+ for (i = 0; i < NTXQSETS(ctx); i++, txq++) {
+ iflib_txq_destroy(txq);
+ for (j = 0; j < sctx->isc_ntxqs; j++)
+ iflib_dma_free(&txq->ift_ifdi[j]);
+ }
+ free(ctx->ifc_txqs, M_IFLIB);
+ ctx->ifc_txqs = NULL;
+ IFDI_QUEUES_FREE(ctx);
+}
+
+/*********************************************************************
+ *
+ * Initialize all receive rings.
+ *
+ **********************************************************************/
+static int
+iflib_rx_structures_setup(if_ctx_t ctx)
+{
+ iflib_rxq_t rxq = ctx->ifc_rxqs;
+ int q;
+#if defined(INET6) || defined(INET)
+ int err, i;
+#endif
+
+ for (q = 0; q < ctx->ifc_softc_ctx.isc_nrxqsets; q++, rxq++) {
+#if defined(INET6) || defined(INET)
+ if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO) {
+ err = tcp_lro_init_args(&rxq->ifr_lc, ctx->ifc_ifp,
+ TCP_LRO_ENTRIES, min(1024,
+ ctx->ifc_softc_ctx.isc_nrxd[rxq->ifr_fl_offset]));
+ if (err != 0) {
+ device_printf(ctx->ifc_dev,
+ "LRO Initialization failed!\n");
+ goto fail;
+ }
+ }
+#endif
+ IFDI_RXQ_SETUP(ctx, rxq->ifr_id);
+ }
+ return (0);
+#if defined(INET6) || defined(INET)
+fail:
+ /*
+ * Free LRO resources allocated so far, we will only handle
+ * the rings that completed, the failing case will have
+ * cleaned up for itself. 'q' failed, so its the terminus.
+ */
+ rxq = ctx->ifc_rxqs;
+ for (i = 0; i < q; ++i, rxq++) {
+ if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO)
+ tcp_lro_free(&rxq->ifr_lc);
+ }
+ return (err);
+#endif
+}
+
+/*********************************************************************
+ *
+ * Free all receive rings.
+ *
+ **********************************************************************/
+static void
+iflib_rx_structures_free(if_ctx_t ctx)
+{
+ iflib_rxq_t rxq = ctx->ifc_rxqs;
+ int i;
+
+ for (i = 0; i < ctx->ifc_softc_ctx.isc_nrxqsets; i++, rxq++) {
+ iflib_rx_sds_free(rxq);
+#if defined(INET6) || defined(INET)
+ if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO)
+ tcp_lro_free(&rxq->ifr_lc);
+#endif
+ }
+ free(ctx->ifc_rxqs, M_IFLIB);
+ ctx->ifc_rxqs = NULL;
+}
+
+static int
+iflib_qset_structures_setup(if_ctx_t ctx)
+{
+ int err;
+
+ /*
+ * It is expected that the caller takes care of freeing queues if this
+ * fails.
+ */
+ if ((err = iflib_tx_structures_setup(ctx)) != 0) {
+ device_printf(ctx->ifc_dev, "iflib_tx_structures_setup failed: %d\n", err);
+ return (err);
+ }
+
+ if ((err = iflib_rx_structures_setup(ctx)) != 0)
+ device_printf(ctx->ifc_dev, "iflib_rx_structures_setup failed: %d\n", err);
+
+ return (err);
+}
+
+int
+iflib_irq_alloc(if_ctx_t ctx, if_irq_t irq, int rid,
+ driver_filter_t filter, void *filter_arg, driver_intr_t handler, void *arg, const char *name)
+{
+
+ return (_iflib_irq_alloc(ctx, irq, rid, filter, handler, arg, name));
+}
+
+#ifdef SMP
+static int
+find_nth(if_ctx_t ctx, int qid)
+{
+ cpuset_t cpus;
+ int i, cpuid, eqid, count;
+
+ CPU_COPY(&ctx->ifc_cpus, &cpus);
+ count = CPU_COUNT(&cpus);
+ eqid = qid % count;
+ /* clear up to the qid'th bit */
+ for (i = 0; i < eqid; i++) {
+ cpuid = CPU_FFS(&cpus);
+ MPASS(cpuid != 0);
+ CPU_CLR(cpuid-1, &cpus);
+ }
+ cpuid = CPU_FFS(&cpus);
+ MPASS(cpuid != 0);
+ return (cpuid-1);
+}
+
+#ifdef SCHED_ULE
+extern struct cpu_group *cpu_top; /* CPU topology */
+
+static int
+find_child_with_core(int cpu, struct cpu_group *grp)
+{
+ int i;
+
+ if (grp->cg_children == 0)
+ return -1;
+
+ MPASS(grp->cg_child);
+ for (i = 0; i < grp->cg_children; i++) {
+ if (CPU_ISSET(cpu, &grp->cg_child[i].cg_mask))
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Find the nth "close" core to the specified core
+ * "close" is defined as the deepest level that shares
+ * at least an L2 cache. With threads, this will be
+ * threads on the same core. If the shared cache is L3
+ * or higher, simply returns the same core.
+ */
+static int
+find_close_core(int cpu, int core_offset)
+{
+ struct cpu_group *grp;
+ int i;
+ int fcpu;
+ cpuset_t cs;
+
+ grp = cpu_top;
+ if (grp == NULL)
+ return cpu;
+ i = 0;
+ while ((i = find_child_with_core(cpu, grp)) != -1) {
+ /* If the child only has one cpu, don't descend */
+ if (grp->cg_child[i].cg_count <= 1)
+ break;
+ grp = &grp->cg_child[i];
+ }
+
+ /* If they don't share at least an L2 cache, use the same CPU */
+ if (grp->cg_level > CG_SHARE_L2 || grp->cg_level == CG_SHARE_NONE)
+ return cpu;
+
+ /* Now pick one */
+ CPU_COPY(&grp->cg_mask, &cs);
+
+ /* Add the selected CPU offset to core offset. */
+ for (i = 0; (fcpu = CPU_FFS(&cs)) != 0; i++) {
+ if (fcpu - 1 == cpu)
+ break;
+ CPU_CLR(fcpu - 1, &cs);
+ }
+ MPASS(fcpu);
+
+ core_offset += i;
+
+ CPU_COPY(&grp->cg_mask, &cs);
+ for (i = core_offset % grp->cg_count; i > 0; i--) {
+ MPASS(CPU_FFS(&cs));
+ CPU_CLR(CPU_FFS(&cs) - 1, &cs);
+ }
+ MPASS(CPU_FFS(&cs));
+ return CPU_FFS(&cs) - 1;
+}
+#else
+static int
+find_close_core(int cpu, int core_offset __unused)
+{
+ return cpu;
+}
+#endif
+
+static int
+get_core_offset(if_ctx_t ctx, iflib_intr_type_t type, int qid)
+{
+ switch (type) {
+ case IFLIB_INTR_TX:
+ /* TX queues get cores which share at least an L2 cache with the corresponding RX queue */
+ /* XXX handle multiple RX threads per core and more than two core per L2 group */
+ return qid / CPU_COUNT(&ctx->ifc_cpus) + 1;
+ case IFLIB_INTR_RX:
+ case IFLIB_INTR_RXTX:
+ /* RX queues get the specified core */
+ return qid / CPU_COUNT(&ctx->ifc_cpus);
+ default:
+ return -1;
+ }
+}
+#else
+#define get_core_offset(ctx, type, qid) CPU_FIRST()
+#define find_close_core(cpuid, tid) CPU_FIRST()
+#define find_nth(ctx, gid) CPU_FIRST()
+#endif
+
+/* Just to avoid copy/paste */
+static inline int
+iflib_irq_set_affinity(if_ctx_t ctx, if_irq_t irq, iflib_intr_type_t type,
+ int qid, struct grouptask *gtask, struct taskqgroup *tqg, void *uniq,
+ const char *name)
+{
+ device_t dev;
+ int co, cpuid, err, tid;
+
+ dev = ctx->ifc_dev;
+ co = ctx->ifc_sysctl_core_offset;
+ if (ctx->ifc_sysctl_separate_txrx && type == IFLIB_INTR_TX)
+ co += ctx->ifc_softc_ctx.isc_nrxqsets;
+ cpuid = find_nth(ctx, qid + co);
+ tid = get_core_offset(ctx, type, qid);
+ if (tid < 0) {
+ device_printf(dev, "get_core_offset failed\n");
+ return (EOPNOTSUPP);
+ }
+ cpuid = find_close_core(cpuid, tid);
+ err = taskqgroup_attach_cpu(tqg, gtask, uniq, cpuid, dev, irq->ii_res,
+ name);
+ if (err) {
+ device_printf(dev, "taskqgroup_attach_cpu failed %d\n", err);
+ return (err);
+ }
+#ifdef notyet
+ if (cpuid > ctx->ifc_cpuid_highest)
+ ctx->ifc_cpuid_highest = cpuid;
+#endif
+ return (0);
+}
+
+int
+iflib_irq_alloc_generic(if_ctx_t ctx, if_irq_t irq, int rid,
+ iflib_intr_type_t type, driver_filter_t *filter,
+ void *filter_arg, int qid, const char *name)
+{
+ device_t dev;
+ struct grouptask *gtask;
+ struct taskqgroup *tqg;
+ iflib_filter_info_t info;
+ gtask_fn_t *fn;
+ int tqrid, err;
+ driver_filter_t *intr_fast;
+ void *q;
+
+ info = &ctx->ifc_filter_info;
+ tqrid = rid;
+
+ switch (type) {
+ /* XXX merge tx/rx for netmap? */
+ case IFLIB_INTR_TX:
+ q = &ctx->ifc_txqs[qid];
+ info = &ctx->ifc_txqs[qid].ift_filter_info;
+ gtask = &ctx->ifc_txqs[qid].ift_task;
+ tqg = qgroup_if_io_tqg;
+ fn = _task_fn_tx;
+ intr_fast = iflib_fast_intr;
+ GROUPTASK_INIT(gtask, 0, fn, q);
+ ctx->ifc_flags |= IFC_NETMAP_TX_IRQ;
+ break;
+ case IFLIB_INTR_RX:
+ q = &ctx->ifc_rxqs[qid];
+ info = &ctx->ifc_rxqs[qid].ifr_filter_info;
+ gtask = &ctx->ifc_rxqs[qid].ifr_task;
+ tqg = qgroup_if_io_tqg;
+ fn = _task_fn_rx;
+ intr_fast = iflib_fast_intr;
+ GROUPTASK_INIT(gtask, 0, fn, q);
+ break;
+ case IFLIB_INTR_RXTX:
+ q = &ctx->ifc_rxqs[qid];
+ info = &ctx->ifc_rxqs[qid].ifr_filter_info;
+ gtask = &ctx->ifc_rxqs[qid].ifr_task;
+ tqg = qgroup_if_io_tqg;
+ fn = _task_fn_rx;
+ intr_fast = iflib_fast_intr_rxtx;
+ GROUPTASK_INIT(gtask, 0, fn, q);
+ break;
+ case IFLIB_INTR_ADMIN:
+ q = ctx;
+ tqrid = -1;
+ info = &ctx->ifc_filter_info;
+ gtask = &ctx->ifc_admin_task;
+ tqg = qgroup_if_config_tqg;
+ fn = _task_fn_admin;
+ intr_fast = iflib_fast_intr_ctx;
+ break;
+ default:
+ device_printf(ctx->ifc_dev, "%s: unknown net intr type\n",
+ __func__);
+ return (EINVAL);
+ }
+
+ info->ifi_filter = filter;
+ info->ifi_filter_arg = filter_arg;
+ info->ifi_task = gtask;
+ info->ifi_ctx = q;
+
+ dev = ctx->ifc_dev;
+ err = _iflib_irq_alloc(ctx, irq, rid, intr_fast, NULL, info, name);
+ if (err != 0) {
+ device_printf(dev, "_iflib_irq_alloc failed %d\n", err);
+ return (err);
+ }
+ if (type == IFLIB_INTR_ADMIN)
+ return (0);
+
+ if (tqrid != -1) {
+ err = iflib_irq_set_affinity(ctx, irq, type, qid, gtask, tqg,
+ q, name);
+ if (err)
+ return (err);
+ } else {
+ taskqgroup_attach(tqg, gtask, q, dev, irq->ii_res, name);
+ }
+
+ return (0);
+}
+
+void
+iflib_softirq_alloc_generic(if_ctx_t ctx, if_irq_t irq, iflib_intr_type_t type, void *arg, int qid, const char *name)
+{
+ struct grouptask *gtask;
+ struct taskqgroup *tqg;
+ gtask_fn_t *fn;
+ void *q;
+ int err;
+
+ switch (type) {
+ case IFLIB_INTR_TX:
+ q = &ctx->ifc_txqs[qid];
+ gtask = &ctx->ifc_txqs[qid].ift_task;
+ tqg = qgroup_if_io_tqg;
+ fn = _task_fn_tx;
+ break;
+ case IFLIB_INTR_RX:
+ q = &ctx->ifc_rxqs[qid];
+ gtask = &ctx->ifc_rxqs[qid].ifr_task;
+ tqg = qgroup_if_io_tqg;
+ fn = _task_fn_rx;
+ break;
+ case IFLIB_INTR_IOV:
+ q = ctx;
+ gtask = &ctx->ifc_vflr_task;
+ tqg = qgroup_if_config_tqg;
+ fn = _task_fn_iov;
+ break;
+ default:
+ panic("unknown net intr type");
+ }
+ GROUPTASK_INIT(gtask, 0, fn, q);
+ if (irq != NULL) {
+ err = iflib_irq_set_affinity(ctx, irq, type, qid, gtask, tqg,
+ q, name);
+ if (err)
+ taskqgroup_attach(tqg, gtask, q, ctx->ifc_dev,
+ irq->ii_res, name);
+ } else {
+ taskqgroup_attach(tqg, gtask, q, NULL, NULL, name);
+ }
+}
+
+void
+iflib_irq_free(if_ctx_t ctx, if_irq_t irq)
+{
+
+ if (irq->ii_tag)
+ bus_teardown_intr(ctx->ifc_dev, irq->ii_res, irq->ii_tag);
+
+ if (irq->ii_res)
+ bus_release_resource(ctx->ifc_dev, SYS_RES_IRQ,
+ rman_get_rid(irq->ii_res), irq->ii_res);
+}
+
+static int
+iflib_legacy_setup(if_ctx_t ctx, driver_filter_t filter, void *filter_arg, int *rid, const char *name)
+{
+ iflib_txq_t txq = ctx->ifc_txqs;
+ iflib_rxq_t rxq = ctx->ifc_rxqs;
+ if_irq_t irq = &ctx->ifc_legacy_irq;
+ iflib_filter_info_t info;
+ device_t dev;
+ struct grouptask *gtask;
+ struct resource *res;
+ struct taskqgroup *tqg;
+ gtask_fn_t *fn;
+ void *q;
+ int err, tqrid;
+
+ q = &ctx->ifc_rxqs[0];
+ info = &rxq[0].ifr_filter_info;
+ gtask = &rxq[0].ifr_task;
+ tqg = qgroup_if_io_tqg;
+ tqrid = *rid;
+ fn = _task_fn_rx;
+
+ ctx->ifc_flags |= IFC_LEGACY;
+ info->ifi_filter = filter;
+ info->ifi_filter_arg = filter_arg;
+ info->ifi_task = gtask;
+ info->ifi_ctx = q;
+
+ dev = ctx->ifc_dev;
+ /* We allocate a single interrupt resource */
+ if ((err = _iflib_irq_alloc(ctx, irq, tqrid, iflib_fast_intr_rxtx,
+ NULL, info, name)) != 0)
+ return (err);
+ GROUPTASK_INIT(gtask, 0, fn, q);
+ res = irq->ii_res;
+ taskqgroup_attach(tqg, gtask, q, dev, res, name);
+
+ GROUPTASK_INIT(&txq->ift_task, 0, _task_fn_tx, txq);
+ taskqgroup_attach(qgroup_if_io_tqg, &txq->ift_task, txq, dev, res,
+ "tx");
+ return (0);
+}
+
+void
+iflib_led_create(if_ctx_t ctx)
+{
+
+ ctx->ifc_led_dev = led_create(iflib_led_func, ctx,
+ device_get_nameunit(ctx->ifc_dev));
+}
+
+void
+iflib_tx_intr_deferred(if_ctx_t ctx, int txqid)
+{
+
+ GROUPTASK_ENQUEUE(&ctx->ifc_txqs[txqid].ift_task);
+}
+
+void
+iflib_rx_intr_deferred(if_ctx_t ctx, int rxqid)
+{
+
+ GROUPTASK_ENQUEUE(&ctx->ifc_rxqs[rxqid].ifr_task);
+}
+
+void
+iflib_admin_intr_deferred(if_ctx_t ctx)
+{
+#ifdef INVARIANTS
+ struct grouptask *gtask;
+
+ gtask = &ctx->ifc_admin_task;
+ MPASS(gtask != NULL && gtask->gt_taskqueue != NULL);
+#endif
+
+ GROUPTASK_ENQUEUE(&ctx->ifc_admin_task);
+}
+
+void
+iflib_iov_intr_deferred(if_ctx_t ctx)
+{
+
+ GROUPTASK_ENQUEUE(&ctx->ifc_vflr_task);
+}
+
+void
+iflib_io_tqg_attach(struct grouptask *gt, void *uniq, int cpu, const char *name)
+{
+
+ taskqgroup_attach_cpu(qgroup_if_io_tqg, gt, uniq, cpu, NULL, NULL,
+ name);
+}
+
+void
+iflib_config_gtask_init(void *ctx, struct grouptask *gtask, gtask_fn_t *fn,
+ const char *name)
+{
+
+ GROUPTASK_INIT(gtask, 0, fn, ctx);
+ taskqgroup_attach(qgroup_if_config_tqg, gtask, gtask, NULL, NULL,
+ name);
+}
+
+void
+iflib_config_gtask_deinit(struct grouptask *gtask)
+{
+
+ taskqgroup_detach(qgroup_if_config_tqg, gtask);
+}
+
+void
+iflib_link_state_change(if_ctx_t ctx, int link_state, uint64_t baudrate)
+{
+ if_t ifp = ctx->ifc_ifp;
+ iflib_txq_t txq = ctx->ifc_txqs;
+
+ if_setbaudrate(ifp, baudrate);
+ if (baudrate >= IF_Gbps(10)) {
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_PREFETCH;
+ STATE_UNLOCK(ctx);
+ }
+ /* If link down, disable watchdog */
+ if ((ctx->ifc_link_state == LINK_STATE_UP) && (link_state == LINK_STATE_DOWN)) {
+ for (int i = 0; i < ctx->ifc_softc_ctx.isc_ntxqsets; i++, txq++)
+ txq->ift_qstatus = IFLIB_QUEUE_IDLE;
+ }
+ ctx->ifc_link_state = link_state;
+ if_link_state_change(ifp, link_state);
+}
+
+static int
+iflib_tx_credits_update(if_ctx_t ctx, iflib_txq_t txq)
+{
+ int credits;
+#ifdef INVARIANTS
+ int credits_pre = txq->ift_cidx_processed;
+#endif
+
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD);
+ if ((credits = ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id, true)) == 0)
+ return (0);
+
+ txq->ift_processed += credits;
+ txq->ift_cidx_processed += credits;
+
+ MPASS(credits_pre + credits == txq->ift_cidx_processed);
+ if (txq->ift_cidx_processed >= txq->ift_size)
+ txq->ift_cidx_processed -= txq->ift_size;
+ return (credits);
+}
+
+static int
+iflib_rxd_avail(if_ctx_t ctx, iflib_rxq_t rxq, qidx_t cidx, qidx_t budget)
+{
+ iflib_fl_t fl;
+ u_int i;
+
+ for (i = 0, fl = &rxq->ifr_fl[0]; i < rxq->ifr_nfl; i++, fl++)
+ bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ return (ctx->isc_rxd_available(ctx->ifc_softc, rxq->ifr_id, cidx,
+ budget));
+}
+
+void
+iflib_add_int_delay_sysctl(if_ctx_t ctx, const char *name,
+ const char *description, if_int_delay_info_t info,
+ int offset, int value)
+{
+ info->iidi_ctx = ctx;
+ info->iidi_offset = offset;
+ info->iidi_value = value;
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(ctx->ifc_dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(ctx->ifc_dev)),
+ OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW,
+ info, 0, iflib_sysctl_int_delay, "I", description);
+}
+
+struct sx *
+iflib_ctx_lock_get(if_ctx_t ctx)
+{
+
+ return (&ctx->ifc_ctx_sx);
+}
+
+static int
+iflib_msix_init(if_ctx_t ctx)
+{
+ device_t dev = ctx->ifc_dev;
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ int admincnt, bar, err, iflib_num_rx_queues, iflib_num_tx_queues;
+ int msgs, queuemsgs, queues, rx_queues, tx_queues, vectors;
+
+ iflib_num_tx_queues = ctx->ifc_sysctl_ntxqs;
+ iflib_num_rx_queues = ctx->ifc_sysctl_nrxqs;
+
+ if (bootverbose)
+ device_printf(dev, "msix_init qsets capped at %d\n",
+ imax(scctx->isc_ntxqsets, scctx->isc_nrxqsets));
+
+ /* Override by tuneable */
+ if (scctx->isc_disable_msix)
+ goto msi;
+
+ /* First try MSI-X */
+ if ((msgs = pci_msix_count(dev)) == 0) {
+ if (bootverbose)
+ device_printf(dev, "MSI-X not supported or disabled\n");
+ goto msi;
+ }
+
+ bar = ctx->ifc_softc_ctx.isc_msix_bar;
+ /*
+ * bar == -1 => "trust me I know what I'm doing"
+ * Some drivers are for hardware that is so shoddily
+ * documented that no one knows which bars are which
+ * so the developer has to map all bars. This hack
+ * allows shoddy garbage to use MSI-X in this framework.
+ */
+ if (bar != -1) {
+ ctx->ifc_msix_mem = bus_alloc_resource_any(dev,
+ SYS_RES_MEMORY, &bar, RF_ACTIVE);
+ if (ctx->ifc_msix_mem == NULL) {
+ device_printf(dev, "Unable to map MSI-X table\n");
+ goto msi;
+ }
+ }
+
+ admincnt = sctx->isc_admin_intrcnt;
+#if IFLIB_DEBUG
+ /* use only 1 qset in debug mode */
+ queuemsgs = min(msgs - admincnt, 1);
+#else
+ queuemsgs = msgs - admincnt;
+#endif
+#ifdef RSS
+ queues = imin(queuemsgs, rss_getnumbuckets());
+#else
+ queues = queuemsgs;
+#endif
+#ifndef __rtems__
+ queues = imin(CPU_COUNT(&ctx->ifc_cpus), queues);
+#else /* __rtems__ */
+ queues = imin(1, queues);
+#endif /* __rtems__ */
+ if (bootverbose)
+ device_printf(dev,
+ "intr CPUs: %d queue msgs: %d admincnt: %d\n",
+#ifndef __rtems__
+ CPU_COUNT(&ctx->ifc_cpus), queuemsgs, admincnt);
+#else /* __rtems__ */
+ 1, queuemsgs, admincnt);
+#endif /* __rtems__ */
+#ifdef RSS
+ /* If we're doing RSS, clamp at the number of RSS buckets */
+ if (queues > rss_getnumbuckets())
+ queues = rss_getnumbuckets();
+#endif
+ if (iflib_num_rx_queues > 0 && iflib_num_rx_queues < queuemsgs - admincnt)
+ rx_queues = iflib_num_rx_queues;
+ else
+ rx_queues = queues;
+
+ if (rx_queues > scctx->isc_nrxqsets)
+ rx_queues = scctx->isc_nrxqsets;
+
+ /*
+ * We want this to be all logical CPUs by default
+ */
+ if (iflib_num_tx_queues > 0 && iflib_num_tx_queues < queues)
+ tx_queues = iflib_num_tx_queues;
+ else
+ tx_queues = mp_ncpus;
+
+ if (tx_queues > scctx->isc_ntxqsets)
+ tx_queues = scctx->isc_ntxqsets;
+
+ if (ctx->ifc_sysctl_qs_eq_override == 0) {
+#ifdef INVARIANTS
+ if (tx_queues != rx_queues)
+ device_printf(dev,
+ "queue equality override not set, capping rx_queues at %d and tx_queues at %d\n",
+ min(rx_queues, tx_queues), min(rx_queues, tx_queues));
+#endif
+ tx_queues = min(rx_queues, tx_queues);
+ rx_queues = min(rx_queues, tx_queues);
+ }
+
+ vectors = rx_queues + admincnt;
+ if (msgs < vectors) {
+ device_printf(dev,
+ "insufficient number of MSI-X vectors "
+ "(supported %d, need %d)\n", msgs, vectors);
+ goto msi;
+ }
+
+ device_printf(dev, "Using %d RX queues %d TX queues\n", rx_queues,
+ tx_queues);
+ msgs = vectors;
+ if ((err = pci_alloc_msix(dev, &vectors)) == 0) {
+ if (vectors != msgs) {
+ device_printf(dev,
+ "Unable to allocate sufficient MSI-X vectors "
+ "(got %d, need %d)\n", vectors, msgs);
+ pci_release_msi(dev);
+ if (bar != -1) {
+ bus_release_resource(dev, SYS_RES_MEMORY, bar,
+ ctx->ifc_msix_mem);
+ ctx->ifc_msix_mem = NULL;
+ }
+ goto msi;
+ }
+ device_printf(dev, "Using MSI-X interrupts with %d vectors\n",
+ vectors);
+ scctx->isc_vectors = vectors;
+ scctx->isc_nrxqsets = rx_queues;
+ scctx->isc_ntxqsets = tx_queues;
+ scctx->isc_intr = IFLIB_INTR_MSIX;
+
+ return (vectors);
+ } else {
+ device_printf(dev,
+ "failed to allocate %d MSI-X vectors, err: %d\n", vectors,
+ err);
+ if (bar != -1) {
+ bus_release_resource(dev, SYS_RES_MEMORY, bar,
+ ctx->ifc_msix_mem);
+ ctx->ifc_msix_mem = NULL;
+ }
+ }
+
+msi:
+ vectors = pci_msi_count(dev);
+ scctx->isc_nrxqsets = 1;
+ scctx->isc_ntxqsets = 1;
+ scctx->isc_vectors = vectors;
+ if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0) {
+ device_printf(dev,"Using an MSI interrupt\n");
+ scctx->isc_intr = IFLIB_INTR_MSI;
+ } else {
+ scctx->isc_vectors = 1;
+ device_printf(dev,"Using a Legacy interrupt\n");
+ scctx->isc_intr = IFLIB_INTR_LEGACY;
+ }
+
+ return (vectors);
+}
+
+static const char *ring_states[] = { "IDLE", "BUSY", "STALLED", "ABDICATED" };
+
+static int
+mp_ring_state_handler(SYSCTL_HANDLER_ARGS)
+{
+ int rc;
+ uint16_t *state = ((uint16_t *)oidp->oid_arg1);
+ struct sbuf *sb;
+ const char *ring_state = "UNKNOWN";
+
+ /* XXX needed ? */
+ rc = sysctl_wire_old_buffer(req, 0);
+ MPASS(rc == 0);
+ if (rc != 0)
+ return (rc);
+ sb = sbuf_new_for_sysctl(NULL, NULL, 80, req);
+ MPASS(sb != NULL);
+ if (sb == NULL)
+ return (ENOMEM);
+ if (state[3] <= 3)
+ ring_state = ring_states[state[3]];
+
+ sbuf_printf(sb, "pidx_head: %04hd pidx_tail: %04hd cidx: %04hd state: %s",
+ state[0], state[1], state[2], ring_state);
+ rc = sbuf_finish(sb);
+ sbuf_delete(sb);
+ return(rc);
+}
+
+enum iflib_ndesc_handler {
+ IFLIB_NTXD_HANDLER,
+ IFLIB_NRXD_HANDLER,
+};
+
+static int
+mp_ndesc_handler(SYSCTL_HANDLER_ARGS)
+{
+ if_ctx_t ctx = (void *)arg1;
+ enum iflib_ndesc_handler type = arg2;
+ char buf[256] = {0};
+ qidx_t *ndesc;
+ char *p, *next;
+ int nqs, rc, i;
+
+ nqs = 8;
+ switch(type) {
+ case IFLIB_NTXD_HANDLER:
+ ndesc = ctx->ifc_sysctl_ntxds;
+ if (ctx->ifc_sctx)
+ nqs = ctx->ifc_sctx->isc_ntxqs;
+ break;
+ case IFLIB_NRXD_HANDLER:
+ ndesc = ctx->ifc_sysctl_nrxds;
+ if (ctx->ifc_sctx)
+ nqs = ctx->ifc_sctx->isc_nrxqs;
+ break;
+ default:
+ printf("%s: unhandled type\n", __func__);
+ return (EINVAL);
+ }
+ if (nqs == 0)
+ nqs = 8;
+
+ for (i=0; i<8; i++) {
+ if (i >= nqs)
+ break;
+ if (i)
+ strcat(buf, ",");
+ sprintf(strchr(buf, 0), "%d", ndesc[i]);
+ }
+
+ rc = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (rc || req->newptr == NULL)
+ return rc;
+
+ for (i = 0, next = buf, p = strsep(&next, " ,"); i < 8 && p;
+ i++, p = strsep(&next, " ,")) {
+ ndesc[i] = strtoul(p, NULL, 10);
+ }
+
+ return(rc);
+}
+
+#define NAME_BUFLEN 32
+static void
+iflib_add_device_sysctl_pre(if_ctx_t ctx)
+{
+ device_t dev = iflib_get_dev(ctx);
+ struct sysctl_oid_list *child, *oid_list;
+ struct sysctl_ctx_list *ctx_list;
+ struct sysctl_oid *node;
+
+ ctx_list = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ ctx->ifc_sysctl_node = node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, "iflib",
+ CTLFLAG_RD, NULL, "IFLIB fields");
+ oid_list = SYSCTL_CHILDREN(node);
+
+ SYSCTL_ADD_CONST_STRING(ctx_list, oid_list, OID_AUTO, "driver_version",
+ CTLFLAG_RD, ctx->ifc_sctx->isc_driver_version,
+ "driver version");
+
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_ntxqs",
+ CTLFLAG_RWTUN, &ctx->ifc_sysctl_ntxqs, 0,
+ "# of txqs to use, 0 => use default #");
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_nrxqs",
+ CTLFLAG_RWTUN, &ctx->ifc_sysctl_nrxqs, 0,
+ "# of rxqs to use, 0 => use default #");
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_qs_enable",
+ CTLFLAG_RWTUN, &ctx->ifc_sysctl_qs_eq_override, 0,
+ "permit #txq != #rxq");
+ SYSCTL_ADD_INT(ctx_list, oid_list, OID_AUTO, "disable_msix",
+ CTLFLAG_RWTUN, &ctx->ifc_softc_ctx.isc_disable_msix, 0,
+ "disable MSI-X (default 0)");
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "rx_budget",
+ CTLFLAG_RWTUN, &ctx->ifc_sysctl_rx_budget, 0,
+ "set the RX budget");
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "tx_abdicate",
+ CTLFLAG_RWTUN, &ctx->ifc_sysctl_tx_abdicate, 0,
+ "cause TX to abdicate instead of running to completion");
+ ctx->ifc_sysctl_core_offset = CORE_OFFSET_UNSPECIFIED;
+ SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "core_offset",
+ CTLFLAG_RDTUN, &ctx->ifc_sysctl_core_offset, 0,
+ "offset to start using cores at");
+ SYSCTL_ADD_U8(ctx_list, oid_list, OID_AUTO, "separate_txrx",
+ CTLFLAG_RDTUN, &ctx->ifc_sysctl_separate_txrx, 0,
+ "use separate cores for TX and RX");
+
+ /* XXX change for per-queue sizes */
+ SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_ntxds",
+ CTLTYPE_STRING|CTLFLAG_RWTUN, ctx, IFLIB_NTXD_HANDLER,
+ mp_ndesc_handler, "A",
+ "list of # of TX descriptors to use, 0 = use default #");
+ SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_nrxds",
+ CTLTYPE_STRING|CTLFLAG_RWTUN, ctx, IFLIB_NRXD_HANDLER,
+ mp_ndesc_handler, "A",
+ "list of # of RX descriptors to use, 0 = use default #");
+}
+
+static void
+iflib_add_device_sysctl_post(if_ctx_t ctx)
+{
+ if_shared_ctx_t sctx = ctx->ifc_sctx;
+ if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
+ device_t dev = iflib_get_dev(ctx);
+ struct sysctl_oid_list *child;
+ struct sysctl_ctx_list *ctx_list;
+ iflib_fl_t fl;
+ iflib_txq_t txq;
+ iflib_rxq_t rxq;
+ int i, j;
+ char namebuf[NAME_BUFLEN];
+ char *qfmt;
+ struct sysctl_oid *queue_node, *fl_node, *node;
+ struct sysctl_oid_list *queue_list, *fl_list;
+ ctx_list = device_get_sysctl_ctx(dev);
+
+ node = ctx->ifc_sysctl_node;
+ child = SYSCTL_CHILDREN(node);
+
+ if (scctx->isc_ntxqsets > 100)
+ qfmt = "txq%03d";
+ else if (scctx->isc_ntxqsets > 10)
+ qfmt = "txq%02d";
+ else
+ qfmt = "txq%d";
+ for (i = 0, txq = ctx->ifc_txqs; i < scctx->isc_ntxqsets; i++, txq++) {
+ snprintf(namebuf, NAME_BUFLEN, qfmt, i);
+ queue_node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, namebuf,
+ CTLFLAG_RD, NULL, "Queue Name");
+ queue_list = SYSCTL_CHILDREN(queue_node);
+#if MEMORY_LOGGING
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_dequeued",
+ CTLFLAG_RD,
+ &txq->ift_dequeued, "total mbufs freed");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_enqueued",
+ CTLFLAG_RD,
+ &txq->ift_enqueued, "total mbufs enqueued");
+#endif
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "mbuf_defrag",
+ CTLFLAG_RD,
+ &txq->ift_mbuf_defrag, "# of times m_defrag was called");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "m_pullups",
+ CTLFLAG_RD,
+ &txq->ift_pullups, "# of times m_pullup was called");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "mbuf_defrag_failed",
+ CTLFLAG_RD,
+ &txq->ift_mbuf_defrag_failed, "# of times m_defrag failed");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "no_desc_avail",
+ CTLFLAG_RD,
+ &txq->ift_no_desc_avail, "# of times no descriptors were available");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "tx_map_failed",
+ CTLFLAG_RD,
+ &txq->ift_map_failed, "# of times DMA map failed");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txd_encap_efbig",
+ CTLFLAG_RD,
+ &txq->ift_txd_encap_efbig, "# of times txd_encap returned EFBIG");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "no_tx_dma_setup",
+ CTLFLAG_RD,
+ &txq->ift_no_tx_dma_setup, "# of times map failed for other than EFBIG");
+ SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_pidx",
+ CTLFLAG_RD,
+ &txq->ift_pidx, 1, "Producer Index");
+ SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_cidx",
+ CTLFLAG_RD,
+ &txq->ift_cidx, 1, "Consumer Index");
+ SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_cidx_processed",
+ CTLFLAG_RD,
+ &txq->ift_cidx_processed, 1, "Consumer Index seen by credit update");
+ SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_in_use",
+ CTLFLAG_RD,
+ &txq->ift_in_use, 1, "descriptors in use");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_processed",
+ CTLFLAG_RD,
+ &txq->ift_processed, "descriptors procesed for clean");
+ SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_cleaned",
+ CTLFLAG_RD,
+ &txq->ift_cleaned, "total cleaned");
+ SYSCTL_ADD_PROC(ctx_list, queue_list, OID_AUTO, "ring_state",
+ CTLTYPE_STRING | CTLFLAG_RD, __DEVOLATILE(uint64_t *, &txq->ift_br->state),
+ 0, mp_ring_state_handler, "A", "soft ring state");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_enqueues",
+ CTLFLAG_RD, &txq->ift_br->enqueues,
+ "# of enqueues to the mp_ring for this queue");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_drops",
+ CTLFLAG_RD, &txq->ift_br->drops,
+ "# of drops in the mp_ring for this queue");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_starts",
+ CTLFLAG_RD, &txq->ift_br->starts,
+ "# of normal consumer starts in the mp_ring for this queue");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_stalls",
+ CTLFLAG_RD, &txq->ift_br->stalls,
+ "# of consumer stalls in the mp_ring for this queue");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_restarts",
+ CTLFLAG_RD, &txq->ift_br->restarts,
+ "# of consumer restarts in the mp_ring for this queue");
+ SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_abdications",
+ CTLFLAG_RD, &txq->ift_br->abdications,
+ "# of consumer abdications in the mp_ring for this queue");
+ }
+
+ if (scctx->isc_nrxqsets > 100)
+ qfmt = "rxq%03d";
+ else if (scctx->isc_nrxqsets > 10)
+ qfmt = "rxq%02d";
+ else
+ qfmt = "rxq%d";
+ for (i = 0, rxq = ctx->ifc_rxqs; i < scctx->isc_nrxqsets; i++, rxq++) {
+ snprintf(namebuf, NAME_BUFLEN, qfmt, i);
+ queue_node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, namebuf,
+ CTLFLAG_RD, NULL, "Queue Name");
+ queue_list = SYSCTL_CHILDREN(queue_node);
+ if (sctx->isc_flags & IFLIB_HAS_RXCQ) {
+ SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "rxq_cq_cidx",
+ CTLFLAG_RD,
+ &rxq->ifr_cq_cidx, 1, "Consumer Index");
+ }
+
+ for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) {
+ snprintf(namebuf, NAME_BUFLEN, "rxq_fl%d", j);
+ fl_node = SYSCTL_ADD_NODE(ctx_list, queue_list, OID_AUTO, namebuf,
+ CTLFLAG_RD, NULL, "freelist Name");
+ fl_list = SYSCTL_CHILDREN(fl_node);
+ SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "pidx",
+ CTLFLAG_RD,
+ &fl->ifl_pidx, 1, "Producer Index");
+ SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "cidx",
+ CTLFLAG_RD,
+ &fl->ifl_cidx, 1, "Consumer Index");
+ SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "credits",
+ CTLFLAG_RD,
+ &fl->ifl_credits, 1, "credits available");
+#if MEMORY_LOGGING
+ SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_m_enqueued",
+ CTLFLAG_RD,
+ &fl->ifl_m_enqueued, "mbufs allocated");
+ SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_m_dequeued",
+ CTLFLAG_RD,
+ &fl->ifl_m_dequeued, "mbufs freed");
+ SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_cl_enqueued",
+ CTLFLAG_RD,
+ &fl->ifl_cl_enqueued, "clusters allocated");
+ SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_cl_dequeued",
+ CTLFLAG_RD,
+ &fl->ifl_cl_dequeued, "clusters freed");
+#endif
+
+ }
+ }
+
+}
+
+void
+iflib_request_reset(if_ctx_t ctx)
+{
+
+ STATE_LOCK(ctx);
+ ctx->ifc_flags |= IFC_DO_RESET;
+ STATE_UNLOCK(ctx);
+}
+
+#ifndef __NO_STRICT_ALIGNMENT
+static struct mbuf *
+iflib_fixup_rx(struct mbuf *m)
+{
+ struct mbuf *n;
+
+ if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) {
+ bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
+ m->m_data += ETHER_HDR_LEN;
+ n = m;
+ } else {
+ MGETHDR(n, M_NOWAIT, MT_DATA);
+ if (n == NULL) {
+ m_freem(m);
+ return (NULL);
+ }
+ bcopy(m->m_data, n->m_data, ETHER_HDR_LEN);
+ m->m_data += ETHER_HDR_LEN;
+ m->m_len -= ETHER_HDR_LEN;
+ n->m_len = ETHER_HDR_LEN;
+ M_MOVE_PKTHDR(n, m);
+ n->m_next = m;
+ }
+ return (n);
+}
+#endif
+
+#ifdef NETDUMP
+static void
+iflib_netdump_init(if_t ifp, int *nrxr, int *ncl, int *clsize)
+{
+ if_ctx_t ctx;
+
+ ctx = if_getsoftc(ifp);
+ CTX_LOCK(ctx);
+ *nrxr = NRXQSETS(ctx);
+ *ncl = ctx->ifc_rxqs[0].ifr_fl->ifl_size;
+ *clsize = ctx->ifc_rxqs[0].ifr_fl->ifl_buf_size;
+ CTX_UNLOCK(ctx);
+}
+
+static void
+iflib_netdump_event(if_t ifp, enum netdump_ev event)
+{
+ if_ctx_t ctx;
+ if_softc_ctx_t scctx;
+ iflib_fl_t fl;
+ iflib_rxq_t rxq;
+ int i, j;
+
+ ctx = if_getsoftc(ifp);
+ scctx = &ctx->ifc_softc_ctx;
+
+ switch (event) {
+ case NETDUMP_START:
+ for (i = 0; i < scctx->isc_nrxqsets; i++) {
+ rxq = &ctx->ifc_rxqs[i];
+ for (j = 0; j < rxq->ifr_nfl; j++) {
+ fl = rxq->ifr_fl;
+ fl->ifl_zone = m_getzone(fl->ifl_buf_size);
+ }
+ }
+ iflib_no_tx_batch = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+iflib_netdump_transmit(if_t ifp, struct mbuf *m)
+{
+ if_ctx_t ctx;
+ iflib_txq_t txq;
+ int error;
+
+ ctx = if_getsoftc(ifp);
+ if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return (EBUSY);
+
+ txq = &ctx->ifc_txqs[0];
+ error = iflib_encap(txq, &m);
+ if (error == 0)
+ (void)iflib_txd_db_check(ctx, txq, true, txq->ift_in_use);
+ return (error);
+}
+
+static int
+iflib_netdump_poll(if_t ifp, int count)
+{
+ if_ctx_t ctx;
+ if_softc_ctx_t scctx;
+ iflib_txq_t txq;
+ int i;
+
+ ctx = if_getsoftc(ifp);
+ scctx = &ctx->ifc_softc_ctx;
+
+ if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return (EBUSY);
+
+ txq = &ctx->ifc_txqs[0];
+ (void)iflib_completed_tx_reclaim(txq, RECLAIM_THRESH(ctx));
+
+ for (i = 0; i < scctx->isc_nrxqsets; i++)
+ (void)iflib_rxeof(&ctx->ifc_rxqs[i], 16 /* XXX */);
+ return (0);
+}
+#endif /* NETDUMP */
diff --git a/freebsd/sys/net/iflib_private.h b/freebsd/sys/net/iflib_private.h
new file mode 100644
index 00000000..eca6be63
--- /dev/null
+++ b/freebsd/sys/net/iflib_private.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2018, Matthew Macy (mmacy@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. Neither the name of Matthew Macy 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __NET_IFLIB_PRIVATE_H_
+#define __NET_IFLIB_PRIVATE_H_
+
+#define IFC_LEGACY 0x001
+#define IFC_QFLUSH 0x002
+#define IFC_MULTISEG 0x004
+#define IFC_SPARE1 0x008
+#define IFC_SC_ALLOCATED 0x010
+#define IFC_INIT_DONE 0x020
+#define IFC_PREFETCH 0x040
+#define IFC_DO_RESET 0x080
+#define IFC_DO_WATCHDOG 0x100
+#define IFC_SPARE0 0x200
+#define IFC_PSEUDO 0x400
+#define IFC_IN_DETACH 0x800
+
+#define IFC_NETMAP_TX_IRQ 0x80000000
+
+MALLOC_DECLARE(M_IFLIB);
+
+struct iflib_cloneattach_ctx {
+ struct if_clone *cc_ifc;
+ caddr_t cc_params;
+ const char *cc_name;
+ int cc_len;
+};
+
+extern driver_t iflib_pseudodriver;
+int noop_attach(device_t dev);
+int iflib_pseudo_detach(device_t dev);
+
+int iflib_pseudo_register(device_t dev, if_shared_ctx_t sctx, if_ctx_t *ctxp,
+ struct iflib_cloneattach_ctx *clctx);
+
+int iflib_pseudo_deregister(if_ctx_t ctx);
+
+uint32_t iflib_get_flags(if_ctx_t ctx);
+void iflib_set_detach(if_ctx_t ctx);
+void iflib_stop(if_ctx_t ctx);
+
+#endif
diff --git a/freebsd/sys/net/mp_ring.c b/freebsd/sys/net/mp_ring.c
new file mode 100644
index 00000000..c2a2e9db
--- /dev/null
+++ b/freebsd/sys/net/mp_ring.c
@@ -0,0 +1,554 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Chelsio Communications, Inc.
+ * All rights reserved.
+ * Written by: Navdeep Parhar <np@FreeBSD.org>
+ *
+ * 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/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/counter.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+#include <machine/cpu.h>
+#include <net/mp_ring.h>
+
+union ring_state {
+ struct {
+ uint16_t pidx_head;
+ uint16_t pidx_tail;
+ uint16_t cidx;
+ uint16_t flags;
+ };
+ uint64_t state;
+};
+
+enum {
+ IDLE = 0, /* consumer ran to completion, nothing more to do. */
+ BUSY, /* consumer is running already, or will be shortly. */
+ STALLED, /* consumer stopped due to lack of resources. */
+ ABDICATED, /* consumer stopped even though there was work to be
+ done because it wants another thread to take over. */
+};
+
+static inline uint16_t
+space_available(struct ifmp_ring *r, union ring_state s)
+{
+ uint16_t x = r->size - 1;
+
+ if (s.cidx == s.pidx_head)
+ return (x);
+ else if (s.cidx > s.pidx_head)
+ return (s.cidx - s.pidx_head - 1);
+ else
+ return (x - s.pidx_head + s.cidx);
+}
+
+static inline uint16_t
+increment_idx(struct ifmp_ring *r, uint16_t idx, uint16_t n)
+{
+ int x = r->size - idx;
+
+ MPASS(x > 0);
+ return (x > n ? idx + n : n - x);
+}
+
+/* Consumer is about to update the ring's state to s */
+static inline uint16_t
+state_to_flags(union ring_state s, int abdicate)
+{
+
+ if (s.cidx == s.pidx_tail)
+ return (IDLE);
+ else if (abdicate && s.pidx_tail != s.pidx_head)
+ return (ABDICATED);
+
+ return (BUSY);
+}
+
+#ifdef MP_RING_NO_64BIT_ATOMICS
+static void
+drain_ring_locked(struct ifmp_ring *r, union ring_state os, uint16_t prev, int budget)
+{
+ union ring_state ns;
+ int n, pending, total;
+ uint16_t cidx = os.cidx;
+ uint16_t pidx = os.pidx_tail;
+
+ MPASS(os.flags == BUSY);
+ MPASS(cidx != pidx);
+
+ if (prev == IDLE)
+ counter_u64_add(r->starts, 1);
+ pending = 0;
+ total = 0;
+
+ while (cidx != pidx) {
+
+ /* Items from cidx to pidx are available for consumption. */
+ n = r->drain(r, cidx, pidx);
+ if (n == 0) {
+ os.state = ns.state = r->state;
+ ns.cidx = cidx;
+ ns.flags = STALLED;
+ r->state = ns.state;
+ if (prev != STALLED)
+ counter_u64_add(r->stalls, 1);
+ else if (total > 0) {
+ counter_u64_add(r->restarts, 1);
+ counter_u64_add(r->stalls, 1);
+ }
+ break;
+ }
+ cidx = increment_idx(r, cidx, n);
+ pending += n;
+ total += n;
+
+ /*
+ * We update the cidx only if we've caught up with the pidx, the
+ * real cidx is getting too far ahead of the one visible to
+ * everyone else, or we have exceeded our budget.
+ */
+ if (cidx != pidx && pending < 64 && total < budget)
+ continue;
+
+ os.state = ns.state = r->state;
+ ns.cidx = cidx;
+ ns.flags = state_to_flags(ns, total >= budget);
+ r->state = ns.state;
+
+ if (ns.flags == ABDICATED)
+ counter_u64_add(r->abdications, 1);
+ if (ns.flags != BUSY) {
+ /* Wrong loop exit if we're going to stall. */
+ MPASS(ns.flags != STALLED);
+ if (prev == STALLED) {
+ MPASS(total > 0);
+ counter_u64_add(r->restarts, 1);
+ }
+ break;
+ }
+
+ /*
+ * The acquire style atomic above guarantees visibility of items
+ * associated with any pidx change that we notice here.
+ */
+ pidx = ns.pidx_tail;
+ pending = 0;
+ }
+}
+#else
+/*
+ * Caller passes in a state, with a guarantee that there is work to do and that
+ * all items up to the pidx_tail in the state are visible.
+ */
+static void
+drain_ring_lockless(struct ifmp_ring *r, union ring_state os, uint16_t prev, int budget)
+{
+ union ring_state ns;
+ int n, pending, total;
+ uint16_t cidx = os.cidx;
+ uint16_t pidx = os.pidx_tail;
+
+ MPASS(os.flags == BUSY);
+ MPASS(cidx != pidx);
+
+ if (prev == IDLE)
+ counter_u64_add(r->starts, 1);
+ pending = 0;
+ total = 0;
+
+ while (cidx != pidx) {
+
+ /* Items from cidx to pidx are available for consumption. */
+ n = r->drain(r, cidx, pidx);
+ if (n == 0) {
+ critical_enter();
+ os.state = r->state;
+ do {
+ ns.state = os.state;
+ ns.cidx = cidx;
+ ns.flags = STALLED;
+ } while (atomic_fcmpset_64(&r->state, &os.state,
+ ns.state) == 0);
+ critical_exit();
+ if (prev != STALLED)
+ counter_u64_add(r->stalls, 1);
+ else if (total > 0) {
+ counter_u64_add(r->restarts, 1);
+ counter_u64_add(r->stalls, 1);
+ }
+ break;
+ }
+ cidx = increment_idx(r, cidx, n);
+ pending += n;
+ total += n;
+
+ /*
+ * We update the cidx only if we've caught up with the pidx, the
+ * real cidx is getting too far ahead of the one visible to
+ * everyone else, or we have exceeded our budget.
+ */
+ if (cidx != pidx && pending < 64 && total < budget)
+ continue;
+ critical_enter();
+ os.state = r->state;
+ do {
+ ns.state = os.state;
+ ns.cidx = cidx;
+ ns.flags = state_to_flags(ns, total >= budget);
+ } while (atomic_fcmpset_acq_64(&r->state, &os.state,
+ ns.state) == 0);
+ critical_exit();
+
+ if (ns.flags == ABDICATED)
+ counter_u64_add(r->abdications, 1);
+ if (ns.flags != BUSY) {
+ /* Wrong loop exit if we're going to stall. */
+ MPASS(ns.flags != STALLED);
+ if (prev == STALLED) {
+ MPASS(total > 0);
+ counter_u64_add(r->restarts, 1);
+ }
+ break;
+ }
+
+ /*
+ * The acquire style atomic above guarantees visibility of items
+ * associated with any pidx change that we notice here.
+ */
+ pidx = ns.pidx_tail;
+ pending = 0;
+ }
+}
+#endif
+
+int
+ifmp_ring_alloc(struct ifmp_ring **pr, int size, void *cookie, mp_ring_drain_t drain,
+ mp_ring_can_drain_t can_drain, struct malloc_type *mt, int flags)
+{
+ struct ifmp_ring *r;
+
+ /* All idx are 16b so size can be 65536 at most */
+ if (pr == NULL || size < 2 || size > 65536 || drain == NULL ||
+ can_drain == NULL)
+ return (EINVAL);
+ *pr = NULL;
+ flags &= M_NOWAIT | M_WAITOK;
+ MPASS(flags != 0);
+
+ r = malloc(__offsetof(struct ifmp_ring, items[size]), mt, flags | M_ZERO);
+ if (r == NULL)
+ return (ENOMEM);
+ r->size = size;
+ r->cookie = cookie;
+ r->mt = mt;
+ r->drain = drain;
+ r->can_drain = can_drain;
+ r->enqueues = counter_u64_alloc(flags);
+ r->drops = counter_u64_alloc(flags);
+ r->starts = counter_u64_alloc(flags);
+ r->stalls = counter_u64_alloc(flags);
+ r->restarts = counter_u64_alloc(flags);
+ r->abdications = counter_u64_alloc(flags);
+ if (r->enqueues == NULL || r->drops == NULL || r->starts == NULL ||
+ r->stalls == NULL || r->restarts == NULL ||
+ r->abdications == NULL) {
+ ifmp_ring_free(r);
+ return (ENOMEM);
+ }
+
+ *pr = r;
+#ifdef MP_RING_NO_64BIT_ATOMICS
+ mtx_init(&r->lock, "mp_ring lock", NULL, MTX_DEF);
+#endif
+ return (0);
+}
+
+void
+ifmp_ring_free(struct ifmp_ring *r)
+{
+
+ if (r == NULL)
+ return;
+
+ if (r->enqueues != NULL)
+ counter_u64_free(r->enqueues);
+ if (r->drops != NULL)
+ counter_u64_free(r->drops);
+ if (r->starts != NULL)
+ counter_u64_free(r->starts);
+ if (r->stalls != NULL)
+ counter_u64_free(r->stalls);
+ if (r->restarts != NULL)
+ counter_u64_free(r->restarts);
+ if (r->abdications != NULL)
+ counter_u64_free(r->abdications);
+
+ free(r, r->mt);
+}
+
+/*
+ * Enqueue n items and maybe drain the ring for some time.
+ *
+ * Returns an errno.
+ */
+#ifdef MP_RING_NO_64BIT_ATOMICS
+int
+ifmp_ring_enqueue(struct ifmp_ring *r, void **items, int n, int budget, int abdicate)
+{
+ union ring_state os, ns;
+ uint16_t pidx_start, pidx_stop;
+ int i;
+
+ MPASS(items != NULL);
+ MPASS(n > 0);
+
+ mtx_lock(&r->lock);
+ /*
+ * Reserve room for the new items. Our reservation, if successful, is
+ * from 'pidx_start' to 'pidx_stop'.
+ */
+ os.state = r->state;
+ if (n >= space_available(r, os)) {
+ counter_u64_add(r->drops, n);
+ MPASS(os.flags != IDLE);
+ mtx_unlock(&r->lock);
+ if (os.flags == STALLED)
+ ifmp_ring_check_drainage(r, 0);
+ return (ENOBUFS);
+ }
+ ns.state = os.state;
+ ns.pidx_head = increment_idx(r, os.pidx_head, n);
+ r->state = ns.state;
+ pidx_start = os.pidx_head;
+ pidx_stop = ns.pidx_head;
+
+ /*
+ * Wait for other producers who got in ahead of us to enqueue their
+ * items, one producer at a time. It is our turn when the ring's
+ * pidx_tail reaches the beginning of our reservation (pidx_start).
+ */
+ while (ns.pidx_tail != pidx_start) {
+ cpu_spinwait();
+ ns.state = r->state;
+ }
+
+ /* Now it is our turn to fill up the area we reserved earlier. */
+ i = pidx_start;
+ do {
+ r->items[i] = *items++;
+ if (__predict_false(++i == r->size))
+ i = 0;
+ } while (i != pidx_stop);
+
+ /*
+ * Update the ring's pidx_tail. The release style atomic guarantees
+ * that the items are visible to any thread that sees the updated pidx.
+ */
+ os.state = ns.state = r->state;
+ ns.pidx_tail = pidx_stop;
+ if (abdicate) {
+ if (os.flags == IDLE)
+ ns.flags = ABDICATED;
+ } else
+ ns.flags = BUSY;
+ r->state = ns.state;
+ counter_u64_add(r->enqueues, n);
+
+ if (!abdicate) {
+ /*
+ * Turn into a consumer if some other thread isn't active as a consumer
+ * already.
+ */
+ if (os.flags != BUSY)
+ drain_ring_locked(r, ns, os.flags, budget);
+ }
+
+ mtx_unlock(&r->lock);
+ return (0);
+}
+#else
+int
+ifmp_ring_enqueue(struct ifmp_ring *r, void **items, int n, int budget, int abdicate)
+{
+ union ring_state os, ns;
+ uint16_t pidx_start, pidx_stop;
+ int i;
+
+ MPASS(items != NULL);
+ MPASS(n > 0);
+
+ /*
+ * Reserve room for the new items. Our reservation, if successful, is
+ * from 'pidx_start' to 'pidx_stop'.
+ */
+ os.state = r->state;
+ for (;;) {
+ if (n >= space_available(r, os)) {
+ counter_u64_add(r->drops, n);
+ MPASS(os.flags != IDLE);
+ if (os.flags == STALLED)
+ ifmp_ring_check_drainage(r, 0);
+ return (ENOBUFS);
+ }
+ ns.state = os.state;
+ ns.pidx_head = increment_idx(r, os.pidx_head, n);
+ critical_enter();
+ if (atomic_fcmpset_64(&r->state, &os.state, ns.state))
+ break;
+ critical_exit();
+ cpu_spinwait();
+ }
+ pidx_start = os.pidx_head;
+ pidx_stop = ns.pidx_head;
+
+ /*
+ * Wait for other producers who got in ahead of us to enqueue their
+ * items, one producer at a time. It is our turn when the ring's
+ * pidx_tail reaches the beginning of our reservation (pidx_start).
+ */
+ while (ns.pidx_tail != pidx_start) {
+ cpu_spinwait();
+ ns.state = r->state;
+ }
+
+ /* Now it is our turn to fill up the area we reserved earlier. */
+ i = pidx_start;
+ do {
+ r->items[i] = *items++;
+ if (__predict_false(++i == r->size))
+ i = 0;
+ } while (i != pidx_stop);
+
+ /*
+ * Update the ring's pidx_tail. The release style atomic guarantees
+ * that the items are visible to any thread that sees the updated pidx.
+ */
+ os.state = r->state;
+ do {
+ ns.state = os.state;
+ ns.pidx_tail = pidx_stop;
+ if (abdicate) {
+ if (os.flags == IDLE)
+ ns.flags = ABDICATED;
+ } else
+ ns.flags = BUSY;
+ } while (atomic_fcmpset_rel_64(&r->state, &os.state, ns.state) == 0);
+ critical_exit();
+ counter_u64_add(r->enqueues, n);
+
+ if (!abdicate) {
+ /*
+ * Turn into a consumer if some other thread isn't active as a consumer
+ * already.
+ */
+ if (os.flags != BUSY)
+ drain_ring_lockless(r, ns, os.flags, budget);
+ }
+
+ return (0);
+}
+#endif
+
+void
+ifmp_ring_check_drainage(struct ifmp_ring *r, int budget)
+{
+ union ring_state os, ns;
+
+ os.state = r->state;
+ if ((os.flags != STALLED && os.flags != ABDICATED) || // Only continue in STALLED and ABDICATED
+ os.pidx_head != os.pidx_tail || // Require work to be available
+ (os.flags != ABDICATED && r->can_drain(r) == 0)) // Can either drain, or everyone left
+ return;
+
+ MPASS(os.cidx != os.pidx_tail); /* implied by STALLED */
+ ns.state = os.state;
+ ns.flags = BUSY;
+
+
+#ifdef MP_RING_NO_64BIT_ATOMICS
+ mtx_lock(&r->lock);
+ if (r->state != os.state) {
+ mtx_unlock(&r->lock);
+ return;
+ }
+ r->state = ns.state;
+ drain_ring_locked(r, ns, os.flags, budget);
+ mtx_unlock(&r->lock);
+#else
+ /*
+ * The acquire style atomic guarantees visibility of items associated
+ * with the pidx that we read here.
+ */
+ if (!atomic_cmpset_acq_64(&r->state, os.state, ns.state))
+ return;
+
+
+ drain_ring_lockless(r, ns, os.flags, budget);
+#endif
+}
+
+void
+ifmp_ring_reset_stats(struct ifmp_ring *r)
+{
+
+ counter_u64_zero(r->enqueues);
+ counter_u64_zero(r->drops);
+ counter_u64_zero(r->starts);
+ counter_u64_zero(r->stalls);
+ counter_u64_zero(r->restarts);
+ counter_u64_zero(r->abdications);
+}
+
+int
+ifmp_ring_is_idle(struct ifmp_ring *r)
+{
+ union ring_state s;
+
+ s.state = r->state;
+ if (s.pidx_head == s.pidx_tail && s.pidx_tail == s.cidx &&
+ s.flags == IDLE)
+ return (1);
+
+ return (0);
+}
+
+int
+ifmp_ring_is_stalled(struct ifmp_ring *r)
+{
+ union ring_state s;
+
+ s.state = r->state;
+ if (s.pidx_head == s.pidx_tail && s.flags == STALLED)
+ return (1);
+
+ return (0);
+}
diff --git a/freebsd/sys/net/mp_ring.h b/freebsd/sys/net/mp_ring.h
new file mode 100644
index 00000000..6dea325d
--- /dev/null
+++ b/freebsd/sys/net/mp_ring.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2014 Chelsio Communications, Inc.
+ * All rights reserved.
+ * Written by: Navdeep Parhar <np@FreeBSD.org>
+ *
+ * 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 __NET_MP_RING_H
+#define __NET_MP_RING_H
+
+#ifndef _KERNEL
+#error "no user-serviceable parts inside"
+#endif
+
+struct ifmp_ring;
+typedef u_int (*mp_ring_drain_t)(struct ifmp_ring *, u_int, u_int);
+typedef u_int (*mp_ring_can_drain_t)(struct ifmp_ring *);
+typedef void (*mp_ring_serial_t)(struct ifmp_ring *);
+
+#if defined(__powerpc__) || defined(__mips__) || defined(__i386__)
+#define MP_RING_NO_64BIT_ATOMICS
+#endif
+
+struct ifmp_ring {
+ volatile uint64_t state __aligned(CACHE_LINE_SIZE);
+
+ int size __aligned(CACHE_LINE_SIZE);
+ void * cookie;
+ struct malloc_type * mt;
+ mp_ring_drain_t drain;
+ mp_ring_can_drain_t can_drain; /* cheap, may be unreliable */
+ counter_u64_t enqueues;
+ counter_u64_t drops;
+ counter_u64_t starts;
+ counter_u64_t stalls;
+ counter_u64_t restarts; /* recovered after stalling */
+ counter_u64_t abdications;
+#ifdef MP_RING_NO_64BIT_ATOMICS
+ struct mtx lock;
+#endif
+ void * volatile items[] __aligned(CACHE_LINE_SIZE);
+};
+
+int ifmp_ring_alloc(struct ifmp_ring **, int, void *, mp_ring_drain_t,
+ mp_ring_can_drain_t, struct malloc_type *, int);
+void ifmp_ring_free(struct ifmp_ring *);
+int ifmp_ring_enqueue(struct ifmp_ring *, void **, int, int, int);
+void ifmp_ring_check_drainage(struct ifmp_ring *, int);
+void ifmp_ring_reset_stats(struct ifmp_ring *);
+int ifmp_ring_is_idle(struct ifmp_ring *);
+int ifmp_ring_is_stalled(struct ifmp_ring *r);
+#endif
diff --git a/freebsd/sys/or1k/include/machine/in_cksum.h b/freebsd/sys/or1k/include/machine/in_cksum.h
new file mode 100644
index 00000000..d55b838b
--- /dev/null
+++ b/freebsd/sys/or1k/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/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/callout.h b/freebsd/sys/sys/callout.h
index e5e5df85..d7b9965a 100644
--- a/freebsd/sys/sys/callout.h
+++ b/freebsd/sys/sys/callout.h
@@ -112,9 +112,15 @@ int callout_reset_sbt_on(struct callout *, sbintime_t, sbintime_t,
#define callout_reset_sbt_curcpu(c, sbt, pr, fn, arg, flags) \
callout_reset_sbt_on((c), (sbt), (pr), (fn), (arg), PCPU_GET(cpuid),\
(flags))
+#ifndef __rtems__
#define callout_reset_on(c, to_ticks, fn, arg, cpu) \
callout_reset_sbt_on((c), tick_sbt * (to_ticks), 0, (fn), (arg), \
(cpu), C_HARDCLOCK)
+#else /* __rtems__ */
+#define callout_reset_on(c, to_ticks, fn, arg, cpu) \
+ callout_reset_sbt_on((c), tick_sbt * (to_ticks), 0, (fn), (arg), \
+ -1, C_HARDCLOCK)
+#endif /* __rtems__ */
#define callout_reset(c, on_tick, fn, arg) \
callout_reset_on((c), (on_tick), (fn), (arg), -1)
#define callout_reset_curcpu(c, on_tick, fn, arg) \
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/rtemsbsd/include/x86/specialreg.h b/freebsd/sys/x86/include/machine/specialreg.h
index f528bad5..f528bad5 100644
--- a/rtemsbsd/include/x86/specialreg.h
+++ b/freebsd/sys/x86/include/machine/specialreg.h
diff --git a/freebsd/sys/x86/include/machine/x86_var.h b/freebsd/sys/x86/include/machine/x86_var.h
new file mode 100644
index 00000000..2028d739
--- /dev/null
+++ b/freebsd/sys/x86/include/machine/x86_var.h
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1995 Bruce D. Evans.
+ * 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 author nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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 _X86_X86_VAR_H_
+#define _X86_X86_VAR_H_
+
+/*
+ * Miscellaneous machine-dependent declarations.
+ */
+
+extern long Maxmem;
+extern u_int basemem;
+extern int busdma_swi_pending;
+extern u_int cpu_exthigh;
+extern u_int cpu_feature;
+extern u_int cpu_feature2;
+extern u_int amd_feature;
+extern u_int amd_feature2;
+extern u_int amd_rascap;
+extern u_int amd_pminfo;
+extern u_int amd_extended_feature_extensions;
+extern u_int via_feature_rng;
+extern u_int via_feature_xcrypt;
+extern u_int cpu_clflush_line_size;
+extern u_int cpu_stdext_feature;
+extern u_int cpu_stdext_feature2;
+extern u_int cpu_stdext_feature3;
+extern uint64_t cpu_ia32_arch_caps;
+extern u_int cpu_fxsr;
+extern u_int cpu_high;
+extern u_int cpu_id;
+extern u_int cpu_max_ext_state_size;
+extern u_int cpu_mxcsr_mask;
+extern u_int cpu_procinfo;
+extern u_int cpu_procinfo2;
+extern char cpu_vendor[];
+extern u_int cpu_vendor_id;
+extern u_int cpu_mon_mwait_flags;
+extern u_int cpu_mon_min_size;
+extern u_int cpu_mon_max_size;
+extern u_int cpu_maxphyaddr;
+extern char ctx_switch_xsave[];
+extern u_int hv_base;
+extern u_int hv_high;
+extern char hv_vendor[];
+extern char kstack[];
+extern char sigcode[];
+extern int szsigcode;
+extern int vm_page_dump_size;
+extern int workaround_erratum383;
+extern int _udatasel;
+extern int _ucodesel;
+extern int _ucode32sel;
+extern int _ufssel;
+extern int _ugssel;
+extern int use_xsave;
+extern uint64_t xsave_mask;
+extern u_int max_apic_id;
+extern int i386_read_exec;
+extern int pti;
+extern int hw_ibrs_active;
+extern int hw_mds_disable;
+extern int hw_ssb_active;
+
+struct pcb;
+struct thread;
+struct reg;
+struct fpreg;
+struct dbreg;
+struct dumperinfo;
+struct trapframe;
+
+/*
+ * The interface type of the interrupt handler entry point cannot be
+ * expressed in C. Use simplest non-variadic function type as an
+ * approximation.
+ */
+typedef void alias_for_inthand_t(void);
+
+bool acpi_get_fadt_bootflags(uint16_t *flagsp);
+void *alloc_fpusave(int flags);
+void busdma_swi(void);
+vm_paddr_t cpu_getmaxphyaddr(void);
+bool cpu_mwait_usable(void);
+void cpu_probe_amdc1e(void);
+void cpu_setregs(void);
+bool disable_wp(void);
+void restore_wp(bool old_wp);
+void dump_add_page(vm_paddr_t);
+void dump_drop_page(vm_paddr_t);
+void finishidentcpu(void);
+void identify_cpu1(void);
+void identify_cpu2(void);
+void identify_cpu_fixup_bsp(void);
+void identify_hypervisor(void);
+void initializecpu(void);
+void initializecpucache(void);
+bool fix_cpuid(void);
+void fillw(int /*u_short*/ pat, void *base, size_t cnt);
+int is_physical_memory(vm_paddr_t addr);
+int isa_nmi(int cd);
+void handle_ibrs_entry(void);
+void handle_ibrs_exit(void);
+void hw_ibrs_recalculate(void);
+void hw_mds_recalculate(void);
+void hw_ssb_recalculate(bool all_cpus);
+void nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame);
+void nmi_call_kdb_smp(u_int type, struct trapframe *frame);
+void nmi_handle_intr(u_int type, struct trapframe *frame);
+void pagecopy(void *from, void *to);
+void printcpuinfo(void);
+int pti_get_default(void);
+int user_dbreg_trap(register_t dr6);
+int minidumpsys(struct dumperinfo *);
+struct pcb *get_pcb_td(struct thread *td);
+
+#endif
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/freebsd/usr.sbin/ifmcstat/ifmcstat.c b/freebsd/usr.sbin/ifmcstat/ifmcstat.c
new file mode 100644
index 00000000..19acaf62
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/ifmcstat.c
@@ -0,0 +1,1295 @@
+#include <machine/rtems-bsd-user-space.h>
+
+#ifdef __rtems__
+#include "rtems-bsd-ifmcstat-namespace.h"
+#include "rtems-bsd-ifmcstat-ifmcstat-data.h"
+#endif /* __rtems__ */
+
+/* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2007-2009 Bruce Simpson.
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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 project 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 PROJECT 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 PROJECT 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.
+ */
+
+#ifdef __rtems__
+#define __need_getopt_newlib
+#include <getopt.h>
+#include <machine/rtems-bsd-program.h>
+#include <machine/rtems-bsd-commands.h>
+#include <rtems/libio_.h>
+#endif /* __rtems__ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#include <netinet/if_ether.h>
+#include <netinet/igmp_var.h>
+
+#ifdef INET6
+#include <netinet/icmp6.h>
+#include <netinet6/mld6_var.h>
+#endif /* INET6 */
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <ifaddrs.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#ifdef KVM
+/*
+ * Currently the KVM build is broken. To be fixed it requires uncovering
+ * large amount of _KERNEL code in include files, and it is also very
+ * tentative to internal kernel ABI changes. If anyone wishes to restore
+ * it, please move it out of src/usr.sbin to src/tools/tools.
+ */
+#include <kvm.h>
+#include <nlist.h>
+#endif
+
+/* XXX: This file currently assumes INET support in the base system. */
+#ifndef INET
+#define INET
+#endif
+
+extern void printb(const char *, unsigned int, const char *);
+
+union sockunion {
+ struct sockaddr_storage ss;
+ struct sockaddr sa;
+ struct sockaddr_dl sdl;
+#ifdef INET
+ struct sockaddr_in sin;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 sin6;
+#endif
+};
+typedef union sockunion sockunion_t;
+
+#ifdef __rtems__
+static
+#endif /* __rtems__ */
+uint32_t ifindex = 0;
+#ifdef __rtems__
+static
+#endif /* __rtems__ */
+int af = AF_UNSPEC;
+#ifdef WITH_KVM
+int Kflag = 0;
+#endif
+#ifdef __rtems__
+static
+#endif /* __rtems__ */
+int vflag = 0;
+
+#define sa_dl_equal(a1, a2) \
+ ((((struct sockaddr_dl *)(a1))->sdl_len == \
+ ((struct sockaddr_dl *)(a2))->sdl_len) && \
+ (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \
+ LLADDR((struct sockaddr_dl *)(a2)), \
+ ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
+
+/*
+ * Most of the code in this utility is to support the use of KVM for
+ * post-mortem debugging of the multicast code.
+ */
+#ifdef WITH_KVM
+
+#ifdef INET
+static void if_addrlist(struct ifaddr *);
+static struct in_multi *
+ in_multientry(struct in_multi *);
+#endif /* INET */
+
+#ifdef INET6
+static void if6_addrlist(struct ifaddr *);
+static struct in6_multi *
+ in6_multientry(struct in6_multi *);
+#endif /* INET6 */
+
+static void kread(u_long, void *, int);
+static void ll_addrlist(struct ifaddr *);
+
+static int ifmcstat_kvm(const char *kernel, const char *core);
+
+#define KREAD(addr, buf, type) \
+ kread((u_long)addr, (void *)buf, sizeof(type))
+
+kvm_t *kvmd;
+struct nlist nl[] = {
+ { "_ifnet", 0, 0, 0, 0, },
+ { "", 0, 0, 0, 0, },
+};
+#define N_IFNET 0
+
+#endif /* WITH_KVM */
+
+static int ifmcstat_getifmaddrs(void);
+#ifdef INET
+static void in_ifinfo(struct igmp_ifinfo *);
+static const char * inm_mode(u_int mode);
+#endif
+#ifdef INET6
+static void in6_ifinfo(struct mld_ifinfo *);
+static const char * inet6_n2a(struct in6_addr *, uint32_t);
+#endif
+#ifdef __rtems__
+static int main(int argc, char *argv[]);
+
+RTEMS_LINKER_RWSET(bsd_prog_ifmcstat, char);
+
+int
+rtems_bsd_command_ifmcstat(int argc, char *argv[])
+{
+ int exit_code;
+ void *data_begin;
+ size_t data_size;
+
+ data_begin = RTEMS_LINKER_SET_BEGIN(bsd_prog_ifmcstat);
+ data_size = RTEMS_LINKER_SET_SIZE(bsd_prog_ifmcstat);
+
+ rtems_bsd_program_lock();
+ exit_code = rtems_bsd_program_call_main_with_data_restore("ifmcstat",
+ main, argc, argv, data_begin, data_size);
+ rtems_bsd_program_unlock();
+
+ return exit_code;
+}
+#else /* __rtems__ */
+int main(int, char **);
+#endif /* __rtems__ */
+
+static void
+usage()
+{
+
+ fprintf(stderr,
+ "usage: ifmcstat [-i interface] [-f address family]"
+ " [-v]"
+#ifdef WITH_KVM
+ " [-K] [-M core] [-N system]"
+#endif
+ "\n");
+ exit(EX_USAGE);
+}
+
+static const char * const options = "i:f:vM:N:"
+#ifdef WITH_KVM
+ "K"
+#endif
+ ;
+
+int
+main(int argc, char **argv)
+{
+ int c, error;
+#ifdef WITH_KVM
+ const char *kernel = NULL;
+ const char *core = NULL;
+#endif
+
+ while ((c = getopt(argc, argv, options)) != -1) {
+ switch (c) {
+ case 'i':
+ if ((ifindex = if_nametoindex(optarg)) == 0) {
+ fprintf(stderr, "%s: unknown interface\n",
+ optarg);
+ exit(EX_NOHOST);
+ }
+ break;
+
+ case 'f':
+#ifdef INET
+ if (strcmp(optarg, "inet") == 0) {
+ af = AF_INET;
+ break;
+ }
+#endif
+#ifdef INET6
+ if (strcmp(optarg, "inet6") == 0) {
+ af = AF_INET6;
+ break;
+ }
+#endif
+ if (strcmp(optarg, "link") == 0) {
+ af = AF_LINK;
+ break;
+ }
+ fprintf(stderr, "%s: unknown address family\n", optarg);
+ exit(EX_USAGE);
+ /*NOTREACHED*/
+ break;
+
+#ifdef WITH_KVM
+ case 'K':
+ ++Kflag;
+ break;
+#endif
+
+ case 'v':
+ ++vflag;
+ break;
+
+#ifdef WITH_KVM
+ case 'M':
+ core = strdup(optarg);
+ break;
+
+ case 'N':
+ kernel = strdup(optarg);
+ break;
+#endif
+
+ default:
+ usage();
+ break;
+ /*NOTREACHED*/
+ }
+ }
+
+ if (af == AF_LINK && vflag)
+ usage();
+
+#ifdef WITH_KVM
+ if (Kflag)
+ error = ifmcstat_kvm(kernel, core);
+ /*
+ * If KVM failed, and user did not explicitly specify a core file,
+ * or force KVM backend to be disabled, try the sysctl backend.
+ */
+ if (!Kflag || (error != 0 && (core == NULL && kernel == NULL)))
+#endif
+ error = ifmcstat_getifmaddrs();
+ if (error != 0)
+ exit(EX_OSERR);
+
+ exit(EX_OK);
+ /*NOTREACHED*/
+}
+
+#ifdef INET
+
+static void
+in_ifinfo(struct igmp_ifinfo *igi)
+{
+
+ printf("\t");
+ switch (igi->igi_version) {
+ case IGMP_VERSION_1:
+ case IGMP_VERSION_2:
+ case IGMP_VERSION_3:
+ printf("igmpv%d", igi->igi_version);
+ break;
+ default:
+ printf("igmpv?(%d)", igi->igi_version);
+ break;
+ }
+ if (igi->igi_flags)
+ printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
+ if (igi->igi_version == IGMP_VERSION_3) {
+ printf(" rv %u qi %u qri %u uri %u",
+ igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
+ }
+ if (vflag >= 2) {
+ printf(" v1timer %u v2timer %u v3timer %u",
+ igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
+ }
+ printf("\n");
+}
+
+static const char * const inm_modes[] = {
+ "undefined",
+ "include",
+ "exclude",
+};
+
+static const char *
+inm_mode(u_int mode)
+{
+
+ if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
+ return (inm_modes[mode]);
+ return (NULL);
+}
+
+#endif /* INET */
+
+#ifdef WITH_KVM
+
+static int
+ifmcstat_kvm(const char *kernel, const char *core)
+{
+ char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
+ struct ifnet *ifp, *nifp, ifnet;
+
+ if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
+ NULL) {
+ perror("kvm_openfiles");
+ return (-1);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ perror("kvm_nlist");
+ return (-1);
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ printf("symbol %s not found\n", nl[N_IFNET].n_name);
+ return (-1);
+ }
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ nifp = ifnet.if_link.tqe_next;
+ if (ifindex && ifindex != ifnet.if_index)
+ goto next;
+
+ printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
+#ifdef INET
+ if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+#endif
+#ifdef INET6
+ if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+#endif
+ if (vflag)
+ ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+ next:
+ ifp = nifp;
+ }
+
+ return (0);
+}
+
+static void
+kread(u_long addr, void *buf, int len)
+{
+
+ if (kvm_read(kvmd, addr, buf, len) != len) {
+ perror("kvm_read");
+ exit(EX_OSERR);
+ }
+}
+
+static void
+ll_addrlist(struct ifaddr *ifap)
+{
+ char addrbuf[NI_MAXHOST];
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct sockaddr_dl sdl;
+ struct ifaddr *ifap0;
+
+ if (af && af != AF_LINK)
+ return;
+
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_LINK)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl);
+ if (sdl.sdl_alen == 0)
+ goto nextifap;
+ addrbuf[0] = '\0';
+ getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ printf("\tlink %s\n", addrbuf);
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_LINK)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl);
+ addrbuf[0] = '\0';
+ getnameinfo((struct sockaddr *)&sdl,
+ sdl.sdl_len, addrbuf, sizeof(addrbuf),
+ NULL, 0, NI_NUMERICHOST);
+ printf("\t\tgroup %s refcnt %d\n",
+ addrbuf, ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+#ifdef INET6
+
+static void
+if6_addrlist(struct ifaddr *ifap)
+{
+ struct ifnet ifnet;
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct in6_ifaddr if6a;
+ struct ifaddr *ifap0;
+
+ if (af && af != AF_INET6)
+ return;
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET6)
+ goto nextifap;
+ KREAD(ifap, &if6a, struct in6_ifaddr);
+ printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr,
+ if6a.ia_addr.sin6_scope_id));
+ /*
+ * Print per-link MLD information, if available.
+ */
+ if (ifa.ifa_ifp != NULL) {
+ struct in6_ifextra ie;
+ struct mld_ifinfo mli;
+
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ KREAD(ifnet.if_afdata[AF_INET6], &ie,
+ struct in6_ifextra);
+ if (ie.mld_ifinfo != NULL) {
+ KREAD(ie.mld_ifinfo, &mli, struct mld_ifinfo);
+ in6_ifinfo(&mli);
+ }
+ }
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_dl sdl;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET6)
+ goto nextmulti;
+ (void)in6_multientry((struct in6_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s refcnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+static struct in6_multi *
+in6_multientry(struct in6_multi *mc)
+{
+ struct in6_multi multi;
+
+ KREAD(mc, &multi, struct in6_multi);
+ printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr, 0));
+ printf(" refcnt %u\n", multi.in6m_refcount);
+
+ return (multi.in6m_entry.le_next);
+}
+
+#endif /* INET6 */
+
+#ifdef INET
+
+static void
+if_addrlist(struct ifaddr *ifap)
+{
+ struct ifaddr ifa;
+ struct ifnet ifnet;
+ struct sockaddr sa;
+ struct in_ifaddr ia;
+ struct ifaddr *ifap0;
+
+ if (af && af != AF_INET)
+ return;
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET)
+ goto nextifap;
+ KREAD(ifap, &ia, struct in_ifaddr);
+ printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
+ /*
+ * Print per-link IGMP information, if available.
+ */
+ if (ifa.ifa_ifp != NULL) {
+ struct in_ifinfo ii;
+ struct igmp_ifinfo igi;
+
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo);
+ if (ii.ii_igmp != NULL) {
+ KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo);
+ in_ifinfo(&igi);
+ }
+ }
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_dl sdl;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET)
+ goto nextmulti;
+ (void)in_multientry((struct in_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s refcnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+static const char *inm_states[] = {
+ "not-member",
+ "silent",
+ "idle",
+ "lazy",
+ "sleeping",
+ "awakening",
+ "query-pending",
+ "sg-query-pending",
+ "leaving"
+};
+
+static const char *
+inm_state(u_int state)
+{
+
+ if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
+ return (inm_states[state]);
+ return (NULL);
+}
+
+#if 0
+static struct ip_msource *
+ims_min_kvm(struct in_multi *pinm)
+{
+ struct ip_msource ims0;
+ struct ip_msource *tmp, *parent;
+
+ parent = NULL;
+ tmp = RB_ROOT(&pinm->inm_srcs);
+ while (tmp) {
+ parent = tmp;
+ KREAD(tmp, &ims0, struct ip_msource);
+ tmp = RB_LEFT(&ims0, ims_link);
+ }
+ return (parent); /* kva */
+}
+
+/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */
+static struct ip_msource *
+ims_next_kvm(struct ip_msource *ims)
+{
+ struct ip_msource ims0, ims1;
+ struct ip_msource *tmp;
+
+ KREAD(ims, &ims0, struct ip_msource);
+ if (RB_RIGHT(&ims0, ims_link)) {
+ ims = RB_RIGHT(&ims0, ims_link);
+ KREAD(ims, &ims1, struct ip_msource);
+ while ((tmp = RB_LEFT(&ims1, ims_link))) {
+ KREAD(tmp, &ims0, struct ip_msource);
+ ims = RB_LEFT(&ims0, ims_link);
+ }
+ } else {
+ tmp = RB_PARENT(&ims0, ims_link);
+ if (tmp) {
+ KREAD(tmp, &ims1, struct ip_msource);
+ if (ims == RB_LEFT(&ims1, ims_link))
+ ims = tmp;
+ } else {
+ while ((tmp = RB_PARENT(&ims0, ims_link))) {
+ KREAD(tmp, &ims1, struct ip_msource);
+ if (ims == RB_RIGHT(&ims1, ims_link)) {
+ ims = tmp;
+ KREAD(ims, &ims0, struct ip_msource);
+ } else
+ break;
+ }
+ ims = RB_PARENT(&ims0, ims_link);
+ }
+ }
+ return (ims); /* kva */
+}
+
+static void
+inm_print_sources_kvm(struct in_multi *pinm)
+{
+ struct ip_msource ims0;
+ struct ip_msource *ims;
+ struct in_addr src;
+ int cnt;
+ uint8_t fmode;
+
+ cnt = 0;
+ fmode = pinm->inm_st[1].iss_fmode;
+ if (fmode == MCAST_UNDEFINED)
+ return;
+ for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) {
+ if (cnt == 0)
+ printf(" srcs ");
+ KREAD(ims, &ims0, struct ip_msource);
+ /* Only print sources in-mode at t1. */
+ if (fmode != ims_get_mode(pinm, ims, 1))
+ continue;
+ src.s_addr = htonl(ims0.ims_haddr);
+ printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src));
+ }
+}
+#endif
+
+static struct in_multi *
+in_multientry(struct in_multi *pinm)
+{
+ struct in_multi inm;
+ const char *state, *mode;
+
+ KREAD(pinm, &inm, struct in_multi);
+ printf("\t\tgroup %s", inet_ntoa(inm.inm_addr));
+ printf(" refcnt %u", inm.inm_refcount);
+
+ state = inm_state(inm.inm_state);
+ if (state)
+ printf(" state %s", state);
+ else
+ printf(" state (%d)", inm.inm_state);
+
+ mode = inm_mode(inm.inm_st[1].iss_fmode);
+ if (mode)
+ printf(" mode %s", mode);
+ else
+ printf(" mode (%d)", inm.inm_st[1].iss_fmode);
+
+ if (vflag >= 2) {
+ printf(" asm %u ex %u in %u rec %u",
+ (u_int)inm.inm_st[1].iss_asm,
+ (u_int)inm.inm_st[1].iss_ex,
+ (u_int)inm.inm_st[1].iss_in,
+ (u_int)inm.inm_st[1].iss_rec);
+ }
+
+#if 0
+ /* Buggy. */
+ if (vflag)
+ inm_print_sources_kvm(&inm);
+#endif
+
+ printf("\n");
+ return (NULL);
+}
+
+#endif /* INET */
+
+#endif /* WITH_KVM */
+
+#ifdef INET6
+
+static void
+in6_ifinfo(struct mld_ifinfo *mli)
+{
+
+ printf("\t");
+ switch (mli->mli_version) {
+ case MLD_VERSION_1:
+ case MLD_VERSION_2:
+ printf("mldv%d", mli->mli_version);
+ break;
+ default:
+ printf("mldv?(%d)", mli->mli_version);
+ break;
+ }
+ if (mli->mli_flags)
+ printb(" flags", mli->mli_flags, "\020\1SILENT\2USEALLOW");
+ if (mli->mli_version == MLD_VERSION_2) {
+ printf(" rv %u qi %u qri %u uri %u",
+ mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri);
+ }
+ if (vflag >= 2) {
+ printf(" v1timer %u v2timer %u", mli->mli_v1_timer,
+ mli->mli_v2_timer);
+ }
+ printf("\n");
+}
+
+static const char *
+inet6_n2a(struct in6_addr *p, uint32_t scope_id)
+{
+ static char buf[NI_MAXHOST];
+ struct sockaddr_in6 sin6;
+ const int niflags = NI_NUMERICHOST;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = *p;
+ sin6.sin6_scope_id = scope_id;
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ buf, sizeof(buf), NULL, 0, niflags) == 0) {
+ return (buf);
+ } else {
+ return ("(invalid)");
+ }
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * Retrieve per-group source filter mode and lists via sysctl.
+ */
+static void
+inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
+{
+#define MAX_SYSCTL_TRY 5
+ int mib[7];
+ int ntry = 0;
+ size_t mibsize;
+ size_t len;
+ size_t needed;
+ size_t cnt;
+ int i;
+ char *buf;
+ struct in_addr *pina;
+ uint32_t *p;
+ uint32_t fmode;
+ const char *modestr;
+
+ mibsize = nitems(mib);
+ if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
+ perror("sysctlnametomib");
+ return;
+ }
+
+ needed = 0;
+ mib[5] = ifindex;
+ mib[6] = gina.s_addr; /* 32 bits wide */
+ mibsize = nitems(mib);
+ do {
+ if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
+ perror("sysctl net.inet.ip.mcast.filters");
+ return;
+ }
+ if ((buf = malloc(needed)) == NULL) {
+ perror("malloc");
+ return;
+ }
+ if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
+ if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
+ perror("sysctl");
+ goto out_free;
+ }
+ free(buf);
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ len = needed;
+ if (len < sizeof(uint32_t)) {
+ perror("sysctl");
+ goto out_free;
+ }
+
+ p = (uint32_t *)buf;
+ fmode = *p++;
+ len -= sizeof(uint32_t);
+
+ modestr = inm_mode(fmode);
+ if (modestr)
+ printf(" mode %s", modestr);
+ else
+ printf(" mode (%u)", fmode);
+
+ if (vflag == 0)
+ goto out_free;
+
+ cnt = len / sizeof(struct in_addr);
+ pina = (struct in_addr *)p;
+
+ for (i = 0; i < cnt; i++) {
+ if (i == 0)
+ printf(" srcs ");
+ fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
+ inet_ntoa(*pina++));
+ len -= sizeof(struct in_addr);
+ }
+ if (len > 0) {
+ fprintf(stderr, "warning: %u trailing bytes from %s\n",
+ (unsigned int)len, "net.inet.ip.mcast.filters");
+ }
+
+out_free:
+ free(buf);
+#undef MAX_SYSCTL_TRY
+}
+
+#endif /* INET */
+
+#ifdef INET6
+/*
+ * Retrieve MLD per-group source filter mode and lists via sysctl.
+ *
+ * Note: The 128-bit IPv6 group address needs to be segmented into
+ * 32-bit pieces for marshaling to sysctl. So the MIB name ends
+ * up looking like this:
+ * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3]
+ * Assumes that pgroup originated from the kernel, so its components
+ * are already in network-byte order.
+ */
+static void
+in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup)
+{
+#define MAX_SYSCTL_TRY 5
+ char addrbuf[INET6_ADDRSTRLEN];
+ int mib[10];
+ int ntry = 0;
+ int *pi;
+ size_t mibsize;
+ size_t len;
+ size_t needed;
+ size_t cnt;
+ int i;
+ char *buf;
+ struct in6_addr *pina;
+ uint32_t *p;
+ uint32_t fmode;
+ const char *modestr;
+
+ mibsize = nitems(mib);
+ if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib,
+ &mibsize) == -1) {
+ perror("sysctlnametomib");
+ return;
+ }
+
+ needed = 0;
+ mib[5] = ifindex;
+ pi = (int *)pgroup;
+ for (i = 0; i < 4; i++)
+ mib[6 + i] = *pi++;
+
+ mibsize = nitems(mib);
+ do {
+ if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
+ perror("sysctl net.inet6.ip6.mcast.filters");
+ return;
+ }
+ if ((buf = malloc(needed)) == NULL) {
+ perror("malloc");
+ return;
+ }
+ if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
+ if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
+ perror("sysctl");
+ goto out_free;
+ }
+ free(buf);
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ len = needed;
+ if (len < sizeof(uint32_t)) {
+ perror("sysctl");
+ goto out_free;
+ }
+
+ p = (uint32_t *)buf;
+ fmode = *p++;
+ len -= sizeof(uint32_t);
+
+ modestr = inm_mode(fmode);
+ if (modestr)
+ printf(" mode %s", modestr);
+ else
+ printf(" mode (%u)", fmode);
+
+ if (vflag == 0)
+ goto out_free;
+
+ cnt = len / sizeof(struct in6_addr);
+ pina = (struct in6_addr *)p;
+
+ for (i = 0; i < cnt; i++) {
+ if (i == 0)
+ printf(" srcs ");
+ inet_ntop(AF_INET6, (const char *)pina++, addrbuf,
+ INET6_ADDRSTRLEN);
+ fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf);
+ len -= sizeof(struct in6_addr);
+ }
+ if (len > 0) {
+ fprintf(stderr, "warning: %u trailing bytes from %s\n",
+ (unsigned int)len, "net.inet6.ip6.mcast.filters");
+ }
+
+out_free:
+ free(buf);
+#undef MAX_SYSCTL_TRY
+}
+#endif /* INET6 */
+
+static int
+ifmcstat_getifmaddrs(void)
+{
+ char thisifname[IFNAMSIZ];
+ char addrbuf[NI_MAXHOST];
+ struct ifaddrs *ifap, *ifa;
+ struct ifmaddrs *ifmap, *ifma;
+ sockunion_t lastifasa;
+ sockunion_t *psa, *pgsa, *pllsa, *pifasa;
+ char *pcolon;
+ char *pafname;
+ uint32_t lastifindex, thisifindex;
+ int error;
+
+ error = 0;
+ ifap = NULL;
+ ifmap = NULL;
+ lastifindex = 0;
+ thisifindex = 0;
+ lastifasa.ss.ss_family = AF_UNSPEC;
+
+ if (getifaddrs(&ifap) != 0) {
+ warn("getifmaddrs");
+ return (-1);
+ }
+
+ if (getifmaddrs(&ifmap) != 0) {
+ warn("getifmaddrs");
+ error = -1;
+ goto out;
+ }
+
+ for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
+ error = 0;
+ if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
+ continue;
+
+ psa = (sockunion_t *)ifma->ifma_name;
+ if (psa->sa.sa_family != AF_LINK) {
+ fprintf(stderr,
+ "WARNING: Kernel returned invalid data.\n");
+ error = -1;
+ break;
+ }
+
+ /* Filter on interface name. */
+ thisifindex = psa->sdl.sdl_index;
+ if (ifindex != 0 && thisifindex != ifindex)
+ continue;
+
+ /* Filter on address family. */
+ pgsa = (sockunion_t *)ifma->ifma_addr;
+ if (af != 0 && pgsa->sa.sa_family != af)
+ continue;
+
+ strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
+ pcolon = strchr(thisifname, ':');
+ if (pcolon)
+ *pcolon = '\0';
+
+ /* Only print the banner for the first ifmaddrs entry. */
+ if (lastifindex == 0 || lastifindex != thisifindex) {
+ lastifindex = thisifindex;
+ fprintf(stdout, "%s:\n", thisifname);
+ }
+
+ /*
+ * Currently, multicast joins only take place on the
+ * primary IPv4 address, and only on the link-local IPv6
+ * address, as per IGMPv2/3 and MLDv1/2 semantics.
+ * Therefore, we only look up the primary address on
+ * the first pass.
+ */
+ pifasa = NULL;
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
+ (ifa->ifa_addr == NULL) ||
+ (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
+ continue;
+ /*
+ * For AF_INET6 only the link-local address should
+ * be returned. If built without IPv6 support,
+ * skip this address entirely.
+ */
+ pifasa = (sockunion_t *)ifa->ifa_addr;
+ if (pifasa->sa.sa_family == AF_INET6
+#ifdef INET6
+ && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
+#endif
+ ) {
+ pifasa = NULL;
+ continue;
+ }
+ break;
+ }
+ if (pifasa == NULL)
+ continue; /* primary address not found */
+
+ if (!vflag && pifasa->sa.sa_family == AF_LINK)
+ continue;
+
+ /* Parse and print primary address, if not already printed. */
+ if (lastifasa.ss.ss_family == AF_UNSPEC ||
+ ((lastifasa.ss.ss_family == AF_LINK &&
+ !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
+ !sa_equal(&lastifasa.sa, &pifasa->sa))) {
+
+ switch (pifasa->sa.sa_family) {
+ case AF_INET:
+ pafname = "inet";
+ break;
+ case AF_INET6:
+ pafname = "inet6";
+ break;
+ case AF_LINK:
+ pafname = "link";
+ break;
+ default:
+ pafname = "unknown";
+ break;
+ }
+
+ switch (pifasa->sa.sa_family) {
+ case AF_INET6:
+#ifdef INET6
+ {
+ const char *p =
+ inet6_n2a(&pifasa->sin6.sin6_addr,
+ pifasa->sin6.sin6_scope_id);
+ strlcpy(addrbuf, p, sizeof(addrbuf));
+ break;
+ }
+#else
+ /* FALLTHROUGH */
+#endif
+ case AF_INET:
+ case AF_LINK:
+ error = getnameinfo(&pifasa->sa,
+ pifasa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error)
+ perror("getnameinfo");
+ break;
+ default:
+ addrbuf[0] = '\0';
+ break;
+ }
+
+ fprintf(stdout, "\t%s %s", pafname, addrbuf);
+#ifdef INET6
+ if (pifasa->sa.sa_family == AF_INET6 &&
+ pifasa->sin6.sin6_scope_id)
+ fprintf(stdout, " scopeid 0x%x",
+ pifasa->sin6.sin6_scope_id);
+#endif
+ fprintf(stdout, "\n");
+#ifdef INET
+ /*
+ * Print per-link IGMP information, if available.
+ */
+ if (pifasa->sa.sa_family == AF_INET) {
+ struct igmp_ifinfo igi;
+ size_t mibsize, len;
+ int mib[5];
+
+ mibsize = nitems(mib);
+ if (sysctlnametomib("net.inet.igmp.ifinfo",
+ mib, &mibsize) == -1) {
+ perror("sysctlnametomib");
+ goto next_ifnet;
+ }
+ mib[mibsize] = thisifindex;
+ len = sizeof(struct igmp_ifinfo);
+ if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
+ 0) == -1) {
+ perror("sysctl net.inet.igmp.ifinfo");
+ goto next_ifnet;
+ }
+ in_ifinfo(&igi);
+ }
+#endif /* INET */
+#ifdef INET6
+ /*
+ * Print per-link MLD information, if available.
+ */
+ if (pifasa->sa.sa_family == AF_INET6) {
+ struct mld_ifinfo mli;
+ size_t mibsize, len;
+ int mib[5];
+
+ mibsize = nitems(mib);
+ if (sysctlnametomib("net.inet6.mld.ifinfo",
+ mib, &mibsize) == -1) {
+ perror("sysctlnametomib");
+ goto next_ifnet;
+ }
+ mib[mibsize] = thisifindex;
+ len = sizeof(struct mld_ifinfo);
+ if (sysctl(mib, mibsize + 1, &mli, &len, NULL,
+ 0) == -1) {
+ perror("sysctl net.inet6.mld.ifinfo");
+ goto next_ifnet;
+ }
+ in6_ifinfo(&mli);
+ }
+#endif /* INET6 */
+#if defined(INET) || defined(INET6)
+next_ifnet:
+#endif
+ lastifasa = *pifasa;
+ }
+
+ /* Print this group address. */
+#ifdef INET6
+ if (pgsa->sa.sa_family == AF_INET6) {
+ const char *p = inet6_n2a(&pgsa->sin6.sin6_addr,
+ pgsa->sin6.sin6_scope_id);
+ strlcpy(addrbuf, p, sizeof(addrbuf));
+ } else
+#endif
+ {
+ error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ if (error)
+ perror("getnameinfo");
+ }
+
+ fprintf(stdout, "\t\tgroup %s", addrbuf);
+#ifdef INET6
+ if (pgsa->sa.sa_family == AF_INET6 &&
+ pgsa->sin6.sin6_scope_id)
+ fprintf(stdout, " scopeid 0x%x",
+ pgsa->sin6.sin6_scope_id);
+#endif
+#ifdef INET
+ if (pgsa->sa.sa_family == AF_INET) {
+ inm_print_sources_sysctl(thisifindex,
+ pgsa->sin.sin_addr);
+ }
+#endif
+#ifdef INET6
+ if (pgsa->sa.sa_family == AF_INET6) {
+ in6m_print_sources_sysctl(thisifindex,
+ &pgsa->sin6.sin6_addr);
+ }
+#endif
+ fprintf(stdout, "\n");
+
+ /* Link-layer mapping, if present. */
+ pllsa = (sockunion_t *)ifma->ifma_lladdr;
+ if (pllsa != NULL) {
+ error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
+ }
+ }
+out:
+ if (ifmap != NULL)
+ freeifmaddrs(ifmap);
+ if (ifap != NULL)
+ freeifaddrs(ifap);
+
+ return (error);
+}
diff --git a/freebsd/usr.sbin/ifmcstat/printb.c b/freebsd/usr.sbin/ifmcstat/printb.c
new file mode 100644
index 00000000..2c979951
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/printb.c
@@ -0,0 +1,75 @@
+#include <machine/rtems-bsd-user-space.h>
+
+#ifdef __rtems__
+#include "rtems-bsd-ifmcstat-namespace.h"
+#include "rtems-bsd-ifmcstat-printb-data.h"
+#endif /* __rtems__ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(const char *s, unsigned int v, const char *bits)
+{
+ int i, any = 0;
+ char c;
+
+ assert(bits != NULL);
+ if (*bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while ((i = *bits++) != '\0') {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h
new file mode 100644
index 00000000..e94df9c0
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h
@@ -0,0 +1,4 @@
+/* generated by userspace-header-gen.py */
+#include <rtems/linkersets.h>
+/* ifmcstat.c */
+/* printb.c */
diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h
new file mode 100644
index 00000000..39287947
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h
@@ -0,0 +1,7 @@
+/* generated by userspace-header-gen.py */
+#include <rtems/linkersets.h>
+#include "rtems-bsd-ifmcstat-data.h"
+/* ifmcstat.c */
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static int af);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static int vflag);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static uint32_t ifindex);
diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h
new file mode 100644
index 00000000..78414818
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h
@@ -0,0 +1,5 @@
+/* generated by userspace-header-gen.py */
+/* ifmcstat.c */
+#define main _bsd_ifmcstat_main
+/* printb.c */
+#define printb _bsd_ifmcstat_printb
diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h
new file mode 100644
index 00000000..3f74bd60
--- /dev/null
+++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h
@@ -0,0 +1,4 @@
+/* generated by userspace-header-gen.py */
+#include <rtems/linkersets.h>
+#include "rtems-bsd-ifmcstat-data.h"
+/* printb.c */
diff --git a/ipsec-tools/src/libipsec/pfkey.c b/ipsec-tools/src/libipsec/pfkey.c
index a621be12..cc6ad816 100644
--- a/ipsec-tools/src/libipsec/pfkey.c
+++ b/ipsec-tools/src/libipsec/pfkey.c
@@ -1,5 +1,12 @@
#include <machine/rtems-bsd-user-space.h>
+#ifdef __rtems__
+/* Only need socket from rtems-bsd-program wrappers! */
+int
+rtems_bsd_program_socket(int domain, int type, int protocol);
+#define socket(domain, type, protocol) \
+ rtems_bsd_program_socket(domain, type, protocol)
+#endif /* __rtems__ */
/* $NetBSD: pfkey.c,v 1.21.2.1 2011/11/14 13:25:06 tteras Exp $ */
/* $KAME: pfkey.c,v 1.47 2003/10/02 19:52:12 itojun Exp $ */
@@ -1829,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 b869a151..51b67d2e 100644
--- a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
+++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
@@ -2,11 +2,8 @@
#include <rtems/linkersets.h>
#include "rtems-bsd-racoon-data.h"
/* session.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set active_mask);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set preset_mask);
+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 fd_monitors[]);
+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 65124c15..bd2bd316 100644
--- a/ipsec-tools/src/racoon/session.c
+++ b/ipsec-tools/src/racoon/session.c
@@ -65,6 +65,10 @@
#include <sys/stat.h>
#include <paths.h>
#include <err.h>
+#ifdef __rtems__
+#include <sys/param.h>
+#include <rtems/libio_.h>
+#endif /* __rtems__ */
#include <netinet/in.h>
#include <resolv.h>
@@ -115,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));
@@ -122,19 +127,34 @@ 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;
static struct fd_monitor fd_monitors[FD_SETSIZE];
+#else /* __rtems__ */
+static fd_set *allocated_preset_mask, *allocated_active_mask;
+static struct fd_monitor *allocated_fd_monitors;
+#define preset_mask (*allocated_preset_mask)
+#define active_mask (*allocated_active_mask)
+#define fd_monitors (allocated_fd_monitors)
+#endif /* __rtems__ */
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)
{
+#ifndef __rtems__
if (fd < 0 || fd >= FD_SETSIZE) {
+#else /* __rtems__ */
+ if (fd < 0 || fd >= rtems_libio_number_iops) {
+#endif /* __rtems__ */
plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun");
exit(1);
}
@@ -158,7 +178,11 @@ monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority)
void
unmonitor_fd(int fd)
{
+#ifndef __rtems__
if (fd < 0 || fd >= FD_SETSIZE) {
+#else /* __rtems__ */
+ if (fd < 0 || fd >= rtems_libio_number_iops) {
+#endif /* __rtems__ */
plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun");
exit(1);
}
@@ -179,21 +203,42 @@ 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;
nfds = 0;
+#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)
+ errx(1, "failed to allocate preset_mask");
+ allocated_active_mask = calloc(sizeof(fd_set),
+ howmany(rtems_libio_number_iops, sizeof(fd_set) * 8));
+ if (allocated_active_mask == NULL)
+ errx(1, "failed to allocate active_mask");
+ allocated_fd_monitors = calloc(
+ rtems_libio_number_iops, sizeof(struct fd_monitor));
+ if (allocated_fd_monitors == NULL)
+ errx(1, "failed to allocate fd_monitors");
+#endif /* __rtems__ */
for (i = 0; i < NUM_PRIORITIES; i++)
TAILQ_INIT(&fd_monitor_tree[i]);
/* initialize schedular */
sched_init();
+#ifndef __rtems__
init_signal();
+#endif /* __rtems__ */
if (pfkey_init() < 0)
errx(1, "failed to initialize pfkey socket");
@@ -290,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) {
@@ -344,6 +396,7 @@ session(void)
}
}
+#ifndef __rtems__
/* clear all status and exit program. */
static void
close_session()
@@ -525,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;
@@ -550,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 511de7af..db390df0 100644
--- a/libbsd.py
+++ b/libbsd.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2015-2016, 2018 Chris Johns <chrisj@rtems.org>.
+# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>.
# All rights reserved.
#
# Copyright (c) 2009, 2018 embedded brains GmbH. All rights reserved.
@@ -56,42 +56,57 @@ _defaults = {
#
# Includes
#
- 'include-paths': ['rtemsbsd/include',
- 'freebsd/sys',
- 'freebsd/sys/contrib/ck/include',
- 'freebsd/sys/contrib/libsodium/src/libsodium/include',
- 'freebsd/sys/contrib/libsodium/src/libsodium/include/sodium',
- 'freebsd/sys/contrib/pf',
- 'freebsd/crypto',
- 'freebsd/crypto/openssl/include',
- 'freebsd/sys/net',
- 'freebsd/include',
- 'freebsd/lib',
- 'freebsd/lib/libbsdstat',
- 'freebsd/lib/libcapsicum',
- 'freebsd/lib/libcasper',
- 'freebsd/lib/libc/include',
- 'freebsd/lib/libc/isc/include',
- 'freebsd/lib/libc/resolv',
- 'freebsd/lib/libutil',
- 'freebsd/lib/libkvm',
- 'freebsd/lib/libmemstat',
- 'freebsd/contrib/expat/lib',
- 'freebsd/contrib/libpcap',
- 'freebsd/contrib/libxo',
- 'ipsec-tools/src/libipsec',
- 'linux/include',
- 'linux/drivers/net/ethernet/freescale/fman',
- 'rtemsbsd/sys',
- 'mDNSResponder/mDNSCore',
- 'mDNSResponder/mDNSShared',
- 'mDNSResponder/mDNSPosix',
- 'testsuite/include'],
- 'cpu-include-paths': ['rtemsbsd/@CPU@/include',
- 'freebsd/sys/@CPU@/include'],
-
- # The path where headers will be copied during build.
- 'build-include-path': ['build-include'],
+ 'include-paths': {
+ # The path where headers will be copied during the build.
+ 'build': ['build-include'],
+ # Kernel header paths
+ 'kernel': ['rtemsbsd/include',
+ 'freebsd/sys',
+ 'freebsd/sys/contrib/ck/include',
+ 'freebsd/sys/contrib/libsodium/src/libsodium/include',
+ 'freebsd/sys/contrib/libsodium/src/libsodium/include/sodium',
+ 'freebsd/sys/contrib/pf',
+ 'freebsd/sys/net',
+ 'ipsec-tools/src/libipsec',
+ 'linux/include',
+ 'linux/drivers/net/ethernet/freescale/fman',
+ 'rtemsbsd/sys'],
+ # User header paths
+ 'user': ['freebsd/crypto',
+ 'freebsd/crypto/openssl/include',
+ 'freebsd/include',
+ 'freebsd/lib',
+ 'freebsd/lib/libbsdstat',
+ 'freebsd/lib/libcapsicum',
+ 'freebsd/lib/libcasper',
+ 'freebsd/lib/libc/include',
+ 'freebsd/lib/libc/isc/include',
+ 'freebsd/lib/libc/resolv',
+ 'freebsd/lib/libutil',
+ 'freebsd/lib/libkvm',
+ 'freebsd/lib/libmemstat',
+ 'freebsd/contrib/expat/lib',
+ 'freebsd/contrib/libpcap',
+ 'freebsd/contrib/libxo',
+ 'mDNSResponder/mDNSCore',
+ 'mDNSResponder/mDNSShared',
+ 'mDNSResponder/mDNSPosix',
+ 'testsuite/include'],
+ # CPU specific path, assumed to be in the kernel context
+ 'cpu': ['rtemsbsd/@CPU@/include',
+ 'freebsd/sys/@CPU@/include'],
+ },
+
+ #
+ # Map paths based on RTEMS naming to FreeBSD naming.
+ #
+ 'path-mappings': [
+ # (source, [targets..])
+ # i386
+ ('freebsd/sys/i386/include', ['freebsd/sys/x86/include', 'freebsd/sys/i386/include']),
+ # arm64
+ ('freebsd/sys/aarch64/include', ['freebsd/sys/aarch64/include', 'freebsd/sys/arm64/include']),
+ ],
#
# Install headers
@@ -129,6 +144,7 @@ _defaults = {
('freebsd/sys/dev/pci', '**/*.h', 'dev/pci'),
('freebsd/sys/dev/nvme', '**/*.h', 'dev/nvme'),
('freebsd/sys/dev/evdev', '**/*.h', 'dev/evdev'),
+ ('freebsd/sys/@CPU@/include', '**/*.h', ''),
('linux/include', '**/*.h', ''),
('mDNSResponder/mDNSCore', 'mDNSDebug.h', ''),
('mDNSResponder/mDNSCore', 'mDNSEmbeddedAPI.h', ''),
@@ -146,7 +162,7 @@ class rtems(builder.Module):
def generate(self):
mm = self.manager
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/bus_if.c',
'local/cryptodev_if.c',
@@ -162,35 +178,11 @@ class rtems(builder.Module):
'local/mmcbr_if.c',
'local/if_dwc_if.c',
'local/gpio_if.c',
- 'rtems/ipsec_get_policylen.c',
- 'rtems/rtems-bsd-arp-processor.c',
'rtems/rtems-bsd-allocator-domain-size.c',
- 'rtems/rtems-bsd-cxx.cc',
'rtems/rtems-bsd-get-allocator-domain-size.c',
- 'rtems/rtems-bsd-get-ethernet-addr.c',
'rtems/rtems-bsd-get-mac-address.c',
'rtems/rtems-bsd-get-task-priority.c',
'rtems/rtems-bsd-get-task-stack-size.c',
- 'rtems/rtems-bsd-ifconfig.c',
- 'rtems/rtems-bsd-ifconfig-lo0.c',
- 'rtems/rtems-bsd-init-dhcp.c',
- 'rtems/rtems-bsd-rc-conf-net.c',
- 'rtems/rtems-bsd-rc-conf-pf.c',
- 'rtems/rtems-bsd-rc-conf.c',
- 'rtems/rtems-bsd-set-if-input.c',
- 'rtems/rtems-bsd-shell-arp.c',
- 'rtems/rtems-bsd-shell-i2c.c',
- 'rtems/rtems-bsd-shell-ifconfig.c',
- 'rtems/rtems-bsd-shell-netstat.c',
- 'rtems/rtems-bsd-shell-nvmecontrol.c',
- 'rtems/rtems-bsd-shell-pfctl.c',
- 'rtems/rtems-bsd-shell-ping.c',
- 'rtems/rtems-bsd-shell-route.c',
- 'rtems/rtems-bsd-shell-stty.c',
- 'rtems/rtems-bsd-shell-sysctl.c',
- 'rtems/rtems-bsd-shell-tcpdump.c',
- 'rtems/rtems-bsd-shell-vmstat.c',
- 'rtems/rtems-bsd-shell-wlanstats.c',
'rtems/rtems-bsd-syscall-api.c',
'rtems/rtems-kernel-assert.c',
'rtems/rtems-kernel-autoconf.c',
@@ -229,16 +221,65 @@ class rtems(builder.Module):
'rtems/rtems-legacy-rtrequest.c',
'rtems/rtems-legacy-newproc.c',
'rtems/rtems-legacy-mii.c',
+ 'sys/arm/lpc/if_lpe.c',
+ 'sys/arm/lpc/lpc_pwr.c',
+ 'sys/dev/atsam/if_atsam.c',
+ 'sys/dev/atsam/if_atsam_media.c',
+ 'sys/dev/dw_mmc/dw_mmc.c',
+ 'sys/dev/ffec/if_ffec_mcf548x.c',
+ 'sys/dev/ffec/if_ffec_mpc8xx.c',
+ 'sys/dev/input/touchscreen/tsc_lpc32xx.c',
+ 'sys/dev/mmc/st-sdmmc.c',
+ 'sys/dev/mmc/st-sdmmc-config.c',
+ 'sys/dev/smc/if_smc_nexus.c',
+ 'sys/dev/stmac/if_stmac.c',
+ 'sys/dev/tsec/if_tsec_nexus.c',
+ 'sys/dev/usb/controller/ehci_mpc83xx.c',
+ 'sys/dev/usb/controller/dwc_otg_nexus.c',
+ 'sys/dev/usb/controller/dwc_otg_stm32h7.c',
+ 'sys/dev/usb/controller/ohci_lpc32xx.c',
+ 'sys/dev/usb/controller/ohci_lpc.c',
+ 'sys/dev/usb/controller/usb_otg_transceiver.c',
+ 'sys/dev/usb/controller/usb_otg_transceiver_dump.c',
+ 'sys/fs/devfs/devfs_devs.c',
+ 'sys/net/if_ppp.c',
+ 'sys/net/ppp_tty.c',
+ ],
+ mm.generator['source']()
+ )
+ self.addRTEMSUserSourceFiles(
+ [
+ 'rtems/ipsec_get_policylen.c',
+ 'rtems/rtems-bsd-arp-processor.c',
+ 'rtems/rtems-bsd-cxx.cc',
+ 'rtems/rtems-bsd-get-ethernet-addr.c',
+ 'rtems/rtems-bsd-ifconfig.c',
+ 'rtems/rtems-bsd-ifconfig-lo0.c',
+ 'rtems/rtems-bsd-init-dhcp.c',
+ 'rtems/rtems-bsd-rc-conf-net.c',
+ 'rtems/rtems-bsd-rc-conf-pf.c',
+ 'rtems/rtems-bsd-rc-conf.c',
+ 'rtems/rtems-bsd-set-if-input.c',
+ 'rtems/rtems-bsd-shell-arp.c',
+ 'rtems/rtems-bsd-shell-i2c.c',
+ 'rtems/rtems-bsd-shell-ifconfig.c',
+ 'rtems/rtems-bsd-shell-ifmcstat.c',
+ 'rtems/rtems-bsd-shell-netstat.c',
+ 'rtems/rtems-bsd-shell-nvmecontrol.c',
+ 'rtems/rtems-bsd-shell-pfctl.c',
+ 'rtems/rtems-bsd-shell-ping.c',
+ 'rtems/rtems-bsd-shell-route.c',
+ 'rtems/rtems-bsd-shell-stty.c',
+ 'rtems/rtems-bsd-shell-sysctl.c',
+ 'rtems/rtems-bsd-shell-tcpdump.c',
+ 'rtems/rtems-bsd-shell-vmstat.c',
+ 'rtems/rtems-bsd-shell-wlanstats.c',
'rtems/rtems-kvm.c',
'rtems/rtems-program.c',
'rtems/rtems-program-socket.c',
'rtems/rtems-routes.c',
'rtems/syslog.c',
'ftpd/ftpd-service.c',
- 'nfsclient/mount_prot_xdr.c',
- 'nfsclient/nfs.c',
- 'nfsclient/nfs_prot_xdr.c',
- 'nfsclient/rpcio.c',
'pppd/auth.c',
'pppd/ccp.c',
'pppd/chap.c',
@@ -255,39 +296,24 @@ class rtems(builder.Module):
'pppd/sys-rtems.c',
'pppd/upap.c',
'pppd/utils.c',
- 'sys/arm/lpc/if_lpe.c',
- 'sys/arm/lpc/lpc_pwr.c',
- 'sys/dev/atsam/if_atsam.c',
- 'sys/dev/atsam/if_atsam_media.c',
- 'sys/dev/dw_mmc/dw_mmc.c',
- 'sys/dev/ffec/if_ffec_mcf548x.c',
- 'sys/dev/ffec/if_ffec_mpc8xx.c',
- 'sys/dev/input/touchscreen/tsc_lpc32xx.c',
- 'sys/dev/smc/if_smc_nexus.c',
- 'sys/dev/tsec/if_tsec_nexus.c',
- 'sys/dev/usb/controller/ehci_mpc83xx.c',
- 'sys/dev/usb/controller/ohci_lpc32xx.c',
- 'sys/dev/usb/controller/ohci_lpc.c',
- 'sys/dev/usb/controller/usb_otg_transceiver.c',
- 'sys/dev/usb/controller/usb_otg_transceiver_dump.c',
- 'sys/fs/devfs/devfs_devs.c',
- 'sys/net/if_ppp.c',
- 'sys/net/ppp_tty.c',
'telnetd/telnetd-service.c',
],
mm.generator['source']()
)
- self.addFile(mm.generator['file']('rtems/rtems-kernel-kvm-symbols.c',
+ self.addFile(mm.generator['file']('kernel',
+ 'rtems/rtems-kernel-kvm-symbols.c',
mm.generator['rtems-path'](),
mm.generator['no-convert'](),
mm.generator['no-convert'](),
mm.generator['kvm-symbols'](includes = 'rtemsbsd/rtems')))
- self.addFile(mm.generator['file']('lib/libc/net/nslexer.l',
+ self.addFile(mm.generator['file']('user',
+ 'lib/libc/net/nslexer.l',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
mm.generator['lex']('_nsyy', 'nsparser.c')))
- self.addFile(mm.generator['file']('lib/libc/net/nsparser.y',
+ self.addFile(mm.generator['file']('user',
+ 'lib/libc/net/nsparser.y',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -322,6 +348,8 @@ class base(builder.Module):
'sys/contrib/ck/include/ck_string.h',
'sys/contrib/ck/include/gcc/aarch64/ck_f_pr.h',
'sys/contrib/ck/include/gcc/aarch64/ck_pr.h',
+ 'sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h',
+ 'sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h',
'sys/contrib/ck/include/gcc/arm/ck_f_pr.h',
'sys/contrib/ck/include/gcc/arm/ck_pr.h',
'sys/contrib/ck/include/gcc/ck_cc.h',
@@ -338,8 +366,6 @@ class base(builder.Module):
'sys/contrib/ck/include/gcc/x86/ck_f_pr.h',
'sys/contrib/ck/include/gcc/x86/ck_pr.h',
'sys/fs/devfs/devfs_int.h',
- 'sys/rpc/netconfig.h',
- 'sys/rpc/types.h',
'sys/security/audit/audit.h',
'sys/security/mac/mac_framework.h',
'sys/sys/acl.h',
@@ -527,7 +553,7 @@ class base(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'COPYRIGHT'
]
@@ -570,7 +596,7 @@ class fdt(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'rtems/ofw_machdep.c',
],
@@ -607,7 +633,7 @@ class tty(builder.Module):
],
mm.generator['source']()
)
-# self.addRTEMSSourceFiles(
+# self.addRTEMSKernelSourceFiles(
# [
# 'rtems/ofw_machdep.c',
# ],
@@ -644,7 +670,7 @@ class mmc(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'sys/arm/at91/at91_mci.c',
],
@@ -683,7 +709,7 @@ class mmc_ti(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/sdhci_if.c',
'local/gpiobus_if.c',
@@ -773,7 +799,7 @@ class iic(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/iicbus_if.c',
'sys/dev/iicbus/rtems-i2c.c',
@@ -827,7 +853,7 @@ class display(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/clknode_if.c',
'local/hdmi_if.c',
@@ -848,20 +874,16 @@ class pinmux(builder.Module):
mm = self.manager
self.addKernelSpaceHeaderFiles(
[
- 'sys/arm/ti/ti_pinmux.h',
'sys/arm/ti/omap4/omap4_scm_padconf.h',
- 'sys/arm/ti/am335x/am335x_scm_padconf.h',
]
)
self.addKernelSpaceSourceFiles(
[
- 'sys/arm/ti/ti_pinmux.c',
'sys/dev/fdt/fdt_pinctrl.c',
- 'sys/arm/ti/am335x/am335x_scm_padconf.c',
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/fdt_pinctrl_if.c',
],
@@ -869,6 +891,24 @@ class pinmux(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):
@@ -903,6 +943,7 @@ class dev_usb(builder.Module):
'sys/dev/usb/usb_ioctl.h',
'sys/dev/usb/usb_mbuf.h',
'sys/dev/usb/usb_msctest.h',
+ 'sys/dev/usb/usb_pci.h',
'sys/dev/usb/usb_pf.h',
'sys/dev/usb/usb_process.h',
'sys/dev/usb/usb_request.h',
@@ -945,7 +986,7 @@ class dev_usb_controller(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/controller/ohci.h',
@@ -965,6 +1006,7 @@ class dev_usb_controller(builder.Module):
[
'sys/dev/usb/controller/ohci.c',
'sys/dev/usb/controller/ehci.c',
+ 'sys/dev/usb/controller/ehci_pci.c',
'sys/dev/usb/controller/usb_controller.c',
'sys/dev/usb/controller/dwc_otg.c',
'sys/dev/usb/controller/dwc_otg_fdt.c',
@@ -984,7 +1026,7 @@ class dev_usb_input(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/input/usb_rdesc.h',
@@ -1012,7 +1054,7 @@ class dev_usb_net(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/net/if_auereg.h',
@@ -1062,7 +1104,7 @@ class dev_usb_quirk(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/quirk/usb_quirk.h',
@@ -1085,7 +1127,7 @@ class dev_usb_serial(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/serial/uftdi_reg.h',
@@ -1130,7 +1172,7 @@ class dev_usb_storage(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceSourceFiles(
[
'sys/dev/usb/storage/umass.c',
@@ -1148,7 +1190,7 @@ class dev_usb_controller_bbb(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/arm/ti/ti_cpuid.h',
@@ -1185,7 +1227,7 @@ class dev_usb_wlan(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/usb/wlan/if_rsureg.h',
@@ -1205,7 +1247,7 @@ class dev_usb_wlan(builder.Module):
'sys/dev/usb/wlan/if_zydreg.h',
]
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/runfw.c',
],
@@ -1235,7 +1277,7 @@ class dev_wlan_rtwn(builder.Module):
def generate(self):
mm = self.manager
- self.addDependency(mm['dev_usb'])
+ self.addDependency('dev_usb')
self.addKernelSpaceHeaderFiles(
[
'sys/dev/rtwn/if_rtwn_beacon.h',
@@ -1317,7 +1359,7 @@ class dev_wlan_rtwn(builder.Module):
'sys/dev/rtwn/usb/rtwn_usb_var.h',
]
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/rtwn-rtl8192cfwT.c',
'local/rtwn-rtl8188eufw.c',
@@ -1498,10 +1540,14 @@ class dev_net(builder.Module):
'sys/net/if_types.h',
'sys/net/if_var.h',
'sys/net/vnet.h',
+ 'sys/net/mp_ring.h',
+ 'sys/net/iflib_private.h',
]
)
self.addKernelSpaceSourceFiles(
[
+ 'sys/net/iflib.c',
+ 'sys/net/mp_ring.c',
'sys/arm/ti/cpsw/if_cpsw.c',
'sys/dev/ffec/if_ffec.c',
'sys/dev/mii/mii.c',
@@ -1522,7 +1568,7 @@ class dev_net(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'sys/dev/mii/ksz8091rnb_50MHz.c',
],
@@ -1553,13 +1599,16 @@ class dev_nic(builder.Module):
)
self.addCPUDependentFreeBSDHeaderFiles(
[
+ 'sys/arm64/include/armreg.h',
+ 'sys/arm64/include/cpufunc.h',
+ 'sys/arm64/include/cpu.h',
'sys/arm/include/cpufunc.h',
- 'sys/i386/include/specialreg.h',
'sys/i386/include/md_var.h',
- 'sys/i386/include/intr_machdep.h',
'sys/x86/include/intr_machdep.h',
- 'sys/x86/include/metadata.h',
'sys/i386/include/cpufunc.h',
+ 'sys/x86/include/intr_machdep.h',
+ 'sys/x86/include/specialreg.h',
+ 'sys/x86/include/x86_var.h',
'sys/mips/include/cpufunc.h',
'sys/mips/include/cpuregs.h',
'sys/powerpc/include/cpufunc.h',
@@ -1683,7 +1732,7 @@ class dev_nic_e1000(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'sys/dev/e1000/LICENSE'
]
@@ -1821,7 +1870,7 @@ class nvme(builder.Module):
],
mm.generator['source']()
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'sys/dev/nvd/nvd.c',
],
@@ -2230,14 +2279,16 @@ class netipsec(builder.Module):
],
mm.generator['source'](libipsec_cflags)
)
- self.addFile(mm.generator['file']('ipsec-tools/src/libipsec/policy_token.l',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/libipsec/policy_token.l',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
mm.generator['lex']('__libipsec',
'policy_parse.c',
libipsec_cflags)))
- self.addFile(mm.generator['file']('ipsec-tools/src/libipsec/policy_parse.y',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/libipsec/policy_parse.y',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2299,7 +2350,8 @@ class netipsec(builder.Module):
mm.generator['source'](racoon_cflags,
['freebsd/crypto/openssl'])
)
- self.addFile(mm.generator['file']('ipsec-tools/src/racoon/cftoken.l',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/racoon/cftoken.l',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2307,7 +2359,8 @@ class netipsec(builder.Module):
'cftoken.c',
racoon_cflags,
build=False)))
- self.addFile(mm.generator['file']('ipsec-tools/src/racoon/cfparse.y',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/racoon/cfparse.y',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2315,7 +2368,8 @@ class netipsec(builder.Module):
'cfparse.h',
racoon_cflags,
build=False)))
- self.addFile(mm.generator['file']('ipsec-tools/src/racoon/prsa_tok.l',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/racoon/prsa_tok.l',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2323,7 +2377,8 @@ class netipsec(builder.Module):
'prsa_tok.c',
racoon_cflags,
build=False)))
- self.addFile(mm.generator['file']('ipsec-tools/src/racoon/prsa_par.y',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/racoon/prsa_par.y',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2336,7 +2391,7 @@ class netipsec(builder.Module):
'rtems/ipsec.h',
]
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-racoon.c',
'rtems/rtems-bsd-rc-conf-ipsec.c',
@@ -2356,7 +2411,8 @@ class netipsec(builder.Module):
],
mm.generator['source'](setkey_cflags)
)
- self.addFile(mm.generator['file']('ipsec-tools/src/setkey/token.l',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/setkey/token.l',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2364,7 +2420,8 @@ class netipsec(builder.Module):
'token.c',
setkey_cflags,
build=False)))
- self.addFile(mm.generator['file']('ipsec-tools/src/setkey/parse.y',
+ self.addFile(mm.generator['file']('user',
+ 'ipsec-tools/src/setkey/parse.y',
mm.generator['path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2372,7 +2429,7 @@ class netipsec(builder.Module):
'parse.h',
setkey_cflags,
build=False)))
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-shell-setkey.c',
],
@@ -2555,7 +2612,7 @@ class opencrypto(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'sys/contrib/libsodium/LICENSE'
]
@@ -2715,6 +2772,156 @@ class pf(builder.Module):
)
#
+# RPC for user space, remove when NFSv2 is removed
+#
+class rpc_user(builder.Module):
+
+ def __init__(self, manager):
+ super(rpc_user, self).__init__(manager, type(self).__name__)
+
+ def generate(self):
+ mm = self.manager
+ # User space support for legacy nfsv2 client, remove when nfsv2 is removed
+ self.addUserSpaceHeaderFiles(
+ [
+ 'include/rpc/clnt.h',
+ 'include/rpc/pmap_rmt.h',
+ 'include/rpc/svc_soc.h',
+ 'include/rpc/nettype.h',
+ 'include/rpc/xdr.h',
+ 'include/rpc/svc.h',
+ 'include/rpc/rpc_msg.h',
+ 'include/rpc/rpcsec_gss.h',
+ 'include/rpc/raw.h',
+ 'include/rpc/clnt_stat.h',
+ 'include/rpc/auth.h',
+ 'include/rpc/svc_dg.h',
+ 'include/rpc/auth_kerb.h',
+ 'include/rpc/auth_des.h',
+ 'include/rpc/rpcb_clnt.h',
+ 'include/rpc/rpc.h',
+ 'include/rpc/des.h',
+ 'include/rpc/des_crypt.h',
+ 'include/rpc/svc_auth.h',
+ 'include/rpc/pmap_clnt.h',
+ 'include/rpc/clnt_soc.h',
+ 'include/rpc/pmap_prot.h',
+ 'include/rpc/auth_unix.h',
+ 'include/rpc/rpc_com.h',
+ 'include/rpc/rpcent.h',
+ 'include/rpcsvc/nis_db.h',
+ 'include/rpcsvc/nislib.h',
+ 'include/rpcsvc/nis_tags.h',
+ 'include/rpcsvc/ypclnt.h',
+ 'include/rpcsvc/yp_prot.h',
+ 'lib/libc/rpc/mt_misc.h',
+ 'lib/libc/rpc/rpc_com.h',
+ ]
+ )
+ self.addFile(mm.generator['file']('user',
+ 'include/rpc/rpcb_prot.x',
+ mm.generator['freebsd-path'](),
+ mm.generator['convert'](),
+ mm.generator['convert'](),
+ mm.generator['rpc-gen']()))
+ self.addFile(mm.generator['file']('user',
+ 'include/rpcsvc/nis.x',
+ mm.generator['freebsd-path'](),
+ mm.generator['convert'](),
+ mm.generator['convert'](),
+ mm.generator['rpc-gen']()))
+ self.addUserSpaceSourceFiles(
+ [
+ 'lib/libc/rpc/auth_des.c',
+ 'lib/libc/rpc/authdes_prot.c',
+ 'lib/libc/rpc/auth_none.c',
+ 'lib/libc/rpc/auth_time.c',
+ 'lib/libc/rpc/auth_unix.c',
+ 'lib/libc/rpc/authunix_prot.c',
+ 'lib/libc/rpc/bindresvport.c',
+ 'lib/libc/rpc/clnt_bcast.c',
+ 'lib/libc/rpc/clnt_dg.c',
+ 'lib/libc/rpc/clnt_generic.c',
+ 'lib/libc/rpc/clnt_perror.c',
+ 'lib/libc/rpc/clnt_raw.c',
+ 'lib/libc/rpc/clnt_simple.c',
+ 'lib/libc/rpc/clnt_vc.c',
+ 'lib/libc/rpc/crypt_client.c',
+ 'lib/libc/rpc/des_crypt.c',
+ 'lib/libc/rpc/des_soft.c',
+ 'lib/libc/rpc/getnetconfig.c',
+ 'lib/libc/rpc/getnetpath.c',
+ 'lib/libc/rpc/getpublickey.c',
+ 'lib/libc/rpc/getrpcent.c',
+ 'lib/libc/rpc/getrpcport.c',
+ 'lib/libc/rpc/key_call.c',
+ 'lib/libc/rpc/key_prot_xdr.c',
+ 'lib/libc/rpc/mt_misc.c',
+ 'lib/libc/rpc/netname.c',
+ 'lib/libc/rpc/netnamer.c',
+ 'lib/libc/rpc/pmap_clnt.c',
+ 'lib/libc/rpc/pmap_getmaps.c',
+ 'lib/libc/rpc/pmap_getport.c',
+ 'lib/libc/rpc/pmap_prot2.c',
+ 'lib/libc/rpc/pmap_prot.c',
+ 'lib/libc/rpc/pmap_rmt.c',
+ 'lib/libc/rpc/rpcb_clnt.c',
+ 'lib/libc/rpc/rpcb_prot.c',
+ 'lib/libc/rpc/rpcb_st_xdr.c',
+ 'lib/libc/rpc/rpc_callmsg.c',
+ 'lib/libc/rpc/rpc_commondata.c',
+ 'lib/libc/rpc/rpcdname.c',
+ 'lib/libc/rpc/rpc_dtablesize.c',
+ 'lib/libc/rpc/rpc_generic.c',
+ 'lib/libc/rpc/rpc_prot.c',
+ 'lib/libc/rpc/rpcsec_gss_stub.c',
+ 'lib/libc/rpc/rpc_soc.c',
+ 'lib/libc/rpc/rtime.c',
+ 'lib/libc/rpc/svc_auth.c',
+ 'lib/libc/rpc/svc_auth_des.c',
+ 'lib/libc/rpc/svc_auth_unix.c',
+ 'lib/libc/rpc/svc.c',
+ 'lib/libc/rpc/svc_dg.c',
+ 'lib/libc/rpc/svc_generic.c',
+ 'lib/libc/rpc/svc_raw.c',
+ 'lib/libc/rpc/svc_run.c',
+ 'lib/libc/rpc/svc_simple.c',
+ 'lib/libc/rpc/svc_vc.c',
+ 'lib/libc/xdr/xdr_array.c',
+ 'lib/libc/xdr/xdr.c',
+ 'lib/libc/xdr/xdr_float.c',
+ 'lib/libc/xdr/xdr_mem.c',
+ 'lib/libc/xdr/xdr_rec.c',
+ 'lib/libc/xdr/xdr_reference.c',
+ 'lib/libc/xdr/xdr_sizeof.c',
+ 'lib/libc/xdr/xdr_stdio.c',
+ ],
+ mm.generator['source'](['-DINET'])
+ )
+
+#
+# NFSv2 Client
+#
+class nfsv2(builder.Module):
+
+ def __init__(self, manager):
+ super(nfsv2, self).__init__(manager, type(self).__name__)
+
+ def generate(self):
+ mm = self.manager
+ self.addDependency('rpc_user')
+ self.addRTEMSUserSourceFiles(
+ [
+ 'nfsclient/mount_prot_xdr.c',
+ 'nfsclient/nfs.c',
+ 'nfsclient/nfs_prot_xdr.c',
+ 'nfsclient/rpcio.c',
+ ],
+ mm.generator['source']()
+ )
+
+
+#
# PCI
#
class pci(builder.Module):
@@ -2745,14 +2952,13 @@ class pci(builder.Module):
self.addCPUDependentFreeBSDHeaderFiles(
[
'sys/i386/include/_bus.h',
- 'sys/i386/include/bus.h',
'sys/x86/include/legacyvar.h',
'sys/x86/include/bus.h',
'sys/x86/include/pci_cfgreg.h',
]
)
self.addCPUDependentFreeBSDSourceFiles(
- [ 'i386' ],
+ [ 'i386', 'powerpc' ],
[
'sys/x86/x86/legacy.c',
'sys/x86/pci/pci_bus.c',
@@ -2760,6 +2966,7 @@ class pci(builder.Module):
mm.generator['source']()
)
+
#
# User space
#
@@ -2796,36 +3003,6 @@ class user_space(builder.Module):
'include/nsswitch.h',
'include/resolv.h',
'include/res_update.h',
- 'include/rpc/clnt.h',
- 'include/rpc/pmap_rmt.h',
- 'include/rpc/svc_soc.h',
- 'include/rpc/nettype.h',
- 'include/rpc/xdr.h',
- 'include/rpc/svc.h',
- 'include/rpc/rpc_msg.h',
- 'include/rpc/rpcsec_gss.h',
- 'include/rpc/raw.h',
- 'include/rpc/clnt_stat.h',
- 'include/rpc/auth.h',
- 'include/rpc/svc_dg.h',
- 'include/rpc/auth_kerb.h',
- 'include/rpc/auth_des.h',
- 'include/rpc/rpcb_clnt.h',
- 'include/rpc/rpc.h',
- 'include/rpc/des.h',
- 'include/rpc/des_crypt.h',
- 'include/rpc/svc_auth.h',
- 'include/rpc/pmap_clnt.h',
- 'include/rpc/clnt_soc.h',
- 'include/rpc/pmap_prot.h',
- 'include/rpc/auth_unix.h',
- 'include/rpc/rpc_com.h',
- 'include/rpc/rpcent.h',
- 'include/rpcsvc/nis_db.h',
- 'include/rpcsvc/nislib.h',
- 'include/rpcsvc/nis_tags.h',
- 'include/rpcsvc/ypclnt.h',
- 'include/rpcsvc/yp_prot.h',
'include/sysexits.h',
'lib/lib80211/lib80211_ioctl.h',
'lib/lib80211/lib80211_regdomain.h',
@@ -2855,8 +3032,6 @@ class user_space(builder.Module):
'lib/libc/net/res_config.h',
'lib/libc/resolv/res_debug.h',
'lib/libc/resolv/res_private.h',
- 'lib/libc/rpc/mt_misc.h',
- 'lib/libc/rpc/rpc_com.h',
'lib/libc/stdio/local.h',
'lib/libkvm/kvm.h',
'lib/libmemstat/memstat.h',
@@ -2872,22 +3047,14 @@ class user_space(builder.Module):
'usr.bin/netstat/netstat.h'
]
)
- self.addFile(mm.generator['file']('include/rpc/rpcb_prot.x',
- mm.generator['freebsd-path'](),
- mm.generator['convert'](),
- mm.generator['convert'](),
- mm.generator['rpc-gen']()))
- self.addFile(mm.generator['file']('include/rpcsvc/nis.x',
- mm.generator['freebsd-path'](),
- mm.generator['convert'](),
- mm.generator['convert'](),
- mm.generator['rpc-gen']()))
- self.addFile(mm.generator['file']('sbin/route/keywords',
+ self.addFile(mm.generator['file']('user',
+ 'sbin/route/keywords',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
mm.generator['route-keywords']()))
- self.addFile(mm.generator['file']('sbin/pfctl/parse.y',
+ self.addFile(mm.generator['file']('user',
+ 'sbin/pfctl/parse.y',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -2927,7 +3094,7 @@ class user_space(builder.Module):
'include/machine/rtems-bsd-regdomain.h',
]
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-regdomain.c',
],
@@ -3022,72 +3189,9 @@ class user_space(builder.Module):
'lib/libc/resolv/res_send.c',
'lib/libc/resolv/res_state.c',
'lib/libc/resolv/res_update.c',
- 'lib/libc/rpc/auth_des.c',
- 'lib/libc/rpc/authdes_prot.c',
- 'lib/libc/rpc/auth_none.c',
- 'lib/libc/rpc/auth_time.c',
- 'lib/libc/rpc/auth_unix.c',
- 'lib/libc/rpc/authunix_prot.c',
- 'lib/libc/rpc/bindresvport.c',
- 'lib/libc/rpc/clnt_bcast.c',
- 'lib/libc/rpc/clnt_dg.c',
- 'lib/libc/rpc/clnt_generic.c',
- 'lib/libc/rpc/clnt_perror.c',
- 'lib/libc/rpc/clnt_raw.c',
- 'lib/libc/rpc/clnt_simple.c',
- 'lib/libc/rpc/clnt_vc.c',
- 'lib/libc/rpc/crypt_client.c',
- 'lib/libc/rpc/des_crypt.c',
- 'lib/libc/rpc/des_soft.c',
- 'lib/libc/rpc/getnetconfig.c',
- 'lib/libc/rpc/getnetpath.c',
- 'lib/libc/rpc/getpublickey.c',
- 'lib/libc/rpc/getrpcent.c',
- 'lib/libc/rpc/getrpcport.c',
- 'lib/libc/rpc/key_call.c',
- 'lib/libc/rpc/key_prot_xdr.c',
- 'lib/libc/rpc/mt_misc.c',
- 'lib/libc/rpc/netname.c',
- 'lib/libc/rpc/netnamer.c',
- 'lib/libc/rpc/pmap_clnt.c',
- 'lib/libc/rpc/pmap_getmaps.c',
- 'lib/libc/rpc/pmap_getport.c',
- 'lib/libc/rpc/pmap_prot2.c',
- 'lib/libc/rpc/pmap_prot.c',
- 'lib/libc/rpc/pmap_rmt.c',
- 'lib/libc/rpc/rpcb_clnt.c',
- 'lib/libc/rpc/rpcb_prot.c',
- 'lib/libc/rpc/rpcb_st_xdr.c',
- 'lib/libc/rpc/rpc_callmsg.c',
- 'lib/libc/rpc/rpc_commondata.c',
- 'lib/libc/rpc/rpcdname.c',
- 'lib/libc/rpc/rpc_dtablesize.c',
- 'lib/libc/rpc/rpc_generic.c',
- 'lib/libc/rpc/rpc_prot.c',
- 'lib/libc/rpc/rpcsec_gss_stub.c',
- 'lib/libc/rpc/rpc_soc.c',
- 'lib/libc/rpc/rtime.c',
- 'lib/libc/rpc/svc_auth.c',
- 'lib/libc/rpc/svc_auth_des.c',
- 'lib/libc/rpc/svc_auth_unix.c',
- 'lib/libc/rpc/svc.c',
- 'lib/libc/rpc/svc_dg.c',
- 'lib/libc/rpc/svc_generic.c',
- 'lib/libc/rpc/svc_raw.c',
- 'lib/libc/rpc/svc_run.c',
- 'lib/libc/rpc/svc_simple.c',
- 'lib/libc/rpc/svc_vc.c',
'lib/libc/stdio/fgetln.c',
'lib/libc/stdlib/strtonum.c',
'lib/libc/string/strsep.c',
- 'lib/libc/xdr/xdr_array.c',
- 'lib/libc/xdr/xdr.c',
- 'lib/libc/xdr/xdr_float.c',
- 'lib/libc/xdr/xdr_mem.c',
- 'lib/libc/xdr/xdr_rec.c',
- 'lib/libc/xdr/xdr_reference.c',
- 'lib/libc/xdr/xdr_sizeof.c',
- 'lib/libc/xdr/xdr_stdio.c',
'lib/libmemstat/memstat_all.c',
'lib/libmemstat/memstat.c',
'lib/libmemstat/memstat_malloc.c',
@@ -3159,10 +3263,12 @@ class user_space(builder.Module):
'usr.bin/vmstat/vmstat.c',
'usr.sbin/arp/arp.c',
'usr.sbin/i2c/i2c.c',
+ 'usr.sbin/ifmcstat/ifmcstat.c',
+ 'usr.sbin/ifmcstat/printb.c',
],
mm.generator['source'](['-DINET'])
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/libxo/LICENSE'
]
@@ -3203,6 +3309,7 @@ class crypto_openssl(builder.Module):
def generate(self):
mm = self.manager
+ self.addDependency('user_space')
self.addUserSpaceHeaderFiles(
[
'crypto/openssl/crypto/aes/aes_locl.h',
@@ -4156,17 +4263,19 @@ class crypto_openssl(builder.Module):
'freebsd/crypto/openssl/crypto/ec/curve448',
'freebsd/crypto/openssl/crypto/ec/curve448/arch_32'])
)
- self.addFile(mm.generator['file']('crypto/openssl/crypto/LPdir_unix.c',
+ self.addFile(mm.generator['file']('user',
+ 'crypto/openssl/crypto/LPdir_unix.c',
mm.generator['freebsd-path'](),
mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](),
mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](),
- mm.generator['buildSystemFragmentComposer']()))
- self.addFile(mm.generator['file']('crypto/openssl/crypto/ec/ecp_nistz256_table.c',
+ mm.generator['buildSystemComposer']()))
+ self.addFile(mm.generator['file']('user',
+ 'crypto/openssl/crypto/ec/ecp_nistz256_table.c',
mm.generator['freebsd-path'](),
mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](),
mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](),
- mm.generator['buildSystemFragmentComposer']()))
- self.addPlainTextFile(
+ mm.generator['buildSystemComposer']()))
+ self.addPlainTextFiles(
[
'crypto/openssl/LICENSE'
]
@@ -4175,7 +4284,6 @@ class crypto_openssl(builder.Module):
#
# /usr/bin/openssl
#
-# depends on crypto_openssl, user_space
class usr_bin_openssl(builder.Module):
def __init__(self, manager):
@@ -4183,6 +4291,7 @@ class usr_bin_openssl(builder.Module):
def generate(self):
mm = self.manager
+ self.addDependency('crypto_openssl')
self.addUserSpaceHeaderFiles(
[
'crypto/openssl/apps/apps.h',
@@ -4255,7 +4364,7 @@ class usr_bin_openssl(builder.Module):
['freebsd/crypto/openssl']
)
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-shell-openssl.c',
],
@@ -4304,7 +4413,7 @@ class contrib_expat(builder.Module):
],
mm.generator['source'](cflags)
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/expat/COPYING'
]
@@ -4375,14 +4484,16 @@ class contrib_libpcap(builder.Module):
)
gen_cflags = cflags + ['-DNEED_YYPARSE_WRAPPER=1',
'-Dyylval=pcap_lval']
- self.addFile(mm.generator['file']('contrib/libpcap/scanner.l',
+ self.addFile(mm.generator['file']('user',
+ 'contrib/libpcap/scanner.l',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
mm.generator['lex']('pcap',
'scanner.c',
gen_cflags)))
- self.addFile(mm.generator['file']('contrib/libpcap/grammar.y',
+ self.addFile(mm.generator['file']('user',
+ 'contrib/libpcap/grammar.y',
mm.generator['freebsd-path'](),
mm.generator['convert'](),
mm.generator['convert'](),
@@ -4407,7 +4518,7 @@ class contrib_libpcap(builder.Module):
],
mm.generator['source'](cflags)
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/libpcap/LICENSE'
]
@@ -4652,7 +4763,7 @@ class usr_sbin_tcpdump(builder.Module):
['freebsd/contrib/tcpdump',
'freebsd/usr.sbin/tcpdump/tcpdump'])
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/tcpdump/LICENSE'
]
@@ -4963,7 +5074,7 @@ class usr_sbin_wpa_supplicant(builder.Module):
'freebsd/usr.sbin/wpa/wpa_supplicant',
'freebsd/crypto/openssl/crypto'])
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-shell-wpa_supplicant.c',
'rtems/rtems-wpa_supplicant_mutex.c',
@@ -4971,7 +5082,7 @@ class usr_sbin_wpa_supplicant(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/wpa/COPYING'
]
@@ -5000,6 +5111,13 @@ class in_cksum(builder.Module):
]
)
self.addTargetSourceCPUDependentHeaderFiles(
+ [ 'arm64' ],
+ 'arm64',
+ [
+ 'sys/arm64/include/in_cksum.h',
+ ]
+ )
+ self.addTargetSourceCPUDependentHeaderFiles(
[ 'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k',
'nios2', 'sh', 'sparc', 'v850' ],
'mips',
@@ -5008,6 +5126,13 @@ class in_cksum(builder.Module):
]
)
self.addCPUDependentFreeBSDSourceFiles(
+ [ 'aarch64', 'arm64' ],
+ [
+ 'sys/arm64/arm64/in_cksum.c',
+ ],
+ mm.generator['source']()
+ )
+ self.addCPUDependentFreeBSDSourceFiles(
[ 'i386' ],
[
'sys/i386/i386/in_cksum.c',
@@ -5031,7 +5156,8 @@ class in_cksum(builder.Module):
self.addCPUDependentFreeBSDSourceFiles(
[
'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k',
- 'mips', 'nios2', 'riscv', 'sh', 'sparc', 'v850'
+ 'mips', 'moxie', 'nios2', 'or1k', 'riscv', 'sh', 'sparc',
+ 'v850'
],
[
'sys/mips/mips/in_cksum.c',
@@ -5077,7 +5203,7 @@ class dhcpcd(builder.Module):
],
mm.generator['source']('-D__FreeBSD__ -DTHERE_IS_NO_FORK -DMASTER_ONLY -DINET')
)
- self.addRTEMSSourceFiles(
+ self.addRTEMSUserSourceFiles(
[
'rtems/rtems-bsd-shell-dhcpcd.c',
],
@@ -5115,6 +5241,7 @@ class mdnsresponder(builder.Module):
mm.generator['source']()
)
+
class dpaa(builder.Module):
def __init__(self, manager):
@@ -5207,6 +5334,12 @@ class imx(builder.Module):
],
mm.generator['source']()
)
+ self.addRTEMSKernelSourceFiles(
+ [
+ 'sys/arm/freescale/imx/imxrt1166_usbphy.c',
+ ],
+ mm.generator['source']()
+ )
class regulator(builder.Module):
def __init__(self, manager):
@@ -5214,7 +5347,7 @@ class regulator(builder.Module):
def generate(self):
mm = self.manager
- self.addRTEMSSourceFiles(
+ self.addRTEMSKernelSourceFiles(
[
'local/regdev_if.c',
'local/regnode_if.c',
@@ -5249,7 +5382,8 @@ class tests(builder.Module):
def generate(self):
mm = self.manager
self.addTest(mm.generator['test']('epoch01', ['test_main'], extraLibs = ['rtemstest']))
- self.addTest(mm.generator['test']('nfs01', ['test_main'], netTest = True))
+ self.addTest(mm.generator['test']('nfs01', ['test_main'],
+ netTest = True, modules = ['nfsv2']))
self.addTest(mm.generator['test']('foobarclient', ['test_main'],
runTest = False, netTest = True))
self.addTest(mm.generator['test']('foobarserver', ['test_main'],
@@ -5295,9 +5429,10 @@ class tests(builder.Module):
runTest = False, netTest = True,
extraLibs = ['telnetd']))
self.addTest(mm.generator['test']('smp01', ['test_main'], extraLibs = ['rtemstest']))
- self.addTest(mm.generator['test']('media01', ['test_main'],
+ 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))
self.addTest(mm.generator['test']('log01', ['test_main']))
@@ -5338,6 +5473,7 @@ 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']))
def load(mm):
@@ -5355,6 +5491,7 @@ def load(mm):
mm.addModule(evdev(mm))
mm.addModule(iic(mm))
mm.addModule(pinmux(mm))
+ mm.addModule(if_mve(mm))
mm.addModule(display(mm))
mm.addModule(dev_usb(mm))
@@ -5382,6 +5519,9 @@ def load(mm):
mm.addModule(pf(mm))
mm.addModule(dev_net(mm))
+ mm.addModule(rpc_user(mm))
+ mm.addModule(nfsv2(mm))
+
# Add PCI
mm.addModule(pci(mm))
@@ -5415,7 +5555,5 @@ def load(mm):
mm.addModule(tests(mm))
- mm.setModuleConfigiuration()
-
# XXX TODO Check that no file is also listed in empty
# XXX TODO Check that no file in in two modules
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/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 92125619..37008cc6 100644
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -54,7 +54,6 @@ RTEMS_BSD_DRIVER_SMC0(0x4e000000, RVPBXA9_IRQ_ETHERNET);
RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL);
SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus);
SYSINIT_DRIVER_REFERENCE(ti_scm, simplebus);
-SYSINIT_DRIVER_REFERENCE(ti_pinmux, simplebus);
SYSINIT_DRIVER_REFERENCE(am335x_prcm, simplebus);
SYSINIT_DRIVER_REFERENCE(am335x_pwmss, simplebus);
SYSINIT_DRIVER_REFERENCE(am335x_lcd, simplebus);
@@ -109,6 +108,20 @@ RTEMS_BSD_DRIVER_XILINX_ZYNQ_SLCR;
RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM0(ZYNQ_IRQ_ETHERNET_0);
RTEMS_BSD_DRIVER_E1000PHY;
+#elif defined(LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_H)
+
+#include <bsp/irq.h>
+
+RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0(ZYNQMP_IRQ_ETHERNET_0);
+RTEMS_BSD_DRIVER_E1000PHY;
+
+#elif defined(LIBBSP_AARCH64_XILINX_VERSAL_BSP_H)
+
+#include <bsp/irq.h>
+
+RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0(VERSAL_IRQ_ETHERNET_0);
+RTEMS_BSD_DRIVER_E1000PHY;
+
#elif defined(LIBBSP_ARM_ATSAM_BSP_H)
RTEMS_BSD_DRIVER_USB;
@@ -152,6 +165,22 @@ RTEMS_BSD_DRIVER_MMC;
SYSINIT_DRIVER_REFERENCE(ofw_regulator_bus, simplebus);
+#elif defined(LIBBSP_ARM_IMXRT_BSP_H)
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus);
+
+SYSINIT_DRIVER_REFERENCE(ffec, simplebus);
+SYSINIT_DRIVER_REFERENCE(ksz8091rnb, miibus);
+
+#if IMXRT_IS_MIMXRT11xx
+SYSINIT_DRIVER_REFERENCE(ehci, simplebus);
+SYSINIT_DRIVER_REFERENCE(imxrt1166_usbphy, simplebus);
+SYSINIT_DRIVER_REFERENCE(usbus, ehci);
+RTEMS_BSD_DRIVER_USB;
+RTEMS_BSD_DRIVER_USB_MASS;
+#endif /* IMXRT_IS_IMXRT11xx */
+
#elif defined(LIBBSP_ARM_LPC24XX_BSP_H)
RTEMS_BSD_DEFINE_NEXUS_DEVICE(ohci, 0, 0, NULL);
@@ -159,6 +188,31 @@ SYSINIT_DRIVER_REFERENCE(usbus, ohci);
RTEMS_BSD_DRIVER_USB;
RTEMS_BSD_DRIVER_USB_MASS;
+#elif defined(LIBBSP_ARM_STM32H7_BSP_H)
+
+#include <stm32h7xx.h>
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(stmac, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+
+static const rtems_bsd_device_resource dwcotg_res[] = {
+ {
+ .type = RTEMS_BSD_RES_MEMORY,
+ .start_request = 0,
+ .start_actual = USB2_OTG_FS_PERIPH_BASE
+ }, {
+ .type = RTEMS_BSD_RES_IRQ,
+ .start_request = 0,
+ .start_actual = OTG_FS_IRQn
+ }
+};
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(dwcotg, 0, RTEMS_ARRAY_SIZE(dwcotg_res),
+ dwcotg_res);
+RTEMS_BSD_DRIVER_ST_SDMMC(0, SDMMC1_BASE, DLYB_SDMMC1_BASE, SDMMC1_IRQn);
+RTEMS_BSD_DRIVER_MMC;
+RTEMS_BSD_DRIVER_USB;
+RTEMS_BSD_DRIVER_USB_MASS;
+
#elif defined(LIBBSP_I386_PC386_BSP_H)
RTEMS_BSD_DRIVER_PC_LEGACY;
@@ -205,6 +259,15 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL);
SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
-#endif
+#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;
+
+#endif /* LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H */
#endif
diff --git a/rtemsbsd/include/bsp/st-sdmmc-config.h b/rtemsbsd/include/bsp/st-sdmmc-config.h
new file mode 100644
index 00000000..b4415991
--- /dev/null
+++ b/rtemsbsd/include/bsp/st-sdmmc-config.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2021 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 BSP_ST_SDMMC_CONFIG_H
+#define BSP_ST_SDMMC_CONFIG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stm32h7/hal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct st_sdmmc_config {
+ /**
+ * Number of data lines. Can be 1, 4 or 8
+ */
+ uint8_t data_lines;
+ /**
+ * Polarity of the DIR pins. See "SDMMC_POWER" register in the
+ * STM32H7xx data sheet for that. If you don't have the lines, you can
+ * use any value.
+ */
+ bool dirpol;
+ /**
+ * Possible OCR voltages. Should be something like
+ * MMC_OCR_290_300 | MMC_OCR_300_310 depending on card supply.
+ */
+ uint32_t ocr_voltage;
+};
+
+/**
+ * Get hardware specific configuration for SDMMC.
+ *
+ * This function can be overwritten in the application to adapt to another board
+ * configuration. The sdmmc_base points to the base address of the SDMMC
+ * instance. The cfg structure is set to zero before calling this function so
+ * that only the necessary fields have to be initialized.
+ */
+void st_sdmmc_get_config(uintptr_t sdmmc_base, struct st_sdmmc_config *cfg);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* BSP_ST_SDMMC_CONFIG_H */
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/frame.h b/rtemsbsd/include/machine/frame.h
new file mode 100644
index 00000000..936ffd88
--- /dev/null
+++ b/rtemsbsd/include/machine/frame.h
@@ -0,0 +1 @@
+/* EMPTY */
diff --git a/rtemsbsd/include/machine/rtems-bsd-cache.h b/rtemsbsd/include/machine/rtems-bsd-cache.h
index 73b55e25..e292b216 100755
--- a/rtemsbsd/include/machine/rtems-bsd-cache.h
+++ b/rtemsbsd/include/machine/rtems-bsd-cache.h
@@ -45,7 +45,7 @@
#if defined(LIBBSP_ARM_LPC24XX_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && defined(LPC32XX_DISABLE_MMU))
/* No cache */
#elif defined(LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H) || \
- defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H)
+ defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H)
/* With cache, no coherency support in hardware */
#define CPU_DATA_CACHE_ALIGNMENT 32
#elif defined(__GEN83xx_BSP_h)
diff --git a/rtemsbsd/include/machine/rtems-bsd-commands.h b/rtemsbsd/include/machine/rtems-bsd-commands.h
index c471d283..a517ed7b 100644
--- a/rtemsbsd/include/machine/rtems-bsd-commands.h
+++ b/rtemsbsd/include/machine/rtems-bsd-commands.h
@@ -50,6 +50,8 @@ int rtems_bsd_command_arp(int argc, char **argv);
int rtems_bsd_command_ifconfig(int argc, char **argv);
+int rtems_bsd_command_ifmcstat(int argc, char **argv);
+
int rtems_bsd_command_netstat(int argc, char **argv);
int rtems_bsd_command_nvmecontrol(int argc, char **argv);
diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
index 75b744a4..97cdb625 100644
--- a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
+++ b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
@@ -5041,7 +5041,6 @@
#define t_functions _bsd_t_functions
#define t_functions_inited _bsd_t_functions_inited
#define ti_am335x_clk_devmap _bsd_ti_am335x_clk_devmap
-#define ti_am335x_pinmux_dev _bsd_ti_am335x_pinmux_dev
#define tick _bsd_tick
#define ticket_altqs_active _bsd_ticket_altqs_active
#define ticket_altqs_inactive _bsd_ticket_altqs_inactive
@@ -5056,10 +5055,6 @@
#define _timeout_task_init _bsd__timeout_task_init
#define timevaladd _bsd_timevaladd
#define timevalsub _bsd_timevalsub
-#define ti_pinmux_padconf_get _bsd_ti_pinmux_padconf_get
-#define ti_pinmux_padconf_get_gpiomode _bsd_ti_pinmux_padconf_get_gpiomode
-#define ti_pinmux_padconf_set _bsd_ti_pinmux_padconf_set
-#define ti_pinmux_padconf_set_gpiomode _bsd_ti_pinmux_padconf_set_gpiomode
#define ti_prcm_clk_disable _bsd_ti_prcm_clk_disable
#define ti_prcm_clk_enable _bsd_ti_prcm_clk_enable
#define ti_prcm_clk_get_source_freq _bsd_ti_prcm_clk_get_source_freq
diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
index 09bcecf1..f2f1b91f 100644
--- a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
+++ b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h
@@ -55,6 +55,12 @@
/* General define to activate BSD kernel parts */
#define _KERNEL 1
+/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */
+#define IN_HISTORICAL_NETS
+
+/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */
+#define IFCAP_NOMAP 0x4000000
+
#include <machine/rtems-bsd-version.h>
#include <machine/rtems-bsd-kernel-namespace.h>
diff --git a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
index ff545dc0..50a43873 100644
--- a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
+++ b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
@@ -243,6 +243,28 @@ extern "C" {
SYSINIT_DRIVER_REFERENCE(mmcsd, mmc)
#endif /* RTEMS_BSD_DRIVER_MMC */
+#if !defined(RTEMS_BSD_DRIVER_ST_SDMMC)
+ #define RTEMS_BSD_DRIVER_ST_SDMMC(_num, _base, _dlyb, _irq) \
+ static const rtems_bsd_device_resource st_sdmmc ## _num ## _res[] = { \
+ { \
+ .type = RTEMS_BSD_RES_MEMORY, \
+ .start_request = 0, \
+ .start_actual = (_base) \
+ }, { \
+ .type = RTEMS_BSD_RES_MEMORY, \
+ .start_request = 1, \
+ .start_actual = (_dlyb) \
+ }, { \
+ .type = RTEMS_BSD_RES_IRQ, \
+ .start_request = 0, \
+ .start_actual = (_irq) \
+ } \
+ }; \
+ RTEMS_BSD_DEFINE_NEXUS_DEVICE(st_sdmmc, 0, \
+ RTEMS_ARRAY_SIZE(st_sdmmc ## _num ## _res), \
+ &st_sdmmc ## _num ## _res[0])
+#endif /* RTEMS_BSD_DRIVER_ST_SDMMC */
+
/*
* USB Drivers.
*/
@@ -371,6 +393,33 @@ extern "C" {
#define RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM1(_irq) \
RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xe000c000, _irq)
#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM1 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0)
+ #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(0, 0xff0b0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1)
+ #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xff0c0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2)
+ #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(2, 0xff0d0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3)
+ #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(3, 0xff0e0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3 */
+/*
+ * Versal has a similar GEM as the CGEM. This should work for now.
+ */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0)
+ #define RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(0, 0xff0c0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0 */
+#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1)
+ #define RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1(_irq) \
+ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xff0d0000, _irq)
+#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1 */
/*
* Designware/Synopsys Ethernet MAC Controller.
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 28d5dd5a..93113b9c 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>
@@ -49,6 +50,9 @@
#include <stdio.h>
+/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */
+#define IFCAP_NOMAP 0x4000000
+
#define O_CLOEXEC 0
#define O_DIRECTORY 0
diff --git a/rtemsbsd/include/rtems/bsd/bsd.h b/rtemsbsd/include/rtems/bsd/bsd.h
index cec14ac4..9e524719 100755
--- a/rtemsbsd/include/rtems/bsd/bsd.h
+++ b/rtemsbsd/include/rtems/bsd/bsd.h
@@ -92,28 +92,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/netcmds-config.h b/rtemsbsd/include/rtems/netcmds-config.h
index 81f6c92e..28961bae 100644
--- a/rtemsbsd/include/rtems/netcmds-config.h
+++ b/rtemsbsd/include/rtems/netcmds-config.h
@@ -32,7 +32,11 @@ extern rtems_shell_cmd_t rtems_shell_PING_Command;
extern rtems_shell_cmd_t rtems_shell_PING6_Command;
extern rtems_shell_cmd_t rtems_shell_IFCONFIG_Command;
+
+extern rtems_shell_cmd_t rtems_shell_IFMCSTAT_Command;
+
extern rtems_shell_cmd_t rtems_shell_ROUTE_Command;
+
extern rtems_shell_cmd_t rtems_shell_NETSTAT_Command;
extern rtems_shell_cmd_t rtems_shell_DHCPCD_Command;
diff --git a/rtemsbsd/include/x86/bus.h b/rtemsbsd/include/x86/bus.h
deleted file mode 100644
index 2427ae51..00000000
--- a/rtemsbsd/include/x86/bus.h
+++ /dev/null
@@ -1,1109 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-NetBSDE
- *
- * Copyright (c) KATO Takenori, 1999.
- *
- * All rights reserved. Unpublished rights reserved under the copyright
- * laws of Japan.
- *
- * 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 as
- * the first lines of this file unmodified.
- * 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.
- *
- * $FreeBSD$
- */
-
-/* $NetBSD: bus.h,v 1.12 1997/10/01 08:25:15 fvdl Exp $ */
-
-/*-
- * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
- * NASA Ames Research Center.
- *
- * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
- */
-
-/*-
- * Copyright (c) 1996 Charles M. Hannum. All rights reserved.
- * Copyright (c) 1996 Christopher G. Demetriou. 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Christopher G. Demetriou
- * for the NetBSD Project.
- * 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.
- */
-
-#ifndef _X86_BUS_H_
-#define _X86_BUS_H_
-
-#include <machine/_bus.h>
-#include <machine/cpufunc.h>
-
-#ifndef __GNUCLIKE_ASM
-#error "no assembler code for your compiler"
-#endif
-
-/*
- * Values for the x86 bus space tag, not to be used directly by MI code.
- */
-#define X86_BUS_SPACE_IO 0 /* space is i/o space */
-#define X86_BUS_SPACE_MEM 1 /* space is mem space */
-
-#define BUS_SPACE_MAXSIZE_24BIT 0xFFFFFF
-#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
-#if defined(__amd64__)
-#define BUS_SPACE_MAXSIZE 0xFFFFFFFFFFFFFFFFULL
-#else
-#define BUS_SPACE_MAXSIZE 0xFFFFFFFF
-#endif
-#define BUS_SPACE_MAXADDR_24BIT 0xFFFFFF
-#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
-#if defined(__amd64__) || defined(PAE)
-#define BUS_SPACE_MAXADDR_48BIT 0xFFFFFFFFFFFFULL
-#define BUS_SPACE_MAXADDR 0xFFFFFFFFFFFFFFFFULL
-#else
-#define BUS_SPACE_MAXADDR 0xFFFFFFFF
-#endif
-
-#define BUS_SPACE_INVALID_DATA (~0)
-#define BUS_SPACE_UNRESTRICTED (~0)
-
-/*
- * Map a region of device bus space into CPU virtual address space.
- */
-
-int bus_space_map(bus_space_tag_t tag, bus_addr_t addr, bus_size_t size,
- int flags, bus_space_handle_t *bshp);
-
-/*
- * Unmap a region of device bus space.
- */
-
-void bus_space_unmap(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t size);
-
-/*
- * Get a new handle for a subregion of an already-mapped area of bus space.
- */
-
-static __inline int bus_space_subregion(bus_space_tag_t t,
- bus_space_handle_t bsh,
- bus_size_t offset, bus_size_t size,
- bus_space_handle_t *nbshp);
-
-static __inline int
-bus_space_subregion(bus_space_tag_t t __unused, bus_space_handle_t bsh,
- bus_size_t offset, bus_size_t size __unused,
- bus_space_handle_t *nbshp)
-{
-
- *nbshp = bsh + offset;
- return (0);
-}
-
-/*
- * Allocate a region of memory that is accessible to devices in bus space.
- */
-
-int bus_space_alloc(bus_space_tag_t t, bus_addr_t rstart,
- bus_addr_t rend, bus_size_t size, bus_size_t align,
- bus_size_t boundary, int flags, bus_addr_t *addrp,
- bus_space_handle_t *bshp);
-
-/*
- * Free a region of bus space accessible memory.
- */
-
-static __inline void bus_space_free(bus_space_tag_t t, bus_space_handle_t bsh,
- bus_size_t size);
-
-static __inline void
-bus_space_free(bus_space_tag_t t __unused, bus_space_handle_t bsh __unused,
- bus_size_t size __unused)
-{
-}
-
-
-/*
- * Read a 1, 2, 4, or 8 byte quantity from bus space
- * described by tag/handle/offset.
- */
-static __inline u_int8_t bus_space_read_1(bus_space_tag_t tag,
- bus_space_handle_t handle,
- bus_size_t offset);
-
-static __inline u_int16_t bus_space_read_2(bus_space_tag_t tag,
- bus_space_handle_t handle,
- bus_size_t offset);
-
-static __inline u_int32_t bus_space_read_4(bus_space_tag_t tag,
- bus_space_handle_t handle,
- bus_size_t offset);
-
-#ifdef __amd64__
-static __inline uint64_t bus_space_read_8(bus_space_tag_t tag,
- bus_space_handle_t handle,
- bus_size_t offset);
-#endif
-
-static __inline u_int8_t
-bus_space_read_1(bus_space_tag_t tag, bus_space_handle_t handle,
- bus_size_t offset)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- return (inb(handle + offset));
- return (*(volatile u_int8_t *)(handle + offset));
-}
-
-static __inline u_int16_t
-bus_space_read_2(bus_space_tag_t tag, bus_space_handle_t handle,
- bus_size_t offset)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- return (inw(handle + offset));
- return (*(volatile u_int16_t *)(handle + offset));
-}
-
-static __inline u_int32_t
-bus_space_read_4(bus_space_tag_t tag, bus_space_handle_t handle,
- bus_size_t offset)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- return (inl(handle + offset));
- return (*(volatile u_int32_t *)(handle + offset));
-}
-
-#ifdef __amd64__
-static __inline uint64_t
-bus_space_read_8(bus_space_tag_t tag, bus_space_handle_t handle,
- bus_size_t offset)
-{
-
- if (tag == X86_BUS_SPACE_IO) /* No 8 byte IO space access on x86 */
- return (BUS_SPACE_INVALID_DATA);
- return (*(volatile uint64_t *)(handle + offset));
-}
-#endif
-
-/*
- * Read `count' 1, 2, 4, or 8 byte quantities from bus space
- * described by tag/handle/offset and copy into buffer provided.
- */
-static __inline void bus_space_read_multi_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t *addr,
- size_t count);
-
-static __inline void bus_space_read_multi_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t *addr,
- size_t count);
-
-static __inline void bus_space_read_multi_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t *addr,
- size_t count);
-
-static __inline void
-bus_space_read_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- insb(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: movb (%2),%%al \n\
- stosb \n\
- loop 1b" :
- "=D" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory");
-#endif
- }
-}
-
-static __inline void
-bus_space_read_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- insw(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: movw (%2),%%ax \n\
- stosw \n\
- loop 1b" :
- "=D" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory");
-#endif
- }
-}
-
-static __inline void
-bus_space_read_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- insl(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: movl (%2),%%eax \n\
- stosl \n\
- loop 1b" :
- "=D" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory");
-#endif
- }
-}
-
-#if 0 /* Cause a link error for bus_space_read_multi_8 */
-#define bus_space_read_multi_8 !!! bus_space_read_multi_8 unimplemented !!!
-#endif
-
-/*
- * Read `count' 1, 2, 4, or 8 byte quantities from bus space
- * described by tag/handle and starting at `offset' and copy into
- * buffer provided.
- */
-static __inline void bus_space_read_region_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t *addr,
- size_t count);
-
-static __inline void bus_space_read_region_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t *addr,
- size_t count);
-
-static __inline void bus_space_read_region_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t *addr,
- size_t count);
-
-
-static __inline void
-bus_space_read_region_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: inb %w2,%%al \n\
- stosb \n\
- incl %2 \n\
- loop 1b" :
- "=D" (addr), "=c" (count), "=d" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsb" :
- "=D" (addr), "=c" (count), "=S" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_read_region_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: inw %w2,%%ax \n\
- stosw \n\
- addl $2,%2 \n\
- loop 1b" :
- "=D" (addr), "=c" (count), "=d" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsw" :
- "=D" (addr), "=c" (count), "=S" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_read_region_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: inl %w2,%%eax \n\
- stosl \n\
- addl $4,%2 \n\
- loop 1b" :
- "=D" (addr), "=c" (count), "=d" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsl" :
- "=D" (addr), "=c" (count), "=S" (_port_) :
- "0" (addr), "1" (count), "2" (_port_) :
- "memory", "cc");
-#endif
- }
-}
-
-#if 0 /* Cause a link error for bus_space_read_region_8 */
-#define bus_space_read_region_8 !!! bus_space_read_region_8 unimplemented !!!
-#endif
-
-/*
- * Write the 1, 2, 4, or 8 byte value `value' to bus space
- * described by tag/handle/offset.
- */
-
-static __inline void bus_space_write_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t value);
-
-static __inline void bus_space_write_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t value);
-
-static __inline void bus_space_write_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t value);
-
-#ifdef __amd64__
-static __inline void bus_space_write_8(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, uint64_t value);
-#endif
-
-static __inline void
-bus_space_write_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t value)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outb(bsh + offset, value);
- else
- *(volatile u_int8_t *)(bsh + offset) = value;
-}
-
-static __inline void
-bus_space_write_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t value)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outw(bsh + offset, value);
- else
- *(volatile u_int16_t *)(bsh + offset) = value;
-}
-
-static __inline void
-bus_space_write_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t value)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outl(bsh + offset, value);
- else
- *(volatile u_int32_t *)(bsh + offset) = value;
-}
-
-#ifdef __amd64__
-static __inline void
-bus_space_write_8(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, uint64_t value)
-{
-
- if (tag == X86_BUS_SPACE_IO) /* No 8 byte IO space access on x86 */
- return;
- else
- *(volatile uint64_t *)(bsh + offset) = value;
-}
-#endif
-
-/*
- * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
- * provided to bus space described by tag/handle/offset.
- */
-
-static __inline void bus_space_write_multi_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int8_t *addr,
- size_t count);
-static __inline void bus_space_write_multi_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int16_t *addr,
- size_t count);
-
-static __inline void bus_space_write_multi_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int32_t *addr,
- size_t count);
-
-static __inline void
-bus_space_write_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int8_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outsb(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsb \n\
- movb %%al,(%2) \n\
- loop 1b" :
- "=S" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_write_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int16_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outsw(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsw \n\
- movw %%ax,(%2) \n\
- loop 1b" :
- "=S" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_write_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int32_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO)
- outsl(bsh + offset, addr, count);
- else {
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsl \n\
- movl %%eax,(%2) \n\
- loop 1b" :
- "=S" (addr), "=c" (count) :
- "r" (bsh + offset), "0" (addr), "1" (count) :
- "%eax", "memory", "cc");
-#endif
- }
-}
-
-#if 0 /* Cause a link error for bus_space_write_multi_8 */
-#define bus_space_write_multi_8(t, h, o, a, c) \
- !!! bus_space_write_multi_8 unimplemented !!!
-#endif
-
-/*
- * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided
- * to bus space described by tag/handle starting at `offset'.
- */
-
-static __inline void bus_space_write_region_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int8_t *addr,
- size_t count);
-static __inline void bus_space_write_region_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int16_t *addr,
- size_t count);
-static __inline void bus_space_write_region_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- const u_int32_t *addr,
- size_t count);
-
-static __inline void
-bus_space_write_region_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int8_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsb \n\
- outb %%al,%w0 \n\
- incl %0 \n\
- loop 1b" :
- "=d" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsb" :
- "=D" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_write_region_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int16_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsw \n\
- outw %%ax,%w0 \n\
- addl $2,%0 \n\
- loop 1b" :
- "=d" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsw" :
- "=D" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "memory", "cc");
-#endif
- }
-}
-
-static __inline void
-bus_space_write_region_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, const u_int32_t *addr, size_t count)
-{
-
- if (tag == X86_BUS_SPACE_IO) {
- int _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- 1: lodsl \n\
- outl %%eax,%w0 \n\
- addl $4,%0 \n\
- loop 1b" :
- "=d" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "%eax", "memory", "cc");
-#endif
- } else {
- bus_space_handle_t _port_ = bsh + offset;
-#ifdef __GNUCLIKE_ASM
- __asm __volatile(" \n\
- cld \n\
- repne \n\
- movsl" :
- "=D" (_port_), "=S" (addr), "=c" (count) :
- "0" (_port_), "1" (addr), "2" (count) :
- "memory", "cc");
-#endif
- }
-}
-
-#if 0 /* Cause a link error for bus_space_write_region_8 */
-#define bus_space_write_region_8 \
- !!! bus_space_write_region_8 unimplemented !!!
-#endif
-
-/*
- * Write the 1, 2, 4, or 8 byte value `val' to bus space described
- * by tag/handle/offset `count' times.
- */
-
-static __inline void bus_space_set_multi_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- u_int8_t value, size_t count);
-static __inline void bus_space_set_multi_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- u_int16_t value, size_t count);
-static __inline void bus_space_set_multi_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset,
- u_int32_t value, size_t count);
-
-static __inline void
-bus_space_set_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- while (count--)
- outb(addr, value);
- else
- while (count--)
- *(volatile u_int8_t *)(addr) = value;
-}
-
-static __inline void
-bus_space_set_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- while (count--)
- outw(addr, value);
- else
- while (count--)
- *(volatile u_int16_t *)(addr) = value;
-}
-
-static __inline void
-bus_space_set_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- while (count--)
- outl(addr, value);
- else
- while (count--)
- *(volatile u_int32_t *)(addr) = value;
-}
-
-#if 0 /* Cause a link error for bus_space_set_multi_8 */
-#define bus_space_set_multi_8 !!! bus_space_set_multi_8 unimplemented !!!
-#endif
-
-/*
- * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described
- * by tag/handle starting at `offset'.
- */
-
-static __inline void bus_space_set_region_1(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t value,
- size_t count);
-static __inline void bus_space_set_region_2(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t value,
- size_t count);
-static __inline void bus_space_set_region_4(bus_space_tag_t tag,
- bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t value,
- size_t count);
-
-static __inline void
-bus_space_set_region_1(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int8_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- for (; count != 0; count--, addr++)
- outb(addr, value);
- else
- for (; count != 0; count--, addr++)
- *(volatile u_int8_t *)(addr) = value;
-}
-
-static __inline void
-bus_space_set_region_2(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int16_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- for (; count != 0; count--, addr += 2)
- outw(addr, value);
- else
- for (; count != 0; count--, addr += 2)
- *(volatile u_int16_t *)(addr) = value;
-}
-
-static __inline void
-bus_space_set_region_4(bus_space_tag_t tag, bus_space_handle_t bsh,
- bus_size_t offset, u_int32_t value, size_t count)
-{
- bus_space_handle_t addr = bsh + offset;
-
- if (tag == X86_BUS_SPACE_IO)
- for (; count != 0; count--, addr += 4)
- outl(addr, value);
- else
- for (; count != 0; count--, addr += 4)
- *(volatile u_int32_t *)(addr) = value;
-}
-
-#if 0 /* Cause a link error for bus_space_set_region_8 */
-#define bus_space_set_region_8 !!! bus_space_set_region_8 unimplemented !!!
-#endif
-
-/*
- * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
- * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
- */
-
-static __inline void bus_space_copy_region_1(bus_space_tag_t tag,
- bus_space_handle_t bsh1,
- bus_size_t off1,
- bus_space_handle_t bsh2,
- bus_size_t off2, size_t count);
-
-static __inline void bus_space_copy_region_2(bus_space_tag_t tag,
- bus_space_handle_t bsh1,
- bus_size_t off1,
- bus_space_handle_t bsh2,
- bus_size_t off2, size_t count);
-
-static __inline void bus_space_copy_region_4(bus_space_tag_t tag,
- bus_space_handle_t bsh1,
- bus_size_t off1,
- bus_space_handle_t bsh2,
- bus_size_t off2, size_t count);
-
-static __inline void
-bus_space_copy_region_1(bus_space_tag_t tag, bus_space_handle_t bsh1,
- bus_size_t off1, bus_space_handle_t bsh2,
- bus_size_t off2, size_t count)
-{
- bus_space_handle_t addr1 = bsh1 + off1;
- bus_space_handle_t addr2 = bsh2 + off2;
-
- if (tag == X86_BUS_SPACE_IO) {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1++, addr2++)
- outb(addr2, inb(addr1));
- } else {
- /* dest after src: copy backwards */
- for (addr1 += (count - 1), addr2 += (count - 1);
- count != 0; count--, addr1--, addr2--)
- outb(addr2, inb(addr1));
- }
- } else {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1++, addr2++)
- *(volatile u_int8_t *)(addr2) =
- *(volatile u_int8_t *)(addr1);
- } else {
- /* dest after src: copy backwards */
- for (addr1 += (count - 1), addr2 += (count - 1);
- count != 0; count--, addr1--, addr2--)
- *(volatile u_int8_t *)(addr2) =
- *(volatile u_int8_t *)(addr1);
- }
- }
-}
-
-static __inline void
-bus_space_copy_region_2(bus_space_tag_t tag, bus_space_handle_t bsh1,
- bus_size_t off1, bus_space_handle_t bsh2,
- bus_size_t off2, size_t count)
-{
- bus_space_handle_t addr1 = bsh1 + off1;
- bus_space_handle_t addr2 = bsh2 + off2;
-
- if (tag == X86_BUS_SPACE_IO) {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1 += 2, addr2 += 2)
- outw(addr2, inw(addr1));
- } else {
- /* dest after src: copy backwards */
- for (addr1 += 2 * (count - 1), addr2 += 2 * (count - 1);
- count != 0; count--, addr1 -= 2, addr2 -= 2)
- outw(addr2, inw(addr1));
- }
- } else {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1 += 2, addr2 += 2)
- *(volatile u_int16_t *)(addr2) =
- *(volatile u_int16_t *)(addr1);
- } else {
- /* dest after src: copy backwards */
- for (addr1 += 2 * (count - 1), addr2 += 2 * (count - 1);
- count != 0; count--, addr1 -= 2, addr2 -= 2)
- *(volatile u_int16_t *)(addr2) =
- *(volatile u_int16_t *)(addr1);
- }
- }
-}
-
-static __inline void
-bus_space_copy_region_4(bus_space_tag_t tag, bus_space_handle_t bsh1,
- bus_size_t off1, bus_space_handle_t bsh2,
- bus_size_t off2, size_t count)
-{
- bus_space_handle_t addr1 = bsh1 + off1;
- bus_space_handle_t addr2 = bsh2 + off2;
-
- if (tag == X86_BUS_SPACE_IO) {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1 += 4, addr2 += 4)
- outl(addr2, inl(addr1));
- } else {
- /* dest after src: copy backwards */
- for (addr1 += 4 * (count - 1), addr2 += 4 * (count - 1);
- count != 0; count--, addr1 -= 4, addr2 -= 4)
- outl(addr2, inl(addr1));
- }
- } else {
- if (addr1 >= addr2) {
- /* src after dest: copy forward */
- for (; count != 0; count--, addr1 += 4, addr2 += 4)
- *(volatile u_int32_t *)(addr2) =
- *(volatile u_int32_t *)(addr1);
- } else {
- /* dest after src: copy backwards */
- for (addr1 += 4 * (count - 1), addr2 += 4 * (count - 1);
- count != 0; count--, addr1 -= 4, addr2 -= 4)
- *(volatile u_int32_t *)(addr2) =
- *(volatile u_int32_t *)(addr1);
- }
- }
-}
-
-#if 0 /* Cause a link error for bus_space_copy_8 */
-#define bus_space_copy_region_8 !!! bus_space_copy_region_8 unimplemented !!!
-#endif
-
-/*
- * Bus read/write barrier methods.
- *
- * void bus_space_barrier(bus_space_tag_t tag, bus_space_handle_t bsh,
- * bus_size_t offset, bus_size_t len, int flags);
- *
- *
- * Note that BUS_SPACE_BARRIER_WRITE doesn't do anything other than
- * prevent reordering by the compiler; all Intel x86 processors currently
- * retire operations outside the CPU in program order.
- */
-#define BUS_SPACE_BARRIER_READ 0x01 /* force read barrier */
-#define BUS_SPACE_BARRIER_WRITE 0x02 /* force write barrier */
-
-static __inline void
-bus_space_barrier(bus_space_tag_t tag __unused, bus_space_handle_t bsh __unused,
- bus_size_t offset __unused, bus_size_t len __unused, int flags)
-{
-#ifdef __GNUCLIKE_ASM
- if (flags & BUS_SPACE_BARRIER_READ)
-#ifdef __amd64__
- __asm __volatile("lock; addl $0,0(%%rsp)" : : : "memory");
-#else
- __asm __volatile("lock; addl $0,0(%%esp)" : : : "memory");
-#endif
- else
- __compiler_membar();
-#endif
-}
-
-#ifdef BUS_SPACE_NO_LEGACY
-#undef inb
-#undef outb
-#define inb(a) compiler_error
-#define inw(a) compiler_error
-#define inl(a) compiler_error
-#define outb(a, b) compiler_error
-#define outw(a, b) compiler_error
-#define outl(a, b) compiler_error
-#endif
-
-#include <machine/bus_dma.h>
-
-/*
- * Stream accesses are the same as normal accesses on x86; there are no
- * supported bus systems with an endianess different from the host one.
- */
-#define bus_space_read_stream_1(t, h, o) bus_space_read_1((t), (h), (o))
-#define bus_space_read_stream_2(t, h, o) bus_space_read_2((t), (h), (o))
-#define bus_space_read_stream_4(t, h, o) bus_space_read_4((t), (h), (o))
-
-#define bus_space_read_multi_stream_1(t, h, o, a, c) \
- bus_space_read_multi_1((t), (h), (o), (a), (c))
-#define bus_space_read_multi_stream_2(t, h, o, a, c) \
- bus_space_read_multi_2((t), (h), (o), (a), (c))
-#define bus_space_read_multi_stream_4(t, h, o, a, c) \
- bus_space_read_multi_4((t), (h), (o), (a), (c))
-
-#define bus_space_write_stream_1(t, h, o, v) \
- bus_space_write_1((t), (h), (o), (v))
-#define bus_space_write_stream_2(t, h, o, v) \
- bus_space_write_2((t), (h), (o), (v))
-#define bus_space_write_stream_4(t, h, o, v) \
- bus_space_write_4((t), (h), (o), (v))
-
-#define bus_space_write_multi_stream_1(t, h, o, a, c) \
- bus_space_write_multi_1((t), (h), (o), (a), (c))
-#define bus_space_write_multi_stream_2(t, h, o, a, c) \
- bus_space_write_multi_2((t), (h), (o), (a), (c))
-#define bus_space_write_multi_stream_4(t, h, o, a, c) \
- bus_space_write_multi_4((t), (h), (o), (a), (c))
-
-#define bus_space_set_multi_stream_1(t, h, o, v, c) \
- bus_space_set_multi_1((t), (h), (o), (v), (c))
-#define bus_space_set_multi_stream_2(t, h, o, v, c) \
- bus_space_set_multi_2((t), (h), (o), (v), (c))
-#define bus_space_set_multi_stream_4(t, h, o, v, c) \
- bus_space_set_multi_4((t), (h), (o), (v), (c))
-
-#define bus_space_read_region_stream_1(t, h, o, a, c) \
- bus_space_read_region_1((t), (h), (o), (a), (c))
-#define bus_space_read_region_stream_2(t, h, o, a, c) \
- bus_space_read_region_2((t), (h), (o), (a), (c))
-#define bus_space_read_region_stream_4(t, h, o, a, c) \
- bus_space_read_region_4((t), (h), (o), (a), (c))
-
-#define bus_space_write_region_stream_1(t, h, o, a, c) \
- bus_space_write_region_1((t), (h), (o), (a), (c))
-#define bus_space_write_region_stream_2(t, h, o, a, c) \
- bus_space_write_region_2((t), (h), (o), (a), (c))
-#define bus_space_write_region_stream_4(t, h, o, a, c) \
- bus_space_write_region_4((t), (h), (o), (a), (c))
-
-#define bus_space_set_region_stream_1(t, h, o, v, c) \
- bus_space_set_region_1((t), (h), (o), (v), (c))
-#define bus_space_set_region_stream_2(t, h, o, v, c) \
- bus_space_set_region_2((t), (h), (o), (v), (c))
-#define bus_space_set_region_stream_4(t, h, o, v, c) \
- bus_space_set_region_4((t), (h), (o), (v), (c))
-
-#define bus_space_copy_region_stream_1(t, h1, o1, h2, o2, c) \
- bus_space_copy_region_1((t), (h1), (o1), (h2), (o2), (c))
-#define bus_space_copy_region_stream_2(t, h1, o1, h2, o2, c) \
- bus_space_copy_region_2((t), (h1), (o1), (h2), (o2), (c))
-#define bus_space_copy_region_stream_4(t, h1, o1, h2, o2, c) \
- bus_space_copy_region_4((t), (h1), (o1), (h2), (o2), (c))
-
-#endif /* _X86_BUS_H_ */
diff --git a/rtemsbsd/include/x86/x86_var.h b/rtemsbsd/include/x86/x86_var.h
index 2028d739..5819205a 100644
--- a/rtemsbsd/include/x86/x86_var.h
+++ b/rtemsbsd/include/x86/x86_var.h
@@ -1,145 +1 @@
-/*-
- * Copyright (c) 1995 Bruce D. Evans.
- * 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 author nor the names of contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * 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 _X86_X86_VAR_H_
-#define _X86_X86_VAR_H_
-
-/*
- * Miscellaneous machine-dependent declarations.
- */
-
-extern long Maxmem;
-extern u_int basemem;
-extern int busdma_swi_pending;
-extern u_int cpu_exthigh;
-extern u_int cpu_feature;
-extern u_int cpu_feature2;
-extern u_int amd_feature;
-extern u_int amd_feature2;
-extern u_int amd_rascap;
-extern u_int amd_pminfo;
-extern u_int amd_extended_feature_extensions;
-extern u_int via_feature_rng;
-extern u_int via_feature_xcrypt;
-extern u_int cpu_clflush_line_size;
-extern u_int cpu_stdext_feature;
-extern u_int cpu_stdext_feature2;
-extern u_int cpu_stdext_feature3;
-extern uint64_t cpu_ia32_arch_caps;
-extern u_int cpu_fxsr;
-extern u_int cpu_high;
-extern u_int cpu_id;
-extern u_int cpu_max_ext_state_size;
-extern u_int cpu_mxcsr_mask;
-extern u_int cpu_procinfo;
-extern u_int cpu_procinfo2;
-extern char cpu_vendor[];
-extern u_int cpu_vendor_id;
-extern u_int cpu_mon_mwait_flags;
-extern u_int cpu_mon_min_size;
-extern u_int cpu_mon_max_size;
-extern u_int cpu_maxphyaddr;
-extern char ctx_switch_xsave[];
-extern u_int hv_base;
-extern u_int hv_high;
-extern char hv_vendor[];
-extern char kstack[];
-extern char sigcode[];
-extern int szsigcode;
-extern int vm_page_dump_size;
-extern int workaround_erratum383;
-extern int _udatasel;
-extern int _ucodesel;
-extern int _ucode32sel;
-extern int _ufssel;
-extern int _ugssel;
-extern int use_xsave;
-extern uint64_t xsave_mask;
-extern u_int max_apic_id;
-extern int i386_read_exec;
-extern int pti;
-extern int hw_ibrs_active;
-extern int hw_mds_disable;
-extern int hw_ssb_active;
-
-struct pcb;
-struct thread;
-struct reg;
-struct fpreg;
-struct dbreg;
-struct dumperinfo;
-struct trapframe;
-
-/*
- * The interface type of the interrupt handler entry point cannot be
- * expressed in C. Use simplest non-variadic function type as an
- * approximation.
- */
-typedef void alias_for_inthand_t(void);
-
-bool acpi_get_fadt_bootflags(uint16_t *flagsp);
-void *alloc_fpusave(int flags);
-void busdma_swi(void);
-vm_paddr_t cpu_getmaxphyaddr(void);
-bool cpu_mwait_usable(void);
-void cpu_probe_amdc1e(void);
-void cpu_setregs(void);
-bool disable_wp(void);
-void restore_wp(bool old_wp);
-void dump_add_page(vm_paddr_t);
-void dump_drop_page(vm_paddr_t);
-void finishidentcpu(void);
-void identify_cpu1(void);
-void identify_cpu2(void);
-void identify_cpu_fixup_bsp(void);
-void identify_hypervisor(void);
-void initializecpu(void);
-void initializecpucache(void);
-bool fix_cpuid(void);
-void fillw(int /*u_short*/ pat, void *base, size_t cnt);
-int is_physical_memory(vm_paddr_t addr);
-int isa_nmi(int cd);
-void handle_ibrs_entry(void);
-void handle_ibrs_exit(void);
-void hw_ibrs_recalculate(void);
-void hw_mds_recalculate(void);
-void hw_ssb_recalculate(bool all_cpus);
-void nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame);
-void nmi_call_kdb_smp(u_int type, struct trapframe *frame);
-void nmi_handle_intr(u_int type, struct trapframe *frame);
-void pagecopy(void *from, void *to);
-void printcpuinfo(void);
-int pti_get_default(void);
-int user_dbreg_trap(register_t dr6);
-int minidumpsys(struct dumperinfo *);
-struct pcb *get_pcb_td(struct thread *td);
-
-#endif
+#include <machine/x86_var.h>
diff --git a/rtemsbsd/nfsclient/nfs.c b/rtemsbsd/nfsclient/nfs.c
index d6f43305..e9e83abb 100644
--- a/rtemsbsd/nfsclient/nfs.c
+++ b/rtemsbsd/nfsclient/nfs.c
@@ -397,71 +397,36 @@ DirInfo dip;
/* Macro for accessing serporid fields
*/
-#define SERP_ARGS(node) ((node)->serporid.serporid_u.serporid.arg_u)
-#define SERP_ATTR(node) ((node)->serporid.serporid_u.serporid.attributes)
-#define SERP_FILE(node) ((node)->serporid.serporid_u.serporid.file)
-
-/*
- * FIXME: The use of the serporid structure with several embedded unions to
- * split up the specific NFS request/response structures is quite a hack. It
- * breaks on 64-bit targets due to the presence of pointer members which affect
- * the overall alignment. Use a packed serporidok structure to hopefully fix
- * this issue.
- */
+#define SERP_ARGS(node) ((node)->serporid.serporid)
+#define SERP_ATTR(node) ((node)->serporid.serporid.attributes)
+#define SERP_FILE(node) ((node)->serporid.serporid.file)
typedef struct serporidok {
fattr attributes;
- nfs_fh file;
union {
- struct {
- filename name;
- } diroparg;
- struct {
- sattr attributes;
- } sattrarg;
- struct {
- uint32_t offset;
- uint32_t count;
- uint32_t totalcount;
- } readarg;
- struct {
- uint32_t beginoffset;
- uint32_t offset;
- uint32_t totalcount;
- struct {
- uint32_t data_len;
- char* data_val;
- } data;
- } writearg;
- struct {
- filename name;
- sattr attributes;
- } createarg;
- struct {
- filename name;
- diropargs to;
- } renamearg;
- struct {
- diropargs to;
- } linkarg;
- struct {
- filename name;
- nfspath to;
- sattr attributes;
- } symlinkarg;
- struct {
- nfscookie cookie;
- uint32_t count;
- } readdirarg;
- } arg_u;
-} RTEMS_PACKED serporidok;
+ nfs_fh file;
+ diropargs diroparg;
+ sattrargs sattrarg;
+ readargs readarg;
+ writeargs writearg;
+ createargs createarg;
+ renameargs renamearg;
+ linkargs linkarg;
+ symlinkargs symlinkarg;
+ readdirargs readdirarg;
+ };
+} serporidok;
+/*
+ * The nfsstat is an enum, so has an integer alignment. The serporid contains
+ * pointers, so has at least a pointer alignment. The packed attribute ensures
+ * that there is no gap between the status and serporid members on 64-bit
+ * targets.
+ */
typedef struct serporid {
nfsstat status;
- union {
- serporidok serporid;
- } serporid_u;
-} serporid;
+ serporidok serporid;
+} RTEMS_PACKED serporid;
/* an XDR routine to encode/decode the inverted diropres
* into an nfsnodestat;
@@ -493,7 +458,7 @@ xdr_serporid(XDR *xdrs, serporid *objp)
return FALSE;
switch (objp->status) {
case NFS_OK:
- if (!xdr_serporidok(xdrs, &objp->serporid_u.serporid))
+ if (!xdr_serporidok(xdrs, &objp->serporid))
return FALSE;
break;
default:
@@ -2040,7 +2005,7 @@ char *dupname;
rtems_clock_get_tod_timeval(&now);
- SERP_ARGS(node).createarg.name = dupname;
+ SERP_ARGS(node).createarg.where.name = dupname;
SERP_ARGS(node).createarg.attributes.mode = mode;
SERP_ARGS(node).createarg.attributes.uid = nfs->uid;
SERP_ARGS(node).createarg.attributes.gid = nfs->gid;
@@ -2093,19 +2058,19 @@ static int nfs_rmnod(
return rv;
}
-static int nfs_utime(
+static int nfs_utimens(
const rtems_filesystem_location_info_t *pathloc, /* IN */
- time_t actime, /* IN */
- time_t modtime /* IN */
+ struct timespec times[2] /* IN */
+
)
{
sattr arg;
/* TODO: add rtems EPOCH - UNIX EPOCH seconds */
- arg.atime.seconds = actime;
- arg.atime.useconds = 0;
- arg.mtime.seconds = modtime;
- arg.mtime.useconds = 0;
+ arg.atime.seconds = times[0].tv_sec;
+ arg.atime.useconds = times[0].tv_nsec / 1000;
+ arg.mtime.seconds = times[1].tv_sec;
+ arg.mtime.useconds = times[1].tv_nsec / 1000;
return nfs_sattr(pathloc->node_access, &arg, SATTR_ATIME | SATTR_MTIME);
}
@@ -2134,7 +2099,7 @@ char *dupname;
rtems_clock_get_tod_timeval(&now);
- SERP_ARGS(node).symlinkarg.name = dupname;
+ SERP_ARGS(node).symlinkarg.from.name = dupname;
SERP_ARGS(node).symlinkarg.to = (nfspath) target;
SERP_ARGS(node).symlinkarg.attributes.mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
@@ -2231,7 +2196,7 @@ static int nfs_rename(
nfs_fh *toDirDst = &SERP_ARGS(oldParentNode).renamearg.to.dir;
nfsstat status;
- SERP_ARGS(oldParentNode).renamearg.name = oldNode->str;
+ SERP_ARGS(oldParentNode).renamearg.from.name = oldNode->str;
SERP_ARGS(oldParentNode).renamearg.to.name = dupname;
memcpy(toDirDst, toDirSrc, sizeof(*toDirDst));
@@ -2297,25 +2262,25 @@ sattr arg;
}
const struct _rtems_filesystem_operations_table nfs_fs_ops = {
- .lock_h = nfs_lock,
- .unlock_h = nfs_unlock,
- .eval_path_h = nfs_eval_path,
- .link_h = nfs_link,
+ .lock_h = nfs_lock,
+ .unlock_h = nfs_unlock,
+ .eval_path_h = nfs_eval_path,
+ .link_h = nfs_link,
.are_nodes_equal_h = nfs_are_nodes_equal,
- .mknod_h = nfs_mknod,
- .rmnod_h = nfs_rmnod,
- .fchmod_h = nfs_fchmod,
- .chown_h = nfs_chown,
- .clonenod_h = nfs_clonenode,
- .freenod_h = nfs_freenode,
- .mount_h = rtems_filesystem_default_mount,
- .unmount_h = rtems_filesystem_default_unmount,
- .fsunmount_me_h = nfs_fsunmount_me,
- .utime_h = nfs_utime,
- .symlink_h = nfs_symlink,
- .readlink_h = nfs_readlink,
- .rename_h = nfs_rename,
- .statvfs_h = rtems_filesystem_default_statvfs
+ .mknod_h = nfs_mknod,
+ .rmnod_h = nfs_rmnod,
+ .fchmod_h = nfs_fchmod,
+ .chown_h = nfs_chown,
+ .clonenod_h = nfs_clonenode,
+ .freenod_h = nfs_freenode,
+ .mount_h = rtems_filesystem_default_mount,
+ .unmount_h = rtems_filesystem_default_unmount,
+ .fsunmount_me_h = nfs_fsunmount_me,
+ .utimens_h = nfs_utimens,
+ .symlink_h = nfs_symlink,
+ .readlink_h = nfs_readlink,
+ .rename_h = nfs_rename,
+ .statvfs_h = rtems_filesystem_default_statvfs
};
/*****************************************
@@ -3149,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/powerpc/include/linux/crc32.h b/rtemsbsd/powerpc/include/linux/crc32.h
index 0edaaa07..a86ac4a0 100644
--- a/rtemsbsd/powerpc/include/linux/crc32.h
+++ b/rtemsbsd/powerpc/include/linux/crc32.h
@@ -28,7 +28,7 @@
#define _LINUX_CRC32_H
#include <linux/types.h>
-#include <sys/libkern.h>
+#include <sys/gsb_crc32.h>
#ifdef __cplusplus
extern "C" {
diff --git a/rtemsbsd/powerpc/include/linux/genalloc.h b/rtemsbsd/powerpc/include/linux/genalloc.h
index 9df0e430..c7ecd7bc 100644
--- a/rtemsbsd/powerpc/include/linux/genalloc.h
+++ b/rtemsbsd/powerpc/include/linux/genalloc.h
@@ -88,7 +88,7 @@ gen_pool_alloc(struct gen_pool *gp, size_t size)
chunks = (size + (1 << gp->gen_chunk_shift) - 1) >> gp->gen_chunk_shift;
mtx_lock(&gp->gen_lock);
- blkno = blist_alloc(gp->gen_list, chunks);
+ blkno = blist_alloc(gp->gen_list, &chunks, chunks);
mtx_unlock(&gp->gen_lock);
if (blkno == SWAPBLK_NONE)
diff --git a/rtemsbsd/powerpc/include/machine/legacyvar.h b/rtemsbsd/powerpc/include/machine/legacyvar.h
new file mode 100644
index 00000000..8683a0e5
--- /dev/null
+++ b/rtemsbsd/powerpc/include/machine/legacyvar.h
@@ -0,0 +1,2 @@
+/* See freebsd/sys/x86/include/machine/legacyvar.h */
+#include <x86/include/machine/legacyvar.h>
diff --git a/rtemsbsd/powerpc/include/machine/pci_cfgreg.h b/rtemsbsd/powerpc/include/machine/pci_cfgreg.h
new file mode 100644
index 00000000..1bfa468e
--- /dev/null
+++ b/rtemsbsd/powerpc/include/machine/pci_cfgreg.h
@@ -0,0 +1,2 @@
+/* See freebsd/sys/x86/include/machine/pci_cfgreg.h */
+#include <x86/include/machine/pci_cfgreg.h>
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-cxx.cc b/rtemsbsd/rtems/rtems-bsd-cxx.cc
index aadb25af..f6ec78e6 100644
--- a/rtemsbsd/rtems/rtems-bsd-cxx.cc
+++ b/rtemsbsd/rtems/rtems-bsd-cxx.cc
@@ -31,6 +31,7 @@
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
+#undef _GNU_SOURCE
#include <machine/rtems-bsd-kernel-space.h>
diff --git a/rtemsbsd/rtems/rtems-bsd-init-dhcp.c b/rtemsbsd/rtems/rtems-bsd-init-dhcp.c
index e0a298f7..d91f439b 100644
--- a/rtemsbsd/rtems/rtems-bsd-init-dhcp.c
+++ b/rtemsbsd/rtems/rtems-bsd-init-dhcp.c
@@ -38,6 +38,7 @@
*/
#include <rtems/bsd/bsd.h>
+#include <rtems/bsd/modules.h>
#include <rtems/dhcpcd.h>
#include <sysexits.h>
@@ -76,7 +77,11 @@ rtems_bsd_initialize_dhcp(void)
#define RTEMS_BSD_CONFIG_NET_PF_UNIX
#define RTEMS_BSD_CONFIG_NET_IP_MROUTE
+
+#ifdef RTEMS_BSD_MODULE_NETINET6
#define RTEMS_BSD_CONFIG_NET_IP6_MROUTE
+#endif
+
#define RTEMS_BSD_CONFIG_NET_IF_BRIDGE
#define RTEMS_BSD_CONFIG_NET_IF_LAGG
#define RTEMS_BSD_CONFIG_NET_IF_VLAN
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-ifmcstat.c b/rtemsbsd/rtems/rtems-bsd-shell-ifmcstat.c
new file mode 100644
index 00000000..c8fbd4a1
--- /dev/null
+++ b/rtemsbsd/rtems/rtems-bsd-shell-ifmcstat.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 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 <rtems/netcmds-config.h>
+#include <machine/rtems-bsd-commands.h>
+
+rtems_shell_cmd_t rtems_shell_IFMCSTAT_Command = {
+ .name = "ifmcstat",
+ .usage = "ifmcstat [args]",
+ .topic = "net",
+ .command = rtems_bsd_command_ifmcstat
+};
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-kernel-epoch.c b/rtemsbsd/rtems/rtems-kernel-epoch.c
index f4fff0ea..642b5854 100644
--- a/rtemsbsd/rtems/rtems-kernel-epoch.c
+++ b/rtemsbsd/rtems/rtems-kernel-epoch.c
@@ -60,6 +60,10 @@ struct epoch_pcpu {
rtems_interrupt_server_request irq_srv_req;
};
+#ifdef PER_CPU_DATA_NEED_INITIALIZATION
+PER_CPU_DATA_NEED_INITIALIZATION();
+#endif
+
static PER_CPU_DATA_ITEM(struct epoch_pcpu, epoch);
static SLIST_HEAD(, epoch) epoch_list = SLIST_HEAD_INITIALIZER(epoch_list);
@@ -345,15 +349,29 @@ epoch_call(epoch_t epoch, epoch_context_t ctx,
void (*callback) (epoch_context_t))
{
Per_CPU_Control *cpu_self;
- struct epoch_record *er;
struct epoch_pcpu *epcpu;
+ struct epoch_record *er;
+#ifdef RTEMS_SMP
+ ISR_Level level;
+ Thread_Control *executing;
+
+ _ISR_Local_disable(level);
+ cpu_self = _Per_CPU_Get();
+ executing = _Per_CPU_Get_executing(cpu_self);
+ _Thread_Pin(executing);
+ _ISR_Local_enable(level);
+#endif
- cpu_self = _Thread_Dispatch_disable();
epcpu = PER_CPU_DATA_GET(cpu_self, struct epoch_pcpu, epoch);
epcpu->cb_count += 1;
er = EPOCH_GET_RECORD(cpu_self, epoch);
ck_epoch_call(&er->er_record, ctx, callback);
+
+#ifdef RTEMS_SMP
+ cpu_self = _Thread_Dispatch_disable();
+ _Thread_Unpin(executing, cpu_self);
_Thread_Dispatch_enable(cpu_self);
+#endif
}
#ifdef INVARIANTS
@@ -407,7 +425,7 @@ epoch_call_drain_cb(void *arg)
struct epoch_record *er;
epoch = arg;
- cpu = _Per_CPU_Get();
+ cpu = _Per_CPU_Get_snapshot();
er = EPOCH_GET_RECORD(cpu, epoch);
epoch_call(epoch, &er->er_drain_ctx, epoch_drain_cb);
}
@@ -421,6 +439,7 @@ epoch_drain_callbacks(epoch_t epoch)
uint32_t cpu_max;
rtems_id id;
rtems_status_code sc;
+ rtems_interrupt_server_request req[CPU_MAXIMUM_PROCESSORS];
#else
struct epoch_record *er;
#endif
@@ -429,6 +448,7 @@ epoch_drain_callbacks(epoch_t epoch)
mtx_lock(&epoch->e_drain_mtx);
#ifdef RTEMS_SMP
+ memset(&req, 0, sizeof(req));
cpu_max = rtems_scheduler_get_processor_maximum();
for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) {
@@ -441,8 +461,15 @@ epoch_drain_callbacks(epoch_t epoch)
for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) {
sc = rtems_scheduler_ident_by_processor(cpu_index, &id);
if (sc == RTEMS_SUCCESSFUL) {
- _SMP_Unicast_action(cpu_index, epoch_call_drain_cb,
+ sc = rtems_interrupt_server_request_initialize(
+ cpu_index, &req[cpu_index], epoch_call_drain_cb,
epoch);
+ if (sc == RTEMS_SUCCESSFUL) {
+ rtems_interrupt_server_request_submit(
+ &req[cpu_index]);
+ } else {
+ panic("no interrupt server for epoch drain");
+ }
}
}
#else
@@ -457,4 +484,12 @@ epoch_drain_callbacks(epoch_t epoch)
mtx_unlock(&epoch->e_drain_mtx);
sx_xunlock(&epoch->e_drain_sx);
+
+#ifdef RTEMS_SMP
+ for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) {
+ if (req[cpu_index].action.handler != NULL) {
+ rtems_interrupt_server_request_destroy(&req[cpu_index]);
+ }
+ }
+#endif
}
diff --git a/rtemsbsd/rtems/rtems-kernel-init.c b/rtemsbsd/rtems/rtems-kernel-init.c
index 7112914e..b0779277 100644
--- a/rtemsbsd/rtems/rtems-kernel-init.c
+++ b/rtemsbsd/rtems/rtems-kernel-init.c
@@ -135,7 +135,9 @@ rtems_bsd_initialize(void)
sbt_tickthreshold = bttosbt(bt_tickthreshold);
maxid_maxcpus = (int) rtems_scheduler_get_processor_maximum();
- 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),
diff --git a/rtemsbsd/rtems/rtems-kernel-thread.c b/rtemsbsd/rtems/rtems-kernel-thread.c
index 8e3344ef..f06999fb 100644
--- a/rtemsbsd/rtems/rtems-kernel-thread.c
+++ b/rtemsbsd/rtems/rtems-kernel-thread.c
@@ -280,13 +280,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)
{
@@ -312,7 +305,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
@@ -340,7 +333,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-program.c b/rtemsbsd/rtems/rtems-program.c
index 204ed248..1ca8e3b9 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/freescale/imx/imx_rtems_gpio.c b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c
index c24732cc..da64922f 100644
--- a/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c
+++ b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c
@@ -27,7 +27,7 @@
*/
#include <bsp.h>
-#if defined(LIBBSP_ARM_IMX_BSP_H)
+#if defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H)
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -303,4 +303,4 @@ EARLY_DRIVER_MODULE(imx_rtems_gpio, simplebus, imx_rtems_gpio_driver,
imx_rtems_gpio_devclass, NULL, NULL,
BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
-#endif /* LIBBSP_ARM_IMX_BSP_H */
+#endif /* LIBBSP_ARM_IMX_BSP_H || LIBBSP_ARM_IMXRT_BSP_H */
diff --git a/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c
new file mode 100644
index 00000000..b8e3a188
--- /dev/null
+++ b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c
@@ -0,0 +1,227 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * Copyright (C) 2023 embedded brains GmbH & Co. KG
+ * 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 <bsp.h>
+#if defined(LIBBSP_ARM_IMXRT_BSP_H)
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USBPHY driver for Freescale i.MXRT1166. Most likely works with the whole
+ * i.MXRT11xx family.
+ *
+ * Based on USBPHY driver for i.MX6.
+ */
+
+#include <rtems/bsd/local/opt_bus.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/regulator/regulator.h>
+
+#include <fsl_device_registers.h>
+#include <fsl_clock.h>
+
+struct imxrt1166_usbphy_softc {
+ device_t dev;
+ struct resource *mem_res;
+ regulator_t supply_vbus;
+ USBPHY_Type *regs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imxrt1166-usbphy", true},
+ {NULL, false}
+};
+
+static int
+imxrt1166_usbphy_detach(device_t dev)
+{
+ struct imxrt1166_usbphy_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (0);
+}
+
+#define BUS_SPACE_PHYSADDR(res, offs) \
+ ((u_int)(rman_get_start(res)+(offs)))
+
+static int
+enable_vbus_supply(device_t dev, struct imxrt1166_usbphy_softc *sc)
+{
+ int rv;
+ phandle_t node;
+
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "vbus-supply")) {
+ rv = regulator_get_by_ofw_property(sc->dev, node, "vbus-supply",
+ &sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get \"vbus\" regulator\n");
+ return ENXIO;
+ }
+ rv = regulator_enable(sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable \"vbus\" regulator\n");
+ return ENXIO;
+ }
+ }
+
+ return 0;
+}
+
+static int
+imxrt1166_usbphy_attach(device_t dev)
+{
+ struct imxrt1166_usbphy_softc *sc;
+ int err, rid;
+#if IMXRT_IS_MIMXRT11xx
+ uint32_t usbClockFreq;
+#endif
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ /* Allocate bus_space resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ /* Enable VBUS Supply if a regulator is given */
+ err = enable_vbus_supply(dev, sc);
+ if (err != 0) {
+ goto out;
+ }
+
+ sc->regs = (USBPHY_Type *)BUS_SPACE_PHYSADDR(sc->mem_res, 0);
+
+#if IMXRT_IS_MIMXRT11xx
+ /* Enable register clock */
+ CLOCK_EnableClock(kCLOCK_Usb);
+
+ usbClockFreq = CLOCK_GetFreq(kCLOCK_Osc24M);
+
+ /*
+ * Set the software reset bit. It will be implicitly cleared when
+ * setting up the clock in the next steps.
+ */
+ sc->regs->CTRL_SET = USBPHY_CTRL_SFTRST_MASK;
+
+ /*
+ * Enable PLLs.
+ *
+ * FIXME: Hacky way to find out the module.
+ */
+ if (sc->regs == USBPHY1) {
+ CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq);
+ CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, usbClockFreq);
+ } else {
+ CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq);
+ CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, usbClockFreq);
+ }
+#else
+ /* Not implemented */
+#endif
+
+ err = 0;
+
+out:
+
+ if (err != 0)
+ imxrt1166_usbphy_detach(dev);
+
+ return (err);
+}
+
+static int
+imxrt1166_usbphy_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Freescale i.MXRT1166 USB PHY");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static device_method_t imxrt1166_usbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, imxrt1166_usbphy_probe),
+ DEVMETHOD(device_attach, imxrt1166_usbphy_attach),
+ DEVMETHOD(device_detach, imxrt1166_usbphy_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t imxrt1166_usbphy_driver = {
+ "imxrt1166_usbphy",
+ imxrt1166_usbphy_methods,
+ sizeof(struct imxrt1166_usbphy_softc)
+};
+
+static devclass_t imxrt1166_usbphy_devclass;
+
+/*
+ * This driver needs to start before the ehci driver, but later than the usual
+ * "special" drivers like clocks and cpu. Ehci starts at DEFAULT so SUPPORTDEV
+ * is where this driver fits most.
+ */
+EARLY_DRIVER_MODULE(imxrt1166_usbphy, simplebus, imxrt1166_usbphy_driver,
+ imxrt1166_usbphy_devclass, 0, 0,
+ BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+
+#endif /* LIBBSP_ARM_IMXRT_BSP_H */
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/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/iicbus/rtems-i2c.c b/rtemsbsd/sys/dev/iicbus/rtems-i2c.c
index b965f248..0fb14d1a 100644
--- a/rtemsbsd/sys/dev/iicbus/rtems-i2c.c
+++ b/rtemsbsd/sys/dev/iicbus/rtems-i2c.c
@@ -87,9 +87,11 @@ rtems_i2c_attach(device_t dev)
len = OF_getprop_alloc(node, "rtems,i2c-path", &sc->path);
if (len == -1){
- device_printf(sc->dev, "Path not found in Device Tree");
- OF_prop_free(sc->path);
- return (ENXIO);
+ len = OF_getprop_alloc(node, "rtems,path", &sc->path);
+ if (len == -1) {
+ device_printf(sc->dev, "Path not found in Device Tree");
+ return (ENXIO);
+ }
}
if ((sc->sc_iicbus = device_add_child(sc->dev, "iicbus", -1)) == NULL) {
diff --git a/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c
index c37bd868..21420689 100644
--- a/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c
+++ b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c
@@ -270,10 +270,10 @@ lpc_tsc_init(struct lpc_tsc_softc *sc)
TSCWRITE4(sc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
}
-static void
-lpc_tsc_ev_close(struct evdev_dev *evdev, void *data)
+static int
+lpc_tsc_ev_close(struct evdev_dev *evdev)
{
- struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data;
+ struct lpc_tsc_softc *sc = evdev_get_softc(evdev);
uint32_t tmp;
LPC_TSC_LOCK_ASSERT(sc);
@@ -283,12 +283,14 @@ lpc_tsc_ev_close(struct evdev_dev *evdev, void *data)
TSCWRITE4(sc, LPC32XX_TSC_CON, tmp);
lpc_adc_module_disable(sc);
+
+ return (0);
}
static int
-lpc_tsc_ev_open(struct evdev_dev *evdev, void *data)
+lpc_tsc_ev_open(struct evdev_dev *evdev)
{
- struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data;
+ struct lpc_tsc_softc *sc = evdev_get_softc(evdev);
LPC_TSC_LOCK_ASSERT(sc);
diff --git a/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c b/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c
new file mode 100644
index 00000000..ec3c9cdd
--- /dev/null
+++ b/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2021 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 <bsp.h>
+
+#ifdef LIBBSP_ARM_STM32H7_BSP_H
+
+#include <bsp/st-sdmmc-config.h>
+#include <dev/mmc/mmcreg.h>
+
+void
+st_sdmmc_get_config(uintptr_t sdmmc_base, struct st_sdmmc_config *cfg)
+{
+ switch (sdmmc_base) {
+ case SDMMC1_BASE:
+ cfg->data_lines = 4;
+ cfg->dirpol = true;
+ /*
+ * FIXME: Also the evaluation board could switch to 1.8V, the
+ * control for the level converter isn't implemented in the
+ * driver yet. So only signal 2.9V.
+ */
+ cfg->ocr_voltage = MMC_OCR_280_290 | MMC_OCR_290_300;
+ break;
+ }
+}
+
+#endif /* LIBBSP_ARM_STM32H7_BSP_H */
diff --git a/rtemsbsd/sys/dev/mmc/st-sdmmc.c b/rtemsbsd/sys/dev/mmc/st-sdmmc.c
new file mode 100644
index 00000000..4b202952
--- /dev/null
+++ b/rtemsbsd/sys/dev/mmc/st-sdmmc.c
@@ -0,0 +1,859 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2021 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.
+ */
+
+/*
+ * STM32H7xx SDMMC controller. Documentation: ST RM0433 (Rev 6), Chapter 54
+ *
+ * According to Linux DTS, the SDMMC is compatible with an ARM Primecell PL18X
+ * with peripheral ID 0x10153180.
+ */
+/*
+ * Note: This driver is inspired by the NetBSD pl181 driver written by Jared D.
+ * McNeill.
+ */
+/*
+ * Note regarding DMA on STM32H7:
+ *
+ * The STM32H7 SDMMC doesn't have an interrupt for few received data (less than
+ * half the FIFO size). So especially for short responses, it is not possible to
+ * use the SDMMC without DMA.
+ *
+ * On the STM32H7 SDMMC there are two DMAs: One IDMA integrated into the SDMMC
+ * and one MDMA that is a general purpose DMA. MDMA can only be used on first
+ * instance of the SDMMC. For the second instance, the trigger signals are not
+ * connected.
+ *
+ * The IDMA of SDMMC1 can only access AXI SRAM, QSPI and FMC (where SDRAM is
+ * located). The IDMA of SDMMC2 could access memory in other domains too.
+ *
+ * MDMA is designed to be a companion for IDMA. It seems that ST thought of a
+ * very specific software structure for that. It can be either used to change
+ * the IDMA buffer addresses (which would allow some kind of scatter gather
+ * functionality with fixed buffer sizes) or to refill IDMA buffers from some
+ * RAM that can't be accessed directly by the IDMA. Take a look at ST AN5200 Rev
+ * 1 "Getting started with STM32H7 Series SDMMC host controller" for more
+ * details.
+ */
+
+#include <rtems/malloc.h>
+#include <rtems/irq-extension.h>
+
+#include <bsp.h>
+
+#ifdef LIBBSP_ARM_STM32H7_BSP_H
+
+#include <stm32h7/hal.h>
+#include <stm32h7/memory.h>
+
+#include <bsp/st-sdmmc-config.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+
+#include <pthread.h>
+
+#include <machine/resource.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#define ST_SDMMC_LOCK(_sc) mtx_lock(&(_sc)->mtx)
+#define ST_SDMMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
+#define ST_SDMMC_LOCK_INIT(_sc) \
+ mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), \
+ "st_sdmmc", MTX_DEF)
+
+#define SDMMC_INT_ERROR_MASK ( \
+ SDMMC_MASK_CTIMEOUTIE | \
+ SDMMC_MASK_CCRCFAILIE | \
+ SDMMC_MASK_DTIMEOUTIE | \
+ SDMMC_MASK_DCRCFAILIE | \
+ SDMMC_MASK_ACKFAILIE | \
+ SDMMC_MASK_RXOVERRIE | \
+ SDMMC_MASK_TXUNDERRIE | \
+ SDMMC_MASK_DABORTIE | \
+ SDMMC_MASK_ACKTIMEOUTIE )
+#define SDMMC_INT_DATA_DONE_MASK ( SDMMC_MASK_IDMABTCIE | SDMMC_MASK_DATAENDIE )
+#define SDMMC_INT_CMD_DONE_MASK ( SDMMC_MASK_CMDSENTIE )
+#define SDMMC_INT_CMD_RESPONSE_DONE_MASK ( SDMMC_MASK_CMDRENDIE )
+
+#define RES_MEM_SDMMC 0
+#define RES_MEM_DLYB 1
+#define RES_IRQ_SDMMC 2
+#define RES_NR 3
+
+/* Maximum non-aligned buffer is 512 byte from mmc_send_ext_csd() */
+#define DMA_BUF_SIZE 512
+
+#if 0
+#define debug_print(sc, lvl, ...) \
+ if (lvl <= 1) device_printf(sc->dev, __VA_ARGS__)
+#else
+#define debug_print(...)
+#endif
+
+struct st_sdmmc_softc;
+
+typedef void (*st_sdmmc_dma_setup_transfer)(struct st_sdmmc_softc *, void *);
+
+struct st_sdmmc_softc {
+ device_t dev;
+ struct mmc_host host;
+
+ struct mtx mtx;
+ rtems_binary_semaphore wait_done;
+ int bus_busy;
+
+ struct resource *res[RES_NR];
+ SDMMC_TypeDef *sdmmc;
+ DLYB_TypeDef *dlyb;
+ rtems_vector_number irq;
+
+ uint32_t sdmmc_ker_ck;
+ struct st_sdmmc_config cfg;
+
+ uint32_t intr_status;
+ uint8_t *dmabuf;
+};
+
+void st_sdmmc_idma_txrx(struct st_sdmmc_softc *sc, void *buf)
+{
+ BSD_ASSERT(
+ (buf >= (void*) stm32h7_memory_sdram_1_begin &&
+ buf < (void*) stm32h7_memory_sdram_1_end) ||
+ (buf >= (void*) stm32h7_memory_sram_axi_begin &&
+ buf < (void*) stm32h7_memory_sram_axi_end) ||
+ (buf >= (void*) stm32h7_memory_sdram_2_begin &&
+ buf < (void*) stm32h7_memory_sdram_2_end) ||
+ (buf >= (void*) stm32h7_memory_quadspi_begin &&
+ buf < (void*) stm32h7_memory_quadspi_end));
+ sc->sdmmc->IDMABASE0 = (uintptr_t) buf;
+ sc->sdmmc->IDMACTRL = SDMMC_IDMA_IDMAEN;
+}
+
+void st_sdmmc_idma_stop(struct st_sdmmc_softc *sc)
+{
+ sc->sdmmc->IDMACTRL = 0;
+}
+
+static void
+st_sdmmc_intr(void *arg)
+{
+ struct st_sdmmc_softc *sc;
+ uint32_t status;
+
+ sc = arg;
+
+ status = sc->sdmmc->STA;
+ sc->sdmmc->ICR = status;
+ sc->intr_status |= status;
+
+ /*
+ * There seems to be some odd combination where the status is zero but
+ * an interrupt occurred. In that case, the task shouldn't wake up.
+ * Therefore check for status != 0.
+ */
+ if (status != 0 &&
+ ((status & SDMMC_STA_BUSYD0) == 0 ||
+ (sc->sdmmc->MASK & SDMMC_STA_BUSYD0END) == 0)) {
+ rtems_binary_semaphore_post(&sc->wait_done);
+ }
+}
+
+static int
+st_sdmmc_probe(device_t dev)
+{
+ device_set_desc(dev, "STM32H7xx SDMMC Host");
+ return (0);
+}
+
+static int
+st_sdmmc_set_clock_and_bus(
+ struct st_sdmmc_softc *sc,
+ uint32_t freq,
+ enum mmc_bus_width width
+)
+{
+ uint32_t clk_div;
+ uint32_t clkcr;
+
+ clkcr = SDMMC_CLKCR_NEGEDGE | SDMMC_CLKCR_PWRSAV | SDMMC_CLKCR_HWFC_EN;
+ clk_div = howmany(sc->sdmmc_ker_ck, freq) / 2;
+ if (clk_div > SDMMC_CLKCR_CLKDIV >> SDMMC_CLKCR_CLKDIV_Pos) {
+ clk_div = SDMMC_CLKCR_CLKDIV >> SDMMC_CLKCR_CLKDIV_Pos;
+ }
+ clkcr |= clk_div << SDMMC_CLKCR_CLKDIV_Pos;
+
+ switch (width) {
+ default:
+ BSD_ASSERT(width == bus_width_1);
+ clkcr |= 0 << SDMMC_CLKCR_WIDBUS_Pos;
+ break;
+ case bus_width_4:
+ clkcr |= 1 << SDMMC_CLKCR_WIDBUS_Pos;
+ break;
+ case bus_width_8:
+ clkcr |= 2 << SDMMC_CLKCR_WIDBUS_Pos;
+ break;
+ }
+
+ sc->sdmmc->CLKCR = clkcr;
+
+ return 0;
+}
+
+static void
+st_sdmmc_host_reset(struct st_sdmmc_softc *sc)
+{
+ sc->sdmmc->MASK = 0;
+ sc->sdmmc->ICR = 0xFFFFFFFF;
+}
+
+static void
+st_sdmmc_hw_init(struct st_sdmmc_softc *sc)
+{
+ st_sdmmc_set_clock_and_bus(sc, 400000, bus_width_1);
+ sc->sdmmc->POWER = 0;
+ if (sc->cfg.dirpol) {
+ sc->sdmmc->POWER |= SDMMC_POWER_DIRPOL;
+ }
+ /* ST example code just set it on. So do the same. */
+ sc->sdmmc->POWER |= SDMMC_POWER_PWRCTRL_0 | SDMMC_POWER_PWRCTRL_1;
+ /*
+ * Wait at least 74 cycles; lowest freq is 400kHz
+ * -> 1/400kHz * 47 = 117us
+ */
+ usleep(120000);
+
+ st_sdmmc_host_reset(sc);
+}
+
+static void
+st_sdmmc_board_init(void)
+{
+ HAL_SD_MspInit(NULL);
+}
+
+static int
+st_sdmmc_attach(device_t dev)
+{
+ struct st_sdmmc_softc *sc;
+ int error = 0;
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ bool interrupt_installed = false;
+
+ sc = device_get_softc(dev);
+
+ memset(sc, 0, sizeof(*sc));
+
+ if (error == 0) {
+ sc->dev = dev;
+ }
+
+ if (error == 0) {
+ ST_SDMMC_LOCK_INIT(sc);
+ rtems_binary_semaphore_init(&sc->wait_done, "sdmmc-sem");
+ }
+
+ if (error == 0) {
+ int rid = 0;
+ sc->res[RES_MEM_SDMMC] = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &rid, 0, ~0, 1, RF_ACTIVE);
+ if (sc->res[RES_MEM_SDMMC] == NULL) {
+ device_printf(dev,
+ "could not allocate sdmmc resource\n");
+ error = ENXIO;
+ } else {
+ sc->sdmmc = (SDMMC_TypeDef *)
+ sc->res[RES_MEM_SDMMC]->r_bushandle;
+ }
+ }
+ if (error == 0) {
+ int rid = 0;
+ sc->res[RES_MEM_DLYB] = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &rid, 1, ~0, 1, RF_ACTIVE);
+ if (sc->res[RES_MEM_DLYB] == NULL) {
+ device_printf(dev,
+ "could not allocate dlyb resource\n");
+ error = ENXIO;
+ } else {
+ sc->dlyb = (DLYB_TypeDef *)
+ sc->res[RES_MEM_DLYB]->r_bushandle;
+ }
+ }
+ if (error == 0) {
+ int rid = 0;
+ sc->res[RES_IRQ_SDMMC] = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &rid, 0, ~0, 1, RF_ACTIVE);
+ if (sc->res[RES_IRQ_SDMMC] == NULL) {
+ device_printf(dev,
+ "could not allocate interrupt resource\n");
+ error = ENXIO;
+ } else {
+ sc->irq = sc->res[RES_IRQ_SDMMC]->r_bushandle;
+ }
+ }
+
+ if (error == 0) {
+ /*
+ * FIXME: This memory should be in AXI SRAM, QSPI or FMC. In the
+ * configurations for our BSP, the heap is either in AXI SRAM or
+ * in the SDRAM. So that is OK for now. Only assert that the
+ * assumption is true. A better solution (like fixed AXI SRAM)
+ * might would be a good idea.
+ */
+ sc->dmabuf = rtems_heap_allocate_aligned_with_boundary(
+ DMA_BUF_SIZE, CPU_CACHE_LINE_BYTES, 0);
+ if (sc->dmabuf == NULL) {
+ device_printf(dev, "could not allocate dma buffer\n");
+ error = ENOMEM;
+ }
+ BSD_ASSERT(
+ ((void*) sc->dmabuf >= (void*) stm32h7_memory_sram_axi_begin &&
+ (void*) sc->dmabuf < (void*) stm32h7_memory_sram_axi_end) ||
+ ((void*) sc->dmabuf >= (void*) stm32h7_memory_sdram_1_begin &&
+ (void*) sc->dmabuf < (void*) stm32h7_memory_sdram_1_end) ||
+ ((void*) sc->dmabuf >= (void*) stm32h7_memory_sdram_2_begin &&
+ (void*) sc->dmabuf < (void*) stm32h7_memory_sdram_2_end) ||
+ ((void*) sc->dmabuf >= (void*) stm32h7_memory_quadspi_begin &&
+ (void*) sc->dmabuf < (void*) stm32h7_memory_quadspi_end));
+ }
+
+ if (error == 0) {
+ pthread_once(&once, st_sdmmc_board_init);
+ }
+
+ if (error == 0) {
+ sc->sdmmc_ker_ck = HAL_RCCEx_GetPeriphCLKFreq(
+ RCC_PERIPHCLK_SDMMC);
+
+ sc->host.f_min = 400000;
+ sc->host.f_max = (int) sc->sdmmc_ker_ck;
+ if (sc->host.f_max > 50000000)
+ sc->host.f_max = 50000000;
+ }
+
+ if (error == 0) {
+ st_sdmmc_get_config((uintptr_t)sc->sdmmc, &sc->cfg);
+ if (sc->cfg.data_lines == 0) {
+ device_printf(dev, "config not found!\n");
+ error = EINVAL;
+ }
+ }
+
+ if (error == 0) {
+ st_sdmmc_hw_init(sc);
+ }
+
+ if (error == 0) {
+ error = rtems_interrupt_handler_install(sc->irq, "SDMMC",
+ RTEMS_INTERRUPT_UNIQUE, st_sdmmc_intr, sc);
+ if (error != 0) {
+ device_printf(dev,
+ "could not setup interrupt handler.\n");
+ } else {
+ interrupt_installed = true;
+ }
+ }
+
+ if (error == 0) {
+ sc->host.host_ocr = sc->cfg.ocr_voltage &
+ ((1 << (MMC_OCR_MAX_VOLTAGE_SHIFT + 1)) - 1);
+
+ sc->host.caps = MMC_CAP_HSPEED;
+ if (sc->cfg.data_lines >= 4) {
+ sc->host.caps |= MMC_CAP_4_BIT_DATA;
+ }
+ if (sc->cfg.data_lines >= 8) {
+ sc->host.caps |= MMC_CAP_8_BIT_DATA;
+ }
+ }
+
+ if (error == 0) {
+ device_add_child(dev, "mmc", -1);
+ error = bus_generic_attach(dev);
+ }
+
+ if (error != 0) {
+ /* Undo relevant parts */
+ if (interrupt_installed) {
+ rtems_interrupt_handler_remove(sc->irq,
+ st_sdmmc_intr, sc);
+ }
+
+ free(sc->dmabuf);
+
+ /*
+ * FIXME: Should free resources but RTEMS doesn't implement
+ * bus_free_resource().
+ */
+ }
+
+ return error;
+}
+
+static int
+st_sdmmc_detach(device_t dev)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Always attached. So this is not necessary. */
+ BSD_ASSERT(0);
+
+ (void)sc;
+
+ return (EBUSY);
+}
+
+static int
+st_sdmmc_update_ios(device_t brdev, device_t reqdev)
+{
+ struct st_sdmmc_softc *sc;
+ struct mmc_ios *ios;
+ int err;
+
+ sc = device_get_softc(brdev);
+
+ ST_SDMMC_LOCK(sc);
+
+ ios = &sc->host.ios;
+ err = st_sdmmc_set_clock_and_bus(sc, ios->clock, ios->bus_width);
+ if (err != 0) {
+ return (err);
+ }
+
+ if (ios->power_mode == power_off) {
+ /*
+ * FIXME: Maybe a reset of the module is necessary instead. But
+ * the ST samples use a power off too so it should work. But
+ * power saving hasn't been tested during development.
+ */
+ sc->sdmmc->POWER &= ~(SDMMC_POWER_PWRCTRL);
+ } else {
+ sc->sdmmc->POWER |= SDMMC_POWER_PWRCTRL;
+ }
+
+ ST_SDMMC_UNLOCK(sc);
+
+ return (EIO);
+}
+
+static int
+st_sdmmc_wait_irq(struct st_sdmmc_softc *sc)
+{
+ int error = 0;
+
+ error = rtems_binary_semaphore_wait_timed_ticks(&sc->wait_done,
+ RTEMS_MILLISECONDS_TO_TICKS(5000));
+
+ if (error != 0) {
+ error = MMC_ERR_TIMEOUT;
+ } else if ((sc->intr_status &
+ (SDMMC_STA_DTIMEOUT | SDMMC_STA_CTIMEOUT)) != 0) {
+ error = MMC_ERR_TIMEOUT;
+ } else if ((sc->intr_status & SDMMC_INT_ERROR_MASK) != 0) {
+ error = MMC_ERR_FAILED;
+ }
+
+ return error;
+}
+
+static void
+st_sdmmc_cmd_do(struct st_sdmmc_softc *sc, struct mmc_command *cmd)
+{
+ uint32_t cmdval;
+ uint32_t xferlen;
+ uint32_t int_mask;
+ uint32_t arg;
+ void *data = NULL;
+ bool short_xfer = false;
+
+ debug_print(sc, 1, "cmd: %d, arg: %08x, flags: 0x%x\n",
+ cmd->opcode, cmd->arg, cmd->flags);
+
+ xferlen = 0;
+ sc->intr_status = 0;
+
+ sc->sdmmc->CMD = 0;
+ /*
+ * There should be a delay of "at least seven sdmmc_hclk clock periods"
+ * before CMD is written again. The sdmmc_hclk is the clock that is used
+ * to access the Registers. Some more registers are accessed before the
+ * next CMD write. So that should be no problem.
+ */
+ sc->sdmmc->MASK = 0;
+ sc->sdmmc->ICR = 0xFFFFFFFF;
+ /*
+ * Make sure the semaphore is cleared at this point. There can be an
+ * error case where a previous command run into a timeout and still
+ * produced an interrupt before it has been disabled.
+ */
+ if (rtems_binary_semaphore_try_wait(&sc->wait_done) == 0) {
+ device_printf(sc->dev, "Semaphore set from last command\n");
+ }
+
+ int_mask = SDMMC_INT_ERROR_MASK;
+
+ arg = cmd->arg;
+ cmdval = (cmd->opcode & SDMMC_CMD_CMDINDEX_Msk) | SDMMC_CMD_CPSMEN;
+
+ if ((cmd->flags & MMC_RSP_PRESENT) != 0) {
+ if ((cmd->flags & MMC_RSP_136) != 0) {
+ cmdval |= SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_WAITRESP_1;
+ } else if ((cmd->flags & MMC_RSP_CRC) != 0) {
+ cmdval |= SDMMC_CMD_WAITRESP_0;
+ } else {
+ cmdval |= SDMMC_CMD_WAITRESP_1;
+ }
+ int_mask |= SDMMC_INT_CMD_RESPONSE_DONE_MASK;
+ } else {
+ int_mask |= SDMMC_INT_CMD_DONE_MASK;
+ }
+
+ if (cmd->opcode == MMC_STOP_TRANSMISSION) {
+ cmdval |= SDMMC_CMD_CMDSTOP;
+ }
+
+ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) {
+ cmdval |= SDMMC_CMD_CMDTRANS;
+ }
+
+ if ((cmd->flags & MMC_RSP_BUSY) != 0) {
+ int_mask |= SDMMC_MASK_BUSYD0ENDIE;
+ }
+
+ if (cmd->data != NULL) {
+ uint32_t blksize;
+ uint32_t dctrl = 0;
+ xferlen = cmd->data->len;
+
+ if (xferlen > MMC_SECTOR_SIZE) {
+ blksize = ffs(MMC_SECTOR_SIZE) - 1;
+ } else {
+ blksize = ffs(xferlen) - 1;
+ }
+
+ debug_print(sc, 1,
+ "data: len: %d, xferlen: %d, blksize: %d, dataflags: 0x%x\n",
+ cmd->data->len, xferlen, blksize, cmd->data->flags);
+
+ BSD_ASSERT(xferlen % (1 << blksize) == 0);
+
+ data = cmd->data->data;
+ /*
+ * Check whether data have to be copied. Reason is either
+ * misaligned start address or misaligned length.
+ */
+ if (((uintptr_t)data % CPU_CACHE_LINE_BYTES != 0) ||
+ (xferlen % CPU_CACHE_LINE_BYTES) != 0) {
+ BSD_ASSERT(xferlen < DMA_BUF_SIZE);
+ if ((cmd->data->flags & MMC_DATA_READ) == 0) {
+ memcpy(sc->dmabuf, cmd->data->data, xferlen);
+ }
+ data = sc->dmabuf;
+ short_xfer = true;
+ }
+
+ dctrl |= blksize << SDMMC_DCTRL_DBLOCKSIZE_Pos;
+ if ((cmd->data->flags & MMC_DATA_READ) != 0) {
+ dctrl |= SDMMC_DCTRL_DTDIR;
+ rtems_cache_invalidate_multiple_data_lines(data,
+ roundup2(xferlen, CPU_CACHE_LINE_BYTES));
+ } else {
+ rtems_cache_flush_multiple_data_lines(data,
+ roundup2(xferlen, CPU_CACHE_LINE_BYTES));
+ }
+ st_sdmmc_idma_txrx(sc, data);
+
+ sc->sdmmc->DTIMER = 0xFFFFFFFF;
+ sc->sdmmc->DLEN = xferlen;
+ sc->sdmmc->DCTRL = dctrl;
+
+ int_mask &= ~(SDMMC_INT_CMD_DONE_MASK |
+ SDMMC_INT_CMD_RESPONSE_DONE_MASK);
+ int_mask |= SDMMC_INT_DATA_DONE_MASK;
+ }
+
+ sc->sdmmc->MASK = int_mask;
+ sc->sdmmc->ARG = arg;
+ sc->sdmmc->CMD = cmdval | cmd->opcode;
+
+ cmd->error = st_sdmmc_wait_irq(sc);
+ if (cmd->error) {
+ sleep(10);
+ device_printf(sc->dev,
+ "error (%d) waiting for xfer: status %08x, cmd: %d, flags: %08x\n",
+ cmd->error, sc->intr_status, cmd->opcode, cmd->flags);
+ } else {
+ if ((cmd->flags & MMC_RSP_PRESENT) != 0) {
+ if ((cmd->flags & MMC_RSP_136) != 0) {
+ cmd->resp[0] = sc->sdmmc->RESP1;
+ cmd->resp[1] = sc->sdmmc->RESP2;
+ cmd->resp[2] = sc->sdmmc->RESP3;
+ cmd->resp[3] = sc->sdmmc->RESP4;
+ debug_print(sc, 2, "rsp: %08x %08x %08x %08x\n",
+ cmd->resp[0],
+ cmd->resp[1],
+ cmd->resp[2],
+ cmd->resp[3]);
+ } else {
+ cmd->resp[0] = sc->sdmmc->RESP1;
+ debug_print(sc, 2, "rsp: %08x\n", cmd->resp[0]);
+ }
+ }
+
+ if (short_xfer && cmd->data != NULL &&
+ (cmd->data->flags & MMC_DATA_READ) != 0) {
+ memcpy(cmd->data->data, sc->dmabuf, xferlen);
+ }
+ }
+
+ st_sdmmc_idma_stop(sc);
+ sc->sdmmc->CMD = 0;
+ sc->sdmmc->MASK = 0;
+ sc->sdmmc->ICR = 0xFFFFFFFF;
+}
+
+static int
+st_sdmmc_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(brdev);
+
+ ST_SDMMC_LOCK(sc);
+ st_sdmmc_cmd_do(sc, req->cmd);
+ if (req->stop != NULL) {
+ st_sdmmc_cmd_do(sc, req->stop);
+ }
+ ST_SDMMC_UNLOCK(sc);
+
+ (*req->done)(req);
+
+ return (0);
+}
+
+static int
+st_sdmmc_get_ro(device_t brdev, device_t reqdev)
+{
+
+ /*
+ * FIXME: Currently just ignore write protection. Micro-SD doesn't have
+ * it anyway and most boards are now using Micro-SD slots.
+ */
+ return (0);
+}
+
+static int
+st_sdmmc_acquire_host(device_t brdev, device_t reqdev)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(brdev);
+
+ ST_SDMMC_LOCK(sc);
+ while (sc->bus_busy)
+ msleep(sc, &sc->mtx, PZERO, "stsdmmcah", hz / 5);
+ sc->bus_busy++;
+ ST_SDMMC_UNLOCK(sc);
+ return (0);
+}
+
+static int
+st_sdmmc_release_host(device_t brdev, device_t reqdev)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(brdev);
+
+ ST_SDMMC_LOCK(sc);
+ sc->bus_busy--;
+ wakeup(sc);
+ ST_SDMMC_UNLOCK(sc);
+ return (0);
+}
+
+static int
+st_sdmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(bus);
+
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ *(int *)result = sc->host.ios.bus_mode;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ *(int *)result = sc->host.ios.bus_width;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ *(int *)result = sc->host.ios.chip_select;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ *(int *)result = sc->host.ios.clock;
+ break;
+ case MMCBR_IVAR_F_MIN:
+ *(int *)result = sc->host.f_min;
+ break;
+ case MMCBR_IVAR_F_MAX:
+ *(int *)result = sc->host.f_max;
+ break;
+ case MMCBR_IVAR_HOST_OCR:
+ *(int *)result = sc->host.host_ocr;
+ break;
+ case MMCBR_IVAR_MODE:
+ *(int *)result = sc->host.mode;
+ break;
+ case MMCBR_IVAR_OCR:
+ *(int *)result = sc->host.ocr;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ *(int *)result = sc->host.ios.power_mode;
+ break;
+ case MMCBR_IVAR_VDD:
+ *(int *)result = sc->host.ios.vdd;
+ break;
+ case MMCBR_IVAR_VCCQ:
+ *(int *)result = sc->host.ios.vccq;
+ break;
+ case MMCBR_IVAR_CAPS:
+ *(int *)result = sc->host.caps;
+ break;
+ case MMCBR_IVAR_MAX_DATA:
+ /*
+ * Note: At the moment of writing this, RTEMS ignores this
+ * value. So it's quite irrelevant what is returned here.
+ */
+ *(int *)result = (SDMMC_DLEN_DATALENGTH_Msk) / MMC_SECTOR_SIZE;
+ break;
+ case MMCBR_IVAR_TIMING:
+ *(int *)result = sc->host.ios.timing;
+ break;
+ }
+ return (0);
+}
+
+static int
+st_sdmmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+{
+ struct st_sdmmc_softc *sc;
+
+ sc = device_get_softc(bus);
+
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ sc->host.ios.bus_mode = value;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ sc->host.ios.bus_width = value;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ sc->host.ios.chip_select = value;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ sc->host.ios.clock = value;
+ break;
+ case MMCBR_IVAR_MODE:
+ sc->host.mode = value;
+ break;
+ case MMCBR_IVAR_OCR:
+ sc->host.ocr = value;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ sc->host.ios.power_mode = value;
+ break;
+ case MMCBR_IVAR_VDD:
+ sc->host.ios.vdd = value;
+ break;
+ case MMCBR_IVAR_TIMING:
+ sc->host.ios.timing = value;
+ break;
+ case MMCBR_IVAR_VCCQ:
+ sc->host.ios.vccq = value;
+ break;
+ /* These are read-only */
+ case MMCBR_IVAR_CAPS:
+ case MMCBR_IVAR_HOST_OCR:
+ case MMCBR_IVAR_F_MIN:
+ case MMCBR_IVAR_F_MAX:
+ case MMCBR_IVAR_MAX_DATA:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static device_method_t st_sdmmc_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, st_sdmmc_probe),
+ DEVMETHOD(device_attach, st_sdmmc_attach),
+ DEVMETHOD(device_detach, st_sdmmc_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, st_sdmmc_read_ivar),
+ DEVMETHOD(bus_write_ivar, st_sdmmc_write_ivar),
+
+ /* mmcbr_if */
+ DEVMETHOD(mmcbr_update_ios, st_sdmmc_update_ios),
+ DEVMETHOD(mmcbr_request, st_sdmmc_request),
+ DEVMETHOD(mmcbr_get_ro, st_sdmmc_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, st_sdmmc_acquire_host),
+ DEVMETHOD(mmcbr_release_host, st_sdmmc_release_host),
+
+ DEVMETHOD_END
+};
+
+static driver_t st_sdmmc_driver = {
+ "st_sdmmc",
+ st_sdmmc_methods,
+ sizeof(struct st_sdmmc_softc)
+};
+
+static devclass_t st_sdmmc_devclass;
+
+DRIVER_MODULE(st_sdmmc, nexus, st_sdmmc_driver, st_sdmmc_devclass, NULL, NULL);
+DRIVER_MODULE(mmc, st_sdmmc, mmc_driver, mmc_devclass, NULL, NULL);
+MODULE_DEPEND(st_sdmmc, mmc, 1, 1, 1);
+
+#endif /* LIBBSP_ARM_STM32H7_BSP_H */
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/nvd/nvd.c b/rtemsbsd/sys/dev/nvd/nvd.c
index 897b0af6..4a8e8ac1 100644
--- a/rtemsbsd/sys/dev/nvd/nvd.c
+++ b/rtemsbsd/sys/dev/nvd/nvd.c
@@ -43,6 +43,7 @@
#include <stdatomic.h>
#include <rtems/blkdev.h>
+#include <rtems/thread.h>
#define NVD_STR "nvd"
@@ -164,10 +165,9 @@ nvd_completion(void *arg, const struct nvme_completion *status)
static int
nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req,
- uint32_t media_blocks_per_block)
+ uint32_t media_block_size)
{
uint32_t i;
- uint32_t lb_count;
uint32_t bufnum;
BSD_ASSERT(req->req == RTEMS_BLKDEV_REQ_READ ||
@@ -178,13 +178,15 @@ nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req,
req->status = RTEMS_SUCCESSFUL;
bufnum = req->bufnum;
req->bufnum |= bufnum << NVD_BUFNUM_SHIFT;
- lb_count = media_blocks_per_block * ndisk->lb_per_media_block;
for (i = 0; i < bufnum; ++i) {
rtems_blkdev_sg_buffer *sg;
+ uint32_t lb_count;
int error;
sg = &req->bufs[i];
+ lb_count = (sg->length / media_block_size) *
+ ndisk->lb_per_media_block;
if (req->req == RTEMS_BLKDEV_REQ_READ) {
error = nvme_ns_cmd_read(ndisk->ns, sg->buffer,
@@ -208,28 +210,27 @@ nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req,
static void
nvd_sync_completion(void *arg, const struct nvme_completion *status)
{
- rtems_status_code sc;
-
- if (nvme_completion_is_error(status)) {
- sc = RTEMS_IO_ERROR;
- } else {
- sc = RTEMS_SUCCESSFUL;
- }
+ rtems_binary_semaphore *sync;
- rtems_blkdev_request_done(arg, sc);
+ sync = arg;
+ rtems_binary_semaphore_post(sync);
}
static int
-nvd_sync(struct nvd_disk *ndisk, rtems_blkdev_request *req)
+nvd_sync(struct nvd_disk *ndisk)
{
+ rtems_binary_semaphore sync;
int error;
- error = nvme_ns_cmd_flush(ndisk->ns, nvd_sync_completion, req);
- if (error != 0) {
- rtems_blkdev_request_done(req, RTEMS_NO_MEMORY);
+ rtems_binary_semaphore_init(&sync, "nvd sync");
+
+ error = nvme_ns_cmd_flush(ndisk->ns, nvd_sync_completion, &sync);
+ if (error == 0) {
+ rtems_binary_semaphore_wait(&sync);
}
- return (0);
+ rtems_binary_semaphore_destroy(&sync);
+ return (error);
}
static int
@@ -240,11 +241,11 @@ nvd_ioctl(rtems_disk_device *dd, uint32_t req, void *arg)
ndisk = rtems_disk_get_driver_data(dd);
if (req == RTEMS_BLKIO_REQUEST) {
- return (nvd_request(ndisk, arg, dd->media_blocks_per_block));
+ return (nvd_request(ndisk, arg, dd->media_block_size));
}
if (req == RTEMS_BLKDEV_REQ_SYNC) {
- return (nvd_sync(ndisk, arg));
+ return (nvd_sync(ndisk));
}
if (req == RTEMS_BLKIO_CAPABILITIES) {
diff --git a/rtemsbsd/sys/dev/stmac/if_stmac.c b/rtemsbsd/sys/dev/stmac/if_stmac.c
new file mode 100644
index 00000000..7e3e07c7
--- /dev/null
+++ b/rtemsbsd/sys/dev/stmac/if_stmac.c
@@ -0,0 +1,1009 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 embedded brains Gmb_h (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_ARM_STM32H7_BSP_H
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/gsb_crc32.h>
+
+#include <net/if.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 <machine/bus.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <rtems/bsd/local/miibus_if.h>
+
+#include <stm32h7xx_hal.h>
+
+#include <rtems/bsd/bsd.h>
+#include <rtems/irq-extension.h>
+#include <rtems/score/armv7m.h>
+
+#define RX_DESC_COUNT 64
+#define TX_DESC_COUNT 256
+
+#define STMAC_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define STMAC_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+
+#define STMAC_TXLOCK(sc) mtx_lock(&(sc)->tx_mtx)
+#define STMAC_TXUNLOCK(sc) mtx_unlock(&(sc)->tx_mtx)
+
+struct stmac_desc {
+ __IO uint32_t DESC0;
+ __IO uint32_t DESC1;
+ __IO uint32_t DESC2;
+ __IO uint32_t DESC3;
+ struct mbuf *m;
+};
+
+struct stmac_softc {
+ ETH_HandleTypeDef heth;
+ uint8_t mac_addr[6];
+ struct ifnet *ifp;
+ struct mtx mtx;
+ device_t miibus;
+ struct mtx mii_mtx;
+ struct mii_data *mii_softc;
+ u_int mii_media_active;
+ u_int mii_media_status;
+ struct callout tick_callout;
+ int if_flags;
+
+ /* RX */
+ struct stmac_desc *rx_desc_ring;
+ uint32_t rx_idx;
+ bool rx_do_alloc;
+
+ /* TX */
+ struct mtx tx_mtx;
+ struct stmac_desc *tx_desc_ring;
+ uint32_t tx_idx_head;
+ uint32_t tx_idx_tail;
+};
+
+static void stmac_init_locked(struct stmac_softc *);
+static void stmac_stop_locked(struct stmac_softc *);
+
+struct mbuf *
+stmac_new_mbuf(struct ifnet *ifp)
+{
+ struct mbuf *m;
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m != NULL) {
+ m->m_data = mtod(m, char *) + ETHER_ALIGN;
+ m->m_pkthdr.rcvif = ifp;
+ rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
+ }
+
+ return m;
+}
+
+static void
+stmac_tick(void *arg)
+{
+ struct stmac_softc *sc;
+ struct ifnet *ifp;
+
+ sc = arg;
+ ifp = sc->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ return;
+ }
+
+ mii_tick(sc->mii_softc);
+ callout_reset(&sc->tick_callout, hz, stmac_tick, sc);
+}
+
+static uint32_t
+stmac_rx_desc3(uint32_t idx)
+{
+
+ if (idx % 16 != 0) {
+ return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V);
+ }
+
+ return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V |
+ ETH_DMARXNDESCRF_IOC);
+}
+
+static void
+stmac_rx_setup_desc(struct stmac_softc *sc)
+{
+ uint32_t idx;
+ bool do_alloc;
+ ETH_TypeDef *regs;
+
+ do_alloc = sc->rx_do_alloc;
+ sc->rx_do_alloc = false;
+ sc->rx_idx = 0;
+
+ for (idx = 0; idx < RX_DESC_COUNT; ++idx) {
+ struct stmac_desc *desc;
+ struct mbuf *m;
+
+ desc = &sc->rx_desc_ring[idx];
+
+ if (do_alloc) {
+ m = stmac_new_mbuf(sc->ifp);
+ BSD_ASSERT(m != NULL);
+ desc->m = m;
+ } else {
+ m = desc->m;
+ }
+
+ WRITE_REG(desc->DESC0, mtod(m, uint32_t));
+ WRITE_REG(desc->DESC1, 0x0);
+ WRITE_REG(desc->DESC2, 0x0);
+ WRITE_REG(desc->DESC3, stmac_rx_desc3(idx));
+ }
+
+ regs = sc->heth.Instance;
+ WRITE_REG(regs->DMACRDRLR, RX_DESC_COUNT -1);
+ WRITE_REG(regs->DMACRDLAR, (uint32_t)&sc->rx_desc_ring[0]);
+ WRITE_REG(regs->DMACRDTPR,
+ (uint32_t)&sc->rx_desc_ring[RX_DESC_COUNT - 1]);
+}
+
+static void
+stmac_tx_setup_desc(struct stmac_softc *sc)
+{
+ uint32_t idx;
+ ETH_TypeDef *regs;
+
+ sc->tx_idx_head = 0;
+ sc->tx_idx_tail = 0;
+
+ for (idx = 0; idx < TX_DESC_COUNT; ++idx) {
+ struct stmac_desc *desc;
+
+ desc = &sc->tx_desc_ring[idx];
+ WRITE_REG(desc->DESC0, 0x0);
+ WRITE_REG(desc->DESC1, 0x0);
+ WRITE_REG(desc->DESC2, 0x0);
+ WRITE_REG(desc->DESC3, 0x0);
+ desc->m = NULL;
+ }
+
+ regs = sc->heth.Instance;
+ WRITE_REG(regs->DMACTDRLR, TX_DESC_COUNT -1);
+ WRITE_REG(regs->DMACTDLAR, (uint32_t)&sc->tx_desc_ring[0]);
+ WRITE_REG(regs->DMACTDTPR, (uint32_t)&sc->tx_desc_ring[0]);
+}
+
+static void
+stmac_init_locked(struct stmac_softc *sc)
+{
+ struct ifnet *ifp;
+ ETH_TypeDef *regs;
+
+ ifp = sc->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ return;
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ stmac_rx_setup_desc(sc);
+ stmac_tx_setup_desc(sc);
+
+ HAL_ETH_Start(&sc->heth);
+ regs = sc->heth.Instance;
+
+ /* Enable interrupts */
+ SET_BIT(regs->DMACIER, ETH_DMACIER_NIE | ETH_DMACIER_RIE |
+ ETH_DMACIER_FBEE | ETH_DMACIER_AIE);
+
+ mii_mediachg(sc->mii_softc);
+ callout_reset(&sc->tick_callout, hz, stmac_tick, sc);
+}
+
+static void
+stmac_tx_reclaim_all(struct stmac_softc *sc)
+{
+ uint32_t idx;
+
+ for (idx = 0; idx < TX_DESC_COUNT; ++idx) {
+ struct stmac_desc *desc;
+ struct mbuf *m;
+
+ desc = &sc->tx_desc_ring[idx];
+ m = desc->m;
+ desc->m = NULL;
+ m_freem(m);
+ }
+}
+
+static void
+stmac_stop_locked(struct stmac_softc *sc)
+{
+ struct ifnet *ifp;
+ ETH_TypeDef *regs;
+ rtems_interrupt_server_entry server_entry;
+ rtems_status_code status;
+ rtems_name name;
+ uint64_t t0;
+
+ ifp = sc->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ return;
+ }
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ regs = sc->heth.Instance;
+
+ /* Disable interrupts */
+ WRITE_REG(regs->DMACIER, 0);
+
+ /* Disable the DMA transmission and reception */
+ CLEAR_BIT(regs->DMACTCR, ETH_DMACTCR_ST);
+ CLEAR_BIT(regs->DMACRCR, ETH_DMACRCR_SR);
+
+ /* Wait for DMA done */
+ t0 = rtems_clock_get_uptime_nanoseconds();
+ while ((regs->MTLTQDR & (ETH_MTLTQDR_TXQSTS |
+ ETH_MTLTQDR_TRCSTS)) != 0 ||
+ (regs->MTLRQDR & (ETH_MTLRQDR_RXQSTS |
+ ETH_MTLRQDR_PRXQ)) != 0) {
+ if (rtems_clock_get_uptime_nanoseconds() - t0 > 500000) {
+ break;
+ }
+ }
+
+ /* Disable the MAC reception and transmission */
+ CLEAR_BIT(regs->MACCR, ETH_MACCR_RE | ETH_MACCR_TE);
+
+ /* Make sure no receive interrupt is in progress */
+ status = rtems_object_get_classic_name(rtems_task_self(), &name);
+ BSD_ASSERT(status == RTEMS_SUCCESSFUL);
+ if (name != rtems_build_name('I', 'R', 'Q', 'S')) {
+ status = rtems_interrupt_server_entry_initialize(
+ RTEMS_INTERRUPT_SERVER_DEFAULT, &server_entry);
+ BSD_ASSERT(status == RTEMS_SUCCESSFUL);
+ rtems_interrupt_server_entry_destroy(&server_entry);
+ }
+
+ stmac_tx_reclaim_all(sc);
+}
+
+static uint8_t
+stmac_bitreverse(uint8_t x)
+{
+ static const uint8_t nibbletab[] = {
+ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
+ };
+
+ return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
+}
+
+static uint64_t
+stmac_hash_bit(const uint8_t *eaddr)
+{
+ uint32_t crc;
+
+ /*
+ * Thanks to the excellent documentation from STM it was easy to figure
+ * this out.
+ */
+ crc = 0;
+ crc = crc32_raw(eaddr, ETHER_ADDR_LEN, ~crc);
+ crc = stmac_bitreverse((uint8_t)~crc);
+ return ((uint64_t)1 << (crc >> 2));
+}
+
+static void
+stmac_rx_setup_filter(struct stmac_softc *sc)
+{
+ struct ifmultiaddr *ifma;
+ struct ifnet *ifp;
+ ETH_TypeDef *regs;
+ uint32_t macpfr;
+ uint64_t machtr;
+ const uint8_t *eaddr;
+
+ ifp = sc->ifp;
+
+ macpfr = ETH_MACPFR_HMC;
+ if ((ifp->if_flags & IFF_PROMISC) != 0) {
+ macpfr |= ETH_MACPFR_PR;
+ }
+
+ machtr = 0;
+ if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
+ machtr = ~machtr;
+ } else {
+ if_maddr_rlock(ifp);
+ CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK) {
+ continue;
+ }
+
+ eaddr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
+ machtr |= stmac_hash_bit(eaddr);
+ }
+ if_maddr_runlock(ifp);
+ }
+
+ regs = sc->heth.Instance;
+ eaddr = IF_LLADDR(ifp);
+ regs->MACA0LR = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
+ (eaddr[3] << 24);
+ regs->MACA0HR = eaddr[4] | (eaddr[5] << 8);
+ regs->MACHT0R = (uint32_t)machtr;
+ regs->MACHT1R = (uint32_t)(machtr >> 32);
+ regs->MACPFR = macpfr;
+}
+
+static int
+stmac_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+ struct stmac_softc *sc;
+ int error;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ STMAC_LOCK(sc);
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ if ((ifp->if_flags ^ sc->if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI))
+ stmac_rx_setup_filter(sc);
+ } else {
+ stmac_init_locked(sc);
+ }
+ } else {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ STMAC_TXLOCK(sc);
+ stmac_stop_locked(sc);
+ STMAC_TXUNLOCK(sc);
+ }
+ }
+ sc->if_flags = ifp->if_flags;
+ STMAC_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ STMAC_LOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ stmac_rx_setup_filter(sc);
+ }
+ STMAC_UNLOCK(sc);
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ mii = sc->mii_softc;
+ if (mii != NULL) {
+ error = ifmedia_ioctl(ifp, (struct ifreq *)data, &mii->mii_media, cmd);
+ } else {
+ error = ether_ioctl(ifp, cmd, data);
+ }
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void
+stmac_init(void *arg)
+{
+ struct stmac_softc *sc;
+
+ sc = arg;
+ STMAC_LOCK(sc);
+ STMAC_TXLOCK(sc);
+ stmac_init_locked(sc);
+ STMAC_TXUNLOCK(sc);
+ STMAC_UNLOCK(sc);
+}
+
+static void
+stmac_media_status(struct ifnet *ifp, struct ifmediareq *ifmr_p)
+{
+ struct stmac_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+
+ STMAC_LOCK(sc);
+ mii = sc->mii_softc;
+ if (mii != NULL) {
+ mii_pollstat(mii);
+ ifmr_p->ifm_active = mii->mii_media_active;
+ ifmr_p->ifm_status = mii->mii_media_status;
+ }
+ STMAC_UNLOCK(sc);
+}
+
+static int
+stmac_media_change(struct ifnet *ifp)
+{
+ struct stmac_softc *sc;
+ int error;
+
+ sc = ifp->if_softc;
+
+ STMAC_LOCK(sc);
+ if (sc->mii_softc != NULL) {
+ error = mii_mediachg(sc->mii_softc);
+ } else {
+ error = ENXIO;
+ }
+ STMAC_UNLOCK(sc);
+ return (error);
+}
+
+static int
+stmac_probe(device_t dev)
+{
+
+ device_set_desc(dev, "STM MAC");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+stmac_rx_checksum(struct mbuf *m, uint32_t desc1)
+{
+
+ if ((desc1 & (ETH_DMARXNDESCWBF_IPCE | ETH_DMARXNDESCWBF_IPCB |
+ ETH_DMARXNDESCWBF_IPHE)) == 0) {
+ uint32_t pt;
+
+ pt = desc1 & ETH_DMARXNDESCWBF_PT;
+ if (pt == ETH_DMARXNDESCWBF_PT_UDP ||
+ pt == ETH_DMARXNDESCWBF_PT_TCP) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED |
+ CSUM_IP_VALID |
+ CSUM_DATA_VALID |
+ CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ } else if ((desc1 & ETH_DMARXNDESCWBF_IPV4) != 0) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED |
+ CSUM_IP_VALID;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+}
+
+static void
+stmac_rx_interrupt(struct stmac_softc *sc, ETH_TypeDef *regs)
+{
+ struct ifnet *ifp;
+ uint32_t idx;
+ struct stmac_desc *desc_ring;
+ struct stmac_desc *desc;
+
+ ifp = sc->ifp;
+ desc_ring = sc->rx_desc_ring;
+ idx = sc->rx_idx;
+ desc = &desc_ring[idx];
+
+ while (true) {
+ uint32_t desc3;
+ struct mbuf *m;
+
+ /*
+ * Make sure the interrupt is cleared and no longer pending
+ * before we read the descriptor status.
+ */
+ regs->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS;
+ regs->DMACSR;
+ _ARMV7M_NVIC_Clear_pending(ETH_IRQn);
+
+ desc3 = desc->DESC3;
+ if ((desc3 & ETH_DMARXNDESCRF_OWN) != 0) {
+ break;
+ }
+
+ m = stmac_new_mbuf(ifp);
+ if (m != NULL) {
+ uint32_t mask;
+ uint32_t set;
+ uint32_t len;
+
+ mask = ETH_DMARXNDESCWBF_CTXT | ETH_DMARXNDESCWBF_FD |
+ ETH_DMARXNDESCWBF_LD | ETH_DMARXNDESCWBF_ES;
+ set = ETH_DMARXNDESCWBF_FD | ETH_DMARXNDESCWBF_LD;
+ len = desc3 & ETH_DMARXNDESCWBF_PL;
+ if ((desc3 & mask) == set && len > ETHER_CRC_LEN) {
+ struct mbuf *rx;
+
+ rx = desc->m;
+ stmac_rx_checksum(rx, desc->DESC1);
+ rx->m_len = len;
+ rx->m_pkthdr.len = len;
+ (*ifp->if_input)(ifp, rx);
+ } else {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ m = desc->m;
+ }
+ } else {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ m = desc->m;
+ }
+
+ WRITE_REG(desc->DESC0, mtod(m, uint32_t));
+ WRITE_REG(desc->DESC1, 0x0);
+ WRITE_REG(desc->DESC2, 0x0);
+ WRITE_REG(desc->DESC3, stmac_rx_desc3(idx));
+ desc->m = m;
+
+ _ARM_Data_synchronization_barrier();
+ WRITE_REG(regs->DMACRDTPR, (uint32_t)desc);
+
+ idx = (idx + 1) % RX_DESC_COUNT;
+ desc = &desc_ring[idx];
+ }
+
+ sc->rx_idx = idx;
+}
+
+static void
+stmac_reset(struct stmac_softc *sc)
+{
+ HAL_StatusTypeDef hal_status;
+ ETH_TypeDef *regs;
+
+ hal_status = HAL_ETH_Init(&sc->heth);
+ BSD_ASSERT(hal_status == HAL_OK);
+
+ regs = sc->heth.Instance;
+
+ /* Set descriptor skip length according to struct stmac_desc */
+ MODIFY_REG(regs->DMACCR, ETH_DMACCR_DSL, ETH_DMACCR_DSL_32BIT);
+
+ /*
+ * FIXME: Just use a random value. It is not clear which clock is used
+ * here.
+ */
+ regs->DMACRIWTR = 0x80;
+}
+
+static void
+stmac_interrupt(void *arg)
+{
+ struct stmac_softc *sc;
+ ETH_TypeDef *regs;
+ uint32_t dmacsr;
+
+ sc = arg;
+ regs = sc->heth.Instance;
+ dmacsr = regs->DMACSR;
+
+ /* This is almost always a receive interrupt */
+ stmac_rx_interrupt(sc, regs);
+
+ if (__predict_false((dmacsr & ETH_DMACSR_FBE) != 0)) {
+ regs->DMACSR = ETH_DMACSR_FBE | ETH_DMACSR_AIS;
+
+ STMAC_LOCK(sc);
+ STMAC_TXLOCK(sc);
+ stmac_stop_locked(sc);
+ stmac_reset(sc);
+ stmac_init_locked(sc);
+ STMAC_TXUNLOCK(sc);
+ STMAC_UNLOCK(sc);
+ }
+}
+
+static void
+stmac_tx_reclaim(struct stmac_softc *sc, struct ifnet *ifp)
+{
+ uint32_t head_idx;
+ uint32_t tail_idx;
+ struct stmac_desc *desc_ring;
+
+ head_idx = sc->tx_idx_head;
+ tail_idx = sc->tx_idx_tail;
+ desc_ring = sc->tx_desc_ring;
+
+ while (head_idx != tail_idx) {
+ struct stmac_desc *tail_desc;
+ uint32_t desc3;
+
+ tail_desc = &desc_ring[tail_idx];
+
+ desc3 = tail_desc->DESC3;
+ if ((desc3 & ETH_DMATXNDESCWBF_OWN) != 0) {
+ break;
+ }
+
+ if ((desc3 & ETH_DMATXNDESCWBF_LD) != 0) {
+ struct mbuf *m;
+ ift_counter cnt;
+
+ if ((desc3 & ETH_DMATXNDESCWBF_ES) == 0) {
+ cnt = IFCOUNTER_OPACKETS;
+ } else {
+ cnt = IFCOUNTER_OERRORS;
+ }
+
+ if_inc_counter(ifp, cnt, 1);
+
+ m = tail_desc->m;
+ tail_desc->m = NULL;
+ m_freem(m);
+ }
+
+ tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
+ }
+
+ sc->tx_idx_tail = tail_idx;
+}
+
+static void
+stmac_cache_flush(uintptr_t begin, uintptr_t size)
+{
+ 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);
+}
+
+static int
+stmac_tx_enqueue(struct stmac_softc *sc, struct ifnet *ifp, struct mbuf *m)
+{
+ uint32_t head_idx;
+ uint32_t tail_idx;
+ uint32_t capacity;
+ uint32_t new_head_idx;
+ uint32_t idx;
+ struct stmac_desc *desc_ring;
+ struct stmac_desc *desc;
+ ETH_TypeDef *regs;
+ uint32_t bufs;
+ uint32_t desc2;
+ uint32_t desc3;
+ struct mbuf *n;
+ int csum_flags;
+
+ head_idx = sc->tx_idx_head;
+ tail_idx = sc->tx_idx_tail;
+ capacity = 2 * ((tail_idx - head_idx - 1) % TX_DESC_COUNT);
+
+ idx = head_idx;
+ desc_ring = sc->tx_desc_ring;
+ desc2 = 0;
+ bufs = 0;
+ n = m;
+
+ do {
+ uintptr_t size;
+
+ desc = &desc_ring[idx];
+
+ size = (uintptr_t)n->m_len;
+ if (__predict_true(size > 0)) {
+ uintptr_t begin;
+
+ ++bufs;
+ if (__predict_false(bufs > capacity)) {
+ return (ENOBUFS);
+ }
+
+ begin = mtod(n, uintptr_t);
+
+ if (bufs % 2 == 1) {
+ desc->DESC0 = begin;
+ desc2 = size;
+ } else {
+ desc->DESC1 = begin;
+ desc->DESC2 = (size << 16) | desc2;
+ idx = (idx + 1) % TX_DESC_COUNT;
+ }
+
+ stmac_cache_flush(begin, size);
+ }
+
+ n = n->m_next;
+ } while (n != NULL);
+
+ if (bufs % 2 == 1) {
+ desc->DESC1 = 0;
+ desc->DESC2 = desc2;
+ idx = (idx + 1) % TX_DESC_COUNT;
+ }
+
+ new_head_idx = idx;
+ sc->tx_idx_head = new_head_idx;
+
+ idx = (idx - 1) % TX_DESC_COUNT;
+ desc = &desc_ring[idx];
+ desc->m = m;
+
+ desc3 = ETH_DMATXNDESCRF_OWN | ETH_DMATXNDESCRF_LD | m->m_pkthdr.len;
+ csum_flags = m->m_pkthdr.csum_flags;
+ if ((csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 |
+ CSUM_UDP_IPV6)) != 0) {
+ desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
+ } else if ((csum_flags & CSUM_IP) != 0) {
+ desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_INSERT;
+ }
+
+ while (idx != head_idx) {
+ desc->DESC3 = desc3;
+ desc3 &= ~ETH_DMATXNDESCRF_LD;
+
+ idx = (idx - 1) % TX_DESC_COUNT;
+ desc = &desc_ring[idx];
+ }
+
+ desc->DESC3 = ETH_DMATXNDESCRF_FD | desc3;
+ _ARM_Data_synchronization_barrier();
+ regs = sc->heth.Instance;
+ WRITE_REG(regs->DMACTDTPR, (uint32_t)&desc_ring[new_head_idx]);
+ return (0);
+}
+
+static int
+stmac_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct stmac_softc *sc;
+ int error;
+
+ sc = ifp->if_softc;
+ STMAC_TXLOCK(sc);
+
+ error = stmac_tx_enqueue(sc, ifp, m);
+ stmac_tx_reclaim(sc, ifp);
+
+ if (__predict_false(error != 0)) {
+ error = stmac_tx_enqueue(sc, ifp, m);
+ if (error != 0) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
+ }
+ }
+
+ STMAC_TXUNLOCK(sc);
+ return (error);
+}
+
+static int
+stmac_transmit_no_link(struct ifnet *ifp, struct mbuf *m)
+{
+
+ (void)ifp;
+ (void)m;
+ return (ENETDOWN);
+}
+
+static void
+stmac_qflush(struct ifnet *ifp)
+{
+
+ (void)ifp;
+}
+
+static int
+stmac_attach(device_t dev)
+{
+ struct stmac_softc *sc;
+ struct ifnet *ifp;
+ int error;
+ struct stmac_desc *descs;
+ ETH_TypeDef *regs;
+ rtems_status_code status;
+
+ sc = device_get_softc(dev);
+
+ sc->rx_do_alloc = true;
+ mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF);
+ mtx_init(&sc->tx_mtx, "stmac tx", MTX_NETWORK_LOCK, MTX_DEF);
+ callout_init_mtx(&sc->tick_callout, &sc->mtx, 0);
+
+ sc->ifp = ifp = if_alloc(IFT_ETHER);
+ ifp->if_softc = sc;
+
+ regs = ETH;
+ sc->heth.Instance = regs;
+
+ descs = rtems_cache_coherent_allocate(sizeof(*descs) *
+ (RX_DESC_COUNT + TX_DESC_COUNT), 4, 0);
+ BSD_ASSERT(descs != NULL);
+
+ sc->tx_desc_ring = &descs[0];
+ sc->rx_desc_ring = &descs[TX_DESC_COUNT];
+
+ rtems_bsd_get_mac_address(device_get_name(dev), device_get_unit(dev),
+ &sc->mac_addr[0]);
+
+ sc->heth.Init.MACAddr = &sc->mac_addr[0];
+ sc->heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
+ sc->heth.Init.RxBuffLen = 1524;
+
+ stmac_reset(sc);
+
+ if_initname(ifp, "stm", device_get_unit(dev));
+ ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST;
+ ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6;
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_hwassist = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
+ ifp->if_transmit = stmac_transmit_no_link;
+ ifp->if_qflush = stmac_qflush;
+ ifp->if_ioctl = stmac_ioctl;
+ ifp->if_init = stmac_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);
+
+ error = mii_attach(dev, &sc->miibus, ifp, stmac_media_change,
+ stmac_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
+ MII_OFFSET_ANY, 0);
+ if (error == 0) {
+ sc->mii_softc = device_get_softc(sc->miibus);
+ }
+
+ ether_ifattach(ifp, &sc->heth.Init.MACAddr[0]);
+
+ status = rtems_interrupt_server_handler_install(
+ RTEMS_INTERRUPT_SERVER_DEFAULT, ETH_IRQn, "stmac",
+ RTEMS_INTERRUPT_SHARED, stmac_interrupt, sc);
+ BSD_ASSERT(status == RTEMS_SUCCESSFUL);
+ return (0);
+}
+
+static int
+stmac_miibus_read(device_t dev, int phy, int reg)
+{
+ struct stmac_softc *sc;
+ uint32_t val;
+ HAL_StatusTypeDef hal_status;
+
+ sc = device_get_softc(dev);
+ hal_status = HAL_ETH_ReadPHYRegister(&sc->heth, (uint32_t)phy,
+ (uint32_t)reg, &val);
+ if (hal_status != HAL_OK) {
+ return (-1);
+ }
+
+ return ((int)val);
+}
+
+static int
+stmac_miibus_write(device_t dev, int phy, int reg, int val)
+{
+ struct stmac_softc *sc;
+ HAL_StatusTypeDef hal_status;
+
+ sc = device_get_softc(dev);
+ hal_status = HAL_ETH_WritePHYRegister(&sc->heth, (uint32_t)phy,
+ (uint32_t)reg, (uint32_t)val);
+ if (hal_status != HAL_OK) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+stmac_miibus_statchg(device_t dev)
+{
+ struct stmac_softc *sc;
+ struct mii_data *mii;
+ u_int active;
+ u_int status;
+ ETH_TypeDef *regs;
+ uint32_t maccr;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->miibus);
+ active = mii->mii_media_active;
+ status = mii->mii_media_status;
+
+ if (sc->mii_media_active == active && sc->mii_media_status == status) {
+ return;
+ }
+
+ sc->mii_media_active = active;
+ sc->mii_media_status = status;
+
+ regs = sc->heth.Instance;
+ maccr = regs->MACCR;
+
+ if ((status & IFM_ACTIVE) != 0) {
+ if_settransmitfn(sc->ifp, stmac_transmit);
+
+ if ((IFM_OPTIONS(active) & IFM_FDX) != 0) {
+ maccr |= ETH_MACCR_DM;
+ } else {
+ maccr &= ~ETH_MACCR_DM;
+ }
+
+ if (IFM_SUBTYPE(active) != IFM_10_T) {
+ maccr |= ETH_MACCR_FES;
+ } else {
+ maccr &= ~ETH_MACCR_FES;
+ }
+ } else {
+ maccr |= ETH_MACCR_DM | ETH_MACCR_FES;
+ }
+
+ regs->MACCR = maccr;
+}
+
+static device_method_t stmac_methods[] = {
+ DEVMETHOD(device_probe, stmac_probe),
+ DEVMETHOD(device_attach, stmac_attach),
+ DEVMETHOD(miibus_readreg, stmac_miibus_read),
+ DEVMETHOD(miibus_writereg, stmac_miibus_write),
+ DEVMETHOD(miibus_statchg, stmac_miibus_statchg),
+ DEVMETHOD_END
+};
+
+driver_t stmac_driver = {
+ "stmac",
+ stmac_methods,
+ sizeof(struct stmac_softc)
+};
+
+static devclass_t stmac_devclass;
+
+DRIVER_MODULE(stmac, nexus, stmac_driver, stmac_devclass, 0, 0);
+DRIVER_MODULE(miibus, stmac, miibus_driver, miibus_devclass, 0, 0);
+
+MODULE_DEPEND(stmac, ether, 1, 1, 1);
+MODULE_DEPEND(stmac, miibus, 1, 1, 1);
+
+#endif /* LIBBSP_ARM_STM32H7_BSP_H */
diff --git a/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c b/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c
new file mode 100644
index 00000000..243470ca
--- /dev/null
+++ b/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c
@@ -0,0 +1,166 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ * Copyright (c) 2012 Hans Petter Selasky. 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 <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+
+static int dwc_otg_detach(device_t);
+
+static int
+dwc_otg_probe(device_t dev)
+{
+
+ device_set_desc(dev, "DWC OTG 2.0 integrated USB controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dwc_otg_attach(device_t dev)
+{
+ struct dwc_otg_softc *sc;
+ int err;
+ int rid;
+
+ sc = device_get_softc(dev);
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = DWC_OTG_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+ sc->sc_mode = DWC_MODE_HOST;
+ sc->sc_phy_type = DWC_OTG_PHY_INTERNAL;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->sc_io_res == NULL) {
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (sc->sc_bus.bdev == NULL) {
+ goto error;
+ }
+
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ dwc_otg_platform_init(sc);
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE,
+ &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = dwc_otg_init(sc);
+ if (err == 0) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ goto error;
+ }
+
+
+ return (0);
+
+error:
+ dwc_otg_detach(dev);
+ return (ENXIO);
+}
+
+static int
+dwc_otg_detach(device_t dev)
+{
+
+ (void)dev;
+ BSD_ASSERT(0);
+ return (0);
+}
+
+static device_method_t dwc_otg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dwc_otg_probe),
+ DEVMETHOD(device_attach, dwc_otg_attach),
+ DEVMETHOD(device_detach, dwc_otg_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+driver_t dwc_otg_driver = {
+ .name = "dwcotg",
+ .methods = dwc_otg_methods,
+ .size = sizeof(struct dwc_otg_softc),
+};
+
+static devclass_t dwc_otg_devclass;
+
+DRIVER_MODULE(dwcotg, nexus, dwc_otg_driver, dwc_otg_devclass, 0, 0);
+MODULE_DEPEND(dwcotg, usb, 1, 1, 1);
diff --git a/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c b/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c
new file mode 100644
index 00000000..464c9ba4
--- /dev/null
+++ b/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 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_ARM_STM32H7_BSP_H
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+
+#include <stm32h7/hal.h>
+
+static const stm32h7_gpio_config dwc_otg_pins_dm_dp = {
+ .regs = GPIOA,
+ .config = {
+ .Pin = GPIO_PIN_11 | GPIO_PIN_12,
+ .Mode = GPIO_MODE_AF_PP,
+ .Pull = GPIO_NOPULL,
+ .Speed = GPIO_SPEED_FREQ_VERY_HIGH,
+ .Alternate = GPIO_AF10_OTG2_FS
+ }
+};
+
+static const stm32h7_gpio_config dwc_otg_pin_id = {
+ .regs = GPIOA,
+ .config = {
+ .Pin = GPIO_PIN_10,
+ .Mode = GPIO_MODE_AF_OD,
+ .Pull = GPIO_PULLUP,
+ .Alternate = GPIO_AF10_OTG2_FS
+ }
+};
+
+void
+dwc_otg_platform_init(struct dwc_otg_softc *sc)
+{
+
+ (void)sc;
+ stm32h7_gpio_init(&dwc_otg_pins_dm_dp);
+ stm32h7_gpio_init(&dwc_otg_pin_id);
+ stm32h7_clk_enable(STM32H7_MODULE_USB2_OTG);
+ stm32h7_clk_low_power_disable(STM32H7_MODULE_USB2_OTG_ULPI);
+ HAL_PWREx_EnableUSBVoltageDetector();
+}
+
+#endif /* LIBBSP_ARM_STM32H7_BSP_H */
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/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/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/default-network-init.h b/testsuite/include/rtems/bsd/test/default-network-init.h
index 3afa20fa..b367d956 100644
--- a/testsuite/include/rtems/bsd/test/default-network-init.h
+++ b/testsuite/include/rtems/bsd/test/default-network-init.h
@@ -255,7 +255,11 @@ Init(rtems_task_argument arg)
#define RTEMS_BSD_CONFIG_NET_PF_UNIX
#define RTEMS_BSD_CONFIG_NET_IP_MROUTE
+
+#ifdef RTEMS_BSD_MODULE_NETINET6
#define RTEMS_BSD_CONFIG_NET_IP6_MROUTE
+#endif
+
#define RTEMS_BSD_CONFIG_NET_IF_BRIDGE
#define RTEMS_BSD_CONFIG_NET_IF_LAGG
#define RTEMS_BSD_CONFIG_NET_IF_VLAN
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/mcast01/test_main.c b/testsuite/mcast01/test_main.c
new file mode 100644
index 00000000..ad6c45b7
--- /dev/null
+++ b/testsuite/mcast01/test_main.c
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @brief Example code to test multicasts.
+ *
+ * ping 224.0.0.1
+ *
+ * Linux:
+ *
+ * echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
+ * echo 1 > /proc/sys/net/ipv4/ip_forward
+ * ip route add 224.0.0.0/4 dev eth0
+ * echo "Hello, world!" > /dev/udp/239.1.2.3/1234
+ */
+
+/*
+ * Copyright (C) 2020 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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <machine/rtems-bsd-commands.h>
+
+#include <rtems/console.h>
+#include <rtems/shell.h>
+#include <rtems/dhcpcd.h>
+
+#define TEST_NAME "LIBBSD MCAST 1"
+#define TEST_STATE_USER_INPUT 1
+
+#define MCAST_PORT 1234
+
+#define MAX_PACKET_SIZE UINT16_MAX
+
+static const char mcast_addr[] = "239.1.2.3";
+
+static const char hello[] = "Hello, world!";
+
+typedef struct {
+ in_addr_t addr;
+ char name[];
+} iface_binding;
+
+static void
+set_mcast_route(char *iface_name)
+{
+ static char net[] = "224.0.0.0/4";
+ int exit_code;
+ char *route[] = {
+ "route",
+ "add",
+ "-net",
+ net,
+ "-iface",
+ iface_name,
+ NULL
+ };
+
+ exit_code = rtems_bsd_command_route(RTEMS_BSD_ARGC(route), route);
+ assert(exit_code == EXIT_SUCCESS);
+}
+
+static void
+mcast_rx_task(rtems_task_argument arg)
+{
+ iface_binding *ifb;
+ struct ip_mreq mreq;
+ int sd;
+ struct sockaddr_in addr;
+ int rv;
+ char addr_buf[INET_ADDRSTRLEN];
+ const char *ip;
+ ssize_t n;
+
+ ifb = (iface_binding *)arg;
+ set_mcast_route(ifb->name);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = ifb->addr;
+
+ ip = inet_ntop(AF_INET, &addr.sin_addr, addr_buf, sizeof(addr_buf));
+ printf("mcast: join %s using interface IP %s\n", mcast_addr, ip);
+
+ sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ assert(sd >= 0);
+
+ addr.sin_port = htons(MCAST_PORT);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ rv = bind(sd, (const struct sockaddr *)&addr, sizeof(addr));
+ assert(rv == 0);
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr.s_addr = inet_addr(mcast_addr);
+ mreq.imr_interface.s_addr = ifb->addr;
+
+ rv = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ assert(rv == 0);
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(MCAST_PORT);
+ addr.sin_addr.s_addr = inet_addr(mcast_addr);
+ n = sendto(sd, hello, sizeof(hello), 0,
+ (const struct sockaddr *)&addr, sizeof(addr));
+
+ if (n != (ssize_t)sizeof(hello)) {
+ perror("send");
+ }
+
+ while (true) {
+ socklen_t addr_len;
+ char buf[MAX_PACKET_SIZE];
+
+ addr_len = sizeof(addr);
+ n = recvfrom(sd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&addr, &addr_len);
+ if (n >= 0 && addr.sin_addr.s_addr != ifb->addr) {
+ size_t m;
+ ip = inet_ntop(AF_INET, &addr.sin_addr, addr_buf,
+ sizeof(addr_buf));
+ printf("mcast: received %zi bytes from %s\n", n, ip);
+
+ addr.sin_port = htons(MCAST_PORT);
+ addr.sin_addr.s_addr = inet_addr(mcast_addr);
+ m = (size_t)n;
+ n = sendto(sd, buf, m, 0,
+ (const struct sockaddr *)&addr,
+ sizeof(addr));
+ assert(n == (ssize_t)m);
+ } else if (n < 0 ) {
+ perror("recvfrom");
+ }
+ }
+}
+
+static void
+start_mcast_rx(const char *iface_ip, const char *iface_name)
+{
+ rtems_status_code sc;
+ rtems_id id;
+ iface_binding *ifb;
+ size_t len;
+
+ len = strlen(iface_name) + 1;
+ ifb = malloc(sizeof(*ifb) + len);
+ assert(ifb != NULL);
+ ifb->addr = inet_addr(iface_ip);
+ memcpy(ifb->name, iface_name, len);
+
+ sc = rtems_task_create(rtems_build_name('M', 'C', 'R', 'X'),
+ RTEMS_MINIMUM_PRIORITY, RTEMS_MINIMUM_STACK_SIZE + MAX_PACKET_SIZE,
+ RTEMS_DEFAULT_MODES, RTEMS_FLOATING_POINT, &id);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(id, mcast_rx_task, (rtems_task_argument)ifb);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static const char *
+get_value(char *const *env, const char *key)
+{
+ size_t len;
+
+ len = strlen(key);
+
+ while (true) {
+ const char *s;
+
+ s = *env;
+
+ if (s == NULL) {
+ return "";
+ }
+
+ if (strncmp(key, s, len) == 0 && s[len] == '=') {
+ return &s[len + 1];
+ }
+
+ ++env;
+ }
+}
+
+static void
+dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env)
+{
+
+ (void)hook;
+
+ if (strcmp(get_value(env, "reason"), "BOUND") == 0) {
+ start_mcast_rx(get_value(env, "new_ip_address"),
+ get_value(env, "interface"));
+ }
+}
+
+static rtems_dhcpcd_hook dhcpcd_hook = {
+ .name = "mcast",
+ .handler = dhcpcd_hook_handler
+};
+
+static void
+test_main(void)
+{
+ rtems_status_code sc;
+ int enable;
+ int rv;
+
+ rtems_dhcpcd_add_hook(&dhcpcd_hook);
+
+ enable = 1;
+ rv = sysctlbyname("net.inet.icmp.bmcastecho", NULL, NULL, &enable,
+ sizeof(enable));
+ assert(rv == 0);
+
+ sc = rtems_shell_init("SHLL", 16 * 1024, 1, CONSOLE_DEVICE_NAME,
+ false, true, NULL);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ exit(0);
+}
+
+#define DEFAULT_NETWORK_DHCPCD_ENABLE
+
+#include <rtems/bsd/test/default-network-init.h>
+
+#define CONFIGURE_SHELL_COMMANDS_INIT
+
+#include <bsp/irq-info.h>
+
+#include <rtems/netcmds-config.h>
+
+#define CONFIGURE_SHELL_USER_COMMANDS \
+ &bsp_interrupt_shell_command, \
+ &rtems_shell_ARP_Command, \
+ &rtems_shell_HOSTNAME_Command, \
+ &rtems_shell_PING_Command, \
+ &rtems_shell_ROUTE_Command, \
+ &rtems_shell_NETSTAT_Command, \
+ &rtems_shell_SYSCTL_Command, \
+ &rtems_shell_IFCONFIG_Command, \
+ &rtems_shell_IFMCSTAT_Command, \
+ &rtems_shell_VMSTAT_Command
+
+#define CONFIGURE_SHELL_COMMAND_CPUINFO
+#define CONFIGURE_SHELL_COMMAND_CPUUSE
+#define CONFIGURE_SHELL_COMMAND_PERIODUSE
+#define CONFIGURE_SHELL_COMMAND_STACKUSE
+#define CONFIGURE_SHELL_COMMAND_MALLOC_INFO
+
+#include <rtems/shellconfig.h>
diff --git a/testsuite/media01/pattern-test.c b/testsuite/media01/pattern-test.c
new file mode 100644
index 00000000..4b00cb73
--- /dev/null
+++ b/testsuite/media01/pattern-test.c
@@ -0,0 +1,337 @@
+/**
+ * @file
+ *
+ * @brief Command to fill a file with a pattern.
+ *
+ * The written pattern is a simple 32 bit counter which makes it easy to find
+ * problems where buffers are not written to the correct location or where some
+ * blocks are missing.
+ *
+ * This file can be either compiled as part of RTEMS or as a stand alone Linux
+ * or (most likely) FreeBSD application. To compile it as stand alone, just use
+ * "gcc pattern-test.c"
+ */
+
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2020 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.
+ */
+
+#ifdef __rtems__
+#include "pattern-test.h"
+#endif /* __rtems__ */
+
+#include <arpa/inet.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+
+static const char * const usage =
+ "Fill a file or a device file with a simple incrementing 32 bit pattern.\n"
+ "Use with:\n"
+ " pattern [fill|check] <file> <size> <block_size> [<start_value> [<output>]]\n"
+ "Arguments:\n"
+ " All numbers can be decimal (e.g. 256) or hex (e.g. 0x100)\n"
+ " <size> and <block_size> is in bytes\n"
+ " <start_value> start with this counter value\n"
+ " <output>: Only relevant for check. Can be: \n"
+ " <nr>: print the first <nr> errors\n"
+ " \"short\": Print all errors but short form\n";
+
+static int
+check_and_process_params(
+ int argc,
+ char *argv[],
+ int open_flags,
+ int *fd,
+ size_t *size,
+ size_t *block_size,
+ uint8_t **block,
+ uint8_t **read_block,
+ int *max_errors,
+ bool *short_output,
+ uint32_t *start_value
+)
+{
+ if (argc < 4) {
+ printf(usage);
+ return -1;
+ }
+
+ if (argc > 4) {
+ *start_value = strtoul(argv[4], NULL, 0);
+ } else {
+ *start_value = 0;
+ }
+
+ if (short_output != NULL && max_errors != NULL) {
+ *short_output = false;
+ *max_errors = 1;
+ if (argc > 5) {
+ if (strcmp("short", argv[5]) == 0) {
+ *max_errors = -1;
+ *short_output = true;
+ } else {
+ *max_errors = strtol(argv[5], NULL, 0);
+ if (*max_errors <= 0) {
+ warn("Can't use <output> parameter.");
+ return -1;
+ }
+ }
+ }
+ }
+
+ *size = strtoul(argv[2], NULL, 0);
+ if (size == 0) {
+ warn("Couldn't convert size or size set to 0");
+ return -1;
+ }
+
+ *block_size = strtoul(argv[3], NULL, 0);
+ if (block_size == 0) {
+ warn("Couldn't convert block_size or block_size set to 0");
+ return -1;
+ }
+
+ if (block != NULL) {
+ *block = malloc(*block_size);
+ if (*block == NULL) {
+ warn("Couldn't allocate block");
+ return -1;
+ }
+ }
+
+ if (read_block != NULL) {
+ *read_block = malloc(*block_size);
+ if (*read_block == NULL) {
+ warn("Couldn't allocate read_block");
+ if (block != NULL) {
+ free(*block);
+ }
+ return -1;
+ }
+ }
+
+ *fd = open(argv[1], open_flags, 0666);
+ if (*fd < 0) {
+ warn("Couldn't open file");
+ if (block != NULL) {
+ free(*block);
+ }
+ if (read_block != NULL) {
+ free(*read_block);
+ }
+ return -1;
+ }
+
+ printf("File: %s\nSize: 0x%x\nBlock size: 0x%x\n",
+ argv[1], *size, *block_size);
+
+ return 0;
+}
+
+/* Write addresses to the block. Keep word boundaries intact. */
+static void
+fill_block(uint8_t *block, size_t size, uint32_t start)
+{
+ uint32_t pattern_size = sizeof(start);
+ uint32_t start_offset = start % pattern_size;
+ uint32_t value_h = start & ~(pattern_size - 1);
+ uint32_t value_n = htonl(value_h);
+ uint8_t *val = (uint8_t *) &value_n;
+
+ if (start_offset != 0) {
+ size_t to_write = MIN(pattern_size - start_offset, size);
+ memcpy(block, val + start_offset, to_write);
+ block += to_write;
+ size -= to_write;
+ value_h += pattern_size;
+ value_n = htonl(value_h);
+ }
+
+ while (size >= pattern_size) {
+ memcpy(block, val, pattern_size);
+ value_h += pattern_size;
+ value_n = htonl(value_h);
+ block += pattern_size;
+ size -= pattern_size;
+ }
+
+ if (size > 0) {
+ memcpy(block, val, size);
+ }
+}
+
+static int
+command_pattern_fill(int argc, char *argv[])
+{
+ int fd;
+ size_t size;
+ size_t block_size;
+ uint8_t *block;
+ uint32_t start_value;
+ int rv;
+
+ rv = check_and_process_params(argc, argv, O_WRONLY | O_CREAT,
+ &fd, &size, &block_size, &block, NULL, NULL, NULL, &start_value);
+ if (rv != 0) {
+ warnx("Error while processing parameters.\n");
+ return rv;
+ }
+
+ for (size_t current = start_value;
+ current < start_value + size;
+ current += block_size) {
+ size_t write_size = MIN(block_size, size-current);
+ ssize_t written;
+ fill_block(block, write_size, current);
+ written = write(fd, block, write_size);
+ if (written != (ssize_t)write_size) {
+ warn("Writing failed on block at 0x%x", current);
+ break;
+ }
+ }
+
+ free(block);
+ close(fd);
+
+ return 0;
+}
+
+static void
+print_block(uint8_t *block, size_t size)
+{
+ for (size_t i = 0; i < size; ++i) {
+ if (i > 0 && i % 0x10 == 0) {
+ printf("\n");
+ }
+ printf("%02x ", block[i]);
+ }
+ printf("\n");
+}
+
+static int
+command_pattern_check(int argc, char *argv[])
+{
+ int fd;
+ size_t size;
+ size_t block_size;
+ uint8_t *block;
+ uint8_t *read_block;
+ int rv;
+ int errors = 0;
+ int max_errors;
+ bool short_output;
+ bool last_was_error = false;
+ uint32_t start_value;
+
+ rv = check_and_process_params(argc, argv, O_RDONLY,
+ &fd, &size, &block_size, &block, &read_block,
+ &max_errors, &short_output, &start_value);
+ if (rv != 0) {
+ warnx("Error while processing parameters.\n");
+ return rv;
+ }
+
+ for (size_t current = start_value;
+ current < size + start_value;
+ current += block_size) {
+ size_t read_size = MIN(block_size, size-current);
+ ssize_t received;
+ fill_block(block, read_size, current);
+ received = read(fd, read_block, read_size);
+ if (received != (ssize_t)read_size) {
+ warn("Reading failed on block at 0x%x", current);
+ break;
+ }
+ rv = memcmp(block, read_block, read_size);
+ if (short_output) {
+ bool is_error = (rv != 0);
+ if (last_was_error != is_error) {
+ warnx("%s: 0x%x",
+ is_error ? "ERR" : "OK ",
+ current);
+ }
+ last_was_error = is_error;
+ } else if (rv != 0) {
+ warnx("Pattern wrong in block at 0x%x", current);
+ warnx("Expected:");
+ print_block(block, read_size);
+ warnx("Got:");
+ print_block(read_block, read_size);
+ ++errors;
+ if (errors >= max_errors) {
+ warnx("Too many errors. Refusing to continue.");
+ break;
+ }
+ }
+ }
+
+ free(read_block);
+ free(block);
+ close(fd);
+
+ return 0;
+}
+
+static int
+command_pattern(int argc, char *argv[])
+{
+ if (argc < 2) {
+ printf(usage);
+ return -1;
+ } else if (strcmp(argv[1], "fill") == 0) {
+ return command_pattern_fill(argc-1, &argv[1]);
+ } else if (strcmp(argv[1], "check") == 0) {
+ return command_pattern_check(argc-1, &argv[1]);
+ } else {
+ printf(usage);
+ return -1;
+ }
+}
+
+#ifdef __rtems__
+rtems_shell_cmd_t shell_PATTERN_Command = {
+ .name = "pattern",
+ .usage = usage,
+ .topic = "files",
+ .command = command_pattern,
+};
+#else /* __rtems__ */
+
+int
+main(int argc, char *argv[])
+{
+ return command_pattern(argc, argv);
+}
+#endif /* __rtems__ */
diff --git a/testsuite/media01/pattern-test.h b/testsuite/media01/pattern-test.h
new file mode 100644
index 00000000..5d89e16c
--- /dev/null
+++ b/testsuite/media01/pattern-test.h
@@ -0,0 +1,44 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef TESTSUITE_PATTERN_H
+#define TESTSUITE_PATTERN_H
+
+#include <rtems.h>
+#include <rtems/shell.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern rtems_shell_cmd_t shell_PATTERN_Command;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* TESTSUITE_PATTERN_H */
diff --git a/testsuite/media01/test_main.c b/testsuite/media01/test_main.c
index 91e0dd5a..001c80a8 100644
--- a/testsuite/media01/test_main.c
+++ b/testsuite/media01/test_main.c
@@ -53,6 +53,8 @@
#include <rtems/shell.h>
#include <rtems/telnetd.h>
+#include "pattern-test.h"
+
#define TEST_NAME "LIBBSD MEDIA 1"
#define TEST_STATE_USER_INPUT 1
@@ -123,7 +125,7 @@ telnet_shell(char *name, void *arg)
{
rtems_shell_env_t env;
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
env.devname = name;
env.taskname = "TLNT";
@@ -199,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
@@ -240,7 +244,9 @@ early_initialization(void)
&rtems_shell_NETSTAT_Command, \
&rtems_shell_SYSCTL_Command, \
&rtems_shell_IFCONFIG_Command, \
- &rtems_shell_VMSTAT_Command
+ &rtems_shell_IFMCSTAT_Command, \
+ &rtems_shell_VMSTAT_Command, \
+ &shell_PATTERN_Command
#define CONFIGURE_SHELL_COMMAND_CPUINFO
#define CONFIGURE_SHELL_COMMAND_CPUUSE
@@ -261,6 +267,7 @@ early_initialization(void)
#define CONFIGURE_SHELL_COMMAND_MV
#define CONFIGURE_SHELL_COMMAND_RM
#define CONFIGURE_SHELL_COMMAND_MALLOC_INFO
+#define CONFIGURE_SHELL_COMMAND_MD5
#define CONFIGURE_SHELL_COMMAND_FDISK
#define CONFIGURE_SHELL_COMMAND_BLKSTATS
diff --git a/testsuite/netshell01/test_main.c b/testsuite/netshell01/test_main.c
index 81941d0b..b967cb87 100644
--- a/testsuite/netshell01/test_main.c
+++ b/testsuite/netshell01/test_main.c
@@ -162,7 +162,7 @@ test_main(void)
{
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/nfs01/test_main.c b/testsuite/nfs01/test_main.c
index 2312040a..21d744d1 100644
--- a/testsuite/nfs01/test_main.c
+++ b/testsuite/nfs01/test_main.c
@@ -63,8 +63,7 @@ test_main(void)
NULL);
} while (rv != 0);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_SHELL
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 92073165..dfb9847b 100644
--- a/testsuite/pf02/test_main.c
+++ b/testsuite/pf02/test_main.c
@@ -132,7 +132,7 @@ telnet_shell(char *name, void *arg)
{
rtems_shell_env_t env;
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
env.devname = name;
env.taskname = "TLNT";
@@ -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 078e96e2..b6e9d4d7 100644
--- a/testsuite/ppp01/test_main.c
+++ b/testsuite/ppp01/test_main.c
@@ -211,7 +211,7 @@ telnet_shell(char *name, void *arg)
{
rtems_shell_env_t env;
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
env.devname = name;
env.taskname = "TLNT";
@@ -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 00916843..d7e50a97 100644
--- a/testsuite/telnetd01/test_main.c
+++ b/testsuite/telnetd01/test_main.c
@@ -50,7 +50,7 @@ telnet_shell(char *name, void *arg)
{
rtems_shell_env_t env;
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
env.devname = name;
env.taskname = "TLNT";
@@ -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 9d5e5bba..48b21452 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/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/zerocopy01/test_main.c b/testsuite/zerocopy01/test_main.c
index 3e7b7b7b..3a03c12c 100644
--- a/testsuite/zerocopy01/test_main.c
+++ b/testsuite/zerocopy01/test_main.c
@@ -190,7 +190,7 @@ telnet_shell(char *name, void *arg)
{
rtems_shell_env_t env;
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
env.devname = name;
env.taskname = "TLNT";
@@ -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 9ac5bf35..f1088584 100644
--- a/waf_libbsd.py
+++ b/waf_libbsd.py
@@ -1,7 +1,10 @@
+# SPDX-License-Identifier: BSD-2-Clause
+"""LibBSD build configuration to waf integration module.
+"""
+
+# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. All rights reserved.
#
-# Copyright (c) 2015-2018 Chris Johns <chrisj@rtems.org>. All rights reserved.
-#
-# Copyright (c) 2009-2015 embedded brains GmbH. All rights reserved.
+# Copyright (c) 2009, 2015 embedded brains GmbH. All rights reserved.
#
# embedded brains GmbH
# Dornierstr. 4
@@ -9,35 +12,30 @@
# Germany
# <info@embedded-brains.de>
#
-# Copyright (c) 2012 OAR Corporation. All rights reserved.
+# Copyright (c) 2012 OAR Corporation. 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.
+# 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 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.
from __future__ import print_function
-# Python 3 does no longer know the basestring class. Catch that.
-try:
- basestring
-except NameError:
- basestring = (str, bytes)
import os
import sys
@@ -48,6 +46,10 @@ import builder
import rtems_waf.rtems as rtems
+
+BUILDSET_DIR = builder.BUILDSET_DIR
+BUILDSET_DEFAULT = builder.BUILDSET_DEFAULT
+
windows = os.name == 'nt'
if windows:
@@ -55,12 +57,16 @@ if windows:
else:
host_shell = ''
+def _add_flags_if_not_present(current_flags, addional_flags):
+ for flag in addional_flags:
+ if flag not in current_flags:
+ current_flags.append(flag)
+
#
# The waf builder for libbsd.
#
class Builder(builder.ModuleManager):
-
- def __init__(self, trace = False):
+ def __init__(self, trace=False):
super(Builder, self).__init__()
self.trace = trace
self.data = {}
@@ -82,12 +88,11 @@ class Builder(builder.ModuleManager):
return sources
def generate(self, rtems_version):
-
- def _dataInsert(data, cpu, frag):
+ def _dataInsert(data, cpu, space, frag):
#
- # The default handler returns an empty string. Skip it.
+ # The default handler returns None. Skip it.
#
- if type(frag) is not str:
+ if frag is not None:
# Start at the top of the tree
d = data
path = frag[0]
@@ -96,6 +101,10 @@ class Builder(builder.ModuleManager):
# Select the sub-part of the tree as the compile options
# specialise how files are built.
d = d[path[0]]
+ # Group based on the space, ie kernel or user
+ if space not in d:
+ d[space] = {}
+ d = d[space]
if type(path[1]) is list:
p = ' '.join(path[1])
else:
@@ -104,14 +113,14 @@ class Builder(builder.ModuleManager):
d[p] = {}
d = d[p]
if cpu not in d:
- d[cpu] = { }
+ d[cpu] = {}
config = frag[0][2][0]
if config != 'default':
if 'configure' not in data:
- data['configure'] = { }
+ data['configure'] = {}
configTest = frag[1]['configTest']
if configTest not in data['configure']:
- data['configure'][configTest] = { }
+ data['configure'][configTest] = {}
data['configure'][configTest][config] = frag[0][2][1]
if type(frag[1]) is list:
if config not in d[cpu]:
@@ -137,15 +146,24 @@ class Builder(builder.ModuleManager):
self.data = {}
- for mn in self.getEnabledModules():
+ enabled_modules = self.getEnabledModules()
+ for mn in enabled_modules:
m = self[mn]
- if m.conditionalOn == "none":
+ enabled = True
+ for dep in m.dependencies:
+ if dep not in enabled_modules:
+ enabled = False
+ break
+ if enabled:
for f in m.files:
- _dataInsert(self.data, 'all', f.getFragment())
- for cpu, files in sorted(m.cpuDependentSourceFiles.items()):
- for f in files:
- _dataInsert(self.data, cpu, f.getFragment())
-
+ _dataInsert(self.data, 'all', f.getSpace(),
+ f.getFragment())
+ for cpu, files in sorted(m.cpuDependentSourceFiles.items()):
+ for f in files:
+ _dataInsert(self.data, cpu, f.getSpace(),
+ f.getFragment())
+
+ # Start here if you need to understand self.data. Add 'True or'
if self.trace:
import pprint
pprint.pprint(self.data)
@@ -156,19 +174,23 @@ class Builder(builder.ModuleManager):
for cfg in self.data['configure'][configTest]:
if configTest == 'header':
for h in self.data['configure'][configTest][cfg]:
- conf.check(header_name = h,
- features = "c",
- includes = conf.env.IFLAGS,
- mandatory = False)
+ conf.check(header_name=h,
+ features="c",
+ includes=conf.env.IFLAGS,
+ mandatory=False)
elif configTest == 'library':
for l in self.data['configure'][configTest][cfg]:
- conf.check_cc(lib = l,
- fragment = rtems.test_application(),
- execute = False,
- mandatory = False)
+ if conf.check_cc(lib=l,
+ fragment=rtems.test_application(),
+ execute=False,
+ mandatory=False):
+ 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"])
def build(self, bld):
#
@@ -204,23 +226,45 @@ class Builder(builder.ModuleManager):
defines += ['%s=1' % (o.strip().upper())]
#
- # Include paths
- #
- includes = []
- buildinclude = 'build-include'
- if 'cpu-include-paths' in config:
+ # Include paths, maintain paths for each build space.
+ #
+ include_paths = config['include-paths']
+ if 'build' not in include_paths:
+ bld.fatal('no build include path found in include-path defaults')
+ buildinclude = include_paths['build']
+ if isinstance(buildinclude, list):
+ buildinclude = buildinclude[0]
+ inc_paths = sorted(include_paths)
+ inc_paths.remove('build')
+ inc_paths.remove('cpu')
+ includes = {}
+ for inc in inc_paths:
+ includes[inc] = include_paths[inc]
+ # cpu include paths must be the first searched
+ if 'cpu' in include_paths:
cpu = bld.get_env()['RTEMS_ARCH']
- if cpu == "i386":
- includes += ['freebsd/sys/x86/include']
- for i in config['cpu-include-paths']:
- includes += [i.replace('@CPU@', cpu)]
- if 'include-paths' in config:
- includes += config['include-paths']
- if 'build-include-path' in config:
- buildinclude = config['build-include-path']
- if not isinstance(buildinclude, basestring):
- buildinclude = buildinclude[0]
- includes += [buildinclude]
+ for i in include_paths['cpu']:
+ includes['kernel'].insert(0, i.replace('@CPU@', cpu))
+ includes['kernel'] += [buildinclude]
+
+ #
+ # Path mappings
+ #
+ if 'path-mappings' in config:
+ for source, target in config['path-mappings']:
+ for space in includes:
+ incs = includes[space]
+ if source in incs:
+ target = [target] if isinstance(target,
+ str) else target
+ i = incs.index(source)
+ incs.remove(source)
+ incs[i:i] = target
+
+ #
+ # Place the kernel include paths after the user paths
+ #
+ includes['user'] += includes['kernel']
#
# Collect the libbsd uses
@@ -231,16 +275,17 @@ class Builder(builder.ModuleManager):
# Network test configuration
#
if not os.path.exists(bld.env.NET_CONFIG):
- bld.fatal('network configuraiton \'%s\' not found' % (bld.env.NET_CONFIG))
- tags = [ 'NET_CFG_INTERFACE_0',
- 'NET_CFG_SELF_IP',
- 'NET_CFG_NETMASK',
- 'NET_CFG_PEER_IP',
- 'NET_CFG_GATEWAY_IP' ]
+ bld.fatal('network configuraiton \'%s\' not found' %
+ (bld.env.NET_CONFIG))
+ tags = [
+ 'NET_CFG_INTERFACE_0', 'NET_CFG_SELF_IP', 'NET_CFG_NETMASK',
+ 'NET_CFG_PEER_IP', 'NET_CFG_GATEWAY_IP'
+ ]
try:
net_cfg_lines = open(bld.env.NET_CONFIG).readlines()
except:
- bld.fatal('network configuraiton \'%s\' read failed' % (bld.env.NET_CONFIG))
+ bld.fatal('network configuraiton \'%s\' read failed' %
+ (bld.env.NET_CONFIG))
lc = 0
sed = 'sed '
for l in net_cfg_lines:
@@ -255,10 +300,10 @@ class Builder(builder.ModuleManager):
for t in tags:
if lhs == t:
sed += "-e 's/@%s@/%s/' " % (t, rhs)
- bld(target = "testsuite/include/rtems/bsd/test/network-config.h",
- source = "testsuite/include/rtems/bsd/test/network-config.h.in",
- rule = sed + " < ${SRC} > ${TGT}",
- update_outputs = True)
+ bld(target="testsuite/include/rtems/bsd/test/network-config.h",
+ source="testsuite/include/rtems/bsd/test/network-config.h.in",
+ rule=sed + " < ${SRC} > ${TGT}",
+ update_outputs=True)
#
# Add a copy rule for all headers where the install path and the source
@@ -266,7 +311,8 @@ class Builder(builder.ModuleManager):
#
if 'header-paths' in config:
header_build_copy_paths = [
- hp for hp in config['header-paths'] if hp[2] != '' and not hp[0].endswith(hp[2])
+ hp for hp in config['header-paths']
+ if hp[2] != '' and not hp[0].endswith(hp[2])
]
for headers in header_build_copy_paths:
target = os.path.join(buildinclude, headers[2])
@@ -274,10 +320,10 @@ class Builder(builder.ModuleManager):
for header in start_dir.ant_glob(headers[1]):
relsourcepath = header.path_from(start_dir)
targetheader = os.path.join(target, relsourcepath)
- bld(features = 'subst',
- target = targetheader,
- source = header,
- is_copy = True)
+ bld(features='subst',
+ target=targetheader,
+ source=header,
+ is_copy=True)
#
# Generate a header that contains information about enabled modules
@@ -296,12 +342,13 @@ class Builder(builder.ModuleManager):
output += '#define RTEMS_BSD_MODULE_{} 1\n'.format(modname)
output += '#endif /* RTEMS_BSD_MODULES_H */\n'
self.outputs[0].write(output)
+
modules_h_file_with_path = os.path.join(buildinclude,
module_header_path,
module_header_name)
- bld(rule = rtems_libbsd_modules_h_gen,
- target = modules_h_file_with_path,
- before = ['c', 'cxx'])
+ bld(rule=rtems_libbsd_modules_h_gen,
+ target=modules_h_file_with_path,
+ before=['c', 'cxx'])
#
# Add the specific rule based builders
@@ -311,20 +358,20 @@ class Builder(builder.ModuleManager):
# KVM Symbols
#
if 'KVMSymbols' in self.data:
- kvmsymbols = self.data['KVMSymbols']
+ kvmsymbols = self.data['KVMSymbols']['kernel']
if 'includes' in kvmsymbols['files']:
kvmsymbols_includes = kvmsymbols['files']['includes']
else:
kvmsymbols_includes = []
- bld(target = kvmsymbols['files']['all']['default'][0],
- source = 'rtemsbsd/rtems/generate_kvm_symbols',
- rule = host_shell + './${SRC} > ${TGT}',
- update_outputs = True)
- bld.objects(target = 'kvmsymbols',
- features = 'c',
- cflags = cflags,
- includes = kvmsymbols_includes + includes,
- source = kvmsymbols['files']['all']['default'][0])
+ bld(target=kvmsymbols['files']['all']['default'][0],
+ source='rtemsbsd/rtems/generate_kvm_symbols',
+ rule=host_shell + './${SRC} > ${TGT}',
+ update_outputs=True)
+ bld.objects(target='kvmsymbols',
+ features='c',
+ cflags=cflags,
+ includes=kvmsymbols_includes + includes['kernel'],
+ source=kvmsymbols['files']['all']['default'][0])
libbsd_use += ["kvmsymbols"]
bld.add_group()
@@ -334,32 +381,30 @@ class Builder(builder.ModuleManager):
#
if 'RPCGen' in self.data:
if bld.env.AUTO_REGEN:
- rpcgen = self.data['RPCGen']
+ rpcgen = self.data['RPCGen']['user']
rpcname = rpcgen['files']['all']['default'][0][:-2]
- bld(target = rpcname + '.h',
- source = rpcname + '.x',
- rule = host_shell + '${RPCGEN} -h -o ${TGT} ${SRC}')
+ bld(target=rpcname + '.h',
+ source=rpcname + '.x',
+ rule=host_shell + '${RPCGEN} -h -o ${TGT} ${SRC}')
#
# Route keywords
#
if 'RouteKeywords' in self.data:
if bld.env.AUTO_REGEN:
- routekw = self.data['RouteKeywords']
+ routekw = self.data['RouteKeywords']['user']
rkwname = routekw['files']['all']['default'][0]
rkw_rule = host_shell + "cat ${SRC} | " + \
"awk 'BEGIN { r = 0 } { if (NF == 1) " + \
"printf \"#define\\tK_%%s\\t%%d\\n\\t{\\\"%%s\\\", K_%%s},\\n\", " + \
"toupper($1), ++r, $1, toupper($1)}' > ${TGT}"
- bld(target = rkwname + '.h',
- source = rkwname,
- rule = rkw_rule)
+ bld(target=rkwname + '.h', source=rkwname, rule=rkw_rule)
#
# Lex
#
if 'lex' in self.data:
- lexes = self.data['lex']
+ lexes = self.data['lex']['user']
for l in sorted(lexes.keys()):
lex = lexes[l]['all']['default']
if 'cflags' in lex:
@@ -373,23 +418,23 @@ class Builder(builder.ModuleManager):
lex_rule = host_shell + '${LEX} -P ' + lex['sym'] + ' -t ${SRC} | ' + \
'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' > ${TGT}'
if bld.env.AUTO_REGEN:
- bld(target = lex['file'][:-2]+ '.c',
- source = lex['file'],
- rule = lex_rule)
+ bld(target=lex['file'][:-2] + '.c',
+ source=lex['file'],
+ rule=lex_rule)
if lex['build']:
- bld.objects(target = 'lex_%s' % (lex['sym']),
- features = 'c',
- cflags = cflags,
- includes = lexIncludes + includes,
- defines = defines + lexDefines,
- source = lex['file'][:-2] + '.c')
+ bld.objects(target='lex_%s' % (lex['sym']),
+ features='c',
+ cflags=cflags,
+ includes=lexIncludes + includes['user'],
+ defines=defines + lexDefines,
+ source=lex['file'][:-2] + '.c')
libbsd_use += ['lex_%s' % (lex['sym'])]
#
# Yacc
#
if 'yacc' in self.data:
- yaccs = self.data['yacc']
+ yaccs = self.data['yacc']['user']
for y in sorted(yaccs.keys()):
yacc = yaccs[y]['all']['default']
yaccFile = yacc['file']
@@ -397,7 +442,8 @@ class Builder(builder.ModuleManager):
yaccSym = yacc['sym']
else:
yaccSym = os.path.basename(yaccFile)[:-2]
- yaccHeader = '%s/%s' % (os.path.dirname(yaccFile), yacc['header'])
+ yaccHeader = '%s/%s' % (os.path.dirname(yaccFile),
+ yacc['header'])
if 'cflags' in yacc:
yaccDefines = [d[2:] for d in yacc['cflags']]
else:
@@ -408,19 +454,20 @@ class Builder(builder.ModuleManager):
yaccIncludes = []
yacc_rule = host_shell + '${YACC} -b ' + yaccSym + \
' -d -p ' + yaccSym + ' ${SRC} && ' + \
- 'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < ' + yaccSym + '.tab.c > ${TGT} && ' + \
+ 'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < ' + \
+ yaccSym + '.tab.c > ${TGT} && ' + \
'rm -f ' + yaccSym + '.tab.c && mv ' + yaccSym + '.tab.h ' + yaccHeader
if bld.env.AUTO_REGEN:
- bld(target = yaccFile[:-2] + '.c',
- source = yaccFile,
- rule = yacc_rule)
+ bld(target=yaccFile[:-2] + '.c',
+ source=yaccFile,
+ rule=yacc_rule)
if yacc['build']:
- bld.objects(target = 'yacc_%s' % (yaccSym),
- features = 'c',
- cflags = cflags,
- includes = yaccIncludes + includes,
- defines = defines + yaccDefines,
- source = yaccFile[:-2] + '.c')
+ bld.objects(target='yacc_%s' % (yaccSym),
+ features='c',
+ cflags=cflags,
+ includes=yaccIncludes + includes['user'],
+ defines=defines + yaccDefines,
+ source=yaccFile[:-2] + '.c')
libbsd_use += ['yacc_%s' % (yaccSym)]
#
@@ -428,56 +475,54 @@ class Builder(builder.ModuleManager):
# specific files for those flags.
#
objs = 0
- sources = sorted(self.data['sources'])
- if 'default' in sources:
- sources.remove('default')
- for flags in sources:
- objs += 1
- build = self.data['sources'][flags]
- target = 'objs%02d' % (objs)
- bld_sources = Builder._sourceList(bld, build['all'])
- archs = sorted(build)
- for i in ['all', 'cflags', 'includes']:
- if i in archs:
- archs.remove(i)
- for arch in archs:
- if bld.get_env()['RTEMS_ARCH'] == arch:
- bld_sources += Builder._sourceList(bld, build[arch])
- if 'cflags' in build:
- bld_defines = [d[2:] for d in build['cflags']]
- else:
- bld_defines = []
- if 'includes' in build:
- bld_includes = build['includes']
- else:
- bld_includes = []
- bld.objects(target = target,
- features = 'c',
- cflags = cflags,
- includes = sorted(bld_includes) + includes,
- defines = defines + sorted(bld_defines),
- source = bld_sources)
- libbsd_use += [target]
-
- #
- # We hold the 'default' cflags set of files to the end to create the
- # static library with.
- #
- build = self.data['sources']['default']
+ for space in sorted(self.data['sources']):
+ sources = sorted(self.data['sources'][space])
+ if space == 'kernel' and 'default' in sources:
+ sources.remove('default')
+ for flags in sources:
+ objs += 1
+ build = self.data['sources'][space][flags]
+ target = 'objs%02d' % (objs)
+ bld_sources = Builder._sourceList(bld, build['all'])
+ archs = sorted(build)
+ for i in ['all', 'cflags', 'includes']:
+ if i in archs:
+ archs.remove(i)
+ for arch in archs:
+ if bld.get_env()['RTEMS_ARCH'] == arch:
+ bld_sources += Builder._sourceList(bld, build[arch])
+ bld_cflags = sorted(build.get('cflags', []))
+ if 'default' in bld_cflags:
+ bld_cflags.remove('default')
+ bld.objects(target=target,
+ features='c cxx',
+ cflags=cflags + bld_cflags,
+ cxxflags=cxxflags,
+ includes=sorted(build.get('includes', [])) +
+ includes[space],
+ defines=defines,
+ source=bld_sources)
+ libbsd_use += [target]
+
+ #
+ # We hold the kernel 'default' cflags set of files to the end to
+ # create the static library with.
+ #
+ build = self.data['sources']['kernel']['default']
bld_sources = Builder._sourceList(bld, build['all'])
archs = sorted(build)
archs.remove('all')
for arch in archs:
if bld.get_env()['RTEMS_ARCH'] == arch:
bld_sources += Builder._sourceList(bld, build[arch])
- bld.stlib(target = 'bsd',
- features = 'c cxx',
- cflags = cflags,
- cxxflags = cxxflags,
- includes = includes,
- defines = defines,
- source = bld_sources,
- use = libbsd_use)
+ bld.stlib(target='bsd',
+ features='c cxx',
+ cflags=cflags,
+ cxxflags=cxxflags,
+ includes=includes['kernel'],
+ defines=defines,
+ source=bld_sources,
+ use=libbsd_use)
#
# Installs.
@@ -494,36 +539,48 @@ class Builder(builder.ModuleManager):
if 'header-paths' in config:
headerPaths = config['header-paths']
cpu = bld.get_env()['RTEMS_ARCH']
- if cpu == "i386":
- cpu = 'x86'
for headers in headerPaths:
- # Get the dest path
- ipath = os.path.join(arch_inc_path, headers[2])
- start_dir = bld.path.find_dir(headers[0].replace('@CPU@', cpu))
- if start_dir != None:
- bld.install_files("${PREFIX}/" + ipath,
- start_dir.ant_glob(headers[1]),
- cwd = start_dir,
- relative_trick = True)
+ paths = [headers[0].replace('@CPU@', cpu)]
+ # Apply the path mappings
+ for source, targets in config['path-mappings']:
+ if source in paths:
+ i = paths.index(source)
+ paths.remove(source)
+ paths[i:i] = targets
+
+ for hp in paths:
+ # Get the dest path
+ ipath = os.path.join(arch_inc_path, headers[2])
+ start_dir = bld.path.find_dir(hp)
+ if start_dir != None:
+ bld.install_files("${PREFIX}/" + ipath,
+ start_dir.ant_glob(headers[1]),
+ cwd=start_dir,
+ relative_trick=True)
bld.install_files(os.path.join("${PREFIX}", arch_inc_path,
module_header_path),
modules_h_file_with_path,
- cwd = bld.path)
+ cwd=bld.path)
#
# Tests
#
tests = []
if 'tests' in self.data:
- tests = self.data['tests']
+ tests = self.data['tests']['user']
+ enabled_modules = self.getEnabledModules()
for testName in sorted(tests):
- test = self.data['tests'][testName]['all']
+ test = tests[testName]['all']
test_source = []
libs = ['bsd', 'm', 'z', 'rtemstest']
for cfg in test:
build_test = True
- if cfg != 'default':
+ for mod in test[cfg]['modules']:
+ if mod not in enabled_modules:
+ build_test = False
+ break
+ if build_test and cfg != 'default':
for c in cfg.split(' '):
if not bld.env['HAVE_%s' % (c)]:
build_test = False
@@ -533,11 +590,11 @@ class Builder(builder.ModuleManager):
for f in test[cfg]['files']]
libs = test[cfg]['libs'] + libs
if build_test:
- bld.program(target = '%s.exe' % (testName),
- features = 'cprogram',
- cflags = cflags,
- includes = includes,
- source = test_sources,
- use = ['bsd'],
- lib = libs,
- install_path = None)
+ bld.program(target='%s.exe' % (testName),
+ features='cprogram',
+ cflags=cflags,
+ includes=includes['user'],
+ source=test_sources,
+ use=['bsd'],
+ lib=libs,
+ install_path=None)
diff --git a/wscript b/wscript
index 03151bb2..574f0168 100644
--- a/wscript
+++ b/wscript
@@ -1,28 +1,32 @@
-#
-# RTEMS Project (https://www.rtems.org/)
-#
+# SPDX-License-Identifier: BSD-2-Clause
+"""RTEMS LibBSD is a transparent source build of the FreeBSD kernel
+source for RTEMS.
+
+To use see README.waf shipped with this file.
+"""
+
# Copyright (c) 2015-2016 Chris Johns <chrisj@rtems.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.
+# 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 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.
#
# RTEMS LibBSD is a transparent source build of the FreeBSD kernel source for RTEMS.
@@ -41,89 +45,43 @@ except:
import sys
sys.exit(1)
-import libbsd
-import waf_libbsd
import os.path
import runpy
import sys
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
+
import waflib.Options
+import libbsd
+import waf_libbsd
+
builders = {}
-BUILDSET_DIR = "buildset"
-BUILDSET_DEFAULT = "buildset/default.ini"
-
-def load_ini(conf, f):
- ini = configparser.ConfigParser()
- ini.read(f)
- if not ini.has_section('general'):
- conf.fatal("'{}' is missing a general section.".format(f))
- if not ini.has_option('general', 'name'):
- conf.fatal("'{}' is missing a general/name.".format(f))
- if ini.has_option('general', 'extends'):
- extends = ini.get('general', 'extends')
- extendfile = None
- basepath = os.path.dirname(f)
- if os.path.isfile(os.path.join(basepath, extends)):
- extendfile = os.path.join(basepath, extends)
- elif os.path.isfile(os.path.join(BUILDSET_DIR, extends)):
- extendfile = os.path.join(BUILDSET_DIR, extends)
- else:
- conf.fatal("'{}': Invalid file given for general/extends:'{}'"
- .format(f, extends))
- base = load_ini(conf, extendfile)
- for s in ini.sections():
- if not base.has_section(s):
- base.add_section(s)
- for o in ini.options(s):
- val = ini.get(s, o)
- base.set(s, o, val)
- ini = base
- return ini
-
-def load_config(conf, f):
- ini = load_ini(conf, f)
- config = {}
-
- config['name'] = ini.get('general', 'name')
-
- config['modules-enabled'] = []
- mods = []
- if ini.has_section('modules'):
- mods = ini.options('modules')
- for mod in mods:
- if ini.getboolean('modules', mod):
- config['modules-enabled'].append(mod)
- return config
def update_builders(ctx, buildset_opt):
global builders
builders = {}
-
buildsets = []
if buildset_opt == []:
- buildset_opt.append(BUILDSET_DEFAULT)
+ buildset_opt.append(waf_libbsd.BUILDSET_DEFAULT)
for bs in buildset_opt:
if os.path.isdir(bs):
for f in os.listdir(bs):
if f[-4:] == ".ini":
- buildsets += [os.path.join(bs,f)]
+ buildsets += [os.path.join(bs, f)]
else:
for f in bs.split(','):
buildsets += [f]
-
for bs in buildsets:
- builder = waf_libbsd.Builder()
- libbsd.load(builder)
- bsconfig = load_config(ctx, bs)
- bsname = bsconfig['name']
- builder.updateConfiguration(bsconfig)
- builder.generate(rtems_version)
- builders[bsname]=builder
+ try:
+ builder = waf_libbsd.Builder()
+ libbsd.load(builder)
+ builder.loadConfig(bs)
+ builder.generate(rtems_version)
+ except Exception as exc:
+ raise
+ ctx.fatal(str(exc))
+ builders[builder.getName()] = builder
+
def bsp_init(ctx, env, contexts):
# This function generates the builders and adds build-xxx, clean-xxx and
@@ -143,6 +101,7 @@ def bsp_init(ctx, env, contexts):
for y in contexts:
newcmd = y.cmd + '-' + builder
newvariant = y.variant + '-' + builder
+
class context(y):
cmd = str(newcmd)
variant = str(newvariant)
@@ -158,49 +117,62 @@ def bsp_init(ctx, env, contexts):
commands += [str(cmd)]
waflib.Options.commands = commands
+
def init(ctx):
- rtems.init(ctx, version = rtems_version, long_commands = True,
- bsp_init = bsp_init)
+ rtems.init(ctx,
+ version=rtems_version,
+ long_commands=True,
+ bsp_init=bsp_init)
+
def options(opt):
rtems.options(opt)
opt.add_option("--enable-auto-regen",
- action = "store_true",
- default = False,
- dest = "auto_regen",
- help = "Enable auto-regeneration of LEX, RPC and YACC files.")
+ action="store_true",
+ default=False,
+ dest="auto_regen",
+ help="Enable auto-regeneration of LEX, RPC and YACC files.")
opt.add_option("--enable-warnings",
- action = "store_true",
- default = False,
- dest = "warnings",
- help = "Enable all warnings. The default is quiet builds.")
+ action="store_true",
+ default=False,
+ dest="warnings",
+ help="Enable all warnings. The default is quiet builds.")
opt.add_option("--net-test-config",
- default = "config.inc",
- dest = "net_config",
- help = "Network test configuration.")
+ default="config.inc",
+ dest="net_config",
+ help="Network test configuration.")
opt.add_option("--freebsd-options",
- action = "store",
- default = "",
- dest = "freebsd_options",
- help = "Set FreeBSD options (developer option).")
- opt.add_option("--optimization",
- action = "store",
- default = "2",
- dest = "optimization",
- help = "Set optimization level to OPTIMIZATION (-On compiler flag). Default is 2 (-O2).")
- opt.add_option("--buildset",
- action = "append",
- default = [],
- dest = "buildset",
- help = "Select build sets to build. If set to a directory, all .ini file in this directory will be used.")
+ action="store",
+ default="",
+ dest="freebsd_options",
+ help="Set FreeBSD options (developer option).")
+ opt.add_option(
+ "--optimization",
+ action="store",
+ default="2",
+ dest="optimization",
+ help=
+ "Set optimization level to OPTIMIZATION (-On compiler flag). Default is 2 (-O2)."
+ )
+ opt.add_option(
+ "--buildset",
+ action="append",
+ default=[],
+ dest="buildset",
+ help=
+ "Select build sets to build. If set to a directory," \
+ " all .ini file in this directory will be used."
+ )
+
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")
+ conf.check(header_name="dlfcn.h", features="c")
+ conf.check(header_name="rtems/pci.h", features="c", mandatory=False)
if rtems.check_networking(conf):
- conf.fatal("RTEMS kernel contains the old network support; configure RTEMS with --disable-networking")
+ conf.fatal(
+ "RTEMS kernel contains the old network support;" \
+ " configure RTEMS with --disable-networking"
+ )
env = conf.env.derive()
for builder in builders:
ab = conf.env.RTEMS_ARCH_BSP
@@ -210,25 +182,28 @@ def bsp_configure(conf, arch_bsp):
builders[builder].bsp_configure(conf, arch_bsp)
conf.setenv(ab)
+
def configure(conf):
if conf.options.auto_regen:
- conf.find_program("lex", mandatory = True)
- conf.find_program("rpcgen", mandatory = True)
- conf.find_program("yacc", mandatory = True)
+ conf.find_program("lex", mandatory=True)
+ conf.find_program("rpcgen", mandatory=True)
+ conf.find_program("yacc", mandatory=True)
conf.env.AUTO_REGEN = conf.options.auto_regen
conf.env.WARNINGS = conf.options.warnings
conf.env.NET_CONFIG = conf.options.net_config
- conf.env.FREEBSD_OPTIONS =conf.options.freebsd_options
+ conf.env.FREEBSD_OPTIONS = conf.options.freebsd_options
conf.env.OPTIMIZATION = conf.options.optimization
conf.env.BUILDSET = conf.options.buildset
if len(conf.env.BUILDSET) == 0:
- conf.env.BUILDSET += [BUILDSET_DEFAULT]
+ conf.env.BUILDSET += [waf_libbsd.BUILDSET_DEFAULT]
update_builders(conf, conf.env.BUILDSET)
rtems.configure(conf, bsp_configure)
+
def test(bld):
rtems.test_uninstall(bld)
+
def build(bld):
rtems.build(bld)
builders[bld.libbsd_buildset_name].build(bld)