diff options
94 files changed, 8764 insertions, 4259 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e1cf152a..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,361 +0,0 @@ -Guidelines for Developing and Contributing Code -=============================================== - -Introduction ------------- - -This guide aims to help developing and contributing code to the libbsd. One -goal of the libbsd is to stay in synchronization with FreeBSD. This is only -feasible if certain rules are in place. Otherwise, managing more than a -thousand imported source files will become too labour intensive eventually. - -What is in the Git Repository ------------------------------ - -The libbsd a self-contained kit with FreeBSD and RTEMS components -pre-merged. The Waf wscript in libbsd automatically generates the build when -you run waf by reading the modules and module's source, header, defines and -special flags from `libbsd.py`. This is the same module data used to manage -the FreeBSD source. - -Any changes to source in the `freebsd` directories will need to be merged -upstream into our master FreeBSD checkout, the `freebsd-org` submodule. - -The repository contains two FreeBSD source trees. In the `freebsd` directory -are the so called *managed* FreeBSD sources used to build the BSD library. -The FreeBSD source in `freebsd-org` is the *master* version. The -`freebsd-to-rtems.py` script is used to transfer files between the two trees -using the module defnitions in `libbsd.py`. In general terms, if you have -modified managed FreeBSD sources, you will need to run the script in *revert* -or *reverse* mode using the `-R` switch. This will copy the source back to -your local copy of the master FreeBSD source so you can run `git diff` against -the upstream FreeBSD source. If you want to transfer source files from the -master FreeBSD source to the manged FreeBSD sources, then you must run the -script in *forward* mode (the default). - -Kernel and User Space ---------------------- - -FreeBSD uses virtual memory to run separate address spaces. The kernel is one -address space and each process the kernel runs is another separate address -space. The FreeBSD build system understands the separation and separately -linked executable for the kernel and user land maintains the separation. - -RTEMS is a single address space operating system and that means the kernel and -user space code have to be linked to together and be able to run side by -side. This creates additional complexity when working with the FreeBSD code, -for example the FreeBSD kernel has a `malloc` call with a different signature -to the user land `malloc` call. The RTEMS LibBSD support code provides -structured ways to manage the separation. - -LibBSD manages the integration of kernel and user code by knowing the context -of the source code. This lets the merge process handle specific changes each -type of file needs. The build system also uses this information to control the -include paths a source file sees. The kernel code sees the kernel, CPU -specific and build system generated include paths in that order. User code -sees the user include paths then the kernel, CPU specific and build system -generated include paths in that order. The FreeBSD OS include path -`/usr/include` has a mix of kernel and user space header files. The kernel -headers let user space code cleanly access structures the kernel exports. If a -user header file has the same name as a kernel header file the user file will -be used in the user code rather than the kernel file. If the user code -includes a kernel header that file will be found and included. - -Organization ------------- - -The top level directory contains a few directories and files. The following -are important to understand - -* `freebsd-to-rtems.py` - script to convert to and free FreeBSD and RTEMS trees, -* `create-kernel-namespace.sh` - script to create the kernel namespace header `<machine/rtems-bsd-kernel-namespace.h>`, -* `wscript` - automatically generates the build from libbsd.py, -* `libbsd.py` - modules, sources, compile flags, and dependencies -* `freebsd/` - from FreeBSD by script, -* `rtemsbsd/` - RTEMS specific implementations of FreeBSD kernel support routines, -* `testsuite/` - RTEMS specific tests, and -* `libbsd.txt` - documentation in Asciidoc. - -Moving Code Between Managed and Master FreeBSD Source ------------------------------------------------------ - -The script `freebsd-to-rtems.py` is used to copy code from FreeBSD to the -rtems-libbsd tree and to reverse this process. This script attempts to -automate this process as much as possible and performs some transformations -on the FreeBSD code. Its command line arguments are shown below: - -``` -freebsd-to-rtems.py [args] - -?|-h|--help print this and exit - -d|--dry-run run program but no modifications - -D|--diff provide diff of files between trees - -e|--early-exit evaluate arguments, print results, and exit - -m|--makefile Warning: depreciated and will be removed - -b|--buildscripts just generate the build scripts - -S|--stats Print a statistics report - -R|--reverse default FreeBSD -> RTEMS, reverse that - -r|--rtems RTEMS Libbsd directory (default: '.') - -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org') - -c|--config Output the configuration then exit - -v|--verbose enable verbose output mode -``` - -In its default mode of operation, `freebsd-to-rtems.py` is used to copy code -from FreeBSD to the rtems-libbsd tree and perform transformations. - -In *reverse mode*, this script undoes those transformations and copies -the source code back to the *master* FreeBSD tree. This allows us to do -'git diff', evaluate changes made by the RTEMS Project, and report changes -back to FreeBSD upstream. - -In either mode, the script may be asked to perform a dry-run or be verbose. -Also, in either mode, the script is also smart enough to avoid copying over -files which have not changed. This means that the timestamps of files are -not changed unless the contents change. The script will also report the -number of files which changed. In verbose mode, the script will print -the name of the files which are changed. - -To add or update files in the RTEMS FreeBSD tree first run the *reverse mode* -and move the current set of patches FreeBSD. The script may warn you if a file -is not present at the destination for the direction. This can happen as files -not avaliable at the FreeBSD snapshot point have been specially added to the -RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of -files in libbsd.py. The reverse mode will result in the FreeBSD having -uncommitted changes. You can ignore these. Once the reverse process has -finished edit libbsd.py and add any new files then run the forwad mode to bring -those files into the RTEMS FreeBSD tree. - -The following is an example forward run with no changes. - -``` -$ ./freebsd-to-rtems.py -v -Verbose: yes (1) -Dry Run: no -Diff Mode Enabled: no -Only Generate Build Scripts: no -RTEMS Libbsd Directory: . -FreeBSD SVN Directory: freebsd-org -Direction: forward -Forward from FreeBSD GIT into . -0 file(s) were changed: -``` - -The script may also be used to generate a diff in either forward or reverse -direction. - -You can add more than one verbose option (-v) to the command line and get more -detail and debug level information from the command. - -FreeBSD Baseline ----------------- - -Use -``` -$ git log freebsd-org -``` -to figure out the current FreeBSD baseline. - -How to Import Code from FreeBSD -------------------------------- - -* In case you import files from a special FreeBSD version, then update the list above. -* Run `git status` and make sure your working directory is clean. -* Run `./freebsd-to-rtems.py -R` -* Run `./freebsd-to-rtems.py` -* Run `git status` and make sure your working directory is clean. If you see modified files, then the `freebsd-to-rtems.py` script needs to be fixed first. -* Add the files to import to `libbsd.py` and your intended build set (for example `buildset/default.ini`. -* Run `./freebsd-to-rtems.py` -* Immediately check in the imported files without the changes to `libbsd.py` and the buildsets. Do not touch the imported files yourself at this point. -* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'. -* Add a test to the testsuite if possible. -* Run `./create-kernel-namespace.sh` if you imported kernel space headers. Add only your new defines via `git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h`. -* Create one commit from this. - -The -S or --stats option generates reports the changes we have made to -FreeBSD. If the code has been reserved into the original FreeBSD tree it will -show nothing has changed. To see what we have change: - -``` -$ cd freebsd-org -$ git checkout -- . -$ cd .. -$ ./freebsd-to-rtems.py -R -S -d - ``` - -The report lists the files change based on the opacity level. The opacity is a -measure on how much of a file differs from the original FreeBSD source. The -lower the value the more transparent the source file it. - -Porting of User-Space Utilities ------------------------------- - -The theory behind the described method is to put all BSS and initialized data -objects into a named section. This section then will be saved before the code is -executed and restored after it has finished. This method limits to a single -threaded execution of the application but minimizes the necessary changes to the -original FreeBSD code. - -* Import and commit the unchanged source files like described above. -* Add the files to the [libbsd.py](libbsd.py) and build them. -* Check the sources for everything that can be made const. This type of patches - should go back to the upstream FreeBSD sources. -* Move static variables out of functions if necessary (search for - "\tstatic"). These patches most likely will not be accepted into FreeBSD. -* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file - containing the main function (e.g. PROGNAME = pfctl). For an example look at - `rtems_bsd_command_pfctl()` in [pfctl.c](freebsd/sbin/pfctl/pfctl.c). -* You probably have to use getopt_r() instead of getopt(). Have a look at - [pfctl.c](freebsd/sbin/pfctl/pfctl.c). -* Build the libbsd without optimization. -* Use the `userspace-header-gen.py` to generate some necessary header - files. It will generate one `rtems-bsd-PROGNAME-MODULE-data.h` per object file, one - `rtems-bsd-PROGNAME-namespace.h` and one `rtems-bsd-PROGNAME-data.h`. To call - the script, you have to compile the objects and afterwards run the helper - script with a call similar to this one: - `python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl` - Replace the name (given via -p option) by the name of the userspace tool. It - has to match the name that is used in the RTEMS linker set further below. - `Note:` the script `userspace-header-gen.py` depends on pyelftools. It can be - installed using pip: - `pip install --user pyelftools` -* If you regenerated files that have already been generated, you may have to - remove RTEMS-specific names from the namespace. The defaults (linker set names - and rtems_bsd_program_.*) should already be filtered. -* Put the generated header files into the same folder like the source files. -* At the top of each source file place the following right after the user-space header: - ```c - #ifdef __rtems__ - #include <machine/rtems-bsd-program.h> - #include "rtems-bsd-PROGNAME-namespace.h" - #endif /* __rtems__ */ - ``` - The following command may be useful: - ``` - sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c - ``` -* At the bottom of each source file place the follwing: - ```c - #ifdef __rtems__ - #include "rtems-bsd-PROGNAME-FILE-data.h" - #endif /* __rtems__ */ - ``` - The following command may be useful: - ``` - for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done - ``` -* Create one compilable commit. - -Rules for Modifying FreeBSD Source ----------------------------------- - -Changes in FreeBSD files must be done using `__rtems__` C pre-processor guards. -This makes synchronization with the FreeBSD upstream easier and is very -important. Patches which do not follow these rules will be rejected. Only add -lines. If your patch contains lines starting with a `-`, then this is wrong. -Subtract code by added `#ifndef __rtems__`. For example: - -```c -/* Global variables for the kernel. */ - -#ifndef __rtems__ -/* 1.1 */ -extern char kernelname[MAXPATHLEN]; -#endif /* __rtems__ */ - -extern int tick; /* usec per tick (1000000 / hz) */ -``` - -```c -#if defined(_KERNEL) || defined(_WANT_FILE) -#ifdef __rtems__ -#include <rtems/libio_.h> -#include <sys/fcntl.h> -#endif /* __rtems__ */ -/* - * Kernel descriptor table. - * One entry for each open kernel vnode and socket. - * - * Below is the list of locks that protects members in struct file. - * - * (f) protected with mtx_lock(mtx_pool_find(fp)) - * (d) cdevpriv_mtx - * none not locked - */ -``` - -```c -extern int profprocs; /* number of process's profiling */ -#ifndef __rtems__ -extern volatile int ticks; -#else /* __rtems__ */ -#include <rtems/score/watchdogimpl.h> -#define ticks _Watchdog_Ticks_since_boot -#endif /* __rtems__ */ - -#endif /* _KERNEL */ -``` - -Add nothing (even blank lines) before or after the `__rtems__` guards. Always -include a `__rtems__` in the guards to make searches easy, so use - -* `#ifndef __rtems__`, -* `#ifdef __rtems__`, -* `#else /* __rtems__ */`, and -* `#endif /* __rtems__ */`. - -The guards must start at the begin of the line. Examples for wrong guards: - -```c -static void -guards_must_start_at_the_begin_of_the_line(int j) -{ - - /* WRONG */ - #ifdef __rtems__ - return (j + 1); - #else /* __rtems__ */ - return (j + 2); - #endif /* __rtems__ */ -} - -static void -missing_rtems_comments_in_the_guards(int j) -{ - -#ifdef __rtems__ - return (j + 3); -/* WRONG */ -#else - return (j + 4); -#endif -} -``` - -The FreeBSD build and configuration system uses option header files, e.g. -`#include "opt_xyz.h"` in an unmodified FreeBSD file. This include is -transformed by the import script into `#include <rtems/bsd/local/opt_xyz.h>`. Do -not disable option header includes via guards. Instead, add an empty option -header, e.g. `touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h`. -```c -/* WRONG */ -#ifndef __rtems__ -#include <rtems/bsd/local/opt_xyz.h> -#endif /* __rtems__ */ -``` - -In general, provide empty header files and do not guard includes. - -For new code use -[STYLE(9)](http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9). - -Do not format original FreeBSD code. Do not perform white space changes even -if you get git commit warnings. - -Automatically Generated FreeBSD Files -------------------------------------- - -Some source and header files are automatically generated during the FreeBSD -build process. The `Makefile.todo` file performs this manually. The should be -included in `freebsd-to-rtems.py` script some time in the future. For details, -see also -[KOBJ(9)](http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0). diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..0b6fc7a0 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,736 @@ +Guidelines for Developing and Contributing Code +*********************************************** + +Introduction +============ + +This guide aims to help developing and contributing code to the LibBSD. One +goal of the LibBSD is to stay in synchronization with FreeBSD. This is only +feasible if certain rules are in place. Otherwise, managing more than a +thousand imported source files will become too labour intensive eventually. + +The LibBSD makes FreeBSD subsystems like TCP/IP, USB, SD/MMC, PCIe, and some +more usable for RTEMS. It tries to follow the FreeBSD development as close as +possible and therefore is updated to the latest FreeBSD HEAD revision of the +associated FreeBSD branch from time to time. To find out which version of +FreeBSD is currently used as the base version for LibBSD please take a look at +the ``freebsd-org`` submodule. + +This guide captures information on the process of merging code from FreeBSD, +RTEMS specific support files, general guidelines on what modifications to the +FreeBSD source are permitted, and some other topics. For building the library, +see the `README <README.rst>`_. + +Goals of the LibBSD activity are + +* provide functionality from FreeBSD to RTEMS, +* ease updating to future FreeBSD versions, +* ease tracking changes in FreeBSD code, +* minimize manual changes in FreeBSD code. + +We will work to push our changes upstream to the FreeBSD Project and minimize +changes required at each update point. + +What is in the Git Repository +============================= + +The LibBSD a self-contained kit with FreeBSD and RTEMS components +pre-merged. The Waf wscript in LibBSD automatically generates the build when +you run waf by reading the modules and module's source, header, defines and +special flags from ``libbsd.py``. This is the same module data used to manage +the FreeBSD source. + +Any changes to source in the ``freebsd`` directories will need to be merged +upstream into our master FreeBSD checkout, the ``freebsd-org`` submodule. + +The repository contains two FreeBSD source trees. In the ``freebsd`` directory +are the so called *managed* FreeBSD sources used to build the BSD library. +The FreeBSD source in ``freebsd-org`` is the *master* version. The +``freebsd-to-rtems.py`` script is used to transfer files between the two trees +using the module defnitions in ``libbsd.py``. In general terms, if you have +modified managed FreeBSD sources, you will need to run the script in *revert* +or *reverse* mode using the ``-R`` switch. This will copy the source back to +your local copy of the master FreeBSD source so you can run ``git diff`` against +the upstream FreeBSD source. If you want to transfer source files from the +master FreeBSD source to the manged FreeBSD sources, then you must run the +script in *forward* mode (the default). + +Kernel and User Space +===================== + +FreeBSD uses virtual memory to run separate address spaces. The kernel is one +address space and each process the kernel runs is another separate address +space. The FreeBSD build system understands the separation and separately +linked executable for the kernel and user land maintains the separation. + +RTEMS is a single address space operating system and that means the kernel and +user space code have to be linked to together and be able to run side by +side. This creates additional complexity when working with the FreeBSD code, +for example the FreeBSD kernel has a ``malloc`` call with a different signature +to the user land ``malloc`` call. The RTEMS LibBSD support code provides +structured ways to manage the separation. + +LibBSD manages the integration of kernel and user code by knowing the context +of the source code. This lets the merge process handle specific changes each +type of file needs. The build system also uses this information to control the +include paths a source file sees. The kernel code sees the kernel, CPU +specific and build system generated include paths in that order. User code +sees the user include paths then the kernel, CPU specific and build system +generated include paths in that order. The FreeBSD OS include path +``/usr/include`` has a mix of kernel and user space header files. The kernel +headers let user space code cleanly access structures the kernel exports. If a +user header file has the same name as a kernel header file the user file will +be used in the user code rather than the kernel file. If the user code +includes a kernel header that file will be found and included. + +Organization +============ + +The top level directory contains a few directories and files. The following +are important to understand + +* ``freebsd-to-rtems.py`` - script to convert to and free FreeBSD and RTEMS trees, +* ``create-kernel-namespace.sh`` - script to create the kernel namespace header ``<machine/rtems-bsd-kernel-namespace.h>``, +* ``wscript`` - automatically generates the build from libbsd.py, +* ``libbsd.py`` - modules, sources, compile flags, and dependencies +* ``freebsd/`` - from FreeBSD by script, +* ``rtemsbsd/`` - RTEMS specific implementations of FreeBSD kernel support routines, +* ``testsuite/`` - RTEMS specific tests, and +* ``libbsd.txt`` - documentation in Asciidoc. + +Moving Code Between Managed and Master FreeBSD Source +===================================================== + +The script ``freebsd-to-rtems.py`` is used to copy code from FreeBSD to the +rtems-libbsd tree and to reverse this process. This script attempts to +automate this process as much as possible and performs some transformations +on the FreeBSD code. Its command line arguments are shown below: + +.. code-block:: none + + freebsd-to-rtems.py [args] + -?|-h|--help print this and exit + -d|--dry-run run program but no modifications + -D|--diff provide diff of files between trees + -e|--early-exit evaluate arguments, print results, and exit + -m|--makefile Warning: depreciated and will be removed + -b|--buildscripts just generate the build scripts + -S|--stats Print a statistics report + -R|--reverse default FreeBSD -> RTEMS, reverse that + -r|--rtems RTEMS Libbsd directory (default: '.') + -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org') + -c|--config Output the configuration then exit + -v|--verbose enable verbose output mode + +In its default mode of operation, ``freebsd-to-rtems.py`` is used to copy code +from FreeBSD to the rtems-libbsd tree and perform transformations. + +In *reverse mode*, this script undoes those transformations and copies +the source code back to the *master* FreeBSD tree. This allows us to do +'git diff', evaluate changes made by the RTEMS Project, and report changes +back to FreeBSD upstream. + +In either mode, the script may be asked to perform a dry-run or be verbose. +Also, in either mode, the script is also smart enough to avoid copying over +files which have not changed. This means that the timestamps of files are +not changed unless the contents change. The script will also report the +number of files which changed. In verbose mode, the script will print +the name of the files which are changed. + +To add or update files in the RTEMS FreeBSD tree first run the *reverse mode* +and move the current set of patches FreeBSD. The script may warn you if a file +is not present at the destination for the direction. This can happen as files +not avaliable at the FreeBSD snapshot point have been specially added to the +RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of +files in libbsd.py. The reverse mode will result in the FreeBSD having +uncommitted changes. You can ignore these. Once the reverse process has +finished edit libbsd.py and add any new files then run the forwad mode to bring +those files into the RTEMS FreeBSD tree. + +The following is an example forward run with no changes. + +.. code-block:: none + + $ ./freebsd-to-rtems.py -v + Verbose: yes (1) + Dry Run: no + Diff Mode Enabled: no + Only Generate Build Scripts: no + RTEMS Libbsd Directory: . + FreeBSD SVN Directory: freebsd-org + Direction: forward + Forward from FreeBSD GIT into . + 0 file(s) were changed: + +The script may also be used to generate a diff in either forward or reverse +direction. + +You can add more than one verbose option (-v) to the command line and get more +detail and debug level information from the command. + +FreeBSD Baseline +================ + +Use + +.. code-block:: none + + $ git log freebsd-org + +to figure out the current FreeBSD baseline. + +How to Import Code from FreeBSD +=============================== + +* In case you import files from a special FreeBSD version, then update the list above. +* Run ``git status`` and make sure your working directory is clean. +* Run ``./freebsd-to-rtems.py -R`` +* Run ``./freebsd-to-rtems.py`` +* Run ``git status`` and make sure your working directory is clean. If you see modified files, then the ``freebsd-to-rtems.py`` script needs to be fixed first. +* Add the files to import to ``libbsd.py`` and your intended build set (for example ``buildset/default.ini``. +* Run ``./freebsd-to-rtems.py`` +* Immediately check in the imported files without the changes to ``libbsd.py`` and the buildsets. Do not touch the imported files yourself at this point. +* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'. +* Add a test to the testsuite if possible. +* Run ``./create-kernel-namespace.sh`` if you imported kernel space headers. Add only your new defines via ``git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h``. +* Create one commit from this. + +The -S or --stats option generates reports the changes we have made to +FreeBSD. If the code has been reserved into the original FreeBSD tree it will +show nothing has changed. To see what we have change: + +.. code-block:: none + + $ cd freebsd-org + $ git checkout -- . + $ cd .. + $ ./freebsd-to-rtems.py -R -S -d + +The report lists the files change based on the opacity level. The opacity is a +measure on how much of a file differs from the original FreeBSD source. The +lower the value the more transparent the source file it. + +Porting of User-Space Utilities +=============================== + +The theory behind the described method is to put all BSS and initialized data +objects into a named section. This section then will be saved before the code is +executed and restored after it has finished. This method limits to a single +threaded execution of the application but minimizes the necessary changes to the +original FreeBSD code. + +* Import and commit the unchanged source files like described above. +* Add the files to the `<libbsd.py>`_ and build them. +* Check the sources for everything that can be made const. This type of patches + should go back to the upstream FreeBSD sources. +* Move static variables out of functions if necessary (search for + "\tstatic"). These patches most likely will not be accepted into FreeBSD. +* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file + containing the main function (e.g. PROGNAME = pfctl). For an example look at + ``rtems_bsd_command_pfctl()`` in `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_. +* You probably have to use getopt_r() instead of getopt(). Have a look at + `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_. +* Build the LibBSD without optimization. +* Use the ``userspace-header-gen.py`` to generate some necessary header + files. It will generate one ``rtems-bsd-PROGNAME-MODULE-data.h`` per object file, one + ``rtems-bsd-PROGNAME-namespace.h`` and one ``rtems-bsd-PROGNAME-data.h``. To call + the script, you have to compile the objects and afterwards run the helper + script with a call similar to this one: + ``python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl`` + Replace the name (given via -p option) by the name of the userspace tool. It + has to match the name that is used in the RTEMS linker set further below. + ``Note:`` the script ``userspace-header-gen.py`` depends on pyelftools. It can be + installed using pip: + ``pip install --user pyelftools`` +* If you regenerated files that have already been generated, you may have to + remove RTEMS-specific names from the namespace. The defaults (linker set names + and rtems_bsd_program_.*) should already be filtered. +* Put the generated header files into the same folder like the source files. +* At the top of each source file place the following right after the user-space header: + + .. code-block:: c + + #ifdef __rtems__ + #include <machine/rtems-bsd-program.h> + #include "rtems-bsd-PROGNAME-namespace.h" + #endif /* __rtems__ */ + + The following command may be useful: + + .. code-block:: none + + sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c + +* At the bottom of each source file place the follwing: + + .. code-block:: c + + #ifdef __rtems__ + #include "rtems-bsd-PROGNAME-FILE-data.h" + #endif /* __rtems__ */ + + The following command may be useful: + + .. code-block:: none + + for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done +* Create one compilable commit. + +Rules for Modifying FreeBSD Source +================================== + +Do not reformat original FreeBSD code. Do not perform white space changes even +if you get git commit warnings. **Check your editor settings so that it does +not perform white space changes automatically**, for example adding a newline +to the end of the file. White space changes may result in conflicts during +updates, especially changes at the end of a file. + +Changes in FreeBSD files must be done using ``__rtems__`` C pre-processor guards. +This makes synchronization with the FreeBSD upstream easier and is very +important. Patches which do not follow these rules will be rejected. Only add +lines. If your patch contains lines starting with a ``-``, then this is wrong. +Subtract code by added ``#ifndef __rtems__``. For example: + +.. code-block:: c + + /* Global variables for the kernel. */ + + #ifndef __rtems__ + /* 1.1 */ + extern char kernelname[MAXPATHLEN]; + #endif /* __rtems__ */ + + extern int tick; /* usec per tick (1000000 / hz) */ + +.. code-block:: c + + #if defined(_KERNEL) || defined(_WANT_FILE) + #ifdef __rtems__ + #include <rtems/libio_.h> + #include <sys/fcntl.h> + #endif /* __rtems__ */ + /* + * Kernel descriptor table. + * One entry for each open kernel vnode and socket. + * + * Below is the list of locks that protects members in struct file. + * + * (f) protected with mtx_lock(mtx_pool_find(fp)) + * (d) cdevpriv_mtx + * none not locked + */ + +.. code-block:: c + + extern int profprocs; /* number of process's profiling */ + #ifndef __rtems__ + extern volatile int ticks; + #else /* __rtems__ */ + #include <rtems/score/watchdogimpl.h> + #define ticks _Watchdog_Ticks_since_boot + #endif /* __rtems__ */ + + #endif /* _KERNEL */ + +Add nothing (even blank lines) before or after the ``__rtems__`` guards. Always +include a ``__rtems__`` in the guards to make searches easy, so use + +* ``#ifndef __rtems__``, +* ``#ifdef __rtems__``, +* ``#else /* __rtems__ */``, and +* ``#endif /* __rtems__ */``. + +The guards must start at the begin of the line. Examples for wrong guards: + +.. code-block:: c + + static void + guards_must_start_at_the_begin_of_the_line(int j) + { + + /* WRONG */ + #ifdef __rtems__ + return (j + 1); + #else /* __rtems__ */ + return (j + 2); + #endif /* __rtems__ */ + } + + static void + missing_rtems_comments_in_the_guards(int j) + { + + #ifdef __rtems__ + return (j + 3); + /* WRONG */ + #else + return (j + 4); + #endif + } + +The FreeBSD build and configuration system uses option header files, e.g. +``#include "opt_xyz.h"`` in an unmodified FreeBSD file. This include is +transformed by the import script into ``#include <rtems/bsd/local/opt_xyz.h>``. Do +not disable option header includes via guards. Instead, add an empty option +header, e.g. ``touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h``. + +.. code-block:: c + + /* WRONG */ + #ifndef __rtems__ + #include <rtems/bsd/local/opt_xyz.h> + #endif /* __rtems__ */ + +In general, provide empty header files and do not guard includes. + +For new code use +`STYLE(9) <http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9>`_. + +Update FreeBSD Baseline +======================= + +Perform the following steps to do a FreeBSD baseline update: + +* Update ``__FreeBSD_version`` in ``rtemsbsd/include/machine/rtems-bsd-version.h`` + +* Update the namespace header file. + +* Review all code blocks with the ``REVIEW-AFTER-FREEBSD-BASELINE-UPDATE`` tag. + +Automatically Generated FreeBSD Files +===================================== + +Some source and header files are automatically generated during the FreeBSD +build process. The ``Makefile.todo`` file performs this manually. The should be +included in ``freebsd-to-rtems.py`` script some time in the future. For details, +see also +`KOBJ(9) <http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0>`_. + +Reference Board Support Package +=============================== + +The reference BSP for LibBSD development is ``arm/xilinx_zynq_a9_qemu``. All +patches shall be tested for this BSP. The BSP runs on the Qemu simulator which +has some benefits for development and test of the LibBSD + +* ``NULL`` pointer read and write protection, +* Qemu is a fast simulator, +* Qemu provides support for GDB watchpoints, +* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge + devices (you can run multiple test instances on one virtual network). + +Board Support Package Requirements +================================== + +In FreeBSD, interrupt handler may use mutexes. In RTEMS, using mutexes from +within interrupt context is not allowed, so the Board Support Package (BSP) +should support the +`Interrupt Manager <https://docs.rtems.org/branches/master/c-user/interrupt/directives.html#rtems-interrupt-server-handler-install>`_ +in general. + +Network Interface Drivers Hints +=============================== + +Link Up/Down Events +------------------- + +You can notifiy the application space of link up/down events in your network +interface driver via the +``if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN)`` function. The +DHCPCD(8) client is a consumer of these events for example. Make sure that the +interface flag ``IFF_UP`` and the interface driver flag ``IFF_DRV_RUNNING`` is +set in case the link is up, otherwise ``ether_output()`` will return the error +status ``ENETDOWN``. + +FreeBSD Kernel Features Ported to LibBSD +======================================== + +All lock based synchronization primitives are implemented through mutexes using +the priority inheritance protocol. + +* `BUS_DMA(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_dma&sektion=9>`_: Bus and Machine Independent DMA Mapping Interface +* `BUS_SPACE(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_space&sektion=9>`_: Bus space manipulation functions +* `CALLOUT(9) <http://www.freebsd.org/cgi/man.cgi?query=callout&sektion=9>`_: Execute a function after a specified length of time +* `CONDVAR(9) <http://www.freebsd.org/cgi/man.cgi?query=condvar&sektion=9>`_: Kernel condition variable +* `DEVICE(9) <http://www.freebsd.org/cgi/man.cgi?query=device&sektion=9>`_: An abstract representation of a device +* `DRIVER(9) <http://www.freebsd.org/cgi/man.cgi?query=driver&sektion=9>`_: Structure describing a device driver +* `EPOCH(9) <http://www.freebsd.org/cgi/man.cgi?query=epoch&sektion=9>`_: Kernel epoch based reclamation +* `MUTEX(9) <http://www.freebsd.org/cgi/man.cgi?query=mutex&sektion=9>`_: Kernel synchronization primitives +* `RMAN(9) <http://www.freebsd.org/cgi/man.cgi?query=rman&sektion=9>`_: Resource management functions +* `RMLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rmlock&sektion=9>`_: Kernel reader/writer lock optimized for read-mostly access patterns +* `RWLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rwlock&sektion=9>`_: Kernel reader/writer lock +* `SX(9) <http://www.freebsd.org/cgi/man.cgi?query=sx&sektion=9>`_: Kernel shared/exclusive lock +* `SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_: Dynamic and static sysctl MIB creation functions +* `SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_: A framework for dynamic kernel initialization +* `TASKQUEUE(9) <http://www.freebsd.org/cgi/man.cgi?query=taskqueue&sektion=9>`_: Asynchronous task execution +* `UMA(9) <http://www.freebsd.org/cgi/man.cgi?query=uma&sektion=9>`_: General-purpose kernel object allocator + +LibBSD Initialization Details +============================= + +The initialization of LibBSD is based on the FreeBSD +`SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_ +infrastructure. The key to initializing a system is to ensure that the desired +device drivers are explicitly pulled into the linked application. This plus +linking against the LibBSD (``libbsd.a``) will pull in the necessary FreeBSD +infrastructure. + +The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of +object files linked together. If we have a library, then creating the +executable is simple. We begin with a start symbol and recursively resolve all +references. With a bunch of object files linked together we need a different +mechanism. Most object files don't know each other. Lets say we have a driver +module. The rest of the system has no references to this driver module. The +driver module needs a way to tell the rest of the system: Hey, kernel I am +here, please use my services! + +This registration of independent components is performed by SYSINIT(9) and +specializations + +The SYSINIT(9) uses some global data structures that are placed in a certain +section. In the linker command file we need this: + +.. code-block:: none + + .rtemsroset : { + KEEP (*(SORT(.rtemsroset.*))) + } + + .rtemsrwset : { + KEEP (*(SORT(.rtemsrwset.*))) + } + +This results for example in this executable layout: + +.. code-block:: none + + [...] + *(SORT(.rtemsroset.*)) + .rtemsroset.bsd.modmetadata_set.begin + 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000025fe00 _bsd__start_set_modmetadata_set + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o) + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe08 0x4 libbsd.a(kern_module.o) + [...] + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe68 0x4 libbsd.a(mii.o) + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o) + .rtemsroset.bsd.modmetadata_set.end + 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000025fe70 _bsd__stop_set_modmetadata_set + [...] + .rtemsrwset 0x000000000030bad0 0x290 + *(SORT(.rtemsrwset.*)) + .rtemsrwset.bsd.sysinit_set.begin + 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000030bad0 _bsd__start_set_sysinit_set + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030badc 0x4 libbsd.a(init_main.o) + [...] + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bd54 0x4 libbsd.a(frag6.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o) + .rtemsrwset.bsd.sysinit_set.end + 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000030bd60 _bsd__stop_set_sysinit_set + [...] + +Here you can see, that some global data structures are collected into +continuous memory areas. This memory area can be identified by start and stop +symbols. This constructs a table of uniform items. + +The low level FreeBSD code calls at some time during the initialization the +mi_startup() function (machine independent startup). This function will sort +the SYSINIT(9) set and call handler functions which perform further +initialization. The last step is the scheduler invocation. + +The SYSINIT(9) routines are run in ``mi_startup()`` which is called by +``rtems_bsd_initialize()``. This is also explained in "The Design and +Implementation of the FreeBSD Operating System" section 14.3 "Kernel +Initialization". + +In RTEMS, we have a library and not a bunch of object files. Thus we need a +way to pull-in the desired services out of the libbsd. Here the +``rtems-bsd-sysinit.h`` comes into play. The SYSINIT(9) macros have been +modified and extended for RTEMS in ``<sys/kernel.h>``: + +.. code-block:: none + + #ifndef __rtems__ + #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ + static struct sysinit uniquifier ## _sys_init = { \ + subsystem, \ + order, \ + func, \ + (ident) \ + }; \ + DATA_SET(sysinit_set,uniquifier ## _sys_init) + #else /* __rtems__ */ + #define SYSINIT_ENTRY_NAME(uniquifier) \ + _bsd_ ## uniquifier ## _sys_init + #define SYSINIT_REFERENCE_NAME(uniquifier) \ + _bsd_ ## uniquifier ## _sys_init_ref + #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ + struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \ + subsystem, \ + order, \ + func, \ + (ident) \ + }; \ + RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier)) + #define SYSINIT_REFERENCE(uniquifier) \ + extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \ + static struct sysinit const * const \ + SYSINIT_REFERENCE_NAME(uniquifier) __used \ + = &SYSINIT_ENTRY_NAME(uniquifier) + #define SYSINIT_MODULE_REFERENCE(mod) \ + SYSINIT_REFERENCE(mod ## module) + #define SYSINIT_DRIVER_REFERENCE(driver, bus) \ + SYSINIT_MODULE_REFERENCE(driver ## _ ## bus) + #define SYSINIT_DOMAIN_REFERENCE(dom) \ + SYSINIT_REFERENCE(domain_add_ ## dom) + #endif /* __rtems__ */ + +Here you see that the SYSINIT(9) entries are no longer static. The +``*_REFERENCE()`` macros will create references to the corresponding modules +which are later resolved by the linker. The application has to provide an +object file with references to all required FreeBSD modules. + +System Control Hints +==================== + +If you get undefined references to ``_bsd_sysctl_*`` symbols, then you have to +locate and add the associated system control node, see +`SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_. + +Issues and TODO +=============== + +* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map(). + +* Priority queues are broken with clustered scheduling. + +* Per-CPU data should be enabled once the new stack is ready for SMP. + +* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP. + +* Multiple routing tables are not supported. Every FIB value is set to zero + (= BSD_DEFAULT_FIB). + +* Process identifiers are not supported. Every PID value is set to zero + (= BSD_DEFAULT_PID). + +* User credentials are not supported. The following functions allow the + operation for everyone + + * prison_equal_ip4(), + * chgsbsize(), + * cr_cansee(), + * cr_canseesocket() and + * cr_canseeinpcb(). + +* A basic USB functionality test that is known to work on Qemu is desirable. + +* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model + so that those architectures can use new TCP/IP and USB code. + +* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old + RTEMS TCP/IP stack. For some reason, the __printflike markers do not + compile in this environment. We may want to use the FreeBSD syslog.h + and get this addressed. + +* in_cksum implementations for architectures not supported by FreeBSD. + This will require figuring out where to put implementations that do + not originate from FreeBSD and are populated via the script. + +* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c"). + +* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML + parser and mmap(). + +* get_cyclecount(): The implementation is a security problem. + +* What to do with the priority parameter present in the FreeBSD synchronization + primitives and the thread creation functions? + +* TASKQUEUE(9): Support spin mutexes. + +* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c. + +* KQUEUE(2): Choose proper lock for global kqueue list. + +* TIMEOUT(9): Maybe use special task instead of timer server to call + callout_tick(). + +* sysctl_handle_opaque(): Implement reliable snapshots. + +* PING6(8): What to do with SIGALARM? + +* <sys/param.h>: Update Newlib to use a MSIZE of 256. + +* BPF(4): Add support for zero-copy buffers. + +* UNIX(4): Fix race conditions in the area of socket object and file node + destruction. Add support for file descriptor transmission via control + messages. + +* PRINTF(9): Add support for log(), the %D format specifier is missing in the + normal printf() family. + +* Why is the interrupt server used? The BSD interrupt handlers can block on + synchronization primitives like mutexes. This is in contrast to RTEMS + interrupt service routines. The BSPs using the generic interrupt support + must implement the ``bsp_interrupt_vector_enable()`` and + ``bsp_interrupt_vector_disable()`` routines. They normally enable/disable a + particular interrupt source at the interrupt controller. This can be used to + implement the interrupt server. The interrupt server is a task that wakes-up + in case an associated interrupt happens. The interrupt source is disabled in + a generic interrupt handler that wakes-up the interrupt server task. Once + the postponed interrupt processing is performed in the interrupt server the + interrupt source is enabled again. + +* Convert all BSP linkcmds to use a linkcmds.base so the sections are + easier to insert. + +* NIC Device Drivers +* Only common PCI NIC drivers have been included in the initial set. These + do not include any system on chip or ISA drivers. +* PCI configuration probe does not appear to happen to determine if a + NIC is in I/O or memory space. We have worked around this by using a + static hint to tell the fxp driver the correct mode. But this needs to + be addressed. +* The ISA drivers require more BSD infrastructure to be addressed. This was + outside the scope of the initial porting effort. + +* devfs (Device file system): There is a minimal implementation based on IMFS. + The mount point is fixed to "/dev". Note that the devfs is only used by the + cdev subsystem. cdev has been adapted so that the full path (including the + leading "/dev") is given to devfs. This saves some copy operations. + + devfs_create() first creates the full path and then creates an IMFS generic + node for the device. + + TBD: remove empty paths on devfs_destroy(). + +* altq_subr.c - Arbitrary choices were made in this file that RTEMS would not + support tsc frequency change. Additionally, the clock frequency for + machclk_freq is always measured for RTEMS. + +* conf.h - In order to add make_dev and destroy_dev, variables in the cdev + structure that were not being used were conditionally compiled out. The + capability of supporting children did not appear to be needed and was not + implemented in the rtems version of these routines. + +* Problem to report to FreeBSD: The MMAP_NOT_AVAILABLE define is inverted on + its usage. When it is defined the mmap method is called. Additionally, it is + not used thoroughly. It is not used in the unmap portion of the source. The + file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap the call to mmap + and file rec_close.c uses the munmap method. diff --git a/README.md b/README.md deleted file mode 100644 index b1a7e1aa..00000000 --- a/README.md +++ /dev/null @@ -1,304 +0,0 @@ -RTEMS LibBSD -============ - -Welcome to building LibBSD for RTEMS using Waf. This package is a library -containing various parts of the FreeBSD kernel ported to RTEMS. The library -replaces the networking port of FreeBSD in the RTEMS kernel sources. This -package is designed to be updated from the FreeBSD kernel sources and contains -more than just the networking code. - -To build this package you need a current RTEMS tool set for your architecture, -and a recent RTEMS kernel for your BSP configured with networking disabled -built and installed. If you already have this you can skip to step 3 of the -build procedure. - -Building and Installing LibBSD ------------------------------- - -The following instructions show you how to build and install RTEMS Tools and -RTEMS kernel for your BSP in separate paths. Using separate paths for the tools -and BSPs lets you manage what you have installed. If you are happy with a -single path you can use the same path in each stage. - -The Waf build support for RTEMS requires you provide your BSP name as an -architecture and BSP pair. You must provide both or Waf will generate an error -message during the configure phase. - -We will build an Xilinx Zynq QEMU BSP using the name -*arm/xilinx_zynq_a9_qemu*. You can copy and paste the shell commands below to -do this. The individual steps are explained afterwards. - -``` -sandbox="$PWD/sandbox" -mkdir sandbox -cd "$sandbox" -git clone git://git.rtems.org/rtems-source-builder.git -git clone git://git.rtems.org/rtems.git -git clone git://git.rtems.org/rtems-libbsd.git -cd "$sandbox" -cd rtems-source-builder/rtems -../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm -cd "$sandbox" -cd rtems -PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap -cd "$sandbox" -mkdir b-xilinx_zynq_a9_qemu -cd b-xilinx_zynq_a9_qemu -PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \ - --target=arm-rtems5 --prefix="$sandbox/rtems/5" \ - --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu -PATH="$sandbox/rtems/5/bin:$PATH" make -PATH="$sandbox/rtems/5/bin:$PATH" make install -cd "$sandbox" -cd rtems-libbsd -git submodule init -git submodule update rtems_waf -./waf configure --prefix="$sandbox/rtems/5" \ - --rtems-bsps=arm/xilinx_zynq_a9_qemu \ - --buildset=buildset/default.ini -./waf -./waf install -qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none \ - -nographic -M xilinx-zynq-a9 -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/selectpollkqueue01.exe -``` - -1. Create a sandbox directory: - -``` -$ sandbox="$PWD/sandbox" -$ mkdir sandbox -``` - -2. Clone the repositories: - -``` -$ cd "$sandbox" -$ git clone git://git.rtems.org/rtems-source-builder.git -$ git clone git://git.rtems.org/rtems.git -$ git clone git://git.rtems.org/rtems-libbsd.git -``` - -3. Build and install the tools: - -``` -$ cd "$sandbox" -$ cd rtems-source-builder/rtems -$ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm -``` - -4. Bootstrap the RTEMS sources: - -``` -$ cd "$sandbox" -$ cd rtems -$ PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap -``` - -5. Build and install the RTEMS Board Support Packages (BSP) you want to use: - -``` -$ cd "$sandbox" -$ mkdir b-xilinx_zynq_a9_qemu -$ cd b-xilinx_zynq_a9_qemu -$ PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \ - --target=arm-rtems5 --prefix="$sandbox/rtems/5" \ - --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu -$ PATH="$sandbox/rtems/5/bin:$PATH" make -$ PATH="$sandbox/rtems/5/bin:$PATH" make install -``` - -6. Populate the rtems_waf git submodule. Note, make sure you specify - 'rtems_waf' or the FreeBSD kernel source will be cloned: - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ git submodule init -$ git submodule update rtems_waf -``` - -7. Run Waf's configure with your specific settings. In this case the path to - the tools and RTEMS are provided on the command line and so do not need to - be in your path or environment [1]. You can use - '--rtems-archs=arm,sparc,i386' or - '--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586' to build for - more than BSP at a time. Note, you must provide the architecture and BSP as - a pair. Providing just the BSP name will fail. This call also explicitly - provides a buildset via the '--buildset=buildset/default.ini' option. If no - buildset is provided the default one (which is the same as the one provided - explicitly here) will be used. You can also provide multiple buildsets as a - coma separated list or via multiple '--buildset=x' options. - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ ./waf configure --prefix="$sandbox/rtems/5" \ - --rtems-bsps=arm/xilinx_zynq_a9_qemu \ - --buildset=buildset/default.ini -``` - -8. Build and install. The LibBSD package will be installed into the prefix - provided to configure: - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ ./waf -$ ./waf install -``` - -9. Run the tests on QEMU, for example using VDE: - -``` -$ qemu-system-arm -no-reboot -serial null -serial mon:stdio \ - -net nic,model=cadence_gem -net vde,id=vde0,sock=/tmp/vde1 \ - -nographic -M xilinx-zynq-a9 -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/selectpollkqueue01.exe -``` - -[1] It is good practice to keep your environment as empty as possible. Setting - paths to tools or specific values to configure or control a build is - dangerous because settings can leak between different builds and change - what you expect a build to do. The Waf tool used here lets you specify on - the command line the tools and RTEMS paths and this is embedded in Waf's - configuration information. If you have a few source trees working at any - one time with different tool sets or configurations you can easly move - between them safe in the knowledge that one build will not infect another. - -Branches --------- - -* master - branch intended for the RTEMS master which tracks the FreeBSD master - branch. This branch must be used for libbsd development. Back ports to the - 6-freebsd-12 are allowed. - -* 6-freebsd-12 - branch intended for RTEMS 6 which tracks the FreeBSD stable/12 - branch. This branch is maintained and regular updates from FreeBSD are - planned. It is recommended for production systems. - -* 5-freebsd-12 - branch belongs to the RTEMS 5 release. It is based on FreeBSD - stable/12 branch. It is recommended for production systems that use RTEMS 5. - -* 5 - branch belongs to the RTEMS 5 release. It is based on a FreeBSD - development version. - -* freebsd-9.3 - branch for some RTEMS version with a FreeBSD 9.3 baseline. - This branch is unmaintained. It is recommended to update to RTEMS 5 or 6. - -* 4.11 - branch for the RTEMS 4.11 release series. This branch is - unmaintained. It is recommended to update to RTEMS 5 or 6. - -Updating RTEMS Waf Support --------------------------- - -If you have a working libbsd repository and new changes to the `rtems_waf` -submodule has been made, you will need update. A `git status` will indicate -there are new commits with: - -``` -$ git status - [ snip output ] - modified: rtems_waf (new commits) - [ snip output ] -``` - -To update: - -``` -$ git submodule update rtems_waf -``` - -Please make sure you use the exact command or you might find you are cloning -the whole of the FreeBSD source tree. If that happens simply git ^C and try -again. - -FreeBSD Kernel Options ----------------------- - -You can set FreeBSD kernel options during build configuration with the ---freebsd-option=a,b,c,... configuration command option. This is an advanced -option and should only be used if you are familiar with the internals of the -FreeBSD kernel and what these options do. Each of the comma separated options -is converted to uppercase and passed as a compiler command line define (-D). - -The options are listed in: - -https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES - -An example to turn on a verbose kernel boot, verbose sysinit and bus debugging -configure with: - -``` ---freebsd-options=bootverbose,verbose_sysinit,bus_debug -``` - -To enable kernel internal consistency checking use: - -``` ---freebsd-options=invariants,invariant_support -``` - -Qemu and Networking -------------------- - -You can use the Qemu simulator to run a LibBSD based application and connect it -to a virtual network on your host. You have to create a TAP virtual Ethernet -interface for this: - -``` -sudo tunctl -p -t qtap -u $(whoami) -sudo ip link set dev qtap up -sudo ip addr add 169.254.1.1/16 dev qtap -``` - -You can show the interface state with the following command: - -``` -$ ip addr show qtap -27: qtap: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 - link/ether 8e:50:a2:fb:e1:3b brd ff:ff:ff:ff:ff:ff - inet 169.254.1.1/16 scope global qtap - valid_lft forever preferred_lft forever -``` - -You may have to assign the interface to a firewall zone. - -The Qemu command line varies by board support package, here is an example for -the arm/xilinx_zynq_a9_qemu BSP: - -``` -qemu-system-arm -serial null -serial mon:stdio -nographic \ - -M xilinx-zynq-a9 -m 256M \ - -net tap,ifname=qtap,script=no,downscript=no \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:12 \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/media01.exe -``` - -After some seconds it will acquire a IPv4 link-local address, e.g. - -``` -info: cgem0: probing for an IPv4LL address -debug: cgem0: checking for 169.254.159.156 -``` - -You can connect to the target via telnet for example: - -``` -$ telnet 169.254.159.156 -Trying 169.254.159.156... -Connected to 169.254.159.156. -Escape character is '^]'. - -RTEMS Shell on /dev/pty4. Use 'help' to list commands. -TLNT [/] # -``` - -SMP Requirements ----------------- - -In order to support -[EPOCH(9)](https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9) -a scheduler with thread pinning support is required. This is the case if you -use the default scheduler configuration. EPOCH(9) is a central synchronization -mechanism of the network stack. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..9b328078 --- /dev/null +++ b/README.rst @@ -0,0 +1,853 @@ +RTEMS LibBSD +************ + +Welcome to building LibBSD for RTEMS using Waf. This package is a library +containing various parts of the FreeBSD kernel ported to RTEMS. The library +replaces the networking port of FreeBSD in the RTEMS kernel sources. This +package is designed to be updated from the FreeBSD kernel sources and contains +more than just the networking code. + +To build this package you need a current RTEMS tool set for your architecture, +and a recent RTEMS kernel for your BSP installed. If you already have this, you +can skip to step 5 of the build procedure. + +Building and Installing LibBSD +============================== + +The following instructions show you how to build and install the RTEMS Tool +Suite for the ``arm`` target, the RTEMS kernel using the +``arm/xilinx_zynq_a9_qemu`` Board Support Package (BSP), and the LibBSD for this +BSP. + +The Waf build support for RTEMS requires you provide your BSP name as an +architecture and BSP pair. You must provide both or Waf will generate an error +message during the configure phase. + +We will build an Xilinx Zynq Qemu BSP using the name +``arm/xilinx_zynq_a9_qemu``. You can copy and paste the shell commands below to +do this. The individual steps are explained afterwards. + +.. code-block:: none + + sandbox="$PWD/sandbox" + mkdir sandbox + cd "$sandbox" + git clone git://git.rtems.org/rtems-source-builder.git + git clone git://git.rtems.org/rtems.git + git clone git://git.rtems.org/rtems-libbsd.git + cd "$sandbox" + cd rtems-source-builder/rtems + ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm + cd "$sandbox" + cd rtems + echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini + ./waf configure --prefix "$sandbox/rtems/6" + ./waf + ./waf install + cd "$sandbox" + cd rtems-libbsd + git submodule init + git submodule update rtems_waf + ./waf configure --prefix="$sandbox/rtems/6" \ + --rtems-bsps=arm/xilinx_zynq_a9_qemu \ + --buildset=buildset/default.ini + ./waf + ./waf install + ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build + +1. Create a sandbox directory: + + .. code-block:: none + + $ sandbox="$PWD/sandbox" + $ mkdir sandbox + +2. Clone the repositories: + + .. code-block:: none + + $ cd "$sandbox" + $ git clone git://git.rtems.org/rtems-source-builder.git + $ git clone git://git.rtems.org/rtems.git + $ git clone git://git.rtems.org/rtems-libbsd.git + +3. Build and install the tools: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-source-builder/rtems + $ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm + +4. Build and install the RTEMS Board Support Packages (BSP) you want to use: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems + $ echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini + $ ./waf configure --prefix "$sandbox/rtems/6" + $ ./waf + $ ./waf install + +5. Populate the ``rtems_waf`` git submodule. Note, make sure you specify + ``rtems_waf`` or the FreeBSD kernel source will be cloned: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ git submodule init + $ git submodule update rtems_waf + +6. Run Waf's configure with your specific settings. In this case the path to + the tools and RTEMS are provided on the command line and so do not need to + be in your path or environment, see comment below. You can use + ``--rtems-archs=arm,sparc,i386`` or + ``--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586`` to build for + more than BSP at a time. Note, you must provide the architecture and BSP as + a pair. Providing just the BSP name will fail. This call also explicitly + provides a buildset via the ``--buildset=buildset/default.ini`` option. If no + buildset is provided the default one (which is the same as the one provided + explicitly here) will be used. You can also provide multiple buildsets as a + coma separated list or via multiple ``--buildset=x`` options. + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ./waf configure --prefix="$sandbox/rtems/6" \ + --rtems-bsps=arm/xilinx_zynq_a9_qemu \ + --buildset=buildset/default.ini + +7. Build and install. The LibBSD package will be installed into the prefix + provided to configure: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ./waf + $ ./waf install + +9. Run the tests: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build + +It is good practice to keep your environment as empty as possible. Setting +paths to tools or specific values to configure or control a build is dangerous +because settings can leak between different builds and change what you expect a +build to do. The Waf tool used here lets you specify on the command line the +tools and RTEMS paths and this is embedded in Waf's configuration information. +If you have a few source trees working at any one time with different tool sets +or configurations you can easly move between them safe in the knowledge that +one build will not infect another. + +Buildsets +========= + +Note that the LibBSD supports different buildsets. These can be selected with +the ``--buildset=some.ini`` option during the configure phase. Take a look at +the comments in ``buildset/*.ini`` to see which build sets are officially +supported. + +You can also create and provide your own buildset configuration. But remember +that it's quite easy to break something by disabling the wrong modules. Only +the configurations in the ``buildset`` directory are officially maintained. + +Initialization +============== + +To initialise the LibBSD create a suitable ``rc.conf`` file. The FreeBSD man +page `RC.CONF(5) <https://www.freebsd.org/cgi/man.cgi?rc.conf>`_ provides the +details needed to create a suitable format file + +You can call one of three functions to run the initialisation once LibBSD has +initialised: + +* ``rtems_bsd_run_etc_rc_conf()``: Run ``/etc/rc.conf``. +* ``rtems_bsd_run_rc_conf()``: Run a user supplied file. +* ``rtems_bsd_run_rc_conf_script()``: Run the in memory line feed separated text string. + +For exapmle: + +.. code-block:: c + + void + network_init(void) + { + rtems_status_code sc; + + sc = rtems_bsd_initialize(); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_bsd_run_etc_rc_conf(true); /* verbose = true */ + } + +By default the networking support is builtin. Other directives can be added and +are found in ``machine/rtems-bsd-rc-conf-directives.h``. Please check the file +for the list. + +The following network names are supported: + +.. code-block:: none + + cloned_interfaces + ifconfig_'interface' + defaultrouter + hostname + +For example: + +.. code-block:: none + + # + # My BSD initialisation. + # + hostname="myhost" + cloned_interfaces="vlan0 vlan1" + ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0" + fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0" + defaultrouter="10.10.10.1" + +You can also intialise the LibBSD using code. The following code to +initialize the LibBSD: + +.. code-block:: c + + #include <assert.h> + #include <sysexits.h> + + #include <rtems/bsd/bsd.h> + + void + network_init(void) + { + rtems_status_code sc; + int exit_code; + + sc = rtems_bsd_initialize(); + assert(sc == RTEMS_SUCCESSFUL); + + exit_code = rtems_bsd_ifconfig_lo0(); + assert(exit_code == EX_OK); + } + +This performs the basic network stack initialization with a loopback interface. +Further initialization must be done using the standard FreeBSD network +configuration commands +`IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_ +using ``rtems_bsd_command_ifconfig()`` and +`ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_ +using ``rtems_bsd_command_route()``. For an example, please have a look at +`default-network-init.h <testsuite/include/rtems/bsd/test/default-network-init.h>`_. + +Task Priorities and Stack Size +============================== + +The default task priority is 96 for the interrupt server task (name "IRQS"), 98 +for the timer server task (name "TIME") and 100 for all other tasks. The +application may provide their own implementation of the +``rtems_bsd_get_task_priority()`` function if different values are desired (for +example in the translation unit which calls ``rtems_bsd_initialize()``). + +The task stack size is determined by the ``rtems_bsd_get_task_stack_size()`` +function which may be provided by the application in case the default is not +appropriate. + +Size for Allocator Domains +========================== + +The size for an allocator domain can be specified via the +``rtems_bsd_get_allocator_domain_size()`` function. The application may provide +their own implementation of the ``rtems_bsd_get_allocator_domain_size()`` +function (for example in the module which calls ``rtems_bsd_initialize()``) if +different values are desired. The default size is 8MiB for all domains. + +Redirecting or Disabling the Output +=================================== + +A lot of system messages are printed to the ``stdout`` by default. If you want to +redirect them you can overwrite the default print handler. That can even be done +before the libbsd initialization to catch all messages. An example would look +like follows: + +.. code-block:: c + + int my_vprintf_handler(int level, const char *fmt, va_list ap) { + /* Do something with the messages. */ + + return number_of_printed_chars; + } + + ... + /* In your initialization: */ + rtems_bsd_vprintf_handler old; + old = rtems_bsd_set_vprintf_handler(my_vprintf_handler); + ... + +As a special case, you can set the ``rtems_bsd_vprintf_handler_mute(...)`` +provided by LibBSD to suppress all output. + +Branches +======== + +master + This branch is intended for the RTEMS master which tracks the FreeBSD + master branch. This branch must be used for libbsd development. Back + ports to the 6-freebsd-12 are allowed. + +6-freebsd-12 + This branch is intended for RTEMS 6 which tracks the FreeBSD stable/12 + branch. This branch is maintained and regular updates from FreeBSD are + planned. It is recommended for production systems. + +5-freebsd-12 + This branch belongs to the RTEMS 5 release. It is based on FreeBSD + stable/12 branch. It is recommended for production systems that use + RTEMS 5. + +5 + This branch belongs to the RTEMS 5 release. It is based on a FreeBSD + development version. This branch is unmaintained. Use 5-freebsd-12 for + RTEMS 5. + +freebsd-9.3 + Is the branch for some RTEMS version with a FreeBSD 9.3 baseline. This + branch is unmaintained. It is recommended to update to RTEMS 5 or 6. + +4.11 + Is the branch for the RTEMS 4.11 release series. This branch is + unmaintained. It is recommended to update to RTEMS 5 or 6. + +Features +======== + +The following features are available in LibBSD. Some features need device +driver support for a particular target platform. + +* `BPF(4) <http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4>`_: Berkeley Packet Filter +* `DHCPCD(8) <http://roy.marples.name/projects/dhcpcd/index>`_: DHCP client +* `dns_sd.h <mDNSResponder/mDNSShared/dns_sd.h>`_: DNS Service Discovery +* `GETHOSTBYNAME(3) <http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3>`_: Get network host entry +* `IF_BRIDGE(4) <http://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4>`_: Network bridge device +* `INET(4) <http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4>`_: Internet protocol family +* `INET6(4) <http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4>`_: Internet protocol version 6 family +* `IPSEC(4) <http://www.freebsd.org/cgi/man.cgi?query=ipsec&sektion=4>`_: Internet Protocol Security protocol +* `KQUEUE(2) <http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2>`_: Kernel event notification mechanism +* `LAGG(4) <http://www.freebsd.org/cgi/man.cgi?query=lagg&sektion=4>`_: Link aggregation and link failover interface +* `mDNSEmbeddedAPI.h <mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h>`_: Multi-Cast DNS +* `MMC(4) <http://www.freebsd.org/cgi/man.cgi?query=mmc&sektion=4>`_: MultiMediaCard and SD Card bus driver +* `NET80211(4) <http://www.freebsd.org/cgi/man.cgi?query=net80211&sektion=4>`_: Standard interface to IEEE 802.11 devices +* `NVME(4) <http://www.freebsd.org/cgi/man.cgi?query=nvme&sektion=4>`_: NVM Express core driver +* `PCI(4) <http://www.freebsd.org/cgi/man.cgi?query=pci&sektion=4>`_: Generic PCI/PCIe bus driver +* `PF(4) <http://www.freebsd.org/cgi/man.cgi?query=pf&sektion=4>`_: Packet filter +* `POLL(2) <http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2>`_: Synchronous I/O multiplexing +* `RESOLVER(3) <http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3>`_: Resolver routines +* `ROUTE(4) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4>`_: Kernel packet forwarding database +* `SELECT(2) <http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2>`_: Synchronous I/O multiplexing +* `SOCKET(2) <http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2>`_: Create an endpoint for communication +* `SSL(7) <http://www.freebsd.org/cgi/man.cgi?query=ssl&sektion=7>`_: OpenSSL SSL/TLS library +* `SYSCTL(3) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3>`_: Get or set system information +* `TCP(4) <http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4>`_: Internet Transmission Control Protocol +* `UDP(4) <http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4>`_: Internet User Datagram Protocol +* `UMASS(4) <http://www.freebsd.org/cgi/man.cgi?query=umass&sektion=4>`_: USB Mass Storage Devices driver +* `UNIX(4) <http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4>`_: UNIX-domain protocol family +* `USB(4) <http://www.freebsd.org/cgi/man.cgi?query=usb&sektion=4>`_: Universal Serial Bus +* `VLAN(4) <http://www.freebsd.org/cgi/man.cgi?query=vlan&sektion=4>`_: IEEE 802.1Q VLAN network interface + +Commands +======== + +In LibBSD the following ports of FreeBSD command line tools are available. You +can invoke the commands in the RTEMS Shell or through function calls, for +example ``rtems_bsd_command_ifconfig()``. The functions declarations are +available through +`#include <machine/rtems-bsd-commands.h> <rtemsbsd/include/machine/rtems-bsd-commands.h>`_. + +* `ARP(8) <http://www.freebsd.org/cgi/man.cgi?query=arp&sektion=8>`_: Address resolution display and control +* `HOSTNAME(1) <http://www.freebsd.org/cgi/man.cgi?query=hostname&sektion=1>`_: Set or print name of current host system +* `IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_: Configure network interface parameters +* `IFMCSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=ifmcstat&sektion=8>`_: Dump multicast group management statistics per interface +* `NETSTAT(1) <http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1>`_: Show network status +* `NVMECONTROL(8) <http://www.freebsd.org/cgi/man.cgi?query=nvmecontrol&sektion=8>`_: NVM Express control utility +* `OPENSSL(1) <http://www.freebsd.org/cgi/man.cgi?query=openssl&sektion=1>`_: OpenSSL command line tool +* `PFCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_: Control the packet filter (PF) device +* `PING6(8) <http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8>`_: Send ICMPv6 ECHO_REQUEST packets to network hosts +* `PING(8) <http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8>`_: Send ICMP ECHO_REQUEST packets to network hosts +* `RACOON(8) <http://www.freebsd.org/cgi/man.cgi?query=racoon&sektion=8>`_: IKE (ISAKMP/Oakley) key management daemon +* `ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_: Manually manipulate the routing tables +* `SETKEY(8) <http://www.freebsd.org/cgi/man.cgi?query=setkey&sektion=8>`_: Manually manipulate the IPsec SA/SP database +* `STTY(1) <http://www.freebsd.org/cgi/man.cgi?query=stty&sektion=1>`_: Set the options for a terminal device interface +* `SYSCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=8>`_: Get or set kernel state +* `TCPDUMP(1) <http://www.freebsd.org/cgi/man.cgi?query=tcpdump&sektion=1>`_: Dump traffic on a network +* `VMSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=vmstat&sektion=8>`_: Report virtual memory statistics +* `WPA_SUPPLICANT(8) <http://www.freebsd.org/cgi/man.cgi?query=wpa_supplicant&sektion=8>`_: WPA/802.11i Supplicant for wireless network devices + +Command specific notes are listed below. + +HOSTNAME(1) + In addition to the standard options the RTEMS version of the HOSTNAME(1) + command supports the -m flag to set/get the multicast hostname of the mDNS + resolver instance. See also ``rtems_mdns_sethostname()`` and + ``rtems_mdns_gethostname()``. + +Packet Filter (PF, Firewall) +============================ + +It is possible to use PF as a firewall. See the +`FreeBSD Handbook <https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-pf>`_ +for details on the range of functions and for how to configure the firewall. + +Configuration +------------- + +The following is necessary to use PF on RTEMS: + +* You have to provide a ``/etc/pf.os`` file. The firewall can use it for passive + OS fingerprinting. If you don't want to use this feature, the file may contain + nothing except a line of comment (for example "# empty"). + +* If some filters use protocol names (like ``tcp`` or ``udp``) you have to provide a + ``/etc/protocols`` file. + +* If some filters use service names (like ``http`` or ``https``) you have to provide a + ``/etc/services`` file. + +* Create a rule file (normally ``/etc/pf.conf``). See the FreeBSD manual for the + syntax. + +* Load the rule file using the + `pfctl <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_ + command and enable PF. Please note that the pfctl command needs a lot of + stack. You should use at least RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of + stack. An example initialisation can look like follows: + + .. code-block:: c + + int exit_code; + char *argv[] = { + "pfctl", + "-f", + "/etc/pf.conf", + "-e", + NULL + }; + + exit_code = rtems_bsd_command_pfctl(ARGC(argv), argv); + assert(exit_code == EXIT_SUCCSESS); + +Known Restrictions +------------------ + +Currently, PF on RTEMS always uses the configuration for memory restricted +systems (on FreeBSD that means systems with less than 100 MB RAM). This is +fixed in ``pfctl_init_options()``. + +Wireless Network (WLAN) +======================= + +The LibBSD provides a basic support for WLAN. Note that currently this support +is still in an early state. The WLAN support is _not_ enabled in the default +buildset. You have to configure LibBSD with the +``--buildset=buildset/everything.ini`` to enable that feature. + +Configuration +------------- + +The following gives a rough overview over the necessary steps to connect to an +encrypted network with an RTL8188EU based WiFi dongle: + +* Reference all necessary module for your BSP. For some BSPs this is already + done in the ``nexus-devices.h``: + + .. code-block:: none + + SYSINIT_MODULE_REFERENCE(wlan_ratectl_none); + SYSINIT_MODULE_REFERENCE(wlan_sta); + SYSINIT_MODULE_REFERENCE(wlan_amrr); + SYSINIT_MODULE_REFERENCE(wlan_wep); + SYSINIT_MODULE_REFERENCE(wlan_tkip); + SYSINIT_MODULE_REFERENCE(wlan_ccmp); + SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub); + +* Create your wlan device using ifconfig: + + .. code-block:: none + + ifconfig wlan0 create wlandev rtwn0 up + +* Start a ``wpa_supplicant`` instance for that device: + + .. code-block:: none + + wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf + +Note that the wpa_supplicant will only be active till the device goes down. A +workaround is to just restart it every time it exits. + +Known Restrictions +------------------ + +* The network interface (e.g. wlan0) is currently not automatically created. It + would be nice, if some service would create it as soon as for example a USB + device is connected. In FreeBSD the names are assigned via rc.conf with lines + like ``wlans_rtwn0="wlan0"``. + +* ``wpa_supplicant`` hast to be started after the device is created. It has to be + restarted every time the connection goes down. Instead of this behaviour, + there should be some service that starts and restarts ``wpa_supplicant`` + automatically if a interface is ready. Probably the dhcpcd hooks could be used + for that. + +* The current ``wpa_supplicant`` implementation is protected with a lock so it can't + be started more than one time. If multiple interface should be used, all have + to be handled by that single instance. That makes it hard to add interfaces + dynamically. ``wpa_supplicant`` should be reviewed thoroughly whether multiple + instances could be started in parallel. + +* The control interface of ``wpa_supplicant`` most likely doesn't work. The wpa_cli + application is not ported. + +IPSec +===== + +The IPSec support is optional in LibBSD. It is disabled in the default build +set. Please make sure to use a build set with ``netipsec = on``. + +Configuration +------------- + +To use IPSec the following configuration is necessary: + +.. code-block:: none + + SYSINIT_MODULE_REFERENCE(if_gif); + SYSINIT_MODULE_REFERENCE(cryptodev); + RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec) + RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL); + +Alternatively, you can use the ``RTEMS_BSD_CONFIG_IPSEC`` which also includes the +rc.conf support for ipsec. It's still necessary to include a crypto device in +your config (``cryptosoft`` in the above sample). + +The necessary initialization steps for a IPSec connection are similar to the +steps on a FreeBSD-System. The example assumes the following setup: + +- RTEMS external IP: 192.168.10.1/24 +- RTEMS internal IP: 10.10.1.1/24 +- remote external IP: 192.168.10.10/24 +- remote internal IP: 172.24.0.1/24 +- shared key: "mysecretkey" + +With this the following steps are necessary: + +* Create a gif0 device: + + .. code-block:: none + + ifconfig gif0 create + +* Configure the gif0 device: + + .. code-block:: none + + ifconfig gif0 10.10.1.1 172.24.0.1 + ifconfig gif0 tunnel 192.168.10.1 192.168.10.10 + +* Add a route to the remote net via the remote IP: + + .. code-block:: none + + route add 172.24.0.0/24 172.24.0.1 + +* Create a correct rule set in ``/etc/setkey.conf``: + + .. code-block:: none + + flush; + spdflush; + spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use; + spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use; + +* Call ``setkey``: + + .. code-block:: none + + setkey -f /etc/setkey.conf + +* Create a correct configuration in ``/etc/racoon.conf``: + + .. code-block:: none + + path pre_shared_key "/etc/racoon_psk.txt"; + log info; + + padding # options are not to be changed + { + maximum_length 20; + randomize off; + strict_check off; + exclusive_tail off; + } + + listen # address [port] that racoon will listen on + { + isakmp 192.168.10.1[500]; + } + + remote 192.168.10.10 [500] + { + exchange_mode main; + my_identifier address 192.168.10.1; + peers_identifier address 192.168.10.10; + proposal_check obey; + proposal { + encryption_algorithm 3des; + hash_algorithm md5; + authentication_method pre_shared_key; + lifetime time 3600 sec; + dh_group 2; + } + } + + sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any) + { + pfs_group 2; + lifetime time 28800 sec; + encryption_algorithm 3des; + authentication_algorithm hmac_md5; + compression_algorithm deflate; + } + +* Create a correct configuration in ``/etc/racoon_psk.txt``: + + .. code-block:: none + + 192.168.10.10 mysecretkey + +* Start a ike-daemon (racoon): + + .. code-block:: none + + racoon -F -f /etc/racoon.conf +---- + +All commands can be called via the respective API functions. For racoon there is +a ``rtems_bsd_racoon_daemon()`` function that forks of racoon as a task. + +Alternatively, IPSec can also be configured via rc.conf entries: + +.. code-block:: none + + cloned_interfaces="gif0" + ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10" + ike_enable="YES" + ike_program="racoon" + ike_flags="-F -f /etc/racoon.conf" + ike_priority="250" + + ipsec_enable="YES" + ipsec_file="/etc/setkey.conf" + +ATTENTION: It is possible that the first packets slip through the tunnel without +encryption (true for FreeBSD as well as RTEMS). You might want to set up a +firewall rule to prevent that. + +Updating RTEMS Waf Support +========================== + +If you have a working libbsd repository and new changes to the ``rtems_waf`` +submodule has been made, you will need update. A ``git status`` will indicate +there are new commits with: + +.. code-block:: none + + $ git status + [ snip output ] + modified: rtems_waf (new commits) + [ snip output ] + +To update: + +.. code-block:: none + + $ git submodule update rtems_waf + +Please make sure you use the exact command or you might find you are cloning +the whole of the FreeBSD source tree. If that happens simply git ^C and try +again. + +FreeBSD Kernel Options +====================== + +You can set FreeBSD kernel options during build configuration with the +--freebsd-option=a,b,c,... configuration command option. This is an advanced +option and should only be used if you are familiar with the internals of the +FreeBSD kernel and what these options do. Each of the comma separated options +is converted to uppercase and passed as a compiler command line define (-D). + +The options are listed in the FreeBSD +`NOTES <https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES>`_ +file. + +An example to turn on a verbose kernel boot, verbose sysinit and bus debugging +configure with: + +.. code-block:: none + + --freebsd-options=bootverbose,verbose_sysinit,bus_debug + +To enable kernel internal consistency checking use: + +.. code-block:: none + + --freebsd-options=invariants,invariant_support + +SMP Requirements +================ + +In order to support +`EPOCH(9) <https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9>`_ +a scheduler with thread pinning support is required. This is the case if you +use the default scheduler configuration. EPOCH(9) is a central synchronization +mechanism of the network stack. + +Configuration for Network Tests +=============================== + +If you need some other IP configuration for the network tests that use a fixed +IP config you can copy ``config.inc`` to a location outside to the source tree and +adapt it. Then use the option ``--net-test-config=NET_CONFIG`` to pass the file to +Waf's configure command. + +.. code-block:: none + + NET_CFG_SELF_IP = 10.0.0.2 + NET_CFG_NETMASK = 255.255.0.0 + NET_CFG_PEER_IP = 10.0.0.1 + NET_CFG_GATEWAY_IP = 10.0.0.1 + +Qemu and Networking +=================== + +You can use the Qemu simulator to run a LibBSD based application and connect it +to a virtual network on your host. + +Networking with TAP Interface +----------------------------- + +One option for networking with Qemu is using a TAP interface (virtual +Ethernet). You can create a TAP interface with these commands on Linux: + +.. code-block:: none + + sudo ip tuntap add qtap mode tap user $(whoami) + sudo ip link set dev qtap up + sudo ip addr add 169.254.1.1/16 dev qtap + +You can show the interface state with the following command: + +.. code-block:: none + + $ ip addr show qtap + 27: qtap: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 + link/ether 8e:50:a2:fb:e1:3b brd ff:ff:ff:ff:ff:ff + inet 169.254.1.1/16 scope global qtap + valid_lft forever preferred_lft forever + +You may have to assign the interface to a firewall zone. + +The Qemu command line varies by board support package, here is an example for +the arm/xilinx_zynq_a9_qemu BSP: + +.. code-block:: none + + qemu-system-arm -serial null -serial mon:stdio -nographic \ + -M xilinx-zynq-a9 -m 256M \ + -net nic,model=cadence_gem \ + -net tap,ifname=qtap,script=no,downscript=no \ + -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/media01.exe + +Make sure that each Qemu instance uses its own MAC address to avoid an address +conflict (or otherwise use it as a test). After some seconds it will acquire a +IPv4 link-local address, for example: + +.. code-block:: none + + info: cgem0: probing for an IPv4LL address + debug: cgem0: checking for 169.254.159.156 + +You can connect to the target via telnet, for example: + +.. code-block:: none + + $ telnet 169.254.159.156 + Trying 169.254.159.156... + Connected to 169.254.159.156. + Escape character is '^]'. + + RTEMS Shell on /dev/pty4. Use 'help' to list commands. + TLNT [/] # + +Virtual Distributed Ethernet (VDE) +---------------------------------- + +You can use a Virtual Distributed Ethernet (VDE) to create a network +environment that does not need to run Qemu as root or needing to drop the tap's +privileges to run Qemu. + +VDE creates a software switch with a default of 32 ports which means a single +kernel tap can support 32 Qemu networking sessions. + +To use VDE you need to build Qemu with VDE support. The RSB can detect a VDE +plug and enable VDE support in Qemu when building. On FreeBSD install the VDE +support with: + +.. code-block:: none + + pkg install -u vde2 + +Build Qemu with the RSB. + +To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD +add to your ``/etc/rc.conf``: + +.. code-block:: none + + cloned_interfaces="bridge0 tap0" + autobridge_interfaces="bridge0" + autobridge_bridge0="re0 tap0" + ifconfig_re0="up" + ifconfig_tap0="up" + ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0" + defaultrouter="10.10.1.1" + +Start the VDE switch as root: + +.. code-block:: none + + sysctl net.link.tap.user_open=1 + sysctl net.link.tap.up_on_open=1 + vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660 + chmod 660 /dev/tap0 + +You can connect to the VDE switch's management channel using: + +.. code-block:: none + + vdeterm /tmp/mgmt1 + +To run Qemu: + +.. code-block:: none + + qemu-system-arm -serial null -serial mon:stdio -nographic \ + -M xilinx-zynq-a9 -m 256M \ + -net nic,model=cadence_gem \ + -net vde,id=vde0,sock=/tmp/vde1 + -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/rcconf02.exe @@ -883,7 +883,7 @@ class Module(object): ] return files - def addPlainTextFile(self, files): + def addPlainTextFiles(self, files): self.files += self.addFiles('user', files, FreeBSDPathComposer(), Converter(), Converter(), assertNothing) diff --git a/buildset/default.ini b/buildset/default.ini index 472c535d..88379a96 100644 --- a/buildset/default.ini +++ b/buildset/default.ini @@ -43,6 +43,7 @@ dhcpcd = on dpaa = on evdev = on fdt = on +if_mve = on imx = on in_cksum = on mdnsresponder = on diff --git a/dhcpcd/dhcpcd.c b/dhcpcd/dhcpcd.c index b7839d49..93620727 100644 --- a/dhcpcd/dhcpcd.c +++ b/dhcpcd/dhcpcd.c @@ -1155,7 +1155,7 @@ dhcpcd_task(rtems_task_argument arg) (*config->destroy)(config, exit_code); } - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } rtems_status_code diff --git a/freebsd-org b/freebsd-org -Subproject 6b0307a0a5184339393f555d5d424190d8a8277 +Subproject 5d85e12f44ccb0e5728344a002c108ddc105e03 diff --git a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h index d021ae99..2c8ff3b8 100644 --- a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h +++ b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h @@ -139,7 +139,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern int nd_smi_module_loaded); /* print-sll.c */ /* print-slow.c */ /* print-smb.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern u_char const *startbuf); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern unsigned char const *startbuf); /* print-smtp.c */ /* print-snmp.c */ /* print-stp.c */ diff --git a/freebsd/contrib/tcpdump/tcpdump.c b/freebsd/contrib/tcpdump/tcpdump.c index 3b68ed51..24c6a29c 100644 --- a/freebsd/contrib/tcpdump/tcpdump.c +++ b/freebsd/contrib/tcpdump/tcpdump.c @@ -143,6 +143,7 @@ The Regents of the University of California. All rights reserved.\n"; #include <sys/sysctl.h> #include <machine/rtems-bsd-commands.h> #include <assert.h> +#include <sched.h> #include <rtems.h> #include <rtems/linkersets.h> #define setpriority(a, b, c) @@ -210,8 +211,10 @@ cap_channel_t *capdns; static void error(FORMAT_STRING(const char *), ...) NORETURN PRINTFLIKE(1, 2); static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2); static void exit_tcpdump(int) NORETURN; +#ifndef __rtems__ static RETSIGTYPE cleanup(int); static RETSIGTYPE child_cleanup(int); +#endif /* __rtems__ */ static void print_version(void); static void print_usage(void); static void show_tstamp_types_and_exit(pcap_t *, const char *device) NORETURN; @@ -223,6 +226,7 @@ static void show_devices_and_exit (void) NORETURN; static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); +#ifndef __rtems__ static void droproot(const char *, const char *); #ifdef SIGNAL_REQ_INFO @@ -236,6 +240,7 @@ RETSIGTYPE requestinfo(int); #elif defined(HAVE_ALARM) static void verbose_stats_dump(int sig); #endif +#endif /* __rtems__ */ static void info(int); static u_int packets_captured; @@ -627,6 +632,7 @@ static const struct option longopts[] = { { NULL, 0, NULL, 0 } }; +#ifndef __rtems__ #ifndef _WIN32 /* Drop root privileges and chroot if necessary */ static void @@ -659,7 +665,6 @@ droproot(const char *username, const char *chroot_dir) } } #else -#ifndef __rtems__ if (initgroups(pw->pw_name, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { fprintf(stderr, "%s: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", @@ -672,7 +677,6 @@ droproot(const char *username, const char *chroot_dir) else { fprintf(stderr, "dropped privs to %s\n", username); } -#endif /* __rtems__ */ #endif /* HAVE_LIBCAP_NG */ } else { @@ -694,6 +698,7 @@ droproot(const char *username, const char *chroot_dir) } #endif /* _WIN32 */ +#endif /* __rtems__ */ static int getWflagChars(int x) @@ -1219,26 +1224,22 @@ typedef struct { FILE *in; pcap_t *pd; rtems_id master; + bool terminate; } pcap_loop_context; static void pcap_loop_monitor(rtems_task_argument arg) { - pcap_loop_context *ctx; + const pcap_loop_context *ctx; FILE *in; pcap_t *pd; - rtems_id master; rtems_status_code sc; - ctx = (pcap_loop_context *)arg; + ctx = (const pcap_loop_context *)arg; in = ctx->in; pd = ctx->pd; - master = ctx->master; - - sc = rtems_event_transient_send(master); - assert(sc == RTEMS_SUCCESSFUL); - while (true) { + while (!ctx->terminate) { int c; c = fgetc(in); @@ -1247,19 +1248,22 @@ pcap_loop_monitor(rtems_task_argument arg) pcap_breakloop(pd); break; } + + sched_yield(); } - rtems_task_delete(RTEMS_SELF); - assert(0); + sc = rtems_event_transient_send(ctx->master); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_task_exit(); } -static int -pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud) +static void +pcap_create_loop_monitor(pcap_loop_context *ctx, pcap_t *pd) { rtems_status_code sc; rtems_task_priority priority; rtems_id id; - pcap_loop_context ctx; sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); @@ -1269,27 +1273,38 @@ pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud) RTEMS_MINIMUM_STACK_SIZE, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id); if (sc != RTEMS_SUCCESSFUL) { - fprintf(stderr, "tcpdump: cannot create helper thread: %s\n", + error("cannot create pcap loop monitor thread: %s\n", rtems_status_text(sc)); - return (-1); } fprintf(stdout, "tcpdump: press <ENTER> or 'q' or 'Q' to quit\n"); - ctx.in = stdin; - ctx.pd = pd; - ctx.master = rtems_task_self(); + ctx->in = stdin; + ctx->pd = pd; + ctx->master = rtems_task_self(); + ctx->terminate = false; sc = rtems_task_start(id, pcap_loop_monitor, - (rtems_task_argument)&ctx); + (rtems_task_argument)ctx); assert(sc == RTEMS_SUCCESSFUL); +} + +static void +pcap_terminate_loop_monitor(pcap_loop_context *ctx) +{ + rtems_status_code sc; + + ctx->terminate = true; sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); assert(sc == RTEMS_SUCCESSFUL); - - return (pcap_loop(pd, cnt, cb, ud)); } -#define pcap_loop(pd, cnt, cb, ud) pcap_loop_wrapper(pd, cnt, cb, ud) +static void +destroy_pcap_dumper(void *arg) +{ + + pcap_dump_close(arg); +} #endif /* __rtems__ */ int #ifndef __rtems__ @@ -1306,15 +1321,19 @@ main(int argc, char **argv) int dlt; const char *dlt_name; struct bpf_program fcode; +#ifndef __rtems__ #ifndef _WIN32 RETSIGTYPE (*oldhandler)(int); #endif +#endif /* __rtems__ */ struct dump_info dumpinfo; u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; char VFileLine[PATH_MAX + 1]; +#ifndef __rtems__ char *username = NULL; char *chroot_dir = NULL; +#endif /* __rtems__ */ char *ret = NULL; char *end; #ifdef HAVE_PCAP_FINDALLDEVS @@ -1686,9 +1705,11 @@ main(int argc, char **argv) zflag = optarg; break; +#ifndef __rtems__ case 'Z': username = optarg; break; +#endif /* __rtems__ */ case '#': ndo->ndo_packet_number = 1; @@ -1985,6 +2006,7 @@ main(int argc, char **argv) init_print(ndo, localnet, netmask, timezone_offset); +#ifndef __rtems__ #ifndef _WIN32 (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); @@ -2052,6 +2074,7 @@ main(int argc, char **argv) } #endif /* _WIN32 */ +#endif /* __rtems__ */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); @@ -2146,6 +2169,12 @@ main(int argc, char **argv) if (Uflag) pcap_dump_flush(p); #endif +#ifdef __rtems__ + if (rtems_bsd_program_add_destructor(destroy_pcap_dumper, p) == + NULL) { + error("cannot add destructor"); + } +#endif /* __rtems__ */ } else { dlt = pcap_datalink(pd); ndo->ndo_if_printer = get_if_printer(ndo, dlt); @@ -2153,6 +2182,7 @@ main(int argc, char **argv) pcap_userdata = (u_char *)ndo; } +#ifndef __rtems__ #ifdef SIGNAL_REQ_INFO /* * We can't get statistics when reading from a file rather @@ -2177,6 +2207,7 @@ main(int argc, char **argv) alarm(1); #endif } +#endif /* __rtems__ */ if (RFileName == NULL) { /* @@ -2219,7 +2250,19 @@ main(int argc, char **argv) #endif /* HAVE_CAPSICUM */ do { +#ifdef __rtems__ + pcap_loop_context ctx; + + if (RFileName == NULL) { + pcap_create_loop_monitor(&ctx, pd); + } +#endif /* __rtems__ */ status = pcap_loop(pd, cnt, callback, pcap_userdata); +#ifdef __rtems__ + if (RFileName == NULL) { + pcap_terminate_loop_monitor(&ctx); + } +#endif /* __rtems__ */ if (WFileName == NULL) { /* * We're printing packets. Flush the printed output, @@ -2338,6 +2381,7 @@ main(int argc, char **argv) exit_tcpdump(status == -1 ? 1 : 0); } +#ifndef __rtems__ /* make a clean exit on interrupts */ static RETSIGTYPE cleanup(int signo _U_) @@ -2390,6 +2434,7 @@ child_cleanup(int signo _U_) wait(NULL); } #endif /* HAVE_FORK && HAVE_VFORK */ +#endif /* __rtems__ */ static void info(register int verbose) @@ -2750,6 +2795,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) char Wpcap_version[]="3.1"; #endif +#ifndef __rtems__ #ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int signo _U_) { @@ -2778,6 +2824,7 @@ static void verbose_stats_dump(int sig _U_) alarm(1); } #endif +#endif /* __rtems__ */ USES_APPLE_DEPRECATED_API static void diff --git a/freebsd/contrib/wpa/src/utils/eloop.c b/freebsd/contrib/wpa/src/utils/eloop.c index 41de0f79..09493b89 100644 --- a/freebsd/contrib/wpa/src/utils/eloop.c +++ b/freebsd/contrib/wpa/src/utils/eloop.c @@ -16,6 +16,9 @@ #include "list.h" #include "eloop.h" +#ifdef __rtems__ +#define CONFIG_ELOOP_KQUEUE +#endif /* __rtems__ */ #if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL) #error Do not define both of poll and epoll #endif @@ -955,6 +958,7 @@ int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, } +#ifndef __rtems__ #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { @@ -966,8 +970,10 @@ static void eloop_handle_alarm(int sig) exit(1); } #endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* __rtems__ */ +#ifndef __rtems__ static void eloop_handle_signal(int sig) { int i; @@ -990,6 +996,7 @@ static void eloop_handle_signal(int sig) } } } +#endif /* __rtems__ */ static void eloop_process_pending_signals(void) @@ -1001,9 +1008,11 @@ static void eloop_process_pending_signals(void) eloop.signaled = 0; if (eloop.pending_terminate) { +#ifndef __rtems__ #ifndef CONFIG_NATIVE_WINDOWS alarm(0); #endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* __rtems__ */ eloop.pending_terminate = 0; } @@ -1017,6 +1026,7 @@ static void eloop_process_pending_signals(void) } +#ifndef __rtems__ int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { @@ -1037,26 +1047,35 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, return 0; } +#endif /* __rtems__ */ int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data) { +#ifndef __rtems__ int ret = eloop_register_signal(SIGINT, handler, user_data); if (ret == 0) ret = eloop_register_signal(SIGTERM, handler, user_data); return ret; +#else /* __rtems__ */ + return 0; +#endif /* __rtems__ */ } int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data) { +#ifndef __rtems__ #ifdef CONFIG_NATIVE_WINDOWS return 0; #else /* CONFIG_NATIVE_WINDOWS */ return eloop_register_signal(SIGHUP, handler, user_data); #endif /* CONFIG_NATIVE_WINDOWS */ +#else /* __rtems__ */ + return 0; +#endif /* __rtems__ */ } diff --git a/freebsd/crypto/openssl/apps/ocsp.c b/freebsd/crypto/openssl/apps/ocsp.c index 7ff6a20c..dba8e6a9 100644 --- a/freebsd/crypto/openssl/apps/ocsp.c +++ b/freebsd/crypto/openssl/apps/ocsp.c @@ -58,7 +58,7 @@ NON_EMPTY_TRANSLATION_UNIT #endif # if !defined(NO_FORK) && !defined(OPENSSL_NO_SOCK) \ - && !defined(OPENSSL_NO_POSIX_IO) + && !defined(OPENSSL_NO_POSIX_IO) && !defined(__rtems__) # define OCSP_DAEMON # include <sys/types.h> # include <sys/wait.h> diff --git a/freebsd/crypto/openssl/apps/openssl.c b/freebsd/crypto/openssl/apps/openssl.c index 31ec58d8..cdbb262b 100644 --- a/freebsd/crypto/openssl/apps/openssl.c +++ b/freebsd/crypto/openssl/apps/openssl.c @@ -89,9 +89,11 @@ static void calculate_columns(DISPLAY_COLUMNS *dc) static int apps_startup(void) { +#ifndef __rtems__ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif +#endif /* __rtems__ */ /* Set non-default library initialisation settings */ if (!OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN diff --git a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h index 2c2b926e..90043fb0 100644 --- a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h +++ b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h @@ -3,6 +3,4 @@ #include "rtems-bsd-openssl-data.h" /* ocsp.c */ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static char *prog); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int acfd); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int multi); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int termsig); diff --git a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c index 03596eee..6a040553 100644 --- a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c +++ b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c @@ -158,11 +158,13 @@ struct IOSB { # endif /* Define globals. They are protected by a lock */ +#ifndef __rtems__ # ifdef SIGACTION static struct sigaction savsig[NX509_SIG]; # else static void (*savsig[NX509_SIG]) (int); # endif +#endif /* __rtems__ */ # ifdef OPENSSL_SYS_VMS static struct IOSB iosb; @@ -185,7 +187,9 @@ static int is_a_tty; /* Declare static functions */ # if !defined(OPENSSL_SYS_WINCE) static int read_till_nl(FILE *); +#ifndef __rtems__ static void recsig(int); +#endif /* __rtems__ */ static void pushsig(void); static void popsig(void); # endif @@ -588,6 +592,7 @@ static int close_console(UI *ui) /* Internal functions to handle signals and act on them */ static void pushsig(void) { +#ifndef __rtems__ # ifndef OPENSSL_SYS_WIN32 int i; # endif @@ -630,10 +635,12 @@ static void pushsig(void) # ifdef SIGWINCH signal(SIGWINCH, SIG_DFL); # endif +#endif /* __rtems__ */ } static void popsig(void) { +#ifndef __rtems__ # ifdef OPENSSL_SYS_WIN32 signal(SIGABRT, savsig[SIGABRT]); signal(SIGFPE, savsig[SIGFPE]); @@ -659,12 +666,15 @@ static void popsig(void) # endif } # endif +#endif /* __rtems__ */ } +#ifndef __rtems__ static void recsig(int i) { intr_signal = i; } +#endif /* __rtems__ */ # endif /* Internal functions specific for Windows */ diff --git a/freebsd/lib/libc/include/libc_private.h b/freebsd/lib/libc/include/libc_private.h index fb3a4bb2..4a699e93 100644 --- a/freebsd/lib/libc/include/libc_private.h +++ b/freebsd/lib/libc/include/libc_private.h @@ -36,8 +36,12 @@ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ +#ifndef __rtems__ #include <sys/_types.h> #include <sys/_pthreadtypes.h> +#else /* __rtems__ */ +#include <sys/types.h> +#endif /* __rtems__ */ /* * This global flag is non-zero when a process has created one diff --git a/freebsd/lib/libc/stdio/local.h b/freebsd/lib/libc/stdio/local.h index bed0b232..5ec1d3f5 100644 --- a/freebsd/lib/libc/stdio/local.h +++ b/freebsd/lib/libc/stdio/local.h @@ -78,13 +78,15 @@ extern int __srefill(FILE *); */ extern int __srefill_r(struct _reent *,FILE *); -#define __srefill(_x) __srefill_r(__getreent(), _x) +#define __srefill(_x) __srefill_r(_REENT, _x) #endif /* __rtems__ */ extern int __sread(void *, char *, int); extern int __swrite(void *, char const *, int); extern fpos_t __sseek(void *, fpos_t, int); extern int __sclose(void *); +#ifndef __rtems__ extern void __sinit(void); +#endif /* __rtems__ */ extern void _cleanup(void); extern void __smakebuf(FILE *); extern int __swhatbuf(FILE *, size_t *, int *); diff --git a/freebsd/sbin/pfctl/pfctl_altq.c b/freebsd/sbin/pfctl/pfctl_altq.c index 7cf72b43..05c7da22 100644 --- a/freebsd/sbin/pfctl/pfctl_altq.c +++ b/freebsd/sbin/pfctl/pfctl_altq.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #define PFIOC_USE_LATEST +#define _WANT_FREEBSD_BITSET #include <sys/types.h> #include <sys/bitset.h> diff --git a/freebsd/sbin/pfctl/pfctl_parser.c b/freebsd/sbin/pfctl/pfctl_parser.c index f339d972..9d752d26 100644 --- a/freebsd/sbin/pfctl/pfctl_parser.c +++ b/freebsd/sbin/pfctl/pfctl_parser.c @@ -1351,10 +1351,17 @@ get_socket_domain(void) return (sdom); } +#ifdef __rtems__ +static int pfctl_s = -1; +#endif /* __rtems__ */ int get_query_socket(void) { +#ifndef __rtems__ static int s = -1; +#else /* __rtems__ */ +#define s pfctl_s +#endif /* __rtems__ */ if (s == -1) { if ((s = socket(get_socket_domain(), SOCK_DGRAM, 0)) == -1) @@ -1362,6 +1369,9 @@ get_query_socket(void) } return (s); +#ifdef __rtems__ +#undef s +#endif /* __rtems__ */ } /* diff --git a/freebsd/sbin/pfctl/pfctl_parser.h b/freebsd/sbin/pfctl/pfctl_parser.h index aa6d98d7..7d92b1db 100644 --- a/freebsd/sbin/pfctl/pfctl_parser.h +++ b/freebsd/sbin/pfctl/pfctl_parser.h @@ -178,7 +178,7 @@ struct node_queue_opt { }; #define QPRI_BITSET_SIZE 256 -BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); +__BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); LIST_HEAD(gen_sc, segment); struct pfctl_altq { diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h index 1712b9e6..2f7fb828 100644 --- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h +++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h @@ -35,6 +35,7 @@ #define parseport _bsd_pfctl_parseport #define pfctl_cmdline_symset _bsd_pfctl_pfctl_cmdline_symset #define pfctl_load_anchors _bsd_pfctl_pfctl_load_anchors +#define pfctl_s _bsd_pfctl_s #define pfctlychar _bsd_pfctl_pfctlychar #define pfctlydebug _bsd_pfctl_pfctlydebug #define pfctlyerrflag _bsd_pfctl_pfctlyerrflag diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h index bb8832ac..9bbec579 100644 --- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h +++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h @@ -3,3 +3,4 @@ #include "rtems-bsd-pfctl-data.h" /* pfctl_parser.c */ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct node_host *iftab); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static int pfctl_s); diff --git a/freebsd/sbin/ping/ping.c b/freebsd/sbin/ping/ping.c index e31941b8..fae24e53 100644 --- a/freebsd/sbin/ping/ping.c +++ b/freebsd/sbin/ping/ping.c @@ -307,7 +307,9 @@ main(int argc, char *const *argv) #endif struct sockaddr_in *to; double t; +#ifndef __rtems__ u_long alarmtimeout; +#endif /* __rtems__ */ long ltmp; int almost_done, ch, df, hold, i, icmp_len, mib[4], preload; int ssend_errno, srecv_errno, tos, ttl; @@ -370,7 +372,9 @@ main(int argc, char *const *argv) err(EX_OSERR, "srecv socket"); } +#ifndef __rtems__ alarmtimeout = df = preload = tos = 0; +#endif /* __rtems__ */ outpack = outpackhdr + sizeof(struct ip); while ((ch = getopt(argc, argv, @@ -572,6 +576,7 @@ main(int argc, char *const *argv) mttl = ltmp; options |= F_MTTL; break; +#ifndef __rtems__ case 't': alarmtimeout = strtoul(optarg, &ep, 0); if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) @@ -582,6 +587,7 @@ main(int argc, char *const *argv) optarg, MAXALARM); alarm((int)alarmtimeout); break; +#endif /* __rtems__ */ case 'v': options |= F_VERBOSE; break; @@ -1518,8 +1524,10 @@ static void finish(void) { +#ifndef __rtems__ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGALRM, SIG_IGN); +#endif /* __rtems__ */ (void)putchar('\n'); (void)fflush(stdout); (void)printf("--- %s ping statistics ---\n", hostname); diff --git a/freebsd/sbin/ping6/ping6.c b/freebsd/sbin/ping6/ping6.c index 2d6e5ade..921797d0 100644 --- a/freebsd/sbin/ping6/ping6.c +++ b/freebsd/sbin/ping6/ping6.c @@ -287,7 +287,9 @@ static void fill(char *, char *); static int get_hoplim(struct msghdr *); static int get_pathmtu(struct msghdr *); static struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); +#ifndef __rtems__ static void onsignal(int); +#endif /* __rtems__ */ static void onint(int); static size_t pingerlen(void); static int pinger(void); @@ -347,7 +349,9 @@ main(int argc, char *argv[]) struct timespec last, intvl; struct sockaddr_in6 from, *sin6; struct addrinfo hints, *res; +#ifndef __rtems__ struct sigaction si_sa; +#endif /* __rtems__ */ int cc, i; int almost_done, ch, hold, packlen, preload, optval, error; int nig_oldmcprefix = -1; @@ -374,7 +378,9 @@ main(int argc, char *argv[]) char *policy_out = NULL; #endif double t; +#ifndef __rtems__ u_long alarmtimeout; +#endif /* __rtems__ */ size_t rthlen; #ifdef IPV6_USE_MIN_MTU int mflag = 0; @@ -400,7 +406,9 @@ main(int argc, char *argv[]) intvl.tv_sec = interval / 1000; intvl.tv_nsec = interval % 1000 * 1000000; +#ifndef __rtems__ alarmtimeout = preload = 0; +#endif /* __rtems__ */ datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; capdns = capdns_setup(); #ifndef IPSEC @@ -628,6 +636,7 @@ main(int argc, char *argv[]) options |= F_WAITTIME; waittime = (int)t; break; +#ifndef __rtems__ case 't': alarmtimeout = strtoul(optarg, &e, 0); if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) @@ -638,6 +647,7 @@ main(int argc, char *argv[]) optarg, MAXALARM); alarm((int)alarmtimeout); break; +#endif /* __rtems__ */ #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC case 'P': @@ -1174,6 +1184,7 @@ main(int argc, char *argv[]) } clock_gettime(CLOCK_MONOTONIC, &last); +#ifndef __rtems__ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = onsignal; @@ -1189,6 +1200,7 @@ main(int argc, char *argv[]) if (sigaction(SIGALRM, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGALRM"); } +#endif /* __rtems__ */ if (options & F_FLOOD) { intvl.tv_sec = 0; intvl.tv_nsec = 10000000; @@ -1310,11 +1322,13 @@ main(int argc, char *argv[]) } } } +#ifndef __rtems__ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = SIG_IGN; sigaction(SIGINT, &si_sa, 0); sigaction(SIGALRM, &si_sa, 0); +#endif /* __rtems__ */ summary(); if(packet != NULL) @@ -1323,6 +1337,7 @@ main(int argc, char *argv[]) exit(nreceived == 0 ? 2 : 0); } +#ifndef __rtems__ static void onsignal(int sig) { @@ -1339,6 +1354,7 @@ onsignal(int sig) #endif } } +#endif /* __rtems__ */ /* * pinger -- diff --git a/freebsd/sys/arm/freescale/imx/imx6_ccm.c b/freebsd/sys/arm/freescale/imx/imx6_ccm.c index 78bbd5c1..7fdb69b8 100644 --- a/freebsd/sys/arm/freescale/imx/imx6_ccm.c +++ b/freebsd/sys/arm/freescale/imx/imx6_ccm.c @@ -368,6 +368,7 @@ imx6_ccm_sata_enable(void) return 0; } +#ifndef __rtems__ uint32_t imx_ccm_ecspi_hz(void) { @@ -408,6 +409,7 @@ imx_ccm_ahb_hz(void) { return (132000000); } +#endif /* __rtems__ */ void imx_ccm_ipu_enable(int ipu) diff --git a/freebsd/sys/arm64/include/machine/in_cksum.h b/freebsd/sys/arm64/include/machine/in_cksum.h index d55b838b..522ba005 100644 --- a/freebsd/sys/arm64/include/machine/in_cksum.h +++ b/freebsd/sys/arm64/include/machine/in_cksum.h @@ -1,6 +1,4 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause - * * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * @@ -31,7 +29,6 @@ * from tahoe: in_cksum.c 1.2 86/01/05 * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp - * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg * $FreeBSD$ */ @@ -40,44 +37,16 @@ #include <sys/cdefs.h> +#ifdef _KERNEL #define in_cksum(m, len) in_cksum_skip(m, len, 0) - +u_short in_addword(u_short sum, u_short b); +u_short in_cksum_skip(struct mbuf *m, int len, int skip); +u_int do_cksum(const void *, int); #if defined(IPVERSION) && (IPVERSION == 4) -/* - * It it useful to have an Internet checksum routine which is inlineable - * and optimized specifically for the task of computing IP header checksums - * in the normal case (where there are no options and the header length is - * therefore always exactly five 32-bit words. - */ -#ifdef __CC_SUPPORTS___INLINE - -static __inline void -in_cksum_update(struct ip *ip) -{ - int __tmpsum; - __tmpsum = (int)ntohs(ip->ip_sum) + 256; - ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); -} - -#else - -#define in_cksum_update(ip) \ - do { \ - int __tmpsum; \ - __tmpsum = (int)ntohs(ip->ip_sum) + 256; \ - ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \ - } while(0) - -#endif +u_int in_cksum_hdr(const struct ip *); #endif -#ifdef _KERNEL -#if defined(IPVERSION) && (IPVERSION == 4) -u_int in_cksum_hdr(const struct ip *ip); -#endif -u_short in_addword(u_short sum, u_short b); u_short in_pseudo(u_int sum, u_int b, u_int c); -u_short in_cksum_skip(struct mbuf *m, int len, int skip); -#endif +#endif /* _KERNEL */ #endif /* _MACHINE_IN_CKSUM_H_ */ diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c index 47c0f770..316e077c 100644 --- a/freebsd/sys/dev/ffec/if_ffec.c +++ b/freebsd/sys/dev/ffec/if_ffec.c @@ -139,9 +139,17 @@ static struct ofw_compat_data compat_data[] = { /* * Driver data and defines. The descriptor counts must be a power of two. */ +#ifndef __rtems__ #define RX_DESC_COUNT 512 +#else /* __rtems__ */ +#define RX_DESC_COUNT 64 +#endif /* __rtems__ */ #define RX_DESC_SIZE (sizeof(struct ffec_hwdesc) * RX_DESC_COUNT) +#ifndef __rtems__ #define TX_DESC_COUNT 512 +#else /* __rtems__ */ +#define TX_DESC_COUNT 64 +#endif /* __rtems__ */ #define TX_DESC_SIZE (sizeof(struct ffec_hwdesc) * TX_DESC_COUNT) #define TX_MAX_DMA_SEGS 8 @@ -201,6 +209,11 @@ struct ffec_softc { int rx_ic_count; /* RW, valid values 0..255 */ int tx_ic_time; int tx_ic_count; +#ifdef __rtems__ + + device_t mdio_device; + struct mtx mdio_mtx; +#endif /* __rtems__ */ }; static struct resource_spec irq_res_spec[MAX_IRQ_COUNT + 1] = { @@ -368,6 +381,13 @@ ffec_miibus_readreg(device_t dev, int phy, int reg) int val; sc = device_get_softc(dev); +#ifdef __rtems__ + if (sc->mdio_device) { + return (MIIBUS_READREG(sc->mdio_device, phy, reg)); + } + + mtx_lock(&sc->mdio_mtx); +#endif /* __rtems__ */ WR4(sc, FEC_IER_REG, FEC_IER_MII); @@ -378,11 +398,17 @@ ffec_miibus_readreg(device_t dev, int phy, int reg) if (!ffec_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii read\n"); +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (-1); /* All-ones is a symptom of bad mdio. */ } val = RD4(sc, FEC_MMFR_REG) & FEC_MMFR_DATA_MASK; +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (val); } @@ -392,6 +418,13 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val) struct ffec_softc *sc; sc = device_get_softc(dev); +#ifdef __rtems__ + if (sc->mdio_device) { + return (MIIBUS_WRITEREG(sc->mdio_device, phy, reg, val)); + } + + mtx_lock(&sc->mdio_mtx); +#endif /* __rtems__ */ WR4(sc, FEC_IER_REG, FEC_IER_MII); @@ -403,9 +436,15 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val) if (!ffec_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii write\n"); +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (-1); } +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (0); } @@ -1569,6 +1608,9 @@ ffec_detach(device_t dev) if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); +#ifdef __rtems__ + mtx_destroy(&sc->mtx); +#endif /* __rtems__ */ FFEC_LOCK_DESTROY(sc); return (0); } @@ -1718,6 +1760,80 @@ ffec_set_txic(struct ffec_softc *sc) ffec_set_ic(sc, FEC_TXIC0_REG, sc->tx_ic_count, sc->tx_ic_time); } +#ifdef __rtems__ +int +ffec_get_phy_information( + struct ffec_softc *sc, + phandle_t node, + device_t dev, + int *phy_addr +) +{ + phandle_t phy_node; + phandle_t parent_node; + pcell_t phy_handle, phy_reg; + device_t other; + phandle_t xref; + + /* Search for the phy-handle and get the address */ + + if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, + sizeof(phy_handle)) <= 0) + return (ENXIO); + + phy_node = OF_node_from_xref(phy_handle); + + if (OF_getencprop(phy_node, "reg", (void *)&phy_reg, + sizeof(phy_reg)) <= 0) + return (ENXIO); + + *phy_addr = phy_reg; + + /* Detect whether PHY handle is connected to this or another FFEC. */ + parent_node = phy_node; + + while (parent_node != 0) { + if (parent_node == node) { + /* PHY is directly connected. That's easy. */ + sc->mdio_device = NULL; + return 0; + } + + /* + * Check whether the node is also an Ethernet controller. Do + * that by just assuming that every Ethernet controller has a + * PHY attached to it. + */ + if (OF_getencprop(parent_node, "phy-handle", + (void *)&phy_handle, sizeof(phy_handle)) > 0) { + /* + * Try to find the device of the other Ethernet + * controller and use that for MDIO communication. + * Note: This is not really a nice workaround but it + * works. + */ + xref = OF_xref_from_node(parent_node); + if (xref == 0) { + device_printf(dev, + "Couldn't get device that handles PHY\n"); + return (ENXIO); + } + other = OF_device_from_xref(xref); + if (other == 0) { + device_printf(dev, + "Couldn't get device that handles PHY\n"); + return (ENXIO); + } + sc->mdio_device = other; + return 0; + } + + parent_node = OF_parent(parent_node); + } + return (ENXIO); +} + +#endif /* __rtems__ */ static int ffec_attach(device_t dev) { @@ -1735,6 +1851,10 @@ ffec_attach(device_t dev) sc->dev = dev; FFEC_LOCK_INIT(sc); +#ifdef __rtems__ + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), + MTX_NETWORK_LOCK, MTX_DEF); +#endif /* __rtems__ */ /* * There are differences in the implementation and features of the FEC @@ -2039,9 +2159,17 @@ ffec_attach(device_t dev) ffec_miigasket_setup(sc); /* Attach the mii driver. */ +#ifdef __rtems__ + OF_device_register_xref(OF_xref_from_node(ofw_node), dev); + if (ffec_get_phy_information(sc, ofw_node, dev, &phynum) != 0) { + phynum = MII_PHY_ANY; + } + (void) dummy; +#else /* __rtems__ */ if (fdt_get_phyaddr(ofw_node, dev, &phynum, &dummy) != 0) { phynum = MII_PHY_ANY; } +#endif /* __rtems__ */ error = mii_attach(dev, &sc->miibus, ifp, ffec_media_change, ffec_media_status, BMSR_DEFCAPMASK, phynum, MII_OFFSET_ANY, (sc->fecflags & FECTYPE_MVF) ? MIIF_FORCEANEG : 0); diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c index 11cf945f..ff517abc 100644 --- a/freebsd/sys/dev/mmc/mmcsd.c +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -546,6 +546,11 @@ mmcsd_attach(device_t dev) */ rev = ext_csd[EXT_CSD_REV]; +/* + * Cache flush functions are currently not available. Use of on-device cache can + * cause data loss. + */ +#ifndef __rtems__ /* * With revision 1.5 (MMC v4.5, EXT_CSD_REV == 6) and later, take * advantage of the device R/W cache if present and useage is not @@ -567,6 +572,7 @@ mmcsd_attach(device_t dev) sc->flags |= MMCSD_FLUSH_CACHE; } } +#endif /* * Ignore user-creatable enhanced user data area and general purpose diff --git a/freebsd/sys/sys/_domainset.h b/freebsd/sys/sys/_domainset.h index 5685d532..443c68fd 100644 --- a/freebsd/sys/sys/_domainset.h +++ b/freebsd/sys/sys/_domainset.h @@ -43,7 +43,7 @@ #define DOMAINSET_SETSIZE DOMAINSET_MAXSIZE #endif -BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); +__BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); typedef struct _domainset domainset_t; /* diff --git a/freebsd/sys/sys/domainset.h b/freebsd/sys/sys/domainset.h index 5a00347f..e028f3e9 100644 --- a/freebsd/sys/sys/domainset.h +++ b/freebsd/sys/sys/domainset.h @@ -44,34 +44,34 @@ sizeof(__XSTRING(MAXMEMDOM))) -#define DOMAINSET_CLR(n, p) BIT_CLR(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_COPY(f, t) BIT_COPY(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_ISSET(n, p) BIT_ISSET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET(n, p) BIT_SET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_ZERO(p) BIT_ZERO(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FILL(p) BIT_FILL(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SETOF(n, p) BIT_SETOF(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_EMPTY(p) BIT_EMPTY(DOMAINSET_SETSIZE, p) -#define DOMAINSET_ISFULLSET(p) BIT_ISFULLSET(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SUBSET(p, c) BIT_SUBSET(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OVERLAP(p, c) BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_CMP(p, c) BIT_CMP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OR(d, s) BIT_OR(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_AND(d, s) BIT_AND(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_NAND(d, s) BIT_NAND(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET_ATOMIC(n, p) BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_CLR(n, p) __BIT_CLR(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_COPY(f, t) __BIT_COPY(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_ISSET(n, p) __BIT_ISSET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET(n, p) __BIT_SET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_ZERO(p) __BIT_ZERO(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FILL(p) __BIT_FILL(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SETOF(n, p) __BIT_SETOF(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_EMPTY(p) __BIT_EMPTY(DOMAINSET_SETSIZE, p) +#define DOMAINSET_ISFULLSET(p) __BIT_ISFULLSET(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SUBSET(p, c) __BIT_SUBSET(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OVERLAP(p, c) __BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_CMP(p, c) __BIT_CMP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OR(d, s) __BIT_OR(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_AND(d, s) __BIT_AND(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_NAND(d, s) __BIT_NAND(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) #define DOMAINSET_SET_ATOMIC_ACQ(n, p) \ - BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_AND_ATOMIC(n, p) BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_OR_ATOMIC(d, s) BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) + __BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) #define DOMAINSET_COPY_STORE_REL(f, t) \ - BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_FFS(p) BIT_FFS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FLS(p) BIT_FLS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_COUNT(p) BIT_COUNT(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FSET BITSET_FSET(_NDOMAINSETWORDS) -#define DOMAINSET_T_INITIALIZER BITSET_T_INITIALIZER + __BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_FFS(p) __BIT_FFS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FLS(p) __BIT_FLS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_COUNT(p) __BIT_COUNT(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FSET __BITSET_FSET(_NDOMAINSETWORDS) +#define DOMAINSET_T_INITIALIZER __BITSET_T_INITIALIZER #define DOMAINSET_POLICY_INVALID 0 #define DOMAINSET_POLICY_ROUNDROBIN 1 diff --git a/freebsd/tools/tools/net80211/wlanstats/main.c b/freebsd/tools/tools/net80211/wlanstats/main.c index 3a6fd204..ef2caf56 100644 --- a/freebsd/tools/tools/net80211/wlanstats/main.c +++ b/freebsd/tools/tools/net80211/wlanstats/main.c @@ -43,6 +43,7 @@ #ifdef __rtems__ #define __need_getopt_newlib #include <getopt.h> +#include <string.h> #include <machine/rtems-bsd-program.h> #include <machine/rtems-bsd-commands.h> #endif /* __rtems__ */ @@ -96,6 +97,7 @@ getfmt(const char *tag) return tag; } +#ifndef __rtems__ static int signalled; static void @@ -103,6 +105,7 @@ catchalarm(int signo __unused) { signalled = 1; } +#endif /* __rtems__ */ #if 0 static void @@ -262,6 +265,7 @@ main(int argc, char *argv[]) wf->setstamac(wf, mac); if (argc > 0) { +#ifndef __rtems__ u_long interval = strtoul(argv[0], NULL, 0); int line, omask; @@ -283,24 +287,10 @@ main(int argc, char *argv[]) wf->print_total(wf, stdout); } fflush(stdout); -#ifndef __rtems__ omask = sigblock(sigmask(SIGALRM)); if (!signalled) sigpause(0); sigsetmask(omask); -#else /* __rtems__ */ - { - sigset_t oldmask, desired, empty; - - sigemptyset(&empty); - sigemptyset(&desired); - sigaddset(&desired, SIGALRM); - sigprocmask(SIG_BLOCK, &desired, &oldmask); - while (!signalled) - sigsuspend(&desired); - sigprocmask(SIG_SETMASK, &oldmask, NULL); - } -#endif /* __rtems__ */ signalled = 0; alarm(interval); line++; @@ -346,6 +336,10 @@ main(int argc, char *argv[]) } while (len >= sizeof(struct ieee80211req_sta_info)); } #endif +#else /* __rtems__ */ + (void)mode; + printf("wlanstats: not implemented\n"); +#endif /* __rtems__ */ } else { wf->collect_tot(wf); wf->print_verbose(wf, stdout); diff --git a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h index f88c5834..afb145be 100644 --- a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h +++ b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h @@ -1,5 +1,3 @@ /* generated by userspace-header-gen.py */ #include <rtems/linkersets.h> #include "rtems-bsd-wlanstats-data.h" -/* main.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_wlanstats, static int signalled); diff --git a/freebsd/usr.bin/netstat/if.c b/freebsd/usr.bin/netstat/if.c index c578629e..ffb639c6 100644 --- a/freebsd/usr.bin/netstat/if.c +++ b/freebsd/usr.bin/netstat/if.c @@ -512,6 +512,7 @@ intpr(void (*pfunc)(char *), int af) freeifmaddrs(ifmap); } +#ifndef __rtems__ struct iftot { u_long ift_ip; /* input packets */ u_long ift_ie; /* input errors */ @@ -575,6 +576,7 @@ catchalarm(int signo __unused) { signalled = true; } +#endif /* __rtems__ */ /* * Print a running summary of interface statistics. @@ -585,6 +587,7 @@ catchalarm(int signo __unused) static void sidewaysintpr(void) { +#ifndef __rtems__ struct iftot ift[2], *new, *old; struct itimerval interval_it; int oldmask, line; @@ -619,26 +622,11 @@ loop: xo_close_list("interface-statistics"); return; } -#ifdef __rtems__ - { - sigset_t oldmask, desired, empty; - - sigemptyset(&empty); - sigemptyset(&desired); - sigaddset(&desired, SIGALRM); - sigprocmask(SIG_BLOCK, &desired, &oldmask); - while (!signalled) - sigsuspend(&desired); - signalled = false; - sigprocmask(SIG_SETMASK, &oldmask, NULL); - } -#else /* __rtems__ */ oldmask = sigblock(sigmask(SIGALRM)); while (!signalled) sigpause(0); signalled = false; sigsetmask(oldmask); -#endif /* __rtems__ */ line++; fill_iftot(new); @@ -681,4 +669,5 @@ loop: goto loop; /* NOTREACHED */ +#endif /* __rtems__ */ } diff --git a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h index 9e12b65e..9a685f33 100644 --- a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h +++ b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h @@ -1,5 +1,3 @@ /* generated by userspace-header-gen.py */ #include <rtems/linkersets.h> #include "rtems-bsd-netstat-data.h" -/* if.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_netstat, static sig_atomic_t signalled); diff --git a/ipsec-tools/src/libipsec/pfkey.c b/ipsec-tools/src/libipsec/pfkey.c index 385a21a9..cc6ad816 100644 --- a/ipsec-tools/src/libipsec/pfkey.c +++ b/ipsec-tools/src/libipsec/pfkey.c @@ -1836,8 +1836,18 @@ pfkey_open(void) (void)setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsiz_wanted, sizeof(bufsiz_wanted)); +#ifndef __rtems__ /* Try to have have at least 2MB. If we have more, do not lower it. */ bufsiz_wanted = 2 * 1024 * 1024; +#else /* __rtems__ */ + /* + * The bufsize_wanted has an influence on the maximum number of SPDs. We + * don't really need that much of them on an embedded system. If some + * application really needs it, this can be overwritten with the + * pfkey_buffer option in the config file. + */ + bufsiz_wanted = 128 * 1024; +#endif /* __rtems__ */ len = sizeof(bufsiz_current); ret = getsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsiz_current, &len); diff --git a/ipsec-tools/src/racoon/privsep.c b/ipsec-tools/src/racoon/privsep.c index 8efdae84..914d8a44 100644 --- a/ipsec-tools/src/racoon/privsep.c +++ b/ipsec-tools/src/racoon/privsep.c @@ -80,7 +80,9 @@ static int privsep_sock[2] = { -1, -1 }; static int privsep_recv(int, struct privsep_com_msg **, size_t *); static int privsep_send(int, struct privsep_com_msg *, size_t); static int safety_check(struct privsep_com_msg *, int i); +#ifndef __rtems__ static int port_check(int); +#endif /* __rtems__ */ static int unsafe_env(char *const *); static int unknown_name(int); static int unsafe_path(char *, int); @@ -321,7 +323,6 @@ privsep_init(void) #if defined(__NetBSD__) || defined(__FreeBSD__) setproctitle("[priv]"); #endif -#endif /* __rtems__ */ /* * Don't catch any signal @@ -334,13 +335,16 @@ privsep_init(void) signal(SIGUSR1, SIG_DFL); signal(SIGUSR2, SIG_DFL); signal(SIGCHLD, SIG_DFL); +#endif /* __rtems__ */ while (1) { size_t len; struct privsep_com_msg *combuf; struct privsep_com_msg *reply; char *data; +#ifndef __rtems__ size_t *buflen; +#endif /* __rtems__ */ size_t totallen; char *bufs[PRIVSEP_NBUF_MAX]; int i; @@ -1067,7 +1071,9 @@ privsep_getpsk(str, keylen) vchar_t *psk; struct privsep_com_msg *msg; size_t len; +#ifndef __rtems__ int *keylenp; +#endif /* __rtems__ */ char *data; if (geteuid() == 0) @@ -1129,7 +1135,11 @@ privsep_socket(domain, type, protocol) size_t len; char *data; struct socket_args socket_args; +#ifndef __rtems__ int s, saved_errno = 0; +#else /* __rtems__ */ + int s; +#endif /* __rtems__ */ if (geteuid() == 0) return socket(domain, type, protocol); diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h index 7f1e9247..6867269e 100644 --- a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h +++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h @@ -56,7 +56,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern struct dhgroup dh_modp8192); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern char *pname); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int f_foreground); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int print_location); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern u_int32_t loglevel); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern __uint32_t loglevel); /* policy.c */ /* privsep.c */ /* proposal.c */ diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h index cdf8a74d..51b67d2e 100644 --- a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h +++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h @@ -5,8 +5,5 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_active_mask); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_preset_mask); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int nfds); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int signals[]); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static sig_atomic_t volatile volatile sigreq[]); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor *allocated_fd_monitors); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor_list fd_monitor_tree[]); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct sched scflushsa); diff --git a/ipsec-tools/src/racoon/session.c b/ipsec-tools/src/racoon/session.c index 90120c76..bd2bd316 100644 --- a/ipsec-tools/src/racoon/session.c +++ b/ipsec-tools/src/racoon/session.c @@ -119,6 +119,7 @@ struct fd_monitor { #define NUM_PRIORITIES 2 +#ifndef __rtems__ static void close_session __P((void)); static void initfds __P((void)); static void init_signal __P((void)); @@ -126,6 +127,7 @@ static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int)))); static void check_sigreq __P((void)); static void check_flushsa __P((void)); static int close_sockets __P((void)); +#endif /* __rtems__ */ #ifndef __rtems__ static fd_set preset_mask, active_mask; @@ -140,8 +142,10 @@ static struct fd_monitor *allocated_fd_monitors; static TAILQ_HEAD(fd_monitor_list, fd_monitor) fd_monitor_tree[NUM_PRIORITIES]; static int nfds = 0; +#ifndef __rtems__ static volatile sig_atomic_t sigreq[NSIG + 1]; static struct sched scflushsa = SCHED_INITIALIZER(); +#endif /* __rtems__ */ void monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority) @@ -199,9 +203,11 @@ session(void) { struct timeval *timeout; int error; +#ifndef __rtems__ char pid_file[MAXPATHLEN]; FILE *fp; pid_t racoon_pid = 0; +#endif /* __rtems__ */ int i, count; struct fd_monitor *fdm; @@ -209,6 +215,8 @@ session(void) #ifndef __rtems__ FD_ZERO(&preset_mask); #else /* __rtems__ */ + size_t allocated_mask_size = sizeof(fd_set) * + howmany(rtems_libio_number_iops, sizeof(fd_set) * 8); allocated_preset_mask = calloc(sizeof(fd_set), howmany(rtems_libio_number_iops, sizeof(fd_set) * 8)); if (allocated_preset_mask == NULL) @@ -228,7 +236,9 @@ session(void) /* initialize schedular */ sched_init(); +#ifndef __rtems__ init_signal(); +#endif /* __rtems__ */ if (pfkey_init() < 0) errx(1, "failed to initialize pfkey socket"); @@ -325,24 +335,31 @@ session(void) racoon_pid = getpid(); fprintf(fp, "%ld\n", (long)racoon_pid); fclose(fp); -#endif /* __rtems__ */ for (i = 0; i <= NSIG; i++) sigreq[i] = 0; +#endif /* __rtems__ */ while (1) { +#ifndef __rtems__ /* * asynchronous requests via signal. * make sure to reset sigreq to 0. */ check_sigreq(); +#endif /* __rtems__ */ /* scheduling */ timeout = schedular(); /* schedular can change select() mask, so we reset * the working copy here */ +#ifndef __rtems__ active_mask = preset_mask; +#else /* __rtems__ */ + memcpy(allocated_active_mask, allocated_preset_mask, + allocated_mask_size); +#endif /* __rtems__ */ error = select(nfds + 1, &active_mask, NULL, NULL, timeout); if (error < 0) { @@ -379,6 +396,7 @@ session(void) } } +#ifndef __rtems__ /* clear all status and exit program. */ static void close_session() @@ -391,11 +409,6 @@ close_session() flushsainfo(); close_sockets(); backupsa_clean(); -#ifdef __rtems__ - free(allocated_preset_mask); allocated_preset_mask = NULL; - free(allocated_active_mask); allocated_active_mask = NULL; - free(allocated_fd_monitors); allocated_fd_monitors = NULL; -#endif /* __rtems__ */ plog(LLV_INFO, LOCATION, NULL, "racoon process %d shutdown\n", getpid()); @@ -565,11 +578,7 @@ set_signal(sig, func) memset((caddr_t)&sa, 0, sizeof(sa)); sa.sa_handler = func; -#ifndef __rtems__ sa.sa_flags = SA_RESTART; -#else /* __rtems__ */ - sa.sa_flags = 0; -#endif /* __rtems__ */ if (sigemptyset(&sa.sa_mask) < 0) return -1; @@ -590,6 +599,7 @@ close_sockets() #endif return 0; } +#endif /* __rtems__ */ #ifdef __rtems__ #include "rtems-bsd-racoon-session-data.h" @@ -553,7 +553,7 @@ class base(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'COPYRIGHT' ] @@ -891,6 +891,24 @@ class pinmux(builder.Module): ) # +# MV643XX Ethernet driver +# +class if_mve(builder.Module): + + def __init__(self, manager): + super(if_mve, self).__init__(manager, type(self).__name__) + + def generate(self): + mm = self.manager + self.addRTEMSKernelSourceFiles( + [ + 'sys/dev/mve/if_mve.c', + 'sys/dev/mve/if_mve_nexus.c', + ], + mm.generator['source']() + ) + +# # USB # class dev_usb(builder.Module): @@ -1714,7 +1732,7 @@ class dev_nic_e1000(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'sys/dev/e1000/LICENSE' ] @@ -2594,7 +2612,7 @@ class opencrypto(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'sys/contrib/libsodium/LICENSE' ] @@ -3250,7 +3268,7 @@ class user_space(builder.Module): ], mm.generator['source'](['-DINET']) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/libxo/LICENSE' ] @@ -4257,7 +4275,7 @@ class crypto_openssl(builder.Module): mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](), mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](), mm.generator['buildSystemComposer']())) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'crypto/openssl/LICENSE' ] @@ -4395,7 +4413,7 @@ class contrib_expat(builder.Module): ], mm.generator['source'](cflags) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/expat/COPYING' ] @@ -4500,7 +4518,7 @@ class contrib_libpcap(builder.Module): ], mm.generator['source'](cflags) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/libpcap/LICENSE' ] @@ -4745,7 +4763,7 @@ class usr_sbin_tcpdump(builder.Module): ['freebsd/contrib/tcpdump', 'freebsd/usr.sbin/tcpdump/tcpdump']) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/tcpdump/LICENSE' ] @@ -5064,7 +5082,7 @@ class usr_sbin_wpa_supplicant(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/wpa/COPYING' ] @@ -5316,6 +5334,12 @@ class imx(builder.Module): ], mm.generator['source']() ) + self.addRTEMSKernelSourceFiles( + [ + 'sys/arm/freescale/imx/imxrt1166_usbphy.c', + ], + mm.generator['source']() + ) class regulator(builder.Module): def __init__(self, manager): @@ -5407,7 +5431,7 @@ class tests(builder.Module): self.addTest(mm.generator['test']('smp01', ['test_main'], extraLibs = ['rtemstest'])) self.addTest(mm.generator['test']('media01', ['test_main', 'pattern-test'], runTest = False, - extraLibs = ['ftpd', 'telnetd'])) + extraLibs = ['tftpfs', 'ftpd', 'telnetd'])) self.addTest(mm.generator['test']('mcast01', ['test_main'])) self.addTest(mm.generator['test']('vlan01', ['test_main'], netTest = True)) self.addTest(mm.generator['test']('lagg01', ['test_main'], netTest = True)) @@ -5449,6 +5473,7 @@ class tests(builder.Module): self.addTest(mm.generator['test']('ipsec01', ['test_main'])) self.addTest(mm.generator['test']('openssl01', ['test_main'])) self.addTest(mm.generator['test']('openssl02', ['test_main'])) + self.addTest(mm.generator['test']('tcpdump01', ['test_main'])) def load(mm): @@ -5466,6 +5491,7 @@ def load(mm): mm.addModule(evdev(mm)) mm.addModule(iic(mm)) mm.addModule(pinmux(mm)) + mm.addModule(if_mve(mm)) mm.addModule(display(mm)) mm.addModule(dev_usb(mm)) diff --git a/libbsd.txt b/libbsd.txt deleted file mode 100644 index 3d665500..00000000 --- a/libbsd.txt +++ /dev/null @@ -1,1331 +0,0 @@ -RTEMS BSD Library Guide -======================= -:toc: -:icons: -:numbered: -:website: http://www.rtems.org/ - -The libbsd makes FreeBSD subsystems like TCP/IP, USB, SD and some more usable -for RTEMS. It tries to follow the FreeBSD development as close as possible and -therefore is updated to the latest FreeBSD HEAD revision from time to time. -To find out which version of FreeBSD is currently used as the base version for -libbsd please take a look at the -https://git.rtems.org/rtems-libbsd/log/freebsd-org[freebsd-org] submodule. - -This is a guide which captures information on the -process of merging code from FreeBSD, building this library, -RTEMS specific support files, and general guidelines on what -modifications to the FreeBSD source are permitted. - -Goals of this effort are - -* update TCP/IP and provide USB in RTEMS, -* ease updating to future FreeBSD versions, -* ease tracking changes in FreeBSD code, -* minimize manual changes in FreeBSD code, and -* define stable kernel/device driver API which is implemented -by both RTEMS and FreeBSD. This is the foundation of the port. - -We will work to push our changes upstream to the FreeBSD Project -and minimize changes required at each update point. - -******************************************************************************* -This is a work in progress and is very likely to be incomplete. -Please help by adding to it. -******************************************************************************* - -== Getting Started - -=== Tool Chain === - -You need a tool chain for RTEMS based on the latest RTEMS Source Builder (RSB). - -=== Installation Overview === - -. You must configure your BSP with the +--disable-networking+ option to disable -the old network stack. Make sure no header files of the old network stack are -installed. - -. Clone the Git repository +git clone git://git.rtems.org/rtems-libbsd.git+. -. Change into the RTEMS BSD library root directory. -. If you want to run tests with a custom IP configuration instead of the default - one you can use an adjusted `config.inc` configuration file. -. Run +waf configure ...+. -. Run +waf+. -. Run +waf install+. - -Refer to the README.waf for Waf building instructions. - -Make sure the submodules have been initialised and are updated. If a 'git -status' says `rtems_waf` need updating run the submodule update command: - - $ git submodule sync - $ git submodule rtems_waf update - -=== Board Support Package Requirements === - -You need the latest RTEMS version to build the libbsd master. The Board -Support Package (BSP) must support the -http://www.rtems.org/onlinedocs/doxygen/cpukit/html/group\__rtems\__interrupt__extension.html[Interrupt Manager Extension] -// The first underscores have to be masked to stop asciidoc interpreting them -to make use of generic FreeBSD based drivers. - -=== Board Support Package Configuration and Build === - -You need to configure RTEMS for the desired BSP and install it. The BSP must -be configured with a disabled network stack. The BSD library containing the -new network stack is a separate package. Using a BSP installation containing -the old network stack may lead to confusion and unpredictable results. - -The following script is used to build the `arm/xilinx_zynq_a9_qemu` BSP for -our internal testing purposes: - -------------------------------------------------------------------------------- -#!/bin/sh - -cd ${HOME}/sandbox -rm -rf b-xilinx_zynq_a9_qemu -mkdir b-xilinx_zynq_a9_qemu -cd b-xilinx_zynq_a9_qemu -${HOME}/git-rtems/configure \ - --prefix=${HOME}/sandbox/install \ - --target=arm-rtems5 \ - --enable-rtemsbsp=xilinx_zynq_a9_qemu \ - --disable-networking && \ - make && \ - make install -------------------------------------------------------------------------------- - -The `arm/xilinx_zynq_a9_qemu` BSP running on the Qemu simulator has some -benefits for development and test of the BSD library - -* it offers a NULL pointer read and write protection, -* Qemu is a fast simulator, -* Qemu provides support for GDB watchpoints, -* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge -devices (you can run multiple test instances on one virtual network). - -=== BSD Library Configuration and Build === - -The build system based on the Waf build system. To build with Waf please refer -to the README.waf file. - -Note that the libbsd supports different buildsets. These can be selected with -the `--buildset=xxx.ini` option during the configure phase. Take a look at the -comments in `buildset/*.ini` to see which build sets are officially supported. - -You can also create and provide your own buildset configuration. But remember -that it's quite easy to break something by disabling the wrong modules. Only the -configurations in the `buildset` directory are officially maintained. - -===== Example Configuration for Network Tests ===== - -If you need some other IP configuration for the network tests that use a fixed -IP config you can copy `config.inc` to a location outside to the source tree and -adapt it. Then use the option `--net-test-config=NET_CONFIG` to pass the file to -waf's configure command. - -------------------------------------------------------------------------------- -NET_CFG_SELF_IP = 10.0.0.2 -NET_CFG_NETMASK = 255.255.0.0 -NET_CFG_PEER_IP = 10.0.0.1 -NET_CFG_GATEWAY_IP = 10.0.0.1 -------------------------------------------------------------------------------- - -=== BSD Library Initialization === - -To initialise the BSD Library create a suitable rc.conf file. The FreeBSD man -page rc.conf(5) provides the details needed to create a suitable format file: - - https://www.freebsd.org/cgi/man.cgi?rc.conf - -You can call one of three functions to run the initialisation once BSD has -initialised: - - - rtems_bsd_run_etc_rc_conf: Run /etc/rc.conf. - - rtems_bsd_run_rc_conf: Run a user supplied file. - - rtems_bsd_run_rc_conf_script: Run the in memory line feed separated text string. - -For exapmle: - - void - network_init(void) - { - rtems_status_code sc; - - sc = rtems_bsd_initialize(); - assert(sc == RTEMS_SUCCESSFUL); - - rtems_bsd_run_etc_rc_conf(true); /* verbose = true */ - -} - -By default the networking support is builtin. Other directives can be added and -are found in 'machine/rtems-bsd-rc-conf-directives.h'. Please check the file -for the list. - -The following network names are supported: - - cloned_interfaces - ifconfig_'interface' - defaultrouter - hostname - -For example: - - # - # My BSD initialisation. - # - hostname="myhost" - cloned_interfaces="vlan0 vlan1" - ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0" - fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0" - defaultrouter="10.10.10.1" - -You can also intialise the BSD library using code. The following code to -initialize the BSD library: - -------------------------------------------------------------------------------- -#include <assert.h> -#include <sysexits.h> - -#include <rtems/bsd/bsd.h> - -void -network_init(void) -{ - rtems_status_code sc; - int exit_code; - - sc = rtems_bsd_initialize(); - assert(sc == RTEMS_SUCCESSFUL); - - exit_code = rtems_bsd_ifconfig_lo0(); - assert(exit_code == EX_OK); -} -------------------------------------------------------------------------------- - -This performs the basic network stack initialization with a loopback interface. -Further initialization must be done using the standard BSD network -configuration commands -http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)] -using `rtems_bsd_command_ifconfig()` and -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)] -using `rtems_bsd_command_route()`. For an example please have a look at -`testsuite/include/rtems/bsd/test/default-network-init.h`. - -=== Task Priorities and Stack Size === - -The default task priority is 96 for the interrupt server task (name "IRQS"), 98 -for the timer server task (name "TIME") and 100 for all other tasks. The -application may provide their own implementation of the -`rtems_bsd_get_task_priority()` function (for example in the module which calls -`rtems_bsd_initialize()`) if different values are desired. - -The task stack size is determined by the `rtems_bsd_get_task_stack_size()` -function which may be provided by the application in case the default is not -appropriate. - -=== Size for Allocator Domains === - -The size for an allocator domain can be specified via the -`rtems_bsd_get_allocator_domain_size()` function. The application may provide -their own implementation of the `rtems_bsd_get_allocator_domain_size()` -function (for example in the module which calls `rtems_bsd_initialize()`) if -different values are desired. The default size is 8MiB for all domains. - -=== Redirecting or Disabling the Output === - -A lot of system messages are printed to the stdout by default. If you want to -redirect them you can overwrite the default print handler. That can even be done -before the libbsd initialization to catch all messages. An example would look -like follows: - -------------------------------------------------------------------------------- -int my_vprintf_handler(int level, const char *fmt, va_list ap) { - /* Do something with the messages. */ - - return number_of_printed_chars; -} - -... - /* In your initialization: */ - rtems_bsd_vprintf_handler old; - old = rtems_bsd_set_vprintf_handler(my_vprintf_handler); -... -------------------------------------------------------------------------------- - -As a special case, you can set the `rtems_bsd_vprintf_handler_mute(...)` -provided by libbsd to suppress all output. - -== Network Stack Features - -http://roy.marples.name/projects/dhcpcd/index[DHCPCD(8)]:: DHCP client - -https://developer.apple.com/library/mac/documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html[dns_sd.h]:: DNS Service Discovery - -http://www.opensource.apple.com/source/mDNSResponder/mDNSResponder-320.10/mDNSCore/mDNSEmbeddedAPI.h[mDNS]:: Multi-Cast DNS - -http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4[UNIX(4)]:: UNIX-domain protocol family - -http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4[INET(4)]:: Internet protocol family - -http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4[INET6(4)]:: Internet protocol version 6 family - -http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4[TCP(4)]:: Internet Transmission Control Protocol - -http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4[UDP(4)]:: Internet User Datagram Protocol - -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4[ROUTE(4)]:: Kernel packet forwarding database - -http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4[BPF(4)]:: Berkeley Packet Filter - -http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2[SOCKET(2)]:: Create an endpoint for communication - -http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2[KQUEUE(2)]:: Kernel event notification mechanism - -http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2[SELECT(2)]:: Synchronous I/O multiplexing - -http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2[POLL(2)]:: Synchronous I/O multiplexing - -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)]:: Manually manipulate the routing tables - -http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)]:: Configure network interface parameters - -http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1[NETSTAT(1)]:: Show network status - -http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8[PING(8)]:: Send ICMP ECHO_REQUEST packets to network hosts - -http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8[PING6(8)]:: Send ICMPv6 ECHO_REQUEST packets to network hosts - -http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3[SYSCTL(3)]:: Get or set system information - -http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3[RESOLVER(3)]:: Resolver routines - -http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3[GETHOSTBYNAME(3)]:: Get network host entry - -== Network Interface Drivers - -=== Link Up/Down Events - -You can notifiy the application space of link up/down events in your network -interface driver via the if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN) -function. The DHCPCD(8) client is a consumer of these events for example. -Make sure that the interface flag IFF_UP and the interface driver flag -IFF_DRV_RUNNING is set in case the link is up, otherwise ether_output() will -return the error status ENETDOWN. - -== Shell Commands - -=== HOSTNAME(1) - -In addition to the standard options the RTEMS version of the HOSTNAME(1) -command supports the -m flag to set/get the multicast hostname of the -mDNS resolver instance. See also rtems_mdns_sethostname() and -rtems_mdns_gethostname(). - -== Qemu - -Use the following script to set up a virtual network with three tap devices -connected via one bridge device. - -------------------------------------------------------------------------------- -#!/bin/sh -x - -user=`whoami` -interfaces=(1 2 3) - -tap=qtap -bri=qbri - -case $1 in - up) - sudo -i brctl addbr $bri - for i in ${interfaces[@]} ; do - sudo -i tunctl -t $tap$i -u $user ; - sudo -i ifconfig $tap$i up ; - sudo -i brctl addif $bri $tap$i ; - done - sudo -i ifconfig $bri up - ;; - down) - for i in ${interfaces[@]} ; do - sudo -i ifconfig $tap$i down ; - sudo -i tunctl -d $tap$i ; - done - sudo -i ifconfig $bri down - sudo -i brctl delbr $bri - ;; -esac -------------------------------------------------------------------------------- - -Connect your Qemu instance to one of the tap devices, e.g. - -------------------------------------------------------------------------------- -qemu-system-i386 -m 512 -boot a -cpu pentium3 \ - -drive file=$HOME/qemu/pc386_fda,index=0,if=floppy,format=raw \ - -drive file=fat:$HOME/qemu/hd,format=raw \ - -net nic,model=e1000,macaddr=0e:b0:ba:5e:ba:11 \ - -net tap,ifname=qtap1,script=no,downscript=no \ - -nodefaults -nographic -serial stdio -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -qemu-system-arm \ - -serial null \ - -serial mon:stdio \ - -nographic \ - -M xilinx-zynq-a9 \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \ - -net tap,ifname=qtap1,script=no,downscript=no \ - -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/media01.exe -------------------------------------------------------------------------------- - -Make sure that each Qemu instance uses its own MAC address to avoid an address -conflict (or otherwise use it as a test). - -To connect the Qemu instances with your local network use the following -(replace 'eth0' with the network interface of your host). - -------------------------------------------------------------------------------- -ifconfig eth0 0.0.0.0 -brctl addif qbri eth0 -dhclient qbri -------------------------------------------------------------------------------- - -=== VDE and QEMU - -On FreeBSD you can create VDE or the Virtual Distributed Ethernet to create a -network environment that does not need to run qemu as root or needing to drop -the tap's privileges to run qemu. - -VDE creates a software switch with a default of 32 ports which means a single -kernel tap can support 32 qemu networking sessions. - -To use VDE you need to build qemu with VDE support. The RSB can detect a VDE -plug and enable VDE support in qemu when building. On FreeBSD install the VDE -support with: - - # pkg install -u vde2 - -Build qemu with the RSB. - -To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD -add to your /etc/rc.conf: - - cloned_interfaces="bridge0 tap0" - autobridge_interfaces="bridge0" - autobridge_bridge0="re0 tap0" - ifconfig_re0="up" - ifconfig_tap0="up" - ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0" - defaultrouter="10.10.1.1" - -Start the VDE switch as root: - - # sysctl net.link.tap.user_open=1 - # sysctl net.link.tap.up_on_open=1 - # vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660 - # chmod 660 /dev/tap0 - -You can connect to the VDE switch's management channel using: - - $ vdeterm /tmp/mgmt1 - -To run qemu: - - $ qemu-system-arm \ - -serial null \ - -serial mon:stdio \ - -nographic \ - -M xilinx-zynq-a9 \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \ - -net vde,id=vde0,sock=/tmp/vde1 - -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/rcconf02.exe - -== Issues and TODO - -* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map(). - -* Priority queues are broken with clustered scheduling. - -* Per-CPU data should be enabled once the new stack is ready for SMP. - -* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP. - -* Multiple routing tables are not supported. Every FIB value is set to zero - (= BSD_DEFAULT_FIB). - -* Process identifiers are not supported. Every PID value is set to zero - (= BSD_DEFAULT_PID). - -* User credentials are not supported. The following functions allow the - operation for everyone - - prison_equal_ip4(), - - chgsbsize(), - - cr_cansee(), - - cr_canseesocket() and - - cr_canseeinpcb(). - -* A basic USB functionality test that is known to work on Qemu is desirable. - -* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model - so that those architectures can use new TCP/IP and USB code. - -* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old - RTEMS TCP/IP stack. For some reason, the __printflike markers do not - compile in this environment. We may want to use the FreeBSD syslog.h - and get this addressed. - -* in_cksum implementations for architectures not supported by FreeBSD. - This will require figuring out where to put implementations that do - not originate from FreeBSD and are populated via the script. - -* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c"). - -* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML - parser and mmap(). - -* get_cyclecount(): The implementation is a security problem. - -* What to do with the priority parameter present in the FreeBSD synchronization - primitives and the thread creation functions? - -* TASKQUEUE(9): Support spin mutexes. - -* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c. - -* KQUEUE(2): Choose proper lock for global kqueue list. - -* TIMEOUT(9): Maybe use special task instead of timer server to call - callout_tick(). - -* sysctl_handle_opaque(): Implement reliable snapshots. - -* PING6(8): What to do with SIGALARM? - -* <sys/param.h>: Update Newlib to use a MSIZE of 256. - -* BPF(4): Add support for zero-copy buffers. - -* UNIX(4): Fix race conditions in the area of socket object and file node - destruction. Add support for file descriptor transmission via control - messages. - -* PRINTF(9): Add support for log(), the %D format specifier is missing in the - normal printf() family. - -* Why is the interrupt server used? The BSD interrupt handlers can block on -synchronization primitives like mutexes. This is in contrast to RTEMS -interrupt service routines. The BSPs using the generic interrupt support must -implement the `bsp_interrupt_vector_enable()` and -`bsp_interrupt_vector_disable()` routines. They normally enable/disable a -particular interrupt source at the interrupt controller. This can be used to -implement the interrupt server. The interrupt server is a task that wakes-up -in case an associated interrupt happens. The interrupt source is disabled in -a generic interrupt handler that wakes-up the interrupt server task. Once the -postponed interrupt processing is performed in the interrupt server the -interrupt source is enabled again. - -* Convert all BSP linkcmds to use a linkcmds.base so the sections are -easier to insert. - -* NIC Device Drivers -- Only common PCI NIC drivers have been included in the initial set. These -do not include any system on chip or ISA drivers. -- PCI configuration probe does not appear to happen to determine if a -NIC is in I/O or memory space. We have worked around this by using a -static hint to tell the fxp driver the correct mode. But this needs to -be addressed. -- The ISA drivers require more BSD infrastructure to be addressed. This was -outside the scope of the initial porting effort. - -== FreeBSD Source - -You should be able to rely on FreebSD manual pages and documentation -for details on the code itself. - -== BSD Library Source - -== Initialization of the BSD Library - -The initialization of the BSD library is based on the FreeBSD SYSINIT(9) -infrastructure. The key to initializing a system is to ensure that the desired -device drivers are explicitly pulled into the linked application. This plus -linking against the BSD library (`libbsd.a`) will pull in the necessary FreeBSD -infrastructure. - -The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of -object files linked together. If we have a library, then creating the -executable is simple. We begin with a start symbol and recursively resolve all -references. With a bunch of object files linked together we need a different -mechanism. Most object files don't know each other. Lets say we have a driver -module. The rest of the system has no references to this driver module. The -driver module needs a way to tell the rest of the system: Hey, kernel I am -here, please use my services! - -This registration of independent components is performed by SYSINIT(9) and -specializations: - -http://www.freebsd.org/cgi/man.cgi?query=SYSINIT - -The SYSINIT(9) uses some global data structures that are placed in a certain -section. In the linker command file we need this: - -------------------------------------------------------------------------------- -.rtemsroset : { - KEEP (*(SORT(.rtemsroset.*))) -} - -.rtemsrwset : { - KEEP (*(SORT(.rtemsrwset.*))) -} -------------------------------------------------------------------------------- - -This results for example in this executable layout: - -------------------------------------------------------------------------------- -[...] - *(SORT(.rtemsroset.*)) - .rtemsroset.bsd.modmetadata_set.begin - 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000025fe00 _bsd__start_set_modmetadata_set - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o) - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe08 0x4 libbsd.a(kern_module.o) -[...] - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe68 0x4 libbsd.a(mii.o) - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o) - .rtemsroset.bsd.modmetadata_set.end - 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000025fe70 _bsd__stop_set_modmetadata_set -[...] -.rtemsrwset 0x000000000030bad0 0x290 - *(SORT(.rtemsrwset.*)) - .rtemsrwset.bsd.sysinit_set.begin - 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000030bad0 _bsd__start_set_sysinit_set - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030badc 0x4 libbsd.a(init_main.o) -[...] - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bd54 0x4 libbsd.a(frag6.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o) - .rtemsrwset.bsd.sysinit_set.end - 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000030bd60 _bsd__stop_set_sysinit_set -[...] -------------------------------------------------------------------------------- - -Here you can see, that some global data structures are collected into -continuous memory areas. This memory area can be identified by start and stop -symbols. This constructs a table of uniform items. - -The low level FreeBSD code calls at some time during the initialization the -mi_startup() function (machine independent startup). This function will sort -the SYSINIT(9) set and call handler functions which perform further -initialization. The last step is the scheduler invocation. - -The SYSINIT(9) routines are run in mi_startup() which is called by -rtems_bsd_initialize(). - -This is also explained in "The Design and Implementation of the FreeBSD -Operating System" section 14.3 "Kernel Initialization". - -In RTEMS we have a library and not a bunch of object files. Thus we need a way -to pull-in the desired services out of the libbsd. Here the -`rtems-bsd-sysinit.h` comes into play. The SYSINIT(9) macros have been -modified and extended for RTEMS in `<sys/kernel.h>`: - -------------------------------------------------------------------------------- -#ifndef __rtems__ -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - static struct sysinit uniquifier ## _sys_init = { \ - subsystem, \ - order, \ - func, \ - (ident) \ - }; \ - DATA_SET(sysinit_set,uniquifier ## _sys_init) -#else /* __rtems__ */ -#define SYSINIT_ENTRY_NAME(uniquifier) \ - _bsd_ ## uniquifier ## _sys_init -#define SYSINIT_REFERENCE_NAME(uniquifier) \ - _bsd_ ## uniquifier ## _sys_init_ref -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \ - subsystem, \ - order, \ - func, \ - (ident) \ - }; \ - RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier)) -#define SYSINIT_REFERENCE(uniquifier) \ - extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \ - static struct sysinit const * const \ - SYSINIT_REFERENCE_NAME(uniquifier) __used \ - = &SYSINIT_ENTRY_NAME(uniquifier) -#define SYSINIT_MODULE_REFERENCE(mod) \ - SYSINIT_REFERENCE(mod ## module) -#define SYSINIT_DRIVER_REFERENCE(driver, bus) \ - SYSINIT_MODULE_REFERENCE(driver ## _ ## bus) -#define SYSINIT_DOMAIN_REFERENCE(dom) \ - SYSINIT_REFERENCE(domain_add_ ## dom) -#endif /* __rtems__ */ -------------------------------------------------------------------------------- - -Here you see that the SYSINIT(9) entries are no longer static. The -\*_REFERENCE() macros will create references to the corresponding modules which -are later resolved by the linker. The application has to provide an object -file with references to all required FreeBSD modules. - -The FreeBSD device model is quite elaborated (with follow-ups): - -http://www.freebsd.org/cgi/man.cgi?query=driver - -The devices form a tree with the Nexus device at a high-level. This Nexus -device is architecture specific in FreeBSD. In RTEMS we have our own Nexus -device, see `rtemsbsd/bsp/bsp-bsd-nexus-devices.c`. - -=== SYSCTL_NODE Example - -During development, we had an undefined reference to -_bsd_sysctl__net_children that we had trouble tracking down. Thanks to -Chris Johns, we located it. He explained how to read SYSCTL_NODE -definitions. This line from freebsd/netinet/in_proto.c is attempting -to add the "inet" node to the parent node "_net". - ----- -SYSCTL_NODE(_net, PF_INET, inet, CTLFLAG_RW, 0, - "Internet Family"); ----- - -Our problem was that we could not find where _bsd_sysctl__net_children -was defined. Chris suggested that when in doubt compile with -save-temps -and look at the preprocessed .i files. But he did not need that. He -explained that this the symbol name _bsd_sysctl__net_children was -automatically generated by a SYSCTL_NODE as follows: - -* _bsd_ - added by RTEMS modifications to SYSCTL_NODE macro -* sysctl_ - boilerplace added by SYSCTL_NODE macro -* "" - empty string for parent node -* net - name of SYSCTL_NODE -* children - added by SYSCTL macros - -This was all generated by a support macro declaring the node as this: - ----- -struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name); ----- - -Given this information, we located this SYSCTL_NODE declaration in -kern/kern_mib.c - ----- -SYSCTL_NODE(, CTL_KERN, kern, CTLFLAG_RW, 0, - "High kernel, proc, limits &c"); ----- - -== Core FreeBSD APIs and RTEMS Replacements == - -=== SX(9) (Shared/exclusive locks) === - -http://www.freebsd.org/cgi/man.cgi?query=sx - -Binary semaphores (this neglects the ability to allow shared access). - -=== MUTEX(9) (Mutual exclusion) === - -http://www.freebsd.org/cgi/man.cgi?query=mutex - -Binary semaphores (not recursive mutexes are not supported this way). - -=== RWLOCK(9) (Reader/writer lock) === - -http://www.freebsd.org/cgi/man.cgi?query=rwlock - -POSIX r/w lock. - -=== RMLOCK(9) (Reader/writer lock optimized for mostly read access patterns) === - -Note: This object was implemented as a wrapper for RWLOCK in the rm_lock header file. - -http://www.freebsd.org/cgi/man.cgi?query=rmlock - -POSIX r/w lock. - -=== CONDVAR(9) (Condition variables) === - -http://www.freebsd.org/cgi/man.cgi?query=condvar - -POSIX condition variables with modifications (hack). - -=== CALLOUT(9) (Timer functions) === - -http://www.freebsd.org/cgi/man.cgi?query=callout - -Timer server. - -=== TASKQUEUE(9) (Asynchronous task execution) === - -http://www.freebsd.org/cgi/man.cgi?query=taskqueue - -TBD. - -=== KTHREAD(9), KPROC(9) (Tasks) === - -http://www.freebsd.org/cgi/man.cgi?query=kthread - -http://www.freebsd.org/cgi/man.cgi?query=kproc - -Tasks. - -=== ZONE(9) (Zone allocator) === - -http://www.freebsd.org/cgi/man.cgi?query=zone - -TBD. - -=== devfs (Device file system) === - -There is a minimal implementation based on IMFS. The mount point is fixed to -"/dev". Note that the devfs is only used by the cdev subsystem. cdev has been -adapted so that the full path (including the leading "/dev") is given to devfs. -This saves some copy operations. - -devfs_create() first creates the full path and then creates an IMFS generic node -for the device. - -TBD: remove empty paths on devfs_destroy(). - -=== psignal (Signals) === - -TBD. Seems to be not needed. - -=== poll, select === - -TBD. Seems to be not needed. - -=== RMAN(9) (Resource management) === - -http://www.freebsd.org/cgi/man.cgi?query=rman - -TBD. Seems to be not needed. - -=== DEVCLASS(9), DEVICE(9), DRIVER(9), MAKE_DEV(9) (Device management) === - -http://www.freebsd.org/cgi/man.cgi?query=devclass - -http://www.freebsd.org/cgi/man.cgi?query=device - -http://www.freebsd.org/cgi/man.cgi?query=driver - -http://www.freebsd.org/cgi/man.cgi?query=make_dev - -Use FreeBSD implementation as far as possible. FreeBSD has a nice API for -dynamic device handling. It may be interesting for RTEMS to use this API -internally in the future. - -=== BUS_SPACE(9), BUS_DMA(9) (Bus and DMA access) === - -http://www.freebsd.org/cgi/man.cgi?query=bus_space - -http://www.freebsd.org/cgi/man.cgi?query=bus_dma - -Likely BSP dependent. A default implementation for memory mapped linear access -is easy to provide. The current heap implementation supports all properties -demanded by bus_dma (including the boundary constraint). - -== RTEMS Replacements by File Description == - -Note: Files with a status of USB are used by the USB test and have at least -been partially tested. If they contain both USB and Nic, then they are used -by both and MAY contain methods that have not been tested yet. Files that -are only used by the Nic test are the most suspect. - ----- -rtems-libbsd File: rtems-bsd-assert.c -FreeBSD File: rtems-bsd-config.h redefines BSD_ASSERT. -Description: This file contains the support method rtems_bsd_assert_func(). -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-autoconf.c -FreeBSD File: FreeBSD has BSP specific autoconf.c -Description: This file contains configuration methods that are used to setup the system. -Status: USB - -rtems-libbsd File: rtems-bsd-bus-dma.c -FreeBSD File: FreeBSD has BSP specific busdma_machdep.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-bus-dma-mbuf.c -FreeBSD File: FreeBSD has BSP specific busdma_machdep.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-callout.c -FreeBSD File: kern/kern_timeout.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-cam.c -FreeBSD File: cam/cam_sim.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-condvar.c -FreeBSD File: kern/kern_condvar.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-copyinout.c -FreeBSD File: bsp specific copyinout.c ) -Description: Note: The FreeBSD file is split with some methods being in rtems-bsd-support -Status: Nic - -rtems-libbsd File: rtems-bsd-delay.c -FreeBSD File: bsp specific file with multiple names -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-descrip.c -FreeBSD File: kern/kern_descrip.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-generic.c -FreeBSD File: kern/sys_generic.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-init.c -FreeBSD File: N/A -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-init-with-irq.c -FreeBSD File: N/A -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-jail.c -FreeBSD File: kern/kern_jail.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-lock.c -FreeBSD File: kern/subr_lock.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-log.c -FreeBSD File: kern/subr_prf.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-malloc.c -FreeBSD File: kern/kern_malloc.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-mutex.c -FreeBSD File: kern/kern_mutex.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-newproc.c -FreeBSD File: N/A -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-nexus.c -FreeBSD File: bsp specific nexus.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-panic.c -FreeBSD File: boot/common/panic.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-rwlock.c -FreeBSD File: kern_rwlock.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-shell.c -FreeBSD File: N/A -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-signal.c -FreeBSD File: kern/kern_sig.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-smp.c -FreeBSD File: N/A -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-support.c -FreeBSD File: bsp specific copyinout.c -Description: Note: the FreeBSD file is split with some methods being in rtems-bsd-copyinout. -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-sx.c -FreeBSD File: kern/kern_sx.c -Description: Status: USB, Nic - -rtems-libbsd File: rtems-bsd-synch.c -FreeBSD File: kern/kern_synch.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-syscalls.c -FreeBSD File: User API for kern/uipc_syscalls.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-sysctlbyname.c -FreeBSD File: User API for sysctlbyname(3) -Description: -Status: - -rtems-libbsd File: rtems-bsd-sysctl.c -FreeBSD File: User API for sysctl(8) -Description: -Status: - -rtems-libbsd File: rtems-bsd-sysctlnametomib.c -FreeBSD File: User API for sysctlnametomib -Description: -Status: - -rtems-libbsd File: rtems-bsd-taskqueue.c -FreeBSD File: kern/subr_taskqueue.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-thread.c -FreeBSD File: kern/kern_kthread.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-timeout.c -FreeBSD File: kern/kern_timeout.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-timesupport.c -FreeBSD File: kern/kern_clock.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-vm_glue.c -FreeBSD File: vm/vm_glue.c -Description: -Status: USB, Nic ----- - -== Notes by File == - -altq_subr.c - Arbitrary choices were made in this file that RTEMS would -not support tsc frequency change. Additionally, the clock frequency -for machclk_freq is always measured for RTEMS. - -conf.h - In order to add make_dev and destroy_dev, variables in the cdev -structure that were not being used were conditionally compiled out. The -capability of supporting children did not appear to be needed and was -not implemented in the rtems version of these routines. - -== NICs Status == - ----- -Driver Symbol Status -====== ====== ====== -RealTek _bsd_re_pcimodule_sys_init Links -EtherExpress _bsd_fxp_pcimodule_sys_init Links -DEC tulip _bsd_dc_pcimodule_sys_init Links -Broadcom BCM57xxx _bsd_bce_pcimodule_sys_init Links -Broadcom BCM4401 _bsd_bfe_pcimodule_sys_init Links -Broadcom BCM570x _bsd_bge_pcimodule_sys_init Needs Symbols (A) -E1000 IGB _bsd_igb_pcimodule_sys_init Links -E1000 EM _bsd_em_pcimodule_sys_init Links -Cadence ? Links, works. ----- - -To add a NIC edit rtemsbsd/include/bsp/nexus-devices.h and add the driver -reference to the architecture and/or BSP. For example to add the RealTek driver -add: - -SYSINIT_DRIVER_REFERENCE(re, pci); - -and to add the MII PHY driver add: - -SYSINIT_DRIVER_REFERENCE(rge, miibus); - -The PC BSP has these entries. - -Symbols (A) - pci_get_vpd_ident - -=== Cadence === - -The cadence driver works on the Xilinx Zynq platform. The hardware checksum -support works on real hardware but does not seem to be supported on qemu -therefore the default state is to disable TXCSUM and RXCSUM and this can be -enabled from the shell with: - - # ifconfig cgem0 rxcsum txcsum - -or with an ioctl call to the network interface driver with SIOCSIFCAP and the -mask IFCAP_TXCSUM and IFCAP_RXCSUM set. - -== PF (Firewall) == - -It is possible to use PF as a firewall. See -[https://www.freebsd.org/doc/handbook/firewalls-pf.html] for details on the -range of functions and for how to configure the firewall. - -The following is necessary to use PF on RTEMS: - -- You have to provide a +/etc/pf.os+ file. The firewall can use it for passive - OS fingerprinting. If you don't want to use this feature, the file may contain - nothing except a line of comment (for example "# empty"). - -- If some filters use protocol names (like tcp or udp) you have to provide a - +/etc/protocols+ file. - -- If some filters use service names (like ssh or http) you have to provide a - +/etc/services+ file. - -- Create a rule file (normally +/etc/pf.conf+). See the FreeBSD manual for the - syntax. - -- Load the rule file using the pfctl command and enable pf. Please note that the - pfctl command needs a lot of stack. You should use at least - RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of stack. An example initialisation can - look like follows: - ----- - int exit_code; - char *params[] = { - "pfctl", - "-f", - "/etc/pf.conf", - "-e", - NULL - }; - - exit_code = rtems_bsd_command_pfctl(ARGC(params), params); - assert(exit_code == EXIT_SUCCSESS); ----- - -=== Known restrictions === - -- Currently PF on RTEMS always uses the configuration for memory restricted - systems (on FreeBSD that means systems with less than 100 MB RAM). This is - fixed in +pfctl_init_options()+. - -== Wireless Network (WLAN) == - -The libbsd provides a basic support for WLAN. Note that currently this support -is still in an early state. The WLAN support is _not_ enabled in the default -buildset. You have to configure libbsd with the -`--buildset=buildset/everything.ini` to enable that feature. - -The following gives a rough overview over the necessary steps to connect to an -encrypted network with an RTL8188EU based WiFi dongle: - -- Reference all necessary module for your BSP. For some BSPs this is already - done in the nexus-devices.h: - ----- - SYSINIT_MODULE_REFERENCE(wlan_ratectl_none); - SYSINIT_MODULE_REFERENCE(wlan_sta); - SYSINIT_MODULE_REFERENCE(wlan_amrr); - SYSINIT_MODULE_REFERENCE(wlan_wep); - SYSINIT_MODULE_REFERENCE(wlan_tkip); - SYSINIT_MODULE_REFERENCE(wlan_ccmp); - SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub); - SYSINIT_REFERENCE(rtwn_rtl8188eufw); ----- - -- Create your wlan device using ifconfig: - +ifconfig wlan0 create wlandev rtwn0 up+ - -- Start a wpa_supplicant instance for that device: - + wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf+ - -Note that the wpa_supplicant will only be active till the device goes down. A -workaround is to just restart it every time it exits. - -=== Known restrictions === - -- The network interface (e.g. wlan0) is currently not automatically created. It - would be nice, if some service would create it as soon as for example a USB - device is connected. In FreeBSD the names are assigned via rc.conf with lines - like +wlans_rtwn0="wlan0"+. - -- wpa_supplicant hast to be started after the device is created. It has to be - restarted every time the connection goes down. Instead of this behaviour, - there should be some service that starts and restarts wpa_supplicant - automatically if a interface is ready. Probably the dhcpcd hooks could be used - for that. - -- The current wpa_supplicant implementation is protected with a lock so it can't - be started more than one time. If multiple interface should be used, all have - to be handled by that single instance. That makes it hard to add interfaces - dynamically. wpa_supplicant should be reviewed thoroughly whether multiple - instances could be started in parallel. - -- The control interface of wpa_supplicant most likely doesn't work. The wpa_cli - application is not ported. - -== IPSec == - -The IPSec support is optional in libbsd. It is disabled in the default build -set. Please make sure to use a build set with +netipsec = on+. - -To use IPSec the following configuration is necessary: - ----- -SYSINIT_MODULE_REFERENCE(if_gif); -SYSINIT_MODULE_REFERENCE(cryptodev); -RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec) -RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL); ----- - -Alternatively you can use the `RTEMS_BSD_CONFIG_IPSEC` which also includes the -rc.conf support for ipsec. It's still necessary to include a crypto device in -your config (`cryptosoft` in the above sample). - -The necessary initialization steps for a IPSec connection are similar to the -steps on a FreeBSD-System. The example assumes the following setup: - -- RTEMS external IP: 192.168.10.1/24 -- RTEMS internal IP: 10.10.1.1/24 -- remote external IP: 192.168.10.10/24 -- remote internal IP: 172.24.0.1/24 -- shared key: "mysecretkey" - -With this the following steps are necessary: - -- Create a gif0 device: - ----- -SHLL [/] # ifconfig gif0 create ----- - -- Configure the gif0 device: - ----- -SHLL [/] # ifconfig gif0 10.10.1.1 172.24.0.1 -SHLL [/] # ifconfig gif0 tunnel 192.168.10.1 192.168.10.10 ----- - -- Add a route to the remote net via the remote IP: - ----- -SHLL [/] # route add 172.24.0.0/24 172.24.0.1 ----- - -- Call `setkey` with a correct rule set: - ----- -SHLL [/] # cat /etc/setkey.conf -flush; -spdflush; -spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use; -spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use; -SHLL [/] # setkey -f /etc/setkey.conf ----- - -- Start a ike-daemon (racoon) with a correct configuration. ----- -SHLL [/] # cat /etc/racoon.conf -path pre_shared_key "/etc/racoon_psk.txt"; -log info; - -padding # options are not to be changed -{ - maximum_length 20; - randomize off; - strict_check off; - exclusive_tail off; -} - -listen # address [port] that racoon will listen on -{ - isakmp 192.168.10.1[500]; -} - -remote 192.168.10.10 [500] -{ - exchange_mode main; - my_identifier address 192.168.10.1; - peers_identifier address 192.168.10.10; - proposal_check obey; - - proposal { - encryption_algorithm 3des; - hash_algorithm md5; - authentication_method pre_shared_key; - lifetime time 3600 sec; - dh_group 2; - } -} - -sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any) -{ - pfs_group 2; - lifetime time 28800 sec; - encryption_algorithm 3des; - authentication_algorithm hmac_md5; - compression_algorithm deflate; -} -SHLL [/] # cat /etc/racoon_psk.txt -192.168.10.10 mysecretkey -SHLL [/] # racoon -F -f /etc/racoon.conf ----- - -All commands can be called via the respective API functions. For racoon there is -a `rtems_bsd_racoon_daemon()` function that forks of racoon as a task. - -Alternatively IPSec can also be configured via rc.conf entries: - ----- -cloned_interfaces="gif0" -ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10" -ike_enable="YES" -ike_program="racoon" -ike_flags="-F -f /etc/racoon.conf" -ike_priority="250" - -ipsec_enable="YES" -ipsec_file="/etc/setkey.conf" ----- - -ATTENTION: It is possible that the first packets slip through the tunnel without -encryption (true for FreeBSD as well as RTEMS). You might want to set up a -firewall rule to prevent that. - -== Problems to report to FreeBSD == - -The MMAP_NOT_AVAILABLE define is inverted on its usage. When it is -defined the mmap method is called. Additionally, it is not used -thoroughly. It is not used in the unmap portion of the source. -The file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap -the call to mmap and file rec_close.c uses the munmap method. diff --git a/rtemsbsd/arm/include/arm/lpc/probe.h b/rtemsbsd/arm/include/arm/lpc/probe.h new file mode 100644 index 00000000..54f9ebda --- /dev/null +++ b/rtemsbsd/arm/include/arm/lpc/probe.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_LPC_PROBE_H +#define _ARM_LPC_PROBE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int lpc_eth_probe(int unit); + +int lpc_ohci_probe(int unit); + +#ifdef __cplusplus +} +#endif + +#endif /* _ARM_LPC_PROBE_H */ diff --git a/rtemsbsd/include/bsp/mv643xx_eth.h b/rtemsbsd/include/bsp/mv643xx_eth.h new file mode 100644 index 00000000..7d122cd6 --- /dev/null +++ b/rtemsbsd/include/bsp/mv643xx_eth.h @@ -0,0 +1,397 @@ +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifndef MV643XX_LOWLEVEL_DRIVER_H +#define MV643XX_LOWLEVEL_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MV643XXETH_NUM_DRIVER_SLOTS 2 + +struct mveth_private; + +/* Create interface. + * Allocates resources for descriptor rings and sets up the driver software structure. + * + * Arguments: + * unit: + * interface # (1..2). The interface must not be attached to BSD. + * + * driver_tid: + * optional driver task-id (can be retrieved with BSP_mve_get_tid()). + * Not used by the low-level driver. + * + * isr(isr_arg): + * user ISR. + * + * void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred): + * Pointer to user-supplied callback to release a buffer that had been sent + * by BSP_mve_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg' + * and a flag indicating whether the send had been successful. + * The driver no longer accesses 'user_buf' after invoking this callback. + * CONTEXT: This callback is executed either by BSP_mve_swipe_tx() or + * BSP_mve_send_buf(), BSP_mve_init_hw(), BSP_mve_stop_hw() (the latter + * ones calling BSP_mve_swipe_tx()). + * void *cleanup_txbuf_arg: + * Closure argument that is passed on to 'cleanup_txbuf()' callback; + * + * void *(*alloc_rxbuf)(int *p_size, unsigned long *p_data_addr), + * Pointer to user-supplied callback to allocate a buffer for subsequent + * insertion into the RX ring by the driver. + * RETURNS: opaque handle to the buffer (which may be a more complex object + * such as an 'mbuf'). The handle is not used by the driver directly + * but passed back to the 'consume_rxbuf()' callback. + * Size of the available data area and pointer to buffer's data area + * in '*psize' and '*p_data_area', respectively. + * If no buffer is available, this routine should return NULL in which + * case the driver drops the last packet and re-uses the last buffer + * instead of handing it out to 'consume_rxbuf()'. + * CONTEXT: Called when initializing the RX ring (BSP_mve_init_hw()) or when + * swiping it (BSP_mve_swipe_rx()). + * + * + * void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len); + * Pointer to user-supplied callback to pass a received buffer back to + * the user. The driver no longer accesses the buffer after invoking this + * callback (with 'len'>0, see below). 'user_buf' is the buffer handle + * previously generated by 'alloc_rxbuf()'. + * The callback is passed 'cleanup_rxbuf_arg' and a 'len' + * argument giving the number of bytes that were received. + * 'len' may be <=0 in which case the 'user_buf' argument is NULL. + * 'len' == 0 means that the last 'alloc_rxbuf()' had failed, + * 'len' < 0 indicates a receiver error. In both cases, the last packet + * was dropped/missed and the last buffer will be re-used by the driver. + * NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header + * is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes) + * is appended. 'len' accounts for both. + * CONTEXT: Called from BSP_mve_swipe_rx(). + * void *cleanup_rxbuf_arg: + * Closure argument that is passed on to 'consume_rxbuf()' callback; + * + * rx_ring_size, tx_ring_size: + * How many big to make the RX and TX descriptor rings. Note that the sizes + * may be 0 in which case a reasonable default will be used. + * If either ring size is < 0 then the RX or TX will be disabled. + * Note that it is illegal in this case to use BSP_mve_swipe_rx() or + * BSP_mve_swipe_tx(), respectively. + * + * irq_mask: + * Interrupts to enable. OR of flags from above. + * + */ + +/* Direct assignment of MVE flags to user API relies on irqs and x-irqs not overlapping */ +#define MV643XX_ETH_IRQ_RX_DONE (1<<2) +#define MV643XX_ETH_EXT_IRQ_TX_DONE (1<<0) +#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16) + +struct mveth_private * +BSP_mve_create( + int unit, + rtems_id tid, + void (*isr)(void*isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +); + +/* + * Clear multicast hash filter. No multicast frames are accepted + * after executing this routine (unless the hardware was initialized + * in 'promiscuous' mode). + */ +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp); + +/* + * Program multicast filter to accept all multicast frames + */ +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp); + +/* + * Add a MAC address to the multicast filter. + * Existing entries are not changed but note that + * the filter is imperfect, i.e., multiple MAC addresses + * may alias to a single filter entry. Hence software + * filtering must still be performed. + * + * If a higher-level driver implements IP multicasting + * then multiple IP addresses may alias to the same MAC + * address. This driver maintains a 'reference-count' + * which is incremented every time the same MAC-address + * is passed to this routine; the address is only removed + * from the filter if BSP_mve_mcast_filter_accept_del() + * is called the same number of times (or by BSP_mve_mcast_filter_clear). + */ +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr); + +/* + * Remove a MAC address from the multicast filter. + * This routine decrements the reference count of the given + * MAC-address and removes it from the filter once the + * count reaches zero. + */ +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr); + + +/* Enable/disable promiscuous mode */ +void +BSP_mve_promisc_set(struct mveth_private *mp, int promisc); + +/* calls BSP_mve_stop_hw(), releases all resources and marks the interface + * as unused. + * RETURNS 0 on success, nonzero on failure. + * NOTE: the handle MUST NOT be used after successful execution of this + * routine. + */ +int +BSP_mve_detach(struct mveth_private *mp); + +/* Enqueue a buffer chain for transmission. + * + * RETURN: #bytes sent or -1 if there are not enough descriptors + * -2 is returned if the caller should attempt to + * repackage the chain into a smaller one. + * + * Comments: software cache-flushing incurs a penalty if the + * packet cannot be queued since it is flushed anyways. + * The algorithm is slightly more efficient in the normal + * case, though. + */ + +typedef struct MveEthBufIter { + void *hdl; /* opaque handle for the iterator implementor */ + void *data; /* data to be sent */ + size_t len; /* data size (in octets) */ + void *uptr; /* user-pointer to go into the descriptor; + note: cleanup_txbuf is only called for desriptors + where this field is non-NULL (for historical + reasons) */ +} MveEthBufIter; + +typedef MveEthBufIter *(*MveEthBufIterNext)(MveEthBufIter*); + +int +BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it); + + +/* Legacy entry point to send a header + a buffer */ +int +BSP_mve_send_buf_raw(struct mveth_private *mp, void *head_p, int h_len, void *data_p, int d_len); + +/* Descriptor scavenger; cleanup the TX ring, passing all buffers + * that have been sent to the cleanup_tx() callback. + * This routine is called from BSP_mve_send_buf(), BSP_mve_init_hw(), + * BSP_mve_stop_hw(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_tx(struct mveth_private *mp); + +/* Retrieve all received buffers from the RX ring, replacing them + * by fresh ones (obtained from the alloc_rxbuf() callback). The + * received buffers are passed to consume_rxbuf(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_rx(struct mveth_private *mp); + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr); + +/* Interrupt related routines */ + +/* Note: the BSP_mve_enable/disable/ack_irqs() entry points + * are deprecated. + * The newer API where the user passes a mask allows + * for more selective control. + */ + +/* Enable all supported interrupts at device */ +void +BSP_mve_enable_irqs(struct mveth_private *mp); + +/* Disable all supported interrupts at device */ +void +BSP_mve_disable_irqs(struct mveth_private *mp); + +/* Acknowledge (and clear) all supported interrupts. + * RETURNS: interrupts that were raised. + */ +uint32_t +BSP_mve_ack_irqs(struct mveth_private *mp); + +/* Enable interrupts included in 'mask' (leaving + * already enabled interrupts on). If the mask + * includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + */ +void +BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Disable interrupts included in 'mask' (leaving + * other ones that are currently enabled on). If the + * mask includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + * + * RETURNS: Bitmask of interrupts that were enabled upon entry + * into this routine. This can be used to restore the + * previous state. + */ +uint32_t +BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Acknowledge and clear selected interrupts. + * + * RETURNS: All pending interrupts. + * + * NOTE: Only pending interrupts contained in 'mask' + * are cleared. Others are left pending. + * + * This routine can be used to check for pending + * interrupts (pass mask == 0) or to clear all + * interrupts (pass mask == -1). + */ +uint32_t +BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask); + +/* Retrieve the driver daemon TID that was passed to + * BSP_mve_setup(). + */ + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp); + +/* Dump statistics to file (stdout if NULL) + * + * NOTE: this routine is not thread safe + */ +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f); + +#define MV643XX_MEDIA_LINK (1<<0) +#define MV643XX_MEDIA_FD (1<<1) +#define MV643XX_MEDIA_10 (1<<8) +#define MV643XX_MEDIA_100 (2<<8) +#define MV643XX_MEDIA_1000 (3<<8) +#define MV643XX_MEDIA_SPEED_MSK (0xff00) + +/* + * Initialize interface hardware + * + * 'mp' handle obtained by from BSP_mve_setup(). + * 'promisc' whether to set promiscuous flag. + * 'enaddr' pointer to six bytes with MAC address. Read + * from the device if NULL. + * 'speed' current speed and link status as read from the PHY. + * + * Note: Multicast filters are cleared by this routine. + * However, in promiscuous mode the mcast filters + * are programmed to accept all multicast frames. + */ +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int speed); + +/* + * Update the serial port to a new speed (e.g., result of a PHY IRQ) + */ +void +BSP_mve_update_serial_port(struct mveth_private *mp, int speed); + +/* + * Shutdown hardware and clean out the rings + */ +void +BSP_mve_stop_hw(struct mveth_private *mp); + +unsigned +BSP_mve_mii_read(struct mveth_private *mp, unsigned addr); + +unsigned +BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v); + +unsigned +BSP_mve_mii_read_early(int port, unsigned addr); + +unsigned +BSP_mve_mii_write_early(int port, unsigned addr, unsigned v); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h index e6487470..37008cc6 100644 --- a/rtemsbsd/include/bsp/nexus-devices.h +++ b/rtemsbsd/include/bsp/nexus-devices.h @@ -173,6 +173,14 @@ SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus); SYSINIT_DRIVER_REFERENCE(ffec, simplebus); SYSINIT_DRIVER_REFERENCE(ksz8091rnb, miibus); +#if IMXRT_IS_MIMXRT11xx +SYSINIT_DRIVER_REFERENCE(ehci, simplebus); +SYSINIT_DRIVER_REFERENCE(imxrt1166_usbphy, simplebus); +SYSINIT_DRIVER_REFERENCE(usbus, ehci); +RTEMS_BSD_DRIVER_USB; +RTEMS_BSD_DRIVER_USB_MASS; +#endif /* IMXRT_IS_IMXRT11xx */ + #elif defined(LIBBSP_ARM_LPC24XX_BSP_H) RTEMS_BSD_DEFINE_NEXUS_DEVICE(ohci, 0, 0, NULL); @@ -251,6 +259,11 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus); RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL); SYSINIT_DRIVER_REFERENCE(ukphy, miibus); +#elif defined(LIBBSP_BEATNIK_BSP_H) + +RTEMS_BSD_DEFINE_NEXUS_DEVICE(mve, 0, 0, NULL); +SYSINIT_DRIVER_REFERENCE(ukphy, miibus); + #elif defined(LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H) RTEMS_BSD_DRIVER_PC_LEGACY; diff --git a/rtemsbsd/include/machine/_kernel_if.h b/rtemsbsd/include/machine/_kernel_if.h index 08086658..16733fe3 100644 --- a/rtemsbsd/include/machine/_kernel_if.h +++ b/rtemsbsd/include/machine/_kernel_if.h @@ -41,6 +41,20 @@ MALLOC_DECLARE(M_IFADDR); MALLOC_DECLARE(M_IFMADDR); #endif +extern struct sx ifnet_detach_sxlock; + +struct nvlist; +struct ifcap_nv_bit_name; +int if_capnv_to_capint(const struct nvlist *nv, int *old_cap, + const struct ifcap_nv_bit_name *nn, bool all); +void if_capint_to_capnv(struct nvlist *nv, + const struct ifcap_nv_bit_name *nn, int ifr_cap, int ifr_req); +struct siocsifcapnv_driver_data { + int reqcap; + int reqcap2; + struct nvlist *nvcap; +}; + #define ifr_buffer ifr_ifru.ifru_buffer /* user supplied buffer with its length */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ diff --git a/rtemsbsd/include/machine/_kernel_socket.h b/rtemsbsd/include/machine/_kernel_socket.h index e9acc744..3acee460 100644 --- a/rtemsbsd/include/machine/_kernel_socket.h +++ b/rtemsbsd/include/machine/_kernel_socket.h @@ -46,6 +46,7 @@ #define MSG_SOCALLBCK 0x00010000 /* for use by socket callbacks - soreceive (TCP) */ #define MSG_MORETOCOME 0x00100000 /* additional data pending */ +#define MSG_TLSAPPDATA 0x00200000 /* do not soreceive() alert rec. (TLS) */ #define CMSG_ALIGN(n) _ALIGN(n) diff --git a/rtemsbsd/include/machine/rtems-bsd-cache.h b/rtemsbsd/include/machine/rtems-bsd-cache.h index 73b55e25..e292b216 100755 --- a/rtemsbsd/include/machine/rtems-bsd-cache.h +++ b/rtemsbsd/include/machine/rtems-bsd-cache.h @@ -45,7 +45,7 @@ #if defined(LIBBSP_ARM_LPC24XX_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && defined(LPC32XX_DISABLE_MMU)) /* No cache */ #elif defined(LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H) || \ - defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H) + defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H) /* With cache, no coherency support in hardware */ #define CPU_DATA_CACHE_ALIGNMENT 32 #elif defined(__GEN83xx_BSP_h) diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h index 09bcecf1..f2f1b91f 100644 --- a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h +++ b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h @@ -55,6 +55,12 @@ /* General define to activate BSD kernel parts */ #define _KERNEL 1 +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IN_HISTORICAL_NETS + +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IFCAP_NOMAP 0x4000000 + #include <machine/rtems-bsd-version.h> #include <machine/rtems-bsd-kernel-namespace.h> diff --git a/rtemsbsd/include/machine/rtems-bsd-program.h b/rtemsbsd/include/machine/rtems-bsd-program.h index f71ac9cd..3062c1a2 100644 --- a/rtemsbsd/include/machine/rtems-bsd-program.h +++ b/rtemsbsd/include/machine/rtems-bsd-program.h @@ -60,6 +60,12 @@ rtems_bsd_program_call_main_with_data_restore(const char *name, int (*main)(int, char **), int argc, char **argv, void *data_buf, const size_t data_size); +void * +rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg); + +void +rtems_bsd_program_remove_destructor(void *cookie, bool call); + void rtems_bsd_program_exit(int exit_code) __dead2; @@ -198,7 +204,7 @@ rtems_bsd_program_free(void *ptr); #endif #ifndef RTEMS_BSD_PROGRAM_NO_FREE_WRAP - #define free(ptr) rtems_bsd_program_free(ptr); + #define free(ptr) rtems_bsd_program_free(ptr) #endif __END_DECLS diff --git a/rtemsbsd/include/machine/rtems-bsd-user-space.h b/rtemsbsd/include/machine/rtems-bsd-user-space.h index 28d5dd5a..93113b9c 100644 --- a/rtemsbsd/include/machine/rtems-bsd-user-space.h +++ b/rtemsbsd/include/machine/rtems-bsd-user-space.h @@ -41,6 +41,7 @@ #define _RTEMS_BSD_MACHINE_RTEMS_BSD_USER_SPACE_H_ #define __FreeBSD__ 1 +#define _WANT_FREEBSD_BITSET #include <rtems/bsd/local/opt_inet6.h> #include <machine/rtems-bsd-version.h> @@ -49,6 +50,9 @@ #include <stdio.h> +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IFCAP_NOMAP 0x4000000 + #define O_CLOEXEC 0 #define O_DIRECTORY 0 diff --git a/rtemsbsd/include/rtems/bsd/bsd.h b/rtemsbsd/include/rtems/bsd/bsd.h index cec14ac4..9e524719 100755 --- a/rtemsbsd/include/rtems/bsd/bsd.h +++ b/rtemsbsd/include/rtems/bsd/bsd.h @@ -92,28 +92,6 @@ typedef struct { rtems_status_code rtems_bsd_initialize(void); /** - * @brief Initializes the libbsd and starts a DHCPCD task. - * - * The libbsd is initialized via rtems_bsd_initialize(). If this is - * successful, then the loop back interfaces are created. If this is - * successful, then a DHCPCD task is started at the least important priority. - * - * The default devices of the BSP are initialized. Support for - * - IF_BRIDGE(4), - * - LAGG(4), - * - multicast routing, - * - UNIX(4), and - * - VLAN(4), - * is enabled. - * - * No RTEMS shell commands are registered. - * - * @retval RTEMS_SUCCESSFUL Successful operation. - * @retval otherwise An error occurred. - */ -rtems_status_code rtems_bsd_initialize_dhcp(void); - -/** * @brief Configures the lo0 (loopback) interface. * * @return Returns an exit code, see also <sysexits.h>. diff --git a/rtemsbsd/nfsclient/nfs.c b/rtemsbsd/nfsclient/nfs.c index baada6ce..e9e83abb 100644 --- a/rtemsbsd/nfsclient/nfs.c +++ b/rtemsbsd/nfsclient/nfs.c @@ -3114,7 +3114,7 @@ rtems_filesystem_location_info_t old; rtems_filesystem_current->location = old; } rtems_semaphore_release(rpa->sync); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } diff --git a/rtemsbsd/pppd/rtemspppd.c b/rtemsbsd/pppd/rtemspppd.c index cf237a81..c001eff5 100644 --- a/rtemsbsd/pppd/rtemspppd.c +++ b/rtemsbsd/pppd/rtemspppd.c @@ -71,7 +71,7 @@ static rtems_task pppTask(rtems_task_argument arg) /* terminate myself */ rtems_pppd_taskid = 0; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } int rtems_pppd_initialize(void) diff --git a/rtemsbsd/rtems/program-internal.h b/rtemsbsd/rtems/program-internal.h index da817130..2104c064 100644 --- a/rtemsbsd/rtems/program-internal.h +++ b/rtemsbsd/rtems/program-internal.h @@ -60,6 +60,12 @@ struct program_allocmem_item { LIST_ENTRY(program_allocmem_item) entries; }; +struct program_destructor { + void (*destructor)(void *); + void *arg; + LIST_ENTRY(program_destructor) link; +}; + struct rtems_bsd_program_control { void *context; int exit_code; @@ -68,6 +74,7 @@ struct rtems_bsd_program_control { LIST_HEAD(, program_fd_item) open_fd; LIST_HEAD(, program_file_item) open_file; LIST_HEAD(, program_allocmem_item) allocated_mem; + LIST_HEAD(, program_destructor) destructors; }; struct rtems_bsd_program_control *rtems_bsd_program_get_control_or_null(void); diff --git a/rtemsbsd/rtems/rtems-bsd-racoon.c b/rtemsbsd/rtems/rtems-bsd-racoon.c index c7ea3594..e6e6205c 100644 --- a/rtemsbsd/rtems/rtems-bsd-racoon.c +++ b/rtemsbsd/rtems/rtems-bsd-racoon.c @@ -75,7 +75,7 @@ racoon_task(rtems_task_argument arg) } clean_up_args(args); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } rtems_status_code diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c index 23ee15db..8ffaa914 100644 --- a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c +++ b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c @@ -103,7 +103,9 @@ cloned_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) "ifconfig", aa->argv[arg], "create", NULL }; rtems_bsd_rc_conf_print_cmd(rc_conf, "cloning_interfaces", 3, ifconfg_args); - rtems_bsd_command_ifconfig(3, (char**) ifconfg_args); + if (rtems_bsd_command_ifconfig(3, (char**) ifconfg_args)) { + return -1; + } } return 0; @@ -377,7 +379,7 @@ defaultrouter(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, bool memset(&sin, 0, sizeof(sin)); memset(&rti_info[0], 0, sizeof(rti_info)); sin.sin_family = AF_INET; - inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr); + (void) inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr); r = rtems_get_route(&sin, rti_info); if (r == 0 && rti_info[RTAX_GATEWAY] != NULL) { @@ -710,9 +712,9 @@ run_dhcp(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) } dd->config.priority = priority; - rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv); + r = rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv); - if (dd->argc_argv->argc > 0) { + if (r == 0 && dd->argc_argv->argc > 0) { dd->config.argc = dd->argc_argv->argc; dd->config.argv = dd->argc_argv->argv; } diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf.c b/rtemsbsd/rtems/rtems-bsd-rc-conf.c index 36f90a1d..88d98c3e 100644 --- a/rtemsbsd/rtems/rtems-bsd-rc-conf.c +++ b/rtemsbsd/rtems/rtems-bsd-rc-conf.c @@ -260,12 +260,14 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, */ length = strnlen(text, RTEMS_BSD_RC_CONF_MAX_SIZE); if (length == RTEMS_BSD_RC_CONF_MAX_SIZE) { + free(_rc_conf); errno = E2BIG; return -1; } copy = strdup(text); if (copy == NULL) { + free(_rc_conf); errno = ENOMEM; return -1; } @@ -286,6 +288,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, lines = malloc(sizeof(char*) * line_count); if (lines == NULL) { free(copy); + free(_rc_conf); errno = ENOMEM; return -1; } @@ -335,6 +338,13 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, if (timeout >= 0) _rc_conf->waiter = rtems_task_self(); + if (_rc_conf->name == NULL) { + free((void*) _rc_conf->lines); + free((void*) _rc_conf->data); + free(_rc_conf); + return -1; + } + /* * Create the lock. */ @@ -343,6 +353,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, free((void*) _rc_conf->name); free((void*) _rc_conf->lines); free((void*) _rc_conf->data); + free(_rc_conf); return -1; } @@ -714,6 +725,7 @@ rc_conf_worker(rtems_task_argument task_argument) rtems_chain_node* node = rtems_chain_first(&services); int r = 0; int error; + bool rc_conf_verbose; /* * Check for a syslog priority before any services are run. @@ -748,6 +760,8 @@ rc_conf_worker(rtems_task_argument task_argument) if (r < 0) rc_conf->error_code = error; + rc_conf_verbose = rc_conf->verbose; + /* * If there is a waiter signal else clean up because the waiter has gone. */ @@ -760,10 +774,10 @@ rc_conf_worker(rtems_task_argument task_argument) rc_conf_destroy(rc_conf); } - if (rc_conf->verbose) + if (rc_conf_verbose) printf("rc.conf: finished\n"); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } int @@ -793,6 +807,7 @@ rtems_bsd_run_rc_conf_script(const char* name, if (sc != RTEMS_SUCCESSFUL) { fprintf(stderr, "error: %s: get priority: %s\n", name, rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return -1; } @@ -805,6 +820,7 @@ rtems_bsd_run_rc_conf_script(const char* name, &worker); if (sc != RTEMS_SUCCESSFUL) { fprintf (stderr, "error: worker create: %s", rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return -1; } @@ -814,6 +830,7 @@ rtems_bsd_run_rc_conf_script(const char* name, (rtems_task_argument) rc_conf); if (sc != RTEMS_SUCCESSFUL) { fprintf (stderr, "error: worker start: %s", rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return - 1; } @@ -869,7 +886,7 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose) if (r < 0) return r; - rc_conf = malloc(sb.st_size); + rc_conf = malloc(sb.st_size + 1); if (rc_conf == NULL) { errno = ENOMEM; return -1; @@ -892,6 +909,8 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose) fclose(file); + rc_conf[sb.st_size] = '\0'; + r = rtems_bsd_run_rc_conf_script(name, rc_conf, timeout, verbose); free(rc_conf); diff --git a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c index 4af789cc..3f705975 100644 --- a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c +++ b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c @@ -53,7 +53,7 @@ new_wpa_supplicant_task(rtems_task_argument arg) free(params->argv); free(params); - rtems_task_delete( RTEMS_SELF ); + rtems_task_exit(); } int rtems_bsd_command_wpa_supplicant_fork(int argc, char **argv) diff --git a/rtemsbsd/rtems/rtems-kernel-init.c b/rtemsbsd/rtems/rtems-kernel-init.c index 7112914e..b0779277 100644 --- a/rtemsbsd/rtems/rtems-kernel-init.c +++ b/rtemsbsd/rtems/rtems-kernel-init.c @@ -135,7 +135,9 @@ rtems_bsd_initialize(void) sbt_tickthreshold = bttosbt(bt_tickthreshold); maxid_maxcpus = (int) rtems_scheduler_get_processor_maximum(); - mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO); + if (mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + return RTEMS_UNSATISFIED; + } sc = rtems_timer_initiate_server( rtems_bsd_get_task_priority(name), diff --git a/rtemsbsd/rtems/rtems-kernel-thread.c b/rtemsbsd/rtems/rtems-kernel-thread.c index 8e3344ef..f06999fb 100644 --- a/rtemsbsd/rtems/rtems-kernel-thread.c +++ b/rtemsbsd/rtems/rtems-kernel-thread.c @@ -280,13 +280,6 @@ rtems_bsd_thread_start(struct thread **td_ptr, void (*func)(void *), void *arg, return eno; } -static __dead2 void -rtems_bsd_thread_delete(void) -{ - rtems_task_delete(RTEMS_SELF); - BSD_PANIC("delete self failed"); -} - void kproc_start(const void *udata) { @@ -312,7 +305,7 @@ kproc_create(void (*func)(void *), void *arg, struct proc **newpp, int flags, in void kproc_exit(int ecode) { - rtems_bsd_thread_delete(); + rtems_task_exit(); } void @@ -340,7 +333,7 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, struct thread **new void kthread_exit(void) { - rtems_bsd_thread_delete(); + rtems_task_exit(); } int diff --git a/rtemsbsd/rtems/rtems-program.c b/rtemsbsd/rtems/rtems-program.c index 204ed248..1ca8e3b9 100644 --- a/rtemsbsd/rtems/rtems-program.c +++ b/rtemsbsd/rtems/rtems-program.c @@ -224,6 +224,18 @@ allocmem_free_all(struct rtems_bsd_program_control *prog_ctrl) } } +static void +call_destructors(struct rtems_bsd_program_control *prog_ctrl) +{ + struct program_destructor *node; + struct program_destructor *tmp; + + LIST_FOREACH_SAFE(node, &prog_ctrl->destructors, link, tmp) { + (*node->destructor)(node->arg); + free(node); + } +} + int rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) { @@ -251,6 +263,7 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) LIST_INIT(&prog_ctrl->open_fd); LIST_INIT(&prog_ctrl->open_file); LIST_INIT(&prog_ctrl->allocated_mem); + LIST_INIT(&prog_ctrl->destructors); if (setjmp(prog_ctrl->return_context) == 0) { exit_code = (*prog)(context); @@ -262,10 +275,48 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) fd_close_all(prog_ctrl); file_close_all(prog_ctrl); allocmem_free_all(prog_ctrl); + call_destructors(prog_ctrl); free(prog_ctrl); return (exit_code); } +void * +rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg) +{ + struct rtems_bsd_program_control *prog_ctrl; + struct program_destructor *node; + + prog_ctrl = rtems_bsd_program_get_control_or_null(); + if (prog_ctrl == NULL) { + return (NULL); + } + + node = malloc(sizeof(*node)); + if (node == NULL) { + return (NULL); + } + + node->destructor = destructor; + node->arg = arg; + LIST_INSERT_HEAD(&prog_ctrl->destructors, node, link); + return (node); +} + +void +rtems_bsd_program_remove_destructor(void *cookie, bool call) +{ + struct program_destructor *node; + + node = cookie; + LIST_REMOVE(node, link); + + if (call) { + (*node->destructor)(node->arg); + } + + free(node); +} + void rtems_bsd_program_exit(int exit_code) { diff --git a/rtemsbsd/rtems/rtems-routes.c b/rtemsbsd/rtems/rtems-routes.c index 6663e8d4..0b5250f0 100644 --- a/rtemsbsd/rtems/rtems-routes.c +++ b/rtemsbsd/rtems/rtems-routes.c @@ -85,8 +85,10 @@ int rtems_get_route(const struct sockaddr_in* sin, struct sockaddr** rti_info) } s = socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC); - if (s < 0) + if (s < 0) { + free(buf); return -1; + } rtm = (struct rt_msghdr *) buf; rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); diff --git a/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c index c24732cc..da64922f 100644 --- a/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c +++ b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c @@ -27,7 +27,7 @@ */ #include <bsp.h> -#if defined(LIBBSP_ARM_IMX_BSP_H) +#if defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H) #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -303,4 +303,4 @@ EARLY_DRIVER_MODULE(imx_rtems_gpio, simplebus, imx_rtems_gpio_driver, imx_rtems_gpio_devclass, NULL, NULL, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); -#endif /* LIBBSP_ARM_IMX_BSP_H */ +#endif /* LIBBSP_ARM_IMX_BSP_H || LIBBSP_ARM_IMXRT_BSP_H */ diff --git a/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c new file mode 100644 index 00000000..b8e3a188 --- /dev/null +++ b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c @@ -0,0 +1,227 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (c) 2013 Ian Lepore <ian@freebsd.org> + * Copyright (C) 2023 embedded brains GmbH & Co. KG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <bsp.h> +#if defined(LIBBSP_ARM_IMXRT_BSP_H) + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * USBPHY driver for Freescale i.MXRT1166. Most likely works with the whole + * i.MXRT11xx family. + * + * Based on USBPHY driver for i.MX6. + */ + +#include <rtems/bsd/local/opt_bus.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <machine/bus.h> + +#include <dev/extres/regulator/regulator.h> + +#include <fsl_device_registers.h> +#include <fsl_clock.h> + +struct imxrt1166_usbphy_softc { + device_t dev; + struct resource *mem_res; + regulator_t supply_vbus; + USBPHY_Type *regs; +}; + +static struct ofw_compat_data compat_data[] = { + {"fsl,imxrt1166-usbphy", true}, + {NULL, false} +}; + +static int +imxrt1166_usbphy_detach(device_t dev) +{ + struct imxrt1166_usbphy_softc *sc; + + sc = device_get_softc(dev); + + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (0); +} + +#define BUS_SPACE_PHYSADDR(res, offs) \ + ((u_int)(rman_get_start(res)+(offs))) + +static int +enable_vbus_supply(device_t dev, struct imxrt1166_usbphy_softc *sc) +{ + int rv; + phandle_t node; + + node = ofw_bus_get_node(dev); + if (OF_hasprop(node, "vbus-supply")) { + rv = regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", + &sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get \"vbus\" regulator\n"); + return ENXIO; + } + rv = regulator_enable(sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable \"vbus\" regulator\n"); + return ENXIO; + } + } + + return 0; +} + +static int +imxrt1166_usbphy_attach(device_t dev) +{ + struct imxrt1166_usbphy_softc *sc; + int err, rid; +#if IMXRT_IS_MIMXRT11xx + uint32_t usbClockFreq; +#endif + + sc = device_get_softc(dev); + err = 0; + + /* Allocate bus_space resources. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + err = ENXIO; + goto out; + } + + /* Enable VBUS Supply if a regulator is given */ + err = enable_vbus_supply(dev, sc); + if (err != 0) { + goto out; + } + + sc->regs = (USBPHY_Type *)BUS_SPACE_PHYSADDR(sc->mem_res, 0); + +#if IMXRT_IS_MIMXRT11xx + /* Enable register clock */ + CLOCK_EnableClock(kCLOCK_Usb); + + usbClockFreq = CLOCK_GetFreq(kCLOCK_Osc24M); + + /* + * Set the software reset bit. It will be implicitly cleared when + * setting up the clock in the next steps. + */ + sc->regs->CTRL_SET = USBPHY_CTRL_SFTRST_MASK; + + /* + * Enable PLLs. + * + * FIXME: Hacky way to find out the module. + */ + if (sc->regs == USBPHY1) { + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, usbClockFreq); + } else { + CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq); + CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, usbClockFreq); + } +#else + /* Not implemented */ +#endif + + err = 0; + +out: + + if (err != 0) + imxrt1166_usbphy_detach(dev); + + return (err); +} + +static int +imxrt1166_usbphy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Freescale i.MXRT1166 USB PHY"); + + return (BUS_PROBE_DEFAULT); +} + +static device_method_t imxrt1166_usbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, imxrt1166_usbphy_probe), + DEVMETHOD(device_attach, imxrt1166_usbphy_attach), + DEVMETHOD(device_detach, imxrt1166_usbphy_detach), + + DEVMETHOD_END +}; + +static driver_t imxrt1166_usbphy_driver = { + "imxrt1166_usbphy", + imxrt1166_usbphy_methods, + sizeof(struct imxrt1166_usbphy_softc) +}; + +static devclass_t imxrt1166_usbphy_devclass; + +/* + * This driver needs to start before the ehci driver, but later than the usual + * "special" drivers like clocks and cpu. Ehci starts at DEFAULT so SUPPORTDEV + * is where this driver fits most. + */ +EARLY_DRIVER_MODULE(imxrt1166_usbphy, simplebus, imxrt1166_usbphy_driver, + imxrt1166_usbphy_devclass, 0, 0, + BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); + +#endif /* LIBBSP_ARM_IMXRT_BSP_H */ diff --git a/rtemsbsd/sys/arm/lpc/if_lpe.c b/rtemsbsd/sys/arm/lpc/if_lpe.c index 40ac162e..87ca9ff7 100755 --- a/rtemsbsd/sys/arm/lpc/if_lpe.c +++ b/rtemsbsd/sys/arm/lpc/if_lpe.c @@ -1,1428 +1,1769 @@ -#include <machine/rtems-bsd-kernel-space.h> - -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD +/** + * @file * - * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> - * All rights reserved. + * @ingroup lpc_eth * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * @brief Ethernet driver. + */ + +/* + * Copyright (C) 2009, 2022 embedded brains GmbH * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); + +#include <machine/rtems-bsd-kernel-space.h> + +#include <bsp.h> + +#if defined(LIBBSP_ARM_LPC24XX_BSP_H) || defined(LIBBSP_ARM_LPC32XX_BSP_H) #include <sys/param.h> -#include <sys/endian.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> #include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> #include <sys/module.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/rman.h> -#include <sys/bus.h> #include <sys/socket.h> +#include <sys/sockio.h> + +#include <sys/bus.h> #include <machine/bus.h> -#ifndef __rtems__ -#include <machine/intr.h> -#endif /* __rtems__ */ #include <net/if.h> -#include <net/if_arp.h> #include <net/ethernet.h> +#include <net/if_arp.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> #include <net/if_var.h> -#include <net/bpf.h> +#include <dev/mii/mii.h> -#ifndef __rtems__ -#include <dev/ofw/ofw_bus.h> -#include <dev/ofw/ofw_bus_subr.h> -#endif /* __rtems__ */ +#include <rtems/bsd/bsd.h> -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> +#include <arm/lpc/probe.h> -#include <arm/lpc/lpcreg.h> -#include <arm/lpc/lpcvar.h> -#include <arm/lpc/if_lpereg.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/lpc-ethernet-config.h> +#include <bsp/utility.h> -#include <rtems/bsd/local/miibus_if.h> -#ifdef __rtems__ -#include <machine/rtems-bsd-cache.h> -#include <rtems/bsd/bsd.h> -#endif /* __rtems__ */ +#if MCLBYTES > (2 * 1024) + #error "MCLBYTES to large" +#endif -#ifdef DEBUG -#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ - printf(fmt,##args); } while (0) +#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + #define LPC_ETH_CONFIG_TX_BUF_SIZE sizeof(struct mbuf *) #else -#define debugf(fmt, args...) + #define LPC_ETH_CONFIG_TX_BUF_SIZE 1518U #endif -struct lpe_dmamap_arg { - bus_addr_t lpe_dma_busaddr; -}; +#define DEFAULT_PHY 0 +#define WATCHDOG_TIMEOUT 5 + +typedef struct { + uint32_t start; + uint32_t control; +} lpc_eth_transfer_descriptor; + +typedef struct { + uint32_t info; + uint32_t hash_crc; +} lpc_eth_receive_status; + +typedef struct { + uint32_t mac1; + uint32_t mac2; + uint32_t ipgt; + uint32_t ipgr; + uint32_t clrt; + uint32_t maxf; + uint32_t supp; + uint32_t test; + uint32_t mcfg; + uint32_t mcmd; + uint32_t madr; + uint32_t mwtd; + uint32_t mrdd; + uint32_t mind; + uint32_t reserved_0 [2]; + uint32_t sa0; + uint32_t sa1; + uint32_t sa2; + uint32_t reserved_1 [45]; + uint32_t command; + uint32_t status; + uint32_t rxdescriptor; + uint32_t rxstatus; + uint32_t rxdescriptornum; + uint32_t rxproduceindex; + uint32_t rxconsumeindex; + uint32_t txdescriptor; + uint32_t txstatus; + uint32_t txdescriptornum; + uint32_t txproduceindex; + uint32_t txconsumeindex; + uint32_t reserved_2 [10]; + uint32_t tsv0; + uint32_t tsv1; + uint32_t rsv; + uint32_t reserved_3 [3]; + uint32_t flowcontrolcnt; + uint32_t flowcontrolsts; + uint32_t reserved_4 [34]; + uint32_t rxfilterctrl; + uint32_t rxfilterwolsts; + uint32_t rxfilterwolclr; + uint32_t reserved_5 [1]; + uint32_t hashfilterl; + uint32_t hashfilterh; + uint32_t reserved_6 [882]; + uint32_t intstatus; + uint32_t intenable; + uint32_t intclear; + uint32_t intset; + uint32_t reserved_7 [1]; + uint32_t powerdown; +} lpc_eth_controller; + +#define LPE_LOCK(e) mtx_lock(&(e)->mtx) + +#define LPE_UNLOCK(e) mtx_unlock(&(e)->mtx) + +static volatile lpc_eth_controller *const lpc_eth = + (volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE; + +/* ETH_RX_CTRL */ + +#define ETH_RX_CTRL_SIZE_MASK 0x000007ffU +#define ETH_RX_CTRL_INTERRUPT 0x80000000U + +/* ETH_RX_STAT */ + +#define ETH_RX_STAT_RXSIZE_MASK 0x000007ffU +#define ETH_RX_STAT_BYTES 0x00000100U +#define ETH_RX_STAT_CONTROL_FRAME 0x00040000U +#define ETH_RX_STAT_VLAN 0x00080000U +#define ETH_RX_STAT_FAIL_FILTER 0x00100000U +#define ETH_RX_STAT_MULTICAST 0x00200000U +#define ETH_RX_STAT_BROADCAST 0x00400000U +#define ETH_RX_STAT_CRC_ERROR 0x00800000U +#define ETH_RX_STAT_SYMBOL_ERROR 0x01000000U +#define ETH_RX_STAT_LENGTH_ERROR 0x02000000U +#define ETH_RX_STAT_RANGE_ERROR 0x04000000U +#define ETH_RX_STAT_ALIGNMENT_ERROR 0x08000000U +#define ETH_RX_STAT_OVERRUN 0x10000000U +#define ETH_RX_STAT_NO_DESCRIPTOR 0x20000000U +#define ETH_RX_STAT_LAST_FLAG 0x40000000U +#define ETH_RX_STAT_ERROR 0x80000000U + +/* ETH_TX_CTRL */ + +#define ETH_TX_CTRL_SIZE_MASK 0x7ffU +#define ETH_TX_CTRL_SIZE_SHIFT 0 +#define ETH_TX_CTRL_OVERRIDE 0x04000000U +#define ETH_TX_CTRL_HUGE 0x08000000U +#define ETH_TX_CTRL_PAD 0x10000000U +#define ETH_TX_CTRL_CRC 0x20000000U +#define ETH_TX_CTRL_LAST 0x40000000U +#define ETH_TX_CTRL_INTERRUPT 0x80000000U + +/* ETH_TX_STAT */ + +#define ETH_TX_STAT_COLLISION_COUNT_MASK 0x01e00000U +#define ETH_TX_STAT_DEFER 0x02000000U +#define ETH_TX_STAT_EXCESSIVE_DEFER 0x04000000U +#define ETH_TX_STAT_EXCESSIVE_COLLISION 0x08000000U +#define ETH_TX_STAT_LATE_COLLISION 0x10000000U +#define ETH_TX_STAT_UNDERRUN 0x20000000U +#define ETH_TX_STAT_NO_DESCRIPTOR 0x40000000U +#define ETH_TX_STAT_ERROR 0x80000000U + +/* ETH_INT */ + +#define ETH_INT_RX_OVERRUN 0x00000001U +#define ETH_INT_RX_ERROR 0x00000002U +#define ETH_INT_RX_FINISHED 0x00000004U +#define ETH_INT_RX_DONE 0x00000008U +#define ETH_INT_TX_UNDERRUN 0x00000010U +#define ETH_INT_TX_ERROR 0x00000020U +#define ETH_INT_TX_FINISHED 0x00000040U +#define ETH_INT_TX_DONE 0x00000080U +#define ETH_INT_SOFT 0x00001000U +#define ETH_INT_WAKEUP 0x00002000U + +/* ETH_RX_FIL_CTRL */ + +#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST 0x00000001U +#define ETH_RX_FIL_CTRL_ACCEPT_BROADCAST 0x00000002U +#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST 0x00000004U +#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST_HASH 0x00000008U +#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH 0x00000010U +#define ETH_RX_FIL_CTRL_ACCEPT_PERFECT 0x00000020U +#define ETH_RX_FIL_CTRL_MAGIC_PACKET_WOL 0x00001000U +#define ETH_RX_FIL_CTRL_RX_FILTER_WOL 0x00002000U + +/* ETH_CMD */ + +#define ETH_CMD_RX_ENABLE 0x00000001U +#define ETH_CMD_TX_ENABLE 0x00000002U +#define ETH_CMD_REG_RESET 0x00000008U +#define ETH_CMD_TX_RESET 0x00000010U +#define ETH_CMD_RX_RESET 0x00000020U +#define ETH_CMD_PASS_RUNT_FRAME 0x00000040U +#define ETH_CMD_PASS_RX_FILTER 0X00000080U +#define ETH_CMD_TX_FLOW_CONTROL 0x00000100U +#define ETH_CMD_RMII 0x00000200U +#define ETH_CMD_FULL_DUPLEX 0x00000400U -struct lpe_rxdesc { - struct mbuf * lpe_rxdesc_mbuf; -#ifndef __rtems__ - bus_dmamap_t lpe_rxdesc_dmamap; -#endif /* __rtems__ */ -}; +/* ETH_STAT */ -struct lpe_txdesc { - int lpe_txdesc_first; - struct mbuf * lpe_txdesc_mbuf; -#ifndef __rtems__ - bus_dmamap_t lpe_txdesc_dmamap; -#endif /* __rtems__ */ -}; +#define ETH_STAT_RX_ACTIVE 0x00000001U +#define ETH_STAT_TX_ACTIVE 0x00000002U -struct lpe_chain_data { - bus_dma_tag_t lpe_parent_tag; - bus_dma_tag_t lpe_tx_ring_tag; - bus_dmamap_t lpe_tx_ring_map; - bus_dma_tag_t lpe_tx_status_tag; - bus_dmamap_t lpe_tx_status_map; - bus_dma_tag_t lpe_tx_buf_tag; - bus_dma_tag_t lpe_rx_ring_tag; - bus_dmamap_t lpe_rx_ring_map; - bus_dma_tag_t lpe_rx_status_tag; - bus_dmamap_t lpe_rx_status_map; - bus_dma_tag_t lpe_rx_buf_tag; - struct lpe_rxdesc lpe_rx_desc[LPE_RXDESC_NUM]; - struct lpe_txdesc lpe_tx_desc[LPE_TXDESC_NUM]; - int lpe_tx_prod; - int lpe_tx_last; - int lpe_tx_used; -}; +/* ETH_MAC2 */ -struct lpe_ring_data { - struct lpe_hwdesc * lpe_rx_ring; - struct lpe_hwstatus * lpe_rx_status; - bus_addr_t lpe_rx_ring_phys; - bus_addr_t lpe_rx_status_phys; - struct lpe_hwdesc * lpe_tx_ring; - struct lpe_hwstatus * lpe_tx_status; - bus_addr_t lpe_tx_ring_phys; - bus_addr_t lpe_tx_status_phys; -}; +#define ETH_MAC2_FULL_DUPLEX BSP_BIT32(8) -struct lpe_softc { - struct ifnet * lpe_ifp; - struct mtx lpe_mtx; -#ifndef __rtems__ - phandle_t lpe_ofw; -#endif /* __rtems__ */ - device_t lpe_dev; - device_t lpe_miibus; - uint8_t lpe_enaddr[6]; - struct resource * lpe_mem_res; - struct resource * lpe_irq_res; - void * lpe_intrhand; - bus_space_tag_t lpe_bst; - bus_space_handle_t lpe_bsh; -#define LPE_FLAG_LINK (1 << 0) - uint32_t lpe_flags; - int lpe_watchdog_timer; - struct callout lpe_tick; - struct lpe_chain_data lpe_cdata; - struct lpe_ring_data lpe_rdata; -}; +/* ETH_SUPP */ -static int lpe_probe(device_t); -static int lpe_attach(device_t); -static int lpe_detach(device_t); -static int lpe_miibus_readreg(device_t, int, int); -static int lpe_miibus_writereg(device_t, int, int, int); -static void lpe_miibus_statchg(device_t); - -static void lpe_reset(struct lpe_softc *); -static void lpe_init(void *); -static void lpe_init_locked(struct lpe_softc *); -static void lpe_start(struct ifnet *); -static void lpe_start_locked(struct ifnet *); -static void lpe_stop(struct lpe_softc *); -static void lpe_stop_locked(struct lpe_softc *); -static int lpe_ioctl(struct ifnet *, u_long, caddr_t); -static void lpe_set_rxmode(struct lpe_softc *); -static void lpe_set_rxfilter(struct lpe_softc *); -static void lpe_intr(void *); -static void lpe_rxintr(struct lpe_softc *); -static void lpe_txintr(struct lpe_softc *); -static void lpe_tick(void *); -static void lpe_watchdog(struct lpe_softc *); -static int lpe_encap(struct lpe_softc *, struct mbuf **); -static int lpe_dma_alloc(struct lpe_softc *); -static int lpe_dma_alloc_rx(struct lpe_softc *); -static int lpe_dma_alloc_tx(struct lpe_softc *); -static int lpe_init_rx(struct lpe_softc *); -static int lpe_init_rxbuf(struct lpe_softc *, int); -static void lpe_discard_rxbuf(struct lpe_softc *, int); -static void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int); -static int lpe_ifmedia_upd(struct ifnet *); -static void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *); - -#define lpe_lock(_sc) mtx_lock(&(_sc)->lpe_mtx) -#define lpe_unlock(_sc) mtx_unlock(&(_sc)->lpe_mtx) -#define lpe_lock_assert(_sc) mtx_assert(&(_sc)->lpe_mtx, MA_OWNED) - -#define lpe_read_4(_sc, _reg) \ - bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg)) -#define lpe_write_4(_sc, _reg, _val) \ - bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val)) - -#define LPE_HWDESC_RXERRS (LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \ - LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \ - LPE_HWDESC_RXNODESCR) - -#define LPE_HWDESC_TXERRS (LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \ - LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR) - -static int -lpe_probe(device_t dev) -{ - -#ifndef __rtems__ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); +#define ETH_SUPP_SPEED BSP_BIT32(8) - if (!ofw_bus_is_compatible(dev, "lpc,ethernet")) - return (ENXIO); -#endif /* __rtems__ */ +/* ETH_MCFG */ - device_set_desc(dev, "LPC32x0 10/100 Ethernet"); - return (BUS_PROBE_DEFAULT); -} +#define ETH_MCFG_CLOCK_SELECT(val) BSP_FLD32(val, 2, 4) -static int -lpe_attach(device_t dev) -{ - struct lpe_softc *sc = device_get_softc(dev); - struct ifnet *ifp; -#ifndef __rtems__ - int rid, i; - uint32_t val; -#else /* __rtems__ */ - int rid; -#endif /* __rtems__ */ - - sc->lpe_dev = dev; -#ifndef __rtems__ - sc->lpe_ofw = ofw_bus_get_node(dev); - - i = OF_getprop(sc->lpe_ofw, "local-mac-address", (void *)&sc->lpe_enaddr, 6); - if (i != 6) { - sc->lpe_enaddr[0] = 0x00; - sc->lpe_enaddr[1] = 0x11; - sc->lpe_enaddr[2] = 0x22; - sc->lpe_enaddr[3] = 0x33; - sc->lpe_enaddr[4] = 0x44; - sc->lpe_enaddr[5] = 0x55; - } -#else /* __rtems__ */ - rtems_bsd_get_mac_address(device_get_name(sc->lpe_dev), device_get_unit(sc->lpe_dev), sc->lpe_enaddr); -#endif /* __rtems__ */ - - mtx_init(&sc->lpe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF); - - callout_init_mtx(&sc->lpe_tick, &sc->lpe_mtx, 0); - - rid = 0; - sc->lpe_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (!sc->lpe_mem_res) { - device_printf(dev, "cannot allocate memory window\n"); - goto fail; - } - - sc->lpe_bst = rman_get_bustag(sc->lpe_mem_res); - sc->lpe_bsh = rman_get_bushandle(sc->lpe_mem_res); - - rid = 0; - sc->lpe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); - if (!sc->lpe_irq_res) { - device_printf(dev, "cannot allocate interrupt\n"); - goto fail; - } - - sc->lpe_ifp = if_alloc(IFT_ETHER); - if (!sc->lpe_ifp) { - device_printf(dev, "cannot allocated ifnet\n"); - goto fail; - } - - ifp = sc->lpe_ifp; - - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_softc = sc; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_start = lpe_start; - ifp->if_ioctl = lpe_ioctl; - ifp->if_init = lpe_init; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; - IFQ_SET_READY(&ifp->if_snd); - - ether_ifattach(ifp, sc->lpe_enaddr); - - if (bus_setup_intr(dev, sc->lpe_irq_res, INTR_TYPE_NET, NULL, - lpe_intr, sc, &sc->lpe_intrhand)) { - device_printf(dev, "cannot establish interrupt handler\n"); - ether_ifdetach(ifp); - goto fail; - } - - /* Enable Ethernet clock */ -#ifndef __rtems__ - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(3)); -#else /* __rtems__ */ -#ifdef LPC32XX_ETHERNET_RMII - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(3)); -#else - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(1)); -#endif -#endif /* __rtems__ */ - - /* Reset chip */ - lpe_reset(sc); - - /* Initialize MII */ -#ifndef __rtems__ - val = lpe_read_4(sc, LPE_COMMAND); - lpe_write_4(sc, LPE_COMMAND, val | LPE_COMMAND_RMII); - - if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd, - lpe_ifmedia_sts, BMSR_DEFCAPMASK, 0x01, - MII_OFFSET_ANY, 0)) { - device_printf(dev, "cannot find PHY\n"); - goto fail; - } -#else /* __rtems__ */ - if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd, - lpe_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, - MII_OFFSET_ANY, 0)) { - device_printf(dev, "cannot find PHY\n"); - goto fail; - } -#endif /* __rtems__ */ - - lpe_dma_alloc(sc); - - return (0); - -fail: - if (sc->lpe_ifp) - if_free(sc->lpe_ifp); - if (sc->lpe_intrhand) - bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand); - if (sc->lpe_irq_res) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res); - if (sc->lpe_mem_res) - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res); - return (ENXIO); -} +#define ETH_MCFG_RESETMIIMGMT BSP_BIT32(15) -static int -lpe_detach(device_t dev) -{ - struct lpe_softc *sc = device_get_softc(dev); +/* ETH_MCMD */ - lpe_stop(sc); +#define ETH_MCMD_READ BSP_BIT32(0) +#define ETH_MCMD_SCAN BSP_BIT32(1) - if_free(sc->lpe_ifp); - bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res); - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res); +/* ETH_MADR */ - return (0); -} +#define ETH_MADR_REG(val) BSP_FLD32(val, 0, 4) +#define ETH_MADR_PHY(val) BSP_FLD32(val, 8, 12) -static int -lpe_miibus_readreg(device_t dev, int phy, int reg) -{ - struct lpe_softc *sc = device_get_softc(dev); - uint32_t val; - int result; +/* ETH_MIND */ - lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ); - lpe_write_4(sc, LPE_MADR, - (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT | - (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT); +#define ETH_MIND_BUSY BSP_BIT32(0) +#define ETH_MIND_SCANNING BSP_BIT32(1) +#define ETH_MIND_NOT_VALID BSP_BIT32(2) +#define ETH_MIND_MII_LINK_FAIL BSP_BIT32(3) - val = lpe_read_4(sc, LPE_MIND); +/* Events */ - /* Wait until request is completed */ - while (val & LPE_MIND_BUSY) { - val = lpe_read_4(sc, LPE_MIND); - DELAY(10); - } +#define LPC_ETH_EVENT_INIT_RX RTEMS_EVENT_0 - if (val & LPE_MIND_INVALID) - return (0); +#define LPC_ETH_EVENT_INIT_TX RTEMS_EVENT_1 - lpe_write_4(sc, LPE_MCMD, 0); - result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK); - debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result); +#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3 - return (result); -} +#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4 -static int -lpe_miibus_writereg(device_t dev, int phy, int reg, int data) -{ - struct lpe_softc *sc = device_get_softc(dev); - uint32_t val; +/* Status */ - debugf("phy=%d reg=%d data=0x%04x\n", phy, reg, data); +#define LPC_ETH_INTERRUPT_RECEIVE \ + (ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE) - lpe_write_4(sc, LPE_MCMD, LPE_MCMD_WRITE); - lpe_write_4(sc, LPE_MADR, - (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT | - (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT); +#define LPC_ETH_RX_STAT_ERRORS \ + (ETH_RX_STAT_CRC_ERROR \ + | ETH_RX_STAT_SYMBOL_ERROR \ + | ETH_RX_STAT_LENGTH_ERROR \ + | ETH_RX_STAT_ALIGNMENT_ERROR \ + | ETH_RX_STAT_OVERRUN \ + | ETH_RX_STAT_NO_DESCRIPTOR) - lpe_write_4(sc, LPE_MWTD, (data & LPE_MWTD_DATAMASK)); +#define LPC_ETH_LAST_FRAGMENT_FLAGS \ + (ETH_TX_CTRL_OVERRIDE \ + | ETH_TX_CTRL_PAD \ + | ETH_TX_CTRL_CRC \ + | ETH_TX_CTRL_INTERRUPT \ + | ETH_TX_CTRL_LAST) - val = lpe_read_4(sc, LPE_MIND); +/* Debug */ - /* Wait until request is completed */ - while (val & LPE_MIND_BUSY) { - val = lpe_read_4(sc, LPE_MIND); - DELAY(10); - } +#ifdef DEBUG + #define LPC_ETH_PRINTF(...) printf(__VA_ARGS__) + #define LPC_ETH_PRINTK(...) printk(__VA_ARGS__) +#else + #define LPC_ETH_PRINTF(...) + #define LPC_ETH_PRINTK(...) +#endif - return (0); +typedef enum { + LPC_ETH_STATE_NOT_INITIALIZED = 0, + LPC_ETH_STATE_DOWN, + LPC_ETH_STATE_UP +} lpc_eth_state; + +typedef struct { + device_t dev; + struct ifnet *ifp; + struct mtx mtx; + lpc_eth_state state; + uint32_t anlpar; + struct callout watchdog_callout; + rtems_id receive_task; + unsigned rx_unit_count; + unsigned tx_unit_count; + volatile lpc_eth_transfer_descriptor *rx_desc_table; + volatile lpc_eth_receive_status *rx_status_table; + struct mbuf **rx_mbuf_table; + volatile lpc_eth_transfer_descriptor *tx_desc_table; + volatile uint32_t *tx_status_table; + void *tx_buf_table; + uint32_t tx_produce_index; + uint32_t tx_consume_index; + unsigned received_frames; + unsigned receive_interrupts; + unsigned transmitted_frames; + unsigned receive_drop_errors; + unsigned receive_overrun_errors; + unsigned receive_fragment_errors; + unsigned receive_crc_errors; + unsigned receive_symbol_errors; + unsigned receive_length_errors; + unsigned receive_alignment_errors; + unsigned receive_no_descriptor_errors; + unsigned receive_fatal_errors; + unsigned transmit_underrun_errors; + unsigned transmit_late_collision_errors; + unsigned transmit_excessive_collision_errors; + unsigned transmit_excessive_defer_errors; + unsigned transmit_no_descriptor_errors; + unsigned transmit_overflow_errors; + unsigned transmit_fatal_errors; + uint32_t phy_id; + int phy; + rtems_vector_number interrupt_number; + rtems_id control_task; + int if_flags; + struct ifmedia ifmedia; +} lpc_eth_driver_entry; + +static void lpc_eth_interface_watchdog(void *arg); + +static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e); + +static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e) +{ + rtems_status_code sc = rtems_event_transient_send(e->control_task); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); } -static void -lpe_miibus_statchg(device_t dev) +static void lpc_eth_control_request( + lpc_eth_driver_entry *e, + rtems_id task, + rtems_event_set event +) { - struct lpe_softc *sc = device_get_softc(dev); - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - -#ifndef __rtems__ - lpe_lock(sc); -#endif /* __rtems__ */ - - if ((mii->mii_media_status & IFM_ACTIVE) && - (mii->mii_media_status & IFM_AVALID)) - sc->lpe_flags |= LPE_FLAG_LINK; - else - sc->lpe_flags &= ~LPE_FLAG_LINK; - -#ifndef __rtems__ - lpe_unlock(sc); -#endif /* __rtems__ */ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + e->control_task = rtems_task_self(); + + sc = rtems_event_send(task, event); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + e->control_task = 0; } -static void -lpe_reset(struct lpe_softc *sc) +static inline uint32_t lpc_eth_increment( + uint32_t value, + uint32_t cycle +) { - uint32_t mac1; - -#ifndef __rtems__ - /* Enter soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX); - - /* Reset registers, Tx path and Rx path */ - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET | - LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET); - - /* Set station address */ - lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]); - lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]); - lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]); - - /* Leave soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX)); -#else /* __rtems__ */ - /* Reset registers, Tx path and Rx path */ - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET | LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET); - - /* Enter soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX); - - /* Leave soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX)); - - /* Reinitialize registers */ - lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(0x7)); - lpe_write_4(sc, LPE_MAC2, LPE_MAC2_PADCRCENABLE | LPE_MAC2_CRCENABLE | LPE_MAC2_FULLDUPLEX); - lpe_write_4(sc, LPE_IPGT, 0x15); - lpe_write_4(sc, LPE_IPGR, 0x12); - lpe_write_4(sc, LPE_CLRT, 0x370f); - lpe_write_4(sc, LPE_MAXF, 0x0600); - lpe_write_4(sc, LPE_SUPP, LPE_SUPP_SPEED); - lpe_write_4(sc, LPE_TEST, 0x0); -#ifdef LPC32XX_ETHERNET_RMII - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX | LPE_COMMAND_RMII); -#else - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX); -#endif - lpe_write_4(sc, LPE_INTENABLE, 0x0); - lpe_write_4(sc, LPE_INTCLEAR, 0x30ff); - lpe_write_4(sc, LPE_POWERDOWN, 0x0); - - /* Set station address */ - lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]); - lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]); - lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]); -#endif /* __rtems__ */ + if (value < cycle) { + return ++value; + } else { + return 0; + } } -static void -lpe_init(void *arg) +static void lpc_eth_enable_promiscous_mode(bool enable) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - - lpe_lock(sc); - lpe_init_locked(sc); - lpe_unlock(sc); + if (enable) { + lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_UNICAST + | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST + | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST; + } else { + lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_PERFECT + | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH + | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST; + } } -static void -lpe_init_locked(struct lpe_softc *sc) +static void lpc_eth_interrupt_handler(void *arg) { - struct ifnet *ifp = sc->lpe_ifp; - uint32_t cmd, mac1; - - lpe_lock_assert(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - return; + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; + rtems_event_set re = 0; + rtems_event_set te = 0; + uint32_t ie = 0; + + /* Get interrupt status */ + uint32_t im = lpc_eth->intenable; + uint32_t is = lpc_eth->intstatus & im; + + /* Check receive interrupts */ + if ((is & (ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN)) != 0) { + if ((is & ETH_INT_RX_OVERRUN) != 0) { + re = LPC_ETH_EVENT_INIT_RX; + ++e->receive_fatal_errors; + } + + if ((is & ETH_INT_TX_UNDERRUN) != 0) { + re = LPC_ETH_EVENT_INIT_TX; + ++e->transmit_fatal_errors; + } + } else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) { + re = LPC_ETH_EVENT_INTERRUPT; + ie |= LPC_ETH_INTERRUPT_RECEIVE; + ++e->receive_interrupts; + } + + /* Send events to receive task */ + if (re != 0) { + (void) rtems_event_send(e->receive_task, re); + } + + LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te); + + /* Update interrupt mask */ + lpc_eth->intenable = im & ~ie; + + /* Clear interrupts */ + lpc_eth->intclear = is; +} - /* Enable Tx and Rx */ - cmd = lpe_read_4(sc, LPE_COMMAND); - lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE | - LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME); +static void lpc_eth_enable_receive_interrupts(void) +{ + rtems_interrupt_level level; - /* Enable receive */ - mac1 = lpe_read_4(sc, LPE_MAC1); -#ifdef __rtems__ - (void)mac1; -#endif /* __rtems__ */ - lpe_write_4(sc, LPE_MAC1, /*mac1 |*/ LPE_MAC1_RXENABLE | LPE_MAC1_PASSALL); + rtems_interrupt_disable(level); + lpc_eth->intenable |= LPC_ETH_INTERRUPT_RECEIVE; + rtems_interrupt_enable(level); +} - lpe_write_4(sc, LPE_MAC2, LPE_MAC2_CRCENABLE | LPE_MAC2_PADCRCENABLE | - LPE_MAC2_FULLDUPLEX); +static void lpc_eth_disable_receive_interrupts(void) +{ + rtems_interrupt_level level; - lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7)); + rtems_interrupt_disable(level); + lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_RECEIVE; + rtems_interrupt_enable(level); +} - /* Set up Rx filter */ - lpe_set_rxmode(sc); +static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e) +{ + volatile uint32_t *const status = e->tx_status_table; + uint32_t const index_max = e->tx_unit_count - 1; + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + #endif + uint32_t produce_index; + + /* Disable transmitter */ + lpc_eth->command &= ~ETH_CMD_TX_ENABLE; + + /* Wait for inactive status */ + while ((lpc_eth->status & ETH_STAT_TX_ACTIVE) != 0) { + /* Wait */ + } + + /* Reset */ + lpc_eth->command |= ETH_CMD_TX_RESET; + + /* Transmit descriptors */ + lpc_eth->txdescriptornum = index_max; + lpc_eth->txdescriptor = (uint32_t) desc; + lpc_eth->txstatus = (uint32_t) status; + + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Discard outstanding fragments (= data loss) */ + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + m_freem(mbufs [produce_index]); + mbufs [produce_index] = NULL; + } + #else + /* Initialize descriptor table */ + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + desc [produce_index].start = + (uint32_t) (buf + produce_index * LPC_ETH_CONFIG_TX_BUF_SIZE); + } + #endif + + /* Initialize indices */ + e->tx_produce_index = lpc_eth->txproduceindex; + e->tx_consume_index = lpc_eth->txconsumeindex; + + /* Enable transmitter */ + lpc_eth->command |= ETH_CMD_TX_ENABLE; +} - /* Enable interrupts */ - lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR | - LPE_INT_RXFINISH | LPE_INT_RXDONE | LPE_INT_TXUNDERRUN | - LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE); +#define LPC_ETH_RX_DATA_OFFSET 2 - sc->lpe_cdata.lpe_tx_prod = 0; - sc->lpe_cdata.lpe_tx_last = 0; - sc->lpe_cdata.lpe_tx_used = 0; +static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait) +{ + struct mbuf *m = NULL; + int mw = wait ? M_WAITOK : M_NOWAIT; + + MGETHDR(m, mw, MT_DATA); + if (m != NULL) { + MCLGET(m, mw); + if ((m->m_flags & M_EXT) != 0) { + /* Set receive interface */ + m->m_pkthdr.rcvif = ifp; + + /* Adjust by two bytes for proper IP header alignment */ + m->m_data = mtod(m, char *) + LPC_ETH_RX_DATA_OFFSET; + + return m; + } else { + m_free(m); + } + } + + return NULL; +} - lpe_init_rx(sc); +static bool lpc_eth_add_new_mbuf( + struct ifnet *ifp, + volatile lpc_eth_transfer_descriptor *desc, + struct mbuf **mbufs, + uint32_t i, + bool wait +) +{ + /* New mbuf */ + struct mbuf *m = lpc_eth_new_mbuf(ifp, wait); + + /* Check mbuf */ + if (m != NULL) { + /* Cache invalidate */ + rtems_cache_invalidate_multiple_data_lines( + mtod(m, void *), + MCLBYTES - LPC_ETH_RX_DATA_OFFSET + ); + + /* Add mbuf to queue */ + desc [i].start = mtod(m, uint32_t); + desc [i].control = (MCLBYTES - LPC_ETH_RX_DATA_OFFSET - 1) + | ETH_RX_CTRL_INTERRUPT; + + /* Cache flush of descriptor */ + rtems_cache_flush_multiple_data_lines( + (void *) &desc [i], + sizeof(desc [0]) + ); + + /* Add mbuf to table */ + mbufs [i] = m; + + return true; + } else { + return false; + } +} - /* Initialize Rx packet and status descriptor heads */ - lpe_write_4(sc, LPE_RXDESC, sc->lpe_rdata.lpe_rx_ring_phys); - lpe_write_4(sc, LPE_RXSTATUS, sc->lpe_rdata.lpe_rx_status_phys); - lpe_write_4(sc, LPE_RXDESC_NUMBER, LPE_RXDESC_NUM - 1); - lpe_write_4(sc, LPE_RXDESC_CONS, 0); +static void lpc_eth_receive_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_event_set events = 0; + lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg; + struct ifnet *const ifp = e->ifp; + volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table; + volatile lpc_eth_receive_status *const status = e->rx_status_table; + struct mbuf **const mbufs = e->rx_mbuf_table; + uint32_t const index_max = e->rx_unit_count - 1; + uint32_t produce_index = 0; + uint32_t consume_index = 0; + + LPC_ETH_PRINTF("%s\n", __func__); + + /* Main event loop */ + while (true) { + /* Wait for events */ + sc = rtems_event_receive( + LPC_ETH_EVENT_INIT_RX + | LPC_ETH_EVENT_INIT_TX + | LPC_ETH_EVENT_STOP + | LPC_ETH_EVENT_INTERRUPT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events); + + /* Stop receiver? */ + if ((events & LPC_ETH_EVENT_STOP) != 0) { + lpc_eth_control_request_complete(e); + + /* Wait for events */ + continue; + } + + /* Initialize receiver or transmitter? */ + if ((events & (LPC_ETH_EVENT_INIT_RX | LPC_ETH_EVENT_INIT_TX)) != 0) { + if ((events & LPC_ETH_EVENT_INIT_RX) != 0) { + /* Disable receive interrupts */ + lpc_eth_disable_receive_interrupts(); + + /* Disable receiver */ + lpc_eth->command &= ~ETH_CMD_RX_ENABLE; + + /* Wait for inactive status */ + while ((lpc_eth->status & ETH_STAT_RX_ACTIVE) != 0) { + /* Wait */ + } + + /* Reset */ + lpc_eth->command |= ETH_CMD_RX_RESET; + + /* Clear receive interrupts */ + lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE; + + /* Move existing mbufs to the front */ + consume_index = 0; + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + if (mbufs [produce_index] != NULL) { + mbufs [consume_index] = mbufs [produce_index]; + ++consume_index; + } + } + + /* Fill receive queue */ + for ( + produce_index = consume_index; + produce_index <= index_max; + ++produce_index + ) { + lpc_eth_add_new_mbuf(ifp, desc, mbufs, produce_index, true); + } + + /* Receive descriptor table */ + lpc_eth->rxdescriptornum = index_max; + lpc_eth->rxdescriptor = (uint32_t) desc; + lpc_eth->rxstatus = (uint32_t) status; + + /* Initialize indices */ + produce_index = lpc_eth->rxproduceindex; + consume_index = lpc_eth->rxconsumeindex; + + /* Enable receiver */ + lpc_eth->command |= ETH_CMD_RX_ENABLE; + + /* Enable receive interrupts */ + lpc_eth_enable_receive_interrupts(); + + lpc_eth_control_request_complete(e); + } + + if ((events & LPC_ETH_EVENT_INIT_TX) != 0) { + LPE_LOCK(e); + lpc_eth_initialize_transmit(e); + LPE_UNLOCK(e); + } + + /* Wait for events */ + continue; + } + + while (true) { + /* Clear receive interrupt status */ + lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE; + + /* Get current produce index */ + produce_index = lpc_eth->rxproduceindex; + + if (consume_index != produce_index) { + uint32_t stat = 0; + + /* Fragment status */ + rtems_cache_invalidate_multiple_data_lines( + (void *) &status [consume_index], + sizeof(status [0]) + ); + stat = status [consume_index].info; + + if ( + (stat & ETH_RX_STAT_LAST_FLAG) != 0 + && (stat & LPC_ETH_RX_STAT_ERRORS) == 0 + ) { + /* Received mbuf */ + struct mbuf *m = mbufs [consume_index]; + + if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) { + /* Discard Ethernet CRC */ + int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1 - ETHER_CRC_LEN; + + /* Update mbuf */ + m->m_len = sz; + m->m_pkthdr.len = sz; + + LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz); + + /* Hand over */ + (*ifp->if_input)(ifp, m); + + /* Increment received frames counter */ + ++e->received_frames; + } else { + ++e->receive_drop_errors; + } + } else { + /* Update error counters */ + if ((stat & ETH_RX_STAT_OVERRUN) != 0) { + ++e->receive_overrun_errors; + } + if ((stat & ETH_RX_STAT_LAST_FLAG) == 0) { + ++e->receive_fragment_errors; + } + if ((stat & ETH_RX_STAT_CRC_ERROR) != 0) { + ++e->receive_crc_errors; + } + if ((stat & ETH_RX_STAT_SYMBOL_ERROR) != 0) { + ++e->receive_symbol_errors; + } + if ((stat & ETH_RX_STAT_LENGTH_ERROR) != 0) { + ++e->receive_length_errors; + } + if ((stat & ETH_RX_STAT_ALIGNMENT_ERROR) != 0) { + ++e->receive_alignment_errors; + } + if ((stat & ETH_RX_STAT_NO_DESCRIPTOR) != 0) { + ++e->receive_no_descriptor_errors; + } + } + + /* Increment and update consume index */ + consume_index = lpc_eth_increment(consume_index, index_max); + lpc_eth->rxconsumeindex = consume_index; + } else { + /* Nothing to do, enable receive interrupts */ + lpc_eth_enable_receive_interrupts(); + break; + } + } + } +} - /* Initialize Tx packet and status descriptor heads */ - lpe_write_4(sc, LPE_TXDESC, sc->lpe_rdata.lpe_tx_ring_phys); - lpe_write_4(sc, LPE_TXSTATUS, sc->lpe_rdata.lpe_tx_status_phys); - lpe_write_4(sc, LPE_TXDESC_NUMBER, LPE_TXDESC_NUM - 1); - lpe_write_4(sc, LPE_TXDESC_PROD, 0); +static struct mbuf *lpc_eth_next_fragment( + struct ifnet *ifp, + struct mbuf *m, + uint32_t *ctrl +) +{ + struct mbuf *n; + int size; + + while (true) { + /* Get fragment size */ + size = m->m_len; + + if (size > 0) { + /* Now we have a not empty fragment */ + break; + } else { + /* Skip empty fragments */ + m = m->m_next; + + if (m == NULL) { + return NULL; + } + } + } + + /* Set fragment size */ + *ctrl = (uint32_t) (size - 1); + + /* Discard empty successive fragments */ + n = m->m_next; + while (n != NULL && n->m_len <= 0) { + n = m_free(n); + } + m->m_next = n; + + /* Is our fragment the last in the frame? */ + if (n == NULL) { + *ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS; + } + + return m; +} - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +static void lpc_eth_tx_reclaim(lpc_eth_driver_entry *e, struct ifnet *ifp) +{ + volatile uint32_t *const status = e->tx_status_table; + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + #endif + uint32_t const index_max = e->tx_unit_count - 1; + uint32_t consume_index = e->tx_consume_index; + + /* Free consumed fragments */ + while (true) { + /* Save last known consume index */ + uint32_t c = consume_index; + + /* Get new consume index */ + consume_index = lpc_eth->txconsumeindex; + + /* Nothing consumed in the meantime? */ + if (c == consume_index) { + break; + } + + while (c != consume_index) { + uint32_t s = status [c]; + + /* Update error counters */ + if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) { + if ((s & ETH_TX_STAT_UNDERRUN) != 0) { + ++e->transmit_underrun_errors; + } + if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) { + ++e->transmit_late_collision_errors; + } + if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) { + ++e->transmit_excessive_collision_errors; + } + if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) { + ++e->transmit_excessive_defer_errors; + } + if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) { + ++e->transmit_no_descriptor_errors; + } + } + + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Release mbuf */ + m_freem(mbufs [c]); + mbufs [c] = NULL; + #endif + + /* Next consume index */ + c = lpc_eth_increment(c, index_max); + } + } + + e->tx_consume_index = consume_index; +} - callout_reset(&sc->lpe_tick, hz, lpe_tick, sc); +static int lpc_eth_tx_enqueue( + lpc_eth_driver_entry *e, + struct ifnet *ifp, + struct mbuf *m0 +) +{ + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + uint32_t frame_length; + char *frame_buffer; + #endif + uint32_t const index_max = e->tx_unit_count - 1; + uint32_t produce_index = e->tx_produce_index; + uint32_t consume_index = e->tx_consume_index; + struct mbuf *m = m0; + + while (true) { + uint32_t ctrl; + + /* Compute next produce index */ + uint32_t p = lpc_eth_increment(produce_index, index_max); + + /* Queue full? */ + if (p == consume_index) { + LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m); + + /* The queue is full */ + return ENOBUFS; + } + + /* Get next fragment and control value */ + m = lpc_eth_next_fragment(ifp, m, &ctrl); + + /* New fragment? */ + if (m != NULL) { + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Set the transfer data */ + rtems_cache_flush_multiple_data_lines( + mtod(m, const void *), + (size_t) m->m_len + ); + desc [produce_index].start = mtod(m, uint32_t); + desc [produce_index].control = ctrl; + rtems_cache_flush_multiple_data_lines( + (void *) &desc [produce_index], + sizeof(desc [0]) + ); + + LPC_ETH_PRINTF( + "tx: %02" PRIu32 ": %u %s\n", + produce_index, m->m_len, + (ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : "" + ); + + /* Next produce index */ + produce_index = p; + + /* Last fragment of a frame? */ + if ((ctrl & ETH_TX_CTRL_LAST) != 0) { + /* Update the produce index */ + lpc_eth->txproduceindex = produce_index; + e->tx_produce_index = produce_index; + + mbufs [produce_index] = m0; + + /* Increment transmitted frames counter */ + ++e->transmitted_frames; + + return 0; + } + + /* Next fragment of the frame */ + m = m->m_next; + #else + size_t fragment_length = (size_t) m->m_len; + void *fragment_start = mtod(m, void *); + uint32_t new_frame_length = frame_length + fragment_length; + + /* Check buffer size */ + if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) { + LPC_ETH_PRINTF("tx: overflow\n"); + + /* Discard overflow data */ + new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE; + fragment_length = new_frame_length - frame_length; + + /* Finalize frame */ + ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS; + + /* Update error counter */ + ++e->transmit_overflow_errors; + } + + LPC_ETH_PRINTF( + "tx: copy: %" PRIu32 "%s%s\n", + fragment_length, + (m->m_flags & M_EXT) != 0 ? ", E" : "", + (m->m_flags & M_PKTHDR) != 0 ? ", H" : "" + ); + + /* Copy fragment to buffer in Ethernet RAM */ + memcpy(frame_buffer, fragment_start, fragment_length); + + if ((ctrl & ETH_TX_CTRL_LAST) != 0) { + /* Finalize descriptor */ + desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK) + | (new_frame_length - 1); + + LPC_ETH_PRINTF( + "tx: %02" PRIu32 ": %" PRIu32 "\n", + produce_index, + new_frame_length + ); + + /* Cache flush of data */ + rtems_cache_flush_multiple_data_lines( + (const void *) desc [produce_index].start, + new_frame_length + ); + + /* Cache flush of descriptor */ + rtems_cache_flush_multiple_data_lines( + (void *) &desc [produce_index], + sizeof(desc [0]) + ); + + /* Next produce index */ + produce_index = p; + + /* Update the produce index */ + lpc_eth->txproduceindex = produce_index; + + /* Fresh frame length and buffer start */ + frame_length = 0; + frame_buffer = (char *) desc [produce_index].start; + + /* Increment transmitted frames counter */ + ++e->transmitted_frames; + } else { + /* New frame length */ + frame_length = new_frame_length; + + /* Update current frame buffer start */ + frame_buffer += fragment_length; + } + + /* Free mbuf and get next */ + m = m_free(m); + #endif + } else { + /* Nothing to transmit */ + m_freem(m0); + return 0; + } + } } -static void -lpe_start(struct ifnet *ifp) +static int lpc_eth_mdio_wait_for_not_busy(void) { - struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc; + rtems_interval one_second = rtems_clock_get_ticks_per_second(); + rtems_interval i = 0; - lpe_lock(sc); - lpe_start_locked(ifp); - lpe_unlock(sc); + while ((lpc_eth->mind & ETH_MIND_BUSY) != 0 && i < one_second) { + rtems_task_wake_after(1); + ++i; + } + + LPC_ETH_PRINTK("tx: lpc_eth_mdio_wait %s after %d\n", + i != one_second? "succeed": "timeout", i); + + return i != one_second ? 0 : ETIMEDOUT; } -static void -lpe_start_locked(struct ifnet *ifp) +static uint32_t lpc_eth_mdio_read_anlpar(int phy) { - struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc; - struct mbuf *m_head; - int encap = 0; + uint32_t madr = ETH_MADR_REG(MII_ANLPAR) | ETH_MADR_PHY(phy); + uint32_t anlpar = 0; + int eno = 0; - lpe_lock_assert(sc); + if (lpc_eth->madr != madr) { + lpc_eth->madr = madr; + } - while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { - if (lpe_read_4(sc, LPE_TXDESC_PROD) == - lpe_read_4(sc, LPE_TXDESC_CONS) - 5) - break; + if (lpc_eth->mcmd != ETH_MCMD_READ) { + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; + } - /* Dequeue first packet */ - IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); - if (!m_head) - break; + eno = lpc_eth_mdio_wait_for_not_busy(); + if (eno == 0) { + anlpar = lpc_eth->mrdd; + } - lpe_encap(sc, &m_head); + /* Start next read */ + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; - encap++; - } - - /* Submit new descriptor list */ - if (encap) { - lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod); - sc->lpe_watchdog_timer = 5; - } - + return anlpar; } -#ifdef __rtems__ -static int -lpe_get_segs_for_tx(struct mbuf *m, bus_dma_segment_t segs[LPE_MAXFRAGS], - int *nsegs) +static int lpc_eth_mdio_read( + int phy, + void *arg RTEMS_UNUSED, + unsigned reg, + uint32_t *val +) { - int i = 0; - - do { - if (m->m_len > 0) { - segs[i].ds_addr = mtod(m, bus_addr_t); - segs[i].ds_len = m->m_len; -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(m->m_data, m->m_len); -#endif - ++i; - } - m = m->m_next; - if (m == NULL) { - *nsegs = i; - return (0); - } - } while (i < LPE_MAXFRAGS); - return (EFBIG); + int eno = 0; + + if (0 <= phy && phy <= 31) { + lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy); + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; + eno = lpc_eth_mdio_wait_for_not_busy(); + + if (eno == 0) { + *val = lpc_eth->mrdd; + } + } else { + eno = EINVAL; + } + + return eno; } -#endif /* __rtems__ */ -static int -lpe_encap(struct lpe_softc *sc, struct mbuf **m_head) -{ - struct lpe_txdesc *txd; - struct lpe_hwdesc *hwd; - bus_dma_segment_t segs[LPE_MAXFRAGS]; - int i, err, nsegs, prod; - - lpe_lock_assert(sc); - M_ASSERTPKTHDR((*m_head)); - - prod = sc->lpe_cdata.lpe_tx_prod; - txd = &sc->lpe_cdata.lpe_tx_desc[prod]; - - debugf("starting with prod=%d\n", prod); - -#ifndef __rtems__ - err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); -#else /* __rtems__ */ - err = lpe_get_segs_for_tx(*m_head, segs, &nsegs); -#endif /* __rtems__ */ - - if (err) - return (err); - - if (nsegs == 0) { - m_freem(*m_head); - *m_head = NULL; - return (EIO); - } - -#ifndef __rtems__ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap, - BUS_DMASYNC_PREREAD); -#endif /* __rtems__ */ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - - txd->lpe_txdesc_first = 1; - txd->lpe_txdesc_mbuf = *m_head; - - for (i = 0; i < nsegs; i++) { - hwd = &sc->lpe_rdata.lpe_tx_ring[prod]; - hwd->lhr_data = segs[i].ds_addr; - hwd->lhr_control = segs[i].ds_len - 1; - - if (i == nsegs - 1) { - hwd->lhr_control |= LPE_HWDESC_LASTFLAG; - hwd->lhr_control |= LPE_HWDESC_INTERRUPT; - hwd->lhr_control |= LPE_HWDESC_CRC; - hwd->lhr_control |= LPE_HWDESC_PAD; - } - -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd)); -#endif -#endif /* __rtems__ */ - LPE_INC(prod, LPE_TXDESC_NUM); - } - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +static int lpc_eth_mdio_write( + int phy, + void *arg RTEMS_UNUSED, + unsigned reg, + uint32_t val +) +{ + int eno = 0; - sc->lpe_cdata.lpe_tx_used += nsegs; - sc->lpe_cdata.lpe_tx_prod = prod; + if (0 <= phy && phy <= 31) { + lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy); + lpc_eth->mwtd = val; + eno = lpc_eth_mdio_wait_for_not_busy(); + } else { + eno = EINVAL; + } - return (0); + return eno; } -static void -lpe_stop(struct lpe_softc *sc) +static int lpc_eth_phy_get_id(int phy, uint32_t *id) { - lpe_lock(sc); - lpe_stop_locked(sc); - lpe_unlock(sc); -} + uint32_t id1 = 0; + int eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR1, &id1); -static void -lpe_stop_locked(struct lpe_softc *sc) -{ - lpe_lock_assert(sc); + if (eno == 0) { + uint32_t id2 = 0; - callout_stop(&sc->lpe_tick); + eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR2, &id2); + if (eno == 0) { + *id = (id1 << 16) | (id2 & 0xfff0); + } + } - /* Disable interrupts */ - lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff); + return eno; +} - /* Stop EMAC */ - lpe_write_4(sc, LPE_MAC1, 0); - lpe_write_4(sc, LPE_MAC2, 0); - lpe_write_4(sc, LPE_COMMAND, 0); +#define PHY_KSZ80X1RNL 0x221550 +#define PHY_DP83848 0x20005c90 - sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; -} +typedef struct { + unsigned reg; + uint32_t set; + uint32_t clear; +} lpc_eth_phy_action; -static int -lpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +static int lpc_eth_phy_set_and_clear( + lpc_eth_driver_entry *e, + const lpc_eth_phy_action *actions, + size_t n +) { - struct lpe_softc *sc = ifp->if_softc; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - struct ifreq *ifr = (struct ifreq *)data; - int err = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - lpe_lock(sc); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - lpe_set_rxmode(sc); - lpe_set_rxfilter(sc); - } else - lpe_init_locked(sc); - } else - lpe_stop(sc); - lpe_unlock(sc); - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - lpe_lock(sc); - lpe_set_rxfilter(sc); - lpe_unlock(sc); - } - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); - break; - default: - err = ether_ioctl(ifp, cmd, data); - break; - } - - return (err); + int eno = 0; + size_t i; + + for (i = 0; eno == 0 && i < n; ++i) { + const lpc_eth_phy_action *action = &actions [i]; + uint32_t val; + + eno = lpc_eth_mdio_read(e->phy, NULL, action->reg, &val); + if (eno == 0) { + val |= action->set; + val &= ~action->clear; + eno = lpc_eth_mdio_write(e->phy, NULL, action->reg, val); + } + } + + return eno; } -static void lpe_set_rxmode(struct lpe_softc *sc) -{ - struct ifnet *ifp = sc->lpe_ifp; - uint32_t rxfilt; +static const lpc_eth_phy_action lpc_eth_phy_up_action_default [] = { + { MII_BMCR, 0, BMCR_PDOWN }, + { MII_BMCR, BMCR_RESET, 0 }, + { MII_BMCR, BMCR_AUTOEN, 0 } +}; - rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT; +static const lpc_eth_phy_action lpc_eth_phy_up_pre_action_KSZ80X1RNL [] = { + /* Disable slow oscillator mode */ + { 0x11, 0, 0x10 } +}; - if (ifp->if_flags & IFF_BROADCAST) - rxfilt |= LPE_RXFILTER_BROADCAST; +static const lpc_eth_phy_action lpc_eth_phy_up_post_action_KSZ80X1RNL [] = { + /* Enable energy detect power down (EDPD) mode */ + { 0x18, 0x0800, 0 }, + /* Turn PLL of automatically in EDPD mode */ + { 0x10, 0x10, 0 } +}; - if (ifp->if_flags & IFF_PROMISC) - rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST; +static int lpc_eth_phy_up(lpc_eth_driver_entry *e) +{ + int eno; + int retries = 64; + uint32_t val; + + e->phy = DEFAULT_PHY - 1; + while (true) { + e->phy = (e->phy + 1) % 32; + + --retries; + eno = lpc_eth_phy_get_id(e->phy, &e->phy_id); + if ( + (eno == 0 && e->phy_id != 0xfffffff0 && e->phy_id != 0) + || retries <= 0 + ) { + break; + } + + rtems_task_wake_after(1); + } + + LPC_ETH_PRINTF("lpc_eth_phy_get_id: 0x%08" PRIx32 " from phy %d retries %d\n", + e->phy_id, e->phy, retries); + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_pre_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_pre_action_KSZ80X1RNL) + ); + break; + case PHY_DP83848: + eno = lpc_eth_mdio_read(e->phy, NULL, 0x17, &val); + LPC_ETH_PRINTF("phy PHY_DP83848 RBR 0x%08" PRIx32 "\n", val); + /* val = 0x21; */ + val = 0x32 ; + eno = lpc_eth_mdio_write(e->phy, NULL, 0x17, val); + break; + case 0: + case 0xfffffff0: + eno = EIO; + e->phy = DEFAULT_PHY; + break; + default: + break; + } + + if (eno == 0) { + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_action_default [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_action_default) + ); + } + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_post_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_post_action_KSZ80X1RNL) + ); + break; + default: + break; + } + } + } else { + e->phy_id = 0; + } + + return eno; +} - if (ifp->if_flags & IFF_ALLMULTI) - rxfilt |= LPE_RXFILTER_MULTICAST; +static const lpc_eth_phy_action lpc_eth_phy_down_action_default [] = { + { MII_BMCR, BMCR_PDOWN, 0 } +}; - lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt); -} +static const lpc_eth_phy_action lpc_eth_phy_down_post_action_KSZ80X1RNL [] = { + /* Enable slow oscillator mode */ + { 0x11, 0x10, 0 } +}; -static void lpe_set_rxfilter(struct lpe_softc *sc) +static void lpc_eth_phy_down(lpc_eth_driver_entry *e) { - struct ifnet *ifp = sc->lpe_ifp; - struct ifmultiaddr *ifma; - int index; - uint32_t hashl, hashh; - - hashl = 0; - hashh = 0; - - if_maddr_rlock(ifp); - CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - - index = ether_crc32_be(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f; - - if (index > 31) - hashh |= (1 << (index - 32)); - else - hashl |= (1 << index); - } - if_maddr_runlock(ifp); - - /* Program new hash filter */ - lpe_write_4(sc, LPE_HASHFILTER_L, hashl); - lpe_write_4(sc, LPE_HASHFILTER_H, hashh); + int eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_down_action_default [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_down_action_default) + ); + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_down_post_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_down_post_action_KSZ80X1RNL) + ); + break; + default: + break; + } + } } -static void -lpe_intr(void *arg) +static void lpc_eth_soft_reset(void) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - uint32_t intstatus; - - debugf("status=0x%08x\n", lpe_read_4(sc, LPE_INTSTATUS)); - - lpe_lock(sc); - - while ((intstatus = lpe_read_4(sc, LPE_INTSTATUS))) { - if (intstatus & LPE_INT_RXDONE) - lpe_rxintr(sc); - -#ifndef __rtems__ - if (intstatus & LPE_INT_TXDONE) - lpe_txintr(sc); - -#else /* __rtems__ */ - if (intstatus & LPE_INT_TXUNDERRUN) { - if_inc_counter(sc->lpe_ifp, IFCOUNTER_OERRORS, 1); - lpe_stop_locked(sc); - lpe_init_locked(sc); - } - else if (intstatus & (LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE)) - lpe_txintr(sc); -#endif /* __rtems__ */ - lpe_write_4(sc, LPE_INTCLEAR, 0xffff); - } - - lpe_unlock(sc); + lpc_eth->command = 0x38; + lpc_eth->mac1 = 0xcf00; + lpc_eth->mac1 = 0x0; } -static void -lpe_rxintr(struct lpe_softc *sc) +static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up) { - struct ifnet *ifp = sc->lpe_ifp; - struct lpe_hwdesc *hwd; - struct lpe_hwstatus *hws; - struct lpe_rxdesc *rxd; - struct mbuf *m; - int prod, cons; - - for (;;) { - prod = lpe_read_4(sc, LPE_RXDESC_PROD); - cons = lpe_read_4(sc, LPE_RXDESC_CONS); - - if (prod == cons) - break; - - rxd = &sc->lpe_cdata.lpe_rx_desc[cons]; - hwd = &sc->lpe_rdata.lpe_rx_ring[cons]; - hws = &sc->lpe_rdata.lpe_rx_status[cons]; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(rxd, sizeof(*rxd)); - rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd)); - rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws)); -#endif -#endif /* __rtems__ */ - - /* Check received frame for errors */ - if (hws->lhs_info & LPE_HWDESC_RXERRS) { - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - lpe_discard_rxbuf(sc, cons); - lpe_init_rxbuf(sc, cons); - goto skip; - } - - m = rxd->lpe_rxdesc_mbuf; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len); -#endif -#endif /* __rtems__ */ - m->m_pkthdr.rcvif = ifp; - m->m_data += 2; - - if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); - - lpe_unlock(sc); - (*ifp->if_input)(ifp, m); - lpe_lock(sc); - - lpe_init_rxbuf(sc, cons); -skip: - LPE_INC(cons, LPE_RXDESC_NUM); - lpe_write_4(sc, LPE_RXDESC_CONS, cons); - } + int eno = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct ifnet *ifp = e->ifp; + + if (up && e->state == LPC_ETH_STATE_DOWN) { + lpc_eth_config_module_enable(); + + /* Enable RX/TX reset and disable soft reset */ + lpc_eth->mac1 = 0xf00; + + /* Initialize PHY */ + /* Clock value 10 (divide by 44 ) is safe on LPC178x up to 100 MHz AHB clock */ + lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10) | ETH_MCFG_RESETMIIMGMT; + rtems_task_wake_after(1); + lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10); + rtems_task_wake_after(1); + eno = lpc_eth_phy_up(e); + + if (eno == 0) { + const uint8_t *eaddr; + + /* + * We must have a valid external clock from the PHY at this point, + * otherwise the system bus hangs and only a watchdog reset helps. + */ + lpc_eth_soft_reset(); + + /* Reinitialize registers */ + lpc_eth->mac2 = 0x31; + lpc_eth->ipgt = 0x15; + lpc_eth->ipgr = 0x12; + lpc_eth->clrt = 0x370f; + lpc_eth->maxf = 0x0600; + lpc_eth->supp = ETH_SUPP_SPEED; + lpc_eth->test = 0; + #ifdef LPC_ETH_CONFIG_RMII + lpc_eth->command = 0x0600; + #else + lpc_eth->command = 0x0400; + #endif + lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN; + lpc_eth->intclear = 0x30ff; + lpc_eth->powerdown = 0; + + /* MAC address */ + eaddr = IF_LLADDR(e->ifp); + lpc_eth->sa0 = ((uint32_t) eaddr [5] << 8) | (uint32_t) eaddr [4]; + lpc_eth->sa1 = ((uint32_t) eaddr [3] << 8) | (uint32_t) eaddr [2]; + lpc_eth->sa2 = ((uint32_t) eaddr [1] << 8) | (uint32_t) eaddr [0]; + + lpc_eth_setup_rxfilter(e); + + /* Enable receiver */ + lpc_eth->mac1 = 0x03; + + /* Initialize tasks */ + lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_INIT_RX); + lpc_eth_initialize_transmit(e); + + /* Install interrupt handler */ + sc = rtems_interrupt_handler_install( + e->interrupt_number, + "Ethernet", + RTEMS_INTERRUPT_UNIQUE, + lpc_eth_interrupt_handler, + e + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + /* Start watchdog timer */ + callout_reset(&e->watchdog_callout, hz, lpc_eth_interface_watchdog, e); + + /* Change state */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + e->state = LPC_ETH_STATE_UP; + } + } else if (!up && e->state == LPC_ETH_STATE_UP) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* Remove interrupt handler */ + sc = rtems_interrupt_handler_remove( + e->interrupt_number, + lpc_eth_interrupt_handler, + e + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + /* Stop task */ + lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP); + + lpc_eth_soft_reset(); + lpc_eth_phy_down(e); + lpc_eth_config_module_disable(); + + /* Stop watchdog timer */ + callout_stop(&e->watchdog_callout); + + /* Change state */ + e->state = LPC_ETH_STATE_DOWN; + } + + return eno; } -static void -lpe_txintr(struct lpe_softc *sc) +static void lpc_eth_interface_init(void *arg) { - struct ifnet *ifp = sc->lpe_ifp; - struct lpe_hwdesc *hwd; - struct lpe_hwstatus *hws; - struct lpe_txdesc *txd; - int cons, last; - - for (;;) { - cons = lpe_read_4(sc, LPE_TXDESC_CONS); - last = sc->lpe_cdata.lpe_tx_last; - - if (cons == last) - break; - - txd = &sc->lpe_cdata.lpe_tx_desc[last]; - hwd = &sc->lpe_rdata.lpe_tx_ring[last]; - hws = &sc->lpe_rdata.lpe_tx_status[last]; - -#ifndef __rtems__ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE); -#else /* __rtems__ */ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(txd, sizeof(*txd)); - rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd)); - rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws)); -#endif -#endif /* __rtems__ */ - - if_inc_counter(ifp, IFCOUNTER_COLLISIONS, LPE_HWDESC_COLLISIONS(hws->lhs_info)); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; - if (hws->lhs_info & LPE_HWDESC_TXERRS) - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - else - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - - if (txd->lpe_txdesc_first) { -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap); -#endif /* __rtems__ */ - - m_freem(txd->lpe_txdesc_mbuf); - txd->lpe_txdesc_mbuf = NULL; - txd->lpe_txdesc_first = 0; - } + (void) lpc_eth_up_or_down(e, true); +} - sc->lpe_cdata.lpe_tx_used--; - LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM); - } +static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e) +{ + struct ifnet *ifp = e->ifp; + + lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0); + + if ((ifp->if_flags & IFF_ALLMULTI)) { + lpc_eth->hashfilterl = 0xffffffff; + lpc_eth->hashfilterh = 0xffffffff; + } else { + struct ifmultiaddr *ifma; + + lpc_eth->hashfilterl = 0x0; + lpc_eth->hashfilterh = 0x0; + + if_maddr_rlock(ifp); + CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + uint32_t crc; + uint32_t index; + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + /* XXX: ether_crc32_le() does not work, why? */ + crc = ether_crc32_be( + LLADDR((struct sockaddr_dl *) ifma->ifma_addr), + ETHER_ADDR_LEN + ); + index = (crc >> 23) & 0x3f; + + if (index < 32) { + lpc_eth->hashfilterl |= 1U << index; + } else { + lpc_eth->hashfilterh |= 1U << (index - 32); + } + } + if_maddr_runlock(ifp); + } +} - if (!sc->lpe_cdata.lpe_tx_used) - sc->lpe_watchdog_timer = 0; +static int lpc_eth_interface_ioctl( + struct ifnet *ifp, + ioctl_command_t cmd, + caddr_t data +) +{ + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int eno = 0; + + LPC_ETH_PRINTF("%s\n", __func__); + + switch (cmd) { + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + eno = ifmedia_ioctl(ifp, ifr, &e->ifmedia, cmd); + break; + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl(ifp, cmd, data); + break; + case SIOCSIFFLAGS: + LPE_LOCK(e); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ e->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) { + lpc_eth_setup_rxfilter(e); + } + } else { + eno = lpc_eth_up_or_down(e, true); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + eno = lpc_eth_up_or_down(e, false); + } + } + e->if_flags = ifp->if_flags; + LPE_UNLOCK(e); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + LPE_LOCK(e); + lpc_eth_setup_rxfilter(e); + LPE_UNLOCK(e); + } + break; + default: + eno = ether_ioctl(ifp, cmd, data); + break; + } + + return eno; } -static void -lpe_tick(void *arg) +static int lpc_eth_interface_transmit(struct ifnet *ifp, struct mbuf *m) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; + int eno; - lpe_lock_assert(sc); - - mii_tick(mii); - lpe_watchdog(sc); + LPE_LOCK(e); - callout_reset(&sc->lpe_tick, hz, lpe_tick, sc); -} + if (e->state == LPC_ETH_STATE_UP) { + eno = lpc_eth_tx_enqueue(e, ifp, m); + lpc_eth_tx_reclaim(e, ifp); -static void -lpe_watchdog(struct lpe_softc *sc) -{ - struct ifnet *ifp = sc->lpe_ifp; + if (__predict_false(eno != 0)) { + struct mbuf *n; - lpe_lock_assert(sc); + n = m_defrag(m, M_NOWAIT); + if (n != NULL) { + m = n; + } - if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--) - return; + eno = lpc_eth_tx_enqueue(e, ifp, m); + } + } else { + eno = ENETDOWN; + } - /* Chip has stopped responding */ - device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n"); - lpe_stop_locked(sc); - lpe_init_locked(sc); + if (eno != 0) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + } - /* Try to resend packets */ - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - lpe_start_locked(ifp); + LPE_UNLOCK(e); + return eno; } -static int -lpe_dma_alloc(struct lpe_softc *sc) +static void lpc_eth_interface_watchdog(void *arg) { - int err; - - /* Create parent DMA tag */ - err = bus_dma_tag_create( - bus_get_dma_tag(sc->lpe_dev), - 1, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */ - BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_parent_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create parent DMA tag\n"); - return (err); - } - - err = lpe_dma_alloc_rx(sc); - if (err) - return (err); - - err = lpe_dma_alloc_tx(sc); - if (err) - return (err); - - return (0); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; + + if (e->state == LPC_ETH_STATE_UP) { + uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy); + + if (e->anlpar != anlpar) { + bool full_duplex = false; + bool speed = false; + + e->anlpar = anlpar; + + if ((anlpar & ANLPAR_TX_FD) != 0) { + full_duplex = true; + speed = true; + } else if ((anlpar & ANLPAR_T4) != 0) { + speed = true; + } else if ((anlpar & ANLPAR_TX) != 0) { + speed = true; + } else if ((anlpar & ANLPAR_10_FD) != 0) { + full_duplex = true; + } + + if (full_duplex) { + lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX; + } else { + lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX; + } + + if (speed) { + lpc_eth->supp |= ETH_SUPP_SPEED; + } else { + lpc_eth->supp &= ~ETH_SUPP_SPEED; + } + } + + callout_reset(&e->watchdog_callout, WATCHDOG_TIMEOUT * hz, lpc_eth_interface_watchdog, e); + } } -static int -lpe_dma_alloc_rx(struct lpe_softc *sc) +static int lpc_eth_media_change(struct ifnet *ifp) { - struct lpe_rxdesc *rxd; - struct lpe_dmamap_arg ctx; - int err, i; - - /* Create tag for Rx ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_RXDESC_SIZE, 1, /* maxsize, nsegments */ - LPE_RXDESC_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_ring_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx ring DMA tag\n"); - goto fail; - } - - /* Create tag for Rx status ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_RXSTATUS_SIZE, 1, /* maxsize, nsegments */ - LPE_RXSTATUS_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_status_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx status ring DMA tag\n"); - goto fail; - } - - /* Create tag for Rx buffers */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - MCLBYTES * LPE_RXDESC_NUM, /* maxsize */ - LPE_RXDESC_NUM, /* segments */ - MCLBYTES, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_buf_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx buffers DMA tag\n"); - goto fail; - } - - /* Allocate Rx DMA ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_ring_tag, - (void **)&sc->lpe_rdata.lpe_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_ring_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_ring_tag, - sc->lpe_cdata.lpe_rx_ring_map, sc->lpe_rdata.lpe_rx_ring, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_rx_ring_phys = ctx.lpe_dma_busaddr; - - /* Allocate Rx status ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_status_tag, - (void **)&sc->lpe_rdata.lpe_rx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_status_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_status_tag, - sc->lpe_cdata.lpe_rx_status_map, sc->lpe_rdata.lpe_rx_status, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_rx_status_phys = ctx.lpe_dma_busaddr; - - - /* Create Rx buffers DMA map */ - for (i = 0; i < LPE_RXDESC_NUM; i++) { - rxd = &sc->lpe_cdata.lpe_rx_desc[i]; - rxd->lpe_rxdesc_mbuf = NULL; -#ifndef __rtems__ - rxd->lpe_rxdesc_dmamap = NULL; - - err = bus_dmamap_create(sc->lpe_cdata.lpe_rx_buf_tag, 0, - &rxd->lpe_rxdesc_dmamap); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx DMA map\n"); - return (err); - } -#endif /* __rtems__ */ - } - - return (0); -fail: - return (err); + (void) ifp; + return EINVAL; } -static int -lpe_dma_alloc_tx(struct lpe_softc *sc) +static void lpc_eth_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct lpe_txdesc *txd; - struct lpe_dmamap_arg ctx; - int err, i; - - /* Create tag for Tx ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_TXDESC_SIZE, 1, /* maxsize, nsegments */ - LPE_TXDESC_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_ring_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx ring DMA tag\n"); - goto fail; - } - - /* Create tag for Tx status ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_TXSTATUS_SIZE, 1, /* maxsize, nsegments */ - LPE_TXSTATUS_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_status_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx status ring DMA tag\n"); - goto fail; - } - - /* Create tag for Tx buffers */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - MCLBYTES * LPE_TXDESC_NUM, /* maxsize */ - LPE_TXDESC_NUM, /* segments */ - MCLBYTES, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_buf_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx buffers DMA tag\n"); - goto fail; - } - - /* Allocate Tx DMA ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_ring_tag, - (void **)&sc->lpe_rdata.lpe_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_ring_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_ring_tag, - sc->lpe_cdata.lpe_tx_ring_map, sc->lpe_rdata.lpe_tx_ring, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_tx_ring_phys = ctx.lpe_dma_busaddr; - - /* Allocate Tx status ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_status_tag, - (void **)&sc->lpe_rdata.lpe_tx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_status_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_status_tag, - sc->lpe_cdata.lpe_tx_status_map, sc->lpe_rdata.lpe_tx_status, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_tx_status_phys = ctx.lpe_dma_busaddr; - - - /* Create Tx buffers DMA map */ - for (i = 0; i < LPE_TXDESC_NUM; i++) { - txd = &sc->lpe_cdata.lpe_tx_desc[i]; - txd->lpe_txdesc_mbuf = NULL; -#ifndef __rtems__ - txd->lpe_txdesc_dmamap = NULL; -#endif /* __rtems__ */ - txd->lpe_txdesc_first = 0; - -#ifndef __rtems__ - err = bus_dmamap_create(sc->lpe_cdata.lpe_tx_buf_tag, 0, - &txd->lpe_txdesc_dmamap); -#endif /* __rtems__ */ - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx DMA map\n"); - return (err); - } - } - - return (0); -fail: - return (err); + (void) ifp; + + imr->ifm_status = IFM_AVALID | IFM_ACTIVE; + imr->ifm_active = IFM_ETHER; + + if ((lpc_eth->supp & ETH_SUPP_SPEED) != 0) { + imr->ifm_active |= IFM_100_TX; + } else { + imr->ifm_active |= IFM_10_T; + } + + if ((lpc_eth->mac2 & ETH_MAC2_FULL_DUPLEX) != 0) { + imr->ifm_active |= IFM_FDX; + } else { + imr->ifm_active |= IFM_HDX; + } } -static int -lpe_init_rx(struct lpe_softc *sc) +__weak_symbol int lpc_eth_probe(int unit) { - int i, err; + if (unit != 0) { + return ENXIO; + } - for (i = 0; i < LPE_RXDESC_NUM; i++) { - err = lpe_init_rxbuf(sc, i); - if (err) - return (err); - } - - return (0); + return BUS_PROBE_DEFAULT; } -static int -lpe_init_rxbuf(struct lpe_softc *sc, int n) +static int lpc_eth_do_probe(device_t dev) { - struct lpe_rxdesc *rxd; - struct lpe_hwdesc *hwd; -#ifndef __rtems__ - struct lpe_hwstatus *hws; -#endif /* __rtems__ */ - struct mbuf *m; - bus_dma_segment_t segs[1]; -#ifndef __rtems__ - int nsegs; -#endif /* __rtems__ */ - - rxd = &sc->lpe_cdata.lpe_rx_desc[n]; - hwd = &sc->lpe_rdata.lpe_rx_ring[n]; -#ifndef __rtems__ - hws = &sc->lpe_rdata.lpe_rx_status[n]; -#endif /* __rtems__ */ - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - - if (!m) { - device_printf(sc->lpe_dev, "WARNING: mbufs exhausted!\n"); - return (ENOBUFS); - } - - m->m_len = m->m_pkthdr.len = MCLBYTES; - -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap); - - if (bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_rx_buf_tag, - rxd->lpe_rxdesc_dmamap, m, segs, &nsegs, 0)) { - m_freem(m); - return (ENOBUFS); - } - - bus_dmamap_sync(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap, - BUS_DMASYNC_PREREAD); -#else /* __rtems__ */ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len); -#endif - segs[0].ds_addr = mtod(m, bus_addr_t); -#endif /* __rtems__ */ - - rxd->lpe_rxdesc_mbuf = m; - hwd->lhr_data = segs[0].ds_addr + 2; - hwd->lhr_control = (segs[0].ds_len - 1) | LPE_HWDESC_INTERRUPT; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd)); -#endif -#endif /* __rtems__ */ - - return (0); + device_set_desc(dev, "LPC32x0 Ethernet controller"); + return lpc_eth_probe(device_get_unit(dev)); } -static void -lpe_discard_rxbuf(struct lpe_softc *sc, int n) +static int lpc_eth_attach(device_t dev) { - struct lpe_rxdesc *rxd; - struct lpe_hwdesc *hwd; - - rxd = &sc->lpe_cdata.lpe_rx_desc[n]; - hwd = &sc->lpe_rdata.lpe_rx_ring[n]; - -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap); -#endif /* __rtems__ */ - - hwd->lhr_data = 0; - hwd->lhr_control = 0; - - if (rxd->lpe_rxdesc_mbuf) { - m_freem(rxd->lpe_rxdesc_mbuf); - rxd->lpe_rxdesc_mbuf = NULL; - } + lpc_eth_driver_entry *e = device_get_softc(dev); + struct ifnet *ifp = NULL; + char *unit_name = NULL; + int unit_index = device_get_unit(dev); + size_t table_area_size = 0; + char *table_area = NULL; + char *table_location = NULL; + rtems_status_code status; + uint8_t eaddr[ETHER_ADDR_LEN]; + + BSD_ASSERT(e->state == LPC_ETH_STATE_NOT_INITIALIZED); + + mtx_init(&e->mtx, device_get_nameunit(e->dev), MTX_NETWORK_LOCK, MTX_DEF); + + ifmedia_init(&e->ifmedia, 0, lpc_eth_media_change, lpc_eth_media_status); + ifmedia_add(&e->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&e->ifmedia, IFM_ETHER | IFM_AUTO); + + callout_init_mtx(&e->watchdog_callout, &e->mtx, 0); + + /* Receive unit count */ + e->rx_unit_count = LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT; + + /* Transmit unit count */ + e->tx_unit_count = LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT; + + /* Remember interrupt number */ + e->interrupt_number = LPC_ETH_CONFIG_INTERRUPT; + + /* Allocate and clear table area */ + table_area_size = + e->rx_unit_count + * (sizeof(lpc_eth_transfer_descriptor) + + sizeof(lpc_eth_receive_status) + + sizeof(struct mbuf *)) + + e->tx_unit_count + * (sizeof(lpc_eth_transfer_descriptor) + + sizeof(uint32_t) + + LPC_ETH_CONFIG_TX_BUF_SIZE); + table_area = lpc_eth_config_alloc_table_area(table_area_size); + if (table_area == NULL) { + return ENOMEM; + } + memset(table_area, 0, table_area_size); + + table_location = table_area; + + /* + * The receive status table must be the first one since it has the strictest + * alignment requirements. + */ + e->rx_status_table = (volatile lpc_eth_receive_status *) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_status_table [0]); + + e->rx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_desc_table [0]); + + e->rx_mbuf_table = (struct mbuf **) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_mbuf_table [0]); + + e->tx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location; + table_location += e->tx_unit_count * sizeof(e->tx_desc_table [0]); + + e->tx_status_table = (volatile uint32_t *) table_location; + table_location += e->tx_unit_count * sizeof(e->tx_status_table [0]); + + e->tx_buf_table = table_location; + + /* Set interface data */ + e->dev = dev; + e->ifp = ifp = if_alloc(IFT_ETHER); + ifp->if_softc = e; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_init = lpc_eth_interface_init; + ifp->if_ioctl = lpc_eth_interface_ioctl; + ifp->if_transmit = lpc_eth_interface_transmit; + ifp->if_qflush = if_qflush; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; + IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1); + ifp->if_snd.ifq_drv_maxlen = LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1; + IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = sizeof(struct ether_header); + + rtems_bsd_get_mac_address(device_get_name(e->dev), unit_index, eaddr); + + /* Create tasks */ + status = rtems_task_create( + rtems_build_name('n', 't', 'r', 'x'), + rtems_bsd_get_task_priority(device_get_name(e->dev)), + 4096, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &e->receive_task + ); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + status = rtems_task_start( + e->receive_task, + lpc_eth_receive_task, + (rtems_task_argument)e + ); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + + if_link_state_change(e->ifp, LINK_STATE_UP); + + /* Change status */ + e->state = LPC_ETH_STATE_DOWN; + + /* Attach the interface */ + ether_ifattach(ifp, eaddr); + + return 0; } -static void -lpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +static int lpc_eth_detach(device_t dev) { - struct lpe_dmamap_arg *ctx; + /* FIXME: Detach the interface from the upper layers? */ - if (error) - return; + /* Module soft reset */ + lpc_eth->command = 0x38; + lpc_eth->mac1 = 0xcf00; - ctx = (struct lpe_dmamap_arg *)arg; - ctx->lpe_dma_busaddr = segs[0].ds_addr; -} + /* FIXME: More cleanup */ -static int -lpe_ifmedia_upd(struct ifnet *ifp) -{ - return (0); -} - -static void -lpe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct lpe_softc *sc = ifp->if_softc; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - - lpe_lock(sc); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - lpe_unlock(sc); + return ENXIO; } static device_method_t lpe_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, lpe_probe), - DEVMETHOD(device_attach, lpe_attach), - DEVMETHOD(device_detach, lpe_detach), - - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - - /* MII interface */ - DEVMETHOD(miibus_readreg, lpe_miibus_readreg), - DEVMETHOD(miibus_writereg, lpe_miibus_writereg), - DEVMETHOD(miibus_statchg, lpe_miibus_statchg), - { 0, 0 } + DEVMETHOD(device_probe, lpc_eth_do_probe), + DEVMETHOD(device_attach, lpc_eth_attach), + DEVMETHOD(device_detach, lpc_eth_detach), + DEVMETHOD_END }; -static driver_t lpe_driver = { - "lpe", - lpe_methods, - sizeof(struct lpe_softc), +static driver_t lpe_nexus_driver = { + "lpe", + lpe_methods, + sizeof(lpc_eth_driver_entry) }; static devclass_t lpe_devclass; - -#ifndef __rtems__ -DRIVER_MODULE(lpe, simplebus, lpe_driver, lpe_devclass, 0, 0); -#else /* __rtems__ */ -DRIVER_MODULE(lpe, nexus, lpe_driver, lpe_devclass, 0, 0); -#endif /* __rtems__ */ -DRIVER_MODULE(miibus, lpe, miibus_driver, miibus_devclass, 0, 0); -MODULE_DEPEND(lpe, obio, 1, 1, 1); -MODULE_DEPEND(lpe, miibus, 1, 1, 1); +DRIVER_MODULE(lpe, nexus, lpe_nexus_driver, lpe_devclass, 0, 0); +MODULE_DEPEND(lpe, nexus, 1, 1, 1); MODULE_DEPEND(lpe, ether, 1, 1, 1); + +#endif /* LIBBSP_ARM_LPC24XX_BSP_H || LIBBSP_ARM_LPC32XX_BSP_H */ diff --git a/rtemsbsd/sys/arm/lpc/if_lpereg.h b/rtemsbsd/sys/arm/lpc/if_lpereg.h deleted file mode 100644 index a40bf8b5..00000000 --- a/rtemsbsd/sys/arm/lpc/if_lpereg.h +++ /dev/null @@ -1,210 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _ARM_LPC_IF_LPEREG_H -#define _ARM_LPC_IF_LPEREG_H - -#define LPE_MAC1 0x000 -#define LPE_MAC1_RXENABLE (1 << 0) -#define LPE_MAC1_PASSALL (1 << 1) -#define LPE_MAC1_RXFLOWCTRL (1 << 2) -#define LPE_MAC1_TXFLOWCTRL (1 << 3) -#define LPE_MAC1_LOOPBACK (1 << 4) -#define LPE_MAC1_RESETTX (1 << 8) -#define LPE_MAC1_RESETMCSTX (1 << 9) -#define LPE_MAC1_RESETRX (1 << 10) -#define LPE_MAC1_RESETMCSRX (1 << 11) -#define LPE_MAC1_SIMRESET (1 << 14) -#define LPE_MAC1_SOFTRESET (1 << 15) -#define LPE_MAC2 0x004 -#define LPE_MAC2_FULLDUPLEX (1 << 0) -#define LPE_MAC2_FRAMELENCHECK (1 << 1) -#define LPE_MAC2_HUGEFRAME (1 << 2) -#define LPE_MAC2_DELAYEDCRC (1 << 3) -#define LPE_MAC2_CRCENABLE (1 << 4) -#define LPE_MAC2_PADCRCENABLE (1 << 5) -#define LPE_MAC2_VLANPADENABLE (1 << 6) -#define LPE_MAC2_AUTOPADENABLE (1 << 7) -#define LPE_MAC2_PUREPREAMBLE (1 << 8) -#define LPE_MAC2_LONGPREAMBLE (1 << 9) -#define LPE_MAC2_NOBACKOFF (1 << 12) -#define LPE_MAC2_BACKPRESSURE (1 << 13) -#define LPE_MAC2_EXCESSDEFER (1 << 14) -#define LPE_IPGT 0x008 -#define LPE_IPGR 0x00c -#define LPE_CLRT 0x010 -#define LPE_MAXF 0x014 -#define LPE_SUPP 0x018 -#define LPE_SUPP_SPEED (1 << 8) -#define LPE_TEST 0x01c -#define LPE_MCFG 0x020 -#define LPE_MCFG_SCANINCR (1 << 0) -#define LPE_MCFG_SUPPREAMBLE (1 << 1) -#define LPE_MCFG_CLKSEL(_n) ((_n & 0x7) << 2) -#define LPC_MCFG_RESETMII (1 << 15) -#define LPE_MCMD 0x024 -#define LPE_MCMD_READ (1 << 0) -#define LPE_MCMD_WRITE (0 << 0) -#define LPE_MCMD_SCAN (1 << 1) -#define LPE_MADR 0x028 -#define LPE_MADR_REGMASK 0x1f -#define LPE_MADR_REGSHIFT 0 -#define LPE_MADR_PHYMASK 0x1f -#define LPE_MADR_PHYSHIFT 8 -#define LPE_MWTD 0x02c -#define LPE_MWTD_DATAMASK 0xffff -#define LPE_MRDD 0x030 -#define LPE_MRDD_DATAMASK 0xffff -#define LPE_MIND 0x034 -#define LPE_MIND_BUSY (1 << 0) -#define LPE_MIND_SCANNING (1 << 1) -#define LPE_MIND_INVALID (1 << 2) -#define LPE_MIND_MIIFAIL (1 << 3) -#define LPE_SA0 0x040 -#define LPE_SA1 0x044 -#define LPE_SA2 0x048 -#define LPE_COMMAND 0x100 -#define LPE_COMMAND_RXENABLE (1 << 0) -#define LPE_COMMAND_TXENABLE (1 << 1) -#define LPE_COMMAND_REGRESET (1 << 3) -#define LPE_COMMAND_TXRESET (1 << 4) -#define LPE_COMMAND_RXRESET (1 << 5) -#define LPE_COMMAND_PASSRUNTFRAME (1 << 6) -#define LPE_COMMAND_PASSRXFILTER (1 << 7) -#define LPE_COMMAND_TXFLOWCTL (1 << 8) -#define LPE_COMMAND_RMII (1 << 9) -#define LPE_COMMAND_FULLDUPLEX (1 << 10) -#define LPE_STATUS 0x104 -#define LPE_STATUS_RXACTIVE (1 << 0) -#define LPE_STATUS_TXACTIVE (1 << 1) -#define LPE_RXDESC 0x108 -#define LPE_RXSTATUS 0x10c -#define LPE_RXDESC_NUMBER 0x110 -#define LPE_RXDESC_PROD 0x114 -#define LPE_RXDESC_CONS 0x118 -#define LPE_TXDESC 0x11c -#define LPE_TXSTATUS 0x120 -#define LPE_TXDESC_NUMBER 0x124 -#define LPE_TXDESC_PROD 0x128 -#define LPE_TXDESC_CONS 0x12c -#define LPE_TSV0 0x158 -#define LPE_TSV1 0x15c -#define LPE_RSV 0x160 -#define LPE_FLOWCONTROL_COUNTER 0x170 -#define LPE_FLOWCONTROL_STATUS 0x174 -#define LPE_RXFILTER_CTRL 0x200 -#define LPE_RXFILTER_UNICAST (1 << 0) -#define LPE_RXFILTER_BROADCAST (1 << 1) -#define LPE_RXFILTER_MULTICAST (1 << 2) -#define LPE_RXFILTER_UNIHASH (1 << 3) -#define LPE_RXFILTER_MULTIHASH (1 << 4) -#define LPE_RXFILTER_PERFECT (1 << 5) -#define LPE_RXFILTER_WOL (1 << 12) -#define LPE_RXFILTER_FILTWOL (1 << 13) -#define LPE_RXFILTER_WOL_STATUS 0x204 -#define LPE_RXFILTER_WOL_CLEAR 0x208 -#define LPE_HASHFILTER_L 0x210 -#define LPE_HASHFILTER_H 0x214 -#define LPE_INTSTATUS 0xfe0 -#define LPE_INTENABLE 0xfe4 -#define LPE_INTCLEAR 0xfe8 -#define LPE_INTSET 0xfec -#define LPE_INT_RXOVERRUN (1 << 0) -#define LPE_INT_RXERROR (1 << 1) -#define LPE_INT_RXFINISH (1 << 2) -#define LPE_INT_RXDONE (1 << 3) -#define LPE_INT_TXUNDERRUN (1 << 4) -#define LPE_INT_TXERROR (1 << 5) -#define LPE_INT_TXFINISH (1 << 6) -#define LPE_INT_TXDONE (1 << 7) -#define LPE_INT_SOFTINT (1 << 12) -#define LPE_INTWAKEUPINT (1 << 13) -#define LPE_POWERDOWN 0xff4 - -#define LPE_DESC_ALIGN 8 -#define LPE_TXDESC_NUM 128 -#define LPE_RXDESC_NUM 128 -#define LPE_TXDESC_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwdesc)) -#define LPE_RXDESC_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwdesc)) -#define LPE_TXSTATUS_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwstatus)) -#define LPE_RXSTATUS_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwstatus)) -#define LPE_MAXFRAGS 8 - -struct lpe_hwdesc { - uint32_t lhr_data; - uint32_t lhr_control; -}; - -struct lpe_hwstatus { - uint32_t lhs_info; - uint32_t lhs_crc; -}; - -#define LPE_INC(x, y) (x) = ((x) == ((y)-1)) ? 0 : (x)+1 - -/* These are valid for both Rx and Tx descriptors */ -#define LPE_HWDESC_SIZE_MASK (1 << 10) -#define LPE_HWDESC_INTERRUPT (1U << 31) - -/* These are valid for Tx descriptors */ -#define LPE_HWDESC_LAST (1 << 30) -#define LPE_HWDESC_CRC (1 << 29) -#define LPE_HWDESC_PAD (1 << 28) -#define LPE_HWDESC_HUGE (1 << 27) -#define LPE_HWDESC_OVERRIDE (1 << 26) - -/* These are valid for Tx status descriptors */ -#define LPE_HWDESC_COLLISIONS(_n) (((_n) >> 21) & 0x7) -#define LPE_HWDESC_DEFER (1 << 25) -#define LPE_HWDESC_EXCDEFER (1 << 26) -#define LPE_HWDESC_EXCCOLL (1 << 27) -#define LPE_HWDESC_LATECOLL (1 << 28) -#define LPE_HWDESC_UNDERRUN (1 << 29) -#define LPE_HWDESC_TXNODESCR (1 << 30) -#define LPE_HWDESC_ERROR (1U << 31) - -/* These are valid for Rx status descriptors */ -#define LPE_HWDESC_CONTROL (1 << 18) -#define LPE_HWDESC_VLAN (1 << 19) -#define LPE_HWDESC_FAILFILTER (1 << 20) -#define LPE_HWDESC_MULTICAST (1 << 21) -#define LPE_HWDESC_BROADCAST (1 << 22) -#define LPE_HWDESC_CRCERROR (1 << 23) -#define LPE_HWDESC_SYMBOLERROR (1 << 24) -#define LPE_HWDESC_LENGTHERROR (1 << 25) -#define LPE_HWDESC_RANGEERROR (1 << 26) -#define LPE_HWDESC_ALIGNERROR (1 << 27) -#define LPE_HWDESC_OVERRUN (1 << 28) -#define LPE_HWDESC_RXNODESCR (1 << 29) -#define LPE_HWDESC_LASTFLAG (1 << 30) -#define LPE_HWDESC_ERROR (1U << 31) - - -#endif /* _ARM_LPC_IF_LPEREG_H */ diff --git a/rtemsbsd/sys/dev/atsam/if_atsam.c b/rtemsbsd/sys/dev/atsam/if_atsam.c index ff8219f4..21a28fcd 100644 --- a/rtemsbsd/sys/dev/atsam/if_atsam.c +++ b/rtemsbsd/sys/dev/atsam/if_atsam.c @@ -42,6 +42,7 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/mbuf.h> +#include <sys/sbuf.h> #include <sys/socket.h> #include <sys/sockio.h> #include <sys/kernel.h> @@ -49,7 +50,9 @@ #include <sys/bus.h> #include <sys/sysctl.h> +#include <net/bpf.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_var.h> #include <net/if_types.h> #include <net/if_media.h> @@ -66,6 +69,7 @@ #include <rtems/bsd/local/miibus_if.h> #include <rtems/bsd/if_atsam.h> +#include <rtems/bsd/bsd.h> /* * Number of interfaces supported by the driver @@ -90,46 +94,40 @@ /** The runtime pin configure list for GMAC */ #define BOARD_GMAC_RUN_PINS BOARD_GMAC_PINS -/** Multicast Enable */ -#define GMAC_MC_ENABLE (1u << 6) -#define HASH_INDEX_AMOUNT 6 -#define HASH_ELEMENTS_PER_INDEX 8 -#define MAC_ADDR_MASK 0x0000FFFFFFFFFFFF -#define MAC_IDX_MASK (1u << 0) - -/** Promiscuous Mode Enable */ -#define GMAC_PROM_ENABLE (1u << 4) - /** RX Defines */ #define GMAC_RX_BUFFER_SIZE 1536 #define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC -#define GMAC_RX_SET_OFFSET (1u << 15) -#define GMAC_RX_SET_USED_WRAP ((1u << 1) | (1u << 0)) -#define GMAC_RX_SET_WRAP (1u << 1) -#define GMAC_RX_SET_USED (1u << 0) -/** TX Defines */ -#define GMAC_TX_SET_EOF (1u << 15) -#define GMAC_TX_SET_WRAP (1u << 30) -#define GMAC_TX_SET_USED (1u << 31) #define GMAC_DESCRIPTOR_ALIGNMENT 8 /** Events */ #define ATSAMV7_ETH_RX_EVENT_INTERRUPT RTEMS_EVENT_1 -#define ATSAMV7_ETH_TX_EVENT_INTERRUPT RTEMS_EVENT_2 -#define ATSAMV7_ETH_START_TRANSMIT_EVENT RTEMS_EVENT_3 - -#define ATSAMV7_ETH_RX_DATA_OFFSET 2 - -#define WATCHDOG_TIMEOUT 5 /* FIXME: Make these configurable */ #define MDIO_RETRIES 10 #define MDIO_PHY MII_PHY_ANY -#define RXBUF_COUNT 8 -#define TXBUF_COUNT 64 #define IGNORE_RX_ERR false +#define RX_INTERRUPTS (GMAC_ISR_RCOMP | GMAC_ISR_RXUBR | GMAC_ISR_ROVR) + +#define RX_DESC_LOG2 3 +#define RX_DESC_COUNT (1U << RX_DESC_LOG2) +#define RX_DESC_WRAP(idx) \ + ((((idx) + 1) & RX_DESC_COUNT) >> (RX_DESC_LOG2 - 1)) +RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 1) == + GMAC_RX_WRAP_BIT, rx_desc_wrap); +RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 2) == + 0, rx_desc_no_wrap); + +#define TX_DESC_LOG2 6 +#define TX_DESC_COUNT (1U << TX_DESC_LOG2) +#define TX_DESC_WRAP(idx) \ + ((((idx) + 1) & TX_DESC_COUNT) << (30 - TX_DESC_LOG2)) +RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 1) == + GMAC_TX_WRAP_BIT, tx_desc_wrap); +RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 2) == + 0, tx_desc_no_wrap); + /** The PINs for GMAC */ static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS }; @@ -139,11 +137,13 @@ typedef struct if_atsam_gmac { uint32_t retries; } if_atsam_gmac; -typedef struct ring_buffer { - unsigned tx_bd_used; - unsigned tx_bd_free; - size_t length; -} ring_buffer; +struct if_atsam_tx_bds { + volatile sGmacTxDescriptor bds[TX_DESC_COUNT]; +}; + +struct if_atsam_rx_bds { + volatile sGmacRxDescriptor bds[RX_DESC_COUNT]; +}; /* * Per-device data @@ -156,17 +156,16 @@ typedef struct if_atsam_softc { struct ifnet *ifp; struct mtx mtx; if_atsam_gmac Gmac_inst; + size_t tx_idx_head; + size_t tx_idx_tail; + struct if_atsam_tx_bds *tx; + struct mbuf *tx_mbufs[TX_DESC_COUNT]; + size_t rx_idx_head; + struct if_atsam_rx_bds *rx; + struct mbuf *rx_mbufs[RX_DESC_COUNT]; uint8_t GMacAddress[6]; rtems_id rx_daemon_tid; - rtems_id tx_daemon_tid; rtems_vector_number interrupt_number; - struct mbuf **rx_mbuf; - struct mbuf **tx_mbuf; - volatile sGmacTxDescriptor *tx_bd_base; - size_t rx_bd_fill_idx; - size_t amount_rx_buf; - size_t amount_tx_buf; - ring_buffer tx_ring; struct callout tick_ch; /* @@ -190,13 +189,12 @@ typedef struct if_atsam_softc { struct if_atsam_stats { /* Software */ uint32_t rx_overrun_errors; + uint32_t rx_used_bit_reads; uint32_t rx_interrupts; - uint32_t tx_complete_int; uint32_t tx_tur_errors; uint32_t tx_rlex_errors; uint32_t tx_tfc_errors; uint32_t tx_hresp_errors; - uint32_t tx_interrupts; /* Hardware */ uint64_t octets_transm; @@ -243,6 +241,8 @@ typedef struct if_atsam_softc { uint32_t tcp_checksum_errors; uint32_t udp_checksum_errors; } stats; + + int if_flags; } if_atsam_softc; static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc); @@ -251,27 +251,6 @@ static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc); #define IF_ATSAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -static void if_atsam_event_send(rtems_id task, rtems_event_set event) -{ - rtems_event_send(task, event); -} - - -static void if_atsam_event_receive(if_atsam_softc *sc, rtems_event_set in) -{ - rtems_event_set out; - - IF_ATSAM_UNLOCK(sc); - rtems_event_receive( - in, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, - &out - ); - IF_ATSAM_LOCK(sc); -} - - static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp) { struct mbuf *m; @@ -356,11 +335,10 @@ if_atsam_miibus_readreg(device_t dev, int phy, int reg) static int if_atsam_miibus_writereg(device_t dev, int phy, int reg, int data) { - uint8_t err; if_atsam_softc *sc = device_get_softc(dev); IF_ATSAM_LOCK(sc); - err = if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, + (void)if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, (uint8_t)phy, (uint8_t)reg, data, sc->Gmac_inst.retries); IF_ATSAM_UNLOCK(sc); @@ -403,63 +381,53 @@ if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck, static void if_atsam_interrupt_handler(void *arg) { if_atsam_softc *sc = (if_atsam_softc *)arg; - uint32_t irq_status_val; - rtems_event_set rx_event = 0; - rtems_event_set tx_event = 0; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + uint32_t is; /* Get interrupt status */ - irq_status_val = GMAC_GetItStatus(pHw, 0); + is = pHw->GMAC_ISR; - /* Check receive interrupts */ - if ((irq_status_val & GMAC_IER_ROVR) != 0) { - ++sc->stats.rx_overrun_errors; - rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_RCOMP) != 0) { - rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT; + if (__predict_false((is & GMAC_TX_ERR_BIT) != 0)) { + if ((is & GMAC_IER_TUR) != 0) { + ++sc->stats.tx_tur_errors; + } + if ((is & GMAC_IER_RLEX) != 0) { + ++sc->stats.tx_rlex_errors; + } + if ((is & GMAC_IER_TFC) != 0) { + ++sc->stats.tx_tfc_errors; + } + if ((is & GMAC_IER_HRESP) != 0) { + ++sc->stats.tx_hresp_errors; + } } - /* Send events to receive task and switch off rx interrupts */ - if (rx_event != 0) { + + /* Check receive interrupts */ + if (__predict_true((is & RX_INTERRUPTS) != 0)) { + if (__predict_false((is & GMAC_ISR_RXUBR) != 0)) { + ++sc->stats.rx_used_bit_reads; + } + + if (__predict_false((is & GMAC_ISR_ROVR) != 0)) { + ++sc->stats.rx_overrun_errors; + } + ++sc->stats.rx_interrupts; - /* Erase the interrupts for RX completion and errors */ - GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); - (void)if_atsam_event_send(sc->rx_daemon_tid, rx_event); - } - if ((irq_status_val & GMAC_IER_TUR) != 0) { - ++sc->stats.tx_tur_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_RLEX) != 0) { - ++sc->stats.tx_rlex_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_TFC) != 0) { - ++sc->stats.tx_tfc_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_HRESP) != 0) { - ++sc->stats.tx_hresp_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_TCOMP) != 0) { - ++sc->stats.tx_complete_int; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - /* Send events to transmit task and switch off tx interrupts */ - if (tx_event != 0) { - ++sc->stats.tx_interrupts; - /* Erase the interrupts for TX completion and errors */ - GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0); - (void)if_atsam_event_send(sc->tx_daemon_tid, tx_event); + + /* Disable RX interrupts */ + pHw->GMAC_IDR = RX_INTERRUPTS; + + (void)rtems_event_send(sc->rx_daemon_tid, + ATSAMV7_ETH_RX_EVENT_INTERRUPT); } } -static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) +static void +if_atsam_rx_update_mbuf(struct mbuf *m, uint32_t status) { int frame_len; - frame_len = (int) (buffer_desc->status.bm.len); + frame_len = (int)(status & GMAC_LENGTH_FRAME); m->m_data = mtod(m, char*)+ETHER_ALIGN; m->m_len = frame_len; @@ -467,7 +435,7 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) /* check checksum offload result */ m->m_pkthdr.csum_flags = 0; - switch (buffer_desc->status.bm.typeIDMatchOrCksumResult) { + switch ((status >> 22) & 0x3) { case GMAC_RXDESC_ST_CKSUM_RESULT_IP_CHECKED: m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID; m->m_pkthdr.csum_data = 0xffff; @@ -481,21 +449,17 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) } } -/* - * Receive daemon - */ -static void if_atsam_rx_daemon(void *arg) +static void +if_atsam_rx_daemon(rtems_task_argument arg) { if_atsam_softc *sc = (if_atsam_softc *)arg; struct ifnet *ifp = sc->ifp; - rtems_event_set events = 0; - void *rx_bd_base; - struct mbuf *m; - struct mbuf *n; - volatile sGmacRxDescriptor *buffer_desc; - uint32_t tmp_rx_bd_address; - size_t i; + volatile sGmacRxDescriptor *base; + struct if_atsam_rx_bds *rx; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + size_t idx; + struct mbuf **mbufs; + struct mbuf *m; IF_ATSAM_LOCK(sc); @@ -506,47 +470,37 @@ static void if_atsam_rx_daemon(void *arg) } /* Allocate memory space for priority queue descriptor list */ - rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor), + base = rtems_cache_coherent_allocate(sizeof(*base), GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(rx_bd_base != NULL); + assert(base != NULL); - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; - buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP; - buffer_desc->status.val = 0; + base->addr.val = GMAC_RX_OWNERSHIP_BIT | GMAC_RX_WRAP_BIT; + base->status.val = 0; - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1); - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2); + GMAC_SetRxQueue(pHw, (uint32_t)base, 1); + GMAC_SetRxQueue(pHw, (uint32_t)base, 2); /* Allocate memory space for buffer descriptor list */ - rx_bd_base = rtems_cache_coherent_allocate( - sc->amount_rx_buf * sizeof(sGmacRxDescriptor), + rx = rtems_cache_coherent_allocate(sizeof(*rx), GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(rx_bd_base != NULL); - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; + assert(rx != NULL); + sc->rx = rx; + mbufs = &sc->rx_mbufs[0]; /* Create descriptor list and mark as empty */ - for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < sc->amount_rx_buf; - ++sc->rx_bd_fill_idx) { + for (idx = 0; idx < RX_DESC_COUNT; ++idx) { m = if_atsam_new_mbuf(ifp); assert(m != NULL); - sc->rx_mbuf[sc->rx_bd_fill_idx] = m; - buffer_desc->addr.val = ((uint32_t)m->m_data) & - GMAC_RX_BUF_DESC_ADDR_MASK; - buffer_desc->status.val = 0; - if (sc->rx_bd_fill_idx == (sc->amount_rx_buf - 1)) { - buffer_desc->addr.bm.bWrap = 1; - } else { - buffer_desc++; - } + mbufs[idx] = m; + rx->bds[idx].addr.val = mtod(m, uint32_t) | RX_DESC_WRAP(idx); } - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; /* Set 2 Byte Receive Buffer Offset */ - pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET; + pHw->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO(2); /* Write Buffer Queue Base Address Register */ GMAC_ReceiveEnable(pHw, 0); - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0); + GMAC_SetRxQueue(pHw, (uint32_t)&rx->bds[0], 0); /* Set address for address matching */ GMAC_SetAddress(pHw, 0, sc->GMacAddress); @@ -554,306 +508,230 @@ static void if_atsam_rx_daemon(void *arg) /* Enable Receiving of data */ GMAC_ReceiveEnable(pHw, 1); - /* Setup the interrupts for RX completion and errors */ - GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); + IF_ATSAM_UNLOCK(sc); - sc->rx_bd_fill_idx = 0; + idx = 0; while (true) { - /* Wait for events */ - if_atsam_event_receive(sc, ATSAMV7_ETH_RX_EVENT_INTERRUPT); + rtems_event_set out; - /* - * Check for all packets with a set ownership bit - */ - while (buffer_desc->addr.bm.bOwnership == 1) { - if (buffer_desc->status.bm.bEof == 1) { - m = sc->rx_mbuf[sc->rx_bd_fill_idx]; + sc->rx_idx_head = idx; - /* New mbuf for desc */ - n = if_atsam_new_mbuf(ifp); - if (n != NULL) { - rx_update_mbuf(m, buffer_desc); + /* Enable RX interrupts */ + pHw->GMAC_IER = RX_INTERRUPTS; - IF_ATSAM_UNLOCK(sc); - sc->ifp->if_input(ifp, m); - IF_ATSAM_LOCK(sc); - m = n; - } else { - (void)if_atsam_event_send( - sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT); - } - sc->rx_mbuf[sc->rx_bd_fill_idx] = m; - tmp_rx_bd_address = (uint32_t)m->m_data & - GMAC_RX_BUF_DESC_ADDR_MASK; - - /* Switch pointer to next buffer descriptor */ - if (sc->rx_bd_fill_idx == - (sc->amount_rx_buf - 1)) { - tmp_rx_bd_address |= GMAC_RX_SET_WRAP; - sc->rx_bd_fill_idx = 0; - } else { - ++sc->rx_bd_fill_idx; - } + (void) rtems_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT, + RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out); - /* - * Give ownership to GMAC for further processing - */ - tmp_rx_bd_address &= ~GMAC_RX_SET_USED; - _ARM_Data_synchronization_barrier(); - buffer_desc->addr.val = tmp_rx_bd_address; + while (true) { + uint32_t addr; + uint32_t status; - buffer_desc = (sGmacRxDescriptor *)rx_bd_base - + sc->rx_bd_fill_idx; + addr = rx->bds[idx].addr.val; + if ((addr & GMAC_RX_OWNERSHIP_BIT) == 0) { + break; } - } - /* Setup the interrupts for RX completion and errors */ - GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); - } -} -/* - * Update of current transmit buffer position. - */ -static void if_atsam_tx_bd_pos_update(size_t *pos, size_t amount_tx_buf) -{ - *pos = (*pos + 1) % amount_tx_buf; -} + status = rx->bds[idx].status.val; + m = mbufs[idx]; -/* - * Is RingBuffer empty - */ -static bool if_atsam_ring_buffer_empty(ring_buffer *ring_buffer) -{ - return (ring_buffer->tx_bd_used == ring_buffer->tx_bd_free); -} + if (__predict_true((status & GMAC_RX_EOF_BIT) != 0)) { + struct mbuf *n; -/* - * Is RingBuffer full - */ -static bool if_atsam_ring_buffer_full(ring_buffer *ring_buffer) -{ - size_t tx_bd_used_next = ring_buffer->tx_bd_used; + n = if_atsam_new_mbuf(ifp); + if (n != NULL) { + if_atsam_rx_update_mbuf(m, status); + (*ifp->if_input)(ifp, m); + m = n; + } + } else { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + } - if_atsam_tx_bd_pos_update(&tx_bd_used_next, ring_buffer->length); - return (tx_bd_used_next == ring_buffer->tx_bd_free); -} + mbufs[idx] = m; + rx->bds[idx].addr.val = mtod(m, uint32_t) | + RX_DESC_WRAP(idx); -/* - * Cleanup transmit file descriptors by freeing mbufs which are not needed any - * longer due to correct transmission. - */ -static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc) -{ - struct mbuf *m; - volatile sGmacTxDescriptor *cur; - bool eof_needed = false; - - while (!if_atsam_ring_buffer_empty(&sc->tx_ring)){ - cur = sc->tx_bd_base + sc->tx_ring.tx_bd_free; - if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) { - eof_needed = true; - cur->status.val |= GMAC_TX_SET_USED; - m = sc->tx_mbuf[sc->tx_ring.tx_bd_free]; - m_free(m); - sc->tx_mbuf[sc->tx_ring.tx_bd_free] = 0; - if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_free, - sc->tx_ring.length); - if (cur->status.bm.bLastBuffer) { - eof_needed = false; - } - } else { - break; + idx = (idx + 1) % RX_DESC_COUNT; } } } -/* - * Prepare Ethernet frame to start transmission. - */ -static bool if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m) +static void +if_atsam_tx_reclaim(struct if_atsam_softc *sc, struct ifnet *ifp) { - volatile sGmacTxDescriptor *cur; - volatile sGmacTxDescriptor *start_packet_tx_bd = 0; - int pos = 0; - uint32_t tmp_val = 0; - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - bool success; - int csum_flags = m->m_pkthdr.csum_flags; + uint32_t head_idx; + uint32_t tail_idx; + volatile sGmacTxDescriptor *base; - if_atsam_tx_bd_cleanup(sc); - /* Wait for interrupt in case no buffer descriptors are available */ - /* Wait for events */ - while (true) { - if (if_atsam_ring_buffer_full(&sc->tx_ring)) { - /* Setup the interrupts for TX completion and errors */ - GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0); - success = false; + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + base = &sc->tx->bds[0]; + + while (head_idx != tail_idx) { + uint32_t status; + ift_counter cnt; + struct mbuf *m; + + status = base[tail_idx].status.val; + + if ((status & GMAC_TX_USED_BIT) == 0) { break; } - /* - * Get current mbuf for data fill - */ - cur = &sc->tx_bd_base[sc->tx_ring.tx_bd_used]; - /* Set the transfer data */ - if (m->m_len) { - uintptr_t cache_adjustment = mtod(m, uintptr_t) % 32; - - rtems_cache_flush_multiple_data_lines( - mtod(m, const char *) - cache_adjustment, - (size_t)(m->m_len + cache_adjustment)); - - cur->addr = mtod(m, uint32_t); - tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED; - if (sc->tx_ring.tx_bd_used == (sc->tx_ring.length - 1)) { - tmp_val |= GMAC_TX_SET_WRAP; - } - if (pos == 0) { - start_packet_tx_bd = cur; - } - sc->tx_mbuf[sc->tx_ring.tx_bd_used] = m; - m = m->m_next; - if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_used, - sc->tx_ring.length); + if (__predict_true((status & GMAC_TX_ERR_BITS) == 0)) { + cnt = IFCOUNTER_OPACKETS; } else { - /* Discard empty mbufs */ - m = m_free(m); + cnt = IFCOUNTER_OERRORS; } - /* - * Send out the buffer once the complete mbuf_chain has been - * processed - */ - if (m == NULL) { - tmp_val |= GMAC_TX_SET_EOF; - tmp_val &= ~GMAC_TX_SET_USED; - if ((csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP | - CSUM_TCP_IPV6 | CSUM_UDP_IPV6)) != 0) { - start_packet_tx_bd->status.bm.bNoCRC = 0; - } else { - start_packet_tx_bd->status.bm.bNoCRC = 1; - } - _ARM_Data_synchronization_barrier(); - cur->status.val = tmp_val; - start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED; - _ARM_Data_synchronization_barrier(); - GMAC_TransmissionStart(pHw); - success = true; - break; - } else { - if (pos > 0) { - tmp_val &= ~GMAC_TX_SET_USED; + while ((m = sc->tx_mbufs[tail_idx]) == NULL ) { + base[tail_idx].status.val = status | GMAC_TX_USED_BIT; + tail_idx = (tail_idx + 1) % TX_DESC_COUNT; + status = base[tail_idx].status.val; + + if (__predict_false((status & GMAC_TX_ERR_BITS) != 0)) { + cnt = IFCOUNTER_OERRORS; } - pos++; - cur->status.val = tmp_val; } + + base[tail_idx].status.val = status | GMAC_TX_USED_BIT; + if_inc_counter(ifp, cnt, 1); + sc->tx_mbufs[tail_idx] = NULL; + m_freem(m); + + tail_idx = (tail_idx + 1) % TX_DESC_COUNT; } - return success; -} + sc->tx_idx_tail = tail_idx; +} -/* - * Transmit daemon - */ -static void if_atsam_tx_daemon(void *arg) +static void +if_atsam_cache_flush(uintptr_t begin, uintptr_t size) { - if_atsam_softc *sc = (if_atsam_softc *)arg; - rtems_event_set events = 0; - sGmacTxDescriptor *buffer_desc; - int bd_number; - void *tx_bd_base; - struct mbuf *m; - bool success; + uintptr_t end; + uintptr_t mask; + + /* Align begin and end of the data to a cache line */ + end = begin + size; + mask = CPU_CACHE_LINE_BYTES - 1; + begin &= ~mask; + end = (end + mask) & ~mask; + rtems_cache_flush_multiple_data_lines((void *)begin, end - begin); +} - IF_ATSAM_LOCK(sc); +static int +if_atsam_tx_enqueue(struct if_atsam_softc *sc, struct ifnet *ifp, struct mbuf *m) +{ + size_t head_idx; + size_t tail_idx; + size_t capacity; + size_t idx; + volatile sGmacTxDescriptor *base; + volatile sGmacTxDescriptor *desc; + size_t bufs; + uint32_t status; + struct mbuf *n; - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - struct ifnet *ifp = sc->ifp; + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + capacity = (tail_idx - head_idx - 1) % TX_DESC_COUNT; - GMAC_TransmitEnable(pHw, 0); + idx = head_idx; + base = &sc->tx->bds[0]; + bufs = 0; + n = m; - /* Allocate memory space for priority queue descriptor list */ - tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor), - GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(tx_bd_base != NULL); + do { + uint32_t size; - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; - buffer_desc->addr = 0; - buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP; + desc = &base[idx]; - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1); - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2); + size = (uint32_t)n->m_len; + if (__predict_true(size > 0)) { + uintptr_t begin; - /* Allocate memory space for buffer descriptor list */ - tx_bd_base = rtems_cache_coherent_allocate( - sc->amount_tx_buf * sizeof(sGmacTxDescriptor), - GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(tx_bd_base != NULL); - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; + ++bufs; + if (__predict_false(bufs > capacity)) { + return (ENOBUFS); + } - /* Create descriptor list and mark as empty */ - for (bd_number = 0; bd_number < sc->amount_tx_buf; bd_number++) { - buffer_desc->addr = 0; - buffer_desc->status.val = GMAC_TX_SET_USED; - if (bd_number == (sc->amount_tx_buf - 1)) { - buffer_desc->status.bm.bWrap = 1; - } else { - buffer_desc++; + begin = mtod(n, uintptr_t); + desc->addr = (uint32_t)begin; + status = GMAC_TX_USED_BIT | TX_DESC_WRAP(idx) | size; + desc->status.val = status; + if_atsam_cache_flush(begin, size); + idx = (idx + 1) % TX_DESC_COUNT; } + + n = n->m_next; + } while (n != NULL); + + sc->tx_idx_head = idx; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &base[idx]; + sc->tx_mbufs[idx] = m; + status = GMAC_TX_LAST_BUFFER_BIT; + + while (idx != head_idx) { + desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | + status; + status = 0; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &base[idx]; } - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; - /* Write Buffer Queue Base Address Register */ - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0); + desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | status; + _ARM_Data_synchronization_barrier(); + sc->Gmac_inst.gGmacd.pHw->GMAC_NCR |= GMAC_NCR_TSTART; + ETHER_BPF_MTAP(ifp, m); + return (0); +} - /* Enable Transmission of data */ - GMAC_TransmitEnable(pHw, 1); +static int +if_atsam_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct if_atsam_softc *sc; + int error; - /* Set variables in context */ - sc->tx_bd_base = tx_bd_base; + if (__predict_false((m->m_flags & M_VLANTAG) != 0)) { + struct mbuf *n; - while (true) { - /* Wait for events */ - if_atsam_event_receive(sc, - ATSAMV7_ETH_START_TRANSMIT_EVENT | - ATSAMV7_ETH_TX_EVENT_INTERRUPT); - //printf("TX Transmit Event received\n"); - - /* - * Send packets till queue is empty - */ - while (true) { - /* - * Get the mbuf chain to transmit - */ - if_atsam_tx_bd_cleanup(sc); - IF_DEQUEUE(&ifp->if_snd, m); - if (!m) { - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - break; - } - success = if_atsam_send_packet(sc, m); - if (!success){ - break; - } + n = ether_vlanencap(m, m->m_pkthdr.ether_vtag); + if (n == NULL) { + m_freem(m); + return (ENOBUFS); } + + m = n; } -} + sc = ifp->if_softc; + IF_ATSAM_LOCK(sc); -/* - * Send packet (caller provides header). - */ -static void if_atsam_enet_start(struct ifnet *ifp) -{ - if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; + error = if_atsam_tx_enqueue(sc, ifp, m); + if_atsam_tx_reclaim(sc, ifp); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - if_atsam_event_send(sc->tx_daemon_tid, - ATSAMV7_ETH_START_TRANSMIT_EVENT); -} + if (__predict_false(error != 0)) { + struct mbuf *n; + n = m_defrag(m, M_NOWAIT); + if (n != NULL) { + m = n; + } + + error = if_atsam_tx_enqueue(sc, ifp, m); + if (error != 0) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + } + } + + IF_ATSAM_UNLOCK(sc); + return (error); +} static uint8_t if_atsam_get_gmac_linkspeed_from_media(uint32_t media_subtype) { @@ -975,23 +853,55 @@ if_atsam_tick(void *context) callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc); } +static void +if_atsam_setup_tx(struct if_atsam_softc *sc) +{ + sGmacTxDescriptor *base; + struct if_atsam_tx_bds *tx; + size_t i; + Gmac *pHw; -/* - * Sets up the hardware and chooses the interface to be used - */ -static void if_atsam_init(void *arg) + pHw = sc->Gmac_inst.gGmacd.pHw; + GMAC_TransmitEnable(pHw, 0); + + /* Allocate memory space for priority queue descriptor list */ + base = rtems_cache_coherent_allocate(sizeof(base), + GMAC_DESCRIPTOR_ALIGNMENT, 0); + assert(base != NULL); + + base->addr = 0; + base->status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT; + + GMAC_SetTxQueue(pHw, (uint32_t)base, 1); + GMAC_SetTxQueue(pHw, (uint32_t)base, 2); + + /* Allocate memory space for buffer descriptor list */ + tx = rtems_cache_coherent_allocate(sizeof(*sc->tx), + GMAC_DESCRIPTOR_ALIGNMENT, 0); + assert(tx != NULL); + + /* Set variables in context */ + sc->tx = tx; + + /* Create descriptor list and mark as empty */ + for (i = 0; i < TX_DESC_COUNT; ++i) { + tx->bds[i].addr = 0; + tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i); + } + + /* Write Buffer Queue Base Address Register */ + GMAC_SetTxQueue(pHw, (uint32_t)&tx->bds[0], 0); + + /* Enable Transmission of data */ + GMAC_TransmitEnable(pHw, 1); +} + +static void +if_atsam_init(if_atsam_softc *sc) { rtems_status_code status; - - if_atsam_softc *sc = (if_atsam_softc *)arg; - struct ifnet *ifp = sc->ifp; uint32_t dmac_cfg = 0; - uint32_t gmii_val = 0; - if (ifp->if_flags & IFF_DRV_RUNNING) { - return; - } - ifp->if_flags |= IFF_DRV_RUNNING; sc->interrupt_number = GMAC_IRQn; /* Enable Peripheral Clock */ @@ -1000,7 +910,6 @@ static void if_atsam_init(void *arg) } /* Setup interrupts */ NVIC_ClearPendingIRQ(GMAC_IRQn); - NVIC_EnableIRQ(GMAC_IRQn); /* Configuration of DMAC */ dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) | @@ -1011,20 +920,17 @@ static void if_atsam_init(void *arg) /* Enable hardware checksum offload for receive */ sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_RXCOEN; + /* Use Multicast Hash Filter */ + sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN; + sc->Gmac_inst.gGmacd.pHw->GMAC_HRB = 0; + sc->Gmac_inst.gGmacd.pHw->GMAC_HRT = 0; + /* Shut down Transmit and Receive */ GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1); - /* - * Allocate mbuf pointers - */ - sc->rx_mbuf = malloc(sc->amount_rx_buf * sizeof *sc->rx_mbuf, - M_TEMP, M_NOWAIT); - sc->tx_mbuf = malloc(sc->amount_tx_buf * sizeof *sc->tx_mbuf, - M_TEMP, M_NOWAIT); - /* Install interrupt handler */ status = rtems_interrupt_handler_install(sc->interrupt_number, "Ethernet", @@ -1036,30 +942,143 @@ static void if_atsam_init(void *arg) /* * Start driver tasks */ - sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096, - if_atsam_rx_daemon, sc); - sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096, - if_atsam_tx_daemon, sc); + + status = rtems_task_create(rtems_build_name('S', 'C', 'r', 'x'), + rtems_bsd_get_task_priority(device_get_name(sc->dev)), 4096, + RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_MODES, &sc->rx_daemon_tid); + assert(status == RTEMS_SUCCESSFUL); + + status = rtems_task_start(sc->rx_daemon_tid, if_atsam_rx_daemon, + (rtems_task_argument)sc); + assert(status == RTEMS_SUCCESSFUL); callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc); + if_atsam_setup_tx(sc); +} + +static int +if_atsam_get_hash_index(const uint8_t *eaddr) +{ + uint64_t eaddr64; + int index; + int i; + + eaddr64 = eaddr[5]; + + for (i = 4; i >= 0; --i) { + eaddr64 <<= 8; + eaddr64 |= eaddr[i]; + } + + index = 0; + + for (i = 0; i < 6; ++i) { + uint64_t bits; + int j; + int hash; + + bits = eaddr64 >> i; + hash = bits & 1; + + for (j = 1; j < 8; ++j) { + bits >>= 6; + hash ^= bits & 1; + } + + index |= hash << i; + } + + return index; +} + +static void +if_atsam_setup_rxfilter(struct if_atsam_softc *sc) +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint64_t mhash; + Gmac *pHw; + + pHw = sc->Gmac_inst.gGmacd.pHw; + + if ((sc->ifp->if_flags & IFF_PROMISC) != 0) { + pHw->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } else { + pHw->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } + + ifp = sc->ifp; + + if ((ifp->if_flags & IFF_ALLMULTI)) + mhash = 0xffffffffffffffffLLU; + else { + mhash = 0; + if_maddr_rlock(ifp); + CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + mhash |= 1LLU << if_atsam_get_hash_index( + LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); + } + if_maddr_runlock(ifp); + } + + pHw->GMAC_HRB = (uint32_t)mhash; + pHw->GMAC_HRT = (uint32_t)(mhash >> 32); +} + +static void +if_atsam_start_locked(struct if_atsam_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + return; + } + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + if_atsam_setup_rxfilter(sc); + + /* Enable TX/RX */ + pHw->GMAC_NCR |= GMAC_NCR_RXEN | GMAC_NCR_TXEN; } +static void +if_atsam_start(void *arg) +{ + struct if_atsam_softc *sc = arg; -/* - * Stop the device - */ -static void if_atsam_stop(struct if_atsam_softc *sc) + IF_ATSAM_LOCK(sc); + if_atsam_start_locked(sc); + IF_ATSAM_UNLOCK(sc); +} + +static void +if_atsam_stop_locked(struct if_atsam_softc *sc) { struct ifnet *ifp = sc->ifp; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + size_t i; - ifp->if_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - /* Disable MDIO interface and TX/RX */ + /* Disable TX/RX */ pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); - pHw->GMAC_NCR &= ~GMAC_NCR_MPE; + + /* Reinitialize the TX descriptors */ + + sc->tx_idx_head = 0; + sc->tx_idx_tail = 0; + + for (i = 0; i < TX_DESC_COUNT; ++i) { + sc->tx->bds[i].addr = 0; + sc->tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i); + m_freem(sc->tx_mbufs[i]); + sc->tx_mbufs[i] = NULL; + } } @@ -1070,7 +1089,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; octets = pHw->GMAC_OTLO; - octets |= pHw->GMAC_OTHI << 32; + octets |= (uint64_t)pHw->GMAC_OTHI << 32; sc->stats.octets_transm += octets; sc->stats.frames_transm += pHw->GMAC_FT; sc->stats.broadcast_frames_transm += pHw->GMAC_BCFT; @@ -1092,7 +1111,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) sc->stats.carrier_sense_errors += pHw->GMAC_CSE; octets = pHw->GMAC_ORLO; - octets |= pHw->GMAC_ORHI << 32; + octets |= (uint64_t)pHw->GMAC_ORHI << 32; sc->stats.octets_rec += octets; sc->stats.frames_rec += pHw->GMAC_FR; sc->stats.broadcast_frames_rec += pHw->GMAC_BCFR; @@ -1120,24 +1139,159 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) sc->stats.udp_checksum_errors += pHw->GMAC_UCE; } +static int +if_atsam_stats_reset(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + int value; + int error; + + value = 0; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error != 0 || req->newptr == NULL) { + return (error); + } + + if (value != 0) { + IF_ATSAM_LOCK(sc); + if_atsam_poll_hw_stats(sc); + memset(&sc->stats, 0, sizeof(sc->stats)); + IF_ATSAM_UNLOCK(sc); + } + + return (0); +} + +static int +if_atsam_sysctl_reg(SYSCTL_HANDLER_ARGS) +{ + u_int value; + + value = *(uint32_t *)arg1; + return (sysctl_handle_int(oidp, &value, 0, req)); +} + +static int +if_atsam_sysctl_tx_desc(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + struct sbuf *sb; + int error; + size_t i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) { + return (error); + } + + sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); + if (sb == NULL) { + return (ENOMEM); + } + + sbuf_printf(sb, "\n\tHead %u\n", sc->tx_idx_head); + sbuf_printf(sb, "\tTail %u\n", sc->tx_idx_tail); + sbuf_printf(sb, "\tDMA %u\n", + (pHw->GMAC_TBQB - (uintptr_t)&sc->tx->bds[0]) / 8); + + for (i = 0; i < TX_DESC_COUNT; ++i) { + sbuf_printf(sb, "\t[%2u] %08x %08x\n", i, + sc->tx->bds[i].status.val, sc->tx->bds[i].addr); + } + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} + +static int +if_atsam_sysctl_rx_desc(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + struct sbuf *sb; + int error; + size_t i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) { + return (error); + } + + sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); + if (sb == NULL) { + return (ENOMEM); + } + + sbuf_printf(sb, "\n\tHead %u\n", sc->rx_idx_head); + sbuf_printf(sb, "\tDMA %u\n", + (pHw->GMAC_RBQB - (uintptr_t)&sc->rx->bds[0]) / 8); + + for (i = 0; i < RX_DESC_COUNT; ++i) { + sbuf_printf(sb, "\t[%2u] %08x %08x\n", i, + sc->rx->bds[i].status.val, sc->rx->bds[i].addr); + } + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} static void if_atsam_add_sysctls(device_t dev) { struct if_atsam_softc *sc = device_get_softc(dev); + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *base; struct sysctl_oid_list *statsnode; struct sysctl_oid_list *hwstatsnode; struct sysctl_oid_list *child; struct sysctl_oid *tree; ctx = device_get_sysctl_ctx(dev); - child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + base = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); - tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "regs", CTLFLAG_RD, + NULL, "if_atsam registers"); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdesc", CTLTYPE_STRING | + CTLFLAG_RD, sc, 0, if_atsam_sysctl_tx_desc, "A", + "Transmit Descriptors"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxdesc", CTLTYPE_STRING | + CTLFLAG_RD, sc, 0, if_atsam_sysctl_rx_desc, "A", + "Receive Descriptors"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "imr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_IMR), 0, + if_atsam_sysctl_reg, "I", "IMR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "isr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_ISR), 0, + if_atsam_sysctl_reg, "I", "ISR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_RSR), 0, + if_atsam_sysctl_reg, "I", "RSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_TSR), 0, + if_atsam_sysctl_reg, "I", "TSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "nsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NSR), 0, + if_atsam_sysctl_reg, "I", "NSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncfgr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCFGR), 0, + if_atsam_sysctl_reg, "I", "NCFGR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCR), 0, + if_atsam_sysctl_reg, "I", "NCR"); + + tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "stats", CTLFLAG_RD, NULL, "if_atsam statistics"); statsnode = SYSCTL_CHILDREN(tree); + SYSCTL_ADD_PROC(ctx, statsnode, OID_AUTO, "reset", CTLTYPE_INT | + CTLFLAG_WR, sc, 0, if_atsam_stats_reset, "I", "Reset"); + tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "sw", CTLFLAG_RD, NULL, "if_atsam software statistics"); child = SYSCTL_CHILDREN(tree); @@ -1145,12 +1299,12 @@ if_atsam_add_sysctls(device_t dev) SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overrun_errors", CTLFLAG_RD, &sc->stats.rx_overrun_errors, 0, "RX overrun errors"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_used_bit_reads", + CTLFLAG_RD, &sc->stats.rx_used_bit_reads, 0, + "RX used bit reads"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_interrupts", CTLFLAG_RD, &sc->stats.rx_interrupts, 0, "Rx interrupts"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_complete_int", - CTLFLAG_RD, &sc->stats.tx_complete_int, 0, - "Tx complete interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_tur_errors", CTLFLAG_RD, &sc->stats.tx_tur_errors, 0, "Error Tur Tx interrupts"); @@ -1163,9 +1317,6 @@ if_atsam_add_sysctls(device_t dev) SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_hresp_errors", CTLFLAG_RD, &sc->stats.tx_hresp_errors, 0, "Error Hresp Tx interrupts"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_interrupts", - CTLFLAG_RD, &sc->stats.tx_interrupts, 0, - "Tx interrupts"); tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "hw", CTLFLAG_RD, NULL, "if_atsam hardware statistics"); @@ -1175,40 +1326,40 @@ if_atsam_add_sysctls(device_t dev) NULL, "if_atsam hardware transmit statistics"); child = SYSCTL_CHILDREN(tree); - SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_transm", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets", CTLFLAG_RD, &sc->stats.octets_transm, "Octets Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames", CTLFLAG_RD, &sc->stats.frames_transm, 0, "Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames", CTLFLAG_RD, &sc->stats.broadcast_frames_transm, 0, "Broadcast Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames", CTLFLAG_RD, &sc->stats.multicast_frames_transm, 0, "Multicast Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames", CTLFLAG_RD, &sc->stats.pause_frames_transm, 0, "Pause Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes", CTLFLAG_RD, &sc->stats.frames_64_byte_transm, 0, "64 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes", CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_transm, 0, "65 to 127 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes", CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_transm, 0, "128 to 255 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes", CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_transm, 0, "256 to 511 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes", CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_transm, 0, "512 to 1023 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes", CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_transm, 0, "1024 to 1518 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_greater_1518_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes", CTLFLAG_RD, &sc->stats.frames_greater_1518_byte_transm, 0, "Greater Than 1518 Byte Frames Transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "transmit_underruns", @@ -1237,49 +1388,49 @@ if_atsam_add_sysctls(device_t dev) NULL, "if_atsam hardware receive statistics"); child = SYSCTL_CHILDREN(tree); - SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_rec", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets", CTLFLAG_RD, &sc->stats.octets_rec, "Octets Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames", CTLFLAG_RD, &sc->stats.frames_rec, 0, "Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames", CTLFLAG_RD, &sc->stats.broadcast_frames_rec, 0, "Broadcast Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames", CTLFLAG_RD, &sc->stats.multicast_frames_rec, 0, "Multicast Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames", CTLFLAG_RD, &sc->stats.pause_frames_rec, 0, "Pause Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes", CTLFLAG_RD, &sc->stats.frames_64_byte_rec, 0, "64 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes", CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_rec, 0, "65 to 127 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes", CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_rec, 0, "128 to 255 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes", CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_rec, 0, "256 to 511 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes", CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_rec, 0, "512 to 1023 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes", CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_rec, 0, "1024 to 1518 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes", CTLFLAG_RD, &sc->stats.frames_1519_to_maximum_byte_rec, 0, "1519 to Maximum Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames", CTLFLAG_RD, &sc->stats.undersize_frames_rec, 0, "Undersize Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames", CTLFLAG_RD, &sc->stats.oversize_frames_rec, 0, "Oversize Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers", CTLFLAG_RD, &sc->stats.jabbers_rec, 0, "Jabbers Received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frame_check_sequence_errors", @@ -1311,49 +1462,6 @@ if_atsam_add_sysctls(device_t dev) "UDP Checksum Errors"); } - -/* - * Calculates the index that is to be sent into the hash registers - */ -static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val) -{ - uint64_t tmp_val; - uint8_t i, j; - uint64_t idx; - int offset = 0; - - addr &= MAC_ADDR_MASK; - - for (i = 0; i < HASH_INDEX_AMOUNT; ++i) { - tmp_val = 0; - offset = 0; - for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) { - idx = (addr >> (offset + i)) & MAC_IDX_MASK; - tmp_val ^= idx; - offset += HASH_INDEX_AMOUNT; - } - if (tmp_val > 0) { - *val |= (1u << i); - } - } -} - - -/* - * Dis/Enable promiscuous Mode - */ -static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable) -{ - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - - if (enable) { - pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE; - } else { - pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE; - } -} - - static int if_atsam_mediaioctl(if_atsam_softc *sc, struct ifreq *ifr, u_long command) { @@ -1380,8 +1488,6 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int rv = 0; - bool prom_enable; - struct mii_data *mii; switch (command) { case SIOCGIFMEDIA: @@ -1389,17 +1495,31 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) rv = if_atsam_mediaioctl(sc, ifr, command); break; case SIOCSIFFLAGS: + IF_ATSAM_LOCK(sc); if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - if_atsam_init(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) { + if_atsam_setup_rxfilter(sc); + } + } else { + if_atsam_start_locked(sc); } - prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0); - if_atsam_promiscuous_mode(sc, prom_enable); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - if_atsam_stop(sc); + if_atsam_stop_locked(sc); } } + sc->if_flags = ifp->if_flags; + IF_ATSAM_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + IF_ATSAM_LOCK(sc); + if_atsam_setup_rxfilter(sc); + IF_ATSAM_UNLOCK(sc); + } break; default: rv = ether_ioctl(ifp, command, data); @@ -1416,7 +1536,6 @@ static int if_atsam_driver_attach(device_t dev) if_atsam_softc *sc; struct ifnet *ifp; int unit; - char *unitName; uint8_t eaddr[ETHER_ADDR_LEN]; sc = device_get_softc(dev); @@ -1437,13 +1556,6 @@ static int if_atsam_driver_attach(device_t dev) memcpy(sc->GMacAddress, eaddr, ETHER_ADDR_LEN); - sc->amount_rx_buf = RXBUF_COUNT; - sc->amount_tx_buf = TXBUF_COUNT; - - sc->tx_ring.tx_bd_used = 0; - sc->tx_ring.tx_bd_free = 0; - sc->tx_ring.length = sc->amount_tx_buf; - /* Set Initial Link Speed */ sc->link_speed = GMAC_SPEED_100M; sc->link_duplex = GMAC_DUPLEX_FULL; @@ -1486,17 +1598,20 @@ static int if_atsam_driver_attach(device_t dev) */ ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_init = if_atsam_init; + ifp->if_init = if_atsam_start; ifp->if_ioctl = if_atsam_ioctl; - ifp->if_start = if_atsam_enet_start; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + ifp->if_transmit = if_atsam_transmit; + ifp->if_qflush = if_qflush; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 | - IFCAP_VLAN_HWCSUM; + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTAGGING; + ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP; - IFQ_SET_MAXLEN(&ifp->if_snd, TXBUF_COUNT - 1); - ifp->if_snd.ifq_drv_maxlen = TXBUF_COUNT - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); + ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* * Attach the interface @@ -1504,6 +1619,7 @@ static int if_atsam_driver_attach(device_t dev) ether_ifattach(ifp, eaddr); if_atsam_add_sysctls(dev); + if_atsam_init(sc); return (0); } diff --git a/rtemsbsd/sys/dev/mve/if_mve.c b/rtemsbsd/sys/dev/mve/if_mve.c new file mode 100644 index 00000000..517484ee --- /dev/null +++ b/rtemsbsd/sys/dev/mve/if_mve.c @@ -0,0 +1,2389 @@ +/* RTEMS driver for the mv643xx gigabit ethernet chip */ + +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* + * NOTE: Some register (e.g., the SMI register) are SHARED among the + * three devices. Concurrent access protection is provided by + * the global networking semaphore. + * If other drivers are running on a subset of IFs then proper + * locking of all shared registers must be implemented! + * + * Some things I learned about this hardware can be found + * further down... + */ + +/* +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif +*/ + +#include <bsp.h> + +#ifdef LIBBSP_BEATNIK_BSP_H + +#include <rtems/bspIo.h> +#include <rtems/error.h> +#include <bsp/irq.h> +#include <bsp/gtreg.h> +#include <libcpu/byteorder.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <bsp/mv643xx_eth.h> + +/* CONFIGURABLE PARAMETERS */ + +/* Enable Hardware Snooping; if this is disabled (undefined), + * cache coherency is maintained by software. + */ +#undef ENABLE_HW_SNOOPING + +/* Compile-time debugging features */ + +/* Enable paranoia assertions and checks; reduce # of descriptors to minimum for stressing */ +#define MVETH_TESTING + +/* Enable debugging messages and some support routines (dump rings etc.) */ +#undef MVETH_DEBUG + +#define TX_NUM_TAG_SLOTS 1 /* leave room for tag; must not be 0 */ + +/* This is REAL; chip reads from 64-bit down-aligned buffer + * if the buffer size is < 8 !!! for buffer sizes 8 and upwards + * alignment is not an issue. This was verified using the + * 'mve_smallbuf_test.c' + */ +#define ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + +/* Chip register configuration values */ +#define MVETH_PORT_CONFIG_VAL (0 \ + | MV643XX_ETH_DFLT_RX_Q(0) \ + | MV643XX_ETH_DFLT_RX_ARP_Q(0) \ + | MV643XX_ETH_DFLT_RX_TCP_Q(0) \ + | MV643XX_ETH_DFLT_RX_UDP_Q(0) \ + | MV643XX_ETH_DFLT_RX_BPDU_Q(0) \ + ) + + +#define MVETH_PORT_XTEND_CONFIG_VAL 0 + +#ifdef OLDCONFIGVAL +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + | MV643XX_ETH_SET_FULL_DUPLEX \ + | MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD \ + ) +#endif +/* If we enable autoneg (duplex, speed, ...) then it seems + * that the chip automatically updates link settings + * (correct link settings are reflected in PORT_STATUS_R). + * However, when we disable aneg in the PHY then things + * can get messed up and the port doesn't work anymore. + * => we follow the linux driver in disabling all aneg + * in the serial config reg. and manually updating the + * speed & duplex bits when the phy link status changes. + * FIXME: don't know what to do about pause/flow-ctrl. + * It is best to just use ANEG anyways!!! + */ +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + ) + +#define MVETH_SERIAL_CTRL_CONFIG_MSK (0 \ + | MV643XX_ETH_SERIAL_PORT_ENBL \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_SC_MAX_RX_MASK \ + ) + + +#ifdef __PPC__ +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_4_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_4_64BIT \ + ) +#else +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_16_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_16_64BIT \ + ) +#endif + +/* minimal frame size we accept */ +#define MVETH_MIN_FRAMSZ_CONFIG_VAL 40 + +/* END OF CONFIGURABLE SECTION */ + +/* + * Here's stuff I learned about this chip: + * + * + * RX interrupt flags: + * + * broadcast packet RX: 0x00000005 + * last buf: 0x00000c05 + * overrun: 0x00000c00 + * unicast packet RX: 0x00000005 + * bad CRC received: 0x00000005 + * + * clearing 0x00000004 -> clears 0x00000001 + * clearing 0x00000400 -> clears 0x00000800 + * + * --> 0x0801 are probably some sort of summary bits. + * + * TX interrupt flags: + * + * broadcast packet in 1 buf: xcause: 0x00000001 (cause 0x00080000) + * into disconn. link: " " + * + * in some cases, I observed xcause: 0x00000101 (reason for 0x100 unknown + * but the linux driver accepts it also). + * + * + * Here a few more ugly things about this piece of hardware I learned + * (painfully, painfully; spending many many hours & nights :-() + * + * a) Especially in the case of 'chained' descriptors, the DMA keeps + * clobbering 'cmd_sts' long after it cleared the OWNership flag!!! + * Only after the whole chain is processed (OWN cleared on the + * last descriptor) it is safe to change cmd_sts. + * However, in the case of hardware snooping I found that the + * last descriptor in chain has its cmd_sts still clobbered *after* + * checking ownership!, I.e., + * if ( ! OWN & cmd_sts ) { + * cmd_sts = 0; + * } + * --> sometimes, cmd_sts is STILL != 0 here + * + * b) Sometimes, the OWNership flag is *not cleared*. + * + * c) Weird things happen if the chip finds a descriptor with 'OWN' + * still set (i.e., not properly loaded), i.e., corrupted packets + * are sent [with OK checksum since the chip calculates it]. + * + * Combine a+b+c and we end up with a real mess. + * + * The fact that the chip doesn't reliably reset OWN and that OTOH, + * it can't be reliably reset by the driver and still, the chip needs + * it for proper communication doesn't make things easy... + * + * Here the basic workarounds: + * + * - In addition to check OWN, the scavenger compares the "currently + * served desc" register to the descriptor it tries to recover and + * ignores OWN if they do not match. Hope this is OK. + * Otherwise, we could scan the list of used descriptors and proceed + * recycling descriptors if we find a !OWNed one behind the target... + * + * - Always keep an empty slot around to mark the end of the list of + * jobs. The driver clears the descriptor ahead when enqueueing a new + * packet. + */ + +#define DRVNAME "mve" +#define MAX_NUM_SLOTS 3 + +#if MV643XXETH_NUM_DRIVER_SLOTS > MAX_NUM_SLOTS +#error "mv643xxeth: only MAX_NUM_SLOTS supported" +#endif + +#ifdef NDEBUG +#error "Driver uses assert() statements with side-effects; MUST NOT define NDEBUG" +#endif + +#ifdef MVETH_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#define TX_AVAILABLE_RING_SIZE(mp) ((mp)->xbuf_count - (TX_NUM_TAG_SLOTS)) + +/* macros for ring alignment; proper alignment is a hardware req; . */ + +#ifdef ENABLE_HW_SNOOPING + +#define RING_ALIGNMENT 16 +/* rx buffers must be 64-bit aligned (chip requirement) */ +#define RX_BUF_ALIGNMENT 8 + +#else /* ENABLE_HW_SNOOPING */ + +/* Software cache management */ + +#ifndef __PPC__ +#error "Dont' know how to deal with cache on this CPU architecture" +#endif + +/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency + * management works for cache line sizes of 16 and 32 bytes only. If the line size + * is bigger, the descriptors could be padded... + */ +#if PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32 +#error "Cache line size must be 16 or 32" +#else +#define RING_ALIGNMENT PPC_CACHE_ALIGNMENT +#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT +#endif + +#endif /* ENABLE_HW_SNOOPING */ + + +/* HELPER MACROS */ + +/* Align base to alignment 'a' */ +#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1))) + +#define NOOP() do {} while(0) + +/* Function like macros */ +#define MV_READ(off) \ + ld_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off))) +#define MV_WRITE(off, data) \ + st_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off)), ((unsigned)data)) + + +/* ENET window mapped 1:1 to CPU addresses by our BSP/MotLoad + * -- if this is changed, we should think about caching the 'next' and 'buf' pointers. + */ +#define CPUADDR2ENET(a) ((Dma_addr_t)(a)) +#define ENET2CPUADDR(a) (a) + +#if 1 /* Whether to automatically try to reclaim descriptors when enqueueing new packets */ +#define MVETH_CLEAN_ON_SEND(mp) (BSP_mve_swipe_tx(mp)) +#else +#define MVETH_CLEAN_ON_SEND(mp) (-1) +#endif + +#define NEXT_TXD(d) (d)->next +#define NEXT_RXD(d) (d)->next + +/* REGISTER AND DESCRIPTOR OFFSET AND BIT DEFINITIONS */ + +/* Descriptor Definitions */ +/* Rx descriptor */ +#define RDESC_ERROR (1<< 0) /* Error summary */ + +/* Error code (bit 1&2) is only valid if summary bit is set */ +#define RDESC_CRC_ERROR ( 1) +#define RDESC_OVERRUN_ERROR ( 3) +#define RDESC_MAX_FRAMELENGTH_ERROR ( 5) +#define RDESC_RESOURCE_ERROR ( 7) + +#define RDESC_LAST (1<<26) /* Last Descriptor */ +#define RDESC_FRST (1<<27) /* First Descriptor */ +#define RDESC_INT_ENA (1<<29) /* Enable Interrupts */ +#define RDESC_DMA_OWNED (1<<31) + +/* Tx descriptor */ +#define TDESC_ERROR (1<< 0) /* Error summary */ +#define TDESC_ZERO_PAD (1<<19) +#define TDESC_LAST (1<<20) /* Last Descriptor */ +#define TDESC_FRST (1<<21) /* First Descriptor */ +#define TDESC_GEN_CRC (1<<22) +#define TDESC_INT_ENA (1<<23) /* Enable Interrupts */ +#define TDESC_DMA_OWNED (1<<31) + + + +/* Register Definitions */ +#define MV643XX_ETH_PHY_ADDR_R (0x2000) +#define MV643XX_ETH_SMI_R (0x2004) +#define MV643XX_ETH_SMI_BUSY (1<<28) +#define MV643XX_ETH_SMI_VALID (1<<27) +#define MV643XX_ETH_SMI_OP_WR (0<<26) +#define MV643XX_ETH_SMI_OP_RD (1<<26) + +#define MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port) (0x2448 + ((port)<<10)) +#define MV643XX_ETH_TX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_TX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_TX_START_M(queues) ((queues)&0xff) +#define MV643XX_ETH_TX_STOP_M(queues) (((queues)&0xff)<<8) +#define MV643XX_ETH_TX_STOP_ALL (0xff00) +#define MV643XX_ETH_TX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(port) (0x2680 + ((port)<<10)) +#define MV643XX_ETH_RX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_RX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_RX_STOP_ALL (0xff00) +#define MV643XX_ETH_RX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_CURRENT_SERVED_TX_DESC(port) (0x2684 + ((port)<<10)) + +/* The chip puts the ethernet header at offset 2 into the buffer so + * that the payload is aligned + */ +#define ETH_RX_OFFSET 2 +#define ETH_CRC_LEN 4 /* strip FCS at end of packet */ + + +#define MV643XX_ETH_INTERRUPT_CAUSE_R(port) (0x2460 + ((port)<<10)) +/* not fully understood; RX seems to raise 0x0005 or 0x0c05 if last buffer is filled and 0x0c00 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_IRQS (0x07ffffff) +#define MV643XX_ETH_KNOWN_IRQS (0x00080c07) +#define MV643XX_ETH_IRQ_EXT_ENA (1<<1) +/* defined in public header +#define MV643XX_ETH_IRQ_RX_DONE (1<<2) + */ +#define MV643XX_ETH_IRQ_RX_NO_DESC (1<<10) +#define MV643XX_ETH_TX_Q_N_END(n) (1<<((n)+19)) +/* We just use queue 0 */ +#define MV643XX_ETH_TX_Q_END MV643XX_ETH_TX_Q_N_END(0) + +#define MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(port) (0x2464 + ((port)<<10)) +/* not fully understood; TX seems to raise 0x0001 and link change is 0x00010000 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_EXT_IRQS (0x0011ffff) +/* Recent (2013) linux driver mentions both bits 0x00110000 as 'link change' causes */ +#define MV643XX_ETH_KNOWN_EXT_IRQS (0x00110101) +/* TX queues 0..7 */ +#define MV643XX_ETH_EXT_IRQ_TXN_DONE(n) (1<<(n)) +/* We just use queue 0 */ +/* defined in public header +#define MV643XX_ETH_EXT_IRQ_TX_DONE MV643XX_ETH_EXT_IRQ_TXN_DONE(0) +#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16) + */ +#define MV643XX_ETH_INTERRUPT_ENBL_R(port) (0x2468 + ((port)<<10)) +#define MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(port) (0x246c + ((port)<<10)) + +/* port configuration */ +#define MV643XX_ETH_PORT_CONFIG_R(port) (0x2400 + ((port)<<10)) +#define MV643XX_ETH_UNICAST_PROMISC_MODE (1<<0) +#define MV643XX_ETH_DFLT_RX_Q(q) ((q)<<1) +#define MV643XX_ETH_DFLT_RX_ARP_Q(q) ((q)<<4) +#define MV643XX_ETH_REJ_BCAST_IF_NOT_IP_OR_ARP (1<<7) +#define MV643XX_ETH_REJ_BCAST_IF_IP (1<<8) +#define MV643XX_ETH_REJ_BCAST_IF_ARP (1<<9) +#define MV643XX_ETH_TX_AM_NO_UPDATE_ERR_SUMMARY (1<<12) +#define MV643XX_ETH_CAPTURE_TCP_FRAMES_ENBL (1<<14) +#define MV643XX_ETH_CAPTURE_UDP_FRAMES_ENBL (1<<15) +#define MV643XX_ETH_DFLT_RX_TCP_Q(q) ((q)<<16) +#define MV643XX_ETH_DFLT_RX_UDP_Q(q) ((q)<<19) +#define MV643XX_ETH_DFLT_RX_BPDU_Q(q) ((q)<<22) + + + +#define MV643XX_ETH_PORT_CONFIG_XTEND_R(port) (0x2404 + ((port)<<10)) +#define MV643XX_ETH_CLASSIFY_ENBL (1<<0) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_AS_NORMAL (0<<1) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_2_Q7 (1<<1) +#define MV643XX_ETH_PARTITION_DISBL (0<<2) +#define MV643XX_ETH_PARTITION_ENBL (1<<2) + +#define MV643XX_ETH_SDMA_CONFIG_R(port) (0x241c + ((port)<<10)) +#define MV643XX_ETH_SDMA_RIFB (1<<0) +#define MV643XX_ETH_RX_BURST_SZ_1_64BIT (0<<1) +#define MV643XX_ETH_RX_BURST_SZ_2_64BIT (1<<1) +#define MV643XX_ETH_RX_BURST_SZ_4_64BIT (2<<1) +#define MV643XX_ETH_RX_BURST_SZ_8_64BIT (3<<1) +#define MV643XX_ETH_RX_BURST_SZ_16_64BIT (4<<1) +#define MV643XX_ETH_SMDA_BLM_RX_NO_SWAP (1<<4) +#define MV643XX_ETH_SMDA_BLM_TX_NO_SWAP (1<<5) +#define MV643XX_ETH_SMDA_DESC_BYTE_SWAP (1<<6) +#define MV643XX_ETH_TX_BURST_SZ_1_64BIT (0<<22) +#define MV643XX_ETH_TX_BURST_SZ_2_64BIT (1<<22) +#define MV643XX_ETH_TX_BURST_SZ_4_64BIT (2<<22) +#define MV643XX_ETH_TX_BURST_SZ_8_64BIT (3<<22) +#define MV643XX_ETH_TX_BURST_SZ_16_64BIT (4<<22) + +#define MV643XX_ETH_RX_MIN_FRAME_SIZE_R(port) (0x247c + ((port)<<10)) + + +#define MV643XX_ETH_SERIAL_CONTROL_R(port) (0x243c + ((port)<<10)) +#define MV643XX_ETH_SERIAL_PORT_ENBL (1<<0) /* Enable serial port */ +#define MV643XX_ETH_FORCE_LINK_PASS (1<<1) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX (1<<2) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL (1<<3) +#define MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL (1<<4) +#define MV643XX_ETH_FORCE_FC_MODE_TX_PAUSE_DIS (1<<5) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX (1<<7) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1<<8) +#define MV643XX_ETH_BIT9_UNKNOWN (1<<9) /* unknown purpose; linux sets this */ +#define MV643XX_ETH_FORCE_LINK_FAIL_DISABLE (1<<10) +#define MV643XX_ETH_RETRANSMIT_FOREVER (1<<11) /* limit to 16 attempts if clear */ +#define MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII (1<<13) +#define MV643XX_ETH_DTE_ADV_1 (1<<14) +#define MV643XX_ETH_AUTO_NEG_BYPASS_ENBL (1<<15) +#define MV643XX_ETH_RESTART_AUTO_NEG (1<<16) +#define MV643XX_ETH_SC_MAX_RX_1518 (0<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1522 (1<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1552 (2<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9022 (3<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9192 (4<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9700 (5<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_MASK (7<<17) /* bitmask */ +#define MV643XX_ETH_SET_EXT_LOOPBACK (1<<20) +#define MV643XX_ETH_SET_FULL_DUPLEX (1<<21) +#define MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD (1<<22) /* enable flow ctrl on rx and tx in full-duplex */ +#define MV643XX_ETH_SET_GMII_SPEED_1000 (1<<23) /* 10/100 if clear */ +#define MV643XX_ETH_SET_MII_SPEED_100 (1<<24) /* 10 if clear */ + +#define MV643XX_ETH_PORT_STATUS_R(port) (0x2444 + ((port)<<10)) + +#define MV643XX_ETH_PORT_STATUS_MODE_10_BIT (1<<0) +#define MV643XX_ETH_PORT_STATUS_LINK_UP (1<<1) +#define MV643XX_ETH_PORT_STATUS_FDX (1<<2) +#define MV643XX_ETH_PORT_STATUS_FC (1<<3) +#define MV643XX_ETH_PORT_STATUS_1000 (1<<4) +#define MV643XX_ETH_PORT_STATUS_100 (1<<5) +/* PSR bit 6 unknown */ +#define MV643XX_ETH_PORT_STATUS_TX_IN_PROGRESS (1<<7) +#define MV643XX_ETH_PORT_STATUS_ANEG_BYPASSED (1<<8) +#define MV643XX_ETH_PORT_STATUS_PARTITION (1<<9) +#define MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY (1<<10) + +#define MV643XX_ETH_MIB_COUNTERS(port) (0x3000 + ((port)<<7)) +#define MV643XX_ETH_NUM_MIB_COUNTERS 32 + +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO (0) +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_HI (1<<2) +#define MV643XX_ETH_MIB_BAD_OCTS_RCVD (2<<2) +#define MV643XX_ETH_MIB_INTERNAL_MAC_TX_ERR (3<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_RCVD (4<<2) +#define MV643XX_ETH_MIB_BAD_FRAMES_RCVD (5<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_RCVD (6<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_RCVD (7<<2) +#define MV643XX_ETH_MIB_FRAMES_64_OCTS (8<<2) +#define MV643XX_ETH_MIB_FRAMES_65_127_OCTS (9<<2) +#define MV643XX_ETH_MIB_FRAMES_128_255_OCTS (10<<2) +#define MV643XX_ETH_MIB_FRAMES_256_511_OCTS (11<<2) +#define MV643XX_ETH_MIB_FRAMES_512_1023_OCTS (12<<2) +#define MV643XX_ETH_MIB_FRAMES_1024_MAX_OCTS (13<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO (14<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_HI (15<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_SENT (16<<2) +#define MV643XX_ETH_MIB_EXCESSIVE_COLL (17<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_SENT (18<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_SENT (19<<2) +#define MV643XX_ETH_MIB_UNREC_MAC_CTRL_RCVD (20<<2) +#define MV643XX_ETH_MIB_FC_SENT (21<<2) +#define MV643XX_ETH_MIB_GOOD_FC_RCVD (22<<2) +#define MV643XX_ETH_MIB_BAD_FC_RCVD (23<<2) +#define MV643XX_ETH_MIB_UNDERSIZE_RCVD (24<<2) +#define MV643XX_ETH_MIB_FRAGMENTS_RCVD (25<<2) +#define MV643XX_ETH_MIB_OVERSIZE_RCVD (26<<2) +#define MV643XX_ETH_MIB_JABBER_RCVD (27<<2) +#define MV643XX_ETH_MIB_MAC_RX_ERR (28<<2) +#define MV643XX_ETH_MIB_BAD_CRC_EVENT (29<<2) +#define MV643XX_ETH_MIB_COLL (30<<2) +#define MV643XX_ETH_MIB_LATE_COLL (31<<2) + +#define MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(port) (0x3400+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(port) (0x3500+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_UNICAST_TBL(port) (0x3600+((port)<<10)) +#define MV643XX_ETH_NUM_MCAST_ENTRIES 64 +#define MV643XX_ETH_NUM_UNICAST_ENTRIES 4 + +#define MV643XX_ETH_BAR_0 (0x2200) +#define MV643XX_ETH_SIZE_R_0 (0x2204) +#define MV643XX_ETH_BAR_1 (0x2208) +#define MV643XX_ETH_SIZE_R_1 (0x220c) +#define MV643XX_ETH_BAR_2 (0x2210) +#define MV643XX_ETH_SIZE_R_2 (0x2214) +#define MV643XX_ETH_BAR_3 (0x2218) +#define MV643XX_ETH_SIZE_R_3 (0x221c) +#define MV643XX_ETH_BAR_4 (0x2220) +#define MV643XX_ETH_SIZE_R_4 (0x2224) +#define MV643XX_ETH_BAR_5 (0x2228) +#define MV643XX_ETH_SIZE_R_5 (0x222c) +#define MV643XX_ETH_NUM_BARS 6 + +/* Bits in the BAR reg to program cache snooping */ +#define MV64360_ENET2MEM_SNOOP_NONE 0x0000 +#define MV64360_ENET2MEM_SNOOP_WT 0x1000 +#define MV64360_ENET2MEM_SNOOP_WB 0x2000 +#define MV64360_ENET2MEM_SNOOP_MSK 0x3000 + + +#define MV643XX_ETH_BAR_ENBL_R (0x2290) +#define MV643XX_ETH_BAR_DISABLE(bar) (1<<(bar)) +#define MV643XX_ETH_BAR_DISBL_ALL 0x3f + +#define MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(port) (0x260c+((port)<<10)) +#define MV643XX_ETH_RX_Q1_CURRENT_DESC_PTR(port) (0x261c+((port)<<10)) +#define MV643XX_ETH_RX_Q2_CURRENT_DESC_PTR(port) (0x262c+((port)<<10)) +#define MV643XX_ETH_RX_Q3_CURRENT_DESC_PTR(port) (0x263c+((port)<<10)) +#define MV643XX_ETH_RX_Q4_CURRENT_DESC_PTR(port) (0x264c+((port)<<10)) +#define MV643XX_ETH_RX_Q5_CURRENT_DESC_PTR(port) (0x265c+((port)<<10)) +#define MV643XX_ETH_RX_Q6_CURRENT_DESC_PTR(port) (0x266c+((port)<<10)) +#define MV643XX_ETH_RX_Q7_CURRENT_DESC_PTR(port) (0x267c+((port)<<10)) + +#define MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(port) (0x26c0+((port)<<10)) +#define MV643XX_ETH_TX_Q1_CURRENT_DESC_PTR(port) (0x26c4+((port)<<10)) +#define MV643XX_ETH_TX_Q2_CURRENT_DESC_PTR(port) (0x26c8+((port)<<10)) +#define MV643XX_ETH_TX_Q3_CURRENT_DESC_PTR(port) (0x26cc+((port)<<10)) +#define MV643XX_ETH_TX_Q4_CURRENT_DESC_PTR(port) (0x26d0+((port)<<10)) +#define MV643XX_ETH_TX_Q5_CURRENT_DESC_PTR(port) (0x26d4+((port)<<10)) +#define MV643XX_ETH_TX_Q6_CURRENT_DESC_PTR(port) (0x26d8+((port)<<10)) +#define MV643XX_ETH_TX_Q7_CURRENT_DESC_PTR(port) (0x26dc+((port)<<10)) + +#define MV643XX_ETH_MAC_ADDR_LO(port) (0x2414+((port)<<10)) +#define MV643XX_ETH_MAC_ADDR_HI(port) (0x2418+((port)<<10)) + +/* TYPE DEFINITIONS */ + +/* just to make the purpose explicit; vars of this + * type may need CPU-dependent address translation, + * endian conversion etc. + */ +typedef uint32_t Dma_addr_t; + +typedef volatile struct mveth_rx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t buf_size; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + void *u_buf; /* user buffer */ + volatile struct mveth_rx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ + uint32_t pad[2]; +} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthRxDescRec, *MvEthRxDesc; + +typedef volatile struct mveth_tx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t l4i_chk; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + uint32_t workaround[2]; /* use this space to work around the 8byte problem (is this real?) */ + void *u_buf; /* user buffer */ + volatile struct mveth_tx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ +} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthTxDescRec, *MvEthTxDesc; + +/* Assume there are never more then 64k aliasing entries */ +typedef uint16_t Mc_Refcnt[MV643XX_ETH_NUM_MCAST_ENTRIES*4]; + +/* driver private data and bsdnet interface structure */ +struct mveth_private { + MvEthRxDesc rx_ring; /* pointers to aligned ring area */ + MvEthTxDesc tx_ring; /* pointers to aligned ring area */ + MvEthRxDesc ring_area; /* allocated ring area */ + int rbuf_count, xbuf_count; /* saved ring sizes from ifconfig */ + int port_num; + int phy; + MvEthRxDesc d_rx_t; /* tail of the RX ring; next received packet */ + MvEthTxDesc d_tx_t, d_tx_h; + uint32_t rx_desc_dma, tx_desc_dma; /* ring address as seen by DMA; (1:1 on this BSP) */ + int avail; + void (*isr)(void*); + void *isr_arg; + /* Callbacks to handle buffers */ + void (*cleanup_txbuf)(void*, void*, int); /* callback to cleanup TX buffer */ + void *cleanup_txbuf_arg; + void *(*alloc_rxbuf)(int *psize, uintptr_t *paddr); /* allocate RX buffer */ + void (*consume_rxbuf)(void*, void*, int); /* callback to consume RX buffer */ + void *consume_rxbuf_arg; + rtems_id tid; + uint32_t irq_mask; /* IRQs we use */ + uint32_t xirq_mask; + int promisc; + struct { + unsigned irqs; + unsigned maxchain; + unsigned repack; + unsigned packet; + unsigned idrops; /* no counter in core code */ + struct { + uint64_t good_octs_rcvd; /* 64-bit */ + uint32_t bad_octs_rcvd; + uint32_t internal_mac_tx_err; + uint32_t good_frames_rcvd; + uint32_t bad_frames_rcvd; + uint32_t bcast_frames_rcvd; + uint32_t mcast_frames_rcvd; + uint32_t frames_64_octs; + uint32_t frames_65_127_octs; + uint32_t frames_128_255_octs; + uint32_t frames_256_511_octs; + uint32_t frames_512_1023_octs; + uint32_t frames_1024_max_octs; + uint64_t good_octs_sent; /* 64-bit */ + uint32_t good_frames_sent; + uint32_t excessive_coll; + uint32_t mcast_frames_sent; + uint32_t bcast_frames_sent; + uint32_t unrec_mac_ctrl_rcvd; + uint32_t fc_sent; + uint32_t good_fc_rcvd; + uint32_t bad_fc_rcvd; + uint32_t undersize_rcvd; + uint32_t fragments_rcvd; + uint32_t oversize_rcvd; + uint32_t jabber_rcvd; + uint32_t mac_rx_err; + uint32_t bad_crc_event; + uint32_t coll; + uint32_t late_coll; + } mib; + } stats; + struct { + Mc_Refcnt specl, other; + } mc_refcnt; +}; + +/* GLOBAL VARIABLES */ + +/* Format strings for statistics messages */ +static const char *mibfmt[] = { + " GOOD_OCTS_RCVD: %"PRIu64"\n", + 0, + " BAD_OCTS_RCVD: %"PRIu32"\n", + " INTERNAL_MAC_TX_ERR: %"PRIu32"\n", + " GOOD_FRAMES_RCVD: %"PRIu32"\n", + " BAD_FRAMES_RCVD: %"PRIu32"\n", + " BCAST_FRAMES_RCVD: %"PRIu32"\n", + " MCAST_FRAMES_RCVD: %"PRIu32"\n", + " FRAMES_64_OCTS: %"PRIu32"\n", + " FRAMES_65_127_OCTS: %"PRIu32"\n", + " FRAMES_128_255_OCTS: %"PRIu32"\n", + " FRAMES_256_511_OCTS: %"PRIu32"\n", + " FRAMES_512_1023_OCTS:%"PRIu32"\n", + " FRAMES_1024_MAX_OCTS:%"PRIu32"\n", + " GOOD_OCTS_SENT: %"PRIu64"\n", + 0, + " GOOD_FRAMES_SENT: %"PRIu32"\n", + " EXCESSIVE_COLL: %"PRIu32"\n", + " MCAST_FRAMES_SENT: %"PRIu32"\n", + " BCAST_FRAMES_SENT: %"PRIu32"\n", + " UNREC_MAC_CTRL_RCVD: %"PRIu32"\n", + " FC_SENT: %"PRIu32"\n", + " GOOD_FC_RCVD: %"PRIu32"\n", + " BAD_FC_RCVD: %"PRIu32"\n", + " UNDERSIZE_RCVD: %"PRIu32"\n", + " FRAGMENTS_RCVD: %"PRIu32"\n", + " OVERSIZE_RCVD: %"PRIu32"\n", + " JABBER_RCVD: %"PRIu32"\n", + " MAC_RX_ERR: %"PRIu32"\n", + " BAD_CRC_EVENT: %"PRIu32"\n", + " COLL: %"PRIu32"\n", + " LATE_COLL: %"PRIu32"\n", +}; + +/* Interrupt Handler Connection */ + +/* forward decls + implementation for IRQ API funcs */ + +STATIC int +mveth_init_rx_desc_ring(struct mveth_private *mp); + +STATIC int +mveth_init_tx_desc_ring(struct mveth_private *mp); + +int +BSP_mve_dring_nonsync(struct mveth_private *mp); + +static void mveth_isr(rtems_irq_hdl_param unit); +static void noop(const rtems_irq_connect_data *unused) {} +static int noop1(const rtems_irq_connect_data *unused) { return 0; } + +static rtems_irq_connect_data irq_data[MAX_NUM_SLOTS] = { + { + BSP_IRQ_ETH0, + 0, + (rtems_irq_hdl_param)0, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH1, + 0, + (rtems_irq_hdl_param)1, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH2, + 0, + (rtems_irq_hdl_param)2, + noop, + noop, + noop1 + }, +}; + +/* LOW LEVEL SUPPORT ROUTINES */ + +/* Software Cache Coherency */ +#ifndef ENABLE_HW_SNOOPING +#ifndef __PPC__ +#error "Software cache coherency maintenance is not implemented for your CPU architecture" +#endif + +static inline unsigned INVAL_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbi 0, %1":"=m"(*(cache_line*)d):"r"(d)); + return (unsigned)d; /* so this can be used in comma expression */ +} + +static inline void FLUSH_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbf 0, %0"::"r"(d),"m"(*(cache_line*)d)); +} + +static inline void FLUSH_BARRIER(void) +{ + asm volatile("eieio"); +} + +/* RX buffers are always cache-line aligned + * ASSUMPTIONS: + * - 'addr' is cache aligned + * - len is a multiple >0 of cache lines + */ +static inline void INVAL_BUF(register uintptr_t addr, register int len) +{ +typedef char maxbuf[2048]; /* more than an ethernet packet */ + do { + len -= RX_BUF_ALIGNMENT; + asm volatile("dcbi %0, %1"::"b"(addr),"r"(len)); + } while (len > 0); + asm volatile("":"=m"(*(maxbuf*)addr)); +} + +/* Flushing TX buffers is a little bit trickier; we don't really know their + * alignment but *assume* adjacent addresses are covering 'ordinary' memory + * so that flushing them does no harm! + */ +static inline void FLUSH_BUF(register uintptr_t addr, register int len) +{ + asm volatile("":::"memory"); + len = MV643XX_ALIGN(len, RX_BUF_ALIGNMENT); + do { + asm volatile("dcbf %0, %1"::"b"(addr),"r"(len)); + len -= RX_BUF_ALIGNMENT; + } while ( len >= 0 ); +} + +#else /* hardware snooping enabled */ + +/* inline this to silence compiler warnings */ +static inline int INVAL_DESC(volatile void *d) +{ return 0; } + +#define FLUSH_DESC(d) NOOP() +#define INVAL_BUF(b,l) NOOP() +#define FLUSH_BUF(b,l) NOOP() +#define FLUSH_BARRIER() NOOP() + +#endif /* cache coherency support */ + +/* Synchronize memory access */ +#ifdef __PPC__ +static inline void membarrier(void) +{ + asm volatile("sync":::"memory"); +} +#else +#error "memory barrier instruction not defined (yet) for this CPU" +#endif + +/* Enable and disable interrupts at the device */ +static inline void +mveth_enable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + val = (val | mask | MV643XX_ETH_IRQ_EXT_ENA) & mp->irq_mask; + + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), val); + + val = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + val = (val | mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), val); + + rtems_interrupt_enable(l); +} + +static inline uint32_t +mveth_disable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val,xval,tmp; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + tmp = ( (val & ~mask) | MV643XX_ETH_IRQ_EXT_ENA ) & mp->irq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), tmp); + + xval = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + tmp = (xval & ~mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), tmp); + + rtems_interrupt_enable(l); + + return (val | xval); +} + +/* This should be safe even w/o turning off interrupts if multiple + * threads ack different bits in the cause register (and ignore + * other ones) since writing 'ones' into the cause register doesn't + * 'stick'. + */ + +static inline uint32_t +mveth_ack_irqs(struct mveth_private *mp, uint32_t mask) +{ +register uint32_t x,xe,p; +register uint32_t rval; + + p = mp->port_num; + /* Get cause */ + x = MV_READ(MV643XX_ETH_INTERRUPT_CAUSE_R(p)); + + /* Ack interrupts filtering the ones we're interested in */ + + /* Note: EXT_IRQ bit clears by itself if EXT interrupts are cleared */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(p), ~ (x & mp->irq_mask & mask)); + + /* linux driver tests 1<<1 as a summary bit for extended interrupts; + * the mv64360 seems to use 1<<19 for that purpose; for the moment, + * I just check both. + * Update: link status irq (1<<16 in xe) doesn't set (1<<19) in x! + */ + if ( 1 /* x & 2 */ ) + { + xe = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p)); + + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p), ~ (xe & mp->xirq_mask & mask)); + } else { + xe = 0; + } +#ifdef MVETH_TESTING + if ( ((x & MV643XX_ETH_ALL_IRQS) & ~MV643XX_ETH_KNOWN_IRQS) + || ((xe & MV643XX_ETH_ALL_EXT_IRQS) & ~MV643XX_ETH_KNOWN_EXT_IRQS) ) { + fprintf(stderr, "Unknown IRQs detected; leaving all disabled for debugging:\n"); + fprintf(stderr, "Cause reg was 0x%08x, ext cause 0x%08x\n", x, xe); +/* + mp->irq_mask = 0; + mp->xirq_mask = 0; +*/ + } +#endif + /* luckily, the extended and 'normal' interrupts we use don't overlap so + * we can just OR them into a single word + */ + rval = (xe & mp->xirq_mask) | (x & mp->irq_mask); + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: mveth_ack_irqs 0x%08x\n", rval); +#endif + return rval; +} + +static void mveth_isr(rtems_irq_hdl_param arg) +{ +struct mveth_private *mp = (struct mveth_private*) arg; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mveth_isr\n"); +#endif + mp->stats.irqs++; + mp->isr(mp->isr_arg); +} + +static void +mveth_clear_mib_counters(struct mveth_private *mp) +{ +register int i; +register uint32_t b; + /* reading the counters resets them */ + b = MV643XX_ETH_MIB_COUNTERS(mp->port_num); + for (i=0; i< MV643XX_ETH_NUM_MIB_COUNTERS; i++, b+=4) + (void)MV_READ(b); +} + +/* Reading a MIB register also clears it. Hence we read the lo + * register first, then the hi one. Correct reading is guaranteed since + * the 'lo' register cannot overflow after it is read since it had + * been reset to 0. + */ +static unsigned long long +read_long_mib_counter(int port_num, int idx) +{ +unsigned long lo; +unsigned long long hi; + lo = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + idx++; + hi = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + return (hi<<32) | lo; +} + +static inline unsigned long +read_mib_counter(int port_num, int idx) +{ + return MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); +} + + +/* write ethernet address from buffer to hardware (need to change unicast filter after this) */ +static void +mveth_write_eaddr(struct mveth_private *mp, unsigned char *eaddr) +{ +int i; +uint32_t x; + + /* build hi word */ + for (i=4,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_HI(mp->port_num), x); + + /* build lo word */ + for (i=2,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_LO(mp->port_num), x); +} + +static inline int +port2phy(int port) +{ + port &= 0x1f; + /* during early init we may not know the phy and we are given a port number instead! */ + return ( (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*port)) & 0x1f ); +} + +/* PHY/MII Interface + * + * Read/write a PHY register; + * + * NOTE: The SMI register is shared among the three devices. + * Protection is provided by the global networking semaphore. + * If non-bsd drivers are running on a subset of IFs proper + * locking of all shared registers must be implemented! + */ +static unsigned +do_mii_read(int phy, unsigned addr) +{ +unsigned v; +unsigned wc = 0; + + addr &= 0x1f; + + /* wait until not busy */ + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_RD ); + + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + if (wc>0xffff) + wc = 0xffff; + return (wc<<16) | (v & 0xffff); +} + +unsigned +BSP_mve_mii_read(struct mveth_private *mp, unsigned addr) +{ +unsigned rval = do_mii_read(mp->phy, addr); +#ifdef MVETH_DEBUG + printk(DRVNAME": BSP_mve_mii_read(%d): 0x%08x\n", addr, rval); +#endif + return rval; +} + +unsigned +BSP_mve_mii_read_early(int port, unsigned addr) +{ + return do_mii_read(port2phy(port), addr); +} + +static unsigned +do_mii_write(int phy, unsigned addr, unsigned v) +{ +unsigned wc = 0; + + addr &= 0x1f; + v &= 0xffff; + + /* busywait is ugly but not preventing ISRs or high priority tasks from + * preempting us + */ + + /* wait until not busy */ + while ( MV643XX_ETH_SMI_BUSY & MV_READ(MV643XX_ETH_SMI_R) ) + wc++ /* wait */; + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_WR | v ); + + return wc; +} + +unsigned +BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v) +{ +#ifdef MVETH_DEBUG + printk(DRVNAME": BSP_mve_mii_write(%d): 0x%08x\n", addr, v); +#endif + return do_mii_write( mp->phy, addr, v ); +} + +unsigned +BSP_mve_mii_write_early(int port, unsigned addr, unsigned v) +{ + return do_mii_write(port2phy(port), addr, v); +} + + +/* MID-LAYER SUPPORT ROUTINES */ + +/* Start TX if descriptors are exhausted */ +static __inline__ void +mveth_start_tx(struct mveth_private *mp) +{ +uint32_t running; + if ( mp->avail <= 0 ) { + running = MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num)); + if ( ! (running & MV643XX_ETH_TX_START(0)) ) { + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + } + } +} + +/* Stop TX and wait for the command queues to stop and the fifo to drain */ +static uint32_t +mveth_stop_tx(int port) +{ +uint32_t active_q; + + active_q = (MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) & MV643XX_ETH_TX_ANY_RUNNING); + + if ( active_q ) { + /* Halt TX and wait for activity to stop */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port), MV643XX_ETH_TX_STOP_ALL); + while ( MV643XX_ETH_TX_ANY_RUNNING & MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) ) + /* poll-wait */; + /* Wait for Tx FIFO to drain */ + while ( ! (MV643XX_ETH_PORT_STATUS_R(port) & MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY) ) + /* poll-wait */; + } + + return active_q; +} + +void +BSP_mve_promisc_set(struct mveth_private *mp, int promisc) +{ +uint32_t v; + + v = MV_READ(MV643XX_ETH_PORT_CONFIG_R(mp->port_num)); + if ( (mp->promisc = promisc) ) + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + else + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), v); +} + +/* update serial port settings from current link status */ + +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; +uint32_t v = mp->promisc ? 0x01010101 : 0x00000000; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) { + MV_WRITE(s,v); + MV_WRITE(o,v); + s+=4; + o+=4; + } + for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i] = 0; + mp->mc_refcnt.other[i] = 0; + } +} + +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) { + MV_WRITE(s,0x01010101); + MV_WRITE(o,0x01010101); + s+=4; + o+=4; + /* Not clear what we should do with the reference count. + * For now just increment it. + */ + for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i]++; + mp->mc_refcnt.other[i]++; + } + } +} + +static void add_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( 0 == (*refcnt)[hash]++ ) { + val = MV_READ(off+slot) | ( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +static void del_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( (*refcnt)[hash] > 0 && 0 == --(*refcnt)[hash] ) { + val = MV_READ(off+slot) & ~( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + add_entry(tabl, hash, refcnt); +} + +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + del_entry(tabl, hash, refcnt); +} + +/* Clear all address filters (multi- and unicast) */ +static void +mveth_clear_addr_filters(struct mveth_private *mp) +{ +register int i; +register uint32_t u; + u = MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_UNICAST_ENTRIES; i++) { + MV_WRITE(u,0); + u+=4; + } + BSP_mve_mcast_filter_clear(mp); +} + +/* Setup unicast filter for a given MAC address (least significant nibble) */ +static void +mveth_ucfilter(struct mveth_private *mp, unsigned char mac_lsbyte, int accept) +{ +unsigned nib, slot, bit; +uint32_t val; + /* compute slot in table */ + nib = mac_lsbyte & 0xf; /* strip nibble */ + slot = nib & ~3; /* (nibble/4)*4 */ + bit = (nib & 3)<<3; /* 8*(nibble % 4) */ + val = MV_READ(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot); + if ( accept ) { + val |= 0x01 << bit; + } else { + val &= 0x0e << bit; + } + MV_WRITE(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot, val); +} + +#if defined( ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM ) && 0 +/* Currently unused; small unaligned buffers seem to be rare + * so we just use memcpy()... + */ + +/* memcpy for 0..7 bytes; arranged so that gcc + * optimizes for powerpc... + */ + +static inline void memcpy8(void *to, void *fr, unsigned x) +{ +register uint8_t *d = to, *s = fro; + + d+=l; s+=l; + if ( l & 1 ) { + *--d=*--s; + } + if ( l & 2 ) { + /* pre-decrementing causes gcc to use auto-decrementing + * PPC instructions (lhzu rx, -2(ry)) + */ + d-=2; s-=2; + /* use memcpy; don't cast to short -- accessing + * misaligned data as short is not portable + * (but it works on PPC). + */ + __builtin_memcpy(d,s,2); + } + if ( l & 4 ) { + d-=4; s-=4; + /* see above */ + __builtin_memcpy(d,s,4); + } +} +#endif + +static int +mveth_assign_desc_raw(MvEthTxDesc d, void *buf, int len, void *uptr, unsigned long extra) +{ +int rval = (d->byte_cnt = len); + +#ifdef MVETH_TESTING + assert( !d->u_buf ); + assert( len ); +#endif + + /* set CRC on all descriptors; seems to be necessary */ + d->cmd_sts = extra | (TDESC_GEN_CRC | TDESC_ZERO_PAD); + +#ifdef ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + /* The buffer must be 64bit aligned if the payload is <8 (??) */ + if ( rval < 8 && ( ((uintptr_t)buf) & 7) ) { + d->buf_ptr = CPUADDR2ENET( d->workaround ); + memcpy((void*)d->workaround, buf, rval); + } else +#endif + { + d->buf_ptr = CPUADDR2ENET( (unsigned long)buf ); + } + d->u_buf = uptr; + d->l4i_chk = 0; + return rval; +} + +/* + * Ring Initialization + * + * ENDIAN ASSUMPTION: DMA engine matches CPU endianness (???) + * + * Linux driver discriminates __LITTLE and __BIG endian for re-arranging + * the u16 fields in the descriptor structs. However, no endian conversion + * is done on the individual fields (SDMA byte swapping is disabled on LE). + */ + +STATIC int +mveth_init_rx_desc_ring(struct mveth_private *mp) +{ +int i,sz; +MvEthRxDesc d; +uintptr_t baddr; + + memset((void*)mp->rx_ring, 0, sizeof(*mp->rx_ring)*mp->rbuf_count); + + mp->rx_desc_dma = CPUADDR2ENET(mp->rx_ring); + + for ( i=0, d = mp->rx_ring; i<mp->rbuf_count; i++, d++ ) { + d->u_buf = mp->alloc_rxbuf(&sz, &baddr); + assert( d->u_buf ); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + + d->buf_size = sz; + d->byte_cnt = 0; + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + d->next = mp->rx_ring + (i+1) % mp->rbuf_count; + + d->buf_ptr = CPUADDR2ENET( baddr ); + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_rx_t = mp->rx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->rx_desc_dma); + + + return i; +} + +STATIC int +mveth_init_tx_desc_ring(struct mveth_private *mp) +{ +int i; +MvEthTxDesc d; + + memset((void*)mp->tx_ring, 0, sizeof(*mp->tx_ring)*mp->xbuf_count); + + /* DMA and CPU live in the same address space (rtems) */ + mp->tx_desc_dma = CPUADDR2ENET(mp->tx_ring); + mp->avail = TX_AVAILABLE_RING_SIZE(mp); + + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++,d++ ) { + d->l4i_chk = 0; + d->byte_cnt = 0; + d->cmd_sts = 0; + d->buf_ptr = 0; + + d->next = mp->tx_ring + (i+1) % mp->xbuf_count; + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_tx_h = mp->d_tx_t = mp->tx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->tx_desc_dma); + + return i; +} + +/* PUBLIC LOW-LEVEL DRIVER ACCESS */ + +struct mveth_private * +BSP_mve_create( + int unit, + rtems_id tid, + void (*isr)(void*isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +) +{ +struct mveth_private *mp; +int InstallISRSuccessful; + + if ( unit <= 0 || unit > MV643XXETH_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, MV643XXETH_NUM_DRIVER_SLOTS); + return 0; + } + + if ( rx_ring_size < 0 && tx_ring_size < 0 ) + return 0; + + if ( MV_64360 != BSP_getDiscoveryVersion(0) ) { + printk(DRVNAME": not mv64360 chip\n"); + return 0; + } + + if ( tx_ring_size < 1 ) { + printk(DRVNAME": tx ring size must not be zero (networking configuration issue?)\n"); + return 0; + } + + if ( rx_ring_size < 1 ) { + printk(DRVNAME": rx ring size must not be zero (networking configuration issue?)\n"); + return 0; + } + + mp = calloc( 1, sizeof *mp ); + + mp->port_num = unit-1; + mp->phy = port2phy(mp->port_num); + + mp->tid = tid; + mp->isr = isr; + mp->isr_arg = isr_arg; + + mp->cleanup_txbuf = cleanup_txbuf; + mp->cleanup_txbuf_arg = cleanup_txbuf_arg; + mp->alloc_rxbuf = alloc_rxbuf; + mp->consume_rxbuf = consume_rxbuf; + mp->consume_rxbuf_arg = consume_rxbuf_arg; + + mp->rbuf_count = rx_ring_size; + mp->xbuf_count = tx_ring_size; + + if ( mp->xbuf_count > 0 ) + mp->xbuf_count += TX_NUM_TAG_SLOTS; + + if ( mp->rbuf_count < 0 ) + mp->rbuf_count = 0; + if ( mp->xbuf_count < 0 ) + mp->xbuf_count = 0; + + /* allocate ring area; add 1 entry -- room for alignment */ + assert( !mp->ring_area ); + mp->ring_area = malloc( sizeof(*mp->ring_area) * (mp->rbuf_count + mp->xbuf_count + 1) ); + assert( mp->ring_area ); + + BSP_mve_stop_hw(mp); + + if ( irq_mask ) { + irq_data[mp->port_num].hdl = mveth_isr; + irq_data[mp->port_num].handle = (rtems_irq_hdl_param)mp; + InstallISRSuccessful = BSP_install_rtems_irq_handler( &irq_data[mp->port_num] ); + assert( InstallISRSuccessful ); + } + + if ( rx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_IRQ_RX_DONE; + if ( tx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_EXT_IRQ_TX_DONE; + + mp->irq_mask = (irq_mask & MV643XX_ETH_IRQ_RX_DONE); + if ( (irq_mask &= (MV643XX_ETH_EXT_IRQ_TX_DONE | MV643XX_ETH_EXT_IRQ_LINK_CHG)) ) { + mp->irq_mask |= MV643XX_ETH_IRQ_EXT_ENA; + mp->xirq_mask = irq_mask; + } else { + mp->xirq_mask = 0; + } + + return mp; +} + +void +BSP_mve_update_serial_port(struct mveth_private *mp, int media) +{ +int port = mp->port_num; +uint32_t old, new; + +#ifdef MVETH_DEBUG + printk(DRVNAME": Entering BSP_mve_update_serial_port()\n"); +#endif + + new = old = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(port)); + + /* mask speed and duplex settings */ + new &= ~( MV643XX_ETH_SET_GMII_SPEED_1000 + | MV643XX_ETH_SET_MII_SPEED_100 + | MV643XX_ETH_SET_FULL_DUPLEX ); + + if ( (MV643XX_MEDIA_FD & media) ) + new |= MV643XX_ETH_SET_FULL_DUPLEX; + + switch ( (media & MV643XX_MEDIA_SPEED_MSK) ) { + default: /* treat as 10 */ + break; + case MV643XX_MEDIA_100: + new |= MV643XX_ETH_SET_MII_SPEED_100; + break; + case MV643XX_MEDIA_1000: + new |= MV643XX_ETH_SET_GMII_SPEED_1000; + break; + } + + + if ( new != old ) { + if ( ! (MV643XX_ETH_SERIAL_PORT_ENBL & new) ) { + /* just write */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + } else { + uint32_t were_running; + + were_running = mveth_stop_tx(port); + + old &= ~MV643XX_ETH_SERIAL_PORT_ENBL; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), old); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + /* linux driver writes twice... */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + + if ( were_running ) { + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + } + } + } + /* If TX stalled because there was no buffer then whack it */ + mveth_start_tx(mp); +} + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp) +{ + return mp->tid; +} + +int +BSP_mve_detach(struct mveth_private *mp) +{ + BSP_mve_stop_hw(mp); + if ( mp->irq_mask || mp->xirq_mask ) { + if ( !BSP_remove_rtems_irq_handler( &irq_data[mp->port_num] ) ) + return -1; + } + free( (void*)mp->ring_area ); + memset(mp, 0, sizeof(*mp)); + __asm__ __volatile__("":::"memory"); + return 0; +} + +/* MAIN RX-TX ROUTINES + * + * BSP_mve_swipe_tx(): descriptor scavenger; releases mbufs + * BSP_mve_send_buf(): xfer mbufs from IF to chip + * BSP_mve_swipe_rx(): enqueue received mbufs to interface + * allocate new ones and yield them to the + * chip. + */ + +/* clean up the TX ring freeing up buffers */ +int +BSP_mve_swipe_tx(struct mveth_private *mp) +{ +int rval = 0; +register MvEthTxDesc d; + + for ( d = mp->d_tx_t; d->buf_ptr; d = NEXT_TXD(d) ) { + + INVAL_DESC(d); + + if ( (TDESC_DMA_OWNED & d->cmd_sts) + && (uint32_t)d == MV_READ(MV643XX_ETH_CURRENT_SERVED_TX_DESC(mp->port_num)) ) + break; + + /* d->u_buf is only set on the last descriptor in a chain; + * we only count errors in the last descriptor; + */ + if ( d->u_buf ) { + mp->cleanup_txbuf(d->u_buf, mp->cleanup_txbuf_arg, (d->cmd_sts & TDESC_ERROR) ? 1 : 0); + d->u_buf = 0; + } + + d->buf_ptr = 0; + + rval++; + } + mp->d_tx_t = d; + mp->avail += rval; + + return mp->avail; +} + +int +BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it) +{ +int rval; +register MvEthTxDesc l,d,h; +int nmbs; +MveEthBufIter head = *it; + + rval = 0; + + /* if no descriptor is available; try to wipe the queue */ + if ( (mp->avail < 1) && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + /* Maybe TX is stalled and needs to be restarted */ + mveth_start_tx(mp); + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->u_buf ); +#endif + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second mbuf and fill the first descriptor + * last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + nmbs = 1; + while ( (it = next(it)) ) { + if ( 0 == it->len ) + continue; /* skip empty mbufs */ + + nmbs++; + + if ( mp->avail < 1 && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + /* Maybe TX was stalled - try to restart */ + mveth_start_tx(mp); + + /* not enough descriptors; cleanup... + * the first slot was never used, so we start + * at mp->d_tx_h->next; + */ + for ( l = NEXT_TXD(h); l!=d; l=NEXT_TXD(l) ) { +#ifdef MVETH_TESTING + assert( l->u_buf == 0 ); +#endif + l->buf_ptr = 0; + l->cmd_sts = 0; + mp->avail++; + } + mp->avail++; + if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) { + /* this chain will never fit into the ring */ + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + mp->stats.repack++; + /* caller may reorganize chain */ + return -2; + } + return -1; + } + + mp->avail--; + +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + + /* fill this slot */ + rval += mveth_assign_desc_raw(d, it->data, it->len, it->uptr, TDESC_DMA_OWNED); + + FLUSH_BUF( (uint32_t)it->data, it->len ); + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, head.data, head.len, head.uptr, TDESC_FRST); + + + FLUSH_BUF((uint32_t)head.data, head.len); + + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + h->cmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +int +BSP_mve_send_buf_raw( + struct mveth_private *mp, + void *head_p, + int h_len, + void *data_p, + int d_len) +{ +int rval; +register MvEthTxDesc l,d,h; +int needed; +void *frst_buf; +int frst_len; +void *uarg; + + rval = 0; + +#ifdef MVETH_TESTING + assert(head_p || data_p); +#endif + + needed = head_p && data_p ? 2 : 1; + + /* if no descriptor is available; try to wipe the queue */ + if ( ( mp->avail < needed ) + && ( MVETH_CLEAN_ON_SEND(mp) <= 0 || mp->avail < needed ) ) { + /* Maybe TX was stalled and needs a restart */ + mveth_start_tx(mp); + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->u_buf ); +#endif + + /* find the 'first' user buffer */ + if ( (frst_buf = head_p) && (h_len > 0) ) { + frst_len = h_len; + } else { + frst_buf = data_p; + frst_len = d_len; + } + + uarg = (head_p && ! h_len) ? head_p : frst_buf; + + /* Legacy: if h_len == 0 but head_p is not then use that for the user arg */ + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second (optional) slot and fill the first + * descriptor last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + if ( needed > 1 ) { + mp->avail--; +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + rval += mveth_assign_desc_raw(d, data_p, d_len, 0, TDESC_DMA_OWNED); + FLUSH_BUF( (uint32_t)data_p, d_len ); + d->u_buf = data_p; + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot with raw buffer - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, frst_buf, frst_len, 0, TDESC_FRST); + + FLUSH_BUF( (uint32_t)frst_buf, frst_len); + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + + /* first buffer of 'chain' goes into last desc */ + l->u_buf = uarg; + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + h->cmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( needed > mp->stats.maxchain ) + mp->stats.maxchain = needed; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +/* send received buffers upwards and replace them + * with freshly allocated ones; + * ASSUMPTION: buffer length NEVER changes and is set + * when the ring is initialized. + * TS 20060727: not sure if this assumption is still necessary - I believe it isn't. + */ + +int +BSP_mve_swipe_rx(struct mveth_private *mp) +{ +int rval = 0, err; +register MvEthRxDesc d; +void *newbuf; +int sz; +uintptr_t baddr; + + for ( d = mp->d_rx_t; ! (INVAL_DESC(d), (RDESC_DMA_OWNED & d->cmd_sts)); d=NEXT_RXD(d) ) { + +#ifdef MVETH_TESTING + assert(d->u_buf); +#endif + + err = (RDESC_ERROR & d->cmd_sts); + + if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) { + /* drop packet and recycle buffer */ + newbuf = d->u_buf; + mp->stats.idrops++; + } else { +#ifdef MVETH_TESTING + assert( d->byte_cnt > 0 ); +#endif + mp->consume_rxbuf(d->u_buf, mp->consume_rxbuf_arg, d->byte_cnt); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + d->u_buf = newbuf; + d->buf_ptr = CPUADDR2ENET(baddr); + d->buf_size = sz; + FLUSH_DESC(d); + } + + membarrier(); + + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + + FLUSH_DESC(d); + + rval++; + } + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + mp->d_rx_t = d; + return rval; +} + +/* Stop hardware and clean out the rings */ +void +BSP_mve_stop_hw(struct mveth_private *mp) +{ +MvEthTxDesc d; +MvEthRxDesc r; +int i; + + mveth_disable_irqs(mp, -1); + + mveth_stop_tx(mp->port_num); + + /* cleanup TX rings */ + if (mp->d_tx_t) { /* maybe ring isn't initialized yet */ + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) { + /* should be safe to clear ownership */ + d->cmd_sts &= ~TDESC_DMA_OWNED; + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + BSP_mve_swipe_tx(mp); + +#ifdef MVETH_TESTING + assert( mp->d_tx_h == mp->d_tx_t ); + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) { + assert( !d->buf_ptr ); + } +#endif + } + + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_STOP_ALL); + while ( MV643XX_ETH_RX_ANY_RUNNING & MV_READ(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num)) ) + /* poll-wait */; + + /* stop serial port */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)) + & ~( MV643XX_ETH_SERIAL_PORT_ENBL | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE | MV643XX_ETH_FORCE_LINK_PASS) + ); + + /* clear pending interrupts */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + + /* cleanup RX rings */ + if ( mp->rx_ring ) { + for ( i=0, r=mp->rx_ring; i<mp->rbuf_count; i++, r++ ) { + /* should be OK to clear ownership flag */ + r->cmd_sts = 0; + FLUSH_DESC(r); + mp->consume_rxbuf(r->u_buf, mp->consume_rxbuf_arg, 0); + r->u_buf = 0; + } + FLUSH_BARRIER(); + } + + +} + +uint32_t mveth_serial_ctrl_config_val = MVETH_SERIAL_CTRL_CONFIG_VAL; + +/* Fire up the low-level driver + * + * - make sure hardware is halted + * - enable cache snooping + * - clear address filters + * - clear mib counters + * - reset phy + * - initialize (or reinitialize) descriptor rings + * - check that the firmware has set up a reasonable mac address. + * - generate unicast filter entry for our mac address + * - write register config values to the chip + * - start hardware (serial port and SDMA) + */ + +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int media) +{ +int i; +uint32_t v; +static int inited = 0; + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Entering BSP_mve_init_hw()\n", mp->port_num+1); +#endif + + /* since enable/disable IRQ routine only operate on select bitsets + * we must make sure everything is masked initially. + */ + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), 0); + + BSP_mve_stop_hw(mp); + + memset(&mp->stats, 0, sizeof(mp->stats)); + + mp->promisc = promisc; + + /* MotLoad has cache snooping disabled on the ENET2MEM windows. + * Some comments in (linux) indicate that there are errata + * which cause problems which would be a real bummer. + * We try it anyways... + */ + if ( !inited ) { + unsigned long disbl, bar; + inited = 1; /* FIXME: non-thread safe lazy init */ + disbl = MV_READ(MV643XX_ETH_BAR_ENBL_R); + /* disable all 6 windows */ + MV_WRITE(MV643XX_ETH_BAR_ENBL_R, MV643XX_ETH_BAR_DISBL_ALL); + /* set WB snooping on enabled bars */ + for ( i=0; i<MV643XX_ETH_NUM_BARS*8; i+=8 ) { + if ( (bar = MV_READ(MV643XX_ETH_BAR_0 + i)) && MV_READ(MV643XX_ETH_SIZE_R_0 + i) ) { +#ifdef ENABLE_HW_SNOOPING + MV_WRITE(MV643XX_ETH_BAR_0 + i, bar | MV64360_ENET2MEM_SNOOP_WB); +#else + MV_WRITE(MV643XX_ETH_BAR_0 + i, bar & ~MV64360_ENET2MEM_SNOOP_MSK); +#endif + /* read back to flush fifo [linux comment] */ + (void)MV_READ(MV643XX_ETH_BAR_0 + i); + } + } + /* restore/re-enable */ + MV_WRITE(MV643XX_ETH_BAR_ENBL_R, disbl); + } + + mveth_clear_mib_counters(mp); + mveth_clear_addr_filters(mp); + +/* Just leave it alone... + reset_phy(); +*/ + + if ( mp->rbuf_count > 0 ) { + mp->rx_ring = (MvEthRxDesc)MV643XX_ALIGN(mp->ring_area, RING_ALIGNMENT); + mveth_init_rx_desc_ring(mp); + } + + if ( mp->xbuf_count > 0 ) { + mp->tx_ring = (MvEthTxDesc)mp->rx_ring + mp->rbuf_count; + mveth_init_tx_desc_ring(mp); + } + + if ( enaddr ) { + /* set ethernet address from arpcom struct */ +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Writing MAC addr ", mp->port_num+1); + for (i=5; i>=0; i--) { + printk("%02X%c", enaddr[i], i?':':'\n'); + } +#endif + mveth_write_eaddr(mp, enaddr); + } + + /* set mac address and unicast filter */ + + { + uint32_t machi, maclo; + maclo = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + machi = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* ASSUME: firmware has set the mac address for us + * - if assertion fails, we have to do more work... + */ + assert( maclo && machi && maclo != 0xffffffff && machi != 0xffffffff ); + mveth_ucfilter(mp, maclo&0xff, 1/* accept */); + } + + /* port, serial and sdma configuration */ + v = MVETH_PORT_CONFIG_VAL; + if ( promisc ) { + /* multicast filters were already set up to + * accept everything (mveth_clear_addr_filters()) + */ + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + } else { + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + } + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), + v); + MV_WRITE(MV643XX_ETH_PORT_CONFIG_XTEND_R(mp->port_num), + MVETH_PORT_XTEND_CONFIG_VAL); + + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + v &= ~(MVETH_SERIAL_CTRL_CONFIG_MSK); + v |= mveth_serial_ctrl_config_val; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), v); + + if ( (MV643XX_MEDIA_LINK & media) ) { + BSP_mve_update_serial_port(mp, media); + } + + /* enable serial port */ + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + v | MV643XX_ETH_SERIAL_PORT_ENBL); + +#ifndef __BIG_ENDIAN__ +#error "byte swapping needs to be disabled for little endian machines" +#endif + MV_WRITE(MV643XX_ETH_SDMA_CONFIG_R(mp->port_num), MVETH_SDMA_CONFIG_VAL); + + /* allow short frames */ + MV_WRITE(MV643XX_ETH_RX_MIN_FRAME_SIZE_R(mp->port_num), MVETH_MIN_FRAMSZ_CONFIG_VAL); + + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + /* TODO: set irq coalescing */ + + /* enable Rx */ + if ( mp->rbuf_count > 0 ) { + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + } + + mveth_enable_irqs(mp, -1); + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Leaving BSP_mve_init_hw()\n", mp->port_num+1); +#endif +} + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr) +{ +int i; +uint32_t x; +unsigned char buf[6], *eaddr; + + eaddr = oeaddr ? oeaddr : buf; + + eaddr += 5; + x = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + + /* lo word */ + for (i=2; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + x = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* hi word */ + for (i=4; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + if ( !oeaddr ) { + printf("%02X",buf[0]); + for (i=1; i<sizeof(buf); i++) + printf(":%02X",buf[i]); + printf("\n"); + } +} + +void +BSP_mve_enable_irqs(struct mveth_private *mp) +{ + mveth_enable_irqs(mp, -1); +} + +void +BSP_mve_disable_irqs(struct mveth_private *mp) +{ + mveth_disable_irqs(mp, -1); +} + +uint32_t +BSP_mve_ack_irqs(struct mveth_private *mp) +{ + return mveth_ack_irqs(mp, -1); +} + + +void +BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + mveth_enable_irqs(mp, mask); +} + +uint32_t +BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + return mveth_disable_irqs(mp, mask); +} + +uint32_t +BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + return mveth_ack_irqs(mp, mask); +} + +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f) +{ +int p = mp->port_num; +int idx; +uint32_t v; + + if ( !f ) + f = stdout; + + fprintf(f, DRVNAME"%i Statistics:\n", mp->port_num + 1); + fprintf(f, " # IRQS: %i\n", mp->stats.irqs); + fprintf(f, " Max. mbuf chain length: %i\n", mp->stats.maxchain); + fprintf(f, " # repacketed: %i\n", mp->stats.repack); + fprintf(f, " # packets: %i\n", mp->stats.packet); + fprintf(f, " # buffer alloc failed: %i\n", mp->stats.idrops); + fprintf(f, "MIB Counters:\n"); + for ( idx = MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2; + idx < MV643XX_ETH_NUM_MIB_COUNTERS; + idx++ ) { + switch ( idx ) { + case MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2: + mp->stats.mib.good_octs_rcvd += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_rcvd); + idx++; + break; + + case MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO>>2: + mp->stats.mib.good_octs_sent += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_sent); + idx++; + break; + + default: + v = ((uint32_t*)&mp->stats.mib)[idx] += read_mib_counter(p, idx); + fprintf(f, mibfmt[idx], v); + break; + } + } + fprintf(f, "\n"); +} + +#ifdef MVETH_DEBUG +/* Display/dump descriptor rings */ + +/* These low-level routines need to be synchronized with + * any Tx/Rx threads! + */ +int +BSP_mve_dring_nonsync(struct mveth_private *mp) +{ +int i; +if (1) { +MvEthRxDesc pr; +printf("RX:\n"); + + for (i=0, pr=mp->rx_ring; i<mp->rbuf_count; i++, pr++) { + printf("cnt: 0x%04x, size: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n", + pr->byte_cnt, pr->buf_size, pr->cmd_sts, (uint32_t)pr->next_desc_ptr, pr->buf_ptr); + } +} +if (1) { +MvEthTxDesc pt; +printf("TX:\n"); + for (i=0, pt=mp->tx_ring; i<mp->xbuf_count; i++, pt++) { + printf("cnt: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x, mb: 0x%08x\n", + pt->byte_cnt, pt->cmd_sts, (uint32_t)pt->next_desc_ptr, pt->buf_ptr, + (uint32_t)pt->u_buf); + } +} + return 0; +} +#endif + +#endif /* LIBBSP_BEATNIK_BSP_H */ diff --git a/rtemsbsd/sys/dev/mve/if_mve_nexus.c b/rtemsbsd/sys/dev/mve/if_mve_nexus.c new file mode 100644 index 00000000..be9433da --- /dev/null +++ b/rtemsbsd/sys/dev/mve/if_mve_nexus.c @@ -0,0 +1,935 @@ +/* RTEMS driver for the mv643xx gigabit ethernet chip */ + +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* Nexus port by Till Straumann, <till.straumann@psi.ch>, 3/2021 */ + +#include <machine/rtems-bsd-kernel-space.h> +#include <bsp.h> + +#ifdef LIBBSP_BEATNIK_BSP_H + +#include <sys/types.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/ethernet.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <rtems/bsd/local/miibus_if.h> +#include <stdio.h> +#include <bsp/mv643xx_eth.h> + +#define DRVNAME "mv63xx_nexus" + +#undef MVETH_DEBUG + +/* Define default ring sizes */ + +#undef MVETH_TESTING + +#ifdef MVETH_TESTING +/* hard and small defaults */ +#define MV643XX_RX_RING_SIZE 2 +#define MV643XX_TX_QUEUE_SIZE 4 +#define MV643XX_BD_PER_PACKET 1 +#define TX_LOWWATER 1 + +#else /* MVETH_TESTING */ + +#define MV643XX_RX_RING_SIZE 40 /* attached buffers are always 2k clusters, i.e., this + * driver - with a configured ring size of 40 - constantly + * locks 80k of cluster memory - your app config better + * provides enough space! + */ +#define MV643XX_TX_QUEUE_SIZE 40 +#define MV643XX_BD_PER_PACKET 10 +#define TX_LOWWATER (4*(MV643XX_BD_PER_PACKET)) +#endif /* MVETH_TESTING */ + +/* NOTE: tx ring size MUST be > max. # of fragments / mbufs in a chain; + * I observed chains of >17 entries regularly! + */ +#define MV643XX_TX_RING_SIZE ((MV643XX_TX_QUEUE_SIZE) * (MV643XX_BD_PER_PACKET)) + +/* The chip puts the ethernet header at offset 2 into the buffer so + * that the payload is aligned + */ +#define ETH_RX_OFFSET 2 +#define ETH_CRC_LEN 4 /* strip FCS at end of packet */ + +#ifndef __PPC__ +#error "Dont' know how to deal with cache on this CPU architecture" +#endif + +/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency + * management works for cache line sizes of 16 and 32 bytes only. If the line size + * is bigger, the descriptors could be padded... + */ +#if !defined(PPC_CACHE_ALIGNMENT) +#error "PPC_CACHE_ALIGNMENT not defined" +#elif PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32 +#error "Cache line size must be 16 or 32" +#else +#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT +#endif + +/* HELPER MACROS */ + +/* Align base to alignment 'a' */ +#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1))) + + +#define IRQ_EVENT RTEMS_EVENT_0 +#define TX_EVENT RTEMS_EVENT_1 + +/* Hacks -- FIXME */ +rtems_id +rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg); +#define SIO_RTEMS_SHOW_STATS _IO('i', 250) + +#define MVE643XX_DUMMY_PHY 0 /* phy is defined by low-level driver */ + +struct mve_enet_softc { + device_t dev; + struct ifnet *ifp; + device_t miibus; + struct mii_data *mii_softc; + struct mveth_private *mp; + struct mtx mtx; + struct callout wdCallout; + rtems_id daemonTid; + int oif_flags; +}; + +static struct mve_enet_softc * ifaces[MV643XXETH_NUM_DRIVER_SLOTS] = { 0 }; + +typedef struct MveMbufIter { + MveEthBufIter it; + struct mbuf *next; + struct mbuf *head; +} MveMbufIter; + +/* Forward Declarations */ +struct mve_enet_softc; + +static void +mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr); + +static int +mve_media_change(struct ifnet *ifp); + +static void +mve_set_filters(struct ifnet *ifp); + +static int +xlateMediaFlags(const struct mii_data *mid); + +static void +mve_ack_link_change(struct mve_enet_softc *sc); + +static __inline__ void +mve_send_event(struct mve_enet_softc *sc, rtems_event_set ev) +{ +rtems_status_code st; + if ( RTEMS_SUCCESSFUL != (st = rtems_event_send(sc->daemonTid, ev)) ) { + printk(DRVNAME": rtems_event_send returned 0x%08x (TID: 0x%08x, sc: 0x%08x)\n", st, sc->daemonTid, sc); + rtems_panic(DRVNAME": rtems_event_send() failed!\n"); + } +} + +static __inline__ void +mve_lock(struct mve_enet_softc *sc, const char *from) +{ + mtx_lock( & sc->mtx ); +/*printk("L V %s\n", from);*/ +} + +static __inline__ void +mve_unlock(struct mve_enet_softc *sc, const char *from) +{ +/*printk("L ^ %s\n", from);*/ + mtx_unlock( & sc->mtx ); +} + +static int +mve_probe(device_t dev) +{ + int unit = device_get_unit(dev); + int err; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_probe (entering)\n"); +#endif + + if ( unit >= 0 && unit < MV643XXETH_NUM_DRIVER_SLOTS ) { + err = BUS_PROBE_DEFAULT; + } else { + err = ENXIO; + } + + return err; +} + +/* + * starting at 'm' scan the buffer chain until we + * find a non-empty buffer (which we return) + */ +static __inline__ struct mbuf * +skipEmpty(struct mbuf *m) +{ + while ( m && ( 0 == m->m_len ) ) { + m = m->m_next; + } + return m; +} + +/* + * Record a buffer's info in the low-leve driver 'iterator' struct. + * Also scan ahead to find the next non-empty buffer (store it in + * the iterator's 'next' field). This info is needed because we + * want to know if 'this' buffer is the last (non-empty!) one + * in a chain. + * + * On entry 'it->next' identifies 'this' buffer and on return + * 'it->next' points to the next non-empty buffer. + */ +static MveEthBufIter * +nextBuf(MveEthBufIter *arg) +{ +MveMbufIter *it = (MveMbufIter*)arg; +struct mbuf *m; + /* If 'this' buffer is non-null */ + if ( (m = it->next) ) { + /* find next non-empty buffer */ + it->next = skipEmpty( m->m_next ); + /* record 'this' buffer's info */ + it->it.data = mtod(m, void*); + it->it.len = m->m_len; + /* if there is a non-empty buffer after 'this' uptr is NULL + * if this is tha last buffer in a chain then record the + * head of the chain in the uptr (for eventual cleanup + * by release_tx_mbuf()). + */ + it->it.uptr = it->next ? 0 : it->head; + return (MveEthBufIter*)it; + } + return 0; +} + +/* + * Initialize the iterator struct + */ +static MveEthBufIter * +initIter(MveMbufIter *it, struct mbuf *m) +{ + /* record the head of the chain */ + it->head = m; + /* initialize 'next' field to the first non-empty buffer. + * This may be NULL if the chain is entirely empty but + * that is handled correctly. + */ + it->next = skipEmpty( m ); + /* Fill with first buf info */ + return nextBuf( &it->it ); +} + +static int +mve_send_mbuf( struct mve_enet_softc *sc, struct mbuf *m_head ) +{ +MveMbufIter iter; +int rval; + + if ( ! m_head ) { + return 0; + } + + if ( ! initIter( &iter, m_head ) ) { + /* completely empty chain */ + m_freem( m_head ); + return 0; + } + + rval = BSP_mve_send_buf_chain( sc->mp, nextBuf, &iter.it ); + + return rval; +} + +static void +mve_isr(void *closure) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_isr; posting event to %x\n", sc->daemonTid); +#endif + BSP_mve_disable_irqs( sc->mp ); + mve_send_event( sc, IRQ_EVENT ); +} + +static void +mve_stop(struct mve_enet_softc *sc) +{ + BSP_mve_stop_hw( sc->mp ); + /* clear IF flags */ + if_setdrvflagbits(sc->ifp, 0, (IFF_DRV_OACTIVE | IFF_DRV_RUNNING)); +} + +static void +mve_set_filters(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +int iff = if_getflags(ifp); +struct ifmultiaddr *ifma; +unsigned char *lladdr; + + BSP_mve_promisc_set( sc->mp, !!(iff & IFF_PROMISC)); + + if ( iff & (IFF_PROMISC | IFF_ALLMULTI) ) { + BSP_mve_mcast_filter_accept_all(sc->mp); + } else { + BSP_mve_mcast_filter_clear( sc->mp ); + + if_maddr_rlock( ifp ); + + CK_STAILQ_FOREACH( ifma, &ifp->if_multiaddrs, ifma_link ) { + + if ( ifma->ifma_addr->sa_family != AF_LINK ) { + continue; + } + + lladdr = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); + + BSP_mve_mcast_filter_accept_add( sc->mp, lladdr ); + + } + + if_maddr_runlock( ifp ); + } +} + +/* Daemon task does all the 'interrupt' work */ +static void +mve_daemon(void *arg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) arg; +struct ifnet *ifp = sc->ifp; +rtems_event_set evs; +struct mbuf *m; +int avail; +int sndStat; +uint32_t irqstat; + +#ifdef MVETH_DEBUG + printk(DRVNAME": bsdnet mveth_daemon started\n"); +#endif + + mve_lock( sc, "daemon" ); + + for (;;) { + + mve_unlock( sc, "daemon" ); + if ( RTEMS_SUCCESSFUL != rtems_event_receive( (IRQ_EVENT | TX_EVENT), (RTEMS_WAIT | RTEMS_EVENT_ANY), RTEMS_NO_TIMEOUT, &evs ) ) { + rtems_panic(DRVNAME": rtems_event_receive() failed!\n"); + } + mve_lock( sc, "daemon" ); + +#ifdef MVETH_DEBUG + printk(DRVNAME": bsdnet mveth_daemon event received 0x%x\n", evs); +#endif + + if ( !(if_getflags(ifp) & IFF_UP) ) { + mve_stop(sc); + /* clear flag */ + if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING); + continue; + } + + if ( ! (if_getdrvflags(ifp) & IFF_DRV_RUNNING) ) { + /* event could have been pending at the time hw was stopped; + * just ignore... + */ + continue; + } + + if ( (evs & IRQ_EVENT) ) { + irqstat = BSP_mve_ack_irqs(sc->mp); + } else { + irqstat = 0; + } + + if ( (MV643XX_ETH_EXT_IRQ_LINK_CHG & irqstat) && sc->mii_softc ) { + /* phy status changed */ + mii_pollstat( sc->mii_softc ); + } + + /* free tx chain and send */ + if ( (evs & TX_EVENT) || (MV643XX_ETH_EXT_IRQ_TX_DONE & irqstat) ) { + while ( (avail = BSP_mve_swipe_tx( sc->mp )) > TX_LOWWATER ) { + IF_DEQUEUE( &ifp->if_snd, m ); + if ( ! m ) { + /* clear active bit */ + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + break; + } + sndStat = mve_send_mbuf( sc, m ); + if ( sndStat < 0 ) { + /* maybe not enough space right now; requeue and wait for next IRQ */ + IF_PREPEND( &ifp->if_snd, m ); + break; + } + } + } + if ( (MV643XX_ETH_IRQ_RX_DONE & irqstat) ) { + BSP_mve_swipe_rx(sc->mp); + } + + BSP_mve_enable_irqs(sc->mp); + } + + mve_unlock( sc, "daemon (xit)" ); +} + +static void +release_tx_mbuf(void *user_buf, void *closure, int error_on_tx_occurred) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +struct ifnet *ifp = sc->ifp; +struct mbuf *mb = (struct mbuf*)user_buf; + + if ( error_on_tx_occurred ) { + if_inc_counter( ifp, IFCOUNTER_OERRORS, 1 ); + } else { + if_inc_counter( ifp, IFCOUNTER_OPACKETS, 1 ); + if_inc_counter( ifp, IFCOUNTER_OBYTES, mb->m_pkthdr.len ); + } + m_freem(mb); +} + +static void * +alloc_rx_mbuf(int *p_size, uintptr_t *p_data) +{ +struct mbuf *m; +unsigned long l,o; + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + + if ( m ) { + + o = mtod(m, unsigned long); + l = MV643XX_ALIGN(o, RX_BUF_ALIGNMENT) - o; + + /* align start of buffer */ + m->m_data += l; + + /* reduced length */ + l = MCLBYTES - l; + + m->m_len = m->m_pkthdr.len = l; + *p_size = m->m_len; + *p_data = mtod(m, uintptr_t); + } + + return (void*) m; +} + + +static void +consume_rx_mbuf(void *user_buf, void *closure, int len) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +struct ifnet *ifp = sc->ifp; +struct mbuf *m = (struct mbuf*)user_buf; + + if ( len <= 0 ) { + if_inc_counter( ifp, IFCOUNTER_IQDROPS, 1 ); + if ( len < 0 ) { + if_inc_counter( ifp, IFCOUNTER_IERRORS, 1 ); + } + m_freem(m); + } else { + m->m_len = m->m_pkthdr.len = len - ETH_RX_OFFSET - ETH_CRC_LEN; + m->m_data += ETH_RX_OFFSET; + m->m_pkthdr.rcvif = ifp; + + if_inc_counter( ifp, IFCOUNTER_IPACKETS, 1 ); + if_inc_counter( ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len ); + + if (0) { + /* Low-level debugging */ + int i; + for (i=0; i<m->m_len; i++) { + if ( !(i&15) ) + printk("\n"); + printk("0x%02x ",mtod(m,char*)[i]); + } + printk("\n"); + } + + mve_unlock( sc, "rx_cleanup" ); + (*ifp->if_input)(ifp, m); + mve_lock( sc, "rx_cleanup" ); + } +} + +/* Translate IFFLAGS to low-level driver representation */ +static int +xlateMediaFlags(const struct mii_data *mid) +{ +int lowLevelFlags = 0; +int msk = IFM_AVALID | IFM_ACTIVE; + + if ( (mid->mii_media_status & msk) == msk ) { + lowLevelFlags |= MV643XX_MEDIA_LINK; + + if ( IFM_OPTIONS( mid->mii_media_active ) & IFM_FDX ) { + lowLevelFlags |= MV643XX_MEDIA_FD; + } + + switch ( IFM_ETHER_SUBTYPE_GET( mid->mii_media_active ) ) { + default: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: UNKNOWN SPEED\n"); +#endif + break; /* UNKNOWN -- FIXME */ + case IFM_10_T: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 10baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_10; + break; + case IFM_100_TX: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 100baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_100; + break; + case IFM_1000_T: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 1000baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_1000; + break; + } + } else { +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: NO LINK\n"); +#endif + } + return lowLevelFlags; +} + +static void +mve_init(void *arg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)arg; +struct ifnet *ifp = sc->ifp; +int lowLevelMediaStatus = 0; +int promisc; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_init (entering)\n"); +#endif + + if ( sc->mii_softc ) { + mii_pollstat( sc->mii_softc ); + lowLevelMediaStatus = xlateMediaFlags( sc->mii_softc ); + if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) { + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + } else { + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + } + } + + promisc = !! (if_getdrvflags(ifp) & IFF_PROMISC); + + BSP_mve_init_hw(sc->mp, promisc, if_getlladdr(ifp), lowLevelMediaStatus); + + /* if promiscuous then there is no need to change */ + if ( ! promisc ) { + mve_set_filters(ifp); + } + + + if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); +} + +static void +mve_start(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); + mve_lock( sc, "mve_start" ); + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + mve_unlock( sc, "mve_start" ); + mve_send_event( sc, TX_EVENT ); +} + +static int +mve_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct ifreq *ifr = (struct ifreq *)data; +int err = 0; +int f, df; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_ioctl (entering)\n"); +#endif + + mve_lock( sc, "mve_ioctl" ); + + switch ( cmd ) { + case SIOCSIFFLAGS: + f = if_getflags( ifp ); + df = if_getdrvflags( ifp ); + if ( (f & IFF_UP) ) { + if ( ! ( df & IFF_DRV_RUNNING ) ) { + mve_init( (void*)sc ); + } else { + if ( (f & IFF_PROMISC) != (sc->oif_flags & IFF_PROMISC) ) { + mve_set_filters(ifp); + } + /* FIXME: other flag changes are ignored/unimplemented */ + } + } else { + if ( df & IFF_DRV_RUNNING ) { + mve_stop(sc); + } + } + sc->oif_flags = f; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if ( sc->mii_softc ) { + err = ifmedia_ioctl( ifp, ifr, &sc->mii_softc->mii_media, cmd ); + } else { + err = EINVAL; + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if ( if_getdrvflags( ifp ) & IFF_DRV_RUNNING ) { + mve_set_filters(ifp); + } + break; + + case SIO_RTEMS_SHOW_STATS: + BSP_mve_dump_stats(sc->mp, stdout); + break; + + default: + err = ether_ioctl(ifp, cmd, data); + break; + } + + mve_unlock( sc, "mve_ioctl" ); + + return err; +} + +/* SIO RTEMS_SHOW_STATS is too cumbersome to use -- for debugging, provide direct hack */ +int +mv643xx_nexus_dump_stats(int unit, FILE *f) +{ + if ( unit < 0 || unit >= MV643XXETH_NUM_DRIVER_SLOTS || ! ifaces[unit] ) + return -EINVAL; + if ( ! f ) + f = stdout; + BSP_mve_dump_stats(ifaces[unit]->mp, f); + return 0; +} + +/* + * Used to update speed settings in the hardware + * when the phy setup changes. + * + * ASSUME: caller holds lock + */ +static void +mve_ack_link_change(struct mve_enet_softc *sc) +{ +struct mii_data *mii = sc->mii_softc; +int lowLevelMediaStatus; + + if ( !mii ) + return; + + lowLevelMediaStatus = xlateMediaFlags( mii ); + + if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) { + BSP_mve_update_serial_port( sc->mp, lowLevelMediaStatus ); + if_setdrvflagbits( sc->ifp, 0, IFF_DRV_OACTIVE ); + mve_start( sc->ifp ); + } else { + if_setdrvflagbits( sc->ifp, IFF_DRV_OACTIVE, 0 ); + } +} + +/* Callback from ifmedia_ioctl() + * + * Caller probably holds the lock already but + * since it is recursive we may as well make sure + * in case there are other possible execution paths. + */ +static int +mve_media_change(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct mii_data *mii = sc->mii_softc; +int err; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_media_change\n"); +#endif + + if ( ! mii ) { + return ENXIO; + } + + err = mii_mediachg( mii ); + + return err; +} + +static void +mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct mii_data *mii = sc->mii_softc; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_media_status\n"); +#endif + + if ( mii ) { + mii_pollstat( mii ); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + } +} + +static int +mve_attach(device_t dev) +{ +struct mve_enet_softc *sc; +struct ifnet *ifp; +uint8_t hwaddr[ETHER_ADDR_LEN]; +struct mveth_private *mp; +int unit = device_get_unit(dev); +int tx_ring_size = MV643XX_TX_RING_SIZE; +int rx_ring_size = MV643XX_RX_RING_SIZE; +int tx_q_size = MV643XX_TX_QUEUE_SIZE; + + sc = device_get_softc( dev ); + sc->dev = dev; + sc->ifp = ifp = if_alloc(IFT_ETHER); + sc->daemonTid = 0; + sc->mii_softc = 0; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_attach (entering)\n"); +#endif + + mtx_init( &sc->mtx, device_get_nameunit( sc->dev ), MTX_NETWORK_LOCK, MTX_RECURSE ); + callout_init_mtx( &sc->wdCallout, &sc->mtx, 0 ); + + if_setsoftc ( ifp, sc ); + if_initname ( ifp, device_get_name(dev), unit); + if_setinitfn ( ifp, mve_init ); + if_setioctlfn ( ifp, mve_ioctl ); + if_setstartfn ( ifp, mve_start ); + if_setflags ( ifp, (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX) ); + sc->oif_flags = if_getflags( ifp ); + + if_setsendqlen ( ifp, tx_q_size ); + if_setsendqready( ifp ); + + mp = BSP_mve_create( + unit + 1, /* low-level driver' unit numbers are 1-based */ + 0, + mve_isr, (void*)sc, + release_tx_mbuf, (void*)sc, + alloc_rx_mbuf, + consume_rx_mbuf, (void*)sc, + rx_ring_size, + tx_ring_size, + ( MV643XX_ETH_IRQ_RX_DONE + | MV643XX_ETH_EXT_IRQ_TX_DONE + | MV643XX_ETH_EXT_IRQ_LINK_CHG)); + + if ( ! mp ) { + rtems_panic("Unable to create mv643xx low-level driver"); + } + + sc->mp = mp; + + BSP_mve_read_eaddr( mp, hwaddr ); + + if ( 0 == mii_attach( sc->dev, + &sc->miibus, + ifp, + mve_media_change, + mve_media_status, + BMSR_DEFCAPMASK, + MVE643XX_DUMMY_PHY, + MII_OFFSET_ANY, + 0 ) ) { + sc->mii_softc = device_get_softc( sc->miibus ); + } + + sc->daemonTid = rtems_bsdnet_newproc("MVE", 4096, mve_daemon, (void*)sc); + + ether_ifattach( ifp, hwaddr ); + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_attach (leaving)\n"); +#endif + + ifaces[unit] = sc; + + return 0; +} + +static int +mve_miibus_read_reg(device_t dev, int phy, int reg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); + + /* low-level driver knows what phy to use; ignore arg */ + return (int) BSP_mve_mii_read( sc->mp, reg ); +} + +static int +mve_miibus_write_reg(device_t dev, int phy, int reg, int val) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); + + /* low-level driver knows what phy to use; ignore arg */ + BSP_mve_mii_write( sc->mp, reg, val ); + return 0; +} + +static void +mve_miibus_statchg(device_t dev) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_miibus_statchg\n"); +#endif + /* assume this ends up being called either from the ioctl or the driver + * task -- either of which holds the lock. + */ + mve_ack_link_change( sc ); +} + +static void +mve_miibus_linkchg(device_t dev) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_miibus_linkchg\n"); +#endif + /* assume this ends up being called either from the ioctl or the driver + * task -- either of which holds the lock. + */ + mve_ack_link_change( sc ); +} + + +static device_method_t mve_methods[] = { + DEVMETHOD(device_probe, mve_probe ), + DEVMETHOD(device_attach, mve_attach), + + DEVMETHOD(miibus_readreg, mve_miibus_read_reg ), + DEVMETHOD(miibus_writereg, mve_miibus_write_reg), + DEVMETHOD(miibus_statchg , mve_miibus_statchg ), + DEVMETHOD(miibus_linkchg , mve_miibus_linkchg ), + + DEVMETHOD_END +}; + +static driver_t mve_nexus_driver = { + "mve", + mve_methods, + sizeof( struct mve_enet_softc ) +}; + +static devclass_t mve_devclass; + +DRIVER_MODULE(mve, nexus, mve_nexus_driver, mve_devclass, 0, 0); +DRIVER_MODULE(miibus, mve, miibus_driver, miibus_devclass, 0, 0); + +MODULE_DEPEND(mve, nexus, 1, 1, 1); +MODULE_DEPEND(mve, ether, 1, 1, 1); + +#endif /* LIBBSP_BEATNIK_BSP_H */ diff --git a/rtemsbsd/sys/dev/stmac/if_stmac.c b/rtemsbsd/sys/dev/stmac/if_stmac.c index 614c51b9..7e3e07c7 100644 --- a/rtemsbsd/sys/dev/stmac/if_stmac.c +++ b/rtemsbsd/sys/dev/stmac/if_stmac.c @@ -40,6 +40,7 @@ #include <sys/module.h> #include <sys/socket.h> #include <sys/sockio.h> +#include <sys/gsb_crc32.h> #include <net/if.h> #include <net/ethernet.h> diff --git a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c index c7c09bb4..d02bba98 100755 --- a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c +++ b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include <dev/usb/controller/ohci.h> #include <dev/usb/controller/ohcireg.h> +#include <arm/lpc/probe.h> #include <arm/lpc/lpcreg.h> #include <arm/lpc/lpcvar.h> @@ -90,7 +91,7 @@ __FBSDID("$FreeBSD$"); while ((lpc_otg_read_4(_sc, _sreg) & _value) != _value); \ } while (0); -static int lpc_ohci_probe(device_t dev); +static int lpc_ohci_do_probe(device_t dev); static int lpc_ohci_attach(device_t dev); static int lpc_ohci_detach(device_t dev); @@ -107,12 +108,20 @@ static int lpc_otg_i2c_wait_for_transaction_done(struct ohci_softc *sc); static int lpc_otg_i2c_read(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t *value); static int lpc_otg_i2c_write(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t value); +__weak_symbol int +lpc_ohci_probe(int unit) +{ + + (void)unit; + return (BUS_PROBE_DEFAULT); +} + static int -lpc_ohci_probe(device_t dev) +lpc_ohci_do_probe(device_t dev) { device_set_desc(dev, "LPC32x0 USB OHCI controller"); - return (BUS_PROBE_DEFAULT); + return (lpc_ohci_probe(device_get_unit(dev))); } static int @@ -473,7 +482,7 @@ lpc_ohci_resume(device_t dev) static device_method_t lpc_ohci_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, lpc_ohci_probe), + DEVMETHOD(device_probe, lpc_ohci_do_probe), DEVMETHOD(device_attach, lpc_ohci_attach), DEVMETHOD(device_detach, lpc_ohci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), diff --git a/rtemsbsd/sys/net/if_ppp.c b/rtemsbsd/sys/net/if_ppp.c index 709f13e0..e134dc76 100644 --- a/rtemsbsd/sys/net/if_ppp.c +++ b/rtemsbsd/sys/net/if_ppp.c @@ -313,11 +313,12 @@ static rtems_task ppp_txdaemon(rtems_task_argument arg) frag=0; /* initialize output values */ - sc->sc_outfcs = PPP_INITFCS; - sc->sc_outbuf = (u_char *)0; - sc->sc_outlen = (short )0; - sc->sc_outoff = (short )0; - sc->sc_outfcslen = (short )0; + sc->sc_outfcs = PPP_INITFCS; + sc->sc_outbuf = (u_char *)0; + sc->sc_outlen = (short )0; + sc->sc_outoff = (short )0; + sc->sc_outoff_update = false; + sc->sc_outfcslen = (short )0; /* printf("Start Transmit Packet..\n"); */ diff --git a/rtemsbsd/sys/net/if_pppvar.h b/rtemsbsd/sys/net/if_pppvar.h index fdfb56df..bd11bcbc 100644 --- a/rtemsbsd/sys/net/if_pppvar.h +++ b/rtemsbsd/sys/net/if_pppvar.h @@ -117,6 +117,7 @@ struct ppp_softc { struct ifqueue sc_freeq; /* free packets */ short sc_outoff; /* output packet byte offset */ + bool sc_outoff_update; /* outoff needs update in pppstart */ short sc_outflag; /* output status flag */ short sc_outlen; /* length of output packet */ short sc_outfcslen; /* length of output fcs data */ diff --git a/rtemsbsd/sys/net/ppp_tty.c b/rtemsbsd/sys/net/ppp_tty.c index 80d4fee1..2e850dc7 100644 --- a/rtemsbsd/sys/net/ppp_tty.c +++ b/rtemsbsd/sys/net/ppp_tty.c @@ -124,7 +124,7 @@ int pppread(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args); int pppwrite(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args); int ppptioctl(struct rtems_termios_tty *tty, rtems_libio_ioctl_args_t *args); int pppinput(int c, struct rtems_termios_tty *tty); -int pppstart(struct rtems_termios_tty *tp); +int pppstart(struct rtems_termios_tty *tp, int len); u_short pppfcs(u_short fcs, u_char *cp, int len); void pppallocmbuf(struct ppp_softc *sc, struct mbuf **mp); @@ -557,7 +557,7 @@ pppasyncctlp( * Called at spltty or higher. */ int -pppstart(struct rtems_termios_tty *tp) +pppstart(struct rtems_termios_tty *tp, int len) { u_char *sendBegin; u_long ioffset = (u_long )0; @@ -567,6 +567,13 @@ pppstart(struct rtems_termios_tty *tp) /* ensure input is valid and we are busy */ if (( sc != NULL ) && ( sc->sc_outflag & SC_TX_BUSY )) { + /* Adapt offsets if necessary */ + if ( sc->sc_outoff_update ) { + sc->sc_stats.ppp_obytes += len; + sc->sc_outoff += len; + sc->sc_outoff_update = false; + } + /* check to see if we need to get the next buffer */ /* Ready with PPP_FLAG Character ? */ @@ -644,8 +651,25 @@ pppstart(struct rtems_termios_tty *tp) /* write out the character(s) and update the stats */ (*tp->handler.write)(ctx, (char *)sendBegin, (ioffset > 0) ? ioffset : 1); - sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1; - sc->sc_outoff += ioffset; + /* + * In case of polled drivers, everything is sent here. So adapt the + * offsets. In case of interrupt or task driven drivers, we don't know + * whether all characters have been sent. We only get feedback via + * rtems_termios_dequeue_characters() function which is the one that is + * calling us. + */ + if (tp->handler.mode == TERMIOS_POLLED) { + sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1; + sc->sc_outoff += ioffset; + sc->sc_outoff_update = false; + } else { + if (ioffset > 0) { + sc->sc_outoff_update = true; + } else { + sc->sc_outoff_update = false; + sc->sc_stats.ppp_obytes += 1; + } + } return (0); } diff --git a/testsuite/dhcpcd01/test_main.c b/testsuite/dhcpcd01/test_main.c index c1d97a3f..b982bfc9 100644 --- a/testsuite/dhcpcd01/test_main.c +++ b/testsuite/dhcpcd01/test_main.c @@ -44,6 +44,7 @@ #include <rtems/dhcpcd.h> #define TEST_NAME "LIBBSD DHCPCD 1" +#define TEST_STATE_USER_INPUT 1 static void dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env) @@ -67,9 +68,7 @@ test_main(void) { rtems_dhcpcd_add_hook(&dhcpcd_hook); - - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/dhcpcd02/test_main.c b/testsuite/dhcpcd02/test_main.c index 221b096f..cbfd0846 100644 --- a/testsuite/dhcpcd02/test_main.c +++ b/testsuite/dhcpcd02/test_main.c @@ -40,12 +40,13 @@ #include <rtems.h> #define TEST_NAME "LIBBSD DHCPCD 2" +#define TEST_STATE_USER_INPUT 1 static void test_main(void) { - rtems_task_delete(RTEMS_SELF); - assert(0); + + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/evdev01/init.c b/testsuite/evdev01/init.c index fe588ff4..5a8b0beb 100644 --- a/testsuite/evdev01/init.c +++ b/testsuite/evdev01/init.c @@ -341,7 +341,7 @@ evdev_scan_task(rtems_task_argument arg) } } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -401,7 +401,7 @@ err: } } ktask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -484,7 +484,7 @@ err: } } mtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -561,7 +561,7 @@ err: } } ttask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/foobarclient/test_main.c b/testsuite/foobarclient/test_main.c index d55b55c6..71b4774d 100644 --- a/testsuite/foobarclient/test_main.c +++ b/testsuite/foobarclient/test_main.c @@ -272,8 +272,7 @@ test_main(void) foobar_register(&question); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/ftpd01/test_main.c b/testsuite/ftpd01/test_main.c index 7ec17b96..cc7e8a00 100644 --- a/testsuite/ftpd01/test_main.c +++ b/testsuite/ftpd01/test_main.c @@ -79,8 +79,7 @@ test_main(void) rv = rtems_initialize_ftpd(); assert(rv == 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/loopback01/test_main.c b/testsuite/loopback01/test_main.c index 1b5c0064..51d5e5b2 100644 --- a/testsuite/loopback01/test_main.c +++ b/testsuite/loopback01/test_main.c @@ -133,7 +133,7 @@ static rtems_task workerTask(rtems_task_argument arg) if (close(s) < 0) printf("Can't close worker task socket: %s\n", strerror(errno)); printf("Worker task terminating.\n"); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } /* @@ -230,7 +230,7 @@ static rtems_task clientTask(rtems_task_argument arg) clientWorker(arg); sendClientEventToMasterTask(arg); printf("Client task terminating.\n"); - rtems_task_delete( RTEMS_SELF ); + rtems_task_exit(); } /* diff --git a/testsuite/media01/test_main.c b/testsuite/media01/test_main.c index 2a1c9aae..001c80a8 100644 --- a/testsuite/media01/test_main.c +++ b/testsuite/media01/test_main.c @@ -201,6 +201,8 @@ early_initialization(void) #define CONFIGURE_FILESYSTEM_DOSFS +#define CONFIGURE_FILESYSTEM_TFTPFS + #define CONFIGURE_MAXIMUM_PROCESSORS 32 #define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 4096 diff --git a/testsuite/nfs01/test_main.c b/testsuite/nfs01/test_main.c index 2312040a..21d744d1 100644 --- a/testsuite/nfs01/test_main.c +++ b/testsuite/nfs01/test_main.c @@ -63,8 +63,7 @@ test_main(void) NULL); } while (rv != 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_SHELL diff --git a/testsuite/openssl02/test_main.c b/testsuite/openssl02/test_main.c index 32e9c03a..11229e58 100644 --- a/testsuite/openssl02/test_main.c +++ b/testsuite/openssl02/test_main.c @@ -46,6 +46,8 @@ #include <sysexits.h> #include <unistd.h> +#include <rtems/bsd/modules.h> + #include <machine/rtems-bsd-commands.h> #define TEST_NAME "LIBBSD OPENSSL 2" diff --git a/testsuite/pf02/test_main.c b/testsuite/pf02/test_main.c index 5941ea5a..dfb9847b 100644 --- a/testsuite/pf02/test_main.c +++ b/testsuite/pf02/test_main.c @@ -167,6 +167,7 @@ test_main(void) { rtems_status_code sc; int rv; + rtems_shell_env_t env; prepare_files(); @@ -176,9 +177,7 @@ test_main(void) rv = rtems_initialize_ftpd(); assert(rv == 0); - rtems_shell_env_t env; - - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); rtems_shell_main_loop( &env ); exit(0); diff --git a/testsuite/ppp01/test_main.c b/testsuite/ppp01/test_main.c index d4baf5db..b6e9d4d7 100644 --- a/testsuite/ppp01/test_main.c +++ b/testsuite/ppp01/test_main.c @@ -272,8 +272,7 @@ test_main(void) rv = rtems_pppd_connect(); assert(rv == 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } RTEMS_BSD_DEFINE_NEXUS_DEVICE(ppp, 0, 0, NULL); diff --git a/testsuite/tcpdump01/test_main.c b/testsuite/tcpdump01/test_main.c new file mode 100644 index 00000000..0f31cdde --- /dev/null +++ b/testsuite/tcpdump01/test_main.c @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @brief Tests the tcpdump command. + */ + +/* + * Copyright (C) 2022 embedded brains GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <vm/uma.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> + +#include <assert.h> +#include <ck_epoch.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/bsd/bsd.h> +#include <rtems/console.h> +#include <rtems/libcsupport.h> + +#include <machine/rtems-bsd-commands.h> + +#define TEST_NAME "LIBBSD TCPDUMP 1" + +typedef struct { + rtems_id main_id; + int sp[2]; +} test_context; + +static test_context test_instance; + +static void +epoch_cleanup(void) +{ + rtems_status_code sc; + + sc = rtems_task_wake_after(CK_EPOCH_LENGTH); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void +init_addr(struct sockaddr_in *addr) +{ + int ok; + + memset(addr, 0, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + ok = inet_aton("127.0.0.1", &addr->sin_addr); + assert(ok != 0); +} + +static rtems_id +start_task(rtems_task_entry entry, void *arg) +{ + rtems_task_priority prio; + rtems_id id; + rtems_status_code sc; + + sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_create(rtems_build_name('T', 'E', 'S', 'T' ), prio - 1, + 32 * 1024, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start(id, entry, (rtems_task_argument)arg); + assert(sc == RTEMS_SUCCESSFUL); + + return id; +} + +static void +test_tcpdump_help(void) +{ + char *argv[] = { + "tcpdump", + "-h", + NULL + }; + rtems_resource_snapshot snapshot; + int exit_code; + + rtems_resource_snapshot_take(&snapshot); + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + assert(rtems_resource_snapshot_check(&snapshot)); +} + +static const char raw_packets_file[] = "packets.bin"; + +static void +run_tcpdump_write_raw_packets(rtems_task_argument arg) +{ + test_context *ctx; + FILE *file; + FILE *saved_stdin; + FILE *saved_stdout; + FILE *saved_stderr; + int rv; + char *argv[] = { + "tcpdump", + "-n", + "-i", + "lo0", + "-w", + RTEMS_DECONST(char *, raw_packets_file), + NULL + }; + int exit_code; + rtems_status_code sc; + + ctx = (test_context *)arg; + + saved_stdin = stdin; + saved_stdout = stdout; + saved_stderr = stderr; + + file = fdopen(ctx->sp[1], "r+"); + assert(file != NULL); + + stdin = file; + stdout = file; + stderr = file; + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + stdin = saved_stdin; + stdout = saved_stdout; + stderr = saved_stderr; + + rv = fclose(file); + assert(rv == 0); + + sc = rtems_event_transient_send(ctx->main_id); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_task_exit(); +} + +static void +test_tcpdump_write_raw_packets(test_context *ctx) +{ + char *argv[] = { + "tcpdump", + "-n", + "-r", + RTEMS_DECONST(char *, raw_packets_file), + NULL + }; + rtems_resource_snapshot snapshot; + int in; + int out; + int rv; + char c; + ssize_t n; + struct sockaddr_in addr; + socklen_t addr_len; + rtems_status_code sc; + int exit_code; + + rtems_resource_snapshot_take(&snapshot); + + rv = socketpair(PF_UNIX, SOCK_STREAM, 0, ctx->sp); + assert(rv == 0); + + start_task(run_tcpdump_write_raw_packets, ctx); + + init_addr(&addr); + + in = socket(PF_INET, SOCK_DGRAM, 0); + assert(out >= 0); + + rv = bind(in, (const struct sockaddr *) &addr, sizeof(addr)); + assert(rv == 0); + + out = socket(PF_INET, SOCK_DGRAM, 0); + assert(out >= 0); + + c = 'x'; + n = sendto(out, &c, sizeof(c), 0, + (const struct sockaddr *) &addr, sizeof(addr)); + assert(n == 1); + + c = 'y'; + addr_len = sizeof(addr); + n = recvfrom(in, &c, sizeof(c), 0, + (struct sockaddr *) &addr, &addr_len); + assert(n == 1); + assert(c == 'x'); + + /* The tcpdump pcap read timeout is 1000ms */ + sc = rtems_task_wake_after(rtems_clock_get_ticks_per_second()); + assert(sc == RTEMS_SUCCESSFUL); + + c = 'q'; + n = write(ctx->sp[0], &c, sizeof(c)); + assert(n == 1); + + rv = close(out); + assert(rv == 0); + + rv = close(in); + assert(rv == 0); + + sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + assert(sc == RTEMS_SUCCESSFUL); + + rv = close(ctx->sp[0]); + assert(rv == 0); + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + rv = unlink(raw_packets_file); + assert(rv == 0); + + epoch_cleanup(); + assert(rtems_resource_snapshot_check(&snapshot)); +} + +static void +test_main(void) +{ + test_context *ctx; + FILE *file; + int rv; + + ctx = &test_instance; + ctx->main_id = rtems_task_self(); + + /* Fill dynamic file pool in Newlib */ + file = fopen(CONSOLE_DEVICE_NAME, "r+"); + assert(file != NULL); + rv = fclose(file); + assert(rv == 0); + + /* + * Stop interferences of uma_timeout() which may need some dynamic + * memory. + */ + rtems_uma_drain_timeout(); + + rtems_bsd_ifconfig_lo0(); + epoch_cleanup(); + + test_tcpdump_help(); + test_tcpdump_write_raw_packets(ctx); + + exit(0); +} + +#include <rtems/bsd/test/default-init.h> diff --git a/testsuite/telnetd01/test_main.c b/testsuite/telnetd01/test_main.c index 73d19c4e..d7e50a97 100644 --- a/testsuite/telnetd01/test_main.c +++ b/testsuite/telnetd01/test_main.c @@ -75,8 +75,7 @@ test_main(void) rtems_status_code sc = rtems_telnetd_initialize(); assert(sc == RTEMS_SUCCESSFUL); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_SHELL diff --git a/testsuite/thread01/test_main.c b/testsuite/thread01/test_main.c index 9d5e5bba..48b21452 100644 --- a/testsuite/thread01/test_main.c +++ b/testsuite/thread01/test_main.c @@ -109,13 +109,10 @@ wait_for_worker_thread(void) static void non_bsd_thread(rtems_task_argument arg) { - rtems_status_code sc; test_curthread(""); wake_up_main_thread(); - - sc = rtems_task_delete(RTEMS_SELF); - assert(sc == RTEMS_SUCCESSFUL); + rtems_task_exit(); } static void diff --git a/testsuite/usbkbd01/init.c b/testsuite/usbkbd01/init.c index 0ea2d2b7..9800b871 100644 --- a/testsuite/usbkbd01/init.c +++ b/testsuite/usbkbd01/init.c @@ -101,7 +101,7 @@ usb_keyboard_read_task(rtems_task_argument arg) rtems_message_queue_send(omid, &msg, sizeof(msg)); } rtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -141,7 +141,7 @@ usb_keyboard_open_task(rtems_task_argument arg) printf("keyboard device closed\n"); } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/usbmouse01/init.c b/testsuite/usbmouse01/init.c index 5bf732d8..56518d67 100644 --- a/testsuite/usbmouse01/init.c +++ b/testsuite/usbmouse01/init.c @@ -103,7 +103,7 @@ usb_mouse_read_task(rtems_task_argument arg) } rtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -143,7 +143,7 @@ usb_mouse_open_task(rtems_task_argument arg) printf("mouse device closed\n"); } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/zerocopy01/test_main.c b/testsuite/zerocopy01/test_main.c index 1be546e4..3a03c12c 100644 --- a/testsuite/zerocopy01/test_main.c +++ b/testsuite/zerocopy01/test_main.c @@ -240,8 +240,7 @@ test_main(void) sc = rtems_task_start(id, network_flood_task, (rtems_task_argument) bc); assert(sc == RTEMS_SUCCESSFUL); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE @@ -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 0bd4fd3d..f1088584 100644 --- a/waf_libbsd.py +++ b/waf_libbsd.py @@ -180,16 +180,17 @@ class Builder(builder.ModuleManager): mandatory=False) elif configTest == 'library': for l in self.data['configure'][configTest][cfg]: - conf.check_cc(lib=l, - fragment=rtems.test_application(), - execute=False, - mandatory=False) + if conf.check_cc(lib=l, + fragment=rtems.test_application(), + execute=False, + mandatory=False): + conf.env['HAVE_%s' % l.upper()] = True else: bld.fatal('invalid config test: %s' % (configTest)) - section_flags = ["-fdata-sections", "-ffunction-sections"] - _add_flags_if_not_present(conf.env.CFLAGS, section_flags) - _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags) - _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"]) + section_flags = ["-fdata-sections", "-ffunction-sections"] + _add_flags_if_not_present(conf.env.CFLAGS, section_flags) + _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags) + _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"]) def build(self, bld): # @@ -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;" \ |