diff options
69 files changed, 4034 insertions, 3674 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 57b05e42..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,364 +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. Check your editor settings so that it doesn't -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. - -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/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/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 6661f8de..5ec1d3f5 100644 --- a/freebsd/lib/libc/stdio/local.h +++ b/freebsd/lib/libc/stdio/local.h @@ -78,7 +78,7 @@ 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); 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.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/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/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/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" @@ -5334,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): 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/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h index d98e6f76..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); 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-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/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-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 f7ab01d4..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> @@ -107,6 +108,8 @@ #define MDIO_PHY MII_PHY_ANY #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) \ @@ -157,6 +160,7 @@ typedef struct if_atsam_softc { 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]; @@ -185,6 +189,7 @@ 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_tur_errors; uint32_t tx_rlex_errors; @@ -398,15 +403,19 @@ static void if_atsam_interrupt_handler(void *arg) } /* Check receive interrupts */ - if (__predict_true((is & (GMAC_IER_ROVR | GMAC_IER_RCOMP)) != 0)) { - if (__predict_false((is & GMAC_IER_ROVR) != 0)) { + 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 */ - pHw->GMAC_IDR = GMAC_IER_RCOMP | GMAC_IER_ROVR; + /* Disable RX interrupts */ + pHw->GMAC_IDR = RX_INTERRUPTS; (void)rtems_event_send(sc->rx_daemon_tid, ATSAMV7_ETH_RX_EVENT_INTERRUPT); @@ -506,8 +515,10 @@ if_atsam_rx_daemon(rtems_task_argument arg) while (true) { rtems_event_set out; + sc->rx_idx_head = idx; + /* Enable RX interrupts */ - pHw->GMAC_IER = GMAC_IER_RCOMP | GMAC_IER_ROVR; + pHw->GMAC_IER = RX_INTERRUPTS; (void) rtems_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT, RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out); @@ -1128,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); @@ -1153,6 +1299,9 @@ 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"); @@ -1177,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", @@ -1239,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", 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/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/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/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/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 @@ -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];j9QtZt>ܮ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_3os2lDs4;mqE6M`Vd]j◘5o# UI7wDKU/~oBE"SU5k3khȋ 7#%H*-E :$H& "HP !#%h#%$@dP@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, &Tl(#.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"TT63,YLS,JJSLl)%2RZ!գ%AEVMdjEHkD$mhŊl2ѵ0U@iR F2&ƍ&di ժ* KY4IR1!MlY"Y1Jm*RYaZDFK"e5MMk,Xlk+,l Db4!-T%KQY&6LJ,&l#."+,E1Jl4AE$&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~ՋNZ,3cRR(lrrXݳfVc`I"lLhcz瓓G]w^\/uu\LP"W4LvcK@JAF&ēMb3(]enY74h6m`ٗ(c2@szo(7pc#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!Mte#6R79,פFtxV@Gd%l p<k9c|Ǚ#tE]HP0T[_#zrHȦwԊW7mxVY?)rvjoQwEme#.dԂbN!e &u/K`nnT)"*#6d9#c*w&bۆfR6z:'.k,YL#6N*}y>㊝U"֍UOpi)8+ Ӭ,j[vlQa1KFS7mZ*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#6lQMZH"JuJ*Ó#6ILeS$+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ۺ1ilm|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_tRn(PbRfWYʡcPBYׅDQQe0x~{*nf wiXT)6-;ˣw$3}T|-z85`Bhҡ~JZ+9rlVx#6h6Dx0)AX*0_#.ǷzeH,5Aޟ{e7S&yYLS?ك-y69^}pBIiK&nǨsιߎe,&2:L[xHǮ*U91DL5;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@ejNec;8`ij%cW?DM@0L\'kKNϧcV*F֘9Oǻ*5,<:ؒ|EI$$G4{S#%beTQ!Pve6^\FfX=*n>Q/nNQ#64E^~`G'0\PDL7KuohsyQ*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Բ?[s5YgkqjWXv_WmMuZ3Q_{=㳗(5#<ID93^y%0Ij[J3NMTnPG4_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#%$̐@%"iGngsos+-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,UGRvpOg[\]EŚ@Bcv8]ѷ7Bov?U?o;2>^R F\ެCtFX 8,;hXj$8Yxp6p ݤ$A7c%#%%"A/c9X]5GY"E5#/*9/Q?Bujm-&;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[Jl1H#.%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魳Lgav_EF -G8?mRG=bSfq7Ai+s"|;O$#Iai[H[#.uƎX[ȗJ<TQj@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^mO8,}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 ә/QEKNW==^u?כʓ5+8~яZF0oћ48@_*c0Z4JqF=t%lnb昦J(Bwy.BjܘxHQT84c#6|md@#%奉c6PaFĈ`]w&q22GQۚ1R^+LZyn@m2(ʼn-eFK`(euD8IwN`;4,:$VbV66PEBD 2NY;6}r=k#6_S0*Byy,SX.9u@+#%)|@zX"%F#%s&;zi|&툝0t651L=õH9v 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#.)ri/:'D\lm#.2!1M&F)dXUK@md2,tE4'F5F:Ep[̐RM0o5"HJ)#.#6)$դo.)ڊ"0ѥbQѸ&b̴cu^،j5 F=3#.MC]o/~aƄDb'YCB&gLF[oQLi4˃Gd78H]g(q5L;ERi\Dyb9iȦa<LwͣBM"1ȥYi]\b/]Nsp#fz܈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:7Zf6NGQ8@sIۤj#%;qnwj٘Q3ai(Vj(bV\M0kD>UzpNzb#.:8J}COv(O68H^&ץӳ?ty#%#6]V3eGY`I·% |HsϺEH:u) ,Fj0sVY#W6щ(4"4Yadt#6#}ܶ`5Mffmj(#.rV;'\S)=Ǹ#%'܀#%z\n@|ʼm6 7ǿe)vaҡHFXw?!џj̞xOuw4r }7>(8_]`#v#%H=UQ0rmH%x; .?VI|gxq[$}PT=]#6Lk+ρKd9r_bDA( #6#fcBt~jl3~inn>y{~] n`"1{Y.HGs$<\G<Dߧ=~Crqy!^HU$2n cb3Aa.Gc@#6\Q\R9U)uۯqVRVܰJ#%Gh#%f,Q߷vKx#% #6wEBcϗj<}z4:C폝7dBb3h"%D͕,J/14iWvevRIKRIXj_^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?CCcXPRvą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ӝtoqiA@gjav{>u.?Bʹtg*hDASyGbe>_)n8h>#.[gj ;0v=>14x gP#hp_Y;|PVQ=?~umA#6`Ǚ+Ժ%#.ÓSwt/vn#Ͷ?!bpv l;̙/|S9?"(H;G*t<&/ֆëGKDj`9K#%#61T'&D[-2+`IBfH&ڒ~;Awŏ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|x0Ϩ]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|HccY|u>S#`{Bfe̍cv9G?GF54iTlT Ԣn7#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}ZbvU&겷[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\mFAKTQeBlB#.QtMy3w%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""'NOU876VY}NO#j@T9svA#%_${ӫԎ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巗dkkm0TJ5l$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#63ɤ@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 H0N3\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ӫ&~bqMN6kmc99ļkgX3sStc/+F!2xX?]BM#%R3. s)3F~~!i98ItgHCBʠ@FEɁtxbT{IZkΟr[J(7Zפґ齥4%ngWUpzƚ.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#.п46zXPť\91mRt}yUQ}[;os-<TvZHsĕ寻&?ߙf}>WOwbe'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>#dqK{e"ϽF@}#6#6pi1Ic1|Im'T\&Ilgp/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@#%mpONajeTI*xzԢ4lGGޏtОq͇5KVxd*W6XSm$H/FS<?z̈GP-;yZOD5˷giT2 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$oLsaHR1T *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#6T2yqв#6k1~6,%gK"hi2K;e#6 fTX]ӦhvְNZPqWPI|gi ㍢\Ҫaa8ibPJrkL0v\q!ytؠ:ܵo<g=o6IޏblHCZn7Fndʿ_քVMc:v+F;ܬ)&#.M'#.bYw:$=Z' &hhmZX\,ʊ9C!90r=m3[ZWT+(a19ł<搡vFx~GBUU(;}nzo]4B:=.at$eJ`r=q`iCJD#.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 Oz㍉-|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@~3Z/sۤzk.x|~D#%鮈u*Z#.nP>uxiԨ&'aS92)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 n17_?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|YY=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&H6PXciK$q4a-)>N995v8: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$42qme#.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$\#6P8tbOIan0aL bB#. ]\BET d(so)g#.4F̷e`1L`yJIG¥#?YzK2#.:7ߍaNQi2}}Joȝ.L_fs@v#6ĥ7;_{ɞFy"]yXm~,@yNr+(kخ3zL;,"썼BN3$>q6yð]|'<@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;UZoX: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`߷"lPG?`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#aj6MIFE.3+;b$K*ْTHBn|4D3#6ٽ3${O٫Hi"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{KqE]R*8#.6lTx2iDLj22JˑuP3Tj:Ŧ%tCA0۳=f[/0MksvLoTwe1 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,#lswB1Q"ݐ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.\H9Tw_vEF`0HʡE%x+2<<-#.}ϤyT[%)740O:dXoYt{{U;R%D P0Y3#%*Vq@iAFL`'!#AV#(RBVc3Ea )X`_ɝ XhD;e'4QJ#6Q`(T"@ K `nƴZ:uYƏtF-RTfsK>BoH~YusPѿ 9:Y&ۘ:cU:ٛBr0>^i[d6p;6O#.c>U o*&L:.$Lx㤴~9'tld!1#.@u*n&HQT*C.졉=9"E.Ԝ+ueHA$}{;~J\MDF`aGnJBP=#.ݱcLB"YYj@=<q!ɋflc\1GVl.o6linDCDK)Uf[(8?B^f] tJM/BC]郯i^|E7u[dgZ;K`3;`n節qi8OB!2CP;{]Gʂ0QvD9Y=sEB4:vAC2.\|iovZFQ4ZOi2e671kk/#.]6@4x3telG͆ђ@R])NueVL5).s7@4/:U]uC<R\w'UW:QvbFHRS,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,[Yobod݊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(9OK#6=mcᄡ'J1EwgOR" m"@D__q4|`R쎉+j`Q(L&ПK#.FM=33ISإcҚ ȹ~>qU-^uH19aY?NNѯ]#.y `2/ÈDϐ>#6m*H<\2=|.mcw~w`PM9^j=XfulDR,BpRH݉)_Ůߦ.|IMJîEO"aOA$F5xqFwd;zС.'g|9lrl:vGi2] @3#M.;G9BG8\D=h^=k<RDD`_"xKyy+75]*:p#%"t-8j Ri4i><z%LQ>\3pӰv1gocj^\: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.0SFNON#.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ѵKQmmsVhU_@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ԇ%<GzZaFK^Ԝ%zdM5:Rcw{WhOoly$z0mCgpPhUA|!=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Я[JDqDQJh<^gP%mn=3td2ïATd^soRj=\,c-#%óx; WxzX\3] K^G1ժćk{1BϧY]~<%HmƔ2U\ E\D#6=E̒㶉rǑY%f=MKtZ'l88{6TF=E|"#.UGVmϑiY'. n(h\jdWMأGX|^uS& ]^]s}GPWɵ]>~+}"6U=c8j~>?ss"Cqv?N=3 ύDs\=y-&*J[wbDfNHZZJQb|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녋23W\p4J#.q&zB/!<9#.lcwMzME#.CEEE#%<shypR#6O}E=8{q! =ܝƢ۩)C?s_]#6T?@-!/MJk覱{Z-@?U빙G-ǟ@{cnLQ-eFAk93xx/4PHDү z@Ux#%o22iz k$~c5o}RH14}KLvJjJ%}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 vk$9T4h ~m*8`#%X}lJ^. #.JCVg5t^7!߆ ee#%~zT)AY`NU)>SrYXP\!&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#6OG_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}=ӷUAvrv94>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.dyr %(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{OjATP}1%"͢1#6HsMǡQ5̽9>uR#}3vagvmZU#qVXH2#.֛@$7c<!"2.!҇5Lڶ慙`Vb#6#6ϧ(a'9#6`4Z >P4i~q\KA!nȂ)YɼL644m3r !Z-*5/!*kVDK;"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`YN18ؔ+n5#F]"RO]WQ5#./٪~a#%(-+Gc`|^D]Ĝ#6El\q`!zI<)MG~oxE߿F'Qn#%xTL9-4Oxp8Yx@o\DNuWL7COZ#6iY|VA]&[_5i>sE/#6%5/N(l9s7hgTHYg}_A0VSNĂHQ'UGJB3qdZgЇqq_a8#%I~Jڞ796`|6/ Yrh]3K⻳|3|cuyb3fCx-X3PXt?QL#628~s;{|%UVv,pgYT*Br)S0ep>V_b?BrC9%_$TIUPYEZAa#^ؔX81#%h]0;5gYv|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^RONYGT%X|B54T(X#ee=]H#%wZH_6lw0%=i'`w%#{;r6,#.,ćzdx`9pCYTe*iD-& H<>Rg#65Y$?.xRR=TU}@1ME~ kmmG&Fefj~GM2YAwY #%yMo#̉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+.^%6fg-Ԧɽ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%Fmf0/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?~3PGpC :3yC?P/id+?GɬV7LUQX4=2ݖ|*kCԦjI'> 5UA4d>/ЯHWb)Z]CZ{_\0GLrupZG}6\ҍT20Uʒ8MA#.7;{tc$:F,!::`bqesdQPNJC2UZ*>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(\DdHH"8ERG5֠AC D55-~}#.>(#.<1Z*'Qbr#%*I}kOH~)۴LAGQbY()9gb4 G~s3ܓfHz3J`V߶P+_>q#64X̢3|ͯrҸn[]0Pmy]<(hW?f3MA};`%f#%'YH[#hqk6CH&j Y1#(#.DB2`JhLE(Ҏ*XZO/';+ÓxBJ<9/;3^-i^C#6@գwܟ}RN57n9D#%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 =,>zDZNNV2K.*~ GX5!/KۅةӀyzK*ü`An߉l/g*|[BUBP 4ń8'Pf,͊dď1k;Ծ#ՄQRQ{mzIIJ0ĉÖd1*0%v_η?nTt#l1绸UgP=`*II8'ʨFPA_NAJ`}ÿ|/O>9X<|z`7VtdH^mQkKH.pDGb\3>:ZA*=^W<e|9 p]-hẙN{qbB:8jAKF#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<<=_yaaLf9mxjt3]]~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ߊ\a7g<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כcr1J?yU`< z9=(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;ȘQT4ƒ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&vvy8`pBDdkOVR\:+;#sLŀxrk.̇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#6GMo#.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]<#6i5{WB<ᤜ16; D`#6ɈN^DYuQXhgLm2xNKaʣR+q (X8LoEh.JhBHN_C]Y.u-,\@ +ǯ53,UeǙ2ffX12%X<cjl$0n&z'qQ*WFz>7w&C9#%!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(Cyj4Kd;Ģڅ#.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}2AAwR@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٬cz9$u[}nS(G"uM#%²/lM`3 IBŅܣPb0*z*RIzM7Mv%^uy;%ewh䉽#.Eh% Ԧ>6|,}l@$~#%=$.4eEY $$g|o'&_xre{ڃq#/9xsLXDo7zF%A\Gvg}hKmBx#6I!ybh*O{hV[sU>2f00$Yӑ·+?㐖#%OP?UHjgy诨f#.rk,CǕs(ldXT#.-T``0f( C.h_<rXz.[9WlD~++yKD5ۭ=氇_uZIti!,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늣5hXdi#.,_*i{WBI*@<&k(}HtM#.%hkI/ҲfIrw*(w*RZ(xN{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|MNV2]Q5J:4x(.QHMn]E>,xC6th/ɏ{hDh8_ē+A#.Zbi0"$qݻ1;nxIx5S2#.Ou *}9,1KE垭')#%B H싶0=I;Bq@li8"d3!x#.AR:pxԁ$S'CߣslQap:rS^uz놳dT$֏` >D=N3%cxIʼiă=I#.-3}r#%4vvu#%NvnBqY(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*UGLc3Zo㥷;WecpqF;jt,٤m*a\|k/оhDP$Sx7m"[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*vl6ywP/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)Dr4I#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!F9uVWTvgN^ׁt) c=#j7kgKm&ܹ5.Sɸk4JApN: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-hvWqDAt@QD [!Rs't-T#%g雕*YKtF"(UV#60&(&T0}zOQj#%Nn(VMBsbLcC@!.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+j0jO|f>-=q:¿ע1 ^P\aaOr@^u4} xc4uXgds;",_MltKV7Lš8 9T(5TH p_;KeYJ9-UAXI.#.#blImUdA)jѹMF4mj!6I&̓&1>!m.[R>]]+.]xjaJ-#.#6%dAafȭ[&fVI.۹9ML5##|[Au_kA:QB3e/F ws+-+p.QS&h%mKO֨föҊ:yG`$F"|hP;#.aH4K_k@ip[66y3xpBFfL[6knkTݥʗ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.'QE5bG9;#.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{*bmd Z^o_bc5#fV*(i`!Fa><90ݽX #6"tXa3ٟyNUMaYb+NbC4}0_[/cyOW}#k#6xn4O3=GQP+|ɠV_fw&}z^ p{"r>MG]eUY#.q E)AT$/rdǥoK}eG2XiK%x4mcǗ$I*#18--ߣ8#+gǏdC֓Im{N`g"PREΨ)xcB?+#6cguVˬ!EPӊ#6+G0'#66[G8 pI5,nm_&ɂ2Wc.["^7L<1YCu-4'ѯ$41g 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ĦaiFELu530W>ًө<4̵<3t,ņq4hQSRJb5D(EVLWUޜŵ (%?Kľ$.1^{S{eL:"a*cuo\ a\MLhzIƴlV#6'S;ٌ;WGv8acg9t!^Ѵd5+65#.,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ƙCAibuS"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:jn21O)&PmV\[lf5,yULb0vB&ҪCr# SH^i!ʻI>v8Y٪WaEZNχ+a'gj0U☁rӊ*L#6$ 3v{"aN+(0e#6Bu R`hÀpPI,#. fF#6ڰڸcM_{Rƣe\1f2R;Zaf%ñb"zͷ9V2#.#@DEuBI@as#]hPh}uz1U tͮNzzOQ(TwqhR`pZPL;7CN]"̎̀#6.sZÒ4' ^le,Tmt 쉌SR3@0ty\)&#.:b8!K@]Nc4܂@re53Al2ȈbiA-RrvֺS%#6# i"23SdtvNI-"S#.['2`Rqfd5eR#.XsL%qiL§n2c*<.G(NcimT&pL*[#%X*tvmB$I3/ObYDw\#.dx/#.Ӊ\l#6e47U3}/lpNZ)jx#uMXNRҬΠ|`5`p3Pf8&7Pn°j;'CS_&ӌPiI+]#%OD,ze̲9i7R#6tS0@eusǁM#6K5BE!UAIHX.b004ڐb;G.NGIF1T*N_;QwWm3oA8TX$GaJtކ`g N }[aYIB5R[#%l8q+Cȡ#.F+2fis l3YHu#.Iawmw#6naqʌW;eU88JD61C#%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\0RpBev#6h"Jk[m&Rj*ᚈPRqX1H_D#.o=#6JABp5㊡\C$;8SoW4U*#%nnR6._SWwUٍn.nn-sd5<Fȵ*}A<`PLv*r8#6#%|(u/qz0g-Z"({'@7>GԻo?62.0V<?|~r=FwEM44,;Si#67PonFbh1D) 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#%mLNLuY^sŲa#%=wKbmlc+A)aLDH-5Iq0]i> k@ /_m!* I,ݏ;dD_&,ŠúMʊ6{8Q`oUNJ## qe]gRrE0;>/#:>mcz"4U#.2]l0jiiZ}kw3FCzi!Kbg:'5bCVmc{^ZH8 ˿+#%ӾZAI4z^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+cdrpQM=W0arr1@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#%)"nesf: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#6RTY#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\2eWC|sл\Lu#68TT>KtJZ~VPvHdְ# = 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.mH)fu'6ơXVFA4^Zjvg6& b SRF[K#6@QIL*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:Tshi5JKF'3GÜysKM:]` 2UKՕJ̫ZN7~R(: @"ȡQffđiDThA~~N>TPQpGǚpCaFE<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-:+~A8uN35PpCm$/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)<( 7uh%,3al! hv>3]l4}X(qOtT5(i(#8+!iH@aS*VzPT*dWfTp9P5tQꪅE#5v"IUl#%&+t00pF`&,}Cpt,VpCA2o|'Pg=b,q B=cik=A~x#6gLqˀ.H\h]I1#%%扨&?dI~;#.a aQhFpA24Mj-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~jDP$SҞԣCN/PlV_UAd: C[!ak%EIH^EWc|sRgńX!`≫r'P;OybPWG>'`y~6ߝ=a,rtԟHT̉I9VTSRTIPц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!Imwe7Yq1 +p8#.ߣ.H(MT?90`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@,erIcQ4״0NK2#.#%0#qMnXq[l'}@a%ǿ374&iG<!DWbUTU˛[H=TvU hZ7aqP0a̰oˎu> ECObȀ(|Md=` T6qr.;Q$!a^ҥ#.-TiKJTcjUk)WQE%D/9@5];c{nJ*dBda6'.vFqpFCN3BKQ4]#6Ӌ#.g8bCְ :P&&42iPZ0#%G{"M18=ɉG`+h}*Mb]OCXdI"BMQQ<nF^Ϻ!}z`ûdW#"%54I7L03'|@YA#6?KmɃ:Mlp(̓V "ԧVl@,jnF@$+RM6SHDB0 D#PN5X4O4D1͗='155{:eQi`K;41$еcF^#=ΤA>iTSMZʸk2P|T5PajJLie5tA&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*ax2^30xp#6XXPȅp>WU8;g&Lx;Q7VZK6x#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(P8' J*ƛ%S -l;wDÑ@P@4BPEBȔ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%$,`#%*#%ADPȊҥh#In1l+p<;T) ٿ:Uyne#6eǷVQ12Q#%dd!CBbuE!Gv#6($U`ѽsqKѣv\5 H%A#%Ν|:LOBŜN#64/ LdMr5wmJIǒBHT*F2D#6_J(-!nwrPl W1`Gnb 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-`UvI{!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䃉eSZXKfsM%8Tb\?}{>%a8qgd;1C) T'd%Bnp>f>yKp#GsmGycQۚ%\U#.$HH0QKACoUУR*s$.?Kb\ň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# jG#.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!kq.aP0OvѤgNJO5Ĩwyrm|#%#%p+UC'G j&˖ ![#%BOE+QZ5mdƩ5Y,mb@*TEM25Wn\4"Ԗ* ARUz,.`!i p:6vg7nY;d(])5=:C7-\n}cuWC2mFmCEli 2dS ~(Oy!wF?"5j<CiRPl.@#6%5U=Ѯ%O?#6\p`v㋜NJɆ@;$s]_2BPV⢁0`Cm79FD7|Y&V,[}]t(L:"W.{@,ϬGo(TJsI/#6yfx&qK*.q<L%#6eq}Kw4(#H *EZ#66-5W`[@DFq#. ̪MZPwg{7>Gcc05⻝vo1kp&|OYQ>#.zޓŇ'kjaАTʥl$Bm=eYb]ع`#%.L?ɧtnu#%NL81zp#.nʪKqt/B+5֍kYf2UBCYI MIǵ܄$:¤*uηLw]#jffPh]u<QfnBhTci0x`hpTZJ0`28hJ!ѭS LkLDa6(߃pHW֙%Do!HHIjUM̋6J\23Y2 rdŠ#6&ۤ 3PEZH Z@0V52lfS$e6,3$e-/]˵ri˛rAf[sNݽy"hR3wٛf輸"FJ2وA\h@4Ecf16#.j*cq0UkH[yIQMҁp&b\3Pֆ4ս1ΙEnՊzEFI[#6<qTUu5!(db吴#k7+6\]\Y"i1yic$Ii%ehCVc"sz(T&0D1~/aen)ᙐi3aCwc<j[J\Mb0s.#.֤2E5`il(AGMٷ2ATnܞ]DO3JiAӫ_ݻ8+skhgjFV4.#%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~(Hgf='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#6kfl#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#6OF6ӻݤ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#.#.H9F1KQnmkU~[` #.;@@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!QBt?g>YAh7vgy\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'=ؠ1HAcQAj=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ݸ6ht#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,yJ2&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"9nH<J#ر#%1Mn xaOڟNuQbz` 74*9Nt Szօ8 zJ;ExW3Cd6JHa`&C! <,]IZ:}P#6f27T6EnmFؙᝎOumoٜSV#6ԒI&#}3lCY]6djs݅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@ٹuWnq/(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[(g8uOڨ 0V#% FB$<jOfD+8Ə#%A)hB+ qk_g??̬OS/P@JFQ?2:߷gdH5R#;M羄#%xN_n1lX+g_4k3>Vgl6өCqEW9v@яaN7vV?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#&DVEFi(a S#6H-2@aZM'"1Aƍ<j˧lUM8KAء)"Z9fJ6ޘќ d'̓4ǸjN0fii(0bQHD5qen#.nO\m*%oC#.ʌMu
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;[U0IrYvk^(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ßXJ^v# ECyƱ, A2AETNRQeTމr\95j#.&t0cZb8z0mEfHMq49 #.UHr\vV^QkrKhKrK\6UƎnV鵷MY#.cnm&k-͝r,mjQ+;-#.w˻$v07<}Zkzo=H^"!7@A"#6ޔ=Dv_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*kjZVjZ52bci5[Ϻϝn$cu#.Ǎ"6W"Dne6aPA$rtӣҮ=t<iqDO c!&$*+hlc H-a"#6@`i#.H4ECJP*(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ŗf5wV\)2l`qDxf^aTkĆWnh6m<py_Z>ؗ[hNI/$f7)֟Gb$CÌCC:s/}wʀDJnTQjPxbZ:>춖9W_Jװyixuw}:甼'u{a*^WW0o8Vx|ʼntCVo#6Uvga*Flg/=5ln.b</Deó՝<s>NΠlrA<Ec!"D^y8ܣB6Kۭ8,g :dDLoY0Pڌ/iysd)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:L6x`v`qG=269kSAC#%6s^zv Z3K3.&˘sx\#6?#6_Dd\PG`{uDO.O'6[k,* Gg ¾T"Vs##lNƹkFaCghz:癎>cETQSSNFb((SۜMe{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&ow7A%#0M8pq<~7Sۻ߃LΥmZ\dQ{x09hRĥ#%}D4bͅ3璚E=;H>.x[CUZ=7,htz=<7~OfBEo79qLB6Y܉Y;W\q.6)sf\=#ՃV4(v<@ǔ,,g (yzw-H]BEq3:f,ҭ'! @1HF vM\; 3@.g$$VtIm-SL0w0'#%Y"aU]C#.`f+um?NCw"0)qOn"xXN&y~(p`\r P <߂(;VL{U#.>xKfw'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@Y0fL7zU3RXQ6֥4ێvFjhMUzmkZALk.<c9eoL$DMqP\`SZXL*ccix12BC$Υվ59;haYSJ!#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("фe0QszXr8cMA@=6alS!vH#.0Ռ 0\d#3$d9:WbTFMP1#BCV#6e-ͳZ"ۭhDFb0f8҆±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$a0{w@y9;bƪ['0ot<(kqΑH!Q:ǧ @H!5i WjKV\Ջm]7hlPPHІi#D~9ߥ}~v"R(H#%VZ#61&1D(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`6kgμ6r1JN2,=4,p| d<۫6jHElm#6#%("3@pBh_*UYU#6!պL@ @+-= #%]~7c#`>Ͼ3CftB$#!?"dꅄ|پ~h%{#.,(`塤|ؔn2L3@>7Vƒiy°=ߵR|2Zf5Uo[52UQJmZJK*G;&uqmg*4-o^n2nF-tiD7C56&TS`&!*QAr]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$&5DEPڥHA~3#% LC}Yf-"}qIQ%^FZTzHK"@Spu^<o.B3@DmjtalrX@zQfkQA(n7۞W ":7`R7{BdXg&Sf% 0,DuS2cre38t#%a%筻#6LpOpߒX9WDvkb+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\REFe%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㌱4l?nMM$Y'Btoi\+~8]-Y[tͤp?UQm2LqE#.q<<4x1 (3Hr<9kt Ơrz]Y1,̊sզ)F.zy|+d׳F~Baud֠lŇ-Ņ3XSC=qth:j=iu]p3Bt_ m}i2H۠bvvx$3@̷gnՠ PJPHq!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ǴQPP4wi8,H%Fl!6ae0\*hiD58h陖*P K#6@ALCD6DS*wB]4,6"wA3b")٘iUHMlE 1 #66v4#60gSP#.FHWU%0ME& 83Ą3mtHrĢBBꙕ3[^6_#.c%%yyCkU@(צȚnf C5#6,"B@MbP l:uy\eXky+'J#.?>M܇)xm&-BkM7IfsQfE뻺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읯!<ZH|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ǿ00~tm6V,V"$skҌʸ 0')HH2DO"ZͭiHJXDVBEGEE0cUƄe5@mn\0P`M`8#%#%Iѕ*Bah<2ƦZ\Ȥ H:bX18#6 #%H*ߖ[|4O4o}#9thg#.`Ÿ3*\Y9)h#65P4aePM2Xl+%ʫEEDLNAGUym^VvV`%QV@INWP3:$#.EG^2S4#%ܥM2,s4PN0:=G,0!&*Ƿ=aօз;sHcift1aS& 7!,H#%5@%EsRr_kW:*'õnH[h4`wAuu#.,q89 J;3O8~k1 $9%y6ZhIEG!+]fvo8`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)\eqACD8: 7$l}ufێ6c6ZAI%!pj[-[+cTdLͶf܀#%PYl!VYCrhM#.ѭN0LoţJ1ȲnFJֆbk#.?{5b]%OZZl}0 E!,"g4 )@w}* uAԠ$ALpIFwIϞ/tAO!## jU6t<4O+taL;X#6ɨF#%oٔ1d@!T{WnWm'y͌6@g8AZ0?y<vulfPx"%70'ws7u"ݗf500.=Ԗl:;X澐QP@?LSa#%G1#6AffXC&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[RTl@njma0 NR(+\/*SRe/n.gUPdt=v9.kNi`Mljh([;R*5XVO8"Ņk4#.y@^K?W-%lc#nzK!XЇovM)%I51R`FZ7m+WZ(FAUdQ$9,i!`P*6+0rBF"EH(Ʌ)hlQ,#%h}qz/uJQ!"kWmF-ڔ5F6XXQZ[3:zYݽ%$kg#6с5#1KL5VFLкƗI$ȫmiGl/$3"*hc<jֆӃ90(8AK"2ڷnI-6N^56&\wY2nڊڂKdYywcMkRTȔkΨ$ISa!H6bcMi^חjMIUZuujlm(eRםܷktYc)&AaNΓ#%#6qVm3{䚂5Hd>QEF*d#%@ S㒡[Fhȓ(1P{5\&4)Dڛ7եG46Y3O `>p?~fE#6ݧ#RffKXZ#.Y9~07"^GM#.UJmG/lr$aB1SJ@#o($QG)ļ.#.Lo.E`%1GP5/mlpB#%|$xk(#6(i8>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#̍ѨU1Qߪ@[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ߐjBMρ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\UPCGݼម-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|KzG)=;{<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]LnT;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&Jd0Be$Y5RDKJl!EYR6dM؉")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&$қFQH6cA22Ac0eLcH2 )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[dhMe-!1!գ%AF&QZEHkD$mbj6hZLQLM14-4m15[$XDRֲC!2D#4FŒ+jUcҩe*f#+"5Y54lBS)aɬPPʍ$HE"ņi01$Ch4&1#+[FJ%EM4l4Y#*)H6DVYJQJl4AE$&Fm6ccd)SL#+1#*1$d#!4LQJdRP5BT4 JBj-"ͨQ)$ hRb"m&Ƥ,"XBbM,TJYJ2V0H؆5"jњ"R#*1Rٔ@ Ldؒ2)6IR`0٤lfERZdmYL)aD!2!FlQIS1II hQZ3-#*$,FZ#+6,e$1RJm@F5D(LTPXMI5L1ƩJX#*)Y#+($U2KD,EV(-&&eRC2Q%MDLQhdmSiEd[ZLRVSllCD%2E 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'_YLvPCoEG.)$M>fU#G?~{Z?J/e+(4{Jvo\T55bӶ'$#*R(8'%f`I"lLhchɣwHLo\үRY9IuS Ś&U;1̡LU)I1HP[xenTޛ4V"S8͆@.9_Xk#)#*)X)hvIlF1IޢܼYzfձS))Y*"i^@"nDDWXж(D%gF[1D!9$n䠐խF4iY#+qAW6d5snv$؟]mx,EAoOW[Lmː9x#Q~="!AeRhrꠊXOUԆ\2MM;Q7~z\n^+\Ѩ8wcr˙(ˮ$scKTSAr6bDa#+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=8fw^w0{(ʚ҅A@P#+m5͵>S6^41X?ó4E#T/65cOvn#)(5Q#)6i\-z˽zFiVJܱhܖqQ7O~FɷIoШR:e!x鵘OrTB)jKGrPaLֶjϽ$+?+uedDvUд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`XOq\NY'mg2||#ӌց\KP6@{"i:O8hQXPl-y)<!Ɏ1N8r~NZpdn$gesZ=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;~i2i>5e 2}֙;vdM&;D;/[0eg#</G5Sӳk~ {bYׅDQQe0x~r{*nf gsk#bS!Pm쪦HVmw~W5FA?8.6e*DF/:hʳqhPmԉlaKR7gFTaes^o#*b,7K\ϫfF5L*9]Lu,a?ʝu9s}r~)|ݳ-7gց"+E3|E['yn[[(/c#M5;7uvkBoyq#:*!ߡ@2,S\&X@E$@jS%ѯ9Ar*[ڭ9مHT9#*|P+JemAisW9^!UdTā qpԡ)M_DO߇79;ҙ&)/)UEYL68:<pw+q.~?߯ƴwZQgYgoKR!e=cêۛo`Γq+\Gkvji7XH0U4UtSfrD^8Ƿ˧oy=O*91btEWZ#*&5OׇNz\패gvǓ:TZ()'u\><:<&.#*iEEP~P9*18pM0tmH][џ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#*DGN66YXlGgFfJPF^>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\irjh=~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əAnNPNKYn̎ZntvfbU'A#+Ks3%ʘ,"'Q({eڶӑKD@JE̱#+hYS-.6-s0(<1qۋȤ&X5lS9;\+=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ڄCMUY} ˖p.p~Af<OAE;4JbNCF$t%_6!4q=2L: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.?2hfDj0[Ԡd>ToX[0v?}QU6q>?kН#v%6g#{`,d>o,ôOrb5)צ[HmtDͷϖ6.w8wk3{euUWK"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ֱsTnS3yfl+F2EI&f3sՔ'6@aENo+_WGTx^b'Gp b@1@00{a(zݢkˈtۡIb#*FWHqFYT_",P~յZI.3Ku-(T:EZX2}u!;vHUa=j1s3#+Q}cJ7oQHR'E9jy?(=$yq#Qg(^ɧ;B2Z!(lNE fR:]y飙8<XHtnyPЙ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^,[fn8#)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<+v8Y,݇~&#+?]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.-R665k5phQV#*RK܉jQc*CPDo[Hf#*҆K_vjNf֡55iUeNQ8'@PwAo[N=媼[Jѐkl|{>P@KJ5th,+^ݧGGZ6!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 #+#T0dbZ0 |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"MDa,~rd[xO#(lYCq!7iCD~h&ʊR@pVJde/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#*/S5n䟦?!_ Iz}$a>TWV4(H"4BUW%gr4#+bXXH࿎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{GoM3hr+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,4w{W=:}t4^OBG|.͇,qp;E5÷lmj*<Kj[6w#=+c'!;^u?}cdኪjߍK{{HTͶ(Ga2|v'FK|5gA7jy:-Mqp?+W};hmLa82l>ͼ4Bu"q+QهWu0٦KttMA_,B8aSG`x8WUVϾϷ<?I]F pc?gʏN~GwN^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/߷5FRp7Wit|?oGр#L8|Ǐl?-O8~ϼ)bykG*By-cڂh)u&fXC6ԥApoNnvu].}-[O'I'm<EX#DPw/)*t<f/؆Α â@m,#)(yPƪ0alKC2u8ekhltxb+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~NpPThxDoa$a*?dSC<QJyGX7Q#).ym6`+w?Al:9O*?pf@9% Y`#F54iTlTԢn7#+҃,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::RpIC␂mh ͧ`2zXUֻ]b0Q$I8~UIG E,IW2*EsEQUڙQdec=Drf |AI$JCo!A"^1ba/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-#*..TAbXU441nEtH7^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ݣ#Zyt^@>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蛎JYY1D1ʛtikITi#+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",#)̅L0~I}ڗeC#v!`X#)$;#6CU*?#*vg~ GT$'C#)7QnVC@923^ȈpIcg{7yIvc5#)s.1x"]m GG$\NjI6Ӊ)J{F`??[\mÑDyŁ)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($x21~HO#+?p=[uL~;ii1#aAIXI1A*ãl9HZahJDG9|8#*Ӟj"!4~Uc3h#2к5(rd#z4"TĽ~;DxZjm9QƈG2'tBw#+ϥb_;2I#i܌"B樻㼄t&p71$RLf2TOsũw)nL9)_Gˏq\Cۺlp]EE߹՚ORsK`5Tj^QDT}1D7ㇶ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<hYoFI#*K&5ȣc:=ֻKtϜ7jmcЮѠ)*Dw(:@*as6Py#+$E)}'}'nkcg6|$ PF詠or8'je;MFlrF= *\:Eٴ8بzi=Fo%*ag37s{>g:$qCnsl \=<U"x/O`,'{YE]*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[ڬ:]~p6Lqi1={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~tnja6;RȪ!m2h#+GMqrsvyhgImw="]ZH&ѥ)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}vZzm9ten"7EoTv8#+g8{fj*b6hD6&݄,Rjtq0'H dJ({Ҵu>H)#IY_#+Jp|JBNaỞ`Vjkٲ!y\piR-A bg=3KiWƕ}ZKQdvivW#+1Yjȗ|C_(يG`HU\,:^8Bq-]#*S~43Sjn{w46]_b9hx #*ӓcZ#*jZt+d$Ō'[_zZC1M+g#*#E-_+g.; @\#+59Bp!M6iPg,21TY)z>/#+,:ν1KSo@@28 )Vy-U~vC#+%PoYZQƜ6ϩÎ9QT~uj/EJFjmxR$ d5Uݴ_#+q#<<̈GX-;Z/D5˷WiT2 bhCk3d9t{m!ˏ|xʅÏ:/f//xN;Tǧ?]uG<3+#+^w)ZsÉDmyL?港LK|RԎ#*]lZKMeϔl0"DYH]n)OI:|Zz]~77Mts%YIQNuTLF`oѵKKM)U;,=xmDt8upq qc%M)l*jt0:>1[OS;dē{79ȷӻ;+o,a0CuqoL_T:6mrEK_s){g6,e>t&<'k+Ŝ./ՂTې!#yFG{S6:e#)}+ˀ|1ҷfN<1ӓi\7lQloa#+d$cA |zպtǤjR<G{}~>LR,9cC;-r)ܩrĠ:9UNmƧ}[6j5tM x/p_ftJ6LY5TpGC{ۈ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֚_Ru0r8z\ADWNxҺiU~iAxb@ is=G!QDbJ)і"#FBW23fYRDHҮw{#l/e3-v"U4EPMwgNu.b-ڿI!"`}f|~ݼ5H^wlDm^Bnb3F۟j!l /VԤk$%Usׂ|&i{)OJl+}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*=ZWxKcUb-[ 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ǿwjm$?;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Ύ`ӌ}#)zzqĆd-./ԈyuIQXn,-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[qJ)羗\l[stGˁ +Q`-$9$(!=e,ˢ#s\{uܣUA*O.6yRN-_x-wړ6JsLC|*DpG#*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õFctmFhMrk^`GQ^#m#*#+t3:PJ70KOC/ooOߚۺܳl3$m]9#+T@5ME#+*C\t(sݰ=%b(laM^~\GNrjμߑnSQOIjcasብ#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,_©*",{|stN")!{!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~tfSQ#)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@zH6iR(V npm"&wa!Hqs~!/1xۈ댫{KqF|!Y'YG&͊e'uZm~Dw`wFjVP?Ωi$gWvll<㢲YDtZe1!!އ=Ώ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؆p5VD9?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ȡ(AVnN]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{KLUA`wLjjm1yw&}֪@TLv9\I^:F}r_MC$&N1҅@"1#*@u&{kuvPć"ŗN5Q\\DA7')Cq#)r#+DhRHz4y.(Pw?kVsok0 P!3@$QM ZlƖKQ.1X~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#)PK-<*x,Ԕl}Cu7aX:0!,xDC@m쾷FÓ$cy;oLIW8J_δ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_QhH#+#)H( eٞ_*43#)GDw#)G#)"kO^#)C,?=3Axt|'Q@|}"ϢD*??o9φ`pFC^G3Za&nyEukʽ#9r }%'63nMQםO7B3f{spRJ|}@y]nGEZbᑯl-͢@wv=1Kx"#)ˤ~P!&-AQQ:^G#)@ܿ7jLjJm[Rlve(Wnp'y"H߃l ]o2LY~ŝC375_[Pb l?,&Gʼؽum{~aOe'CJ2>3Ҳ3* {G˭>#*#);9 h`6P#+!#)(|6OO#:BoLN:?mwg՝_Q_Cq3"n6Ϯ;mP6CCKyT"œVtƪ[ÍENZV@L~q98:Nαw6oglyϤ_~N4lmC<{=\ݜѴ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֪MdžV3BADR!)FSmaz85Ղ"4lFlx\C#c`YJ=]A*w'z>1Om&k*#*x-uvNwe2N@@+[(M]dA7FIΕAp)#)TDENFr*s`#*e:DHyj"&]#u5;]%'yfoVo5~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>hAKՉ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 6ywN;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ᚱ>5GS>kDmo IN#+r@({ 8x0P\BQ[čX#+BiLxrD)fKAMs_p!)uӼ/[;h/KyNi6+[G$oܵQbPp.2|b龞h"&jt&J\E/zaݕ.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$XY-$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\^! nEmMoP<&GjNJ&dqY-8kuZ7s$qL4Av#yH%XjBu3tgLiqTGQu0D_$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?RUTA)S̗$tOϪRJ0` ^#)moIj(߫C}f#*NϲeQb< #)pIٶ@kz7s8muq/H2>t~6~ŧB*E@9[>RLQ (dnLلF@dd0JDpIe!$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:aM̀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܈wFlZmoXBCQObDXkSێ/kq6#VPKtsTK+Au~km7ބ:òa,&q\$@ɯK#*J2uvݧ3ǏH.b2e%kۚt=#*ctn^`$ Mu^Q`mclR5:<0yuvY<DB-źy~oғU^uDo3q";b0z߂.hthBmOrv@0(㍉KI¶[R9dkb(e$UuXFnhi4LBc"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ñ.Auq#+iSrk8*8BW_J0T6( k?3u]*=cڎ]1rAZ;"dT,+.'P%х\K59=M@e#*C\73O =:$A7g.wKj]q|t1fwՁUE]-Go==Cd_C. Y@#)`tk_?Wr0kmd#*P6}unT` ieݠi#+vB1>m>StKKP}EjIz.^/C`xk\;&TZ4A)rln#)#*#*nQX$j<#+ǃ*CؾAV|4U!Nä+mښVij9(Kc]qXK"XYHq; tvHYlN鹱{N#*cr6I0űBt% w{qWM)'NY@ V6kMT(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|@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+pok.%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*oN]DYdR2>X9εw 'Ƞg7;Z>Ht*?}]]%콅x_dW'>&X1UD bVnZCU[ևL1 <FI$@&!V+ ]4d>ЯI>S;GW 35[S_B#*/T ߐ20U*Z3#*û#*|5e_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^hc%~( !ICT<eƔ!W1´ww3Ԇ% UASKȴO|Ok #* #)PC2?8'QEE%*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ʇTdK9lQ^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}CC ."=='3pFP}Ծ#8&`wl?:8z/(AJ8gILozma=j$@;]wwe}ؼ+!ku7mQ8cBaADBOoݮp]ИEΰ6JalU6 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!! [0o,|]ejft̶#*#*]*EEFeRC,D%%D_hq=[VkAUE`<5030dz&ϥQf8M!C[10~Q-1 *SVoY\fP#*DRH2[hV&X`iopKʁ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 7Yh3k=[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}GJb0o~ 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κ`enQz('ntZax_&zV*]0{fX..{pHYثmy00'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`62߁`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,<vz4݄}g1V!{QiM5vi(E* @-iI-,aNa'#+x+@ n@sm #+uɬu^n(CwwYݸAq*$Cv: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-2gsck.̇xԩh)&D0aBUj#җ:&e%Ds.5Ĭ^r)g[b.;a3X8:7o<(yv9@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֦HAzik5lL㳜H ES-ΧMPak)5D&ap+J=+lBuE^»bhloo? &6LEN`0IDWxa;X(`YXj60҃$9gv1N!lzgUQ#+ P6{"!WfD'afvc=t"Z[}zuR˘#)rxd3U0P$#+Oa)Dp7w7Mo6-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ϦS#*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#*5TpTPt!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_fzyAJR]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(%nxwsH`{!!%-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#+;eQB6EKVlUFւ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[Ř̑qUDvB422n*@]t$ 5%Z-٫I*1XM6i%lkImdeF)#)V1~_йmn<j}P;XI W|#)#)`;>öd[&UvBCc*j 21aYF6(~#)VlFTHmkܢVHcQ$4hx&4_*J2-#)0_@/d5^=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#*Zbi1픉;+.9!N}P_ۮo>*NK2~G[P>XQC .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@57u2T;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եǁb8Ja^XR잠r@vLxztuD+A*)gh&ܕ0b7PTFFQ8o%Ƶ>kv(M!;2#+!#)FB@wZӰ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뮹gwl6Y;6DdSW/WVA|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,yjU1ULgĒ5 >"qIy9o$B3{@]k[j=cj7kgKm&ܹ.Sɸs4JApN:lEƭm2L?]pYԡR}A}!9Φ(CvA />!!hl5Vd)p.NE# кXX)sC n@d#*sV r ( HWÃ>4Dg1 $~̰$#)#+A̬hk7Y LBFWS#*#*0dL0Pqgֈ="ɘj8NXRzA#*``(3S@FQuu˯.,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}GdGH#)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#+ 1hE2V"""X0dLU)hn8Q4-"X 1fȭ[&fVI]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ԡ"PZXnPɭ&,LɉfHTPĥC#i,)%%0AD(ɒ5Dd)24iLcIM)ܓyS#+{'t{?++UW(}ٍ|?uّ;!;8H*9>s#*#*SrYqS(hޙ#)#37ZuKEF>6%vۋm%A<,]\vidZƗCZF|w.vcn6_`?YB~+6&pʦAJZ^oůbc-1Y6#d(a;3çQ跍bLQ43ٟc7aAfJg9W8Ć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*.#+mf8XPVڐ(il_zРQgrIWŭ` 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"aQOJRGkBkbl8gm]%|;]Ȓ*&Ps3 j5'2;Cq#+;<2 'XgE%p@kṠl5|*=W_ʳKh#hc#+Z7o6̚u56qFXnLQ桦eiiKdhѨ0Wb$YB,.7:fڊzsְ'|JҔwSU/^,3e7Q~ɇ[#)Dg 28?7V̶9ѩpiy+;Cs}j#*.S06{P#Orf+%gQzC#*CpKfē:l.v0dA:o,7#+UEAbYi疙GA˜>#*Y5V\0>x$IHݘ5ΙwJՆBh`X;!67LjL =_mb.Pq#Z'=嘤:5%ܚ0{01lGjij%\X5vxͽ4V4ZMDx<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^mKa+a[*ɾU쌦!2(1 ٞY5d7s%}.oͧ)8RcLܐcZX!L2@&hCI5$mwH0냦`\%Cj3fHA А#č5ND#*@\@;a#+i[T싇ZN#)]`rmepn%>PiI#+46Ijv0,iQ!XlhpNVNځ]#* QH.q.bJ2E6zr'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:jn21O)&PmV\[lf5,yULb0vBW5Y֙fB0 +.2#+A1BPi:c=Xj8rUW8bvgN+X0N4a9q{"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( A1ej7)aD@J lͪviZN(XS7pͤCa9Zf)!윒ZEO7"eҤ<0j֥:3T#*J\sCUxeV''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ǭf9i7U@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ΜkWg``f!xF##<f-[,pCp"o ŵRnKA#pB: 4*DQ*4kjIQ#+"PT!&)2Щ 4B,'^8(ɥgp2C~u6|٧b *EUnRZ9uwu]26J槗[vK#)\vcTw;7#)$3C}C̡8jQaGS`=?Htsב}+&EA-`'c̾B6zfiZLPOB0@cK.õ6x eLfk;[&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Ƅubl 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Ӄƶ>s7a6WIT%/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%!##+\P0hHhmQ "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赬fn51bHh$̕5,aiiC6O}od@\b*FB,Z2"SF;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>WhJ~=;6@=ں6v#*G][{LcښPP,;n !&'xQ\̟y^)6#*iXVYlQ2$QhќRh4C D6D5$-p#*9m&R(7#Jh\#*ch7#*KT3lLeq,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¹(774Lmu#MCgӗ@&MD#*Ӓ5Ռ-8vI9:bˑ(K!18rUb*^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`1vE%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/a7f{+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~ݫjD@$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#+p2SPaDX!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ÿCdgW+0bAJb#)h|&W28܋C{WFJ4*jkWli5+(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^zgR}Ϙ:z#)CVr̔>?#)8 $j<#)j4["كRRdLŵ5&ƴdkev DʙignmjlE3X%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')&#+YA ,S4]P&云keeI߅z_;#*xoMt#)O 1 !n[$#M0)#m$_/w)b`HE)*#ӲvDIUV@Z-Qzal5Ov=5#)ٓ=UZH_#),H BʆPjp jETq14s#D>i@ L`)v@Ge5ΰ3ɗI#+53@!duhA-͏--14Xdxa,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'Pe;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 MB$ƒbjĈ&MmRmZTH2c,@=Bd?vi|%Bn%I-[CbCCU-03U;i]=MұiƱ4 I^e^5%^;WKj#+-20,f(Egr/E!cLL9x5bP#Xed9di%<y#hH$lx0@#*9B,\*ii@e3lb8wn_\\;*gjI KU8#)ʘˊ|6'!>M-.rg^Cu';IÃ8'Iϭ#)}t%u@1$D8yK*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>EA!#'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`#4g7N9S-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#)82A#*U *B8{K=yztgf#!A@҅STU7I209ux/7D?qpkAk Tik"9qE8Qs,86i-2wCc8wF6bb,x3Ϟm\YA~{#)^DàQ =t8x3jjgoQW<!uEFD NCYI MIڍBU<aRavw]#jffhh!#*ЗʃzY(TP$Ie#p`48*j0bnVjHptkFTĨ$HrEiA К;[$1dOʮ!nfUDTD1jW7fV&Fy&T(#+v7[QAD#%[(Lo##+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ùYi6tӛ3,ۺUY).YA@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^5a9UEfT5p#*PMOF,bGI1DP#*Ы()e z9RAUnnnQh P4-[y*6TCH0c,%B1TAv D$ЀrӁϨT}C`奙a'44HjZ1?h4Q>Ͻoz4q=6uߡI[]TXbjDפ=+F'6#*=)doowIعq7M^O7ɫ;2)eYIN[{7tn(D$幙ru7<yp3G#*#+#*E~XFp6b9;TXM$+[rs;0D9B]va&}&~Ҍn^>GXg~{ϬO9..ݚ>tLa#܍)*"e!柟5)NKl7<-6lCe~홦jθjRNC?S|pO|{!0AdȊaDl- u%>*GBY#)!ΚywwM;y[J⫳kpmRm]bmy.;s!ίQOXHJu]ݫԪ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.gEc,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!QBt?w>,Yi^ygy\2嫆uf"i$l&ʖ۴%(l`%\^M]v9й]ݺEroJl̷W.,hv6V;mnZdJXr2d[tΚZ39WlTZ4ri-_y^j#),*=P#*; D!#)zw@gb0#)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Ɔ٘\.RHAL R$Љ!@X(&eIvlI$T8OoPʫ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|%DtopdP.ҵ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ެWdSٜc$}%9<fHSrT`0>UNl "^ߊ%Ͼ#昦>YmVЅ,.]oF3qN8L-a}d,Y<c2H#*#*0oPŹk=i4bs'4e*,sN#H,I8h-0Qsz&O4{֞m#)BvRMѼ-?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Ғ& ^ |L5I2lo3A\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*G2pzcCQNq#)s#*An#-GCs.6c;7Ǿq1qq.tr Sյmh<4kfDW@~9({5BMJu;ǶH@wQ`DGK >XG1ci>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#IJ4fC#+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Ռ#VM3OK .U׀AǕbbJ ,|u=7jdtEƶpf3fIkRPnrnbZ@<(ӄ}dU*8` w3NXդ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"524K7 )"#)8ZhpP;zR%R%Z+wv?ytŨkutur;rV;|-WJj-(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()fCB$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\eb0NRQmTޤ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'"0Xdk0#)6mjIXy&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)`Gpo27b4yR@TҀ"Q1n6FAX1$ABl-0RBV!" P!#)-u4_!Pj,`ş_pAm)h@'.jnٍí>vASm("!bBchC@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'Flg/=a~U0˾{0yJ|]%.Iwǯ;#*[qwb3vXqXic8Li%J'ş3s4.r6Dt6cM,ʝ~܈MNPPs1fJeީt[h|Tl1')'<u,Gf(kA2ʔ;[xpņj^#*u鮡4)1ow$fV"cR!=b#6.WQ G4U%5!Czt;@lF1`:LokAa+a2pu姃<9zp6W:FŮ]p#+A pL@M*7p}@$$gL"kLC*#)@>#+ )@#A#-qlߣqwas3iaHN(FN]F4:0a(@<eghYP s2l750FA5Dv[sJ|rtmN-3Ðk,2pLWJ%g>vdym98ճI6*fN#+*5/32{\U"*PQb,Mez]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¨ay9r)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-S62I#)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{nmt1h]2*vlZqU')fMAC*#Hz2Ƴ,"E#+#*aDi6K)a.+M1@%rQSO1#+|'XuHW>lL#7T`&581"v\jVp#+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]o94Uy\vWy9+Ȓ3#)mz%r6RNum/Rz@#+mCjp%V[1b[p̔0e}0~1xʲ@ks9QBF+ n>.AQtCȢFO>!I!pN]{9WQFN~ʈ\ѫP#+?3VmD6RmcGZͦƥ#+MFkb3]VZJ}ia, ˿B3(d"ٛVt`ϊ(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߭*K6je%=WTwLMKjDCHĎWػo#`="[S[dʊlA`5%4݊ [ByZrZ}^`z86WҜJ5y<H'YN}G}kɹvSIϜ.b'zFIJS5OflSc/2B I<3Twj7V<@MZ?nj2`#q%A{NqHR,""#+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#)wfAjSuVA/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>}~Dr4vkc#+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,(uvZ0 (K(fcn8>{rko)ި ij!A Q4ZsÖ&&ڦ,`sD^ەk; MNNpW4W*aۓP&~ןX;Qav3at&nɦGY.ٮjߒm1 S{qb: !0dG4l]+Tp:5dAHChEAFH"P5S8:єa0QcN<Slð;*0%DH!.+TКY0#*#*L#)N.jd2#*JCS5&̚,MV6fe`Q:ViE]\Q$xTʮ:!Зz2*#*lC|ظ#aȪ#+v6f4odADàġddjҥ͕MP6'HD6N4#+0`Fp$50JuќQ3#)`q:# 2:@#+S&pI`^0giHDByy9FQQ`1M#*c_P3{!! &U#+(tCy콀:UbTW KvҳS%gy3>F[JPEM܇)|=6MmTchSuHs#3A7b$8l\$ rK5^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;goCwڵ"rNYc/#*XAEF'j#)5#+4V%vHJa^0kbkP<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㍘#*RhIHnbtqVsF?+#+: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&*mJmcE.#)&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.)E4QFzQ%遦Ix$ȫmiGl'Yl홑#*Ņ9=EC&WM-i54R4a&4ɭW%Ef̪뛶vm^]Dl*dJlhV@U#*V$Jko"o%i^ˈJil*uo:W3Le]ۢMtٚHVFZ`&O5UYtXnBQ){Z6gTW9dck?KhAT.Fo#)9 vp\Mp,ir{Qv~/มpBKy70źsIڶ)uFἷBˑȦF$ǩCURWtoQ-xz)ad(E#8I⽄#)5:05$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\Jm5uWC%ۇq<e~e"hK8>Y[9||lpYyL(7|5N<-X 8M[.8#1Yڈ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+VE2Z@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ӛ#gw9h8+^{:~Y&l&6칰Bw$Y#\{b#)c.4B#*LL<2t)zׂ}voi64k)#+fL}2Bh3^iIqacFU1].`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]OcS9ɬצU1#s\p-ܗumP^ljoEwʠ[%=ǕN7<X?럨N#+3ɚgY,L#+<}<*j8# #*D'GDqIJ5=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'OAQ $iwDep#+?dY6occ-3{(WQ6#Uf'uĔ6Q*QһxULqs8մD2ye#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 d860e687..f1088584 100644 --- a/waf_libbsd.py +++ b/waf_libbsd.py @@ -187,10 +187,10 @@ class Builder(builder.ModuleManager): conf.env['HAVE_%s' % l.upper()] = True else: bld.fatal('invalid config test: %s' % (configTest)) - section_flags = ["-fdata-sections", "-ffunction-sections"] - _add_flags_if_not_present(conf.env.CFLAGS, section_flags) - _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags) - _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"]) + 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): # @@ -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;" \ |