summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md361
-rw-r--r--CONTRIBUTING.rst736
-rw-r--r--README.md304
-rw-r--r--README.rst853
-rwxr-xr-xbuilder.py2
-rw-r--r--buildset/default.ini1
-rw-r--r--dhcpcd/dhcpcd.c2
m---------freebsd-org0
-rw-r--r--freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h2
-rw-r--r--freebsd/contrib/tcpdump/tcpdump.c95
-rw-r--r--freebsd/contrib/wpa/src/utils/eloop.c19
-rw-r--r--freebsd/crypto/openssl/apps/ocsp.c2
-rw-r--r--freebsd/crypto/openssl/apps/openssl.c2
-rw-r--r--freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h2
-rw-r--r--freebsd/crypto/openssl/crypto/ui/ui_openssl.c10
-rw-r--r--freebsd/lib/libc/include/libc_private.h4
-rw-r--r--freebsd/lib/libc/stdio/local.h4
-rw-r--r--freebsd/sbin/pfctl/pfctl_altq.c1
-rw-r--r--freebsd/sbin/pfctl/pfctl_parser.c10
-rw-r--r--freebsd/sbin/pfctl/pfctl_parser.h2
-rw-r--r--freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h1
-rw-r--r--freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h1
-rw-r--r--freebsd/sbin/ping/ping.c8
-rw-r--r--freebsd/sbin/ping6/ping6.c16
-rw-r--r--freebsd/sys/arm/freescale/imx/imx6_ccm.c2
-rw-r--r--freebsd/sys/arm64/include/machine/in_cksum.h43
-rw-r--r--freebsd/sys/dev/ffec/if_ffec.c128
-rw-r--r--freebsd/sys/dev/mmc/mmcsd.c6
-rw-r--r--freebsd/sys/sys/_domainset.h2
-rw-r--r--freebsd/sys/sys/domainset.h52
-rw-r--r--freebsd/tools/tools/net80211/wlanstats/main.c22
-rw-r--r--freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h2
-rw-r--r--freebsd/usr.bin/netstat/if.c19
-rw-r--r--freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h2
-rw-r--r--ipsec-tools/src/libipsec/pfkey.c10
-rw-r--r--ipsec-tools/src/racoon/privsep.c12
-rw-r--r--ipsec-tools/src/racoon/rtems-bsd-racoon-data.h2
-rw-r--r--ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h3
-rw-r--r--ipsec-tools/src/racoon/session.c30
-rw-r--r--libbsd.py46
-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.h13
-rw-r--r--rtemsbsd/include/machine/_kernel_if.h14
-rw-r--r--rtemsbsd/include/machine/_kernel_socket.h1
-rwxr-xr-xrtemsbsd/include/machine/rtems-bsd-cache.h2
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-space.h6
-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/nfsclient/nfs.c2
-rw-r--r--rtemsbsd/pppd/rtemspppd.c2
-rw-r--r--rtemsbsd/rtems/program-internal.h7
-rw-r--r--rtemsbsd/rtems/rtems-bsd-racoon.c2
-rw-r--r--rtemsbsd/rtems/rtems-bsd-rc-conf-net.c10
-rw-r--r--rtemsbsd/rtems/rtems-bsd-rc-conf.c25
-rw-r--r--rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c2
-rw-r--r--rtemsbsd/rtems/rtems-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/mve/if_mve.c2389
-rw-r--r--rtemsbsd/sys/dev/mve/if_mve_nexus.c935
-rw-r--r--rtemsbsd/sys/dev/stmac/if_stmac.c1
-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/loopback01/test_main.c4
-rw-r--r--testsuite/media01/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.c5
-rw-r--r--testsuite/ppp01/test_main.c3
-rw-r--r--testsuite/tcpdump01/test_main.c292
-rw-r--r--testsuite/telnetd01/test_main.c3
-rw-r--r--testsuite/thread01/test_main.c5
-rw-r--r--testsuite/usbkbd01/init.c4
-rw-r--r--testsuite/usbmouse01/init.c4
-rw-r--r--testsuite/zerocopy01/test_main.c3
-rwxr-xr-xwaf16
-rw-r--r--waf_libbsd.py17
-rw-r--r--wscript4
94 files changed, 8764 insertions, 4259 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index e1cf152a..00000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,361 +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 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:
-
-```
-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.
-
-```
-$ ./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/README.md b/README.md
deleted file mode 100644
index b1a7e1aa..00000000
--- a/README.md
+++ /dev/null
@@ -1,304 +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 using VDE:
-
-```
-$ qemu-system-arm -no-reboot -serial null -serial mon:stdio \
- -net nic,model=cadence_gem -net vde,id=vde0,sock=/tmp/vde1 \
- -nographic -M xilinx-zynq-a9 -m 256M \
- -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/selectpollkqueue01.exe
-```
-
-[1] It is good practice to keep your environment as empty as possible. Setting
- paths to tools or specific values to configure or control a build is
- dangerous because settings can leak between different builds and change
- what you expect a build to do. The Waf tool used here lets you specify on
- the command line the tools and RTEMS paths and this is embedded in Waf's
- configuration information. If you have a few source trees working at any
- one time with different tool sets or configurations you can easly move
- between them safe in the knowledge that one build will not infect another.
-
-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 [/] #
-```
-
-SMP Requirements
-----------------
-
-In order to support
-[EPOCH(9)](https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9)
-a scheduler with thread pinning support is required. This is the case if you
-use the default scheduler configuration. EPOCH(9) is a central synchronization
-mechanism of the network stack.
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..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 a34a1518..b43eb618 100755
--- a/builder.py
+++ b/builder.py
@@ -883,7 +883,7 @@ class Module(object):
]
return files
- def addPlainTextFile(self, files):
+ def addPlainTextFiles(self, files):
self.files += self.addFiles('user', files,
FreeBSDPathComposer(), Converter(),
Converter(), assertNothing)
diff --git a/buildset/default.ini b/buildset/default.ini
index 472c535d..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
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/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/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 2d6e5ade..921797d0 100644
--- a/freebsd/sbin/ping6/ping6.c
+++ b/freebsd/sbin/ping6/ping6.c
@@ -287,7 +287,9 @@ static void fill(char *, char *);
static int get_hoplim(struct msghdr *);
static int get_pathmtu(struct msghdr *);
static struct in6_pktinfo *get_rcvpktinfo(struct msghdr *);
+#ifndef __rtems__
static void onsignal(int);
+#endif /* __rtems__ */
static void onint(int);
static size_t pingerlen(void);
static int pinger(void);
@@ -347,7 +349,9 @@ main(int argc, char *argv[])
struct timespec last, intvl;
struct sockaddr_in6 from, *sin6;
struct addrinfo hints, *res;
+#ifndef __rtems__
struct sigaction si_sa;
+#endif /* __rtems__ */
int cc, i;
int almost_done, ch, hold, packlen, preload, optval, error;
int nig_oldmcprefix = -1;
@@ -374,7 +378,9 @@ main(int argc, char *argv[])
char *policy_out = NULL;
#endif
double t;
+#ifndef __rtems__
u_long alarmtimeout;
+#endif /* __rtems__ */
size_t rthlen;
#ifdef IPV6_USE_MIN_MTU
int mflag = 0;
@@ -400,7 +406,9 @@ main(int argc, char *argv[])
intvl.tv_sec = interval / 1000;
intvl.tv_nsec = interval % 1000 * 1000000;
+#ifndef __rtems__
alarmtimeout = preload = 0;
+#endif /* __rtems__ */
datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
capdns = capdns_setup();
#ifndef IPSEC
@@ -628,6 +636,7 @@ main(int argc, char *argv[])
options |= F_WAITTIME;
waittime = (int)t;
break;
+#ifndef __rtems__
case 't':
alarmtimeout = strtoul(optarg, &e, 0);
if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
@@ -638,6 +647,7 @@ main(int argc, char *argv[])
optarg, MAXALARM);
alarm((int)alarmtimeout);
break;
+#endif /* __rtems__ */
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
case 'P':
@@ -1174,6 +1184,7 @@ main(int argc, char *argv[])
}
clock_gettime(CLOCK_MONOTONIC, &last);
+#ifndef __rtems__
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = onsignal;
@@ -1189,6 +1200,7 @@ main(int argc, char *argv[])
if (sigaction(SIGALRM, &si_sa, 0) == -1)
err(EX_OSERR, "sigaction SIGALRM");
}
+#endif /* __rtems__ */
if (options & F_FLOOD) {
intvl.tv_sec = 0;
intvl.tv_nsec = 10000000;
@@ -1310,11 +1322,13 @@ main(int argc, char *argv[])
}
}
}
+#ifndef __rtems__
sigemptyset(&si_sa.sa_mask);
si_sa.sa_flags = 0;
si_sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &si_sa, 0);
sigaction(SIGALRM, &si_sa, 0);
+#endif /* __rtems__ */
summary();
if(packet != NULL)
@@ -1323,6 +1337,7 @@ main(int argc, char *argv[])
exit(nreceived == 0 ? 2 : 0);
}
+#ifndef __rtems__
static void
onsignal(int sig)
{
@@ -1339,6 +1354,7 @@ onsignal(int sig)
#endif
}
}
+#endif /* __rtems__ */
/*
* pinger --
diff --git a/freebsd/sys/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/arm64/include/machine/in_cksum.h b/freebsd/sys/arm64/include/machine/in_cksum.h
index d55b838b..522ba005 100644
--- a/freebsd/sys/arm64/include/machine/in_cksum.h
+++ b/freebsd/sys/arm64/include/machine/in_cksum.h
@@ -1,6 +1,4 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause
- *
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
@@ -31,7 +29,6 @@
* from tahoe: in_cksum.c 1.2 86/01/05
* from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
* from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
- * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg
* $FreeBSD$
*/
@@ -40,44 +37,16 @@
#include <sys/cdefs.h>
+#ifdef _KERNEL
#define in_cksum(m, len) in_cksum_skip(m, len, 0)
-
+u_short in_addword(u_short sum, u_short b);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+u_int do_cksum(const void *, int);
#if defined(IPVERSION) && (IPVERSION == 4)
-/*
- * It it useful to have an Internet checksum routine which is inlineable
- * and optimized specifically for the task of computing IP header checksums
- * in the normal case (where there are no options and the header length is
- * therefore always exactly five 32-bit words.
- */
-#ifdef __CC_SUPPORTS___INLINE
-
-static __inline void
-in_cksum_update(struct ip *ip)
-{
- int __tmpsum;
- __tmpsum = (int)ntohs(ip->ip_sum) + 256;
- ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
-}
-
-#else
-
-#define in_cksum_update(ip) \
- do { \
- int __tmpsum; \
- __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
- ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
- } while(0)
-
-#endif
+u_int in_cksum_hdr(const struct ip *);
#endif
-#ifdef _KERNEL
-#if defined(IPVERSION) && (IPVERSION == 4)
-u_int in_cksum_hdr(const struct ip *ip);
-#endif
-u_short in_addword(u_short sum, u_short b);
u_short in_pseudo(u_int sum, u_int b, u_int c);
-u_short in_cksum_skip(struct mbuf *m, int len, int skip);
-#endif
+#endif /* _KERNEL */
#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c
index 47c0f770..316e077c 100644
--- a/freebsd/sys/dev/ffec/if_ffec.c
+++ b/freebsd/sys/dev/ffec/if_ffec.c
@@ -139,9 +139,17 @@ static struct ofw_compat_data compat_data[] = {
/*
* Driver data and defines. The descriptor counts must be a power of two.
*/
+#ifndef __rtems__
#define RX_DESC_COUNT 512
+#else /* __rtems__ */
+#define RX_DESC_COUNT 64
+#endif /* __rtems__ */
#define RX_DESC_SIZE (sizeof(struct ffec_hwdesc) * RX_DESC_COUNT)
+#ifndef __rtems__
#define TX_DESC_COUNT 512
+#else /* __rtems__ */
+#define TX_DESC_COUNT 64
+#endif /* __rtems__ */
#define TX_DESC_SIZE (sizeof(struct ffec_hwdesc) * TX_DESC_COUNT)
#define TX_MAX_DMA_SEGS 8
@@ -201,6 +209,11 @@ struct ffec_softc {
int rx_ic_count; /* RW, valid values 0..255 */
int tx_ic_time;
int tx_ic_count;
+#ifdef __rtems__
+
+ device_t mdio_device;
+ struct mtx mdio_mtx;
+#endif /* __rtems__ */
};
static struct resource_spec irq_res_spec[MAX_IRQ_COUNT + 1] = {
@@ -368,6 +381,13 @@ ffec_miibus_readreg(device_t dev, int phy, int reg)
int val;
sc = device_get_softc(dev);
+#ifdef __rtems__
+ if (sc->mdio_device) {
+ return (MIIBUS_READREG(sc->mdio_device, phy, reg));
+ }
+
+ mtx_lock(&sc->mdio_mtx);
+#endif /* __rtems__ */
WR4(sc, FEC_IER_REG, FEC_IER_MII);
@@ -378,11 +398,17 @@ ffec_miibus_readreg(device_t dev, int phy, int reg)
if (!ffec_miibus_iowait(sc)) {
device_printf(dev, "timeout waiting for mii read\n");
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (-1); /* All-ones is a symptom of bad mdio. */
}
val = RD4(sc, FEC_MMFR_REG) & FEC_MMFR_DATA_MASK;
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (val);
}
@@ -392,6 +418,13 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val)
struct ffec_softc *sc;
sc = device_get_softc(dev);
+#ifdef __rtems__
+ if (sc->mdio_device) {
+ return (MIIBUS_WRITEREG(sc->mdio_device, phy, reg, val));
+ }
+
+ mtx_lock(&sc->mdio_mtx);
+#endif /* __rtems__ */
WR4(sc, FEC_IER_REG, FEC_IER_MII);
@@ -403,9 +436,15 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val)
if (!ffec_miibus_iowait(sc)) {
device_printf(dev, "timeout waiting for mii write\n");
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (-1);
}
+#ifdef __rtems__
+ mtx_unlock(&sc->mdio_mtx);
+#endif /* __rtems__ */
return (0);
}
@@ -1569,6 +1608,9 @@ ffec_detach(device_t dev)
if (sc->mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+#ifdef __rtems__
+ mtx_destroy(&sc->mtx);
+#endif /* __rtems__ */
FFEC_LOCK_DESTROY(sc);
return (0);
}
@@ -1718,6 +1760,80 @@ ffec_set_txic(struct ffec_softc *sc)
ffec_set_ic(sc, FEC_TXIC0_REG, sc->tx_ic_count, sc->tx_ic_time);
}
+#ifdef __rtems__
+int
+ffec_get_phy_information(
+ struct ffec_softc *sc,
+ phandle_t node,
+ device_t dev,
+ int *phy_addr
+)
+{
+ phandle_t phy_node;
+ phandle_t parent_node;
+ pcell_t phy_handle, phy_reg;
+ device_t other;
+ phandle_t xref;
+
+ /* Search for the phy-handle and get the address */
+
+ if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
+ sizeof(phy_handle)) <= 0)
+ return (ENXIO);
+
+ phy_node = OF_node_from_xref(phy_handle);
+
+ if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
+ sizeof(phy_reg)) <= 0)
+ return (ENXIO);
+
+ *phy_addr = phy_reg;
+
+ /* Detect whether PHY handle is connected to this or another FFEC. */
+ parent_node = phy_node;
+
+ while (parent_node != 0) {
+ if (parent_node == node) {
+ /* PHY is directly connected. That's easy. */
+ sc->mdio_device = NULL;
+ return 0;
+ }
+
+ /*
+ * Check whether the node is also an Ethernet controller. Do
+ * that by just assuming that every Ethernet controller has a
+ * PHY attached to it.
+ */
+ if (OF_getencprop(parent_node, "phy-handle",
+ (void *)&phy_handle, sizeof(phy_handle)) > 0) {
+ /*
+ * Try to find the device of the other Ethernet
+ * controller and use that for MDIO communication.
+ * Note: This is not really a nice workaround but it
+ * works.
+ */
+ xref = OF_xref_from_node(parent_node);
+ if (xref == 0) {
+ device_printf(dev,
+ "Couldn't get device that handles PHY\n");
+ return (ENXIO);
+ }
+ other = OF_device_from_xref(xref);
+ if (other == 0) {
+ device_printf(dev,
+ "Couldn't get device that handles PHY\n");
+ return (ENXIO);
+ }
+ sc->mdio_device = other;
+ return 0;
+ }
+
+ parent_node = OF_parent(parent_node);
+ }
+ return (ENXIO);
+}
+
+#endif /* __rtems__ */
static int
ffec_attach(device_t dev)
{
@@ -1735,6 +1851,10 @@ ffec_attach(device_t dev)
sc->dev = dev;
FFEC_LOCK_INIT(sc);
+#ifdef __rtems__
+ mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+#endif /* __rtems__ */
/*
* There are differences in the implementation and features of the FEC
@@ -2039,9 +2159,17 @@ ffec_attach(device_t dev)
ffec_miigasket_setup(sc);
/* Attach the mii driver. */
+#ifdef __rtems__
+ OF_device_register_xref(OF_xref_from_node(ofw_node), dev);
+ if (ffec_get_phy_information(sc, ofw_node, dev, &phynum) != 0) {
+ phynum = MII_PHY_ANY;
+ }
+ (void) dummy;
+#else /* __rtems__ */
if (fdt_get_phyaddr(ofw_node, dev, &phynum, &dummy) != 0) {
phynum = MII_PHY_ANY;
}
+#endif /* __rtems__ */
error = mii_attach(dev, &sc->miibus, ifp, ffec_media_change,
ffec_media_status, BMSR_DEFCAPMASK, phynum, MII_OFFSET_ANY,
(sc->fecflags & FECTYPE_MVF) ? MIIF_FORCEANEG : 0);
diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c
index 11cf945f..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
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/domainset.h b/freebsd/sys/sys/domainset.h
index 5a00347f..e028f3e9 100644
--- a/freebsd/sys/sys/domainset.h
+++ b/freebsd/sys/sys/domainset.h
@@ -44,34 +44,34 @@
sizeof(__XSTRING(MAXMEMDOM)))
-#define DOMAINSET_CLR(n, p) BIT_CLR(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_COPY(f, t) BIT_COPY(DOMAINSET_SETSIZE, f, t)
-#define DOMAINSET_ISSET(n, p) BIT_ISSET(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_SET(n, p) BIT_SET(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_ZERO(p) BIT_ZERO(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FILL(p) BIT_FILL(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_SETOF(n, p) BIT_SETOF(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_EMPTY(p) BIT_EMPTY(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_ISFULLSET(p) BIT_ISFULLSET(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_SUBSET(p, c) BIT_SUBSET(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_OVERLAP(p, c) BIT_OVERLAP(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_CMP(p, c) BIT_CMP(DOMAINSET_SETSIZE, p, c)
-#define DOMAINSET_OR(d, s) BIT_OR(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_AND(d, s) BIT_AND(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_NAND(d, s) BIT_NAND(DOMAINSET_SETSIZE, d, s)
-#define DOMAINSET_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_SET_ATOMIC(n, p) BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_CLR(n, p) __BIT_CLR(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_COPY(f, t) __BIT_COPY(DOMAINSET_SETSIZE, f, t)
+#define DOMAINSET_ISSET(n, p) __BIT_ISSET(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_SET(n, p) __BIT_SET(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_ZERO(p) __BIT_ZERO(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FILL(p) __BIT_FILL(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_SETOF(n, p) __BIT_SETOF(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_EMPTY(p) __BIT_EMPTY(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_ISFULLSET(p) __BIT_ISFULLSET(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_SUBSET(p, c) __BIT_SUBSET(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_OVERLAP(p, c) __BIT_OVERLAP(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_CMP(p, c) __BIT_CMP(DOMAINSET_SETSIZE, p, c)
+#define DOMAINSET_OR(d, s) __BIT_OR(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_AND(d, s) __BIT_AND(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_NAND(d, s) __BIT_NAND(DOMAINSET_SETSIZE, d, s)
+#define DOMAINSET_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p)
#define DOMAINSET_SET_ATOMIC_ACQ(n, p) \
- BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_AND_ATOMIC(n, p) BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p)
-#define DOMAINSET_OR_ATOMIC(d, s) BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s)
+ __BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p)
+#define DOMAINSET_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s)
#define DOMAINSET_COPY_STORE_REL(f, t) \
- BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t)
-#define DOMAINSET_FFS(p) BIT_FFS(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FLS(p) BIT_FLS(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_COUNT(p) BIT_COUNT(DOMAINSET_SETSIZE, p)
-#define DOMAINSET_FSET BITSET_FSET(_NDOMAINSETWORDS)
-#define DOMAINSET_T_INITIALIZER BITSET_T_INITIALIZER
+ __BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t)
+#define DOMAINSET_FFS(p) __BIT_FFS(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FLS(p) __BIT_FLS(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_COUNT(p) __BIT_COUNT(DOMAINSET_SETSIZE, p)
+#define DOMAINSET_FSET __BITSET_FSET(_NDOMAINSETWORDS)
+#define DOMAINSET_T_INITIALIZER __BITSET_T_INITIALIZER
#define DOMAINSET_POLICY_INVALID 0
#define DOMAINSET_POLICY_ROUNDROBIN 1
diff --git a/freebsd/tools/tools/net80211/wlanstats/main.c b/freebsd/tools/tools/net80211/wlanstats/main.c
index 3a6fd204..ef2caf56 100644
--- a/freebsd/tools/tools/net80211/wlanstats/main.c
+++ b/freebsd/tools/tools/net80211/wlanstats/main.c
@@ -43,6 +43,7 @@
#ifdef __rtems__
#define __need_getopt_newlib
#include <getopt.h>
+#include <string.h>
#include <machine/rtems-bsd-program.h>
#include <machine/rtems-bsd-commands.h>
#endif /* __rtems__ */
@@ -96,6 +97,7 @@ getfmt(const char *tag)
return tag;
}
+#ifndef __rtems__
static int signalled;
static void
@@ -103,6 +105,7 @@ catchalarm(int signo __unused)
{
signalled = 1;
}
+#endif /* __rtems__ */
#if 0
static void
@@ -262,6 +265,7 @@ main(int argc, char *argv[])
wf->setstamac(wf, mac);
if (argc > 0) {
+#ifndef __rtems__
u_long interval = strtoul(argv[0], NULL, 0);
int line, omask;
@@ -283,24 +287,10 @@ main(int argc, char *argv[])
wf->print_total(wf, stdout);
}
fflush(stdout);
-#ifndef __rtems__
omask = sigblock(sigmask(SIGALRM));
if (!signalled)
sigpause(0);
sigsetmask(omask);
-#else /* __rtems__ */
- {
- sigset_t oldmask, desired, empty;
-
- sigemptyset(&empty);
- sigemptyset(&desired);
- sigaddset(&desired, SIGALRM);
- sigprocmask(SIG_BLOCK, &desired, &oldmask);
- while (!signalled)
- sigsuspend(&desired);
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- }
-#endif /* __rtems__ */
signalled = 0;
alarm(interval);
line++;
@@ -346,6 +336,10 @@ main(int argc, char *argv[])
} while (len >= sizeof(struct ieee80211req_sta_info));
}
#endif
+#else /* __rtems__ */
+ (void)mode;
+ printf("wlanstats: not implemented\n");
+#endif /* __rtems__ */
} else {
wf->collect_tot(wf);
wf->print_verbose(wf, stdout);
diff --git a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
index f88c5834..afb145be 100644
--- a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
+++ b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h
@@ -1,5 +1,3 @@
/* generated by userspace-header-gen.py */
#include <rtems/linkersets.h>
#include "rtems-bsd-wlanstats-data.h"
-/* main.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_wlanstats, static int signalled);
diff --git a/freebsd/usr.bin/netstat/if.c b/freebsd/usr.bin/netstat/if.c
index c578629e..ffb639c6 100644
--- a/freebsd/usr.bin/netstat/if.c
+++ b/freebsd/usr.bin/netstat/if.c
@@ -512,6 +512,7 @@ intpr(void (*pfunc)(char *), int af)
freeifmaddrs(ifmap);
}
+#ifndef __rtems__
struct iftot {
u_long ift_ip; /* input packets */
u_long ift_ie; /* input errors */
@@ -575,6 +576,7 @@ catchalarm(int signo __unused)
{
signalled = true;
}
+#endif /* __rtems__ */
/*
* Print a running summary of interface statistics.
@@ -585,6 +587,7 @@ catchalarm(int signo __unused)
static void
sidewaysintpr(void)
{
+#ifndef __rtems__
struct iftot ift[2], *new, *old;
struct itimerval interval_it;
int oldmask, line;
@@ -619,26 +622,11 @@ loop:
xo_close_list("interface-statistics");
return;
}
-#ifdef __rtems__
- {
- sigset_t oldmask, desired, empty;
-
- sigemptyset(&empty);
- sigemptyset(&desired);
- sigaddset(&desired, SIGALRM);
- sigprocmask(SIG_BLOCK, &desired, &oldmask);
- while (!signalled)
- sigsuspend(&desired);
- signalled = false;
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
- }
-#else /* __rtems__ */
oldmask = sigblock(sigmask(SIGALRM));
while (!signalled)
sigpause(0);
signalled = false;
sigsetmask(oldmask);
-#endif /* __rtems__ */
line++;
fill_iftot(new);
@@ -681,4 +669,5 @@ loop:
goto loop;
/* NOTREACHED */
+#endif /* __rtems__ */
}
diff --git a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
index 9e12b65e..9a685f33 100644
--- a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
+++ b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h
@@ -1,5 +1,3 @@
/* generated by userspace-header-gen.py */
#include <rtems/linkersets.h>
#include "rtems-bsd-netstat-data.h"
-/* if.c */
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_netstat, static sig_atomic_t signalled);
diff --git a/ipsec-tools/src/libipsec/pfkey.c b/ipsec-tools/src/libipsec/pfkey.c
index 385a21a9..cc6ad816 100644
--- a/ipsec-tools/src/libipsec/pfkey.c
+++ b/ipsec-tools/src/libipsec/pfkey.c
@@ -1836,8 +1836,18 @@ pfkey_open(void)
(void)setsockopt(so, SOL_SOCKET, SO_SNDBUF,
&bufsiz_wanted, sizeof(bufsiz_wanted));
+#ifndef __rtems__
/* Try to have have at least 2MB. If we have more, do not lower it. */
bufsiz_wanted = 2 * 1024 * 1024;
+#else /* __rtems__ */
+ /*
+ * The bufsize_wanted has an influence on the maximum number of SPDs. We
+ * don't really need that much of them on an embedded system. If some
+ * application really needs it, this can be overwritten with the
+ * pfkey_buffer option in the config file.
+ */
+ bufsiz_wanted = 128 * 1024;
+#endif /* __rtems__ */
len = sizeof(bufsiz_current);
ret = getsockopt(so, SOL_SOCKET, SO_RCVBUF,
&bufsiz_current, &len);
diff --git a/ipsec-tools/src/racoon/privsep.c b/ipsec-tools/src/racoon/privsep.c
index 8efdae84..914d8a44 100644
--- a/ipsec-tools/src/racoon/privsep.c
+++ b/ipsec-tools/src/racoon/privsep.c
@@ -80,7 +80,9 @@ static int privsep_sock[2] = { -1, -1 };
static int privsep_recv(int, struct privsep_com_msg **, size_t *);
static int privsep_send(int, struct privsep_com_msg *, size_t);
static int safety_check(struct privsep_com_msg *, int i);
+#ifndef __rtems__
static int port_check(int);
+#endif /* __rtems__ */
static int unsafe_env(char *const *);
static int unknown_name(int);
static int unsafe_path(char *, int);
@@ -321,7 +323,6 @@ privsep_init(void)
#if defined(__NetBSD__) || defined(__FreeBSD__)
setproctitle("[priv]");
#endif
-#endif /* __rtems__ */
/*
* Don't catch any signal
@@ -334,13 +335,16 @@ privsep_init(void)
signal(SIGUSR1, SIG_DFL);
signal(SIGUSR2, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
+#endif /* __rtems__ */
while (1) {
size_t len;
struct privsep_com_msg *combuf;
struct privsep_com_msg *reply;
char *data;
+#ifndef __rtems__
size_t *buflen;
+#endif /* __rtems__ */
size_t totallen;
char *bufs[PRIVSEP_NBUF_MAX];
int i;
@@ -1067,7 +1071,9 @@ privsep_getpsk(str, keylen)
vchar_t *psk;
struct privsep_com_msg *msg;
size_t len;
+#ifndef __rtems__
int *keylenp;
+#endif /* __rtems__ */
char *data;
if (geteuid() == 0)
@@ -1129,7 +1135,11 @@ privsep_socket(domain, type, protocol)
size_t len;
char *data;
struct socket_args socket_args;
+#ifndef __rtems__
int s, saved_errno = 0;
+#else /* __rtems__ */
+ int s;
+#endif /* __rtems__ */
if (geteuid() == 0)
return socket(domain, type, protocol);
diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
index 7f1e9247..6867269e 100644
--- a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
+++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h
@@ -56,7 +56,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern struct dhgroup dh_modp8192);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern char *pname);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int f_foreground);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int print_location);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern u_int32_t loglevel);
+RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern __uint32_t loglevel);
/* policy.c */
/* privsep.c */
/* proposal.c */
diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
index cdf8a74d..51b67d2e 100644
--- a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
+++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h
@@ -5,8 +5,5 @@
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_active_mask);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_preset_mask);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int nfds);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int signals[]);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static sig_atomic_t volatile volatile sigreq[]);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor *allocated_fd_monitors);
RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor_list fd_monitor_tree[]);
-RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct sched scflushsa);
diff --git a/ipsec-tools/src/racoon/session.c b/ipsec-tools/src/racoon/session.c
index 90120c76..bd2bd316 100644
--- a/ipsec-tools/src/racoon/session.c
+++ b/ipsec-tools/src/racoon/session.c
@@ -119,6 +119,7 @@ struct fd_monitor {
#define NUM_PRIORITIES 2
+#ifndef __rtems__
static void close_session __P((void));
static void initfds __P((void));
static void init_signal __P((void));
@@ -126,6 +127,7 @@ static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int))));
static void check_sigreq __P((void));
static void check_flushsa __P((void));
static int close_sockets __P((void));
+#endif /* __rtems__ */
#ifndef __rtems__
static fd_set preset_mask, active_mask;
@@ -140,8 +142,10 @@ static struct fd_monitor *allocated_fd_monitors;
static TAILQ_HEAD(fd_monitor_list, fd_monitor) fd_monitor_tree[NUM_PRIORITIES];
static int nfds = 0;
+#ifndef __rtems__
static volatile sig_atomic_t sigreq[NSIG + 1];
static struct sched scflushsa = SCHED_INITIALIZER();
+#endif /* __rtems__ */
void
monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority)
@@ -199,9 +203,11 @@ session(void)
{
struct timeval *timeout;
int error;
+#ifndef __rtems__
char pid_file[MAXPATHLEN];
FILE *fp;
pid_t racoon_pid = 0;
+#endif /* __rtems__ */
int i, count;
struct fd_monitor *fdm;
@@ -209,6 +215,8 @@ session(void)
#ifndef __rtems__
FD_ZERO(&preset_mask);
#else /* __rtems__ */
+ size_t allocated_mask_size = sizeof(fd_set) *
+ howmany(rtems_libio_number_iops, sizeof(fd_set) * 8);
allocated_preset_mask = calloc(sizeof(fd_set),
howmany(rtems_libio_number_iops, sizeof(fd_set) * 8));
if (allocated_preset_mask == NULL)
@@ -228,7 +236,9 @@ session(void)
/* initialize schedular */
sched_init();
+#ifndef __rtems__
init_signal();
+#endif /* __rtems__ */
if (pfkey_init() < 0)
errx(1, "failed to initialize pfkey socket");
@@ -325,24 +335,31 @@ session(void)
racoon_pid = getpid();
fprintf(fp, "%ld\n", (long)racoon_pid);
fclose(fp);
-#endif /* __rtems__ */
for (i = 0; i <= NSIG; i++)
sigreq[i] = 0;
+#endif /* __rtems__ */
while (1) {
+#ifndef __rtems__
/*
* asynchronous requests via signal.
* make sure to reset sigreq to 0.
*/
check_sigreq();
+#endif /* __rtems__ */
/* scheduling */
timeout = schedular();
/* schedular can change select() mask, so we reset
* the working copy here */
+#ifndef __rtems__
active_mask = preset_mask;
+#else /* __rtems__ */
+ memcpy(allocated_active_mask, allocated_preset_mask,
+ allocated_mask_size);
+#endif /* __rtems__ */
error = select(nfds + 1, &active_mask, NULL, NULL, timeout);
if (error < 0) {
@@ -379,6 +396,7 @@ session(void)
}
}
+#ifndef __rtems__
/* clear all status and exit program. */
static void
close_session()
@@ -391,11 +409,6 @@ close_session()
flushsainfo();
close_sockets();
backupsa_clean();
-#ifdef __rtems__
- free(allocated_preset_mask); allocated_preset_mask = NULL;
- free(allocated_active_mask); allocated_active_mask = NULL;
- free(allocated_fd_monitors); allocated_fd_monitors = NULL;
-#endif /* __rtems__ */
plog(LLV_INFO, LOCATION, NULL, "racoon process %d shutdown\n", getpid());
@@ -565,11 +578,7 @@ set_signal(sig, func)
memset((caddr_t)&sa, 0, sizeof(sa));
sa.sa_handler = func;
-#ifndef __rtems__
sa.sa_flags = SA_RESTART;
-#else /* __rtems__ */
- sa.sa_flags = 0;
-#endif /* __rtems__ */
if (sigemptyset(&sa.sa_mask) < 0)
return -1;
@@ -590,6 +599,7 @@ close_sockets()
#endif
return 0;
}
+#endif /* __rtems__ */
#ifdef __rtems__
#include "rtems-bsd-racoon-session-data.h"
diff --git a/libbsd.py b/libbsd.py
index c9151901..db390df0 100644
--- a/libbsd.py
+++ b/libbsd.py
@@ -553,7 +553,7 @@ class base(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'COPYRIGHT'
]
@@ -891,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):
@@ -1714,7 +1732,7 @@ class dev_nic_e1000(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'sys/dev/e1000/LICENSE'
]
@@ -2594,7 +2612,7 @@ class opencrypto(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'sys/contrib/libsodium/LICENSE'
]
@@ -3250,7 +3268,7 @@ class user_space(builder.Module):
],
mm.generator['source'](['-DINET'])
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/libxo/LICENSE'
]
@@ -4257,7 +4275,7 @@ class crypto_openssl(builder.Module):
mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](),
mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](),
mm.generator['buildSystemComposer']()))
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'crypto/openssl/LICENSE'
]
@@ -4395,7 +4413,7 @@ class contrib_expat(builder.Module):
],
mm.generator['source'](cflags)
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/expat/COPYING'
]
@@ -4500,7 +4518,7 @@ class contrib_libpcap(builder.Module):
],
mm.generator['source'](cflags)
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/libpcap/LICENSE'
]
@@ -4745,7 +4763,7 @@ class usr_sbin_tcpdump(builder.Module):
['freebsd/contrib/tcpdump',
'freebsd/usr.sbin/tcpdump/tcpdump'])
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/tcpdump/LICENSE'
]
@@ -5064,7 +5082,7 @@ class usr_sbin_wpa_supplicant(builder.Module):
],
mm.generator['source']()
)
- self.addPlainTextFile(
+ self.addPlainTextFiles(
[
'contrib/wpa/COPYING'
]
@@ -5316,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):
@@ -5407,7 +5431,7 @@ class tests(builder.Module):
self.addTest(mm.generator['test']('smp01', ['test_main'], extraLibs = ['rtemstest']))
self.addTest(mm.generator['test']('media01', ['test_main', 'pattern-test'],
runTest = False,
- extraLibs = ['ftpd', 'telnetd']))
+ extraLibs = ['tftpfs', 'ftpd', 'telnetd']))
self.addTest(mm.generator['test']('mcast01', ['test_main']))
self.addTest(mm.generator['test']('vlan01', ['test_main'], netTest = True))
self.addTest(mm.generator['test']('lagg01', ['test_main'], netTest = True))
@@ -5449,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):
@@ -5466,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))
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 e6487470..37008cc6 100644
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -173,6 +173,14 @@ 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);
@@ -251,6 +259,11 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL);
SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+#elif defined(LIBBSP_BEATNIK_BSP_H)
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(mve, 0, 0, NULL);
+SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
+
#elif defined(LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H)
RTEMS_BSD_DRIVER_PC_LEGACY;
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/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-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-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/nfsclient/nfs.c b/rtemsbsd/nfsclient/nfs.c
index baada6ce..e9e83abb 100644
--- a/rtemsbsd/nfsclient/nfs.c
+++ b/rtemsbsd/nfsclient/nfs.c
@@ -3114,7 +3114,7 @@ rtems_filesystem_location_info_t old;
rtems_filesystem_current->location = old;
}
rtems_semaphore_release(rpa->sync);
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
diff --git a/rtemsbsd/pppd/rtemspppd.c b/rtemsbsd/pppd/rtemspppd.c
index cf237a81..c001eff5 100644
--- a/rtemsbsd/pppd/rtemspppd.c
+++ b/rtemsbsd/pppd/rtemspppd.c
@@ -71,7 +71,7 @@ static rtems_task pppTask(rtems_task_argument arg)
/* terminate myself */
rtems_pppd_taskid = 0;
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
int rtems_pppd_initialize(void)
diff --git a/rtemsbsd/rtems/program-internal.h b/rtemsbsd/rtems/program-internal.h
index da817130..2104c064 100644
--- a/rtemsbsd/rtems/program-internal.h
+++ b/rtemsbsd/rtems/program-internal.h
@@ -60,6 +60,12 @@ struct program_allocmem_item {
LIST_ENTRY(program_allocmem_item) entries;
};
+struct program_destructor {
+ void (*destructor)(void *);
+ void *arg;
+ LIST_ENTRY(program_destructor) link;
+};
+
struct rtems_bsd_program_control {
void *context;
int exit_code;
@@ -68,6 +74,7 @@ struct rtems_bsd_program_control {
LIST_HEAD(, program_fd_item) open_fd;
LIST_HEAD(, program_file_item) open_file;
LIST_HEAD(, program_allocmem_item) allocated_mem;
+ LIST_HEAD(, program_destructor) destructors;
};
struct rtems_bsd_program_control *rtems_bsd_program_get_control_or_null(void);
diff --git a/rtemsbsd/rtems/rtems-bsd-racoon.c b/rtemsbsd/rtems/rtems-bsd-racoon.c
index c7ea3594..e6e6205c 100644
--- a/rtemsbsd/rtems/rtems-bsd-racoon.c
+++ b/rtemsbsd/rtems/rtems-bsd-racoon.c
@@ -75,7 +75,7 @@ racoon_task(rtems_task_argument arg)
}
clean_up_args(args);
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
rtems_status_code
diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
index 23ee15db..8ffaa914 100644
--- a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
+++ b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c
@@ -103,7 +103,9 @@ cloned_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa)
"ifconfig", aa->argv[arg], "create", NULL
};
rtems_bsd_rc_conf_print_cmd(rc_conf, "cloning_interfaces", 3, ifconfg_args);
- rtems_bsd_command_ifconfig(3, (char**) ifconfg_args);
+ if (rtems_bsd_command_ifconfig(3, (char**) ifconfg_args)) {
+ return -1;
+ }
}
return 0;
@@ -377,7 +379,7 @@ defaultrouter(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, bool
memset(&sin, 0, sizeof(sin));
memset(&rti_info[0], 0, sizeof(rti_info));
sin.sin_family = AF_INET;
- inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr);
+ (void) inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr);
r = rtems_get_route(&sin, rti_info);
if (r == 0 && rti_info[RTAX_GATEWAY] != NULL) {
@@ -710,9 +712,9 @@ run_dhcp(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa)
}
dd->config.priority = priority;
- rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv);
+ r = rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv);
- if (dd->argc_argv->argc > 0) {
+ if (r == 0 && dd->argc_argv->argc > 0) {
dd->config.argc = dd->argc_argv->argc;
dd->config.argv = dd->argc_argv->argv;
}
diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf.c b/rtemsbsd/rtems/rtems-bsd-rc-conf.c
index 36f90a1d..88d98c3e 100644
--- a/rtemsbsd/rtems/rtems-bsd-rc-conf.c
+++ b/rtemsbsd/rtems/rtems-bsd-rc-conf.c
@@ -260,12 +260,14 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
*/
length = strnlen(text, RTEMS_BSD_RC_CONF_MAX_SIZE);
if (length == RTEMS_BSD_RC_CONF_MAX_SIZE) {
+ free(_rc_conf);
errno = E2BIG;
return -1;
}
copy = strdup(text);
if (copy == NULL) {
+ free(_rc_conf);
errno = ENOMEM;
return -1;
}
@@ -286,6 +288,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
lines = malloc(sizeof(char*) * line_count);
if (lines == NULL) {
free(copy);
+ free(_rc_conf);
errno = ENOMEM;
return -1;
}
@@ -335,6 +338,13 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
if (timeout >= 0)
_rc_conf->waiter = rtems_task_self();
+ if (_rc_conf->name == NULL) {
+ free((void*) _rc_conf->lines);
+ free((void*) _rc_conf->data);
+ free(_rc_conf);
+ return -1;
+ }
+
/*
* Create the lock.
*/
@@ -343,6 +353,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf,
free((void*) _rc_conf->name);
free((void*) _rc_conf->lines);
free((void*) _rc_conf->data);
+ free(_rc_conf);
return -1;
}
@@ -714,6 +725,7 @@ rc_conf_worker(rtems_task_argument task_argument)
rtems_chain_node* node = rtems_chain_first(&services);
int r = 0;
int error;
+ bool rc_conf_verbose;
/*
* Check for a syslog priority before any services are run.
@@ -748,6 +760,8 @@ rc_conf_worker(rtems_task_argument task_argument)
if (r < 0)
rc_conf->error_code = error;
+ rc_conf_verbose = rc_conf->verbose;
+
/*
* If there is a waiter signal else clean up because the waiter has gone.
*/
@@ -760,10 +774,10 @@ rc_conf_worker(rtems_task_argument task_argument)
rc_conf_destroy(rc_conf);
}
- if (rc_conf->verbose)
+ if (rc_conf_verbose)
printf("rc.conf: finished\n");
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
int
@@ -793,6 +807,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
if (sc != RTEMS_SUCCESSFUL) {
fprintf(stderr, "error: %s: get priority: %s\n",
name, rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return -1;
}
@@ -805,6 +820,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
&worker);
if (sc != RTEMS_SUCCESSFUL) {
fprintf (stderr, "error: worker create: %s", rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return -1;
}
@@ -814,6 +830,7 @@ rtems_bsd_run_rc_conf_script(const char* name,
(rtems_task_argument) rc_conf);
if (sc != RTEMS_SUCCESSFUL) {
fprintf (stderr, "error: worker start: %s", rtems_status_text(sc));
+ rc_conf_destroy(rc_conf);
errno = EIO;
return - 1;
}
@@ -869,7 +886,7 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose)
if (r < 0)
return r;
- rc_conf = malloc(sb.st_size);
+ rc_conf = malloc(sb.st_size + 1);
if (rc_conf == NULL) {
errno = ENOMEM;
return -1;
@@ -892,6 +909,8 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose)
fclose(file);
+ rc_conf[sb.st_size] = '\0';
+
r = rtems_bsd_run_rc_conf_script(name, rc_conf, timeout, verbose);
free(rc_conf);
diff --git a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
index 4af789cc..3f705975 100644
--- a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
+++ b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c
@@ -53,7 +53,7 @@ new_wpa_supplicant_task(rtems_task_argument arg)
free(params->argv);
free(params);
- rtems_task_delete( RTEMS_SELF );
+ rtems_task_exit();
}
int rtems_bsd_command_wpa_supplicant_fork(int argc, char **argv)
diff --git a/rtemsbsd/rtems/rtems-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/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/stmac/if_stmac.c b/rtemsbsd/sys/dev/stmac/if_stmac.c
index 614c51b9..7e3e07c7 100644
--- a/rtemsbsd/sys/dev/stmac/if_stmac.c
+++ b/rtemsbsd/sys/dev/stmac/if_stmac.c
@@ -40,6 +40,7 @@
#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>
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/loopback01/test_main.c b/testsuite/loopback01/test_main.c
index 1b5c0064..51d5e5b2 100644
--- a/testsuite/loopback01/test_main.c
+++ b/testsuite/loopback01/test_main.c
@@ -133,7 +133,7 @@ static rtems_task workerTask(rtems_task_argument arg)
if (close(s) < 0)
printf("Can't close worker task socket: %s\n", strerror(errno));
printf("Worker task terminating.\n");
- rtems_task_delete(RTEMS_SELF);
+ rtems_task_exit();
}
/*
@@ -230,7 +230,7 @@ static rtems_task clientTask(rtems_task_argument arg)
clientWorker(arg);
sendClientEventToMasterTask(arg);
printf("Client task terminating.\n");
- rtems_task_delete( RTEMS_SELF );
+ rtems_task_exit();
}
/*
diff --git a/testsuite/media01/test_main.c b/testsuite/media01/test_main.c
index 2a1c9aae..001c80a8 100644
--- a/testsuite/media01/test_main.c
+++ b/testsuite/media01/test_main.c
@@ -201,6 +201,8 @@ early_initialization(void)
#define CONFIGURE_FILESYSTEM_DOSFS
+#define CONFIGURE_FILESYSTEM_TFTPFS
+
#define CONFIGURE_MAXIMUM_PROCESSORS 32
#define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 4096
diff --git a/testsuite/nfs01/test_main.c b/testsuite/nfs01/test_main.c
index 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 5941ea5a..dfb9847b 100644
--- a/testsuite/pf02/test_main.c
+++ b/testsuite/pf02/test_main.c
@@ -167,6 +167,7 @@ test_main(void)
{
rtems_status_code sc;
int rv;
+ rtems_shell_env_t env;
prepare_files();
@@ -176,9 +177,7 @@ test_main(void)
rv = rtems_initialize_ftpd();
assert(rv == 0);
- rtems_shell_env_t env;
-
- memset(&env, 0, sizeof(env));
+ rtems_shell_dup_current_env(&env);
rtems_shell_main_loop( &env );
exit(0);
diff --git a/testsuite/ppp01/test_main.c b/testsuite/ppp01/test_main.c
index d4baf5db..b6e9d4d7 100644
--- a/testsuite/ppp01/test_main.c
+++ b/testsuite/ppp01/test_main.c
@@ -272,8 +272,7 @@ test_main(void)
rv = rtems_pppd_connect();
assert(rv == 0);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
RTEMS_BSD_DEFINE_NEXUS_DEVICE(ppp, 0, 0, NULL);
diff --git a/testsuite/tcpdump01/test_main.c b/testsuite/tcpdump01/test_main.c
new file mode 100644
index 00000000..0f31cdde
--- /dev/null
+++ b/testsuite/tcpdump01/test_main.c
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @brief Tests the tcpdump command.
+ */
+
+/*
+ * Copyright (C) 2022 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <vm/uma.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+
+#include <assert.h>
+#include <ck_epoch.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rtems.h>
+#include <rtems/bsd/bsd.h>
+#include <rtems/console.h>
+#include <rtems/libcsupport.h>
+
+#include <machine/rtems-bsd-commands.h>
+
+#define TEST_NAME "LIBBSD TCPDUMP 1"
+
+typedef struct {
+ rtems_id main_id;
+ int sp[2];
+} test_context;
+
+static test_context test_instance;
+
+static void
+epoch_cleanup(void)
+{
+ rtems_status_code sc;
+
+ sc = rtems_task_wake_after(CK_EPOCH_LENGTH);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void
+init_addr(struct sockaddr_in *addr)
+{
+ int ok;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(1234);
+ ok = inet_aton("127.0.0.1", &addr->sin_addr);
+ assert(ok != 0);
+}
+
+static rtems_id
+start_task(rtems_task_entry entry, void *arg)
+{
+ rtems_task_priority prio;
+ rtems_id id;
+ rtems_status_code sc;
+
+ sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_create(rtems_build_name('T', 'E', 'S', 'T' ), prio - 1,
+ 32 * 1024, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(id, entry, (rtems_task_argument)arg);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ return id;
+}
+
+static void
+test_tcpdump_help(void)
+{
+ char *argv[] = {
+ "tcpdump",
+ "-h",
+ NULL
+ };
+ rtems_resource_snapshot snapshot;
+ int exit_code;
+
+ rtems_resource_snapshot_take(&snapshot);
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ assert(rtems_resource_snapshot_check(&snapshot));
+}
+
+static const char raw_packets_file[] = "packets.bin";
+
+static void
+run_tcpdump_write_raw_packets(rtems_task_argument arg)
+{
+ test_context *ctx;
+ FILE *file;
+ FILE *saved_stdin;
+ FILE *saved_stdout;
+ FILE *saved_stderr;
+ int rv;
+ char *argv[] = {
+ "tcpdump",
+ "-n",
+ "-i",
+ "lo0",
+ "-w",
+ RTEMS_DECONST(char *, raw_packets_file),
+ NULL
+ };
+ int exit_code;
+ rtems_status_code sc;
+
+ ctx = (test_context *)arg;
+
+ saved_stdin = stdin;
+ saved_stdout = stdout;
+ saved_stderr = stderr;
+
+ file = fdopen(ctx->sp[1], "r+");
+ assert(file != NULL);
+
+ stdin = file;
+ stdout = file;
+ stderr = file;
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ stdin = saved_stdin;
+ stdout = saved_stdout;
+ stderr = saved_stderr;
+
+ rv = fclose(file);
+ assert(rv == 0);
+
+ sc = rtems_event_transient_send(ctx->main_id);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rtems_task_exit();
+}
+
+static void
+test_tcpdump_write_raw_packets(test_context *ctx)
+{
+ char *argv[] = {
+ "tcpdump",
+ "-n",
+ "-r",
+ RTEMS_DECONST(char *, raw_packets_file),
+ NULL
+ };
+ rtems_resource_snapshot snapshot;
+ int in;
+ int out;
+ int rv;
+ char c;
+ ssize_t n;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ rtems_status_code sc;
+ int exit_code;
+
+ rtems_resource_snapshot_take(&snapshot);
+
+ rv = socketpair(PF_UNIX, SOCK_STREAM, 0, ctx->sp);
+ assert(rv == 0);
+
+ start_task(run_tcpdump_write_raw_packets, ctx);
+
+ init_addr(&addr);
+
+ in = socket(PF_INET, SOCK_DGRAM, 0);
+ assert(out >= 0);
+
+ rv = bind(in, (const struct sockaddr *) &addr, sizeof(addr));
+ assert(rv == 0);
+
+ out = socket(PF_INET, SOCK_DGRAM, 0);
+ assert(out >= 0);
+
+ c = 'x';
+ n = sendto(out, &c, sizeof(c), 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ assert(n == 1);
+
+ c = 'y';
+ addr_len = sizeof(addr);
+ n = recvfrom(in, &c, sizeof(c), 0,
+ (struct sockaddr *) &addr, &addr_len);
+ assert(n == 1);
+ assert(c == 'x');
+
+ /* The tcpdump pcap read timeout is 1000ms */
+ sc = rtems_task_wake_after(rtems_clock_get_ticks_per_second());
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ c = 'q';
+ n = write(ctx->sp[0], &c, sizeof(c));
+ assert(n == 1);
+
+ rv = close(out);
+ assert(rv == 0);
+
+ rv = close(in);
+ assert(rv == 0);
+
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ rv = close(ctx->sp[0]);
+ assert(rv == 0);
+
+ exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv);
+ assert(exit_code == 0);
+
+ rv = unlink(raw_packets_file);
+ assert(rv == 0);
+
+ epoch_cleanup();
+ assert(rtems_resource_snapshot_check(&snapshot));
+}
+
+static void
+test_main(void)
+{
+ test_context *ctx;
+ FILE *file;
+ int rv;
+
+ ctx = &test_instance;
+ ctx->main_id = rtems_task_self();
+
+ /* Fill dynamic file pool in Newlib */
+ file = fopen(CONSOLE_DEVICE_NAME, "r+");
+ assert(file != NULL);
+ rv = fclose(file);
+ assert(rv == 0);
+
+ /*
+ * Stop interferences of uma_timeout() which may need some dynamic
+ * memory.
+ */
+ rtems_uma_drain_timeout();
+
+ rtems_bsd_ifconfig_lo0();
+ epoch_cleanup();
+
+ test_tcpdump_help();
+ test_tcpdump_write_raw_packets(ctx);
+
+ exit(0);
+}
+
+#include <rtems/bsd/test/default-init.h>
diff --git a/testsuite/telnetd01/test_main.c b/testsuite/telnetd01/test_main.c
index 73d19c4e..d7e50a97 100644
--- a/testsuite/telnetd01/test_main.c
+++ b/testsuite/telnetd01/test_main.c
@@ -75,8 +75,7 @@ test_main(void)
rtems_status_code sc = rtems_telnetd_initialize();
assert(sc == RTEMS_SUCCESSFUL);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_SHELL
diff --git a/testsuite/thread01/test_main.c b/testsuite/thread01/test_main.c
index 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 1be546e4..3a03c12c 100644
--- a/testsuite/zerocopy01/test_main.c
+++ b/testsuite/zerocopy01/test_main.c
@@ -240,8 +240,7 @@ test_main(void)
sc = rtems_task_start(id, network_flood_task, (rtems_task_argument) bc);
assert(sc == RTEMS_SUCCESSFUL);
- rtems_task_delete(RTEMS_SELF);
- assert(0);
+ rtems_task_exit();
}
#define DEFAULT_NETWORK_DHCPCD_ENABLE
diff --git a/waf b/waf
index 7ceee167..3099c10e 100755
--- a/waf
+++ b/waf
@@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE.
import os, sys, inspect
-VERSION="2.0.19"
-REVISION="1f3c580272b15a03d2566843c5fe872a"
-GIT="61ee22b598cf80e260beb64e475966f58b304d0d"
+VERSION="2.0.25"
+REVISION="767522112be77f8585812fcfaa08e805"
+GIT="39ef33e48380a2db38a0eae40a3a4a2c954f4450"
INSTALL=''
-C1='#6'
-C2='#.'
-C3='#%'
+C1='#+'
+C2='#*'
+C3='#)'
cwd = os.getcwd()
join = os.path.join
@@ -168,6 +168,6 @@ if __name__ == '__main__':
Scripting.waf_entry_point(cwd, VERSION, wafdir)
#==>
-#BZh91AY&SY9 \DPm(¬#%00e(b/mЀ#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%zmmkZ旲U#.[iھ:'kd7{o۶ֵK]v'OgvsN)#.Q+ٽpޝ]/{m<{guҋ@{vo;;vi44l{|U9ݟݚyx#%#%@(}h#%` ﲀ=wf:4[aӹyA껴4=d+֨)f0#6 *{Q$#%#6#%YJzV%}jaj3TTf˦Iv]s}6u7z:#.xW_=z֭Z;ۻ}sy{izr>^}azz{x-z̀ݳֲ4fs`uF"EE7w#%(J*OC#%l=n{Ywd >}[vG`>EmkMW`Ly^u#%}at;o}|{mOvpǼ΀o*WƘܶ2>ϟw];j9Qt׷Zt>ܮu|}ܬ&Nq\:.^yaCӶۛХ&nCz|ilͶв[ۺ5koXqؗnl/`{w>PJPTTjB7uv&iv]vݛ:kkUUܘ{x)HЃɯfw#%7ZI#%#%;^3F-mzݙ_U.us[ v]]|H,q{c;vJ{+l+Wg`;{n#]}ww{xv#.mEf"sﯼl$/gєx<V6EǑޮGxO{>w޴#%*;F7;g<uoSkݭӭm9.ݬ ݾwwwtv:#.[.wc ӂhgu^nu/=4#%cM{JU){xW`{VU[}IzovuH*u$I֪nc[נ&ƻ}ac| Gͼ&:}6;vRw}-[nh &#%& CBa0#.#%PTzК#%{SA)B @@$zzI 4#%#%#%#%#%#%#%D!M5TMoU<zT()mG#%#%ѣ@#%#%'RD!4dOD$OFM#.@#.Sj#%#%#%#%#%#%$!#%&@bh&M#%M#.4Si4 #%h#%#%#%I L#A&=&m{SIh#%d4#%#%?Uir\QWwkZvhʃ>5Zu!LA ,LQ*")PcZ5OӦ Jtđ8WoT"]^S_3os2 lDs4;mqE6M`Vd]j◘5o# UI7wDKU/~oBE"SU5k3khȋ 7#%H*-E :$H& "HP !#%h#%$@d P@mfLCLd@M$Rj63525)FSm&‰$J2ZQh[Fi,iZ!#.F)iMF #.1eMDRlZSMhYi#. (cRFQ&JlBjcI@hHRF"[MUifLM& mM65%)-53fZL1fdlB!QfR`4TH!`ؤfJb0lBXhdFIR&B4Cl &)BFRf$@YY5ccE#6dK) -) %&ED2hhɉIF($@VAMEfR` ؉1M ͂ab6V$$PRD%Q0EaIL# &RJMEXHjHb iM$"H[b2F̒3bdE(͑&U4Đ)AQ`,i"5+%6(R"I&aLcI &Qi3H("hkP,&YA2%&DM2*4f6)16A! "1F4, &T՘l(#.H!HСYM$QF&JFhb22iD`JM52i!)#.1Sb5*R)#.2)H(HR"E$M)i5DFh&TiD#.,eDYJbD!f k6lmd$4Ld5EPZ XR$cc1 2RXYJ&EM(ZhII$2ɲ#)E,ѥ15e"$4M`BjfF&RLȈ6,&%T٢-L-bC؈RD#HJm~ka͢lVƶ*6L4R44F5Yc(S*HD[%QIKQ&D5acR#6e2eI*4TB+d"TT›63,YLS,JJSLl)%2RZ!գ%AEVMdjEHkD$mhŊl2ѵ0U@iR F2&ƍ&di ժ* KY4IR1!MlY"Y1Jm*RYaZDFK"e5MMk,Xlk+,l  Db4!-T%KQY&6LJ,&l#."+,E1Jl4 AE$&Fm6F6ScE%bH#!4LQJdRP5BT4 JBj-#636)F̤!Hbki4F5&a $YP-4) Ě4Yda, jE54ECF)6bfQj4  2A&ĕ%I1Rcda)ME`0٤lfECJ2ZfhT2Hl0D2hihl,LDRBAh[ QQ-IM#6LR#b-fC26*2%,ѴkTI5E$ԚPdjkA4!E$Ih5EJ"Fd̡jQHfJ$(j#V#.2,Y*6)42V-&)+)DMTRccdd&#HRLɃA#6)6&"ƨY)&RV-ɈF42JԈieheji5 Q5%)Й̌ƚL&kDlU%hԕ,&Lڐ1lj cc$UE2X61mIQ f-F+iQQJVf*B (4X"J&,ʒ+I[M+FbbIE[lR&T*&$1P"CFi&%Xش[F542Y6!mIME26QAjMIʈBLI&RLd"-тnԥC)B51jTqe+(a(h! b LL$hlNz9SUIP?[lȟYLsȠ;L(0\:8pI d*߱r*3ZI(R+g,$b/0-npz%ZM8wBd~ ՋN؜Z,3cRR(lrrXݳfVc`I"lLhcz瓓G]޹w^\/uu\LP"W4LvcK@JAF&ēMb3(]enY74h6 m `ٗ(c2@szo(7p c#6Gv),`rR[R}dzgsNVQL0ФY;d8$ϣ؞j(%?rõ Jxkcx-2Kw%CUyyܖw^Qr車esnCQW6qllO5Z4EA?_W-ȦWȶIu?Go*y|m<b#4L9cuPEX#.t@#6A!##.)Hz^+\Ѩ8wcr˙(ˮ$s5ZAhJr.YlC,0!M te#6R79,פFtxV@Gd%l p<k9c|Ǚ#tE]H P0T[؃_#zrHȦwԊW7mxVY? )rvjoQwEme#.dԂ׶bN!e &u/K`nnT)"*#6d9 #c*w&bۆfR6z:'.k,YL#6N*}y>㊝U"֍UOpi)8+ Ӭ,j[vlQa1KFS7mZ&#6*NP-#6A@*{iX<ZW(EEEnY5͵w>S6ߋMfhF%2+zmW!bSQIܮuV(i%"@X'+&D)J墶/]^"Rkb_-׻*|{=ol|14> ݌>LȲ"4~"UrPaLֶjϹ$![z۲FlWW=4DalAHPŤ''mBCGE2R]U"wլ*Х²SE#6lQ MZH"JuJ *Ó#6ILe„S$+ bN<kH](#6<*oЗAJ /;qap^Ro^Xj"_g<TCsyXq&qK\:05aeF1PXiM3g{O4urgko3 )#i'־ʣwr8;++oHm^Lq#.1Ir%nCM}ז6qv~5.|bA#S<*Jt\D%*9<}/"#.L3v+T-,g)D!;SŐa=3DKA]( HvR߁#v/6PE#Dž8qΰK(q<>:+#%(\Hp6疴zm*'ܩ1?_k#.VFyeo$Á?jfAv߾ڜNz$!.fܡ\i̩#.#E0F:E&}ٞAg]p?X7d1\NB=}ILƘ#6P#6;g="<R:I2\q.V)gɅ,8`=jPqëZ#.,nMB88׵ۺ1il m׺|5=Z鵦Lu $(w0WX uBdN>ܸIiEw+^{5ZU{)cT @^ǡ=4 Zۍo4^1É -onU>iK|eJ/}m}w*IeQ92e@ hQAA9CrOuu5&JF}w/šjj}( +|~TGR{ES>,߻jj܇Y+$.h>nj]Pꩨ ׶{gmk4Ok{єEˣ;ndmAyQ#6ӫfͲX8TNL\P苁1 63Y yUM3#.X|TW7ΊD^jER,)9߹ȷ)ьuO~$_s4?Zu>]1ykEX)>Om8ŝOz\ڸ;R1~FS_t“Rn(PbRfWYʡcPBYׅDQQe0x~{*nf wiXT)6-;ˣw$3}T|-z85`Bhҡ~J΀Z+9rlVx#6h6Dx0)AX*0_#.ǷzeH,5Aޟ{e7S&yY񯥳LSœ?ك-y69^}pBIiK&nǨsιߎe,&2:L[xHǮ*U91 DL5;i傄QT nUّ`ҟ#oJ41Sv馫a離M4O|I$ {Ur0;0*zG/7+ #%Qb]K\.) I9qa#.5Кw*8A'<:b2xQn:,öTl8:<y9CM\"#.JvX=w%gC͕PGhaH)U.ۙA=s ȫsH]#.$8.\ӳMޛ TTaM} P ȥ>?kborR#R/*3Ѧ4uocȐ#.vnVp)rZ,.O}zwln.LQy@EHiw=Qq"%V2&0+Vqw=Pu7V^-EuQQ'U0D}ޙNi$'1ΝKU=_lS#60D1zǾ+dpڈZ?wx\`<+fJ~WKոݝ8n$zG<c/Y vfz3Lcz7k%EΛY-J`]BvP\Hm2LMwr4:Œ`\Խu}<(f`ňͽUQ9c:O+%PiP/<H`6,I<Ye"4EC@fzbFGB>qz< #?}IL_K'B#+|mתǒ7"nb#sh~?~V]6k)0 UBU,'@5+lX6ɠn.{p}:ybsf19%Z##6 ,&ў3|#6y".$#. #.u. ד*{gdevKfQο7N hÄe hP"1bL`Mm"-X]#6=)#.2¿+]tg:00YUDƊHI#L͒6\j}#.ۻ6=U Sޚt4s ٺ"g.t(:qc1^n*fbfۊ;Xh1as>=N#.(qنfT`al~28r!:Y@e jNec;8`ij%cW?DM@0L\'kKNϧcV*F֘9Oǻ*5,<:ؒ|EI$$G4{S#%beTQ!Pve6^\FfX= *n>Q/nNQ#64E^~`G'0\PDL7؃KuohsyQ*Ss;5XvPf8u[˜Sϒ4s?'ݭi ;n)tYG.K|M6Au8'Uҍ*seܙL)e8|W#%}O`|A>bmf\sIO##y3B#.⊠}աnrCDb+` 왭n>Oqh59b f~~V#+b^Ug:^xTq(Ū1y{TzԲ?[s 5Ygk qjWXv_WmMuZ3Q_{=㳗(5#<ID93^y%0Ij[J3NMT nPG4_o=BKJͿﹹoa^ݶxe@e8GHGƊ"ް w~Sav4LUz+fy޿#./wWd^PĴYKAq:;G2jT wC{Ck5Pur0sl+3۬#8]ŋT:$Kr0(\>C ]m9H X1Ƚ7ٟd-KOqDˋs#%$̐@%"iGn gsos+-m%b"moffm{pYpoÆZ̸ 4bx^SuzVlbݵ ϰPO:Zn(#{7)׹L σYӡ"6zXbcG-7J-YSՖ#.[w_#6}P$RF/:HCCo:T=49yzÆrĕ{R4Iom3]Fm;uctloz`(]􁿍5!.ZUǤe(<V+Ύ3Ӳu:b-F|WS] طM(W:u:jڏ̣HEѣ8`k 7c<ΒW=Sq45#.-ڃ߾E3crĞL^3r"M}1(gD%PI`CȲ-G%nG5lV(#6QEcrͬ١C8z`Cu.7Y`D,U GRvpOg[\]EŚ@Bcv8]ѷ7Bov?U?o;2>^R F\ެCtFX 8,;hXj$8Yxp6p ݤ$A7c%#%%"A/c9X]5GY"E5#/*9/Q?Bujm-&;z|ըl@'(*i|GSөK!7mV>9&u#% ջ-v޻ #.cc D@?4#6o轶j0**ŽsÇ#."M\"#%gޜ4yRb(>@tơR]eѷsvӠn(+VpE+'6N3[(ZaɻI{ax=9"1[Jl 1H#.%O#%#%cmaI#.5ׯ_~?OHGTUMy5ц6cjb$|_Cu0^~c_d "w#%>בj>ER=Ss|5mfտ.4|j]:;n>:n@ )fw#.l0}(WoNs {G4|0t9bb?Gk_f "@kX/E;fLq58d*{/D4tɚotgSw>XB<IMmGu9>zs'^͡c7B +Å3r\po_P=T֓4ZMI:ٜ[+Yȍ{Qho`#67]Rd1U[d"b$#6Jk!g۪Lk $,̣[P&UU1#;&bA1dy%* "/Z7cuBsr-$~mL:zXU Dq#6ߩs <=U~L R5} VI魳Lgav_EF -G8?mRG=bSfq7Ai+s"|;O$#Iai׮[H[#.uƎX[ȗJ<T޺Qj@J.#%W&9oD@#.cA,BCG:lkLu#6ž ?; k#.qvʐ3:^LUk4#6`#.~H5ٝ>}[hyѺBJ2\~de#/a74YUCMs{cB'$;ǩ8#.{,jwe#.r^¾<0fx5&Ī6PcQǘ"5UE7yl)p7!$8>{K2ryUD#lwz=wʋ#6Dn}^})DЊ]uq۳2\S{SXY% qnA#U]=@tj&»5&fpK Sd&vE}zGÝ*B#6 Q>Gpvۅm}M *0p|AvB0;k[#6h"# _ z32&Bх56ܦ2,,|زR"QY ҃ LkG>'j1*uiS1u\SdIiTO6Ƙڱᝑzl_g8hxMX0-#sK5?#%yx)7y#%2w Pc  #%t`=!#.fN%[^v'kQŖ`z)nu#?.IpJ[iB*, =`QpgcIezC,f{2ѪRu 7ӝy_>vt|9VKR;#6(<wC)((Fv02hЭD#0+C9z]>lJYbT-žbFrp*(~sꆄ4uy!j#8sy#.bn=>tzh\bPY:—nT `턱ֽnz1DR+GobѣU$2#t^mO8,}YEω{^IfxOpLG#.pYE>vKU} gاڧ^ϭ+#%[a:9ޚ9d+B~e.wqalwE#%{j,d*1wy]OFnsnZ*4xm1|F4sʌ֞{Ζ,l D {#%DhMD)$CTVʍ}14LT.%" #%l@'D188#.A$^0Ʊ\h${ޮ#.NUzJ);I?$J? v#.TTaƻE5vblyA}ɹRC)K.(sN\0pS"o*!WTCyp2b2/mkYKn9J:ܭ\{oMh]ŠfXn#6ۯd ә/Q؅EKNW== ^u?כʓ5+8~яZF0oћ48@_*c0Z4JqF=t%lnb昦J(Bwy.BjܘxHQT84c#6|md@#%奉c6PaFĈ`]w&q22GQۚ1R^+LZyn@m2(ʼn-eFK`(euD 8IwN`;4,:$VbV66PEBD 2NY;6}r=k#6_S0*Byy,SX.9u@+#%)|@zX"%F #%s&;zi|&툝0t651L=õH 9v R4{mnp+=S!t#6ogctnt,luVFƔ#.%Ʒ_Ns*\8r&AڨN6KY#Zԟ:w9Uf j  0F+Iu,<HF;]=ci?k]wXNړC$#."oo+w6<rgln{/#%ƯۡωzY].Lkk#.Ϸg/!5J R%KAdz8vk#.#xEȿȕv`Ejܡֈ%#d{s1܁Qe&rj; 5w1#.)r i/:'D\lm#.2!1M&F)dXUK@md2,tE4'F5F:Ep[̐RM0o5"HJ)#.#6)$դo.؅)ڊ"0ѥbQѸ&b̴cu^،j5 F=3#. M C]o/~aƄDb'YCB&gLF[oQLi 4˃Gd78H]g(q5L;ERi\Dyb9iȦa<LwͣBM"1ȥYi]\b/]Ns p#f z܈F߉  .I۵#.oQvyيxq=LXM7E+_9x:OF#T`ܳqCjm}#6;dJH!MSxL#6\*"e‡;ߎx#.q#֧i>NcUGE^ۤɎ4$;>)͙n$"ȵ7͖Ѡf1(vhC5ל` {ԠR#7(HTZ'cJ:7 Zf6NGQ8@sIۤ܎j#%;qnwj٘Q3ai(Vj(bV\M0kD>UzpNzb#.:8J}COv(O 68H^&ץӳ?ty#%#6]V3eGY`I·% | HsϺEH:u) ,Fj0sVY#W6щ(4"4Yadt#6#}ܶ`5Mffmj (#.r V;'\S)=Ǹ#%'܀#%z\n@|ʼm6 7ǿe)vaҡHFXw?!џj̞xOuw4r }7>(8_]`#v#%H=UQ0rmH%x; .?VI|gxq[$}PT=]#6 Lk+ρKd9r_bDA( #6#fcBt~jl3~inn>y{~] n`"1{Y.HGs$<\G<Dߧ=~Crqy!^HU$2n cb3Aa.Gc@#6\Q\R9U)uۯqV RVܰJ#%Gh#%f,Q߷vKx#% #6wEBcϗj<}z4:C폝7dBb3h"%D͕,J/14iW vevRIKRIXj_^V&.2SQui]!C^~7^4snu*䔩5uw?%`ѩjRD>=>u>i>:##69dc|Vnm/~J:3#'$!PY]oRm$Z&֯SQUQF@#a}Cuib)fy&D*n&NSӰ%$T^%\wbIlI{VTejŋ:1&WR>2#.#6q4~X30~>{P?F(,>_#63~#6 0\rf#6C j1T,jq#66Od'@h'$K媯1 † ː=r2WPŌw1P(ډPΔVRh!0VJ@-I[\T(#6N=>$o-#o}~?S>}+Sy-[O?8[FDctz/~7|6`V˹G8hqp"#.$`{zEVsbGL<OeпIG=}},k<v}#.i{+p߳0@{u#uwDan'@pTc#POR1@y^P8jenvaAcU&d#6V]#GS " Kw-4Pd9p.Q~9ra#.xR^ seHq(>9{ϬQEVp2/Xu|]c=!U%i?C CcXPRvą24q`am; ۾_\PW2ī> ~>=TM@jj*B''%=-uHk8ܪpXؼ&>]Cp}!8BvZ>-꘶*ĄǸ$Mp9@IHg2q#.SuFf3yXx{P-2ѰoIO$'ѿƊ4 Cc 5yo|z1QK,,$o_Cd́bWά ZV3XCE.}<t֚vW:N#Ka<XZ0:44n')c*(Z1F Ip[ ]E}zԉ@x#Muaͧ{yo6lTG76}Y]~z)`]$Ƃ=\YS"#o}?=;~cvφ\X!kC~8ED]mR>^&_#68pboח;vدZSqiojgmysrGt{vǃh`G7mSpa:H~ Rj9\RA~^]%="x h^LGe#.s-я3ٍ̝|%n|jWuz}Fp̜S1LSy}{`--z9%[5v2;'D?/?؃}f]wT(9=E)#.]YYxpaE3ƣ0ѼF%.lA>2.sU?F6e2w?.ſ=nn;"^N]i=[<JsCác!-96GM\RD8;DXhk1j|G#efzu?|{?b죩]<q2yW4sf_Ida2#푷#.+΍-al޿(/#.l<t+{t3./ڠ2@cL|>y8bwc|{ar#gwv[q$RS>]wtf֞O{<Kjtc ]yDZFդpzA:}"p+QՇ_^/m&<7;(|]oO%~W2lS7|r,vENJ^[#=6{}¶um`~۟]n)mϪ#6GFq^F74?w(?1脌`O)[" ƎH#%"2 P~kZ#%RE)#%:O2<~s?u%WwP4I~%Iiӝto򚐺qiA@gjav{>u.?Bʹtg*hDA SyGbe>_)n8h>#.[gj ;0v=>14x gP#hp_Y;|PVQ=?~umA#6`Ǚ+Ժ%#.Ó Swt/vn#Ͷ?!bpv l;̙/|S9? "(H;G*t<&/ֆëGKDj`9K#%#61T'&D񪤽[-2+`IBfH&ڒ~;A΋wŏ8,ȿ{#Y^tM ώ7},Ep#.JR#4Lh+w.X%qh !~I}A7w!h; ->B) J҈ǁ>;ZZ;|%9dCPSxsZgDswr+)1/1B+Sk<kQ}=:ѧBx&"8K.iYk BIeWR͠z4%fj|x΃0Ϩ]Rbo<<rga^Ǽ;y-Y&aI$": 8wu\vioMե|Y1x{]2^aP-zם4-]l_3T*|)8:aΓ>_!(F+~4sgWM/ݎ߶N3hl3-m=Op>#&Q0/S3>mMw\\} $wyR$|_ID'1uluLׄ[=8'x3A#%/SCI%yb1FɥG*bGѺC IO4|Hc cY|u>S#`{Bfe̍cv9G?GF54iTlT Ԣn؀7#6҃,tڛH}=3֤On `a-AȱaILA"hhn)"4[VWc'#.Ss6]&6)":[֡ɠ\6B{qZ8du["?9gBPP}S< =a5rA]EcXw\1ac0]WoOS\V##E4DCH@bi71lXTQ&FCV6ƛfJj*PYO9qߛrӛܙpAFތi|-'lDjUuwu_fWVl:e1ջ(LS>ܬ9H<jUm}Z bvU&겷[GmCfj#6#$pw]uG<~ӧ٣OZx#%8g#65U6;>aL~Yw?VPwu~Uw\3ܮk.&5)n|?>&nnM>MwL[% "B<ëά#6)_@\S|8ћ^F+rEh[J%clX*5gtu^y9fRɢ(([\0ģ#6iȢ chbȣ44W VRңFR\mFAKTQ eBlB#.Q tMy3w%r(*`2shفX;g6JhTCh% 8+:-!itu,#.!@AFY٬uDm#.Q$I8 ~UI#,D֖%\ȪrF(3*®ʌp#R1lbJx3!U$IsJCCID&A;/N} -LH^@u3D>z#.QUhzOT=gx$gSP-Ίj#6ףLPL*fԧ!3F9j~hxc""'NOU876 VY}NO#j@T9s vA#%_${ӫԎV6T=~{ml_/ٵBč'pqC9ˋ!yI<쩫.V|!檣#6uyC'>ܯAe;6LǨ49CHQH2t2wT9"+1r_uLUvhFꑀwf\F>[QZ%T#6լM[he$#܂#.Dl˦mʊBYQYCFF7__{HUu;wOɓA󭬺!BAT1sa虑s,]Wh&6p41-6nH)1=jo\AyY=Zﶊ#6gApE30BHV*h/mM}RdC7y#6ڴsjd$ 8Amb{&o* H86AUrYbZէhCZۅ2SSD#.H#6J lXdL1a4KD#.G3u#.1llX -m(Uzhɐ1(UX0ؔDlp#.i@OlշtS'30<slPt⢲o#6!B#6-o.O6ں<Alqgwff4b}SuT044V\w:{yзc(~Qɨ<ޠOͫчJtzA'ku#%Ǐ"#,,6aiUSҪ4]v,DjGHҌiO|UdNG#"DfV5[D*#.m4&W(_t53MСA!yK#.TU:]*EPT巗dkkm0TJ 5l$2~?sg:?.\`<<@6>\GJ`CT<2+riM -+ATcz^ǶG=4R뫏65atHNj0 FG[qN𮋳  <6ӈ!#6^"X<(Мk]o3H5KKpL,+&,ʁp#.#Za*V>#%h8p͆K9e.lm8V6>d4#C୶Aܺ&F KbV4 =ݠDn$DNAPӌ+gbX̑FP2-c+P)22+*vw?zw_9[ߙoinAyb'j3Xt1m,3tz~a#.6uqMۉq9g,;͝S(v-`kb6V۾ϱ)`f=NRAUHtFE1OSxWxejhSiyif6ܑRn1v6DE qT'wπ.gh#i^Mh3`Qq&ri3haԔ9bXAO}`PwU3~o_FŚ<N=,9}'pF_smA#6#.:M-ɰz! 1I(D8Mp@Τ2l+"Vq*t;$io;n3ن-όu#.pQboyN?uL#.}#.IM$ x)Ϯvюn@:k#.KiccndԪAj[ɕP]K)pU7U"wW; ߒ!r7 JQoŲmZ\IAƆQ#6:26#.];C⍶볺 K%cTnqQ峺m!X kַF=ˤVvNSus'&vl4rb<vjN19b"0NrcZ6W$dB\֊wl #6Өc.ld6&Dm<͎c/r5ߠՁo:ٛD$o}䫂y<YhՊ@μKaNWGi/_ym,qGr0GܜFDdV׆dIF6$,5\m4b-TM^"R".f哽wuRM|an!ԥAlK<F)FP6,-e4RoNYy\#6wƷŀn,ۧGtJLL k8!Lm?oxta^4@en8¹bUֵim;'yhz7,+ɫyDCTTo89IY<\`%dn;,B68u67'}^YtNWd;=/fRͲc pFiUsk[377И{1F.L4<n#%SGFӃ9fiCzү,6bi#6am7Of#.5`fzץqau [A(gzdGߨrt5:'cvP$۳Ftx]L@VA^yw17sGqAكdf_ģ7nh'8u,.y0tO`DT#6 3 ɤ@jP3<#.FR/(?7G:ix{D#%y#8=l{}r\́> 2䀹iߥCKWnר>pbx_9WΎ@TBъZB얔b>_cOVz|ymx#6fǼYJ- 0#6@wwvOӮ!xnpFQ#%jǷe#1TRV1(340 cpTAϨ9z"3łN<ycМrv>ZQ:%Д~ND|Eɴ%< &فq4 )Uy_u«޼?zg n;q;҄&LS1eyg)IV\.Bm0J[qkdŰ#-`Rb]83cO]B$K|\ț|jg}'$jaȵ<4=6=&|6]OvpX=TV*yum+1e#gDt,DFQiwqlޟ#.س^O.Ϫ-I]AQ72I8xLjoLk.ˎMDz=t&:o$48;!Cpa}j bݛ94g|3߽4#.O%ԍء:<4E#.543RTW-kycUN-<#.'ik;!:-D7)%#.M;$h.ؖCpJi5Z9#%7CpC #rӬGrɧ嶖9EJ͕aՅ`xf-0%k2#x#.|^:+Њ S9lVoaz3!m <yCo{v|edQ茌FTĽ~;wjl .r<0X1`1߹i1йKjӧ\;d|:Oeѡcsc↛Z<LQE-a H 0N3\JܘrSs t U>\}[crXZ.,#Ώfi?'D`@#6o~h0*:֠hKCi|__.71u"rFxô⠘#Lҹky>Q9ȳ#%h(+fLmh}uڎBN[R fJ{2APjפ/t那Y$yP֖뢕@)է)O~U$BO3ud1/5<kUxduta>y65NQpNHe>D.*Edn2,Y(迃f/sқ DҋؗЦwNI#.PBbVu]Un=El㋷>JE":ǔ̝[~ڋ&Oĭ)%&UrfIJNgqaCFpzPv߷SC:l#:=ɪ/6<0DUF9R3}m6y)87տQe#$\Ƨ^q1J=f3k$L{=Jm&@׆v|^m"X<cQRHyr#6̪\hlτ#6Qɺ*h952g(eG=!B-F= *\[=4ʅܧImЊFo%*ags7s>:$qCntl Œ=DO@};_+'绠EFD_@ʴ/H(%ۏ~!ʨsMG4E8yKڄc66vvl#.gt>";mu,vut=;2h!0H(ލ}s^G ap%N=cQ<dZfW=y[g|̉ÿi2L7#.nF0r I8Ť6D$M/#6o@ HܡYeVrDD_MptCy:1_G h.:zӫ&~b qMN6kmc99ļkgX3sStc/+F!2xX?]BM#%R3. s)3F~~! i98ItgHCBʠ@FEɁt xbT{IZkΟr[J(7Zפґ齥4%ngWUp‰zƚ. DcF\sTfW#6/9=jѴSeEB#%Xu֊N,&6#6B=p]ٝ#6f|:DaF/+6|Շ,2'7;5m̩]`x2k\U_vX),{wY%q\j|x,`RWnưM0<X5Uo6<-)d^b#%ڰ[>Iz]lS#6eh$'Hf#6^UЃt..k#.п46 zXPť\91mRt}yUQ}[;os-<TvZHsĕ寻&?ߙf}>W޳Owbe'qt_I&5q"gj2E"*[Wn2AM*aFkm0*e"벰I@.q|%hygt>G0xQZQ]~Lf9hj'YtX?PTQj?o]YQf[DԳGtP?eϤ;m\4x5K@Y ([u`"%K%b5j!#TR,Ы`A5w,u1K#6u+;zEko.V߇>(XV9Cg u<w]7+#6~Jo8#!PJ#%)W&89/o7vv- 6mC+WAz)zV{u|NKҏ tܹ"E/séWaƺ\T:#%̹ovJ$oe@tLE#. HЭxiz @pyM>gV0P/WF꠪gR0y\ +nSGJՖ^IY}轄BYC;-|CDfVs#%5\a? vs>_5Fypfuʝ#-/\Tz>#dq K{e"ϽF@}#6#6pi1Ic1 |Im'T\&Ilgp/j~?5v ܲ5b7fnN3[al@}sم_ XFۄ'm6)k4', #6֕KdUC#6F6HDpJWSRs~ axƫaz57m.m*E$ CAlBIm*ҡ˳545Jj9}xH5ZQ<WNVDhU|f+<v ^-~]BA`nW>WB(P%D7K#..mLj+,#e3m{wl4/}}YsEC9H2tflkAM1C,`lرkOIiN}#6n#%A^(l!.o7B[8fXHڀWz2s@ɓjcoN=,qkPx١˷qX8~ =ޡOϒӯtq8=žxE@#%mpONaj eTI*xzԢ4lGGޏtОq͇5KVxd*W6XSm$H/᦯FS<?z̈GP-;yZOD5˷giT 2 ry~NKGkgv|W痿~xLη1\8} ӮW6jS;TǯOmuW<ߍ38z%~rĢ3̦ۤq^Ng'uk//u65>Q{he!w(Ϛ]ȗc!OZ_y1L6P>eza_j~Z^iJtQa[›9-Qz$oL saHR1T *au|bȳē{S-p5cާws_LG偎c} 5IjJ#fعM$rKLgs9a--Jɖ,㾛`|r]x+n/՜ELmb3O]a(!^\C-(r"FgRҸnXم꣥1+/!ƚ6d#6P dITHS{b31棆N \`- xf<3#6  g |c$S@g"Ġ:9.mچ|[6jg5s͢ٽK=W#%U)AMU7/EVb>|sDD#6T2y qв#6k1~6,%gK"hi2K;e#6 fT X]ӦhvְNZPqWPI| gi ㍢\Ҫaa8ibPJrkL0v\q!ytؠ:ܵo<g=o6IޏblHCZn7Fndʿ_քVMc:v+F;ܬ)&#.M'#.bYw:$= Z' &hhmZX\,ʊ9C!90r =޵m3[ ZWT+(a 19ł<搡vFx~GBUU(;}n޹zo]4B:=.at$eJ`r=q`iC JD#.BY`fYBAy U /v/C3$sk:!r=.Y8=}[SdІ*M(RԐx5&GԆ)pI{ .xGtơ',iAxdtY7Fs<dWߣl`)N}͙fKH}C"qQ/Dqh%YNuaf6O}_+s$;zm$$LLzb˞*'%$i:tٙcysQi':-'(`b6<-8.um]{gj9 ^J'o}҇D-W8g{mqnkZ99֍, C`N`[™y#6Z=s'W'd\U|M۵ъ{zrEntwi0J:m O z㍉-|o)#XW>s=o[DЂ un2`1NA;o$x<EqK/ߐbK/ath$&c%nl#'s(Tiاۤ^v*1.)F<eZxA)NT(AБ?>?N_X#6bo1QbDzҸnBhtRߌM~޹h>(= 66M1@|]f.s۞(::lzjOk)pVzקkj3pV>If$J?LV2e*Xp平n(#2Yzk09<-wu͈lnIv("`Taߦ:bdBzGm#6yh3儌#.v=Bj+8;>N8dݺu]:D~/Ү*zpIs>ӾĶ7|Џ.Co<Ozې5*Ќ}&Exъ!SbGeFI-U#%$ԳIٙlM,01*Fe` S~S G>2@~3 Z/sۤzk.x|~D#%鮈u*Z#.nP>uxiԨ&'a S92)igkH|`ܡiS{`'+U<:I,7_ǍV5O/I~!ZRpqy^Km$?ru;46CR;,VҤu'/0?5{is"Vu jO3KONw/6 .#6XrpoH~!RKz%dn()!ߞnȀrlGܟ#DΎN5wP$~tԟNBekM n 17_޹?5;{Ӻ'ό{q(#%`@)(Ԋ9Mv#(JL+G;I@zxԾ(H)gxTWƮ] oEk\lIi?KWؔRmc-5L>(2͛bFmpߋg~:@4>$_#p$ Ő"AY &ׇz:-&OySg]ܞlf^ʓAo?xG57BH\N91IY[Gt#%qHTi*7+?YCxa(z~[_ ]>Nt"rNK(zKd!ڇEtV}nCi]Y??znN:*F`6$t%5Ɛ󳃊\#%|ӹ1?/Y8K,H|"~Щj!#.Kwk|Y Y=v&S7}fhP#6̷ Ϩ:_B>s<7EaVNK](Q/~w+E@L0\"XB!?_G7Lz߄m>+emf c(QƊ#6"u9#.P?KO L09]#%n?@/T#%tx"RKTgLJæhZ\j +r Lt#"#%o(~#%cmfU#%@ER،ğxiBa`8&H6PXci K$q4a-)>N99‰5v8:je`a"eOX2~ZhCq yWiƺ(Tۿol|H,z(`L${9gWR|1{՟@yi<ހ$ LJS(OgIեOSI#zco*(Bv?t,xT[]3X(~R#6`UMK;6(ܣ؊y?= а섀> aI\}V$42q me#.fqpikZԷ(bRf%ER1#p2\4SywE9g+m!#耠(&]#%iuQ:vٮJ5I4n2mp諡nDA|],ʈ[#6y`#6PIow,lN"c;Y:=Z&W#6fY`38Ȋ7K-T"EvFQ`\P.Dp1Cfoe_}gc'KT֨`XD$\#6P8tbOIan0a֐L bB#. ]\BET d(so)g #.4F̷e`1L`yJIG¥#?YzK2 #.:7ߍaNQi2}}Joȝ.L_fs@v#6ĥ7;_{ɞFy"]yXm~,@yNr+(kخ3zL;," 썼BN3$>q6׿yð]|'<@HZ [*sX@U-0e{3ӓTi7qIy΅'V?rH!d=1#3te~AN{4N Ӈ I5(tD# uՙ`QP+&֗`KHZh! A (e<dem5^ޢ)?̸皖6`{kdVԝ2S;7N@`wd@`s59_EOR _zqKs^Gl<\BÖBdF&!\[S~zd'$6٢ɟ6NX |A(噘r;Xq-rI{@{"{Lź5y\JE㛏%9!}CI(s=>X.~Xp^{ka%&⒒Ww,/mzN5ǎ/mK$j +K6L8~1o'\8`-#!C<׮ l28XNeRb/!E75pdpBq;8]c5 ٽ= L=7#%X%3#6,4"uo8K<׬(E;UZ oX:IMejyyBBT#6=q<,; q̷L-rj~Ă=%u:Rcx+#.Ve(Jź Cצm"1I2*ܼF2?0k˳ߔ?\Cc{ >儯p/UM<g;e#6,ۡ(zbJ :7㠜lG1џt|<X'M1I /T])o9_Urb,OL{S_mTܶ㝳ZY:T9oƚ(ul;g}m@ߑYCfMjd<#.x:O)m/4@[Z; hhhP ;w҂V_Fm?2_P|M9ܩ|r]ko {<xrdLf,z7g>Px,l5 =J{CTjhbcDi:L$eRItt)ߙ~t#.p}w)@)) 2dt`!. 3Yb(%D#%tdb#7Bʩ'ثYcCgMRa2[4 ]gbҕwoNݫ,.1DXT<5'.?*biW-c@UT #%AJ#%? >[{}aEaE(ώ?Nx` ߷"lP ޷G?`l= j—\4ʤAP(A&0Ax2EMr2I#6Q0D%90(^Cg~ەRT:Fgw}}vT$/d*>Q4CިوVTńKv#.[T_l6q :A-E. x42#.ދ|¥qڡKj#32r2Ț#6<7Y1֑$h>GTx~2@*%39hZ[`tgؒjcz! ,fǭٔd"@2 v0k(!h[YR |"h/#.( (00 Mm>+dʈDTKGE@2. &cu8 #a j6MIFE.3+;b$K*ْTHBn|4D3#6ٽ3${O٫H i"Q`UKs;$>5P({lp?),rb"ZKAƒ;5Wژ O~~ON{Zc#1P'O)ϭΣM!+E>T9sΚ? #%`cnh?+BGG%om_} MQ' wGύwE&hn%pm"&w&$)rup)\uUd{Kq E]R*8#.6lTx2iDLj22JˑuP3Tj :Ŧ%tCA0۳=f[/0MksvLoTw e1 C{R_qZkc>afоlJBBr';<[Kufȫ%3Ja1b{=G\ULj#ERX城ncsrsVm;X;MV ]#.Zm2$ g-$Fñ L%._L6QSxݚ#.I!M#.FϪMCvB#%! qI*tmơt:C\$@0!#6%BeV7vdcA֩FvtFem6ۨ_^q3_évjᙎ:7T%#.a 3#D<d!'=Ws= "{r3xXL#{F0񜓹<t'(vU\s[%kUZslUʮ@K!E T$Uh^eeq0Qj6x$AICtM4i@\!{!xt,cߓx ltHh<y̯wy|3p+4 )I HE@;4['H!ljQ^58u9^Si|E_PEܚ4ixjRdF[I)-|݇E#.BJ}ؠS0+Zc8VrQ5TQ[u-*gu^24ytKkv_\hG}{NߨV I tUb*ڧVKf,#l swB1Q"ݐ N]S}|x׭?=,%xo:!I*soɋ1J:򤴡eB#.#%,$9gnyϨ7#%&u$36;7R71)T$#6!HTZaH $"%;QҬ߃(Z"(MPB0ˎw@Ne{]=d[rnVoÔR@<$D DG׶-"+joaeSUX'҂B,ںKԸg/7vjRK]#.e+|xzp®ބERǓPig>,PC{Ɩ򽘽Xܩ_=qAg6ОO_*P!6E387Xd]#6m2M#%Fk#U@Ty3o#f, ;Ηm0T&&45I$p~41r/#6jwXh:nxf|LJgXCPt/yҟr#6\(<UTȵ\[#%^H#.}qVFX+1DӭB#..rmȽ͗Y]ZI8LA5N0$юm#6#.Xa.\H܎9Tw_vEF`0HʡE%x+2<<-#.}ϤyT[%)740O:dXoYt{{U;R %D P0Y3#%*V q@iAFL`'!#AV#(RBVc3Ea )X`_ɝ XhD;e􉌎'4QJ#6 Q`(T"@ K `nƴ Z:uYƏtF-RTfsK>BoH~YusPѿ 9:Y&ۘ: cU:ٛBr0>^i[d6p;6 O#.c>U o*&L:.$Lx㤴~9'tld!1#.@u*n&HQT*C.졉=9"E.Ԝ+ueHA$}{;~J\MDF`aGnJBP=#.ݱcLB"YYj@=<q!ɋflc\1GVl.o6linD CD K)Uf[(8?B^f] tJM/BC]郯׻i^|E7u[dgZ;K` 3;`n 節qi8׏OB!2CP;{]Gʂ0QvD9Y=sEB4:vAC2.\|iovZFQ4ZOi2e671kk/#.]6@4x3telG͆ђ@R])NueVL5).s7@4/:U]uC<R\w'UW:QvbFH RS,w=uYV\<wWa"wߤ # q.#8%vωIu-?8>2RMģ8{x]zB<bcz:yfGhbVbZًU%Q>V'׿_S26_g#%:ɩ_xy#%Dه~J$?~~8f>=G/e%A7q,[Yob΢od݊4Cę?a?o$6aH*2AHe,-3W>|opgrjntK;߭%JMYTzQ<u`XXCWKq_A?oMW*ˤ,/70o/8qI&eU՝ωkR2XIlW+YsC?۠GeݞYg |#%$ǡh[<cOgNk¼Mbf"\ 0{P?oG)>p?y?=BVsɼcNG8(5YyPYξjv1}+sk6dص2y$ܡi)_?_3C6?LG_ȯ5Mu=!~*+LOk2b2V 7DDa{';(#j%3tiץAQǎ2Yhqߞw/Z7#6uiMZ0XL!9zzYϿhnHr`f5N$f"Vޱe>?.3/>K6M(Ÿ9O K#6=mcᄡ'J1EwgO R " m"@D__q4|`R쎉+j`Q(L&‡ПK#.FM=33ISإcҚ ȹ~>qU-^uH19aY?NNѯ]#.y `2/ÈDϐ>#6m*H<\2=|.mcw~w`PM9^j=XŽfulDR,BpRH݉)_Ůߦ.|I MJî EO"aOA$F5xqFwd;zС.'g|9lrl:vGi2]  @  3#M.;G9BG8\D=h^=k<RDD` _"xKyy+75]*:p#%"t-8j Ri4i><z%LQ>\3pӰv1go cj^\:Hr!i ~ZTװ-(Lr'@ٷU:k+cJ ]8<rɹ#6ȄԘ^c՝gqoTL#6$8=<1c4+?Gx#.\Zs ܟF)Ehs.`.z0ʹQR~m+0._˗Cn5utWv#60蔪Z.0𷵍SFNON#.Ee32J|;MmQ vjxrJ~Ѐpr)c~;woj1sTh#0!RUW}hb ex "|PH[ǧ:J/qDCIuG_z8XzgϗN:ӿxaFӤ̺["ejMۙAA&6)BЁ;XG꧕('ꢵIB(~ a{p5. 55.>#b=]oGTmü#%AP1@zsnl?]ުzxk@2 e*+h۝:wݩΆ~ TJ^9 ͑4hB&#.xbP;Ԣ^}؀K}!I+@=@- )"-a7&#.J+ǢLp#.uڅ,f5(TMB9l05Ւ:sV6z=or,Ư~\ݿmW#Lv#.{PP6).zE{an!@\N*}W3S+Phucm Ӈ]<y'ztW{ڳ`̫- XLm# FuZ侵9S9}8+n^n$dE['}tѵKQm﷓msVhU_@6oOCUsV:JM,tnO伎r'JhϓW* IJTy`,xPL*3(#.s(5.w~>>j; A^ZՊ0Mǿ*vaF7#%g}l-az3ur#.?̶#6!8P-l]EǮZ[RӇºH535qݥ(*SvAkܴiXE%qan_#%Ee.g}H#lk>.ԯ-(2}T|p= m.C:)]ݐy 07aYј Eg'>ELNz1(\xΩ wiYئe0wԇ%<GzZ aFK^Ԝ%zdM5:Rcw{WhOoly$z 0mCgpPh UA| !=yyDzs9UI#I¹#6Wegz8V)tFBXfG|o.l&)pMSӟ5#.2sQ"w"ju5N7}uKfs=}؊z_CdlOr%#|*gsg~D\͈zw~h鬻{xHgP68ߍt[i(NIБSmwts:'?g6 z)=nЯ[J DqD QJh<^gP%mn=3td2ïATd^soRj=\,c-#%óx; WxzX\3] K^G1ժćk{1BϧY]~<%HmƔ2 U\ E\D#6=E̒㶉rǑY%f=M޳KtZ'l88{6TF=E|"#.UGVmϑiY'. n(h\jdWMأGX|^uS& ]^]s}GPWɵ]>~+} "6U =c8j~>?ss"Cqv? N=3 ύDs\=y-&*J[w bDfNHZZJQb|vꚊz(VtAq#.6r#\g},#6NjBp~ÇBY7;O߫* =\Fv̉RMeE\G)Lŭ<];8iƣ ] Y\6g#%U{[.I<:g#6Q.AQ7##.h;!]X0mgݚ19mH_HGYӼ?NwQ:)$/Kyi6+vJEB@ƉPz,miKeAx1_S4`wٍN{PTXPD]*DCEYc^FOz]cpUʏU@,#%pHmUWa*|HΈ}u'2ԤGC9BSu&35h=3m"|\!XP.{yH[BJ+GPS#.O2,,<ӿf;rM`)@9b\m@@@]ئD #_KcR(?#QQpF#%:U#%"m݄#.4(^(EJB녋2 3W\p4J#.q&zB/!<9#.lcwMzME׮#.CEEE#%<shypR#6O}E=8{q! =ܝƢ۩)C?s_]#6T?@-!/MJk覱{Z-@?U빙G-ǟ@{cnLQ-eFAk93xx/4PH Dү z@Ux#%o22iz k$~c5o}RH1 4}K LvJjJ%}AbArQ1oڷX#66H.>q|$T"~$#.YqrA@f#%W@C:Њi&s7&BOCf+uj$ߜzEO$>hntH!#*1".N;]\xVd^ w|gO6;J0&5#%}8Ur/$߅3>x,Cq;@v/#%@]C9Pgr(9 v k$9T4h ~m*8꘻` #%X} lJ^. #.JCVg5t^7!߆ ee#%~zT)AY`N U)>S׋rYXP\!&N0#6zY*Mt tl,tgg2jbON@q7&F)=(({;P'+wV_?tf@nX.3Q%x]]v[kסT4%e^ROkl(FX8g&w!?g#.#Yj~ieu'ݘe;gTLq1 #(#,ptA5+ldw{v偐;mP'@}o=p2H#6O G_0P&J#%TO\jhDpɓêКy9Srx-hvd#.Y|PmS_V?L_"'(PvR6$Z'x=#.A='TP)%}jW\Ҩ3ʡ̸|Z~cF0V(i+t|~!s#.@Zxa%GK1*p'=6qK/ x ҽdRl/UdAr#6+G&InBs\g" _K?Ma{4ÝCɸ~܅zN}˝9)JIƀf<w=,&M!.pVNteزӦp1ИzrŝXw+8Soy1,#.0#йB9:-{~1[r[/{"V*&dqY-.pO5w;- Jff ǻçH|Wɡ!I1eEֿ9AtP_kWHV#6%嬭1^#0)Eg*mŲ VBo^E4yְ. Fz4`(٧W'fJb;:Z;ne,5_==:8*_oz|q5v]Qs.2#6Ӝ~*Eۑ"c|T=<##6`-P#.L6 Ѻ GTt6k^mur%D>ڇQM~p݌,&_AgNa{'##6ʰYȇ#s\-~mɱ*6iI@PUG/G$YYzR0gC~6SP^Ӥ#.zkHmim.3xK~%"+G;Lnc53>pxqd} =ӷU Avrv94>LpgL{t6{<]^ޝ X \ ALo8P6;Ak#.njxmXE- ">/ѣ5`[R6q./#%Pr9TX8H^ѬP~ZS~!<פUO'U0°*!5`Y$D#%<珟 A8yF*$PIe#%Z;d>{/ n\A8*#vw3*5`,r5#./܎4WiZG魟!u&(_qDg3$ga:,M{k!R [#.Mi a6`3"N+bvd뗊ZO9$-]ۖ"fpELiUHEe.CgA6ygÏ]wU })#.A`lAd9j*݆ 渻,h ܣdw.dy r %(seFzeڭX#.KiC'~Z6# sYJaLJ #%39FQ"^D6{PSS3yL'n's*zcT#. 2!r(PhP37nPF;E6#%-}]Rk >t[ڍٯkY,oŝѲZf'̆F"_v8<[8X˫ɝ;ru06fڹ$H__B'sW$m#6E]}#.~j4a٣ Mf \=Jq΂{O jATP}1%"͢1#6HsMǡQ5̽9>uR#}3vagvmZU#qVXH2 #.֛@$7c<!"2. !҇5Lڶ慙`Vb#6#6ϧ(݋a'9#6`4Z >P4i~q\KA!nȂ)YɼL6޼44m3r !Z-*5/!*k VDK;"n[z2b}ֽJ"Xvh{XfbZ辴+Au~Oڰqmބ:Ca,&q\șA̶n<l4(WΔ"o?wzg\.f]%kْt<#.ctgߛr#%s-  mhŻ]N4(Zsc.x*sgi5JI@od/(&_XCZOV?0d<֑ĉ0斕;bzUKd3 ʗ h@ـQmc`Y N18ؔ+n5#F]"RO]WQ5#./٪~a#% (-+Gc`|^D]Ĝ#6El\q`!z I<)MG~oxE߿F'Qn#%xTL9-4Oxp8Yx@o\DNuWL7COZ#6iY|VA]&[_5i>sE/#6%5/N(l9s7hgTHYg}_A0VSNĂHQ'U GJB3qdZ gЇqq׋_a8#%I~Jڞ796`|6/ Yrh ]3K⻳|3|cuyb3f Cx-X3PXt?Q L#628~s;{|%UVv,pgYT*Br)S0ep>V_b?BrC9%_$TIUPYEZAa#^ؔX81#%h] 0;5 gYv|vvގc˫_wNxA3Caڗ :]۸xn4{3 HYTBTCal<;`xG{ڝ{a{.A4A#.P=+Y;]N5%х\K59=} FB t}:$A7߾]M D|_*U./,!nP=`d_&\B7d{ A qP#%)Xu##\>G+[q7b<*w=OGH|=NA LRzH?yCԔV>!0н^.46#.?ipv7jѠX%.S7pMb64yCaΚ#.̷Qv &P<#6vT} XҥW`_ }9w[wmZ?C` t{s?OqVȨ7R.Ȓ(H0p#%o{x-)F,=ٲ"Ý̄䗰9Nw.icvL0*z}pAZsmy^RONY GT%X|B54 T(X# ee=]H#%wZH_6lw0%=i'`w%#{;r6,#.,ćzdx`9pCYTe*iD-& H<>Rg#65Y$?.xRR=TU}@1ME~ kmmG&Fefj~GM2YAwY #%y Mo#̉lB@"$e |5:IF~"->x"#.?#6[p4* *D usmos!fM*fo5йdƿ=1Vmk86l\gbݒK!UU;I#6lJf86#uoz <:YhyfΙSlK6d.SA~!#6|N3;Oy K~İSf=#.vd?IU6[~@@X#6`/{> #6Q@lΦ\)%P7`ׅC.up`gV}|F1bcC2˜0}"Ao#. '#%h&F7yG2M:D(3_}HlqE#.2+ϭM039!F9-E V#6 `dxk #%O-=fq/ʃ*`k|X}y^E!l<FDYA TD )#3S :iq`wr$+">,q$DL#63kNk^!rCi 93C<2.#.Y$࠸oXw^#%rUvA+RWVoqvތz$ceXj1-.Xf]dmffT! }Ŵ5N*nl+CEגI$5LT˭MIikmjzef׌: '^kAַfuGV:eA)2gY(:#6X#64YH0 AP~_bjTm2sxqnaX;ڟ%tG^^Ȟ˼I~}~ռQR}q#%!>^Cwl9;6Iݷ;d]d>a؈ZjH M)QlЪVY$ rPRm82d?ۍc;xzZU#AIE5&Wf^%S-5Rkӄ]Z̿19l=1IAJ~uE4+.^%6 fg -ԦɽI"H~WN3v7()sp*K;϶3dc_dDv~Q![~1mؾ!t)lH3+:K<QRR"LٰLsMw` ieB@0bRK:Rd|~n|WnG ݽ}8_<'aVQ5o\W^0'h72u%Fm f0/i@P.b+?NTPDs#%>!@Y68x\ż.?-Q*DjIa-zRϢ*%Q<r0}y,z >[v`#.&2(Ab:69v`9_>ʧ86x'7u'{;HA5twPI(?xyX"DN}+ysFY3m;0#6J#%aAD<Y( .pWapls8?~3 PGpC :3yC?P/id+?GɬV7LUQX4=2ݖ|* kCԦjI'> 5UA4d>/ЯHWb)Z]CZ{_\0GLrupZG}6\ҍT᠞20Uʒ8MA#.7; {tc$:F,!::`bq esdQPNJC2U Z*>RQИeLHc;vbNTmT&tKPtUH%HYW>nC-mur6E+n}|=i'2)E^S<a$Ed@$ZЫF)76,BĤJ(~'QvVbJN|=<OZ'Ih<ް#%`s#.dU6c-3#%M_/_k/qcu{??#%#49'J<JĆÔ5m᥈8`:m _ \ͩRJJQkuOI'l~3Gb=hj=u$!Lw!(TCD~Cϡ\ৠTT!S䧊r>2Y"C}iJ;).ٹ h_籄w!,#6|N}C=)BBh"3애kE,e'?`v\>(}M|)k`a]˕aEh#F%LV#%I0% Z\Bah%R2`lHvE6(\Dd󈸂HH"8ERG5֠AC D55-~}#.>(#.<1Z*'Qbr#%*I}kOH~)۴LAGQbY()9gb4 G~s3ܓfHz3J`V߶P+_>q#64X̢3|ͯrҸn[]0Pmy]<(hW?f3M A};`%f #%'YH[#hqk6CH&j Y1#(#.DB2`JhLE(Ҏ*XZO/';+ÓxBJ<9/;3^-i^C#6@գwܟ}RN5 7n9D#%pHo( u}J6+pi:<s[cے. 49 <<gۃ; =a]l#.)#6(߽d`~YIIRJ9\8#66zC|>4~1؊&IlLatɆ,#Z4|kf_ K\ U0$UOqHhG:#Y9!|r=H͊@p2 B#t*KHٷZ<C$"4?}z3ԟx'M'o#%;)B0o%dL,6f(ߤJu.[<r!JS_#6 #%PC2??fY1?I#6z:h"Vwg#6 )ʏT0=iڠώBi0“% gp c%U!11*D\?S:PzΟ æ˗HJMRd1?;c.<b;nN[ ݞ{Cz!R. S#6 =,>zDZN􁷕NV2K.*~ GX5!/KۅةӀyzK*ü`An߉l/g*|[BUBP 4ń8'Pf,͊dď1k;Ծ#ՄQRQ{mzIIJ0ĉÖd1 *0%v_η?nTt#l1绸UgP=`*II8'ʨFP A_NAJ`}ÿ|/O>9X<|z`7VtdH^mQkKH.pDGb\3>:ZA*=^W< e|9 p]-hẙN{qbB:8jAK F#6#}띸O`yJ <÷ ]ȐJʪ%[9_;#.H.҂POܶܒQμ8*ex _X(A=I\,R <nn3'_gUJW(X7><? =>cp`Av}ϰO"<R{8r?OsoS~4j0g+d(Z@A}lrޟ̤]אݮ#%5I#%M$ee5hPm؇5oLpxoJ#.0I ,6BXZPw*o6`>Ϲ#.,~謙!" "? `]Q{jNo.˾0,bd%pAk:.zh*;ϻk'u9V/N|%bdϡ?*=ym+Pw!d#.[*p aln7^9Lйp4#6}>|{Vu-M)mvnEQ3t6nٺn]C9Qp]q#.v˴[Q̚DF-hja^BVͶl؁#%PXV0PPӮ: nzgxDeb>yk>z{h}s1AdH>fX}Ծ͞#8&`w6pFBBM#6I#6~랠H7V%/xn=@E=jQQEV$[jUUD:z⤊^*eVؔ!&&rl|D\Xi?Xi%x< zfSo[֨ s4'cyPvS0"# ͑~{D_*9ոS^t(Mg#r=})NVA>?e P/#.Yl{d+V=p0tԹ20{Qnwv8Kۨzk02#%#.qTK뇱+WUшxɰ *(D!Q4HT,P@X$Y5eHr;lllWTKw{ZĚ?N=g\31Il݃'0`+m@#.Ycy#/#%B._0j%/9Qr#6W=r(#6`1M3-Sm)s [!DTLYߨ虎hq9"T"#6+AᩅqڟYfg (kf:،C#.XaŨklmYq3~73.%Q: v-r 0#6lH`}gtL|~Gnv<! #.wʟ,ð*U0BA_Kҡ(I:*M?ͮ5=N}N\׳&?zФY-*~#%S Qk#%Zz#%W;τm`%#%&U1~{qT{,eM"b!֟͏"qzN|w1eɻD@ y#6#2;₾%2zTXA#%28*5@'nlV#6~\bHQ^螿,[>G;H=iPJޝb$ L.`4m(4^?bS*dp7fM ?A7D$˗t̛d>o#%d*6<<=͸_yaa Lf9mxjt3]]~N.5U4K.wͅGQފi}kCe.Z8=6tYr#6%7"sPDL1ܱ^'y=;*Slbl{*R{ \2@kPᬅDG#6@<0Gz|~HzNcG]bcy҅!!?o?TQ)ލQ6_7ۃ#.ڊp7?v~AJ'b,z@^@q(M9T*.vmº9ψY t"&;:<݇L0{R/$3#6?>_u,6捚slsl˩0躨sG#.uggmPkl$XEvQ ?[IGGHNwS -\x9s{`J:^B4?zf)#.!a1K2_[{lAnLL#6#. q͡S'[oGf(`{Jw$Ru:g<] 3dCp֓48óe2vw]L0Wk8h[, {}/l\_GH#6^'l#6 .rw 0~->]sws&e]]234ٗUt[mjp9-@Ab#6AklԕoTq4g3,Θw<v/ñ3#639ߊ\a 7g< I$ #%!|_1w痣6t3k'6e>1~&#+Uܿ}.ƳӦK7wMTD_Ur{RniU!ȸ>mi?]sy:#63P[~O.p?h^Uf| 7 DN&6iQ-9z-bw{K4d@uL:؀r}Ӷ8\4ݜ&dOxtՊ1oSt}FZ|iw\E^q.y߳#Fכcr 1J?yU`< z 9=(V#%ۻ#%Y]NIr+0Շ#%)aN^/X#$ 2OaA~eoa#%aNͿGη8$Ks{tJ^eQ(L̐#.!~=.gm iij]bM6E@v`waY<<;%3!.I353差N (냝6c9vD[4;ȘQT 4ƒR.aV&BկNhFz *QHj$fU"67U<yb ןb T#.ZdK9̑?TRq߯:YEzhO'}ߏϯ!hRg8_+v4xD]g#.ZgP};lȰIKlpHN#?bp~#%$!!#%ܕUl?zSl]~aĭɘk jk#6v]#.HIH{?;uM·YMQ[qɃ[N&unWx0籈p4-P\I7\܂Bǽ9t<#%<3#.2sk!(*]A+hX™?v)i0h~~[v[a,͐wMN0|<Д'Q{jW[ 9`MP (y9qEXs!GWQ-g4'io/R PkȎB;"ks_}5fcfS.냀2 `DSI0&b1"J5ih>fRHNs8 FeqWzd5օ!;x>eeB-!؝m0M."7Nb>}X3:N7[Saߦ#Hwo̳۴!;Z-"zbS #6ՒAApieTA @h=Bˌf?5q&}rd^W?k{K5,_{/+Yt1Q!%*d`ڟ@R9AF"ݷf۞Pw|}h {g =׷HER!A˴,?=ȪDy_7x<,+.ehX؉+VBG=݊V =R7.rT'(fhV7ʑsWВ&QN!MpqZ%dn;'%1LhUQ#6l}i "e<B|3<<tR`6&vv y8`pBDdkOVR\:+;#sLŀx rk.̇xԩ4gu"0!we*t5:&e%Ds.5Ĭ^r)g[b.;a3X8:^ӷ&s^wG$f>Ww}wλw̸Ŷ՚Y$6a74E#v8omfC'amH|M4!vk!VB: ]&@[1#.s3/MC4X#6E)n؝'a[\f@n|?_wݨϦYl2K?*#%a8xI׾=sArfA#6 GMo#.F*wQT`7F4kA#.20hXTZߴb =H<XoA#.!rqg#%[:ylTsT#q`Z͓^!|Bl=1#8έ$'`Q.%+&{zy5 my,T6$pd(Q13(%&ŤBlaHr#.cΝ<Bɤ޻[ZPXwouM̪Li]C0=Xu]$8d;u!@0PTaxofvfWl׀ GઈQp@MqG{t&Sl* z39=Ny"w'Uq5v/24@l'lȳw@'gyvkҢ d̈́4Υ|#65tM]<#6 i5{WB<ᤜ16; D`#6ɈN^DYuQXhgLm2xNKaʣR+q (X8LoEh.JhBHN_C]Y.u-,\@ +ǯ53,UeǙ2ffX12%X<cjl$0n&z 'qQ*WFz>7w&C›9#%!zlP?V[=s֙&w"TK=k9zpm)i֚3=FChYCS8G+}95Gsid%(#6nBihIwP%,h ^4{w+#6]nԍJ,!25.!bΒ[sHƶg6|kP奕e#.dӒCG)AkotcC~[^#%DBI(׼U6U;}= |#%֫լ7:UC#6S;\6FjU*F7`rtXȂ@v^G1)^4N)LvY`']L5m3}Y5B@- F&)l۵m,r,aH}3V%O2(J(Hk2fdqDy"N5۹]#.70 :06쪻BR$cHJ$tU&WD9ς*!b<B#.#.EFyhaE 38a-'/JZ*Vi4`hRq!]#6Ρ0Qe._B"ɥ#:+R*Fq=r@_9!/MHYLӑgq.t=v7]Xu&I(Cyj4K d;Ģڅ#.J$E1Φ1dk[&ᨡrGX!"|&4уIr`<4MkWg7&6nS./ްF2N;@Df⓻U%Nn_~L DHf |oRo85IF+:5_`d{uȦ\\g#_-7{"#YCi#.W7R!E*.I#6wעp$Nj$#.xy|FPLZ#.(̗nm&C#.F"뼡%z9jě60oHCǚ7L!3 0q#%;{[@o $VI6-p%A<O'PMu#%I|E>8qD-0Ƙ:=&-OgƠ]+<GHk%٢*>KM>i#.$w_ܟь$g\W3nWVGMOUy#.Y7#.ﷻ=h=BRg_t=V[(@2N. c !H>((Q67*=eHor.#6\HzyC?eBVK<`PA>B*!>y5T.(f(ε<Uv|Gzl!g"N`~aCD#6F [ۺ7n6R[ P!PE.x5 #.+i} 2AAw R@HA)Ң+4#6 fkك:oNq$$$byp$@( ާpm`<lr#.YW,$ ]CP;?8Dv#6#bB-h I{ĐB"@|\*X4GZA#.ʛÙx{i#.Pd ##%=A`|x_ CR4ԣ`$kB1K#6Xƨ1tIʝ{m|#%P580.HD[ꏒ{-ύ0~6#.ۜuHKȬ;+(0#6A3%_\$͕>&@!h!7#%#.[]쁸F/5"i^Udwn\q/ p<` b鷤5e6}cj墩i,AE`d;;` U4W٬c z9$u[}nS(G" uM#%²/lM` 3  IBŅܣPb 0*z*RIzM7Mv%^uy;%ewh䉽#.Eh% Ԧ>6|,}l@$~#%=$.4eEY $$g񝁸|o'&_xre{ڃq#/9xsLXDo7 zF%A\Gvg}hKmBx#6I!ybh*O{hV[sU>2f00$Yӑ·+?㐖#%OP?UHjgy诨f#. rk,CǕs(ldXT #.-T``0f( C.h_ <rXz.[9WlD~++yKD5ۭ=氇_uZIt i!,h~aC#.vߓ!@#.W8G䴮M[q'5cyΉ\sU1,;n)ȣDl{#6ERNϤ:oNEbz5RnJ\E k}Gp05+"Nz|JNqz$Mk/Hx8S{Qy"؛}+<1#k*h()de1*##{<#6B~0PPaVd6jƒlJV++-MZI[[YQ @AdD8ZUG]WHs"zdH2#% ug"V7Ձ6?e늣5 hXdi#.,_*i{W BI*@<&k(}HtM#.%hkI/ҲfIrw*(w*RZ(x՘N{up&ݨ$[j]8lJ*ʽmuA}C˕##%QM: *$g3tBE P'H!-Ej+ܦl)S(f#.EI)Oh!&8@#% HAdANUԣ>|zՋr7Hda&fA=/Qywv) #%H}0!!?o6*9}H $g/b5B^! i >ЌhFD"#誨d|M׽NV2]Q5J:4x(.QHMn]E>,xC6th/ɏ{hDh8_ē+A#.Zb i0"$qݻ1;nxIx5S2#.Ou *}9,1KE垭')#%B H싶0=I;B q@li8"d3!x#.AR:pxԁ$S'CߣslQap:rS^uz놳dT$֏` >D=N3%cxIʼiă=I#.-3}r#%4vvu#%NvnBq Y(lSiLc&b3Ox#.k#6P2C+LjAX9pqfzblS-&+XRka\rnj`X#% ⒢ ;xh}6u:1[w~ ?7(iHPjj4QPuQt?_bɱ*w(QaDM Ebyf:e8C8PX{il[I~#6!OLc욾@#9OVvøX79#@AV/5MP40#.Bgk7ÿ^ο}(=c~'!Dv^#6@ZXz}fT.R>ޙSڶ8cs(:>z ,>c1t*UGL c3Zo޵㥷;WecpqF;jt,٤m*a\|k/оhDP$S x7m"[lmp`h8x}7:דlAÐ|v#6w;60n1ljm>;Qcn$[9ڌ5)܋QaH$h8f{xl._+bsaצ#%YbdSdƁ#.ps{Il0R&A#6BHjPA)HTL3 #%!+RϢVAHJcv>8}h2|!#%;v+"x%c@x@ 8`ŶoS'+96*v񑭚l6ywP/fW #.W# dn;vFl[ w6%9MpZdK[?i`WXAԄ}{n#.eA=B: h#$R"!>|8f#.(SܾcC|6RGdL}=H)G&r 0zs:wd-㡧"$3!~ I$߿4!#( ] kl1v.ŽsϺ&]CjJm7ڴAh)(rVTu55#h~=Yk"N(DK%OԎLzJ"^q\ѕX0&aŐ?ճhFE5|eܼ_ '_&P5' ;`VSS *Us43d\I]Mښ[]TR5 0`X%JBi)dpnDpfݳ XVh55fʑE$MꝠ!ģc#6l1gk~cginu[P #6+dmJO}g#%#%C#6t|lv+" S)Dr 4I#gmSPp㲵maآumw`WvC0_#%bn*F[fUۦ<ސNHON'H٥#6#.meQFdQB$J`D*XR AiPbB>&'5mSaP#.3U_\ƃnXڀItAjrir%^[p!1XZڧTYfK`^B:"^%GP߭:D-nD\iK6!E$H#%;+:(gw'`v2OAڡbb$(cB6H &:[Bn6LY&ooagZ4S$.v}^Gwx?LD(:lڣ|^ys* oo{o)4"E֊"gьf:JM.8g8$Ie7!F9u VWTvgN^ׁt) c=#j7kgKm&ܹ5.Sɸk4 JApN:leƭm2L?]YءªCC<sLTCvA />!!hl5\ЙJ\ ץ dHd43d=BH#pK5Qz 5Zt%^EA@@Ȝ_e{> < w+s, "5#%BPiu+554o.#*H)BF`Ș`Yzɘj8M!KI} A#.``(3Uԍ&MlUj0%ԥ-_烞;^Q=\)I#6wH`|FmA;8ck{ {44 Jܙπ}LPCyBMdYbyOĒ䵒~ɹ#.C"I<;Ջ{Finl~}|B{F d#6!!1134ct'HC-&#6hvWqDAt@QD [! Rs't-T#%g雕*YKtF"(UV#60&(&T0}zOQj#%Nn(VMBs bLcC@!.t1'?]& i(DyZ*BejMdFFtHF}Z0"'?h;dYC*;C PCt_#%2NdH<%qHMI\u ΠLĦpM<#.8%fLQ&ј\*01xaj;{<(pю =Hy!wD0ǕAc&b.5sSI1j2EH,k=2E (ʽ :9ZkO<שMF!Lc`03s@}YN#6late<p0J"DzyCr6#%=l+j0 jO|f>-=q:¿ע1 ^P\aaOr@^u4} xc4uXgds;",_MltKV7Lš8 9T(5TH p_;KeYJ9-UAXI.#.#b lImUdA)jѹMF4mj!6I&̓&1>!m.[R>]]+.]xjaJ-#.#6%dAafȭ[&fVI.۹9ML5##|[Au_kA:QB3e/F ws+-+p.QS&h%mKO֨fö Ҋ:yG`$F"|hP;#.aH4K_k@ip[66y3xpBFfL[6knk׫TݥʗN 2I+Fmk>)4+.b$馬˶?mS>^O㍳*0h'~;j0պ5-NeF9HGIr,=4!{n8n ~:ggЃ`-@T/?D/اB !DDE%#.&3R60ܠu*:h$M4h@I&M/WX(,fh#6l#%S`X nyzڹiRJkVr2L7krEERJɷwXU :c7weڊ(ALoܝX-=1&XL9! }IǾ\y$WG~E4mS_ G#%svYBog?~A%*jڹۈ,#6ѵTjն5mͶ-V6vC.'Q E5bG9;#.Y4(u;kۜ+4ֿD4$S&e3J,)I)5EAΤZ1Q4ԡ"P~ܡZLY%[4)4̑hEQCMDFY)PT%ңELFhL)T&%!MAALcH`4S&ge/#6b|[|X&A~lU_SBݘf̈;708Y!,mfvs9` 8#.4czKj\ 0]iټ (5zmn.֛,mDs0xxN;Z-_K# }YJһ0te^fw~A{*񌎃bm d Z^o_bc5#fV*(i`!Fa><90ݽX #6"tXa3ٟyNUMaYb†+NbC 4}0_[/cyOW}#k#6xn4O3=GQP+|ɠV_fw&}z^ p{"r>MG]eUY#.q E)AT$/rdǥoK}eG2XiK􈣬%x4mcǗ$I*#18--ߣ8#+gǏd C֓Im{N`g"PREΨ)xcB?+#6cguVˬ!EPӊ#6+G0'#66[G8 pI5,nm_&ɂ2Wc.["^7L<1YCu-4'ѯ$41 g OS䇽^RA虚O?M" CqdNIDBxK'٪ յގgd6E)Fm2Ib=Oi,(5X@\{w7g FHC:ڷש']z vBx}I\wvϷӖ_? 畧ЪX@w ߜ5#6)~.wzD nGkBkjl8gm]%}; f.IG^fPs3 j5'B;Cq #6;뀛<r &\?#EC8(p+x#.j_N9ph>?/>50Y7Q6ȣ0Ħa iFELŒu530W>ًө<4̵<3t,ņq4hQSRJb5D(EVL›WUޜŵ (%?Kľ$.1^{S{eL:"a *cuo\ a\MLhzIƴlV#6'S;ٌ;WGv8 acg9t!^ Ѵd5+6 5#.,0&bNJ:~g]`ɐ/,7#6R[&*_4To#.?02!n6#.ΩсĐ4&:JF9]Ѥ]Ҭuay:0j2w67LL$g}n@upB텴ց=,Y?9@íRPɬ@ZlAs;oNqbg6fl-&<F!OB|VvDDr/,ڋz"VnfrgSqH·:Ӻ6=V_-eǮƱmOt)0ej=1F1WVգi7)Rw_<0^Pd>llg:5%GQa3:;1d9LJn5͚']ߗQt\P##.'yJX5\Fָ2L/F.ZX]G(jS }5#6jVͶ]*XTqdFSBc\ll^s9*N띡,ߞ]̕O,K+Thu(v[=eƙCAib uS"q0V^ ZkkO+gX]Ne6_y#nukct9)Y69Znl8\e#A#M|fS*CP7vBEVU;"֓X.\[DY\('>;8ghh:yYz&YNڋ0,iQ!XlhpNVNځ]#. H.q:RIFȡb<Pԙ)>4=#6YE&)+a0[3A@bXQ*1X1qIN~.2ߩh7)nepOVRQ9B&P7sinc7ۃuU6vublwsaevfcy59ݬe|[u`z ۙ L>8O/ R=:%)ǒ:{ZQF72f2"t[LLLb|ZFcE8S7GQgrܨ;翩1W i#\Z%pUX6s2B9H4q:jn21 O)&PmV\[lf5,yULb0vB&ҪCr# SH^i!ʻI>v8Y٪WaEZNχ+a'gj0U☁rӊ*L#6$ 3v{"aN+(0e#6Bu R`hÀp PI,#. fF#6ڰڸcM_{Rƣe\1f2R;Zaf%ñb"zͷ9V2#.#@DEuBI@as#]hP h}uz1U tͮNzzOQ(TwqhR`pZPL;7CN]"̎̀#6.s׫ZÒ4' ^le,Tmt 쉌SR 3 @0ty\)&#.:b8!K@]Nc4܂@re53Al2ȈbiA-RrvֺS%#6# i"23SdtvNI-"S#.['2`Rqf d5eR#.XsL%qiL§n2c*<.G (Ncim T&pL*[#%X*tvmB $I3/ObYDw\#.dx/#.Ӊ\l#6e47U3}/lpNZ)jx#uMXNRҬΠ|`5`p3Pf8&7Pn°j;'CS_&ӌPiI+]#%OD,ze̲9i7R#6tS0@eusǁM׉#6K5BE !UAI HX.b004ڐb;G.NGIF1T*N_;QwWm3oA8TX$GaJtކ`g N }[aYIB5R[#%l8q+Cȡ#.F+2fis l3YHu#.Iawmw#6naqʌW;eU 88JD61C#%CGf1g$NɆ#.f i q0#. ( hLJII4dpjri3f̺Eِ9Fm0jJG8gt^Rg~V@.uXil4I%lK900[Xn&uA#%0IF %#6qC$R5@w;iSh#┈1A6*?g|z(W*I"H#$d#%|j?3$䈌@q::7HhuF@&4TC8 #%-=k<~X$*vʪL>Jhi/'س7RM߬YEr,;B_}P4&A\s'M.iRk5Bh7n20+a7u% exnX!Ս#%c=^4pu\0R pBev#6h"Jk[m&Rj*ᚈPRqX1H_D#.o=#6JABp5㊡\C$;8SoW4U*#%nnR 6._SWwUٍn.nn-sd5<Fȵ*}A<`PLv*r8#6#%|(u/qz0g-Z" ({'@7>GԻo?62.0V<?|~ r=FwEM 44,;Si#67Pon Fbh1D) S@n0.֔.eA2Aczf$YЂQ#.cG3!:?+x˖%A.9FKP?V?_Ui&RkFQ[FIDL`3-lX|Z0POҴTR᳧zK:k5vz4D+Z"kDFI0#%D #EV-ރ xfPD`@[΋I3_k̹< Պ~Qb*Hj}Ja_x;w$l[^A4Ldpb7N(cKi"F'f5j]*MKoB5L1fR@l@E`+KD#0QR&lc8B+ Hy˱JEHq @QUo#.;eK'(z#.sdd%$Ϗ&ٻ#.PԅHT]c(iEj#.^&АZHp08W.cc#%m LNLuY^sŲa#%=wKbmlc+A)aLDH -5Iq0]i> k@ /_m!*  I, ݏ;dD_&,ŠúMʊ6 {8Q`oUNJ## qe]gRrE0;>/#:>mcz"4U#. 2]l0jiiZ}kw 3FCzi!Kbg:'5bCVmc{^ZH8 ˿+#%ӾZ AI4z^ym'Gi9E$P%* UeYNB1B)#669sypY.ݘUfylMxkDilʋ]ѠC|F1#6h7nbC#%ktE.J ԘDbTJ Q$أJ#.L@c#%-P$PR[ t`luqlgL #6?@ B<5 ;+>ER+ɟzM8Qb{(=G+t ~L>WT/k <1GA|A]fs10#!G"dFH"bƨɶZM "BC5 ?=xT\KJ [ikE/k|tu+W n.rr$E#.{:iY%leʵS0:2KYk8ijm&&dv5-zbScA##6pH.KӃƶ>-IS1d<SKۦ3ے$iȉ3̸jMsy@#Dmã(ьjuN&Hmr=fWN5凍˭i>S)1jnI$C#:y>*iyXzMp\J}e)3&=&B"75+cdrpQ M=W0 arr1@tXEDLX%ѵeB̴˕kU\^y^ƺi ,sKB4!_hRM:#RD͔Dkm^| U"hI1<jY*dK 퍃0]#6j&nu&\ɖ*lLi[c]hTe?HÅ(ѳRme-U$f5S5ԵҴ5SUF6^xME *fVЕl҄T7ytccQթ`IPQ6@bQQPX!+D='qz.kWaK9S@6yO;ngq Oz,EqhKS-#%DFOXť*jXɪm'O#;LMl1JS "[KRU(J01!UЊa#%j#%)"n͸esf:fsp#%H5.Gf` #6 2މeRDT5yk#.#.=A4\Twm?2sO^nU \#%ruJo%ŀ AS(L!$yB`w?6lx3ݟbL8o\rEDS,`&Q7ht{+on/1~bx#%.jޡ ~0Tƹlod8YG|fQ0*mʂ< h%j6E(eQMmr,kMc)m&6-")Y,JU,g]aM)l1&FڦMӊ+"+ }^ݻ*b(2#6em)IkjhƥWƯ~l-cdHen]iheRMKmMl3#6ilMKlcn͖;R2hkRW5xؔĪFk[L7LUqTǎR,^?/>w>Bms'`Oe3$SF<XqIFzTX@񘅪m_NW{<{1MEC=#%.b3#E $T B 6أ^S "-J٦m鶱oK5soה#6QdFAR*fSJl)iJ),E֖FR3E E&KmiiY5Қ)R|V (-EeQ-%(JHQ)M"jLѰ1ba+e2IdhXԚj#6RT Y#jQ#.M !BJLX)2 IJjƨ$mE,Lړ$6֥ɓFқlZkVҲMԆM6)Vf6{*zV-RmFդJ*mmZc^6g]! n5x{5`fpbz7WkN-WmE<!#%yFoDz$>Xq⢵E^\$1ga>kg]7b٠3mez4:.L@ݤ"+Ļ/7_K3=n9=C#6?qő@P \cha! )]28Ƙ (W_@p$ -΄>lsu)P;+:lP .3уILFz%;8mfЫԬov.yg PM"~}b\2e؁WC|sл\Lu#68TT>KtJZ~VPv Hdְ# = s~OlqGF#6ƺj76Wi0#._}/[onӀ @#%#%ljʲN"Q@R+r'8#6o("2! Dxbu^U=_v҅Xd[&ya&\TCA!$*c#. SL`OI.!&*xVz@]7=v#.W]{|)LcܚP fsD\Ox#6((S)b#RdA] %@gaZeF6HF&cD2L#l#6h{C5U.m H)fu'6ơXVFA4^Zj vg6& b SRF[K#6@QI&#6L*cR⑿JT#.t#%6:^tqs(Q jQhXzA;Pl|߶4" JLhJX`8ŤM5k"k;CXxj/z/#P|4L`Zh-AXAKۭ[E9b$>#%0 =V.nkm>s gźSi{HԷt0Jc*$L¼Z$!$wI;tnl -i!I#.njRYVTʁ[lc0XlA4(F/nOdʃte&YA16a2e8Qv!h+br@Å/))iQO9&B>|ɭp*YKmji!:I$mDVCCpPc&P1ssgٙ .!H #6PR3xQȵxǙcQ:Ts hi5JKF'3GÜysKM:]` 2UKՕJ̫ZN7~R(: @"ȡQffđiDThA~~N>TPQpGǚp󠥀CaFE<Q#%,2䰮G&9Ť;1AuȌh)`kND =#.Áq [tcv#.iLjD.1V&q"J5`̆/:lF#6ROQz#%=;No+fy*wЙF|"TXBSM4DB *rJZeE@rfP\/Q#%(tWx.ǨP QՂBUN#("X2#%LFM`'fT(#.2p.=Zpv3{raY &&4G] :\cl,u/ xanG$46PDkIt2v+,u<xϹem "#%$l`1R{nz3D2ә#.Şd߆nh/!ݭ]n-:+~A8uN35P pCm$/R4,í1=;Pp'#.> UGLc0u{2=A+('_HCX=DmqzEn$ON-ˡe#6SY}Y#6I %ݣH?]$E/]j˼nnșHNZj%mjRC@mQc^:wn) 1K)y#6QҐ4l߷w7D9;nբꙏtN(5IrB̩[q_SRgs%_ƗM~%Db+)'hwJM05YFqt "懲:ʭ4ѴLZQM4jW]6rbK$ج[FߝVbVkJ*K-k嬳oki[V*rtt7ĸe h 'kг)K ]e"JJPB7&r2(-o)<( 7 uh%,3al! hv>3]l4}X(qOtT5(i(#8+!iH@aS*VzPT*dWfTp9P5tQꪅE#5v"IUl#%&+t00pF`&,܏}Cpt,VpCA2 o|'Pg=b,q B=cik=A~x#6gLqˀ.H\h]I1#%%扨&?dI~;#.a aQhFpA24 Mj-F?=+Rl~C~$4K^n&߃By(`=4x(C~A@TTвa81#6{B|<Lu߻/6;pt6STSbGzCw.;=#%d#%RI #6J*#6DYX@PPflY!xEdBD b[3\0_aˏq+bIbNQ~0Gq9eSQ~j DP$SҞԣCN/PlV_UAd: C[!ak%E IH^EWc|s RgńX!`≫r'P;OybPWG>'`y~6ߝ=a,rtԟHT̉I9VTSRT IPцj,TU#63SE,#6@I#6e,%ZeC@Q>9y_-6#Dca]<>Zcm\g:F0 0#B &DeıGf4'F4҄qIlb-LQX(J*Ef-H#.Ԓa4J@f@ I2Pْbb$]LR`MS$ ZLibm-B1+=B`HVZMF`L%mV>L䱼xaEXVÆު AF4gJZ,jj0'6Ѵ\6rܾ#.c^.4<-14\3lApjӴK&D`:3k0,$iKbc,PyA5&ΨhQ.H5@k[R5O<#.C:Q6%e{]N! uP2h|imO:4Mix,#>c!Imwe7Y q1 +p8#.ߣ.H( MT?9 0`1 8A%Q#FljTj,TIn0Qr]JƬQmDbd@b -eAn*Z$A$TɸC )ȰD@QHXHEǼ?X#.Gꂤ@Yg¬{S(kf!W]n[erZO¡.TPI&#uV1 Ng["ALlBPoTBDWUZP4mdJT淍V̈+XlTzŊ껪-zjב/]n\|ךZƯ7uY$թ3JA@G2<㥩X|!kkE{\5pʑE.I#%}3@ ,e rIcQ4״0NK2#.#% 0#q MnXq[l'}@a%ǿ3š74&iG<!DWbUTU˛[H=TvU hZ7aqP׈0a̰oˎu> ECObȀ(|Md=` T6qr.;Q$!a^ ҥ#.-TiKJTcj׼Uk)WQE%D/9@5];c{nJ*dBda6'.vFqpFCN3BKQ4]#6Ӌ#.g8bCְ :P&&42iPZ0#%G{" M18=ɉG`‡+h}*M b]OCXdI"BMQQ<nF^Ϻ!}z`ûdW#" %54I7L03'|@YA#6?KmɃ:Mlp(̓V "ԧVl@,jnF@$+RM6SHDB0 D#PN5X4O4D1͗='155{:eQi`K;41$еcF^#=ΤA>iTSMZʸk2P|T5PajJLi֌e5tA&2jܶe-,`LKll6Lm$J"3f4M5MUQli$1H$#.GiGL"8lƉ)D MVVŮkvo^aQoѴצA'"vC#%#%FȍD*TK[R]KUm~B #K=Q_fHEQ GZ#>adzApQٸhB9!OGk!' %J؁!NHl`G>B zAm#%܂%KtDByxq;8<I#% CPŌГyzԴ!7$=6*a x2^30xp#6XXPȅp>WU8;g&Lx;Q7VZ֐K6x#6ءp[1v*@7̨ݣB8#. \;:hzp!a#.DV#mbL(4t!#6IO;NϔRb.-+?VF0V~}мmFiAQ:ĕ#%>#&!-ctL!/ٯ9vWԐr[ 9+S:~rt&-twL ,S4]P云C#6(,HQXP9x諒#%2O&ѬH8ՖY!D14P)#.h4L*v#6HLMIJB#.X11"lN9anTGd#6숒6%  `6Ĵwc&i︷lC#.@6dZHfhU)%E`҉c JO'T*cmhF&(}q#%DB;sR; STyzg?~<s/T #.\/l:o\qF(P8' J*ƛ%S -l;wDÑ@P@4B PEBȔPe'#6zxvi=>~ Qu}&`]|N^@UBfUЪ`EZ}MI#GG$=;}%&Zi]4ff٫ECnBvP`Cqh\5&+&WsLjF|fw{rTP#6`R#.!%r)(#6a<,<[މK:Md.!Y;0UQTY"$D0BR0!D.bo#]̚T`ѱ".)drf5"^ ih%|#6eOuBȩPW.T[#.i?L#%Ez#636~*(4ͻCVn=辻VHfmڨaYe֔ je 5VWE[$FBNy3, /Xp 8gdp:>j"O=HEeh ˋgؤHޒ%?^uFVmTSĺ+njB#69Br,81rsU:bkg:܋|Ʋ:#6K%$,`#%*#%A DPȊҥh#I n1l+p<;T) ٿ:Uyne#6eǷVQ12Q#%dd!CBbuE!Gv#6($U`ѽsqKѣv\5 H%A#%Ν|:LOBŜN#64/ LdMr5wmJIǒBHT*F2D#6 _J(-!n֐wrPl W1`Gnb bFϸ:64kR&'&{_)wV7t\-25bB<* \KlH2Ym@&"-Pf{n{G.z&#.zP1`WP2O!-0ȔfFD$ #fK0#.`mZhmmؕX"bͲ9Pí( XmG0.M쀿˜iLI!1b;EP DSqScvw'祥U<zNu 'IfbWGq'NP!#%ĐIm^efʍdj)}ʾLU?OǗkە氵0}ސ^xa6<XJ.mڦv"OmՐ}Pr)N=\wˈ(ײ|J+T-`U vI{!WoUx$UYYya<n6I6= q.`vǁ]to +M`sZQ4$n@tp&/mHF1מw߸,bB&3k&72^A#%4߹LLK3Jj >E/A!~,v7ۯ#sz%2tƶBÊ -*F,"s^C^GUG݂!p6m=m#6N0^b!d%]-.JZʝ0O ^!0ןb-sy䃉eSZXKfsM%8Tb\?}{>%a8qgd;1C ) T'd%Bnp>f>yKp#GsmGycQۚ%\U#.$HH0QKACoUУR*s$.?K b\ňaY2J2TX (Vy۹uުZۦ#.1AA( ^3@dntyr=EI&01$ե["'4|lMGQOgC_4wA0CfeEpIGG$끪UtZ0hk>1"՗,W%F;q56z{@@q(yCScp_{J!0:t4QZ$\v庺ͩNݵu&4\^.^]Vl@ #%C@US_Bڔ5)ALjrS9 5TXlzMu>#.3k# j G#. 0nJYUiK:ap<h6`g5!50zp՝H!upS-VMdmL܆6u|#. @{&}vuE'sj>Z2nǁ׬-Wß #%`+ގɬ'm'#.|_uTF#65*oX~\gi[4Gk]9*itQ0#6'wgo`!(RY} _b.5(,DPRSX%DA&?3Y{Z$*mk_z.#mlAA[v,a$v+'C-H4(an~#.šX>7PţFsEӣp&K~iKn<{6gTSE_ϞRI'E,#.mQC^[#6<PiqL =Lo#%{BhҬ܁ls-jg0xAӥgLeXdb`Y)0(ϐQ3I!aÌ:ТIe)Ղ<=c),[I _3r^B8i5V-WBCGcHgMard7O1U;f,1!k q.aP0OvѤg NJO5Ĩwy rm|#%#%p+UC'G j&˖ ![#%BOE+QZ5mdƩ5Y,mb @*TEM25Wn\4"Ԗ* ARUz,.`!i p:6vg7nY;d(])5=:C7-\n}cuWC2mFm CEli 2dS ~(Oy!wF?"5j<CiRPl.@#6%5U=Ѯ%O?#6\p`v㋜NJɆ@;$s]_2BPV⢁0`Cm79FD7|Y&V,[}]t(L:"W.{@,ϬGo (TJs I/#6yfx&qK*.q<L%#6eq}Kw4(#H *EZ#66-5W`[@DFq#. ̪MZPwg{7>Gcc05⻝vo1kp&|OYQ>#.zޓŇ'kjaАTʥl$ Bm=eYb]ع`#%.L?ɧtnu#%N؀L81zp#.nʪKqt/B+5֍kYf2UBCYI MIǵ܄$:¤*uηLw]#jffPh]u<QfnBhTci0x`hpTZJ0`28hJ!ѭS LkLDa6(߃pHW֙%Do!HHIjUM̋6J\23Y2 rdŠ#6&ۤ 3PE ZH Z@ 0V52lfS$e6,3$e-/]˵ri˛rAf[sNݽy"hR3wٛ f輸"FJ2وA\h@4Ecf16#.j*cq0UkH[yIQMҁp&b\3 Pֆ4ս1ΙEnՊzEFI[#6<qTUu5!(db吴 #k7 +6\]\Y"i1 y ic$Ii%ehCVc"sz(T&0D1~/aen)ᙐi3aCwc<j[J\Mb0s.#.֤2E5`il(AGMٷ2ATnܞ]DO3JiAӫ_ݻ8+skh gjF V4.#%cKlTMX,A(SXzG 2#F4Äf#.nd|Pk#zL1fݗd6ro?v:WGƎ.5FcmtWǰ1qƌM!jMVoz5 dvUjhٵ#.\"9lT=C#-q}&0#.7BB]&$,eeUc GO8a) P4/t֛{5E]5fT@& 6:  `"0lU,P]DQ %Ħ tt=An n,8Eiqu#%T#.Pqbe4~(Hg޷f='Oq6y&V?狪+lMHCޱ\#.h#3mK#{{Nˏyj=y\M\lّK*̵Jpuj-kٻqFb!$-ۓټ讹ۄF6q8hPh,C7ふCdqm#MBy|3y!7稺(4QX0w,h#6?!\o/gg^ij#.M,=6ͪ뚷곐xFpS4쨉F,~Jֱr+id_>M#6kfl#6놡){={v R $T@# *%l#%H*GBXtL,:jU(lF `][Iwbםr髵6r%HTwUڱZ+U[[I@%rء#6BKI`W(06nnaI}JX9*ae(Q#P1EZ.pRշӛ͗Zr%&X %Os}2PBTBeER_QMZf&iE5E6+5IYLɉTDmLɲlklj79? >ߺDEOjM+ RR9⟋^lxe˟EӸz8ӝaT9h !M6Ѿ7ѽ]BfV#6O F6ӻݤKnk5]+ZVvj(JEHf;YȌIb2_l($D@ :o',PBM4p#6a#.gWPm+4 Nκkfֶ!4>>k6wĐ 0Ϻ訅]hʈ,`O]3]7Lf7?,lc]̓MBbA1H4id8w&N)Ĕv#.u?! 51<t2btV8U;"ǑC}tȎ|w^]3a8f=6̈́%~ѶzW\=XZha(ƙm<HUvЬbEȝ#6N۱=L~+"e)~V<0T&*ž2uJکXBxQG`h -TѪKo/ZB2$"c\I\rh{:UlmڹU#U#%iy?3vLJ$e#%b ZTy/o\.@ں[%]-lfmBl@E1#DBR8$j\0zF [oZ[[fIlm\X>*˼UDF>wWْKh*7j*A#6V(1*AXU!R#.aS#6$DQ#6#6,ʎXIvUDdА%0zh RLcAAT#.9?ϝ*(hy]asa`sp%Xgՙ3n#.#.H9F1KQn mkU~[` #.;@@HnݡQ!*'[Cm/C ;&ƽqj#%AD^G9z*J#.,Xc#6+fz#%fθC8>yvo֢JF鍔8ꦈbCD#%E7G*% t4n#61*M63qM!!ȵW|TaX## hLCZxbi(6t\YlEX#%$p4b9&K`Ȱy~5D_ #%^3;HנXYU["zGEMpCݑц|JNqI.k6#.?XC߶&UMWJ(]eB%\;(BLgޭWNWu\vegd!Q Bt?g> YA h7vgy\2嫆uf"i$l&ʖ۴&S60xMܮU͍w^vo.ul ۤW/9y<urk2Ƈnjh宛VKhԥη),'5wft$RќʻeγQhX"Dhm #6XTbzvA؉CH#%mo;T<8#%G; #P A;XalEx*oNx{SpAJA!E #%w'+}uh%z3#6ܟm:r#%r"1 VkZ+kg #6#%Ξ6S!5jjХ5{|1PA!%AbBVjuԲc#.ݵZc^li#. 5w!,8*N߉D l&PtIi&I#.֭|\{U۽(DH"^b#6#.)k`ETb"M(0'n\)OVu#6'=ؠ1HAcQ Aj=a3D#%;" T͖iJfM!/Y@sV2#ͩZdQڅD&1#.@ ˆHH"Zf0mFkBkmˆ7Lvvk_T,T $6#%DOPp%*CrW6}w~|d`B2gC#%aaHX4)RkUMfOv`iΌlDHI'2'sђy?b@coS5cX^-pqZ#%#.IY%iQ8$M$4B£Q!.pK#.dmsWI*^&ŭ*6VjBTBW@#6#.c0LK1B-$ok#% "JM!hKE̩#~N͊/8o:f$^쪰pք 331(`mx]O,).d0L\Ҳ u"b0h BXT+"7* H4u͒it52LURnݸ6 ht#w2H`*{Ntp~mfEDAى`=aPP&iPE*#6E#KT-ѺZƞ^v0lađ$b St\vݪʭ[.꾻mR׺؍+fMĔf#.-RHE9xOd#.yþg2>?D[a3FAR1m6q#.rPqP4`!FDm6ɒc$cvHEJG+2ĹR&.-S9;73P-2}ӒxPצ!DO[m4;CAA?{@~Fʈ;g#. ##6&dT#%O]#.0UX%H+Q#6'dg";#%h#%6ӆ#.!]zLɿ$уHX*fO'"[>PA#%GQMA={}ᧃvY)uA_dFϲc[%oE,y J2&ot;6^ouA(77) YLxl+ۚS54w$;8SVT:<t(HȪ',RڀRgpoɍ2"ZB0/x~TCIh&"#6bC;3ْ>Wc~ q'lc0U>L#8US3Hw B?kz&)Em[p!oqJЗY{L7LAӎ V0>BG.#DDJ )M+Ik=i4bs '4>}2ksH|DaXwk}e&fRyiΔK=iٰ y/u+`x$0}]J3d-^϶9@Ѽ#.3]7@XT՝F{벛Q2fqLd!o!t8<6c8CgB~QMKCm"9n H<J#ر#%׋1Mn xaOڟNuQbz` 74*9Nt Szօ8 zJ;ExW3Cd6JH a`&C! <,]IZ:}P#6f27T6EnmFؙᝎOumoٜSV#6ԒI&#}3lCY]6dj s݅M)gV\,'r8PC5U4v!yc:pkd^קD~ ??[TEw;kW]!Aprmd+ #L` | P Hƹ_gXo,i\ Xy)4`8kgBCHNw3Uv0/n83'=1Mwə1}2xERPL@עLQRI5=x_ZC#.eGke(҄ F@ٹuWn q/(o`ĂHI/#% $;H| !8$DYDvtFX*G^ҙ @E8*m81P}f>ޝs#.An"3-GCt.9#:>pwP +m&#%fFִd92hş$Ey#6,:@(:xwu#66쯟Tή>H{:rzY5z[(g 8uOڨ 0V#% FB$<jOfD+8Ə#%A)hB+ qk_g??̬OS/P@JFQ?2:߷gdH5R#;M羄#%x N_n1lX+ g_4k3>Vgl6өCqEW9v@яaN7 vV ?T'e;}`9=A+/nwBv>U;QUΆRt:`#A'B1a+1MpAH!OmmCP6*#r=??KTH?U7IȗۗXrxi7ƚ:t^ͣ!K,8Azw(I hQw,.1Zwb!&t4!.2E)OkR4?eBF1e$8xnnx-PɌ^û,XFKP<+wOjѬEfMRbZRMR(gfsʵ$u.kOU;HEUԫ@ȥ~ש?~dmeI0hmLBLhb#M*FJ&!&͓a#6$gxɳ':7ܛo'6yRMv̠0h4M4aK7tzh%*M^&y}G1Ԧ׃M6`m8@{*E#R5sQ틓ގ&D +fCx&cz\/ӆ[]4>nܟh#.L 9obkMNu.j!2̺ju^83f[G _3O8 m𦶰9T/ 3#6Rim#%F"[miG#%0NPȩ-D#61u2HQU;JnVݮsuukYۉ׷7 +l4h@1A*%*@’&M˘#%_2HAB,$֙Wrrsk$3*Fד2LE:UA-HK29XarReF?6tees4~/c hX!#6Tr!S/:kz)\Q!T`U7*͈ll/c\(!6B5A;ͷr.xǮuQQiIħ%4Ъ!h&M9Q%SML<D$AVeCbz*:R\+(mUš:‰WT7"B\+-u66Rʖ Uj<«5C`5&nnU.*.TZ%&#.*AѩŶeތ1'39Da[#.iHH7SlYeUƫA@; )ucN }f=,$|WoAR<&QAg#‹\\Cz!GK1dR0qy8B ټ1EMjJ#.BmKH'pJ*8p#0oŁ-ZN5FA4B戠2+j!B SR1F/40#.bR sȱTnͯ7nnXʾNL!ILsC#&DVE Fi(a S#6H-2@aZM'"1Aƍ<j˧lUM8KAء)"Z9fJ6ޘќ d'̓4ǸjN0fii(0bQHD5qen#.nO\m*%oC#.ʌMu…wjT&Cr@ޱp6z$&p@V[iYD(F)96pҡ& 4J(0\0 (!BQ!bJ"4 ՃGMI)Ǹ9pd!U![;+QbQHCTEI;ڋmͦl"jC`.3#64J("#6H==Rc.e}>RDFY>REB#%!TAܑB{T}*=U&00ҠW!P':Dg<F rO#.4P쬐p2Fn[{k;[U0Ir Yvk^(RLdRuB滳qP(f3Т#omB7\㌊L6-"-۹Vt$YIkX$Yjt/vA>#.H1G۫DPV( NLar($ВwRb4w^׻C/X,^bK\ٽ-!lMv|!Ȧ7@_]̏u(7#%˷I#;}+?}n*>[{oѤvB ~?c(G U#6+杂d礇콦zR'j[br#.v#%ηUT71wVfmEßX J^v# ECyƱ, A2AETNRQeTމr\95j#.&t0cZb8z0mEfHMq4 9 #.UHr\vV^QkrKhKrK\6UƎnV鵷MY#.cnm&k-͝r,mj Q+;-#.w˻$v07<}Zkzo=H^"΁!7@A"#6ޔ=D v_oQy>)G#6"4=$S2w@d#% Gq1(IBH%J*N^T#6Z\#x b*""([u#.mt~{oFRd%T] *Q@QHѶ%[~k!D,#6B?@#%$Cϥ'HjMFF{ZPuPP#%;f_@OA$;&Ԭu`٨3`:H#"&Q)T(uqe-L͒IJ1idi66hmIEbRRJd4)E*kjZVjZ52b֋ci5[Ϻϝn$cu#.Ǎ"6W"Dne6aPA$rtӣҮ=t<iqDO c!&$*+hlc H-a"#6@`i#.H 4ECJP*(B*Z0$(B #6Y2?5#% D% %ݘ:WV(@d$f2@M%^B3`N"vmwcٔQRN {ABH,OVȌ5Oȡf2vpW_[,1Z%/-l@ҨܨMxFߗj1pV!TH!PJu0UR8GaܜNFnɼ˩R/]sQhZp9v?HEozp2#6e_j`8C+dX{Ap!>(YD˳eJ[LTzF"``΃ՁRՠiJ%MYS||#6uֽusFp"}rC"lʪ<oבMr# X}vt;?;#'Ri@ܡM|]?ί^B}#.#%`A/ў|hl\1**;3*T4~3a)GZ*zUƶ` p]ڣ]u\ʭJQU3&eI#J{i'[lǿo5٥'Q#%3!O"eBGVH#'53Z#.PRn tCxmUy/VA<(}Z81K$=E4Bl:ESlW_׹/#.ۇQu:vG\{KX{V#%L#6Bk7(/%ÞԦb-ĢFix$ZF;vq!g7tVBZS/ yo#߫%&sX*77/cMrW^3$"Q?:y6"ƺ{#.V~ggsmm9)jWMmf 虘8k[]6rXNVRTYZ;grsByo,pѾa& q<jf)nۛEŗf5w V\)2l`qDxf^aTkĆWnh6m<py_Z>ؗ[hNI/$f7)֟Gb$CÌCC:s/}wʀDJnTQjPxbZ:>춖9W_Jװyixuw}:甼'u{a*^WW0o򁹘8Vx˜|ʼntCVo#6Uvga*F򪋭lg/=5ln.b</Deó՝<s>NΠlrA׬<Ec!"D^y8ܣB6Kۭ8,g :dDLoY0Pڌ/iys d)7T&hdr#6%#.-/C&d.]'O8m7UI,`W28N$`.X@=ZmRmf3g#RcQb3x00_$gBț0ں9#6) 'ϕǖ ke yc0)Հt( dJݧ-AǓ0<^ G& @1[cUNcAn>Рo#.(#LC#%@: #6`FaPa:L6 x`v`qG=269kSAC#%6s^zv Z3K3.&˘sx\#6?#6_Dd\PG`{uDO.O'6 [k,* Gg ¾T"Vs ##lNƹkFaCghz:癎>cETQSSNFb((SۜM e{A]k]zV4o{#.U*#%9G֓6(#.o϶;3\4G>FD(ѽp9f7Z0C㒚֋N21D!.e-lZ'UbQء~%@viVkQ-29ys83D^[)Xɚ" Dv:e;F#6~ٸwÌWoq̋';Cs:#.a :YT [4v4nsx; 2~sw~/j<\WtmG^5&ow7 A%#0M8pq<~7Sۻ߃LΥmZ\dQ{x09hRĥ#%}D4bͅ3璚E=;H>.x[CUZ=7,htz=<7~OfBEo79qLB6Y܉Y;W \q.6 )sf\=#ՃV4(v<@ǔ,,g (y zw-H]BEq3:f,ҭ'! @1HF vM\; 3@.g$$VtIm-SL0w0'#%Y"aU]C#.`f+um?NCw"0)qOn"xXN&y~(p`\r P <߂(;VL{U#.>xKf w'y;`kphSTCç2gϥ_VGG8v'$ODMN};O[Tb,ҡE~ eDamlsԸ#I#6K0m&@<ƅ(`YF91!"DȢEXB!$Jս+F5nZZ"(ciFPTҊaQFR0!\g&x؅ @L##vЅ$c9bmnEɱ*mVUQUUUײ܌P:~C5]c-AKx*1K+ud/io #.hHicf-+W SkN4bJ:`@,זcɠ(2tHqbePDiOR;]xa%dH!V7V (&icRE,%Ecbi4CZ*=[gAuwLtd>lL!P9l#6@Y0 fL7zU3RXQ6֥4ێvFjhMUzmkZALk.<c9eoL$DMqP\`SZXL*ccix12BC$Υվ59;haY SJ!#6A,TP2cLhǻ*sPAnU"^Ck*V%+XIw&]J&0ghnothsiHN'ai]P \R8F0Tj?޺֓cS򣜵.CZFLdq"7*#6* Ѹ0m0m15+Fn-,za[ tC 2CdT]&09EZ*#.hFemF?S5@LRg%|4k h~Ɗ$*7Į@zi<D]8& .r#.VFq#BZYmrm2L䭄lbWfJڢ&n\cFV=^1QimX9Vޟފ=Gye}#611("фe0Qs zXr8cMA@=6alS!vH#.0Ռ 0\d#3$d9:WbTFMP1#BCV#6e-ͳZ"ۭhDF b0f8҆±Fx7ԃ{ A6&'"ː9#.+UN!U*2s1M`C!0rc$n瀿+iL5M6H$䔴$zO;bCHwڪwU֭4*Sm U+ {}E*XS^#.qeӦ3~s+?2C9Ag**JAF#.oBxzQ#ț<*vA K?~Cy\Ube>\f={p1$a0{w@y9;bƪ['0ot<(k qΑH!Q:ǧ @H !5i WjKV\Ջm]7hlPPHІi#D~9ߥ}~v"R(H#%V Z#61&1 D(ZUil[%RQ$4(ʍ͡M5#JMQJHIQhDKFHS4TȥSFal%(I1X!#%:xg9S[4Uy~Ws5W$$g1$8)ڵW#o$VElO">#.4~Hgahf6ؽLKnR& ~zkpC%߁(2H\9q]=haѹS(BY$ߴsCJFqyڵNj{!:o?Wjߍ&XKX֭siBQݫC!bRbXK20'uE$fP#6>"ٛ).#.>+Qb0rcD>n.5(XRb"D.f+@6lxRXV,I.1{.Гь6&sDܷ/PQ. a#%!`СGLĈĽGI{5OEɟ'& u1p6eJ'LHQb`VAdb(#.LG`6 kgμ6r1JN2,=4,p| d<۫6jHElm#6#%("3@pBh_*UYU#6!պL@ @+-= #%]~7c#`>Ͼ3CftB$#!?"dꅄ|پ~h%{#. ,(`塤|ؔn2L3@>7Vƒiy°=ߵR|2Zf5Uo[52UQJmZJK*G;&u qm g*4-o^n2nF-tiD7C56&TS` &!*QA r]u+NAB/{,tOئӒrFAJ+)( ;Ns>]M˰#.L|R!)B9^jf<;#6A0xw;HA)4gfzqFcEk|!uMG&~#%Q;t@d"Ȉ*#.>/}Rb_cT#D:Ζʱr'5>|}._Tl=Բ]4X`C$0V9'Hv'FIj &g@>3#%pJr`{7s@q{A/U\zge)^=sDX1ƣԌ吰nd[wofXg)P6B\+E,3̈́JSuUtD19W53pTl#.ch` 0pP#.@n`%V TU*2/&Yr-ި5!P0E#QHa6P";^-@Y1n5͗#%1 ,!sYڌflzٹciD&Ho?/3нX2(ṵdݭzYg$&5D EPڥHA~3#% LC}Yf-"}qIQ%^FZTzHK"@Spu^<o.B3@DmjtalrX@zQfkQ A(n7۞W ":7`R7{BdXg&Sf% 0,DuS2cre38t#%a%筻#6LpOpߒX9 WDvkb+L#.ˆ#.BC7<D#6H2#% !!P X#6ɊH y-_F<an۵So1[VWe 2ptc,2i혎s4F!RAV,ᇉ$Eæm;cHo!@3k9Ç_'LYBi1@'*kM.ƣ66 AmL\RE΢Fe%oݓ*,u`ё91260o)i#.&Í@V$ltvckrF)KE M,vL.\{՗[>!mic#4!k}avߤljW'7q$[vLUD<jlH=fU̜L3a萒'>4OЌ!ZS5{XC&;8k\@r2n&='IZBŠF՗,\*V׳p`\HfJY#%㚨"ߑkk%O+vv&?gr{޺(UI XE#656ɛ!dhm^]04qs:0@E}hnLe}8܃^-1|2õ@pVכ;"'塄X%􌁖#%5`+IB27t#.NZũBAb+m:+BN\mgY[6LVlaDg@iDбP<B㌱4 l?nMM$Y'Btoi\+~8]-Y[tͤp?UQm2LqE#.q<<4x1 (3Hr<9kt Ơrz]Y1,̊sզ)F.z—y| +d׳F~Baud֠lŇ-Ņ3XSC=qth:j&#61iu]p3Bt_ m}i2H۠bvvx$3@̷gnՠ PJP Hq!Y%j`"#I#P/=fgMC;,LMM4Yp'*SxEYvMY@S̎#6ݹpf#%;tyA&wbOLO#.&?8L89ݓM7%89-u#. /L]\տ%.cxb4&!`,flLM#6p:5HdAH0̑ 4EqTk&x3)e(1Y`ǎ6! ].$#.  T0%DH!Z9ǴQPP4 wi8,H%Fl!6ae0\*hiD58h陖*P K#6@ALCD6DS*wB]4,6"wA3b")٘iU HMlE 1 #66v4#60gSP#.FHWU%0ME& 83Ą3mtHrĢBBꙕ 3[^6_#.c%%yyCkU@(צȚnf C5#6,"B@MbP l:uy\eXky+'J#.?>M܇)xm&-BkM7If sQfE뻺f$$#6DXfY#%W%UEMѐ^hI#%JSx|k0Jh(Gfuazf?n'ҫ*3#R'W((q#Hei3%< 쪌Jrz#67oSkJF9T@Z0TV(NAqKV+ma4}o{;٬66[[:>DH{hˇ}*s9eDʳ:d{T3D#.CvN]GL@Sޡ#%4C^WfDΗZR7p#6VX"k˻[$2%5;o7vȮ^vҹR#6A0@45*F 8iQ3 iGZK(4]-UD7v)<MFgV#34u*bQ GF읯!<Z H|pDA#%ϡ3jd.1<#P$xamVĮL+ؚ$p((7'&9w#6ncl!#.:&LYU !G(|#%mr,A-aNKMN8R0;n[5')L^b>%C38]6uLD`X B!:Oi悘n%ڒ"a\qK董LQ#.i1ǿ0 0~tm6V,V"$skҌʸ 0')HH2DO"ZͭiHJXDVBEGEE0cUƄe5@mn\0P`M`8#%#%Iѕ*Bah<2ƦZ\Ȥ H:bX1 8 #6 #%H*ߖ[|4O4o}#9thg#.`޻Ÿ3*\Y9)h#65P4aePM2Xl+%ʫEEDLNAGUym^VvV`%QV@INWP3:$#.EG^2S4#%ܥM2,s4PN0:=G,0!&*Ƿ=aօз;sHcif t1aS& 7!,H#%5@% EsRr_kW:*'õnH[h4׷`wAuu#.,q89 J;3O8~k1 $9%y6ZhIEG!+]fvo՘8`n^tY[Ba֛iA~_X>H&삉Xb#.45%!rKl}9='p>(^7/$jYiVIJ#6ba&Ċ*kLm&ߺZkYks<X员aޢs: #.#.+;ÀMI@\z/aٰn#%N=ǾꉑKFzFC?6(ukàXE{J9&rP|i4-J[hyôme;;#.MIqAq)\eqACD 8: 7$l}ufێ6c6ZAI%!pj[-[ +cTdLͶf܀#%PYl!VYCrhM #.ѭN0LoţJ1ȲnFJֆ bk#.?{5b]%OZZl}0 E!΀,"g4 )@w}* uAԠ$ALpIFwIϞ/tAO!## j U6t<4O+ta L;X#6ɨF#%oٔ1d@!T {WnWm'y͌6@g8AZ0?y<vulfPx"%70 'ws7u"𒎚ݗf500.=Ԗl:;X澐QP@?LSa#%G1#6 AffXC&3#lDhK #.Wa]\]ųɔSd5Qy7s]&Ѷs\+yv[yo[s!S$S`6m#6*%|}<L=AIYa<>#%<>,U!f,ʹ5j"-62)E4jtj7~#. D1SQ*)Jzh'̋x"#%Z"gBR1ȸ0ć0HrHNT$>#.D1)-fQlc+E*(@'](]4{qM_Osfaב$W #%'}eL|*j #6{.Ƀxv,!3Iy8Uo{,EE'Ft &nyBQbpt"@T֡dHE9Z B!JSjh("jKNkTRiI[RT l@njma0 NR(+\/*SRe/n.gUP dt=v9.k Ni`Mljh([;R*5XVO8"Ņk4#.y@^K?W-%lc#nzK!XЇovM)%I51R`F&#6Z7m+WZ(F AUdQ$9,i!`P*6+0rBF"EH(Ʌ)hlQ,#%h}qz/uJQ!"kWmF-ڔ5F6XXQZ[3:zYݽ%$kg#6с5#1KL5VFLкƗI$ȫmiGl/$3" *hc<jֆӃ90(8A K"2ڷnI-6N^56&\wY2nڊڂKdYywcMkRTȔ󺷚kΨ$ISa!H6bcMi^חjMIUZuujlm(eRםܷktYc)&A aNΓ#%#6qVm 3{䚂5Hd>QEF*d#%@ S㒡[Fhȓ(1P{5\&4)Dڛ7եG46Y3O `>p?~fE#6ݧ#RffKXZ#.Y9~07"^GM#.UJmG/lr$aB1 SJ@#o($QG)ļ.#.Lo.E`%1GP5/mlpB#%|$xk(#6(i 8>Jiߎ>9t(a-Qޖ3oUbٛ`|B h)Iiף|#.AqK5"gtdBKVw|jPOt~J'`O8)A.F'RK`,n#f4lQ!n`C#wƽ!ȱ|* DaMF՚WuzC<kGG Ղ]j?70EyZ$YϨЁǽuMm/ЅZ8@?j8睯fYF4d-DEC=vN|m:Pi\X"#.%t#̍ѨU׎1Qߪ@[8A ʍ;çKB4Db:[nX#.dRS(@r:|Xrc'pdlA47m~f1ޓBG_hGC"Ws;/~D#.fY{ao}ik*>D-(?Ld* zQf@ݨQD߳2Tm`=W#%w>uL䑙 l#%#%b#%?s/_H]>_|O?~#/?|/=GG__PSLB?i,ϳDy;"e\?E7;sL@Zm4 #%)40)*ǹI IFVh #%QߐjB MρESY8,g +,C҂x#.Ǒ^A:0f@#̏%*Ul·i\şyvǧ‡eԫj݆떅 &P"ƾrUr#.7_ua:Xs+u#6ٙ.7*jHxiiQ]d*iea˜T0eTh޶եpvmTYn44Bl`sO6e75m8q훓Mmƶ9TFdMmTIqx,Y.5myx*ŧ7FskCF #y{4h2faqS1a HF#ydH?F:o4)Ц^EKN\-NS4 '>:`wJ4#.p@ĞGF;tk5Sޒ 넞g w#6Hp,#6^>`L5RE "IEqOv].fB< 3PEATV*AB9wtc߷Y2 k\69U>!<N(K61$@ 0P2Hqw;\xH1M}lı38bKuɀ\ ,F#6"`T70]#. wbs $LjJlkY3[FmDŅS_EWZ|:־:?E3t#%]Ti#I(co۪zSl#%M}OFA~o;|mq>^F4?cS9ɬצU1"ǻ'_6+W] 扄8uKxte}C3zg,r.;}0זfT9P^]*zg_Ƴ8ΎQHԼ=#6@C&9Ғ#.QO#.D'G\U򻵊PCGݼម-v챯[kش|nmcm(oSEMAjZU݁y]#6.Q#. |P?!xndN2Ap'PQsp]f!OVݤ7Ub*J9H{cԼ(5@$fR'w=<܎_.EqPX#"(];#D)Qb(mLtjfh ! H 3ئ"~({fYr0mCN.!xN ,A<?  &^|YY?B-6t&E hN'c`[l@%AOeC.G ʽ1'waB+!,y>^#y;9ѵgs(?l-A!ԥG#%#%(#6Cۢ OI[BY=+4ks]B_F*N#.6pJqTE3oYwfӧm3,M/-80aw<}8fJ$ o]ax`H$Y@zn= i~1^#.Jz(LPc薪"|xg?I~"]B@&
+#BZh91AY&SY#*s#)w#)Pe((00b{@#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)}=yl=C(4ӷ޳>lb#i5;zhm2_<>;o9tzQRQ^{ZSvvnV|KzG)=;{<w`dkovns`#):s#)<t#+0fx{z{woi#)Z=2zR QJRwcPPTR)QU"-4S@UBZ]ojoxթ3#*TThm2*U;h;vڵϷ^݇<9=ؾvsm]M=cΎJ%FP!RAִ+nݎwZWMNHvz #+UDg%/l{s[m{J>#*{_}ޔH#)mkoE#)%ްrMoxyoͽ-n}½_uקʣ^piw>onۗtn%ju^^==׏wΥؒl4k#*m-n肵fmR^\kS7Ϲ;۞==k{Ruc_YGgu=D#)7ӗ;.|hX:E!#+vrT[뒝ݗ`v= Tו{خS=R-mGB5#)#)]#)[^szyn ^E;Ӳjt{|ΈO:"Ǜ^g)Wҕ0WrxiAk뵇>۞޳3n7;xtynǽbE`3EhYۣ)꧞/<7}}^ª\zR}ƕm#+Mw9s#+k/awv4J2]޼Ln T;PG{}{[#* Ym::#)J]zP(ٷ{[l\nu[PUUMwbHݵfF4.47zu"Xzҏ9s|C`OcB= ='Pu}}t:g5`wwn:xo;ΎZ5w#*4@#)#)@M#)&L#)O%44#)hFj=)B&41SS&Sz='4#)#)#)#)#)H!4C@#+i$$GF#*@#)#)h#)#)#)IꔑFMOj&mOSj#)@=@#)#)#)#)#)#)LM 2##)4ɢz&(#)#)4#)#*#)I  hhCLO%<OjhzOG2#)#)#*#)F]rO97jkى%0S=diyUhᶒ""1=g$=1c#+{ؠx):*e[\UzVXbŲ C^p:]4P*_v6:U4\\=:|\Qur<c -ޞbqX|D= &b'm*UXV5UjkXѵEY5WZ #)=Ra*( XYJ#)HVkj~#*m[j!d3P4F hK#)J)aDd FKSQ6#* h%IZ!#*FJDSA ѩcCLYSh%E K-4 bZCf`33ԑZ$iM(M4 ,`E)ji%k+[FK3!4D2ɶm4ԔlZkc-MeJZL1fdlB!QfR`4TH!`ؤلfJb1($D- #jDF"fhd1J0̂VX31"Rɬ,Q0" R4m) %&ED2hi1 (%+ Ɖ"Q3)iS0JdlDf0lK+EI)65ZK&DA&J d0Be$Y5RDKJl!E؀YR6dM؉")Fl2$YJaQ`,i"5+%6(R"I&aLcIBI%e5L6( Pi6FB1dȚ R%Ff҅&5H$$Rf0"ł%a1A$ʙm%3!)+(QPE"$$cc*iňHRf`Sa-IIM3 1E!DTآJCddLm!E1J2QI&$қFQH6cA22Ac0eLcH 2 )LR,!-f J`2Hi,j$MaH!ă#*IcQf(14Qja&e&(&Ȍ Fɔ*kF &hdbiE$̈jSl#+YRiHYSfF$(BD!BSkMJۢ 1Qح)M5&#*-!j6MFDai2F`%Iȃ134d1))j6d ŋ S-,ɐ&TlHSe,RS#+lfjdEledJe6URd[dh Me-!1!գ%AF&QZEHkD$mbj6hZLQLM14-4m15[$XDRֲC!2D#4FŒ+jUcҩe*f#+"5Y54lBS)aɬPPʍ$HE"ņi01$Ch4&1#+[FJ%E M4l4Y#*)H6DVYJQJl4 AE$&Fm6ccd)SL#+1#*1$d#!4LQJdRP5BT4 JBj-"ͨQ)$ hRb"m&Ƥ,"XB bM,TJYJ2V0H؆5"jњ"R#*1Rٔ@ Ldؒ2)6I R`0٤lfERZdmYL)aD!2!FlQIS1II hQZ3-#*$,FZ#+6,e$1RJm@F5D(LTPXMI5L1 ƩJX#*)Y#+($U2KD,EV(-&&e RC2Q%MDLQhdmSiEd[ZLRVSllCD%2 E cQQh$Hj)&`VdFٛMcj*ed244QHEQ(Zl6LE(#I$HZ6Z-J6["Z(HȦFdf4Ԉġ$hmd։R-DlldU4hc3J!,V%b*66*)JҌB4Y@ѢTi6ƱfTZJزmZ4kLh6mXLTLIDbDM2MmlZfZJke,jjmV&HJ(6I,EbI2dFF#!d2I4[3S܈*JaR,DB{$ ej'X;#+#? $I6|'=v9OޙMr=2'_YLvPCoE G.)$M>fU#G?~{Z?J/e+(4{Jvo\T55bӶ'$񖿋#*R(8'%f`I"lLhchɣwHLo\үRY9IuS Ś&U;1̡LU)I1H P[xenTޛ4V"S8͆@.9_Xk#)#*)X)h vIlF1IޢܼYzfձS ))Y*"i^@"nDDWXж (D%gF[1D!9$n䠐խF4iY#+qAW6d5snv$؟]mx,EAoOW[Lmː9x#Q~="!AeRhrꠊXOUԆ\2MM;Q7~z\n^+\Ѩ8wcr˙(ˮ$scK TSAr6bDa#+hgˍV,QJ>~-Ҷ.ofd4x_QI)饖PC`QV%.xۈs@G㡆[cɑ!$Ā|{kdu\(3)^vj1kgaN#Ux:8XMFP&<^wPCANME)ߣN95RDUH,ܛtj#+M4+1e)Ֆd|h*e,YI#+N*}y>劝U"֍UOpi)O$WrHoL[1[3&)}]uCAbĭ^JFmǶAp=8 fw^w0{(ʚ҅A@P#+m5͵>S6^41X?ó4E#T/65cOvn#)(5Q#)6i\-z˽zFiVJܱhܖqQ7׿O~FɷIoШR:e!x鵘OrTB)jKGrPaLֶjϽ$+?+ue dDvUдp$XSͲ]#ICHs֞M]J)")٥ZVS^X1a#+p(Mj!iL$0J *DbMZҗucg~,'~6QXXy"<4:QSxU<b_3|ᮁ 3VoMX犈rW`c su$߲)kGAF5ڔd(̍)/s1MLOom$|2̥@j"*i_mQ{{p}XaRiǿW-#*{iRJnCMυז1n#+61KvB9&#*Xe5y:UCsGڅ$侻ZqZ#+9JQo`XO q\NY'mg2||#ӌց\KP6@{"i:O8hQXPl-y)<!Ɏ1N8r~NZpdn$g esZ=M]}ʑs 07UXkӾvT9 0D*?<c4f\[Fy#+I#+CPXgCӛO~/gE0F:E&}ٞk^~NX#* `8FW_vSr (=Fj/~wfg[T<ytr ۴iK$'!rE s741Ţ6gl&?H\[ խW|-F&!z]\krvmcm~h0z뵍I",P>aSץ)F6 #*΃`wD T f(W\N0!oǺê3h4{:#*#+M-;2V[rf6q6>ݵXdȶ_fտ'qvNS#Ybd6CTEM'k7T^DAEtEܮwSe(AP3~- UKTWXR/ú%}E}?Bo`*~no=b~ڭUgLZ{vkܬ'5X黄n#9<$0 1,|ݮ>LT0$4ԺBQ\?л#m* Ҋ'; 0O'IXbW |.aðXOLTC] `GE"4df\}(EfF"daIήYd|wjfs|4Hm#*Anj0h-+mnnΛry=m`p;~i2 i>5e 2}֙;vdM&;D;/[0eg#</G5Sӳk~ {bYׅDQQe0x~r{*nf gsk#b󼺟S!P m쪦HVm w~W5FA?8.6e*DF/:hʳqhPmԉlaKR7gFTaes^o#*b,7K\ϫfF5L*9]Lu,a?ʝu9s}r~)|ݳ-7gց"+E3|E['yn[[(/c#M5;7uvkBoy q#:*!ߡ@2,S\&X@E$@jS %ѯ9Ar*[ڭ9مHT9#*|P+JemAŽisW9^!UdTā qpԡ)M_DO߇7•9;ҙ&)/)UEYL68:<pw+q.~?߯ƴwZQgYgoKR!e=cê޺ۛo`Γq+\Gkvji7XH0U4UtSfrD^8Ƿ˧oy=O*91btEWZ#*&5OׇNz\패gvǓ:TZ()'u\><:<&.#*iEEP~P9*18pM0޸tmH][џVuS)¡#*:I)D}U}g]_lS#+0D v>wNۭb8mDz-x8}^i~8ߕrn;wCփO'D׶ø1;x< փܛ*E''Ni<HD}*qlSvsx-k##lJ#+sR`4ř"{r6uTn<HvA˻6<8b?-tsPZk%|e ƅJj6J8Mf_~et"߈d/ F=Da3eyB>H2Hł~\nGKTCrhf,8`{3Ѹc0q֣QAdX"D+j_} Y؁ύ~?Omvij؜ٌNIi_qηowҡG8KU?d~:kRv 洕ՠlj90#)3mp-#+F,V! \bŸ G5:3yfXOLbE\:bEmM#LtбDk6<8!7WRz-sܺ%У$cf"6.u^EJ)*&h=خ9XKaiCBhJBllĨ黌?b<M`I0^Tj#*DGN66YXlGg FfJPF^>RQU)YSWۛr)quYyljbkxs# < Myw2Ԩt԰$#+IrHRUN|. @bX}#)A0#)An/\Fz(T 9h}F*yi>+_\z.z>Jj3PS}4&Qhsy[Q|*S&; 4 P!p\irj׋h=~9qm"1R;jQùk;FfP@OX#e~E`iF!1!8,]\=](aqNy͡Hi/U#)|7?^ʍ#*0qm|42eom>Uͦ.Kd}&ݪ%cB=<w?GZgPDdaGд#Nj=pH.]Ũs]!یD}7*gJ)Hg#U(+mV)Ul\*!",c8@^e\7Erv-4DܚH`6n#)QvOq1>ӺI7ϓ*a#>f0Նv^͐)X+4ͦxӺ*aOtl1Vc;ee`=xwc8əA nNPNKYn̎ZntvfbU'A#+Ks3%ʘ,"'Q({eڶӑKD@JE̱#+hYS-.6-s0(<1qۋȤ&X 5lS9;\+=hˑ~-l6Yn{j(RdW=Q1+׬60իHY.!\1YmaknèŮԸm&mHI'"7Ĕh2 Ď 0,ȅ͝,|Qu11GrߦZ]Sƴ#*y[Od@P%GWvT鎉?z(x&gAaBC?N\v#Ll܃B@BtR2~Wjսg@ƎTrJ&æs/KH}Dd#+Wg}QϪu:;b-#K/+.t`z}jv-J#|וF~zEڄC񴯬MUY} ˖p.p~Af<OAE;4JbNCF$t%_6! 4q=2 L:c&8SmEj9+r9jN@RfV9m\܉O'*?ێT19b7;q?B0+HLoDkݏ׻/!pEz#p$#*[wW.:=(qwQ>aUb}o#)#)r `M3#*]#\)lq񮪣-,"$.qQq|"I|^hnlۨ5y#*GOXځ!W;=::a:ATX^#*NBrAH 'Z.Kbᓥz@;@2Ù-` r/Ѥ_R4#)ռڿ>!fnB$xw H>u7##) 0o2p_#*DuH??=ίH=PYc ?sZkV3]d訉.(S#*סؔ5M#*:U@8~xf#)4_Liyp۫o2 <B@TUĎ΃dF\1cW6ږ_#*e)}c0t?ILd@\Fx˦.-.x9}:y0moڦӛ;Kv5G|]GSgg!5,`mm(}8U}=9 z#+qWjwu ;ZVic֣܈m?L|2lEIH41Ra-ȥA%Yń*sȄVFS{o==(6$,ы-'d{Vd0MT֓q{*H("'Cܛ3kyEk?ʕ} a.|m;w#*>2a&0֛:|Ǘ0dYqG0xԫ%[I(nT$~W!>hjI67#o.˖93O~6Fp+jf_4 ed(K.?2hfDj0[Ԡd>ToX[0v?}QU 6q>?kН#v%6g#{`,d>o,ôOrb5)צ[HmtDͷϖ6.w8wk3{e uUWK"pGxX $Tf~e$uod|c.9ߝ0zr2}y#}#)8Ot Q[]Y.*}`#)HYr-ފdp:_RzNa #+#+k9&)=#*cCWdƬ!TY^pxFv<GtIPEG`W˾q9ᴇ'LK:(Bd4Z L1-`,hT;ߛWkAr4#߬u3_Ib([uN: _5/Z#*a{cįQL69GׂtToN7}F"^=#*B#+ Y9ZX`P!N`XPLP"=n}6NFaתb(VÇBiw޺|SL( )#ֻFj4[W~baJ!F#fWAB-(0\tֱsTnS3 yfl+F2EI&f3sՔ'6@aENo+_WGTx^b'Gp b@1@00{a(zݢkˈtۡIb#*FWHqFYT_",P~յZI.3Ku-(T:EZX2}u!;vH Ua=j1‚s3#+Q}cJ7oQHR'E9jy?(=$yq#Qg(^ɧ;B2Z!(lNE fR:]y飙8<XH΢tnyPЙF:ݼ!#*lBI::˦849BÜV%UfeԱoߣ)vfG<\%s͍")X:?(ح8QC-nP7/.#*K/.t͕tn5<VY6x0Q↸)<?~B} 3S;ToA |*(t=5s'֢(#W24U>jWzm]Kv.wu`#G7) ,ѯ-qКUtc{^}v3N^+H$`1U1lj'X^,[f n8#)HrdC5ۖG.+#*J l-$ #aSM8^M^yȃ^ϴJZH(\Xb0`C[! N'r`?Sڡ- *#)ȴ#+dU<D\ u{:k4Im0PFΉΧEUL#eVI6NuJZv#*r_#cE .  ?FVX7@!#)F8Fr͵h^ EXfMb.hgN^޶5#*9f)dn)l'xXX)k0$`Q1aX@ѤK"4V-,KX!3e e0H'aK|7<6M%쮚1j߭4ȣ&(4d "XTMPÄtTd"+j1"Q$@C(mc3f-ݶ#+_[0* Byz%,5`|]mH!U#ڀW#)R፩`0@~m'c۳̟xO߶stDქ{^aeoѩ?7srtZ&R 5rz}~[to_({4qNռ)Jnt,X)bKoS/?R嵩Ñ2%@*qԏZ﬒֤g`T`=Vb$O<+v 8Y,݇~&#+?]D驗 7R0'vHDߺ(9ﭧ0^:^ɱ˿5~fbU`z;l&z+z#*s@ikxM,V AKs#n.݌a߱o0;pCǍ`yQZ(A+#)Zı`qS ._O^BvB";3)H|aξFV^TN޹-%#)LdC&xVDVANL@2,* 6Jd :u#`؍)cQG#*$CMB&+J,;"7b3^u{ZU,q;FǦaA!b%ن/w7@䡻i[ dikݼmME1w.#+W;K<8G'nXH߈g(q5L+ERi4xriȦa<jF?zc;GUdRˬ<> BⱣs|>~p6n)ɱǭȏ.m@nP$ܰgWGK#+EpRۖ`>#*LF#+):IO>\zR0CKIR7ɹ؏ۛT}6YČOJ9Uד,}:&;`̒6d],ߐ|?H-AuȹHƇ4ڂ:ts lV+TF:}Wv}t<΍֙y#)*\r7#vm#)~tsV¶4&`l5]ULI(Vj1zxk.i&0Tn&9ioXqxQ3J`r'~Sa0mpJ%l//Lg>#) *#M45~(LĴ룫si·& A7cΥ7՜N.-R66 5k5ph QV#*RK܉jQc*CPDo[Hf#*҆K_vjN f֡55iUeNQ8'@PwAo[N=媼[Jѐkl|{>P@KJ5th,+^ݧGGZ 6!ZS<Y%o_.8#*^"?ju+Cq_S ~a?*VI|g^Hܧq\f#*ep}2<cùCbG7A,yw@]_.ߟzuj#)Q!^wHQۺ.O^n-g_TGӳi~͢7`6#*L=VAE;ܒG<}'DD'u CRzi #+#T 0d bZ0 |Oіv4[#*?dqXW7R)_⭄Falg2#)Gh#)F2O??#)RPJ@J_mϦ8q{Ǽí;7$HfG3**9SQlFzZmId& 3eK8MlUjvݨX5RhTI\Ki_뷇ݳuM#*8dmv ,PXTR* @I)R Wt/bkSWr46l_q<Bd|VnV57RɢPљ.)92eAU#*Ҏ (aTUS!<a?~;hnm6#E,/8h+H(#*mm~ a:EɃ*W77,u/R4!DZ[0Vj^?s8kVlV#*),GG%@?֌QDY|4pǧ9XdR( YTsg CV"KQ*cT+ @C`z"MD a,~rd[xO#(lYCq!7iCD~h&ʊR@pVJd͹e/o7gkw~|=kL=0-ۧjCdigOC]^%05#W nlm9V+G@R-`":D& ^]J|lX8萉(3y/Bƻ] ]̻N,f#*mTߜ#*-] 8;od7W|Fp*9jʞ6ے@T_0cKޡQiK1*Mo#*-@;yĐB"ьӍ˯x_)i>'Q~;r}]5H@U|8Lf-Ȁ7J#?v0a ^o(hV(Z0yo6Trl9Du#)j#+NXS#*n#+7:MvWȭ-5#+Nt|`2JҤD6梪+0mD(pRsS_ܢB~*Cy+eyPd+ʂJ0oߚbk'oޑM#*/S5 n䟦?!_ Iz}$a>TWV4(H"4BUW%gr4#+bXX H࿎A9įXTCAצkODZW(qHO#*P- nH=9=<9kFF7 * %T-#*DK):TżWןiiծ s?<)]ϫ槳.>늪oubgVY\8yl鵂v|J[]r{_n=w~o.[za"Z×cTyOz9{Go M3hr+Jo"_}ޭ>\=>pm6\\,'vz5>S[h=oВ=^G!#)ˊ@i>+ۡ]줧Y!=7~w*lqdyPw:Nm8}.d^C٬uƌ45q91Tϡfk4kmiގt= Vh]L"h[o[ȥ(}Jyt;u4kc#+`|LvRT-?9Q ;˛8rnAۨcyȗ׾V{=[44<T0",C5-:=vGU\RD8<$DT,4 w{W=:}t4^OBG|.͇,qp;E5÷lmj*<K j[6w#=+c'!;^u?}cdኪjߍK{{HTͶ(Ga2|v'FK|5gA՘7jy:-Mqp?+W};hmLa82l>ͼ4Bu"q+QهWu0٦KttM׌A_,B8aSG`x8WUVϾϷ<?I]F pc?gʏN~G􅕮wN^fɥ#*F~%P=1B,Xן ayyiЩޢW39w_/+<~_ AE(XbïC?s:~XO9S07w>W/gf}_۱OSߠߧݹsD[lٲٷGԅ۩^?0{sßPnML.,gպAKSܓybhvnO\4/߷5FRp7 Wit|?oGр#L8|Ǐl?-O8~ϼ)bykG*By-cڂh)u&fXC6ԥApoNnvu].}-[O'I'm<EX#DPw/)*t<f/؆Α â@m,#)(yPƪ0alKC2u8ekhltx׺b+r4.l?"r:J알렦i^xḭ|wFiG\8CnRDv0`5>r?-(^g`E38KACn>i螟'o~sMX</#*"+]iDWsdяְV)OQ//ֲQ9\nZ沛 c"1ƺ>ϥ]p?!O]PB:sG}eaPuѭ3e*qc/`'{Vi h.-ޜTh8h^"M+x7u&CPGX ?= U<ͮpB/~Y6>f4W7Ø yA?.?ϊaǤQ=<!JU5K§XIғ9SAQ9: 1Eztϣ_]U÷WҤrw#* DLXofq|\?~(#&q0/]:lm<Njruguܯ`=vvNB'i=ш{c8~NpPTh xDoa$a*?dSC<QJyGX7Q#).ym6`+w?Al:9O*?pf@9% Y`#ŸF54iTlTԢn؀7#+҃,tM`Vj5fYЈD0QJ[M`cXLVDRE,h5b9O#*Ss6]&6) [x90$ОV6bxȏٜ3((>~kS< =a5$3Q]EcXwBRdK`zq |& "4i[F2DH@bi7C$MR%dl!#*YRMم$slmg:> oϗ噧?ƚ?χ>}8$2piY2suϖ}G͕^6yǩVϿzxF_.2N'},Z$A1lx[~{4,eVMqeo950J0}(HAáߘz;=߫_WNgyDA8s8#)/QM]qt#+ed,ΰ8t:ןYGa75_0Ku;z5+uj~1l@L/  J(} 8=uQN^v&1[-DP9+#+0q,j[KAi$JD||ף^^|R"Plch1J1BdQZa++)YlW֣F*\mFAKTQ[*86FH2#*ө7#+F5ߐMAS(0F&bpuں)lcQ#*c̉fU4::RpI C␂mh ͧ`2zXUֻ]b0Q$I8 ~UIG E,IW2*EsEQUڙQdec=Drf |AI$JCo!A"^1b޾޳a/NNdjEb:aN5TR:s^>)쇻v S"?]u>ϖU0H#ǰ=z6)R#kީBݚV=A{_y}~m""t^3za6Uq8i?oQdOepKAr2ÉN 50ȏh6tMHneD ~zԪI%UUQ8$whM 9,HbzyB>Sˋ!z=#+-c>Mɬ[ 5<Sd$>]L5o )ٲgF1#*/XnFDy:|V519$,0*IHKק#*740z`۟!OzkF Tf@"&ˉw\{[u_ץ]`Oyyte(7bdk{e>\4U!hy œQ#+#+XRfDQrKOlfr41-6nH)1=jo\AyY=Zﺊ#+Fy# =fhPi12X_;&-/؈&D+剕.!&횙7I%6mlst+#*߼h5`yTeLzDm,ݹ#+$d]#)˒tjEfޭ;Bm6,q3cRblfrP&%L"#)n嵍G[#Hcm'[C`2麰!05igN=Pp$Xj0C^e]w :wP( +8F )J`٭ҴOT1˷rCwG; MIg瘸(!1&^3M8a=A/q~3A}qBݎtzmG6A>#+W-~:Sh v-#*..TAbX U441nEtH7^ZQ"TOKVItDqH2$HMC߮EĉT~Dgl=zM7Bv<Wk@KEȴA#+q64(XĸҪUN3ub!П:M(oV~[~gt\`<}^vZI<=U ПT#*? 4.ZV#qn&:xgN|4R뫏ːcmcҴ@}u{#+2XHQE\b0AF:7)z6EZy6fD٪\]ţ`QeAY5dC`1TAXo$e*QM5CEZ-#*H®YÌccCB;:y#+iypMDˬdn`h#+$.cI6] ODD{5#*8±w(e&Ev`j0tE&Y#FS%|FX3g~vnt.1z~8[3ʦ6ͪ[ mϩӑ'̎p8QZmh*`Ωܔ;PVM!_[mmcFSf=R@mc1_C5-ƫ:ٶ?8<oj6ܑRn1v6DE $MBws=m(#*fzmE=a43B#JHXX () -U{j5BŚ<Ci7t >c5P 0ڱIqkYp80$',5ùC:˱`X~qXayi{q}CF#+~a]]7HImS/Lyݣ#Zy t^@>QcV+UZL]IK*Nj;κc`oȶ@A@G!xLRx-n:>%!Dvx(6)vӽ#+6ڎ#+ɼIJV;F-o#+OX#*.:1jhD7M]g2r3eI(ijb<@.6bsœ6Ea`0cZ67'8+].aZu `NM I42 ils}ncV<fm !(n,~>)2p:䤡K9S!mD;Ӗ#*֎)=0b#%To,ڶ54"N'"1$@Xd )ci)xAQ5{؉TygWKgQY˕$׽F蛎JYY1 D1ʛtik IœTi#+uoB]O#+3"0EP)<TG$&I7~WEI#*|qhPp%ц=;fw;}1XW;?(lMgW,_1V--`O0mB&坹blqD>yIeF;<A?h& Jzv#*-d l#267'}_YtNWd;=/fRͲcR\'7И{15HtΚ؞~ǥ_iM[AJ6E;#*1(zZUެM12. DNA2D\69/Z%뗕kMv8ux#+-2tl_.#*A$Xfߡj7<YG| NQJ}Jڊa"o g'l/Qwvr{#bO@w/&!EU<̆I@k39Hڠbz2Ǒ8zGV{D#)z#"#Ƞ Ei\v.:zW&x|n@\41i%+kx蘞AC5ƕQʃ0FB뙌012ϟm޾",#)̅L 0~I}ڗeC#v!`X#)$;#6CU*?#*vg~ GT$'C #)7QnVC@923^ȈpIcg{7yIvc5#)s.1x"]m GG$\NjI6Ӊ)J{F`??[\mÑD yŁ)x0Lcuȥ%[YpF\#+Q*ی[&-hKrIpCG}OϦIb%.`&1CI5W[摟#+ݮIHB4,tJzqU]nkt0X=TV*y:vB_aG39#+"q$@LT&!u<aoWziUKOIC{ !{MM"92I#+Xv\sG't&A钒yYGfXFީ:f 94g|)ښ'm^8OzLwLYȄV}UrlDk$4JhTψeik;#Iq1#6O:U$UrƊ8/vd7'hSK'pk-at($x2 1~HO#+?p=[uL~;ii1#aAIXI1A*ã l9HZahJDG9|8#*Ӟj"!4~Uc3h#2к5(rd#z4"TĽ~;DxZjm 9QƈG2'tBw#+ϥb_;2I#᎜i܌"B樻㼄t&p71$RLf2TOsũw)nL9)_Gˏq\Cۺlp] EE߹՚ORsK`5Tj^QDT}1 D7ㇶh0*:֠h#)Zi4QRZR:$:h2`U6eV1#*vYƩ"er!\30*+YQ_Fu-Ŕ,dDQhtsc%[R f{3Ak٨ӕ %dP񎶖颕@)է)OHg}b^j~8y$*GV7Y{mL|6w(V$s\\sK[Ʃv!sYPN+3ǥ|msΠ&:/OK3m)ӥ鵒|tp&`ȃ4995l2 ۙ{#*-\d6V^!6xUrXmD6s*kb'&x\֮]Bfێ)aMQ|ѱݟxvbTci|#*Y<hYo F I#*K&5ȣc:=ֻKtϜ7jmcЮѠ)*Dw(:@*as6Py#+$E)}'}'nkcg6|$ PF詠or8'je;MFlrF= *\:Eٴ8بzi=Fo%*ag37s{>g:$qCnsl \Œ=<U"x/O`,'{Y E]*4XAyn ǥ؇{*6z)~!/;cjQrlJ{&y%ݍ؋u,vut=Zj}o8ލ}s^G ap%N4e iQIe]RQحfD߬FRkd{⛽Wv߯<o Dq¥cfFVۛ[hr$ޔ:W?QVܘ2(uOLnߢ|ɇȶ9E|H1+Nj",evYsBSF:eӇɚH6]|icƵL#+ e>#)vy=<N\봈I_*Z"&e.Pj⚛?ߟrbgIXȢ+S\}uF/>͋u]d:FNxbT{IZkΟ$r[J(7ZR:چ齄vlh[0^1De-FsTfW#+/期LYf#x#!#)t֓nW4Ubsa1RXߓL).XmzQjJQgXo=ss*vn*pW[ڬ:]~p6 Lqi1޳={j_h,`RWs}*F[,Úڌ2SXBX>Ǽ9Fmd"R-Kk#*6:#+vL-,Ztw&A%^]7JrpuG8/qX4Vتچ-. ًERt}yuQ[Cos-&RS*:SʤR9q%y㯳L.ܧ"!̦9B ղ(YI|m\t3slLޡ -k^pmViT#+3XW (]Jl_'AZY/#*qƳ 2( 1Qٚ8Y]{>Dob|,KӃk"3QCz΍-%K>qnAZW\e'(ʺd2^$'ۻ*_F.M佅LBK#+:xQ5.#+y*f_zW@]뙆р33J<K\Mm%֪uqx{6^qtoB%os4Z~t nja6;RȪ!m2h#+GM q rsvyhgImw="]Z H&ѥ)W<0r9I[dn}mKTYyQr:5vyWKC; BV$p(6XJoLPP#+.w9{}*ݧϷ<#+<e<lN#+nBϲ^[s:TlvMʗڮ΋H!a-9<FeiE`#*[vF#*maqX'd+1w ۮySeUG;;I㉟/E7O~Ϫ\Ҭf#*-P Rl(TEE&XǎUW_:#[ ss}vZzm9t͹en"7EoTv8#+g8 {fj*b6hD6&݄,Rjtq0'H dJ({Ҵu>H)#IY_#+Jp|JBNaỞ`Vjkٲ!y\piR-A b g=3KiWƕ}ZKQdvivW#+1Yjȗ|C_(يG`HU\,:^8Bq-]#*S~43 Sjn{w46]_ b9hx #*ӓcZ#*jZt+d$Ō'[_zZC1M+g#*#E-_+g .; @\#+59Bp!M6iPg,21T Y)z>/#+,:ν1KSo@@28 )Vy-U~vC#+%PoYZQƜ6ϩÎ9QT~uj/ EJFjmxR$ d5Uݴ_#+q#<<̈GX-;Z/D5˷WiT 2 bhCk3d9t{m!ˏ|xʅÏ:/f//xN;Tǧ?]uG<3+#+^w)ZsÉDmyL?港LK|RԎ#*]lZKMeϔl0"DYH]n)OI: |Zz]~77Mts%YI QNuTLF`oѵK KM)U;,= xmDt8upq qc%M)l*jt0:>1[OS;dē{79ȷ߻ӻ;+o,a0CuqoL_T:6mrEK_s){g6,e>t&<'k+Ŝ./ՂTې!#y FG{S6:e#)}+ˀ|1ҷfN<1ӓi\7lQloa#+d$c׍A |zպtǤjR<G{}~>LR,9c C;-r)ܩrĠ:9UNmƧ}[6j5tM x/p_ftJ6LY5T޾pGC{ۈrDK#)C,#+h:- 0!C$trYiNWaS-tnZLCa.U*BVulJ;FX#* -8St,sA<EY{As!LKl{nŶN.X.BNU:i`BNTj#k-RE9Qr>ȋ3<{s3r;Xz̽Df)9Ku:mhEdׯÌl}gN)<V56\6^x0}]`2+gs@x8lkްŭpAa&=}j,x=xŭ<K|+9/+,| YRgl%k^R0~3{,R9>]>y#+tTl0-AnC͋>D ϟ{uv&ɜ_UHȔ##.斎ZL_~1/ekel}XqaGW8$i,NHs<#+y#+ت^@^..NXѩ4釅e#)w.mMB6֢sҽKRBa֚_Ru0r 8z\A DWNxҺiU~iAxb@ is=G!QDbJ)і"#FBW23fYRDHҮw{#l/e3-v"U4EPMwg Nu.b-ڿI!"`}f|~ݼ5H^wlDm^Bnb3F۟޳j!l /VԤk$%Usׂ|&i{)OJ l+}X}6z^Vsk1I0X>7pb` -e֍, C`[ȩ{ΎRz~:\g5~:e\nݮPU-ѸFqkZ9d*tۿ5􃒞[r|o)#XW>z^x݈eQʃ#*`1NA;o$x<Cfc;ԗ#Z_fg~A-wD s6כ:L]9˯P_qNK>O_3E>Hا\ M_~:'ϓ\?M*=Z WxKcUb-[ 3`#kcdۣHdIZVmA9W5~!ی[gyov"Zn#+sԤoaӃGzC۝ &(B~[P:mPG~Q@E#Ta#G]8s>_Xxhsrxg24jf3 (x!{Ux+Y8;>w^`q6ɇs%,tF._\T Rz |'+mloE}dxCb?ʔf܁Wքc2-Ix!bGe(Ô"LI'Kbjd¤d#+oR`b&d#*}g:Cq!ezFy7ѹYTJ@ ;X]M;otQ_UgXA4!;Mbĺ#OC<_ˇjtnPA4d) '+U<ʝ#*$r7gˍ^k^˴pejrBǿwj m$?;0nf1U##+ڔ?򣢦5p&Z~r"Η#*Iq~9;WmڛTƗB!Wos}1+/82zRB+MtgmAH Y4rЖh|K::q,cZ &C(;&r/@tB,_@יg?2/NW>2Ġ#)PO֊9Mw#(JL+G;Q@{<K ! A(AF#Wyl.%8vλĖ;e6Hړluէ@bq2"Z"@46o+,q[~-,k_|FxI$"ARI&ׇz:r:L-&Oxj--<ّpj9I2KW}& h'7#+&-{$C#)G㘋`$-Q]ʿ8,dU,FrޛeE'Po(͡md^H=D-ރ=b/whYbz96]0nCq.|#*znbb5idԐҔػZC)r%a{R9#MLЩ; $=Bj}HC嬉?Gة*!#*KwkS?ܘ#+}s"kU@UVeU7oԿv%\īB#*r\Edн0_菳|\e'gs*j2l2yc% 3psy5IF{ kܝXrȦ~w@8>{)LW@%B!kRw!%"_VU<'lC&Vnp#+d)4`/\д,ԓD xp ƈ #+Tv%g#)L#OFińCϋlNJa`8&`Fvk}N =k!z13H7*NT] ]v̯>[0nݮiGTqtEʮoj#)P ̰L{Ww-%fᭅUu%:F`oL_]P#qlQ#)}"b ^<#{o9*]U׻_셑md(߄ 9_7J^UAުBzJP+j_#*F$zӇ/[O2FPU3) K^bO|41 5qXG]u/q<2=c-QuH@i6ā"m!;ϕz6~}u<VF&:B룻##*Zk۲٢\DAN=W<1J`QfYA7M*"%l+a僿 (1A&Ç]3hrr^{䨡¡lΏgV#+@D* f#!Of[՞oen+݉FS? q@s#HHj )2qށ|7vo$L625g$&ESPI4JΎ`ӌ }#)zz qĆd-./ԈyuIQ Xn,-p#)DlL#0q'#+΃ΰ_N!^\:Bn/ c<`IßQCmQyE㷤@iCT%.T1rLyeTDgF|Fޓ7ȃ;#oHՌʼn?MϹ\E#*40B)hB-L зUrI(HZa<!94. #1'֩#*$nE"#+ xw .Oۜū֑BzbF;f-]z|BshC%B[q J)羗\l[st Gˁ +Q`-$9$(!=e,ˢ#s\{uܣUA*O.6yRN-_x-wړ6JsLC|*D pG#*AΌMnioBzz2@ͣ5#oE ܮPءaa!# #*0u.-?Eo3 IQq-,fG,ؖEKn%vouGˬFsH3s|km~$䙨 BTZ/y(й.DSctayﲡnS߆,7"9axhU+0k^2/$j +peU?~7Γ.\0`P#+q7n%(BRqT^=G^9w\so{I#*;[^dym_p2S0JN:B+o|`^4mC{qtZ@zp1$@o"0CY$SimI(g{w,R#+ZH#s[9Ҕ98'5 Zʀ;*9:  Tptlc*S#*yv{mwT~w_(BgzVFrWyaǐX#+ a#+QĂ??8lu8'.E8pI -,ԨC)yOW+dFǭ>Mj~5'JW>-8JV`s-tHm]pkefõFctmFhMr k^`GQ^#m#*#+t3:PJ70򍸧KOC/ooOߚۺܳl3$m]9#+T@5ME#+*C\t(sݰ=% b(laM^~\GNrjμߑnSQO Ijcasብ#8o;FG[1)O!\Hμ\o$*В?PbJk<G+Dqd1h ť+v߰*݅(R{òx#)i>]q~L+|̓wYgAUOv 0OH19q<r{C'Sߟ^X $#*- CȋP#φ?[S=iᡀ3~t@/z_?^b*Kp#+Rєʢ2Bx/Q)Yƻ81 >F:9V^"p,_©*",{|st N")!{!Q*E6F:jaY1k{ۉDZyJGJ ydo~#)K|]`hGgYE>AR?xpBN#l#M_VDb|ץwۨݙC U.Vfr;+_/LOPOyQ(#)zXlD'?}sgoo?7}?ӈ/Yy>Ww~`#)ofXU?SXq|گC"Q .Y0~t fSQ#)rP\ D :D,be:97ء4#)HҀ2a #)+#)Lk),dEI:\tQPY#2\5R&MK`K%LlJYFD%P*m:/U/XK8,=u* bwy7l8#+p\Bsx$AdCa5!^dˢgeн1Sfbxިh>o-cGhBWz}w\9gEN&ܨ>Խ/2GGom_f=3r@z H6 iR(V npm"&wa!Hqs~!/1xۈ댫{KqF|!Y'YG&͊e'uZm~Dw`wFjVP?Ωi$gWvll<㢲YDtZ e1!!އ=ΏB֚c#*q{iڬ?8k?`6bW?"?9ٝ-%v+FM1fY)TZfJbEor,r4UU1UB}j#*O{r;# \C9pr.Sp70GY|HDHg$" E=_M-6i#*wT7fRn2AðDmqL(t8d+owZKBl S;4<'vdf-04 (x#+mVHY֩Fv5ѿYFE#*:8 Tct\So +! :,jθ04q"z^2؆p 5VD9?h4UtXiPM`9'rx#dN(PeDT#+H5V|eY*ث\\%Z櫥z/:׭uʫ)4`HIdMWUt/>;@ApCYfj49<xvt/#*-ro=o"tkP/c>XgOɴVE_N9#*cV#+rë Y_1rUȥXɣ@Ǫ)e&I$eT‚$dSZOx#6>7+u9l(փB*(튄ݫ{mklYHJX%PIR$;<6t:6[@<X$&I;6!6!Ւ<2 2gu: ;9ȡ(AVn N]S}|x׭?7$9Ř%yRZP!Pm'=w=Gpn#)MI glvoz#)h%RӍ7P5"#+@Y%(9ҎgL%J+"##*tn;2`,!UHw ]!!#)Q'rQĵHڜv8(SUXԂB, P+ו׃<qf$.+|# TU<ɚ#*A#*{A4ً5H4n=BBy>%|IB@D5dXc<uE@:oudlx"gwӊyNჟ.dzY:,%3g`}〭#+CeoP;c.L!o@?l+ 2R`]Qw4lQLHR%x~]FKBmT̩7ﲓd=f4{C>gJuKwJ9*L6T^ˎ˃^&a Dn4(#+;!v@Ay&6cי߻gӖMr#r8꒩J-JD("`kJWuqdyx,Ax0qǎ4 ټT94)H齁e<ꝑ`yFEWtww댪wTKFN8#+KFd&d1О'҃==[ 7ߜ/7o}xPp|;mU0/Nz$΄ج\4Qv.kyOCh*r2(%*Nۥv]p9 :WqG;aL(W:궂}k]]-bz}>kҸim{f|{=V #*'&#)NlbJb{KL UA`wLjjm 1yw&}֪@TLv9\I^:F}r_MC $&N1҅@"1#*@u&{kuvPć"ŗ N5Q\\DA7')Cq#)r#+DhRHz4y.(Pw?kVsok0 P!3@$QM ZlƖKQ.1 X~kBֹs2WySV#`T:l#+wH#+uh~^A~s]Ё>c:R-¡ MSvƛ-@@nXʌ>ÜL6ԎhQr(I.țKf$nָ6W &'8@!}J#*!B CLIlLMm14G6v8F(fZ烉EP{% r`0 ۟]j15=So]i?'P#)P׍K-<*x,Ԕl}Cu7aX:0!,xDC@m쾷FÓ$c y;oLIW8 J_δHM#*~X*ve\ٔ<zd4yo>pβNu#)KX?xϧ@n;0>#+'AJ/q.Hp_b}[<,keqK-n^@ ׌o#+s&)G<#)bidz'\Ayp>c8v9&ycımf>: v+#)ę<5f  e !.XZgz<߆OՖ7:%;xߣ!)'_·\|y90#*Q̈jn1Kr[z?.wd'o|rGQX:mf|"HZ-jE_Q hH#+#)H( eٞ_*43#)GDw#)G#)"kO^#)C,?=3Axt|'Q@|}"ϢD*??o9φ`pFC ^G3Za&nyEukʽ #9r ԰}%'63nMQםO7B3f{spRJ|}@y]nGEZbᑯl-͢@wv=1Kx"#)ˤ~P!&-AQ Q:^G#)@ܿ7jLjJm[Rlve(Wnp'y"H߃l ]o2L΍Y~ŝC375_[Pb l?,&Gʼ ؽum{~aOe'CJ2>3Ҳ3* {G޽˭>#*#);9 h`6P#+!#)(|6OO#:BoLN:?mwg՝_Q_Cq3"n6 Ϯ;mP6CCKyT"œVtƪ[ÍE NZV@L~q98:Nαw6oglyϤ_~N4lmC<{=\ݜѴy-z,#)#+{2UnKX} jijYfs(/6Gݤ4nkyihPa*\ŷs R#)uO9)W#+]Eg"aOsGox|D#*3KݭdU=#*%ut<GYL=~<n:wӤz#*#+ */HP(HCsE{򧮼RFW^DąH0y1AvW!+j]C+cg(+P"@>#):u~w=L#*_*ߘya`{;yGJ~Qק-#dy9K"1s8l#*H⺥zcAupZQfd>QPhp;S}yf.Y(b4Esff#*!꯱6(,yb'?j4p|]Lq~:pOL#)̃=۟:EyP9W[wA,0d(Sy 3Ff LNϣ;y4[q 7yqXj ]Sv4Hu3^QLB"$r.kߦ}v֪M­džV3BADR!)FSmaz85Ղ"4lFlx\C#c`YJ=]A*w'z>1Om&k*#*x-uvNwe2N@@+[(M]dA7FIΕAp)#)TDENFr*s`#*e:DHyj"&]#u5;]%'yfoVo5~mXgb@CZ 6x ?Pv_dѝwjsTT;s.lxt]'meڽatp.C+i@ ,770u#)%JLRW<zhW I!mύsܘ5(Ǥ`g=Rà5kֱxkQQ5#+з/+&#*P̚H՘/<&j-{cGlUsqvsC`p,*)G<nh9YXխW`5(xwlUyS'ɞ&^ZF+z6脖^|>褑w.?OgEvk9~$̢CH!ѥhbWgzlXuA_R"e2"链)_Fa.uDumlCoEcΈ[ØN1YB`a <?,u$'F>MK"t0a#+5#+&LKܠMbUy`[hX£2S>YF9\3~裰X##)la׼ ᲫcoxʯPr3u#* r,HeXo9U^zISeNInIB&V # pqyCzY]rѤaņ7M~XEeDBI TܳqΗuɤK9D[6egE+KUy 07QYј AyEg'/HG&'w=}Sqou;uT;Ac4Sye0vԇ"#={_xֿnX" %.m_w)UWG뽏*%o>gh9z7)s>h AKՉ7Snz)DT^Gm(y#*mTZruH[l=/vmo(PaN#7_h➜iS$ӨRwѾw/k[3읈C(Sa;`Ifuyu)'GU*<x;I&fMI?S2E>rΡmmXqrߍt[)(@Iw 6ywN;L;\Ҕ Xz=nү^l9$d-wrލ]j[(beW8s_cA*$=.=-z"c:"X=W+~d0zOɢ;)p;}ֹv=aɤi.#+T#+!ؽLJ%Hʔ;pwJ88#*Jhы=~e̒㶉ۏg-1Y|佧A<yV!{}YQx}{ڪۗiY''~ms&h\jdWMأ<i>/K:`g0h7tq~{]\ulE*mk2#*v#*;jx9s6xR \G }bq|j't>d='촘7+pǟ}ޠv2ʼn!̝lJ?AZ9H^Me?=`yګE` ځ,E^"G>ښ@p KcIgy`PsR( a_sCrZe{pA8CòtQ5j~2'uKi7pRSᚱ>5G S>kDmo IN#+r@({ 8x0P\BQ[čX#+BiLxrD)fKAMs_p!)uӼ/[;h/KyNi6+[G$oܵQbPp.2|b龞h "&jt&J\E/z aݕ.Q!"_[$=؍K$ÃΉ <;24II][9ϻotүN*0VEG\.OgĻ~#+Vf:'4e(b<f=Pe<\x(`BB*D0au"oo(e(%c:FЮa`pqpw;pĸ"@ "J9$IHQ6T*ꘊ,0 2B'}Q<P.TxYρ:)Jrᄣ) ,, 낱^s¸ү973-(KA#I YoluQ\|םDZ.z>D#) 1=?~qfp辬jtKd{ƙ:W~C #=Y?E"r,Bj~d;34V?2?P+YTI#)V\-ǧ=^19a+7>0ׄG3f&8Ly}2WhB?}٧xh=GΚzCmA.zDg7v+;I ˨^#0Wj<bO8-3J~)¤H(ʃAvD\{5^_j(ە!rLFI$/`#)bSϾ\䂀4#+okaFf#*/?(6$=Y*$?in) `9ʌH6W}F~@~P`H#*M= D>mR1+7I檻5R@#*)h!nqN5']$"KQkgۧ?=EDsmr)DVcL4{xu]h,` c.[ׯ #)U'57 gFe#)~z)Aa`Aָ_!}$zf-nfAaAɊ A/pF `c̒&k)Eџ+!]?e9#)X޳Ě4P)M1@D9]v^#4JXG2t+{Ke(ֽ#+0Q,+*Jb$X Y-$R%˻(4g7nmW]fa8quW S>D1ʢ+!:ѯ*9jW$3Vf)*lWؙ(¶y!vQBTTr"h4zI͂A64JPoZ8}36#)3TJ#lU#*>I{^n; {$Pkma!|"Q|AYJ̐Bi:#]4 QG8,^@28pQVs;HFm?Ou`W|r@#)HWx8%%$cU^oz?k1su#n:g] 5|Og-VSW3M+곍"4k]]!c+|]3SX{^%a0fx8y =1\`zN}ˡ9)h޲-#))F"It@*JL:Tq[ǽ9ybSNDK*1A`a}܂#VD#*Iei8Ze \^! nEm׌MoP<&GjNJ&dqY-8kuZ7s$ qL4Av#yH%XjBu3tgL iqTGQu0D_$k:zgj{9i+ ADZ#*)MG3}ޣǾwUsvKyM>Ku!ˁ:q76ތ&I6uT3p$e)V#+lLk3REUДZD #*}P~}^psM㝗q=rI.{? {߷>Y0BU!"cZnP.f[%")apͥtQC:G3fm.Yr&K}DJ)6umv0 Hp#*u-}~B yVU̬#)E{ p ]|-*uɵ*,}rVY>Y\l1|~Xc}E:E#*;8ؼ{ZCkNirrT8=K#+Qit"}Ʀ{͎7 ^oa۩"籝j$rRfkݢ)' } #+tǩ#*WLK劫[ϫS;uݠyaqZ%qulv< d-#*6Sȱj[nD|^N2Psg&<W./c.M;>/dMy?RUT A)S̗$tOϪRJ0` ^#)moIj(‰߫C}f#*NϲeQb< #)pIٶ@kz7s8muq/H2>t~6~ŧB*E@9[>RLQ (dnLلF@dd0JD pIe!$ 82#* fiY;m!FL=xGWc'dzuQ!j߆a6ÚQI `;{UR&nFYKpxTR⒵B~X:^zmVPG<u`06 `vqpl#*&s\]SxhմxQ; f`A]@sys %(tceFzX#*kqcg~zw4# sqg 3'Zh۫i9 lMu3ڠU)#+NR 4̡̅AClݽ\"S#M<klax^Ʉd:0`-L2Y\%kcc"ɪ"5e8:"55wȍ'CaunNȒIؑz?o0xOr|IRB®Ȇ%rDhr c+ k(HAAʃqN<Oy^Π/y߿#+Sx##*jeA *{mr:a M̀b&ifWBVdZ>R#2P.=BriW#)l$; . T,{?6PHӂ Q 5A~Qw|'x::J1Ӄ>k]NfOc+lr]h{'Տ}GnŰ%UGDB-CQuPs_Wu[PsJd&.B$M#*#*b܂Hlyၭ=2MZp܈wFlZmoXB CQOb׾DXkSێ/kq6#VPKtsTK+Au~km7ބ:òa,&q\$@ɯ׎K#*J2uvݧ3ǏH.b2e%kۚt=#*ctn^`$ Mu^Q`mclR5:<0yuvY<DB-źy~oғU^uDo3q" ;b0z߂.hth BmOrv@0(㍉KI¶[R9dkb(e$UuXFnhi4 LBc"Pd#*KG`/'8BZ@0\k#)"p#5E`spZ#+s pl(3"ȉ*+9Oox\TJjY _$#+Ժz2ߟG5.-yxw,)F8}zt oOGסQ̉'+Nx;}s鱭ٶk>IVssBiT,[g$ȵ4MF`{~>;Ͽ-Z'kjy\PN#)B1۹yy#*՝N'e:͟/_yG<xpp!`o]E(,DcBVD#*&j?`ǽa#)JؽYT*Br)S0e3_wsBO܇$AaAng͡FT*@]ja#`4E.`P-㞷۝ok[3sGZ0}SRfñ.Au q#+iSrk8*8BW_J0T6( k?3u]*=cڎ]1rA Z;"dT,+.'P%х\K59=M@e#*C\73O =:$A7g.wKj]q|t1f wՁUE]-Go==Cd_C. Y@#)`tk_?Wr0kmd#*P6}unT` ie ݠi#+vB1>m>StKKP}EjIz.^/C`xk\;&TZ4 A)rln#)#* #*n QX$ j<#+ǃ*CؾAV|4U!Nä+mښVij9(Kc]qXK" XYHq; tvHYlN鹱{N#*cr6I0ű Bt% w{qWM)'NY @ V6kM T(X# cN O#)@#+]k"M~ڎw겿~մđ#~X;ux!F2n÷#abߍbHzGFF#'#+0E#*%ArJ"kmb5j"H?br;)X`4U{5mGs?y&G&mfkBdٮl2!\"ty\MPg7;ϭ_s,uvN Gy6:$OnK,͆$'i='Y!as#+Rc$a%`u)]#)S%SEĹHsIh1zGz)KܵJG9jUG,Kϗfx6#.M{={ފg6=!FA#*ه<:LH;ޝ53ijlfIbQMC_o"d!_I<`''% .%9zlCۇ`Ez`|cd AL+b['#+K鵱g?Ccjz\[Be$#6)R<Aychng+5zV4iDPH(89H6(#)4N&F7yG2MY!?ІߩH4P 6㚯PWa9-E V#+Xdxk #)O,#*!n#+TgyL\9IA)8娦&fe"ٸq`v$+fF{fUXSQ#+D[.7~q9'g\3z$:'1`g7-"a@5H#+ rW@#*\?́UJIpV˚6k]7 #*DEAAUWFo;!QEhFhiZNA@ٚ|nKhjTfW)H5# $L&*e֦Yk5q2]Czկ5Ոhk[M3t#cFYF56ӯ[}jrѵDERLefnv#*ܷ<[t!;n'jfwďQ4@z#+.*00(E,nʸB)(@vGƞRhބCeK& dTE0\ %Y,l|&#6@SUެX0IA#))l>Pd:۷rhY+{I#Wy]:e%5&VKDux#*w6ޡn4saJ#+S_S/yX>p2{Sha,s|t应}Z6aY7箜fJnP.nS)QMݴ&a{w#)x;G<y޿_WQAg|Q:7#*Ԏ66Oa!8zu+po k.%G+p=xʩEIJR(oi#*qo5Bق5K#)J,H)|#v#*r#*+P é?#:@Z#+(e,OHE-N7ή k ܬpv=/uaG ,p§_ʉj#)"V?QB#19Y B?&JK>Z mDd\n>i=9e9{MdP"H}#*ws8{A|?cr?I8CeP}XxyC<#)#)@#*ϏjLIF zwy]ǣihBqGSi*o􎦈N] DYdR2>X9ε w 'Ƞg7;Z>Ht*?}]]%콅x_dW'>&X1UD bVnZ CU[ևL 1 <FI$ @&!V+ ]4d>ЯI>S;GW 35[S_B#*/T ߐ20U*Z3#*û#*|5 e_c$=#Cv#)u0q`8Ijoy99s0d$ہzLjb#Jp2nF!~2s$qrp`'G/Rq֡CJOg@<Dc *RqϛFcČnF=7;etԛ:y9L*<5L$EdPZ-hU_; r%"QC:KplV#*p:t#)N>0O:>|ޠ#)ٿd@<U{MÿKh )SjEx{wm~¥ b#*a,nOlgDMI9QGIB8rxib*ΛH#)obJRUj}]v<mlqLq="/דK}DoIzC[a?5=CgP%3F;OA2AE!PO|ٚ.8\X֨WQ$Nz@mr`%~w}x Ykw:0JPPZ8}%h"9BNɰ?&p5S*1sF7.WF%F%LV#)I0*ɜ K@9mUQ*̀n#+7(/`< 1R"QTv(&op~9k|4#c#cΥh,PMad'Qbr@)c)$S6'?jS3XQXJ#+Nd؍1HGG4-s=8گTmW/4UXS }wThYPX-Foy[\VL6T& ZUq*lajb'W<qp2ϳ)ӸPPo_ 2$Z)g7##*nĎA:ͯѨ#+lH&j X@FDa#+2 K8.p #)CtR}(⥅R:뿓xBJ<:Ny/H/!څ jѻunO>)'74=#)6:#)_fҍ#+<Nv9TMlsrEdͅ@a}VQ=yd:OLIsI LPQE%O=g̀iZ/wۂs|e6zPH_g$Mx*3&jB!(,H\`ɺ;kRZ51 fT<clI$}FGǍȁ6z#9哒ϫצ2 6)ʄ8& X>W̩-#7^h c% ~( !ICT<eƔ !W1´ww3Ԇ% UASKȴO|Ok #* #)PC2?8'Q EE%*G 8Sul2/a_=ڠJb*IR{\,q'TU, V) B)WH A8ӝ#+{l|tثH!B  fEq:D3O@?,_}rlޏA#)c#+FpHPLWQrNaz8yTi#$Y:#*)ya6.!XyzK*ü`pR(Mގ߃*|BUBP h q pNX?J3!_ӣKb=0:J@j1^/.o *T5UʇTdK 9lQ^aW,I*yPrGF|w#+mrT>UE:5,OZZ#*w#+Tsa%j:`|sx<9x`7#*6Og"w`PjI&Tp2߬rs3:W˦]Kgϧ;H%C'*wˀ>;@ rA[g+Lհ"􃚡EM`wA1!#*p #h"H@otۉB{P^= [66"N iUz-\N<gCٰ5#J B;}rTrZO!BuS(/bΌ>x̕l/lD$wQlPʟ__-:>4-ҀxBH",bgrx#TɍYD>8ydT**n %,"p~6(ϵ 噝l_Ύ7 Üw~՝o{ޜ u7kzs#*DD#+˲*m-h `>rB#*A#*!a#*lu84BG:R쏳WzY~6"g ,4:/.˺0,Bd%hAk:.Ywn}Z]ttU_; U2H?ᡑ?zVC쐡,`067#+/@zÁaBv%E+{{w}dP(A*JnEލepGٺn]C9Qp]q#*v˴[Q1<kCS#*J08"/zU1(~ nx<"2+wi_W;X}C C ."=='3pFP}Ծ޽#8&`wl?:8z/(AJ8g ILoz΋ma=j$@;]wwe}ؼ+!ku7mQ8cBaADBOoݮp]ИEΰ6J alU6 dcc.@M[:OōpOOBSA]OOD.?#0Y?cCU*9ոS^t$u#*8oOR)ߜp(MRxW{*Gr`өrd/!"C=;̻wu%#+j8$4̨g@:>\rU76$.w[S Zօh҉&/Ĉ#)a`d<o NgP]Q,EDcN_jd!ˏLcvfh@s{@]hD,}rVaorX}/Jr=B\~{aXC*^ޢW.1s!! [0 o,|]ejft̶#*#*]*EEFeRC,D%%D_hq=[VkAUE`<5030dz&ϥQf8M!C[1׈0~Q-1 *SVo Y\fP#*DRH2[hV&X`i o pKʁGOdfWp+ju'wQFH+\"z12'CdR_mN !xׯg<L!?PhRZ,BRTz?P[ϑkhI  ~ qA:彴jr4wNλ|aߦ:x~*=V21UegcN??2ON#9FC ;C#+r_잵VPq#) 1#+AE9{7Be6BIH+bH|? /n,nQ<XǓDRf{LA/!,'ɤ}/LC0|E:o#)淉M&Y&\{"m|<Ș*6<}|<=`͸_W&`ܘr 7Y h3k=[eHuX+óe̠ DgZx>ix|šky!->;2ı^'s"*St^lo< 3@k(P4Bx"sށ '*7槒Owa* 4/olr]lo:P=?Wnϫ1Bbᤌ*fv5à؄tx~JE8d_y%wBwsD#eўNѿPh8#+,j{(*]{anWYJ}GJ b0o~ qctWXUr}oÛzo&DY-8Bub8*b #)QA[T?9 =d|0(Cx:'IGGHNwS+-5k~W9E0{}#*%{U:V^B4xVscߜ @uTfkcD;!x[[Sufrҩi˃a:E8W+ç)#+QAE`wRwgyOխ&iLqfeκ`en؀Qz('ntZax_&z V*]0{fX..{pHYثmy׎00'w1#*h\ݶX@S.y~yel2ozkST!n" ELѠjJmz#s:Fe~@%qTl‚) P.lq\g]_I#)#) p^#TY^gߏTG&ʓfgc"#7wTdF괜ړsHY/<ϟ;w9OTg٘zB| #v&w(Q@ Vh39T,4ć#)[C !P@ا&j$E?UL~+Jȁ`P)@ %bx#GR6نiXO5(Պ1l}{Ҡ} dnTwͲ"/io5{mF#.̹#+"OcʯMB2d)~Yrr#)= \R&f.A )@!L:VA$)rRIǣzG3[Jn=B' F:~2t&fHG/;BdǒPvLdd^Hp1z<_ޛ;%Ky@DXvt!p}fbb!|X[[E%%Ov`9l=ug3ϋZbjD)={5e/Ca%Bqv`5$fEnnL/$ytOL\ 1VS#+äB>r/b{_#) s,Q` |\|=ihBФ+8_v4x)z49EJkuj"&lZ_}Ma//E5UVLq@sٷ8@u1`6 2߁`SP#)Qw#*#"1jEvjQ;I|r#*nu: #)\ kպu^ñLC@8j"Yq'r[ʢfN=3'>#<4_<Pw4G"Hbz#*8i|BHt\vﵷeTaWCX B56<OgJ>wWg# ,)'Cgsy~Qk|eBmcȆxe;+#*p}3ez&;1r"B&qd$ #*@ cX`DRAZ,<vz 4݄}g1V!{QiM5vi(E* @-iI-,aNa'#+x+@ n@sm #+uɬu^n(CwwYݸAq*$C v:URZ&Fqi"JdDAz#+C.2 0pD(ܚ&E~?fh6uyXyQAFݩ#+Wև(@H165 Pǻvp{{5I*D(9blDGwş0ؕU.J`pFΎC"`ÕrՈw<˃]$NP!Cvo$4m ڨ!0cKN5CSXȖvoMT0|D!ɍJ?9Myh0<@o25n@"j^ ns8Gln+ +Tp*v hGvG-2g sck.̇xԩh)&D0aBUj#җ:&e%Ds.5Ĭ^r)g[b.;a3X8: 7o<(y󷮷v9@Ruǎy[o-YClf|^sDR7ckfk#+2p$BVi-n퓤`UPaD ս܁,x<\ئfX=qm`& hl#*nX r:XXM}n>)d3#)a8wIӶ={ow !a*Z|.65s<JhFfފQEAyxO#*Sw@#*^j _ieQB b֦HAzi k5lL㳜H ES-ΧMPak)5D&ap+J=+lBuE^»bhloo? &6LEN`0IDWxa;X(`YXj60҃$9gv1N!lzgUQ#+ P6{"!WfD'afv  c =t "Z[}zuR˘#)rxd3U0P$#+Oa)Dp7w7Mo 6-KܘSPȸBH$=aɳ~#+4Ύ<}|Mt5,8Nrٟ6sTUY) sa#*=3z;B8<*]-Sv{O#BMp/ 2]چhtPEQ*4po&&r%j˪Ĕ07)#*ײ=sF!g&(P(X8LoEu3XMHI xFzy]N:9p ~o-fSlUeǙ2ffX12%X.5[Zm#*;qg]wNbiRqD<8ZK#+l7#)B6;쮱C/|铮e((*q@^zsϦ&#x2S#*3=FChYCS8G%kQvf92uE+]s% v|%qt:Jc輾JAX˸-#Vy9ZE&F,YKb_Niύac{*؊qX.μM9$;r)ADF47 Q`PNs**á^bs!#*kUZJhw*E#+ Rڝ >Zw}"tv˃^YxZۯ_IWfQJxף{q#t:e9o&u-L!ZZNVxn4: ([F:sV(#+#*daÅZ#.\,X|i.d_1ΊhpQEq$532s]"zuhuLZ+[^"H_E{_^Y-aLn1ù6W&̒ c90ILhs>#+`HA-DZa#*\'#)R4PjxIҖiknXaۻG-9!r=lrTq8 v;= QL_n+5E!T`yXw4Aua#*!DCs#*5T pTPt!Ma!}bI#*qɁp#*fMQCJoH^陖XKa#+ ԩcHs-5^:FuF nܷ'b<,!ьS5Q\Rw|j#Zq;v#)cDxM]Q4٪f(G1ЄH.,85IF+:5_`d{S_OK[3z#+tusYe+\\g؇"2E3 ޫHʂOJ{ $1ZB!B@&9͗c|<Y^~166l tV_VNכ˭<ސƐ0\`#)1PCnW(#**&`%mĨ G@PIyѻ#0LܦdYܯYTS}kl奵NZMlug5>l:#' 8*xd;D#Ϋ#+d#+X/ef3+я6#*MZ8g_fzy AJR]EDibѢa #*;!H=(Q6o!UJJP#+ !`ʑ=kӊyw+#+O׆z_fY?8tEdrP#)%/؄]?[Zvٛ&Qj&:>Ϸm̔#*Cg?itӥCĚRT5JDĭZHŁDBSaJ7JUơphۍP?{-X+>*T*ck(%n xwsH`{!!%-zw>I#*P*A >;iDGN-#)ߦ;,rIo^x)="%Ԛ#)aIf]ؐKd4~;H#r"*X4GZ#*ʛäA=0"A >|/ R韴z5(ؐ-hF)w]cTIʝ{m|#%Xtkp`]0D[YQᗾ3dvxHInbt:2Wy{C#)X0 zLΠ~}P6T<&zk7VץwEdwin\i/E՘(x@* #)4d2mcj墩i,TQC /킀ȊH""" #))zmKnS#+;eQB6E KVlUFւnb%Ȱ#) ݁uy^YWe5xע$xpMnk(ݠ6<o7LwrnY++G$Mh%+F2 L>6|,rl@$~=$.4e ET`{ OfՓ'!KQ8!G_r= yib^-8f|HH+]TѤt)mQ#)I$/,V#*_=}Z4w90Uomۚ4Xif,&t9YؼG#)N~`?՚ξޜ9<h\˕]awD$ -1( hY<!Pp2DVb4S{XR\l@1"u@;G㕪Go ]GwⷔMHM~Fu'1?۸U{`K/p]g^I gkCEW; ݫ.#+x=%rjۈ<͞?nĩ'z<[JA6?Iaw1Kc(Q(9?BUJE'g_9NEbzi5Rt'!W" 5";YE*t+e+):cbQXs-PBNڄU-6&n~5[Ř̑qU DvB422n*@]t$ 5%Z-٫I*1XM6i%lkImdeF)#)V1~_йmn<j}P;XI W|#)#)`;>öd[&UvBCc*j 21aYF6(~#)VlFTHmkܢVHcQ$4hx&4_*J2-#)0_@/d 5^=OlQQv)-v|5#)zNd[mK#* IU{X ζӌڹͯ>sE,#)X~6$#*H@`~ykIj+W6\3aJES0j*Mlm!(~m{wWޖr8@#) HAdAN#)5(HwI=8*i|F\"ndxW'h=b@OB4OC/؀p|#*B">! ]O\zjm#)H0cu!h Qˆ?93<zitӌ iyD9!6}ZitGM0?ZOve%neK!oIv9'+A#*Zb i1픉;+.9!N}P_ۮo>*NK2~G[P>X QC .ozSu%ٸ6E42T]%rNEX78#+^'I byg@"O#)bcFʇ}GU|>/ׁtt#+ Ę?|#1A>W<Q(hvŮ_#*9W#*gNٳȽ˟0ǭ#)?":#)f8Ȼ]H{w^ P,\:J f2S>bFzbr؝Zq5 #+P NC>+3wf!t2B"b33u(7CĀ#)B" )**;f'ήGEfnCD,?2ƚ*#*(D>އ‌WwkCEX_tP Ebx:/ˣpMPb7qBUjY#)<E?άpaLxGCE#)d8H#)E&F|밲@rٌ5@57u2 T;w?uedŠwwň!K/޵ďC!m01Y6&#*#+Yβ3//23q0-sitoeZqێ}kߎ\.(]Ye4%B0.Hҹغ:qfEE07~6ٮ2.H#+a`)'gNFڃM 3l#+vl`۾}F&h(t؏˿;j4L)>0.fmQ}t*;#*|!Gç=ӎ#)ɍ7>wa{3N8N\uÙ*C{#+~YrBYa b3H)}F7kեǁb8 Ja^XR잠r@vLxztuD+A*)gh&ܕ0b7PTFFQ8o%Ƶ>kv(M!;2#+!#)FB@w ZӰk2p#*4n;ulK lKr&Jfv5w&8WmomR"Lyt6Y7,Ln.-]A}"CPd^DZ,AFs$L94OVD`vi,nOmcc"Yq/7@uc/>ӷN۴Luzti9ML!vkl1,%aoq.¾\Mq(|<iF#+rn:驩GȎ漉qB'}sTx%'4f뮹gw l6Y;6DdSW/WV΢A|C^̚׹@i}tYT`#+b͒se'uu6r.KiKkyruڼQFBXQ(nY)v{.cewl,E"#):7\0mIjk-|Z1#)B$4|,`unV;~JpK(ԅ X#$Pzn p#+@"<#)&!{Sخt$N I@g./#*iA/cah1PSׄBMe>5H}WvC0_#)bn*F[fUnۦ<vܑNHOu2]kxnE.Ȁ"TcM7BJb#)#* @$K#+Qcj#*KBC{IcֈQptLX6UZh:-WEFzp!1p>Be١͎MglF2uE#)K:S-bWm)c[bfUT7&a&"A#)'\= Yx)ۋ0(FBgrB '9=z}]oXdMS6Vh[hֹ1T?hN (Kd*Ex֙1Uh1Ez#fCDň$X=h,yj󫤮U1UL gĒ5 >"qIy9o$B3{@]k[j=cj7kgKm&ܹ.Sɸs4 JApN:lEƭm2L?]pYԡR}A}!9Φ(CvA />!!hl5Vd)p.NE# кXX)sC n@d#*sV r ( HWÃ>4Dg1 $~̰$#)#+A̬ hk7Y LBFWS#*#*0dL0Pqgֈ="ɘj8NXRzA#*``(3S@FQ uu˯.,0ny~sxB&}$qMEdN#*y@TbrȾLk 3O3pd١9#)PNή|by֋Ȳ:;Զ6] WdǦOi@,I#*[6q>r.0\3kz(dܝf_m~X#*)Qc'HC-&#+TDhvOidAsFZ%α0g,#+Nw3%@{no,n< πh{*^#+c>MÿaTZac$/ \)(DO֔#+ yh~ b#+1#*A+$t1'?]& P#*"\PAqz(ғl&7e =ZFtHF}Z0"'?=4ķQClV}Gd GH#)Tjn(2^ԕZ@yF):6t:Gka=8fY]rmUȒ^X>w#+IT,Q2xQ#+\zHy!wD0Ǖ-겑#M$g!S`b>b1X2EH,ckmy3OFkصj#+2j!FNCY)hS`6cHϺpTc *aX[*Ee; mc#){Zv#)ƭ$5ُWL fI ٹwXU0Hzq"LYq?͵;D6C;#BT&嘌#*ؿƛ Î&C'Xn@vEE#*b6HB 2&.A#P;ZvM˒rZ@ر,\/ĥ qJDMCsAAa% fnh4Q(Q#*Fa#+a&dl;7-2tp#+ 1h E2V"""X0dLU)hn8Q4-" X 1fȭ[&fVI]sYML5# I^LƭΠ7CJ=9t8gf ˁ_;*1h 'bG+\{HƉ#** @R #)ٰk/I0GØ̺>|ƥhYJ)r! .6XD.=ZΈ68# >eр44-Wt^agtzdMtS[w /4vf]60TSB(N5gAF]!&|#+.{+Empd6&M+&Rh9jwpD -0>-D9Kg#*B;ün ~m9ggähAdN9XߛN|ߣB !DDE%#*&3ҒCK0#)F$I :$sMeX<5\lda#)!y6:1@i5i$#) jM%#*7&&Y=5{Q#{ /NR@Iu9Y^|6b]*#)4&̢&Z+NNr?#+e),̸b;}< @Tnwx<>xHB~eA`VEW-[cZڊڹȀ2(A!CB<5__#+):6۹W*@33p:K<C<u6HED!$hILfYRLRjINZ1Q4ԡ"PZX׿nPɭ&,LɉfHTPĥC#i,)%%0AD(ɒ5Dd)24iLcIM)ܓyS#+{'t{?++UW(}ٍ|?uّ;!;8H*9>s#*#*SrYqS(hޙ #)#37ZuKEF>6%vۋm%A<,]\vidZƗCZF|w.vc n6_`?YB~+6& pʦAJZ^oůbc-1Y6#d(a;3çQ跍bLQ43ٟc7aAfJg9 W8Ć62i\[/czCH `MWSaO=G"!vNJ/4J|,ܸϞr%܏}bD!UVm\jQJA(U$I#)ؙ1[WWpL~ݶYw1R(2F^#*>p}1c"$o7|^ZܞrXLg/.>^?#+5#*j*[iӇe*t*.#+mf8XP†Vڐ(il_zРQgr IWŭ` L|dj:rߡe8yacniq=xKC ͮyap& EJz3\{e%+ii(P?K GdpNB"T]SbY=P#)ƭu;/N6m̽"lG9vefSnIr'SV5:W]v>OA8}_EqAi9*HgtwԜ<!yCseZs9Z{K(;^禷;{B{_;Fp"aQOJ RGkBkbl8gm]%|;]Ȓ*&Ps3 j5'2;Cq#+;<2 'XgE%p@kṠl5|*=W_ʳKh#hc#+Z7o6̚Œu56qFXnLQ桦eiiKd hѨ0Wb$YB,.7:fڊzsְ'|JҔwSU/^,3e7Q~ɇ[#)Dg 28?7V̶9ѩpiy+;Cs}j#*.S06{P#Orf+%gQz C#*CpK fē:l.v0dA:o,7#+UEAb Yi疙GA˜>#*Y5V\0>x$IHݘ5ΙwJՆBh`X;!67LjL =_mb.Pq#Z'=嘤 :5% ܚ 0{01lGjij%\X5vxͽ4V 4ZMDx<Y0g;iʼbLj-Y5ɜ^W7N#:LNvY6P<zmkHpbo܄äCDY}֫hNy(#;cCHMz\j9ow1Mbsz!zBT;AJ>#ccYwn(}q|oWa3:;1d9LJn5͚']ߗQt\P##)^N:82%pd"^!\(2CAl(jS }5\7HIl^mK a+a[*ɾU쌦!2(1 ٞY5d 7s%}.oͧ)8RcLܐcZX!L2@&hCI5$mwH0냦`\%Cj3f HA А#č5ND#*@\@;a#+i[T싇ZN#)]`rmepn%>PiI#+46Ijv0,iQ!XlhpNVNځ]#* QH.q.bJ2E 6zr'i3jR}huhf,GmHBfY|c90B P!j1%EN74BbeӟK7mppRj#*'5xs/ps[Dr1QvkwgJ]qEBIB H9amoaq&SLMx;dv#*o!Ձ8fmqKfs%O6;!,HD#+%'E@IQ(Qn\11yxƧ#+3[TBUqxk ZG-,ʃy.vX̼!$UH-G5fJ1[s2B9H4q:jn21 O)&PmV\[lf5,yULb0vBW5Y֙fB0 +.2#+A1BP i:c=Xj8rUW8bvgN+X0N4a 9q{"aN+(0e#+Bu R`hÄE8($N3L#ItqFXrm\1&ԯcQE%*rK#*t1#>"<4[K5l^;6Z|g95{dmP)M#)$̠0oj9ّ4tP)QII@x i4JLٝTwdZ>h!#)4BX#+_,nۦP&R ˰dYٰܱ>!W<Dw6"d^Dk¡H1Rd-XZvCϤ0Cgys( A1 ej7) aD@J lͪviZN(XS7pͤC a9Zf)!윒ZEO7"eҤ<0j֥:3T#*J\sCU xeV''q#+X@N"g¨ c8zf\#)CP16`uFjlA;Hwn˗۶L>8跎Hy}hn|Jfn#+e󍙍>tCʡW4T΅J.cvk\8RҬΠ|`5`pG5DLb]CC[.an!!aAWɄӌPiI+]gw62ǭf 9i7U@8806Dr+xW\+rkǦ-N\)``iڐ蒐A.NGIF1Hcfx˕cik#*F %DvJlRQ]+y"9a48yr26(kXml.@!lykCȡ#*F+2fis l3YU)Q#)e2ɕ%d#)rṙ#+9Ň32ƎpUC=NA#*BÙp+q%ʁh2\f$#*DN4;1͋ F94Sld̞)Iy#pt( q3a4nf:ͫt5a23iRR8Ƌv={fͨss!Dw[p #)C@#)búgTđb% P?IMDT)rR(#_GǼ= I$IddBO}QcQ"'T:FԑBl@Q(#+@D3#)P.H;ߖρip酂INUI>܍VCuu v,ťkFw-QE܋h^uL4&Rc'j_XGxΜkW g``f!xF##<f-[,pCp"o ŵRnK A#pB: 4*DQ*4kjIQ#+"PT! &) 2Щ 4B,'^8(ɥgp2C~u6|٧b *EUnR Z9uwu]26J槗[vK#)\vcTw;7#)$3C}C̡8jQaGS`=?Htsב}+&EA-`'c̾B6zfiZLPOB0@cK.õ6x e Lfk;[&dBboQ*4#+:\sqvΑs.7 #4"k;ϵ"Û񷇀\b^z#o$57=~_bMIL*ѵj$Ƣ&IfDQm$Be|džhԧ#*$2]v@YC摢#؈Ad#L#+$Ej;HmUqyx:osש2i;kD{5|)KXQs>5a$'-ZDW@Hm<23+i0Ʌ#H7GEi1d#Bn^ʺU|zބj#+.Ɍ[2b+aZ\34B1c@s23cw,!hFX#+D]Dh8Y (I@U~X8M;&Ty;byޥ\7j." C} c* 65$*G/z.G5D; tf$^&А6fjL*qLuquELhF1IoGTK&Bf:ql? #)=Iz{J(*-!VDS04x#F%ƅ*U#*D30c#*4$!|Kxz[4g<0F:N%E`֙7pQr0s|## qe]cCNHgDҘo8wh"4TuL4AѴ-ㅥjAl"$2kBTLH#+gN6:Ů(lYM{^ZH8[3<#)UehNå=(JvnN<mmP%* UeYN9#+)a:@mQ'u=ppSɱ4\CbFq=}l7;VƄubŽl El0,SFQuv03[)ex#*aL*d@!L@*#* #)_z-$BE,$:bm7kB ?PWruH2gޮSN)XQ#+4@.y_#+zfcN& @QhWYLL#){澦H`!$1Mk^6zŶUj-m "#+H$ Cܦh|^Zܹz>#+ˑ>!5#*-hq\Mov΍^AY[ QYr$E#*{9Y%leʵS0:2Kl4ivlt2VV1Flc6h9qH.KӃƶ> s7a 6WIT%/4嫈DSNDIR7DM̗CsF7ʚ1NͣqNv˧~Q&؈h5)a=`C8ۧ E2±J|&-M 315ę3fgSbiyXzMp\J}d3}xdǷW!2dž9l4(wMc&U/dآsLggjRP@Æc$"1a9 czպR,"`L1`mYG]qK%!##+\P 0hHhmQ "D"5P@@`~ V,0ܝqJԃؐ"8qUv;bMI#VeH!V75t*i5t)7]L<wWbcJMn4H,)806xmʤ"l6ƴԊfVRm,ʚ45j60X-[I B*BzH:#+cV@YKj@D8Ed0 (TM ؔDACAnDvA{,=$# r,msj'>b @a赬fn51b Hh$̕5,aii۝C6O}od@\b*FB,Z2"S F;iwo.kGe]}y,n#)ת 2!MiK٘ˆ<LEz0"@Ѫsdxcj.\*;6zipC~g%T2r "ˎ-#r9,!3`ZI!!k**k8噧&(\ :a%e;N_#6Y fK[Z8&ajt'wMȠ3 (}JD#)"v5e*̣_](:~BIBv{[VZVH]L12IE25bZie-FŤE4%JP)F~EڳLfi6*֩zqEdVw]6/i^ݻ*b(2#+fҌ֔1iUU־"XݲMElD&ٵ˭-LIm)Q-6(L¦6[6{:mb<mYuurJUM#*rYJ]fXHxMvM 0E#$eViˁ|+-,|;+զj4 d٪-Dh2|R"bOk=NZe,Xe׍#)jIDtKM6ԤHro#*CɀQE$$T*QsbB%@jGu)mW65k}1қ#+jRdj+e6%-3E E&KmiTͬiMM+rIIA(4Ғhm6SH4l4ehV)bKBƤڔ#*EJlJdJIT6ڋYв2RcAIJddfmVDRdffdѥ$, %R#+@`4M-ZSUe ,#)v 2"R-ڴU"´m?Puq: 2:B}o5pbu9J&'qMt#*GxR<r7RB,8 gE}<tT$1g ~6ιų@f ivz.Mv H+ѽ7e;Ґsq!~FD2lm \cha!#)SsLot%)uB + yTE p}Uta)ːRu#+*W)A=|5Hqf.3#*L}6dXs1"$]n{&olˡ=87](p]H<Ĺ2RedBS1*LPT_P.F)iY`'qg#*`EFtM*CzX^7:ߣjSI|k}ٮFrp[B\<H LUU"!˸#)#)V-w]!pvVyFT Zɴ؜pciVUD`SH#)Y;+s8efwm}ٲweptcoM>WhJ~=;6@=ں6v#*G][{LcښPP,;n !&'xQ\̟y^)6#*iXVYlQ2$QhќRh4C D6D5$-p#*9m&R(7#Jh\#*ch7#*KT3 l Leq,TCD.XUTƺ]$ *Z<R)0ΡHkF*LHM\z5J<BGB)55N ҃ā҃4CR4j#+pv]-"j8s*Wgq'N\$w0'M;}z"ط7;#+@TFAj#+#+_gmo?Z٩rH|~p U7p۵}NgB O>1Mnx$WUc^o#M! yh^ QÛokٌ4!L }IfzIYS*#oӽ#*C{c2>HT#J)X`hb!EXR$S$>]Z<._; 8RN0$ S >&¶m%ümji&4jRRyzj"@2>8#*>w8篌1vb]00PȵH =G"3Tmpܡ\}jsfF'`aZt0Qii}'S~UD=9J^P'e\fz:qyD#) NbCJ&&@C8DϾw~>gک47uEi΁`sCg|#*#)Q0jH¹(7׎74Lmu#MCgӗ@&MD#*Ӓ5Ռ-8vI9:bˑ(K!1€8rUb*^zv3VT.4;3sΌ9 b*,R)`f"DB *zIJXB%9FCqpGH]iHtGV#+ $iU8@b# ꢱ$\YuJIKNzszLo;؃S>5&#*HR*ZDI4uԛ3X|D$ "de<s.W[rY<Axkz' "#)$l`1 vE%0Xpe2Q0{݄'C[ћc[J;Pa*CZ&=#m>[tu࣯֭ Y8,ז)B -XLT,KԘ8R6y:cّi]PA:B'n3|s+tF,zvlt-~} -$@U&j*h9;%D(YzwܰjN,0⩂e캒E֗zr7RmdL$'z^^tc7mww@5*Ha(QcO;˘씼UCV(iH@M6;"说h4z&cvD RMpuHwf=5~/k]f"~tZkZhՓjP:U2df`mEouM4m(֫iTI蹷CYh*܌Xѭ|iEUz^ּnZ6ɫikm::by) *p%\TMH#*K8 BЊR2DZq1'+C oЂM2PrHD}:Y4wfa#*r5lm؛= I>l#*hov^J*_r-0v&ଅ!mOʩTe҂WK$2͆Ypt!U#+#Q#+DUKL&Tju۪f^#k*7/a7 f{+ab( dYQy8ൢ:u-Ɔ#),I)M3#)vD[BNgٌ(4dA(蚁CϮmwQ,#*A {k Cb%58\4jm9Gk DT]dn&ߥ=ŇwuyBU?oG }/?$D'&\^ọBn͟C!ϼP:͍؃#__;^H$PRID #+J*#+DUYXDPPflYx@ ,%faNk3.?ކ=3R0V(,i,X)6B\r;>SSQ~ݫj D@$SОģCN/PlV_$PewGjBR+Do,11Yzqa8X8j܉8:Ӝda#uA5m,W5Xi.̷ܤN*^D"ta5ٰTY*6.j,?6a&uCF QV$6ky Dj6daXZ? Q֏){~]jiRD@]C*H0Pr5u8bEXwγ##*fQ"#+RfXj+H46fP)11#*YTdEa)VkBS ,H¶)&Y#+p2SPa DX!l #*m&46!FFs!0! -&h t 7 6ȫ#+p´XLrt5{{wvwnksV6+Q\/xĘ׮K.odI4\3lApjӴK&Di:3ui4Z1(\'(2Ƥ#*0%I@ 8ZԬ<5MSX3b]W?4}0A,m*[lʤZxpu>KϦ4onnMnq1 ǞVqz~#*ݣ.H( MT?< 0X1 EDF5*Y-Uvƕd[,k\WRm7[dX% YPu#)IIn>aXvj ,b$"?y}_A`5~UX鮿w}XQc/rCCPbPPBT`T"P*(R$ 0 :njĀ+q3-" <MP}!UD$I~X) Ũ5#*!,mn*YUs[m,Ȋ-ŶUGXצkyWɭyjwQERMjMZ4K߰`iI#)#+Dt dyKR9uCE$+ܢQJW#+.I#)}@!x!&Xb6 b"ٙ R;#)&@ʟGQ#b0d0AmCrÊؤa=#) .=Y= oiL8`1*E*g/nԈSN#EB݅MC^$`1v rÿCdg W+0bAJb#)h|&W28܋C{WFJ4*j kWli5+(F0XDds2ӷ}v@G(P] `>^8ʵQ~'G<nѐ캓zh4hATqp<sf$1#*k#+CT0f#*I#(ecm#&Č0aCAw<ޮت$IhLUGs'#y绖z-cW*,NPYT.ZjB̟@YA?B3u\9#QCS&>^J8V "ujS6ak907Mrb&'D0#*dIP0l0FfjRFy#+!l i=5$wL\jfbvC<ZK&$Hnd&l^zŒgR}Ϙ:z#)CVr̔>?#)8 $j<#)j4["ك RRdLŵ5&ƴdkev DʙignmjlE 3X%lS5T6̵m"-(M46UEͦ}Wf&N~EӖl$"jp+#ֺۤj]Mk\#+£רIi'@6Ѳ6s6(崷ꐒ ]^FBD2$N80) 0ه|1q@a>VG{1jC~ N'( %J whzӡ#*Q!(dI-$$)#+@nAE"#+T<BsEc>3=\ׂ#)% S">ʥm#*%"M\˨<RкD+J'`4=#VNOy9Wwj2j6x#)!֡p[5vAՂo/ Ǖ7# Hpm`q>#*S9m+.!Fb+BCCD6LмLӰ,X˕;:>1J=h} ~nkIP)ItSBe 's|a#)p -A箐_ϑъQ7oE/6 p)ߑ3')&#+Y A ,S4]P&云keeI߅׬z_;#*xoMt#)O 1 򢁴!n[$#M0 )#m$_/w )b`HE)*#ӲvDIUV@Z-Qzal 5Ov= 5#)ٓ=UZH_#),H BʆPjp jETq14s#D>i@ L`)v@Ge5ΰ3ɗI#+53@!duhA -͏--14X dxa,H2:#+0ҁ 0B4#*K ) NMxvi=>~(:>0C.j]$N$dG !ETBG|bOb~)]fѷÄ!,4FDZ&NAB`5ݮltf,'e[_e緶ƽ#j0QSASR03RBnFP,Kح^gDILy#o" $M[QR]o((|q"!Ī(Y]iu#|-0"`dKbz $mqII2053-S.80iq*JʖW"Ka'nk#)DEOy$($4#*q[BqK ]FCl &Me֔ je 5VWE[$FBq?52wbhGrE<3U6D]_1"bv'P e;deg͝Rħλ#4=*v8ZmP^OMWMs(@d'/m1 ,'=QC0ֳBo~[5PPZcyqURE^u4^3b(ޖ)DTqa[PxAOi#),H#)vgom0WWۮ05j( 2#)!GHGR:Dz`JX#*@\#)q8R`h#*}]^Hjۼ2jY8}ޗVI M B$ƒbjĈ&MmRmZTH2c,@=Bd?vi|%Bn%I -[CbCCU-0 3U;i]=Mұi Ʊ4 I^e^5%^;WKj#+-20,f(E΍gr/E!cLL9x5bP#Xed9di%<y#hH$lx0@#*9B,\*ii@e3lb8wn_\\;*gjI KU8#)ʘˊ| 6'!>M-.rg^Cu ';IÃ8'Iϭ#)}t%u@1$D8 yK*5&Z6KIcT~VLU=lX[$)#+a 0?OBzKe(j8 UXz;-fC5"ˁFˊ׮|7ׂ02W;n|<UoUx$#*kȶ #*Iعa$8UAvS&lw?͹)H"7 #r)1{l.op0B1VFy#c7CD0I%:EGs|AAo-/3m]'щ)MA=gK.Ź6H_:m֎=;v9@xF?J@(/#+6eg9m7>G> iؼ`Rv@ !*nQvRջfMO6Bdm@8LZA) y~LM"&V\s!JDH@zp.Ɇӑ.ǬJ [#+yaB`*  {P'6G109_(G1>Na{{D-<j5m:mV\U#*$HH0QKACoỤݨ#*([ڹy~uAb ,Zk,D#&:ɒQ"ĐH0nlPHpM$PofK>E A!#'v#*sDc/ytTa_ 9&-V)7EOi˖8΃=q#*~hiѦ`5b$ &PoA%IT)1`F#w#Ij"hkXH咊HڇX&O>%2jlqqNF#rs*+AZܷWY4Քgnںd54W)vo,d#) #)d:0l( }~z+MzPSnk.EaQ,#+I=Ł?5`|~dd|؅_ۓcP&ڑA#+ڤsc ^lxl6%jC"jP4FXl=j$RZ`#4 g7N9S-VMdmLOjnW&VB~l>7 Q#)2}MBM=ZuJtywϩy'|_h#fQu:ea sl1Gk}8IDdP+ğ/Wo-ODSR$]'v5ÂkPX爱SX%DA&?9-`65 .Hy4#)jP{݋ BoQp<Au#*NP1hm0z#*VѿⱒKNØF\'J^EIP]Ո$uK39gpֳfxQ-cBb8L6dfM.ۊ`D/a#+b@!K4Utxb7axAӥgLeXP.zY)0(πgC >|. !;‰+"d̊aB;)=^ycK-L7"O譚xޥs,LWkC;9y^y[uvdlpIfmAk<h:݉(6cz0RR5|mV]2R6:bAa誄!裐fxrS4ST#)jqSKRPNx-XFXFhն4[ RkFXƵ*^5mʣmsY#fك]Ύev")CUz,.`!i <zmݿvMDpw>7N#+bMjFnYLgQn-T}jctW3d7MH0`yo#*u#1'qB~8}a0y+0#*HH^zd"{`{r}sdï mg;6Q80;qcd 9Ǧů!H(zh{+qQ@f?!Vb9HC!?E) jK~`&#*Iy$ wm١#+ X*J >$ד:.g"gMZT YlPWu WS X( Ua[#*aAN{-{g#)]!7#)8 2A#*U *B8{K=yztgf#!A@҅STU7I209ux/7D?qpkA k Tik"9qE8Qs,86i-2wCc8wF6bb,x3Ϟ m\YA~{#)^DàQ =t8x3jjgo QW<!uEFD NCYI MIڍBU<aRavw]#jffhh!#*ЗʃzY ( TP$Ie#p`48*j0bnVjHptkFTĨ$HrEiA К;[$1dOʮ!nfUDTD1jW7fV&Fy&T(#+v7[QAD#%[(L o##+a#++&YFkA4#)5+e+4%1RI&6,3$e+/]˭ri˚ u-nw4Pj}>͆ E#+YK"r#*uSl5X<'Z4AvX==Ycn#*dZ #*}Vj$qy&i@r1i(fC}`jqҟD^^X #+׭QHùYi6 tӛ3,ۺUY).Y A@1p!*$#+1#+E45-w%WȊ1n0-&`i%ehCVS1&;"f;jc ߻.q#Q7! nL@67v:VW ˉZԆH#*2M4tq(6C NyVu5H*@qiM(9yCKugbY#Vnmm8lHjƚE`h1U)b#*M$}&ށ v] 1̳?ф7#*0.a1_#)ǩsSb<4$wO.y~nl\IXWي-d1!fkD7\``HZ%Smir=S|qS^5 a9UEfT5p#*PMOF,bGI1DP#*Ы()e z9R AUnnnQh P4-[y*6TC H0c,%B1TAv D$ЀrӁϨT}C`奙a'44HjZ1?h4Q>Ͻoz4q=6uߡ I[]TXbjDפ=+F' 6#*=)doowIعq7M^O7ɫ;2)eYIN[{7tn(D$幙ru7<yp3G#*#+#*E~XFp6b 9;TXM$+[ rs;0D9 B]va&}&~Ҍn^ >GXg~{ϬO9..ݚ>tLa#܍)*"e!柟5 )NKl7<-6lCe~홦jθjRNC?S|pO|{!0AdȊaDl- u%>*GBY#)! ΚywwM;y[J⫳kpmRm]bmy.;s!ίQO XHJu]ݫԪ4Z[y6[7V4mm5^|Y\ٻ4aF=F9"]+b*Z(Q"4uک皩f^.!{,4H k^ֵye#*b) @Tj)jE]j)XfT[cQZck2hэ3T%D̘J(6@͍mWK;)@"g ~G!OU4%I"H)e;/OLx~?EӴp8#;(s/xI%M6h77z5Qfַ#+OzѦn#m;:JZۘsYnέiZ۳Y#)#+R-4}6!7eg"1&Y~%_t($D`b#*7`Ij #*ȴp#+a#*_GPm+lr50^5+)6!4o@vu\Z;HCq3訅]hʈ<`6l<Zƍ K1&~VX82&ă#+bX3ji8$ pMS(Qqu?߭! 51<t2bsV8U;"ǑC}scewo0<&1de"9.{miOPց'kF)[htKEH *Ӹ #*1.gE𓶅c,DRsfGa#+&6g-dž*CbSVK55QGP'75dlmF-TDB!0?A#+>mo$uQpA,[w+oei8coE @fꗺm]-kۋ3#+"؀b-8#DF-37lZRE=A!¶ު2LFɫjUu\D]"65NzM^[f2@I@qqH̠F#*[F٫Rmf* 0#+"J#+#+ceG,B0DdА%aј@' x8 _ xgb1/+=]?'õX*x3&mYvgd$k%۷OnhUN$E#);KHFI$$ h`UH Oǰ<3> tPG;Q?<!l5@TFQxEs#*7d<2qO_}͓ON?,|:9hTO@Zu:d.Hв(?IF[.^2#)1Qg#*p#+j8Is /)CpQ.0ъ'A7g\#n=#+x8ح7̢l]"P)ZE&#) yZirL&`!O #)3R< 7S֩>ʛ&6#mѣ N3*zQCL_!eAwsmj]F!Ҕ1" gJr;S6|.;#*#+bD!Q Bt?w>,Yi^ygy\2嫆uf"i$l&ʖ۴%(l` %͹\^M]v9й]ݺEroJl̷W.,hv6V;mnZdJXr2d[tΚZ39WlTZ4ri-_y^j#),*=P #*; D!#)zw@g b0#)vC#P A;al@ExjoNyx~{ASq)| o=E=&!p(P&Ө`#)7 2#ըfi}6r[b )T\QEc#)L?o&5jjХ&a>I #** +WjuXC"Bбc 1<B%%ZBY(0vm$^!#x#) J#*L`)#+5zk[zV@ґfeQ@LF* 12#+SAJμD20ЉbJ!뚏]`5C5qw#)\Kb+R͖iJ1`ކ"&ܾEt:f5fԍ-2fʡ$Q #)<X&5#)$vj(Ԗ&Q@#X.8":dk33T]`#NqS]wM@QٓQ3egm=@ֆ0Oz3?^W}wդ#)XqT6cȐ34 ;Y04'h04gF6"H$8C]a~!y,r<-%CM>Z#)#*IY%#+U„"mA##)l(DtR]0KDdK#+A`Fi&ŭ*6VjBTE+k3PƆ٘\.RH AL R$Љ! @X(&eIvlI$T8‚OoPʫ3h} H\U3*\c@zyd4 #*7<'W5R^#*- ȈHqR@(!]!lnq)7n4}#wIRBB@WtFЦ咐 z XLviQHm-PFkxyӱ΁G#Ihպ#*#)%- X#) *@t* Cm{Ҿi]Zvͭ#PZtExO\My'K(Hz(?ܶnkؘ@sj'k*mpEg/<5괹JԷyo!,&8(BIa]f<dTL1}#)0'F>-A*ʫm+Xim9/'zaaH>6 邠?Dv#u|%Dtop dP.ҵinʪKKsT{BhsJlo#)qoKS;i[zN&GciI4A,3EE`>"jܣ=<}#+J:.(+ EGoᢘ eeta>75 /#*Pf#*IZ[{&6ŶT)y#*NՍ* VȺx$LdUC#*xYLm@iq)37#*D#)adƍ-!c?;CqsbtdVެW dSٜc$}%9<fHSrT`0>UNl "^ߊ%Ͼ#昦>YmVЅ,.]oF3qN8L- a}d,Y<c2H#*#*0oPŹk=i4bs '4e*,sN#H,I8h-0Qsz&O4{֞m#)B vRMѼ-?Nj}v)io㤮< nM^YE}\5e@æjm!%Hna17-a^glOb*) wQ%q-W$G-u4B!@6Rz"f)#*];z>C1vU/OU n*~D"йgA\}yȿ6&hlsҒ& ^ |L5I2lo3 A\0COL襨$D3MAF{k<3r?nr}l<X+5 `~I5j阷JI !0$Deh#*h$mY ϨH#=%$3XUSGRggYF`IY_=># e߽ׄr- Tؾa:6p&P{ wh|Qc!i@RdNAV)GJ-tfuw-*zf-paG'S⛄>?#+R ܁ѹmQv0/n83'=1K)`ĖȃƧN9#*T!1]OCJ z1SG#+,62RopiB#0^:LblJà bA$_ #)$PG!G/` $$""@ Cqz~X*G2pz cCQNq#)s#*An#-GCs.6c;7Ǿq1qq.tr Sյmh<4kfDW@~9({5BMJu;ǶH@wQ`DGK >X G1ci>b"v`U*^yH0^:=#)IKB#)1XH sZ9+fee$^~zdz[-@'ӗ`i! JP<Ao=&Ł#9q.JO%~Қ5ufvn}:rCq3Cus8d#*1a(G"tB.;!W<:+_5#*<GIk+ش#+WB_o\7$}lΊv6;;˅(:NbXê& xt.9B?!(6>ų?}«Ln39/.);̩D{%[џPAo_]HwLZ;u55(tЇ;R:R:eT@#v<7e1*2,Ε;r~j2ZJ,JrWtU&b-[5jkҕj6d#**ԒD7!й\u(F(zZ#)E9`g?O#)FeRDh(&Dj"%JHIdP$]'6 'ȋ^d;z㷇6]yRMv̠0h5$lAQR0%o[=4 8oG4߇+k09vg\vru{R2L(Jِ11ޗ `-MU_@i0bVǠ-Mci㊇U'nk٤ecjTCf[ri#+kk5I"X@ɗo猝TMp$ -e*JVi(KBjEIj0 Qi@FQU;JW+ntd#IJ4f C#+1&1;`f#+nJ.0ɔ6\P2#)(PMixW.,5dFr>\fT#+EHSH"Ogi-knqH܃oͨsp!4\4E¿g<e8tHL%BgFK6*:m1bCb%m`tDy>m#Iy`ppln8F11!9Z4c#*'mܲ5#)vjH-M#* @JrSM#+a㐎(Ymۉc"JibnD=#*bUDV).Qلm6*НaDE!P1Cie(lo0QZcaUJ0lCezOv9*A)V(SEĴj @ŏѩŶeތcvCg@TF4p֔ԃqs`UƫA@2AՌ#V M3OK .U׀AǕbbJ ,|u=7jdtEƶpf3fIkRPn rnbZ@<(ӄ}dU*8 ` w3N XդT`hdJt.h"dj!B!SUPj8Ό@Ɍ3@FoЈ/#XGdm5&Q@ݤ*W:22dI@PdIH*(h#+Й BA(0XѽGUtn\LA]IqЩDYcYR }#*<6 `᪢8A6( =M8@ ) qen#*nOdnDDayQ]}XPXѐܥ7j"B$5fl7Mrg +eF"&&8xt$Z!LaMHZ#*(!BQ!bJ"524 K7 )"#)8Zhp P;zR%R%Z+wv?ytŨkutur;rV;|-WJ j-(Z6ɓV6B!X.Gn45*v2,PݜԠY鐒nL銤jC;(Zr'Y "#)P1F"krAԤyt/}^T$IB\*dH0,痚.P0#+;+$(`fH؛;b1komu҇w `wJ .P0n@bƒ֪Q)2CYrT PjnkeF()fC B$p#+.<pd͵61.X QnO͊o#*o ;#*/=IEJ͞fA  ^#L_Hpm:/W~^P5R@[Ery%C˂PTP'BJρ܃MI}[^`z.Qrf,C^&پ9TkCOLKgueڂ7~#))%#)Ӗ2ai|DGޙAKed"Iy#*EP@BtHY|,fcn& N.=FX,:V*m(z PTZ-:#+@'?`{*y\Pȇ`;b\؂eb 0NRQmTޤQUsk"U Laƴj樤0mEMg#*Α9H<\fɱ&ě#m1\WHFɽ+d{5b[wV-pTk9[5d55\USݮEmA5W"5AKpD8É7cu=#*Q_g@QZ4׌ę6 ][7RD"#D@<QV҇,D@I;o8/#~ |}ȇ"Ё6I,|D:#+E#)v<UL) Q"FP74 0'"0Xdk 0#)6mjIX y&B]ނ-YFY#Fl "D,(B?#)&InZX0/CE!+ʕ<kJ#+1 KQ&e$ wڕt:҇unXKݮߛm]m_KS3dRZY#-MRQX6#+V#j[ڍiS*V-h2ГBa*$ cnC\M/]zTyC)`G po27b4yR@TҀ"Q1n6FAX1$ABl-0RBV!" P!#)-u4_!Pj,`ş_pAm)h@'.jnٍí>vASm("!bB chC@M%^B3`N"vTsm㺲הQRN {AD! ۇllkTE&˜SG]0􆆀\Z#\bl2 (!mqQ#+@|k.1b>flQjG4{Sv!ܩ#*k;Ǚ˩R/]rѵhք$@!pil@z%3GQ1TUwp̤BGtK*Pto=Kao~XTٲ-?dW<`x7)Kբ4/!ժ0#+Y*07ɫ'șૌ*'#*'$;,<βdrslsB>;Zut~Ǻ)a=igWP!>#)샭^(quԨryqTOi6.YRHTwQlvrߵ`n=Ҭfޥsqq07%5X̪qĨuUc2|oq@$q8ܝ=U2moqGnf<r!fB%PDPe[_9 OGk\7#*BU&<? 29M|%lw^._­`N"Dh}ZܜYD뢚M:@$6\eu[Q7nJvv GrvY.I-@BHQ3Hh`14]AwA.:.}Jmv"J/\-\L !$a\fVXI~g-R㳨C19n欄i*^GG{K2MBzT.W7/cMrN|$"Q?*y[6"ƺ{K873C(!%͵LT>{c&A31o#*\pׇizFx]YsCʗC I$;/O;U#Qa"#*`cr6b#*;w;\kg#*/+6kSb1ĬtRrAێ2yu-1Yf>Z!ۺ#*O9[ҷO%qK$xF4^DtwqHrd3'8z|6ș)Qm\z@Aq=0zJ1KR_)Lt;Rz[m88פ6e3ZyOܞ=#JGպ6de6 kL*YY#+bJںr%ҩ#*Yywy=p'F򪋭lg/=a~U0˾{0޵yJ|]%.Iwǯ;#*[qwb3vXqXic8Li%J'ş3s4. r6Dt6cM,ʝ~܈M NPP޲s1fJeީt[h|Tl1')'<u,Gf(kA2ʔ;[xpņj^#*u鮡4)1ow$fV"cR!=b#6.WQ G4U%5!Czt;@lF1`:L”okAa+a 2pu姃<9zp6W:FŮ]p#+A pL@M*7p}@$$gL"kLC*#)@>#+ )@#A#-qlߣqwas3iaHN(FN]F4:0a(@<eghYP s2l750FA5 Dv[sJ|rtmN-3Ðk,2pLWJ%g>vdym98ճI6*fN#+*5/32{\U"*PQb,M ez]Qk]94nzFQWLZKB`c5I4q;Buo||- #i5Qj(dvxwug~E5db#T@/-lZ/yŨPĠ^4n5sNx/-kd:ѝc=Xt牻\!jm.;uW Өk vGMu7%Otn= ru Ǹny_6Y_XL0xNq]3Ͷ  xGT#T8bWo){ɛ.<r r9ԭT+OC*0w-#+X4`#)ϴpR¨ay 9r)tY#ۋp<8UV{@#*7'vs:ᙐ\h0,;}jl<M)vlCaa#偄3f.wK͡C#+\ٗc`դ#*#+:o,1 .#h5.W!N岩T0kGm=L=ԫIj:º HJh&؅3P"9O$Q]i-S6 2I#)2=\IK#)}M@%:#*ފȥ>eAb: `PnQVt#*~#*s!tV\~ g6;4q[1UT4u-)12vC#*MR_gίU#)=idO8Á\v2YͥB,#)_`DamlsԱU%&FbuBD d ShXЂ6c#I#*U@ x"s@ACUkzVnkQZܵl(vR,u5*2ƔTE#+2($F3='3-!#)ɀ۝pR.4$,g9+x܋cm*ζ[EW-TkU^]{27#3t9t~}XeR9c`#+D"5j4{nm֌t1h]2*vlZqU')fMAC*#Hz2Ƴ,"E#+#*aDi6K)a.+M1@%rQSO1#+|'XuHW>lL#7T`&581"v\jV p#+1{zԦZ;YMiMkT7 glԖZtsղ 6Ȇ<`"3@Ŏ ōVP˭+mIK_֧<ݣLd39N+;rDHcBA@ic]cwC1!sR%4ƮeoiY2R .7 R ǷVI'tR٫#*w4狮.D)l|D4< R޺֓cS򣜵.CZFLdq"7*#+* Ѹ0m0m15+Fn-,za[ tC 2CdT]&09EZ*#*hFemF=]qD&Z㙢͈аiSqWU[UO$*7Įif:pw`cL"b[cMUSWE(VUZ1еr3L=&g63r<@5śıqɦ3NUm!C4@]Qj>( QsA_-$u)(h1n#*`9e4<i#+pŅ)L8 4V3<m-aZ2F;gHm&47,J4pɪ$bYj\̬Mng%MfHT06%Tr$!htq`8XqF\X5tc`qRc֮xI/>.vOj,H,F#+Q؆v2I-;V\aݜae2mMڿoe&}vݮHI9;'b NSfƵn1Rnq(0UQR=M5?G5{O{x9Pv5b,Ve&8O'}`o#+2;GU/dJp'ih?wdPlR<gosn7m#*H1"a~rwn͛UdX\<B= r;ƺi![kHポ"̍ge<OE7=`nbIIWjJBHF!6$#*V`@H*#+{Հ7~<Dznm{kׯb5CLci1LcHRPLضKImXlIY4hQMF!BjF$f41HhMJ-$l0لJPcVAUo7/虨Ng:MHd)F]o9΍4Uy\vWy9+Ȓ3#)mz%r6RNum/Rz@#+mCjp%V[1b[p̔0e}0~1xʲ@ks9QBF+ n>.AQtCȢFO>!I!pN]{9WQ FN~ʈ\ѫP #+?3VmD6RmcGZͦƥ#+MFkb3]VZJ}ia, ˿B 3(d"ٛV t`ϊ(TGaEi7?,5XC82V)#f#*UE"#+tF! `#+@qܓ I%jouݺvs^uy1FLX#-(`%$&j#+8E 2lZ( XC>#*F`rgŐn.̴JEIj#L!(HR:n`b9כ1U"tqa!o@D0#!on ##kmA(*kH"p#+KH[ŒDI#U*!ѺL@id)JqKO5^zݑBW#){8~rzdHFBn/~̉:zjپ^H%{҉u:Dx %BɄbce+kS4 B+C",dr1*጗Yxx*7߭*K6׭je%=WTwLMKjDCHĎWػo#`="[S[dʊlA`5%4݊ [ByZr Z}^`z86WҜJ5y<H'YN}G}kɹvSIϜ.b'zFIJS5OflSc/2B I<3Twj7V<@MZ?nj2`#q%A{NqH R,""#+OzGq1/5mPq#{=JaoǵG3x%kٮ{d*hU0\Ctbt^~I;L X=;/#)'?ehO;ʤA/U\yge)^=sDi~{u#9d,٬"]!ntr #d+}»H|YJwa*'*ƦxN#+G x(`Y#)wfAjSuV A/BhI9Qw.֍8e$R5)&n$pc#*҄QF¡0&9팈@p,@2g,3cvAK4"qHd:K'[YlK,䜒aFq*Tp(6<f`P1)5B08olEW<mla\e!uLFa #* X'W$DHawn:v (L弰ɪt2BC #)b [+>7ۗ%/F~yvE2"97@1-$TLNIgl_NVqLɔ|e)1>}~Dr 4vkc#+1e2nмP:"Pԅ$uwZ"4d)/BVb gq'^c5@Ilg ԬǍ^nݭ3yzhⲾ#*3(I#y`pHXls 5vA[魏 :fӽf40mg8pp$3k:{OA9 6q8v5NRd7mX)ʒ(LZ1cvL4f\ydɽ>9LG&F%5!Ҟ8dZ{ vk)m]Hg0H6SDK;K.eψ[m0Zyk.eFM:-_g|X[pBsqWbEX4ƴN2b-M>ʲÂYs-00 SpU1zDS\v'#*tݓJs&itԋ\sH`Za#*YxB˕aVj&dKZ{pwreP_.uaw<mMs\jJݝo{޺g(*r5ih+BFčf8#*\J)9Xw0r:^1;N5!?'*8;_}fഎk(D$N 6)C f`f(i@;MX#+RPM#)Ɠ)S1jnZH+BN\mgY%C]V@#*4ќLk#)"@x;ve9cftR^=k4Q&={Mߊmb5h.m#Is."!ybi|qζ#+lƧwu#)i֯\uK!~~՝#rNzXQL;T_!5qUԾj&7, FJMjfLX|2X[[#51 !#+عb᫁0tLb 6#+#*f \&8>LJdw+m4,(uvZ 0 (K(fcn8>{rko)ި ij!A Q4ZsÖ&&ڦ ,`sD^ەk; MNNpW4W*aۓP&~ןX;Qav3at&nɦG Y.ٮjߒ m1 S{qb: !0dG4l]+Tp:5dAHChEAFH"P5S8:єa0QcN <Slð;*0%DH!.+TКY0#*#*L#)N.jd2#*JC S5&̚,MV6fe`Q:ViE]\Q$xTʮ:!Зz2 *#*lC|ظ#aȪ#+v6f4odADàġddjҥ͕MP6'HD 6N4#+0`Fp$50JuќQ3#)`q:# 2:@#+S&pI`^0giHDByy9FQQ`1M#*c_P3 {!! &U#+(tCy콀:UbTW KvҳS%gy3>F[JPEM܇)|=6MmTchSuHs#3A7b$8l\$ r K5^z䪠Z 2˹DJR*UzRYSAEB;40k4AvM2p#**J;2;˕"pp224P}Q-#*{4GV}&2xvms,:n8֕Ԍ8"s!#*~Pٛ,%5JؒWoxzBBLTE u/UL}*TQ_)τht^C#)ϲ mr>d$uJ%U+'L,P`ƥ#+5Q(NN5Jd*vZ0b#i`+qC&J-jwn6fH $H06B#+AvaHXi#Ԫ iZ4(3 IG\H2?=Íj(b4j%օ#34u*bQ GF;goC wڵ"rNYc/#*XAEF'j#)5#+4V%vHJa^0kbk‚P<Pq)wӹg1g>hQ4eU=jbʯ,_r}C8~&e9/958MHlԟeVBq-(ӅkTMA!zN͸<WjH@ ׅsCaƗG#*5|zgkLY}^H`1-db'(g<#*(ɝ+ZS{b$DZE%kZRmMzXDVBEGEVE!F[L!3p(.(DB&W#);#){rȢH]2"Cg~U!Շv9,a0"HB;t5*c~q+ 8۷*xpop6tthg#*`޻Ž9{[#AZHS0Ѳ ,I6Uko'k.gt;x׮۔ZɍYd,*K4fuHdl,"lf>z8)a 1V=K6-h] s1;&lgN#*76QW"#)16sԅ2gQf08D$PuS @!2)#)&!0PGkT<UZ,CČ?;hW0P'x(e[̅Уo8J-9:T89acJq.Bh=D6g }PQ: PARIJך'|D,5_vV G/ DR)I%d-f&Л(1jm576*dfhwoD#+ o,8dE͟"6ON{( T{NBfA$Ǝ}QsI*74 +ԴA#)7MW!jRCQH6٦S3 @047 G˹,O6PjaWD6f ED&FZе)u㍘#*֐RhIHnb tqVsF?+#+:9\1#+ -2[BU(nC- uވT>1`!}L˸,ťLPTkAV0XXpm/٭c"YXS2`:=/0lQ{䳻#+P]%,B#Ct:L#+0"0!1sUbӸX'̡FA ĒK@9& *'IF/XC^XI{҈PE2t#+!o~;{bG?>9fӇ"vR6f=aE;o=J(#+<Q ې 'wC{G=__snc蚀UIv|5Ҋ7E6sq@]4l:5/0$2c2<]FF&]HlWϼW79瞻RlnyMms\+yv歼ɷVv*M@Peg;%U|Jlx/fzTyyH)P::ß4d#)(Iu=b!Q A")E4jQw#+4Cicmjv&Il? `l!F+a\X]O#aMGmBB.}*J)-fQQfF+X[,m&*mJmc E.#)&s0ȒwMzÀQ>geL|5\~YAdż:{#*XH.ȳ/yq좫!"N3?t &nyBQbp oMw#+@A֡0tZ !BS|WFԖۤ֩"dd騠85ٴU)#*aJ.))VEeCGǞ4!ka5W4&cĴLVu3Wch µ}ƀM%͠9j-&գ{>r61T+`{ ?W@WRC¤iISLJdj)lbMom#WZ(M2ؕ,[(D$#+B<Ы| @*FF&L(MCfҍa`7@}0z/u[imoԍkWmF-ڔ5F6XXR 3:t63kiUJHl}(!X0.)E4ƒQFzQ%遦Ix$ȫmiGl'Yl홑#*Ņ9= EC&WM-i54R4a&4ɭW%Ef̪뛶vm^]Dl*dJlhV@U#*V$Jko"o%i^ˈJil*uo:W3Le]ۢMtٚ HVFZ`&O5UYtXnBQ){Z6g TW9dck?Kh AT.Fo#)9 vp\Mp,ir{Qv~/มpBKy70źs Iڶ)uFἷBˑȦF$ǩCURWtoQ-xz)ad(E#8I⽄#)5:05$3Uw̤ 8;x_j+.IcE<@yr#+R!*F<ֱUfoՁ%hG|6U:Pj ~Vov#*p`LݟlvhKxP4pcoG> 7$5-XI0TR'!4/p֚j ha׻[0Qlc#+]':@Yz\Jm5u܎WC%ۇq<e~e"hK8>Y[9||lpYyL(7|5N<-X 8M[.8#1Yڈp2\{h$읊խPnh6EQFnh*ƅ@[8A ʍ;çA!"1OAsܱ2ȤQht -u1pdl0yo=(1l=E9G"=гx2 os+:ίmm=ڌgTOd  3dL-rCp@CQ~4PUOft#)OfYv35^Usb2#)?$RVwާцRNLL#)p`Ho!Pv#*%ηS+VE 2Z@CbŽQn;$ 33UwU$rφB@;3թ.6kC2tB9DmG}/dpuf鴠m#*'/Ќ{qWbA|3;:hd\:_`6lCt~+ڳ_ˍ=CLC2JCx#+,]d'D$oUrOü#*7KeL1ҷYs#5 {Ro):ZefY#+#+]#*T0eTo[xSs#;^lL++UX9ai̦@Gٹ4#)fkcAlaD!vI^$l,rAC 6bӛ#gw9h8+^{: ~Y&l&6칰Bw$Y#\{b#)c.4B#*LL<2t)zׂ}vo i64k)#+fL}2Bh3^i IqacFU1].`AHIz|KRCd0Uy=#*ax1wfL򹃶 ۳]8,gl+P=X#+ ♏vf``3(H&p&V P:Pđ@$HBH˴e!Ay(8caƗ0 {M ;Yݲ@X D'-tn`6bt#+DմLjJlkY3[FmDŅS_;ڨ::"fOe#)ȇ#)/ΪZ,Rزs=rˉM@=?##*"Lk]O׬cS9ɬצU1#s\p-ܗumP^ljoEwʠ[%=ǕN7<X?럨N#+3ɚgY,L#+<}<*j8# #*D'GD᡽qIJ5=7mJS{8rW4 Rl8EE]o1h6ܭWWսMT6{@i볰X6*<00HF 22O#+7q q(vع8.P#+Щːǐ7Ub*J9DA]Kƒ\dFn+Sez=(GS/" lyͬPȌ,XmvԍP6kk%2իj&IWѪ$ѨRwP));%ce=g0@fx{}ȝg &l=PH,#+,ZU'?"=d12>6xh?~i/#*<+:qM:uЧGy%ޟ_NV'OA Q $iwDep#+?dY6occ-3{(WQ6#Uf'uĔ6Q*QһxULqs8մD 2ye#p尖7'ו . Ȭ2LMfypx`H$Y@z= jϫ_E=Tg6(g-UF7;mm_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 0bd4fd3d..f1088584 100644
--- a/waf_libbsd.py
+++ b/waf_libbsd.py
@@ -180,16 +180,17 @@ class Builder(builder.ModuleManager):
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"])
+ 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):
#
diff --git a/wscript b/wscript
index 745ee6f8..574f0168 100644
--- a/wscript
+++ b/wscript
@@ -168,10 +168,6 @@ def options(opt):
def bsp_configure(conf, arch_bsp):
conf.check(header_name="dlfcn.h", features="c")
conf.check(header_name="rtems/pci.h", features="c", mandatory=False)
- if not rtems.check_posix(conf):
- conf.fatal(
- "RTEMS kernel POSIX support is disabled; configure RTEMS with --enable-posix"
- )
if rtems.check_networking(conf):
conf.fatal(
"RTEMS kernel contains the old network support;" \