diff options
159 files changed, 25207 insertions, 7408 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f2e5da21..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,327 +0,0 @@ -Guidelines for Developing and Contributing Code -=============================================== - -Introduction ------------- - -This guide aims to help developing and contributing code to the libbsd. One -goal of the libbsd is to stay in synchronization with FreeBSD. This is only -feasible if certain rules are in place. Otherwise, managing more than a -thousand imported source files will become too labour intensive eventually. - -What is in the Git Repository ------------------------------ - -The libbsd a self-contained kit with FreeBSD and RTEMS components pre-merged. -The Waf wscript in libbsd is automatically generated. - -Any changes to source in the `freebsd` directories will need to be merged -upstream into our master FreeBSD checkout, the `freebsd-org` submodule. - -The repository contains two FreeBSD source trees. In the `freebsd` directory -are the so called *managed* FreeBSD sources used to build the BSD library. The -FreeBSD source in `freebsd-org` is the *master* version. The -`freebsd-to-rtems.py` script is used to transfer files between the two trees. -In general terms, if you have modified managed FreeBSD sources, you will need -to run the script in *revert* or *reverse* mode using the `-R` switch. This -will copy the source back to your local copy of the master FreeBSD source so -you can run `git diff` against the upstream FreeBSD source. If you want to -transfer source files from the master FreeBSD source to the manged FreeBSD -sources, then you must run the script in *forward* mode (the default). - -Organization ------------- - -The top level directory contains a few directories and files. The following -are important to understand - -* `freebsd-to-rtems.py` - script to convert to and free FreeBSD and RTEMS trees, -* `create-kernel-namespace.sh` - script to create the kernel namespace header `<machine/rtems-bsd-kernel-namespace.h>`, -* `wscript` - automatically generated, -* `freebsd/` - from FreeBSD by script, -* `rtemsbsd/` - RTEMS specific implementations of FreeBSD kernel support routines, -* `testsuite/` - RTEMS specific tests, and -* `libbsd.txt` - documentation in Asciidoc. - -Moving Code Between Managed and Master FreeBSD Source ------------------------------------------------------ - -The script `freebsd-to-rtems.py` is used to copy code from FreeBSD to the -rtems-libbsd tree and to reverse this process. This script attempts to -automate this process as much as possible and performs some transformations -on the FreeBSD code. Its command line arguments are shown below: - -``` -freebsd-to-rtems.py [args] - -?|-h|--help print this and exit - -d|--dry-run run program but no modifications - -D|--diff provide diff of files between trees - -e|--early-exit evaluate arguments, print results, and exit - -m|--makefile Warning: depreciated and will be removed - -b|--buildscripts just generate the build scripts - -S|--stats Print a statistics report - -R|--reverse default FreeBSD -> RTEMS, reverse that - -r|--rtems RTEMS Libbsd directory (default: '.') - -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org') - -v|--verbose enable verbose output mode -``` - -In its default mode of operation, freebsd-to-rtems.py is used to copy code -from FreeBSD to the rtems-libbsd tree and perform transformations. - -In *reverse mode*, this script undoes those transformations and copies -the source code back to the *master* FreeBSD tree. This allows us to do -'git diff', evaluate changes made by the RTEMS Project, and report changes -back to FreeBSD upstream. - -In either mode, the script may be asked to perform a dry-run or be verbose. -Also, in either mode, the script is also smart enough to avoid copying over -files which have not changed. This means that the timestamps of files are -not changed unless the contents change. The script will also report the -number of files which changed. In verbose mode, the script will print -the name of the files which are changed. - -To add or update files in the RTEMS FreeBSD tree first run the *reverse mode* -and move the current set of patches FreeBSD. The script may warn you if a file -is not present at the destination for the direction. This can happen as files -not avaliable at the FreeBSD snapshot point have been specially added to the -RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of -files in libbsd.py. The reverse mode will result in the FreeBSD having -uncommitted changes. You can ignore these. Once the reverse process has -finished edit libbsd.py and add any new files then run the forwad mode to bring -those files into the RTEMS FreeBSD tree. - -The following is an example forward run with no changes. - -``` -$ ./freebsd-to-rtems.py -v -Verbose: yes (1) -Dry Run: no -Diff Mode Enabled: no -Only Generate Build Scripts: no -RTEMS Libbsd Directory: . -FreeBSD SVN Directory: freebsd-org -Direction: forward -Forward from FreeBSD GIT into . -0 file(s) were changed: -``` - -The script may also be used to generate a diff in either forward or reverse -direction. - -You can add more than one verbose option (-v) to the command line and get more -detail and debug level information from the command. - -FreeBSD Baseline ----------------- - -Use -``` -$ git log freebsd-org -``` -to figure out the current FreeBSD baseline. - -How to Import Code from FreeBSD -------------------------------- - -* In case you import files from a special FreeBSD version, then update the list above. -* Run `git status` and make sure your working directory is clean. -* Run `./freebsd-to-rtems.py -R` -* Run `./freebsd-to-rtems.py` -* Run `git status` and make sure your working directory is clean. If you see modified files, then the `freebsd-to-rtems.py` script needs to be fixed first. -* Add the files to import to `libbsd.py` and your intended build set (for example `buildset/default.ini`. -* Run `./freebsd-to-rtems.py` -* Immediately check in the imported files without the changes to `libbsd.py` and the buildsets. Do not touch the imported files yourself at this point. -* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'. -* Add a test to the testsuite if possible. -* Run `./create-kernel-namespace.sh` if you imported kernel space headers. Add only your new defines via `git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h`. -* Create one commit from this. - -The -S or --stats option generates reports the changes we have made to -FreeBSD. If the code has been reserved into the original FreeBSD tree it will -show nothing has changed. To see what we have change: - -``` -$ cd freebsd-org -$ git checkout -- . -$ cd .. -$ ./freebsd-to-rtems.py -R -S -d - ``` - -The report lists the files change based on the opacity level. The opacity is a -measure on how much of a file differs from the original FreeBSD source. The -lower the value the more transparent the source file it. - -Porting of User-Space Utilities ------------------------------- - -The theory behind the described method is to put all BSS and initialized data -objects into a named section. This section then will be saved before the code is -executed and restored after it has finished. This method limits to a single -threaded execution of the application but minimizes the necessary changes to the -original FreeBSD code. - -* Import and commit the unchanged source files like described above. -* Add the files to the [libbsd.py](libbsd.py) and build them. -* Check the sources for everything that can be made const. This type of patches - should go back to the upstream FreeBSD sources. -* Move static variables out of functions if necessary (search for - "\tstatic"). These patches most likely will not be accepted into FreeBSD. -* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file - containing the main function (e.g. PROGNAME = pfctl). For an example look at - `rtems_bsd_command_pfctl()` in [pfctl.c](freebsd/sbin/pfctl/pfctl.c). -* You probably have to use getopt_r() instead of getopt(). Have a look at - [pfctl.c](freebsd/sbin/pfctl/pfctl.c). -* Build the libbsd without optimization. -* Use the `userspace-header-gen.py` to generate some necessary header - files. It will generate one `rtems-bsd-PROGNAME-MODULE-data.h` per object file, one - `rtems-bsd-PROGNAME-namespace.h` and one `rtems-bsd-PROGNAME-data.h`. To call - the script, you have to compile the objects and afterwards run the helper - script with a call similar to this one: - `python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl` - Replace the name (given via -p option) by the name of the userspace tool. It - has to match the name that is used in the RTEMS linker set further below. - `Note:` the script `userspace-header-gen.py` depends on pyelftools. It can be - installed using pip: - `pip install --user pyelftools` -* If you regenerated files that have already been generated, you may have to - remove RTEMS-specific names from the namespace. The defaults (linker set names - and rtems_bsd_program_.*) should already be filtered. -* Put the generated header files into the same folder like the source files. -* At the top of each source file place the following right after the user-space header: - ```c - #ifdef __rtems__ - #include <machine/rtems-bsd-program.h> - #include "rtems-bsd-PROGNAME-namespace.h" - #endif /* __rtems__ */ - ``` - The following command may be useful: - ``` - sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c - ``` -* At the bottom of each source file place the follwing: - ```c - #ifdef __rtems__ - #include "rtems-bsd-PROGNAME-FILE-data.h" - #endif /* __rtems__ */ - ``` - The following command may be useful: - ``` - for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done - ``` -* Create one compilable commit. - -Rules for Modifying FreeBSD Source ----------------------------------- - -Changes in FreeBSD files must be done using `__rtems__` C pre-processor guards. -This makes synchronization with the FreeBSD upstream easier and is very -important. Patches which do not follow these rules will be rejected. Only add -lines. If your patch contains lines starting with a `-`, then this is wrong. -Subtract code by added `#ifndef __rtems__`. For example: - -```c -/* Global variables for the kernel. */ - -#ifndef __rtems__ -/* 1.1 */ -extern char kernelname[MAXPATHLEN]; -#endif /* __rtems__ */ - -extern int tick; /* usec per tick (1000000 / hz) */ -``` - -```c -#if defined(_KERNEL) || defined(_WANT_FILE) -#ifdef __rtems__ -#include <rtems/libio_.h> -#include <sys/fcntl.h> -#endif /* __rtems__ */ -/* - * Kernel descriptor table. - * One entry for each open kernel vnode and socket. - * - * Below is the list of locks that protects members in struct file. - * - * (f) protected with mtx_lock(mtx_pool_find(fp)) - * (d) cdevpriv_mtx - * none not locked - */ -``` - -```c -extern int profprocs; /* number of process's profiling */ -#ifndef __rtems__ -extern volatile int ticks; -#else /* __rtems__ */ -#include <rtems/score/watchdogimpl.h> -#define ticks _Watchdog_Ticks_since_boot -#endif /* __rtems__ */ - -#endif /* _KERNEL */ -``` - -Add nothing (even blank lines) before or after the `__rtems__` guards. Always -include a `__rtems__` in the guards to make searches easy, so use - -* `#ifndef __rtems__`, -* `#ifdef __rtems__`, -* `#else /* __rtems__ */`, and -* `#endif /* __rtems__ */`. - -The guards must start at the begin of the line. Examples for wrong guards: - -```c -static void -guards_must_start_at_the_begin_of_the_line(int j) -{ - - /* WRONG */ - #ifdef __rtems__ - return (j + 1); - #else /* __rtems__ */ - return (j + 2); - #endif /* __rtems__ */ -} - -static void -missing_rtems_comments_in_the_guards(int j) -{ - -#ifdef __rtems__ - return (j + 3); -/* WRONG */ -#else - return (j + 4); -#endif -} -``` - -The FreeBSD build and configuration system uses option header files, e.g. -`#include "opt_xyz.h"` in an unmodified FreeBSD file. This include is -transformed by the import script into `#include <rtems/bsd/local/opt_xyz.h>`. Do -not disable option header includes via guards. Instead, add an empty option -header, e.g. `touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h`. -```c -/* WRONG */ -#ifndef __rtems__ -#include <rtems/bsd/local/opt_xyz.h> -#endif /* __rtems__ */ -``` - -In general, provide empty header files and do not guard includes. - -For new code use -[STYLE(9)](http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9). - -Do not format original FreeBSD code. Do not perform white space changes even -if you get git commit warnings. - -Automatically Generated FreeBSD Files -------------------------------------- - -Some source and header files are automatically generated during the FreeBSD -build process. The `Makefile.todo` file performs this manually. The should be -included in `freebsd-to-rtems.py` script some time in the future. For details, -see also -[KOBJ(9)](http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0). diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..0b6fc7a0 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,736 @@ +Guidelines for Developing and Contributing Code +*********************************************** + +Introduction +============ + +This guide aims to help developing and contributing code to the LibBSD. One +goal of the LibBSD is to stay in synchronization with FreeBSD. This is only +feasible if certain rules are in place. Otherwise, managing more than a +thousand imported source files will become too labour intensive eventually. + +The LibBSD makes FreeBSD subsystems like TCP/IP, USB, SD/MMC, PCIe, and some +more usable for RTEMS. It tries to follow the FreeBSD development as close as +possible and therefore is updated to the latest FreeBSD HEAD revision of the +associated FreeBSD branch from time to time. To find out which version of +FreeBSD is currently used as the base version for LibBSD please take a look at +the ``freebsd-org`` submodule. + +This guide captures information on the process of merging code from FreeBSD, +RTEMS specific support files, general guidelines on what modifications to the +FreeBSD source are permitted, and some other topics. For building the library, +see the `README <README.rst>`_. + +Goals of the LibBSD activity are + +* provide functionality from FreeBSD to RTEMS, +* ease updating to future FreeBSD versions, +* ease tracking changes in FreeBSD code, +* minimize manual changes in FreeBSD code. + +We will work to push our changes upstream to the FreeBSD Project and minimize +changes required at each update point. + +What is in the Git Repository +============================= + +The LibBSD a self-contained kit with FreeBSD and RTEMS components +pre-merged. The Waf wscript in LibBSD automatically generates the build when +you run waf by reading the modules and module's source, header, defines and +special flags from ``libbsd.py``. This is the same module data used to manage +the FreeBSD source. + +Any changes to source in the ``freebsd`` directories will need to be merged +upstream into our master FreeBSD checkout, the ``freebsd-org`` submodule. + +The repository contains two FreeBSD source trees. In the ``freebsd`` directory +are the so called *managed* FreeBSD sources used to build the BSD library. +The FreeBSD source in ``freebsd-org`` is the *master* version. The +``freebsd-to-rtems.py`` script is used to transfer files between the two trees +using the module defnitions in ``libbsd.py``. In general terms, if you have +modified managed FreeBSD sources, you will need to run the script in *revert* +or *reverse* mode using the ``-R`` switch. This will copy the source back to +your local copy of the master FreeBSD source so you can run ``git diff`` against +the upstream FreeBSD source. If you want to transfer source files from the +master FreeBSD source to the manged FreeBSD sources, then you must run the +script in *forward* mode (the default). + +Kernel and User Space +===================== + +FreeBSD uses virtual memory to run separate address spaces. The kernel is one +address space and each process the kernel runs is another separate address +space. The FreeBSD build system understands the separation and separately +linked executable for the kernel and user land maintains the separation. + +RTEMS is a single address space operating system and that means the kernel and +user space code have to be linked to together and be able to run side by +side. This creates additional complexity when working with the FreeBSD code, +for example the FreeBSD kernel has a ``malloc`` call with a different signature +to the user land ``malloc`` call. The RTEMS LibBSD support code provides +structured ways to manage the separation. + +LibBSD manages the integration of kernel and user code by knowing the context +of the source code. This lets the merge process handle specific changes each +type of file needs. The build system also uses this information to control the +include paths a source file sees. The kernel code sees the kernel, CPU +specific and build system generated include paths in that order. User code +sees the user include paths then the kernel, CPU specific and build system +generated include paths in that order. The FreeBSD OS include path +``/usr/include`` has a mix of kernel and user space header files. The kernel +headers let user space code cleanly access structures the kernel exports. If a +user header file has the same name as a kernel header file the user file will +be used in the user code rather than the kernel file. If the user code +includes a kernel header that file will be found and included. + +Organization +============ + +The top level directory contains a few directories and files. The following +are important to understand + +* ``freebsd-to-rtems.py`` - script to convert to and free FreeBSD and RTEMS trees, +* ``create-kernel-namespace.sh`` - script to create the kernel namespace header ``<machine/rtems-bsd-kernel-namespace.h>``, +* ``wscript`` - automatically generates the build from libbsd.py, +* ``libbsd.py`` - modules, sources, compile flags, and dependencies +* ``freebsd/`` - from FreeBSD by script, +* ``rtemsbsd/`` - RTEMS specific implementations of FreeBSD kernel support routines, +* ``testsuite/`` - RTEMS specific tests, and +* ``libbsd.txt`` - documentation in Asciidoc. + +Moving Code Between Managed and Master FreeBSD Source +===================================================== + +The script ``freebsd-to-rtems.py`` is used to copy code from FreeBSD to the +rtems-libbsd tree and to reverse this process. This script attempts to +automate this process as much as possible and performs some transformations +on the FreeBSD code. Its command line arguments are shown below: + +.. code-block:: none + + freebsd-to-rtems.py [args] + -?|-h|--help print this and exit + -d|--dry-run run program but no modifications + -D|--diff provide diff of files between trees + -e|--early-exit evaluate arguments, print results, and exit + -m|--makefile Warning: depreciated and will be removed + -b|--buildscripts just generate the build scripts + -S|--stats Print a statistics report + -R|--reverse default FreeBSD -> RTEMS, reverse that + -r|--rtems RTEMS Libbsd directory (default: '.') + -f|--freebsd FreeBSD SVN directory (default: 'freebsd-org') + -c|--config Output the configuration then exit + -v|--verbose enable verbose output mode + +In its default mode of operation, ``freebsd-to-rtems.py`` is used to copy code +from FreeBSD to the rtems-libbsd tree and perform transformations. + +In *reverse mode*, this script undoes those transformations and copies +the source code back to the *master* FreeBSD tree. This allows us to do +'git diff', evaluate changes made by the RTEMS Project, and report changes +back to FreeBSD upstream. + +In either mode, the script may be asked to perform a dry-run or be verbose. +Also, in either mode, the script is also smart enough to avoid copying over +files which have not changed. This means that the timestamps of files are +not changed unless the contents change. The script will also report the +number of files which changed. In verbose mode, the script will print +the name of the files which are changed. + +To add or update files in the RTEMS FreeBSD tree first run the *reverse mode* +and move the current set of patches FreeBSD. The script may warn you if a file +is not present at the destination for the direction. This can happen as files +not avaliable at the FreeBSD snapshot point have been specially added to the +RTEMS FreeBSD tree. Warnings can also appear if you have changed the list of +files in libbsd.py. The reverse mode will result in the FreeBSD having +uncommitted changes. You can ignore these. Once the reverse process has +finished edit libbsd.py and add any new files then run the forwad mode to bring +those files into the RTEMS FreeBSD tree. + +The following is an example forward run with no changes. + +.. code-block:: none + + $ ./freebsd-to-rtems.py -v + Verbose: yes (1) + Dry Run: no + Diff Mode Enabled: no + Only Generate Build Scripts: no + RTEMS Libbsd Directory: . + FreeBSD SVN Directory: freebsd-org + Direction: forward + Forward from FreeBSD GIT into . + 0 file(s) were changed: + +The script may also be used to generate a diff in either forward or reverse +direction. + +You can add more than one verbose option (-v) to the command line and get more +detail and debug level information from the command. + +FreeBSD Baseline +================ + +Use + +.. code-block:: none + + $ git log freebsd-org + +to figure out the current FreeBSD baseline. + +How to Import Code from FreeBSD +=============================== + +* In case you import files from a special FreeBSD version, then update the list above. +* Run ``git status`` and make sure your working directory is clean. +* Run ``./freebsd-to-rtems.py -R`` +* Run ``./freebsd-to-rtems.py`` +* Run ``git status`` and make sure your working directory is clean. If you see modified files, then the ``freebsd-to-rtems.py`` script needs to be fixed first. +* Add the files to import to ``libbsd.py`` and your intended build set (for example ``buildset/default.ini``. +* Run ``./freebsd-to-rtems.py`` +* Immediately check in the imported files without the changes to ``libbsd.py`` and the buildsets. Do not touch the imported files yourself at this point. +* Port the imported files to RTEMS. See 'Rules for Modifying FreeBSD Source'. +* Add a test to the testsuite if possible. +* Run ``./create-kernel-namespace.sh`` if you imported kernel space headers. Add only your new defines via ``git add -p rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h``. +* Create one commit from this. + +The -S or --stats option generates reports the changes we have made to +FreeBSD. If the code has been reserved into the original FreeBSD tree it will +show nothing has changed. To see what we have change: + +.. code-block:: none + + $ cd freebsd-org + $ git checkout -- . + $ cd .. + $ ./freebsd-to-rtems.py -R -S -d + +The report lists the files change based on the opacity level. The opacity is a +measure on how much of a file differs from the original FreeBSD source. The +lower the value the more transparent the source file it. + +Porting of User-Space Utilities +=============================== + +The theory behind the described method is to put all BSS and initialized data +objects into a named section. This section then will be saved before the code is +executed and restored after it has finished. This method limits to a single +threaded execution of the application but minimizes the necessary changes to the +original FreeBSD code. + +* Import and commit the unchanged source files like described above. +* Add the files to the `<libbsd.py>`_ and build them. +* Check the sources for everything that can be made const. This type of patches + should go back to the upstream FreeBSD sources. +* Move static variables out of functions if necessary (search for + "\tstatic"). These patches most likely will not be accepted into FreeBSD. +* Add a rtems_bsd_command_PROGNAME() wrapper function to the source file + containing the main function (e.g. PROGNAME = pfctl). For an example look at + ``rtems_bsd_command_pfctl()`` in `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_. +* You probably have to use getopt_r() instead of getopt(). Have a look at + `pfctl.c <freebsd/sbin/pfctl/pfctl.c>`_. +* Build the LibBSD without optimization. +* Use the ``userspace-header-gen.py`` to generate some necessary header + files. It will generate one ``rtems-bsd-PROGNAME-MODULE-data.h`` per object file, one + ``rtems-bsd-PROGNAME-namespace.h`` and one ``rtems-bsd-PROGNAME-data.h``. To call + the script, you have to compile the objects and afterwards run the helper + script with a call similar to this one: + ``python ./userspace-header-gen.py build/arm-rtems4.12-xilinx_zynq_a9_qemu/freebsd/sbin/pfctl/*.o -p pfctl`` + Replace the name (given via -p option) by the name of the userspace tool. It + has to match the name that is used in the RTEMS linker set further below. + ``Note:`` the script ``userspace-header-gen.py`` depends on pyelftools. It can be + installed using pip: + ``pip install --user pyelftools`` +* If you regenerated files that have already been generated, you may have to + remove RTEMS-specific names from the namespace. The defaults (linker set names + and rtems_bsd_program_.*) should already be filtered. +* Put the generated header files into the same folder like the source files. +* At the top of each source file place the following right after the user-space header: + + .. code-block:: c + + #ifdef __rtems__ + #include <machine/rtems-bsd-program.h> + #include "rtems-bsd-PROGNAME-namespace.h" + #endif /* __rtems__ */ + + The following command may be useful: + + .. code-block:: none + + sed -i 's%#include <machine/rtems-bsd-user-space.h>%#include <machine/rtems-bsd-user-space.h>\n\n#ifdef __rtems__\n#include <machine/rtems-bsd-program.h>\n#include "rtems-bsd-PROGNAME-namespace.h"\n#endif /* __rtems__ */%' *.c + +* At the bottom of each source file place the follwing: + + .. code-block:: c + + #ifdef __rtems__ + #include "rtems-bsd-PROGNAME-FILE-data.h" + #endif /* __rtems__ */ + + The following command may be useful: + + .. code-block:: none + + for i in *.c ; do n=$(basename $i .c) ; echo -e "#ifdef __rtems__\n#include \"rtems-bsd-PROGNAME-$n-data.h\"\n#endif /* __rtems__ */" >> $i ; done +* Create one compilable commit. + +Rules for Modifying FreeBSD Source +================================== + +Do not reformat original FreeBSD code. Do not perform white space changes even +if you get git commit warnings. **Check your editor settings so that it does +not perform white space changes automatically**, for example adding a newline +to the end of the file. White space changes may result in conflicts during +updates, especially changes at the end of a file. + +Changes in FreeBSD files must be done using ``__rtems__`` C pre-processor guards. +This makes synchronization with the FreeBSD upstream easier and is very +important. Patches which do not follow these rules will be rejected. Only add +lines. If your patch contains lines starting with a ``-``, then this is wrong. +Subtract code by added ``#ifndef __rtems__``. For example: + +.. code-block:: c + + /* Global variables for the kernel. */ + + #ifndef __rtems__ + /* 1.1 */ + extern char kernelname[MAXPATHLEN]; + #endif /* __rtems__ */ + + extern int tick; /* usec per tick (1000000 / hz) */ + +.. code-block:: c + + #if defined(_KERNEL) || defined(_WANT_FILE) + #ifdef __rtems__ + #include <rtems/libio_.h> + #include <sys/fcntl.h> + #endif /* __rtems__ */ + /* + * Kernel descriptor table. + * One entry for each open kernel vnode and socket. + * + * Below is the list of locks that protects members in struct file. + * + * (f) protected with mtx_lock(mtx_pool_find(fp)) + * (d) cdevpriv_mtx + * none not locked + */ + +.. code-block:: c + + extern int profprocs; /* number of process's profiling */ + #ifndef __rtems__ + extern volatile int ticks; + #else /* __rtems__ */ + #include <rtems/score/watchdogimpl.h> + #define ticks _Watchdog_Ticks_since_boot + #endif /* __rtems__ */ + + #endif /* _KERNEL */ + +Add nothing (even blank lines) before or after the ``__rtems__`` guards. Always +include a ``__rtems__`` in the guards to make searches easy, so use + +* ``#ifndef __rtems__``, +* ``#ifdef __rtems__``, +* ``#else /* __rtems__ */``, and +* ``#endif /* __rtems__ */``. + +The guards must start at the begin of the line. Examples for wrong guards: + +.. code-block:: c + + static void + guards_must_start_at_the_begin_of_the_line(int j) + { + + /* WRONG */ + #ifdef __rtems__ + return (j + 1); + #else /* __rtems__ */ + return (j + 2); + #endif /* __rtems__ */ + } + + static void + missing_rtems_comments_in_the_guards(int j) + { + + #ifdef __rtems__ + return (j + 3); + /* WRONG */ + #else + return (j + 4); + #endif + } + +The FreeBSD build and configuration system uses option header files, e.g. +``#include "opt_xyz.h"`` in an unmodified FreeBSD file. This include is +transformed by the import script into ``#include <rtems/bsd/local/opt_xyz.h>``. Do +not disable option header includes via guards. Instead, add an empty option +header, e.g. ``touch rtemsbsd/include/rtems/bsd/local/opt_xyz.h``. + +.. code-block:: c + + /* WRONG */ + #ifndef __rtems__ + #include <rtems/bsd/local/opt_xyz.h> + #endif /* __rtems__ */ + +In general, provide empty header files and do not guard includes. + +For new code use +`STYLE(9) <http://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9>`_. + +Update FreeBSD Baseline +======================= + +Perform the following steps to do a FreeBSD baseline update: + +* Update ``__FreeBSD_version`` in ``rtemsbsd/include/machine/rtems-bsd-version.h`` + +* Update the namespace header file. + +* Review all code blocks with the ``REVIEW-AFTER-FREEBSD-BASELINE-UPDATE`` tag. + +Automatically Generated FreeBSD Files +===================================== + +Some source and header files are automatically generated during the FreeBSD +build process. The ``Makefile.todo`` file performs this manually. The should be +included in ``freebsd-to-rtems.py`` script some time in the future. For details, +see also +`KOBJ(9) <http://www.freebsd.org/cgi/man.cgi?query=kobj&sektion=9&apropos=0>`_. + +Reference Board Support Package +=============================== + +The reference BSP for LibBSD development is ``arm/xilinx_zynq_a9_qemu``. All +patches shall be tested for this BSP. The BSP runs on the Qemu simulator which +has some benefits for development and test of the LibBSD + +* ``NULL`` pointer read and write protection, +* Qemu is a fast simulator, +* Qemu provides support for GDB watchpoints, +* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge + devices (you can run multiple test instances on one virtual network). + +Board Support Package Requirements +================================== + +In FreeBSD, interrupt handler may use mutexes. In RTEMS, using mutexes from +within interrupt context is not allowed, so the Board Support Package (BSP) +should support the +`Interrupt Manager <https://docs.rtems.org/branches/master/c-user/interrupt/directives.html#rtems-interrupt-server-handler-install>`_ +in general. + +Network Interface Drivers Hints +=============================== + +Link Up/Down Events +------------------- + +You can notifiy the application space of link up/down events in your network +interface driver via the +``if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN)`` function. The +DHCPCD(8) client is a consumer of these events for example. Make sure that the +interface flag ``IFF_UP`` and the interface driver flag ``IFF_DRV_RUNNING`` is +set in case the link is up, otherwise ``ether_output()`` will return the error +status ``ENETDOWN``. + +FreeBSD Kernel Features Ported to LibBSD +======================================== + +All lock based synchronization primitives are implemented through mutexes using +the priority inheritance protocol. + +* `BUS_DMA(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_dma&sektion=9>`_: Bus and Machine Independent DMA Mapping Interface +* `BUS_SPACE(9) <http://www.freebsd.org/cgi/man.cgi?query=bus_space&sektion=9>`_: Bus space manipulation functions +* `CALLOUT(9) <http://www.freebsd.org/cgi/man.cgi?query=callout&sektion=9>`_: Execute a function after a specified length of time +* `CONDVAR(9) <http://www.freebsd.org/cgi/man.cgi?query=condvar&sektion=9>`_: Kernel condition variable +* `DEVICE(9) <http://www.freebsd.org/cgi/man.cgi?query=device&sektion=9>`_: An abstract representation of a device +* `DRIVER(9) <http://www.freebsd.org/cgi/man.cgi?query=driver&sektion=9>`_: Structure describing a device driver +* `EPOCH(9) <http://www.freebsd.org/cgi/man.cgi?query=epoch&sektion=9>`_: Kernel epoch based reclamation +* `MUTEX(9) <http://www.freebsd.org/cgi/man.cgi?query=mutex&sektion=9>`_: Kernel synchronization primitives +* `RMAN(9) <http://www.freebsd.org/cgi/man.cgi?query=rman&sektion=9>`_: Resource management functions +* `RMLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rmlock&sektion=9>`_: Kernel reader/writer lock optimized for read-mostly access patterns +* `RWLOCK(9) <http://www.freebsd.org/cgi/man.cgi?query=rwlock&sektion=9>`_: Kernel reader/writer lock +* `SX(9) <http://www.freebsd.org/cgi/man.cgi?query=sx&sektion=9>`_: Kernel shared/exclusive lock +* `SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_: Dynamic and static sysctl MIB creation functions +* `SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_: A framework for dynamic kernel initialization +* `TASKQUEUE(9) <http://www.freebsd.org/cgi/man.cgi?query=taskqueue&sektion=9>`_: Asynchronous task execution +* `UMA(9) <http://www.freebsd.org/cgi/man.cgi?query=uma&sektion=9>`_: General-purpose kernel object allocator + +LibBSD Initialization Details +============================= + +The initialization of LibBSD is based on the FreeBSD +`SYSINIT(9) <http://www.freebsd.org/cgi/man.cgi?query=sysinit&sektion=9>`_ +infrastructure. The key to initializing a system is to ensure that the desired +device drivers are explicitly pulled into the linked application. This plus +linking against the LibBSD (``libbsd.a``) will pull in the necessary FreeBSD +infrastructure. + +The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of +object files linked together. If we have a library, then creating the +executable is simple. We begin with a start symbol and recursively resolve all +references. With a bunch of object files linked together we need a different +mechanism. Most object files don't know each other. Lets say we have a driver +module. The rest of the system has no references to this driver module. The +driver module needs a way to tell the rest of the system: Hey, kernel I am +here, please use my services! + +This registration of independent components is performed by SYSINIT(9) and +specializations + +The SYSINIT(9) uses some global data structures that are placed in a certain +section. In the linker command file we need this: + +.. code-block:: none + + .rtemsroset : { + KEEP (*(SORT(.rtemsroset.*))) + } + + .rtemsrwset : { + KEEP (*(SORT(.rtemsrwset.*))) + } + +This results for example in this executable layout: + +.. code-block:: none + + [...] + *(SORT(.rtemsroset.*)) + .rtemsroset.bsd.modmetadata_set.begin + 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000025fe00 _bsd__start_set_modmetadata_set + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o) + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe08 0x4 libbsd.a(kern_module.o) + [...] + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe68 0x4 libbsd.a(mii.o) + .rtemsroset.bsd.modmetadata_set.content + 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o) + .rtemsroset.bsd.modmetadata_set.end + 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000025fe70 _bsd__stop_set_modmetadata_set + [...] + .rtemsrwset 0x000000000030bad0 0x290 + *(SORT(.rtemsrwset.*)) + .rtemsrwset.bsd.sysinit_set.begin + 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000030bad0 _bsd__start_set_sysinit_set + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030badc 0x4 libbsd.a(init_main.o) + [...] + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bd54 0x4 libbsd.a(frag6.o) + .rtemsrwset.bsd.sysinit_set.content + 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o) + .rtemsrwset.bsd.sysinit_set.end + 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o) + 0x000000000030bd60 _bsd__stop_set_sysinit_set + [...] + +Here you can see, that some global data structures are collected into +continuous memory areas. This memory area can be identified by start and stop +symbols. This constructs a table of uniform items. + +The low level FreeBSD code calls at some time during the initialization the +mi_startup() function (machine independent startup). This function will sort +the SYSINIT(9) set and call handler functions which perform further +initialization. The last step is the scheduler invocation. + +The SYSINIT(9) routines are run in ``mi_startup()`` which is called by +``rtems_bsd_initialize()``. This is also explained in "The Design and +Implementation of the FreeBSD Operating System" section 14.3 "Kernel +Initialization". + +In RTEMS, we have a library and not a bunch of object files. Thus we need a +way to pull-in the desired services out of the libbsd. Here the +``rtems-bsd-sysinit.h`` comes into play. The SYSINIT(9) macros have been +modified and extended for RTEMS in ``<sys/kernel.h>``: + +.. code-block:: none + + #ifndef __rtems__ + #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ + static struct sysinit uniquifier ## _sys_init = { \ + subsystem, \ + order, \ + func, \ + (ident) \ + }; \ + DATA_SET(sysinit_set,uniquifier ## _sys_init) + #else /* __rtems__ */ + #define SYSINIT_ENTRY_NAME(uniquifier) \ + _bsd_ ## uniquifier ## _sys_init + #define SYSINIT_REFERENCE_NAME(uniquifier) \ + _bsd_ ## uniquifier ## _sys_init_ref + #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ + struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \ + subsystem, \ + order, \ + func, \ + (ident) \ + }; \ + RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier)) + #define SYSINIT_REFERENCE(uniquifier) \ + extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \ + static struct sysinit const * const \ + SYSINIT_REFERENCE_NAME(uniquifier) __used \ + = &SYSINIT_ENTRY_NAME(uniquifier) + #define SYSINIT_MODULE_REFERENCE(mod) \ + SYSINIT_REFERENCE(mod ## module) + #define SYSINIT_DRIVER_REFERENCE(driver, bus) \ + SYSINIT_MODULE_REFERENCE(driver ## _ ## bus) + #define SYSINIT_DOMAIN_REFERENCE(dom) \ + SYSINIT_REFERENCE(domain_add_ ## dom) + #endif /* __rtems__ */ + +Here you see that the SYSINIT(9) entries are no longer static. The +``*_REFERENCE()`` macros will create references to the corresponding modules +which are later resolved by the linker. The application has to provide an +object file with references to all required FreeBSD modules. + +System Control Hints +==================== + +If you get undefined references to ``_bsd_sysctl_*`` symbols, then you have to +locate and add the associated system control node, see +`SYSCTL(9) <http://www.freebsd.org/cgi/man.cgi?query=SYSCTL_DECL&sektion=9>`_. + +Issues and TODO +=============== + +* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map(). + +* Priority queues are broken with clustered scheduling. + +* Per-CPU data should be enabled once the new stack is ready for SMP. + +* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP. + +* Multiple routing tables are not supported. Every FIB value is set to zero + (= BSD_DEFAULT_FIB). + +* Process identifiers are not supported. Every PID value is set to zero + (= BSD_DEFAULT_PID). + +* User credentials are not supported. The following functions allow the + operation for everyone + + * prison_equal_ip4(), + * chgsbsize(), + * cr_cansee(), + * cr_canseesocket() and + * cr_canseeinpcb(). + +* A basic USB functionality test that is known to work on Qemu is desirable. + +* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model + so that those architectures can use new TCP/IP and USB code. + +* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old + RTEMS TCP/IP stack. For some reason, the __printflike markers do not + compile in this environment. We may want to use the FreeBSD syslog.h + and get this addressed. + +* in_cksum implementations for architectures not supported by FreeBSD. + This will require figuring out where to put implementations that do + not originate from FreeBSD and are populated via the script. + +* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c"). + +* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML + parser and mmap(). + +* get_cyclecount(): The implementation is a security problem. + +* What to do with the priority parameter present in the FreeBSD synchronization + primitives and the thread creation functions? + +* TASKQUEUE(9): Support spin mutexes. + +* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c. + +* KQUEUE(2): Choose proper lock for global kqueue list. + +* TIMEOUT(9): Maybe use special task instead of timer server to call + callout_tick(). + +* sysctl_handle_opaque(): Implement reliable snapshots. + +* PING6(8): What to do with SIGALARM? + +* <sys/param.h>: Update Newlib to use a MSIZE of 256. + +* BPF(4): Add support for zero-copy buffers. + +* UNIX(4): Fix race conditions in the area of socket object and file node + destruction. Add support for file descriptor transmission via control + messages. + +* PRINTF(9): Add support for log(), the %D format specifier is missing in the + normal printf() family. + +* Why is the interrupt server used? The BSD interrupt handlers can block on + synchronization primitives like mutexes. This is in contrast to RTEMS + interrupt service routines. The BSPs using the generic interrupt support + must implement the ``bsp_interrupt_vector_enable()`` and + ``bsp_interrupt_vector_disable()`` routines. They normally enable/disable a + particular interrupt source at the interrupt controller. This can be used to + implement the interrupt server. The interrupt server is a task that wakes-up + in case an associated interrupt happens. The interrupt source is disabled in + a generic interrupt handler that wakes-up the interrupt server task. Once + the postponed interrupt processing is performed in the interrupt server the + interrupt source is enabled again. + +* Convert all BSP linkcmds to use a linkcmds.base so the sections are + easier to insert. + +* NIC Device Drivers +* Only common PCI NIC drivers have been included in the initial set. These + do not include any system on chip or ISA drivers. +* PCI configuration probe does not appear to happen to determine if a + NIC is in I/O or memory space. We have worked around this by using a + static hint to tell the fxp driver the correct mode. But this needs to + be addressed. +* The ISA drivers require more BSD infrastructure to be addressed. This was + outside the scope of the initial porting effort. + +* devfs (Device file system): There is a minimal implementation based on IMFS. + The mount point is fixed to "/dev". Note that the devfs is only used by the + cdev subsystem. cdev has been adapted so that the full path (including the + leading "/dev") is given to devfs. This saves some copy operations. + + devfs_create() first creates the full path and then creates an IMFS generic + node for the device. + + TBD: remove empty paths on devfs_destroy(). + +* altq_subr.c - Arbitrary choices were made in this file that RTEMS would not + support tsc frequency change. Additionally, the clock frequency for + machclk_freq is always measured for RTEMS. + +* conf.h - In order to add make_dev and destroy_dev, variables in the cdev + structure that were not being used were conditionally compiled out. The + capability of supporting children did not appear to be needed and was not + implemented in the rtems version of these routines. + +* Problem to report to FreeBSD: The MMAP_NOT_AVAILABLE define is inverted on + its usage. When it is defined the mmap method is called. Additionally, it is + not used thoroughly. It is not used in the unmap portion of the source. The + file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap the call to mmap + and file rec_close.c uses the munmap method. diff --git a/Makefile.todo b/Makefile.todo index 6822e812..9e6084ed 100644 --- a/Makefile.todo +++ b/Makefile.todo @@ -95,6 +95,7 @@ program-header: cd freebsd/tools/tools/net80211/wlanstats && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/tools/tools/net80211/wlanstats/*.o -p wlanstats cd freebsd/usr.bin/netstat && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.bin/netstat/*.o -p netstat cd freebsd/usr.sbin/arp && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.sbin/arp/*.o -p arp + cd freebsd/usr.sbin/ifmcstat && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/freebsd/usr.sbin/ifmcstat/*.o -p ifmcstat cd ipsec-tools/src/racoon && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/ipsec-tools/src/racoon/*.o -p racoon cd ipsec-tools/src/setkey && $(BASE)/userspace-header-gen.py $(BUILD_BSP)/ipsec-tools/src/setkey/*.o -p setkey diff --git a/README.md b/README.md deleted file mode 100644 index 7794b12b..00000000 --- a/README.md +++ /dev/null @@ -1,294 +0,0 @@ -RTEMS LibBSD -============ - -Welcome to building LibBSD for RTEMS using Waf. This package is a library -containing various parts of the FreeBSD kernel ported to RTEMS. The library -replaces the networking port of FreeBSD in the RTEMS kernel sources. This -package is designed to be updated from the FreeBSD kernel sources and contains -more than just the networking code. - -To build this package you need a current RTEMS tool set for your architecture, -and a recent RTEMS kernel for your BSP configured with networking disabled -built and installed. If you already have this you can skip to step 3 of the -build procedure. - -Building and Installing LibBSD ------------------------------- - -The following instructions show you how to build and install RTEMS Tools and -RTEMS kernel for your BSP in separate paths. Using separate paths for the tools -and BSPs lets you manage what you have installed. If you are happy with a -single path you can use the same path in each stage. - -The Waf build support for RTEMS requires you provide your BSP name as an -architecture and BSP pair. You must provide both or Waf will generate an error -message during the configure phase. - -We will build an Xilinx Zynq QEMU BSP using the name -*arm/xilinx_zynq_a9_qemu*. You can copy and paste the shell commands below to -do this. The individual steps are explained afterwards. - -``` -sandbox="$PWD/sandbox" -mkdir sandbox -cd "$sandbox" -git clone git://git.rtems.org/rtems-source-builder.git -git clone git://git.rtems.org/rtems.git -git clone git://git.rtems.org/rtems-libbsd.git -cd "$sandbox" -cd rtems-source-builder/rtems -../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm -cd "$sandbox" -cd rtems -PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap -cd "$sandbox" -mkdir b-xilinx_zynq_a9_qemu -cd b-xilinx_zynq_a9_qemu -PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \ - --target=arm-rtems5 --prefix="$sandbox/rtems/5" \ - --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu -PATH="$sandbox/rtems/5/bin:$PATH" make -PATH="$sandbox/rtems/5/bin:$PATH" make install -cd "$sandbox" -cd rtems-libbsd -git submodule init -git submodule update rtems_waf -./waf configure --prefix="$sandbox/rtems/5" \ - --rtems-bsps=arm/xilinx_zynq_a9_qemu \ - --buildset=buildset/default.ini -./waf -./waf install -qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none \ - -nographic -M xilinx-zynq-a9 -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/selectpollkqueue01.exe -``` - -1. Create a sandbox directory: - -``` -$ sandbox="$PWD/sandbox" -$ mkdir sandbox -``` - -2. Clone the repositories: - -``` -$ cd "$sandbox" -$ git clone git://git.rtems.org/rtems-source-builder.git -$ git clone git://git.rtems.org/rtems.git -$ git clone git://git.rtems.org/rtems-libbsd.git -``` - -3. Build and install the tools: - -``` -$ cd "$sandbox" -$ cd rtems-source-builder/rtems -$ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/5" 5/rtems-arm -``` - -4. Bootstrap the RTEMS sources: - -``` -$ cd "$sandbox" -$ cd rtems -$ PATH="$sandbox/rtems/5/bin:$PATH" ./bootstrap -``` - -5. Build and install the RTEMS Board Support Packages (BSP) you want to use: - -``` -$ cd "$sandbox" -$ mkdir b-xilinx_zynq_a9_qemu -$ cd b-xilinx_zynq_a9_qemu -$ PATH="$sandbox/rtems/5/bin:$PATH" "$sandbox/rtems/configure" \ - --target=arm-rtems5 --prefix="$sandbox/rtems/5" \ - --disable-networking --enable-rtemsbsp=xilinx_zynq_a9_qemu -$ PATH="$sandbox/rtems/5/bin:$PATH" make -$ PATH="$sandbox/rtems/5/bin:$PATH" make install -``` - -6. Populate the rtems_waf git submodule. Note, make sure you specify - 'rtems_waf' or the FreeBSD kernel source will be cloned: - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ git submodule init -$ git submodule update rtems_waf -``` - -7. Run Waf's configure with your specific settings. In this case the path to - the tools and RTEMS are provided on the command line and so do not need to - be in your path or environment [1]. You can use - '--rtems-archs=arm,sparc,i386' or - '--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586' to build for - more than BSP at a time. Note, you must provide the architecture and BSP as - a pair. Providing just the BSP name will fail. This call also explicitly - provides a buildset via the '--buildset=buildset/default.ini' option. If no - buildset is provided the default one (which is the same as the one provided - explicitly here) will be used. You can also provide multiple buildsets as a - coma separated list or via multiple '--buildset=x' options. - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ ./waf configure --prefix="$sandbox/rtems/5" \ - --rtems-bsps=arm/xilinx_zynq_a9_qemu \ - --buildset=buildset/default.ini -``` - -8. Build and install. The LibBSD package will be installed into the prefix - provided to configure: - -``` -$ cd "$sandbox" -$ cd rtems-libbsd -$ ./waf -$ ./waf install -``` - -9. Run the tests on QEMU, for example: - -``` -$ qemu-system-arm -no-reboot -serial null -serial mon:stdio -net none \ -$ -nographic -M xilinx-zynq-a9 -m 256M \ -$ -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/selectpollkqueue01.exe -``` - -[1] It is good practice to keep your environment as empty as possible. Setting - paths to tools or specific values to configure or control a build is - dangerous because settings can leak between different builds and change - what you expect a build to do. The Waf tool used here lets you specify on - the command line the tools and RTEMS paths and this is embedded in Waf's - configuration information. If you have a few source trees working at any - one time with different tool sets or configurations you can easly move - between them safe in the knowledge that one build will not infect another. - -Branches --------- - -* master - branch intended for the RTEMS master which tracks the FreeBSD master - branch. This branch must be used for libbsd development. Back ports to the - 6-freebsd-12 are allowed. - -* 6-freebsd-12 - branch intended for RTEMS 6 which tracks the FreeBSD stable/12 - branch. This branch is maintained and regular updates from FreeBSD are - planned. It is recommended for production systems. - -* 5-freebsd-12 - branch belongs to the RTEMS 5 release. It is based on FreeBSD - stable/12 branch. It is recommended for production systems that use RTEMS 5. - -* 5 - branch belongs to the RTEMS 5 release. It is based on a FreeBSD - development version. - -* freebsd-9.3 - branch for some RTEMS version with a FreeBSD 9.3 baseline. - This branch is unmaintained. It is recommended to update to RTEMS 5 or 6. - -* 4.11 - branch for the RTEMS 4.11 release series. This branch is - unmaintained. It is recommended to update to RTEMS 5 or 6. - -Updating RTEMS Waf Support --------------------------- - -If you have a working libbsd repository and new changes to the `rtems_waf` -submodule has been made, you will need update. A `git status` will indicate -there are new commits with: - -``` -$ git status - [ snip output ] - modified: rtems_waf (new commits) - [ snip output ] -``` - -To update: - -``` -$ git submodule update rtems_waf -``` - -Please make sure you use the exact command or you might find you are cloning -the whole of the FreeBSD source tree. If that happens simply git ^C and try -again. - -FreeBSD Kernel Options ----------------------- - -You can set FreeBSD kernel options during build configuration with the ---freebsd-option=a,b,c,... configuration command option. This is an advanced -option and should only be used if you are familiar with the internals of the -FreeBSD kernel and what these options do. Each of the comma separated options -is converted to uppercase and passed as a compiler command line define (-D). - -The options are listed in: - -https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES - -An example to turn on a verbose kernel boot, verbose sysinit and bus debugging -configure with: - -``` ---freebsd-options=bootverbose,verbose_sysinit,bus_debug -``` - -To enable kernel internal consistency checking use: - -``` ---freebsd-options=invariants,invariant_support -``` - -Qemu and Networking -------------------- - -You can use the Qemu simulator to run a LibBSD based application and connect it -to a virtual network on your host. You have to create a TAP virtual Ethernet -interface for this: - -``` -sudo tunctl -p -t qtap -u $(whoami) -sudo ip link set dev qtap up -sudo ip addr add 169.254.1.1/16 dev qtap -``` - -You can show the interface state with the following command: - -``` -$ ip addr show qtap -27: qtap: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 - link/ether 8e:50:a2:fb:e1:3b brd ff:ff:ff:ff:ff:ff - inet 169.254.1.1/16 scope global qtap - valid_lft forever preferred_lft forever -``` - -You may have to assign the interface to a firewall zone. - -The Qemu command line varies by board support package, here is an example for -the arm/xilinx_zynq_a9_qemu BSP: - -``` -qemu-system-arm -serial null -serial mon:stdio -nographic \ - -M xilinx-zynq-a9 -m 256M \ - -net tap,ifname=qtap,script=no,downscript=no \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:12 \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu-default/media01.exe -``` - -After some seconds it will acquire a IPv4 link-local address, e.g. - -``` -info: cgem0: probing for an IPv4LL address -debug: cgem0: checking for 169.254.159.156 -``` - -You can connect to the target via telnet for example: - -``` -$ telnet 169.254.159.156 -Trying 169.254.159.156... -Connected to 169.254.159.156. -Escape character is '^]'. - -RTEMS Shell on /dev/pty4. Use 'help' to list commands. -TLNT [/] # -``` diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..9b328078 --- /dev/null +++ b/README.rst @@ -0,0 +1,853 @@ +RTEMS LibBSD +************ + +Welcome to building LibBSD for RTEMS using Waf. This package is a library +containing various parts of the FreeBSD kernel ported to RTEMS. The library +replaces the networking port of FreeBSD in the RTEMS kernel sources. This +package is designed to be updated from the FreeBSD kernel sources and contains +more than just the networking code. + +To build this package you need a current RTEMS tool set for your architecture, +and a recent RTEMS kernel for your BSP installed. If you already have this, you +can skip to step 5 of the build procedure. + +Building and Installing LibBSD +============================== + +The following instructions show you how to build and install the RTEMS Tool +Suite for the ``arm`` target, the RTEMS kernel using the +``arm/xilinx_zynq_a9_qemu`` Board Support Package (BSP), and the LibBSD for this +BSP. + +The Waf build support for RTEMS requires you provide your BSP name as an +architecture and BSP pair. You must provide both or Waf will generate an error +message during the configure phase. + +We will build an Xilinx Zynq Qemu BSP using the name +``arm/xilinx_zynq_a9_qemu``. You can copy and paste the shell commands below to +do this. The individual steps are explained afterwards. + +.. code-block:: none + + sandbox="$PWD/sandbox" + mkdir sandbox + cd "$sandbox" + git clone git://git.rtems.org/rtems-source-builder.git + git clone git://git.rtems.org/rtems.git + git clone git://git.rtems.org/rtems-libbsd.git + cd "$sandbox" + cd rtems-source-builder/rtems + ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm + cd "$sandbox" + cd rtems + echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini + ./waf configure --prefix "$sandbox/rtems/6" + ./waf + ./waf install + cd "$sandbox" + cd rtems-libbsd + git submodule init + git submodule update rtems_waf + ./waf configure --prefix="$sandbox/rtems/6" \ + --rtems-bsps=arm/xilinx_zynq_a9_qemu \ + --buildset=buildset/default.ini + ./waf + ./waf install + ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build + +1. Create a sandbox directory: + + .. code-block:: none + + $ sandbox="$PWD/sandbox" + $ mkdir sandbox + +2. Clone the repositories: + + .. code-block:: none + + $ cd "$sandbox" + $ git clone git://git.rtems.org/rtems-source-builder.git + $ git clone git://git.rtems.org/rtems.git + $ git clone git://git.rtems.org/rtems-libbsd.git + +3. Build and install the tools: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-source-builder/rtems + $ ../source-builder/sb-set-builder --prefix="$sandbox/rtems/6" 6/rtems-arm + +4. Build and install the RTEMS Board Support Packages (BSP) you want to use: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems + $ echo -e "[arm/xilinx_zynq_a9_qemu]" > config.ini + $ ./waf configure --prefix "$sandbox/rtems/6" + $ ./waf + $ ./waf install + +5. Populate the ``rtems_waf`` git submodule. Note, make sure you specify + ``rtems_waf`` or the FreeBSD kernel source will be cloned: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ git submodule init + $ git submodule update rtems_waf + +6. Run Waf's configure with your specific settings. In this case the path to + the tools and RTEMS are provided on the command line and so do not need to + be in your path or environment, see comment below. You can use + ``--rtems-archs=arm,sparc,i386`` or + ``--rtems-bsps=arm/xilinx_zynq_a9_qemu,sparc/sis,i386/pc586`` to build for + more than BSP at a time. Note, you must provide the architecture and BSP as + a pair. Providing just the BSP name will fail. This call also explicitly + provides a buildset via the ``--buildset=buildset/default.ini`` option. If no + buildset is provided the default one (which is the same as the one provided + explicitly here) will be used. You can also provide multiple buildsets as a + coma separated list or via multiple ``--buildset=x`` options. + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ./waf configure --prefix="$sandbox/rtems/6" \ + --rtems-bsps=arm/xilinx_zynq_a9_qemu \ + --buildset=buildset/default.ini + +7. Build and install. The LibBSD package will be installed into the prefix + provided to configure: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ./waf + $ ./waf install + +9. Run the tests: + + .. code-block:: none + + $ cd "$sandbox" + $ cd rtems-libbsd + $ ../rtems/6/bin/rtems-test --rtems-bsp=xilinx_zynq_a9_qemu build + +It is good practice to keep your environment as empty as possible. Setting +paths to tools or specific values to configure or control a build is dangerous +because settings can leak between different builds and change what you expect a +build to do. The Waf tool used here lets you specify on the command line the +tools and RTEMS paths and this is embedded in Waf's configuration information. +If you have a few source trees working at any one time with different tool sets +or configurations you can easly move between them safe in the knowledge that +one build will not infect another. + +Buildsets +========= + +Note that the LibBSD supports different buildsets. These can be selected with +the ``--buildset=some.ini`` option during the configure phase. Take a look at +the comments in ``buildset/*.ini`` to see which build sets are officially +supported. + +You can also create and provide your own buildset configuration. But remember +that it's quite easy to break something by disabling the wrong modules. Only +the configurations in the ``buildset`` directory are officially maintained. + +Initialization +============== + +To initialise the LibBSD create a suitable ``rc.conf`` file. The FreeBSD man +page `RC.CONF(5) <https://www.freebsd.org/cgi/man.cgi?rc.conf>`_ provides the +details needed to create a suitable format file + +You can call one of three functions to run the initialisation once LibBSD has +initialised: + +* ``rtems_bsd_run_etc_rc_conf()``: Run ``/etc/rc.conf``. +* ``rtems_bsd_run_rc_conf()``: Run a user supplied file. +* ``rtems_bsd_run_rc_conf_script()``: Run the in memory line feed separated text string. + +For exapmle: + +.. code-block:: c + + void + network_init(void) + { + rtems_status_code sc; + + sc = rtems_bsd_initialize(); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_bsd_run_etc_rc_conf(true); /* verbose = true */ + } + +By default the networking support is builtin. Other directives can be added and +are found in ``machine/rtems-bsd-rc-conf-directives.h``. Please check the file +for the list. + +The following network names are supported: + +.. code-block:: none + + cloned_interfaces + ifconfig_'interface' + defaultrouter + hostname + +For example: + +.. code-block:: none + + # + # My BSD initialisation. + # + hostname="myhost" + cloned_interfaces="vlan0 vlan1" + ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0" + fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0" + defaultrouter="10.10.10.1" + +You can also intialise the LibBSD using code. The following code to +initialize the LibBSD: + +.. code-block:: c + + #include <assert.h> + #include <sysexits.h> + + #include <rtems/bsd/bsd.h> + + void + network_init(void) + { + rtems_status_code sc; + int exit_code; + + sc = rtems_bsd_initialize(); + assert(sc == RTEMS_SUCCESSFUL); + + exit_code = rtems_bsd_ifconfig_lo0(); + assert(exit_code == EX_OK); + } + +This performs the basic network stack initialization with a loopback interface. +Further initialization must be done using the standard FreeBSD network +configuration commands +`IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_ +using ``rtems_bsd_command_ifconfig()`` and +`ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_ +using ``rtems_bsd_command_route()``. For an example, please have a look at +`default-network-init.h <testsuite/include/rtems/bsd/test/default-network-init.h>`_. + +Task Priorities and Stack Size +============================== + +The default task priority is 96 for the interrupt server task (name "IRQS"), 98 +for the timer server task (name "TIME") and 100 for all other tasks. The +application may provide their own implementation of the +``rtems_bsd_get_task_priority()`` function if different values are desired (for +example in the translation unit which calls ``rtems_bsd_initialize()``). + +The task stack size is determined by the ``rtems_bsd_get_task_stack_size()`` +function which may be provided by the application in case the default is not +appropriate. + +Size for Allocator Domains +========================== + +The size for an allocator domain can be specified via the +``rtems_bsd_get_allocator_domain_size()`` function. The application may provide +their own implementation of the ``rtems_bsd_get_allocator_domain_size()`` +function (for example in the module which calls ``rtems_bsd_initialize()``) if +different values are desired. The default size is 8MiB for all domains. + +Redirecting or Disabling the Output +=================================== + +A lot of system messages are printed to the ``stdout`` by default. If you want to +redirect them you can overwrite the default print handler. That can even be done +before the libbsd initialization to catch all messages. An example would look +like follows: + +.. code-block:: c + + int my_vprintf_handler(int level, const char *fmt, va_list ap) { + /* Do something with the messages. */ + + return number_of_printed_chars; + } + + ... + /* In your initialization: */ + rtems_bsd_vprintf_handler old; + old = rtems_bsd_set_vprintf_handler(my_vprintf_handler); + ... + +As a special case, you can set the ``rtems_bsd_vprintf_handler_mute(...)`` +provided by LibBSD to suppress all output. + +Branches +======== + +master + This branch is intended for the RTEMS master which tracks the FreeBSD + master branch. This branch must be used for libbsd development. Back + ports to the 6-freebsd-12 are allowed. + +6-freebsd-12 + This branch is intended for RTEMS 6 which tracks the FreeBSD stable/12 + branch. This branch is maintained and regular updates from FreeBSD are + planned. It is recommended for production systems. + +5-freebsd-12 + This branch belongs to the RTEMS 5 release. It is based on FreeBSD + stable/12 branch. It is recommended for production systems that use + RTEMS 5. + +5 + This branch belongs to the RTEMS 5 release. It is based on a FreeBSD + development version. This branch is unmaintained. Use 5-freebsd-12 for + RTEMS 5. + +freebsd-9.3 + Is the branch for some RTEMS version with a FreeBSD 9.3 baseline. This + branch is unmaintained. It is recommended to update to RTEMS 5 or 6. + +4.11 + Is the branch for the RTEMS 4.11 release series. This branch is + unmaintained. It is recommended to update to RTEMS 5 or 6. + +Features +======== + +The following features are available in LibBSD. Some features need device +driver support for a particular target platform. + +* `BPF(4) <http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4>`_: Berkeley Packet Filter +* `DHCPCD(8) <http://roy.marples.name/projects/dhcpcd/index>`_: DHCP client +* `dns_sd.h <mDNSResponder/mDNSShared/dns_sd.h>`_: DNS Service Discovery +* `GETHOSTBYNAME(3) <http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3>`_: Get network host entry +* `IF_BRIDGE(4) <http://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4>`_: Network bridge device +* `INET(4) <http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4>`_: Internet protocol family +* `INET6(4) <http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4>`_: Internet protocol version 6 family +* `IPSEC(4) <http://www.freebsd.org/cgi/man.cgi?query=ipsec&sektion=4>`_: Internet Protocol Security protocol +* `KQUEUE(2) <http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2>`_: Kernel event notification mechanism +* `LAGG(4) <http://www.freebsd.org/cgi/man.cgi?query=lagg&sektion=4>`_: Link aggregation and link failover interface +* `mDNSEmbeddedAPI.h <mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h>`_: Multi-Cast DNS +* `MMC(4) <http://www.freebsd.org/cgi/man.cgi?query=mmc&sektion=4>`_: MultiMediaCard and SD Card bus driver +* `NET80211(4) <http://www.freebsd.org/cgi/man.cgi?query=net80211&sektion=4>`_: Standard interface to IEEE 802.11 devices +* `NVME(4) <http://www.freebsd.org/cgi/man.cgi?query=nvme&sektion=4>`_: NVM Express core driver +* `PCI(4) <http://www.freebsd.org/cgi/man.cgi?query=pci&sektion=4>`_: Generic PCI/PCIe bus driver +* `PF(4) <http://www.freebsd.org/cgi/man.cgi?query=pf&sektion=4>`_: Packet filter +* `POLL(2) <http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2>`_: Synchronous I/O multiplexing +* `RESOLVER(3) <http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3>`_: Resolver routines +* `ROUTE(4) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4>`_: Kernel packet forwarding database +* `SELECT(2) <http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2>`_: Synchronous I/O multiplexing +* `SOCKET(2) <http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2>`_: Create an endpoint for communication +* `SSL(7) <http://www.freebsd.org/cgi/man.cgi?query=ssl&sektion=7>`_: OpenSSL SSL/TLS library +* `SYSCTL(3) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3>`_: Get or set system information +* `TCP(4) <http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4>`_: Internet Transmission Control Protocol +* `UDP(4) <http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4>`_: Internet User Datagram Protocol +* `UMASS(4) <http://www.freebsd.org/cgi/man.cgi?query=umass&sektion=4>`_: USB Mass Storage Devices driver +* `UNIX(4) <http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4>`_: UNIX-domain protocol family +* `USB(4) <http://www.freebsd.org/cgi/man.cgi?query=usb&sektion=4>`_: Universal Serial Bus +* `VLAN(4) <http://www.freebsd.org/cgi/man.cgi?query=vlan&sektion=4>`_: IEEE 802.1Q VLAN network interface + +Commands +======== + +In LibBSD the following ports of FreeBSD command line tools are available. You +can invoke the commands in the RTEMS Shell or through function calls, for +example ``rtems_bsd_command_ifconfig()``. The functions declarations are +available through +`#include <machine/rtems-bsd-commands.h> <rtemsbsd/include/machine/rtems-bsd-commands.h>`_. + +* `ARP(8) <http://www.freebsd.org/cgi/man.cgi?query=arp&sektion=8>`_: Address resolution display and control +* `HOSTNAME(1) <http://www.freebsd.org/cgi/man.cgi?query=hostname&sektion=1>`_: Set or print name of current host system +* `IFCONFIG(8) <http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8>`_: Configure network interface parameters +* `IFMCSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=ifmcstat&sektion=8>`_: Dump multicast group management statistics per interface +* `NETSTAT(1) <http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1>`_: Show network status +* `NVMECONTROL(8) <http://www.freebsd.org/cgi/man.cgi?query=nvmecontrol&sektion=8>`_: NVM Express control utility +* `OPENSSL(1) <http://www.freebsd.org/cgi/man.cgi?query=openssl&sektion=1>`_: OpenSSL command line tool +* `PFCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_: Control the packet filter (PF) device +* `PING6(8) <http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8>`_: Send ICMPv6 ECHO_REQUEST packets to network hosts +* `PING(8) <http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8>`_: Send ICMP ECHO_REQUEST packets to network hosts +* `RACOON(8) <http://www.freebsd.org/cgi/man.cgi?query=racoon&sektion=8>`_: IKE (ISAKMP/Oakley) key management daemon +* `ROUTE(8) <http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8>`_: Manually manipulate the routing tables +* `SETKEY(8) <http://www.freebsd.org/cgi/man.cgi?query=setkey&sektion=8>`_: Manually manipulate the IPsec SA/SP database +* `STTY(1) <http://www.freebsd.org/cgi/man.cgi?query=stty&sektion=1>`_: Set the options for a terminal device interface +* `SYSCTL(8) <http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=8>`_: Get or set kernel state +* `TCPDUMP(1) <http://www.freebsd.org/cgi/man.cgi?query=tcpdump&sektion=1>`_: Dump traffic on a network +* `VMSTAT(8) <http://www.freebsd.org/cgi/man.cgi?query=vmstat&sektion=8>`_: Report virtual memory statistics +* `WPA_SUPPLICANT(8) <http://www.freebsd.org/cgi/man.cgi?query=wpa_supplicant&sektion=8>`_: WPA/802.11i Supplicant for wireless network devices + +Command specific notes are listed below. + +HOSTNAME(1) + In addition to the standard options the RTEMS version of the HOSTNAME(1) + command supports the -m flag to set/get the multicast hostname of the mDNS + resolver instance. See also ``rtems_mdns_sethostname()`` and + ``rtems_mdns_gethostname()``. + +Packet Filter (PF, Firewall) +============================ + +It is possible to use PF as a firewall. See the +`FreeBSD Handbook <https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-pf>`_ +for details on the range of functions and for how to configure the firewall. + +Configuration +------------- + +The following is necessary to use PF on RTEMS: + +* You have to provide a ``/etc/pf.os`` file. The firewall can use it for passive + OS fingerprinting. If you don't want to use this feature, the file may contain + nothing except a line of comment (for example "# empty"). + +* If some filters use protocol names (like ``tcp`` or ``udp``) you have to provide a + ``/etc/protocols`` file. + +* If some filters use service names (like ``http`` or ``https``) you have to provide a + ``/etc/services`` file. + +* Create a rule file (normally ``/etc/pf.conf``). See the FreeBSD manual for the + syntax. + +* Load the rule file using the + `pfctl <http://www.freebsd.org/cgi/man.cgi?query=pfctl&sektion=8>`_ + command and enable PF. Please note that the pfctl command needs a lot of + stack. You should use at least RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of + stack. An example initialisation can look like follows: + + .. code-block:: c + + int exit_code; + char *argv[] = { + "pfctl", + "-f", + "/etc/pf.conf", + "-e", + NULL + }; + + exit_code = rtems_bsd_command_pfctl(ARGC(argv), argv); + assert(exit_code == EXIT_SUCCSESS); + +Known Restrictions +------------------ + +Currently, PF on RTEMS always uses the configuration for memory restricted +systems (on FreeBSD that means systems with less than 100 MB RAM). This is +fixed in ``pfctl_init_options()``. + +Wireless Network (WLAN) +======================= + +The LibBSD provides a basic support for WLAN. Note that currently this support +is still in an early state. The WLAN support is _not_ enabled in the default +buildset. You have to configure LibBSD with the +``--buildset=buildset/everything.ini`` to enable that feature. + +Configuration +------------- + +The following gives a rough overview over the necessary steps to connect to an +encrypted network with an RTL8188EU based WiFi dongle: + +* Reference all necessary module for your BSP. For some BSPs this is already + done in the ``nexus-devices.h``: + + .. code-block:: none + + SYSINIT_MODULE_REFERENCE(wlan_ratectl_none); + SYSINIT_MODULE_REFERENCE(wlan_sta); + SYSINIT_MODULE_REFERENCE(wlan_amrr); + SYSINIT_MODULE_REFERENCE(wlan_wep); + SYSINIT_MODULE_REFERENCE(wlan_tkip); + SYSINIT_MODULE_REFERENCE(wlan_ccmp); + SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub); + +* Create your wlan device using ifconfig: + + .. code-block:: none + + ifconfig wlan0 create wlandev rtwn0 up + +* Start a ``wpa_supplicant`` instance for that device: + + .. code-block:: none + + wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf + +Note that the wpa_supplicant will only be active till the device goes down. A +workaround is to just restart it every time it exits. + +Known Restrictions +------------------ + +* The network interface (e.g. wlan0) is currently not automatically created. It + would be nice, if some service would create it as soon as for example a USB + device is connected. In FreeBSD the names are assigned via rc.conf with lines + like ``wlans_rtwn0="wlan0"``. + +* ``wpa_supplicant`` hast to be started after the device is created. It has to be + restarted every time the connection goes down. Instead of this behaviour, + there should be some service that starts and restarts ``wpa_supplicant`` + automatically if a interface is ready. Probably the dhcpcd hooks could be used + for that. + +* The current ``wpa_supplicant`` implementation is protected with a lock so it can't + be started more than one time. If multiple interface should be used, all have + to be handled by that single instance. That makes it hard to add interfaces + dynamically. ``wpa_supplicant`` should be reviewed thoroughly whether multiple + instances could be started in parallel. + +* The control interface of ``wpa_supplicant`` most likely doesn't work. The wpa_cli + application is not ported. + +IPSec +===== + +The IPSec support is optional in LibBSD. It is disabled in the default build +set. Please make sure to use a build set with ``netipsec = on``. + +Configuration +------------- + +To use IPSec the following configuration is necessary: + +.. code-block:: none + + SYSINIT_MODULE_REFERENCE(if_gif); + SYSINIT_MODULE_REFERENCE(cryptodev); + RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec) + RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL); + +Alternatively, you can use the ``RTEMS_BSD_CONFIG_IPSEC`` which also includes the +rc.conf support for ipsec. It's still necessary to include a crypto device in +your config (``cryptosoft`` in the above sample). + +The necessary initialization steps for a IPSec connection are similar to the +steps on a FreeBSD-System. The example assumes the following setup: + +- RTEMS external IP: 192.168.10.1/24 +- RTEMS internal IP: 10.10.1.1/24 +- remote external IP: 192.168.10.10/24 +- remote internal IP: 172.24.0.1/24 +- shared key: "mysecretkey" + +With this the following steps are necessary: + +* Create a gif0 device: + + .. code-block:: none + + ifconfig gif0 create + +* Configure the gif0 device: + + .. code-block:: none + + ifconfig gif0 10.10.1.1 172.24.0.1 + ifconfig gif0 tunnel 192.168.10.1 192.168.10.10 + +* Add a route to the remote net via the remote IP: + + .. code-block:: none + + route add 172.24.0.0/24 172.24.0.1 + +* Create a correct rule set in ``/etc/setkey.conf``: + + .. code-block:: none + + flush; + spdflush; + spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use; + spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use; + +* Call ``setkey``: + + .. code-block:: none + + setkey -f /etc/setkey.conf + +* Create a correct configuration in ``/etc/racoon.conf``: + + .. code-block:: none + + path pre_shared_key "/etc/racoon_psk.txt"; + log info; + + padding # options are not to be changed + { + maximum_length 20; + randomize off; + strict_check off; + exclusive_tail off; + } + + listen # address [port] that racoon will listen on + { + isakmp 192.168.10.1[500]; + } + + remote 192.168.10.10 [500] + { + exchange_mode main; + my_identifier address 192.168.10.1; + peers_identifier address 192.168.10.10; + proposal_check obey; + proposal { + encryption_algorithm 3des; + hash_algorithm md5; + authentication_method pre_shared_key; + lifetime time 3600 sec; + dh_group 2; + } + } + + sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any) + { + pfs_group 2; + lifetime time 28800 sec; + encryption_algorithm 3des; + authentication_algorithm hmac_md5; + compression_algorithm deflate; + } + +* Create a correct configuration in ``/etc/racoon_psk.txt``: + + .. code-block:: none + + 192.168.10.10 mysecretkey + +* Start a ike-daemon (racoon): + + .. code-block:: none + + racoon -F -f /etc/racoon.conf +---- + +All commands can be called via the respective API functions. For racoon there is +a ``rtems_bsd_racoon_daemon()`` function that forks of racoon as a task. + +Alternatively, IPSec can also be configured via rc.conf entries: + +.. code-block:: none + + cloned_interfaces="gif0" + ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10" + ike_enable="YES" + ike_program="racoon" + ike_flags="-F -f /etc/racoon.conf" + ike_priority="250" + + ipsec_enable="YES" + ipsec_file="/etc/setkey.conf" + +ATTENTION: It is possible that the first packets slip through the tunnel without +encryption (true for FreeBSD as well as RTEMS). You might want to set up a +firewall rule to prevent that. + +Updating RTEMS Waf Support +========================== + +If you have a working libbsd repository and new changes to the ``rtems_waf`` +submodule has been made, you will need update. A ``git status`` will indicate +there are new commits with: + +.. code-block:: none + + $ git status + [ snip output ] + modified: rtems_waf (new commits) + [ snip output ] + +To update: + +.. code-block:: none + + $ git submodule update rtems_waf + +Please make sure you use the exact command or you might find you are cloning +the whole of the FreeBSD source tree. If that happens simply git ^C and try +again. + +FreeBSD Kernel Options +====================== + +You can set FreeBSD kernel options during build configuration with the +--freebsd-option=a,b,c,... configuration command option. This is an advanced +option and should only be used if you are familiar with the internals of the +FreeBSD kernel and what these options do. Each of the comma separated options +is converted to uppercase and passed as a compiler command line define (-D). + +The options are listed in the FreeBSD +`NOTES <https://github.com/freebsd/freebsd/blob/master/sys/conf/NOTES>`_ +file. + +An example to turn on a verbose kernel boot, verbose sysinit and bus debugging +configure with: + +.. code-block:: none + + --freebsd-options=bootverbose,verbose_sysinit,bus_debug + +To enable kernel internal consistency checking use: + +.. code-block:: none + + --freebsd-options=invariants,invariant_support + +SMP Requirements +================ + +In order to support +`EPOCH(9) <https://www.freebsd.org/cgi/man.cgi?query=epoch&apropos=0&sektion=9>`_ +a scheduler with thread pinning support is required. This is the case if you +use the default scheduler configuration. EPOCH(9) is a central synchronization +mechanism of the network stack. + +Configuration for Network Tests +=============================== + +If you need some other IP configuration for the network tests that use a fixed +IP config you can copy ``config.inc`` to a location outside to the source tree and +adapt it. Then use the option ``--net-test-config=NET_CONFIG`` to pass the file to +Waf's configure command. + +.. code-block:: none + + NET_CFG_SELF_IP = 10.0.0.2 + NET_CFG_NETMASK = 255.255.0.0 + NET_CFG_PEER_IP = 10.0.0.1 + NET_CFG_GATEWAY_IP = 10.0.0.1 + +Qemu and Networking +=================== + +You can use the Qemu simulator to run a LibBSD based application and connect it +to a virtual network on your host. + +Networking with TAP Interface +----------------------------- + +One option for networking with Qemu is using a TAP interface (virtual +Ethernet). You can create a TAP interface with these commands on Linux: + +.. code-block:: none + + sudo ip tuntap add qtap mode tap user $(whoami) + sudo ip link set dev qtap up + sudo ip addr add 169.254.1.1/16 dev qtap + +You can show the interface state with the following command: + +.. code-block:: none + + $ ip addr show qtap + 27: qtap: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 + link/ether 8e:50:a2:fb:e1:3b brd ff:ff:ff:ff:ff:ff + inet 169.254.1.1/16 scope global qtap + valid_lft forever preferred_lft forever + +You may have to assign the interface to a firewall zone. + +The Qemu command line varies by board support package, here is an example for +the arm/xilinx_zynq_a9_qemu BSP: + +.. code-block:: none + + qemu-system-arm -serial null -serial mon:stdio -nographic \ + -M xilinx-zynq-a9 -m 256M \ + -net nic,model=cadence_gem \ + -net tap,ifname=qtap,script=no,downscript=no \ + -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/media01.exe + +Make sure that each Qemu instance uses its own MAC address to avoid an address +conflict (or otherwise use it as a test). After some seconds it will acquire a +IPv4 link-local address, for example: + +.. code-block:: none + + info: cgem0: probing for an IPv4LL address + debug: cgem0: checking for 169.254.159.156 + +You can connect to the target via telnet, for example: + +.. code-block:: none + + $ telnet 169.254.159.156 + Trying 169.254.159.156... + Connected to 169.254.159.156. + Escape character is '^]'. + + RTEMS Shell on /dev/pty4. Use 'help' to list commands. + TLNT [/] # + +Virtual Distributed Ethernet (VDE) +---------------------------------- + +You can use a Virtual Distributed Ethernet (VDE) to create a network +environment that does not need to run Qemu as root or needing to drop the tap's +privileges to run Qemu. + +VDE creates a software switch with a default of 32 ports which means a single +kernel tap can support 32 Qemu networking sessions. + +To use VDE you need to build Qemu with VDE support. The RSB can detect a VDE +plug and enable VDE support in Qemu when building. On FreeBSD install the VDE +support with: + +.. code-block:: none + + pkg install -u vde2 + +Build Qemu with the RSB. + +To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD +add to your ``/etc/rc.conf``: + +.. code-block:: none + + cloned_interfaces="bridge0 tap0" + autobridge_interfaces="bridge0" + autobridge_bridge0="re0 tap0" + ifconfig_re0="up" + ifconfig_tap0="up" + ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0" + defaultrouter="10.10.1.1" + +Start the VDE switch as root: + +.. code-block:: none + + sysctl net.link.tap.user_open=1 + sysctl net.link.tap.up_on_open=1 + vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660 + chmod 660 /dev/tap0 + +You can connect to the VDE switch's management channel using: + +.. code-block:: none + + vdeterm /tmp/mgmt1 + +To run Qemu: + +.. code-block:: none + + qemu-system-arm -serial null -serial mon:stdio -nographic \ + -M xilinx-zynq-a9 -m 256M \ + -net nic,model=cadence_gem \ + -net vde,id=vde0,sock=/tmp/vde1 + -kernel build/arm-rtems6-xilinx_zynq_a9_qemu-default/rcconf02.exe @@ -1,7 +1,10 @@ +# SPDX-License-Identifier: BSD-2-Clause +"""Manage the libbsd build configuration data. +""" + +# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. All rights reserved. # -# Copyright (c) 2015-2016 Chris Johns <chrisj@rtems.org>. All rights reserved. -# -# Copyright (c) 2009, 2017 embedded brains GmbH. All rights reserved. +# Copyright (c) 2009, 2017 embedded brains GmbH. All rights reserved. # # embedded brains GmbH # Dornierstr. 4 @@ -9,42 +12,40 @@ # Germany # <info@embedded-brains.de> # -# Copyright (c) 2012 OAR Corporation. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# FreeBSD: http://svn.freebsd.org/base/releng/8.2/sys (revision 222485) +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function -import shutil +import codecs +import copy +import difflib import os import re import sys -import getopt -import filecmp -import difflib -import codecs -import copy + +try: + import configparser +except ImportError: + import ConfigParser as configparser # # Global controls. @@ -60,31 +61,22 @@ filesTotal = 0 filesTotalLines = 0 filesTotalInserts = 0 filesTotalDeletes = 0 -diffDetails = { } +diffDetails = {} verboseInfo = 1 verboseDetail = 2 verboseMoreDetail = 3 verboseDebug = 4 -def _cflagsIncludes(cflags, includes): - if type(cflags) is not list: - if cflags is not None: - _cflags = cflags.split(' ') - else: - _cflags = [None] - else: - _cflags = cflags - if type(includes) is not list: - _includes = [includes] - else: - _includes = includes - return _cflags, _includes - -def verbose(level = verboseInfo): +BUILDSET_DIR = "buildset" +BUILDSET_DEFAULT = "buildset/default.ini" + + +def verbose(level=verboseInfo): return verboseLevel >= level -def changedFileSummary(statsReport = False): + +def changedFileSummary(statsReport=False): global filesTotal, filesTotalLines, filesTotalInserts, filesTotalDeletes @@ -107,18 +99,25 @@ def changedFileSummary(statsReport = False): # # Sort by opacity. # - ordered_diffs = sorted(diffDetails.items(), key = lambda diff: diff[1].opacity, reverse = True) + ordered_diffs = sorted(diffDetails.items(), + key=lambda diff: diff[1].opacity, + reverse=True) for f in ordered_diffs: print(' %s' % (diffDetails[f[0]].status())) + def readFile(name): try: - contents = codecs.open(name, mode = 'r', encoding = 'utf-8', errors = 'ignore').read() + contents = codecs.open(name, + mode='r', + encoding='utf-8', + errors='ignore').read() except UnicodeDecodeError as ude: print('error: reading: %s: %s' % (name, ude)) sys.exit(1) return contents + def writeFile(name, contents): path = os.path.dirname(name) if not os.path.exists(path): @@ -128,11 +127,13 @@ def writeFile(name, contents): print('error: cannot create directory: %s: %s' % (path, oe)) sys.exit(1) try: - codecs.open(name, mode = 'w', encoding = 'utf-8', errors = 'ignore').write(contents) + codecs.open(name, mode='w', encoding='utf-8', + errors='ignore').write(contents) except UnicodeDecodeError as ude: print('error: write: %s: %s' % (name, ude)) sys.exit(1) + # # A builder error. # @@ -140,11 +141,14 @@ class error(Exception): """Base class for exceptions.""" def __init__(self, msg): self.msg = 'error: %s' % (msg) + def set_output(self, msg): self.msg = msg + def __str__(self): return self.msg + # # Diff Record # @@ -158,7 +162,8 @@ class diffRecord: self.inserts = inserts self.deletes = deletes self.changes = inserts + deletes - self.opacity = (float(self.changes) / (self.lines + self.changes)) * 100.0 + self.opacity = (float(self.changes) / + (self.lines + self.changes)) * 100.0 def __repr__(self): return self.src @@ -167,36 +172,50 @@ class diffRecord: return 'opacity:%5.1f%% edits:%4d (+):%-4d (-):%-4d %s' % \ (self.opacity, self.changes, self.inserts, self.deletes, self.src) + # # This stuff needs to move to libbsd.py. # + # Move target dependent files under a machine directory def mapCPUDependentPath(path): - return path.replace("include/", "include/machine/") + return path.replace("include/", "include/machine/") + def fixIncludes(data): - data = re.sub('#include <sys/resource.h>', '#include <rtems/bsd/sys/resource.h>', data) - data = re.sub('#include <sys/unistd.h>', '#include <rtems/bsd/sys/unistd.h>', data) + data = re.sub('#include <sys/resource.h>', + '#include <rtems/bsd/sys/resource.h>', data) + data = re.sub('#include <sys/unistd.h>', + '#include <rtems/bsd/sys/unistd.h>', data) return data + # revert fixing the include paths inside a C or .h file def revertFixIncludes(data): - data = re.sub('#include <rtems/bsd/', '#include <', data) - data = re.sub('#include <util.h>', '#include <rtems/bsd/util.h>', data) - data = re.sub('#include <bsd.h>', '#include <rtems/bsd/bsd.h>', data) - data = re.sub('#include <zerocopy.h>', '#include <rtems/bsd/zerocopy.h>', data) - data = re.sub('#include <modules.h>', '#include <rtems/bsd/modules.h>', data) + data = re.sub('#include <rtems/bsd/', '#include <', data) + data = re.sub('#include <util.h>', '#include <rtems/bsd/util.h>', data) + data = re.sub('#include <bsd.h>', '#include <rtems/bsd/bsd.h>', data) + data = re.sub('#include <zerocopy.h>', '#include <rtems/bsd/zerocopy.h>', + data) + data = re.sub('#include <modules.h>', '#include <rtems/bsd/modules.h>', + data) return data + # fix include paths inside a C or .h file def fixLocalIncludes(data): - data = re.sub('#include "opt_([^"]*)"', '#include <rtems/bsd/local/opt_\\1>', data) - data = re.sub('#include "([^"]*)_if.h"', '#include <rtems/bsd/local/\\1_if.h>', data) - data = re.sub('#include "miidevs([^"]*)"', '#include <rtems/bsd/local/miidevs\\1>', data) - data = re.sub('#include "usbdevs([^"]*)"', '#include <rtems/bsd/local/usbdevs\\1>', data) + data = re.sub('#include "opt_([^"]*)"', + '#include <rtems/bsd/local/opt_\\1>', data) + data = re.sub('#include "([^"]*)_if.h"', + '#include <rtems/bsd/local/\\1_if.h>', data) + data = re.sub('#include "miidevs([^"]*)"', + '#include <rtems/bsd/local/miidevs\\1>', data) + data = re.sub('#include "usbdevs([^"]*)"', + '#include <rtems/bsd/local/usbdevs\\1>', data) return data + # revert fixing the include paths inside a C or .h file def revertFixLocalIncludes(data): data = re.sub('#include <rtems/bsd/local/([^>]*)>', '#include "\\1"', data) @@ -211,42 +230,48 @@ def assertHeaderFile(path): print("*** Move it to a C source file list") sys.exit(2) + def assertSourceFile(path): if path[-2:] != '.c' and path[-2:] != '.S' and path[-3:] != '.cc': print("*** " + path + " does not end in .c, .cc or .S") print("*** Move it to a header file list") sys.exit(2) + def assertHeaderOrSourceFile(path): if path[-2] != '.' or (path[-1] != 'h' and path[-1] != 'c'): print("*** " + path + " does not end in .h or .c") print("*** Move it to another list") sys.exit(2) + def diffSource(dstLines, srcLines, src, dst): global filesTotal, filesTotalLines, filesTotalInserts, filesTotalDeletes # # Diff, note there is no line termination on each string. Expand the # generator to list because the generator is not reusable. # - diff = list(difflib.unified_diff(dstLines, - srcLines, - fromfile = src, - tofile = dst, - n = 5, - lineterm = '')) + diff = list( + difflib.unified_diff(dstLines, + srcLines, + fromfile=src, + tofile=dst, + n=5, + lineterm='')) inserts = 0 deletes = 0 if len(diff) > 0: if src in diffDetails and \ diffDetails[src].dst != dst and diffDetails[src].diff != diff: - raise error('repeated diff of file different: src:%s dst:%s' % (src, dst)) + raise error('repeated diff of file different: src:%s dst:%s' % + (src, dst)) for l in diff: if l[0] == '-': deletes += 1 elif l[0] == '+': inserts += 1 - diffDetails[src] = diffRecord(src, dst, srcLines, diff, inserts, deletes) + diffDetails[src] = diffRecord(src, dst, srcLines, diff, inserts, + deletes) # # Count the total files, lines and the level of changes. @@ -258,6 +283,7 @@ def diffSource(dstLines, srcLines, src, dst): return diff + # # Converters provide a way to alter the various types of code. The conversion # process filters a file as it is copies from the source path to the @@ -265,8 +291,12 @@ def diffSource(dstLines, srcLines, src, dst): # source. # class Converter(object): - - def convert(self, src, dst, hasSource = True, sourceFilter = None, srcContents = None): + def convert(self, + src, + dst, + hasSource=True, + sourceFilter=None, + srcContents=None): global filesProcessed, filesProcessedCount @@ -337,10 +367,12 @@ class Converter(object): for l in diff: print(l) + class NoConverter(Converter): - def convert(self, src, dst, hasSource = True, sourceFilter = None): + def convert(self, src, dst, hasSource=True, sourceFilter=None): return '/* EMPTY */\n' + class FromFreeBSDToRTEMSHeaderConverter(Converter): def sourceFilter(self, data): data = fixLocalIncludes(data) @@ -349,7 +381,8 @@ class FromFreeBSDToRTEMSHeaderConverter(Converter): def convert(self, src, dst): sconverter = super(FromFreeBSDToRTEMSHeaderConverter, self) - sconverter.convert(src, dst, sourceFilter = self.sourceFilter) + sconverter.convert(src, dst, sourceFilter=self.sourceFilter) + class FromFreeBSDToRTEMSUserSpaceHeaderConverter(Converter): def sourceFilter(self, data): @@ -358,7 +391,8 @@ class FromFreeBSDToRTEMSUserSpaceHeaderConverter(Converter): def convert(self, src, dst): sconverter = super(FromFreeBSDToRTEMSUserSpaceHeaderConverter, self) - sconverter.convert(src, dst, sourceFilter = self.sourceFilter) + sconverter.convert(src, dst, sourceFilter=self.sourceFilter) + class FromFreeBSDToRTEMSSourceConverter(Converter): def sourceFilter(self, data): @@ -369,7 +403,8 @@ class FromFreeBSDToRTEMSSourceConverter(Converter): def convert(self, src, dst): sconverter = super(FromFreeBSDToRTEMSSourceConverter, self) - sconverter.convert(src, dst, sourceFilter = self.sourceFilter) + sconverter.convert(src, dst, sourceFilter=self.sourceFilter) + class FromFreeBSDToRTEMSUserSpaceSourceConverter(Converter): def sourceFilter(self, data): @@ -379,7 +414,8 @@ class FromFreeBSDToRTEMSUserSpaceSourceConverter(Converter): def convert(self, src, dst): sconverter = super(FromFreeBSDToRTEMSUserSpaceSourceConverter, self) - sconverter.convert(src, dst, sourceFilter = self.sourceFilter) + sconverter.convert(src, dst, sourceFilter=self.sourceFilter) + class FromRTEMSToFreeBSDHeaderConverter(Converter): def sourceFilter(self, data): @@ -389,19 +425,29 @@ class FromRTEMSToFreeBSDHeaderConverter(Converter): def convert(self, src, dst): sconverter = super(FromRTEMSToFreeBSDHeaderConverter, self) - sconverter.convert(src, dst, hasSource = False, sourceFilter = self.sourceFilter) + sconverter.convert(src, + dst, + hasSource=False, + sourceFilter=self.sourceFilter) + class FromRTEMSToFreeBSDSourceConverter(Converter): def sourceFilter(self, data): - data = re.sub('#include <machine/rtems-bsd-kernel-space.h>\n\n', '', data) - data = re.sub('#include <machine/rtems-bsd-user-space.h>\n\n', '', data) + data = re.sub('#include <machine/rtems-bsd-kernel-space.h>\n\n', '', + data) + data = re.sub('#include <machine/rtems-bsd-user-space.h>\n\n', '', + data) data = revertFixLocalIncludes(data) data = revertFixIncludes(data) return data def convert(self, src, dst): sconverter = super(FromRTEMSToFreeBSDSourceConverter, self) - sconverter.convert(src, dst, hasSource = False, sourceFilter = self.sourceFilter) + sconverter.convert(src, + dst, + hasSource=False, + sourceFilter=self.sourceFilter) + # # Compose a path based for the various parts of the source tree. @@ -413,6 +459,7 @@ class PathComposer(object): def composeLibBSDPath(self, path, prefix): return os.path.join(prefix, path) + class FreeBSDPathComposer(PathComposer): def composeOriginPath(self, path): return os.path.join(FreeBSD_DIR, path) @@ -420,6 +467,7 @@ class FreeBSDPathComposer(PathComposer): def composeLibBSDPath(self, path, prefix): return os.path.join(prefix, 'freebsd', path) + class RTEMSPathComposer(PathComposer): def composeOriginPath(self, path): return path @@ -427,6 +475,7 @@ class RTEMSPathComposer(PathComposer): def composeLibBSDPath(self, path, prefix): return os.path.join(prefix, 'rtemsbsd', path) + class LinuxPathComposer(PathComposer): def composeOriginPath(self, path): return path @@ -434,236 +483,375 @@ class LinuxPathComposer(PathComposer): def composeLibBSDPath(self, path, prefix): return os.path.join(prefix, 'linux', path) + class CPUDependentFreeBSDPathComposer(FreeBSDPathComposer): def composeLibBSDPath(self, path, prefix): - path = super(CPUDependentFreeBSDPathComposer, self).composeLibBSDPath(path, prefix) + path = super(CPUDependentFreeBSDPathComposer, + self).composeLibBSDPath(path, prefix) path = mapCPUDependentPath(path) return path + class CPUDependentRTEMSPathComposer(RTEMSPathComposer): def composeLibBSDPath(self, path, prefix): - path = super(CPUDependentRTEMSPathComposer, self).composeLibBSDPath(path, prefix) + path = super(CPUDependentRTEMSPathComposer, + self).composeLibBSDPath(path, prefix) path = mapCPUDependentPath(path) return path + class CPUDependentLinuxPathComposer(LinuxPathComposer): def composeLibBSDPath(self, path, prefix): - path = super(CPUDependentLinuxPathComposer, self).composeLibBSDPath(path, prefix) + path = super(CPUDependentLinuxPathComposer, + self).composeLibBSDPath(path, prefix) path = mapCPUDependentPath(path) return path + class TargetSourceCPUDependentPathComposer(CPUDependentFreeBSDPathComposer): def __init__(self, targetCPU, sourceCPU): self.targetCPU = targetCPU self.sourceCPU = sourceCPU def composeLibBSDPath(self, path, prefix): - path = super(TargetSourceCPUDependentPathComposer, self).composeLibBSDPath(path, prefix) + path = super(TargetSourceCPUDependentPathComposer, + self).composeLibBSDPath(path, prefix) path = path.replace(self.sourceCPU, self.targetCPU) return path -class BuildSystemFragmentComposer(object): - def __init__(self, includes = None): + +class BuildSystemComposer(object): + def __init__(self, includes=None): if type(includes) is not list: self.includes = [includes] else: self.includes = includes - def compose(self, path): + def __str__(self): return '' -class SourceFileFragmentComposer(BuildSystemFragmentComposer): - - def __init__(self, cflags = "default", includes = None): - self.cflags, self.includes = _cflagsIncludes(cflags, includes) + def getIncludes(self): + if None in self.includes: + incs = [] + else: + incs = self.includes + return incs def compose(self, path): - if None in self.includes: - flags = self.cflags + """A None result means there is nothing to build.""" + return None + + @staticmethod + def filesAsDefines(files): + define_keys = '' + for f in files: + f = f.upper() + for c in '\/-.': + f = f.replace(c, '_') + define_keys += ' ' + f + return define_keys.strip() + + @staticmethod + def cflagsIncludes(cflags, includes): + if type(cflags) is not list: + if cflags is not None: + _cflags = cflags.split(' ') + else: + _cflags = [None] + else: + _cflags = cflags + if type(includes) is not list: + _includes = [includes] else: - flags = self.cflags + self.includes - return ['sources', flags, ('default', None)], [path], self.cflags, self.includes + _includes = includes + return _cflags, _includes + + +class SourceFileBuildComposer(BuildSystemComposer): + def __init__(self, cflags="default", includes=None): + self.cflags, self.includes = self.cflagsIncludes(cflags, includes) + + def __str__(self): + return 'SF: ' + ' '.join(self.getFlags()) + + def compose(self, path): + flags = self.getFlags() + return ['sources', flags, ('default', None)], \ + [path], self.cflags, self.includes + + def getFlags(self): + return self.cflags + self.getIncludes() -class SourceFileIfHeaderComposer(SourceFileFragmentComposer): - def __init__(self, headers, cflags = "default", includes = None): +class SourceFileIfHeaderComposer(SourceFileBuildComposer): + def __init__(self, headers, cflags="default", includes=None): if headers is not list: headers = [headers] self.headers = headers - super(SourceFileIfHeaderComposer, self).__init__(cflags = cflags, includes = includes) + super(SourceFileIfHeaderComposer, self).__init__(cflags=cflags, + includes=includes) + + def __str__(self): + return 'SFIH:' + ' '.join(self.getFlags()) + \ + ' ' + self.filesAsDefines(self.headers) def compose(self, path): - r = SourceFileFragmentComposer.compose(self, path) - define_keys = '' - for h in self.headers: - h = h.upper() - for c in '\/-.': - h = h.replace(c, '_') - define_keys += ' ' + h - r[0][2] = (define_keys.strip(), self.headers) + r = SourceFileBuildComposer.compose(self, path) + r[0][2] = (self.filesAsDefines(self.headers), self.headers) return r -class TestFragementComposer(BuildSystemFragmentComposer): - def __init__(self, testName, fileFragments, configTest = None, runTest = True, netTest = False, extraLibs = []): +class TestFragementComposer(BuildSystemComposer): + def __init__(self, + testName, + fileFragments, + configTest=None, + runTest=True, + netTest=False, + extraLibs=[], + modules=[]): self.testName = testName self.fileFragments = fileFragments self.configTest = configTest self.runTest = runTest self.netTest = netTest self.extraLibs = extraLibs + self.modules = modules + + def __str__(self): + return 'TEST: ' + self.testName def compose(self, path): - return ['tests', self.testName, ('default', None)], { 'configTest': self.configTest, - 'files': self.fileFragments, - 'run': self.runTest, - 'net': self.netTest, - 'libs': self.extraLibs} + return ['tests', self.testName, ('default', None)], { + 'configTest': self.configTest, + 'files': self.fileFragments, + 'run': self.runTest, + 'net': self.netTest, + 'libs': self.extraLibs, + 'modules': self.modules, + } -class TestIfHeaderComposer(TestFragementComposer): - def __init__(self, testName, headers, fileFragments, runTest = True, netTest = False, extraLibs = []): +class TestIfHeaderComposer(TestFragementComposer): + def __init__(self, + testName, + headers, + fileFragments, + runTest=True, + netTest=False, + extraLibs=[], + modules=[]): if headers is not list: headers = [headers] self.headers = headers - super(TestIfHeaderComposer, self).__init__(testName, fileFragments, 'header', - runTest = runTest, netTest = netTest, - extraLibs = extraLibs) + super(TestIfHeaderComposer, self).__init__(testName, + fileFragments, + 'header', + runTest=runTest, + netTest=netTest, + extraLibs=extraLibs, + modules=modules) def compose(self, path): r = TestFragementComposer.compose(self, path) - define_keys = '' - for h in self.headers: - h = h.upper() - for c in '\/-.': - h = h.replace(c, '_') - define_keys += ' ' + h - r[0][2] = (define_keys.strip(), self.headers) + r[0][2] = (self.filesAsDefines(self.headers), self.headers) return r -class TestIfLibraryComposer(TestFragementComposer): - def __init__(self, testName, libraries, fileFragments, runTest = True, netTest = False, extraLibs = []): +class TestIfLibraryComposer(TestFragementComposer): + def __init__(self, + testName, + libraries, + fileFragments, + runTest=True, + netTest=False, + extraLibs=[], + modules=[]): if libraries is not list: libraries = [libraries] self.libraries = libraries - super(TestIfLibraryComposer, self).__init__(testName, fileFragments, 'library', - runTest = runTest, netTest = netTest, - extraLibs = extraLibs) + super(TestIfLibraryComposer, self).__init__(testName, + fileFragments, + 'library', + runTest=runTest, + netTest=netTest, + extraLibs=extraLibs, + modules=modules) def compose(self, path): r = TestFragementComposer.compose(self, path) - define_keys = '' - for l in self.libraries: - l = l.upper() - for c in '\/-.': - l = l.replace(c, '_') - define_keys += ' ' + l - r[0][2] = (define_keys.strip(), self.libraries) + r[0][2] = (self.filesAsDefines(self.libraries), self.libraries) return r -class KVMSymbolsFragmentComposer(BuildSystemFragmentComposer): +class KVMSymbolsBuildComposer(BuildSystemComposer): def compose(self, path): - return ['KVMSymbols', 'files', ('default', None)], [path], self.includes + return ['KVMSymbols', 'files', ('default', None)], \ + [path], self.includes -class RPCGENFragmentComposer(BuildSystemFragmentComposer): +class RPCGENBuildComposer(BuildSystemComposer): def compose(self, path): - return ['RPCGen', 'files', ('default', None)], [path] + return ['RPCGen', 'files', ('default', None)], \ + [path] -class RouteKeywordsFragmentComposer(BuildSystemFragmentComposer): +class RouteKeywordsBuildComposer(BuildSystemComposer): def compose(self, path): - return ['RouteKeywords', 'files', ('default', None)], [path] + return ['RouteKeywords', 'files', ('default', None)], \ + [path] -class LexFragmentComposer(BuildSystemFragmentComposer): - def __init__(self, sym, dep, cflags = None, includes = None, build = True): +class LexBuildComposer(BuildSystemComposer): + def __init__(self, sym, dep, cflags=None, includes=None, build=True): self.sym = sym self.dep = dep - self.cflags, self.includes = _cflagsIncludes(cflags, includes) + self.cflags, self.includes = self.cflagsIncludes(cflags, includes) self.build = build def compose(self, path): - d = { 'file': path, - 'sym': self.sym, - 'dep': self.dep, - 'build': self.build } + d = { + 'file': path, + 'sym': self.sym, + 'dep': self.dep, + 'build': self.build + } if None not in self.cflags: d['cflags'] = self.cflags if None not in self.includes: d['includes'] = self.includes return ['lex', path, ('default', None)], d -class YaccFragmentComposer(BuildSystemFragmentComposer): - def __init__(self, sym, header, cflags = None, includes = None, build = True): +class YaccBuildComposer(BuildSystemComposer): + def __init__(self, sym, header, cflags=None, includes=None, build=True): self.sym = sym self.header = header - self.cflags, self.includes = _cflagsIncludes(cflags, includes) + self.cflags, self.includes = self.cflagsIncludes(cflags, includes) self.build = build def compose(self, path): - d = { 'file': path, - 'sym': self.sym, - 'header': self.header, - 'build': self.build } + d = { + 'file': path, + 'sym': self.sym, + 'header': self.header, + 'build': self.build + } if None not in self.cflags: d['cflags'] = self.cflags if None not in self.includes: d['includes'] = self.includes return ['yacc', path, ('default', None)], d -# -# File - a file in the source we move backwards and forwards. -# + class File(object): - def __init__(self, path, pathComposer, - forwardConverter, reverseConverter, buildSystemComposer): + '''A file of source we move backwards and forwards and build.''' + def __init__(self, space, path, pathComposer, forwardConverter, + reverseConverter, buildSystemComposer): if verbose(verboseMoreDetail): - print("FILE: %-50s F:%-45s R:%-45s" % \ - (path, + print("FILE: %-6s %-50s F:%-45s R:%-45s" % \ + (space, path, forwardConverter.__class__.__name__, reverseConverter.__class__.__name__)) + self.space = space self.path = path self.pathComposer = pathComposer self.originPath = self.pathComposer.composeOriginPath(self.path) - self.libbsdPath = self.pathComposer.composeLibBSDPath(self.path, LIBBSD_DIR) + self.libbsdPath = self.pathComposer.composeLibBSDPath( + self.path, LIBBSD_DIR) self.forwardConverter = forwardConverter self.reverseConverter = reverseConverter self.buildSystemComposer = buildSystemComposer + def __str__(self): + out = self.space[0].upper() + ' ' + self.path + bsc = str(self.buildSystemComposer) + if len(bsc) > 0: + out += ' (' + bsc + ')' + return out + + def __eq__(self, other): + state = self.space == other.space + state = state and (self.path == self.path) + state = state and (self.pathComposer == self.pathComposer) + state = state and (self.originPath == self.originPath) + state = state and (self.forwardConverter == self.forwardConverter) + state = state and (self.self.reverseConverter + == self.self.reverseConverter) + state = state and (self.buildSystemComposer + == self.buildSystemComposer) + return state + def processSource(self, forward): if forward: if verbose(verboseDetail): - print("process source: %s => %s" % (self.originPath, self.libbsdPath)) + print("process source: %s => %s" % + (self.originPath, self.libbsdPath)) self.forwardConverter.convert(self.originPath, self.libbsdPath) else: if verbose(verboseDetail): print("process source: %s => %s converter:%s" % \ - (self.libbsdPath, self.originPath, self.reverseConverter.__class__.__name__)) + (self.libbsdPath, self.originPath, + self.reverseConverter.__class__.__name__)) self.reverseConverter.convert(self.libbsdPath, self.originPath) def getFragment(self): - return self.buildSystemComposer.compose(self.pathComposer.composeLibBSDPath(self.path, '')) + return self.buildSystemComposer.compose( + self.pathComposer.composeLibBSDPath(self.path, '')) + + def getPath(self): + return self.path + + def getSpace(self): + return self.space + -# -# Module - logical group of related files we can perform actions on -# class Module(object): - def __init__(self, manager, name, enabled = True): + '''Logical group of related files we can perform actions on''' + def __init__(self, manager, name, enabled=True): self.manager = manager self.name = name - self.conditionalOn = "none" self.files = [] self.cpuDependentSourceFiles = {} self.dependencies = [] + def __str__(self): + out = [self.name + ':'] + if len(self.dependencies) > 0: + out += [' Deps: ' + str(len(self.dependencies))] + out += [' ' + type(d).__name__ for d in self.dependencies] + if len(self.files) > 0: + counts = {} + for f in self.files: + space = f.getSpace() + if space not in counts: + counts[space] = 0 + counts[space] += 1 + count_str = '' + for space in sorted(counts.keys()): + count_str += '%s:%d ' % (space[0].upper(), counts[space]) + count_str = count_str[:-1] + out += [' Files: %d (%s)' % (len(self.files), count_str)] + out += [' ' + str(f) for f in self.files] + if len(self.cpuDependentSourceFiles) > 0: + out += [' CPU Dep: ' + str(len(self.cpuDependentSourceFiles))] + for cpu in self.cpuDependentSourceFiles: + out += [' ' + cpu + ':'] + out += [ + ' ' + str(f) for f in self.cpuDependentSourceFiles[cpu] + ] + return os.linesep.join(out) + def initCPUDependencies(self, cpu): if cpu not in self.cpuDependentSourceFiles: self.cpuDependentSourceFiles[cpu] = [] + def getName(self): + return self.name + + def getFiles(self): + return (f for f in self.files) + def processSource(self, direction): if verbose(verboseDetail): print("process module: %s" % (self.name)) @@ -673,135 +861,156 @@ class Module(object): for f in files: f.processSource(direction) - def addFiles(self, newFiles, buildSystemComposer = BuildSystemFragmentComposer()): - files = [] - for newFile in newFiles: - assertFile(newFile) - files += [File(newFile, composers, buildSystemComposer)] - return files - def addFile(self, f): + if not isinstance(f, File): + raise TypeError('invalid type for addFiles: %s' % (type(f))) self.files += [f] - def addFiles(self, newFiles, - pathComposer, forwardConverter, reverseConverter, - assertFile, buildSystemComposer = BuildSystemFragmentComposer()): + def addFiles(self, + space, + newFiles, + pathComposer, + forwardConverter, + reverseConverter, + assertFile, + buildSystemComposer=BuildSystemComposer()): files = [] for newFile in newFiles: assertFile(newFile) - files += [File(newFile, pathComposer, forwardConverter, - reverseConverter, buildSystemComposer)] + files += [ + File(space, newFile, pathComposer, forwardConverter, + reverseConverter, buildSystemComposer) + ] return files - def addPlainTextFile(self, files): - self.files += self.addFiles(files, + def addPlainTextFiles(self, files): + self.files += self.addFiles('user', files, FreeBSDPathComposer(), Converter(), Converter(), assertNothing) def addKernelSpaceHeaderFiles(self, files): - self.files += self.addFiles(files, - FreeBSDPathComposer(), FromFreeBSDToRTEMSHeaderConverter(), - FromRTEMSToFreeBSDHeaderConverter(), assertHeaderOrSourceFile) + self.files += self.addFiles('kernel', files, FreeBSDPathComposer(), + FromFreeBSDToRTEMSHeaderConverter(), + FromRTEMSToFreeBSDHeaderConverter(), + assertHeaderOrSourceFile) def addUserSpaceHeaderFiles(self, files): - self.files += self.addFiles(files, - FreeBSDPathComposer(), FromFreeBSDToRTEMSUserSpaceHeaderConverter(), - FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile) + self.files += self.addFiles( + 'user', files, FreeBSDPathComposer(), + FromFreeBSDToRTEMSUserSpaceHeaderConverter(), + FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile) def addRTEMSHeaderFiles(self, files): - self.files += self.addFiles(files, RTEMSPathComposer(), - NoConverter(), NoConverter(), assertHeaderFile) + self.files += self.addFiles('user', files, RTEMSPathComposer(), + NoConverter(), NoConverter(), + assertHeaderFile) def addLinuxHeaderFiles(self, files): - self.files += self.addFiles(files, - PathComposer(), NoConverter(), - NoConverter(), assertHeaderFile) + self.files += self.addFiles('kernel', files, PathComposer(), + NoConverter(), NoConverter(), + assertHeaderFile) def addCPUDependentFreeBSDHeaderFiles(self, files): - self.files += self.addFiles(files, - CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSHeaderConverter(), - FromRTEMSToFreeBSDHeaderConverter(), assertHeaderFile) + self.files += self.addFiles('kernel', files, + CPUDependentFreeBSDPathComposer(), + FromFreeBSDToRTEMSHeaderConverter(), + FromRTEMSToFreeBSDHeaderConverter(), + assertHeaderFile) def addCPUDependentLinuxHeaderFiles(self, files): - self.files += self.addFiles(files, - CPUDependentLinuxPathComposer(), NoConverter(), - NoConverter(), assertHeaderFile) + self.files += self.addFiles('kernel', files, + CPUDependentLinuxPathComposer(), + NoConverter(), NoConverter(), + assertHeaderFile) - def addTargetSourceCPUDependentHeaderFiles(self, targetCPUs, sourceCPU, files): + def addTargetSourceCPUDependentHeaderFiles(self, targetCPUs, sourceCPU, + files): for cpu in targetCPUs: - self.files += self.addFiles(files, - TargetSourceCPUDependentPathComposer(cpu, sourceCPU), - FromFreeBSDToRTEMSHeaderConverter(), - NoConverter(), assertHeaderFile) - - def addSourceFiles(self, files, sourceFileFragmentComposer): - self.files += self.addFiles(files, - PathComposer(), NoConverter(), NoConverter(), assertSourceFile, - sourceFileFragmentComposer) - - def addKernelSpaceSourceFiles(self, files, sourceFileFragmentComposer): - self.files += self.addFiles(files, - FreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(), - FromRTEMSToFreeBSDSourceConverter(), assertSourceFile, - sourceFileFragmentComposer) - - def addUserSpaceSourceFiles(self, files, sourceFileFragmentComposer): - self.files += self.addFiles(files, - FreeBSDPathComposer(), - FromFreeBSDToRTEMSUserSpaceSourceConverter(), - FromRTEMSToFreeBSDSourceConverter(), assertSourceFile, - sourceFileFragmentComposer) - - def addRTEMSSourceFiles(self, files, sourceFileFragmentComposer): - self.files += self.addFiles(files, - RTEMSPathComposer(), NoConverter(), NoConverter(), - assertSourceFile, sourceFileFragmentComposer) - - def addLinuxSourceFiles(self, files, sourceFileFragmentComposer): - self.files += self.addFiles(files, - PathComposer(), NoConverter(), + self.files += self.addFiles( + 'kernel', files, + TargetSourceCPUDependentPathComposer(cpu, sourceCPU), + FromFreeBSDToRTEMSHeaderConverter(), NoConverter(), + assertHeaderFile) + + def addSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles('user', + files, PathComposer(), NoConverter(), NoConverter(), assertSourceFile, - sourceFileFragmentComposer) - - def addCPUDependentFreeBSDSourceFiles(self, cpus, files, sourceFileFragmentComposer): + sourceFileBuildComposer) + + def addKernelSpaceSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles('kernel', files, FreeBSDPathComposer(), + FromFreeBSDToRTEMSSourceConverter(), + FromRTEMSToFreeBSDSourceConverter(), + assertSourceFile, sourceFileBuildComposer) + + def addUserSpaceSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles( + 'user', files, FreeBSDPathComposer(), + FromFreeBSDToRTEMSUserSpaceSourceConverter(), + FromRTEMSToFreeBSDSourceConverter(), assertSourceFile, + sourceFileBuildComposer) + + def addRTEMSKernelSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles('kernel', files, RTEMSPathComposer(), + NoConverter(), NoConverter(), + assertSourceFile, sourceFileBuildComposer) + + def addRTEMSUserSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles('user', files, RTEMSPathComposer(), + NoConverter(), NoConverter(), + assertSourceFile, sourceFileBuildComposer) + + def addLinuxSourceFiles(self, files, sourceFileBuildComposer): + self.files += self.addFiles('kernel', files, PathComposer(), + NoConverter(), NoConverter(), + assertSourceFile, sourceFileBuildComposer) + + def addCPUDependentFreeBSDSourceFiles(self, cpus, files, + sourceFileBuildComposer): for cpu in cpus: self.initCPUDependencies(cpu) self.cpuDependentSourceFiles[cpu] += \ - self.addFiles(files, - CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(), - FromRTEMSToFreeBSDSourceConverter(), assertSourceFile, - sourceFileFragmentComposer) - - def addCPUDependentRTEMSSourceFiles(self, cpus, files, sourceFileFragmentComposer): + self.addFiles( + 'kernel', files, + CPUDependentFreeBSDPathComposer(), FromFreeBSDToRTEMSSourceConverter(), + FromRTEMSToFreeBSDSourceConverter(), assertSourceFile, + sourceFileBuildComposer) + + def addCPUDependentRTEMSSourceFiles(self, cpus, files, + sourceFileBuildComposer): for cpu in cpus: self.initCPUDependencies(cpu) self.cpuDependentSourceFiles[cpu] += \ - self.addFiles(files, + self.addFiles('kernel', files, CPUDependentRTEMSPathComposer(), NoConverter(), NoConverter(), assertSourceFile, - sourceFileFragmentComposer) + sourceFileBuildComposer) - def addCPUDependentLinuxSourceFiles(self, cpus, files, sourceFileFragmentComposer): + def addCPUDependentLinuxSourceFiles(self, cpus, files, + sourceFileBuildComposer): for cpu in cpus: self.initCPUDependencies(cpu) self.cpuDependentSourceFiles[cpu] += \ - self.addFiles(files, + self.addFiles('kernel', files, CPUDependentLinuxPathComposer(), NoConverter(), NoConverter(), assertSourceFile, - sourceFileFragmentComposer) + sourceFileBuildComposer) def addTest(self, testFragementComposer): - self.files += [File(testFragementComposer.testName, - PathComposer(), NoConverter(), NoConverter(), - testFragementComposer)] + self.files += [ + File('user', testFragementComposer.testName, PathComposer(), + NoConverter(), NoConverter(), testFragementComposer) + ] def addDependency(self, dep): + if not isinstance(dep, str): + raise TypeError('dependencies are a string: %s' % (self.name)) self.dependencies += [dep] -# -# Manager - a collection of modules. -# + class ModuleManager(object): + '''A manager for a collection of modules.''' def __init__(self): self.modules = {} self.generator = {} @@ -813,18 +1022,82 @@ class ModuleManager(object): raise KeyError('module %s not found' % (key)) return self.modules[key] + def __str__(self): + out = ['Modules: ' + str(len(self.modules)), ''] + for m in sorted(self.modules): + out += [str(self.modules[m]), ''] + return os.linesep.join(out) + + def _loadIni(self, ini_file): + if not os.path.exists(ini_file): + raise FileNotFoundError('file not found: %s' % (ini_file)) + ini = configparser.ConfigParser() + ini.read(ini_file) + if not ini.has_section('general'): + raise Exception( + "'{}' is missing a general section.".format(ini_file)) + if not ini.has_option('general', 'name'): + raise Exception("'{}' is missing a general/name.".format(ini_file)) + if ini.has_option('general', 'extends'): + extends = ini.get('general', 'extends') + extendfile = None + basepath = os.path.dirname(ini_file) + if os.path.isfile(os.path.join(basepath, extends)): + extendfile = os.path.join(basepath, extends) + elif os.path.isfile(os.path.join(BUILDSET_DIR, extends)): + extendfile = os.path.join(BUILDSET_DIR, extends) + else: + raise Exception( + "'{}': Invalid file given for general/extends:'{}'".format( + ini_file, extends)) + base = self._loadIni(extendfile) + for s in ini.sections(): + if not base.has_section(s): + base.add_section(s) + for o in ini.options(s): + val = ini.get(s, o) + base.set(s, o, val) + ini = base + return ini + + def _checkDependencies(self): + enabled_modules = self.getEnabledModules() + if 'tests' in enabled_modules: + enabled_modules.remove('tests') + for mod in enabled_modules: + if mod not in self.modules: + raise KeyError('enabled module not found: %s' % (mod)) + for dep in self.modules[mod].dependencies: + if dep not in self.modules: + print(type(dep)) + raise KeyError('dependent module not found: %s' % (dep)) + if dep not in enabled_modules: + raise Exception('module "%s" dependency "%s" not enabled' % + (mod, dep)) + def getAllModules(self): if 'modules' in self.configuration: - return self.configuration['modules'] + return sorted(self.configuration['modules']) return [] def getEnabledModules(self): if 'modules-enabled' in self.configuration: - return self.configuration['modules-enabled'] + return sorted(self.configuration['modules-enabled']) return [] def addModule(self, module): - self.modules[module.name] = module + name = module.name + if name in self.modules: + raise KeyError('module already added: %' % (name)) + self.modules[name] = module + if 'modules' not in self.configuration: + self.configuration['modules'] = [] + if 'modules-enabled' not in self.configuration: + self.configuration['modules-enabled'] = [] + self.configuration['modules'] += [name] + self.configuration['modules-enabled'] += [name] + self.configuration['modules'].sort() + self.configuration['modules-enabled'].sort def processSource(self, direction): if verbose(verboseDetail): @@ -836,16 +1109,7 @@ class ModuleManager(object): self.configuration = copy.deepcopy(config) def getConfiguration(self): - return self.configuration - - def updateConfiguration(self, config): - self.configuration.update(config) - - def setModuleConfigiuration(self): - mods = sorted(self.modules.keys()) - self.configuration['modules'] = mods - # Enabled modules are overwritten by config file. Default to all. - self.configuration['modules-enabled'] = mods + return copy.deepcopy(self.configuration) def generateBuild(self, only_enabled=True): modules_to_process = self.getEnabledModules() @@ -853,14 +1117,53 @@ class ModuleManager(object): if only_enabled == False: modules_to_process = self.getAllModules() for m in modules_to_process: + if m not in self.modules: + raise KeyError('enabled module not registered: %s' % (m)) self.modules[m].generate() + self._checkDependencies() + + def duplicateCheck(self): + dups = [] + modules_to_check = sorted(self.getAllModules(), reverse=True) + while len(modules_to_check) > 1: + mod = modules_to_check.pop() + for m in modules_to_check: + if m not in self.modules: + raise KeyError('enabled module not registered: %s' % (m)) + for fmod in self.modules[mod].getFiles(): + for fm in self.modules[m].getFiles(): + if fmod.getPath() == fm.getPath(): + dups += [(m, mod, fm.getPath(), fm.getSpace())] + return dups + + def loadConfig(self, config=BUILDSET_DEFAULT): + if 'name' in self.configuration: + raise KeyError('configuration already loaded: %s (%s)' % \ + (self.configuration['name'], config)) + ini = self._loadIni(config) + self.configuration['name'] = ini.get('general', 'name') + self.configuration['modules-enabled'] = [] + mods = [] + if ini.has_section('modules'): + mods = ini.options('modules') + for mod in mods: + if ini.getboolean('modules', mod): + self.configuration['modules-enabled'].append(mod) + + def getName(self): + if 'name' not in self.configuration: + raise KeyError('configuration not loaded') + return self.configuration['name'] def setGenerators(self): self.generator['convert'] = Converter self.generator['no-convert'] = NoConverter - self.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'] = FromFreeBSDToRTEMSUserSpaceSourceConverter - self.generator['from-RTEMS-To-FreeBSD-SourceConverter'] = FromRTEMSToFreeBSDSourceConverter - self.generator['buildSystemFragmentComposer'] = BuildSystemFragmentComposer + self.generator[ + 'from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'] = \ + FromFreeBSDToRTEMSUserSpaceSourceConverter + self.generator[ + 'from-RTEMS-To-FreeBSD-SourceConverter'] = FromRTEMSToFreeBSDSourceConverter + self.generator['buildSystemComposer'] = BuildSystemComposer self.generator['file'] = File @@ -868,15 +1171,16 @@ class ModuleManager(object): self.generator['freebsd-path'] = FreeBSDPathComposer self.generator['rtems-path'] = RTEMSPathComposer self.generator['cpu-path'] = CPUDependentFreeBSDPathComposer - self.generator['target-src-cpu--path'] = TargetSourceCPUDependentPathComposer + self.generator[ + 'target-src-cpu--path'] = TargetSourceCPUDependentPathComposer - self.generator['source'] = SourceFileFragmentComposer + self.generator['source'] = SourceFileBuildComposer self.generator['test'] = TestFragementComposer - self.generator['kvm-symbols'] = KVMSymbolsFragmentComposer - self.generator['rpc-gen'] = RPCGENFragmentComposer - self.generator['route-keywords'] = RouteKeywordsFragmentComposer - self.generator['lex'] = LexFragmentComposer - self.generator['yacc'] = YaccFragmentComposer + self.generator['kvm-symbols'] = KVMSymbolsBuildComposer + self.generator['rpc-gen'] = RPCGENBuildComposer + self.generator['route-keywords'] = RouteKeywordsBuildComposer + self.generator['lex'] = LexBuildComposer + self.generator['yacc'] = YaccBuildComposer self.generator['source-if-header'] = SourceFileIfHeaderComposer self.generator['test-if-header'] = TestIfHeaderComposer diff --git a/buildset/default.ini b/buildset/default.ini index 741357fb..88379a96 100644 --- a/buildset/default.ini +++ b/buildset/default.ini @@ -43,6 +43,7 @@ dhcpcd = on dpaa = on evdev = on fdt = on +if_mve = on imx = on in_cksum = on mdnsresponder = on @@ -53,11 +54,13 @@ net80211 = off netinet = on netinet6 = on netipsec = off +nfsv2 = on nvme = on opencrypto = on pci = on pf = on regulator = on +rpc_user = on rtems = on tests = on tty = on diff --git a/dhcpcd/dhcpcd.c b/dhcpcd/dhcpcd.c index b7839d49..93620727 100644 --- a/dhcpcd/dhcpcd.c +++ b/dhcpcd/dhcpcd.c @@ -1155,7 +1155,7 @@ dhcpcd_task(rtems_task_argument arg) (*config->destroy)(config, exit_code); } - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } rtems_status_code diff --git a/freebsd-org b/freebsd-org -Subproject 6b0307a0a5184339393f555d5d424190d8a8277 +Subproject 5d85e12f44ccb0e5728344a002c108ddc105e03 diff --git a/freebsd-to-rtems.py b/freebsd-to-rtems.py index 3f06bdf9..cda3ff3a 100755 --- a/freebsd-to-rtems.py +++ b/freebsd-to-rtems.py @@ -47,6 +47,7 @@ import libbsd isForward = True isEarlyExit = False statsReport = False +isConfig = False def usage(): print("freebsd-to-rtems.py [args]") @@ -59,14 +60,15 @@ def usage(): print(" -R|--reverse default origin -> LibBSD, reverse that") print(" -r|--rtems LibBSD directory (default: '.')") print(" -f|--freebsd FreeBSD origin directory (default: 'freebsd-org')") + print(" -c|--config Output the configuration then exit") print(" -v|--verbose enable verbose output mode") # Parse the arguments def parseArguments(): - global isForward, isEarlyExit, statsReport + global isForward, isEarlyExit, statsReport, isConfig try: opts, args = getopt.getopt(sys.argv[1:], - "?hdDembSRr:f:v", + "?hdDembSRr:f:cv", [ "help", "help", "dry-run" @@ -78,6 +80,7 @@ def parseArguments(): "stats" "rtems=" "freebsd=" + "config" "verbose" ]) except getopt.GetoptError as err: # print help information and exit: @@ -104,6 +107,8 @@ def parseArguments(): builder.LIBBSD_DIR = a elif o in ("-f", "--freebsd"): builder.FreeBSD_DIR = a + elif o in ("-c", "--config"): + isConfig = True else: assert False, "unhandled option" @@ -127,24 +132,44 @@ def wasDirectorySet(desc, path): print("error:" + desc + " Directory (" + path + ") does not exist") sys.exit(2) -# Were directories specified? -wasDirectorySet( "LibBSD", builder.LIBBSD_DIR ) -wasDirectorySet( "FreeBSD", builder.FreeBSD_DIR ) +try: + if not isConfig: + # Were directories specified? + wasDirectorySet( "LibBSD", builder.LIBBSD_DIR ) + wasDirectorySet( "FreeBSD", builder.FreeBSD_DIR ) -# Are we generating or reverting? -if isForward == True: - print("Forward from", builder.FreeBSD_DIR, "into", builder.LIBBSD_DIR) -else: - print("Reverting from", builder.LIBBSD_DIR) + # Are we generating or reverting? + if isForward == True: + print("Forward from", builder.FreeBSD_DIR, "into", builder.LIBBSD_DIR) + else: + print("Reverting from", builder.LIBBSD_DIR) -if isEarlyExit == True: - print("Early exit at user request") - sys.exit(0) + if isEarlyExit == True: + print("Early exit at user request") + sys.exit(0) -try: build = builder.ModuleManager() libbsd.load(build) + build.loadConfig() build.generateBuild(only_enabled=False) + + dups = build.duplicateCheck() + if len(dups) > 0: + print() + print('Duplicates: %d' % (len(dups))) + mods = list(set([dup[0] for dup in dups])) + max_mod_len = max(len(dup[1]) for dup in dups) + for mod in mods: + print(' %s:' % (mod)) + for dup in [dup for dup in dups if dup[0] == mod]: + print(' %-*s %s %s' % (max_mod_len, dup[1], dup[3][0].upper(), dup[2])) + print() + + if isConfig: + print() + print(build) + sys.exit(0) + build.processSource(isForward) builder.changedFileSummary(statsReport) except IOError as ioe: diff --git a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h index d021ae99..2c8ff3b8 100644 --- a/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h +++ b/freebsd/contrib/tcpdump/rtems-bsd-tcpdump-data.h @@ -139,7 +139,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern int nd_smi_module_loaded); /* print-sll.c */ /* print-slow.c */ /* print-smb.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern u_char const *startbuf); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_tcpdump, extern unsigned char const *startbuf); /* print-smtp.c */ /* print-snmp.c */ /* print-stp.c */ diff --git a/freebsd/contrib/tcpdump/tcpdump.c b/freebsd/contrib/tcpdump/tcpdump.c index 3b68ed51..24c6a29c 100644 --- a/freebsd/contrib/tcpdump/tcpdump.c +++ b/freebsd/contrib/tcpdump/tcpdump.c @@ -143,6 +143,7 @@ The Regents of the University of California. All rights reserved.\n"; #include <sys/sysctl.h> #include <machine/rtems-bsd-commands.h> #include <assert.h> +#include <sched.h> #include <rtems.h> #include <rtems/linkersets.h> #define setpriority(a, b, c) @@ -210,8 +211,10 @@ cap_channel_t *capdns; static void error(FORMAT_STRING(const char *), ...) NORETURN PRINTFLIKE(1, 2); static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2); static void exit_tcpdump(int) NORETURN; +#ifndef __rtems__ static RETSIGTYPE cleanup(int); static RETSIGTYPE child_cleanup(int); +#endif /* __rtems__ */ static void print_version(void); static void print_usage(void); static void show_tstamp_types_and_exit(pcap_t *, const char *device) NORETURN; @@ -223,6 +226,7 @@ static void show_devices_and_exit (void) NORETURN; static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); +#ifndef __rtems__ static void droproot(const char *, const char *); #ifdef SIGNAL_REQ_INFO @@ -236,6 +240,7 @@ RETSIGTYPE requestinfo(int); #elif defined(HAVE_ALARM) static void verbose_stats_dump(int sig); #endif +#endif /* __rtems__ */ static void info(int); static u_int packets_captured; @@ -627,6 +632,7 @@ static const struct option longopts[] = { { NULL, 0, NULL, 0 } }; +#ifndef __rtems__ #ifndef _WIN32 /* Drop root privileges and chroot if necessary */ static void @@ -659,7 +665,6 @@ droproot(const char *username, const char *chroot_dir) } } #else -#ifndef __rtems__ if (initgroups(pw->pw_name, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { fprintf(stderr, "%s: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", @@ -672,7 +677,6 @@ droproot(const char *username, const char *chroot_dir) else { fprintf(stderr, "dropped privs to %s\n", username); } -#endif /* __rtems__ */ #endif /* HAVE_LIBCAP_NG */ } else { @@ -694,6 +698,7 @@ droproot(const char *username, const char *chroot_dir) } #endif /* _WIN32 */ +#endif /* __rtems__ */ static int getWflagChars(int x) @@ -1219,26 +1224,22 @@ typedef struct { FILE *in; pcap_t *pd; rtems_id master; + bool terminate; } pcap_loop_context; static void pcap_loop_monitor(rtems_task_argument arg) { - pcap_loop_context *ctx; + const pcap_loop_context *ctx; FILE *in; pcap_t *pd; - rtems_id master; rtems_status_code sc; - ctx = (pcap_loop_context *)arg; + ctx = (const pcap_loop_context *)arg; in = ctx->in; pd = ctx->pd; - master = ctx->master; - - sc = rtems_event_transient_send(master); - assert(sc == RTEMS_SUCCESSFUL); - while (true) { + while (!ctx->terminate) { int c; c = fgetc(in); @@ -1247,19 +1248,22 @@ pcap_loop_monitor(rtems_task_argument arg) pcap_breakloop(pd); break; } + + sched_yield(); } - rtems_task_delete(RTEMS_SELF); - assert(0); + sc = rtems_event_transient_send(ctx->master); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_task_exit(); } -static int -pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud) +static void +pcap_create_loop_monitor(pcap_loop_context *ctx, pcap_t *pd) { rtems_status_code sc; rtems_task_priority priority; rtems_id id; - pcap_loop_context ctx; sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); @@ -1269,27 +1273,38 @@ pcap_loop_wrapper(pcap_t *pd, int cnt, pcap_handler cb, u_char *ud) RTEMS_MINIMUM_STACK_SIZE, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id); if (sc != RTEMS_SUCCESSFUL) { - fprintf(stderr, "tcpdump: cannot create helper thread: %s\n", + error("cannot create pcap loop monitor thread: %s\n", rtems_status_text(sc)); - return (-1); } fprintf(stdout, "tcpdump: press <ENTER> or 'q' or 'Q' to quit\n"); - ctx.in = stdin; - ctx.pd = pd; - ctx.master = rtems_task_self(); + ctx->in = stdin; + ctx->pd = pd; + ctx->master = rtems_task_self(); + ctx->terminate = false; sc = rtems_task_start(id, pcap_loop_monitor, - (rtems_task_argument)&ctx); + (rtems_task_argument)ctx); assert(sc == RTEMS_SUCCESSFUL); +} + +static void +pcap_terminate_loop_monitor(pcap_loop_context *ctx) +{ + rtems_status_code sc; + + ctx->terminate = true; sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); assert(sc == RTEMS_SUCCESSFUL); - - return (pcap_loop(pd, cnt, cb, ud)); } -#define pcap_loop(pd, cnt, cb, ud) pcap_loop_wrapper(pd, cnt, cb, ud) +static void +destroy_pcap_dumper(void *arg) +{ + + pcap_dump_close(arg); +} #endif /* __rtems__ */ int #ifndef __rtems__ @@ -1306,15 +1321,19 @@ main(int argc, char **argv) int dlt; const char *dlt_name; struct bpf_program fcode; +#ifndef __rtems__ #ifndef _WIN32 RETSIGTYPE (*oldhandler)(int); #endif +#endif /* __rtems__ */ struct dump_info dumpinfo; u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; char VFileLine[PATH_MAX + 1]; +#ifndef __rtems__ char *username = NULL; char *chroot_dir = NULL; +#endif /* __rtems__ */ char *ret = NULL; char *end; #ifdef HAVE_PCAP_FINDALLDEVS @@ -1686,9 +1705,11 @@ main(int argc, char **argv) zflag = optarg; break; +#ifndef __rtems__ case 'Z': username = optarg; break; +#endif /* __rtems__ */ case '#': ndo->ndo_packet_number = 1; @@ -1985,6 +2006,7 @@ main(int argc, char **argv) init_print(ndo, localnet, netmask, timezone_offset); +#ifndef __rtems__ #ifndef _WIN32 (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); @@ -2052,6 +2074,7 @@ main(int argc, char **argv) } #endif /* _WIN32 */ +#endif /* __rtems__ */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); @@ -2146,6 +2169,12 @@ main(int argc, char **argv) if (Uflag) pcap_dump_flush(p); #endif +#ifdef __rtems__ + if (rtems_bsd_program_add_destructor(destroy_pcap_dumper, p) == + NULL) { + error("cannot add destructor"); + } +#endif /* __rtems__ */ } else { dlt = pcap_datalink(pd); ndo->ndo_if_printer = get_if_printer(ndo, dlt); @@ -2153,6 +2182,7 @@ main(int argc, char **argv) pcap_userdata = (u_char *)ndo; } +#ifndef __rtems__ #ifdef SIGNAL_REQ_INFO /* * We can't get statistics when reading from a file rather @@ -2177,6 +2207,7 @@ main(int argc, char **argv) alarm(1); #endif } +#endif /* __rtems__ */ if (RFileName == NULL) { /* @@ -2219,7 +2250,19 @@ main(int argc, char **argv) #endif /* HAVE_CAPSICUM */ do { +#ifdef __rtems__ + pcap_loop_context ctx; + + if (RFileName == NULL) { + pcap_create_loop_monitor(&ctx, pd); + } +#endif /* __rtems__ */ status = pcap_loop(pd, cnt, callback, pcap_userdata); +#ifdef __rtems__ + if (RFileName == NULL) { + pcap_terminate_loop_monitor(&ctx); + } +#endif /* __rtems__ */ if (WFileName == NULL) { /* * We're printing packets. Flush the printed output, @@ -2338,6 +2381,7 @@ main(int argc, char **argv) exit_tcpdump(status == -1 ? 1 : 0); } +#ifndef __rtems__ /* make a clean exit on interrupts */ static RETSIGTYPE cleanup(int signo _U_) @@ -2390,6 +2434,7 @@ child_cleanup(int signo _U_) wait(NULL); } #endif /* HAVE_FORK && HAVE_VFORK */ +#endif /* __rtems__ */ static void info(register int verbose) @@ -2750,6 +2795,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) char Wpcap_version[]="3.1"; #endif +#ifndef __rtems__ #ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int signo _U_) { @@ -2778,6 +2824,7 @@ static void verbose_stats_dump(int sig _U_) alarm(1); } #endif +#endif /* __rtems__ */ USES_APPLE_DEPRECATED_API static void diff --git a/freebsd/contrib/wpa/src/crypto/md5.h b/freebsd/contrib/wpa/src/crypto/md5.h index 33f8426c..48afb955 100644 --- a/freebsd/contrib/wpa/src/crypto/md5.h +++ b/freebsd/contrib/wpa/src/crypto/md5.h @@ -11,6 +11,10 @@ #define MD5_MAC_LEN 16 +#ifdef __rtems__ +#define hmac_md5_vector wpa_hmac_md5_vector +#define hmac_md5 wpa_hmac_md5 +#endif /* __rtems__ */ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, diff --git a/freebsd/contrib/wpa/src/utils/eloop.c b/freebsd/contrib/wpa/src/utils/eloop.c index 41de0f79..09493b89 100644 --- a/freebsd/contrib/wpa/src/utils/eloop.c +++ b/freebsd/contrib/wpa/src/utils/eloop.c @@ -16,6 +16,9 @@ #include "list.h" #include "eloop.h" +#ifdef __rtems__ +#define CONFIG_ELOOP_KQUEUE +#endif /* __rtems__ */ #if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL) #error Do not define both of poll and epoll #endif @@ -955,6 +958,7 @@ int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, } +#ifndef __rtems__ #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { @@ -966,8 +970,10 @@ static void eloop_handle_alarm(int sig) exit(1); } #endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* __rtems__ */ +#ifndef __rtems__ static void eloop_handle_signal(int sig) { int i; @@ -990,6 +996,7 @@ static void eloop_handle_signal(int sig) } } } +#endif /* __rtems__ */ static void eloop_process_pending_signals(void) @@ -1001,9 +1008,11 @@ static void eloop_process_pending_signals(void) eloop.signaled = 0; if (eloop.pending_terminate) { +#ifndef __rtems__ #ifndef CONFIG_NATIVE_WINDOWS alarm(0); #endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* __rtems__ */ eloop.pending_terminate = 0; } @@ -1017,6 +1026,7 @@ static void eloop_process_pending_signals(void) } +#ifndef __rtems__ int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { @@ -1037,26 +1047,35 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, return 0; } +#endif /* __rtems__ */ int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data) { +#ifndef __rtems__ int ret = eloop_register_signal(SIGINT, handler, user_data); if (ret == 0) ret = eloop_register_signal(SIGTERM, handler, user_data); return ret; +#else /* __rtems__ */ + return 0; +#endif /* __rtems__ */ } int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data) { +#ifndef __rtems__ #ifdef CONFIG_NATIVE_WINDOWS return 0; #else /* CONFIG_NATIVE_WINDOWS */ return eloop_register_signal(SIGHUP, handler, user_data); #endif /* CONFIG_NATIVE_WINDOWS */ +#else /* __rtems__ */ + return 0; +#endif /* __rtems__ */ } diff --git a/freebsd/crypto/openssl/apps/ocsp.c b/freebsd/crypto/openssl/apps/ocsp.c index 7ff6a20c..dba8e6a9 100644 --- a/freebsd/crypto/openssl/apps/ocsp.c +++ b/freebsd/crypto/openssl/apps/ocsp.c @@ -58,7 +58,7 @@ NON_EMPTY_TRANSLATION_UNIT #endif # if !defined(NO_FORK) && !defined(OPENSSL_NO_SOCK) \ - && !defined(OPENSSL_NO_POSIX_IO) + && !defined(OPENSSL_NO_POSIX_IO) && !defined(__rtems__) # define OCSP_DAEMON # include <sys/types.h> # include <sys/wait.h> diff --git a/freebsd/crypto/openssl/apps/openssl.c b/freebsd/crypto/openssl/apps/openssl.c index 31ec58d8..cdbb262b 100644 --- a/freebsd/crypto/openssl/apps/openssl.c +++ b/freebsd/crypto/openssl/apps/openssl.c @@ -89,9 +89,11 @@ static void calculate_columns(DISPLAY_COLUMNS *dc) static int apps_startup(void) { +#ifndef __rtems__ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif +#endif /* __rtems__ */ /* Set non-default library initialisation settings */ if (!OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN diff --git a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h index 2c2b926e..90043fb0 100644 --- a/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h +++ b/freebsd/crypto/openssl/apps/rtems-bsd-openssl-ocsp-data.h @@ -3,6 +3,4 @@ #include "rtems-bsd-openssl-data.h" /* ocsp.c */ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static char *prog); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int acfd); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int multi); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_openssl, static int termsig); diff --git a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c index 03596eee..6a040553 100644 --- a/freebsd/crypto/openssl/crypto/ui/ui_openssl.c +++ b/freebsd/crypto/openssl/crypto/ui/ui_openssl.c @@ -158,11 +158,13 @@ struct IOSB { # endif /* Define globals. They are protected by a lock */ +#ifndef __rtems__ # ifdef SIGACTION static struct sigaction savsig[NX509_SIG]; # else static void (*savsig[NX509_SIG]) (int); # endif +#endif /* __rtems__ */ # ifdef OPENSSL_SYS_VMS static struct IOSB iosb; @@ -185,7 +187,9 @@ static int is_a_tty; /* Declare static functions */ # if !defined(OPENSSL_SYS_WINCE) static int read_till_nl(FILE *); +#ifndef __rtems__ static void recsig(int); +#endif /* __rtems__ */ static void pushsig(void); static void popsig(void); # endif @@ -588,6 +592,7 @@ static int close_console(UI *ui) /* Internal functions to handle signals and act on them */ static void pushsig(void) { +#ifndef __rtems__ # ifndef OPENSSL_SYS_WIN32 int i; # endif @@ -630,10 +635,12 @@ static void pushsig(void) # ifdef SIGWINCH signal(SIGWINCH, SIG_DFL); # endif +#endif /* __rtems__ */ } static void popsig(void) { +#ifndef __rtems__ # ifdef OPENSSL_SYS_WIN32 signal(SIGABRT, savsig[SIGABRT]); signal(SIGFPE, savsig[SIGFPE]); @@ -659,12 +666,15 @@ static void popsig(void) # endif } # endif +#endif /* __rtems__ */ } +#ifndef __rtems__ static void recsig(int i) { intr_signal = i; } +#endif /* __rtems__ */ # endif /* Internal functions specific for Windows */ diff --git a/freebsd/lib/libc/include/libc_private.h b/freebsd/lib/libc/include/libc_private.h index fb3a4bb2..4a699e93 100644 --- a/freebsd/lib/libc/include/libc_private.h +++ b/freebsd/lib/libc/include/libc_private.h @@ -36,8 +36,12 @@ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ +#ifndef __rtems__ #include <sys/_types.h> #include <sys/_pthreadtypes.h> +#else /* __rtems__ */ +#include <sys/types.h> +#endif /* __rtems__ */ /* * This global flag is non-zero when a process has created one diff --git a/freebsd/lib/libc/stdio/local.h b/freebsd/lib/libc/stdio/local.h index bed0b232..5ec1d3f5 100644 --- a/freebsd/lib/libc/stdio/local.h +++ b/freebsd/lib/libc/stdio/local.h @@ -78,13 +78,15 @@ extern int __srefill(FILE *); */ extern int __srefill_r(struct _reent *,FILE *); -#define __srefill(_x) __srefill_r(__getreent(), _x) +#define __srefill(_x) __srefill_r(_REENT, _x) #endif /* __rtems__ */ extern int __sread(void *, char *, int); extern int __swrite(void *, char const *, int); extern fpos_t __sseek(void *, fpos_t, int); extern int __sclose(void *); +#ifndef __rtems__ extern void __sinit(void); +#endif /* __rtems__ */ extern void _cleanup(void); extern void __smakebuf(FILE *); extern int __swhatbuf(FILE *, size_t *, int *); diff --git a/freebsd/sbin/pfctl/pfctl_altq.c b/freebsd/sbin/pfctl/pfctl_altq.c index 7cf72b43..05c7da22 100644 --- a/freebsd/sbin/pfctl/pfctl_altq.c +++ b/freebsd/sbin/pfctl/pfctl_altq.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #define PFIOC_USE_LATEST +#define _WANT_FREEBSD_BITSET #include <sys/types.h> #include <sys/bitset.h> diff --git a/freebsd/sbin/pfctl/pfctl_parser.c b/freebsd/sbin/pfctl/pfctl_parser.c index f339d972..9d752d26 100644 --- a/freebsd/sbin/pfctl/pfctl_parser.c +++ b/freebsd/sbin/pfctl/pfctl_parser.c @@ -1351,10 +1351,17 @@ get_socket_domain(void) return (sdom); } +#ifdef __rtems__ +static int pfctl_s = -1; +#endif /* __rtems__ */ int get_query_socket(void) { +#ifndef __rtems__ static int s = -1; +#else /* __rtems__ */ +#define s pfctl_s +#endif /* __rtems__ */ if (s == -1) { if ((s = socket(get_socket_domain(), SOCK_DGRAM, 0)) == -1) @@ -1362,6 +1369,9 @@ get_query_socket(void) } return (s); +#ifdef __rtems__ +#undef s +#endif /* __rtems__ */ } /* diff --git a/freebsd/sbin/pfctl/pfctl_parser.h b/freebsd/sbin/pfctl/pfctl_parser.h index aa6d98d7..7d92b1db 100644 --- a/freebsd/sbin/pfctl/pfctl_parser.h +++ b/freebsd/sbin/pfctl/pfctl_parser.h @@ -178,7 +178,7 @@ struct node_queue_opt { }; #define QPRI_BITSET_SIZE 256 -BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); +__BITSET_DEFINE(qpri_bitset, QPRI_BITSET_SIZE); LIST_HEAD(gen_sc, segment); struct pfctl_altq { diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h index 1712b9e6..2f7fb828 100644 --- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h +++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-namespace.h @@ -35,6 +35,7 @@ #define parseport _bsd_pfctl_parseport #define pfctl_cmdline_symset _bsd_pfctl_pfctl_cmdline_symset #define pfctl_load_anchors _bsd_pfctl_pfctl_load_anchors +#define pfctl_s _bsd_pfctl_s #define pfctlychar _bsd_pfctl_pfctlychar #define pfctlydebug _bsd_pfctl_pfctlydebug #define pfctlyerrflag _bsd_pfctl_pfctlyerrflag diff --git a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h index bb8832ac..9bbec579 100644 --- a/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h +++ b/freebsd/sbin/pfctl/rtems-bsd-pfctl-pfctl_parser-data.h @@ -3,3 +3,4 @@ #include "rtems-bsd-pfctl-data.h" /* pfctl_parser.c */ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static struct node_host *iftab); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_pfctl, static int pfctl_s); diff --git a/freebsd/sbin/ping/ping.c b/freebsd/sbin/ping/ping.c index e31941b8..fae24e53 100644 --- a/freebsd/sbin/ping/ping.c +++ b/freebsd/sbin/ping/ping.c @@ -307,7 +307,9 @@ main(int argc, char *const *argv) #endif struct sockaddr_in *to; double t; +#ifndef __rtems__ u_long alarmtimeout; +#endif /* __rtems__ */ long ltmp; int almost_done, ch, df, hold, i, icmp_len, mib[4], preload; int ssend_errno, srecv_errno, tos, ttl; @@ -370,7 +372,9 @@ main(int argc, char *const *argv) err(EX_OSERR, "srecv socket"); } +#ifndef __rtems__ alarmtimeout = df = preload = tos = 0; +#endif /* __rtems__ */ outpack = outpackhdr + sizeof(struct ip); while ((ch = getopt(argc, argv, @@ -572,6 +576,7 @@ main(int argc, char *const *argv) mttl = ltmp; options |= F_MTTL; break; +#ifndef __rtems__ case 't': alarmtimeout = strtoul(optarg, &ep, 0); if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) @@ -582,6 +587,7 @@ main(int argc, char *const *argv) optarg, MAXALARM); alarm((int)alarmtimeout); break; +#endif /* __rtems__ */ case 'v': options |= F_VERBOSE; break; @@ -1518,8 +1524,10 @@ static void finish(void) { +#ifndef __rtems__ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGALRM, SIG_IGN); +#endif /* __rtems__ */ (void)putchar('\n'); (void)fflush(stdout); (void)printf("--- %s ping statistics ---\n", hostname); diff --git a/freebsd/sbin/ping6/ping6.c b/freebsd/sbin/ping6/ping6.c index a56aae1d..921797d0 100644 --- a/freebsd/sbin/ping6/ping6.c +++ b/freebsd/sbin/ping6/ping6.c @@ -115,6 +115,7 @@ __FBSDID("$FreeBSD$"); #include <getopt.h> #include <machine/rtems-bsd-program.h> #include <machine/rtems-bsd-commands.h> +#include <rtems/libio_.h> #endif /* __rtems__ */ #include <sys/param.h> #include <sys/capsicum.h> @@ -286,7 +287,9 @@ static void fill(char *, char *); static int get_hoplim(struct msghdr *); static int get_pathmtu(struct msghdr *); static struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); +#ifndef __rtems__ static void onsignal(int); +#endif /* __rtems__ */ static void onint(int); static size_t pingerlen(void); static int pinger(void); @@ -346,7 +349,9 @@ main(int argc, char *argv[]) struct timespec last, intvl; struct sockaddr_in6 from, *sin6; struct addrinfo hints, *res; +#ifndef __rtems__ struct sigaction si_sa; +#endif /* __rtems__ */ int cc, i; int almost_done, ch, hold, packlen, preload, optval, error; int nig_oldmcprefix = -1; @@ -373,7 +378,9 @@ main(int argc, char *argv[]) char *policy_out = NULL; #endif double t; +#ifndef __rtems__ u_long alarmtimeout; +#endif /* __rtems__ */ size_t rthlen; #ifdef IPV6_USE_MIN_MTU int mflag = 0; @@ -399,7 +406,9 @@ main(int argc, char *argv[]) intvl.tv_sec = interval / 1000; intvl.tv_nsec = interval % 1000 * 1000000; +#ifndef __rtems__ alarmtimeout = preload = 0; +#endif /* __rtems__ */ datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; capdns = capdns_setup(); #ifndef IPSEC @@ -627,6 +636,7 @@ main(int argc, char *argv[]) options |= F_WAITTIME; waittime = (int)t; break; +#ifndef __rtems__ case 't': alarmtimeout = strtoul(optarg, &e, 0); if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) @@ -637,6 +647,7 @@ main(int argc, char *argv[]) optarg, MAXALARM); alarm((int)alarmtimeout); break; +#endif /* __rtems__ */ #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC case 'P': @@ -1173,6 +1184,7 @@ main(int argc, char *argv[]) } clock_gettime(CLOCK_MONOTONIC, &last); +#ifndef __rtems__ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = onsignal; @@ -1188,6 +1200,7 @@ main(int argc, char *argv[]) if (sigaction(SIGALRM, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGALRM"); } +#endif /* __rtems__ */ if (options & F_FLOOD) { intvl.tv_sec = 0; intvl.tv_nsec = 10000000; @@ -1198,7 +1211,13 @@ main(int argc, char *argv[]) struct timespec now, timeout; struct msghdr m; struct iovec iov[2]; +#ifndef __rtems__ fd_set rfds; +#else /* __rtems__ */ + fd_set big_enough_rfds[howmany(rtems_libio_number_iops, + sizeof(fd_set) * 8)]; +#define rfds (*(fd_set *)(&big_enough_rfds[0])) +#endif /* __rtems__ */ int n; /* signal handling */ @@ -1211,7 +1230,11 @@ main(int argc, char *argv[]) continue; } #endif +#ifndef __rtems__ FD_ZERO(&rfds); +#else /* __rtems__ */ + memset(big_enough_rfds, 0, sizeof(big_enough_rfds)); +#endif /* __rtems__ */ FD_SET(srecv, &rfds); clock_gettime(CLOCK_MONOTONIC, &now); timespecadd(&last, &intvl, &timeout); @@ -1299,11 +1322,13 @@ main(int argc, char *argv[]) } } } +#ifndef __rtems__ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = SIG_IGN; sigaction(SIGINT, &si_sa, 0); sigaction(SIGALRM, &si_sa, 0); +#endif /* __rtems__ */ summary(); if(packet != NULL) @@ -1312,6 +1337,7 @@ main(int argc, char *argv[]) exit(nreceived == 0 ? 2 : 0); } +#ifndef __rtems__ static void onsignal(int sig) { @@ -1328,6 +1354,7 @@ onsignal(int sig) #endif } } +#endif /* __rtems__ */ /* * pinger -- diff --git a/freebsd/sys/arm/freescale/imx/imx6_ccm.c b/freebsd/sys/arm/freescale/imx/imx6_ccm.c index 78bbd5c1..7fdb69b8 100644 --- a/freebsd/sys/arm/freescale/imx/imx6_ccm.c +++ b/freebsd/sys/arm/freescale/imx/imx6_ccm.c @@ -368,6 +368,7 @@ imx6_ccm_sata_enable(void) return 0; } +#ifndef __rtems__ uint32_t imx_ccm_ecspi_hz(void) { @@ -408,6 +409,7 @@ imx_ccm_ahb_hz(void) { return (132000000); } +#endif /* __rtems__ */ void imx_ccm_ipu_enable(int ipu) diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c b/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c deleted file mode 100644 index 8823b6af..00000000 --- a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.c +++ /dev/null @@ -1,305 +0,0 @@ -#include <machine/rtems-bsd-kernel-space.h> - -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/module.h> -#include <sys/bus.h> -#include <rtems/bsd/sys/resource.h> -#include <sys/rman.h> -#include <sys/lock.h> -#include <sys/malloc.h> - -#include <machine/bus.h> -#include <machine/resource.h> -#include <machine/intr.h> -#include <sys/gpio.h> - -#include <arm/ti/tivar.h> -#include <arm/ti/ti_pinmux.h> - -#include <arm/ti/am335x/am335x_scm_padconf.h> - -#define _PIN(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \ - { .reg_off = r, \ - .gpio_pin = gp, \ - .gpio_mode = gm, \ - .ballname = b, \ - .muxmodes[0] = m0, \ - .muxmodes[1] = m1, \ - .muxmodes[2] = m2, \ - .muxmodes[3] = m3, \ - .muxmodes[4] = m4, \ - .muxmodes[5] = m5, \ - .muxmodes[6] = m6, \ - .muxmodes[7] = m7, \ - } - -const static struct ti_pinmux_padstate ti_padstate_devmap[] = { - {"output", PADCONF_OUTPUT }, - {"output_pullup", PADCONF_OUTPUT_PULLUP }, - {"input", PADCONF_INPUT }, - {"input_pulldown", PADCONF_INPUT_PULLDOWN }, - {"input_pullup", PADCONF_INPUT_PULLUP }, - {"i2c", PADCONF_INPUT_PULLUP_SLOW }, - { .state = NULL } -}; - -const static struct ti_pinmux_padconf ti_padconf_devmap[] = { - _PIN(0x000, "GPMC_AD0", 32, 7,"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"), - _PIN(0x004, "GPMC_AD1", 33, 7,"gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"), - _PIN(0x008, "GPMC_AD2", 34, 7,"gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"), - _PIN(0x00C, "GPMC_AD3", 35, 7,"gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"), - _PIN(0x010, "GPMC_AD4", 36, 7,"gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"), - _PIN(0x014, "GPMC_AD5", 37, 7,"gpmc_ad5", "mmc1_dat5", NULL, NULL, NULL, NULL, NULL, "gpio1_5"), - _PIN(0x018, "GPMC_AD6", 38, 7,"gpmc_ad6", "mmc1_dat6", NULL, NULL, NULL, NULL, NULL, "gpio1_6"), - _PIN(0x01C, "GPMC_AD7", 39, 7,"gpmc_ad7", "mmc1_dat7", NULL, NULL, NULL, NULL, NULL, "gpio1_7"), - _PIN(0x020, "GPMC_AD8", 22, 7, "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4", "ehrpwm2A", NULL, NULL, "gpio0_22"), - _PIN(0x024, "GPMC_AD9", 23, 7, "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5", "ehrpwm2B", NULL, NULL, "gpio0_23"), - _PIN(0x028, "GPMC_AD10", 26, 7, "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6", "ehrpwm2_tripzone_in", NULL, NULL, "gpio0_26"), - _PIN(0x02C, "GPMC_AD11", 27, 7, "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7", "ehrpwm0_synco", NULL, NULL, "gpio0_27"), - _PIN(0x030, "GPMC_AD12", 44, 7, "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0", "eQEP2A_in", "pr1_mii0_txd2", "pr1_pru0_pru_r30_14", "gpio1_12"), - _PIN(0x034, "GPMC_AD13", 45, 7, "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1", "eQEP2B_in", "pr1_mii0_txd1", "pr1_pru0_pru_r30_15", "gpio1_13"), - _PIN(0x038, "GPMC_AD14", 46, 7, "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2", "eQEP2_index", "pr1_mii0_txd0", "pr1_pru0_pru_r31_14", "gpio1_14"), - _PIN(0x03C, "GPMC_AD15", 47, 7, "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3", "eQEP2_strobe", "pr1_ecap0_ecap_capin_apwm_o", "pr1_pru0_pru_r31_15", "gpio1_15"), - _PIN(0x040, "GPMC_A0", 48, 7, "gpmc_a0", "gmii2_txen", "rgmii2_tctl", "rmii2_txen", "gpmc_a16", "pr1_mii_mt1_clk", "ehrpwm1_tripzone_input", "gpio1_16"), - _PIN(0x044, "GPMC_A1", 49, 7, "gpmc_a1", "gmii2_rxdv", "rgmii2_rctl", "mmc2_dat0", "gpmc_a17", "pr1_mii1_txd3", "ehrpwm0_synco", "gpio1_17"), - _PIN(0x048, "GPMC_A2", 50, 7, "gpmc_a2", "gmii2_txd3", "rgmii2_td3", "mmc2_dat1", "gpmc_a18", "pr1_mii1_txd2", "ehrpwm1A", "gpio1_18"), - _PIN(0x04C, "GPMC_A3", 51, 7, "gpmc_a3", "gmii2_txd2", "rgmii2_td2", "mmc2_dat2", "gpmc_a19", "pr1_mii1_txd1", "ehrpwm1B", "gpio1_19"), - _PIN(0x050, "GPMC_A4", 52, 7, "gpmc_a4", "gmii2_txd1", "rgmii2_td1", "rmii2_tdx1", "gpmc_a20", "pr1_mii1_txd0", "eQEP1A_in", "gpio1_20"), - _PIN(0x054, "GPMC_A5", 53, 7, "gpmc_a5", "gmii2_txd0", "rgmii2_td0", "rmii2_txd0", "gpmc_a21", "pr1_mii1_rxd3", "eQEP1B_in", "gpio1_21"), - _PIN(0x058, "GPMC_A6", 54, 7, "gpmc_a6", "gmii2_txclk", "rgmii2_tclk", "mmc2_dat4", "gpmc_a22", "pr1_mii1_rxd2", "eQEP1_index", "gpio1_22"), - _PIN(0x05C, "GPMC_A7", 55, 7, "gpmc_a7", "gmii2_rxclk", "rgmii2_rclk", "mmc2_dat5", "gpmc_a23", "pr1_mii1_rxd1", "eQEP1_strobe", "gpio1_23"), - _PIN(0x060, "GPMC_A8", 56, 7, "gpmc_a8", "gmii2_rxd3", "rgmii2_rd3", "mmc2_dat6", "gpmc_a24", "pr1_mii1_rxd0", "mcasp0_aclkx", "gpio1_24"), - _PIN(0x064, "GPMC_A9", 57, 7, "gmpc_a9", "gmii2_rxd2", "rgmii2_rd2", "mmc2_dat7 / rmii2_crs_dv", "gpmc_a25", "pr1_mii_mr1_clk", "mcasp0_fsx", "gpio1_25"), - _PIN(0x068, "GPMC_A10", 58, 7, "gmpc_a10", "gmii2_rxd1", "rgmii2_rd1", "rmii2_rxd1", "gpmc_a26", "pr1_mii1_rxdv", "mcasp0_arx0", "gpio1_26"), - _PIN(0x06C, "GPMC_A11", 59, 7, "gmpc_a11", "gmii2_rxd0", "rgmii2_rd0", "rmii2_rxd0", "gpmc_a27", "pr1_mii1_rxer", "mcasp0_axr1", "gpio1_27"), - _PIN(0x070, "GPMC_WAIT0", 30, 7, "gpmc_wait0", "gmii2_crs", "gpmc_csn4", "rmii2_crs_dv", "mmc1_sdcd", "pr1_mii1_col", "uart4_rxd", "gpio0_30"), - _PIN(0x074, "GPMC_WPn", 31, 7, "gpmc_wpn", "gmii2_rxerr", "gpmc_csn5", "rmii2_rxerr", "mmc2_sdcd", "pr1_mii1_txen", "uart4_txd", "gpio0_31"), - _PIN(0x078, "GPMC_BEn1", 60, 7, "gpmc_be1n", "gmii2_col", "gmpc_csn6","mmc2_dat3", "gpmc_dir", "pr1_mii1_rxlink", "mcasp0_aclkr", "gpio1_28"), - _PIN(0x07c, "GPMC_CSn0", 61, 7, "gpmc_csn0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio1_29"), - _PIN(0x080, "GPMC_CSn1", 62, 7, "gpmc_csn1", "gpmc_clk", "mmc1_clk", "pr1_edio_data_in6", "pr1_edio_data_out6", "pr1_pru1_pru_r30_12", "pr1_pru1_pru_r31_12", "gpio1_30"), - _PIN(0x084, "GPMC_CSn2", 63, 7, "gpmc_csn2", "gpmc_be1n", "mmc1_cmd", "pr1_edio_data_in7", "pr1_edio_data_out7", "pr1_pru1_pru_r30_13", "pr1_pru1_pru_r31_13", "gpio1_31"), - _PIN(0x088, "GPMC_CSn3", 64, 7, "gpmc_csn3", "gpmc_a3", "rmii2_crs_dv", "mmc2_cmd", "pr1_mii0_crs", "pr1_mdio_data", "EMU4", "gpio2_0"), - _PIN(0x08c, "GPMC_CLK", 65, 7, "gpmc_clk", "lcd_memory_clk", "gpmc_wait1", "mmc2_clk", "pr1_mii1_crs", "pr1_mdio_mdclk", "mcasp0_fsr", "gpio2_1"), - _PIN(0x090, "GPMC_ADVn_ALE", 66, 7, "gpmc_advn_ale", NULL, "timer4", NULL, NULL, NULL, NULL, "gpio2_2"), - _PIN(0x094, "GPMC_OEn_REn", 67, 7, "gpmc_oen_ren", NULL, "timer7", NULL, NULL, NULL, NULL, "gpio2_3"), - _PIN(0x098, "GPMC_WEn", 68, 7, "gpmc_wen", NULL, "timer6", NULL, NULL, NULL, NULL, "gpio2_4"), - _PIN(0x09c, "GPMC_BEn0_CLE", 67, 7, "gpmc_ben0_cle", NULL, "timer5", NULL, NULL, NULL, NULL, "gpio2_5"), - _PIN(0x0a0, "LCD_DATA0", 68, 7, "lcd_data0", "gpmc_a0", "pr1_mii_mt0_clk", "ehrpwm2A", NULL, "pr1_pru1_pru_r30_0", "pr1_pru1_pru_r31_0", "gpio2_6"), - _PIN(0x0a4, "LCD_DATA1", 69, 7, "lcd_data1", "gpmc_a1", "pr1_mii0_txen", "ehrpwm2B", NULL, "pr1_pru1_pru_r30_1", "pr1_pru1_pru_r31_1", "gpio2_7"), - _PIN(0x0a8, "LCD_DATA2", 70, 7, "lcd_data2", "gpmc_a2", "pr1_mii0_txd3", "ehrpwm2_tripzone_input", NULL, "pr1_pru1_pru_r30_2", "pr1_pru1_pru_r31_2", "gpio2_8"), - _PIN(0x0ac, "LCD_DATA3", 71, 7, "lcd_data3", "gpmc_a3", "pr1_mii0_txd2", "ehrpwm0_synco", NULL, "pr1_pru1_pru_r30_3", "pr1_pru1_pru_r31_3", "gpio2_9"), - _PIN(0x0b0, "LCD_DATA4", 72, 7, "lcd_data4", "gpmc_a4", "pr1_mii0_txd1", "eQEP2A_in", NULL, "pr1_pru1_pru_r30_4", "pr1_pru1_pru_r31_4", "gpio2_10"), - _PIN(0x0b4, "LCD_DATA5", 73, 7, "lcd_data5", "gpmc_a5", "pr1_mii0_txd0", "eQEP2B_in", NULL, "pr1_pru1_pru_r30_5", "pr1_pru1_pru_r31_5", "gpio2_11"), - _PIN(0x0b8, "LCD_DATA6", 74, 7, "lcd_data6", "gpmc_a6", "pr1_edio_data_in6", "eQEP2_index", "pr1_edio_data_out6", "pr1_pru1_pru_r30_6", "pr1_pru1_pru_r31_6", "gpio2_12"), - _PIN(0x0bc, "LCD_DATA7", 75, 7, "lcd_data7", "gpmc_a7", "pr1_edio_data_in7", "eQEP2_strobe", "pr1_edio_data_out7", "pr1_pru1_pru_r30_7", "pr1_pru1_pru_r31_7", "gpio2_13"), - _PIN(0x0c0, "LCD_DATA8", 76, 7, "lcd_data8", "gpmc_a12", "ehrpwm1_tripzone_input", "mcasp0_aclkx", "uart5_txd", "pr1_mii0_rxd3", "uart2_ctsn", "gpio2_14"), - _PIN(0x0c4, "LCD_DATA9", 76, 7, "lcd_data9", "gpmc_a13", "ehrpwm0_synco", "mcasp0_fsx", "uart5_rxd", "pr1_mii0_rxd2", "uart2_rtsn", "gpio2_15"), - _PIN(0x0c8, "LCD_DATA10", 77, 7, "lcd_data10", "gpmc_a14", "ehrpwm1A", "mcasp0_axr0", NULL, "pr1_mii0_rxd1", "uart3_ctsn", "gpio2_16"), - _PIN(0x0cc, "LCD_DATA11", 78, 7, "lcd_data11", "gpmc_a15", "ehrpwm1B", "mcasp0_ahclkr", "mcasp0_axr2", "pr1_mii0_rxd0", "uart3_rtsn", "gpio2_17"), - _PIN(0x0d0, "LCD_DATA12", 8, 7, "lcd_data12", "gpmc_a16", "eQEP1A_in", "mcasp0_aclkr", "mcasp0_axr2", "pr1_mii0_rxlink", "uart4_ctsn", "gpio0_8"), - _PIN(0x0d4, "LCD_DATA13", 9, 7, "lcd_data13", "gpmc_a17", "eQEP1B_in", "mcasp0_fsr", "mcasp0_axr3", "pr1_mii0_rxer", "uart4_rtsn", "gpio0_9"), - _PIN(0x0d8, "LCD_DATA14", 10, 7, "lcd_data14", "gpmc_a18", "eQEP1_index", "mcasp0_axr1", "uart5_rxd", "pr1_mii_mr0_clk", "uart5_ctsn", "gpio0_10"), - _PIN(0x0dc, "LCD_DATA15", 11, 7, "lcd_data15", "gpmc_a19", "eQEP1_strobe", "mcasp0_ahclkx", "mcasp0_axr3", "pr1_mii0_rxdv", "uart5_rtsn", "gpio0_11"), - _PIN(0x0e0, "LCD_VSYNC", 86, 7, "lcd_vsync", "gpmc_a8", "gpmc_a1", "pr1_edio_data_in2", "pr1_edio_data_out2", "pr1_pru1_pru_r30_8", "pr1_pru1_pru_r31_8", "gpio2_22"), - _PIN(0x0e4, "LCD_HSYNC", 87, 7, "lcd_hsync", "gmpc_a9", "gpmc_a2", "pr1_edio_data_in3", "pr1_edio_data_out3", "pr1_pru1_pru_r30_9", "pr1_pru1_pru_r31_9", "gpio2_23"), - _PIN(0x0e8, "LCD_PCLK", 88, 7, "lcd_pclk", "gpmc_a10", "pr1_mii0_crs", "pr1_edio_data_in4", "pr1_edio_data_out4", "pr1_pru1_pru_r30_10", "pr1_pru1_pru_r31_10", "gpio2_24"), - _PIN(0x0ec, "LCD_AC_BIAS_EN", 89, 7, "lcd_ac_bias_en", "gpmc_a11", "pr1_mii1_crs", "pr1_edio_data_in5", "pr1_edio_data_out5", "pr1_pru1_pru_r30_11", "pr1_pru1_pru_r31_11", "gpio2_25"), - _PIN(0x0f0, "MMC0_DAT3", 90, 7, "mmc0_dat3", "gpmc_a20", "uart4_ctsn", "timer5", "uart1_dcdn", "pr1_pru0_pru_r30_8", "pr1_pru0_pru_r31_8", "gpio2_26"), - _PIN(0x0f4, "MMC0_DAT2", 91, 7, "mmc0_dat2", "gpmc_a21", "uart4_rtsn", "timer6", "uart1_dsrn", "pr1_pru0_pru_r30_9", "pr1_pru0_pru_r31_9", "gpio2_27"), - _PIN(0x0f8, "MMC0_DAT1", 92, 7, "mmc0_dat1", "gpmc_a22", "uart5_ctsn", "uart3_rxd", "uart1_dtrn", "pr1_pru0_pru_r30_10", "pr1_pru0_pru_r31_10", "gpio2_28"), - _PIN(0x0fc, "MMC0_DAT0", 93, 7, "mmc0_dat0", "gpmc_a23", "uart5_rtsn", "uart3_txd", "uart1_rin", "pr1_pru0_pru_r30_11", "pr1_pru0_pru_r31_11", "gpio2_29"), - _PIN(0x100, "MMC0_CLK", 94, 7, "mmc0_clk", "gpmc_a24", "uart3_ctsn", "uart2_rxd", "dcan1_tx", "pr1_pru0_pru_r30_12", "pr1_pru0_pru_r31_12", "gpio2_30"), - _PIN(0x104, "MMC0_CMD", 95, 7, "mmc0_cmd", "gpmc_a25", "uart3_rtsn", "uart2_txd", "dcan1_rx", "pr1_pru0_pru_r30_13", "pr1_pru0_pru_r31_13", "gpio2_31"), - _PIN(0x108, "MII1_COL", 96, 7, "gmii1_col", "rmii2_refclk", "spi1_sclk", "uart5_rxd", "mcasp1_axr2", "mmc2_dat3", "mcasp0_axr2", "gpio3_0"), - _PIN(0x10c, "MII1_CRS", 97, 7, "gmii1_crs", "rmii1_crs_dv", "spi1_d0", "I2C1_SDA", "mcasp1_aclkx", "uart5_ctsn", "uart2_rxd", "gpio3_1"), - _PIN(0x110, "MII1_RX_ER", 98, 7, "gmii1_rxerr", "rmii1_rxerr", "spi1_d1", "I2C1_SCL", "mcasp1_fsx", "uart5_rtsn", "uart2_txd", "gpio3_2"), - _PIN(0x114, "MII1_TX_EN", 99, 7, "gmii1_txen", "rmii1_txen", "rgmii1_tctl", "timer4", "mcasp1_axr0", "eQEP0_index", "mmc2_cmd", "gpio3_3"), - _PIN(0x118, "MII1_RX_DV", 100, 7, "gmii1_rxdv", "cd_memory_clk", "rgmii1_rctl", "uart5_txd", "mcasp1_aclkx", "mmc2_dat0", "mcasp0_aclkr", "gpio3_4"), - _PIN(0x11c, "MII1_TXD3", 16, 7, "gmii1_txd3", "dcan0_tx", "rgmii1_td3", "uart4_rxd", "mcasp1_fsx", "mmc2_dat1", "mcasp0_fsr", "gpio0_16"), - _PIN(0x120, "MII1_TXD2", 17, 7, "gmii1_txd2", "dcan0_rx", "rgmii1_td2", "uart4_txd", "mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"), - _PIN(0x124, "MII1_TXD1", 21, 7, "gmii1_txd1", "rmii1_txd1", "rgmii1_td1", "mcasp1_fsr", "mcasp1_axr1", "eQEP0A_in", "mmc1_cmd", "gpio0_21"), - _PIN(0x128, "MII1_TXD0", 28, 7, "gmii1_txd0", "rmii1_txd0", "rgmii1_td0", "mcasp1_axr2", "mcasp1_aclkr", "eQEP0B_in", "mmc1_clk", "gpio0_28"), - _PIN(0x12c, "MII1_TX_CLK", 105, 7, "gmii1_txclk", "uart2_rxd", "rgmii1_tclk", "mmc0_dat7", "mmc1_dat0", "uart1_dcdn", "mcasp0_aclkx", "gpio3_9"), - _PIN(0x130, "MII1_RX_CLK", 106, 7, "gmii1_rxclk", "uart2_txd", "rgmii1_rclk", "mmc0_dat6", "mmc1_dat1", "uart1_dsrn", "mcasp0_fsx", "gpio3_10"), - _PIN(0x134, "MII1_RXD3", 82, 7, "gmii1_rxd3", "uart3_rxd", "rgmii1_rd3", "mmc0_dat5", "mmc1_dat2", "uart1_dtrn", "mcasp0_axr0", "gpio2_18"), - _PIN(0x138, "MII1_RXD2", 83, 7, "gmii1_rxd2", "uart3_txd", "rgmii1_rd2", "mmc0_dat4", "mmc1_dat3", "uart1_rin", "mcasp0_axr1", "gpio2_19"), - _PIN(0x13c, "MII1_RXD1", 84, 7, "gmii1_rxd1", "rmii1_rxd1", "rgmii1_rd1", "mcasp1_axr3", "mcasp1_fsr", "eQEP0_strobe", "mmc2_clk", "gpio2_20"), - _PIN(0x140, "MII1_RXD0", 85, 7, "gmii1_rxd0", "rmii1_rxd0", "rgmii1_rd0", "mcasp1_ahclkx", "mcasp1_ahclkr", "mcasp1_aclkr", "mcasp0_axr3", "gpio2_21"), - _PIN(0x144, "RMII1_REF_CLK", 29, 7, "rmii1_refclk", "xdma_event_intr2", "spi1_cs0", "uart5_txd", "mcasp1_axr3", "mmc0_pow", "mcasp1_ahclkx", "gpio0_29"), - _PIN(0x148, "MDIO", 0, 7, "mdio_data", "timer6", "uart5_rxd", "uart3_ctsn", "mmc0_sdcd","mmc1_cmd", "mmc2_cmd","gpio0_0"), - _PIN(0x14c, "MDC", 1, 7, "mdio_clk", "timer5", "uart5_txd", "uart3_rtsn", "mmc0_sdwp", "mmc1_clk", "mmc2_clk", "gpio0_1"), - _PIN(0x150, "SPI0_SCLK", 2, 7, "spi0_sclk", "uart2_rxd", "I2C2_SDA", "ehrpwm0A", "pr1_uart0_cts_n", "pr1_edio_sof", "EMU2", "gpio0_2"), - _PIN(0x154, "SPI0_D0", 3, 7, "spi0_d0", "uart2_txd", "I2C2_SCL", "ehrpwm0B", "pr1_uart0_rts_n", "pr1_edio_latch_in", "EMU3", "gpio0_3"), - _PIN(0x158, "SPI0_D1", 4, 7, "spi0_d1", "mmc1_sdwp", "I2C1_SDA", "ehrpwm0_tripzone_input", "pr1_uart0_rxd", "pr1_edio_data_in0", "pr1_edio_data_out0", "gpio0_4"), - _PIN(0x15c, "SPI0_CS0", 5, 7, "spi0_cs0", "mmc2_sdwp", "I2C1_SCL", "ehrpwm0_synci", "pr1_uart0_txd", "pr1_edio_data_in1", "pr1_edio_data_out1", "gpio0_5"), - _PIN(0x160, "SPI0_CS1", 6, 7, "spi0_cs1", "uart3_rxd", "eCAP1_in_PWM1_out", "mcc0_pow", "xdm_event_intr2", "mmc0_sdcd", "EMU4", "gpio0_6"), - _PIN(0x164, "ECAP0_IN_PWM0_OUT",7, 7, "eCAP0_in_PWM0_out", "uart3_txd", "spi1_cs1", "pr1_ecap0_ecap_capin_apwm_o", "spi1_sclk", "mmc0_sdwp", "xdma_event_intr2", "gpio0_7"), - _PIN(0x168, "UART0_CTSn", 40, 7, "uart0_ctsn", "uart4_rxd", "dcan1_tx", "I2C1_SDA", "spi1_d0", "timer7", "pr1_edc_sync0_out", "gpio1_8"), - _PIN(0x16c, "UART0_RTSn", 41, 7, "uart0_rtsn", "uart4_txd", "dcan1_rx", "I2C1_SCL", "spi1_d1", "spi1_cs0", "pr1_edc_sync1_out", "gpio1_9"), - _PIN(0x170, "UART0_rxd", 42, 7, "uart0_rxd", "spi1_cs0", "dcan0_tx", "I2C2_SDA", "eCAP2_in_PWM2_out", "pr1_pru1_pru_r30_14", "pr1_pru1_pru_r31_14", "gpio1_10"), - _PIN(0x174, "UART0_txd", 43, 7, "uart0_txd", "spi1_cs1", "dcan0_rx", "I2C2_SCL", "eCAP1_in_PWM1_out", "pr1_pru1_pru_r30_15", "pr1_pru1_pru_r31_15", "gpio1_11"), - _PIN(0x178, "UART1_CTSn", 12, 7, "uart1_ctsn", "timer6_mux1", "dcan0_tx", "I2C2_SDA", "spi1_cs0", "pr1_uart0_cts_n", "pr1_edc_latch0_in", "gpio0_12"), - _PIN(0x17c, "UART1_RTSn", 13, 7, "uart1_rtsn", "timer5_mux1", "dcan0_rx", "I2C2_SCL", "spi1_cs1", "pr1_uart0_rts_n", "pr1_edc_latch1_in", "gpio0_13"), - _PIN(0x180, "UART1_RXD", 14, 7, "uart1_rxd", "mmc1_sdwp", "dcan1_tx", "I2C1_SDA", NULL, "pr1_uart0_rxd", "pr1_pru1_pru_r31_16", "gpio0_14"), - _PIN(0x184, "UART1_TXD", 15, 7, "uart1_txd", "mmc2_sdwp", "dcan1_rx", "I2C1_SCL", NULL, "pr1_uart0_txd", "pr1_pru0_pru_r31_16", "gpio0_15"), - _PIN(0x188, "I2C0_SDA", 101, 7, "I2C0_SDA", "timer4", "uart2_ctsn", "eCAP2_in_PWM2_out", NULL, NULL, NULL, "gpio3_5"), - _PIN(0x18c, "I2C0_SCL", 102, 7, "I2C0_SCL", "timer7", "uart2_rtsn", "eCAP1_in_PWM1_out", NULL, NULL, NULL, "gpio3_6"), - _PIN(0x190, "MCASP0_ACLKX", 110, 7, "mcasp0_aclkx", "ehrpwm0A", NULL, "spi1_sclk", "mmc0_sdcd", "pr1_pru0_pru_r30_0", "pr1_pru0_pru_r31_0", "gpio3_14"), - _PIN(0x194, "MCASP0_FSX", 111, 7, "mcasp0_fsx", "ehrpwm0B", NULL, "spi1_d0", "mmc1_sdcd", "pr1_pru0_pru_r30_1", "pr1_pru0_pru_r31_1", "gpio3_15"), - _PIN(0x198, "MCASP0_AXR0", 112, 7, "mcasp0_axr0", "ehrpwm0_tripzone_input", NULL, "spi1_d1", "mmc2_sdcd", "pr1_pru0_pru_r30_2", "pr1_pru0_pru_r31_2", "gpio3_16"), - _PIN(0x19c, "MCASP0_AHCLKR", 113, 7, "mcasp0_ahclkr", "ehrpwm0_synci", "mcasp0_axr2", "spi1_cs0", "eCAP2_in_PWM2_out", "pr1_pru0_pru_r30_3", "pr1_pru0_pru_r31_3", "gpio3_17"), - _PIN(0x1a0, "MCASP0_ACLKR", 114, 7, "mcasp0_aclkr", "eQEP0A_in", "mcasp0_axr2", "mcasp1_aclkx", "mmc0_sdwp", "pr1_pru0_pru_r30_4", "pr1_pru0_pru_r31_4", "gpio3_18"), - _PIN(0x1a4, "MCASP0_FSR", 115, 7, "mcasp0_fsr", "eQEP0B_in", "mcasp0_axr3", "mcasp1_fsx", "EMU2", "pr1_pru0_pru_r30_5", "pr1_pru0_pru_r31_5", "gpio3_19"), - _PIN(0x1a8, "MCASP0_AXR1", 116, 7, "mcasp0_axr1", "eQEP0_index", NULL, "mcasp1_axr0", "EMU3", "pr1_pru0_pru_r30_6", "pr1_pru0_pru_r31_6", "gpio3_20"), - _PIN(0x1ac, "MCASP0_AHCLKX", 117, 7, "mcasp0_ahclkx", "eQEP0_strobe", "mcasp0_axr3", "mcasp1_axr1", "EMU4", "pr1_pru0_pru_r30_7", "pr1_pru0_pru_r31_7", "gpio3_21"), - _PIN(0x1b0, "XDMA_EVENT_INTR0", 19, 7, "xdma_event_intr0", NULL, "timer4", "clkout1", "spi1_cs1", "pr1_pru1_pru_r31_16", "EMU2", "gpio0_19"), - _PIN(0x1b4, "XDMA_EVENT_INTR1", 20, 7, "xdma_event_intr1", NULL, "tclkin", "clkout2", "timer7", "pr1_pru0_pru_r31_16", "EMU3", "gpio0_20"), -#if 0 - _PIN(0x1b8, "nresetin_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1bc, "porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1c0, "nnmi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1c4, "osc0_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1c8, "osc0_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1cc, "osc0_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1d0, "tms", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1d4, "tdi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1d8, "tdo", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1dc, "tck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1e0, "ntrst", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), -#endif - _PIN(0x1e4, "EMU0", 103, 7, "EMU0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_7"), - _PIN(0x1e8, "EMU1", 104, 0, "EMU1", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_8"), -#if 0 - _PIN(0x1ec, "osc1_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1f0, "osc1_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1f4, "osc1_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1f8, "rtc_porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x1fc, "pmic_power_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x200, "ext_wakeup", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x204, "enz_kaldo_1p8v", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), -#endif - _PIN(0x208, "USB0_DM", 0, 0, "USB0_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x20c, "USB0_DP", 0, 0, "USB0_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x210, "USB0_CE", 0, 0, "USB0_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x214, "USB0_ID", 0, 0, "USB0_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x218, "USB0_VBUS", 0, 0, "USB0_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x21c, "USB0_DRVVBUS", 18, 7, "USB0_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio0_18"), - _PIN(0x220, "USB1_DM", 0, 0, "USB1_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x224, "USB1_DP", 0, 0, "USB1_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x228, "USB1_CE", 0, 0, "USB1_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x22c, "USB1_ID", 0, 0, "USB1_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x230, "USB1_VBUS", 0, 0, "USB1_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x234, "USB1_DRVVBUS", 109, 7, "USB1_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_13"), -#if 0 - _PIN(0x238, "ddr_resetn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x23c, "ddr_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x240, "ddr_cke", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x244, "ddr_ck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x248, "ddr_nck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x24c, "ddr_casn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x250, "ddr_rasn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x254, "ddr_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x258, "ddr_ba0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x25c, "ddr_ba1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x260, "ddr_ba2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x264, "ddr_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x268, "ddr_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x26c, "ddr_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x270, "ddr_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x274, "ddr_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x278, "ddr_a5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x27c, "ddr_a6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x280, "ddr_a7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x284, "ddr_a8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x288, "ddr_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x28c, "ddr_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x290, "ddr_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x294, "ddr_a12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x298, "ddr_a13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x29c, "ddr_a14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2a0, "ddr_a15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2a4, "ddr_odt", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2a8, "ddr_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2ac, "ddr_d1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2b0, "ddr_d2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2b4, "ddr_d3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2b8, "ddr_d4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2bc, "ddr_d5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2c0, "ddr_d6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2c4, "ddr_d7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2c8, "ddr_d8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2cc, "ddr_d9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2d0, "ddr_d10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2d4, "ddr_d11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2d8, "ddr_d12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2dc, "ddr_d13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2e0, "ddr_d14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2e4, "ddr_d15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2e8, "ddr_dqm0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2ec, "ddr_dqm1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2f0, "ddr_dqs0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2f4, "ddr_dqsn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2f8, "ddr_dqs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x2fc, "ddr_dqsn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x300, "ddr_vref", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x304, "ddr_vtp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x308, "ddr_strben0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x30c, "ddr_strben1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x32c, "ain0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x328, "ain1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x324, "ain2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x320, "ain3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x31c, "ain4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x318, "ain5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x314, "ain6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x310, "ain7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x330, "vrefp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x334, "vrefn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x338, "avdd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x33c, "avss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x340, "iforce", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x344, "vsense", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), - _PIN(0x348, "testout", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), -#endif - { .ballname = NULL }, -}; - -const struct ti_pinmux_device ti_am335x_pinmux_dev = { - .padconf_muxmode_mask = 0x7, - .padconf_sate_mask = 0x78, - .padstate = ti_padstate_devmap, - .padconf = ti_padconf_devmap, -}; diff --git a/freebsd/sys/arm/ti/ti_pinmux.c b/freebsd/sys/arm/ti/ti_pinmux.c deleted file mode 100644 index af47d074..00000000 --- a/freebsd/sys/arm/ti/ti_pinmux.c +++ /dev/null @@ -1,463 +0,0 @@ -#include <machine/rtems-bsd-kernel-space.h> - -/* - * Copyright (c) 2010 - * Ben Gray <ben.r.gray@gmail.com>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Ben Gray. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Exposes pinmux module to pinctrl-compatible interface - */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/module.h> -#include <sys/bus.h> -#include <rtems/bsd/sys/resource.h> -#include <sys/rman.h> -#include <sys/lock.h> -#include <sys/mutex.h> - -#include <machine/bus.h> -#include <machine/resource.h> - -#include <dev/ofw/openfirm.h> -#include <dev/ofw/ofw_bus.h> -#include <dev/ofw/ofw_bus_subr.h> -#include <dev/fdt/fdt_pinctrl.h> - -#include <arm/ti/omap4/omap4_scm_padconf.h> -#include <arm/ti/am335x/am335x_scm_padconf.h> -#include <arm/ti/ti_cpuid.h> -#include "ti_pinmux.h" - -struct pincfg { - uint32_t reg; - uint32_t conf; -}; - -static struct resource_spec ti_pinmux_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ - { -1, 0 } -}; - -static struct ti_pinmux_softc *ti_pinmux_sc; - -#define ti_pinmux_read_2(sc, reg) \ - bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) -#define ti_pinmux_write_2(sc, reg, val) \ - bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) -#define ti_pinmux_read_4(sc, reg) \ - bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) -#define ti_pinmux_write_4(sc, reg, val) \ - bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) - - -/** - * ti_padconf_devmap - Array of pins, should be defined one per SoC - * - * This array is typically defined in one of the targeted *_scm_pinumx.c - * files and is specific to the given SoC platform. Each entry in the array - * corresponds to an individual pin. - */ -static const struct ti_pinmux_device *ti_pinmux_dev; - - -/** - * ti_pinmux_padconf_from_name - searches the list of pads and returns entry - * with matching ball name. - * @ballname: the name of the ball - * - * RETURNS: - * A pointer to the matching padconf or NULL if the ball wasn't found. - */ -static const struct ti_pinmux_padconf* -ti_pinmux_padconf_from_name(const char *ballname) -{ - const struct ti_pinmux_padconf *padconf; - - padconf = ti_pinmux_dev->padconf; - while (padconf->ballname != NULL) { - if (strcmp(ballname, padconf->ballname) == 0) - return(padconf); - padconf++; - } - - return (NULL); -} - -/** - * ti_pinmux_padconf_set_internal - sets the muxmode and state for a pad/pin - * @padconf: pointer to the pad structure - * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" - * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? - * - * - * LOCKING: - * Internally locks it's own context. - * - * RETURNS: - * 0 on success. - * EINVAL if pin requested is outside valid range or already in use. - */ -static int -ti_pinmux_padconf_set_internal(struct ti_pinmux_softc *sc, - const struct ti_pinmux_padconf *padconf, - const char *muxmode, unsigned int state) -{ - unsigned int mode; - uint16_t reg_val; - - /* populate the new value for the PADCONF register */ - reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask); - - /* find the new mode requested */ - for (mode = 0; mode < 8; mode++) { - if ((padconf->muxmodes[mode] != NULL) && - (strcmp(padconf->muxmodes[mode], muxmode) == 0)) { - break; - } - } - - /* couldn't find the mux mode */ - if (mode >= 8) { - printf("Invalid mode \"%s\"\n", muxmode); - return (EINVAL); - } - - /* set the mux mode */ - reg_val |= (uint16_t)(mode & ti_pinmux_dev->padconf_muxmode_mask); - - if (bootverbose) - device_printf(sc->sc_dev, "setting internal %x for %s\n", - reg_val, muxmode); - /* write the register value (16-bit writes) */ - ti_pinmux_write_2(sc, padconf->reg_off, reg_val); - - return (0); -} - -/** - * ti_pinmux_padconf_set - sets the muxmode and state for a pad/pin - * @padname: the name of the pad, i.e. "c12" - * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" - * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? - * - * - * LOCKING: - * Internally locks it's own context. - * - * RETURNS: - * 0 on success. - * EINVAL if pin requested is outside valid range or already in use. - */ -int -ti_pinmux_padconf_set(const char *padname, const char *muxmode, unsigned int state) -{ - const struct ti_pinmux_padconf *padconf; - - if (!ti_pinmux_sc) - return (ENXIO); - - /* find the pin in the devmap */ - padconf = ti_pinmux_padconf_from_name(padname); - if (padconf == NULL) - return (EINVAL); - - return (ti_pinmux_padconf_set_internal(ti_pinmux_sc, padconf, muxmode, state)); -} - -/** - * ti_pinmux_padconf_get - gets the muxmode and state for a pad/pin - * @padname: the name of the pad, i.e. "c12" - * @muxmode: upon return will contain the name of the muxmode of the pin - * @state: upon return will contain the state of the pad/pin - * - * - * LOCKING: - * Internally locks it's own context. - * - * RETURNS: - * 0 on success. - * EINVAL if pin requested is outside valid range or already in use. - */ -int -ti_pinmux_padconf_get(const char *padname, const char **muxmode, - unsigned int *state) -{ - const struct ti_pinmux_padconf *padconf; - uint16_t reg_val; - - if (!ti_pinmux_sc) - return (ENXIO); - - /* find the pin in the devmap */ - padconf = ti_pinmux_padconf_from_name(padname); - if (padconf == NULL) - return (EINVAL); - - /* read the register value (16-bit reads) */ - reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off); - - /* save the state */ - if (state) - *state = (reg_val & ti_pinmux_dev->padconf_sate_mask); - - /* save the mode */ - if (muxmode) - *muxmode = padconf->muxmodes[(reg_val & ti_pinmux_dev->padconf_muxmode_mask)]; - - return (0); -} - -/** - * ti_pinmux_padconf_set_gpiomode - converts a pad to GPIO mode. - * @gpio: the GPIO pin number (0-195) - * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? - * - * - * - * LOCKING: - * Internally locks it's own context. - * - * RETURNS: - * 0 on success. - * EINVAL if pin requested is outside valid range or already in use. - */ -int -ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state) -{ - const struct ti_pinmux_padconf *padconf; - uint16_t reg_val; - - if (!ti_pinmux_sc) - return (ENXIO); - - /* find the gpio pin in the padconf array */ - padconf = ti_pinmux_dev->padconf; - while (padconf->ballname != NULL) { - if (padconf->gpio_pin == gpio) - break; - padconf++; - } - if (padconf->ballname == NULL) - return (EINVAL); - - /* populate the new value for the PADCONF register */ - reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask); - - /* set the mux mode */ - reg_val |= (uint16_t)(padconf->gpio_mode & ti_pinmux_dev->padconf_muxmode_mask); - - /* write the register value (16-bit writes) */ - ti_pinmux_write_2(ti_pinmux_sc, padconf->reg_off, reg_val); - - return (0); -} - -/** - * ti_pinmux_padconf_get_gpiomode - gets the current GPIO mode of the pin - * @gpio: the GPIO pin number (0-195) - * @state: upon return will contain the state - * - * - * - * LOCKING: - * Internally locks it's own context. - * - * RETURNS: - * 0 on success. - * EINVAL if pin requested is outside valid range or not configured as GPIO. - */ -int -ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state) -{ - const struct ti_pinmux_padconf *padconf; - uint16_t reg_val; - - if (!ti_pinmux_sc) - return (ENXIO); - - /* find the gpio pin in the padconf array */ - padconf = ti_pinmux_dev->padconf; - while (padconf->ballname != NULL) { - if (padconf->gpio_pin == gpio) - break; - padconf++; - } - if (padconf->ballname == NULL) - return (EINVAL); - - /* read the current register settings */ - reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off); - - /* check to make sure the pins is configured as GPIO in the first state */ - if ((reg_val & ti_pinmux_dev->padconf_muxmode_mask) != padconf->gpio_mode) - return (EINVAL); - - /* read and store the reset of the state, i.e. pull-up, pull-down, etc */ - if (state) - *state = (reg_val & ti_pinmux_dev->padconf_sate_mask); - - return (0); -} - -static int -ti_pinmux_configure_pins(device_t dev, phandle_t cfgxref) -{ - struct pincfg *cfgtuples, *cfg; - phandle_t cfgnode; - int i, ntuples; - static struct ti_pinmux_softc *sc; - - sc = device_get_softc(dev); - cfgnode = OF_node_from_xref(cfgxref); - ntuples = OF_getencprop_alloc_multi(cfgnode, "pinctrl-single,pins", - sizeof(*cfgtuples), (void **)&cfgtuples); - - if (ntuples < 0) - return (ENOENT); - - if (ntuples == 0) - return (0); /* Empty property is not an error. */ - - for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) { - if (bootverbose) { - char name[32]; - OF_getprop(cfgnode, "name", &name, sizeof(name)); - printf("%16s: muxreg 0x%04x muxval 0x%02x\n", - name, cfg->reg, cfg->conf); - } - - /* write the register value (16-bit writes) */ - ti_pinmux_write_2(sc, cfg->reg, cfg->conf); - } - - OF_prop_free(cfgtuples); - - return (0); -} - -/* - * Device part of OMAP SCM driver - */ - -static int -ti_pinmux_probe(device_t dev) -{ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "pinctrl-single")) - return (ENXIO); - - if (ti_pinmux_sc) { - printf("%s: multiple pinctrl modules in device tree data, ignoring\n", - __func__); - return (EEXIST); - } - switch (ti_chip()) { -#ifdef SOC_OMAP4 - case CHIP_OMAP_4: - ti_pinmux_dev = &omap4_pinmux_dev; - break; -#endif -#ifdef SOC_TI_AM335X - case CHIP_AM335X: - ti_pinmux_dev = &ti_am335x_pinmux_dev; - break; -#endif - default: - printf("Unknown CPU in pinmux\n"); - return (ENXIO); - } - - - device_set_desc(dev, "TI Pinmux Module"); - return (BUS_PROBE_DEFAULT); -} - -/** - * ti_pinmux_attach - attaches the pinmux to the simplebus - * @dev: new device - * - * RETURNS - * Zero on success or ENXIO if an error occuried. - */ -static int -ti_pinmux_attach(device_t dev) -{ - struct ti_pinmux_softc *sc = device_get_softc(dev); - -#if 0 - if (ti_pinmux_sc) - return (ENXIO); -#endif - - sc->sc_dev = dev; - - if (bus_alloc_resources(dev, ti_pinmux_res_spec, sc->sc_res)) { - device_printf(dev, "could not allocate resources\n"); - return (ENXIO); - } - - sc->sc_bst = rman_get_bustag(sc->sc_res[0]); - sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); - - if (ti_pinmux_sc == NULL) - ti_pinmux_sc = sc; - - fdt_pinctrl_register(dev, "pinctrl-single,pins"); - fdt_pinctrl_configure_tree(dev); - - return (0); -} - -static device_method_t ti_pinmux_methods[] = { - DEVMETHOD(device_probe, ti_pinmux_probe), - DEVMETHOD(device_attach, ti_pinmux_attach), - - /* fdt_pinctrl interface */ - DEVMETHOD(fdt_pinctrl_configure, ti_pinmux_configure_pins), - { 0, 0 } -}; - -static driver_t ti_pinmux_driver = { - "ti_pinmux", - ti_pinmux_methods, - sizeof(struct ti_pinmux_softc), -}; - -static devclass_t ti_pinmux_devclass; - -DRIVER_MODULE(ti_pinmux, simplebus, ti_pinmux_driver, ti_pinmux_devclass, 0, 0); diff --git a/freebsd/sys/arm/ti/ti_pinmux.h b/freebsd/sys/arm/ti/ti_pinmux.h deleted file mode 100644 index c299c494..00000000 --- a/freebsd/sys/arm/ti/ti_pinmux.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2010 - * Ben Gray <ben.r.gray@gmail.com>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Ben Gray. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - - -/** - * Functions to configure the PIN multiplexing on the chip. - * - * This is different from the GPIO module in that it is used to configure the - * pins between modules not just GPIO input output. - * - */ -#ifndef _TI_PINMUX_H_ -#define _TI_PINMUX_H_ - -struct ti_pinmux_padconf { - uint16_t reg_off; - uint16_t gpio_pin; - uint16_t gpio_mode; - const char *ballname; - const char *muxmodes[8]; -}; - -struct ti_pinmux_padstate { - const char *state; - uint16_t reg; -}; - -struct ti_pinmux_device { - uint16_t padconf_muxmode_mask; - uint16_t padconf_sate_mask; - const struct ti_pinmux_padstate *padstate; - const struct ti_pinmux_padconf *padconf; -}; - -struct ti_pinmux_softc { - device_t sc_dev; - struct resource * sc_res[4]; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; -}; - -int ti_pinmux_padconf_set(const char *padname, const char *muxmode, - unsigned int state); -int ti_pinmux_padconf_get(const char *padname, const char **muxmode, - unsigned int *state); -int ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state); -int ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state); - -#endif /* _TI_SCM_H_ */ diff --git a/freebsd/sys/arm64/arm64/in_cksum.c b/freebsd/sys/arm64/arm64/in_cksum.c new file mode 100644 index 00000000..a2c6cd1e --- /dev/null +++ b/freebsd/sys/arm64/arm64/in_cksum.c @@ -0,0 +1,243 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */ + +/*- + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1996 + * Matt Thomas <matt@3am-software.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 + */ + +#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/systm.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <machine/in_cksum.h> + +/* + * Checksum routine for Internet Protocol family headers + * (Portable Alpha version). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + */ + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE32 \ + { \ + q_util.q = sum; \ + sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \ + } +#define REDUCE16 \ + { \ + q_util.q = sum; \ + l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \ + sum = l_util.s[0] + l_util.s[1]; \ + ADDCARRY(sum); \ + } + +static const u_int32_t in_masks[] = { + /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/ + 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */ + 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */ + 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */ + 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */ +}; + +union l_util { + u_int16_t s[2]; + u_int32_t l; +}; +union q_util { + u_int16_t s[4]; + u_int32_t l[2]; + u_int64_t q; +}; + +static u_int64_t +in_cksumdata(const void *buf, int len) +{ + const u_int32_t *lw = (const u_int32_t *) buf; + u_int64_t sum = 0; + u_int64_t prefilled; + int offset; + union q_util q_util; + + if ((3 & (long) lw) == 0 && len == 20) { + sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4]; + REDUCE32; + return sum; + } + + if ((offset = 3 & (long) lw) != 0) { + const u_int32_t *masks = in_masks + (offset << 2); + lw = (u_int32_t *) (((long) lw) - offset); + sum = *lw++ & masks[len >= 3 ? 3 : len]; + len -= 4 - offset; + if (len <= 0) { + REDUCE32; + return sum; + } + } +#if 0 + /* + * Force to cache line boundary. + */ + offset = 32 - (0x1f & (long) lw); + if (offset < 32 && len > offset) { + len -= offset; + if (4 & offset) { + sum += (u_int64_t) lw[0]; + lw += 1; + } + if (8 & offset) { + sum += (u_int64_t) lw[0] + lw[1]; + lw += 2; + } + if (16 & offset) { + sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3]; + lw += 4; + } + } +#endif + /* + * access prefilling to start load of next cache line. + * then add current cache line + * save result of prefilling for loop iteration. + */ + prefilled = lw[0]; + while ((len -= 32) >= 4) { + u_int64_t prefilling = lw[8]; + sum += prefilled + lw[1] + lw[2] + lw[3] + + lw[4] + lw[5] + lw[6] + lw[7]; + lw += 8; + prefilled = prefilling; + } + if (len >= 0) { + sum += prefilled + lw[1] + lw[2] + lw[3] + + lw[4] + lw[5] + lw[6] + lw[7]; + lw += 8; + } else { + len += 32; + } + while ((len -= 16) >= 0) { + sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3]; + lw += 4; + } + len += 16; + while ((len -= 4) >= 0) { + sum += (u_int64_t) *lw++; + } + len += 4; + if (len > 0) + sum += (u_int64_t) (in_masks[len] & *lw); + REDUCE32; + return sum; +} + +u_short +in_addword(u_short a, u_short b) +{ + u_int64_t sum = a + b; + + ADDCARRY(sum); + return (sum); +} + +u_short +in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c) +{ + u_int64_t sum; + union q_util q_util; + union l_util l_util; + + sum = (u_int64_t) a + b + c; + REDUCE16; + return (sum); +} + +u_short +in_cksum_skip(struct mbuf *m, int len, int skip) +{ + u_int64_t sum = 0; + int mlen = 0; + int clen = 0; + caddr_t addr; + union q_util q_util; + union l_util l_util; + + len -= skip; + for (; skip && m; m = m->m_next) { + if (m->m_len > skip) { + mlen = m->m_len - skip; + addr = mtod(m, caddr_t) + skip; + goto skip_start; + } else { + skip -= m->m_len; + } + } + + for (; m && len; m = m->m_next) { + if (m->m_len == 0) + continue; + mlen = m->m_len; + addr = mtod(m, caddr_t); +skip_start: + if (len < mlen) + mlen = len; + if ((clen ^ (long) addr) & 1) + sum += in_cksumdata(addr, mlen) << 8; + else + sum += in_cksumdata(addr, mlen); + + clen += mlen; + len -= mlen; + } + REDUCE16; + return (~sum & 0xffff); +} + +u_int in_cksum_hdr(const struct ip *ip) +{ + u_int64_t sum = in_cksumdata(ip, sizeof(struct ip)); + union q_util q_util; + union l_util l_util; + REDUCE16; + return (~sum & 0xffff); +} diff --git a/freebsd/sys/arm64/include/machine/armreg.h b/freebsd/sys/arm64/include/machine/armreg.h new file mode 100644 index 00000000..72f48aab --- /dev/null +++ b/freebsd/sys/arm64/include/machine/armreg.h @@ -0,0 +1,665 @@ +/*- + * Copyright (c) 2013, 2014 Andrew Turner + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Andrew Turner under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _MACHINE_ARMREG_H_ +#define _MACHINE_ARMREG_H_ + +#define INSN_SIZE 4 + +#define READ_SPECIALREG(reg) \ +({ uint64_t val; \ + __asm __volatile("mrs %0, " __STRING(reg) : "=&r" (val)); \ + val; \ +}) +#define WRITE_SPECIALREG(reg, val) \ + __asm __volatile("msr " __STRING(reg) ", %0" : : "r"((uint64_t)val)) + +#define UL(x) UINT64_C(x) + +/* CNTHCTL_EL2 - Counter-timer Hypervisor Control register */ +#define CNTHCTL_EVNTI_MASK (0xf << 4) /* Bit to trigger event stream */ +#define CNTHCTL_EVNTDIR (1 << 3) /* Control transition trigger bit */ +#define CNTHCTL_EVNTEN (1 << 2) /* Enable event stream */ +#define CNTHCTL_EL1PCEN (1 << 1) /* Allow EL0/1 physical timer access */ +#define CNTHCTL_EL1PCTEN (1 << 0) /*Allow EL0/1 physical counter access*/ + +/* CPACR_EL1 */ +#define CPACR_FPEN_MASK (0x3 << 20) +#define CPACR_FPEN_TRAP_ALL1 (0x0 << 20) /* Traps from EL0 and EL1 */ +#define CPACR_FPEN_TRAP_EL0 (0x1 << 20) /* Traps from EL0 */ +#define CPACR_FPEN_TRAP_ALL2 (0x2 << 20) /* Traps from EL0 and EL1 */ +#define CPACR_FPEN_TRAP_NONE (0x3 << 20) /* No traps */ +#define CPACR_TTA (0x1 << 28) + +/* CTR_EL0 - Cache Type Register */ +#define CTR_DLINE_SHIFT 16 +#define CTR_DLINE_MASK (0xf << CTR_DLINE_SHIFT) +#define CTR_DLINE_SIZE(reg) (((reg) & CTR_DLINE_MASK) >> CTR_DLINE_SHIFT) +#define CTR_ILINE_SHIFT 0 +#define CTR_ILINE_MASK (0xf << CTR_ILINE_SHIFT) +#define CTR_ILINE_SIZE(reg) (((reg) & CTR_ILINE_MASK) >> CTR_ILINE_SHIFT) + +/* DAIF - Interrupt Mask Bits */ +#define DAIF_D_MASKED (1 << 9) +#define DAIF_A_MASKED (1 << 8) +#define DAIF_I_MASKED (1 << 7) +#define DAIF_F_MASKED (1 << 6) + +/* DCZID_EL0 - Data Cache Zero ID register */ +#define DCZID_DZP (1 << 4) /* DC ZVA prohibited if non-0 */ +#define DCZID_BS_SHIFT 0 +#define DCZID_BS_MASK (0xf << DCZID_BS_SHIFT) +#define DCZID_BS_SIZE(reg) (((reg) & DCZID_BS_MASK) >> DCZID_BS_SHIFT) + +/* ESR_ELx */ +#define ESR_ELx_ISS_MASK 0x00ffffff +#define ISS_INSN_FnV (0x01 << 10) +#define ISS_INSN_EA (0x01 << 9) +#define ISS_INSN_S1PTW (0x01 << 7) +#define ISS_INSN_IFSC_MASK (0x1f << 0) +#define ISS_DATA_ISV (0x01 << 24) +#define ISS_DATA_SAS_MASK (0x03 << 22) +#define ISS_DATA_SSE (0x01 << 21) +#define ISS_DATA_SRT_MASK (0x1f << 16) +#define ISS_DATA_SF (0x01 << 15) +#define ISS_DATA_AR (0x01 << 14) +#define ISS_DATA_FnV (0x01 << 10) +#define ISS_DATA_EA (0x01 << 9) +#define ISS_DATA_CM (0x01 << 8) +#define ISS_DATA_S1PTW (0x01 << 7) +#define ISS_DATA_WnR (0x01 << 6) +#define ISS_DATA_DFSC_MASK (0x3f << 0) +#define ISS_DATA_DFSC_ASF_L0 (0x00 << 0) +#define ISS_DATA_DFSC_ASF_L1 (0x01 << 0) +#define ISS_DATA_DFSC_ASF_L2 (0x02 << 0) +#define ISS_DATA_DFSC_ASF_L3 (0x03 << 0) +#define ISS_DATA_DFSC_TF_L0 (0x04 << 0) +#define ISS_DATA_DFSC_TF_L1 (0x05 << 0) +#define ISS_DATA_DFSC_TF_L2 (0x06 << 0) +#define ISS_DATA_DFSC_TF_L3 (0x07 << 0) +#define ISS_DATA_DFSC_AFF_L1 (0x09 << 0) +#define ISS_DATA_DFSC_AFF_L2 (0x0a << 0) +#define ISS_DATA_DFSC_AFF_L3 (0x0b << 0) +#define ISS_DATA_DFSC_PF_L1 (0x0d << 0) +#define ISS_DATA_DFSC_PF_L2 (0x0e << 0) +#define ISS_DATA_DFSC_PF_L3 (0x0f << 0) +#define ISS_DATA_DFSC_EXT (0x10 << 0) +#define ISS_DATA_DFSC_EXT_L0 (0x14 << 0) +#define ISS_DATA_DFSC_EXT_L1 (0x15 << 0) +#define ISS_DATA_DFSC_EXT_L2 (0x16 << 0) +#define ISS_DATA_DFSC_EXT_L3 (0x17 << 0) +#define ISS_DATA_DFSC_ECC (0x18 << 0) +#define ISS_DATA_DFSC_ECC_L0 (0x1c << 0) +#define ISS_DATA_DFSC_ECC_L1 (0x1d << 0) +#define ISS_DATA_DFSC_ECC_L2 (0x1e << 0) +#define ISS_DATA_DFSC_ECC_L3 (0x1f << 0) +#define ISS_DATA_DFSC_ALIGN (0x21 << 0) +#define ISS_DATA_DFSC_TLB_CONFLICT (0x30 << 0) +#define ESR_ELx_IL (0x01 << 25) +#define ESR_ELx_EC_SHIFT 26 +#define ESR_ELx_EC_MASK (0x3f << 26) +#define ESR_ELx_EXCEPTION(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT) +#define EXCP_UNKNOWN 0x00 /* Unkwn exception */ +#define EXCP_FP_SIMD 0x07 /* VFP/SIMD trap */ +#define EXCP_ILL_STATE 0x0e /* Illegal execution state */ +#define EXCP_SVC32 0x11 /* SVC trap for AArch32 */ +#define EXCP_SVC64 0x15 /* SVC trap for AArch64 */ +#define EXCP_MSR 0x18 /* MSR/MRS trap */ +#define EXCP_INSN_ABORT_L 0x20 /* Instruction abort, from lower EL */ +#define EXCP_INSN_ABORT 0x21 /* Instruction abort, from same EL */ +#define EXCP_PC_ALIGN 0x22 /* PC alignment fault */ +#define EXCP_DATA_ABORT_L 0x24 /* Data abort, from lower EL */ +#define EXCP_DATA_ABORT 0x25 /* Data abort, from same EL */ +#define EXCP_SP_ALIGN 0x26 /* SP slignment fault */ +#define EXCP_TRAP_FP 0x2c /* Trapped FP exception */ +#define EXCP_SERROR 0x2f /* SError interrupt */ +#define EXCP_SOFTSTP_EL0 0x32 /* Software Step, from lower EL */ +#define EXCP_SOFTSTP_EL1 0x33 /* Software Step, from same EL */ +#define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */ +#define EXCP_BRK 0x3c /* Breakpoint */ + +/* ICC_CTLR_EL1 */ +#define ICC_CTLR_EL1_EOIMODE (1U << 1) + +/* ICC_IAR1_EL1 */ +#define ICC_IAR1_EL1_SPUR (0x03ff) + +/* ICC_IGRPEN0_EL1 */ +#define ICC_IGRPEN0_EL1_EN (1U << 0) + +/* ICC_PMR_EL1 */ +#define ICC_PMR_EL1_PRIO_MASK (0xFFUL) + +/* ICC_SGI1R_EL1 */ +#define ICC_SGI1R_EL1_TL_MASK 0xffffUL +#define ICC_SGI1R_EL1_AFF1_SHIFT 16 +#define ICC_SGI1R_EL1_SGIID_SHIFT 24 +#define ICC_SGI1R_EL1_AFF2_SHIFT 32 +#define ICC_SGI1R_EL1_AFF3_SHIFT 48 +#define ICC_SGI1R_EL1_SGIID_MASK 0xfUL +#define ICC_SGI1R_EL1_IRM (0x1UL << 40) + +/* ICC_SRE_EL1 */ +#define ICC_SRE_EL1_SRE (1U << 0) + +/* ICC_SRE_EL2 */ +#define ICC_SRE_EL2_SRE (1U << 0) +#define ICC_SRE_EL2_EN (1U << 3) + +/* ID_AA64DFR0_EL1 */ +#define ID_AA64DFR0_MASK UL(0x0000000ff0f0ffff) +#define ID_AA64DFR0_DebugVer_SHIFT 0 +#define ID_AA64DFR0_DebugVer_MASK (UL(0xf) << ID_AA64DFR0_DebugVer_SHIFT) +#define ID_AA64DFR0_DebugVer(x) ((x) & ID_AA64DFR0_DebugVer_MASK) +#define ID_AA64DFR0_DebugVer_8 (UL(0x6) << ID_AA64DFR0_DebugVer_SHIFT) +#define ID_AA64DFR0_DebugVer_8_VHE (UL(0x7) << ID_AA64DFR0_DebugVer_SHIFT) +#define ID_AA64DFR0_DebugVer_8_2 (UL(0x8) << ID_AA64DFR0_DebugVer_SHIFT) +#define ID_AA64DFR0_TraceVer_SHIFT 4 +#define ID_AA64DFR0_TraceVer_MASK (UL(0xf) << ID_AA64DFR0_TraceVer_SHIFT) +#define ID_AA64DFR0_TraceVer(x) ((x) & ID_AA64DFR0_TraceVer_MASK) +#define ID_AA64DFR0_TraceVer_NONE (UL(0x0) << ID_AA64DFR0_TraceVer_SHIFT) +#define ID_AA64DFR0_TraceVer_IMPL (UL(0x1) << ID_AA64DFR0_TraceVer_SHIFT) +#define ID_AA64DFR0_PMUVer_SHIFT 8 +#define ID_AA64DFR0_PMUVer_MASK (UL(0xf) << ID_AA64DFR0_PMUVer_SHIFT) +#define ID_AA64DFR0_PMUVer(x) ((x) & ID_AA64DFR0_PMUVer_MASK) +#define ID_AA64DFR0_PMUVer_NONE (UL(0x0) << ID_AA64DFR0_PMUVer_SHIFT) +#define ID_AA64DFR0_PMUVer_3 (UL(0x1) << ID_AA64DFR0_PMUVer_SHIFT) +#define ID_AA64DFR0_PMUVer_3_1 (UL(0x4) << ID_AA64DFR0_PMUVer_SHIFT) +#define ID_AA64DFR0_PMUVer_IMPL (UL(0xf) << ID_AA64DFR0_PMUVer_SHIFT) +#define ID_AA64DFR0_BRPs_SHIFT 12 +#define ID_AA64DFR0_BRPs_MASK (UL(0xf) << ID_AA64DFR0_BRPs_SHIFT) +#define ID_AA64DFR0_BRPs(x) \ + ((((x) >> ID_AA64DFR0_BRPs_SHIFT) & 0xf) + 1) +#define ID_AA64DFR0_WRPs_SHIFT 20 +#define ID_AA64DFR0_WRPs_MASK (UL(0xf) << ID_AA64DFR0_WRPs_SHIFT) +#define ID_AA64DFR0_WRPs(x) \ + ((((x) >> ID_AA64DFR0_WRPs_SHIFT) & 0xf) + 1) +#define ID_AA64DFR0_CTX_CMPs_SHIFT 28 +#define ID_AA64DFR0_CTX_CMPs_MASK (UL(0xf) << ID_AA64DFR0_CTX_CMPs_SHIFT) +#define ID_AA64DFR0_CTX_CMPs(x) \ + ((((x) >> ID_AA64DFR0_CTX_CMPs_SHIFT) & 0xf) + 1) +#define ID_AA64DFR0_PMSVer_SHIFT 32 +#define ID_AA64DFR0_PMSVer_MASK (UL(0xf) << ID_AA64DFR0_PMSVer_SHIFT) +#define ID_AA64DFR0_PMSVer(x) ((x) & ID_AA64DFR0_PMSVer_MASK) +#define ID_AA64DFR0_PMSVer_NONE (UL(0x0) << ID_AA64DFR0_PMSVer_SHIFT) +#define ID_AA64DFR0_PMSVer_V1 (UL(0x1) << ID_AA64DFR0_PMSVer_SHIFT) + +/* ID_AA64ISAR0_EL1 */ +#define ID_AA64ISAR0_MASK UL(0x0000fffff0fffff0) +#define ID_AA64ISAR0_AES_SHIFT 4 +#define ID_AA64ISAR0_AES_MASK (UL(0xf) << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES(x) ((x) & ID_AA64ISAR0_AES_MASK) +#define ID_AA64ISAR0_AES_NONE (UL(0x0) << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES_BASE (UL(0x1) << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES_PMULL (UL(0x2) << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_SHA1_SHIFT 8 +#define ID_AA64ISAR0_SHA1_MASK (UL(0xf) << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA1(x) ((x) & ID_AA64ISAR0_SHA1_MASK) +#define ID_AA64ISAR0_SHA1_NONE (UL(0x0) << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA1_BASE (UL(0x1) << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA2_SHIFT 12 +#define ID_AA64ISAR0_SHA2_MASK (UL(0xf) << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_SHA2(x) ((x) & ID_AA64ISAR0_SHA2_MASK) +#define ID_AA64ISAR0_SHA2_NONE (UL(0x0) << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_SHA2_BASE (UL(0x1) << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_SHA2_512 (UL(0x2) << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_CRC32_SHIFT 16 +#define ID_AA64ISAR0_CRC32_MASK (UL(0xf) << ID_AA64ISAR0_CRC32_SHIFT) +#define ID_AA64ISAR0_CRC32(x) ((x) & ID_AA64ISAR0_CRC32_MASK) +#define ID_AA64ISAR0_CRC32_NONE (UL(0x0) << ID_AA64ISAR0_CRC32_SHIFT) +#define ID_AA64ISAR0_CRC32_BASE (UL(0x1) << ID_AA64ISAR0_CRC32_SHIFT) +#define ID_AA64ISAR0_Atomic_SHIFT 20 +#define ID_AA64ISAR0_Atomic_MASK (UL(0xf) << ID_AA64ISAR0_Atomic_SHIFT) +#define ID_AA64ISAR0_Atomic(x) ((x) & ID_AA64ISAR0_Atomic_MASK) +#define ID_AA64ISAR0_Atomic_NONE (UL(0x0) << ID_AA64ISAR0_Atomic_SHIFT) +#define ID_AA64ISAR0_Atomic_IMPL (UL(0x2) << ID_AA64ISAR0_Atomic_SHIFT) +#define ID_AA64ISAR0_RDM_SHIFT 28 +#define ID_AA64ISAR0_RDM_MASK (UL(0xf) << ID_AA64ISAR0_RDM_SHIFT) +#define ID_AA64ISAR0_RDM(x) ((x) & ID_AA64ISAR0_RDM_MASK) +#define ID_AA64ISAR0_RDM_NONE (UL(0x0) << ID_AA64ISAR0_RDM_SHIFT) +#define ID_AA64ISAR0_RDM_IMPL (UL(0x1) << ID_AA64ISAR0_RDM_SHIFT) +#define ID_AA64ISAR0_SHA3_SHIFT 32 +#define ID_AA64ISAR0_SHA3_MASK (UL(0xf) << ID_AA64ISAR0_SHA3_SHIFT) +#define ID_AA64ISAR0_SHA3(x) ((x) & ID_AA64ISAR0_SHA3_MASK) +#define ID_AA64ISAR0_SHA3_NONE (UL(0x0) << ID_AA64ISAR0_SHA3_SHIFT) +#define ID_AA64ISAR0_SHA3_IMPL (UL(0x1) << ID_AA64ISAR0_SHA3_SHIFT) +#define ID_AA64ISAR0_SM3_SHIFT 36 +#define ID_AA64ISAR0_SM3_MASK (UL(0xf) << ID_AA64ISAR0_SM3_SHIFT) +#define ID_AA64ISAR0_SM3(x) ((x) & ID_AA64ISAR0_SM3_MASK) +#define ID_AA64ISAR0_SM3_NONE (UL(0x0) << ID_AA64ISAR0_SM3_SHIFT) +#define ID_AA64ISAR0_SM3_IMPL (UL(0x1) << ID_AA64ISAR0_SM3_SHIFT) +#define ID_AA64ISAR0_SM4_SHIFT 40 +#define ID_AA64ISAR0_SM4_MASK (UL(0xf) << ID_AA64ISAR0_SM4_SHIFT) +#define ID_AA64ISAR0_SM4(x) ((x) & ID_AA64ISAR0_SM4_MASK) +#define ID_AA64ISAR0_SM4_NONE (UL(0x0) << ID_AA64ISAR0_SM4_SHIFT) +#define ID_AA64ISAR0_SM4_IMPL (UL(0x1) << ID_AA64ISAR0_SM4_SHIFT) +#define ID_AA64ISAR0_DP_SHIFT 44 +#define ID_AA64ISAR0_DP_MASK (UL(0xf) << ID_AA64ISAR0_DP_SHIFT) +#define ID_AA64ISAR0_DP(x) ((x) & ID_AA64ISAR0_DP_MASK) +#define ID_AA64ISAR0_DP_NONE (UL(0x0) << ID_AA64ISAR0_DP_SHIFT) +#define ID_AA64ISAR0_DP_IMPL (UL(0x1) << ID_AA64ISAR0_DP_SHIFT) + +/* ID_AA64ISAR1_EL1 */ +#define ID_AA64ISAR1_MASK UL(0x00000000ffffffff) +#define ID_AA64ISAR1_DPB_SHIFT 0 +#define ID_AA64ISAR1_DPB_MASK (UL(0xf) << ID_AA64ISAR1_DPB_SHIFT) +#define ID_AA64ISAR1_DPB(x) ((x) & ID_AA64ISAR1_DPB_MASK) +#define ID_AA64ISAR1_DPB_NONE (UL(0x0) << ID_AA64ISAR1_DPB_SHIFT) +#define ID_AA64ISAR1_DPB_IMPL (UL(0x1) << ID_AA64ISAR1_DPB_SHIFT) +#define ID_AA64ISAR1_APA_SHIFT 4 +#define ID_AA64ISAR1_APA_MASK (UL(0xf) << ID_AA64ISAR1_APA_SHIFT) +#define ID_AA64ISAR1_APA(x) ((x) & ID_AA64ISAR1_APA_MASK) +#define ID_AA64ISAR1_APA_NONE (UL(0x0) << ID_AA64ISAR1_APA_SHIFT) +#define ID_AA64ISAR1_APA_IMPL (UL(0x1) << ID_AA64ISAR1_APA_SHIFT) +#define ID_AA64ISAR1_API_SHIFT 8 +#define ID_AA64ISAR1_API_MASK (UL(0xf) << ID_AA64ISAR1_API_SHIFT) +#define ID_AA64ISAR1_API(x) ((x) & ID_AA64ISAR1_API_MASK) +#define ID_AA64ISAR1_API_NONE (UL(0x0) << ID_AA64ISAR1_API_SHIFT) +#define ID_AA64ISAR1_API_IMPL (UL(0x1) << ID_AA64ISAR1_API_SHIFT) +#define ID_AA64ISAR1_JSCVT_SHIFT 12 +#define ID_AA64ISAR1_JSCVT_MASK (UL(0xf) << ID_AA64ISAR1_JSCVT_SHIFT) +#define ID_AA64ISAR1_JSCVT(x) ((x) & ID_AA64ISAR1_JSCVT_MASK) +#define ID_AA64ISAR1_JSCVT_NONE (UL(0x0) << ID_AA64ISAR1_JSCVT_SHIFT) +#define ID_AA64ISAR1_JSCVT_IMPL (UL(0x1) << ID_AA64ISAR1_JSCVT_SHIFT) +#define ID_AA64ISAR1_FCMA_SHIFT 16 +#define ID_AA64ISAR1_FCMA_MASK (UL(0xf) << ID_AA64ISAR1_FCMA_SHIFT) +#define ID_AA64ISAR1_FCMA(x) ((x) & ID_AA64ISAR1_FCMA_MASK) +#define ID_AA64ISAR1_FCMA_NONE (UL(0x0) << ID_AA64ISAR1_FCMA_SHIFT) +#define ID_AA64ISAR1_FCMA_IMPL (UL(0x1) << ID_AA64ISAR1_FCMA_SHIFT) +#define ID_AA64ISAR1_LRCPC_SHIFT 20 +#define ID_AA64ISAR1_LRCPC_MASK (UL(0xf) << ID_AA64ISAR1_LRCPC_SHIFT) +#define ID_AA64ISAR1_LRCPC(x) ((x) & ID_AA64ISAR1_LRCPC_MASK) +#define ID_AA64ISAR1_LRCPC_NONE (UL(0x0) << ID_AA64ISAR1_LRCPC_SHIFT) +#define ID_AA64ISAR1_LRCPC_IMPL (UL(0x1) << ID_AA64ISAR1_LRCPC_SHIFT) +#define ID_AA64ISAR1_GPA_SHIFT 24 +#define ID_AA64ISAR1_GPA_MASK (UL(0xf) << ID_AA64ISAR1_GPA_SHIFT) +#define ID_AA64ISAR1_GPA(x) ((x) & ID_AA64ISAR1_GPA_MASK) +#define ID_AA64ISAR1_GPA_NONE (UL(0x0) << ID_AA64ISAR1_GPA_SHIFT) +#define ID_AA64ISAR1_GPA_IMPL (UL(0x1) << ID_AA64ISAR1_GPA_SHIFT) +#define ID_AA64ISAR1_GPI_SHIFT 28 +#define ID_AA64ISAR1_GPI_MASK (UL(0xf) << ID_AA64ISAR1_GPI_SHIFT) +#define ID_AA64ISAR1_GPI(x) ((x) & ID_AA64ISAR1_GPI_MASK) +#define ID_AA64ISAR1_GPI_NONE (UL(0x0) << ID_AA64ISAR1_GPI_SHIFT) +#define ID_AA64ISAR1_GPI_IMPL (UL(0x1) << ID_AA64ISAR1_GPI_SHIFT) + +/* ID_AA64MMFR0_EL1 */ +#define ID_AA64MMFR0_MASK UL(0x00000000ffffffff) +#define ID_AA64MMFR0_PARange_SHIFT 0 +#define ID_AA64MMFR0_PARange_MASK (UL(0xf) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange(x) ((x) & ID_AA64MMFR0_PARange_MASK) +#define ID_AA64MMFR0_PARange_4G (UL(0x0) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_64G (UL(0x1) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_1T (UL(0x2) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_4T (UL(0x3) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_16T (UL(0x4) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_256T (UL(0x5) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_PARange_4P (UL(0x6) << ID_AA64MMFR0_PARange_SHIFT) +#define ID_AA64MMFR0_ASIDBits_SHIFT 4 +#define ID_AA64MMFR0_ASIDBits_MASK (UL(0xf) << ID_AA64MMFR0_ASIDBits_SHIFT) +#define ID_AA64MMFR0_ASIDBits(x) ((x) & ID_AA64MMFR0_ASIDBits_MASK) +#define ID_AA64MMFR0_ASIDBits_8 (UL(0x0) << ID_AA64MMFR0_ASIDBits_SHIFT) +#define ID_AA64MMFR0_ASIDBits_16 (UL(0x2) << ID_AA64MMFR0_ASIDBits_SHIFT) +#define ID_AA64MMFR0_BigEnd_SHIFT 8 +#define ID_AA64MMFR0_BigEnd_MASK (UL(0xf) << ID_AA64MMFR0_BigEnd_SHIFT) +#define ID_AA64MMFR0_BigEnd(x) ((x) & ID_AA64MMFR0_BigEnd_MASK) +#define ID_AA64MMFR0_BigEnd_FIXED (UL(0x0) << ID_AA64MMFR0_BigEnd_SHIFT) +#define ID_AA64MMFR0_BigEnd_MIXED (UL(0x1) << ID_AA64MMFR0_BigEnd_SHIFT) +#define ID_AA64MMFR0_SNSMem_SHIFT 12 +#define ID_AA64MMFR0_SNSMem_MASK (UL(0xf) << ID_AA64MMFR0_SNSMem_SHIFT) +#define ID_AA64MMFR0_SNSMem(x) ((x) & ID_AA64MMFR0_SNSMem_MASK) +#define ID_AA64MMFR0_SNSMem_NONE (UL(0x0) << ID_AA64MMFR0_SNSMem_SHIFT) +#define ID_AA64MMFR0_SNSMem_DISTINCT (UL(0x1) << ID_AA64MMFR0_SNSMem_SHIFT) +#define ID_AA64MMFR0_BigEndEL0_SHIFT 16 +#define ID_AA64MMFR0_BigEndEL0_MASK (UL(0xf) << ID_AA64MMFR0_BigEndEL0_SHIFT) +#define ID_AA64MMFR0_BigEndEL0(x) ((x) & ID_AA64MMFR0_BigEndEL0_MASK) +#define ID_AA64MMFR0_BigEndEL0_FIXED (UL(0x0) << ID_AA64MMFR0_BigEndEL0_SHIFT) +#define ID_AA64MMFR0_BigEndEL0_MIXED (UL(0x1) << ID_AA64MMFR0_BigEndEL0_SHIFT) +#define ID_AA64MMFR0_TGran16_SHIFT 20 +#define ID_AA64MMFR0_TGran16_MASK (UL(0xf) << ID_AA64MMFR0_TGran16_SHIFT) +#define ID_AA64MMFR0_TGran16(x) ((x) & ID_AA64MMFR0_TGran16_MASK) +#define ID_AA64MMFR0_TGran16_NONE (UL(0x0) << ID_AA64MMFR0_TGran16_SHIFT) +#define ID_AA64MMFR0_TGran16_IMPL (UL(0x1) << ID_AA64MMFR0_TGran16_SHIFT) +#define ID_AA64MMFR0_TGran64_SHIFT 24 +#define ID_AA64MMFR0_TGran64_MASK (UL(0xf) << ID_AA64MMFR0_TGran64_SHIFT) +#define ID_AA64MMFR0_TGran64(x) ((x) & ID_AA64MMFR0_TGran64_MASK) +#define ID_AA64MMFR0_TGran64_IMPL (UL(0x0) << ID_AA64MMFR0_TGran64_SHIFT) +#define ID_AA64MMFR0_TGran64_NONE (UL(0xf) << ID_AA64MMFR0_TGran64_SHIFT) +#define ID_AA64MMFR0_TGran4_SHIFT 28 +#define ID_AA64MMFR0_TGran4_MASK (UL(0xf) << ID_AA64MMFR0_TGran4_SHIFT) +#define ID_AA64MMFR0_TGran4(x) ((x) & ID_AA64MMFR0_TGran4_MASK) +#define ID_AA64MMFR0_TGran4_IMPL (UL(0x0) << ID_AA64MMFR0_TGran4_SHIFT) +#define ID_AA64MMFR0_TGran4_NONE (UL(0xf) << ID_AA64MMFR0_TGran4_SHIFT) + +/* ID_AA64MMFR1_EL1 */ +#define ID_AA64MMFR1_MASK UL(0x00000000ffffffff) +#define ID_AA64MMFR1_HAFDBS_SHIFT 0 +#define ID_AA64MMFR1_HAFDBS_MASK (UL(0xf) << ID_AA64MMFR1_HAFDBS_SHIFT) +#define ID_AA64MMFR1_HAFDBS(x) ((x) & ID_AA64MMFR1_HAFDBS_MASK) +#define ID_AA64MMFR1_HAFDBS_NONE (UL(0x0) << ID_AA64MMFR1_HAFDBS_SHIFT) +#define ID_AA64MMFR1_HAFDBS_AF (UL(0x1) << ID_AA64MMFR1_HAFDBS_SHIFT) +#define ID_AA64MMFR1_HAFDBS_AF_DBS (UL(0x2) << ID_AA64MMFR1_HAFDBS_SHIFT) +#define ID_AA64MMFR1_VMIDBits_SHIFT 4 +#define ID_AA64MMFR1_VMIDBits_MASK (UL(0xf) << ID_AA64MMFR1_VMIDBits_SHIFT) +#define ID_AA64MMFR1_VMIDBits(x) ((x) & ID_AA64MMFR1_VMIDBits_MASK) +#define ID_AA64MMFR1_VMIDBits_8 (UL(0x0) << ID_AA64MMFR1_VMIDBits_SHIFT) +#define ID_AA64MMFR1_VMIDBits_16 (UL(0x2) << ID_AA64MMFR1_VMIDBits_SHIFT) +#define ID_AA64MMFR1_VH_SHIFT 8 +#define ID_AA64MMFR1_VH_MASK (UL(0xf) << ID_AA64MMFR1_VH_SHIFT) +#define ID_AA64MMFR1_VH(x) ((x) & ID_AA64MMFR1_VH_MASK) +#define ID_AA64MMFR1_VH_NONE (UL(0x0) << ID_AA64MMFR1_VH_SHIFT) +#define ID_AA64MMFR1_VH_IMPL (UL(0x1) << ID_AA64MMFR1_VH_SHIFT) +#define ID_AA64MMFR1_HPDS_SHIFT 12 +#define ID_AA64MMFR1_HPDS_MASK (UL(0xf) << ID_AA64MMFR1_HPDS_SHIFT) +#define ID_AA64MMFR1_HPDS(x) ((x) & ID_AA64MMFR1_HPDS_MASK) +#define ID_AA64MMFR1_HPDS_NONE (UL(0x0) << ID_AA64MMFR1_HPDS_SHIFT) +#define ID_AA64MMFR1_HPDS_HPD (UL(0x1) << ID_AA64MMFR1_HPDS_SHIFT) +#define ID_AA64MMFR1_HPDS_TTPBHA (UL(0x2) << ID_AA64MMFR1_HPDS_SHIFT) +#define ID_AA64MMFR1_LO_SHIFT 16 +#define ID_AA64MMFR1_LO_MASK (UL(0xf) << ID_AA64MMFR1_LO_SHIFT) +#define ID_AA64MMFR1_LO(x) ((x) & ID_AA64MMFR1_LO_MASK) +#define ID_AA64MMFR1_LO_NONE (UL(0x0) << ID_AA64MMFR1_LO_SHIFT) +#define ID_AA64MMFR1_LO_IMPL (UL(0x1) << ID_AA64MMFR1_LO_SHIFT) +#define ID_AA64MMFR1_PAN_SHIFT 20 +#define ID_AA64MMFR1_PAN_MASK (UL(0xf) << ID_AA64MMFR1_PAN_SHIFT) +#define ID_AA64MMFR1_PAN(x) ((x) & ID_AA64MMFR1_PAN_MASK) +#define ID_AA64MMFR1_PAN_NONE (UL(0x0) << ID_AA64MMFR1_PAN_SHIFT) +#define ID_AA64MMFR1_PAN_IMPL (UL(0x1) << ID_AA64MMFR1_PAN_SHIFT) +#define ID_AA64MMFR1_PAN_ATS1E1 (UL(0x2) << ID_AA64MMFR1_PAN_SHIFT) +#define ID_AA64MMFR1_SpecSEI_SHIFT 24 +#define ID_AA64MMFR1_SpecSEI_MASK (UL(0xf) << ID_AA64MMFR1_SpecSEI_SHIFT) +#define ID_AA64MMFR1_SpecSEI(x) ((x) & ID_AA64MMFR1_SpecSEI_MASK) +#define ID_AA64MMFR1_SpecSEI_NONE (UL(0x0) << ID_AA64MMFR1_SpecSEI_SHIFT) +#define ID_AA64MMFR1_SpecSEI_IMPL (UL(0x1) << ID_AA64MMFR1_SpecSEI_SHIFT) +#define ID_AA64MMFR1_XNX_SHIFT 28 +#define ID_AA64MMFR1_XNX_MASK (UL(0xf) << ID_AA64MMFR1_XNX_SHIFT) +#define ID_AA64MMFR1_XNX(x) ((x) & ID_AA64MMFR1_XNX_MASK) +#define ID_AA64MMFR1_XNX_NONE (UL(0x0) << ID_AA64MMFR1_XNX_SHIFT) +#define ID_AA64MMFR1_XNX_IMPL (UL(0x1) << ID_AA64MMFR1_XNX_SHIFT) + +/* ID_AA64MMFR2_EL1 */ +#define ID_AA64MMFR2_EL1 S3_0_C0_C7_2 +#define ID_AA64MMFR2_MASK UL(0x000000000fffffff) +#define ID_AA64MMFR2_CnP_SHIFT 0 +#define ID_AA64MMFR2_CnP_MASK (UL(0xf) << ID_AA64MMFR2_CnP_SHIFT) +#define ID_AA64MMFR2_CnP(x) ((x) & ID_AA64MMFR2_CnP_MASK) +#define ID_AA64MMFR2_CnP_NONE (UL(0x0) << ID_AA64MMFR2_CnP_SHIFT) +#define ID_AA64MMFR2_CnP_IMPL (UL(0x1) << ID_AA64MMFR2_CnP_SHIFT) +#define ID_AA64MMFR2_UAO_SHIFT 4 +#define ID_AA64MMFR2_UAO_MASK (UL(0xf) << ID_AA64MMFR2_UAO_SHIFT) +#define ID_AA64MMFR2_UAO(x) ((x) & ID_AA64MMFR2_UAO_MASK) +#define ID_AA64MMFR2_UAO_NONE (UL(0x0) << ID_AA64MMFR2_UAO_SHIFT) +#define ID_AA64MMFR2_UAO_IMPL (UL(0x1) << ID_AA64MMFR2_UAO_SHIFT) +#define ID_AA64MMFR2_LSM_SHIFT 8 +#define ID_AA64MMFR2_LSM_MASK (UL(0xf) << ID_AA64MMFR2_LSM_SHIFT) +#define ID_AA64MMFR2_LSM(x) ((x) & ID_AA64MMFR2_LSM_MASK) +#define ID_AA64MMFR2_LSM_NONE (UL(0x0) << ID_AA64MMFR2_LSM_SHIFT) +#define ID_AA64MMFR2_LSM_IMPL (UL(0x1) << ID_AA64MMFR2_LSM_SHIFT) +#define ID_AA64MMFR2_IESB_SHIFT 12 +#define ID_AA64MMFR2_IESB_MASK (UL(0xf) << ID_AA64MMFR2_IESB_SHIFT) +#define ID_AA64MMFR2_IESB(x) ((x) & ID_AA64MMFR2_IESB_MASK) +#define ID_AA64MMFR2_IESB_NONE (UL(0x0) << ID_AA64MMFR2_IESB_SHIFT) +#define ID_AA64MMFR2_IESB_IMPL (UL(0x1) << ID_AA64MMFR2_IESB_SHIFT) +#define ID_AA64MMFR2_VARange_SHIFT 16 +#define ID_AA64MMFR2_VARange_MASK (UL(0xf) << ID_AA64MMFR2_VARange_SHIFT) +#define ID_AA64MMFR2_VARange(x) ((x) & ID_AA64MMFR2_VARange_MASK) +#define ID_AA64MMFR2_VARange_48 (UL(0x0) << ID_AA64MMFR2_VARange_SHIFT) +#define ID_AA64MMFR2_VARange_52 (UL(0x1) << ID_AA64MMFR2_VARange_SHIFT) +#define ID_AA64MMFR2_CCIDX_SHIFT 20 +#define ID_AA64MMFR2_CCIDX_MASK (UL(0xf) << ID_AA64MMFR2_CCIDX_SHIFT) +#define ID_AA64MMFR2_CCIDX(x) ((x) & ID_AA64MMFR2_CCIDX_MASK) +#define ID_AA64MMFR2_CCIDX_32 (UL(0x0) << ID_AA64MMFR2_CCIDX_SHIFT) +#define ID_AA64MMFR2_CCIDX_64 (UL(0x1) << ID_AA64MMFR2_CCIDX_SHIFT) +#define ID_AA64MMFR2_NV_SHIFT 24 +#define ID_AA64MMFR2_NV_MASK (UL(0xf) << ID_AA64MMFR2_NV_SHIFT) +#define ID_AA64MMFR2_NV(x) ((x) & ID_AA64MMFR2_NV_MASK) +#define ID_AA64MMFR2_NV_NONE (UL(0x0) << ID_AA64MMFR2_NV_SHIFT) +#define ID_AA64MMFR2_NV_IMPL (UL(0x1) << ID_AA64MMFR2_NV_SHIFT) + +/* ID_AA64PFR0_EL1 */ +#define ID_AA64PFR0_MASK UL(0x0000000fffffffff) +#define ID_AA64PFR0_EL0_SHIFT 0 +#define ID_AA64PFR0_EL0_MASK (UL(0xf) << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL0(x) ((x) & ID_AA64PFR0_EL0_MASK) +#define ID_AA64PFR0_EL0_64 (UL(0x1) << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL0_64_32 (UL(0x2) << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL1_SHIFT 4 +#define ID_AA64PFR0_EL1_MASK (UL(0xf) << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL1(x) ((x) & ID_AA64PFR0_EL1_MASK) +#define ID_AA64PFR0_EL1_64 (UL(0x1) << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL1_64_32 (UL(0x2) << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL2_SHIFT 8 +#define ID_AA64PFR0_EL2_MASK (UL(0xf) << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2(x) ((x) & ID_AA64PFR0_EL2_MASK) +#define ID_AA64PFR0_EL2_NONE (UL(0x0) << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2_64 (UL(0x1) << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2_64_32 (UL(0x2) << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL3_SHIFT 12 +#define ID_AA64PFR0_EL3_MASK (UL(0xf) << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3(x) ((x) & ID_AA64PFR0_EL3_MASK) +#define ID_AA64PFR0_EL3_NONE (UL(0x0) << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3_64 (UL(0x1) << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3_64_32 (UL(0x2) << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_FP_SHIFT 16 +#define ID_AA64PFR0_FP_MASK (UL(0xf) << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_FP(x) ((x) & ID_AA64PFR0_FP_MASK) +#define ID_AA64PFR0_FP_IMPL (UL(0x0) << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_FP_HP (UL(0x1) << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_FP_NONE (UL(0xf) << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_AdvSIMD_SHIFT 20 +#define ID_AA64PFR0_AdvSIMD_MASK (UL(0xf) << ID_AA64PFR0_AdvSIMD_SHIFT) +#define ID_AA64PFR0_AdvSIMD(x) ((x) & ID_AA64PFR0_AdvSIMD_MASK) +#define ID_AA64PFR0_AdvSIMD_IMPL (UL(0x0) << ID_AA64PFR0_AdvSIMD_SHIFT) +#define ID_AA64PFR0_AdvSIMD_HP (UL(0x1) << ID_AA64PFR0_AdvSIMD_SHIFT) +#define ID_AA64PFR0_AdvSIMD_NONE (UL(0xf) << ID_AA64PFR0_AdvSIMD_SHIFT) +#define ID_AA64PFR0_GIC_BITS 0x4 /* Number of bits in GIC field */ +#define ID_AA64PFR0_GIC_SHIFT 24 +#define ID_AA64PFR0_GIC_MASK (UL(0xf) << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_GIC(x) ((x) & ID_AA64PFR0_GIC_MASK) +#define ID_AA64PFR0_GIC_CPUIF_NONE (UL(0x0) << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_GIC_CPUIF_EN (UL(0x1) << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_RAS_SHIFT 28 +#define ID_AA64PFR0_RAS_MASK (UL(0xf) << ID_AA64PFR0_RAS_SHIFT) +#define ID_AA64PFR0_RAS(x) ((x) & ID_AA64PFR0_RAS_MASK) +#define ID_AA64PFR0_RAS_NONE (UL(0x0) << ID_AA64PFR0_RAS_SHIFT) +#define ID_AA64PFR0_RAS_V1 (UL(0x1) << ID_AA64PFR0_RAS_SHIFT) +#define ID_AA64PFR0_SVE_SHIFT 32 +#define ID_AA64PFR0_SVE_MASK (UL(0xf) << ID_AA64PFR0_SVE_SHIFT) +#define ID_AA64PFR0_SVE(x) ((x) & ID_AA64PFR0_SVE_MASK) +#define ID_AA64PFR0_SVE_NONE (UL(0x0) << ID_AA64PFR0_SVE_SHIFT) +#define ID_AA64PFR0_SVE_IMPL (UL(0x1) << ID_AA64PFR0_SVE_SHIFT) + +/* MAIR_EL1 - Memory Attribute Indirection Register */ +#define MAIR_ATTR_MASK(idx) (0xff << ((n)* 8)) +#define MAIR_ATTR(attr, idx) ((attr) << ((idx) * 8)) +#define MAIR_DEVICE_nGnRnE 0x00 +#define MAIR_NORMAL_NC 0x44 +#define MAIR_NORMAL_WT 0xbb +#define MAIR_NORMAL_WB 0xff + +/* PAR_EL1 - Physical Address Register */ +#define PAR_F_SHIFT 0 +#define PAR_F (0x1 << PAR_F_SHIFT) +#define PAR_SUCCESS(x) (((x) & PAR_F) == 0) +/* When PAR_F == 0 (success) */ +#define PAR_SH_SHIFT 7 +#define PAR_SH_MASK (0x3 << PAR_SH_SHIFT) +#define PAR_NS_SHIFT 9 +#define PAR_NS_MASK (0x3 << PAR_NS_SHIFT) +#define PAR_PA_SHIFT 12 +#define PAR_PA_MASK 0x0000fffffffff000 +#define PAR_ATTR_SHIFT 56 +#define PAR_ATTR_MASK (0xff << PAR_ATTR_SHIFT) +/* When PAR_F == 1 (aborted) */ +#define PAR_FST_SHIFT 1 +#define PAR_FST_MASK (0x3f << PAR_FST_SHIFT) +#define PAR_PTW_SHIFT 8 +#define PAR_PTW_MASK (0x1 << PAR_PTW_SHIFT) +#define PAR_S_SHIFT 9 +#define PAR_S_MASK (0x1 << PAR_S_SHIFT) + +/* SCTLR_EL1 - System Control Register */ +#define SCTLR_RES0 0xc8222440 /* Reserved ARMv8.0, write 0 */ +#define SCTLR_RES1 0x30d00800 /* Reserved ARMv8.0, write 1 */ + +#define SCTLR_M 0x00000001 +#define SCTLR_A 0x00000002 +#define SCTLR_C 0x00000004 +#define SCTLR_SA 0x00000008 +#define SCTLR_SA0 0x00000010 +#define SCTLR_CP15BEN 0x00000020 +/* Bit 6 is reserved */ +#define SCTLR_ITD 0x00000080 +#define SCTLR_SED 0x00000100 +#define SCTLR_UMA 0x00000200 +/* Bit 10 is reserved */ +/* Bit 11 is reserved */ +#define SCTLR_I 0x00001000 +#define SCTLR_EnDB 0x00002000 /* ARMv8.3 */ +#define SCTLR_DZE 0x00004000 +#define SCTLR_UCT 0x00008000 +#define SCTLR_nTWI 0x00010000 +/* Bit 17 is reserved */ +#define SCTLR_nTWE 0x00040000 +#define SCTLR_WXN 0x00080000 +/* Bit 20 is reserved */ +#define SCTLR_IESB 0x00200000 /* ARMv8.2 */ +/* Bit 22 is reserved */ +#define SCTLR_SPAN 0x00800000 /* ARMv8.1 */ +#define SCTLR_EOE 0x01000000 +#define SCTLR_EE 0x02000000 +#define SCTLR_UCI 0x04000000 +#define SCTLR_EnDA 0x08000000 /* ARMv8.3 */ +#define SCTLR_nTLSMD 0x10000000 /* ARMv8.2 */ +#define SCTLR_LSMAOE 0x20000000 /* ARMv8.2 */ +#define SCTLR_EnIB 0x40000000 /* ARMv8.3 */ +#define SCTLR_EnIA 0x80000000 /* ARMv8.3 */ + +/* SPSR_EL1 */ +/* + * When the exception is taken in AArch64: + * M[3:2] is the exception level + * M[1] is unused + * M[0] is the SP select: + * 0: always SP0 + * 1: current ELs SP + */ +#define PSR_M_EL0t 0x00000000 +#define PSR_M_EL1t 0x00000004 +#define PSR_M_EL1h 0x00000005 +#define PSR_M_EL2t 0x00000008 +#define PSR_M_EL2h 0x00000009 +#define PSR_M_MASK 0x0000000f + +#define PSR_AARCH32 0x00000010 +#define PSR_F 0x00000040 +#define PSR_I 0x00000080 +#define PSR_A 0x00000100 +#define PSR_D 0x00000200 +#define PSR_IL 0x00100000 +#define PSR_SS 0x00200000 +#define PSR_V 0x10000000 +#define PSR_C 0x20000000 +#define PSR_Z 0x40000000 +#define PSR_N 0x80000000 +#define PSR_FLAGS 0xf0000000 + +/* TCR_EL1 - Translation Control Register */ +#define TCR_ASID_16 (1 << 36) + +#define TCR_IPS_SHIFT 32 +#define TCR_IPS_32BIT (0 << TCR_IPS_SHIFT) +#define TCR_IPS_36BIT (1 << TCR_IPS_SHIFT) +#define TCR_IPS_40BIT (2 << TCR_IPS_SHIFT) +#define TCR_IPS_42BIT (3 << TCR_IPS_SHIFT) +#define TCR_IPS_44BIT (4 << TCR_IPS_SHIFT) +#define TCR_IPS_48BIT (5 << TCR_IPS_SHIFT) + +#define TCR_TG1_SHIFT 30 +#define TCR_TG1_16K (1 << TCR_TG1_SHIFT) +#define TCR_TG1_4K (2 << TCR_TG1_SHIFT) +#define TCR_TG1_64K (3 << TCR_TG1_SHIFT) + +#define TCR_SH1_SHIFT 28 +#define TCR_SH1_IS (0x3UL << TCR_SH1_SHIFT) +#define TCR_ORGN1_SHIFT 26 +#define TCR_ORGN1_WBWA (0x1UL << TCR_ORGN1_SHIFT) +#define TCR_IRGN1_SHIFT 24 +#define TCR_IRGN1_WBWA (0x1UL << TCR_IRGN1_SHIFT) +#define TCR_SH0_SHIFT 12 +#define TCR_SH0_IS (0x3UL << TCR_SH0_SHIFT) +#define TCR_ORGN0_SHIFT 10 +#define TCR_ORGN0_WBWA (0x1UL << TCR_ORGN0_SHIFT) +#define TCR_IRGN0_SHIFT 8 +#define TCR_IRGN0_WBWA (0x1UL << TCR_IRGN0_SHIFT) + +#define TCR_CACHE_ATTRS ((TCR_IRGN0_WBWA | TCR_IRGN1_WBWA) |\ + (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA)) + +#ifdef SMP +#define TCR_SMP_ATTRS (TCR_SH0_IS | TCR_SH1_IS) +#else +#define TCR_SMP_ATTRS 0 +#endif + +#define TCR_T1SZ_SHIFT 16 +#define TCR_T0SZ_SHIFT 0 +#define TCR_T1SZ(x) ((x) << TCR_T1SZ_SHIFT) +#define TCR_T0SZ(x) ((x) << TCR_T0SZ_SHIFT) +#define TCR_TxSZ(x) (TCR_T1SZ(x) | TCR_T0SZ(x)) + +/* Saved Program Status Register */ +#define DBG_SPSR_SS (0x1 << 21) + +/* Monitor Debug System Control Register */ +#define DBG_MDSCR_SS (0x1 << 0) +#define DBG_MDSCR_KDE (0x1 << 13) +#define DBG_MDSCR_MDE (0x1 << 15) + +/* Perfomance Monitoring Counters */ +#define PMCR_E (1 << 0) /* Enable all counters */ +#define PMCR_P (1 << 1) /* Reset all counters */ +#define PMCR_C (1 << 2) /* Clock counter reset */ +#define PMCR_D (1 << 3) /* CNTR counts every 64 clk cycles */ +#define PMCR_X (1 << 4) /* Export to ext. monitoring (ETM) */ +#define PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ +#define PMCR_LC (1 << 6) /* Long cycle count enable */ +#define PMCR_IMP_SHIFT 24 /* Implementer code */ +#define PMCR_IMP_MASK (0xff << PMCR_IMP_SHIFT) +#define PMCR_IDCODE_SHIFT 16 /* Identification code */ +#define PMCR_IDCODE_MASK (0xff << PMCR_IDCODE_SHIFT) +#define PMCR_IDCODE_CORTEX_A57 0x01 +#define PMCR_IDCODE_CORTEX_A72 0x02 +#define PMCR_IDCODE_CORTEX_A53 0x03 +#define PMCR_N_SHIFT 11 /* Number of counters implemented */ +#define PMCR_N_MASK (0x1f << PMCR_N_SHIFT) + +#endif /* !_MACHINE_ARMREG_H_ */ diff --git a/freebsd/sys/arm64/include/machine/cpu.h b/freebsd/sys/arm64/include/machine/cpu.h new file mode 100644 index 00000000..5663e50e --- /dev/null +++ b/freebsd/sys/arm64/include/machine/cpu.h @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * Copyright (c) 2014-2016 The FreeBSD Foundation + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 + * from: FreeBSD: src/sys/i386/include/cpu.h,v 1.62 2001/06/29 + * $FreeBSD$ + */ + +#ifndef _MACHINE_CPU_H_ +#define _MACHINE_CPU_H_ + +#include <machine/atomic.h> +#include <machine/frame.h> +#include <machine/armreg.h> + +#define TRAPF_PC(tfp) ((tfp)->tf_lr) +#define TRAPF_USERMODE(tfp) (((tfp)->tf_spsr & PSR_M_MASK) == PSR_M_EL0t) + +#define cpu_getstack(td) ((td)->td_frame->tf_sp) +#define cpu_setstack(td, sp) ((td)->td_frame->tf_sp = (sp)) +#define cpu_spinwait() __asm __volatile("yield" ::: "memory") +#define cpu_lock_delay() DELAY(1) + +/* Extract CPU affinity levels 0-3 */ +#define CPU_AFF0(mpidr) (u_int)(((mpidr) >> 0) & 0xff) +#define CPU_AFF1(mpidr) (u_int)(((mpidr) >> 8) & 0xff) +#define CPU_AFF2(mpidr) (u_int)(((mpidr) >> 16) & 0xff) +#define CPU_AFF3(mpidr) (u_int)(((mpidr) >> 32) & 0xff) +#define CPU_AFF0_MASK 0xffUL +#define CPU_AFF1_MASK 0xff00UL +#define CPU_AFF2_MASK 0xff0000UL +#define CPU_AFF3_MASK 0xff00000000UL +#define CPU_AFF_MASK (CPU_AFF0_MASK | CPU_AFF1_MASK | \ + CPU_AFF2_MASK| CPU_AFF3_MASK) /* Mask affinity fields in MPIDR_EL1 */ + +#ifdef _KERNEL + +#define CPU_IMPL_ARM 0x41 +#define CPU_IMPL_BROADCOM 0x42 +#define CPU_IMPL_CAVIUM 0x43 +#define CPU_IMPL_DEC 0x44 +#define CPU_IMPL_INFINEON 0x49 +#define CPU_IMPL_FREESCALE 0x4D +#define CPU_IMPL_NVIDIA 0x4E +#define CPU_IMPL_APM 0x50 +#define CPU_IMPL_QUALCOMM 0x51 +#define CPU_IMPL_MARVELL 0x56 +#define CPU_IMPL_INTEL 0x69 + +/* ARM Part numbers */ +#define CPU_PART_FOUNDATION 0xD00 +#define CPU_PART_CORTEX_A35 0xD04 +#define CPU_PART_CORTEX_A53 0xD03 +#define CPU_PART_CORTEX_A55 0xD05 +#define CPU_PART_CORTEX_A57 0xD07 +#define CPU_PART_CORTEX_A72 0xD08 +#define CPU_PART_CORTEX_A73 0xD09 +#define CPU_PART_CORTEX_A75 0xD0A + +/* Cavium Part numbers */ +#define CPU_PART_THUNDERX 0x0A1 +#define CPU_PART_THUNDERX_81XX 0x0A2 +#define CPU_PART_THUNDERX_83XX 0x0A3 +#define CPU_PART_THUNDERX2 0x0AF + +#define CPU_REV_THUNDERX_1_0 0x00 +#define CPU_REV_THUNDERX_1_1 0x01 + +#define CPU_REV_THUNDERX2_0 0x00 + +/* APM / Ampere Part Number */ +#define CPU_PART_EMAG8180 0x000 + +#define CPU_IMPL(midr) (((midr) >> 24) & 0xff) +#define CPU_PART(midr) (((midr) >> 4) & 0xfff) +#define CPU_VAR(midr) (((midr) >> 20) & 0xf) +#define CPU_REV(midr) (((midr) >> 0) & 0xf) + +#define CPU_IMPL_TO_MIDR(val) (((val) & 0xff) << 24) +#define CPU_PART_TO_MIDR(val) (((val) & 0xfff) << 4) +#define CPU_VAR_TO_MIDR(val) (((val) & 0xf) << 20) +#define CPU_REV_TO_MIDR(val) (((val) & 0xf) << 0) + +#define CPU_IMPL_MASK (0xff << 24) +#define CPU_PART_MASK (0xfff << 4) +#define CPU_VAR_MASK (0xf << 20) +#define CPU_REV_MASK (0xf << 0) + +#define CPU_ID_RAW(impl, part, var, rev) \ + (CPU_IMPL_TO_MIDR((impl)) | \ + CPU_PART_TO_MIDR((part)) | CPU_VAR_TO_MIDR((var)) | \ + CPU_REV_TO_MIDR((rev))) + +#define CPU_MATCH(mask, impl, part, var, rev) \ + (((mask) & PCPU_GET(midr)) == \ + ((mask) & CPU_ID_RAW((impl), (part), (var), (rev)))) + +#define CPU_MATCH_RAW(mask, devid) \ + (((mask) & PCPU_GET(midr)) == ((mask) & (devid))) + +/* + * Chip-specific errata. This defines are intended to be + * booleans used within if statements. When an appropriate + * kernel option is disabled, these defines must be defined + * as 0 to allow the compiler to remove a dead code thus + * produce better optimized kernel image. + */ +/* + * Vendor: Cavium + * Chip: ThunderX + * Revision(s): Pass 1.0, Pass 1.1 + */ +#ifdef THUNDERX_PASS_1_1_ERRATA +#define CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1 \ + (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \ + CPU_IMPL_CAVIUM, CPU_PART_THUNDERX, 0, CPU_REV_THUNDERX_1_0) || \ + CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \ + CPU_IMPL_CAVIUM, CPU_PART_THUNDERX, 0, CPU_REV_THUNDERX_1_1)) +#else +#define CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1 0 +#endif + + +extern char btext[]; +extern char etext[]; + +extern uint64_t __cpu_affinity[]; + +void cpu_halt(void) __dead2; +void cpu_reset(void) __dead2; +void fork_trampoline(void); +void identify_cpu(void); +void install_cpu_errata(void); +void print_cpu_features(u_int); +void swi_vm(void *v); + +#define CPU_AFFINITY(cpu) __cpu_affinity[(cpu)] +#define CPU_CURRENT_SOCKET \ + (CPU_AFF2(CPU_AFFINITY(PCPU_GET(cpuid)))) + +static __inline uint64_t +get_cyclecount(void) +{ + uint64_t ret; + + ret = READ_SPECIALREG(cntvct_el0); + + return (ret); +} + +#define ADDRESS_TRANSLATE_FUNC(stage) \ +static inline uint64_t \ +arm64_address_translate_ ##stage (uint64_t addr) \ +{ \ + uint64_t ret; \ + \ + __asm __volatile( \ + "at " __STRING(stage) ", %1 \n" \ + "mrs %0, par_el1" : "=r"(ret) : "r"(addr)); \ + \ + return (ret); \ +} + +ADDRESS_TRANSLATE_FUNC(s1e0r) +ADDRESS_TRANSLATE_FUNC(s1e0w) +ADDRESS_TRANSLATE_FUNC(s1e1r) +ADDRESS_TRANSLATE_FUNC(s1e1w) + +#endif + +#endif /* !_MACHINE_CPU_H_ */ diff --git a/freebsd/sys/arm64/include/machine/cpufunc.h b/freebsd/sys/arm64/include/machine/cpufunc.h new file mode 100644 index 00000000..c3e2a36e --- /dev/null +++ b/freebsd/sys/arm64/include/machine/cpufunc.h @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2014 Andrew Turner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _MACHINE_CPUFUNC_H_ +#define _MACHINE_CPUFUNC_H_ + +static __inline void +breakpoint(void) +{ + + __asm("brk #0"); +} + +#ifdef _KERNEL + +#include <machine/armreg.h> + +void pan_enable(void); + +static __inline register_t +dbg_disable(void) +{ + uint32_t ret; + + __asm __volatile( + "mrs %x0, daif \n" + "msr daifset, #8 \n" + : "=&r" (ret)); + + return (ret); +} + +static __inline void +dbg_enable(void) +{ + + __asm __volatile("msr daifclr, #8"); +} + +static __inline register_t +intr_disable(void) +{ + /* DAIF is a 32-bit register */ + uint32_t ret; + + __asm __volatile( + "mrs %x0, daif \n" + "msr daifset, #2 \n" + : "=&r" (ret)); + + return (ret); +} + +static __inline void +intr_restore(register_t s) +{ + + WRITE_SPECIALREG(daif, s); +} + +static __inline void +intr_enable(void) +{ + + __asm __volatile("msr daifclr, #2"); +} + +static __inline register_t +get_midr(void) +{ + uint64_t midr; + + midr = READ_SPECIALREG(midr_el1); + + return (midr); +} + +static __inline register_t +get_mpidr(void) +{ + uint64_t mpidr; + + mpidr = READ_SPECIALREG(mpidr_el1); + + return (mpidr); +} + +static __inline void +clrex(void) +{ + + /* + * Ensure compiler barrier, otherwise the monitor clear might + * occur too late for us ? + */ + __asm __volatile("clrex" : : : "memory"); +} + +extern int64_t dcache_line_size; +extern int64_t icache_line_size; +extern int64_t idcache_line_size; +extern int64_t dczva_line_size; + +#define cpu_nullop() arm64_nullop() +#define cpufunc_nullop() arm64_nullop() +#define cpu_setttb(a) arm64_setttb(a) + +#define cpu_tlb_flushID() arm64_tlb_flushID() + +#define cpu_dcache_wbinv_range(a, s) arm64_dcache_wbinv_range((a), (s)) +#define cpu_dcache_inv_range(a, s) arm64_dcache_inv_range((a), (s)) +#define cpu_dcache_wb_range(a, s) arm64_dcache_wb_range((a), (s)) + +#define cpu_idcache_wbinv_range(a, s) arm64_idcache_wbinv_range((a), (s)) +#define cpu_icache_sync_range(a, s) arm64_icache_sync_range((a), (s)) + +void arm64_nullop(void); +void arm64_setttb(vm_offset_t); +void arm64_tlb_flushID(void); +void arm64_tlb_flushID_SE(vm_offset_t); +void arm64_icache_sync_range(vm_offset_t, vm_size_t); +void arm64_idcache_wbinv_range(vm_offset_t, vm_size_t); +void arm64_dcache_wbinv_range(vm_offset_t, vm_size_t); +void arm64_dcache_inv_range(vm_offset_t, vm_size_t); +void arm64_dcache_wb_range(vm_offset_t, vm_size_t); + +#endif /* _KERNEL */ +#endif /* _MACHINE_CPUFUNC_H_ */ diff --git a/freebsd/sys/arm64/include/machine/in_cksum.h b/freebsd/sys/arm64/include/machine/in_cksum.h new file mode 100644 index 00000000..522ba005 --- /dev/null +++ b/freebsd/sys/arm64/include/machine/in_cksum.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from tahoe: in_cksum.c 1.2 86/01/05 + * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 + * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp + * $FreeBSD$ + */ + +#ifndef _MACHINE_IN_CKSUM_H_ +#define _MACHINE_IN_CKSUM_H_ 1 + +#include <sys/cdefs.h> + +#ifdef _KERNEL +#define in_cksum(m, len) in_cksum_skip(m, len, 0) +u_short in_addword(u_short sum, u_short b); +u_short in_cksum_skip(struct mbuf *m, int len, int skip); +u_int do_cksum(const void *, int); +#if defined(IPVERSION) && (IPVERSION == 4) +u_int in_cksum_hdr(const struct ip *); +#endif + +u_short in_pseudo(u_int sum, u_int b, u_int c); + +#endif /* _KERNEL */ +#endif /* _MACHINE_IN_CKSUM_H_ */ diff --git a/freebsd/sys/contrib/ck/include/ck_pr.h b/freebsd/sys/contrib/ck/include/ck_pr.h index 2a8c5398..d2b53a5c 100644 --- a/freebsd/sys/contrib/ck/include/ck_pr.h +++ b/freebsd/sys/contrib/ck/include/ck_pr.h @@ -51,7 +51,9 @@ #if __ARM_ARCH >= 6 #include "gcc/arm/ck_pr.h" #else +#ifndef __rtems__ #include "gcc/arm/ck_pr_armv4.h" +#endif /* __rtems__ */ #endif #elif defined(__aarch64__) #include "gcc/aarch64/ck_pr.h" diff --git a/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h new file mode 100644 index 00000000..aa4e3090 --- /dev/null +++ b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h @@ -0,0 +1,352 @@ +/* + * Copyright 2009-2016 Samy Al Bahra. + * Copyright 2013-2016 Olivier Houchard. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CK_PR_AARCH64_LLSC_H +#define CK_PR_AARCH64_LLSC_H + +#ifndef CK_PR_H +#error Do not include this file directly, use ck_pr.h +#endif + +CK_CC_INLINE static bool +ck_pr_cas_64_2_value(uint64_t target[2], uint64_t compare[2], uint64_t set[2], uint64_t value[2]) +{ + uint64_t tmp1, tmp2; + + __asm__ __volatile__("1:" + "ldxp %0, %1, [%4];" + "mov %2, %0;" + "mov %3, %1;" + "eor %0, %0, %5;" + "eor %1, %1, %6;" + "orr %1, %0, %1;" + "mov %w0, #0;" + "cbnz %1, 2f;" + "stxp %w0, %7, %8, [%4];" + "cbnz %w0, 1b;" + "mov %w0, #1;" + "2:" + : "=&r" (tmp1), "=&r" (tmp2), "=&r" (value[0]), "=&r" (value[1]) + : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1]) + : "cc", "memory"); + + return (tmp1); +} + +CK_CC_INLINE static bool +ck_pr_cas_ptr_2_value(void *target, void *compare, void *set, void *value) +{ + return (ck_pr_cas_64_2_value(CK_CPP_CAST(uint64_t *, target), + CK_CPP_CAST(uint64_t *, compare), + CK_CPP_CAST(uint64_t *, set), + CK_CPP_CAST(uint64_t *, value))); +} + +CK_CC_INLINE static bool +ck_pr_cas_64_2(uint64_t target[2], uint64_t compare[2], uint64_t set[2]) +{ + uint64_t tmp1, tmp2; + + __asm__ __volatile__("1:" + "ldxp %0, %1, [%2];" + "eor %0, %0, %3;" + "eor %1, %1, %4;" + "orr %1, %0, %1;" + "mov %w0, #0;" + "cbnz %1, 2f;" + "stxp %w0, %5, %6, [%2];" + "cbnz %w0, 1b;" + "mov %w0, #1;" + "2:" + : "=&r" (tmp1), "=&r" (tmp2) + : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1]) + : "cc", "memory"); + + return (tmp1); +} +CK_CC_INLINE static bool +ck_pr_cas_ptr_2(void *target, void *compare, void *set) +{ + return (ck_pr_cas_64_2(CK_CPP_CAST(uint64_t *, target), + CK_CPP_CAST(uint64_t *, compare), + CK_CPP_CAST(uint64_t *, set))); +} + + +#define CK_PR_CAS(N, M, T, W, R) \ + CK_CC_INLINE static bool \ + ck_pr_cas_##N##_value(M *target, T compare, T set, M *value) \ + { \ + T previous; \ + T tmp; \ + __asm__ __volatile__("1:" \ + "ldxr" W " %" R "0, [%2];" \ + "cmp %" R "0, %" R "4;" \ + "b.ne 2f;" \ + "stxr" W " %w1, %" R "3, [%2];" \ + "cbnz %w1, 1b;" \ + "2:" \ + : "=&r" (previous), \ + "=&r" (tmp) \ + : "r" (target), \ + "r" (set), \ + "r" (compare) \ + : "memory", "cc"); \ + *(T *)value = previous; \ + return (previous == compare); \ + } \ + CK_CC_INLINE static bool \ + ck_pr_cas_##N(M *target, T compare, T set) \ + { \ + T previous; \ + T tmp; \ + __asm__ __volatile__( \ + "1:" \ + "ldxr" W " %" R "0, [%2];" \ + "cmp %" R "0, %" R "4;" \ + "b.ne 2f;" \ + "stxr" W " %w1, %" R "3, [%2];" \ + "cbnz %w1, 1b;" \ + "2:" \ + : "=&r" (previous), \ + "=&r" (tmp) \ + : "r" (target), \ + "r" (set), \ + "r" (compare) \ + : "memory", "cc"); \ + return (previous == compare); \ + } + +CK_PR_CAS(ptr, void, void *, "", "") + +#define CK_PR_CAS_S(N, M, W, R) CK_PR_CAS(N, M, M, W, R) +CK_PR_CAS_S(64, uint64_t, "", "") +#ifndef CK_PR_DISABLE_DOUBLE +CK_PR_CAS_S(double, double, "", "") +#endif +CK_PR_CAS_S(32, uint32_t, "", "w") +CK_PR_CAS_S(uint, unsigned int, "", "w") +CK_PR_CAS_S(int, int, "", "w") +CK_PR_CAS_S(16, uint16_t, "h", "w") +CK_PR_CAS_S(8, uint8_t, "b", "w") +CK_PR_CAS_S(short, short, "h", "w") +CK_PR_CAS_S(char, char, "b", "w") + + +#undef CK_PR_CAS_S +#undef CK_PR_CAS + +#define CK_PR_FAS(N, M, T, W, R) \ + CK_CC_INLINE static T \ + ck_pr_fas_##N(M *target, T v) \ + { \ + T previous; \ + T tmp; \ + __asm__ __volatile__("1:" \ + "ldxr" W " %" R "0, [%2];" \ + "stxr" W " %w1, %" R "3, [%2];"\ + "cbnz %w1, 1b;" \ + : "=&r" (previous), \ + "=&r" (tmp) \ + : "r" (target), \ + "r" (v) \ + : "memory", "cc"); \ + return (previous); \ + } + +CK_PR_FAS(64, uint64_t, uint64_t, "", "") +CK_PR_FAS(32, uint32_t, uint32_t, "", "w") +CK_PR_FAS(ptr, void, void *, "", "") +CK_PR_FAS(int, int, int, "", "w") +CK_PR_FAS(uint, unsigned int, unsigned int, "", "w") +CK_PR_FAS(16, uint16_t, uint16_t, "h", "w") +CK_PR_FAS(8, uint8_t, uint8_t, "b", "w") +CK_PR_FAS(short, short, short, "h", "w") +CK_PR_FAS(char, char, char, "b", "w") + + +#undef CK_PR_FAS + +#define CK_PR_UNARY(O, N, M, T, I, W, R) \ + CK_CC_INLINE static void \ + ck_pr_##O##_##N(M *target) \ + { \ + T previous = 0; \ + T tmp = 0; \ + __asm__ __volatile__("1:" \ + "ldxr" W " %" R "0, [%2];" \ + I ";" \ + "stxr" W " %w1, %" R "0, [%2];" \ + "cbnz %w1, 1b;" \ + : "=&r" (previous), \ + "=&r" (tmp) \ + : "r" (target) \ + : "memory", "cc"); \ + return; \ + } + +CK_PR_UNARY(inc, ptr, void, void *, "add %0, %0, #1", "", "") +CK_PR_UNARY(dec, ptr, void, void *, "sub %0, %0, #1", "", "") +CK_PR_UNARY(not, ptr, void, void *, "mvn %0, %0", "", "") +CK_PR_UNARY(inc, 64, uint64_t, uint64_t, "add %0, %0, #1", "", "") +CK_PR_UNARY(dec, 64, uint64_t, uint64_t, "sub %0, %0, #1", "", "") +CK_PR_UNARY(not, 64, uint64_t, uint64_t, "mvn %0, %0", "", "") + +#define CK_PR_UNARY_S(S, T, W) \ + CK_PR_UNARY(inc, S, T, T, "add %w0, %w0, #1", W, "w") \ + CK_PR_UNARY(dec, S, T, T, "sub %w0, %w0, #1", W, "w") \ + CK_PR_UNARY(not, S, T, T, "mvn %w0, %w0", W, "w") \ + +CK_PR_UNARY_S(32, uint32_t, "") +CK_PR_UNARY_S(uint, unsigned int, "") +CK_PR_UNARY_S(int, int, "") +CK_PR_UNARY_S(16, uint16_t, "h") +CK_PR_UNARY_S(8, uint8_t, "b") +CK_PR_UNARY_S(short, short, "h") +CK_PR_UNARY_S(char, char, "b") + +#undef CK_PR_UNARY_S +#undef CK_PR_UNARY + +#define CK_PR_BINARY(O, N, M, T, I, W, R) \ + CK_CC_INLINE static void \ + ck_pr_##O##_##N(M *target, T delta) \ + { \ + T previous; \ + T tmp; \ + __asm__ __volatile__("1:" \ + "ldxr" W " %" R "0, [%2];"\ + I " %" R "0, %" R "0, %" R "3;" \ + "stxr" W " %w1, %" R "0, [%2];" \ + "cbnz %w1, 1b;" \ + : "=&r" (previous), \ + "=&r" (tmp) \ + : "r" (target), \ + "r" (delta) \ + : "memory", "cc"); \ + return; \ + } + +CK_PR_BINARY(and, ptr, void, uintptr_t, "and", "", "") +CK_PR_BINARY(add, ptr, void, uintptr_t, "add", "", "") +CK_PR_BINARY(or, ptr, void, uintptr_t, "orr", "", "") +CK_PR_BINARY(sub, ptr, void, uintptr_t, "sub", "", "") +CK_PR_BINARY(xor, ptr, void, uintptr_t, "eor", "", "") +CK_PR_BINARY(and, 64, uint64_t, uint64_t, "and", "", "") +CK_PR_BINARY(add, 64, uint64_t, uint64_t, "add", "", "") +CK_PR_BINARY(or, 64, uint64_t, uint64_t, "orr", "", "") +CK_PR_BINARY(sub, 64, uint64_t, uint64_t, "sub", "", "") +CK_PR_BINARY(xor, 64, uint64_t, uint64_t, "eor", "", "") + +#define CK_PR_BINARY_S(S, T, W) \ + CK_PR_BINARY(and, S, T, T, "and", W, "w") \ + CK_PR_BINARY(add, S, T, T, "add", W, "w") \ + CK_PR_BINARY(or, S, T, T, "orr", W, "w") \ + CK_PR_BINARY(sub, S, T, T, "sub", W, "w") \ + CK_PR_BINARY(xor, S, T, T, "eor", W, "w") + +CK_PR_BINARY_S(32, uint32_t, "") +CK_PR_BINARY_S(uint, unsigned int, "") +CK_PR_BINARY_S(int, int, "") +CK_PR_BINARY_S(16, uint16_t, "h") +CK_PR_BINARY_S(8, uint8_t, "b") +CK_PR_BINARY_S(short, short, "h") +CK_PR_BINARY_S(char, char, "b") + +#undef CK_PR_BINARY_S +#undef CK_PR_BINARY + +CK_CC_INLINE static void * +ck_pr_faa_ptr(void *target, uintptr_t delta) +{ + uintptr_t previous, r, tmp; + + __asm__ __volatile__("1:" + "ldxr %0, [%3];" + "add %1, %4, %0;" + "stxr %w2, %1, [%3];" + "cbnz %w2, 1b;" + : "=&r" (previous), + "=&r" (r), + "=&r" (tmp) + : "r" (target), + "r" (delta) + : "memory", "cc"); + + return (void *)(previous); +} + +CK_CC_INLINE static uint64_t +ck_pr_faa_64(uint64_t *target, uint64_t delta) +{ + uint64_t previous, r, tmp; + + __asm__ __volatile__("1:" + "ldxr %0, [%3];" + "add %1, %4, %0;" + "stxr %w2, %1, [%3];" + "cbnz %w2, 1b;" + : "=&r" (previous), + "=&r" (r), + "=&r" (tmp) + : "r" (target), + "r" (delta) + : "memory", "cc"); + + return (previous); +} + +#define CK_PR_FAA(S, T, W) \ + CK_CC_INLINE static T \ + ck_pr_faa_##S(T *target, T delta) \ + { \ + T previous, r, tmp; \ + __asm__ __volatile__("1:" \ + "ldxr" W " %w0, [%3];" \ + "add %w1, %w4, %w0;" \ + "stxr" W " %w2, %w1, [%3];" \ + "cbnz %w2, 1b;" \ + : "=&r" (previous), \ + "=&r" (r), \ + "=&r" (tmp) \ + : "r" (target), \ + "r" (delta) \ + : "memory", "cc"); \ + return (previous); \ + } + +CK_PR_FAA(32, uint32_t, "") +CK_PR_FAA(uint, unsigned int, "") +CK_PR_FAA(int, int, "") +CK_PR_FAA(16, uint16_t, "h") +CK_PR_FAA(8, uint8_t, "b") +CK_PR_FAA(short, short, "h") +CK_PR_FAA(char, char, "b") + +#undef CK_PR_FAA + +#endif /* CK_PR_AARCH64_LLSC_H */ diff --git a/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h new file mode 100644 index 00000000..e2c9554c --- /dev/null +++ b/freebsd/sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h @@ -0,0 +1,298 @@ +/* + * Copyright 2009-2016 Samy Al Bahra. + * Copyright 2013-2016 Olivier Houchard. + * Copyright 2016 Alexey Kopytov. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CK_PR_AARCH64_LSE_H +#define CK_PR_AARCH64_LSE_H + +#ifndef CK_PR_H +#error Do not include this file directly, use ck_pr.h +#endif + +CK_CC_INLINE static bool +ck_pr_cas_64_2_value(uint64_t target[2], uint64_t compare[2], uint64_t set[2], uint64_t value[2]) +{ + uint64_t tmp1; + uint64_t tmp2; + register uint64_t x0 __asm__ ("x0") = compare[0]; + register uint64_t x1 __asm__ ("x1") = compare[1]; + register uint64_t x2 __asm__ ("x2") = set[0]; + register uint64_t x3 __asm__ ("x3") = set[1]; + + __asm__ __volatile__("casp %0, %1, %4, %5, [%6];" + "eor %2, %0, %7;" + "eor %3, %1, %8;" + "orr %2, %2, %3;" + : "+&r" (x0), "+&r" (x1), "=&r" (tmp1), "=&r" (tmp2) + : "r" (x2), "r" (x3), "r" (target), "r" (compare[0]), "r" (compare[1]) + : "memory"); + + value[0] = x0; + value[1] = x1; + + return (!!tmp1); +} + +CK_CC_INLINE static bool +ck_pr_cas_ptr_2_value(void *target, void *compare, void *set, void *value) +{ + return (ck_pr_cas_64_2_value(CK_CPP_CAST(uint64_t *, target), + CK_CPP_CAST(uint64_t *, compare), + CK_CPP_CAST(uint64_t *, set), + CK_CPP_CAST(uint64_t *, value))); +} + +CK_CC_INLINE static bool +ck_pr_cas_64_2(uint64_t target[2], uint64_t compare[2], uint64_t set[2]) +{ + register uint64_t x0 __asm__ ("x0") = compare[0]; + register uint64_t x1 __asm__ ("x1") = compare[1]; + register uint64_t x2 __asm__ ("x2") = set[0]; + register uint64_t x3 __asm__ ("x3") = set[1]; + + __asm__ __volatile__("casp %0, %1, %2, %3, [%4];" + "eor %0, %0, %5;" + "eor %1, %1, %6;" + "orr %0, %0, %1;" + : "+&r" (x0), "+&r" (x1) + : "r" (x2), "r" (x3), "r" (target), "r" (compare[0]), "r" (compare[1]) + : "memory"); + + return (!!x0); +} +CK_CC_INLINE static bool +ck_pr_cas_ptr_2(void *target, void *compare, void *set) +{ + return (ck_pr_cas_64_2(CK_CPP_CAST(uint64_t *, target), + CK_CPP_CAST(uint64_t *, compare), + CK_CPP_CAST(uint64_t *, set))); +} + + +#define CK_PR_CAS(N, M, T, W, R) \ + CK_CC_INLINE static bool \ + ck_pr_cas_##N##_value(M *target, T compare, T set, M *value) \ + { \ + *(T *)value = compare; \ + __asm__ __volatile__( \ + "cas" W " %" R "0, %" R "2, [%1];" \ + : "+&r" (*(T *)value) \ + : "r" (target), \ + "r" (set) \ + : "memory"); \ + return (*(T *)value == compare); \ + } \ + CK_CC_INLINE static bool \ + ck_pr_cas_##N(M *target, T compare, T set) \ + { \ + T previous = compare; \ + __asm__ __volatile__( \ + "cas" W " %" R "0, %" R "2, [%1];" \ + : "+&r" (previous) \ + : "r" (target), \ + "r" (set) \ + : "memory"); \ + return (previous == compare); \ + } + +CK_PR_CAS(ptr, void, void *, "", "") + +#define CK_PR_CAS_S(N, M, W, R) CK_PR_CAS(N, M, M, W, R) +CK_PR_CAS_S(64, uint64_t, "", "") +#ifndef CK_PR_DISABLE_DOUBLE +CK_PR_CAS_S(double, double, "", "") +#endif +CK_PR_CAS_S(32, uint32_t, "", "w") +CK_PR_CAS_S(uint, unsigned int, "", "w") +CK_PR_CAS_S(int, int, "", "w") +CK_PR_CAS_S(16, uint16_t, "h", "w") +CK_PR_CAS_S(8, uint8_t, "b", "w") +CK_PR_CAS_S(short, short, "h", "w") +CK_PR_CAS_S(char, char, "b", "w") + + +#undef CK_PR_CAS_S +#undef CK_PR_CAS + +#define CK_PR_FAS(N, M, T, W, R) \ + CK_CC_INLINE static T \ + ck_pr_fas_##N(M *target, T v) \ + { \ + T previous; \ + __asm__ __volatile__( \ + "swp" W " %" R "2, %" R "0, [%1];" \ + : "=&r" (previous) \ + : "r" (target), \ + "r" (v) \ + : "memory"); \ + return (previous); \ + } + +CK_PR_FAS(64, uint64_t, uint64_t, "", "") +CK_PR_FAS(32, uint32_t, uint32_t, "", "w") +CK_PR_FAS(ptr, void, void *, "", "") +CK_PR_FAS(int, int, int, "", "w") +CK_PR_FAS(uint, unsigned int, unsigned int, "", "w") +CK_PR_FAS(16, uint16_t, uint16_t, "h", "w") +CK_PR_FAS(8, uint8_t, uint8_t, "b", "w") +CK_PR_FAS(short, short, short, "h", "w") +CK_PR_FAS(char, char, char, "b", "w") + + +#undef CK_PR_FAS + +#define CK_PR_UNARY(O, N, M, T, I, W, R, S) \ + CK_CC_INLINE static void \ + ck_pr_##O##_##N(M *target) \ + { \ + __asm__ __volatile__(I ";" \ + "st" S W " " R "0, [%0];" \ + : \ + : "r" (target) \ + : "x0", "memory"); \ + return; \ + } + +CK_PR_UNARY(inc, ptr, void, void *, "mov x0, 1", "", "x", "add") +CK_PR_UNARY(dec, ptr, void, void *, "mov x0, -1", "", "x", "add") +CK_PR_UNARY(not, ptr, void, void *, "mov x0, -1", "", "x", "eor") +CK_PR_UNARY(inc, 64, uint64_t, uint64_t, "mov x0, 1", "", "x", "add") +CK_PR_UNARY(dec, 64, uint64_t, uint64_t, "mov x0, -1", "", "x", "add") +CK_PR_UNARY(not, 64, uint64_t, uint64_t, "mov x0, -1", "", "x", "eor") + +#define CK_PR_UNARY_S(S, T, W) \ + CK_PR_UNARY(inc, S, T, T, "mov w0, 1", W, "w", "add") \ + CK_PR_UNARY(dec, S, T, T, "mov w0, -1", W, "w", "add") \ + CK_PR_UNARY(not, S, T, T, "mov w0, -1", W, "w", "eor") \ + +CK_PR_UNARY_S(32, uint32_t, "") +CK_PR_UNARY_S(uint, unsigned int, "") +CK_PR_UNARY_S(int, int, "") +CK_PR_UNARY_S(16, uint16_t, "h") +CK_PR_UNARY_S(8, uint8_t, "b") +CK_PR_UNARY_S(short, short, "h") +CK_PR_UNARY_S(char, char, "b") + +#undef CK_PR_UNARY_S +#undef CK_PR_UNARY + +#define CK_PR_BINARY(O, N, M, T, S, W, R, I) \ + CK_CC_INLINE static void \ + ck_pr_##O##_##N(M *target, T delta) \ + { \ + __asm__ __volatile__(I ";" \ + "st" S W " %" R "0, [%1];" \ + : "+&r" (delta) \ + : "r" (target) \ + : "memory"); \ + return; \ + } + +CK_PR_BINARY(and, ptr, void, uintptr_t, "clr", "", "", "mvn %0, %0") +CK_PR_BINARY(add, ptr, void, uintptr_t, "add", "", "", "") +CK_PR_BINARY(or, ptr, void, uintptr_t, "set", "", "", "") +CK_PR_BINARY(sub, ptr, void, uintptr_t, "add", "", "", "neg %0, %0") +CK_PR_BINARY(xor, ptr, void, uintptr_t, "eor", "", "", "") +CK_PR_BINARY(and, 64, uint64_t, uint64_t, "clr", "", "", "mvn %0, %0") +CK_PR_BINARY(add, 64, uint64_t, uint64_t, "add", "", "", "") +CK_PR_BINARY(or, 64, uint64_t, uint64_t, "set", "", "", "") +CK_PR_BINARY(sub, 64, uint64_t, uint64_t, "add", "", "", "neg %0, %0") +CK_PR_BINARY(xor, 64, uint64_t, uint64_t, "eor", "", "", "") + +#define CK_PR_BINARY_S(S, T, W) \ + CK_PR_BINARY(and, S, T, T, "clr", W, "w", "mvn %w0, %w0") \ + CK_PR_BINARY(add, S, T, T, "add", W, "w", "") \ + CK_PR_BINARY(or, S, T, T, "set", W, "w", "") \ + CK_PR_BINARY(sub, S, T, T, "add", W, "w", "neg %w0, %w0") \ + CK_PR_BINARY(xor, S, T, T, "eor", W, "w", "") + +CK_PR_BINARY_S(32, uint32_t, "") +CK_PR_BINARY_S(uint, unsigned int, "") +CK_PR_BINARY_S(int, int, "") +CK_PR_BINARY_S(16, uint16_t, "h") +CK_PR_BINARY_S(8, uint8_t, "b") +CK_PR_BINARY_S(short, short, "h") +CK_PR_BINARY_S(char, char, "b") + +#undef CK_PR_BINARY_S +#undef CK_PR_BINARY + +CK_CC_INLINE static void * +ck_pr_faa_ptr(void *target, uintptr_t delta) +{ + uintptr_t previous; + + __asm__ __volatile__( + "ldadd %2, %0, [%1];" + : "=r" (previous) + : "r" (target), + "r" (delta) + : "memory"); + + return (void *)(previous); +} + +CK_CC_INLINE static uint64_t +ck_pr_faa_64(uint64_t *target, uint64_t delta) +{ + uint64_t previous; + + __asm__ __volatile__( + "ldadd %2, %0, [%1];" + : "=r" (previous) + : "r" (target), + "r" (delta) + : "memory"); + + return (previous); +} + +#define CK_PR_FAA(S, T, W) \ + CK_CC_INLINE static T \ + ck_pr_faa_##S(T *target, T delta) \ + { \ + T previous; \ + __asm__ __volatile__( \ + "ldadd" W " %w2, %w0, [%1];" \ + : "=r" (previous) \ + : "r" (target), \ + "r" (delta) \ + : "memory"); \ + return (previous); \ + } + +CK_PR_FAA(32, uint32_t, "") +CK_PR_FAA(uint, unsigned int, "") +CK_PR_FAA(int, int, "") +CK_PR_FAA(16, uint16_t, "h") +CK_PR_FAA(8, uint8_t, "b") +CK_PR_FAA(short, short, "h") +CK_PR_FAA(char, char, "b") + +#undef CK_PR_FAA + +#endif /* CK_PR_AARCH64_LSE_H */ diff --git a/freebsd/sys/dev/e1000/if_em.c b/freebsd/sys/dev/e1000/if_em.c index 32eb4afe..4fc6e7b6 100644 --- a/freebsd/sys/dev/e1000/if_em.c +++ b/freebsd/sys/dev/e1000/if_em.c @@ -1837,8 +1837,14 @@ em_if_update_admin_status(if_ctx_t ctx) if (adapter->hw.mac.type < em_mac_min) lem_smartspeed(adapter); +#ifdef __rtems__ + else if (hw->mac.type == e1000_82574 && + adapter->intr_type == IFLIB_INTR_MSIX) + E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC); +#else /* __rtems__ */ E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC); +#endif /* __rtems__ */ } static void diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c index e8287ed2..316e077c 100644 --- a/freebsd/sys/dev/ffec/if_ffec.c +++ b/freebsd/sys/dev/ffec/if_ffec.c @@ -139,9 +139,17 @@ static struct ofw_compat_data compat_data[] = { /* * Driver data and defines. The descriptor counts must be a power of two. */ +#ifndef __rtems__ #define RX_DESC_COUNT 512 +#else /* __rtems__ */ +#define RX_DESC_COUNT 64 +#endif /* __rtems__ */ #define RX_DESC_SIZE (sizeof(struct ffec_hwdesc) * RX_DESC_COUNT) +#ifndef __rtems__ #define TX_DESC_COUNT 512 +#else /* __rtems__ */ +#define TX_DESC_COUNT 64 +#endif /* __rtems__ */ #define TX_DESC_SIZE (sizeof(struct ffec_hwdesc) * TX_DESC_COUNT) #define TX_MAX_DMA_SEGS 8 @@ -201,6 +209,11 @@ struct ffec_softc { int rx_ic_count; /* RW, valid values 0..255 */ int tx_ic_time; int tx_ic_count; +#ifdef __rtems__ + + device_t mdio_device; + struct mtx mdio_mtx; +#endif /* __rtems__ */ }; static struct resource_spec irq_res_spec[MAX_IRQ_COUNT + 1] = { @@ -368,6 +381,13 @@ ffec_miibus_readreg(device_t dev, int phy, int reg) int val; sc = device_get_softc(dev); +#ifdef __rtems__ + if (sc->mdio_device) { + return (MIIBUS_READREG(sc->mdio_device, phy, reg)); + } + + mtx_lock(&sc->mdio_mtx); +#endif /* __rtems__ */ WR4(sc, FEC_IER_REG, FEC_IER_MII); @@ -378,11 +398,17 @@ ffec_miibus_readreg(device_t dev, int phy, int reg) if (!ffec_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii read\n"); +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (-1); /* All-ones is a symptom of bad mdio. */ } val = RD4(sc, FEC_MMFR_REG) & FEC_MMFR_DATA_MASK; +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (val); } @@ -392,6 +418,13 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val) struct ffec_softc *sc; sc = device_get_softc(dev); +#ifdef __rtems__ + if (sc->mdio_device) { + return (MIIBUS_WRITEREG(sc->mdio_device, phy, reg, val)); + } + + mtx_lock(&sc->mdio_mtx); +#endif /* __rtems__ */ WR4(sc, FEC_IER_REG, FEC_IER_MII); @@ -403,9 +436,15 @@ ffec_miibus_writereg(device_t dev, int phy, int reg, int val) if (!ffec_miibus_iowait(sc)) { device_printf(dev, "timeout waiting for mii write\n"); +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (-1); } +#ifdef __rtems__ + mtx_unlock(&sc->mdio_mtx); +#endif /* __rtems__ */ return (0); } @@ -714,15 +753,16 @@ ffec_encap(struct ifnet *ifp, struct ffec_softc *sc, struct mbuf *m0, tx_desc->buf_paddr = segs[i].ds_addr; tx_desc->flags2 = flags2; #ifdef __rtems__ - uintptr_t addr_flush = (uintptr_t)segs[i].ds_addr; + uintptr_t first_flush = (uintptr_t)segs[i].ds_addr; size_t len_flush = segs[i].ds_len; #ifdef CPU_CACHE_LINE_BYTES + uintptr_t last_flush = first_flush + len_flush; /* mbufs should be cache line aligned. So we can just round. */ - addr_flush = addr_flush & ~(CPU_CACHE_LINE_BYTES - 1); - len_flush = (len_flush + (CPU_CACHE_LINE_BYTES - 1)) & - ~(CPU_CACHE_LINE_BYTES - 1); + first_flush = rounddown2(first_flush, CPU_CACHE_LINE_BYTES); + last_flush = roundup2(last_flush, CPU_CACHE_LINE_BYTES); + len_flush = last_flush - first_flush; #endif - rtems_cache_flush_multiple_data_lines((void*)addr_flush, + rtems_cache_flush_multiple_data_lines((void*)first_flush, len_flush); #endif /* __rtems__ */ @@ -1568,6 +1608,9 @@ ffec_detach(device_t dev) if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); +#ifdef __rtems__ + mtx_destroy(&sc->mtx); +#endif /* __rtems__ */ FFEC_LOCK_DESTROY(sc); return (0); } @@ -1717,6 +1760,80 @@ ffec_set_txic(struct ffec_softc *sc) ffec_set_ic(sc, FEC_TXIC0_REG, sc->tx_ic_count, sc->tx_ic_time); } +#ifdef __rtems__ +int +ffec_get_phy_information( + struct ffec_softc *sc, + phandle_t node, + device_t dev, + int *phy_addr +) +{ + phandle_t phy_node; + phandle_t parent_node; + pcell_t phy_handle, phy_reg; + device_t other; + phandle_t xref; + + /* Search for the phy-handle and get the address */ + + if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, + sizeof(phy_handle)) <= 0) + return (ENXIO); + + phy_node = OF_node_from_xref(phy_handle); + + if (OF_getencprop(phy_node, "reg", (void *)&phy_reg, + sizeof(phy_reg)) <= 0) + return (ENXIO); + + *phy_addr = phy_reg; + + /* Detect whether PHY handle is connected to this or another FFEC. */ + parent_node = phy_node; + + while (parent_node != 0) { + if (parent_node == node) { + /* PHY is directly connected. That's easy. */ + sc->mdio_device = NULL; + return 0; + } + + /* + * Check whether the node is also an Ethernet controller. Do + * that by just assuming that every Ethernet controller has a + * PHY attached to it. + */ + if (OF_getencprop(parent_node, "phy-handle", + (void *)&phy_handle, sizeof(phy_handle)) > 0) { + /* + * Try to find the device of the other Ethernet + * controller and use that for MDIO communication. + * Note: This is not really a nice workaround but it + * works. + */ + xref = OF_xref_from_node(parent_node); + if (xref == 0) { + device_printf(dev, + "Couldn't get device that handles PHY\n"); + return (ENXIO); + } + other = OF_device_from_xref(xref); + if (other == 0) { + device_printf(dev, + "Couldn't get device that handles PHY\n"); + return (ENXIO); + } + sc->mdio_device = other; + return 0; + } + + parent_node = OF_parent(parent_node); + } + return (ENXIO); +} + +#endif /* __rtems__ */ static int ffec_attach(device_t dev) { @@ -1734,6 +1851,10 @@ ffec_attach(device_t dev) sc->dev = dev; FFEC_LOCK_INIT(sc); +#ifdef __rtems__ + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), + MTX_NETWORK_LOCK, MTX_DEF); +#endif /* __rtems__ */ /* * There are differences in the implementation and features of the FEC @@ -2038,9 +2159,17 @@ ffec_attach(device_t dev) ffec_miigasket_setup(sc); /* Attach the mii driver. */ +#ifdef __rtems__ + OF_device_register_xref(OF_xref_from_node(ofw_node), dev); + if (ffec_get_phy_information(sc, ofw_node, dev, &phynum) != 0) { + phynum = MII_PHY_ANY; + } + (void) dummy; +#else /* __rtems__ */ if (fdt_get_phyaddr(ofw_node, dev, &phynum, &dummy) != 0) { phynum = MII_PHY_ANY; } +#endif /* __rtems__ */ error = mii_attach(dev, &sc->miibus, ifp, ffec_media_change, ffec_media_status, BMSR_DEFCAPMASK, phynum, MII_OFFSET_ANY, (sc->fecflags & FECTYPE_MVF) ? MIIF_FORCEANEG : 0); diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c index 3960845c..ff517abc 100644 --- a/freebsd/sys/dev/mmc/mmcsd.c +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -546,6 +546,11 @@ mmcsd_attach(device_t dev) */ rev = ext_csd[EXT_CSD_REV]; +/* + * Cache flush functions are currently not available. Use of on-device cache can + * cause data loss. + */ +#ifndef __rtems__ /* * With revision 1.5 (MMC v4.5, EXT_CSD_REV == 6) and later, take * advantage of the device R/W cache if present and useage is not @@ -567,6 +572,7 @@ mmcsd_attach(device_t dev) sc->flags |= MMCSD_FLUSH_CACHE; } } +#endif /* * Ignore user-creatable enhanced user data area and general purpose @@ -1196,7 +1202,11 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) goto out; } if (len != 0) { +#ifndef __rtems__ dp = malloc(len, M_TEMP, M_WAITOK); +#else /* __rtems__ */ + dp = rtems_cache_aligned_malloc(len); +#endif /* __rtems__ */ err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len); if (err != 0) goto out; diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.c b/freebsd/sys/dev/usb/controller/dwc_otg.c index 005c7ece..b75b8872 100644 --- a/freebsd/sys/dev/usb/controller/dwc_otg.c +++ b/freebsd/sys/dev/usb/controller/dwc_otg.c @@ -470,38 +470,31 @@ dwc_otg_uses_split(struct usb_device *udev) static void dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc) { - - /* - * Disabled until further. Assuming that the register is already - * programmed correctly by the boot loader. - */ -#if 0 + uint32_t fslpclksel; + uint32_t frint; uint32_t temp; - /* setup HOST frame interval register, based on existing value */ - temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK; - if (temp >= 10000) - temp /= 1000; - else - temp /= 125; - - /* figure out nearest X-tal value */ - if (temp >= 54) - temp = 60; /* MHz */ - else if (temp >= 39) - temp = 48; /* MHz */ - else - temp = 30; /* MHz */ - - if (sc->sc_flags.status_high_speed) - temp *= 125; - else - temp *= 1000; - - DPRINTF("HFIR=0x%08x\n", temp); + if (sc->sc_flags.status_high_speed || + sc->sc_phy_type != DWC_OTG_PHY_INTERNAL) { + fslpclksel = 0; + frint = 60000; + } else if (sc->sc_flags.status_low_speed) { + fslpclksel = 2; + frint = 6000; + } else { + fslpclksel = 1; + frint = 48000; + } + temp = DWC_OTG_READ_4(sc, DOTG_HFIR); + temp &= ~HFIR_FRINT_MASK; + temp |= frint; DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp); -#endif + + temp = DWC_OTG_READ_4(sc, DOTG_HCFG); + temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK); + temp |= (fslpclksel << HCFG_FSLSPCLKSEL_SHIFT); + DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp); } static void @@ -3049,8 +3042,10 @@ dwc_otg_interrupt(void *arg) else sc->sc_flags.status_high_speed = 0; - if (hprt & HPRT_PRTCONNDET) + if (hprt & HPRT_PRTCONNDET) { sc->sc_flags.change_connect = 1; + dwc_otg_update_host_frame_interval(sc); + } if (hprt & HPRT_PRTSUSP) dwc_otg_suspend_irq(sc); @@ -3059,9 +3054,6 @@ dwc_otg_interrupt(void *arg) /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); - - /* update host frame interval */ - dwc_otg_update_host_frame_interval(sc); } /* @@ -4049,14 +4041,6 @@ dwc_otg_init(struct dwc_otg_softc *sc) DWC_OTG_WRITE_4(sc, DOTG_DAINTMSK, 0xFFFF); } - if (sc->sc_mode == DWC_MODE_OTG || sc->sc_mode == DWC_MODE_HOST) { - /* setup clocks */ - temp = DWC_OTG_READ_4(sc, DOTG_HCFG); - temp &= ~(HCFG_FSLSSUPP | HCFG_FSLSPCLKSEL_MASK); - temp |= (1 << HCFG_FSLSPCLKSEL_SHIFT); - DWC_OTG_WRITE_4(sc, DOTG_HCFG, temp); - } - /* only enable global IRQ */ DWC_OTG_WRITE_4(sc, DOTG_GAHBCFG, GAHBCFG_GLBLINTRMSK); diff --git a/freebsd/sys/dev/usb/controller/dwc_otg.h b/freebsd/sys/dev/usb/controller/dwc_otg.h index 2de0214f..fd8ce96e 100644 --- a/freebsd/sys/dev/usb/controller/dwc_otg.h +++ b/freebsd/sys/dev/usb/controller/dwc_otg.h @@ -223,5 +223,8 @@ driver_filter_t dwc_otg_filter_interrupt; driver_intr_t dwc_otg_interrupt; int dwc_otg_init(struct dwc_otg_softc *); void dwc_otg_uninit(struct dwc_otg_softc *); +#ifdef __rtems__ +void dwc_otg_platform_init(struct dwc_otg_softc *); +#endif /* __rtems__ */ #endif /* _DWC_OTG_H_ */ diff --git a/freebsd/sys/dev/usb/controller/ehci_pci.c b/freebsd/sys/dev/usb/controller/ehci_pci.c new file mode 100644 index 00000000..58789221 --- /dev/null +++ b/freebsd/sys/dev/usb/controller/ehci_pci.c @@ -0,0 +1,593 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. + * + * The EHCI 1.0 spec can be found at + * http://developer.intel.com/technology/usb/download/ehci-r10.pdf + * and the USB 2.0 spec at + * http://www.usb.org/developers/docs/usb_20.zip + */ + +/* The low level controller code for EHCI has been split into + * PCI probes and EHCI specific code. This was done to facilitate the + * sharing of code between *BSD's + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> +#include <dev/usb/usb_pci.h> +#include <dev/usb/controller/ehci.h> +#include <dev/usb/controller/ehcireg.h> +#include <rtems/bsd/local/usb_if.h> + +#define PCI_EHCI_VENDORID_ACERLABS 0x10b9 +#define PCI_EHCI_VENDORID_AMD 0x1022 +#define PCI_EHCI_VENDORID_APPLE 0x106b +#define PCI_EHCI_VENDORID_ATI 0x1002 +#define PCI_EHCI_VENDORID_CMDTECH 0x1095 +#define PCI_EHCI_VENDORID_INTEL 0x8086 +#define PCI_EHCI_VENDORID_NEC 0x1033 +#define PCI_EHCI_VENDORID_OPTI 0x1045 +#define PCI_EHCI_VENDORID_PHILIPS 0x1131 +#define PCI_EHCI_VENDORID_SIS 0x1039 +#define PCI_EHCI_VENDORID_NVIDIA 0x12D2 +#define PCI_EHCI_VENDORID_NVIDIA2 0x10DE +#define PCI_EHCI_VENDORID_VIA 0x1106 + +static device_probe_t ehci_pci_probe; +static device_attach_t ehci_pci_attach; +static device_detach_t ehci_pci_detach; +static usb_take_controller_t ehci_pci_take_controller; + +static const char * +ehci_pci_match(device_t self) +{ + uint32_t device_id = pci_get_devid(self); + + switch (device_id) { + case 0x523910b9: + return "ALi M5239 USB 2.0 controller"; + + case 0x10227463: + return "AMD 8111 USB 2.0 controller"; + + case 0x20951022: + return ("AMD CS5536 (Geode) USB 2.0 controller"); + case 0x78081022: + return ("AMD FCH USB 2.0 controller"); + + case 0x43451002: + return "ATI SB200 USB 2.0 controller"; + case 0x43731002: + return "ATI SB400 USB 2.0 controller"; + case 0x43961002: + return ("AMD SB7x0/SB8x0/SB9x0 USB 2.0 controller"); + + case 0x0f348086: + return ("Intel BayTrail USB 2.0 controller"); + case 0x1c268086: + return ("Intel Cougar Point USB 2.0 controller"); + case 0x1c2d8086: + return ("Intel Cougar Point USB 2.0 controller"); + case 0x1d268086: + return ("Intel Patsburg USB 2.0 controller"); + case 0x1d2d8086: + return ("Intel Patsburg USB 2.0 controller"); + case 0x1e268086: + return ("Intel Panther Point USB 2.0 controller"); + case 0x1e2d8086: + return ("Intel Panther Point USB 2.0 controller"); + case 0x1f2c8086: + return ("Intel Avoton USB 2.0 controller"); + case 0x25ad8086: + return "Intel 6300ESB USB 2.0 controller"; + case 0x24cd8086: + return "Intel 82801DB/L/M (ICH4) USB 2.0 controller"; + case 0x24dd8086: + return "Intel 82801EB/R (ICH5) USB 2.0 controller"; + case 0x265c8086: + return "Intel 82801FB (ICH6) USB 2.0 controller"; + case 0x268c8086: + return ("Intel 63XXESB USB 2.0 controller"); + case 0x27cc8086: + return "Intel 82801GB/R (ICH7) USB 2.0 controller"; + case 0x28368086: + return "Intel 82801H (ICH8) USB 2.0 controller USB2-A"; + case 0x283a8086: + return "Intel 82801H (ICH8) USB 2.0 controller USB2-B"; + case 0x293a8086: + return "Intel 82801I (ICH9) USB 2.0 controller"; + case 0x293c8086: + return "Intel 82801I (ICH9) USB 2.0 controller"; + case 0x3a3a8086: + return "Intel 82801JI (ICH10) USB 2.0 controller USB-A"; + case 0x3a3c8086: + return "Intel 82801JI (ICH10) USB 2.0 controller USB-B"; + case 0x3b348086: + return ("Intel PCH USB 2.0 controller USB-A"); + case 0x3b3c8086: + return ("Intel PCH USB 2.0 controller USB-B"); + case 0x8c268086: + return ("Intel Lynx Point USB 2.0 controller USB-A"); + case 0x8c2d8086: + return ("Intel Lynx Point USB 2.0 controller USB-B"); + case 0x8ca68086: + return ("Intel Wildcat Point USB 2.0 controller USB-A"); + case 0x8cad8086: + return ("Intel Wildcat Point USB 2.0 controller USB-B"); + case 0x8d268086: + return ("Intel Wellsburg USB 2.0 controller"); + case 0x8d2d8086: + return ("Intel Wellsburg USB 2.0 controller"); + case 0x9c268086: + return ("Intel Lynx Point-LP USB 2.0 controller"); + + case 0x00e01033: + return ("NEC uPD 72010x USB 2.0 controller"); + + case 0x006810de: + return "NVIDIA nForce2 USB 2.0 controller"; + case 0x008810de: + return "NVIDIA nForce2 Ultra 400 USB 2.0 controller"; + case 0x00d810de: + return "NVIDIA nForce3 USB 2.0 controller"; + case 0x00e810de: + return "NVIDIA nForce3 250 USB 2.0 controller"; + case 0x005b10de: + return "NVIDIA nForce CK804 USB 2.0 controller"; + case 0x036d10de: + return "NVIDIA nForce MCP55 USB 2.0 controller"; + case 0x03f210de: + return "NVIDIA nForce MCP61 USB 2.0 controller"; + case 0x0aa610de: + return "NVIDIA nForce MCP79 USB 2.0 controller"; + case 0x0aa910de: + return "NVIDIA nForce MCP79 USB 2.0 controller"; + case 0x0aaa10de: + return "NVIDIA nForce MCP79 USB 2.0 controller"; + + case 0x15621131: + return "Philips ISP156x USB 2.0 controller"; + + case 0x70021039: + return "SiS 968 USB 2.0 controller"; + + case 0x31041106: + return ("VIA VT6202 USB 2.0 controller"); + + default: + break; + } + + if ((pci_get_class(self) == PCIC_SERIALBUS) + && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) + && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) { + return ("EHCI (generic) USB 2.0 controller"); + } + return (NULL); /* dunno */ +} + +static int +ehci_pci_probe(device_t self) +{ + const char *desc = ehci_pci_match(self); + + if (desc) { + device_set_desc(self, desc); + return (BUS_PROBE_DEFAULT); + } else { + return (ENXIO); + } +} + +static void +ehci_pci_ati_quirk(device_t self, uint8_t is_sb700) +{ + device_t smbdev; + uint32_t val; + + if (is_sb700) { + /* Lookup SMBUS PCI device */ + smbdev = pci_find_device(PCI_EHCI_VENDORID_ATI, 0x4385); + if (smbdev == NULL) + return; + val = pci_get_revid(smbdev); + if (val != 0x3a && val != 0x3b) + return; + } + + /* + * Note: this bit is described as reserved in SB700 + * Register Reference Guide. + */ + val = pci_read_config(self, 0x53, 1); + if (!(val & 0x8)) { + val |= 0x8; + pci_write_config(self, 0x53, val, 1); + device_printf(self, "AMD SB600/700 quirk applied\n"); + } +} + +static void +ehci_pci_via_quirk(device_t self) +{ + uint32_t val; + + if ((pci_get_device(self) == 0x3104) && + ((pci_get_revid(self) & 0xf0) == 0x60)) { + /* Correct schedule sleep time to 10us */ + val = pci_read_config(self, 0x4b, 1); + if (val & 0x20) + return; + val |= 0x20; + pci_write_config(self, 0x4b, val, 1); + device_printf(self, "VIA-quirk applied\n"); + } +} + +static int +ehci_pci_attach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + int rid; + + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + pci_enable_busmaster(self); + + switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { + case PCI_USB_REV_PRE_1_0: + case PCI_USB_REV_1_0: + case PCI_USB_REV_1_1: + /* + * NOTE: some EHCI USB controllers have the wrong USB + * revision number. It appears those controllers are + * fully compliant so we just ignore this value in + * some common cases. + */ + device_printf(self, "pre-2.0 USB revision (ignored)\n"); + /* fallthrough */ + case PCI_USB_REV_2_0: + break; + default: + /* Quirk for Parallels Desktop 4.0 */ + device_printf(self, "USB revision is unknown. Assuming v2.0.\n"); + break; + } + + rid = PCI_CBMEM; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + + /* + * ehci_pci_match will never return NULL if ehci_pci_probe + * succeeded + */ + device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self)); + switch (pci_get_vendor(self)) { + case PCI_EHCI_VENDORID_ACERLABS: + sprintf(sc->sc_vendor, "AcerLabs"); + break; + case PCI_EHCI_VENDORID_AMD: + sprintf(sc->sc_vendor, "AMD"); + break; + case PCI_EHCI_VENDORID_APPLE: + sprintf(sc->sc_vendor, "Apple"); + break; + case PCI_EHCI_VENDORID_ATI: + sprintf(sc->sc_vendor, "ATI"); + break; + case PCI_EHCI_VENDORID_CMDTECH: + sprintf(sc->sc_vendor, "CMDTECH"); + break; + case PCI_EHCI_VENDORID_INTEL: + sprintf(sc->sc_vendor, "Intel"); + break; + case PCI_EHCI_VENDORID_NEC: + sprintf(sc->sc_vendor, "NEC"); + break; + case PCI_EHCI_VENDORID_OPTI: + sprintf(sc->sc_vendor, "OPTi"); + break; + case PCI_EHCI_VENDORID_PHILIPS: + sprintf(sc->sc_vendor, "Philips"); + break; + case PCI_EHCI_VENDORID_SIS: + sprintf(sc->sc_vendor, "SiS"); + break; + case PCI_EHCI_VENDORID_NVIDIA: + case PCI_EHCI_VENDORID_NVIDIA2: + sprintf(sc->sc_vendor, "nVidia"); + break; + case PCI_EHCI_VENDORID_VIA: + sprintf(sc->sc_vendor, "VIA"); + break; + default: + if (bootverbose) + device_printf(self, "(New EHCI DeviceId=0x%08x)\n", + pci_get_devid(self)); + sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); + } + +#if (__FreeBSD_version >= 700031) + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); +#else + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); +#endif + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + ehci_pci_take_controller(self); + + /* Undocumented quirks taken from Linux */ + + switch (pci_get_vendor(self)) { + case PCI_EHCI_VENDORID_ATI: + /* SB600 and SB700 EHCI quirk */ + switch (pci_get_device(self)) { + case 0x4386: + ehci_pci_ati_quirk(self, 0); + break; + case 0x4396: + ehci_pci_ati_quirk(self, 1); + break; + default: + break; + } + break; + + case PCI_EHCI_VENDORID_VIA: + ehci_pci_via_quirk(self); + break; + + default: + break; + } + + /* Dropped interrupts workaround */ + switch (pci_get_vendor(self)) { + case PCI_EHCI_VENDORID_ATI: + case PCI_EHCI_VENDORID_VIA: + sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; + if (bootverbose) + device_printf(self, + "Dropped interrupts workaround enabled\n"); + break; + default: + break; + } + + /* Doorbell feature workaround */ + switch (pci_get_vendor(self)) { + case PCI_EHCI_VENDORID_NVIDIA: + case PCI_EHCI_VENDORID_NVIDIA2: + sc->sc_flags |= EHCI_SCFLG_IAADBUG; + if (bootverbose) + device_printf(self, + "Doorbell workaround enabled\n"); + break; + default: + break; + } + + err = ehci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed err=%d\n", err); + goto error; + } + return (0); + +error: + ehci_pci_detach(self); + return (ENXIO); +} + +static int +ehci_pci_detach(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + /* during module unload there are lots of children leftover */ + device_delete_children(self); + + pci_disable_busmaster(self); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->sc_intr_hdl = NULL; + } + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static int +ehci_pci_take_controller(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + uint32_t cparams; + uint32_t eec; + uint16_t to; + uint8_t eecp; + uint8_t bios_sem; + + cparams = EREAD4(sc, EHCI_HCCPARAMS); + + /* Synchronise with the BIOS if it owns the controller. */ + for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; + eecp = EHCI_EECP_NEXT(eec)) { + eec = pci_read_config(self, eecp, 4); + if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) { + continue; + } + bios_sem = pci_read_config(self, eecp + + EHCI_LEGSUP_BIOS_SEM, 1); + if (bios_sem == 0) { + continue; + } + device_printf(sc->sc_bus.bdev, "waiting for BIOS " + "to give up control\n"); + pci_write_config(self, eecp + + EHCI_LEGSUP_OS_SEM, 1, 1); + to = 500; + while (1) { + bios_sem = pci_read_config(self, eecp + + EHCI_LEGSUP_BIOS_SEM, 1); + if (bios_sem == 0) + break; + + if (--to == 0) { + device_printf(sc->sc_bus.bdev, + "timed out waiting for BIOS\n"); + break; + } + usb_pause_mtx(NULL, hz / 100); /* wait 10ms */ + } + } + return (0); +} + +static device_method_t ehci_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ehci_pci_probe), + DEVMETHOD(device_attach, ehci_pci_attach), + DEVMETHOD(device_detach, ehci_pci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(usb_take_controller, ehci_pci_take_controller), + + DEVMETHOD_END +}; + +static driver_t ehci_driver = { + .name = "ehci", + .methods = ehci_pci_methods, + .size = sizeof(struct ehci_softc), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.h b/freebsd/sys/dev/usb/usb_pci.h index b1fa22ba..86e8cb39 100644 --- a/freebsd/sys/arm/ti/am335x/am335x_scm_padconf.h +++ b/freebsd/sys/dev/usb/usb_pci.h @@ -1,6 +1,8 @@ +/* $FreeBSD$ */ /*- - * Copyright (c) 2012 Damjan Marion <dmarion@FreeBSD.org> - * All rights reserved. + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,26 +24,20 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -#ifndef AM335X_SCM_PADCONF_H -#define AM335X_SCM_PADCONF_H +#ifndef _USB_PCI_H_ +#define _USB_PCI_H_ -#define SLEWCTRL (0x01 << 6) /* faster(0) or slower(1) slew rate. */ -#define RXACTIVE (0x01 << 5) /* Input enable value for the Pad */ -#define PULLTYPESEL (0x01 << 4) /* Pad pullup/pulldown type selection */ -#define PULLUDEN (0x01 << 3) /* Pullup/pulldown disabled */ - -#define PADCONF_OUTPUT (PULLUDEN) -#define PADCONF_OUTPUT_PULLUP (PULLTYPESEL) -#define PADCONF_OUTPUT_PULLDOWN (0) -#define PADCONF_INPUT (RXACTIVE | PULLUDEN) -#define PADCONF_INPUT_PULLUP (RXACTIVE | PULLTYPESEL) -#define PADCONF_INPUT_PULLDOWN (RXACTIVE) -#define PADCONF_INPUT_PULLUP_SLOW (PADCONF_INPUT_PULLUP | SLEWCTRL) +/* + * We don't want the following files included everywhere, that's why + * they are in a separate file. + */ +#ifndef USB_GLOBAL_INCLUDE_FILE +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> -extern const struct ti_pinmux_device ti_am335x_pinmux_dev; +#include <sys/rman.h> +#endif -#endif /* AM335X_SCM_PADCONF_H */ +#endif /* _USB_PCI_H_ */ diff --git a/freebsd/sys/i386/include/machine/bus.h b/freebsd/sys/i386/include/machine/bus.h deleted file mode 100644 index f1af2cf6..00000000 --- a/freebsd/sys/i386/include/machine/bus.h +++ /dev/null @@ -1,6 +0,0 @@ -/*- - * This file is in the public domain. - */ -/* $FreeBSD$ */ - -#include <x86/bus.h> diff --git a/freebsd/sys/i386/include/machine/intr_machdep.h b/freebsd/sys/i386/include/machine/intr_machdep.h deleted file mode 100644 index a0b28387..00000000 --- a/freebsd/sys/i386/include/machine/intr_machdep.h +++ /dev/null @@ -1,6 +0,0 @@ -/*- - * This file is in the public domain. - */ -/* $FreeBSD$ */ - -#include <x86/intr_machdep.h> diff --git a/freebsd/sys/i386/include/machine/legacyvar.h b/freebsd/sys/i386/include/machine/legacyvar.h deleted file mode 100644 index 14dd9e03..00000000 --- a/freebsd/sys/i386/include/machine/legacyvar.h +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _MACHINE_LEGACYVAR_H_ -#define _MACHINE_LEGACYVAR_H_ - -enum legacy_device_ivars { - LEGACY_IVAR_PCIDOMAIN, - LEGACY_IVAR_PCIBUS, - LEGACY_IVAR_PCISLOT, - LEGACY_IVAR_PCIFUNC -}; - -#define LEGACY_ACCESSOR(var, ivar, type) \ - __BUS_ACCESSOR(legacy, var, LEGACY, ivar, type) - -LEGACY_ACCESSOR(pcidomain, PCIDOMAIN, uint32_t) -LEGACY_ACCESSOR(pcibus, PCIBUS, uint32_t) -LEGACY_ACCESSOR(pcislot, PCISLOT, int) -LEGACY_ACCESSOR(pcifunc, PCIFUNC, int) - -#undef LEGACY_ACCESSOR - -int legacy_pcib_maxslots(device_t dev); -uint32_t legacy_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, - u_int reg, int bytes); -int legacy_pcib_read_ivar(device_t dev, device_t child, int which, - uintptr_t *result); -void legacy_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, - u_int reg, u_int32_t data, int bytes); -int legacy_pcib_write_ivar(device_t dev, device_t child, int which, - uintptr_t value); -struct resource *legacy_pcib_alloc_resource(device_t dev, device_t child, - int type, int *rid, u_long start, u_long end, u_long count, u_int flags); -int legacy_pcib_map_msi(device_t pcib, device_t dev, int irq, - uint64_t *addr, uint32_t *data); - -#endif /* !_MACHINE_LEGACYVAR_H_ */ diff --git a/freebsd/sys/i386/include/machine/specialreg.h b/freebsd/sys/i386/include/machine/specialreg.h deleted file mode 100644 index aace4bfd..00000000 --- a/freebsd/sys/i386/include/machine/specialreg.h +++ /dev/null @@ -1,6 +0,0 @@ -/*- - * This file is in the public domain. - */ -/* $FreeBSD$ */ - -#include <x86/specialreg.h> diff --git a/freebsd/sys/moxie/include/machine/in_cksum.h b/freebsd/sys/moxie/include/machine/in_cksum.h new file mode 100644 index 00000000..d55b838b --- /dev/null +++ b/freebsd/sys/moxie/include/machine/in_cksum.h @@ -0,0 +1,83 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from tahoe: in_cksum.c 1.2 86/01/05 + * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 + * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp + * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg + * $FreeBSD$ + */ + +#ifndef _MACHINE_IN_CKSUM_H_ +#define _MACHINE_IN_CKSUM_H_ 1 + +#include <sys/cdefs.h> + +#define in_cksum(m, len) in_cksum_skip(m, len, 0) + +#if defined(IPVERSION) && (IPVERSION == 4) +/* + * It it useful to have an Internet checksum routine which is inlineable + * and optimized specifically for the task of computing IP header checksums + * in the normal case (where there are no options and the header length is + * therefore always exactly five 32-bit words. + */ +#ifdef __CC_SUPPORTS___INLINE + +static __inline void +in_cksum_update(struct ip *ip) +{ + int __tmpsum; + __tmpsum = (int)ntohs(ip->ip_sum) + 256; + ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); +} + +#else + +#define in_cksum_update(ip) \ + do { \ + int __tmpsum; \ + __tmpsum = (int)ntohs(ip->ip_sum) + 256; \ + ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \ + } while(0) + +#endif +#endif + +#ifdef _KERNEL +#if defined(IPVERSION) && (IPVERSION == 4) +u_int in_cksum_hdr(const struct ip *ip); +#endif +u_short in_addword(u_short sum, u_short b); +u_short in_pseudo(u_int sum, u_int b, u_int c); +u_short in_cksum_skip(struct mbuf *m, int len, int skip); +#endif + +#endif /* _MACHINE_IN_CKSUM_H_ */ diff --git a/freebsd/sys/net/iflib.c b/freebsd/sys/net/iflib.c new file mode 100644 index 00000000..733172dc --- /dev/null +++ b/freebsd/sys/net/iflib.c @@ -0,0 +1,6827 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2014-2018, Matthew Macy <mmacy@mattmacy.io> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Neither the name of Matthew Macy nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/local/opt_inet.h> +#include <rtems/bsd/local/opt_inet6.h> +#include <rtems/bsd/local/opt_acpi.h> +#include <rtems/bsd/local/opt_sched.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/eventhandler.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/kobj.h> +#include <sys/rman.h> +#include <sys/sbuf.h> +#include <sys/smp.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/taskqueue.h> +#include <sys/limits.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/bpf.h> +#include <net/ethernet.h> +#include <net/mp_ring.h> +#include <net/pfil.h> +#include <net/vnet.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp_lro.h> +#include <netinet/in_systm.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <netinet/ip_var.h> +#include <netinet/netdump/netdump.h> +#include <netinet6/ip6_var.h> + +#include <machine/bus.h> +#include <machine/in_cksum.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/led/led.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pci_private.h> + +#include <net/iflib.h> +#include <net/iflib_private.h> + +#include <rtems/bsd/local/ifdi_if.h> + +#ifdef PCI_IOV +#include <dev/pci/pci_iov.h> +#endif + +#include <sys/bitstring.h> +/* + * enable accounting of every mbuf as it comes in to and goes out of + * iflib's software descriptor references + */ +#define MEMORY_LOGGING 0 +/* + * Enable mbuf vectors for compressing long mbuf chains + */ + +/* + * NB: + * - Prefetching in tx cleaning should perhaps be a tunable. The distance ahead + * we prefetch needs to be determined by the time spent in m_free vis a vis + * the cost of a prefetch. This will of course vary based on the workload: + * - NFLX's m_free path is dominated by vm-based M_EXT manipulation which + * is quite expensive, thus suggesting very little prefetch. + * - small packet forwarding which is just returning a single mbuf to + * UMA will typically be very fast vis a vis the cost of a memory + * access. + */ + + +/* + * File organization: + * - private structures + * - iflib private utility functions + * - ifnet functions + * - vlan registry and other exported functions + * - iflib public core functions + * + * + */ +MALLOC_DEFINE(M_IFLIB, "iflib", "ifnet library"); + +struct iflib_txq; +typedef struct iflib_txq *iflib_txq_t; +struct iflib_rxq; +typedef struct iflib_rxq *iflib_rxq_t; +struct iflib_fl; +typedef struct iflib_fl *iflib_fl_t; + +struct iflib_ctx; + +static void iru_init(if_rxd_update_t iru, iflib_rxq_t rxq, uint8_t flid); +static void iflib_timer(void *arg); + +typedef struct iflib_filter_info { + driver_filter_t *ifi_filter; + void *ifi_filter_arg; + struct grouptask *ifi_task; + void *ifi_ctx; +} *iflib_filter_info_t; + +struct iflib_ctx { + KOBJ_FIELDS; + /* + * Pointer to hardware driver's softc + */ + void *ifc_softc; + device_t ifc_dev; + if_t ifc_ifp; + +#ifndef __rtems__ + cpuset_t ifc_cpus; +#endif /* __rtems__ */ + if_shared_ctx_t ifc_sctx; + struct if_softc_ctx ifc_softc_ctx; + + struct sx ifc_ctx_sx; + struct mtx ifc_state_mtx; + + iflib_txq_t ifc_txqs; + iflib_rxq_t ifc_rxqs; + uint32_t ifc_if_flags; + uint32_t ifc_flags; + uint32_t ifc_max_fl_buf_size; + uint32_t ifc_rx_mbuf_sz; + + int ifc_link_state; + int ifc_watchdog_events; + struct cdev *ifc_led_dev; + struct resource *ifc_msix_mem; + + struct if_irq ifc_legacy_irq; + struct grouptask ifc_admin_task; + struct grouptask ifc_vflr_task; + struct iflib_filter_info ifc_filter_info; + struct ifmedia ifc_media; + struct ifmedia *ifc_mediap; + + struct sysctl_oid *ifc_sysctl_node; + uint16_t ifc_sysctl_ntxqs; + uint16_t ifc_sysctl_nrxqs; + uint16_t ifc_sysctl_qs_eq_override; + uint16_t ifc_sysctl_rx_budget; + uint16_t ifc_sysctl_tx_abdicate; + uint16_t ifc_sysctl_core_offset; +#define CORE_OFFSET_UNSPECIFIED 0xffff + uint8_t ifc_sysctl_separate_txrx; + + qidx_t ifc_sysctl_ntxds[8]; + qidx_t ifc_sysctl_nrxds[8]; + struct if_txrx ifc_txrx; +#define isc_txd_encap ifc_txrx.ift_txd_encap +#define isc_txd_flush ifc_txrx.ift_txd_flush +#define isc_txd_credits_update ifc_txrx.ift_txd_credits_update +#define isc_rxd_available ifc_txrx.ift_rxd_available +#define isc_rxd_pkt_get ifc_txrx.ift_rxd_pkt_get +#define isc_rxd_refill ifc_txrx.ift_rxd_refill +#define isc_rxd_flush ifc_txrx.ift_rxd_flush +#define isc_rxd_refill ifc_txrx.ift_rxd_refill +#define isc_rxd_refill ifc_txrx.ift_rxd_refill +#define isc_legacy_intr ifc_txrx.ift_legacy_intr + eventhandler_tag ifc_vlan_attach_event; + eventhandler_tag ifc_vlan_detach_event; + struct ether_addr ifc_mac; +}; + +void * +iflib_get_softc(if_ctx_t ctx) +{ + + return (ctx->ifc_softc); +} + +device_t +iflib_get_dev(if_ctx_t ctx) +{ + + return (ctx->ifc_dev); +} + +if_t +iflib_get_ifp(if_ctx_t ctx) +{ + + return (ctx->ifc_ifp); +} + +struct ifmedia * +iflib_get_media(if_ctx_t ctx) +{ + + return (ctx->ifc_mediap); +} + +uint32_t +iflib_get_flags(if_ctx_t ctx) +{ + return (ctx->ifc_flags); +} + +void +iflib_set_mac(if_ctx_t ctx, uint8_t mac[ETHER_ADDR_LEN]) +{ + + bcopy(mac, ctx->ifc_mac.octet, ETHER_ADDR_LEN); +} + +if_softc_ctx_t +iflib_get_softc_ctx(if_ctx_t ctx) +{ + + return (&ctx->ifc_softc_ctx); +} + +if_shared_ctx_t +iflib_get_sctx(if_ctx_t ctx) +{ + + return (ctx->ifc_sctx); +} + +#define IP_ALIGNED(m) ((((uintptr_t)(m)->m_data) & 0x3) == 0x2) +#define CACHE_PTR_INCREMENT (CACHE_LINE_SIZE/sizeof(void*)) +#define CACHE_PTR_NEXT(ptr) ((void *)(((uintptr_t)(ptr)+CACHE_LINE_SIZE-1) & (CACHE_LINE_SIZE-1))) + +#define LINK_ACTIVE(ctx) ((ctx)->ifc_link_state == LINK_STATE_UP) +#define CTX_IS_VF(ctx) ((ctx)->ifc_sctx->isc_flags & IFLIB_IS_VF) + +typedef struct iflib_sw_rx_desc_array { + bus_dmamap_t *ifsd_map; /* bus_dma maps for packet */ + struct mbuf **ifsd_m; /* pkthdr mbufs */ + caddr_t *ifsd_cl; /* direct cluster pointer for rx */ + bus_addr_t *ifsd_ba; /* bus addr of cluster for rx */ +} iflib_rxsd_array_t; + +typedef struct iflib_sw_tx_desc_array { + bus_dmamap_t *ifsd_map; /* bus_dma maps for packet */ + bus_dmamap_t *ifsd_tso_map; /* bus_dma maps for TSO packet */ + struct mbuf **ifsd_m; /* pkthdr mbufs */ +} if_txsd_vec_t; + +/* magic number that should be high enough for any hardware */ +#define IFLIB_MAX_TX_SEGS 128 +#define IFLIB_RX_COPY_THRESH 128 +#define IFLIB_MAX_RX_REFRESH 32 +/* The minimum descriptors per second before we start coalescing */ +#define IFLIB_MIN_DESC_SEC 16384 +#define IFLIB_DEFAULT_TX_UPDATE_FREQ 16 +#define IFLIB_QUEUE_IDLE 0 +#define IFLIB_QUEUE_HUNG 1 +#define IFLIB_QUEUE_WORKING 2 +/* maximum number of txqs that can share an rx interrupt */ +#define IFLIB_MAX_TX_SHARED_INTR 4 + +/* this should really scale with ring size - this is a fairly arbitrary value */ +#define TX_BATCH_SIZE 32 + +#define IFLIB_RESTART_BUDGET 8 + +#define CSUM_OFFLOAD (CSUM_IP_TSO|CSUM_IP6_TSO|CSUM_IP| \ + CSUM_IP_UDP|CSUM_IP_TCP|CSUM_IP_SCTP| \ + CSUM_IP6_UDP|CSUM_IP6_TCP|CSUM_IP6_SCTP) + +struct iflib_txq { + qidx_t ift_in_use; + qidx_t ift_cidx; + qidx_t ift_cidx_processed; + qidx_t ift_pidx; + uint8_t ift_gen; + uint8_t ift_br_offset; + uint16_t ift_npending; + uint16_t ift_db_pending; + uint16_t ift_rs_pending; + /* implicit pad */ + uint8_t ift_txd_size[8]; + uint64_t ift_processed; + uint64_t ift_cleaned; + uint64_t ift_cleaned_prev; +#if MEMORY_LOGGING + uint64_t ift_enqueued; + uint64_t ift_dequeued; +#endif + uint64_t ift_no_tx_dma_setup; + uint64_t ift_no_desc_avail; + uint64_t ift_mbuf_defrag_failed; + uint64_t ift_mbuf_defrag; + uint64_t ift_map_failed; + uint64_t ift_txd_encap_efbig; + uint64_t ift_pullups; + uint64_t ift_last_timer_tick; + + struct mtx ift_mtx; + struct mtx ift_db_mtx; + + /* constant values */ + if_ctx_t ift_ctx; + struct ifmp_ring *ift_br; + struct grouptask ift_task; + qidx_t ift_size; + uint16_t ift_id; + struct callout ift_timer; + + if_txsd_vec_t ift_sds; + uint8_t ift_qstatus; + uint8_t ift_closed; + uint8_t ift_update_freq; + struct iflib_filter_info ift_filter_info; + bus_dma_tag_t ift_buf_tag; + bus_dma_tag_t ift_tso_buf_tag; + iflib_dma_info_t ift_ifdi; +#define MTX_NAME_LEN 16 + char ift_mtx_name[MTX_NAME_LEN]; + bus_dma_segment_t ift_segs[IFLIB_MAX_TX_SEGS] __aligned(CACHE_LINE_SIZE); +#ifdef IFLIB_DIAGNOSTICS + uint64_t ift_cpu_exec_count[256]; +#endif +} __aligned(CACHE_LINE_SIZE); + +struct iflib_fl { + qidx_t ifl_cidx; + qidx_t ifl_pidx; + qidx_t ifl_credits; + uint8_t ifl_gen; + uint8_t ifl_rxd_size; +#if MEMORY_LOGGING + uint64_t ifl_m_enqueued; + uint64_t ifl_m_dequeued; + uint64_t ifl_cl_enqueued; + uint64_t ifl_cl_dequeued; +#endif + /* implicit pad */ + bitstr_t *ifl_rx_bitmap; + qidx_t ifl_fragidx; + /* constant */ + qidx_t ifl_size; + uint16_t ifl_buf_size; + uint16_t ifl_cltype; + uma_zone_t ifl_zone; + iflib_rxsd_array_t ifl_sds; + iflib_rxq_t ifl_rxq; + uint8_t ifl_id; + bus_dma_tag_t ifl_buf_tag; + iflib_dma_info_t ifl_ifdi; + uint64_t ifl_bus_addrs[IFLIB_MAX_RX_REFRESH] __aligned(CACHE_LINE_SIZE); + caddr_t ifl_vm_addrs[IFLIB_MAX_RX_REFRESH]; + qidx_t ifl_rxd_idxs[IFLIB_MAX_RX_REFRESH]; +} __aligned(CACHE_LINE_SIZE); + +static inline qidx_t +get_inuse(int size, qidx_t cidx, qidx_t pidx, uint8_t gen) +{ + qidx_t used; + + if (pidx > cidx) + used = pidx - cidx; + else if (pidx < cidx) + used = size - cidx + pidx; + else if (gen == 0 && pidx == cidx) + used = 0; + else if (gen == 1 && pidx == cidx) + used = size; + else + panic("bad state"); + + return (used); +} + +#define TXQ_AVAIL(txq) (txq->ift_size - get_inuse(txq->ift_size, txq->ift_cidx, txq->ift_pidx, txq->ift_gen)) + +#define IDXDIFF(head, tail, wrap) \ + ((head) >= (tail) ? (head) - (tail) : (wrap) - (tail) + (head)) + +struct iflib_rxq { + if_ctx_t ifr_ctx; + iflib_fl_t ifr_fl; + uint64_t ifr_rx_irq; + struct pfil_head *pfil; + /* + * If there is a separate completion queue (IFLIB_HAS_RXCQ), this is + * the command queue consumer index. Otherwise it's unused. + */ + qidx_t ifr_cq_cidx; + uint16_t ifr_id; + uint8_t ifr_nfl; + uint8_t ifr_ntxqirq; + uint8_t ifr_txqid[IFLIB_MAX_TX_SHARED_INTR]; + uint8_t ifr_fl_offset; + struct lro_ctrl ifr_lc; + struct grouptask ifr_task; + struct iflib_filter_info ifr_filter_info; + iflib_dma_info_t ifr_ifdi; + + /* dynamically allocate if any drivers need a value substantially larger than this */ + struct if_rxd_frag ifr_frags[IFLIB_MAX_RX_SEGS] __aligned(CACHE_LINE_SIZE); +#ifdef IFLIB_DIAGNOSTICS + uint64_t ifr_cpu_exec_count[256]; +#endif +} __aligned(CACHE_LINE_SIZE); + +typedef struct if_rxsd { + caddr_t *ifsd_cl; + iflib_fl_t ifsd_fl; + qidx_t ifsd_cidx; +} *if_rxsd_t; + +/* multiple of word size */ +#ifdef __LP64__ +#define PKT_INFO_SIZE 6 +#define RXD_INFO_SIZE 5 +#define PKT_TYPE uint64_t +#else +#define PKT_INFO_SIZE 11 +#define RXD_INFO_SIZE 8 +#define PKT_TYPE uint32_t +#endif +#define PKT_LOOP_BOUND ((PKT_INFO_SIZE/3)*3) +#define RXD_LOOP_BOUND ((RXD_INFO_SIZE/4)*4) + +typedef struct if_pkt_info_pad { + PKT_TYPE pkt_val[PKT_INFO_SIZE]; +} *if_pkt_info_pad_t; +typedef struct if_rxd_info_pad { + PKT_TYPE rxd_val[RXD_INFO_SIZE]; +} *if_rxd_info_pad_t; + +CTASSERT(sizeof(struct if_pkt_info_pad) == sizeof(struct if_pkt_info)); +CTASSERT(sizeof(struct if_rxd_info_pad) == sizeof(struct if_rxd_info)); + + +static inline void +pkt_info_zero(if_pkt_info_t pi) +{ + if_pkt_info_pad_t pi_pad; + + pi_pad = (if_pkt_info_pad_t)pi; + pi_pad->pkt_val[0] = 0; pi_pad->pkt_val[1] = 0; pi_pad->pkt_val[2] = 0; + pi_pad->pkt_val[3] = 0; pi_pad->pkt_val[4] = 0; pi_pad->pkt_val[5] = 0; +#ifndef __LP64__ + pi_pad->pkt_val[6] = 0; pi_pad->pkt_val[7] = 0; pi_pad->pkt_val[8] = 0; + pi_pad->pkt_val[9] = 0; pi_pad->pkt_val[10] = 0; +#endif +} + +static device_method_t iflib_pseudo_methods[] = { + DEVMETHOD(device_attach, noop_attach), + DEVMETHOD(device_detach, iflib_pseudo_detach), + DEVMETHOD_END +}; + +driver_t iflib_pseudodriver = { + "iflib_pseudo", iflib_pseudo_methods, sizeof(struct iflib_ctx), +}; + +static inline void +rxd_info_zero(if_rxd_info_t ri) +{ + if_rxd_info_pad_t ri_pad; + int i; + + ri_pad = (if_rxd_info_pad_t)ri; + for (i = 0; i < RXD_LOOP_BOUND; i += 4) { + ri_pad->rxd_val[i] = 0; + ri_pad->rxd_val[i+1] = 0; + ri_pad->rxd_val[i+2] = 0; + ri_pad->rxd_val[i+3] = 0; + } +#ifdef __LP64__ + ri_pad->rxd_val[RXD_INFO_SIZE-1] = 0; +#endif +} + +/* + * Only allow a single packet to take up most 1/nth of the tx ring + */ +#define MAX_SINGLE_PACKET_FRACTION 12 +#define IF_BAD_DMA (bus_addr_t)-1 + +#define CTX_ACTIVE(ctx) ((if_getdrvflags((ctx)->ifc_ifp) & IFF_DRV_RUNNING)) + +#define CTX_LOCK_INIT(_sc) sx_init(&(_sc)->ifc_ctx_sx, "iflib ctx lock") +#define CTX_LOCK(ctx) sx_xlock(&(ctx)->ifc_ctx_sx) +#define CTX_UNLOCK(ctx) sx_xunlock(&(ctx)->ifc_ctx_sx) +#define CTX_LOCK_DESTROY(ctx) sx_destroy(&(ctx)->ifc_ctx_sx) + +#define STATE_LOCK_INIT(_sc, _name) mtx_init(&(_sc)->ifc_state_mtx, _name, "iflib state lock", MTX_DEF) +#define STATE_LOCK(ctx) mtx_lock(&(ctx)->ifc_state_mtx) +#define STATE_UNLOCK(ctx) mtx_unlock(&(ctx)->ifc_state_mtx) +#define STATE_LOCK_DESTROY(ctx) mtx_destroy(&(ctx)->ifc_state_mtx) + +#define CALLOUT_LOCK(txq) mtx_lock(&txq->ift_mtx) +#define CALLOUT_UNLOCK(txq) mtx_unlock(&txq->ift_mtx) + +void +iflib_set_detach(if_ctx_t ctx) +{ + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_IN_DETACH; + STATE_UNLOCK(ctx); +} + +/* Our boot-time initialization hook */ +static int iflib_module_event_handler(module_t, int, void *); + +static moduledata_t iflib_moduledata = { + "iflib", + iflib_module_event_handler, + NULL +}; + +DECLARE_MODULE(iflib, iflib_moduledata, SI_SUB_INIT_IF, SI_ORDER_ANY); +MODULE_VERSION(iflib, 1); + +MODULE_DEPEND(iflib, pci, 1, 1, 1); +MODULE_DEPEND(iflib, ether, 1, 1, 1); + +TASKQGROUP_DEFINE(if_io_tqg, mp_ncpus, 1); +TASKQGROUP_DEFINE(if_config_tqg, 1, 1); + +#ifndef IFLIB_DEBUG_COUNTERS +#ifdef INVARIANTS +#define IFLIB_DEBUG_COUNTERS 1 +#else +#define IFLIB_DEBUG_COUNTERS 0 +#endif /* !INVARIANTS */ +#endif + +static SYSCTL_NODE(_net, OID_AUTO, iflib, CTLFLAG_RD, 0, + "iflib driver parameters"); + +/* + * XXX need to ensure that this can't accidentally cause the head to be moved backwards + */ +static int iflib_min_tx_latency = 0; +SYSCTL_INT(_net_iflib, OID_AUTO, min_tx_latency, CTLFLAG_RW, + &iflib_min_tx_latency, 0, "minimize transmit latency at the possible expense of throughput"); +static int iflib_no_tx_batch = 0; +SYSCTL_INT(_net_iflib, OID_AUTO, no_tx_batch, CTLFLAG_RW, + &iflib_no_tx_batch, 0, "minimize transmit latency at the possible expense of throughput"); + + +#if IFLIB_DEBUG_COUNTERS + +static int iflib_tx_seen; +static int iflib_tx_sent; +static int iflib_tx_encap; +static int iflib_rx_allocs; +static int iflib_fl_refills; +static int iflib_fl_refills_large; +static int iflib_tx_frees; + +SYSCTL_INT(_net_iflib, OID_AUTO, tx_seen, CTLFLAG_RD, + &iflib_tx_seen, 0, "# TX mbufs seen"); +SYSCTL_INT(_net_iflib, OID_AUTO, tx_sent, CTLFLAG_RD, + &iflib_tx_sent, 0, "# TX mbufs sent"); +SYSCTL_INT(_net_iflib, OID_AUTO, tx_encap, CTLFLAG_RD, + &iflib_tx_encap, 0, "# TX mbufs encapped"); +SYSCTL_INT(_net_iflib, OID_AUTO, tx_frees, CTLFLAG_RD, + &iflib_tx_frees, 0, "# TX frees"); +SYSCTL_INT(_net_iflib, OID_AUTO, rx_allocs, CTLFLAG_RD, + &iflib_rx_allocs, 0, "# RX allocations"); +SYSCTL_INT(_net_iflib, OID_AUTO, fl_refills, CTLFLAG_RD, + &iflib_fl_refills, 0, "# refills"); +SYSCTL_INT(_net_iflib, OID_AUTO, fl_refills_large, CTLFLAG_RD, + &iflib_fl_refills_large, 0, "# large refills"); + + +static int iflib_txq_drain_flushing; +static int iflib_txq_drain_oactive; +static int iflib_txq_drain_notready; + +SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_flushing, CTLFLAG_RD, + &iflib_txq_drain_flushing, 0, "# drain flushes"); +SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_oactive, CTLFLAG_RD, + &iflib_txq_drain_oactive, 0, "# drain oactives"); +SYSCTL_INT(_net_iflib, OID_AUTO, txq_drain_notready, CTLFLAG_RD, + &iflib_txq_drain_notready, 0, "# drain notready"); + + +static int iflib_encap_load_mbuf_fail; +static int iflib_encap_pad_mbuf_fail; +static int iflib_encap_txq_avail_fail; +static int iflib_encap_txd_encap_fail; + +SYSCTL_INT(_net_iflib, OID_AUTO, encap_load_mbuf_fail, CTLFLAG_RD, + &iflib_encap_load_mbuf_fail, 0, "# busdma load failures"); +SYSCTL_INT(_net_iflib, OID_AUTO, encap_pad_mbuf_fail, CTLFLAG_RD, + &iflib_encap_pad_mbuf_fail, 0, "# runt frame pad failures"); +SYSCTL_INT(_net_iflib, OID_AUTO, encap_txq_avail_fail, CTLFLAG_RD, + &iflib_encap_txq_avail_fail, 0, "# txq avail failures"); +SYSCTL_INT(_net_iflib, OID_AUTO, encap_txd_encap_fail, CTLFLAG_RD, + &iflib_encap_txd_encap_fail, 0, "# driver encap failures"); + +static int iflib_task_fn_rxs; +static int iflib_rx_intr_enables; +static int iflib_fast_intrs; +static int iflib_rx_unavail; +static int iflib_rx_ctx_inactive; +static int iflib_rx_if_input; +static int iflib_rxd_flush; + +static int iflib_verbose_debug; + +SYSCTL_INT(_net_iflib, OID_AUTO, task_fn_rx, CTLFLAG_RD, + &iflib_task_fn_rxs, 0, "# task_fn_rx calls"); +SYSCTL_INT(_net_iflib, OID_AUTO, rx_intr_enables, CTLFLAG_RD, + &iflib_rx_intr_enables, 0, "# RX intr enables"); +SYSCTL_INT(_net_iflib, OID_AUTO, fast_intrs, CTLFLAG_RD, + &iflib_fast_intrs, 0, "# fast_intr calls"); +SYSCTL_INT(_net_iflib, OID_AUTO, rx_unavail, CTLFLAG_RD, + &iflib_rx_unavail, 0, "# times rxeof called with no available data"); +SYSCTL_INT(_net_iflib, OID_AUTO, rx_ctx_inactive, CTLFLAG_RD, + &iflib_rx_ctx_inactive, 0, "# times rxeof called with inactive context"); +SYSCTL_INT(_net_iflib, OID_AUTO, rx_if_input, CTLFLAG_RD, + &iflib_rx_if_input, 0, "# times rxeof called if_input"); +SYSCTL_INT(_net_iflib, OID_AUTO, rxd_flush, CTLFLAG_RD, + &iflib_rxd_flush, 0, "# times rxd_flush called"); +SYSCTL_INT(_net_iflib, OID_AUTO, verbose_debug, CTLFLAG_RW, + &iflib_verbose_debug, 0, "enable verbose debugging"); + +#define DBG_COUNTER_INC(name) atomic_add_int(&(iflib_ ## name), 1) +static void +iflib_debug_reset(void) +{ + iflib_tx_seen = iflib_tx_sent = iflib_tx_encap = iflib_rx_allocs = + iflib_fl_refills = iflib_fl_refills_large = iflib_tx_frees = + iflib_txq_drain_flushing = iflib_txq_drain_oactive = + iflib_txq_drain_notready = + iflib_encap_load_mbuf_fail = iflib_encap_pad_mbuf_fail = + iflib_encap_txq_avail_fail = iflib_encap_txd_encap_fail = + iflib_task_fn_rxs = iflib_rx_intr_enables = iflib_fast_intrs = + iflib_rx_unavail = + iflib_rx_ctx_inactive = iflib_rx_if_input = + iflib_rxd_flush = 0; +} + +#else +#define DBG_COUNTER_INC(name) +static void iflib_debug_reset(void) {} +#endif + +#define IFLIB_DEBUG 0 + +static void iflib_tx_structures_free(if_ctx_t ctx); +static void iflib_rx_structures_free(if_ctx_t ctx); +static int iflib_queues_alloc(if_ctx_t ctx); +static int iflib_tx_credits_update(if_ctx_t ctx, iflib_txq_t txq); +static int iflib_rxd_avail(if_ctx_t ctx, iflib_rxq_t rxq, qidx_t cidx, qidx_t budget); +static int iflib_qset_structures_setup(if_ctx_t ctx); +static int iflib_msix_init(if_ctx_t ctx); +static int iflib_legacy_setup(if_ctx_t ctx, driver_filter_t filter, void *filterarg, int *rid, const char *str); +static void iflib_txq_check_drain(iflib_txq_t txq, int budget); +static uint32_t iflib_txq_can_drain(struct ifmp_ring *); +#ifdef ALTQ +static void iflib_altq_if_start(if_t ifp); +static int iflib_altq_if_transmit(if_t ifp, struct mbuf *m); +#endif +static int iflib_register(if_ctx_t); +static void iflib_deregister(if_ctx_t); +static void iflib_init_locked(if_ctx_t ctx); +static void iflib_add_device_sysctl_pre(if_ctx_t ctx); +static void iflib_add_device_sysctl_post(if_ctx_t ctx); +static void iflib_ifmp_purge(iflib_txq_t txq); +static void _iflib_pre_assert(if_softc_ctx_t scctx); +static void iflib_if_init_locked(if_ctx_t ctx); +static void iflib_free_intr_mem(if_ctx_t ctx); +#ifndef __NO_STRICT_ALIGNMENT +static struct mbuf * iflib_fixup_rx(struct mbuf *m); +#endif + +static SLIST_HEAD(cpu_offset_list, cpu_offset) cpu_offsets = + SLIST_HEAD_INITIALIZER(cpu_offsets); +struct cpu_offset { + SLIST_ENTRY(cpu_offset) entries; + cpuset_t set; + unsigned int refcount; + uint16_t offset; +}; +static struct mtx cpu_offset_mtx; +MTX_SYSINIT(iflib_cpu_offset, &cpu_offset_mtx, "iflib_cpu_offset lock", + MTX_DEF); + +NETDUMP_DEFINE(iflib); + +#ifdef DEV_NETMAP +#include <sys/selinfo.h> +#include <net/netmap.h> +#include <dev/netmap/netmap_kern.h> + +MODULE_DEPEND(iflib, netmap, 1, 1, 1); + +static int netmap_fl_refill(iflib_rxq_t rxq, struct netmap_kring *kring, uint32_t nm_i, bool init); + +/* + * device-specific sysctl variables: + * + * iflib_crcstrip: 0: keep CRC in rx frames (default), 1: strip it. + * During regular operations the CRC is stripped, but on some + * hardware reception of frames not multiple of 64 is slower, + * so using crcstrip=0 helps in benchmarks. + * + * iflib_rx_miss, iflib_rx_miss_bufs: + * count packets that might be missed due to lost interrupts. + */ +SYSCTL_DECL(_dev_netmap); +/* + * The xl driver by default strips CRCs and we do not override it. + */ + +int iflib_crcstrip = 1; +SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_crcstrip, + CTLFLAG_RW, &iflib_crcstrip, 1, "strip CRC on RX frames"); + +int iflib_rx_miss, iflib_rx_miss_bufs; +SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_rx_miss, + CTLFLAG_RW, &iflib_rx_miss, 0, "potentially missed RX intr"); +SYSCTL_INT(_dev_netmap, OID_AUTO, iflib_rx_miss_bufs, + CTLFLAG_RW, &iflib_rx_miss_bufs, 0, "potentially missed RX intr bufs"); + +/* + * Register/unregister. We are already under netmap lock. + * Only called on the first register or the last unregister. + */ +static int +iflib_netmap_register(struct netmap_adapter *na, int onoff) +{ + if_t ifp = na->ifp; + if_ctx_t ctx = ifp->if_softc; + int status; + + CTX_LOCK(ctx); + IFDI_INTR_DISABLE(ctx); + + /* Tell the stack that the interface is no longer active */ + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + if (!CTX_IS_VF(ctx)) + IFDI_CRCSTRIP_SET(ctx, onoff, iflib_crcstrip); + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + iflib_stop(ctx); + iflib_init_locked(ctx); + IFDI_CRCSTRIP_SET(ctx, onoff, iflib_crcstrip); // XXX why twice ? + status = ifp->if_drv_flags & IFF_DRV_RUNNING ? 0 : 1; + if (status) + nm_clear_native_flags(na); + CTX_UNLOCK(ctx); + return (status); +} + +static int +netmap_fl_refill(iflib_rxq_t rxq, struct netmap_kring *kring, uint32_t nm_i, bool init) +{ + struct netmap_adapter *na = kring->na; + u_int const lim = kring->nkr_num_slots - 1; + u_int head = kring->rhead; + struct netmap_ring *ring = kring->ring; + bus_dmamap_t *map; + struct if_rxd_update iru; + if_ctx_t ctx = rxq->ifr_ctx; + iflib_fl_t fl = &rxq->ifr_fl[0]; + uint32_t refill_pidx, nic_i; +#if IFLIB_DEBUG_COUNTERS + int rf_count = 0; +#endif + + if (nm_i == head && __predict_true(!init)) + return 0; + iru_init(&iru, rxq, 0 /* flid */); + map = fl->ifl_sds.ifsd_map; + refill_pidx = netmap_idx_k2n(kring, nm_i); + /* + * IMPORTANT: we must leave one free slot in the ring, + * so move head back by one unit + */ + head = nm_prev(head, lim); + nic_i = UINT_MAX; + DBG_COUNTER_INC(fl_refills); + while (nm_i != head) { +#if IFLIB_DEBUG_COUNTERS + if (++rf_count == 9) + DBG_COUNTER_INC(fl_refills_large); +#endif + for (int tmp_pidx = 0; tmp_pidx < IFLIB_MAX_RX_REFRESH && nm_i != head; tmp_pidx++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + void *addr = PNMB(na, slot, &fl->ifl_bus_addrs[tmp_pidx]); + uint32_t nic_i_dma = refill_pidx; + nic_i = netmap_idx_k2n(kring, nm_i); + + MPASS(tmp_pidx < IFLIB_MAX_RX_REFRESH); + + if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ + return netmap_ring_reinit(kring); + + fl->ifl_vm_addrs[tmp_pidx] = addr; + if (__predict_false(init)) { + netmap_load_map(na, fl->ifl_buf_tag, + map[nic_i], addr); + } else if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + netmap_reload_map(na, fl->ifl_buf_tag, + map[nic_i], addr); + } + slot->flags &= ~NS_BUF_CHANGED; + + nm_i = nm_next(nm_i, lim); + fl->ifl_rxd_idxs[tmp_pidx] = nic_i = nm_next(nic_i, lim); + if (nm_i != head && tmp_pidx < IFLIB_MAX_RX_REFRESH-1) + continue; + + iru.iru_pidx = refill_pidx; + iru.iru_count = tmp_pidx+1; + ctx->isc_rxd_refill(ctx->ifc_softc, &iru); + refill_pidx = nic_i; + for (int n = 0; n < iru.iru_count; n++) { + bus_dmamap_sync(fl->ifl_buf_tag, map[nic_i_dma], + BUS_DMASYNC_PREREAD); + /* XXX - change this to not use the netmap func*/ + nic_i_dma = nm_next(nic_i_dma, lim); + } + } + } + kring->nr_hwcur = head; + + bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + if (__predict_true(nic_i != UINT_MAX)) { + ctx->isc_rxd_flush(ctx->ifc_softc, rxq->ifr_id, fl->ifl_id, nic_i); + DBG_COUNTER_INC(rxd_flush); + } + return (0); +} + +/* + * Reconcile kernel and user view of the transmit ring. + * + * All information is in the kring. + * Userspace wants to send packets up to the one before kring->rhead, + * kernel knows kring->nr_hwcur is the first unsent packet. + * + * Here we push packets out (as many as possible), and possibly + * reclaim buffers from previously completed transmission. + * + * The caller (netmap) guarantees that there is only one instance + * running at any time. Any interference with other driver + * methods should be handled by the individual drivers. + */ +static int +iflib_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + if_t ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int nm_i; /* index into the netmap kring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + struct if_pkt_info pi; + + /* + * interrupts on every tx packet are expensive so request + * them every half ring, or where NS_REPORT is set + */ + u_int report_frequency = kring->nkr_num_slots >> 1; + /* device-specific */ + if_ctx_t ctx = ifp->if_softc; + iflib_txq_t txq = &ctx->ifc_txqs[kring->ring_id]; + + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* + * First part: process new packets to send. + * nm_i is the current index in the netmap kring, + * nic_i is the corresponding index in the NIC ring. + * + * If we have packets to send (nm_i != head) + * iterate over the netmap ring, fetch length and update + * the corresponding slot in the NIC ring. Some drivers also + * need to update the buffer's physical address in the NIC slot + * even NS_BUF_CHANGED is not set (PNMB computes the addresses). + * + * The netmap_reload_map() calls is especially expensive, + * even when (as in this case) the tag is 0, so do only + * when the buffer has actually changed. + * + * If possible do not set the report/intr bit on all slots, + * but only a few times per ring or when NS_REPORT is set. + * + * Finally, on 10G and faster drivers, it might be useful + * to prefetch the next slot and txr entry. + */ + + nm_i = kring->nr_hwcur; + if (nm_i != head) { /* we have new packets to send */ + pkt_info_zero(&pi); + pi.ipi_segs = txq->ift_segs; + pi.ipi_qsidx = kring->ring_id; + nic_i = netmap_idx_k2n(kring, nm_i); + + __builtin_prefetch(&ring->slot[nm_i]); + __builtin_prefetch(&txq->ift_sds.ifsd_m[nic_i]); + __builtin_prefetch(&txq->ift_sds.ifsd_map[nic_i]); + + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + u_int len = slot->len; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + int flags = (slot->flags & NS_REPORT || + nic_i == 0 || nic_i == report_frequency) ? + IPI_TX_INTR : 0; + + /* device-specific */ + pi.ipi_len = len; + pi.ipi_segs[0].ds_addr = paddr; + pi.ipi_segs[0].ds_len = len; + pi.ipi_nsegs = 1; + pi.ipi_ndescs = 0; + pi.ipi_pidx = nic_i; + pi.ipi_flags = flags; + + /* Fill the slot in the NIC ring. */ + ctx->isc_txd_encap(ctx->ifc_softc, &pi); + DBG_COUNTER_INC(tx_encap); + + /* prefetch for next round */ + __builtin_prefetch(&ring->slot[nm_i + 1]); + __builtin_prefetch(&txq->ift_sds.ifsd_m[nic_i + 1]); + __builtin_prefetch(&txq->ift_sds.ifsd_map[nic_i + 1]); + + NM_CHECK_ADDR_LEN(na, addr, len); + + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + netmap_reload_map(na, txq->ift_buf_tag, + txq->ift_sds.ifsd_map[nic_i], addr); + } + /* make sure changes to the buffer are synced */ + bus_dmamap_sync(txq->ift_buf_tag, + txq->ift_sds.ifsd_map[nic_i], + BUS_DMASYNC_PREWRITE); + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = nm_i; + + /* synchronize the NIC ring */ + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* (re)start the tx unit up to slot nic_i (excluded) */ + ctx->isc_txd_flush(ctx->ifc_softc, txq->ift_id, nic_i); + } + + /* + * Second part: reclaim buffers for completed transmissions. + * + * If there are unclaimed buffers, attempt to reclaim them. + * If none are reclaimed, and TX IRQs are not in use, do an initial + * minimal delay, then trigger the tx handler which will spin in the + * group task queue. + */ + if (kring->nr_hwtail != nm_prev(kring->nr_hwcur, lim)) { + if (iflib_tx_credits_update(ctx, txq)) { + /* some tx completed, increment avail */ + nic_i = txq->ift_cidx_processed; + kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); + } + } + if (!(ctx->ifc_flags & IFC_NETMAP_TX_IRQ)) + if (kring->nr_hwtail != nm_prev(kring->nr_hwcur, lim)) { + callout_reset_on(&txq->ift_timer, hz < 2000 ? 1 : hz / 1000, + iflib_timer, txq, txq->ift_timer.c_cpu); + } + return (0); +} + +/* + * Reconcile kernel and user view of the receive ring. + * Same as for the txsync, this routine must be efficient. + * The caller guarantees a single invocations, but races against + * the rest of the driver should be handled here. + * + * On call, kring->rhead is the first packet that userspace wants + * to keep, and kring->rcur is the wakeup point. + * The kernel has previously reported packets up to kring->rtail. + * + * If (flags & NAF_FORCE_READ) also check for incoming packets irrespective + * of whether or not we received an interrupt. + */ +static int +iflib_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct netmap_ring *ring = kring->ring; + if_t ifp = na->ifp; + iflib_fl_t fl; + uint32_t nm_i; /* index into the netmap ring */ + uint32_t nic_i; /* index into the NIC ring */ + u_int i, n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + struct if_rxd_info ri; + + if_ctx_t ctx = ifp->if_softc; + iflib_rxq_t rxq = &ctx->ifc_rxqs[kring->ring_id]; + if (head > lim) + return netmap_ring_reinit(kring); + + /* + * XXX netmap_fl_refill() only ever (re)fills free list 0 so far. + */ + + for (i = 0, fl = rxq->ifr_fl; i < rxq->ifr_nfl; i++, fl++) { + bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + } + + /* + * First part: import newly received packets. + * + * nm_i is the index of the next free slot in the netmap ring, + * nic_i is the index of the next received packet in the NIC ring, + * and they may differ in case if_init() has been called while + * in netmap mode. For the receive ring we have + * + * nic_i = rxr->next_check; + * nm_i = kring->nr_hwtail (previous) + * and + * nm_i == (nic_i + kring->nkr_hwofs) % ring_size + * + * rxr->next_check is set to 0 on a ring reinit + */ + if (netmap_no_pendintr || force_update) { + int crclen = iflib_crcstrip ? 0 : 4; + int error, avail; + + for (i = 0; i < rxq->ifr_nfl; i++) { + fl = &rxq->ifr_fl[i]; + nic_i = fl->ifl_cidx; + nm_i = netmap_idx_n2k(kring, nic_i); + avail = ctx->isc_rxd_available(ctx->ifc_softc, + rxq->ifr_id, nic_i, USHRT_MAX); + for (n = 0; avail > 0; n++, avail--) { + rxd_info_zero(&ri); + ri.iri_frags = rxq->ifr_frags; + ri.iri_qsidx = kring->ring_id; + ri.iri_ifp = ctx->ifc_ifp; + ri.iri_cidx = nic_i; + + error = ctx->isc_rxd_pkt_get(ctx->ifc_softc, &ri); + ring->slot[nm_i].len = error ? 0 : ri.iri_len - crclen; + ring->slot[nm_i].flags = 0; + bus_dmamap_sync(fl->ifl_buf_tag, + fl->ifl_sds.ifsd_map[nic_i], BUS_DMASYNC_POSTREAD); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + if (n) { /* update the state variables */ + if (netmap_no_pendintr && !force_update) { + /* diagnostics */ + iflib_rx_miss ++; + iflib_rx_miss_bufs += n; + } + fl->ifl_cidx = nic_i; + kring->nr_hwtail = nm_i; + } + kring->nr_kflags &= ~NKR_PENDINTR; + } + } + /* + * Second part: skip past packets that userspace has released. + * (kring->nr_hwcur to head excluded), + * and make the buffers available for reception. + * As usual nm_i is the index in the netmap ring, + * nic_i is the index in the NIC ring, and + * nm_i == (nic_i + kring->nkr_hwofs) % ring_size + */ + /* XXX not sure how this will work with multiple free lists */ + nm_i = kring->nr_hwcur; + + return (netmap_fl_refill(rxq, kring, nm_i, false)); +} + +static void +iflib_netmap_intr(struct netmap_adapter *na, int onoff) +{ + if_ctx_t ctx = na->ifp->if_softc; + + CTX_LOCK(ctx); + if (onoff) { + IFDI_INTR_ENABLE(ctx); + } else { + IFDI_INTR_DISABLE(ctx); + } + CTX_UNLOCK(ctx); +} + + +static int +iflib_netmap_attach(if_ctx_t ctx) +{ + struct netmap_adapter na; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + + bzero(&na, sizeof(na)); + + na.ifp = ctx->ifc_ifp; + na.na_flags = NAF_BDG_MAYSLEEP; + MPASS(ctx->ifc_softc_ctx.isc_ntxqsets); + MPASS(ctx->ifc_softc_ctx.isc_nrxqsets); + + na.num_tx_desc = scctx->isc_ntxd[0]; + na.num_rx_desc = scctx->isc_nrxd[0]; + na.nm_txsync = iflib_netmap_txsync; + na.nm_rxsync = iflib_netmap_rxsync; + na.nm_register = iflib_netmap_register; + na.nm_intr = iflib_netmap_intr; + na.num_tx_rings = ctx->ifc_softc_ctx.isc_ntxqsets; + na.num_rx_rings = ctx->ifc_softc_ctx.isc_nrxqsets; + return (netmap_attach(&na)); +} + +static void +iflib_netmap_txq_init(if_ctx_t ctx, iflib_txq_t txq) +{ + struct netmap_adapter *na = NA(ctx->ifc_ifp); + struct netmap_slot *slot; + + slot = netmap_reset(na, NR_TX, txq->ift_id, 0); + if (slot == NULL) + return; + for (int i = 0; i < ctx->ifc_softc_ctx.isc_ntxd[0]; i++) { + + /* + * In netmap mode, set the map for the packet buffer. + * NOTE: Some drivers (not this one) also need to set + * the physical buffer address in the NIC ring. + * netmap_idx_n2k() maps a nic index, i, into the corresponding + * netmap slot index, si + */ + int si = netmap_idx_n2k(na->tx_rings[txq->ift_id], i); + netmap_load_map(na, txq->ift_buf_tag, txq->ift_sds.ifsd_map[i], + NMB(na, slot + si)); + } +} + +static void +iflib_netmap_rxq_init(if_ctx_t ctx, iflib_rxq_t rxq) +{ + struct netmap_adapter *na = NA(ctx->ifc_ifp); + struct netmap_kring *kring = na->rx_rings[rxq->ifr_id]; + struct netmap_slot *slot; + uint32_t nm_i; + + slot = netmap_reset(na, NR_RX, rxq->ifr_id, 0); + if (slot == NULL) + return; + nm_i = netmap_idx_n2k(kring, 0); + netmap_fl_refill(rxq, kring, nm_i, true); +} + +static void +iflib_netmap_timer_adjust(if_ctx_t ctx, iflib_txq_t txq, uint32_t *reset_on) +{ + struct netmap_kring *kring; + uint16_t txqid; + + txqid = txq->ift_id; + kring = NA(ctx->ifc_ifp)->tx_rings[txqid]; + + if (kring->nr_hwcur != nm_next(kring->nr_hwtail, kring->nkr_num_slots - 1)) { + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD); + if (ctx->isc_txd_credits_update(ctx->ifc_softc, txqid, false)) + netmap_tx_irq(ctx->ifc_ifp, txqid); + if (!(ctx->ifc_flags & IFC_NETMAP_TX_IRQ)) { + if (hz < 2000) + *reset_on = 1; + else + *reset_on = hz / 1000; + } + } +} + +#define iflib_netmap_detach(ifp) netmap_detach(ifp) + +#else +#define iflib_netmap_txq_init(ctx, txq) +#define iflib_netmap_rxq_init(ctx, rxq) +#define iflib_netmap_detach(ifp) + +#define iflib_netmap_attach(ctx) (0) +#define netmap_rx_irq(ifp, qid, budget) (0) +#define netmap_tx_irq(ifp, qid) do {} while (0) +#define iflib_netmap_timer_adjust(ctx, txq, reset_on) +#endif + +#if defined(__i386__) || defined(__amd64__) +static __inline void +prefetch(void *x) +{ + __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x)); +} +static __inline void +prefetch2cachelines(void *x) +{ + __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x)); +#if (CACHE_LINE_SIZE < 128) + __asm volatile("prefetcht0 %0" :: "m" (*(((unsigned long *)x)+CACHE_LINE_SIZE/(sizeof(unsigned long))))); +#endif +} +#else +#define prefetch(x) +#define prefetch2cachelines(x) +#endif + +static void +iru_init(if_rxd_update_t iru, iflib_rxq_t rxq, uint8_t flid) +{ + iflib_fl_t fl; + + fl = &rxq->ifr_fl[flid]; + iru->iru_paddrs = fl->ifl_bus_addrs; + iru->iru_vaddrs = &fl->ifl_vm_addrs[0]; + iru->iru_idxs = fl->ifl_rxd_idxs; + iru->iru_qsidx = rxq->ifr_id; + iru->iru_buf_size = fl->ifl_buf_size; + iru->iru_flidx = fl->ifl_id; +} + +static void +_iflib_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) +{ + if (err) + return; + *(bus_addr_t *) arg = segs[0].ds_addr; +} + +int +iflib_dma_alloc_align(if_ctx_t ctx, int size, int align, iflib_dma_info_t dma, int mapflags) +{ + int err; + device_t dev = ctx->ifc_dev; + + err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ + align, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &dma->idi_tag); + if (err) { + device_printf(dev, + "%s: bus_dma_tag_create failed: %d\n", + __func__, err); + goto fail_0; + } + + err = bus_dmamem_alloc(dma->idi_tag, (void**) &dma->idi_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &dma->idi_map); + if (err) { + device_printf(dev, + "%s: bus_dmamem_alloc(%ju) failed: %d\n", + __func__, (uintmax_t)size, err); + goto fail_1; + } + + dma->idi_paddr = IF_BAD_DMA; + err = bus_dmamap_load(dma->idi_tag, dma->idi_map, dma->idi_vaddr, + size, _iflib_dmamap_cb, &dma->idi_paddr, mapflags | BUS_DMA_NOWAIT); + if (err || dma->idi_paddr == IF_BAD_DMA) { + device_printf(dev, + "%s: bus_dmamap_load failed: %d\n", + __func__, err); + goto fail_2; + } + + dma->idi_size = size; + return (0); + +fail_2: + bus_dmamem_free(dma->idi_tag, dma->idi_vaddr, dma->idi_map); +fail_1: + bus_dma_tag_destroy(dma->idi_tag); +fail_0: + dma->idi_tag = NULL; + + return (err); +} + +int +iflib_dma_alloc(if_ctx_t ctx, int size, iflib_dma_info_t dma, int mapflags) +{ + if_shared_ctx_t sctx = ctx->ifc_sctx; + + KASSERT(sctx->isc_q_align != 0, ("alignment value not initialized")); + + return (iflib_dma_alloc_align(ctx, size, sctx->isc_q_align, dma, mapflags)); +} + +int +iflib_dma_alloc_multi(if_ctx_t ctx, int *sizes, iflib_dma_info_t *dmalist, int mapflags, int count) +{ + int i, err; + iflib_dma_info_t *dmaiter; + + dmaiter = dmalist; + for (i = 0; i < count; i++, dmaiter++) { + if ((err = iflib_dma_alloc(ctx, sizes[i], *dmaiter, mapflags)) != 0) + break; + } + if (err) + iflib_dma_free_multi(dmalist, i); + return (err); +} + +void +iflib_dma_free(iflib_dma_info_t dma) +{ + if (dma->idi_tag == NULL) + return; + if (dma->idi_paddr != IF_BAD_DMA) { + bus_dmamap_sync(dma->idi_tag, dma->idi_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->idi_tag, dma->idi_map); + dma->idi_paddr = IF_BAD_DMA; + } + if (dma->idi_vaddr != NULL) { + bus_dmamem_free(dma->idi_tag, dma->idi_vaddr, dma->idi_map); + dma->idi_vaddr = NULL; + } + bus_dma_tag_destroy(dma->idi_tag); + dma->idi_tag = NULL; +} + +void +iflib_dma_free_multi(iflib_dma_info_t *dmalist, int count) +{ + int i; + iflib_dma_info_t *dmaiter = dmalist; + + for (i = 0; i < count; i++, dmaiter++) + iflib_dma_free(*dmaiter); +} + +#ifdef EARLY_AP_STARTUP +static const int iflib_started = 1; +#else +/* + * We used to abuse the smp_started flag to decide if the queues have been + * fully initialized (by late taskqgroup_adjust() calls in a SYSINIT()). + * That gave bad races, since the SYSINIT() runs strictly after smp_started + * is set. Run a SYSINIT() strictly after that to just set a usable + * completion flag. + */ + +static int iflib_started; + +static void +iflib_record_started(void *arg) +{ + iflib_started = 1; +} + +SYSINIT(iflib_record_started, SI_SUB_SMP + 1, SI_ORDER_FIRST, + iflib_record_started, NULL); +#endif + +static int +iflib_fast_intr(void *arg) +{ + iflib_filter_info_t info = arg; + struct grouptask *gtask = info->ifi_task; + int result; + + if (!iflib_started) + return (FILTER_STRAY); + + DBG_COUNTER_INC(fast_intrs); + if (info->ifi_filter != NULL) { + result = info->ifi_filter(info->ifi_filter_arg); + if ((result & FILTER_SCHEDULE_THREAD) == 0) + return (result); + } + + GROUPTASK_ENQUEUE(gtask); + return (FILTER_HANDLED); +} + +static int +iflib_fast_intr_rxtx(void *arg) +{ + iflib_filter_info_t info = arg; + struct grouptask *gtask = info->ifi_task; + if_ctx_t ctx; + iflib_rxq_t rxq = (iflib_rxq_t)info->ifi_ctx; + iflib_txq_t txq; + void *sc; + int i, cidx, result; + qidx_t txqid; + bool intr_enable, intr_legacy; + + if (!iflib_started) + return (FILTER_STRAY); + + DBG_COUNTER_INC(fast_intrs); + if (info->ifi_filter != NULL) { + result = info->ifi_filter(info->ifi_filter_arg); + if ((result & FILTER_SCHEDULE_THREAD) == 0) + return (result); + } + + ctx = rxq->ifr_ctx; + sc = ctx->ifc_softc; + intr_enable = false; + intr_legacy = !!(ctx->ifc_flags & IFC_LEGACY); + MPASS(rxq->ifr_ntxqirq); + for (i = 0; i < rxq->ifr_ntxqirq; i++) { + txqid = rxq->ifr_txqid[i]; + txq = &ctx->ifc_txqs[txqid]; + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD); + if (!ctx->isc_txd_credits_update(sc, txqid, false)) { + if (intr_legacy) + intr_enable = true; + else + IFDI_TX_QUEUE_INTR_ENABLE(ctx, txqid); + continue; + } + GROUPTASK_ENQUEUE(&txq->ift_task); + } + if (ctx->ifc_sctx->isc_flags & IFLIB_HAS_RXCQ) + cidx = rxq->ifr_cq_cidx; + else + cidx = rxq->ifr_fl[0].ifl_cidx; + if (iflib_rxd_avail(ctx, rxq, cidx, 1)) + GROUPTASK_ENQUEUE(gtask); + else { + if (intr_legacy) + intr_enable = true; + else + IFDI_RX_QUEUE_INTR_ENABLE(ctx, rxq->ifr_id); + DBG_COUNTER_INC(rx_intr_enables); + } + if (intr_enable) + IFDI_INTR_ENABLE(ctx); + return (FILTER_HANDLED); +} + + +static int +iflib_fast_intr_ctx(void *arg) +{ + iflib_filter_info_t info = arg; + struct grouptask *gtask = info->ifi_task; + int result; + + if (!iflib_started) + return (FILTER_STRAY); + + DBG_COUNTER_INC(fast_intrs); + if (info->ifi_filter != NULL) { + result = info->ifi_filter(info->ifi_filter_arg); + if ((result & FILTER_SCHEDULE_THREAD) == 0) + return (result); + } + + GROUPTASK_ENQUEUE(gtask); + return (FILTER_HANDLED); +} + +static int +_iflib_irq_alloc(if_ctx_t ctx, if_irq_t irq, int rid, + driver_filter_t filter, driver_intr_t handler, void *arg, + const char *name) +{ + struct resource *res; + void *tag = NULL; + device_t dev = ctx->ifc_dev; + int flags, i, rc; + + flags = RF_ACTIVE; + if (ctx->ifc_flags & IFC_LEGACY) + flags |= RF_SHAREABLE; + MPASS(rid < 512); + i = rid; + res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, flags); + if (res == NULL) { + device_printf(dev, + "failed to allocate IRQ for rid %d, name %s.\n", rid, name); + return (ENOMEM); + } + irq->ii_res = res; + KASSERT(filter == NULL || handler == NULL, ("filter and handler can't both be non-NULL")); + rc = bus_setup_intr(dev, res, INTR_MPSAFE | INTR_TYPE_NET, + filter, handler, arg, &tag); + if (rc != 0) { + device_printf(dev, + "failed to setup interrupt for rid %d, name %s: %d\n", + rid, name ? name : "unknown", rc); + return (rc); + } else if (name) + bus_describe_intr(dev, res, tag, "%s", name); + + irq->ii_tag = tag; + return (0); +} + +/********************************************************************* + * + * Allocate DMA resources for TX buffers as well as memory for the TX + * mbuf map. TX DMA maps (non-TSO/TSO) and TX mbuf map are kept in a + * iflib_sw_tx_desc_array structure, storing all the information that + * is needed to transmit a packet on the wire. This is called only + * once at attach, setup is done every reset. + * + **********************************************************************/ +static int +iflib_txsd_alloc(iflib_txq_t txq) +{ + if_ctx_t ctx = txq->ift_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + device_t dev = ctx->ifc_dev; + bus_size_t tsomaxsize; + int err, nsegments, ntsosegments; + bool tso; + + nsegments = scctx->isc_tx_nsegments; + ntsosegments = scctx->isc_tx_tso_segments_max; + tsomaxsize = scctx->isc_tx_tso_size_max; + if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_VLAN_MTU) + tsomaxsize += sizeof(struct ether_vlan_header); + MPASS(scctx->isc_ntxd[0] > 0); + MPASS(scctx->isc_ntxd[txq->ift_br_offset] > 0); + MPASS(nsegments > 0); + if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_TSO) { + MPASS(ntsosegments > 0); + MPASS(sctx->isc_tso_maxsize >= tsomaxsize); + } + + /* + * Set up DMA tags for TX buffers. + */ + if ((err = bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sctx->isc_tx_maxsize, /* maxsize */ + nsegments, /* nsegments */ + sctx->isc_tx_maxsegsize, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &txq->ift_buf_tag))) { + device_printf(dev,"Unable to allocate TX DMA tag: %d\n", err); + device_printf(dev,"maxsize: %ju nsegments: %d maxsegsize: %ju\n", + (uintmax_t)sctx->isc_tx_maxsize, nsegments, (uintmax_t)sctx->isc_tx_maxsegsize); + goto fail; + } + tso = (if_getcapabilities(ctx->ifc_ifp) & IFCAP_TSO) != 0; + if (tso && (err = bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + tsomaxsize, /* maxsize */ + ntsosegments, /* nsegments */ + sctx->isc_tso_maxsegsize,/* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &txq->ift_tso_buf_tag))) { + device_printf(dev, "Unable to allocate TSO TX DMA tag: %d\n", + err); + goto fail; + } + + /* Allocate memory for the TX mbuf map. */ + if (!(txq->ift_sds.ifsd_m = + (struct mbuf **) malloc(sizeof(struct mbuf *) * + scctx->isc_ntxd[txq->ift_br_offset], M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, "Unable to allocate TX mbuf map memory\n"); + err = ENOMEM; + goto fail; + } + + /* + * Create the DMA maps for TX buffers. + */ + if ((txq->ift_sds.ifsd_map = (bus_dmamap_t *)malloc( + sizeof(bus_dmamap_t) * scctx->isc_ntxd[txq->ift_br_offset], + M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) { + device_printf(dev, + "Unable to allocate TX buffer DMA map memory\n"); + err = ENOMEM; + goto fail; + } + if (tso && (txq->ift_sds.ifsd_tso_map = (bus_dmamap_t *)malloc( + sizeof(bus_dmamap_t) * scctx->isc_ntxd[txq->ift_br_offset], + M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) { + device_printf(dev, + "Unable to allocate TSO TX buffer map memory\n"); + err = ENOMEM; + goto fail; + } + for (int i = 0; i < scctx->isc_ntxd[txq->ift_br_offset]; i++) { + err = bus_dmamap_create(txq->ift_buf_tag, 0, + &txq->ift_sds.ifsd_map[i]); + if (err != 0) { + device_printf(dev, "Unable to create TX DMA map\n"); + goto fail; + } + if (!tso) + continue; + err = bus_dmamap_create(txq->ift_tso_buf_tag, 0, + &txq->ift_sds.ifsd_tso_map[i]); + if (err != 0) { + device_printf(dev, "Unable to create TSO TX DMA map\n"); + goto fail; + } + } + return (0); +fail: + /* We free all, it handles case where we are in the middle */ + iflib_tx_structures_free(ctx); + return (err); +} + +static void +iflib_txsd_destroy(if_ctx_t ctx, iflib_txq_t txq, int i) +{ + bus_dmamap_t map; + + map = NULL; + if (txq->ift_sds.ifsd_map != NULL) + map = txq->ift_sds.ifsd_map[i]; + if (map != NULL) { + bus_dmamap_sync(txq->ift_buf_tag, map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_buf_tag, map); + bus_dmamap_destroy(txq->ift_buf_tag, map); + txq->ift_sds.ifsd_map[i] = NULL; + } + + map = NULL; + if (txq->ift_sds.ifsd_tso_map != NULL) + map = txq->ift_sds.ifsd_tso_map[i]; + if (map != NULL) { + bus_dmamap_sync(txq->ift_tso_buf_tag, map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_tso_buf_tag, map); + bus_dmamap_destroy(txq->ift_tso_buf_tag, map); + txq->ift_sds.ifsd_tso_map[i] = NULL; + } +} + +static void +iflib_txq_destroy(iflib_txq_t txq) +{ + if_ctx_t ctx = txq->ift_ctx; + + for (int i = 0; i < txq->ift_size; i++) + iflib_txsd_destroy(ctx, txq, i); + if (txq->ift_sds.ifsd_map != NULL) { + free(txq->ift_sds.ifsd_map, M_IFLIB); + txq->ift_sds.ifsd_map = NULL; + } + if (txq->ift_sds.ifsd_tso_map != NULL) { + free(txq->ift_sds.ifsd_tso_map, M_IFLIB); + txq->ift_sds.ifsd_tso_map = NULL; + } + if (txq->ift_sds.ifsd_m != NULL) { + free(txq->ift_sds.ifsd_m, M_IFLIB); + txq->ift_sds.ifsd_m = NULL; + } + if (txq->ift_buf_tag != NULL) { + bus_dma_tag_destroy(txq->ift_buf_tag); + txq->ift_buf_tag = NULL; + } + if (txq->ift_tso_buf_tag != NULL) { + bus_dma_tag_destroy(txq->ift_tso_buf_tag); + txq->ift_tso_buf_tag = NULL; + } +} + +static void +iflib_txsd_free(if_ctx_t ctx, iflib_txq_t txq, int i) +{ + struct mbuf **mp; + + mp = &txq->ift_sds.ifsd_m[i]; + if (*mp == NULL) + return; + + if (txq->ift_sds.ifsd_map != NULL) { + bus_dmamap_sync(txq->ift_buf_tag, + txq->ift_sds.ifsd_map[i], BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_buf_tag, txq->ift_sds.ifsd_map[i]); + } + if (txq->ift_sds.ifsd_tso_map != NULL) { + bus_dmamap_sync(txq->ift_tso_buf_tag, + txq->ift_sds.ifsd_tso_map[i], BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_tso_buf_tag, + txq->ift_sds.ifsd_tso_map[i]); + } + m_free(*mp); + DBG_COUNTER_INC(tx_frees); + *mp = NULL; +} + +static int +iflib_txq_setup(iflib_txq_t txq) +{ + if_ctx_t ctx = txq->ift_ctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + iflib_dma_info_t di; + int i; + + /* Set number of descriptors available */ + txq->ift_qstatus = IFLIB_QUEUE_IDLE; + /* XXX make configurable */ + txq->ift_update_freq = IFLIB_DEFAULT_TX_UPDATE_FREQ; + + /* Reset indices */ + txq->ift_cidx_processed = 0; + txq->ift_pidx = txq->ift_cidx = txq->ift_npending = 0; + txq->ift_size = scctx->isc_ntxd[txq->ift_br_offset]; + + for (i = 0, di = txq->ift_ifdi; i < sctx->isc_ntxqs; i++, di++) + bzero((void *)di->idi_vaddr, di->idi_size); + + IFDI_TXQ_SETUP(ctx, txq->ift_id); + for (i = 0, di = txq->ift_ifdi; i < sctx->isc_ntxqs; i++, di++) + bus_dmamap_sync(di->idi_tag, di->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + return (0); +} + +/********************************************************************* + * + * Allocate DMA resources for RX buffers as well as memory for the RX + * mbuf map, direct RX cluster pointer map and RX cluster bus address + * map. RX DMA map, RX mbuf map, direct RX cluster pointer map and + * RX cluster map are kept in a iflib_sw_rx_desc_array structure. + * Since we use use one entry in iflib_sw_rx_desc_array per received + * packet, the maximum number of entries we'll need is equal to the + * number of hardware receive descriptors that we've allocated. + * + **********************************************************************/ +static int +iflib_rxsd_alloc(iflib_rxq_t rxq) +{ + if_ctx_t ctx = rxq->ifr_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + device_t dev = ctx->ifc_dev; + iflib_fl_t fl; + int err; + + MPASS(scctx->isc_nrxd[0] > 0); + MPASS(scctx->isc_nrxd[rxq->ifr_fl_offset] > 0); + + fl = rxq->ifr_fl; + for (int i = 0; i < rxq->ifr_nfl; i++, fl++) { + fl->ifl_size = scctx->isc_nrxd[rxq->ifr_fl_offset]; /* this isn't necessarily the same */ + /* Set up DMA tag for RX buffers. */ + err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sctx->isc_rx_maxsize, /* maxsize */ + sctx->isc_rx_nsegments, /* nsegments */ + sctx->isc_rx_maxsegsize, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &fl->ifl_buf_tag); + if (err) { + device_printf(dev, + "Unable to allocate RX DMA tag: %d\n", err); + goto fail; + } + + /* Allocate memory for the RX mbuf map. */ + if (!(fl->ifl_sds.ifsd_m = + (struct mbuf **) malloc(sizeof(struct mbuf *) * + scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, + "Unable to allocate RX mbuf map memory\n"); + err = ENOMEM; + goto fail; + } + + /* Allocate memory for the direct RX cluster pointer map. */ + if (!(fl->ifl_sds.ifsd_cl = + (caddr_t *) malloc(sizeof(caddr_t) * + scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, + "Unable to allocate RX cluster map memory\n"); + err = ENOMEM; + goto fail; + } + + /* Allocate memory for the RX cluster bus address map. */ + if (!(fl->ifl_sds.ifsd_ba = + (bus_addr_t *) malloc(sizeof(bus_addr_t) * + scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, + "Unable to allocate RX bus address map memory\n"); + err = ENOMEM; + goto fail; + } + + /* + * Create the DMA maps for RX buffers. + */ + if (!(fl->ifl_sds.ifsd_map = + (bus_dmamap_t *) malloc(sizeof(bus_dmamap_t) * scctx->isc_nrxd[rxq->ifr_fl_offset], M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, + "Unable to allocate RX buffer DMA map memory\n"); + err = ENOMEM; + goto fail; + } + for (int i = 0; i < scctx->isc_nrxd[rxq->ifr_fl_offset]; i++) { + err = bus_dmamap_create(fl->ifl_buf_tag, 0, + &fl->ifl_sds.ifsd_map[i]); + if (err != 0) { + device_printf(dev, "Unable to create RX buffer DMA map\n"); + goto fail; + } + } + } + return (0); + +fail: + iflib_rx_structures_free(ctx); + return (err); +} + + +/* + * Internal service routines + */ + +struct rxq_refill_cb_arg { + int error; + bus_dma_segment_t seg; + int nseg; +}; + +static void +_rxq_refill_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct rxq_refill_cb_arg *cb_arg = arg; + + cb_arg->error = error; + cb_arg->seg = segs[0]; + cb_arg->nseg = nseg; +} + +/** + * _iflib_fl_refill - refill an rxq free-buffer list + * @ctx: the iflib context + * @fl: the free list to refill + * @count: the number of new buffers to allocate + * + * (Re)populate an rxq free-buffer list with up to @count new packet buffers. + * The caller must assure that @count does not exceed the queue's capacity. + */ +static void +_iflib_fl_refill(if_ctx_t ctx, iflib_fl_t fl, int count) +{ + struct if_rxd_update iru; + struct rxq_refill_cb_arg cb_arg; + struct mbuf *m; + caddr_t cl, *sd_cl; + struct mbuf **sd_m; + bus_dmamap_t *sd_map; + bus_addr_t bus_addr, *sd_ba; + int err, frag_idx, i, idx, n, pidx; + qidx_t credits; + + sd_m = fl->ifl_sds.ifsd_m; + sd_map = fl->ifl_sds.ifsd_map; + sd_cl = fl->ifl_sds.ifsd_cl; + sd_ba = fl->ifl_sds.ifsd_ba; + pidx = fl->ifl_pidx; + idx = pidx; + frag_idx = fl->ifl_fragidx; + credits = fl->ifl_credits; + + i = 0; + n = count; + MPASS(n > 0); + MPASS(credits + n <= fl->ifl_size); + + if (pidx < fl->ifl_cidx) + MPASS(pidx + n <= fl->ifl_cidx); + if (pidx == fl->ifl_cidx && (credits < fl->ifl_size)) + MPASS(fl->ifl_gen == 0); + if (pidx > fl->ifl_cidx) + MPASS(n <= fl->ifl_size - pidx + fl->ifl_cidx); + + DBG_COUNTER_INC(fl_refills); + if (n > 8) + DBG_COUNTER_INC(fl_refills_large); + iru_init(&iru, fl->ifl_rxq, fl->ifl_id); + while (n--) { + /* + * We allocate an uninitialized mbuf + cluster, mbuf is + * initialized after rx. + * + * If the cluster is still set then we know a minimum sized packet was received + */ + bit_ffc_at(fl->ifl_rx_bitmap, frag_idx, fl->ifl_size, + &frag_idx); + if (frag_idx < 0) + bit_ffc(fl->ifl_rx_bitmap, fl->ifl_size, &frag_idx); + MPASS(frag_idx >= 0); + if ((cl = sd_cl[frag_idx]) == NULL) { + if ((cl = m_cljget(NULL, M_NOWAIT, fl->ifl_buf_size)) == NULL) + break; + + cb_arg.error = 0; + MPASS(sd_map != NULL); + err = bus_dmamap_load(fl->ifl_buf_tag, sd_map[frag_idx], + cl, fl->ifl_buf_size, _rxq_refill_cb, &cb_arg, + BUS_DMA_NOWAIT); + if (err != 0 || cb_arg.error) { + /* + * !zone_pack ? + */ + if (fl->ifl_zone == zone_pack) + uma_zfree(fl->ifl_zone, cl); + break; + } + + sd_ba[frag_idx] = bus_addr = cb_arg.seg.ds_addr; + sd_cl[frag_idx] = cl; +#if MEMORY_LOGGING + fl->ifl_cl_enqueued++; +#endif + } else { + bus_addr = sd_ba[frag_idx]; + } + bus_dmamap_sync(fl->ifl_buf_tag, sd_map[frag_idx], + BUS_DMASYNC_PREREAD); + + if (sd_m[frag_idx] == NULL) { + if ((m = m_gethdr(M_NOWAIT, MT_NOINIT)) == NULL) { + break; + } + sd_m[frag_idx] = m; + } + bit_set(fl->ifl_rx_bitmap, frag_idx); +#if MEMORY_LOGGING + fl->ifl_m_enqueued++; +#endif + + DBG_COUNTER_INC(rx_allocs); + fl->ifl_rxd_idxs[i] = frag_idx; + fl->ifl_bus_addrs[i] = bus_addr; + fl->ifl_vm_addrs[i] = cl; + credits++; + i++; + MPASS(credits <= fl->ifl_size); + if (++idx == fl->ifl_size) { + fl->ifl_gen = 1; + idx = 0; + } + if (n == 0 || i == IFLIB_MAX_RX_REFRESH) { + iru.iru_pidx = pidx; + iru.iru_count = i; + ctx->isc_rxd_refill(ctx->ifc_softc, &iru); + i = 0; + pidx = idx; + fl->ifl_pidx = idx; + fl->ifl_credits = credits; + } + } + + if (i) { + iru.iru_pidx = pidx; + iru.iru_count = i; + ctx->isc_rxd_refill(ctx->ifc_softc, &iru); + fl->ifl_pidx = idx; + fl->ifl_credits = credits; + } + DBG_COUNTER_INC(rxd_flush); + if (fl->ifl_pidx == 0) + pidx = fl->ifl_size - 1; + else + pidx = fl->ifl_pidx - 1; + + bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + ctx->isc_rxd_flush(ctx->ifc_softc, fl->ifl_rxq->ifr_id, fl->ifl_id, pidx); + fl->ifl_fragidx = frag_idx; +} + +static __inline void +__iflib_fl_refill_lt(if_ctx_t ctx, iflib_fl_t fl, int max) +{ + /* we avoid allowing pidx to catch up with cidx as it confuses ixl */ + int32_t reclaimable = fl->ifl_size - fl->ifl_credits - 1; +#ifdef INVARIANTS + int32_t delta = fl->ifl_size - get_inuse(fl->ifl_size, fl->ifl_cidx, fl->ifl_pidx, fl->ifl_gen) - 1; +#endif + + MPASS(fl->ifl_credits <= fl->ifl_size); + MPASS(reclaimable == delta); + + if (reclaimable > 0) + _iflib_fl_refill(ctx, fl, min(max, reclaimable)); +} + +uint8_t +iflib_in_detach(if_ctx_t ctx) +{ + bool in_detach; + + STATE_LOCK(ctx); + in_detach = !!(ctx->ifc_flags & IFC_IN_DETACH); + STATE_UNLOCK(ctx); + return (in_detach); +} + +static void +iflib_fl_bufs_free(iflib_fl_t fl) +{ + iflib_dma_info_t idi = fl->ifl_ifdi; + bus_dmamap_t sd_map; + uint32_t i; + + for (i = 0; i < fl->ifl_size; i++) { + struct mbuf **sd_m = &fl->ifl_sds.ifsd_m[i]; + caddr_t *sd_cl = &fl->ifl_sds.ifsd_cl[i]; + + if (*sd_cl != NULL) { + sd_map = fl->ifl_sds.ifsd_map[i]; + bus_dmamap_sync(fl->ifl_buf_tag, sd_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(fl->ifl_buf_tag, sd_map); + if (*sd_cl != NULL) + uma_zfree(fl->ifl_zone, *sd_cl); + // XXX: Should this get moved out? + if (iflib_in_detach(fl->ifl_rxq->ifr_ctx)) + bus_dmamap_destroy(fl->ifl_buf_tag, sd_map); + if (*sd_m != NULL) { + m_init(*sd_m, M_NOWAIT, MT_DATA, 0); + uma_zfree(zone_mbuf, *sd_m); + } + } else { + MPASS(*sd_cl == NULL); + MPASS(*sd_m == NULL); + } +#if MEMORY_LOGGING + fl->ifl_m_dequeued++; + fl->ifl_cl_dequeued++; +#endif + *sd_cl = NULL; + *sd_m = NULL; + } +#ifdef INVARIANTS + for (i = 0; i < fl->ifl_size; i++) { + MPASS(fl->ifl_sds.ifsd_cl[i] == NULL); + MPASS(fl->ifl_sds.ifsd_m[i] == NULL); + } +#endif + /* + * Reset free list values + */ + fl->ifl_credits = fl->ifl_cidx = fl->ifl_pidx = fl->ifl_gen = fl->ifl_fragidx = 0; + bzero(idi->idi_vaddr, idi->idi_size); +} + +/********************************************************************* + * + * Initialize a free list and its buffers. + * + **********************************************************************/ +static int +iflib_fl_setup(iflib_fl_t fl) +{ + iflib_rxq_t rxq = fl->ifl_rxq; + if_ctx_t ctx = rxq->ifr_ctx; + + bit_nclear(fl->ifl_rx_bitmap, 0, fl->ifl_size - 1); + /* + ** Free current RX buffer structs and their mbufs + */ + iflib_fl_bufs_free(fl); + /* Now replenish the mbufs */ + MPASS(fl->ifl_credits == 0); + fl->ifl_buf_size = ctx->ifc_rx_mbuf_sz; + if (fl->ifl_buf_size > ctx->ifc_max_fl_buf_size) + ctx->ifc_max_fl_buf_size = fl->ifl_buf_size; + fl->ifl_cltype = m_gettype(fl->ifl_buf_size); + fl->ifl_zone = m_getzone(fl->ifl_buf_size); + + + /* avoid pre-allocating zillions of clusters to an idle card + * potentially speeding up attach + */ + _iflib_fl_refill(ctx, fl, min(128, fl->ifl_size)); + MPASS(min(128, fl->ifl_size) == fl->ifl_credits); + if (min(128, fl->ifl_size) != fl->ifl_credits) + return (ENOBUFS); + /* + * handle failure + */ + MPASS(rxq != NULL); + MPASS(fl->ifl_ifdi != NULL); + bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + return (0); +} + +/********************************************************************* + * + * Free receive ring data structures + * + **********************************************************************/ +static void +iflib_rx_sds_free(iflib_rxq_t rxq) +{ + iflib_fl_t fl; + int i, j; + + if (rxq->ifr_fl != NULL) { + for (i = 0; i < rxq->ifr_nfl; i++) { + fl = &rxq->ifr_fl[i]; + if (fl->ifl_buf_tag != NULL) { + if (fl->ifl_sds.ifsd_map != NULL) { + for (j = 0; j < fl->ifl_size; j++) { + if (fl->ifl_sds.ifsd_map[j] == + NULL) + continue; + bus_dmamap_sync( + fl->ifl_buf_tag, + fl->ifl_sds.ifsd_map[j], + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload( + fl->ifl_buf_tag, + fl->ifl_sds.ifsd_map[j]); + } + } + bus_dma_tag_destroy(fl->ifl_buf_tag); + fl->ifl_buf_tag = NULL; + } + free(fl->ifl_sds.ifsd_m, M_IFLIB); + free(fl->ifl_sds.ifsd_cl, M_IFLIB); + free(fl->ifl_sds.ifsd_ba, M_IFLIB); + free(fl->ifl_sds.ifsd_map, M_IFLIB); + fl->ifl_sds.ifsd_m = NULL; + fl->ifl_sds.ifsd_cl = NULL; + fl->ifl_sds.ifsd_ba = NULL; + fl->ifl_sds.ifsd_map = NULL; + } + free(rxq->ifr_fl, M_IFLIB); + rxq->ifr_fl = NULL; + rxq->ifr_cq_cidx = 0; + } +} + +/* + * Timer routine + */ +static void +iflib_timer(void *arg) +{ + iflib_txq_t txq = arg; + if_ctx_t ctx = txq->ift_ctx; + if_softc_ctx_t sctx = &ctx->ifc_softc_ctx; + uint64_t this_tick = ticks; + uint32_t reset_on = hz / 2; + + if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)) + return; + + /* + ** Check on the state of the TX queue(s), this + ** can be done without the lock because its RO + ** and the HUNG state will be static if set. + */ + if (this_tick - txq->ift_last_timer_tick >= hz / 2) { + txq->ift_last_timer_tick = this_tick; + IFDI_TIMER(ctx, txq->ift_id); + if ((txq->ift_qstatus == IFLIB_QUEUE_HUNG) && + ((txq->ift_cleaned_prev == txq->ift_cleaned) || + (sctx->isc_pause_frames == 0))) + goto hung; + + if (ifmp_ring_is_stalled(txq->ift_br)) + txq->ift_qstatus = IFLIB_QUEUE_HUNG; + txq->ift_cleaned_prev = txq->ift_cleaned; + } +#ifdef DEV_NETMAP + if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP) + iflib_netmap_timer_adjust(ctx, txq, &reset_on); +#endif + /* handle any laggards */ + if (txq->ift_db_pending) + GROUPTASK_ENQUEUE(&txq->ift_task); + + sctx->isc_pause_frames = 0; + if (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING) + callout_reset_on(&txq->ift_timer, reset_on, iflib_timer, txq, txq->ift_timer.c_cpu); + return; + + hung: + device_printf(ctx->ifc_dev, + "Watchdog timeout (TX: %d desc avail: %d pidx: %d) -- resetting\n", + txq->ift_id, TXQ_AVAIL(txq), txq->ift_pidx); + STATE_LOCK(ctx); + if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); + ctx->ifc_flags |= (IFC_DO_WATCHDOG|IFC_DO_RESET); + iflib_admin_intr_deferred(ctx); + STATE_UNLOCK(ctx); +} + +static void +iflib_calc_rx_mbuf_sz(if_ctx_t ctx) +{ + if_softc_ctx_t sctx = &ctx->ifc_softc_ctx; + + /* + * XXX don't set the max_frame_size to larger + * than the hardware can handle + */ + if (sctx->isc_max_frame_size <= MCLBYTES) + ctx->ifc_rx_mbuf_sz = MCLBYTES; + else + ctx->ifc_rx_mbuf_sz = MJUMPAGESIZE; +} + +uint32_t +iflib_get_rx_mbuf_sz(if_ctx_t ctx) +{ + + return (ctx->ifc_rx_mbuf_sz); +} + +static void +iflib_init_locked(if_ctx_t ctx) +{ + if_softc_ctx_t sctx = &ctx->ifc_softc_ctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + if_t ifp = ctx->ifc_ifp; + iflib_fl_t fl; + iflib_txq_t txq; + iflib_rxq_t rxq; + int i, j, tx_ip_csum_flags, tx_ip6_csum_flags; + + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); + IFDI_INTR_DISABLE(ctx); + + tx_ip_csum_flags = scctx->isc_tx_csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP); + tx_ip6_csum_flags = scctx->isc_tx_csum_flags & (CSUM_IP6_TCP | CSUM_IP6_UDP | CSUM_IP6_SCTP); + /* Set hardware offload abilities */ + if_clearhwassist(ifp); + if (if_getcapenable(ifp) & IFCAP_TXCSUM) + if_sethwassistbits(ifp, tx_ip_csum_flags, 0); + if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6) + if_sethwassistbits(ifp, tx_ip6_csum_flags, 0); + if (if_getcapenable(ifp) & IFCAP_TSO4) + if_sethwassistbits(ifp, CSUM_IP_TSO, 0); + if (if_getcapenable(ifp) & IFCAP_TSO6) + if_sethwassistbits(ifp, CSUM_IP6_TSO, 0); + + for (i = 0, txq = ctx->ifc_txqs; i < sctx->isc_ntxqsets; i++, txq++) { + CALLOUT_LOCK(txq); + callout_stop(&txq->ift_timer); + CALLOUT_UNLOCK(txq); + iflib_netmap_txq_init(ctx, txq); + } + + /* + * Calculate a suitable Rx mbuf size prior to calling IFDI_INIT, so + * that drivers can use the value when setting up the hardware receive + * buffers. + */ + iflib_calc_rx_mbuf_sz(ctx); + +#ifdef INVARIANTS + i = if_getdrvflags(ifp); +#endif + IFDI_INIT(ctx); + MPASS(if_getdrvflags(ifp) == i); + for (i = 0, rxq = ctx->ifc_rxqs; i < sctx->isc_nrxqsets; i++, rxq++) { + /* XXX this should really be done on a per-queue basis */ + if (if_getcapenable(ifp) & IFCAP_NETMAP) { + MPASS(rxq->ifr_id == i); + iflib_netmap_rxq_init(ctx, rxq); + continue; + } + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) { + if (iflib_fl_setup(fl)) { + device_printf(ctx->ifc_dev, + "setting up free list %d failed - " + "check cluster settings\n", j); + goto done; + } + } + } +done: + if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + IFDI_INTR_ENABLE(ctx); + txq = ctx->ifc_txqs; + for (i = 0; i < sctx->isc_ntxqsets; i++, txq++) + callout_reset_on(&txq->ift_timer, hz/2, iflib_timer, txq, + txq->ift_timer.c_cpu); +} + +static int +iflib_media_change(if_t ifp) +{ + if_ctx_t ctx = if_getsoftc(ifp); + int err; + + CTX_LOCK(ctx); + if ((err = IFDI_MEDIA_CHANGE(ctx)) == 0) + iflib_init_locked(ctx); + CTX_UNLOCK(ctx); + return (err); +} + +static void +iflib_media_status(if_t ifp, struct ifmediareq *ifmr) +{ + if_ctx_t ctx = if_getsoftc(ifp); + + CTX_LOCK(ctx); + IFDI_UPDATE_ADMIN_STATUS(ctx); + IFDI_MEDIA_STATUS(ctx, ifmr); + CTX_UNLOCK(ctx); +} + +void +iflib_stop(if_ctx_t ctx) +{ + iflib_txq_t txq = ctx->ifc_txqs; + iflib_rxq_t rxq = ctx->ifc_rxqs; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + iflib_dma_info_t di; + iflib_fl_t fl; + int i, j; + + /* Tell the stack that the interface is no longer active */ + if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); + + IFDI_INTR_DISABLE(ctx); + DELAY(1000); + IFDI_STOP(ctx); + DELAY(1000); + + iflib_debug_reset(); + /* Wait for current tx queue users to exit to disarm watchdog timer. */ + for (i = 0; i < scctx->isc_ntxqsets; i++, txq++) { + /* make sure all transmitters have completed before proceeding XXX */ + + CALLOUT_LOCK(txq); + callout_stop(&txq->ift_timer); + CALLOUT_UNLOCK(txq); + + /* clean any enqueued buffers */ + iflib_ifmp_purge(txq); + /* Free any existing tx buffers. */ + for (j = 0; j < txq->ift_size; j++) { + iflib_txsd_free(ctx, txq, j); + } + txq->ift_processed = txq->ift_cleaned = txq->ift_cidx_processed = 0; + txq->ift_in_use = txq->ift_gen = txq->ift_cidx = txq->ift_pidx = txq->ift_no_desc_avail = 0; + txq->ift_closed = txq->ift_mbuf_defrag = txq->ift_mbuf_defrag_failed = 0; + txq->ift_no_tx_dma_setup = txq->ift_txd_encap_efbig = txq->ift_map_failed = 0; + txq->ift_pullups = 0; + ifmp_ring_reset_stats(txq->ift_br); + for (j = 0, di = txq->ift_ifdi; j < sctx->isc_ntxqs; j++, di++) + bzero((void *)di->idi_vaddr, di->idi_size); + } + for (i = 0; i < scctx->isc_nrxqsets; i++, rxq++) { + /* make sure all transmitters have completed before proceeding XXX */ + + rxq->ifr_cq_cidx = 0; + for (j = 0, di = rxq->ifr_ifdi; j < sctx->isc_nrxqs; j++, di++) + bzero((void *)di->idi_vaddr, di->idi_size); + /* also resets the free lists pidx/cidx */ + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) + iflib_fl_bufs_free(fl); + } +} + +static inline caddr_t +calc_next_rxd(iflib_fl_t fl, int cidx) +{ + qidx_t size; + int nrxd; + caddr_t start, end, cur, next; + + nrxd = fl->ifl_size; + size = fl->ifl_rxd_size; + start = fl->ifl_ifdi->idi_vaddr; + + if (__predict_false(size == 0)) + return (start); + cur = start + size*cidx; + end = start + size*nrxd; + next = CACHE_PTR_NEXT(cur); + return (next < end ? next : start); +} + +static inline void +prefetch_pkts(iflib_fl_t fl, int cidx) +{ + int nextptr; + int nrxd = fl->ifl_size; + caddr_t next_rxd; + + + nextptr = (cidx + CACHE_PTR_INCREMENT) & (nrxd-1); + prefetch(&fl->ifl_sds.ifsd_m[nextptr]); + prefetch(&fl->ifl_sds.ifsd_cl[nextptr]); + next_rxd = calc_next_rxd(fl, cidx); + prefetch(next_rxd); + prefetch(fl->ifl_sds.ifsd_m[(cidx + 1) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_m[(cidx + 2) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_m[(cidx + 3) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_m[(cidx + 4) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_cl[(cidx + 1) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_cl[(cidx + 2) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_cl[(cidx + 3) & (nrxd-1)]); + prefetch(fl->ifl_sds.ifsd_cl[(cidx + 4) & (nrxd-1)]); +} + +static struct mbuf * +rxd_frag_to_sd(iflib_rxq_t rxq, if_rxd_frag_t irf, bool unload, if_rxsd_t sd, + int *pf_rv, if_rxd_info_t ri) +{ + bus_dmamap_t map; + iflib_fl_t fl; + caddr_t payload; + struct mbuf *m; + int flid, cidx, len, next; + + map = NULL; + flid = irf->irf_flid; + cidx = irf->irf_idx; + fl = &rxq->ifr_fl[flid]; + sd->ifsd_fl = fl; + sd->ifsd_cidx = cidx; + m = fl->ifl_sds.ifsd_m[cidx]; + sd->ifsd_cl = &fl->ifl_sds.ifsd_cl[cidx]; + fl->ifl_credits--; +#if MEMORY_LOGGING + fl->ifl_m_dequeued++; +#endif + if (rxq->ifr_ctx->ifc_flags & IFC_PREFETCH) + prefetch_pkts(fl, cidx); + next = (cidx + CACHE_PTR_INCREMENT) & (fl->ifl_size-1); + prefetch(&fl->ifl_sds.ifsd_map[next]); + map = fl->ifl_sds.ifsd_map[cidx]; + next = (cidx + CACHE_LINE_SIZE) & (fl->ifl_size-1); + + /* not valid assert if bxe really does SGE from non-contiguous elements */ + MPASS(fl->ifl_cidx == cidx); + bus_dmamap_sync(fl->ifl_buf_tag, map, BUS_DMASYNC_POSTREAD); + + if (rxq->pfil != NULL && PFIL_HOOKED_IN(rxq->pfil) && pf_rv != NULL) { + payload = *sd->ifsd_cl; + payload += ri->iri_pad; + len = ri->iri_len - ri->iri_pad; + *pf_rv = pfil_run_hooks(rxq->pfil, payload, ri->iri_ifp, + len | PFIL_MEMPTR | PFIL_IN, NULL); + switch (*pf_rv) { + case PFIL_DROPPED: + case PFIL_CONSUMED: + /* + * The filter ate it. Everything is recycled. + */ + m = NULL; + unload = 0; + break; + case PFIL_REALLOCED: + /* + * The filter copied it. Everything is recycled. + */ + m = pfil_mem2mbuf(payload); + unload = 0; + break; + case PFIL_PASS: + /* + * Filter said it was OK, so receive like + * normal + */ + fl->ifl_sds.ifsd_m[cidx] = NULL; + break; + default: + MPASS(0); + } + } else { + fl->ifl_sds.ifsd_m[cidx] = NULL; + *pf_rv = PFIL_PASS; + } + + if (unload) + bus_dmamap_unload(fl->ifl_buf_tag, map); + fl->ifl_cidx = (fl->ifl_cidx + 1) & (fl->ifl_size-1); + if (__predict_false(fl->ifl_cidx == 0)) + fl->ifl_gen = 0; + bit_clear(fl->ifl_rx_bitmap, cidx); + return (m); +} + +static struct mbuf * +assemble_segments(iflib_rxq_t rxq, if_rxd_info_t ri, if_rxsd_t sd, int *pf_rv) +{ + struct mbuf *m, *mh, *mt; + caddr_t cl; + int *pf_rv_ptr, flags, i, padlen; + bool consumed; + + i = 0; + mh = NULL; + consumed = false; + *pf_rv = PFIL_PASS; + pf_rv_ptr = pf_rv; + do { + m = rxd_frag_to_sd(rxq, &ri->iri_frags[i], !consumed, sd, + pf_rv_ptr, ri); + + MPASS(*sd->ifsd_cl != NULL); + + /* + * Exclude zero-length frags & frags from + * packets the filter has consumed or dropped + */ + if (ri->iri_frags[i].irf_len == 0 || consumed || + *pf_rv == PFIL_CONSUMED || *pf_rv == PFIL_DROPPED) { + if (mh == NULL) { + /* everything saved here */ + consumed = true; + pf_rv_ptr = NULL; + continue; + } + /* XXX we can save the cluster here, but not the mbuf */ + m_init(m, M_NOWAIT, MT_DATA, 0); + m_free(m); + continue; + } + if (mh == NULL) { + flags = M_PKTHDR|M_EXT; + mh = mt = m; + padlen = ri->iri_pad; + } else { + flags = M_EXT; + mt->m_next = m; + mt = m; + /* assuming padding is only on the first fragment */ + padlen = 0; + } + cl = *sd->ifsd_cl; + *sd->ifsd_cl = NULL; + + /* Can these two be made one ? */ + m_init(m, M_NOWAIT, MT_DATA, flags); + m_cljset(m, cl, sd->ifsd_fl->ifl_cltype); + /* + * These must follow m_init and m_cljset + */ + m->m_data += padlen; + ri->iri_len -= padlen; + m->m_len = ri->iri_frags[i].irf_len; + } while (++i < ri->iri_nfrags); + + return (mh); +} + +/* + * Process one software descriptor + */ +static struct mbuf * +iflib_rxd_pkt_get(iflib_rxq_t rxq, if_rxd_info_t ri) +{ + struct if_rxsd sd; + struct mbuf *m; + int pf_rv; + + /* should I merge this back in now that the two paths are basically duplicated? */ + if (ri->iri_nfrags == 1 && + ri->iri_frags[0].irf_len <= MIN(IFLIB_RX_COPY_THRESH, MHLEN)) { + m = rxd_frag_to_sd(rxq, &ri->iri_frags[0], false, &sd, + &pf_rv, ri); + if (pf_rv != PFIL_PASS && pf_rv != PFIL_REALLOCED) + return (m); + if (pf_rv == PFIL_PASS) { + m_init(m, M_NOWAIT, MT_DATA, M_PKTHDR); +#ifndef __NO_STRICT_ALIGNMENT + if (!IP_ALIGNED(m)) + m->m_data += 2; +#endif + memcpy(m->m_data, *sd.ifsd_cl, ri->iri_len); + m->m_len = ri->iri_frags[0].irf_len; + } + } else { + m = assemble_segments(rxq, ri, &sd, &pf_rv); + if (pf_rv != PFIL_PASS && pf_rv != PFIL_REALLOCED) + return (m); + } + m->m_pkthdr.len = ri->iri_len; + m->m_pkthdr.rcvif = ri->iri_ifp; + m->m_flags |= ri->iri_flags; + m->m_pkthdr.ether_vtag = ri->iri_vtag; + m->m_pkthdr.flowid = ri->iri_flowid; + M_HASHTYPE_SET(m, ri->iri_rsstype); + m->m_pkthdr.csum_flags = ri->iri_csum_flags; + m->m_pkthdr.csum_data = ri->iri_csum_data; + return (m); +} + +#if defined(INET6) || defined(INET) +static void +iflib_get_ip_forwarding(struct lro_ctrl *lc, bool *v4, bool *v6) +{ + CURVNET_SET(lc->ifp->if_vnet); +#if defined(INET6) + *v6 = V_ip6_forwarding; +#endif +#if defined(INET) + *v4 = V_ipforwarding; +#endif + CURVNET_RESTORE(); +} + +/* + * Returns true if it's possible this packet could be LROed. + * if it returns false, it is guaranteed that tcp_lro_rx() + * would not return zero. + */ +static bool +iflib_check_lro_possible(struct mbuf *m, bool v4_forwarding, bool v6_forwarding) +{ + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + switch (eh->ether_type) { +#if defined(INET6) + case htons(ETHERTYPE_IPV6): + return (!v6_forwarding); +#endif +#if defined (INET) + case htons(ETHERTYPE_IP): + return (!v4_forwarding); +#endif + } + + return false; +} +#else +static void +iflib_get_ip_forwarding(struct lro_ctrl *lc __unused, bool *v4 __unused, bool *v6 __unused) +{ +} +#endif + +static bool +iflib_rxeof(iflib_rxq_t rxq, qidx_t budget) +{ + if_t ifp; + if_ctx_t ctx = rxq->ifr_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + int avail, i; + qidx_t *cidxp; + struct if_rxd_info ri; + int err, budget_left, rx_bytes, rx_pkts; + iflib_fl_t fl; + int lro_enabled; + bool v4_forwarding, v6_forwarding, lro_possible; + + /* + * XXX early demux data packets so that if_input processing only handles + * acks in interrupt context + */ + struct mbuf *m, *mh, *mt, *mf; + + lro_possible = v4_forwarding = v6_forwarding = false; + ifp = ctx->ifc_ifp; + mh = mt = NULL; + MPASS(budget > 0); + rx_pkts = rx_bytes = 0; + if (sctx->isc_flags & IFLIB_HAS_RXCQ) + cidxp = &rxq->ifr_cq_cidx; + else + cidxp = &rxq->ifr_fl[0].ifl_cidx; + if ((avail = iflib_rxd_avail(ctx, rxq, *cidxp, budget)) == 0) { + for (i = 0, fl = &rxq->ifr_fl[0]; i < sctx->isc_nfl; i++, fl++) + __iflib_fl_refill_lt(ctx, fl, budget + 8); + DBG_COUNTER_INC(rx_unavail); + return (false); + } + + /* pfil needs the vnet to be set */ + CURVNET_SET_QUIET(ifp->if_vnet); + for (budget_left = budget; budget_left > 0 && avail > 0;) { + if (__predict_false(!CTX_ACTIVE(ctx))) { + DBG_COUNTER_INC(rx_ctx_inactive); + break; + } + /* + * Reset client set fields to their default values + */ + rxd_info_zero(&ri); + ri.iri_qsidx = rxq->ifr_id; + ri.iri_cidx = *cidxp; + ri.iri_ifp = ifp; + ri.iri_frags = rxq->ifr_frags; + err = ctx->isc_rxd_pkt_get(ctx->ifc_softc, &ri); + + if (err) + goto err; + rx_pkts += 1; + rx_bytes += ri.iri_len; + if (sctx->isc_flags & IFLIB_HAS_RXCQ) { + *cidxp = ri.iri_cidx; + /* Update our consumer index */ + /* XXX NB: shurd - check if this is still safe */ + while (rxq->ifr_cq_cidx >= scctx->isc_nrxd[0]) + rxq->ifr_cq_cidx -= scctx->isc_nrxd[0]; + /* was this only a completion queue message? */ + if (__predict_false(ri.iri_nfrags == 0)) + continue; + } + MPASS(ri.iri_nfrags != 0); + MPASS(ri.iri_len != 0); + + /* will advance the cidx on the corresponding free lists */ + m = iflib_rxd_pkt_get(rxq, &ri); + avail--; + budget_left--; + if (avail == 0 && budget_left) + avail = iflib_rxd_avail(ctx, rxq, *cidxp, budget_left); + + if (__predict_false(m == NULL)) + continue; + + /* imm_pkt: -- cxgb */ + if (mh == NULL) + mh = mt = m; + else { + mt->m_nextpkt = m; + mt = m; + } + } + CURVNET_RESTORE(); + /* make sure that we can refill faster than drain */ + for (i = 0, fl = &rxq->ifr_fl[0]; i < sctx->isc_nfl; i++, fl++) + __iflib_fl_refill_lt(ctx, fl, budget + 8); + + lro_enabled = (if_getcapenable(ifp) & IFCAP_LRO); + if (lro_enabled) + iflib_get_ip_forwarding(&rxq->ifr_lc, &v4_forwarding, &v6_forwarding); + mt = mf = NULL; + while (mh != NULL) { + m = mh; + mh = mh->m_nextpkt; + m->m_nextpkt = NULL; +#ifndef __NO_STRICT_ALIGNMENT + if (!IP_ALIGNED(m) && (m = iflib_fixup_rx(m)) == NULL) + continue; +#endif + rx_bytes += m->m_pkthdr.len; + rx_pkts++; +#if defined(INET6) || defined(INET) + if (lro_enabled) { + if (!lro_possible) { + lro_possible = iflib_check_lro_possible(m, v4_forwarding, v6_forwarding); + if (lro_possible && mf != NULL) { + ifp->if_input(ifp, mf); + DBG_COUNTER_INC(rx_if_input); + mt = mf = NULL; + } + } + if ((m->m_pkthdr.csum_flags & (CSUM_L4_CALC|CSUM_L4_VALID)) == + (CSUM_L4_CALC|CSUM_L4_VALID)) { + if (lro_possible && tcp_lro_rx(&rxq->ifr_lc, m, 0) == 0) + continue; + } + } +#endif + if (lro_possible) { + ifp->if_input(ifp, m); + DBG_COUNTER_INC(rx_if_input); + continue; + } + + if (mf == NULL) + mf = m; + if (mt != NULL) + mt->m_nextpkt = m; + mt = m; + } + if (mf != NULL) { + ifp->if_input(ifp, mf); + DBG_COUNTER_INC(rx_if_input); + } + + if_inc_counter(ifp, IFCOUNTER_IBYTES, rx_bytes); + if_inc_counter(ifp, IFCOUNTER_IPACKETS, rx_pkts); + + /* + * Flush any outstanding LRO work + */ +#if defined(INET6) || defined(INET) + tcp_lro_flush_all(&rxq->ifr_lc); +#endif + if (avail) + return true; + return (iflib_rxd_avail(ctx, rxq, *cidxp, 1)); +err: + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_DO_RESET; + iflib_admin_intr_deferred(ctx); + STATE_UNLOCK(ctx); + return (false); +} + +#define TXD_NOTIFY_COUNT(txq) (((txq)->ift_size / (txq)->ift_update_freq)-1) +static inline qidx_t +txq_max_db_deferred(iflib_txq_t txq, qidx_t in_use) +{ + qidx_t notify_count = TXD_NOTIFY_COUNT(txq); + qidx_t minthresh = txq->ift_size / 8; + if (in_use > 4*minthresh) + return (notify_count); + if (in_use > 2*minthresh) + return (notify_count >> 1); + if (in_use > minthresh) + return (notify_count >> 3); + return (0); +} + +static inline qidx_t +txq_max_rs_deferred(iflib_txq_t txq) +{ + qidx_t notify_count = TXD_NOTIFY_COUNT(txq); + qidx_t minthresh = txq->ift_size / 8; + if (txq->ift_in_use > 4*minthresh) + return (notify_count); + if (txq->ift_in_use > 2*minthresh) + return (notify_count >> 1); + if (txq->ift_in_use > minthresh) + return (notify_count >> 2); + return (2); +} + +#define M_CSUM_FLAGS(m) ((m)->m_pkthdr.csum_flags) +#define M_HAS_VLANTAG(m) (m->m_flags & M_VLANTAG) + +#define TXQ_MAX_DB_DEFERRED(txq, in_use) txq_max_db_deferred((txq), (in_use)) +#define TXQ_MAX_RS_DEFERRED(txq) txq_max_rs_deferred(txq) +#define TXQ_MAX_DB_CONSUMED(size) (size >> 4) + +/* forward compatibility for cxgb */ +#define FIRST_QSET(ctx) 0 +#define NTXQSETS(ctx) ((ctx)->ifc_softc_ctx.isc_ntxqsets) +#define NRXQSETS(ctx) ((ctx)->ifc_softc_ctx.isc_nrxqsets) +#define QIDX(ctx, m) ((((m)->m_pkthdr.flowid & ctx->ifc_softc_ctx.isc_rss_table_mask) % NTXQSETS(ctx)) + FIRST_QSET(ctx)) +#define DESC_RECLAIMABLE(q) ((int)((q)->ift_processed - (q)->ift_cleaned - (q)->ift_ctx->ifc_softc_ctx.isc_tx_nsegments)) + +/* XXX we should be setting this to something other than zero */ +#define RECLAIM_THRESH(ctx) ((ctx)->ifc_sctx->isc_tx_reclaim_thresh) +#define MAX_TX_DESC(ctx) max((ctx)->ifc_softc_ctx.isc_tx_tso_segments_max, \ + (ctx)->ifc_softc_ctx.isc_tx_nsegments) + +static inline bool +iflib_txd_db_check(if_ctx_t ctx, iflib_txq_t txq, int ring, qidx_t in_use) +{ + qidx_t dbval, max; + bool rang; + + rang = false; + max = TXQ_MAX_DB_DEFERRED(txq, in_use); + if (ring || txq->ift_db_pending >= max) { + dbval = txq->ift_npending ? txq->ift_npending : txq->ift_pidx; + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + ctx->isc_txd_flush(ctx->ifc_softc, txq->ift_id, dbval); + txq->ift_db_pending = txq->ift_npending = 0; + rang = true; + } + return (rang); +} + +#ifdef PKT_DEBUG +static void +print_pkt(if_pkt_info_t pi) +{ + printf("pi len: %d qsidx: %d nsegs: %d ndescs: %d flags: %x pidx: %d\n", + pi->ipi_len, pi->ipi_qsidx, pi->ipi_nsegs, pi->ipi_ndescs, pi->ipi_flags, pi->ipi_pidx); + printf("pi new_pidx: %d csum_flags: %lx tso_segsz: %d mflags: %x vtag: %d\n", + pi->ipi_new_pidx, pi->ipi_csum_flags, pi->ipi_tso_segsz, pi->ipi_mflags, pi->ipi_vtag); + printf("pi etype: %d ehdrlen: %d ip_hlen: %d ipproto: %d\n", + pi->ipi_etype, pi->ipi_ehdrlen, pi->ipi_ip_hlen, pi->ipi_ipproto); +} +#endif + +#define IS_TSO4(pi) ((pi)->ipi_csum_flags & CSUM_IP_TSO) +#define IS_TX_OFFLOAD4(pi) ((pi)->ipi_csum_flags & (CSUM_IP_TCP | CSUM_IP_TSO)) +#define IS_TSO6(pi) ((pi)->ipi_csum_flags & CSUM_IP6_TSO) +#define IS_TX_OFFLOAD6(pi) ((pi)->ipi_csum_flags & (CSUM_IP6_TCP | CSUM_IP6_TSO)) + +static int +iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) +{ + if_shared_ctx_t sctx = txq->ift_ctx->ifc_sctx; + struct ether_vlan_header *eh; + struct mbuf *m; + + m = *mp; + if ((sctx->isc_flags & IFLIB_NEED_SCRATCH) && + M_WRITABLE(m) == 0) { + if ((m = m_dup(m, M_NOWAIT)) == NULL) { + return (ENOMEM); + } else { + m_freem(*mp); + DBG_COUNTER_INC(tx_frees); + *mp = m; + } + } + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present, + * helpful for QinQ too. + */ + if (__predict_false(m->m_len < sizeof(*eh))) { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, sizeof(*eh))) == NULL)) + return (ENOMEM); + } + eh = mtod(m, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + pi->ipi_etype = ntohs(eh->evl_proto); + pi->ipi_ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + } else { + pi->ipi_etype = ntohs(eh->evl_encap_proto); + pi->ipi_ehdrlen = ETHER_HDR_LEN; + } + + switch (pi->ipi_etype) { +#ifdef INET + case ETHERTYPE_IP: + { + struct mbuf *n; + struct ip *ip = NULL; + struct tcphdr *th = NULL; + int minthlen; + + minthlen = min(m->m_pkthdr.len, pi->ipi_ehdrlen + sizeof(*ip) + sizeof(*th)); + if (__predict_false(m->m_len < minthlen)) { + /* + * if this code bloat is causing too much of a hit + * move it to a separate function and mark it noinline + */ + if (m->m_len == pi->ipi_ehdrlen) { + n = m->m_next; + MPASS(n); + if (n->m_len >= sizeof(*ip)) { + ip = (struct ip *)n->m_data; + if (n->m_len >= (ip->ip_hl << 2) + sizeof(*th)) + th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); + } else { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, minthlen)) == NULL)) + return (ENOMEM); + ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen); + } + } else { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, minthlen)) == NULL)) + return (ENOMEM); + ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen); + if (m->m_len >= (ip->ip_hl << 2) + sizeof(*th)) + th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); + } + } else { + ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen); + if (m->m_len >= (ip->ip_hl << 2) + sizeof(*th)) + th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); + } + pi->ipi_ip_hlen = ip->ip_hl << 2; + pi->ipi_ipproto = ip->ip_p; + pi->ipi_flags |= IPI_TX_IPV4; + + /* TCP checksum offload may require TCP header length */ + if (IS_TX_OFFLOAD4(pi)) { + if (__predict_true(pi->ipi_ipproto == IPPROTO_TCP)) { + if (__predict_false(th == NULL)) { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, (ip->ip_hl << 2) + sizeof(*th))) == NULL)) + return (ENOMEM); + th = (struct tcphdr *)((caddr_t)ip + pi->ipi_ip_hlen); + } + pi->ipi_tcp_hflags = th->th_flags; + pi->ipi_tcp_hlen = th->th_off << 2; + pi->ipi_tcp_seq = th->th_seq; + } + if (IS_TSO4(pi)) { + if (__predict_false(ip->ip_p != IPPROTO_TCP)) + return (ENXIO); + /* + * TSO always requires hardware checksum offload. + */ + pi->ipi_csum_flags |= (CSUM_IP_TCP | CSUM_IP); + th->th_sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htons(IPPROTO_TCP)); + pi->ipi_tso_segsz = m->m_pkthdr.tso_segsz; + if (sctx->isc_flags & IFLIB_TSO_INIT_IP) { + ip->ip_sum = 0; + ip->ip_len = htons(pi->ipi_ip_hlen + pi->ipi_tcp_hlen + pi->ipi_tso_segsz); + } + } + } + if ((sctx->isc_flags & IFLIB_NEED_ZERO_CSUM) && (pi->ipi_csum_flags & CSUM_IP)) + ip->ip_sum = 0; + + break; + } +#endif +#ifdef INET6 + case ETHERTYPE_IPV6: + { + struct ip6_hdr *ip6 = (struct ip6_hdr *)(m->m_data + pi->ipi_ehdrlen); + struct tcphdr *th; + pi->ipi_ip_hlen = sizeof(struct ip6_hdr); + + if (__predict_false(m->m_len < pi->ipi_ehdrlen + sizeof(struct ip6_hdr))) { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr))) == NULL)) + return (ENOMEM); + } + th = (struct tcphdr *)((caddr_t)ip6 + pi->ipi_ip_hlen); + + /* XXX-BZ this will go badly in case of ext hdrs. */ + pi->ipi_ipproto = ip6->ip6_nxt; + pi->ipi_flags |= IPI_TX_IPV6; + + /* TCP checksum offload may require TCP header length */ + if (IS_TX_OFFLOAD6(pi)) { + if (pi->ipi_ipproto == IPPROTO_TCP) { + if (__predict_false(m->m_len < pi->ipi_ehdrlen + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))) { + txq->ift_pullups++; + if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))) == NULL)) + return (ENOMEM); + } + pi->ipi_tcp_hflags = th->th_flags; + pi->ipi_tcp_hlen = th->th_off << 2; + pi->ipi_tcp_seq = th->th_seq; + } + if (IS_TSO6(pi)) { + if (__predict_false(ip6->ip6_nxt != IPPROTO_TCP)) + return (ENXIO); + /* + * TSO always requires hardware checksum offload. + */ + pi->ipi_csum_flags |= CSUM_IP6_TCP; + th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0); + pi->ipi_tso_segsz = m->m_pkthdr.tso_segsz; + } + } + break; + } +#endif + default: + pi->ipi_csum_flags &= ~CSUM_OFFLOAD; + pi->ipi_ip_hlen = 0; + break; + } + *mp = m; + + return (0); +} + +/* + * If dodgy hardware rejects the scatter gather chain we've handed it + * we'll need to remove the mbuf chain from ifsg_m[] before we can add the + * m_defrag'd mbufs + */ +static __noinline struct mbuf * +iflib_remove_mbuf(iflib_txq_t txq) +{ + int ntxd, pidx; + struct mbuf *m, **ifsd_m; + + ifsd_m = txq->ift_sds.ifsd_m; + ntxd = txq->ift_size; + pidx = txq->ift_pidx & (ntxd - 1); + ifsd_m = txq->ift_sds.ifsd_m; + m = ifsd_m[pidx]; + ifsd_m[pidx] = NULL; + bus_dmamap_unload(txq->ift_buf_tag, txq->ift_sds.ifsd_map[pidx]); + if (txq->ift_sds.ifsd_tso_map != NULL) + bus_dmamap_unload(txq->ift_tso_buf_tag, + txq->ift_sds.ifsd_tso_map[pidx]); +#if MEMORY_LOGGING + txq->ift_dequeued++; +#endif + return (m); +} + +static inline caddr_t +calc_next_txd(iflib_txq_t txq, int cidx, uint8_t qid) +{ + qidx_t size; + int ntxd; + caddr_t start, end, cur, next; + + ntxd = txq->ift_size; + size = txq->ift_txd_size[qid]; + start = txq->ift_ifdi[qid].idi_vaddr; + + if (__predict_false(size == 0)) + return (start); + cur = start + size*cidx; + end = start + size*ntxd; + next = CACHE_PTR_NEXT(cur); + return (next < end ? next : start); +} + +/* + * Pad an mbuf to ensure a minimum ethernet frame size. + * min_frame_size is the frame size (less CRC) to pad the mbuf to + */ +static __noinline int +iflib_ether_pad(device_t dev, struct mbuf **m_head, uint16_t min_frame_size) +{ + /* + * 18 is enough bytes to pad an ARP packet to 46 bytes, and + * and ARP message is the smallest common payload I can think of + */ + static char pad[18]; /* just zeros */ + int n; + struct mbuf *new_head; + + if (!M_WRITABLE(*m_head)) { + new_head = m_dup(*m_head, M_NOWAIT); + if (new_head == NULL) { + m_freem(*m_head); + device_printf(dev, "cannot pad short frame, m_dup() failed"); + DBG_COUNTER_INC(encap_pad_mbuf_fail); + DBG_COUNTER_INC(tx_frees); + return ENOMEM; + } + m_freem(*m_head); + *m_head = new_head; + } + + for (n = min_frame_size - (*m_head)->m_pkthdr.len; + n > 0; n -= sizeof(pad)) + if (!m_append(*m_head, min(n, sizeof(pad)), pad)) + break; + + if (n > 0) { + m_freem(*m_head); + device_printf(dev, "cannot pad short frame\n"); + DBG_COUNTER_INC(encap_pad_mbuf_fail); + DBG_COUNTER_INC(tx_frees); + return (ENOBUFS); + } + + return 0; +} + +static int +iflib_encap(iflib_txq_t txq, struct mbuf **m_headp) +{ + if_ctx_t ctx; + if_shared_ctx_t sctx; + if_softc_ctx_t scctx; + bus_dma_tag_t buf_tag; + bus_dma_segment_t *segs; + struct mbuf *m_head, **ifsd_m; + void *next_txd; + bus_dmamap_t map; + struct if_pkt_info pi; + int remap = 0; + int err, nsegs, ndesc, max_segs, pidx, cidx, next, ntxd; + + ctx = txq->ift_ctx; + sctx = ctx->ifc_sctx; + scctx = &ctx->ifc_softc_ctx; + segs = txq->ift_segs; + ntxd = txq->ift_size; + m_head = *m_headp; + map = NULL; + + /* + * If we're doing TSO the next descriptor to clean may be quite far ahead + */ + cidx = txq->ift_cidx; + pidx = txq->ift_pidx; + if (ctx->ifc_flags & IFC_PREFETCH) { + next = (cidx + CACHE_PTR_INCREMENT) & (ntxd-1); + if (!(ctx->ifc_flags & IFLIB_HAS_TXCQ)) { + next_txd = calc_next_txd(txq, cidx, 0); + prefetch(next_txd); + } + + /* prefetch the next cache line of mbuf pointers and flags */ + prefetch(&txq->ift_sds.ifsd_m[next]); + prefetch(&txq->ift_sds.ifsd_map[next]); + next = (cidx + CACHE_LINE_SIZE) & (ntxd-1); + } + map = txq->ift_sds.ifsd_map[pidx]; + ifsd_m = txq->ift_sds.ifsd_m; + + if (m_head->m_pkthdr.csum_flags & CSUM_TSO) { + buf_tag = txq->ift_tso_buf_tag; + max_segs = scctx->isc_tx_tso_segments_max; + map = txq->ift_sds.ifsd_tso_map[pidx]; + MPASS(buf_tag != NULL); + MPASS(max_segs > 0); + } else { + buf_tag = txq->ift_buf_tag; + max_segs = scctx->isc_tx_nsegments; + map = txq->ift_sds.ifsd_map[pidx]; + } + if ((sctx->isc_flags & IFLIB_NEED_ETHER_PAD) && + __predict_false(m_head->m_pkthdr.len < scctx->isc_min_frame_size)) { + err = iflib_ether_pad(ctx->ifc_dev, m_headp, scctx->isc_min_frame_size); + if (err) { + DBG_COUNTER_INC(encap_txd_encap_fail); + return err; + } + } + m_head = *m_headp; + + pkt_info_zero(&pi); + pi.ipi_mflags = (m_head->m_flags & (M_VLANTAG|M_BCAST|M_MCAST)); + pi.ipi_pidx = pidx; + pi.ipi_qsidx = txq->ift_id; + pi.ipi_len = m_head->m_pkthdr.len; + pi.ipi_csum_flags = m_head->m_pkthdr.csum_flags; + pi.ipi_vtag = M_HAS_VLANTAG(m_head) ? m_head->m_pkthdr.ether_vtag : 0; + + /* deliberate bitwise OR to make one condition */ + if (__predict_true((pi.ipi_csum_flags | pi.ipi_vtag))) { + if (__predict_false((err = iflib_parse_header(txq, &pi, m_headp)) != 0)) { + DBG_COUNTER_INC(encap_txd_encap_fail); + return (err); + } + m_head = *m_headp; + } + +retry: + err = bus_dmamap_load_mbuf_sg(buf_tag, map, m_head, segs, &nsegs, + BUS_DMA_NOWAIT); +defrag: + if (__predict_false(err)) { + switch (err) { + case EFBIG: + /* try collapse once and defrag once */ + if (remap == 0) { + m_head = m_collapse(*m_headp, M_NOWAIT, max_segs); + /* try defrag if collapsing fails */ + if (m_head == NULL) + remap++; + } + if (remap == 1) { + txq->ift_mbuf_defrag++; + m_head = m_defrag(*m_headp, M_NOWAIT); + } + /* + * remap should never be >1 unless bus_dmamap_load_mbuf_sg + * failed to map an mbuf that was run through m_defrag + */ + MPASS(remap <= 1); + if (__predict_false(m_head == NULL || remap > 1)) + goto defrag_failed; + remap++; + *m_headp = m_head; + goto retry; + break; + case ENOMEM: + txq->ift_no_tx_dma_setup++; + break; + default: + txq->ift_no_tx_dma_setup++; + m_freem(*m_headp); + DBG_COUNTER_INC(tx_frees); + *m_headp = NULL; + break; + } + txq->ift_map_failed++; + DBG_COUNTER_INC(encap_load_mbuf_fail); + DBG_COUNTER_INC(encap_txd_encap_fail); + return (err); + } + ifsd_m[pidx] = m_head; + /* + * XXX assumes a 1 to 1 relationship between segments and + * descriptors - this does not hold true on all drivers, e.g. + * cxgb + */ + if (__predict_false(nsegs + 2 > TXQ_AVAIL(txq))) { + txq->ift_no_desc_avail++; + bus_dmamap_unload(buf_tag, map); + DBG_COUNTER_INC(encap_txq_avail_fail); + DBG_COUNTER_INC(encap_txd_encap_fail); + if ((txq->ift_task.gt_task.ta_flags & TASK_ENQUEUED) == 0) + GROUPTASK_ENQUEUE(&txq->ift_task); + return (ENOBUFS); + } + /* + * On Intel cards we can greatly reduce the number of TX interrupts + * we see by only setting report status on every Nth descriptor. + * However, this also means that the driver will need to keep track + * of the descriptors that RS was set on to check them for the DD bit. + */ + txq->ift_rs_pending += nsegs + 1; + if (txq->ift_rs_pending > TXQ_MAX_RS_DEFERRED(txq) || + iflib_no_tx_batch || (TXQ_AVAIL(txq) - nsegs) <= MAX_TX_DESC(ctx) + 2) { + pi.ipi_flags |= IPI_TX_INTR; + txq->ift_rs_pending = 0; + } + + pi.ipi_segs = segs; + pi.ipi_nsegs = nsegs; + + MPASS(pidx >= 0 && pidx < txq->ift_size); +#ifdef PKT_DEBUG + print_pkt(&pi); +#endif + if ((err = ctx->isc_txd_encap(ctx->ifc_softc, &pi)) == 0) { + bus_dmamap_sync(buf_tag, map, BUS_DMASYNC_PREWRITE); + DBG_COUNTER_INC(tx_encap); + MPASS(pi.ipi_new_pidx < txq->ift_size); + + ndesc = pi.ipi_new_pidx - pi.ipi_pidx; + if (pi.ipi_new_pidx < pi.ipi_pidx) { + ndesc += txq->ift_size; + txq->ift_gen = 1; + } + /* + * drivers can need as many as + * two sentinels + */ + MPASS(ndesc <= pi.ipi_nsegs + 2); + MPASS(pi.ipi_new_pidx != pidx); + MPASS(ndesc > 0); + txq->ift_in_use += ndesc; + + /* + * We update the last software descriptor again here because there may + * be a sentinel and/or there may be more mbufs than segments + */ + txq->ift_pidx = pi.ipi_new_pidx; + txq->ift_npending += pi.ipi_ndescs; + } else { + *m_headp = m_head = iflib_remove_mbuf(txq); + if (err == EFBIG) { + txq->ift_txd_encap_efbig++; + if (remap < 2) { + remap = 1; + goto defrag; + } + } + goto defrag_failed; + } + /* + * err can't possibly be non-zero here, so we don't neet to test it + * to see if we need to DBG_COUNTER_INC(encap_txd_encap_fail). + */ + return (err); + +defrag_failed: + txq->ift_mbuf_defrag_failed++; + txq->ift_map_failed++; + m_freem(*m_headp); + DBG_COUNTER_INC(tx_frees); + *m_headp = NULL; + DBG_COUNTER_INC(encap_txd_encap_fail); + return (ENOMEM); +} + +static void +iflib_tx_desc_free(iflib_txq_t txq, int n) +{ + uint32_t qsize, cidx, mask, gen; + struct mbuf *m, **ifsd_m; + bool do_prefetch; + + cidx = txq->ift_cidx; + gen = txq->ift_gen; + qsize = txq->ift_size; + mask = qsize-1; + ifsd_m = txq->ift_sds.ifsd_m; + do_prefetch = (txq->ift_ctx->ifc_flags & IFC_PREFETCH); + + while (n-- > 0) { + if (do_prefetch) { + prefetch(ifsd_m[(cidx + 3) & mask]); + prefetch(ifsd_m[(cidx + 4) & mask]); + } + if ((m = ifsd_m[cidx]) != NULL) { + prefetch(&ifsd_m[(cidx + CACHE_PTR_INCREMENT) & mask]); + if (m->m_pkthdr.csum_flags & CSUM_TSO) { + bus_dmamap_sync(txq->ift_tso_buf_tag, + txq->ift_sds.ifsd_tso_map[cidx], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_tso_buf_tag, + txq->ift_sds.ifsd_tso_map[cidx]); + } else { + bus_dmamap_sync(txq->ift_buf_tag, + txq->ift_sds.ifsd_map[cidx], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txq->ift_buf_tag, + txq->ift_sds.ifsd_map[cidx]); + } + /* XXX we don't support any drivers that batch packets yet */ + MPASS(m->m_nextpkt == NULL); + m_freem(m); + ifsd_m[cidx] = NULL; +#if MEMORY_LOGGING + txq->ift_dequeued++; +#endif + DBG_COUNTER_INC(tx_frees); + } + if (__predict_false(++cidx == qsize)) { + cidx = 0; + gen = 0; + } + } + txq->ift_cidx = cidx; + txq->ift_gen = gen; +} + +static __inline int +iflib_completed_tx_reclaim(iflib_txq_t txq, int thresh) +{ + int reclaim; + if_ctx_t ctx = txq->ift_ctx; + + KASSERT(thresh >= 0, ("invalid threshold to reclaim")); + MPASS(thresh /*+ MAX_TX_DESC(txq->ift_ctx) */ < txq->ift_size); + + /* + * Need a rate-limiting check so that this isn't called every time + */ + iflib_tx_credits_update(ctx, txq); + reclaim = DESC_RECLAIMABLE(txq); + + if (reclaim <= thresh /* + MAX_TX_DESC(txq->ift_ctx) */) { +#ifdef INVARIANTS + if (iflib_verbose_debug) { + printf("%s processed=%ju cleaned=%ju tx_nsegments=%d reclaim=%d thresh=%d\n", __FUNCTION__, + txq->ift_processed, txq->ift_cleaned, txq->ift_ctx->ifc_softc_ctx.isc_tx_nsegments, + reclaim, thresh); + + } +#endif + return (0); + } + iflib_tx_desc_free(txq, reclaim); + txq->ift_cleaned += reclaim; + txq->ift_in_use -= reclaim; + + return (reclaim); +} + +static struct mbuf ** +_ring_peek_one(struct ifmp_ring *r, int cidx, int offset, int remaining) +{ + int next, size; + struct mbuf **items; + + size = r->size; + next = (cidx + CACHE_PTR_INCREMENT) & (size-1); + items = __DEVOLATILE(struct mbuf **, &r->items[0]); + + prefetch(items[(cidx + offset) & (size-1)]); + if (remaining > 1) { + prefetch2cachelines(&items[next]); + prefetch2cachelines(items[(cidx + offset + 1) & (size-1)]); + prefetch2cachelines(items[(cidx + offset + 2) & (size-1)]); + prefetch2cachelines(items[(cidx + offset + 3) & (size-1)]); + } + return (__DEVOLATILE(struct mbuf **, &r->items[(cidx + offset) & (size-1)])); +} + +static void +iflib_txq_check_drain(iflib_txq_t txq, int budget) +{ + + ifmp_ring_check_drainage(txq->ift_br, budget); +} + +static uint32_t +iflib_txq_can_drain(struct ifmp_ring *r) +{ + iflib_txq_t txq = r->cookie; + if_ctx_t ctx = txq->ift_ctx; + + if (TXQ_AVAIL(txq) > MAX_TX_DESC(ctx) + 2) + return (1); + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD); + return (ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id, + false)); +} + +static uint32_t +iflib_txq_drain(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx) +{ + iflib_txq_t txq = r->cookie; + if_ctx_t ctx = txq->ift_ctx; + if_t ifp = ctx->ifc_ifp; + struct mbuf *m, **mp; + int avail, bytes_sent, consumed, count, err, i, in_use_prev; + int mcast_sent, pkt_sent, reclaimed, txq_avail; + bool do_prefetch, rang, ring; + + if (__predict_false(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING) || + !LINK_ACTIVE(ctx))) { + DBG_COUNTER_INC(txq_drain_notready); + return (0); + } + reclaimed = iflib_completed_tx_reclaim(txq, RECLAIM_THRESH(ctx)); + rang = iflib_txd_db_check(ctx, txq, reclaimed, txq->ift_in_use); + avail = IDXDIFF(pidx, cidx, r->size); + if (__predict_false(ctx->ifc_flags & IFC_QFLUSH)) { + DBG_COUNTER_INC(txq_drain_flushing); + for (i = 0; i < avail; i++) { + if (__predict_true(r->items[(cidx + i) & (r->size-1)] != (void *)txq)) + m_free(r->items[(cidx + i) & (r->size-1)]); + r->items[(cidx + i) & (r->size-1)] = NULL; + } + return (avail); + } + + if (__predict_false(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_OACTIVE)) { + txq->ift_qstatus = IFLIB_QUEUE_IDLE; + CALLOUT_LOCK(txq); + callout_stop(&txq->ift_timer); + CALLOUT_UNLOCK(txq); + DBG_COUNTER_INC(txq_drain_oactive); + return (0); + } + if (reclaimed) + txq->ift_qstatus = IFLIB_QUEUE_IDLE; + consumed = mcast_sent = bytes_sent = pkt_sent = 0; + count = MIN(avail, TX_BATCH_SIZE); +#ifdef INVARIANTS + if (iflib_verbose_debug) + printf("%s avail=%d ifc_flags=%x txq_avail=%d ", __FUNCTION__, + avail, ctx->ifc_flags, TXQ_AVAIL(txq)); +#endif + do_prefetch = (ctx->ifc_flags & IFC_PREFETCH); + txq_avail = TXQ_AVAIL(txq); + err = 0; + for (i = 0; i < count && txq_avail > MAX_TX_DESC(ctx) + 2; i++) { + int rem = do_prefetch ? count - i : 0; + + mp = _ring_peek_one(r, cidx, i, rem); + MPASS(mp != NULL && *mp != NULL); + if (__predict_false(*mp == (struct mbuf *)txq)) { + consumed++; + continue; + } + in_use_prev = txq->ift_in_use; + err = iflib_encap(txq, mp); + if (__predict_false(err)) { + /* no room - bail out */ + if (err == ENOBUFS) + break; + consumed++; + /* we can't send this packet - skip it */ + continue; + } + consumed++; + pkt_sent++; + m = *mp; + DBG_COUNTER_INC(tx_sent); + bytes_sent += m->m_pkthdr.len; + mcast_sent += !!(m->m_flags & M_MCAST); + txq_avail = TXQ_AVAIL(txq); + + txq->ift_db_pending += (txq->ift_in_use - in_use_prev); + ETHER_BPF_MTAP(ifp, m); + if (__predict_false(!(ifp->if_drv_flags & IFF_DRV_RUNNING))) + break; + rang = iflib_txd_db_check(ctx, txq, false, in_use_prev); + } + + /* deliberate use of bitwise or to avoid gratuitous short-circuit */ + ring = rang ? false : (iflib_min_tx_latency | err) || (TXQ_AVAIL(txq) < MAX_TX_DESC(ctx)); + iflib_txd_db_check(ctx, txq, ring, txq->ift_in_use); + if_inc_counter(ifp, IFCOUNTER_OBYTES, bytes_sent); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, pkt_sent); + if (mcast_sent) + if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast_sent); +#ifdef INVARIANTS + if (iflib_verbose_debug) + printf("consumed=%d\n", consumed); +#endif + return (consumed); +} + +static uint32_t +iflib_txq_drain_always(struct ifmp_ring *r) +{ + return (1); +} + +static uint32_t +iflib_txq_drain_free(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx) +{ + int i, avail; + struct mbuf **mp; + iflib_txq_t txq; + + txq = r->cookie; + + txq->ift_qstatus = IFLIB_QUEUE_IDLE; + CALLOUT_LOCK(txq); + callout_stop(&txq->ift_timer); + CALLOUT_UNLOCK(txq); + + avail = IDXDIFF(pidx, cidx, r->size); + for (i = 0; i < avail; i++) { + mp = _ring_peek_one(r, cidx, i, avail - i); + if (__predict_false(*mp == (struct mbuf *)txq)) + continue; + m_freem(*mp); + DBG_COUNTER_INC(tx_frees); + } + MPASS(ifmp_ring_is_stalled(r) == 0); + return (avail); +} + +static void +iflib_ifmp_purge(iflib_txq_t txq) +{ + struct ifmp_ring *r; + + r = txq->ift_br; + r->drain = iflib_txq_drain_free; + r->can_drain = iflib_txq_drain_always; + + ifmp_ring_check_drainage(r, r->size); + + r->drain = iflib_txq_drain; + r->can_drain = iflib_txq_can_drain; +} + +static void +_task_fn_tx(void *context) +{ + iflib_txq_t txq = context; + if_ctx_t ctx = txq->ift_ctx; +#if defined(ALTQ) || defined(DEV_NETMAP) + if_t ifp = ctx->ifc_ifp; +#endif + int abdicate = ctx->ifc_sysctl_tx_abdicate; + +#ifdef IFLIB_DIAGNOSTICS + txq->ift_cpu_exec_count[curcpu]++; +#endif + if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)) + return; +#ifdef DEV_NETMAP + if (if_getcapenable(ifp) & IFCAP_NETMAP) { + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD); + if (ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id, false)) + netmap_tx_irq(ifp, txq->ift_id); + if (ctx->ifc_flags & IFC_LEGACY) + IFDI_INTR_ENABLE(ctx); + else + IFDI_TX_QUEUE_INTR_ENABLE(ctx, txq->ift_id); + return; + } +#endif +#ifdef ALTQ + if (ALTQ_IS_ENABLED(&ifp->if_snd)) + iflib_altq_if_start(ifp); +#endif + if (txq->ift_db_pending) + ifmp_ring_enqueue(txq->ift_br, (void **)&txq, 1, TX_BATCH_SIZE, abdicate); + else if (!abdicate) + ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE); + /* + * When abdicating, we always need to check drainage, not just when we don't enqueue + */ + if (abdicate) + ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE); + if (ctx->ifc_flags & IFC_LEGACY) + IFDI_INTR_ENABLE(ctx); + else + IFDI_TX_QUEUE_INTR_ENABLE(ctx, txq->ift_id); +} + +static void +_task_fn_rx(void *context) +{ + iflib_rxq_t rxq = context; + if_ctx_t ctx = rxq->ifr_ctx; + bool more; + uint16_t budget; + +#ifdef IFLIB_DIAGNOSTICS + rxq->ifr_cpu_exec_count[curcpu]++; +#endif + DBG_COUNTER_INC(task_fn_rxs); + if (__predict_false(!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING))) + return; + more = true; +#ifdef DEV_NETMAP + if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP) { + u_int work = 0; + if (netmap_rx_irq(ctx->ifc_ifp, rxq->ifr_id, &work)) { + more = false; + } + } +#endif + budget = ctx->ifc_sysctl_rx_budget; + if (budget == 0) + budget = 16; /* XXX */ + if (more == false || (more = iflib_rxeof(rxq, budget)) == false) { + if (ctx->ifc_flags & IFC_LEGACY) + IFDI_INTR_ENABLE(ctx); + else + IFDI_RX_QUEUE_INTR_ENABLE(ctx, rxq->ifr_id); + DBG_COUNTER_INC(rx_intr_enables); + } + if (__predict_false(!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING))) + return; + if (more) + GROUPTASK_ENQUEUE(&rxq->ifr_task); +} + +static void +_task_fn_admin(void *context) +{ + if_ctx_t ctx = context; + if_softc_ctx_t sctx = &ctx->ifc_softc_ctx; + iflib_txq_t txq; + int i; + bool oactive, running, do_reset, do_watchdog, in_detach; + uint32_t reset_on = hz / 2; + + STATE_LOCK(ctx); + running = (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING); + oactive = (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_OACTIVE); + do_reset = (ctx->ifc_flags & IFC_DO_RESET); + do_watchdog = (ctx->ifc_flags & IFC_DO_WATCHDOG); + in_detach = (ctx->ifc_flags & IFC_IN_DETACH); + ctx->ifc_flags &= ~(IFC_DO_RESET|IFC_DO_WATCHDOG); + STATE_UNLOCK(ctx); + + if ((!running && !oactive) && !(ctx->ifc_sctx->isc_flags & IFLIB_ADMIN_ALWAYS_RUN)) + return; + if (in_detach) + return; + + CTX_LOCK(ctx); + for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++) { + CALLOUT_LOCK(txq); + callout_stop(&txq->ift_timer); + CALLOUT_UNLOCK(txq); + } + if (do_watchdog) { + ctx->ifc_watchdog_events++; + IFDI_WATCHDOG_RESET(ctx); + } + IFDI_UPDATE_ADMIN_STATUS(ctx); + for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++) { +#ifdef DEV_NETMAP + reset_on = hz / 2; + if (if_getcapenable(ctx->ifc_ifp) & IFCAP_NETMAP) + iflib_netmap_timer_adjust(ctx, txq, &reset_on); +#endif + callout_reset_on(&txq->ift_timer, reset_on, iflib_timer, txq, txq->ift_timer.c_cpu); + } + IFDI_LINK_INTR_ENABLE(ctx); + if (do_reset) + iflib_if_init_locked(ctx); + CTX_UNLOCK(ctx); + + if (LINK_ACTIVE(ctx) == 0) + return; + for (txq = ctx->ifc_txqs, i = 0; i < sctx->isc_ntxqsets; i++, txq++) + iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET); +} + + +static void +_task_fn_iov(void *context) +{ + if_ctx_t ctx = context; + + if (!(if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING) && + !(ctx->ifc_sctx->isc_flags & IFLIB_ADMIN_ALWAYS_RUN)) + return; + + CTX_LOCK(ctx); + IFDI_VFLR_HANDLE(ctx); + CTX_UNLOCK(ctx); +} + +static int +iflib_sysctl_int_delay(SYSCTL_HANDLER_ARGS) +{ + int err; + if_int_delay_info_t info; + if_ctx_t ctx; + + info = (if_int_delay_info_t)arg1; + ctx = info->iidi_ctx; + info->iidi_req = req; + info->iidi_oidp = oidp; + CTX_LOCK(ctx); + err = IFDI_SYSCTL_INT_DELAY(ctx, info); + CTX_UNLOCK(ctx); + return (err); +} + +/********************************************************************* + * + * IFNET FUNCTIONS + * + **********************************************************************/ + +static void +iflib_if_init_locked(if_ctx_t ctx) +{ + iflib_stop(ctx); + iflib_init_locked(ctx); +} + + +static void +iflib_if_init(void *arg) +{ + if_ctx_t ctx = arg; + + CTX_LOCK(ctx); + iflib_if_init_locked(ctx); + CTX_UNLOCK(ctx); +} + +static int +iflib_if_transmit(if_t ifp, struct mbuf *m) +{ + if_ctx_t ctx = if_getsoftc(ifp); + + iflib_txq_t txq; + int err, qidx; + int abdicate = ctx->ifc_sysctl_tx_abdicate; + + if (__predict_false((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || !LINK_ACTIVE(ctx))) { + DBG_COUNTER_INC(tx_frees); + m_freem(m); + return (ENETDOWN); + } + + MPASS(m->m_nextpkt == NULL); + /* ALTQ-enabled interfaces always use queue 0. */ + qidx = 0; + if ((NTXQSETS(ctx) > 1) && M_HASHTYPE_GET(m) && !ALTQ_IS_ENABLED(&ifp->if_snd)) + qidx = QIDX(ctx, m); + /* + * XXX calculate buf_ring based on flowid (divvy up bits?) + */ + txq = &ctx->ifc_txqs[qidx]; + +#ifdef DRIVER_BACKPRESSURE + if (txq->ift_closed) { + while (m != NULL) { + next = m->m_nextpkt; + m->m_nextpkt = NULL; + m_freem(m); + DBG_COUNTER_INC(tx_frees); + m = next; + } + return (ENOBUFS); + } +#endif +#ifdef notyet + qidx = count = 0; + mp = marr; + next = m; + do { + count++; + next = next->m_nextpkt; + } while (next != NULL); + + if (count > nitems(marr)) + if ((mp = malloc(count*sizeof(struct mbuf *), M_IFLIB, M_NOWAIT)) == NULL) { + /* XXX check nextpkt */ + m_freem(m); + /* XXX simplify for now */ + DBG_COUNTER_INC(tx_frees); + return (ENOBUFS); + } + for (next = m, i = 0; next != NULL; i++) { + mp[i] = next; + next = next->m_nextpkt; + mp[i]->m_nextpkt = NULL; + } +#endif + DBG_COUNTER_INC(tx_seen); + err = ifmp_ring_enqueue(txq->ift_br, (void **)&m, 1, TX_BATCH_SIZE, abdicate); + + if (abdicate) + GROUPTASK_ENQUEUE(&txq->ift_task); + if (err) { + if (!abdicate) + GROUPTASK_ENQUEUE(&txq->ift_task); + /* support forthcoming later */ +#ifdef DRIVER_BACKPRESSURE + txq->ift_closed = TRUE; +#endif + ifmp_ring_check_drainage(txq->ift_br, TX_BATCH_SIZE); + m_freem(m); + DBG_COUNTER_INC(tx_frees); + } + + return (err); +} + +#ifdef ALTQ +/* + * The overall approach to integrating iflib with ALTQ is to continue to use + * the iflib mp_ring machinery between the ALTQ queue(s) and the hardware + * ring. Technically, when using ALTQ, queueing to an intermediate mp_ring + * is redundant/unnecessary, but doing so minimizes the amount of + * ALTQ-specific code required in iflib. It is assumed that the overhead of + * redundantly queueing to an intermediate mp_ring is swamped by the + * performance limitations inherent in using ALTQ. + * + * When ALTQ support is compiled in, all iflib drivers will use a transmit + * routine, iflib_altq_if_transmit(), that checks if ALTQ is enabled for the + * given interface. If ALTQ is enabled for an interface, then all + * transmitted packets for that interface will be submitted to the ALTQ + * subsystem via IFQ_ENQUEUE(). We don't use the legacy if_transmit() + * implementation because it uses IFQ_HANDOFF(), which will duplicatively + * update stats that the iflib machinery handles, and which is sensitve to + * the disused IFF_DRV_OACTIVE flag. Additionally, iflib_altq_if_start() + * will be installed as the start routine for use by ALTQ facilities that + * need to trigger queue drains on a scheduled basis. + * + */ +static void +iflib_altq_if_start(if_t ifp) +{ + struct ifaltq *ifq = &ifp->if_snd; + struct mbuf *m; + + IFQ_LOCK(ifq); + IFQ_DEQUEUE_NOLOCK(ifq, m); + while (m != NULL) { + iflib_if_transmit(ifp, m); + IFQ_DEQUEUE_NOLOCK(ifq, m); + } + IFQ_UNLOCK(ifq); +} + +static int +iflib_altq_if_transmit(if_t ifp, struct mbuf *m) +{ + int err; + + if (ALTQ_IS_ENABLED(&ifp->if_snd)) { + IFQ_ENQUEUE(&ifp->if_snd, m, err); + if (err == 0) + iflib_altq_if_start(ifp); + } else + err = iflib_if_transmit(ifp, m); + + return (err); +} +#endif /* ALTQ */ + +static void +iflib_if_qflush(if_t ifp) +{ + if_ctx_t ctx = if_getsoftc(ifp); + iflib_txq_t txq = ctx->ifc_txqs; + int i; + + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_QFLUSH; + STATE_UNLOCK(ctx); + for (i = 0; i < NTXQSETS(ctx); i++, txq++) + while (!(ifmp_ring_is_idle(txq->ift_br) || ifmp_ring_is_stalled(txq->ift_br))) + iflib_txq_check_drain(txq, 0); + STATE_LOCK(ctx); + ctx->ifc_flags &= ~IFC_QFLUSH; + STATE_UNLOCK(ctx); + + /* + * When ALTQ is enabled, this will also take care of purging the + * ALTQ queue(s). + */ + if_qflush(ifp); +} + + +#define IFCAP_FLAGS (IFCAP_HWCSUM_IPV6 | IFCAP_HWCSUM | IFCAP_LRO | \ + IFCAP_TSO | IFCAP_VLAN_HWTAGGING | IFCAP_HWSTATS | \ + IFCAP_VLAN_MTU | IFCAP_VLAN_HWFILTER | \ + IFCAP_VLAN_HWTSO | IFCAP_VLAN_HWCSUM) + +static int +iflib_if_ioctl(if_t ifp, u_long command, caddr_t data) +{ + if_ctx_t ctx = if_getsoftc(ifp); + struct ifreq *ifr = (struct ifreq *)data; +#if defined(INET) || defined(INET6) + struct ifaddr *ifa = (struct ifaddr *)data; +#endif + bool avoid_reset = false; + int err = 0, reinit = 0, bits; + + switch (command) { + case SIOCSIFADDR: +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + avoid_reset = true; +#endif +#ifdef INET6 + if (ifa->ifa_addr->sa_family == AF_INET6) + avoid_reset = true; +#endif + /* + ** Calling init results in link renegotiation, + ** so we avoid doing it when possible. + */ + if (avoid_reset) { + if_setflagbits(ifp, IFF_UP,0); + if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) + reinit = 1; +#ifdef INET + if (!(if_getflags(ifp) & IFF_NOARP)) + arp_ifinit(ifp, ifa); +#endif + } else + err = ether_ioctl(ifp, command, data); + break; + case SIOCSIFMTU: + CTX_LOCK(ctx); + if (ifr->ifr_mtu == if_getmtu(ifp)) { + CTX_UNLOCK(ctx); + break; + } + bits = if_getdrvflags(ifp); + /* stop the driver and free any clusters before proceeding */ + iflib_stop(ctx); + + if ((err = IFDI_MTU_SET(ctx, ifr->ifr_mtu)) == 0) { + STATE_LOCK(ctx); + if (ifr->ifr_mtu > ctx->ifc_max_fl_buf_size) + ctx->ifc_flags |= IFC_MULTISEG; + else + ctx->ifc_flags &= ~IFC_MULTISEG; + STATE_UNLOCK(ctx); + err = if_setmtu(ifp, ifr->ifr_mtu); + } + iflib_init_locked(ctx); + STATE_LOCK(ctx); + if_setdrvflags(ifp, bits); + STATE_UNLOCK(ctx); + CTX_UNLOCK(ctx); + break; + case SIOCSIFFLAGS: + CTX_LOCK(ctx); + if (if_getflags(ifp) & IFF_UP) { + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + if ((if_getflags(ifp) ^ ctx->ifc_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) { + err = IFDI_PROMISC_SET(ctx, if_getflags(ifp)); + } + } else + reinit = 1; + } else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + iflib_stop(ctx); + } + ctx->ifc_if_flags = if_getflags(ifp); + CTX_UNLOCK(ctx); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + CTX_LOCK(ctx); + IFDI_INTR_DISABLE(ctx); + IFDI_MULTI_SET(ctx); + IFDI_INTR_ENABLE(ctx); + CTX_UNLOCK(ctx); + } + break; + case SIOCSIFMEDIA: + CTX_LOCK(ctx); + IFDI_MEDIA_SET(ctx); + CTX_UNLOCK(ctx); + /* FALLTHROUGH */ + case SIOCGIFMEDIA: + case SIOCGIFXMEDIA: + err = ifmedia_ioctl(ifp, ifr, ctx->ifc_mediap, command); + break; + case SIOCGI2C: + { + struct ifi2creq i2c; + + err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c)); + if (err != 0) + break; + if (i2c.dev_addr != 0xA0 && i2c.dev_addr != 0xA2) { + err = EINVAL; + break; + } + if (i2c.len > sizeof(i2c.data)) { + err = EINVAL; + break; + } + + if ((err = IFDI_I2C_REQ(ctx, &i2c)) == 0) + err = copyout(&i2c, ifr_data_get_ptr(ifr), + sizeof(i2c)); + break; + } + case SIOCSIFCAP: + { + int mask, setmask, oldmask; + + oldmask = if_getcapenable(ifp); + mask = ifr->ifr_reqcap ^ oldmask; + mask &= ctx->ifc_softc_ctx.isc_capabilities; + setmask = 0; +#ifdef TCP_OFFLOAD + setmask |= mask & (IFCAP_TOE4|IFCAP_TOE6); +#endif + setmask |= (mask & IFCAP_FLAGS); + setmask |= (mask & IFCAP_WOL); + + /* + * If any RX csum has changed, change all the ones that + * are supported by the driver. + */ + if (setmask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { + setmask |= ctx->ifc_softc_ctx.isc_capabilities & + (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); + } + + /* + * want to ensure that traffic has stopped before we change any of the flags + */ + if (setmask) { + CTX_LOCK(ctx); + bits = if_getdrvflags(ifp); + if (bits & IFF_DRV_RUNNING && setmask & ~IFCAP_WOL) + iflib_stop(ctx); + STATE_LOCK(ctx); + if_togglecapenable(ifp, setmask); + STATE_UNLOCK(ctx); + if (bits & IFF_DRV_RUNNING && setmask & ~IFCAP_WOL) + iflib_init_locked(ctx); + STATE_LOCK(ctx); + if_setdrvflags(ifp, bits); + STATE_UNLOCK(ctx); + CTX_UNLOCK(ctx); + } + if_vlancap(ifp); + break; + } + case SIOCGPRIVATE_0: + case SIOCSDRVSPEC: + case SIOCGDRVSPEC: + CTX_LOCK(ctx); + err = IFDI_PRIV_IOCTL(ctx, command, data); + CTX_UNLOCK(ctx); + break; + default: + err = ether_ioctl(ifp, command, data); + break; + } + if (reinit) + iflib_if_init(ctx); + return (err); +} + +static uint64_t +iflib_if_get_counter(if_t ifp, ift_counter cnt) +{ + if_ctx_t ctx = if_getsoftc(ifp); + + return (IFDI_GET_COUNTER(ctx, cnt)); +} + +/********************************************************************* + * + * OTHER FUNCTIONS EXPORTED TO THE STACK + * + **********************************************************************/ + +static void +iflib_vlan_register(void *arg, if_t ifp, uint16_t vtag) +{ + if_ctx_t ctx = if_getsoftc(ifp); + + if ((void *)ctx != arg) + return; + + if ((vtag == 0) || (vtag > 4095)) + return; + + CTX_LOCK(ctx); + IFDI_VLAN_REGISTER(ctx, vtag); + /* Re-init to load the changes */ + if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) + iflib_if_init_locked(ctx); + CTX_UNLOCK(ctx); +} + +static void +iflib_vlan_unregister(void *arg, if_t ifp, uint16_t vtag) +{ + if_ctx_t ctx = if_getsoftc(ifp); + + if ((void *)ctx != arg) + return; + + if ((vtag == 0) || (vtag > 4095)) + return; + + CTX_LOCK(ctx); + IFDI_VLAN_UNREGISTER(ctx, vtag); + /* Re-init to load the changes */ + if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) + iflib_if_init_locked(ctx); + CTX_UNLOCK(ctx); +} + +static void +iflib_led_func(void *arg, int onoff) +{ + if_ctx_t ctx = arg; + + CTX_LOCK(ctx); + IFDI_LED_FUNC(ctx, onoff); + CTX_UNLOCK(ctx); +} + +/********************************************************************* + * + * BUS FUNCTION DEFINITIONS + * + **********************************************************************/ + +int +iflib_device_probe(device_t dev) +{ + const pci_vendor_info_t *ent; + if_shared_ctx_t sctx; + uint16_t pci_device_id, pci_rev_id, pci_subdevice_id, pci_subvendor_id; + uint16_t pci_vendor_id; + + if ((sctx = DEVICE_REGISTER(dev)) == NULL || sctx->isc_magic != IFLIB_MAGIC) + return (ENOTSUP); + + pci_vendor_id = pci_get_vendor(dev); + pci_device_id = pci_get_device(dev); + pci_subvendor_id = pci_get_subvendor(dev); + pci_subdevice_id = pci_get_subdevice(dev); + pci_rev_id = pci_get_revid(dev); + if (sctx->isc_parse_devinfo != NULL) + sctx->isc_parse_devinfo(&pci_device_id, &pci_subvendor_id, &pci_subdevice_id, &pci_rev_id); + + ent = sctx->isc_vendor_info; + while (ent->pvi_vendor_id != 0) { + if (pci_vendor_id != ent->pvi_vendor_id) { + ent++; + continue; + } + if ((pci_device_id == ent->pvi_device_id) && + ((pci_subvendor_id == ent->pvi_subvendor_id) || + (ent->pvi_subvendor_id == 0)) && + ((pci_subdevice_id == ent->pvi_subdevice_id) || + (ent->pvi_subdevice_id == 0)) && + ((pci_rev_id == ent->pvi_rev_id) || + (ent->pvi_rev_id == 0))) { + + device_set_desc_copy(dev, ent->pvi_name); + /* this needs to be changed to zero if the bus probing code + * ever stops re-probing on best match because the sctx + * may have its values over written by register calls + * in subsequent probes + */ + return (BUS_PROBE_DEFAULT); + } + ent++; + } + return (ENXIO); +} + +int +iflib_device_probe_vendor(device_t dev) +{ + int probe; + + probe = iflib_device_probe(dev); + if (probe == BUS_PROBE_DEFAULT) + return (BUS_PROBE_VENDOR); + else + return (probe); +} + +static void +iflib_reset_qvalues(if_ctx_t ctx) +{ + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + if_shared_ctx_t sctx = ctx->ifc_sctx; + device_t dev = ctx->ifc_dev; + int i; + + if (ctx->ifc_sysctl_ntxqs != 0) + scctx->isc_ntxqsets = ctx->ifc_sysctl_ntxqs; + if (ctx->ifc_sysctl_nrxqs != 0) + scctx->isc_nrxqsets = ctx->ifc_sysctl_nrxqs; + + for (i = 0; i < sctx->isc_ntxqs; i++) { + if (ctx->ifc_sysctl_ntxds[i] != 0) + scctx->isc_ntxd[i] = ctx->ifc_sysctl_ntxds[i]; + else + scctx->isc_ntxd[i] = sctx->isc_ntxd_default[i]; + } + + for (i = 0; i < sctx->isc_nrxqs; i++) { + if (ctx->ifc_sysctl_nrxds[i] != 0) + scctx->isc_nrxd[i] = ctx->ifc_sysctl_nrxds[i]; + else + scctx->isc_nrxd[i] = sctx->isc_nrxd_default[i]; + } + + for (i = 0; i < sctx->isc_nrxqs; i++) { + if (scctx->isc_nrxd[i] < sctx->isc_nrxd_min[i]) { + device_printf(dev, "nrxd%d: %d less than nrxd_min %d - resetting to min\n", + i, scctx->isc_nrxd[i], sctx->isc_nrxd_min[i]); + scctx->isc_nrxd[i] = sctx->isc_nrxd_min[i]; + } + if (scctx->isc_nrxd[i] > sctx->isc_nrxd_max[i]) { + device_printf(dev, "nrxd%d: %d greater than nrxd_max %d - resetting to max\n", + i, scctx->isc_nrxd[i], sctx->isc_nrxd_max[i]); + scctx->isc_nrxd[i] = sctx->isc_nrxd_max[i]; + } + if (!powerof2(scctx->isc_nrxd[i])) { + device_printf(dev, "nrxd%d: %d is not a power of 2 - using default value of %d\n", + i, scctx->isc_nrxd[i], sctx->isc_nrxd_default[i]); + scctx->isc_nrxd[i] = sctx->isc_nrxd_default[i]; + } + } + + for (i = 0; i < sctx->isc_ntxqs; i++) { + if (scctx->isc_ntxd[i] < sctx->isc_ntxd_min[i]) { + device_printf(dev, "ntxd%d: %d less than ntxd_min %d - resetting to min\n", + i, scctx->isc_ntxd[i], sctx->isc_ntxd_min[i]); + scctx->isc_ntxd[i] = sctx->isc_ntxd_min[i]; + } + if (scctx->isc_ntxd[i] > sctx->isc_ntxd_max[i]) { + device_printf(dev, "ntxd%d: %d greater than ntxd_max %d - resetting to max\n", + i, scctx->isc_ntxd[i], sctx->isc_ntxd_max[i]); + scctx->isc_ntxd[i] = sctx->isc_ntxd_max[i]; + } + if (!powerof2(scctx->isc_ntxd[i])) { + device_printf(dev, "ntxd%d: %d is not a power of 2 - using default value of %d\n", + i, scctx->isc_ntxd[i], sctx->isc_ntxd_default[i]); + scctx->isc_ntxd[i] = sctx->isc_ntxd_default[i]; + } + } +} + +static void +iflib_add_pfil(if_ctx_t ctx) +{ + struct pfil_head *pfil; + struct pfil_head_args pa; + iflib_rxq_t rxq; + int i; + + pa.pa_version = PFIL_VERSION; + pa.pa_flags = PFIL_IN; + pa.pa_type = PFIL_TYPE_ETHERNET; + pa.pa_headname = ctx->ifc_ifp->if_xname; + pfil = pfil_head_register(&pa); + + for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) { + rxq->pfil = pfil; + } +} + +static void +iflib_rem_pfil(if_ctx_t ctx) +{ + struct pfil_head *pfil; + iflib_rxq_t rxq; + int i; + + rxq = ctx->ifc_rxqs; + pfil = rxq->pfil; + for (i = 0; i < NRXQSETS(ctx); i++, rxq++) { + rxq->pfil = NULL; + } + pfil_head_unregister(pfil); +} + +static uint16_t +get_ctx_core_offset(if_ctx_t ctx) +{ + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + struct cpu_offset *op; + uint16_t qc; + uint16_t ret = ctx->ifc_sysctl_core_offset; + + if (ret != CORE_OFFSET_UNSPECIFIED) + return (ret); + + if (ctx->ifc_sysctl_separate_txrx) + qc = scctx->isc_ntxqsets + scctx->isc_nrxqsets; + else + qc = max(scctx->isc_ntxqsets, scctx->isc_nrxqsets); + + mtx_lock(&cpu_offset_mtx); +#ifndef __rtems__ + SLIST_FOREACH(op, &cpu_offsets, entries) { + if (CPU_CMP(&ctx->ifc_cpus, &op->set) == 0) { + ret = op->offset; + op->offset += qc; + MPASS(op->refcount < UINT_MAX); + op->refcount++; + break; + } + } +#endif /* __rtems__ */ + if (ret == CORE_OFFSET_UNSPECIFIED) { + ret = 0; + op = malloc(sizeof(struct cpu_offset), M_IFLIB, + M_NOWAIT | M_ZERO); + if (op == NULL) { + device_printf(ctx->ifc_dev, + "allocation for cpu offset failed.\n"); + } else { + op->offset = qc; + op->refcount = 1; +#ifndef __rtems__ + CPU_COPY(&ctx->ifc_cpus, &op->set); +#endif /* __rtems__ */ + SLIST_INSERT_HEAD(&cpu_offsets, op, entries); + } + } + mtx_unlock(&cpu_offset_mtx); + + return (ret); +} + +static void +unref_ctx_core_offset(if_ctx_t ctx) +{ + struct cpu_offset *op, *top; + + mtx_lock(&cpu_offset_mtx); + SLIST_FOREACH_SAFE(op, &cpu_offsets, entries, top) { +#ifndef __rtems__ + if (CPU_CMP(&ctx->ifc_cpus, &op->set) == 0) { +#else /* __rtems__ */ + { +#endif /* __rtems__ */ + MPASS(op->refcount > 0); + op->refcount--; + if (op->refcount == 0) { + SLIST_REMOVE(&cpu_offsets, op, cpu_offset, entries); + free(op, M_IFLIB); + } + break; + } + } + mtx_unlock(&cpu_offset_mtx); +} + +int +iflib_device_register(device_t dev, void *sc, if_shared_ctx_t sctx, if_ctx_t *ctxp) +{ + if_ctx_t ctx; + if_t ifp; + if_softc_ctx_t scctx; + kobjop_desc_t kobj_desc; + kobj_method_t *kobj_method; + int err, msix, rid; + uint16_t main_rxq, main_txq; + + ctx = malloc(sizeof(* ctx), M_IFLIB, M_WAITOK|M_ZERO); + + if (sc == NULL) { + sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK|M_ZERO); + device_set_softc(dev, ctx); + ctx->ifc_flags |= IFC_SC_ALLOCATED; + } + + ctx->ifc_sctx = sctx; + ctx->ifc_dev = dev; + ctx->ifc_softc = sc; + + if ((err = iflib_register(ctx)) != 0) { + device_printf(dev, "iflib_register failed %d\n", err); + goto fail_ctx_free; + } + iflib_add_device_sysctl_pre(ctx); + + scctx = &ctx->ifc_softc_ctx; + ifp = ctx->ifc_ifp; + + iflib_reset_qvalues(ctx); + CTX_LOCK(ctx); + if ((err = IFDI_ATTACH_PRE(ctx)) != 0) { + device_printf(dev, "IFDI_ATTACH_PRE failed %d\n", err); + goto fail_unlock; + } + _iflib_pre_assert(scctx); + ctx->ifc_txrx = *scctx->isc_txrx; + + if (sctx->isc_flags & IFLIB_DRIVER_MEDIA) + ctx->ifc_mediap = scctx->isc_media; + +#ifdef INVARIANTS + if (scctx->isc_capabilities & IFCAP_TXCSUM) + MPASS(scctx->isc_tx_csum_flags); +#endif + + if_setcapabilities(ifp, scctx->isc_capabilities | IFCAP_HWSTATS); + if_setcapenable(ifp, scctx->isc_capenable | IFCAP_HWSTATS); + + if (scctx->isc_ntxqsets == 0 || (scctx->isc_ntxqsets_max && scctx->isc_ntxqsets_max < scctx->isc_ntxqsets)) + scctx->isc_ntxqsets = scctx->isc_ntxqsets_max; + if (scctx->isc_nrxqsets == 0 || (scctx->isc_nrxqsets_max && scctx->isc_nrxqsets_max < scctx->isc_nrxqsets)) + scctx->isc_nrxqsets = scctx->isc_nrxqsets_max; + + main_txq = (sctx->isc_flags & IFLIB_HAS_TXCQ) ? 1 : 0; + main_rxq = (sctx->isc_flags & IFLIB_HAS_RXCQ) ? 1 : 0; + + /* XXX change for per-queue sizes */ + device_printf(dev, "Using %d TX descriptors and %d RX descriptors\n", + scctx->isc_ntxd[main_txq], scctx->isc_nrxd[main_rxq]); + + if (scctx->isc_tx_nsegments > scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION) + scctx->isc_tx_nsegments = max(1, scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION); + if (scctx->isc_tx_tso_segments_max > scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION) + scctx->isc_tx_tso_segments_max = max(1, + scctx->isc_ntxd[main_txq] / MAX_SINGLE_PACKET_FRACTION); + + /* TSO parameters - dig these out of the data sheet - simply correspond to tag setup */ + if (if_getcapabilities(ifp) & IFCAP_TSO) { + /* + * The stack can't handle a TSO size larger than IP_MAXPACKET, + * but some MACs do. + */ + if_sethwtsomax(ifp, min(scctx->isc_tx_tso_size_max, + IP_MAXPACKET)); + /* + * Take maximum number of m_pullup(9)'s in iflib_parse_header() + * into account. In the worst case, each of these calls will + * add another mbuf and, thus, the requirement for another DMA + * segment. So for best performance, it doesn't make sense to + * advertize a maximum of TSO segments that typically will + * require defragmentation in iflib_encap(). + */ + if_sethwtsomaxsegcount(ifp, scctx->isc_tx_tso_segments_max - 3); + if_sethwtsomaxsegsize(ifp, scctx->isc_tx_tso_segsize_max); + } + if (scctx->isc_rss_table_size == 0) + scctx->isc_rss_table_size = 64; + scctx->isc_rss_table_mask = scctx->isc_rss_table_size-1; + + GROUPTASK_INIT(&ctx->ifc_admin_task, 0, _task_fn_admin, ctx); + /* XXX format name */ + taskqgroup_attach(qgroup_if_config_tqg, &ctx->ifc_admin_task, ctx, + NULL, NULL, "admin"); + +#ifndef __rtems__ + /* Set up cpu set. If it fails, use the set of all CPUs. */ + if (bus_get_cpus(dev, INTR_CPUS, sizeof(ctx->ifc_cpus), &ctx->ifc_cpus) != 0) { + device_printf(dev, "Unable to fetch CPU list\n"); + CPU_COPY(&all_cpus, &ctx->ifc_cpus); + } + MPASS(CPU_COUNT(&ctx->ifc_cpus) > 0); +#endif /* __rtems__ */ + + /* + ** Now set up MSI or MSI-X, should return us the number of supported + ** vectors (will be 1 for a legacy interrupt and MSI). + */ + if (sctx->isc_flags & IFLIB_SKIP_MSIX) { + msix = scctx->isc_vectors; + } else if (scctx->isc_msix_bar != 0) + /* + * The simple fact that isc_msix_bar is not 0 does not mean we + * we have a good value there that is known to work. + */ + msix = iflib_msix_init(ctx); + else { + scctx->isc_vectors = 1; + scctx->isc_ntxqsets = 1; + scctx->isc_nrxqsets = 1; + scctx->isc_intr = IFLIB_INTR_LEGACY; + msix = 0; + } + /* Get memory for the station queues */ + if ((err = iflib_queues_alloc(ctx))) { + device_printf(dev, "Unable to allocate queue memory\n"); + goto fail_intr_free; + } + + if ((err = iflib_qset_structures_setup(ctx))) + goto fail_queues; + + /* + * Now that we know how many queues there are, get the core offset. + */ + ctx->ifc_sysctl_core_offset = get_ctx_core_offset(ctx); + + /* + * Group taskqueues aren't properly set up until SMP is started, + * so we disable interrupts until we can handle them post + * SI_SUB_SMP. + * + * XXX: disabling interrupts doesn't actually work, at least for + * the non-MSI case. When they occur before SI_SUB_SMP completes, + * we do null handling and depend on this not causing too large an + * interrupt storm. + */ + IFDI_INTR_DISABLE(ctx); + + if (msix > 1) { + /* + * When using MSI-X, ensure that ifdi_{r,t}x_queue_intr_enable + * aren't the default NULL implementation. + */ + kobj_desc = &ifdi_rx_queue_intr_enable_desc; + kobj_method = kobj_lookup_method(((kobj_t)ctx)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) { + device_printf(dev, + "MSI-X requires ifdi_rx_queue_intr_enable method"); + err = EOPNOTSUPP; + goto fail_queues; + } + kobj_desc = &ifdi_tx_queue_intr_enable_desc; + kobj_method = kobj_lookup_method(((kobj_t)ctx)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) { + device_printf(dev, + "MSI-X requires ifdi_tx_queue_intr_enable method"); + err = EOPNOTSUPP; + goto fail_queues; + } + + /* + * Assign the MSI-X vectors. + * Note that the default NULL ifdi_msix_intr_assign method will + * fail here, too. + */ + err = IFDI_MSIX_INTR_ASSIGN(ctx, msix); + if (err != 0) { + device_printf(dev, "IFDI_MSIX_INTR_ASSIGN failed %d\n", + err); + goto fail_queues; + } + } else if (scctx->isc_intr != IFLIB_INTR_MSIX) { + rid = 0; + if (scctx->isc_intr == IFLIB_INTR_MSI) { + MPASS(msix == 1); + rid = 1; + } + if ((err = iflib_legacy_setup(ctx, ctx->isc_legacy_intr, ctx->ifc_softc, &rid, "irq0")) != 0) { + device_printf(dev, "iflib_legacy_setup failed %d\n", err); + goto fail_queues; + } + } else { + device_printf(dev, + "Cannot use iflib with only 1 MSI-X interrupt!\n"); + err = ENODEV; + goto fail_intr_free; + } + + ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet); + + if ((err = IFDI_ATTACH_POST(ctx)) != 0) { + device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err); + goto fail_detach; + } + + /* + * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported. + * This must appear after the call to ether_ifattach() because + * ether_ifattach() sets if_hdrlen to the default value. + */ + if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU) + if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); + + if ((err = iflib_netmap_attach(ctx))) { + device_printf(ctx->ifc_dev, "netmap attach failed: %d\n", err); + goto fail_detach; + } + *ctxp = ctx; + + NETDUMP_SET(ctx->ifc_ifp, iflib); + + if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter); + iflib_add_device_sysctl_post(ctx); + iflib_add_pfil(ctx); + ctx->ifc_flags |= IFC_INIT_DONE; + CTX_UNLOCK(ctx); + + return (0); + +fail_detach: + ether_ifdetach(ctx->ifc_ifp); +fail_intr_free: + iflib_free_intr_mem(ctx); +fail_queues: + iflib_tx_structures_free(ctx); + iflib_rx_structures_free(ctx); + taskqgroup_detach(qgroup_if_config_tqg, &ctx->ifc_admin_task); + IFDI_DETACH(ctx); +fail_unlock: + CTX_UNLOCK(ctx); + iflib_deregister(ctx); +fail_ctx_free: + device_set_softc(ctx->ifc_dev, NULL); + if (ctx->ifc_flags & IFC_SC_ALLOCATED) + free(ctx->ifc_softc, M_IFLIB); + free(ctx, M_IFLIB); + return (err); +} + +int +iflib_pseudo_register(device_t dev, if_shared_ctx_t sctx, if_ctx_t *ctxp, + struct iflib_cloneattach_ctx *clctx) +{ + int err; + if_ctx_t ctx; + if_t ifp; + if_softc_ctx_t scctx; + int i; + void *sc; + uint16_t main_txq; + uint16_t main_rxq; + + ctx = malloc(sizeof(*ctx), M_IFLIB, M_WAITOK|M_ZERO); + sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK|M_ZERO); + ctx->ifc_flags |= IFC_SC_ALLOCATED; + if (sctx->isc_flags & (IFLIB_PSEUDO|IFLIB_VIRTUAL)) + ctx->ifc_flags |= IFC_PSEUDO; + + ctx->ifc_sctx = sctx; + ctx->ifc_softc = sc; + ctx->ifc_dev = dev; + + if ((err = iflib_register(ctx)) != 0) { + device_printf(dev, "%s: iflib_register failed %d\n", __func__, err); + goto fail_ctx_free; + } + iflib_add_device_sysctl_pre(ctx); + + scctx = &ctx->ifc_softc_ctx; + ifp = ctx->ifc_ifp; + + iflib_reset_qvalues(ctx); + CTX_LOCK(ctx); + if ((err = IFDI_ATTACH_PRE(ctx)) != 0) { + device_printf(dev, "IFDI_ATTACH_PRE failed %d\n", err); + goto fail_unlock; + } + if (sctx->isc_flags & IFLIB_GEN_MAC) + ether_gen_addr(ifp, &ctx->ifc_mac); + if ((err = IFDI_CLONEATTACH(ctx, clctx->cc_ifc, clctx->cc_name, + clctx->cc_params)) != 0) { + device_printf(dev, "IFDI_CLONEATTACH failed %d\n", err); + goto fail_ctx_free; + } + ifmedia_add(ctx->ifc_mediap, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); + ifmedia_add(ctx->ifc_mediap, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(ctx->ifc_mediap, IFM_ETHER | IFM_AUTO); + +#ifdef INVARIANTS + if (scctx->isc_capabilities & IFCAP_TXCSUM) + MPASS(scctx->isc_tx_csum_flags); +#endif + + if_setcapabilities(ifp, scctx->isc_capabilities | IFCAP_HWSTATS | IFCAP_LINKSTATE); + if_setcapenable(ifp, scctx->isc_capenable | IFCAP_HWSTATS | IFCAP_LINKSTATE); + + ifp->if_flags |= IFF_NOGROUP; + if (sctx->isc_flags & IFLIB_PSEUDO) { + ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet); + + if ((err = IFDI_ATTACH_POST(ctx)) != 0) { + device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err); + goto fail_detach; + } + *ctxp = ctx; + + /* + * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported. + * This must appear after the call to ether_ifattach() because + * ether_ifattach() sets if_hdrlen to the default value. + */ + if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU) + if_setifheaderlen(ifp, + sizeof(struct ether_vlan_header)); + + if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter); + iflib_add_device_sysctl_post(ctx); + ctx->ifc_flags |= IFC_INIT_DONE; + return (0); + } + _iflib_pre_assert(scctx); + ctx->ifc_txrx = *scctx->isc_txrx; + + if (scctx->isc_ntxqsets == 0 || (scctx->isc_ntxqsets_max && scctx->isc_ntxqsets_max < scctx->isc_ntxqsets)) + scctx->isc_ntxqsets = scctx->isc_ntxqsets_max; + if (scctx->isc_nrxqsets == 0 || (scctx->isc_nrxqsets_max && scctx->isc_nrxqsets_max < scctx->isc_nrxqsets)) + scctx->isc_nrxqsets = scctx->isc_nrxqsets_max; + + main_txq = (sctx->isc_flags & IFLIB_HAS_TXCQ) ? 1 : 0; + main_rxq = (sctx->isc_flags & IFLIB_HAS_RXCQ) ? 1 : 0; + + /* XXX change for per-queue sizes */ + device_printf(dev, "Using %d TX descriptors and %d RX descriptors\n", + scctx->isc_ntxd[main_txq], scctx->isc_nrxd[main_rxq]); + + if (scctx->isc_tx_nsegments > scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION) + scctx->isc_tx_nsegments = max(1, scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION); + if (scctx->isc_tx_tso_segments_max > scctx->isc_ntxd[main_txq] / + MAX_SINGLE_PACKET_FRACTION) + scctx->isc_tx_tso_segments_max = max(1, + scctx->isc_ntxd[main_txq] / MAX_SINGLE_PACKET_FRACTION); + + /* TSO parameters - dig these out of the data sheet - simply correspond to tag setup */ + if (if_getcapabilities(ifp) & IFCAP_TSO) { + /* + * The stack can't handle a TSO size larger than IP_MAXPACKET, + * but some MACs do. + */ + if_sethwtsomax(ifp, min(scctx->isc_tx_tso_size_max, + IP_MAXPACKET)); + /* + * Take maximum number of m_pullup(9)'s in iflib_parse_header() + * into account. In the worst case, each of these calls will + * add another mbuf and, thus, the requirement for another DMA + * segment. So for best performance, it doesn't make sense to + * advertize a maximum of TSO segments that typically will + * require defragmentation in iflib_encap(). + */ + if_sethwtsomaxsegcount(ifp, scctx->isc_tx_tso_segments_max - 3); + if_sethwtsomaxsegsize(ifp, scctx->isc_tx_tso_segsize_max); + } + if (scctx->isc_rss_table_size == 0) + scctx->isc_rss_table_size = 64; + scctx->isc_rss_table_mask = scctx->isc_rss_table_size-1; + + GROUPTASK_INIT(&ctx->ifc_admin_task, 0, _task_fn_admin, ctx); + /* XXX format name */ + taskqgroup_attach(qgroup_if_config_tqg, &ctx->ifc_admin_task, ctx, + NULL, NULL, "admin"); + + /* XXX --- can support > 1 -- but keep it simple for now */ + scctx->isc_intr = IFLIB_INTR_LEGACY; + + /* Get memory for the station queues */ + if ((err = iflib_queues_alloc(ctx))) { + device_printf(dev, "Unable to allocate queue memory\n"); + goto fail_iflib_detach; + } + + if ((err = iflib_qset_structures_setup(ctx))) { + device_printf(dev, "qset structure setup failed %d\n", err); + goto fail_queues; + } + + /* + * XXX What if anything do we want to do about interrupts? + */ + ether_ifattach(ctx->ifc_ifp, ctx->ifc_mac.octet); + if ((err = IFDI_ATTACH_POST(ctx)) != 0) { + device_printf(dev, "IFDI_ATTACH_POST failed %d\n", err); + goto fail_detach; + } + + /* + * Tell the upper layer(s) if IFCAP_VLAN_MTU is supported. + * This must appear after the call to ether_ifattach() because + * ether_ifattach() sets if_hdrlen to the default value. + */ + if (if_getcapabilities(ifp) & IFCAP_VLAN_MTU) + if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); + + /* XXX handle more than one queue */ + for (i = 0; i < scctx->isc_nrxqsets; i++) + IFDI_RX_CLSET(ctx, 0, i, ctx->ifc_rxqs[i].ifr_fl[0].ifl_sds.ifsd_cl); + + *ctxp = ctx; + + if_setgetcounterfn(ctx->ifc_ifp, iflib_if_get_counter); + iflib_add_device_sysctl_post(ctx); + ctx->ifc_flags |= IFC_INIT_DONE; + CTX_UNLOCK(ctx); + + return (0); +fail_detach: + ether_ifdetach(ctx->ifc_ifp); +fail_queues: + iflib_tx_structures_free(ctx); + iflib_rx_structures_free(ctx); +fail_iflib_detach: + IFDI_DETACH(ctx); +fail_unlock: + CTX_UNLOCK(ctx); + iflib_deregister(ctx); +fail_ctx_free: + free(ctx->ifc_softc, M_IFLIB); + free(ctx, M_IFLIB); + return (err); +} + +int +iflib_pseudo_deregister(if_ctx_t ctx) +{ + if_t ifp = ctx->ifc_ifp; + iflib_txq_t txq; + iflib_rxq_t rxq; + int i, j; + struct taskqgroup *tqg; + iflib_fl_t fl; + + ether_ifdetach(ifp); + /* XXX drain any dependent tasks */ + tqg = qgroup_if_io_tqg; + for (txq = ctx->ifc_txqs, i = 0; i < NTXQSETS(ctx); i++, txq++) { + callout_drain(&txq->ift_timer); + if (txq->ift_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &txq->ift_task); + } + for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) { + if (rxq->ifr_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &rxq->ifr_task); + + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) + free(fl->ifl_rx_bitmap, M_IFLIB); + } + tqg = qgroup_if_config_tqg; + if (ctx->ifc_admin_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &ctx->ifc_admin_task); + if (ctx->ifc_vflr_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &ctx->ifc_vflr_task); + + iflib_tx_structures_free(ctx); + iflib_rx_structures_free(ctx); + + iflib_deregister(ctx); + + if (ctx->ifc_flags & IFC_SC_ALLOCATED) + free(ctx->ifc_softc, M_IFLIB); + free(ctx, M_IFLIB); + return (0); +} + +int +iflib_device_attach(device_t dev) +{ + if_ctx_t ctx; + if_shared_ctx_t sctx; + + if ((sctx = DEVICE_REGISTER(dev)) == NULL || sctx->isc_magic != IFLIB_MAGIC) + return (ENOTSUP); + + pci_enable_busmaster(dev); + + return (iflib_device_register(dev, NULL, sctx, &ctx)); +} + +int +iflib_device_deregister(if_ctx_t ctx) +{ + if_t ifp = ctx->ifc_ifp; + iflib_txq_t txq; + iflib_rxq_t rxq; + device_t dev = ctx->ifc_dev; + int i, j; + struct taskqgroup *tqg; + iflib_fl_t fl; + + /* Make sure VLANS are not using driver */ + if (if_vlantrunkinuse(ifp)) { + device_printf(dev, "Vlan in use, detach first\n"); + return (EBUSY); + } +#ifdef PCI_IOV + if (!CTX_IS_VF(ctx) && pci_iov_detach(dev) != 0) { + device_printf(dev, "SR-IOV in use; detach first.\n"); + return (EBUSY); + } +#endif + + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_IN_DETACH; + STATE_UNLOCK(ctx); + + CTX_LOCK(ctx); + iflib_stop(ctx); + CTX_UNLOCK(ctx); + + /* Unregister VLAN events */ + if (ctx->ifc_vlan_attach_event != NULL) + EVENTHANDLER_DEREGISTER(vlan_config, ctx->ifc_vlan_attach_event); + if (ctx->ifc_vlan_detach_event != NULL) + EVENTHANDLER_DEREGISTER(vlan_unconfig, ctx->ifc_vlan_detach_event); + + iflib_netmap_detach(ifp); + ether_ifdetach(ifp); + iflib_rem_pfil(ctx); + if (ctx->ifc_led_dev != NULL) + led_destroy(ctx->ifc_led_dev); + /* XXX drain any dependent tasks */ + tqg = qgroup_if_io_tqg; + for (txq = ctx->ifc_txqs, i = 0; i < NTXQSETS(ctx); i++, txq++) { + callout_drain(&txq->ift_timer); + if (txq->ift_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &txq->ift_task); + } + for (i = 0, rxq = ctx->ifc_rxqs; i < NRXQSETS(ctx); i++, rxq++) { + if (rxq->ifr_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &rxq->ifr_task); + + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) + free(fl->ifl_rx_bitmap, M_IFLIB); + } + tqg = qgroup_if_config_tqg; + if (ctx->ifc_admin_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &ctx->ifc_admin_task); + if (ctx->ifc_vflr_task.gt_uniq != NULL) + taskqgroup_detach(tqg, &ctx->ifc_vflr_task); + CTX_LOCK(ctx); + IFDI_DETACH(ctx); + CTX_UNLOCK(ctx); + + /* ether_ifdetach calls if_qflush - lock must be destroy afterwards*/ + iflib_free_intr_mem(ctx); + + bus_generic_detach(dev); + + iflib_tx_structures_free(ctx); + iflib_rx_structures_free(ctx); + + iflib_deregister(ctx); + + device_set_softc(ctx->ifc_dev, NULL); + if (ctx->ifc_flags & IFC_SC_ALLOCATED) + free(ctx->ifc_softc, M_IFLIB); + unref_ctx_core_offset(ctx); + free(ctx, M_IFLIB); + return (0); +} + +static void +iflib_free_intr_mem(if_ctx_t ctx) +{ + + if (ctx->ifc_softc_ctx.isc_intr != IFLIB_INTR_MSIX) { + iflib_irq_free(ctx, &ctx->ifc_legacy_irq); + } + if (ctx->ifc_softc_ctx.isc_intr != IFLIB_INTR_LEGACY) { + pci_release_msi(ctx->ifc_dev); + } + if (ctx->ifc_msix_mem != NULL) { + bus_release_resource(ctx->ifc_dev, SYS_RES_MEMORY, + rman_get_rid(ctx->ifc_msix_mem), ctx->ifc_msix_mem); + ctx->ifc_msix_mem = NULL; + } +} + +int +iflib_device_detach(device_t dev) +{ + if_ctx_t ctx = device_get_softc(dev); + + return (iflib_device_deregister(ctx)); +} + +int +iflib_device_suspend(device_t dev) +{ + if_ctx_t ctx = device_get_softc(dev); + + CTX_LOCK(ctx); + IFDI_SUSPEND(ctx); + CTX_UNLOCK(ctx); + + return bus_generic_suspend(dev); +} +int +iflib_device_shutdown(device_t dev) +{ + if_ctx_t ctx = device_get_softc(dev); + + CTX_LOCK(ctx); + IFDI_SHUTDOWN(ctx); + CTX_UNLOCK(ctx); + + return bus_generic_suspend(dev); +} + + +int +iflib_device_resume(device_t dev) +{ + if_ctx_t ctx = device_get_softc(dev); + iflib_txq_t txq = ctx->ifc_txqs; + + CTX_LOCK(ctx); + IFDI_RESUME(ctx); + iflib_if_init_locked(ctx); + CTX_UNLOCK(ctx); + for (int i = 0; i < NTXQSETS(ctx); i++, txq++) + iflib_txq_check_drain(txq, IFLIB_RESTART_BUDGET); + + return (bus_generic_resume(dev)); +} + +int +iflib_device_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params) +{ + int error; + if_ctx_t ctx = device_get_softc(dev); + + CTX_LOCK(ctx); + error = IFDI_IOV_INIT(ctx, num_vfs, params); + CTX_UNLOCK(ctx); + + return (error); +} + +void +iflib_device_iov_uninit(device_t dev) +{ + if_ctx_t ctx = device_get_softc(dev); + + CTX_LOCK(ctx); + IFDI_IOV_UNINIT(ctx); + CTX_UNLOCK(ctx); +} + +int +iflib_device_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params) +{ + int error; + if_ctx_t ctx = device_get_softc(dev); + + CTX_LOCK(ctx); + error = IFDI_IOV_VF_ADD(ctx, vfnum, params); + CTX_UNLOCK(ctx); + + return (error); +} + +/********************************************************************* + * + * MODULE FUNCTION DEFINITIONS + * + **********************************************************************/ + +/* + * - Start a fast taskqueue thread for each core + * - Start a taskqueue for control operations + */ +static int +iflib_module_init(void) +{ + return (0); +} + +static int +iflib_module_event_handler(module_t mod, int what, void *arg) +{ + int err; + + switch (what) { + case MOD_LOAD: + if ((err = iflib_module_init()) != 0) + return (err); + break; + case MOD_UNLOAD: + return (EBUSY); + default: + return (EOPNOTSUPP); + } + + return (0); +} + +/********************************************************************* + * + * PUBLIC FUNCTION DEFINITIONS + * ordered as in iflib.h + * + **********************************************************************/ + + +static void +_iflib_assert(if_shared_ctx_t sctx) +{ + int i; + + MPASS(sctx->isc_tx_maxsize); + MPASS(sctx->isc_tx_maxsegsize); + + MPASS(sctx->isc_rx_maxsize); + MPASS(sctx->isc_rx_nsegments); + MPASS(sctx->isc_rx_maxsegsize); + + MPASS(sctx->isc_nrxqs >= 1 && sctx->isc_nrxqs <= 8); + for (i = 0; i < sctx->isc_nrxqs; i++) { + MPASS(sctx->isc_nrxd_min[i]); + MPASS(powerof2(sctx->isc_nrxd_min[i])); + MPASS(sctx->isc_nrxd_max[i]); + MPASS(powerof2(sctx->isc_nrxd_max[i])); + MPASS(sctx->isc_nrxd_default[i]); + MPASS(powerof2(sctx->isc_nrxd_default[i])); + } + + MPASS(sctx->isc_ntxqs >= 1 && sctx->isc_ntxqs <= 8); + for (i = 0; i < sctx->isc_ntxqs; i++) { + MPASS(sctx->isc_ntxd_min[i]); + MPASS(powerof2(sctx->isc_ntxd_min[i])); + MPASS(sctx->isc_ntxd_max[i]); + MPASS(powerof2(sctx->isc_ntxd_max[i])); + MPASS(sctx->isc_ntxd_default[i]); + MPASS(powerof2(sctx->isc_ntxd_default[i])); + } +} + +static void +_iflib_pre_assert(if_softc_ctx_t scctx) +{ + + MPASS(scctx->isc_txrx->ift_txd_encap); + MPASS(scctx->isc_txrx->ift_txd_flush); + MPASS(scctx->isc_txrx->ift_txd_credits_update); + MPASS(scctx->isc_txrx->ift_rxd_available); + MPASS(scctx->isc_txrx->ift_rxd_pkt_get); + MPASS(scctx->isc_txrx->ift_rxd_refill); + MPASS(scctx->isc_txrx->ift_rxd_flush); +} + +static int +iflib_register(if_ctx_t ctx) +{ + if_shared_ctx_t sctx = ctx->ifc_sctx; + driver_t *driver = sctx->isc_driver; + device_t dev = ctx->ifc_dev; + if_t ifp; + + _iflib_assert(sctx); + + CTX_LOCK_INIT(ctx); + STATE_LOCK_INIT(ctx, device_get_nameunit(ctx->ifc_dev)); + ifp = ctx->ifc_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not allocate ifnet structure\n"); + return (ENOMEM); + } + + /* + * Initialize our context's device specific methods + */ + kobj_init((kobj_t) ctx, (kobj_class_t) driver); + kobj_class_compile((kobj_class_t) driver); + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + if_setsoftc(ifp, ctx); + if_setdev(ifp, dev); + if_setinitfn(ifp, iflib_if_init); + if_setioctlfn(ifp, iflib_if_ioctl); +#ifdef ALTQ + if_setstartfn(ifp, iflib_altq_if_start); + if_settransmitfn(ifp, iflib_altq_if_transmit); + if_setsendqready(ifp); +#else + if_settransmitfn(ifp, iflib_if_transmit); +#endif + if_setqflushfn(ifp, iflib_if_qflush); + if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); + + ctx->ifc_vlan_attach_event = + EVENTHANDLER_REGISTER(vlan_config, iflib_vlan_register, ctx, + EVENTHANDLER_PRI_FIRST); + ctx->ifc_vlan_detach_event = + EVENTHANDLER_REGISTER(vlan_unconfig, iflib_vlan_unregister, ctx, + EVENTHANDLER_PRI_FIRST); + + if ((sctx->isc_flags & IFLIB_DRIVER_MEDIA) == 0) { + ctx->ifc_mediap = &ctx->ifc_media; + ifmedia_init(ctx->ifc_mediap, IFM_IMASK, + iflib_media_change, iflib_media_status); + } + return (0); +} + +static void +iflib_deregister(if_ctx_t ctx) +{ + if_t ifp = ctx->ifc_ifp; + + /* Remove all media */ + ifmedia_removeall(&ctx->ifc_media); + + /* Unregister VLAN events */ + if (ctx->ifc_vlan_attach_event != NULL) { + EVENTHANDLER_DEREGISTER(vlan_config, ctx->ifc_vlan_attach_event); + ctx->ifc_vlan_attach_event = NULL; + } + if (ctx->ifc_vlan_detach_event != NULL) { + EVENTHANDLER_DEREGISTER(vlan_unconfig, ctx->ifc_vlan_detach_event); + ctx->ifc_vlan_detach_event = NULL; + } + + /* Release kobject reference */ + kobj_delete((kobj_t) ctx, NULL); + + /* Free the ifnet structure */ + if_free(ifp); + + STATE_LOCK_DESTROY(ctx); + + /* ether_ifdetach calls if_qflush - lock must be destroy afterwards*/ + CTX_LOCK_DESTROY(ctx); +} + +static int +iflib_queues_alloc(if_ctx_t ctx) +{ + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + device_t dev = ctx->ifc_dev; + int nrxqsets = scctx->isc_nrxqsets; + int ntxqsets = scctx->isc_ntxqsets; + iflib_txq_t txq; + iflib_rxq_t rxq; + iflib_fl_t fl = NULL; + int i, j, cpu, err, txconf, rxconf; + iflib_dma_info_t ifdip; + uint32_t *rxqsizes = scctx->isc_rxqsizes; + uint32_t *txqsizes = scctx->isc_txqsizes; + uint8_t nrxqs = sctx->isc_nrxqs; + uint8_t ntxqs = sctx->isc_ntxqs; + int nfree_lists = sctx->isc_nfl ? sctx->isc_nfl : 1; + caddr_t *vaddrs; + uint64_t *paddrs; + + KASSERT(ntxqs > 0, ("number of queues per qset must be at least 1")); + KASSERT(nrxqs > 0, ("number of queues per qset must be at least 1")); + + /* Allocate the TX ring struct memory */ + if (!(ctx->ifc_txqs = + (iflib_txq_t) malloc(sizeof(struct iflib_txq) * + ntxqsets, M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, "Unable to allocate TX ring memory\n"); + err = ENOMEM; + goto fail; + } + + /* Now allocate the RX */ + if (!(ctx->ifc_rxqs = + (iflib_rxq_t) malloc(sizeof(struct iflib_rxq) * + nrxqsets, M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, "Unable to allocate RX ring memory\n"); + err = ENOMEM; + goto rx_fail; + } + + txq = ctx->ifc_txqs; + rxq = ctx->ifc_rxqs; + + /* + * XXX handle allocation failure + */ + for (txconf = i = 0, cpu = CPU_FIRST(); i < ntxqsets; i++, txconf++, txq++, cpu = CPU_NEXT(cpu)) { + /* Set up some basics */ + + if ((ifdip = malloc(sizeof(struct iflib_dma_info) * ntxqs, + M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) { + device_printf(dev, + "Unable to allocate TX DMA info memory\n"); + err = ENOMEM; + goto err_tx_desc; + } + txq->ift_ifdi = ifdip; + for (j = 0; j < ntxqs; j++, ifdip++) { + if (iflib_dma_alloc(ctx, txqsizes[j], ifdip, 0)) { + device_printf(dev, + "Unable to allocate TX descriptors\n"); + err = ENOMEM; + goto err_tx_desc; + } + txq->ift_txd_size[j] = scctx->isc_txd_size[j]; + bzero((void *)ifdip->idi_vaddr, txqsizes[j]); + } + txq->ift_ctx = ctx; + txq->ift_id = i; + if (sctx->isc_flags & IFLIB_HAS_TXCQ) { + txq->ift_br_offset = 1; + } else { + txq->ift_br_offset = 0; + } + /* XXX fix this */ +#ifndef __rtems__ + txq->ift_timer.c_cpu = cpu; +#endif /* __rtems__ */ + + if (iflib_txsd_alloc(txq)) { + device_printf(dev, "Critical Failure setting up TX buffers\n"); + err = ENOMEM; + goto err_tx_desc; + } + + /* Initialize the TX lock */ + snprintf(txq->ift_mtx_name, MTX_NAME_LEN, "%s:TX(%d):callout", + device_get_nameunit(dev), txq->ift_id); + mtx_init(&txq->ift_mtx, txq->ift_mtx_name, NULL, MTX_DEF); + callout_init_mtx(&txq->ift_timer, &txq->ift_mtx, 0); + + err = ifmp_ring_alloc(&txq->ift_br, 2048, txq, iflib_txq_drain, + iflib_txq_can_drain, M_IFLIB, M_WAITOK); + if (err) { + /* XXX free any allocated rings */ + device_printf(dev, "Unable to allocate buf_ring\n"); + goto err_tx_desc; + } + } + + for (rxconf = i = 0; i < nrxqsets; i++, rxconf++, rxq++) { + /* Set up some basics */ + + if ((ifdip = malloc(sizeof(struct iflib_dma_info) * nrxqs, + M_IFLIB, M_NOWAIT | M_ZERO)) == NULL) { + device_printf(dev, + "Unable to allocate RX DMA info memory\n"); + err = ENOMEM; + goto err_tx_desc; + } + + rxq->ifr_ifdi = ifdip; + /* XXX this needs to be changed if #rx queues != #tx queues */ + rxq->ifr_ntxqirq = 1; + rxq->ifr_txqid[0] = i; + for (j = 0; j < nrxqs; j++, ifdip++) { + if (iflib_dma_alloc(ctx, rxqsizes[j], ifdip, 0)) { + device_printf(dev, + "Unable to allocate RX descriptors\n"); + err = ENOMEM; + goto err_tx_desc; + } + bzero((void *)ifdip->idi_vaddr, rxqsizes[j]); + } + rxq->ifr_ctx = ctx; + rxq->ifr_id = i; + if (sctx->isc_flags & IFLIB_HAS_RXCQ) { + rxq->ifr_fl_offset = 1; + } else { + rxq->ifr_fl_offset = 0; + } + rxq->ifr_nfl = nfree_lists; + if (!(fl = + (iflib_fl_t) malloc(sizeof(struct iflib_fl) * nfree_lists, M_IFLIB, M_NOWAIT | M_ZERO))) { + device_printf(dev, "Unable to allocate free list memory\n"); + err = ENOMEM; + goto err_tx_desc; + } + rxq->ifr_fl = fl; + for (j = 0; j < nfree_lists; j++) { + fl[j].ifl_rxq = rxq; + fl[j].ifl_id = j; + fl[j].ifl_ifdi = &rxq->ifr_ifdi[j + rxq->ifr_fl_offset]; + fl[j].ifl_rxd_size = scctx->isc_rxd_size[j]; + } + /* Allocate receive buffers for the ring */ + if (iflib_rxsd_alloc(rxq)) { + device_printf(dev, + "Critical Failure setting up receive buffers\n"); + err = ENOMEM; + goto err_rx_desc; + } + + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) + fl->ifl_rx_bitmap = bit_alloc(fl->ifl_size, M_IFLIB, + M_WAITOK); + } + + /* TXQs */ + vaddrs = malloc(sizeof(caddr_t)*ntxqsets*ntxqs, M_IFLIB, M_WAITOK); + paddrs = malloc(sizeof(uint64_t)*ntxqsets*ntxqs, M_IFLIB, M_WAITOK); + for (i = 0; i < ntxqsets; i++) { + iflib_dma_info_t di = ctx->ifc_txqs[i].ift_ifdi; + + for (j = 0; j < ntxqs; j++, di++) { + vaddrs[i*ntxqs + j] = di->idi_vaddr; + paddrs[i*ntxqs + j] = di->idi_paddr; + } + } + if ((err = IFDI_TX_QUEUES_ALLOC(ctx, vaddrs, paddrs, ntxqs, ntxqsets)) != 0) { + device_printf(ctx->ifc_dev, + "Unable to allocate device TX queue\n"); + iflib_tx_structures_free(ctx); + free(vaddrs, M_IFLIB); + free(paddrs, M_IFLIB); + goto err_rx_desc; + } + free(vaddrs, M_IFLIB); + free(paddrs, M_IFLIB); + + /* RXQs */ + vaddrs = malloc(sizeof(caddr_t)*nrxqsets*nrxqs, M_IFLIB, M_WAITOK); + paddrs = malloc(sizeof(uint64_t)*nrxqsets*nrxqs, M_IFLIB, M_WAITOK); + for (i = 0; i < nrxqsets; i++) { + iflib_dma_info_t di = ctx->ifc_rxqs[i].ifr_ifdi; + + for (j = 0; j < nrxqs; j++, di++) { + vaddrs[i*nrxqs + j] = di->idi_vaddr; + paddrs[i*nrxqs + j] = di->idi_paddr; + } + } + if ((err = IFDI_RX_QUEUES_ALLOC(ctx, vaddrs, paddrs, nrxqs, nrxqsets)) != 0) { + device_printf(ctx->ifc_dev, + "Unable to allocate device RX queue\n"); + iflib_tx_structures_free(ctx); + free(vaddrs, M_IFLIB); + free(paddrs, M_IFLIB); + goto err_rx_desc; + } + free(vaddrs, M_IFLIB); + free(paddrs, M_IFLIB); + + return (0); + +/* XXX handle allocation failure changes */ +err_rx_desc: +err_tx_desc: +rx_fail: + if (ctx->ifc_rxqs != NULL) + free(ctx->ifc_rxqs, M_IFLIB); + ctx->ifc_rxqs = NULL; + if (ctx->ifc_txqs != NULL) + free(ctx->ifc_txqs, M_IFLIB); + ctx->ifc_txqs = NULL; +fail: + return (err); +} + +static int +iflib_tx_structures_setup(if_ctx_t ctx) +{ + iflib_txq_t txq = ctx->ifc_txqs; + int i; + + for (i = 0; i < NTXQSETS(ctx); i++, txq++) + iflib_txq_setup(txq); + + return (0); +} + +static void +iflib_tx_structures_free(if_ctx_t ctx) +{ + iflib_txq_t txq = ctx->ifc_txqs; + if_shared_ctx_t sctx = ctx->ifc_sctx; + int i, j; + + for (i = 0; i < NTXQSETS(ctx); i++, txq++) { + iflib_txq_destroy(txq); + for (j = 0; j < sctx->isc_ntxqs; j++) + iflib_dma_free(&txq->ift_ifdi[j]); + } + free(ctx->ifc_txqs, M_IFLIB); + ctx->ifc_txqs = NULL; + IFDI_QUEUES_FREE(ctx); +} + +/********************************************************************* + * + * Initialize all receive rings. + * + **********************************************************************/ +static int +iflib_rx_structures_setup(if_ctx_t ctx) +{ + iflib_rxq_t rxq = ctx->ifc_rxqs; + int q; +#if defined(INET6) || defined(INET) + int err, i; +#endif + + for (q = 0; q < ctx->ifc_softc_ctx.isc_nrxqsets; q++, rxq++) { +#if defined(INET6) || defined(INET) + if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO) { + err = tcp_lro_init_args(&rxq->ifr_lc, ctx->ifc_ifp, + TCP_LRO_ENTRIES, min(1024, + ctx->ifc_softc_ctx.isc_nrxd[rxq->ifr_fl_offset])); + if (err != 0) { + device_printf(ctx->ifc_dev, + "LRO Initialization failed!\n"); + goto fail; + } + } +#endif + IFDI_RXQ_SETUP(ctx, rxq->ifr_id); + } + return (0); +#if defined(INET6) || defined(INET) +fail: + /* + * Free LRO resources allocated so far, we will only handle + * the rings that completed, the failing case will have + * cleaned up for itself. 'q' failed, so its the terminus. + */ + rxq = ctx->ifc_rxqs; + for (i = 0; i < q; ++i, rxq++) { + if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO) + tcp_lro_free(&rxq->ifr_lc); + } + return (err); +#endif +} + +/********************************************************************* + * + * Free all receive rings. + * + **********************************************************************/ +static void +iflib_rx_structures_free(if_ctx_t ctx) +{ + iflib_rxq_t rxq = ctx->ifc_rxqs; + int i; + + for (i = 0; i < ctx->ifc_softc_ctx.isc_nrxqsets; i++, rxq++) { + iflib_rx_sds_free(rxq); +#if defined(INET6) || defined(INET) + if (if_getcapabilities(ctx->ifc_ifp) & IFCAP_LRO) + tcp_lro_free(&rxq->ifr_lc); +#endif + } + free(ctx->ifc_rxqs, M_IFLIB); + ctx->ifc_rxqs = NULL; +} + +static int +iflib_qset_structures_setup(if_ctx_t ctx) +{ + int err; + + /* + * It is expected that the caller takes care of freeing queues if this + * fails. + */ + if ((err = iflib_tx_structures_setup(ctx)) != 0) { + device_printf(ctx->ifc_dev, "iflib_tx_structures_setup failed: %d\n", err); + return (err); + } + + if ((err = iflib_rx_structures_setup(ctx)) != 0) + device_printf(ctx->ifc_dev, "iflib_rx_structures_setup failed: %d\n", err); + + return (err); +} + +int +iflib_irq_alloc(if_ctx_t ctx, if_irq_t irq, int rid, + driver_filter_t filter, void *filter_arg, driver_intr_t handler, void *arg, const char *name) +{ + + return (_iflib_irq_alloc(ctx, irq, rid, filter, handler, arg, name)); +} + +#ifdef SMP +static int +find_nth(if_ctx_t ctx, int qid) +{ + cpuset_t cpus; + int i, cpuid, eqid, count; + + CPU_COPY(&ctx->ifc_cpus, &cpus); + count = CPU_COUNT(&cpus); + eqid = qid % count; + /* clear up to the qid'th bit */ + for (i = 0; i < eqid; i++) { + cpuid = CPU_FFS(&cpus); + MPASS(cpuid != 0); + CPU_CLR(cpuid-1, &cpus); + } + cpuid = CPU_FFS(&cpus); + MPASS(cpuid != 0); + return (cpuid-1); +} + +#ifdef SCHED_ULE +extern struct cpu_group *cpu_top; /* CPU topology */ + +static int +find_child_with_core(int cpu, struct cpu_group *grp) +{ + int i; + + if (grp->cg_children == 0) + return -1; + + MPASS(grp->cg_child); + for (i = 0; i < grp->cg_children; i++) { + if (CPU_ISSET(cpu, &grp->cg_child[i].cg_mask)) + return i; + } + + return -1; +} + +/* + * Find the nth "close" core to the specified core + * "close" is defined as the deepest level that shares + * at least an L2 cache. With threads, this will be + * threads on the same core. If the shared cache is L3 + * or higher, simply returns the same core. + */ +static int +find_close_core(int cpu, int core_offset) +{ + struct cpu_group *grp; + int i; + int fcpu; + cpuset_t cs; + + grp = cpu_top; + if (grp == NULL) + return cpu; + i = 0; + while ((i = find_child_with_core(cpu, grp)) != -1) { + /* If the child only has one cpu, don't descend */ + if (grp->cg_child[i].cg_count <= 1) + break; + grp = &grp->cg_child[i]; + } + + /* If they don't share at least an L2 cache, use the same CPU */ + if (grp->cg_level > CG_SHARE_L2 || grp->cg_level == CG_SHARE_NONE) + return cpu; + + /* Now pick one */ + CPU_COPY(&grp->cg_mask, &cs); + + /* Add the selected CPU offset to core offset. */ + for (i = 0; (fcpu = CPU_FFS(&cs)) != 0; i++) { + if (fcpu - 1 == cpu) + break; + CPU_CLR(fcpu - 1, &cs); + } + MPASS(fcpu); + + core_offset += i; + + CPU_COPY(&grp->cg_mask, &cs); + for (i = core_offset % grp->cg_count; i > 0; i--) { + MPASS(CPU_FFS(&cs)); + CPU_CLR(CPU_FFS(&cs) - 1, &cs); + } + MPASS(CPU_FFS(&cs)); + return CPU_FFS(&cs) - 1; +} +#else +static int +find_close_core(int cpu, int core_offset __unused) +{ + return cpu; +} +#endif + +static int +get_core_offset(if_ctx_t ctx, iflib_intr_type_t type, int qid) +{ + switch (type) { + case IFLIB_INTR_TX: + /* TX queues get cores which share at least an L2 cache with the corresponding RX queue */ + /* XXX handle multiple RX threads per core and more than two core per L2 group */ + return qid / CPU_COUNT(&ctx->ifc_cpus) + 1; + case IFLIB_INTR_RX: + case IFLIB_INTR_RXTX: + /* RX queues get the specified core */ + return qid / CPU_COUNT(&ctx->ifc_cpus); + default: + return -1; + } +} +#else +#define get_core_offset(ctx, type, qid) CPU_FIRST() +#define find_close_core(cpuid, tid) CPU_FIRST() +#define find_nth(ctx, gid) CPU_FIRST() +#endif + +/* Just to avoid copy/paste */ +static inline int +iflib_irq_set_affinity(if_ctx_t ctx, if_irq_t irq, iflib_intr_type_t type, + int qid, struct grouptask *gtask, struct taskqgroup *tqg, void *uniq, + const char *name) +{ + device_t dev; + int co, cpuid, err, tid; + + dev = ctx->ifc_dev; + co = ctx->ifc_sysctl_core_offset; + if (ctx->ifc_sysctl_separate_txrx && type == IFLIB_INTR_TX) + co += ctx->ifc_softc_ctx.isc_nrxqsets; + cpuid = find_nth(ctx, qid + co); + tid = get_core_offset(ctx, type, qid); + if (tid < 0) { + device_printf(dev, "get_core_offset failed\n"); + return (EOPNOTSUPP); + } + cpuid = find_close_core(cpuid, tid); + err = taskqgroup_attach_cpu(tqg, gtask, uniq, cpuid, dev, irq->ii_res, + name); + if (err) { + device_printf(dev, "taskqgroup_attach_cpu failed %d\n", err); + return (err); + } +#ifdef notyet + if (cpuid > ctx->ifc_cpuid_highest) + ctx->ifc_cpuid_highest = cpuid; +#endif + return (0); +} + +int +iflib_irq_alloc_generic(if_ctx_t ctx, if_irq_t irq, int rid, + iflib_intr_type_t type, driver_filter_t *filter, + void *filter_arg, int qid, const char *name) +{ + device_t dev; + struct grouptask *gtask; + struct taskqgroup *tqg; + iflib_filter_info_t info; + gtask_fn_t *fn; + int tqrid, err; + driver_filter_t *intr_fast; + void *q; + + info = &ctx->ifc_filter_info; + tqrid = rid; + + switch (type) { + /* XXX merge tx/rx for netmap? */ + case IFLIB_INTR_TX: + q = &ctx->ifc_txqs[qid]; + info = &ctx->ifc_txqs[qid].ift_filter_info; + gtask = &ctx->ifc_txqs[qid].ift_task; + tqg = qgroup_if_io_tqg; + fn = _task_fn_tx; + intr_fast = iflib_fast_intr; + GROUPTASK_INIT(gtask, 0, fn, q); + ctx->ifc_flags |= IFC_NETMAP_TX_IRQ; + break; + case IFLIB_INTR_RX: + q = &ctx->ifc_rxqs[qid]; + info = &ctx->ifc_rxqs[qid].ifr_filter_info; + gtask = &ctx->ifc_rxqs[qid].ifr_task; + tqg = qgroup_if_io_tqg; + fn = _task_fn_rx; + intr_fast = iflib_fast_intr; + GROUPTASK_INIT(gtask, 0, fn, q); + break; + case IFLIB_INTR_RXTX: + q = &ctx->ifc_rxqs[qid]; + info = &ctx->ifc_rxqs[qid].ifr_filter_info; + gtask = &ctx->ifc_rxqs[qid].ifr_task; + tqg = qgroup_if_io_tqg; + fn = _task_fn_rx; + intr_fast = iflib_fast_intr_rxtx; + GROUPTASK_INIT(gtask, 0, fn, q); + break; + case IFLIB_INTR_ADMIN: + q = ctx; + tqrid = -1; + info = &ctx->ifc_filter_info; + gtask = &ctx->ifc_admin_task; + tqg = qgroup_if_config_tqg; + fn = _task_fn_admin; + intr_fast = iflib_fast_intr_ctx; + break; + default: + device_printf(ctx->ifc_dev, "%s: unknown net intr type\n", + __func__); + return (EINVAL); + } + + info->ifi_filter = filter; + info->ifi_filter_arg = filter_arg; + info->ifi_task = gtask; + info->ifi_ctx = q; + + dev = ctx->ifc_dev; + err = _iflib_irq_alloc(ctx, irq, rid, intr_fast, NULL, info, name); + if (err != 0) { + device_printf(dev, "_iflib_irq_alloc failed %d\n", err); + return (err); + } + if (type == IFLIB_INTR_ADMIN) + return (0); + + if (tqrid != -1) { + err = iflib_irq_set_affinity(ctx, irq, type, qid, gtask, tqg, + q, name); + if (err) + return (err); + } else { + taskqgroup_attach(tqg, gtask, q, dev, irq->ii_res, name); + } + + return (0); +} + +void +iflib_softirq_alloc_generic(if_ctx_t ctx, if_irq_t irq, iflib_intr_type_t type, void *arg, int qid, const char *name) +{ + struct grouptask *gtask; + struct taskqgroup *tqg; + gtask_fn_t *fn; + void *q; + int err; + + switch (type) { + case IFLIB_INTR_TX: + q = &ctx->ifc_txqs[qid]; + gtask = &ctx->ifc_txqs[qid].ift_task; + tqg = qgroup_if_io_tqg; + fn = _task_fn_tx; + break; + case IFLIB_INTR_RX: + q = &ctx->ifc_rxqs[qid]; + gtask = &ctx->ifc_rxqs[qid].ifr_task; + tqg = qgroup_if_io_tqg; + fn = _task_fn_rx; + break; + case IFLIB_INTR_IOV: + q = ctx; + gtask = &ctx->ifc_vflr_task; + tqg = qgroup_if_config_tqg; + fn = _task_fn_iov; + break; + default: + panic("unknown net intr type"); + } + GROUPTASK_INIT(gtask, 0, fn, q); + if (irq != NULL) { + err = iflib_irq_set_affinity(ctx, irq, type, qid, gtask, tqg, + q, name); + if (err) + taskqgroup_attach(tqg, gtask, q, ctx->ifc_dev, + irq->ii_res, name); + } else { + taskqgroup_attach(tqg, gtask, q, NULL, NULL, name); + } +} + +void +iflib_irq_free(if_ctx_t ctx, if_irq_t irq) +{ + + if (irq->ii_tag) + bus_teardown_intr(ctx->ifc_dev, irq->ii_res, irq->ii_tag); + + if (irq->ii_res) + bus_release_resource(ctx->ifc_dev, SYS_RES_IRQ, + rman_get_rid(irq->ii_res), irq->ii_res); +} + +static int +iflib_legacy_setup(if_ctx_t ctx, driver_filter_t filter, void *filter_arg, int *rid, const char *name) +{ + iflib_txq_t txq = ctx->ifc_txqs; + iflib_rxq_t rxq = ctx->ifc_rxqs; + if_irq_t irq = &ctx->ifc_legacy_irq; + iflib_filter_info_t info; + device_t dev; + struct grouptask *gtask; + struct resource *res; + struct taskqgroup *tqg; + gtask_fn_t *fn; + void *q; + int err, tqrid; + + q = &ctx->ifc_rxqs[0]; + info = &rxq[0].ifr_filter_info; + gtask = &rxq[0].ifr_task; + tqg = qgroup_if_io_tqg; + tqrid = *rid; + fn = _task_fn_rx; + + ctx->ifc_flags |= IFC_LEGACY; + info->ifi_filter = filter; + info->ifi_filter_arg = filter_arg; + info->ifi_task = gtask; + info->ifi_ctx = q; + + dev = ctx->ifc_dev; + /* We allocate a single interrupt resource */ + if ((err = _iflib_irq_alloc(ctx, irq, tqrid, iflib_fast_intr_rxtx, + NULL, info, name)) != 0) + return (err); + GROUPTASK_INIT(gtask, 0, fn, q); + res = irq->ii_res; + taskqgroup_attach(tqg, gtask, q, dev, res, name); + + GROUPTASK_INIT(&txq->ift_task, 0, _task_fn_tx, txq); + taskqgroup_attach(qgroup_if_io_tqg, &txq->ift_task, txq, dev, res, + "tx"); + return (0); +} + +void +iflib_led_create(if_ctx_t ctx) +{ + + ctx->ifc_led_dev = led_create(iflib_led_func, ctx, + device_get_nameunit(ctx->ifc_dev)); +} + +void +iflib_tx_intr_deferred(if_ctx_t ctx, int txqid) +{ + + GROUPTASK_ENQUEUE(&ctx->ifc_txqs[txqid].ift_task); +} + +void +iflib_rx_intr_deferred(if_ctx_t ctx, int rxqid) +{ + + GROUPTASK_ENQUEUE(&ctx->ifc_rxqs[rxqid].ifr_task); +} + +void +iflib_admin_intr_deferred(if_ctx_t ctx) +{ +#ifdef INVARIANTS + struct grouptask *gtask; + + gtask = &ctx->ifc_admin_task; + MPASS(gtask != NULL && gtask->gt_taskqueue != NULL); +#endif + + GROUPTASK_ENQUEUE(&ctx->ifc_admin_task); +} + +void +iflib_iov_intr_deferred(if_ctx_t ctx) +{ + + GROUPTASK_ENQUEUE(&ctx->ifc_vflr_task); +} + +void +iflib_io_tqg_attach(struct grouptask *gt, void *uniq, int cpu, const char *name) +{ + + taskqgroup_attach_cpu(qgroup_if_io_tqg, gt, uniq, cpu, NULL, NULL, + name); +} + +void +iflib_config_gtask_init(void *ctx, struct grouptask *gtask, gtask_fn_t *fn, + const char *name) +{ + + GROUPTASK_INIT(gtask, 0, fn, ctx); + taskqgroup_attach(qgroup_if_config_tqg, gtask, gtask, NULL, NULL, + name); +} + +void +iflib_config_gtask_deinit(struct grouptask *gtask) +{ + + taskqgroup_detach(qgroup_if_config_tqg, gtask); +} + +void +iflib_link_state_change(if_ctx_t ctx, int link_state, uint64_t baudrate) +{ + if_t ifp = ctx->ifc_ifp; + iflib_txq_t txq = ctx->ifc_txqs; + + if_setbaudrate(ifp, baudrate); + if (baudrate >= IF_Gbps(10)) { + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_PREFETCH; + STATE_UNLOCK(ctx); + } + /* If link down, disable watchdog */ + if ((ctx->ifc_link_state == LINK_STATE_UP) && (link_state == LINK_STATE_DOWN)) { + for (int i = 0; i < ctx->ifc_softc_ctx.isc_ntxqsets; i++, txq++) + txq->ift_qstatus = IFLIB_QUEUE_IDLE; + } + ctx->ifc_link_state = link_state; + if_link_state_change(ifp, link_state); +} + +static int +iflib_tx_credits_update(if_ctx_t ctx, iflib_txq_t txq) +{ + int credits; +#ifdef INVARIANTS + int credits_pre = txq->ift_cidx_processed; +#endif + + bus_dmamap_sync(txq->ift_ifdi->idi_tag, txq->ift_ifdi->idi_map, + BUS_DMASYNC_POSTREAD); + if ((credits = ctx->isc_txd_credits_update(ctx->ifc_softc, txq->ift_id, true)) == 0) + return (0); + + txq->ift_processed += credits; + txq->ift_cidx_processed += credits; + + MPASS(credits_pre + credits == txq->ift_cidx_processed); + if (txq->ift_cidx_processed >= txq->ift_size) + txq->ift_cidx_processed -= txq->ift_size; + return (credits); +} + +static int +iflib_rxd_avail(if_ctx_t ctx, iflib_rxq_t rxq, qidx_t cidx, qidx_t budget) +{ + iflib_fl_t fl; + u_int i; + + for (i = 0, fl = &rxq->ifr_fl[0]; i < rxq->ifr_nfl; i++, fl++) + bus_dmamap_sync(fl->ifl_ifdi->idi_tag, fl->ifl_ifdi->idi_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + return (ctx->isc_rxd_available(ctx->ifc_softc, rxq->ifr_id, cidx, + budget)); +} + +void +iflib_add_int_delay_sysctl(if_ctx_t ctx, const char *name, + const char *description, if_int_delay_info_t info, + int offset, int value) +{ + info->iidi_ctx = ctx; + info->iidi_offset = offset; + info->iidi_value = value; + SYSCTL_ADD_PROC(device_get_sysctl_ctx(ctx->ifc_dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(ctx->ifc_dev)), + OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, + info, 0, iflib_sysctl_int_delay, "I", description); +} + +struct sx * +iflib_ctx_lock_get(if_ctx_t ctx) +{ + + return (&ctx->ifc_ctx_sx); +} + +static int +iflib_msix_init(if_ctx_t ctx) +{ + device_t dev = ctx->ifc_dev; + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + int admincnt, bar, err, iflib_num_rx_queues, iflib_num_tx_queues; + int msgs, queuemsgs, queues, rx_queues, tx_queues, vectors; + + iflib_num_tx_queues = ctx->ifc_sysctl_ntxqs; + iflib_num_rx_queues = ctx->ifc_sysctl_nrxqs; + + if (bootverbose) + device_printf(dev, "msix_init qsets capped at %d\n", + imax(scctx->isc_ntxqsets, scctx->isc_nrxqsets)); + + /* Override by tuneable */ + if (scctx->isc_disable_msix) + goto msi; + + /* First try MSI-X */ + if ((msgs = pci_msix_count(dev)) == 0) { + if (bootverbose) + device_printf(dev, "MSI-X not supported or disabled\n"); + goto msi; + } + + bar = ctx->ifc_softc_ctx.isc_msix_bar; + /* + * bar == -1 => "trust me I know what I'm doing" + * Some drivers are for hardware that is so shoddily + * documented that no one knows which bars are which + * so the developer has to map all bars. This hack + * allows shoddy garbage to use MSI-X in this framework. + */ + if (bar != -1) { + ctx->ifc_msix_mem = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &bar, RF_ACTIVE); + if (ctx->ifc_msix_mem == NULL) { + device_printf(dev, "Unable to map MSI-X table\n"); + goto msi; + } + } + + admincnt = sctx->isc_admin_intrcnt; +#if IFLIB_DEBUG + /* use only 1 qset in debug mode */ + queuemsgs = min(msgs - admincnt, 1); +#else + queuemsgs = msgs - admincnt; +#endif +#ifdef RSS + queues = imin(queuemsgs, rss_getnumbuckets()); +#else + queues = queuemsgs; +#endif +#ifndef __rtems__ + queues = imin(CPU_COUNT(&ctx->ifc_cpus), queues); +#else /* __rtems__ */ + queues = imin(1, queues); +#endif /* __rtems__ */ + if (bootverbose) + device_printf(dev, + "intr CPUs: %d queue msgs: %d admincnt: %d\n", +#ifndef __rtems__ + CPU_COUNT(&ctx->ifc_cpus), queuemsgs, admincnt); +#else /* __rtems__ */ + 1, queuemsgs, admincnt); +#endif /* __rtems__ */ +#ifdef RSS + /* If we're doing RSS, clamp at the number of RSS buckets */ + if (queues > rss_getnumbuckets()) + queues = rss_getnumbuckets(); +#endif + if (iflib_num_rx_queues > 0 && iflib_num_rx_queues < queuemsgs - admincnt) + rx_queues = iflib_num_rx_queues; + else + rx_queues = queues; + + if (rx_queues > scctx->isc_nrxqsets) + rx_queues = scctx->isc_nrxqsets; + + /* + * We want this to be all logical CPUs by default + */ + if (iflib_num_tx_queues > 0 && iflib_num_tx_queues < queues) + tx_queues = iflib_num_tx_queues; + else + tx_queues = mp_ncpus; + + if (tx_queues > scctx->isc_ntxqsets) + tx_queues = scctx->isc_ntxqsets; + + if (ctx->ifc_sysctl_qs_eq_override == 0) { +#ifdef INVARIANTS + if (tx_queues != rx_queues) + device_printf(dev, + "queue equality override not set, capping rx_queues at %d and tx_queues at %d\n", + min(rx_queues, tx_queues), min(rx_queues, tx_queues)); +#endif + tx_queues = min(rx_queues, tx_queues); + rx_queues = min(rx_queues, tx_queues); + } + + vectors = rx_queues + admincnt; + if (msgs < vectors) { + device_printf(dev, + "insufficient number of MSI-X vectors " + "(supported %d, need %d)\n", msgs, vectors); + goto msi; + } + + device_printf(dev, "Using %d RX queues %d TX queues\n", rx_queues, + tx_queues); + msgs = vectors; + if ((err = pci_alloc_msix(dev, &vectors)) == 0) { + if (vectors != msgs) { + device_printf(dev, + "Unable to allocate sufficient MSI-X vectors " + "(got %d, need %d)\n", vectors, msgs); + pci_release_msi(dev); + if (bar != -1) { + bus_release_resource(dev, SYS_RES_MEMORY, bar, + ctx->ifc_msix_mem); + ctx->ifc_msix_mem = NULL; + } + goto msi; + } + device_printf(dev, "Using MSI-X interrupts with %d vectors\n", + vectors); + scctx->isc_vectors = vectors; + scctx->isc_nrxqsets = rx_queues; + scctx->isc_ntxqsets = tx_queues; + scctx->isc_intr = IFLIB_INTR_MSIX; + + return (vectors); + } else { + device_printf(dev, + "failed to allocate %d MSI-X vectors, err: %d\n", vectors, + err); + if (bar != -1) { + bus_release_resource(dev, SYS_RES_MEMORY, bar, + ctx->ifc_msix_mem); + ctx->ifc_msix_mem = NULL; + } + } + +msi: + vectors = pci_msi_count(dev); + scctx->isc_nrxqsets = 1; + scctx->isc_ntxqsets = 1; + scctx->isc_vectors = vectors; + if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0) { + device_printf(dev,"Using an MSI interrupt\n"); + scctx->isc_intr = IFLIB_INTR_MSI; + } else { + scctx->isc_vectors = 1; + device_printf(dev,"Using a Legacy interrupt\n"); + scctx->isc_intr = IFLIB_INTR_LEGACY; + } + + return (vectors); +} + +static const char *ring_states[] = { "IDLE", "BUSY", "STALLED", "ABDICATED" }; + +static int +mp_ring_state_handler(SYSCTL_HANDLER_ARGS) +{ + int rc; + uint16_t *state = ((uint16_t *)oidp->oid_arg1); + struct sbuf *sb; + const char *ring_state = "UNKNOWN"; + + /* XXX needed ? */ + rc = sysctl_wire_old_buffer(req, 0); + MPASS(rc == 0); + if (rc != 0) + return (rc); + sb = sbuf_new_for_sysctl(NULL, NULL, 80, req); + MPASS(sb != NULL); + if (sb == NULL) + return (ENOMEM); + if (state[3] <= 3) + ring_state = ring_states[state[3]]; + + sbuf_printf(sb, "pidx_head: %04hd pidx_tail: %04hd cidx: %04hd state: %s", + state[0], state[1], state[2], ring_state); + rc = sbuf_finish(sb); + sbuf_delete(sb); + return(rc); +} + +enum iflib_ndesc_handler { + IFLIB_NTXD_HANDLER, + IFLIB_NRXD_HANDLER, +}; + +static int +mp_ndesc_handler(SYSCTL_HANDLER_ARGS) +{ + if_ctx_t ctx = (void *)arg1; + enum iflib_ndesc_handler type = arg2; + char buf[256] = {0}; + qidx_t *ndesc; + char *p, *next; + int nqs, rc, i; + + nqs = 8; + switch(type) { + case IFLIB_NTXD_HANDLER: + ndesc = ctx->ifc_sysctl_ntxds; + if (ctx->ifc_sctx) + nqs = ctx->ifc_sctx->isc_ntxqs; + break; + case IFLIB_NRXD_HANDLER: + ndesc = ctx->ifc_sysctl_nrxds; + if (ctx->ifc_sctx) + nqs = ctx->ifc_sctx->isc_nrxqs; + break; + default: + printf("%s: unhandled type\n", __func__); + return (EINVAL); + } + if (nqs == 0) + nqs = 8; + + for (i=0; i<8; i++) { + if (i >= nqs) + break; + if (i) + strcat(buf, ","); + sprintf(strchr(buf, 0), "%d", ndesc[i]); + } + + rc = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (rc || req->newptr == NULL) + return rc; + + for (i = 0, next = buf, p = strsep(&next, " ,"); i < 8 && p; + i++, p = strsep(&next, " ,")) { + ndesc[i] = strtoul(p, NULL, 10); + } + + return(rc); +} + +#define NAME_BUFLEN 32 +static void +iflib_add_device_sysctl_pre(if_ctx_t ctx) +{ + device_t dev = iflib_get_dev(ctx); + struct sysctl_oid_list *child, *oid_list; + struct sysctl_ctx_list *ctx_list; + struct sysctl_oid *node; + + ctx_list = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + ctx->ifc_sysctl_node = node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, "iflib", + CTLFLAG_RD, NULL, "IFLIB fields"); + oid_list = SYSCTL_CHILDREN(node); + + SYSCTL_ADD_CONST_STRING(ctx_list, oid_list, OID_AUTO, "driver_version", + CTLFLAG_RD, ctx->ifc_sctx->isc_driver_version, + "driver version"); + + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_ntxqs", + CTLFLAG_RWTUN, &ctx->ifc_sysctl_ntxqs, 0, + "# of txqs to use, 0 => use default #"); + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_nrxqs", + CTLFLAG_RWTUN, &ctx->ifc_sysctl_nrxqs, 0, + "# of rxqs to use, 0 => use default #"); + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_qs_enable", + CTLFLAG_RWTUN, &ctx->ifc_sysctl_qs_eq_override, 0, + "permit #txq != #rxq"); + SYSCTL_ADD_INT(ctx_list, oid_list, OID_AUTO, "disable_msix", + CTLFLAG_RWTUN, &ctx->ifc_softc_ctx.isc_disable_msix, 0, + "disable MSI-X (default 0)"); + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "rx_budget", + CTLFLAG_RWTUN, &ctx->ifc_sysctl_rx_budget, 0, + "set the RX budget"); + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "tx_abdicate", + CTLFLAG_RWTUN, &ctx->ifc_sysctl_tx_abdicate, 0, + "cause TX to abdicate instead of running to completion"); + ctx->ifc_sysctl_core_offset = CORE_OFFSET_UNSPECIFIED; + SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "core_offset", + CTLFLAG_RDTUN, &ctx->ifc_sysctl_core_offset, 0, + "offset to start using cores at"); + SYSCTL_ADD_U8(ctx_list, oid_list, OID_AUTO, "separate_txrx", + CTLFLAG_RDTUN, &ctx->ifc_sysctl_separate_txrx, 0, + "use separate cores for TX and RX"); + + /* XXX change for per-queue sizes */ + SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_ntxds", + CTLTYPE_STRING|CTLFLAG_RWTUN, ctx, IFLIB_NTXD_HANDLER, + mp_ndesc_handler, "A", + "list of # of TX descriptors to use, 0 = use default #"); + SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_nrxds", + CTLTYPE_STRING|CTLFLAG_RWTUN, ctx, IFLIB_NRXD_HANDLER, + mp_ndesc_handler, "A", + "list of # of RX descriptors to use, 0 = use default #"); +} + +static void +iflib_add_device_sysctl_post(if_ctx_t ctx) +{ + if_shared_ctx_t sctx = ctx->ifc_sctx; + if_softc_ctx_t scctx = &ctx->ifc_softc_ctx; + device_t dev = iflib_get_dev(ctx); + struct sysctl_oid_list *child; + struct sysctl_ctx_list *ctx_list; + iflib_fl_t fl; + iflib_txq_t txq; + iflib_rxq_t rxq; + int i, j; + char namebuf[NAME_BUFLEN]; + char *qfmt; + struct sysctl_oid *queue_node, *fl_node, *node; + struct sysctl_oid_list *queue_list, *fl_list; + ctx_list = device_get_sysctl_ctx(dev); + + node = ctx->ifc_sysctl_node; + child = SYSCTL_CHILDREN(node); + + if (scctx->isc_ntxqsets > 100) + qfmt = "txq%03d"; + else if (scctx->isc_ntxqsets > 10) + qfmt = "txq%02d"; + else + qfmt = "txq%d"; + for (i = 0, txq = ctx->ifc_txqs; i < scctx->isc_ntxqsets; i++, txq++) { + snprintf(namebuf, NAME_BUFLEN, qfmt, i); + queue_node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, namebuf, + CTLFLAG_RD, NULL, "Queue Name"); + queue_list = SYSCTL_CHILDREN(queue_node); +#if MEMORY_LOGGING + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_dequeued", + CTLFLAG_RD, + &txq->ift_dequeued, "total mbufs freed"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_enqueued", + CTLFLAG_RD, + &txq->ift_enqueued, "total mbufs enqueued"); +#endif + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "mbuf_defrag", + CTLFLAG_RD, + &txq->ift_mbuf_defrag, "# of times m_defrag was called"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "m_pullups", + CTLFLAG_RD, + &txq->ift_pullups, "# of times m_pullup was called"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "mbuf_defrag_failed", + CTLFLAG_RD, + &txq->ift_mbuf_defrag_failed, "# of times m_defrag failed"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "no_desc_avail", + CTLFLAG_RD, + &txq->ift_no_desc_avail, "# of times no descriptors were available"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "tx_map_failed", + CTLFLAG_RD, + &txq->ift_map_failed, "# of times DMA map failed"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txd_encap_efbig", + CTLFLAG_RD, + &txq->ift_txd_encap_efbig, "# of times txd_encap returned EFBIG"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "no_tx_dma_setup", + CTLFLAG_RD, + &txq->ift_no_tx_dma_setup, "# of times map failed for other than EFBIG"); + SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_pidx", + CTLFLAG_RD, + &txq->ift_pidx, 1, "Producer Index"); + SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_cidx", + CTLFLAG_RD, + &txq->ift_cidx, 1, "Consumer Index"); + SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_cidx_processed", + CTLFLAG_RD, + &txq->ift_cidx_processed, 1, "Consumer Index seen by credit update"); + SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "txq_in_use", + CTLFLAG_RD, + &txq->ift_in_use, 1, "descriptors in use"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_processed", + CTLFLAG_RD, + &txq->ift_processed, "descriptors procesed for clean"); + SYSCTL_ADD_QUAD(ctx_list, queue_list, OID_AUTO, "txq_cleaned", + CTLFLAG_RD, + &txq->ift_cleaned, "total cleaned"); + SYSCTL_ADD_PROC(ctx_list, queue_list, OID_AUTO, "ring_state", + CTLTYPE_STRING | CTLFLAG_RD, __DEVOLATILE(uint64_t *, &txq->ift_br->state), + 0, mp_ring_state_handler, "A", "soft ring state"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_enqueues", + CTLFLAG_RD, &txq->ift_br->enqueues, + "# of enqueues to the mp_ring for this queue"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_drops", + CTLFLAG_RD, &txq->ift_br->drops, + "# of drops in the mp_ring for this queue"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_starts", + CTLFLAG_RD, &txq->ift_br->starts, + "# of normal consumer starts in the mp_ring for this queue"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_stalls", + CTLFLAG_RD, &txq->ift_br->stalls, + "# of consumer stalls in the mp_ring for this queue"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_restarts", + CTLFLAG_RD, &txq->ift_br->restarts, + "# of consumer restarts in the mp_ring for this queue"); + SYSCTL_ADD_COUNTER_U64(ctx_list, queue_list, OID_AUTO, "r_abdications", + CTLFLAG_RD, &txq->ift_br->abdications, + "# of consumer abdications in the mp_ring for this queue"); + } + + if (scctx->isc_nrxqsets > 100) + qfmt = "rxq%03d"; + else if (scctx->isc_nrxqsets > 10) + qfmt = "rxq%02d"; + else + qfmt = "rxq%d"; + for (i = 0, rxq = ctx->ifc_rxqs; i < scctx->isc_nrxqsets; i++, rxq++) { + snprintf(namebuf, NAME_BUFLEN, qfmt, i); + queue_node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, namebuf, + CTLFLAG_RD, NULL, "Queue Name"); + queue_list = SYSCTL_CHILDREN(queue_node); + if (sctx->isc_flags & IFLIB_HAS_RXCQ) { + SYSCTL_ADD_U16(ctx_list, queue_list, OID_AUTO, "rxq_cq_cidx", + CTLFLAG_RD, + &rxq->ifr_cq_cidx, 1, "Consumer Index"); + } + + for (j = 0, fl = rxq->ifr_fl; j < rxq->ifr_nfl; j++, fl++) { + snprintf(namebuf, NAME_BUFLEN, "rxq_fl%d", j); + fl_node = SYSCTL_ADD_NODE(ctx_list, queue_list, OID_AUTO, namebuf, + CTLFLAG_RD, NULL, "freelist Name"); + fl_list = SYSCTL_CHILDREN(fl_node); + SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "pidx", + CTLFLAG_RD, + &fl->ifl_pidx, 1, "Producer Index"); + SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "cidx", + CTLFLAG_RD, + &fl->ifl_cidx, 1, "Consumer Index"); + SYSCTL_ADD_U16(ctx_list, fl_list, OID_AUTO, "credits", + CTLFLAG_RD, + &fl->ifl_credits, 1, "credits available"); +#if MEMORY_LOGGING + SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_m_enqueued", + CTLFLAG_RD, + &fl->ifl_m_enqueued, "mbufs allocated"); + SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_m_dequeued", + CTLFLAG_RD, + &fl->ifl_m_dequeued, "mbufs freed"); + SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_cl_enqueued", + CTLFLAG_RD, + &fl->ifl_cl_enqueued, "clusters allocated"); + SYSCTL_ADD_QUAD(ctx_list, fl_list, OID_AUTO, "fl_cl_dequeued", + CTLFLAG_RD, + &fl->ifl_cl_dequeued, "clusters freed"); +#endif + + } + } + +} + +void +iflib_request_reset(if_ctx_t ctx) +{ + + STATE_LOCK(ctx); + ctx->ifc_flags |= IFC_DO_RESET; + STATE_UNLOCK(ctx); +} + +#ifndef __NO_STRICT_ALIGNMENT +static struct mbuf * +iflib_fixup_rx(struct mbuf *m) +{ + struct mbuf *n; + + if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) { + bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); + m->m_data += ETHER_HDR_LEN; + n = m; + } else { + MGETHDR(n, M_NOWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + return (NULL); + } + bcopy(m->m_data, n->m_data, ETHER_HDR_LEN); + m->m_data += ETHER_HDR_LEN; + m->m_len -= ETHER_HDR_LEN; + n->m_len = ETHER_HDR_LEN; + M_MOVE_PKTHDR(n, m); + n->m_next = m; + } + return (n); +} +#endif + +#ifdef NETDUMP +static void +iflib_netdump_init(if_t ifp, int *nrxr, int *ncl, int *clsize) +{ + if_ctx_t ctx; + + ctx = if_getsoftc(ifp); + CTX_LOCK(ctx); + *nrxr = NRXQSETS(ctx); + *ncl = ctx->ifc_rxqs[0].ifr_fl->ifl_size; + *clsize = ctx->ifc_rxqs[0].ifr_fl->ifl_buf_size; + CTX_UNLOCK(ctx); +} + +static void +iflib_netdump_event(if_t ifp, enum netdump_ev event) +{ + if_ctx_t ctx; + if_softc_ctx_t scctx; + iflib_fl_t fl; + iflib_rxq_t rxq; + int i, j; + + ctx = if_getsoftc(ifp); + scctx = &ctx->ifc_softc_ctx; + + switch (event) { + case NETDUMP_START: + for (i = 0; i < scctx->isc_nrxqsets; i++) { + rxq = &ctx->ifc_rxqs[i]; + for (j = 0; j < rxq->ifr_nfl; j++) { + fl = rxq->ifr_fl; + fl->ifl_zone = m_getzone(fl->ifl_buf_size); + } + } + iflib_no_tx_batch = 1; + break; + default: + break; + } +} + +static int +iflib_netdump_transmit(if_t ifp, struct mbuf *m) +{ + if_ctx_t ctx; + iflib_txq_t txq; + int error; + + ctx = if_getsoftc(ifp); + if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return (EBUSY); + + txq = &ctx->ifc_txqs[0]; + error = iflib_encap(txq, &m); + if (error == 0) + (void)iflib_txd_db_check(ctx, txq, true, txq->ift_in_use); + return (error); +} + +static int +iflib_netdump_poll(if_t ifp, int count) +{ + if_ctx_t ctx; + if_softc_ctx_t scctx; + iflib_txq_t txq; + int i; + + ctx = if_getsoftc(ifp); + scctx = &ctx->ifc_softc_ctx; + + if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return (EBUSY); + + txq = &ctx->ifc_txqs[0]; + (void)iflib_completed_tx_reclaim(txq, RECLAIM_THRESH(ctx)); + + for (i = 0; i < scctx->isc_nrxqsets; i++) + (void)iflib_rxeof(&ctx->ifc_rxqs[i], 16 /* XXX */); + return (0); +} +#endif /* NETDUMP */ diff --git a/freebsd/sys/net/iflib_private.h b/freebsd/sys/net/iflib_private.h new file mode 100644 index 00000000..eca6be63 --- /dev/null +++ b/freebsd/sys/net/iflib_private.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2018, Matthew Macy (mmacy@freebsd.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Neither the name of Matthew Macy nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __NET_IFLIB_PRIVATE_H_ +#define __NET_IFLIB_PRIVATE_H_ + +#define IFC_LEGACY 0x001 +#define IFC_QFLUSH 0x002 +#define IFC_MULTISEG 0x004 +#define IFC_SPARE1 0x008 +#define IFC_SC_ALLOCATED 0x010 +#define IFC_INIT_DONE 0x020 +#define IFC_PREFETCH 0x040 +#define IFC_DO_RESET 0x080 +#define IFC_DO_WATCHDOG 0x100 +#define IFC_SPARE0 0x200 +#define IFC_PSEUDO 0x400 +#define IFC_IN_DETACH 0x800 + +#define IFC_NETMAP_TX_IRQ 0x80000000 + +MALLOC_DECLARE(M_IFLIB); + +struct iflib_cloneattach_ctx { + struct if_clone *cc_ifc; + caddr_t cc_params; + const char *cc_name; + int cc_len; +}; + +extern driver_t iflib_pseudodriver; +int noop_attach(device_t dev); +int iflib_pseudo_detach(device_t dev); + +int iflib_pseudo_register(device_t dev, if_shared_ctx_t sctx, if_ctx_t *ctxp, + struct iflib_cloneattach_ctx *clctx); + +int iflib_pseudo_deregister(if_ctx_t ctx); + +uint32_t iflib_get_flags(if_ctx_t ctx); +void iflib_set_detach(if_ctx_t ctx); +void iflib_stop(if_ctx_t ctx); + +#endif diff --git a/freebsd/sys/net/mp_ring.c b/freebsd/sys/net/mp_ring.c new file mode 100644 index 00000000..c2a2e9db --- /dev/null +++ b/freebsd/sys/net/mp_ring.c @@ -0,0 +1,554 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2014 Chelsio Communications, Inc. + * All rights reserved. + * Written by: Navdeep Parhar <np@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/counter.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/malloc.h> +#include <machine/cpu.h> +#include <net/mp_ring.h> + +union ring_state { + struct { + uint16_t pidx_head; + uint16_t pidx_tail; + uint16_t cidx; + uint16_t flags; + }; + uint64_t state; +}; + +enum { + IDLE = 0, /* consumer ran to completion, nothing more to do. */ + BUSY, /* consumer is running already, or will be shortly. */ + STALLED, /* consumer stopped due to lack of resources. */ + ABDICATED, /* consumer stopped even though there was work to be + done because it wants another thread to take over. */ +}; + +static inline uint16_t +space_available(struct ifmp_ring *r, union ring_state s) +{ + uint16_t x = r->size - 1; + + if (s.cidx == s.pidx_head) + return (x); + else if (s.cidx > s.pidx_head) + return (s.cidx - s.pidx_head - 1); + else + return (x - s.pidx_head + s.cidx); +} + +static inline uint16_t +increment_idx(struct ifmp_ring *r, uint16_t idx, uint16_t n) +{ + int x = r->size - idx; + + MPASS(x > 0); + return (x > n ? idx + n : n - x); +} + +/* Consumer is about to update the ring's state to s */ +static inline uint16_t +state_to_flags(union ring_state s, int abdicate) +{ + + if (s.cidx == s.pidx_tail) + return (IDLE); + else if (abdicate && s.pidx_tail != s.pidx_head) + return (ABDICATED); + + return (BUSY); +} + +#ifdef MP_RING_NO_64BIT_ATOMICS +static void +drain_ring_locked(struct ifmp_ring *r, union ring_state os, uint16_t prev, int budget) +{ + union ring_state ns; + int n, pending, total; + uint16_t cidx = os.cidx; + uint16_t pidx = os.pidx_tail; + + MPASS(os.flags == BUSY); + MPASS(cidx != pidx); + + if (prev == IDLE) + counter_u64_add(r->starts, 1); + pending = 0; + total = 0; + + while (cidx != pidx) { + + /* Items from cidx to pidx are available for consumption. */ + n = r->drain(r, cidx, pidx); + if (n == 0) { + os.state = ns.state = r->state; + ns.cidx = cidx; + ns.flags = STALLED; + r->state = ns.state; + if (prev != STALLED) + counter_u64_add(r->stalls, 1); + else if (total > 0) { + counter_u64_add(r->restarts, 1); + counter_u64_add(r->stalls, 1); + } + break; + } + cidx = increment_idx(r, cidx, n); + pending += n; + total += n; + + /* + * We update the cidx only if we've caught up with the pidx, the + * real cidx is getting too far ahead of the one visible to + * everyone else, or we have exceeded our budget. + */ + if (cidx != pidx && pending < 64 && total < budget) + continue; + + os.state = ns.state = r->state; + ns.cidx = cidx; + ns.flags = state_to_flags(ns, total >= budget); + r->state = ns.state; + + if (ns.flags == ABDICATED) + counter_u64_add(r->abdications, 1); + if (ns.flags != BUSY) { + /* Wrong loop exit if we're going to stall. */ + MPASS(ns.flags != STALLED); + if (prev == STALLED) { + MPASS(total > 0); + counter_u64_add(r->restarts, 1); + } + break; + } + + /* + * The acquire style atomic above guarantees visibility of items + * associated with any pidx change that we notice here. + */ + pidx = ns.pidx_tail; + pending = 0; + } +} +#else +/* + * Caller passes in a state, with a guarantee that there is work to do and that + * all items up to the pidx_tail in the state are visible. + */ +static void +drain_ring_lockless(struct ifmp_ring *r, union ring_state os, uint16_t prev, int budget) +{ + union ring_state ns; + int n, pending, total; + uint16_t cidx = os.cidx; + uint16_t pidx = os.pidx_tail; + + MPASS(os.flags == BUSY); + MPASS(cidx != pidx); + + if (prev == IDLE) + counter_u64_add(r->starts, 1); + pending = 0; + total = 0; + + while (cidx != pidx) { + + /* Items from cidx to pidx are available for consumption. */ + n = r->drain(r, cidx, pidx); + if (n == 0) { + critical_enter(); + os.state = r->state; + do { + ns.state = os.state; + ns.cidx = cidx; + ns.flags = STALLED; + } while (atomic_fcmpset_64(&r->state, &os.state, + ns.state) == 0); + critical_exit(); + if (prev != STALLED) + counter_u64_add(r->stalls, 1); + else if (total > 0) { + counter_u64_add(r->restarts, 1); + counter_u64_add(r->stalls, 1); + } + break; + } + cidx = increment_idx(r, cidx, n); + pending += n; + total += n; + + /* + * We update the cidx only if we've caught up with the pidx, the + * real cidx is getting too far ahead of the one visible to + * everyone else, or we have exceeded our budget. + */ + if (cidx != pidx && pending < 64 && total < budget) + continue; + critical_enter(); + os.state = r->state; + do { + ns.state = os.state; + ns.cidx = cidx; + ns.flags = state_to_flags(ns, total >= budget); + } while (atomic_fcmpset_acq_64(&r->state, &os.state, + ns.state) == 0); + critical_exit(); + + if (ns.flags == ABDICATED) + counter_u64_add(r->abdications, 1); + if (ns.flags != BUSY) { + /* Wrong loop exit if we're going to stall. */ + MPASS(ns.flags != STALLED); + if (prev == STALLED) { + MPASS(total > 0); + counter_u64_add(r->restarts, 1); + } + break; + } + + /* + * The acquire style atomic above guarantees visibility of items + * associated with any pidx change that we notice here. + */ + pidx = ns.pidx_tail; + pending = 0; + } +} +#endif + +int +ifmp_ring_alloc(struct ifmp_ring **pr, int size, void *cookie, mp_ring_drain_t drain, + mp_ring_can_drain_t can_drain, struct malloc_type *mt, int flags) +{ + struct ifmp_ring *r; + + /* All idx are 16b so size can be 65536 at most */ + if (pr == NULL || size < 2 || size > 65536 || drain == NULL || + can_drain == NULL) + return (EINVAL); + *pr = NULL; + flags &= M_NOWAIT | M_WAITOK; + MPASS(flags != 0); + + r = malloc(__offsetof(struct ifmp_ring, items[size]), mt, flags | M_ZERO); + if (r == NULL) + return (ENOMEM); + r->size = size; + r->cookie = cookie; + r->mt = mt; + r->drain = drain; + r->can_drain = can_drain; + r->enqueues = counter_u64_alloc(flags); + r->drops = counter_u64_alloc(flags); + r->starts = counter_u64_alloc(flags); + r->stalls = counter_u64_alloc(flags); + r->restarts = counter_u64_alloc(flags); + r->abdications = counter_u64_alloc(flags); + if (r->enqueues == NULL || r->drops == NULL || r->starts == NULL || + r->stalls == NULL || r->restarts == NULL || + r->abdications == NULL) { + ifmp_ring_free(r); + return (ENOMEM); + } + + *pr = r; +#ifdef MP_RING_NO_64BIT_ATOMICS + mtx_init(&r->lock, "mp_ring lock", NULL, MTX_DEF); +#endif + return (0); +} + +void +ifmp_ring_free(struct ifmp_ring *r) +{ + + if (r == NULL) + return; + + if (r->enqueues != NULL) + counter_u64_free(r->enqueues); + if (r->drops != NULL) + counter_u64_free(r->drops); + if (r->starts != NULL) + counter_u64_free(r->starts); + if (r->stalls != NULL) + counter_u64_free(r->stalls); + if (r->restarts != NULL) + counter_u64_free(r->restarts); + if (r->abdications != NULL) + counter_u64_free(r->abdications); + + free(r, r->mt); +} + +/* + * Enqueue n items and maybe drain the ring for some time. + * + * Returns an errno. + */ +#ifdef MP_RING_NO_64BIT_ATOMICS +int +ifmp_ring_enqueue(struct ifmp_ring *r, void **items, int n, int budget, int abdicate) +{ + union ring_state os, ns; + uint16_t pidx_start, pidx_stop; + int i; + + MPASS(items != NULL); + MPASS(n > 0); + + mtx_lock(&r->lock); + /* + * Reserve room for the new items. Our reservation, if successful, is + * from 'pidx_start' to 'pidx_stop'. + */ + os.state = r->state; + if (n >= space_available(r, os)) { + counter_u64_add(r->drops, n); + MPASS(os.flags != IDLE); + mtx_unlock(&r->lock); + if (os.flags == STALLED) + ifmp_ring_check_drainage(r, 0); + return (ENOBUFS); + } + ns.state = os.state; + ns.pidx_head = increment_idx(r, os.pidx_head, n); + r->state = ns.state; + pidx_start = os.pidx_head; + pidx_stop = ns.pidx_head; + + /* + * Wait for other producers who got in ahead of us to enqueue their + * items, one producer at a time. It is our turn when the ring's + * pidx_tail reaches the beginning of our reservation (pidx_start). + */ + while (ns.pidx_tail != pidx_start) { + cpu_spinwait(); + ns.state = r->state; + } + + /* Now it is our turn to fill up the area we reserved earlier. */ + i = pidx_start; + do { + r->items[i] = *items++; + if (__predict_false(++i == r->size)) + i = 0; + } while (i != pidx_stop); + + /* + * Update the ring's pidx_tail. The release style atomic guarantees + * that the items are visible to any thread that sees the updated pidx. + */ + os.state = ns.state = r->state; + ns.pidx_tail = pidx_stop; + if (abdicate) { + if (os.flags == IDLE) + ns.flags = ABDICATED; + } else + ns.flags = BUSY; + r->state = ns.state; + counter_u64_add(r->enqueues, n); + + if (!abdicate) { + /* + * Turn into a consumer if some other thread isn't active as a consumer + * already. + */ + if (os.flags != BUSY) + drain_ring_locked(r, ns, os.flags, budget); + } + + mtx_unlock(&r->lock); + return (0); +} +#else +int +ifmp_ring_enqueue(struct ifmp_ring *r, void **items, int n, int budget, int abdicate) +{ + union ring_state os, ns; + uint16_t pidx_start, pidx_stop; + int i; + + MPASS(items != NULL); + MPASS(n > 0); + + /* + * Reserve room for the new items. Our reservation, if successful, is + * from 'pidx_start' to 'pidx_stop'. + */ + os.state = r->state; + for (;;) { + if (n >= space_available(r, os)) { + counter_u64_add(r->drops, n); + MPASS(os.flags != IDLE); + if (os.flags == STALLED) + ifmp_ring_check_drainage(r, 0); + return (ENOBUFS); + } + ns.state = os.state; + ns.pidx_head = increment_idx(r, os.pidx_head, n); + critical_enter(); + if (atomic_fcmpset_64(&r->state, &os.state, ns.state)) + break; + critical_exit(); + cpu_spinwait(); + } + pidx_start = os.pidx_head; + pidx_stop = ns.pidx_head; + + /* + * Wait for other producers who got in ahead of us to enqueue their + * items, one producer at a time. It is our turn when the ring's + * pidx_tail reaches the beginning of our reservation (pidx_start). + */ + while (ns.pidx_tail != pidx_start) { + cpu_spinwait(); + ns.state = r->state; + } + + /* Now it is our turn to fill up the area we reserved earlier. */ + i = pidx_start; + do { + r->items[i] = *items++; + if (__predict_false(++i == r->size)) + i = 0; + } while (i != pidx_stop); + + /* + * Update the ring's pidx_tail. The release style atomic guarantees + * that the items are visible to any thread that sees the updated pidx. + */ + os.state = r->state; + do { + ns.state = os.state; + ns.pidx_tail = pidx_stop; + if (abdicate) { + if (os.flags == IDLE) + ns.flags = ABDICATED; + } else + ns.flags = BUSY; + } while (atomic_fcmpset_rel_64(&r->state, &os.state, ns.state) == 0); + critical_exit(); + counter_u64_add(r->enqueues, n); + + if (!abdicate) { + /* + * Turn into a consumer if some other thread isn't active as a consumer + * already. + */ + if (os.flags != BUSY) + drain_ring_lockless(r, ns, os.flags, budget); + } + + return (0); +} +#endif + +void +ifmp_ring_check_drainage(struct ifmp_ring *r, int budget) +{ + union ring_state os, ns; + + os.state = r->state; + if ((os.flags != STALLED && os.flags != ABDICATED) || // Only continue in STALLED and ABDICATED + os.pidx_head != os.pidx_tail || // Require work to be available + (os.flags != ABDICATED && r->can_drain(r) == 0)) // Can either drain, or everyone left + return; + + MPASS(os.cidx != os.pidx_tail); /* implied by STALLED */ + ns.state = os.state; + ns.flags = BUSY; + + +#ifdef MP_RING_NO_64BIT_ATOMICS + mtx_lock(&r->lock); + if (r->state != os.state) { + mtx_unlock(&r->lock); + return; + } + r->state = ns.state; + drain_ring_locked(r, ns, os.flags, budget); + mtx_unlock(&r->lock); +#else + /* + * The acquire style atomic guarantees visibility of items associated + * with the pidx that we read here. + */ + if (!atomic_cmpset_acq_64(&r->state, os.state, ns.state)) + return; + + + drain_ring_lockless(r, ns, os.flags, budget); +#endif +} + +void +ifmp_ring_reset_stats(struct ifmp_ring *r) +{ + + counter_u64_zero(r->enqueues); + counter_u64_zero(r->drops); + counter_u64_zero(r->starts); + counter_u64_zero(r->stalls); + counter_u64_zero(r->restarts); + counter_u64_zero(r->abdications); +} + +int +ifmp_ring_is_idle(struct ifmp_ring *r) +{ + union ring_state s; + + s.state = r->state; + if (s.pidx_head == s.pidx_tail && s.pidx_tail == s.cidx && + s.flags == IDLE) + return (1); + + return (0); +} + +int +ifmp_ring_is_stalled(struct ifmp_ring *r) +{ + union ring_state s; + + s.state = r->state; + if (s.pidx_head == s.pidx_tail && s.flags == STALLED) + return (1); + + return (0); +} diff --git a/freebsd/sys/net/mp_ring.h b/freebsd/sys/net/mp_ring.h new file mode 100644 index 00000000..6dea325d --- /dev/null +++ b/freebsd/sys/net/mp_ring.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2014 Chelsio Communications, Inc. + * All rights reserved. + * Written by: Navdeep Parhar <np@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef __NET_MP_RING_H +#define __NET_MP_RING_H + +#ifndef _KERNEL +#error "no user-serviceable parts inside" +#endif + +struct ifmp_ring; +typedef u_int (*mp_ring_drain_t)(struct ifmp_ring *, u_int, u_int); +typedef u_int (*mp_ring_can_drain_t)(struct ifmp_ring *); +typedef void (*mp_ring_serial_t)(struct ifmp_ring *); + +#if defined(__powerpc__) || defined(__mips__) || defined(__i386__) +#define MP_RING_NO_64BIT_ATOMICS +#endif + +struct ifmp_ring { + volatile uint64_t state __aligned(CACHE_LINE_SIZE); + + int size __aligned(CACHE_LINE_SIZE); + void * cookie; + struct malloc_type * mt; + mp_ring_drain_t drain; + mp_ring_can_drain_t can_drain; /* cheap, may be unreliable */ + counter_u64_t enqueues; + counter_u64_t drops; + counter_u64_t starts; + counter_u64_t stalls; + counter_u64_t restarts; /* recovered after stalling */ + counter_u64_t abdications; +#ifdef MP_RING_NO_64BIT_ATOMICS + struct mtx lock; +#endif + void * volatile items[] __aligned(CACHE_LINE_SIZE); +}; + +int ifmp_ring_alloc(struct ifmp_ring **, int, void *, mp_ring_drain_t, + mp_ring_can_drain_t, struct malloc_type *, int); +void ifmp_ring_free(struct ifmp_ring *); +int ifmp_ring_enqueue(struct ifmp_ring *, void **, int, int, int); +void ifmp_ring_check_drainage(struct ifmp_ring *, int); +void ifmp_ring_reset_stats(struct ifmp_ring *); +int ifmp_ring_is_idle(struct ifmp_ring *); +int ifmp_ring_is_stalled(struct ifmp_ring *r); +#endif diff --git a/freebsd/sys/or1k/include/machine/in_cksum.h b/freebsd/sys/or1k/include/machine/in_cksum.h new file mode 100644 index 00000000..d55b838b --- /dev/null +++ b/freebsd/sys/or1k/include/machine/in_cksum.h @@ -0,0 +1,83 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from tahoe: in_cksum.c 1.2 86/01/05 + * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91 + * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp + * from: src/sys/alpha/include/in_cksum.h,v 1.7 2005/03/02 21:33:20 joerg + * $FreeBSD$ + */ + +#ifndef _MACHINE_IN_CKSUM_H_ +#define _MACHINE_IN_CKSUM_H_ 1 + +#include <sys/cdefs.h> + +#define in_cksum(m, len) in_cksum_skip(m, len, 0) + +#if defined(IPVERSION) && (IPVERSION == 4) +/* + * It it useful to have an Internet checksum routine which is inlineable + * and optimized specifically for the task of computing IP header checksums + * in the normal case (where there are no options and the header length is + * therefore always exactly five 32-bit words. + */ +#ifdef __CC_SUPPORTS___INLINE + +static __inline void +in_cksum_update(struct ip *ip) +{ + int __tmpsum; + __tmpsum = (int)ntohs(ip->ip_sum) + 256; + ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); +} + +#else + +#define in_cksum_update(ip) \ + do { \ + int __tmpsum; \ + __tmpsum = (int)ntohs(ip->ip_sum) + 256; \ + ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \ + } while(0) + +#endif +#endif + +#ifdef _KERNEL +#if defined(IPVERSION) && (IPVERSION == 4) +u_int in_cksum_hdr(const struct ip *ip); +#endif +u_short in_addword(u_short sum, u_short b); +u_short in_pseudo(u_int sum, u_int b, u_int c); +u_short in_cksum_skip(struct mbuf *m, int len, int skip); +#endif + +#endif /* _MACHINE_IN_CKSUM_H_ */ diff --git a/freebsd/sys/sys/_domainset.h b/freebsd/sys/sys/_domainset.h index 5685d532..443c68fd 100644 --- a/freebsd/sys/sys/_domainset.h +++ b/freebsd/sys/sys/_domainset.h @@ -43,7 +43,7 @@ #define DOMAINSET_SETSIZE DOMAINSET_MAXSIZE #endif -BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); +__BITSET_DEFINE(_domainset, DOMAINSET_SETSIZE); typedef struct _domainset domainset_t; /* diff --git a/freebsd/sys/sys/callout.h b/freebsd/sys/sys/callout.h index e5e5df85..d7b9965a 100644 --- a/freebsd/sys/sys/callout.h +++ b/freebsd/sys/sys/callout.h @@ -112,9 +112,15 @@ int callout_reset_sbt_on(struct callout *, sbintime_t, sbintime_t, #define callout_reset_sbt_curcpu(c, sbt, pr, fn, arg, flags) \ callout_reset_sbt_on((c), (sbt), (pr), (fn), (arg), PCPU_GET(cpuid),\ (flags)) +#ifndef __rtems__ #define callout_reset_on(c, to_ticks, fn, arg, cpu) \ callout_reset_sbt_on((c), tick_sbt * (to_ticks), 0, (fn), (arg), \ (cpu), C_HARDCLOCK) +#else /* __rtems__ */ +#define callout_reset_on(c, to_ticks, fn, arg, cpu) \ + callout_reset_sbt_on((c), tick_sbt * (to_ticks), 0, (fn), (arg), \ + -1, C_HARDCLOCK) +#endif /* __rtems__ */ #define callout_reset(c, on_tick, fn, arg) \ callout_reset_on((c), (on_tick), (fn), (arg), -1) #define callout_reset_curcpu(c, on_tick, fn, arg) \ diff --git a/freebsd/sys/sys/domainset.h b/freebsd/sys/sys/domainset.h index 5a00347f..e028f3e9 100644 --- a/freebsd/sys/sys/domainset.h +++ b/freebsd/sys/sys/domainset.h @@ -44,34 +44,34 @@ sizeof(__XSTRING(MAXMEMDOM))) -#define DOMAINSET_CLR(n, p) BIT_CLR(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_COPY(f, t) BIT_COPY(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_ISSET(n, p) BIT_ISSET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET(n, p) BIT_SET(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_ZERO(p) BIT_ZERO(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FILL(p) BIT_FILL(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SETOF(n, p) BIT_SETOF(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_EMPTY(p) BIT_EMPTY(DOMAINSET_SETSIZE, p) -#define DOMAINSET_ISFULLSET(p) BIT_ISFULLSET(DOMAINSET_SETSIZE, p) -#define DOMAINSET_SUBSET(p, c) BIT_SUBSET(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OVERLAP(p, c) BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_CMP(p, c) BIT_CMP(DOMAINSET_SETSIZE, p, c) -#define DOMAINSET_OR(d, s) BIT_OR(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_AND(d, s) BIT_AND(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_NAND(d, s) BIT_NAND(DOMAINSET_SETSIZE, d, s) -#define DOMAINSET_CLR_ATOMIC(n, p) BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_SET_ATOMIC(n, p) BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_CLR(n, p) __BIT_CLR(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_COPY(f, t) __BIT_COPY(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_ISSET(n, p) __BIT_ISSET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET(n, p) __BIT_SET(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_ZERO(p) __BIT_ZERO(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FILL(p) __BIT_FILL(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SETOF(n, p) __BIT_SETOF(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_EMPTY(p) __BIT_EMPTY(DOMAINSET_SETSIZE, p) +#define DOMAINSET_ISFULLSET(p) __BIT_ISFULLSET(DOMAINSET_SETSIZE, p) +#define DOMAINSET_SUBSET(p, c) __BIT_SUBSET(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OVERLAP(p, c) __BIT_OVERLAP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_CMP(p, c) __BIT_CMP(DOMAINSET_SETSIZE, p, c) +#define DOMAINSET_OR(d, s) __BIT_OR(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_AND(d, s) __BIT_AND(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_NAND(d, s) __BIT_NAND(DOMAINSET_SETSIZE, d, s) +#define DOMAINSET_CLR_ATOMIC(n, p) __BIT_CLR_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_SET_ATOMIC(n, p) __BIT_SET_ATOMIC(DOMAINSET_SETSIZE, n, p) #define DOMAINSET_SET_ATOMIC_ACQ(n, p) \ - BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_AND_ATOMIC(n, p) BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) -#define DOMAINSET_OR_ATOMIC(d, s) BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) + __BIT_SET_ATOMIC_ACQ(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_AND_ATOMIC(n, p) __BIT_AND_ATOMIC(DOMAINSET_SETSIZE, n, p) +#define DOMAINSET_OR_ATOMIC(d, s) __BIT_OR_ATOMIC(DOMAINSET_SETSIZE, d, s) #define DOMAINSET_COPY_STORE_REL(f, t) \ - BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) -#define DOMAINSET_FFS(p) BIT_FFS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FLS(p) BIT_FLS(DOMAINSET_SETSIZE, p) -#define DOMAINSET_COUNT(p) BIT_COUNT(DOMAINSET_SETSIZE, p) -#define DOMAINSET_FSET BITSET_FSET(_NDOMAINSETWORDS) -#define DOMAINSET_T_INITIALIZER BITSET_T_INITIALIZER + __BIT_COPY_STORE_REL(DOMAINSET_SETSIZE, f, t) +#define DOMAINSET_FFS(p) __BIT_FFS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FLS(p) __BIT_FLS(DOMAINSET_SETSIZE, p) +#define DOMAINSET_COUNT(p) __BIT_COUNT(DOMAINSET_SETSIZE, p) +#define DOMAINSET_FSET __BITSET_FSET(_NDOMAINSETWORDS) +#define DOMAINSET_T_INITIALIZER __BITSET_T_INITIALIZER #define DOMAINSET_POLICY_INVALID 0 #define DOMAINSET_POLICY_ROUNDROBIN 1 diff --git a/rtemsbsd/include/x86/specialreg.h b/freebsd/sys/x86/include/machine/specialreg.h index f528bad5..f528bad5 100644 --- a/rtemsbsd/include/x86/specialreg.h +++ b/freebsd/sys/x86/include/machine/specialreg.h diff --git a/freebsd/sys/x86/include/machine/x86_var.h b/freebsd/sys/x86/include/machine/x86_var.h new file mode 100644 index 00000000..2028d739 --- /dev/null +++ b/freebsd/sys/x86/include/machine/x86_var.h @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1995 Bruce D. Evans. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _X86_X86_VAR_H_ +#define _X86_X86_VAR_H_ + +/* + * Miscellaneous machine-dependent declarations. + */ + +extern long Maxmem; +extern u_int basemem; +extern int busdma_swi_pending; +extern u_int cpu_exthigh; +extern u_int cpu_feature; +extern u_int cpu_feature2; +extern u_int amd_feature; +extern u_int amd_feature2; +extern u_int amd_rascap; +extern u_int amd_pminfo; +extern u_int amd_extended_feature_extensions; +extern u_int via_feature_rng; +extern u_int via_feature_xcrypt; +extern u_int cpu_clflush_line_size; +extern u_int cpu_stdext_feature; +extern u_int cpu_stdext_feature2; +extern u_int cpu_stdext_feature3; +extern uint64_t cpu_ia32_arch_caps; +extern u_int cpu_fxsr; +extern u_int cpu_high; +extern u_int cpu_id; +extern u_int cpu_max_ext_state_size; +extern u_int cpu_mxcsr_mask; +extern u_int cpu_procinfo; +extern u_int cpu_procinfo2; +extern char cpu_vendor[]; +extern u_int cpu_vendor_id; +extern u_int cpu_mon_mwait_flags; +extern u_int cpu_mon_min_size; +extern u_int cpu_mon_max_size; +extern u_int cpu_maxphyaddr; +extern char ctx_switch_xsave[]; +extern u_int hv_base; +extern u_int hv_high; +extern char hv_vendor[]; +extern char kstack[]; +extern char sigcode[]; +extern int szsigcode; +extern int vm_page_dump_size; +extern int workaround_erratum383; +extern int _udatasel; +extern int _ucodesel; +extern int _ucode32sel; +extern int _ufssel; +extern int _ugssel; +extern int use_xsave; +extern uint64_t xsave_mask; +extern u_int max_apic_id; +extern int i386_read_exec; +extern int pti; +extern int hw_ibrs_active; +extern int hw_mds_disable; +extern int hw_ssb_active; + +struct pcb; +struct thread; +struct reg; +struct fpreg; +struct dbreg; +struct dumperinfo; +struct trapframe; + +/* + * The interface type of the interrupt handler entry point cannot be + * expressed in C. Use simplest non-variadic function type as an + * approximation. + */ +typedef void alias_for_inthand_t(void); + +bool acpi_get_fadt_bootflags(uint16_t *flagsp); +void *alloc_fpusave(int flags); +void busdma_swi(void); +vm_paddr_t cpu_getmaxphyaddr(void); +bool cpu_mwait_usable(void); +void cpu_probe_amdc1e(void); +void cpu_setregs(void); +bool disable_wp(void); +void restore_wp(bool old_wp); +void dump_add_page(vm_paddr_t); +void dump_drop_page(vm_paddr_t); +void finishidentcpu(void); +void identify_cpu1(void); +void identify_cpu2(void); +void identify_cpu_fixup_bsp(void); +void identify_hypervisor(void); +void initializecpu(void); +void initializecpucache(void); +bool fix_cpuid(void); +void fillw(int /*u_short*/ pat, void *base, size_t cnt); +int is_physical_memory(vm_paddr_t addr); +int isa_nmi(int cd); +void handle_ibrs_entry(void); +void handle_ibrs_exit(void); +void hw_ibrs_recalculate(void); +void hw_mds_recalculate(void); +void hw_ssb_recalculate(bool all_cpus); +void nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame); +void nmi_call_kdb_smp(u_int type, struct trapframe *frame); +void nmi_handle_intr(u_int type, struct trapframe *frame); +void pagecopy(void *from, void *to); +void printcpuinfo(void); +int pti_get_default(void); +int user_dbreg_trap(register_t dr6); +int minidumpsys(struct dumperinfo *); +struct pcb *get_pcb_td(struct thread *td); + +#endif diff --git a/freebsd/tools/tools/net80211/wlanstats/main.c b/freebsd/tools/tools/net80211/wlanstats/main.c index 3a6fd204..ef2caf56 100644 --- a/freebsd/tools/tools/net80211/wlanstats/main.c +++ b/freebsd/tools/tools/net80211/wlanstats/main.c @@ -43,6 +43,7 @@ #ifdef __rtems__ #define __need_getopt_newlib #include <getopt.h> +#include <string.h> #include <machine/rtems-bsd-program.h> #include <machine/rtems-bsd-commands.h> #endif /* __rtems__ */ @@ -96,6 +97,7 @@ getfmt(const char *tag) return tag; } +#ifndef __rtems__ static int signalled; static void @@ -103,6 +105,7 @@ catchalarm(int signo __unused) { signalled = 1; } +#endif /* __rtems__ */ #if 0 static void @@ -262,6 +265,7 @@ main(int argc, char *argv[]) wf->setstamac(wf, mac); if (argc > 0) { +#ifndef __rtems__ u_long interval = strtoul(argv[0], NULL, 0); int line, omask; @@ -283,24 +287,10 @@ main(int argc, char *argv[]) wf->print_total(wf, stdout); } fflush(stdout); -#ifndef __rtems__ omask = sigblock(sigmask(SIGALRM)); if (!signalled) sigpause(0); sigsetmask(omask); -#else /* __rtems__ */ - { - sigset_t oldmask, desired, empty; - - sigemptyset(&empty); - sigemptyset(&desired); - sigaddset(&desired, SIGALRM); - sigprocmask(SIG_BLOCK, &desired, &oldmask); - while (!signalled) - sigsuspend(&desired); - sigprocmask(SIG_SETMASK, &oldmask, NULL); - } -#endif /* __rtems__ */ signalled = 0; alarm(interval); line++; @@ -346,6 +336,10 @@ main(int argc, char *argv[]) } while (len >= sizeof(struct ieee80211req_sta_info)); } #endif +#else /* __rtems__ */ + (void)mode; + printf("wlanstats: not implemented\n"); +#endif /* __rtems__ */ } else { wf->collect_tot(wf); wf->print_verbose(wf, stdout); diff --git a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h index f88c5834..afb145be 100644 --- a/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h +++ b/freebsd/tools/tools/net80211/wlanstats/rtems-bsd-wlanstats-main-data.h @@ -1,5 +1,3 @@ /* generated by userspace-header-gen.py */ #include <rtems/linkersets.h> #include "rtems-bsd-wlanstats-data.h" -/* main.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_wlanstats, static int signalled); diff --git a/freebsd/usr.bin/netstat/if.c b/freebsd/usr.bin/netstat/if.c index c578629e..ffb639c6 100644 --- a/freebsd/usr.bin/netstat/if.c +++ b/freebsd/usr.bin/netstat/if.c @@ -512,6 +512,7 @@ intpr(void (*pfunc)(char *), int af) freeifmaddrs(ifmap); } +#ifndef __rtems__ struct iftot { u_long ift_ip; /* input packets */ u_long ift_ie; /* input errors */ @@ -575,6 +576,7 @@ catchalarm(int signo __unused) { signalled = true; } +#endif /* __rtems__ */ /* * Print a running summary of interface statistics. @@ -585,6 +587,7 @@ catchalarm(int signo __unused) static void sidewaysintpr(void) { +#ifndef __rtems__ struct iftot ift[2], *new, *old; struct itimerval interval_it; int oldmask, line; @@ -619,26 +622,11 @@ loop: xo_close_list("interface-statistics"); return; } -#ifdef __rtems__ - { - sigset_t oldmask, desired, empty; - - sigemptyset(&empty); - sigemptyset(&desired); - sigaddset(&desired, SIGALRM); - sigprocmask(SIG_BLOCK, &desired, &oldmask); - while (!signalled) - sigsuspend(&desired); - signalled = false; - sigprocmask(SIG_SETMASK, &oldmask, NULL); - } -#else /* __rtems__ */ oldmask = sigblock(sigmask(SIGALRM)); while (!signalled) sigpause(0); signalled = false; sigsetmask(oldmask); -#endif /* __rtems__ */ line++; fill_iftot(new); @@ -681,4 +669,5 @@ loop: goto loop; /* NOTREACHED */ +#endif /* __rtems__ */ } diff --git a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h index 9e12b65e..9a685f33 100644 --- a/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h +++ b/freebsd/usr.bin/netstat/rtems-bsd-netstat-if-data.h @@ -1,5 +1,3 @@ /* generated by userspace-header-gen.py */ #include <rtems/linkersets.h> #include "rtems-bsd-netstat-data.h" -/* if.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_netstat, static sig_atomic_t signalled); diff --git a/freebsd/usr.sbin/ifmcstat/ifmcstat.c b/freebsd/usr.sbin/ifmcstat/ifmcstat.c new file mode 100644 index 00000000..19acaf62 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/ifmcstat.c @@ -0,0 +1,1295 @@ +#include <machine/rtems-bsd-user-space.h> + +#ifdef __rtems__ +#include "rtems-bsd-ifmcstat-namespace.h" +#include "rtems-bsd-ifmcstat-ifmcstat-data.h" +#endif /* __rtems__ */ + +/* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2007-2009 Bruce Simpson. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __rtems__ +#define __need_getopt_newlib +#include <getopt.h> +#include <machine/rtems-bsd-program.h> +#include <machine/rtems-bsd-commands.h> +#include <rtems/libio_.h> +#endif /* __rtems__ */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/tree.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/igmp.h> +#include <netinet/if_ether.h> +#include <netinet/igmp_var.h> + +#ifdef INET6 +#include <netinet/icmp6.h> +#include <netinet6/mld6_var.h> +#endif /* INET6 */ + +#include <arpa/inet.h> +#include <netdb.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <ifaddrs.h> +#include <sysexits.h> +#include <unistd.h> + +#ifdef KVM +/* + * Currently the KVM build is broken. To be fixed it requires uncovering + * large amount of _KERNEL code in include files, and it is also very + * tentative to internal kernel ABI changes. If anyone wishes to restore + * it, please move it out of src/usr.sbin to src/tools/tools. + */ +#include <kvm.h> +#include <nlist.h> +#endif + +/* XXX: This file currently assumes INET support in the base system. */ +#ifndef INET +#define INET +#endif + +extern void printb(const char *, unsigned int, const char *); + +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; +#ifdef INET + struct sockaddr_in sin; +#endif +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif +}; +typedef union sockunion sockunion_t; + +#ifdef __rtems__ +static +#endif /* __rtems__ */ +uint32_t ifindex = 0; +#ifdef __rtems__ +static +#endif /* __rtems__ */ +int af = AF_UNSPEC; +#ifdef WITH_KVM +int Kflag = 0; +#endif +#ifdef __rtems__ +static +#endif /* __rtems__ */ +int vflag = 0; + +#define sa_dl_equal(a1, a2) \ + ((((struct sockaddr_dl *)(a1))->sdl_len == \ + ((struct sockaddr_dl *)(a2))->sdl_len) && \ + (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \ + LLADDR((struct sockaddr_dl *)(a2)), \ + ((struct sockaddr_dl *)(a1))->sdl_alen) == 0)) + +/* + * Most of the code in this utility is to support the use of KVM for + * post-mortem debugging of the multicast code. + */ +#ifdef WITH_KVM + +#ifdef INET +static void if_addrlist(struct ifaddr *); +static struct in_multi * + in_multientry(struct in_multi *); +#endif /* INET */ + +#ifdef INET6 +static void if6_addrlist(struct ifaddr *); +static struct in6_multi * + in6_multientry(struct in6_multi *); +#endif /* INET6 */ + +static void kread(u_long, void *, int); +static void ll_addrlist(struct ifaddr *); + +static int ifmcstat_kvm(const char *kernel, const char *core); + +#define KREAD(addr, buf, type) \ + kread((u_long)addr, (void *)buf, sizeof(type)) + +kvm_t *kvmd; +struct nlist nl[] = { + { "_ifnet", 0, 0, 0, 0, }, + { "", 0, 0, 0, 0, }, +}; +#define N_IFNET 0 + +#endif /* WITH_KVM */ + +static int ifmcstat_getifmaddrs(void); +#ifdef INET +static void in_ifinfo(struct igmp_ifinfo *); +static const char * inm_mode(u_int mode); +#endif +#ifdef INET6 +static void in6_ifinfo(struct mld_ifinfo *); +static const char * inet6_n2a(struct in6_addr *, uint32_t); +#endif +#ifdef __rtems__ +static int main(int argc, char *argv[]); + +RTEMS_LINKER_RWSET(bsd_prog_ifmcstat, char); + +int +rtems_bsd_command_ifmcstat(int argc, char *argv[]) +{ + int exit_code; + void *data_begin; + size_t data_size; + + data_begin = RTEMS_LINKER_SET_BEGIN(bsd_prog_ifmcstat); + data_size = RTEMS_LINKER_SET_SIZE(bsd_prog_ifmcstat); + + rtems_bsd_program_lock(); + exit_code = rtems_bsd_program_call_main_with_data_restore("ifmcstat", + main, argc, argv, data_begin, data_size); + rtems_bsd_program_unlock(); + + return exit_code; +} +#else /* __rtems__ */ +int main(int, char **); +#endif /* __rtems__ */ + +static void +usage() +{ + + fprintf(stderr, + "usage: ifmcstat [-i interface] [-f address family]" + " [-v]" +#ifdef WITH_KVM + " [-K] [-M core] [-N system]" +#endif + "\n"); + exit(EX_USAGE); +} + +static const char * const options = "i:f:vM:N:" +#ifdef WITH_KVM + "K" +#endif + ; + +int +main(int argc, char **argv) +{ + int c, error; +#ifdef WITH_KVM + const char *kernel = NULL; + const char *core = NULL; +#endif + + while ((c = getopt(argc, argv, options)) != -1) { + switch (c) { + case 'i': + if ((ifindex = if_nametoindex(optarg)) == 0) { + fprintf(stderr, "%s: unknown interface\n", + optarg); + exit(EX_NOHOST); + } + break; + + case 'f': +#ifdef INET + if (strcmp(optarg, "inet") == 0) { + af = AF_INET; + break; + } +#endif +#ifdef INET6 + if (strcmp(optarg, "inet6") == 0) { + af = AF_INET6; + break; + } +#endif + if (strcmp(optarg, "link") == 0) { + af = AF_LINK; + break; + } + fprintf(stderr, "%s: unknown address family\n", optarg); + exit(EX_USAGE); + /*NOTREACHED*/ + break; + +#ifdef WITH_KVM + case 'K': + ++Kflag; + break; +#endif + + case 'v': + ++vflag; + break; + +#ifdef WITH_KVM + case 'M': + core = strdup(optarg); + break; + + case 'N': + kernel = strdup(optarg); + break; +#endif + + default: + usage(); + break; + /*NOTREACHED*/ + } + } + + if (af == AF_LINK && vflag) + usage(); + +#ifdef WITH_KVM + if (Kflag) + error = ifmcstat_kvm(kernel, core); + /* + * If KVM failed, and user did not explicitly specify a core file, + * or force KVM backend to be disabled, try the sysctl backend. + */ + if (!Kflag || (error != 0 && (core == NULL && kernel == NULL))) +#endif + error = ifmcstat_getifmaddrs(); + if (error != 0) + exit(EX_OSERR); + + exit(EX_OK); + /*NOTREACHED*/ +} + +#ifdef INET + +static void +in_ifinfo(struct igmp_ifinfo *igi) +{ + + printf("\t"); + switch (igi->igi_version) { + case IGMP_VERSION_1: + case IGMP_VERSION_2: + case IGMP_VERSION_3: + printf("igmpv%d", igi->igi_version); + break; + default: + printf("igmpv?(%d)", igi->igi_version); + break; + } + if (igi->igi_flags) + printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK"); + if (igi->igi_version == IGMP_VERSION_3) { + printf(" rv %u qi %u qri %u uri %u", + igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u v3timer %u", + igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer); + } + printf("\n"); +} + +static const char * const inm_modes[] = { + "undefined", + "include", + "exclude", +}; + +static const char * +inm_mode(u_int mode) +{ + + if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) + return (inm_modes[mode]); + return (NULL); +} + +#endif /* INET */ + +#ifdef WITH_KVM + +static int +ifmcstat_kvm(const char *kernel, const char *core) +{ + char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ]; + struct ifnet *ifp, *nifp, ifnet; + + if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) == + NULL) { + perror("kvm_openfiles"); + return (-1); + } + if (kvm_nlist(kvmd, nl) < 0) { + perror("kvm_nlist"); + return (-1); + } + if (nl[N_IFNET].n_value == 0) { + printf("symbol %s not found\n", nl[N_IFNET].n_name); + return (-1); + } + KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *); + while (ifp) { + KREAD(ifp, &ifnet, struct ifnet); + nifp = ifnet.if_link.tqe_next; + if (ifindex && ifindex != ifnet.if_index) + goto next; + + printf("%s:\n", if_indextoname(ifnet.if_index, ifname)); +#ifdef INET + if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); +#endif +#ifdef INET6 + if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); +#endif + if (vflag) + ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead)); + next: + ifp = nifp; + } + + return (0); +} + +static void +kread(u_long addr, void *buf, int len) +{ + + if (kvm_read(kvmd, addr, buf, len) != len) { + perror("kvm_read"); + exit(EX_OSERR); + } +} + +static void +ll_addrlist(struct ifaddr *ifap) +{ + char addrbuf[NI_MAXHOST]; + struct ifaddr ifa; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct ifaddr *ifap0; + + if (af && af != AF_LINK) + return; + + ifap0 = ifap; + while (ifap) { + KREAD(ifap, &ifa, struct ifaddr); + if (ifa.ifa_addr == NULL) + goto nextifap; + KREAD(ifa.ifa_addr, &sa, struct sockaddr); + if (sa.sa_family != PF_LINK) + goto nextifap; + KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl); + if (sdl.sdl_alen == 0) + goto nextifap; + addrbuf[0] = '\0'; + getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len, + addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + printf("\tlink %s\n", addrbuf); + nextifap: + ifap = ifa.ifa_link.tqe_next; + } + if (ifap0) { + struct ifnet ifnet; + struct ifmultiaddr ifm, *ifmp = 0; + + KREAD(ifap0, &ifa, struct ifaddr); + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + if (TAILQ_FIRST(&ifnet.if_multiaddrs)) + ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); + while (ifmp) { + KREAD(ifmp, &ifm, struct ifmultiaddr); + if (ifm.ifma_addr == NULL) + goto nextmulti; + KREAD(ifm.ifma_addr, &sa, struct sockaddr); + if (sa.sa_family != AF_LINK) + goto nextmulti; + KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl); + addrbuf[0] = '\0'; + getnameinfo((struct sockaddr *)&sdl, + sdl.sdl_len, addrbuf, sizeof(addrbuf), + NULL, 0, NI_NUMERICHOST); + printf("\t\tgroup %s refcnt %d\n", + addrbuf, ifm.ifma_refcount); + nextmulti: + ifmp = TAILQ_NEXT(&ifm, ifma_link); + } + } +} + +#ifdef INET6 + +static void +if6_addrlist(struct ifaddr *ifap) +{ + struct ifnet ifnet; + struct ifaddr ifa; + struct sockaddr sa; + struct in6_ifaddr if6a; + struct ifaddr *ifap0; + + if (af && af != AF_INET6) + return; + ifap0 = ifap; + while (ifap) { + KREAD(ifap, &ifa, struct ifaddr); + if (ifa.ifa_addr == NULL) + goto nextifap; + KREAD(ifa.ifa_addr, &sa, struct sockaddr); + if (sa.sa_family != PF_INET6) + goto nextifap; + KREAD(ifap, &if6a, struct in6_ifaddr); + printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr, + if6a.ia_addr.sin6_scope_id)); + /* + * Print per-link MLD information, if available. + */ + if (ifa.ifa_ifp != NULL) { + struct in6_ifextra ie; + struct mld_ifinfo mli; + + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + KREAD(ifnet.if_afdata[AF_INET6], &ie, + struct in6_ifextra); + if (ie.mld_ifinfo != NULL) { + KREAD(ie.mld_ifinfo, &mli, struct mld_ifinfo); + in6_ifinfo(&mli); + } + } + nextifap: + ifap = ifa.ifa_link.tqe_next; + } + if (ifap0) { + struct ifnet ifnet; + struct ifmultiaddr ifm, *ifmp = 0; + struct sockaddr_dl sdl; + + KREAD(ifap0, &ifa, struct ifaddr); + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + if (TAILQ_FIRST(&ifnet.if_multiaddrs)) + ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); + while (ifmp) { + KREAD(ifmp, &ifm, struct ifmultiaddr); + if (ifm.ifma_addr == NULL) + goto nextmulti; + KREAD(ifm.ifma_addr, &sa, struct sockaddr); + if (sa.sa_family != AF_INET6) + goto nextmulti; + (void)in6_multientry((struct in6_multi *) + ifm.ifma_protospec); + if (ifm.ifma_lladdr == 0) + goto nextmulti; + KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); + printf("\t\t\tmcast-macaddr %s refcnt %d\n", + ether_ntoa((struct ether_addr *)LLADDR(&sdl)), + ifm.ifma_refcount); + nextmulti: + ifmp = TAILQ_NEXT(&ifm, ifma_link); + } + } +} + +static struct in6_multi * +in6_multientry(struct in6_multi *mc) +{ + struct in6_multi multi; + + KREAD(mc, &multi, struct in6_multi); + printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr, 0)); + printf(" refcnt %u\n", multi.in6m_refcount); + + return (multi.in6m_entry.le_next); +} + +#endif /* INET6 */ + +#ifdef INET + +static void +if_addrlist(struct ifaddr *ifap) +{ + struct ifaddr ifa; + struct ifnet ifnet; + struct sockaddr sa; + struct in_ifaddr ia; + struct ifaddr *ifap0; + + if (af && af != AF_INET) + return; + ifap0 = ifap; + while (ifap) { + KREAD(ifap, &ifa, struct ifaddr); + if (ifa.ifa_addr == NULL) + goto nextifap; + KREAD(ifa.ifa_addr, &sa, struct sockaddr); + if (sa.sa_family != PF_INET) + goto nextifap; + KREAD(ifap, &ia, struct in_ifaddr); + printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr)); + /* + * Print per-link IGMP information, if available. + */ + if (ifa.ifa_ifp != NULL) { + struct in_ifinfo ii; + struct igmp_ifinfo igi; + + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo); + if (ii.ii_igmp != NULL) { + KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo); + in_ifinfo(&igi); + } + } + nextifap: + ifap = ifa.ifa_link.tqe_next; + } + if (ifap0) { + struct ifmultiaddr ifm, *ifmp = 0; + struct sockaddr_dl sdl; + + KREAD(ifap0, &ifa, struct ifaddr); + KREAD(ifa.ifa_ifp, &ifnet, struct ifnet); + if (TAILQ_FIRST(&ifnet.if_multiaddrs)) + ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs); + while (ifmp) { + KREAD(ifmp, &ifm, struct ifmultiaddr); + if (ifm.ifma_addr == NULL) + goto nextmulti; + KREAD(ifm.ifma_addr, &sa, struct sockaddr); + if (sa.sa_family != AF_INET) + goto nextmulti; + (void)in_multientry((struct in_multi *) + ifm.ifma_protospec); + if (ifm.ifma_lladdr == 0) + goto nextmulti; + KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl); + printf("\t\t\tmcast-macaddr %s refcnt %d\n", + ether_ntoa((struct ether_addr *)LLADDR(&sdl)), + ifm.ifma_refcount); + nextmulti: + ifmp = TAILQ_NEXT(&ifm, ifma_link); + } + } +} + +static const char *inm_states[] = { + "not-member", + "silent", + "idle", + "lazy", + "sleeping", + "awakening", + "query-pending", + "sg-query-pending", + "leaving" +}; + +static const char * +inm_state(u_int state) +{ + + if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) + return (inm_states[state]); + return (NULL); +} + +#if 0 +static struct ip_msource * +ims_min_kvm(struct in_multi *pinm) +{ + struct ip_msource ims0; + struct ip_msource *tmp, *parent; + + parent = NULL; + tmp = RB_ROOT(&pinm->inm_srcs); + while (tmp) { + parent = tmp; + KREAD(tmp, &ims0, struct ip_msource); + tmp = RB_LEFT(&ims0, ims_link); + } + return (parent); /* kva */ +} + +/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */ +static struct ip_msource * +ims_next_kvm(struct ip_msource *ims) +{ + struct ip_msource ims0, ims1; + struct ip_msource *tmp; + + KREAD(ims, &ims0, struct ip_msource); + if (RB_RIGHT(&ims0, ims_link)) { + ims = RB_RIGHT(&ims0, ims_link); + KREAD(ims, &ims1, struct ip_msource); + while ((tmp = RB_LEFT(&ims1, ims_link))) { + KREAD(tmp, &ims0, struct ip_msource); + ims = RB_LEFT(&ims0, ims_link); + } + } else { + tmp = RB_PARENT(&ims0, ims_link); + if (tmp) { + KREAD(tmp, &ims1, struct ip_msource); + if (ims == RB_LEFT(&ims1, ims_link)) + ims = tmp; + } else { + while ((tmp = RB_PARENT(&ims0, ims_link))) { + KREAD(tmp, &ims1, struct ip_msource); + if (ims == RB_RIGHT(&ims1, ims_link)) { + ims = tmp; + KREAD(ims, &ims0, struct ip_msource); + } else + break; + } + ims = RB_PARENT(&ims0, ims_link); + } + } + return (ims); /* kva */ +} + +static void +inm_print_sources_kvm(struct in_multi *pinm) +{ + struct ip_msource ims0; + struct ip_msource *ims; + struct in_addr src; + int cnt; + uint8_t fmode; + + cnt = 0; + fmode = pinm->inm_st[1].iss_fmode; + if (fmode == MCAST_UNDEFINED) + return; + for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) { + if (cnt == 0) + printf(" srcs "); + KREAD(ims, &ims0, struct ip_msource); + /* Only print sources in-mode at t1. */ + if (fmode != ims_get_mode(pinm, ims, 1)) + continue; + src.s_addr = htonl(ims0.ims_haddr); + printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src)); + } +} +#endif + +static struct in_multi * +in_multientry(struct in_multi *pinm) +{ + struct in_multi inm; + const char *state, *mode; + + KREAD(pinm, &inm, struct in_multi); + printf("\t\tgroup %s", inet_ntoa(inm.inm_addr)); + printf(" refcnt %u", inm.inm_refcount); + + state = inm_state(inm.inm_state); + if (state) + printf(" state %s", state); + else + printf(" state (%d)", inm.inm_state); + + mode = inm_mode(inm.inm_st[1].iss_fmode); + if (mode) + printf(" mode %s", mode); + else + printf(" mode (%d)", inm.inm_st[1].iss_fmode); + + if (vflag >= 2) { + printf(" asm %u ex %u in %u rec %u", + (u_int)inm.inm_st[1].iss_asm, + (u_int)inm.inm_st[1].iss_ex, + (u_int)inm.inm_st[1].iss_in, + (u_int)inm.inm_st[1].iss_rec); + } + +#if 0 + /* Buggy. */ + if (vflag) + inm_print_sources_kvm(&inm); +#endif + + printf("\n"); + return (NULL); +} + +#endif /* INET */ + +#endif /* WITH_KVM */ + +#ifdef INET6 + +static void +in6_ifinfo(struct mld_ifinfo *mli) +{ + + printf("\t"); + switch (mli->mli_version) { + case MLD_VERSION_1: + case MLD_VERSION_2: + printf("mldv%d", mli->mli_version); + break; + default: + printf("mldv?(%d)", mli->mli_version); + break; + } + if (mli->mli_flags) + printb(" flags", mli->mli_flags, "\020\1SILENT\2USEALLOW"); + if (mli->mli_version == MLD_VERSION_2) { + printf(" rv %u qi %u qri %u uri %u", + mli->mli_rv, mli->mli_qi, mli->mli_qri, mli->mli_uri); + } + if (vflag >= 2) { + printf(" v1timer %u v2timer %u", mli->mli_v1_timer, + mli->mli_v2_timer); + } + printf("\n"); +} + +static const char * +inet6_n2a(struct in6_addr *p, uint32_t scope_id) +{ + static char buf[NI_MAXHOST]; + struct sockaddr_in6 sin6; + const int niflags = NI_NUMERICHOST; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *p; + sin6.sin6_scope_id = scope_id; + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + buf, sizeof(buf), NULL, 0, niflags) == 0) { + return (buf); + } else { + return ("(invalid)"); + } +} +#endif /* INET6 */ + +#ifdef INET +/* + * Retrieve per-group source filter mode and lists via sysctl. + */ +static void +inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina) +{ +#define MAX_SYSCTL_TRY 5 + int mib[7]; + int ntry = 0; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = nitems(mib); + if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + mib[6] = gina.s_addr; /* 32 bits wide */ + mibsize = nitems(mib); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet.ip.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in_addr); + pina = (struct in_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), + inet_ntoa(*pina++)); + len -= sizeof(struct in_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet.ip.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} + +#endif /* INET */ + +#ifdef INET6 +/* + * Retrieve MLD per-group source filter mode and lists via sysctl. + * + * Note: The 128-bit IPv6 group address needs to be segmented into + * 32-bit pieces for marshaling to sysctl. So the MIB name ends + * up looking like this: + * a.b.c.d.e.ifindex.g[0].g[1].g[2].g[3] + * Assumes that pgroup originated from the kernel, so its components + * are already in network-byte order. + */ +static void +in6m_print_sources_sysctl(uint32_t ifindex, struct in6_addr *pgroup) +{ +#define MAX_SYSCTL_TRY 5 + char addrbuf[INET6_ADDRSTRLEN]; + int mib[10]; + int ntry = 0; + int *pi; + size_t mibsize; + size_t len; + size_t needed; + size_t cnt; + int i; + char *buf; + struct in6_addr *pina; + uint32_t *p; + uint32_t fmode; + const char *modestr; + + mibsize = nitems(mib); + if (sysctlnametomib("net.inet6.ip6.mcast.filters", mib, + &mibsize) == -1) { + perror("sysctlnametomib"); + return; + } + + needed = 0; + mib[5] = ifindex; + pi = (int *)pgroup; + for (i = 0; i < 4; i++) + mib[6 + i] = *pi++; + + mibsize = nitems(mib); + do { + if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) { + perror("sysctl net.inet6.ip6.mcast.filters"); + return; + } + if ((buf = malloc(needed)) == NULL) { + perror("malloc"); + return; + } + if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + perror("sysctl"); + goto out_free; + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + len = needed; + if (len < sizeof(uint32_t)) { + perror("sysctl"); + goto out_free; + } + + p = (uint32_t *)buf; + fmode = *p++; + len -= sizeof(uint32_t); + + modestr = inm_mode(fmode); + if (modestr) + printf(" mode %s", modestr); + else + printf(" mode (%u)", fmode); + + if (vflag == 0) + goto out_free; + + cnt = len / sizeof(struct in6_addr); + pina = (struct in6_addr *)p; + + for (i = 0; i < cnt; i++) { + if (i == 0) + printf(" srcs "); + inet_ntop(AF_INET6, (const char *)pina++, addrbuf, + INET6_ADDRSTRLEN); + fprintf(stdout, "%s%s", (i == 0 ? "" : ","), addrbuf); + len -= sizeof(struct in6_addr); + } + if (len > 0) { + fprintf(stderr, "warning: %u trailing bytes from %s\n", + (unsigned int)len, "net.inet6.ip6.mcast.filters"); + } + +out_free: + free(buf); +#undef MAX_SYSCTL_TRY +} +#endif /* INET6 */ + +static int +ifmcstat_getifmaddrs(void) +{ + char thisifname[IFNAMSIZ]; + char addrbuf[NI_MAXHOST]; + struct ifaddrs *ifap, *ifa; + struct ifmaddrs *ifmap, *ifma; + sockunion_t lastifasa; + sockunion_t *psa, *pgsa, *pllsa, *pifasa; + char *pcolon; + char *pafname; + uint32_t lastifindex, thisifindex; + int error; + + error = 0; + ifap = NULL; + ifmap = NULL; + lastifindex = 0; + thisifindex = 0; + lastifasa.ss.ss_family = AF_UNSPEC; + + if (getifaddrs(&ifap) != 0) { + warn("getifmaddrs"); + return (-1); + } + + if (getifmaddrs(&ifmap) != 0) { + warn("getifmaddrs"); + error = -1; + goto out; + } + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + error = 0; + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + + psa = (sockunion_t *)ifma->ifma_name; + if (psa->sa.sa_family != AF_LINK) { + fprintf(stderr, + "WARNING: Kernel returned invalid data.\n"); + error = -1; + break; + } + + /* Filter on interface name. */ + thisifindex = psa->sdl.sdl_index; + if (ifindex != 0 && thisifindex != ifindex) + continue; + + /* Filter on address family. */ + pgsa = (sockunion_t *)ifma->ifma_addr; + if (af != 0 && pgsa->sa.sa_family != af) + continue; + + strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ); + pcolon = strchr(thisifname, ':'); + if (pcolon) + *pcolon = '\0'; + + /* Only print the banner for the first ifmaddrs entry. */ + if (lastifindex == 0 || lastifindex != thisifindex) { + lastifindex = thisifindex; + fprintf(stdout, "%s:\n", thisifname); + } + + /* + * Currently, multicast joins only take place on the + * primary IPv4 address, and only on the link-local IPv6 + * address, as per IGMPv2/3 and MLDv1/2 semantics. + * Therefore, we only look up the primary address on + * the first pass. + */ + pifasa = NULL; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if ((strcmp(ifa->ifa_name, thisifname) != 0) || + (ifa->ifa_addr == NULL) || + (ifa->ifa_addr->sa_family != pgsa->sa.sa_family)) + continue; + /* + * For AF_INET6 only the link-local address should + * be returned. If built without IPv6 support, + * skip this address entirely. + */ + pifasa = (sockunion_t *)ifa->ifa_addr; + if (pifasa->sa.sa_family == AF_INET6 +#ifdef INET6 + && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr) +#endif + ) { + pifasa = NULL; + continue; + } + break; + } + if (pifasa == NULL) + continue; /* primary address not found */ + + if (!vflag && pifasa->sa.sa_family == AF_LINK) + continue; + + /* Parse and print primary address, if not already printed. */ + if (lastifasa.ss.ss_family == AF_UNSPEC || + ((lastifasa.ss.ss_family == AF_LINK && + !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) || + !sa_equal(&lastifasa.sa, &pifasa->sa))) { + + switch (pifasa->sa.sa_family) { + case AF_INET: + pafname = "inet"; + break; + case AF_INET6: + pafname = "inet6"; + break; + case AF_LINK: + pafname = "link"; + break; + default: + pafname = "unknown"; + break; + } + + switch (pifasa->sa.sa_family) { + case AF_INET6: +#ifdef INET6 + { + const char *p = + inet6_n2a(&pifasa->sin6.sin6_addr, + pifasa->sin6.sin6_scope_id); + strlcpy(addrbuf, p, sizeof(addrbuf)); + break; + } +#else + /* FALLTHROUGH */ +#endif + case AF_INET: + case AF_LINK: + error = getnameinfo(&pifasa->sa, + pifasa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST); + if (error) + perror("getnameinfo"); + break; + default: + addrbuf[0] = '\0'; + break; + } + + fprintf(stdout, "\t%s %s", pafname, addrbuf); +#ifdef INET6 + if (pifasa->sa.sa_family == AF_INET6 && + pifasa->sin6.sin6_scope_id) + fprintf(stdout, " scopeid 0x%x", + pifasa->sin6.sin6_scope_id); +#endif + fprintf(stdout, "\n"); +#ifdef INET + /* + * Print per-link IGMP information, if available. + */ + if (pifasa->sa.sa_family == AF_INET) { + struct igmp_ifinfo igi; + size_t mibsize, len; + int mib[5]; + + mibsize = nitems(mib); + if (sysctlnametomib("net.inet.igmp.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct igmp_ifinfo); + if (sysctl(mib, mibsize + 1, &igi, &len, NULL, + 0) == -1) { + perror("sysctl net.inet.igmp.ifinfo"); + goto next_ifnet; + } + in_ifinfo(&igi); + } +#endif /* INET */ +#ifdef INET6 + /* + * Print per-link MLD information, if available. + */ + if (pifasa->sa.sa_family == AF_INET6) { + struct mld_ifinfo mli; + size_t mibsize, len; + int mib[5]; + + mibsize = nitems(mib); + if (sysctlnametomib("net.inet6.mld.ifinfo", + mib, &mibsize) == -1) { + perror("sysctlnametomib"); + goto next_ifnet; + } + mib[mibsize] = thisifindex; + len = sizeof(struct mld_ifinfo); + if (sysctl(mib, mibsize + 1, &mli, &len, NULL, + 0) == -1) { + perror("sysctl net.inet6.mld.ifinfo"); + goto next_ifnet; + } + in6_ifinfo(&mli); + } +#endif /* INET6 */ +#if defined(INET) || defined(INET6) +next_ifnet: +#endif + lastifasa = *pifasa; + } + + /* Print this group address. */ +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + const char *p = inet6_n2a(&pgsa->sin6.sin6_addr, + pgsa->sin6.sin6_scope_id); + strlcpy(addrbuf, p, sizeof(addrbuf)); + } else +#endif + { + error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + if (error) + perror("getnameinfo"); + } + + fprintf(stdout, "\t\tgroup %s", addrbuf); +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6 && + pgsa->sin6.sin6_scope_id) + fprintf(stdout, " scopeid 0x%x", + pgsa->sin6.sin6_scope_id); +#endif +#ifdef INET + if (pgsa->sa.sa_family == AF_INET) { + inm_print_sources_sysctl(thisifindex, + pgsa->sin.sin_addr); + } +#endif +#ifdef INET6 + if (pgsa->sa.sa_family == AF_INET6) { + in6m_print_sources_sysctl(thisifindex, + &pgsa->sin6.sin6_addr); + } +#endif + fprintf(stdout, "\n"); + + /* Link-layer mapping, if present. */ + pllsa = (sockunion_t *)ifma->ifma_lladdr; + if (pllsa != NULL) { + error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf); + } + } +out: + if (ifmap != NULL) + freeifmaddrs(ifmap); + if (ifap != NULL) + freeifaddrs(ifap); + + return (error); +} diff --git a/freebsd/usr.sbin/ifmcstat/printb.c b/freebsd/usr.sbin/ifmcstat/printb.c new file mode 100644 index 00000000..2c979951 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/printb.c @@ -0,0 +1,75 @@ +#include <machine/rtems-bsd-user-space.h> + +#ifdef __rtems__ +#include "rtems-bsd-ifmcstat-namespace.h" +#include "rtems-bsd-ifmcstat-printb-data.h" +#endif /* __rtems__ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <stdio.h> + +/* + * Print a value a la the %b format of the kernel's printf + */ +void +printb(const char *s, unsigned int v, const char *bits) +{ + int i, any = 0; + char c; + + assert(bits != NULL); + if (*bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h new file mode 100644 index 00000000..e94df9c0 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-data.h @@ -0,0 +1,4 @@ +/* generated by userspace-header-gen.py */ +#include <rtems/linkersets.h> +/* ifmcstat.c */ +/* printb.c */ diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h new file mode 100644 index 00000000..39287947 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-ifmcstat-data.h @@ -0,0 +1,7 @@ +/* generated by userspace-header-gen.py */ +#include <rtems/linkersets.h> +#include "rtems-bsd-ifmcstat-data.h" +/* ifmcstat.c */ +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static int af); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static int vflag); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_ifmcstat, static uint32_t ifindex); diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h new file mode 100644 index 00000000..78414818 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-namespace.h @@ -0,0 +1,5 @@ +/* generated by userspace-header-gen.py */ +/* ifmcstat.c */ +#define main _bsd_ifmcstat_main +/* printb.c */ +#define printb _bsd_ifmcstat_printb diff --git a/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h new file mode 100644 index 00000000..3f74bd60 --- /dev/null +++ b/freebsd/usr.sbin/ifmcstat/rtems-bsd-ifmcstat-printb-data.h @@ -0,0 +1,4 @@ +/* generated by userspace-header-gen.py */ +#include <rtems/linkersets.h> +#include "rtems-bsd-ifmcstat-data.h" +/* printb.c */ diff --git a/ipsec-tools/src/libipsec/pfkey.c b/ipsec-tools/src/libipsec/pfkey.c index a621be12..cc6ad816 100644 --- a/ipsec-tools/src/libipsec/pfkey.c +++ b/ipsec-tools/src/libipsec/pfkey.c @@ -1,5 +1,12 @@ #include <machine/rtems-bsd-user-space.h> +#ifdef __rtems__ +/* Only need socket from rtems-bsd-program wrappers! */ +int +rtems_bsd_program_socket(int domain, int type, int protocol); +#define socket(domain, type, protocol) \ + rtems_bsd_program_socket(domain, type, protocol) +#endif /* __rtems__ */ /* $NetBSD: pfkey.c,v 1.21.2.1 2011/11/14 13:25:06 tteras Exp $ */ /* $KAME: pfkey.c,v 1.47 2003/10/02 19:52:12 itojun Exp $ */ @@ -1829,8 +1836,18 @@ pfkey_open(void) (void)setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsiz_wanted, sizeof(bufsiz_wanted)); +#ifndef __rtems__ /* Try to have have at least 2MB. If we have more, do not lower it. */ bufsiz_wanted = 2 * 1024 * 1024; +#else /* __rtems__ */ + /* + * The bufsize_wanted has an influence on the maximum number of SPDs. We + * don't really need that much of them on an embedded system. If some + * application really needs it, this can be overwritten with the + * pfkey_buffer option in the config file. + */ + bufsiz_wanted = 128 * 1024; +#endif /* __rtems__ */ len = sizeof(bufsiz_current); ret = getsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsiz_current, &len); diff --git a/ipsec-tools/src/racoon/privsep.c b/ipsec-tools/src/racoon/privsep.c index 8efdae84..914d8a44 100644 --- a/ipsec-tools/src/racoon/privsep.c +++ b/ipsec-tools/src/racoon/privsep.c @@ -80,7 +80,9 @@ static int privsep_sock[2] = { -1, -1 }; static int privsep_recv(int, struct privsep_com_msg **, size_t *); static int privsep_send(int, struct privsep_com_msg *, size_t); static int safety_check(struct privsep_com_msg *, int i); +#ifndef __rtems__ static int port_check(int); +#endif /* __rtems__ */ static int unsafe_env(char *const *); static int unknown_name(int); static int unsafe_path(char *, int); @@ -321,7 +323,6 @@ privsep_init(void) #if defined(__NetBSD__) || defined(__FreeBSD__) setproctitle("[priv]"); #endif -#endif /* __rtems__ */ /* * Don't catch any signal @@ -334,13 +335,16 @@ privsep_init(void) signal(SIGUSR1, SIG_DFL); signal(SIGUSR2, SIG_DFL); signal(SIGCHLD, SIG_DFL); +#endif /* __rtems__ */ while (1) { size_t len; struct privsep_com_msg *combuf; struct privsep_com_msg *reply; char *data; +#ifndef __rtems__ size_t *buflen; +#endif /* __rtems__ */ size_t totallen; char *bufs[PRIVSEP_NBUF_MAX]; int i; @@ -1067,7 +1071,9 @@ privsep_getpsk(str, keylen) vchar_t *psk; struct privsep_com_msg *msg; size_t len; +#ifndef __rtems__ int *keylenp; +#endif /* __rtems__ */ char *data; if (geteuid() == 0) @@ -1129,7 +1135,11 @@ privsep_socket(domain, type, protocol) size_t len; char *data; struct socket_args socket_args; +#ifndef __rtems__ int s, saved_errno = 0; +#else /* __rtems__ */ + int s; +#endif /* __rtems__ */ if (geteuid() == 0) return socket(domain, type, protocol); diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h index 7f1e9247..6867269e 100644 --- a/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h +++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-data.h @@ -56,7 +56,7 @@ RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern struct dhgroup dh_modp8192); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern char *pname); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int f_foreground); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern int print_location); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern u_int32_t loglevel); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, extern __uint32_t loglevel); /* policy.c */ /* privsep.c */ /* proposal.c */ diff --git a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h index b869a151..51b67d2e 100644 --- a/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h +++ b/ipsec-tools/src/racoon/rtems-bsd-racoon-session-data.h @@ -2,11 +2,8 @@ #include <rtems/linkersets.h> #include "rtems-bsd-racoon-data.h" /* session.c */ -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set active_mask); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set preset_mask); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_active_mask); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static fd_set *allocated_preset_mask); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int nfds); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static int signals[]); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static sig_atomic_t volatile volatile sigreq[]); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor fd_monitors[]); +RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor *allocated_fd_monitors); RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct fd_monitor_list fd_monitor_tree[]); -RTEMS_LINKER_RWSET_CONTENT(bsd_prog_racoon, static struct sched scflushsa); diff --git a/ipsec-tools/src/racoon/session.c b/ipsec-tools/src/racoon/session.c index 65124c15..bd2bd316 100644 --- a/ipsec-tools/src/racoon/session.c +++ b/ipsec-tools/src/racoon/session.c @@ -65,6 +65,10 @@ #include <sys/stat.h> #include <paths.h> #include <err.h> +#ifdef __rtems__ +#include <sys/param.h> +#include <rtems/libio_.h> +#endif /* __rtems__ */ #include <netinet/in.h> #include <resolv.h> @@ -115,6 +119,7 @@ struct fd_monitor { #define NUM_PRIORITIES 2 +#ifndef __rtems__ static void close_session __P((void)); static void initfds __P((void)); static void init_signal __P((void)); @@ -122,19 +127,34 @@ static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int)))); static void check_sigreq __P((void)); static void check_flushsa __P((void)); static int close_sockets __P((void)); +#endif /* __rtems__ */ +#ifndef __rtems__ static fd_set preset_mask, active_mask; static struct fd_monitor fd_monitors[FD_SETSIZE]; +#else /* __rtems__ */ +static fd_set *allocated_preset_mask, *allocated_active_mask; +static struct fd_monitor *allocated_fd_monitors; +#define preset_mask (*allocated_preset_mask) +#define active_mask (*allocated_active_mask) +#define fd_monitors (allocated_fd_monitors) +#endif /* __rtems__ */ static TAILQ_HEAD(fd_monitor_list, fd_monitor) fd_monitor_tree[NUM_PRIORITIES]; static int nfds = 0; +#ifndef __rtems__ static volatile sig_atomic_t sigreq[NSIG + 1]; static struct sched scflushsa = SCHED_INITIALIZER(); +#endif /* __rtems__ */ void monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority) { +#ifndef __rtems__ if (fd < 0 || fd >= FD_SETSIZE) { +#else /* __rtems__ */ + if (fd < 0 || fd >= rtems_libio_number_iops) { +#endif /* __rtems__ */ plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun"); exit(1); } @@ -158,7 +178,11 @@ monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority) void unmonitor_fd(int fd) { +#ifndef __rtems__ if (fd < 0 || fd >= FD_SETSIZE) { +#else /* __rtems__ */ + if (fd < 0 || fd >= rtems_libio_number_iops) { +#endif /* __rtems__ */ plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun"); exit(1); } @@ -179,21 +203,42 @@ session(void) { struct timeval *timeout; int error; +#ifndef __rtems__ char pid_file[MAXPATHLEN]; FILE *fp; pid_t racoon_pid = 0; +#endif /* __rtems__ */ int i, count; struct fd_monitor *fdm; nfds = 0; +#ifndef __rtems__ FD_ZERO(&preset_mask); +#else /* __rtems__ */ + size_t allocated_mask_size = sizeof(fd_set) * + howmany(rtems_libio_number_iops, sizeof(fd_set) * 8); + allocated_preset_mask = calloc(sizeof(fd_set), + howmany(rtems_libio_number_iops, sizeof(fd_set) * 8)); + if (allocated_preset_mask == NULL) + errx(1, "failed to allocate preset_mask"); + allocated_active_mask = calloc(sizeof(fd_set), + howmany(rtems_libio_number_iops, sizeof(fd_set) * 8)); + if (allocated_active_mask == NULL) + errx(1, "failed to allocate active_mask"); + allocated_fd_monitors = calloc( + rtems_libio_number_iops, sizeof(struct fd_monitor)); + if (allocated_fd_monitors == NULL) + errx(1, "failed to allocate fd_monitors"); +#endif /* __rtems__ */ for (i = 0; i < NUM_PRIORITIES; i++) TAILQ_INIT(&fd_monitor_tree[i]); /* initialize schedular */ sched_init(); +#ifndef __rtems__ init_signal(); +#endif /* __rtems__ */ if (pfkey_init() < 0) errx(1, "failed to initialize pfkey socket"); @@ -290,24 +335,31 @@ session(void) racoon_pid = getpid(); fprintf(fp, "%ld\n", (long)racoon_pid); fclose(fp); -#endif /* __rtems__ */ for (i = 0; i <= NSIG; i++) sigreq[i] = 0; +#endif /* __rtems__ */ while (1) { +#ifndef __rtems__ /* * asynchronous requests via signal. * make sure to reset sigreq to 0. */ check_sigreq(); +#endif /* __rtems__ */ /* scheduling */ timeout = schedular(); /* schedular can change select() mask, so we reset * the working copy here */ +#ifndef __rtems__ active_mask = preset_mask; +#else /* __rtems__ */ + memcpy(allocated_active_mask, allocated_preset_mask, + allocated_mask_size); +#endif /* __rtems__ */ error = select(nfds + 1, &active_mask, NULL, NULL, timeout); if (error < 0) { @@ -344,6 +396,7 @@ session(void) } } +#ifndef __rtems__ /* clear all status and exit program. */ static void close_session() @@ -525,11 +578,7 @@ set_signal(sig, func) memset((caddr_t)&sa, 0, sizeof(sa)); sa.sa_handler = func; -#ifndef __rtems__ sa.sa_flags = SA_RESTART; -#else /* __rtems__ */ - sa.sa_flags = 0; -#endif /* __rtems__ */ if (sigemptyset(&sa.sa_mask) < 0) return -1; @@ -550,6 +599,7 @@ close_sockets() #endif return 0; } +#endif /* __rtems__ */ #ifdef __rtems__ #include "rtems-bsd-racoon-session-data.h" @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2016, 2018 Chris Johns <chrisj@rtems.org>. +# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. # All rights reserved. # # Copyright (c) 2009, 2018 embedded brains GmbH. All rights reserved. @@ -56,42 +56,57 @@ _defaults = { # # Includes # - 'include-paths': ['rtemsbsd/include', - 'freebsd/sys', - 'freebsd/sys/contrib/ck/include', - 'freebsd/sys/contrib/libsodium/src/libsodium/include', - 'freebsd/sys/contrib/libsodium/src/libsodium/include/sodium', - 'freebsd/sys/contrib/pf', - 'freebsd/crypto', - 'freebsd/crypto/openssl/include', - 'freebsd/sys/net', - 'freebsd/include', - 'freebsd/lib', - 'freebsd/lib/libbsdstat', - 'freebsd/lib/libcapsicum', - 'freebsd/lib/libcasper', - 'freebsd/lib/libc/include', - 'freebsd/lib/libc/isc/include', - 'freebsd/lib/libc/resolv', - 'freebsd/lib/libutil', - 'freebsd/lib/libkvm', - 'freebsd/lib/libmemstat', - 'freebsd/contrib/expat/lib', - 'freebsd/contrib/libpcap', - 'freebsd/contrib/libxo', - 'ipsec-tools/src/libipsec', - 'linux/include', - 'linux/drivers/net/ethernet/freescale/fman', - 'rtemsbsd/sys', - 'mDNSResponder/mDNSCore', - 'mDNSResponder/mDNSShared', - 'mDNSResponder/mDNSPosix', - 'testsuite/include'], - 'cpu-include-paths': ['rtemsbsd/@CPU@/include', - 'freebsd/sys/@CPU@/include'], - - # The path where headers will be copied during build. - 'build-include-path': ['build-include'], + 'include-paths': { + # The path where headers will be copied during the build. + 'build': ['build-include'], + # Kernel header paths + 'kernel': ['rtemsbsd/include', + 'freebsd/sys', + 'freebsd/sys/contrib/ck/include', + 'freebsd/sys/contrib/libsodium/src/libsodium/include', + 'freebsd/sys/contrib/libsodium/src/libsodium/include/sodium', + 'freebsd/sys/contrib/pf', + 'freebsd/sys/net', + 'ipsec-tools/src/libipsec', + 'linux/include', + 'linux/drivers/net/ethernet/freescale/fman', + 'rtemsbsd/sys'], + # User header paths + 'user': ['freebsd/crypto', + 'freebsd/crypto/openssl/include', + 'freebsd/include', + 'freebsd/lib', + 'freebsd/lib/libbsdstat', + 'freebsd/lib/libcapsicum', + 'freebsd/lib/libcasper', + 'freebsd/lib/libc/include', + 'freebsd/lib/libc/isc/include', + 'freebsd/lib/libc/resolv', + 'freebsd/lib/libutil', + 'freebsd/lib/libkvm', + 'freebsd/lib/libmemstat', + 'freebsd/contrib/expat/lib', + 'freebsd/contrib/libpcap', + 'freebsd/contrib/libxo', + 'mDNSResponder/mDNSCore', + 'mDNSResponder/mDNSShared', + 'mDNSResponder/mDNSPosix', + 'testsuite/include'], + # CPU specific path, assumed to be in the kernel context + 'cpu': ['rtemsbsd/@CPU@/include', + 'freebsd/sys/@CPU@/include'], + }, + + # + # Map paths based on RTEMS naming to FreeBSD naming. + # + 'path-mappings': [ + # (source, [targets..]) + # i386 + ('freebsd/sys/i386/include', ['freebsd/sys/x86/include', 'freebsd/sys/i386/include']), + # arm64 + ('freebsd/sys/aarch64/include', ['freebsd/sys/aarch64/include', 'freebsd/sys/arm64/include']), + ], # # Install headers @@ -129,6 +144,7 @@ _defaults = { ('freebsd/sys/dev/pci', '**/*.h', 'dev/pci'), ('freebsd/sys/dev/nvme', '**/*.h', 'dev/nvme'), ('freebsd/sys/dev/evdev', '**/*.h', 'dev/evdev'), + ('freebsd/sys/@CPU@/include', '**/*.h', ''), ('linux/include', '**/*.h', ''), ('mDNSResponder/mDNSCore', 'mDNSDebug.h', ''), ('mDNSResponder/mDNSCore', 'mDNSEmbeddedAPI.h', ''), @@ -146,7 +162,7 @@ class rtems(builder.Module): def generate(self): mm = self.manager - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/bus_if.c', 'local/cryptodev_if.c', @@ -162,35 +178,11 @@ class rtems(builder.Module): 'local/mmcbr_if.c', 'local/if_dwc_if.c', 'local/gpio_if.c', - 'rtems/ipsec_get_policylen.c', - 'rtems/rtems-bsd-arp-processor.c', 'rtems/rtems-bsd-allocator-domain-size.c', - 'rtems/rtems-bsd-cxx.cc', 'rtems/rtems-bsd-get-allocator-domain-size.c', - 'rtems/rtems-bsd-get-ethernet-addr.c', 'rtems/rtems-bsd-get-mac-address.c', 'rtems/rtems-bsd-get-task-priority.c', 'rtems/rtems-bsd-get-task-stack-size.c', - 'rtems/rtems-bsd-ifconfig.c', - 'rtems/rtems-bsd-ifconfig-lo0.c', - 'rtems/rtems-bsd-init-dhcp.c', - 'rtems/rtems-bsd-rc-conf-net.c', - 'rtems/rtems-bsd-rc-conf-pf.c', - 'rtems/rtems-bsd-rc-conf.c', - 'rtems/rtems-bsd-set-if-input.c', - 'rtems/rtems-bsd-shell-arp.c', - 'rtems/rtems-bsd-shell-i2c.c', - 'rtems/rtems-bsd-shell-ifconfig.c', - 'rtems/rtems-bsd-shell-netstat.c', - 'rtems/rtems-bsd-shell-nvmecontrol.c', - 'rtems/rtems-bsd-shell-pfctl.c', - 'rtems/rtems-bsd-shell-ping.c', - 'rtems/rtems-bsd-shell-route.c', - 'rtems/rtems-bsd-shell-stty.c', - 'rtems/rtems-bsd-shell-sysctl.c', - 'rtems/rtems-bsd-shell-tcpdump.c', - 'rtems/rtems-bsd-shell-vmstat.c', - 'rtems/rtems-bsd-shell-wlanstats.c', 'rtems/rtems-bsd-syscall-api.c', 'rtems/rtems-kernel-assert.c', 'rtems/rtems-kernel-autoconf.c', @@ -229,16 +221,65 @@ class rtems(builder.Module): 'rtems/rtems-legacy-rtrequest.c', 'rtems/rtems-legacy-newproc.c', 'rtems/rtems-legacy-mii.c', + 'sys/arm/lpc/if_lpe.c', + 'sys/arm/lpc/lpc_pwr.c', + 'sys/dev/atsam/if_atsam.c', + 'sys/dev/atsam/if_atsam_media.c', + 'sys/dev/dw_mmc/dw_mmc.c', + 'sys/dev/ffec/if_ffec_mcf548x.c', + 'sys/dev/ffec/if_ffec_mpc8xx.c', + 'sys/dev/input/touchscreen/tsc_lpc32xx.c', + 'sys/dev/mmc/st-sdmmc.c', + 'sys/dev/mmc/st-sdmmc-config.c', + 'sys/dev/smc/if_smc_nexus.c', + 'sys/dev/stmac/if_stmac.c', + 'sys/dev/tsec/if_tsec_nexus.c', + 'sys/dev/usb/controller/ehci_mpc83xx.c', + 'sys/dev/usb/controller/dwc_otg_nexus.c', + 'sys/dev/usb/controller/dwc_otg_stm32h7.c', + 'sys/dev/usb/controller/ohci_lpc32xx.c', + 'sys/dev/usb/controller/ohci_lpc.c', + 'sys/dev/usb/controller/usb_otg_transceiver.c', + 'sys/dev/usb/controller/usb_otg_transceiver_dump.c', + 'sys/fs/devfs/devfs_devs.c', + 'sys/net/if_ppp.c', + 'sys/net/ppp_tty.c', + ], + mm.generator['source']() + ) + self.addRTEMSUserSourceFiles( + [ + 'rtems/ipsec_get_policylen.c', + 'rtems/rtems-bsd-arp-processor.c', + 'rtems/rtems-bsd-cxx.cc', + 'rtems/rtems-bsd-get-ethernet-addr.c', + 'rtems/rtems-bsd-ifconfig.c', + 'rtems/rtems-bsd-ifconfig-lo0.c', + 'rtems/rtems-bsd-init-dhcp.c', + 'rtems/rtems-bsd-rc-conf-net.c', + 'rtems/rtems-bsd-rc-conf-pf.c', + 'rtems/rtems-bsd-rc-conf.c', + 'rtems/rtems-bsd-set-if-input.c', + 'rtems/rtems-bsd-shell-arp.c', + 'rtems/rtems-bsd-shell-i2c.c', + 'rtems/rtems-bsd-shell-ifconfig.c', + 'rtems/rtems-bsd-shell-ifmcstat.c', + 'rtems/rtems-bsd-shell-netstat.c', + 'rtems/rtems-bsd-shell-nvmecontrol.c', + 'rtems/rtems-bsd-shell-pfctl.c', + 'rtems/rtems-bsd-shell-ping.c', + 'rtems/rtems-bsd-shell-route.c', + 'rtems/rtems-bsd-shell-stty.c', + 'rtems/rtems-bsd-shell-sysctl.c', + 'rtems/rtems-bsd-shell-tcpdump.c', + 'rtems/rtems-bsd-shell-vmstat.c', + 'rtems/rtems-bsd-shell-wlanstats.c', 'rtems/rtems-kvm.c', 'rtems/rtems-program.c', 'rtems/rtems-program-socket.c', 'rtems/rtems-routes.c', 'rtems/syslog.c', 'ftpd/ftpd-service.c', - 'nfsclient/mount_prot_xdr.c', - 'nfsclient/nfs.c', - 'nfsclient/nfs_prot_xdr.c', - 'nfsclient/rpcio.c', 'pppd/auth.c', 'pppd/ccp.c', 'pppd/chap.c', @@ -255,39 +296,24 @@ class rtems(builder.Module): 'pppd/sys-rtems.c', 'pppd/upap.c', 'pppd/utils.c', - 'sys/arm/lpc/if_lpe.c', - 'sys/arm/lpc/lpc_pwr.c', - 'sys/dev/atsam/if_atsam.c', - 'sys/dev/atsam/if_atsam_media.c', - 'sys/dev/dw_mmc/dw_mmc.c', - 'sys/dev/ffec/if_ffec_mcf548x.c', - 'sys/dev/ffec/if_ffec_mpc8xx.c', - 'sys/dev/input/touchscreen/tsc_lpc32xx.c', - 'sys/dev/smc/if_smc_nexus.c', - 'sys/dev/tsec/if_tsec_nexus.c', - 'sys/dev/usb/controller/ehci_mpc83xx.c', - 'sys/dev/usb/controller/ohci_lpc32xx.c', - 'sys/dev/usb/controller/ohci_lpc.c', - 'sys/dev/usb/controller/usb_otg_transceiver.c', - 'sys/dev/usb/controller/usb_otg_transceiver_dump.c', - 'sys/fs/devfs/devfs_devs.c', - 'sys/net/if_ppp.c', - 'sys/net/ppp_tty.c', 'telnetd/telnetd-service.c', ], mm.generator['source']() ) - self.addFile(mm.generator['file']('rtems/rtems-kernel-kvm-symbols.c', + self.addFile(mm.generator['file']('kernel', + 'rtems/rtems-kernel-kvm-symbols.c', mm.generator['rtems-path'](), mm.generator['no-convert'](), mm.generator['no-convert'](), mm.generator['kvm-symbols'](includes = 'rtemsbsd/rtems'))) - self.addFile(mm.generator['file']('lib/libc/net/nslexer.l', + self.addFile(mm.generator['file']('user', + 'lib/libc/net/nslexer.l', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), mm.generator['lex']('_nsyy', 'nsparser.c'))) - self.addFile(mm.generator['file']('lib/libc/net/nsparser.y', + self.addFile(mm.generator['file']('user', + 'lib/libc/net/nsparser.y', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -322,6 +348,8 @@ class base(builder.Module): 'sys/contrib/ck/include/ck_string.h', 'sys/contrib/ck/include/gcc/aarch64/ck_f_pr.h', 'sys/contrib/ck/include/gcc/aarch64/ck_pr.h', + 'sys/contrib/ck/include/gcc/aarch64/ck_pr_lse.h', + 'sys/contrib/ck/include/gcc/aarch64/ck_pr_llsc.h', 'sys/contrib/ck/include/gcc/arm/ck_f_pr.h', 'sys/contrib/ck/include/gcc/arm/ck_pr.h', 'sys/contrib/ck/include/gcc/ck_cc.h', @@ -338,8 +366,6 @@ class base(builder.Module): 'sys/contrib/ck/include/gcc/x86/ck_f_pr.h', 'sys/contrib/ck/include/gcc/x86/ck_pr.h', 'sys/fs/devfs/devfs_int.h', - 'sys/rpc/netconfig.h', - 'sys/rpc/types.h', 'sys/security/audit/audit.h', 'sys/security/mac/mac_framework.h', 'sys/sys/acl.h', @@ -527,7 +553,7 @@ class base(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'COPYRIGHT' ] @@ -570,7 +596,7 @@ class fdt(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'rtems/ofw_machdep.c', ], @@ -607,7 +633,7 @@ class tty(builder.Module): ], mm.generator['source']() ) -# self.addRTEMSSourceFiles( +# self.addRTEMSKernelSourceFiles( # [ # 'rtems/ofw_machdep.c', # ], @@ -644,7 +670,7 @@ class mmc(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'sys/arm/at91/at91_mci.c', ], @@ -683,7 +709,7 @@ class mmc_ti(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/sdhci_if.c', 'local/gpiobus_if.c', @@ -773,7 +799,7 @@ class iic(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/iicbus_if.c', 'sys/dev/iicbus/rtems-i2c.c', @@ -827,7 +853,7 @@ class display(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/clknode_if.c', 'local/hdmi_if.c', @@ -848,20 +874,16 @@ class pinmux(builder.Module): mm = self.manager self.addKernelSpaceHeaderFiles( [ - 'sys/arm/ti/ti_pinmux.h', 'sys/arm/ti/omap4/omap4_scm_padconf.h', - 'sys/arm/ti/am335x/am335x_scm_padconf.h', ] ) self.addKernelSpaceSourceFiles( [ - 'sys/arm/ti/ti_pinmux.c', 'sys/dev/fdt/fdt_pinctrl.c', - 'sys/arm/ti/am335x/am335x_scm_padconf.c', ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/fdt_pinctrl_if.c', ], @@ -869,6 +891,24 @@ class pinmux(builder.Module): ) # +# MV643XX Ethernet driver +# +class if_mve(builder.Module): + + def __init__(self, manager): + super(if_mve, self).__init__(manager, type(self).__name__) + + def generate(self): + mm = self.manager + self.addRTEMSKernelSourceFiles( + [ + 'sys/dev/mve/if_mve.c', + 'sys/dev/mve/if_mve_nexus.c', + ], + mm.generator['source']() + ) + +# # USB # class dev_usb(builder.Module): @@ -903,6 +943,7 @@ class dev_usb(builder.Module): 'sys/dev/usb/usb_ioctl.h', 'sys/dev/usb/usb_mbuf.h', 'sys/dev/usb/usb_msctest.h', + 'sys/dev/usb/usb_pci.h', 'sys/dev/usb/usb_pf.h', 'sys/dev/usb/usb_process.h', 'sys/dev/usb/usb_request.h', @@ -945,7 +986,7 @@ class dev_usb_controller(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/controller/ohci.h', @@ -965,6 +1006,7 @@ class dev_usb_controller(builder.Module): [ 'sys/dev/usb/controller/ohci.c', 'sys/dev/usb/controller/ehci.c', + 'sys/dev/usb/controller/ehci_pci.c', 'sys/dev/usb/controller/usb_controller.c', 'sys/dev/usb/controller/dwc_otg.c', 'sys/dev/usb/controller/dwc_otg_fdt.c', @@ -984,7 +1026,7 @@ class dev_usb_input(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/input/usb_rdesc.h', @@ -1012,7 +1054,7 @@ class dev_usb_net(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/net/if_auereg.h', @@ -1062,7 +1104,7 @@ class dev_usb_quirk(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/quirk/usb_quirk.h', @@ -1085,7 +1127,7 @@ class dev_usb_serial(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/serial/uftdi_reg.h', @@ -1130,7 +1172,7 @@ class dev_usb_storage(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceSourceFiles( [ 'sys/dev/usb/storage/umass.c', @@ -1148,7 +1190,7 @@ class dev_usb_controller_bbb(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/arm/ti/ti_cpuid.h', @@ -1185,7 +1227,7 @@ class dev_usb_wlan(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/usb/wlan/if_rsureg.h', @@ -1205,7 +1247,7 @@ class dev_usb_wlan(builder.Module): 'sys/dev/usb/wlan/if_zydreg.h', ] ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/runfw.c', ], @@ -1235,7 +1277,7 @@ class dev_wlan_rtwn(builder.Module): def generate(self): mm = self.manager - self.addDependency(mm['dev_usb']) + self.addDependency('dev_usb') self.addKernelSpaceHeaderFiles( [ 'sys/dev/rtwn/if_rtwn_beacon.h', @@ -1317,7 +1359,7 @@ class dev_wlan_rtwn(builder.Module): 'sys/dev/rtwn/usb/rtwn_usb_var.h', ] ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/rtwn-rtl8192cfwT.c', 'local/rtwn-rtl8188eufw.c', @@ -1498,10 +1540,14 @@ class dev_net(builder.Module): 'sys/net/if_types.h', 'sys/net/if_var.h', 'sys/net/vnet.h', + 'sys/net/mp_ring.h', + 'sys/net/iflib_private.h', ] ) self.addKernelSpaceSourceFiles( [ + 'sys/net/iflib.c', + 'sys/net/mp_ring.c', 'sys/arm/ti/cpsw/if_cpsw.c', 'sys/dev/ffec/if_ffec.c', 'sys/dev/mii/mii.c', @@ -1522,7 +1568,7 @@ class dev_net(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'sys/dev/mii/ksz8091rnb_50MHz.c', ], @@ -1553,13 +1599,16 @@ class dev_nic(builder.Module): ) self.addCPUDependentFreeBSDHeaderFiles( [ + 'sys/arm64/include/armreg.h', + 'sys/arm64/include/cpufunc.h', + 'sys/arm64/include/cpu.h', 'sys/arm/include/cpufunc.h', - 'sys/i386/include/specialreg.h', 'sys/i386/include/md_var.h', - 'sys/i386/include/intr_machdep.h', 'sys/x86/include/intr_machdep.h', - 'sys/x86/include/metadata.h', 'sys/i386/include/cpufunc.h', + 'sys/x86/include/intr_machdep.h', + 'sys/x86/include/specialreg.h', + 'sys/x86/include/x86_var.h', 'sys/mips/include/cpufunc.h', 'sys/mips/include/cpuregs.h', 'sys/powerpc/include/cpufunc.h', @@ -1683,7 +1732,7 @@ class dev_nic_e1000(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'sys/dev/e1000/LICENSE' ] @@ -1821,7 +1870,7 @@ class nvme(builder.Module): ], mm.generator['source']() ) - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'sys/dev/nvd/nvd.c', ], @@ -2230,14 +2279,16 @@ class netipsec(builder.Module): ], mm.generator['source'](libipsec_cflags) ) - self.addFile(mm.generator['file']('ipsec-tools/src/libipsec/policy_token.l', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/libipsec/policy_token.l', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), mm.generator['lex']('__libipsec', 'policy_parse.c', libipsec_cflags))) - self.addFile(mm.generator['file']('ipsec-tools/src/libipsec/policy_parse.y', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/libipsec/policy_parse.y', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2299,7 +2350,8 @@ class netipsec(builder.Module): mm.generator['source'](racoon_cflags, ['freebsd/crypto/openssl']) ) - self.addFile(mm.generator['file']('ipsec-tools/src/racoon/cftoken.l', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/racoon/cftoken.l', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2307,7 +2359,8 @@ class netipsec(builder.Module): 'cftoken.c', racoon_cflags, build=False))) - self.addFile(mm.generator['file']('ipsec-tools/src/racoon/cfparse.y', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/racoon/cfparse.y', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2315,7 +2368,8 @@ class netipsec(builder.Module): 'cfparse.h', racoon_cflags, build=False))) - self.addFile(mm.generator['file']('ipsec-tools/src/racoon/prsa_tok.l', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/racoon/prsa_tok.l', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2323,7 +2377,8 @@ class netipsec(builder.Module): 'prsa_tok.c', racoon_cflags, build=False))) - self.addFile(mm.generator['file']('ipsec-tools/src/racoon/prsa_par.y', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/racoon/prsa_par.y', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2336,7 +2391,7 @@ class netipsec(builder.Module): 'rtems/ipsec.h', ] ) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-racoon.c', 'rtems/rtems-bsd-rc-conf-ipsec.c', @@ -2356,7 +2411,8 @@ class netipsec(builder.Module): ], mm.generator['source'](setkey_cflags) ) - self.addFile(mm.generator['file']('ipsec-tools/src/setkey/token.l', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/setkey/token.l', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2364,7 +2420,8 @@ class netipsec(builder.Module): 'token.c', setkey_cflags, build=False))) - self.addFile(mm.generator['file']('ipsec-tools/src/setkey/parse.y', + self.addFile(mm.generator['file']('user', + 'ipsec-tools/src/setkey/parse.y', mm.generator['path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2372,7 +2429,7 @@ class netipsec(builder.Module): 'parse.h', setkey_cflags, build=False))) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-shell-setkey.c', ], @@ -2555,7 +2612,7 @@ class opencrypto(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'sys/contrib/libsodium/LICENSE' ] @@ -2715,6 +2772,156 @@ class pf(builder.Module): ) # +# RPC for user space, remove when NFSv2 is removed +# +class rpc_user(builder.Module): + + def __init__(self, manager): + super(rpc_user, self).__init__(manager, type(self).__name__) + + def generate(self): + mm = self.manager + # User space support for legacy nfsv2 client, remove when nfsv2 is removed + self.addUserSpaceHeaderFiles( + [ + 'include/rpc/clnt.h', + 'include/rpc/pmap_rmt.h', + 'include/rpc/svc_soc.h', + 'include/rpc/nettype.h', + 'include/rpc/xdr.h', + 'include/rpc/svc.h', + 'include/rpc/rpc_msg.h', + 'include/rpc/rpcsec_gss.h', + 'include/rpc/raw.h', + 'include/rpc/clnt_stat.h', + 'include/rpc/auth.h', + 'include/rpc/svc_dg.h', + 'include/rpc/auth_kerb.h', + 'include/rpc/auth_des.h', + 'include/rpc/rpcb_clnt.h', + 'include/rpc/rpc.h', + 'include/rpc/des.h', + 'include/rpc/des_crypt.h', + 'include/rpc/svc_auth.h', + 'include/rpc/pmap_clnt.h', + 'include/rpc/clnt_soc.h', + 'include/rpc/pmap_prot.h', + 'include/rpc/auth_unix.h', + 'include/rpc/rpc_com.h', + 'include/rpc/rpcent.h', + 'include/rpcsvc/nis_db.h', + 'include/rpcsvc/nislib.h', + 'include/rpcsvc/nis_tags.h', + 'include/rpcsvc/ypclnt.h', + 'include/rpcsvc/yp_prot.h', + 'lib/libc/rpc/mt_misc.h', + 'lib/libc/rpc/rpc_com.h', + ] + ) + self.addFile(mm.generator['file']('user', + 'include/rpc/rpcb_prot.x', + mm.generator['freebsd-path'](), + mm.generator['convert'](), + mm.generator['convert'](), + mm.generator['rpc-gen']())) + self.addFile(mm.generator['file']('user', + 'include/rpcsvc/nis.x', + mm.generator['freebsd-path'](), + mm.generator['convert'](), + mm.generator['convert'](), + mm.generator['rpc-gen']())) + self.addUserSpaceSourceFiles( + [ + 'lib/libc/rpc/auth_des.c', + 'lib/libc/rpc/authdes_prot.c', + 'lib/libc/rpc/auth_none.c', + 'lib/libc/rpc/auth_time.c', + 'lib/libc/rpc/auth_unix.c', + 'lib/libc/rpc/authunix_prot.c', + 'lib/libc/rpc/bindresvport.c', + 'lib/libc/rpc/clnt_bcast.c', + 'lib/libc/rpc/clnt_dg.c', + 'lib/libc/rpc/clnt_generic.c', + 'lib/libc/rpc/clnt_perror.c', + 'lib/libc/rpc/clnt_raw.c', + 'lib/libc/rpc/clnt_simple.c', + 'lib/libc/rpc/clnt_vc.c', + 'lib/libc/rpc/crypt_client.c', + 'lib/libc/rpc/des_crypt.c', + 'lib/libc/rpc/des_soft.c', + 'lib/libc/rpc/getnetconfig.c', + 'lib/libc/rpc/getnetpath.c', + 'lib/libc/rpc/getpublickey.c', + 'lib/libc/rpc/getrpcent.c', + 'lib/libc/rpc/getrpcport.c', + 'lib/libc/rpc/key_call.c', + 'lib/libc/rpc/key_prot_xdr.c', + 'lib/libc/rpc/mt_misc.c', + 'lib/libc/rpc/netname.c', + 'lib/libc/rpc/netnamer.c', + 'lib/libc/rpc/pmap_clnt.c', + 'lib/libc/rpc/pmap_getmaps.c', + 'lib/libc/rpc/pmap_getport.c', + 'lib/libc/rpc/pmap_prot2.c', + 'lib/libc/rpc/pmap_prot.c', + 'lib/libc/rpc/pmap_rmt.c', + 'lib/libc/rpc/rpcb_clnt.c', + 'lib/libc/rpc/rpcb_prot.c', + 'lib/libc/rpc/rpcb_st_xdr.c', + 'lib/libc/rpc/rpc_callmsg.c', + 'lib/libc/rpc/rpc_commondata.c', + 'lib/libc/rpc/rpcdname.c', + 'lib/libc/rpc/rpc_dtablesize.c', + 'lib/libc/rpc/rpc_generic.c', + 'lib/libc/rpc/rpc_prot.c', + 'lib/libc/rpc/rpcsec_gss_stub.c', + 'lib/libc/rpc/rpc_soc.c', + 'lib/libc/rpc/rtime.c', + 'lib/libc/rpc/svc_auth.c', + 'lib/libc/rpc/svc_auth_des.c', + 'lib/libc/rpc/svc_auth_unix.c', + 'lib/libc/rpc/svc.c', + 'lib/libc/rpc/svc_dg.c', + 'lib/libc/rpc/svc_generic.c', + 'lib/libc/rpc/svc_raw.c', + 'lib/libc/rpc/svc_run.c', + 'lib/libc/rpc/svc_simple.c', + 'lib/libc/rpc/svc_vc.c', + 'lib/libc/xdr/xdr_array.c', + 'lib/libc/xdr/xdr.c', + 'lib/libc/xdr/xdr_float.c', + 'lib/libc/xdr/xdr_mem.c', + 'lib/libc/xdr/xdr_rec.c', + 'lib/libc/xdr/xdr_reference.c', + 'lib/libc/xdr/xdr_sizeof.c', + 'lib/libc/xdr/xdr_stdio.c', + ], + mm.generator['source'](['-DINET']) + ) + +# +# NFSv2 Client +# +class nfsv2(builder.Module): + + def __init__(self, manager): + super(nfsv2, self).__init__(manager, type(self).__name__) + + def generate(self): + mm = self.manager + self.addDependency('rpc_user') + self.addRTEMSUserSourceFiles( + [ + 'nfsclient/mount_prot_xdr.c', + 'nfsclient/nfs.c', + 'nfsclient/nfs_prot_xdr.c', + 'nfsclient/rpcio.c', + ], + mm.generator['source']() + ) + + +# # PCI # class pci(builder.Module): @@ -2745,14 +2952,13 @@ class pci(builder.Module): self.addCPUDependentFreeBSDHeaderFiles( [ 'sys/i386/include/_bus.h', - 'sys/i386/include/bus.h', 'sys/x86/include/legacyvar.h', 'sys/x86/include/bus.h', 'sys/x86/include/pci_cfgreg.h', ] ) self.addCPUDependentFreeBSDSourceFiles( - [ 'i386' ], + [ 'i386', 'powerpc' ], [ 'sys/x86/x86/legacy.c', 'sys/x86/pci/pci_bus.c', @@ -2760,6 +2966,7 @@ class pci(builder.Module): mm.generator['source']() ) + # # User space # @@ -2796,36 +3003,6 @@ class user_space(builder.Module): 'include/nsswitch.h', 'include/resolv.h', 'include/res_update.h', - 'include/rpc/clnt.h', - 'include/rpc/pmap_rmt.h', - 'include/rpc/svc_soc.h', - 'include/rpc/nettype.h', - 'include/rpc/xdr.h', - 'include/rpc/svc.h', - 'include/rpc/rpc_msg.h', - 'include/rpc/rpcsec_gss.h', - 'include/rpc/raw.h', - 'include/rpc/clnt_stat.h', - 'include/rpc/auth.h', - 'include/rpc/svc_dg.h', - 'include/rpc/auth_kerb.h', - 'include/rpc/auth_des.h', - 'include/rpc/rpcb_clnt.h', - 'include/rpc/rpc.h', - 'include/rpc/des.h', - 'include/rpc/des_crypt.h', - 'include/rpc/svc_auth.h', - 'include/rpc/pmap_clnt.h', - 'include/rpc/clnt_soc.h', - 'include/rpc/pmap_prot.h', - 'include/rpc/auth_unix.h', - 'include/rpc/rpc_com.h', - 'include/rpc/rpcent.h', - 'include/rpcsvc/nis_db.h', - 'include/rpcsvc/nislib.h', - 'include/rpcsvc/nis_tags.h', - 'include/rpcsvc/ypclnt.h', - 'include/rpcsvc/yp_prot.h', 'include/sysexits.h', 'lib/lib80211/lib80211_ioctl.h', 'lib/lib80211/lib80211_regdomain.h', @@ -2855,8 +3032,6 @@ class user_space(builder.Module): 'lib/libc/net/res_config.h', 'lib/libc/resolv/res_debug.h', 'lib/libc/resolv/res_private.h', - 'lib/libc/rpc/mt_misc.h', - 'lib/libc/rpc/rpc_com.h', 'lib/libc/stdio/local.h', 'lib/libkvm/kvm.h', 'lib/libmemstat/memstat.h', @@ -2872,22 +3047,14 @@ class user_space(builder.Module): 'usr.bin/netstat/netstat.h' ] ) - self.addFile(mm.generator['file']('include/rpc/rpcb_prot.x', - mm.generator['freebsd-path'](), - mm.generator['convert'](), - mm.generator['convert'](), - mm.generator['rpc-gen']())) - self.addFile(mm.generator['file']('include/rpcsvc/nis.x', - mm.generator['freebsd-path'](), - mm.generator['convert'](), - mm.generator['convert'](), - mm.generator['rpc-gen']())) - self.addFile(mm.generator['file']('sbin/route/keywords', + self.addFile(mm.generator['file']('user', + 'sbin/route/keywords', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), mm.generator['route-keywords']())) - self.addFile(mm.generator['file']('sbin/pfctl/parse.y', + self.addFile(mm.generator['file']('user', + 'sbin/pfctl/parse.y', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -2927,7 +3094,7 @@ class user_space(builder.Module): 'include/machine/rtems-bsd-regdomain.h', ] ) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-regdomain.c', ], @@ -3022,72 +3189,9 @@ class user_space(builder.Module): 'lib/libc/resolv/res_send.c', 'lib/libc/resolv/res_state.c', 'lib/libc/resolv/res_update.c', - 'lib/libc/rpc/auth_des.c', - 'lib/libc/rpc/authdes_prot.c', - 'lib/libc/rpc/auth_none.c', - 'lib/libc/rpc/auth_time.c', - 'lib/libc/rpc/auth_unix.c', - 'lib/libc/rpc/authunix_prot.c', - 'lib/libc/rpc/bindresvport.c', - 'lib/libc/rpc/clnt_bcast.c', - 'lib/libc/rpc/clnt_dg.c', - 'lib/libc/rpc/clnt_generic.c', - 'lib/libc/rpc/clnt_perror.c', - 'lib/libc/rpc/clnt_raw.c', - 'lib/libc/rpc/clnt_simple.c', - 'lib/libc/rpc/clnt_vc.c', - 'lib/libc/rpc/crypt_client.c', - 'lib/libc/rpc/des_crypt.c', - 'lib/libc/rpc/des_soft.c', - 'lib/libc/rpc/getnetconfig.c', - 'lib/libc/rpc/getnetpath.c', - 'lib/libc/rpc/getpublickey.c', - 'lib/libc/rpc/getrpcent.c', - 'lib/libc/rpc/getrpcport.c', - 'lib/libc/rpc/key_call.c', - 'lib/libc/rpc/key_prot_xdr.c', - 'lib/libc/rpc/mt_misc.c', - 'lib/libc/rpc/netname.c', - 'lib/libc/rpc/netnamer.c', - 'lib/libc/rpc/pmap_clnt.c', - 'lib/libc/rpc/pmap_getmaps.c', - 'lib/libc/rpc/pmap_getport.c', - 'lib/libc/rpc/pmap_prot2.c', - 'lib/libc/rpc/pmap_prot.c', - 'lib/libc/rpc/pmap_rmt.c', - 'lib/libc/rpc/rpcb_clnt.c', - 'lib/libc/rpc/rpcb_prot.c', - 'lib/libc/rpc/rpcb_st_xdr.c', - 'lib/libc/rpc/rpc_callmsg.c', - 'lib/libc/rpc/rpc_commondata.c', - 'lib/libc/rpc/rpcdname.c', - 'lib/libc/rpc/rpc_dtablesize.c', - 'lib/libc/rpc/rpc_generic.c', - 'lib/libc/rpc/rpc_prot.c', - 'lib/libc/rpc/rpcsec_gss_stub.c', - 'lib/libc/rpc/rpc_soc.c', - 'lib/libc/rpc/rtime.c', - 'lib/libc/rpc/svc_auth.c', - 'lib/libc/rpc/svc_auth_des.c', - 'lib/libc/rpc/svc_auth_unix.c', - 'lib/libc/rpc/svc.c', - 'lib/libc/rpc/svc_dg.c', - 'lib/libc/rpc/svc_generic.c', - 'lib/libc/rpc/svc_raw.c', - 'lib/libc/rpc/svc_run.c', - 'lib/libc/rpc/svc_simple.c', - 'lib/libc/rpc/svc_vc.c', 'lib/libc/stdio/fgetln.c', 'lib/libc/stdlib/strtonum.c', 'lib/libc/string/strsep.c', - 'lib/libc/xdr/xdr_array.c', - 'lib/libc/xdr/xdr.c', - 'lib/libc/xdr/xdr_float.c', - 'lib/libc/xdr/xdr_mem.c', - 'lib/libc/xdr/xdr_rec.c', - 'lib/libc/xdr/xdr_reference.c', - 'lib/libc/xdr/xdr_sizeof.c', - 'lib/libc/xdr/xdr_stdio.c', 'lib/libmemstat/memstat_all.c', 'lib/libmemstat/memstat.c', 'lib/libmemstat/memstat_malloc.c', @@ -3159,10 +3263,12 @@ class user_space(builder.Module): 'usr.bin/vmstat/vmstat.c', 'usr.sbin/arp/arp.c', 'usr.sbin/i2c/i2c.c', + 'usr.sbin/ifmcstat/ifmcstat.c', + 'usr.sbin/ifmcstat/printb.c', ], mm.generator['source'](['-DINET']) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/libxo/LICENSE' ] @@ -3203,6 +3309,7 @@ class crypto_openssl(builder.Module): def generate(self): mm = self.manager + self.addDependency('user_space') self.addUserSpaceHeaderFiles( [ 'crypto/openssl/crypto/aes/aes_locl.h', @@ -4156,17 +4263,19 @@ class crypto_openssl(builder.Module): 'freebsd/crypto/openssl/crypto/ec/curve448', 'freebsd/crypto/openssl/crypto/ec/curve448/arch_32']) ) - self.addFile(mm.generator['file']('crypto/openssl/crypto/LPdir_unix.c', + self.addFile(mm.generator['file']('user', + 'crypto/openssl/crypto/LPdir_unix.c', mm.generator['freebsd-path'](), mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](), mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](), - mm.generator['buildSystemFragmentComposer']())) - self.addFile(mm.generator['file']('crypto/openssl/crypto/ec/ecp_nistz256_table.c', + mm.generator['buildSystemComposer']())) + self.addFile(mm.generator['file']('user', + 'crypto/openssl/crypto/ec/ecp_nistz256_table.c', mm.generator['freebsd-path'](), mm.generator['from-FreeBSD-to-RTEMS-UserSpaceSourceConverter'](), mm.generator['from-RTEMS-To-FreeBSD-SourceConverter'](), - mm.generator['buildSystemFragmentComposer']())) - self.addPlainTextFile( + mm.generator['buildSystemComposer']())) + self.addPlainTextFiles( [ 'crypto/openssl/LICENSE' ] @@ -4175,7 +4284,6 @@ class crypto_openssl(builder.Module): # # /usr/bin/openssl # -# depends on crypto_openssl, user_space class usr_bin_openssl(builder.Module): def __init__(self, manager): @@ -4183,6 +4291,7 @@ class usr_bin_openssl(builder.Module): def generate(self): mm = self.manager + self.addDependency('crypto_openssl') self.addUserSpaceHeaderFiles( [ 'crypto/openssl/apps/apps.h', @@ -4255,7 +4364,7 @@ class usr_bin_openssl(builder.Module): ['freebsd/crypto/openssl'] ) ) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-shell-openssl.c', ], @@ -4304,7 +4413,7 @@ class contrib_expat(builder.Module): ], mm.generator['source'](cflags) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/expat/COPYING' ] @@ -4375,14 +4484,16 @@ class contrib_libpcap(builder.Module): ) gen_cflags = cflags + ['-DNEED_YYPARSE_WRAPPER=1', '-Dyylval=pcap_lval'] - self.addFile(mm.generator['file']('contrib/libpcap/scanner.l', + self.addFile(mm.generator['file']('user', + 'contrib/libpcap/scanner.l', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), mm.generator['lex']('pcap', 'scanner.c', gen_cflags))) - self.addFile(mm.generator['file']('contrib/libpcap/grammar.y', + self.addFile(mm.generator['file']('user', + 'contrib/libpcap/grammar.y', mm.generator['freebsd-path'](), mm.generator['convert'](), mm.generator['convert'](), @@ -4407,7 +4518,7 @@ class contrib_libpcap(builder.Module): ], mm.generator['source'](cflags) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/libpcap/LICENSE' ] @@ -4652,7 +4763,7 @@ class usr_sbin_tcpdump(builder.Module): ['freebsd/contrib/tcpdump', 'freebsd/usr.sbin/tcpdump/tcpdump']) ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/tcpdump/LICENSE' ] @@ -4963,7 +5074,7 @@ class usr_sbin_wpa_supplicant(builder.Module): 'freebsd/usr.sbin/wpa/wpa_supplicant', 'freebsd/crypto/openssl/crypto']) ) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-shell-wpa_supplicant.c', 'rtems/rtems-wpa_supplicant_mutex.c', @@ -4971,7 +5082,7 @@ class usr_sbin_wpa_supplicant(builder.Module): ], mm.generator['source']() ) - self.addPlainTextFile( + self.addPlainTextFiles( [ 'contrib/wpa/COPYING' ] @@ -5000,6 +5111,13 @@ class in_cksum(builder.Module): ] ) self.addTargetSourceCPUDependentHeaderFiles( + [ 'arm64' ], + 'arm64', + [ + 'sys/arm64/include/in_cksum.h', + ] + ) + self.addTargetSourceCPUDependentHeaderFiles( [ 'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k', 'nios2', 'sh', 'sparc', 'v850' ], 'mips', @@ -5008,6 +5126,13 @@ class in_cksum(builder.Module): ] ) self.addCPUDependentFreeBSDSourceFiles( + [ 'aarch64', 'arm64' ], + [ + 'sys/arm64/arm64/in_cksum.c', + ], + mm.generator['source']() + ) + self.addCPUDependentFreeBSDSourceFiles( [ 'i386' ], [ 'sys/i386/i386/in_cksum.c', @@ -5031,7 +5156,8 @@ class in_cksum(builder.Module): self.addCPUDependentFreeBSDSourceFiles( [ 'arm', 'avr', 'bfin', 'h8300', 'lm32', 'm32c', 'm32r', 'm68k', - 'mips', 'nios2', 'riscv', 'sh', 'sparc', 'v850' + 'mips', 'moxie', 'nios2', 'or1k', 'riscv', 'sh', 'sparc', + 'v850' ], [ 'sys/mips/mips/in_cksum.c', @@ -5077,7 +5203,7 @@ class dhcpcd(builder.Module): ], mm.generator['source']('-D__FreeBSD__ -DTHERE_IS_NO_FORK -DMASTER_ONLY -DINET') ) - self.addRTEMSSourceFiles( + self.addRTEMSUserSourceFiles( [ 'rtems/rtems-bsd-shell-dhcpcd.c', ], @@ -5115,6 +5241,7 @@ class mdnsresponder(builder.Module): mm.generator['source']() ) + class dpaa(builder.Module): def __init__(self, manager): @@ -5207,6 +5334,12 @@ class imx(builder.Module): ], mm.generator['source']() ) + self.addRTEMSKernelSourceFiles( + [ + 'sys/arm/freescale/imx/imxrt1166_usbphy.c', + ], + mm.generator['source']() + ) class regulator(builder.Module): def __init__(self, manager): @@ -5214,7 +5347,7 @@ class regulator(builder.Module): def generate(self): mm = self.manager - self.addRTEMSSourceFiles( + self.addRTEMSKernelSourceFiles( [ 'local/regdev_if.c', 'local/regnode_if.c', @@ -5249,7 +5382,8 @@ class tests(builder.Module): def generate(self): mm = self.manager self.addTest(mm.generator['test']('epoch01', ['test_main'], extraLibs = ['rtemstest'])) - self.addTest(mm.generator['test']('nfs01', ['test_main'], netTest = True)) + self.addTest(mm.generator['test']('nfs01', ['test_main'], + netTest = True, modules = ['nfsv2'])) self.addTest(mm.generator['test']('foobarclient', ['test_main'], runTest = False, netTest = True)) self.addTest(mm.generator['test']('foobarserver', ['test_main'], @@ -5295,9 +5429,10 @@ class tests(builder.Module): runTest = False, netTest = True, extraLibs = ['telnetd'])) self.addTest(mm.generator['test']('smp01', ['test_main'], extraLibs = ['rtemstest'])) - self.addTest(mm.generator['test']('media01', ['test_main'], + self.addTest(mm.generator['test']('media01', ['test_main', 'pattern-test'], runTest = False, - extraLibs = ['ftpd', 'telnetd'])) + extraLibs = ['tftpfs', 'ftpd', 'telnetd'])) + self.addTest(mm.generator['test']('mcast01', ['test_main'])) self.addTest(mm.generator['test']('vlan01', ['test_main'], netTest = True)) self.addTest(mm.generator['test']('lagg01', ['test_main'], netTest = True)) self.addTest(mm.generator['test']('log01', ['test_main'])) @@ -5338,6 +5473,7 @@ class tests(builder.Module): self.addTest(mm.generator['test']('ipsec01', ['test_main'])) self.addTest(mm.generator['test']('openssl01', ['test_main'])) self.addTest(mm.generator['test']('openssl02', ['test_main'])) + self.addTest(mm.generator['test']('tcpdump01', ['test_main'])) def load(mm): @@ -5355,6 +5491,7 @@ def load(mm): mm.addModule(evdev(mm)) mm.addModule(iic(mm)) mm.addModule(pinmux(mm)) + mm.addModule(if_mve(mm)) mm.addModule(display(mm)) mm.addModule(dev_usb(mm)) @@ -5382,6 +5519,9 @@ def load(mm): mm.addModule(pf(mm)) mm.addModule(dev_net(mm)) + mm.addModule(rpc_user(mm)) + mm.addModule(nfsv2(mm)) + # Add PCI mm.addModule(pci(mm)) @@ -5415,7 +5555,5 @@ def load(mm): mm.addModule(tests(mm)) - mm.setModuleConfigiuration() - # XXX TODO Check that no file is also listed in empty # XXX TODO Check that no file in in two modules diff --git a/libbsd.txt b/libbsd.txt deleted file mode 100644 index 3d665500..00000000 --- a/libbsd.txt +++ /dev/null @@ -1,1331 +0,0 @@ -RTEMS BSD Library Guide -======================= -:toc: -:icons: -:numbered: -:website: http://www.rtems.org/ - -The libbsd makes FreeBSD subsystems like TCP/IP, USB, SD and some more usable -for RTEMS. It tries to follow the FreeBSD development as close as possible and -therefore is updated to the latest FreeBSD HEAD revision from time to time. -To find out which version of FreeBSD is currently used as the base version for -libbsd please take a look at the -https://git.rtems.org/rtems-libbsd/log/freebsd-org[freebsd-org] submodule. - -This is a guide which captures information on the -process of merging code from FreeBSD, building this library, -RTEMS specific support files, and general guidelines on what -modifications to the FreeBSD source are permitted. - -Goals of this effort are - -* update TCP/IP and provide USB in RTEMS, -* ease updating to future FreeBSD versions, -* ease tracking changes in FreeBSD code, -* minimize manual changes in FreeBSD code, and -* define stable kernel/device driver API which is implemented -by both RTEMS and FreeBSD. This is the foundation of the port. - -We will work to push our changes upstream to the FreeBSD Project -and minimize changes required at each update point. - -******************************************************************************* -This is a work in progress and is very likely to be incomplete. -Please help by adding to it. -******************************************************************************* - -== Getting Started - -=== Tool Chain === - -You need a tool chain for RTEMS based on the latest RTEMS Source Builder (RSB). - -=== Installation Overview === - -. You must configure your BSP with the +--disable-networking+ option to disable -the old network stack. Make sure no header files of the old network stack are -installed. - -. Clone the Git repository +git clone git://git.rtems.org/rtems-libbsd.git+. -. Change into the RTEMS BSD library root directory. -. If you want to run tests with a custom IP configuration instead of the default - one you can use an adjusted `config.inc` configuration file. -. Run +waf configure ...+. -. Run +waf+. -. Run +waf install+. - -Refer to the README.waf for Waf building instructions. - -Make sure the submodules have been initialised and are updated. If a 'git -status' says `rtems_waf` need updating run the submodule update command: - - $ git submodule sync - $ git submodule rtems_waf update - -=== Board Support Package Requirements === - -You need the latest RTEMS version to build the libbsd master. The Board -Support Package (BSP) must support the -http://www.rtems.org/onlinedocs/doxygen/cpukit/html/group\__rtems\__interrupt__extension.html[Interrupt Manager Extension] -// The first underscores have to be masked to stop asciidoc interpreting them -to make use of generic FreeBSD based drivers. - -=== Board Support Package Configuration and Build === - -You need to configure RTEMS for the desired BSP and install it. The BSP must -be configured with a disabled network stack. The BSD library containing the -new network stack is a separate package. Using a BSP installation containing -the old network stack may lead to confusion and unpredictable results. - -The following script is used to build the `arm/xilinx_zynq_a9_qemu` BSP for -our internal testing purposes: - -------------------------------------------------------------------------------- -#!/bin/sh - -cd ${HOME}/sandbox -rm -rf b-xilinx_zynq_a9_qemu -mkdir b-xilinx_zynq_a9_qemu -cd b-xilinx_zynq_a9_qemu -${HOME}/git-rtems/configure \ - --prefix=${HOME}/sandbox/install \ - --target=arm-rtems5 \ - --enable-rtemsbsp=xilinx_zynq_a9_qemu \ - --disable-networking && \ - make && \ - make install -------------------------------------------------------------------------------- - -The `arm/xilinx_zynq_a9_qemu` BSP running on the Qemu simulator has some -benefits for development and test of the BSD library - -* it offers a NULL pointer read and write protection, -* Qemu is a fast simulator, -* Qemu provides support for GDB watchpoints, -* Qemu provides support for virtual Ethernet networks, e.g. TUN and bridge -devices (you can run multiple test instances on one virtual network). - -=== BSD Library Configuration and Build === - -The build system based on the Waf build system. To build with Waf please refer -to the README.waf file. - -Note that the libbsd supports different buildsets. These can be selected with -the `--buildset=xxx.ini` option during the configure phase. Take a look at the -comments in `buildset/*.ini` to see which build sets are officially supported. - -You can also create and provide your own buildset configuration. But remember -that it's quite easy to break something by disabling the wrong modules. Only the -configurations in the `buildset` directory are officially maintained. - -===== Example Configuration for Network Tests ===== - -If you need some other IP configuration for the network tests that use a fixed -IP config you can copy `config.inc` to a location outside to the source tree and -adapt it. Then use the option `--net-test-config=NET_CONFIG` to pass the file to -waf's configure command. - -------------------------------------------------------------------------------- -NET_CFG_SELF_IP = 10.0.0.2 -NET_CFG_NETMASK = 255.255.0.0 -NET_CFG_PEER_IP = 10.0.0.1 -NET_CFG_GATEWAY_IP = 10.0.0.1 -------------------------------------------------------------------------------- - -=== BSD Library Initialization === - -To initialise the BSD Library create a suitable rc.conf file. The FreeBSD man -page rc.conf(5) provides the details needed to create a suitable format file: - - https://www.freebsd.org/cgi/man.cgi?rc.conf - -You can call one of three functions to run the initialisation once BSD has -initialised: - - - rtems_bsd_run_etc_rc_conf: Run /etc/rc.conf. - - rtems_bsd_run_rc_conf: Run a user supplied file. - - rtems_bsd_run_rc_conf_script: Run the in memory line feed separated text string. - -For exapmle: - - void - network_init(void) - { - rtems_status_code sc; - - sc = rtems_bsd_initialize(); - assert(sc == RTEMS_SUCCESSFUL); - - rtems_bsd_run_etc_rc_conf(true); /* verbose = true */ - -} - -By default the networking support is builtin. Other directives can be added and -are found in 'machine/rtems-bsd-rc-conf-directives.h'. Please check the file -for the list. - -The following network names are supported: - - cloned_interfaces - ifconfig_'interface' - defaultrouter - hostname - -For example: - - # - # My BSD initialisation. - # - hostname="myhost" - cloned_interfaces="vlan0 vlan1" - ifconfig_re0="inet inet 10.10.10.10 netmask 255.255.255.0" - fconfig_vlan0="inet 10.11.10.10 255.255.255.0 vlan 101 vlandev re0" - defaultrouter="10.10.10.1" - -You can also intialise the BSD library using code. The following code to -initialize the BSD library: - -------------------------------------------------------------------------------- -#include <assert.h> -#include <sysexits.h> - -#include <rtems/bsd/bsd.h> - -void -network_init(void) -{ - rtems_status_code sc; - int exit_code; - - sc = rtems_bsd_initialize(); - assert(sc == RTEMS_SUCCESSFUL); - - exit_code = rtems_bsd_ifconfig_lo0(); - assert(exit_code == EX_OK); -} -------------------------------------------------------------------------------- - -This performs the basic network stack initialization with a loopback interface. -Further initialization must be done using the standard BSD network -configuration commands -http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)] -using `rtems_bsd_command_ifconfig()` and -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)] -using `rtems_bsd_command_route()`. For an example please have a look at -`testsuite/include/rtems/bsd/test/default-network-init.h`. - -=== Task Priorities and Stack Size === - -The default task priority is 96 for the interrupt server task (name "IRQS"), 98 -for the timer server task (name "TIME") and 100 for all other tasks. The -application may provide their own implementation of the -`rtems_bsd_get_task_priority()` function (for example in the module which calls -`rtems_bsd_initialize()`) if different values are desired. - -The task stack size is determined by the `rtems_bsd_get_task_stack_size()` -function which may be provided by the application in case the default is not -appropriate. - -=== Size for Allocator Domains === - -The size for an allocator domain can be specified via the -`rtems_bsd_get_allocator_domain_size()` function. The application may provide -their own implementation of the `rtems_bsd_get_allocator_domain_size()` -function (for example in the module which calls `rtems_bsd_initialize()`) if -different values are desired. The default size is 8MiB for all domains. - -=== Redirecting or Disabling the Output === - -A lot of system messages are printed to the stdout by default. If you want to -redirect them you can overwrite the default print handler. That can even be done -before the libbsd initialization to catch all messages. An example would look -like follows: - -------------------------------------------------------------------------------- -int my_vprintf_handler(int level, const char *fmt, va_list ap) { - /* Do something with the messages. */ - - return number_of_printed_chars; -} - -... - /* In your initialization: */ - rtems_bsd_vprintf_handler old; - old = rtems_bsd_set_vprintf_handler(my_vprintf_handler); -... -------------------------------------------------------------------------------- - -As a special case, you can set the `rtems_bsd_vprintf_handler_mute(...)` -provided by libbsd to suppress all output. - -== Network Stack Features - -http://roy.marples.name/projects/dhcpcd/index[DHCPCD(8)]:: DHCP client - -https://developer.apple.com/library/mac/documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html[dns_sd.h]:: DNS Service Discovery - -http://www.opensource.apple.com/source/mDNSResponder/mDNSResponder-320.10/mDNSCore/mDNSEmbeddedAPI.h[mDNS]:: Multi-Cast DNS - -http://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4[UNIX(4)]:: UNIX-domain protocol family - -http://www.freebsd.org/cgi/man.cgi?query=inet&sektion=4[INET(4)]:: Internet protocol family - -http://www.freebsd.org/cgi/man.cgi?query=inet6&sektion=4[INET6(4)]:: Internet protocol version 6 family - -http://www.freebsd.org/cgi/man.cgi?query=tcp&sektion=4[TCP(4)]:: Internet Transmission Control Protocol - -http://www.freebsd.org/cgi/man.cgi?query=udp&sektion=4[UDP(4)]:: Internet User Datagram Protocol - -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=4[ROUTE(4)]:: Kernel packet forwarding database - -http://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4[BPF(4)]:: Berkeley Packet Filter - -http://www.freebsd.org/cgi/man.cgi?query=socket&sektion=2[SOCKET(2)]:: Create an endpoint for communication - -http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2[KQUEUE(2)]:: Kernel event notification mechanism - -http://www.freebsd.org/cgi/man.cgi?query=select&sektion=2[SELECT(2)]:: Synchronous I/O multiplexing - -http://www.freebsd.org/cgi/man.cgi?query=poll&sektion=2[POLL(2)]:: Synchronous I/O multiplexing - -http://www.freebsd.org/cgi/man.cgi?query=route&sektion=8[ROUTE(8)]:: Manually manipulate the routing tables - -http://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8[IFCONFIG(8)]:: Configure network interface parameters - -http://www.freebsd.org/cgi/man.cgi?query=netstat&sektion=1[NETSTAT(1)]:: Show network status - -http://www.freebsd.org/cgi/man.cgi?query=ping&sektion=8[PING(8)]:: Send ICMP ECHO_REQUEST packets to network hosts - -http://www.freebsd.org/cgi/man.cgi?query=ping6&sektion=8[PING6(8)]:: Send ICMPv6 ECHO_REQUEST packets to network hosts - -http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3[SYSCTL(3)]:: Get or set system information - -http://www.freebsd.org/cgi/man.cgi?query=resolver&sektion=3[RESOLVER(3)]:: Resolver routines - -http://www.freebsd.org/cgi/man.cgi?query=gethostbyname&sektion=3[GETHOSTBYNAME(3)]:: Get network host entry - -== Network Interface Drivers - -=== Link Up/Down Events - -You can notifiy the application space of link up/down events in your network -interface driver via the if_link_state_change(LINK_STATE_UP/LINK_STATE_DOWN) -function. The DHCPCD(8) client is a consumer of these events for example. -Make sure that the interface flag IFF_UP and the interface driver flag -IFF_DRV_RUNNING is set in case the link is up, otherwise ether_output() will -return the error status ENETDOWN. - -== Shell Commands - -=== HOSTNAME(1) - -In addition to the standard options the RTEMS version of the HOSTNAME(1) -command supports the -m flag to set/get the multicast hostname of the -mDNS resolver instance. See also rtems_mdns_sethostname() and -rtems_mdns_gethostname(). - -== Qemu - -Use the following script to set up a virtual network with three tap devices -connected via one bridge device. - -------------------------------------------------------------------------------- -#!/bin/sh -x - -user=`whoami` -interfaces=(1 2 3) - -tap=qtap -bri=qbri - -case $1 in - up) - sudo -i brctl addbr $bri - for i in ${interfaces[@]} ; do - sudo -i tunctl -t $tap$i -u $user ; - sudo -i ifconfig $tap$i up ; - sudo -i brctl addif $bri $tap$i ; - done - sudo -i ifconfig $bri up - ;; - down) - for i in ${interfaces[@]} ; do - sudo -i ifconfig $tap$i down ; - sudo -i tunctl -d $tap$i ; - done - sudo -i ifconfig $bri down - sudo -i brctl delbr $bri - ;; -esac -------------------------------------------------------------------------------- - -Connect your Qemu instance to one of the tap devices, e.g. - -------------------------------------------------------------------------------- -qemu-system-i386 -m 512 -boot a -cpu pentium3 \ - -drive file=$HOME/qemu/pc386_fda,index=0,if=floppy,format=raw \ - -drive file=fat:$HOME/qemu/hd,format=raw \ - -net nic,model=e1000,macaddr=0e:b0:ba:5e:ba:11 \ - -net tap,ifname=qtap1,script=no,downscript=no \ - -nodefaults -nographic -serial stdio -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -qemu-system-arm \ - -serial null \ - -serial mon:stdio \ - -nographic \ - -M xilinx-zynq-a9 \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \ - -net tap,ifname=qtap1,script=no,downscript=no \ - -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/media01.exe -------------------------------------------------------------------------------- - -Make sure that each Qemu instance uses its own MAC address to avoid an address -conflict (or otherwise use it as a test). - -To connect the Qemu instances with your local network use the following -(replace 'eth0' with the network interface of your host). - -------------------------------------------------------------------------------- -ifconfig eth0 0.0.0.0 -brctl addif qbri eth0 -dhclient qbri -------------------------------------------------------------------------------- - -=== VDE and QEMU - -On FreeBSD you can create VDE or the Virtual Distributed Ethernet to create a -network environment that does not need to run qemu as root or needing to drop -the tap's privileges to run qemu. - -VDE creates a software switch with a default of 32 ports which means a single -kernel tap can support 32 qemu networking sessions. - -To use VDE you need to build qemu with VDE support. The RSB can detect a VDE -plug and enable VDE support in qemu when building. On FreeBSD install the VDE -support with: - - # pkg install -u vde2 - -Build qemu with the RSB. - -To network create a bridge and a tap. The network is 10.10.1.0/24. On FreeBSD -add to your /etc/rc.conf: - - cloned_interfaces="bridge0 tap0" - autobridge_interfaces="bridge0" - autobridge_bridge0="re0 tap0" - ifconfig_re0="up" - ifconfig_tap0="up" - ifconfig_bridge0="inet 10.1.1.2 netmask 255.255.255.0" - defaultrouter="10.10.1.1" - -Start the VDE switch as root: - - # sysctl net.link.tap.user_open=1 - # sysctl net.link.tap.up_on_open=1 - # vde_switch -d -s /tmp/vde1 -M /tmp/mgmt1 -tap tap0 -m 660 --mgmtmode 660 - # chmod 660 /dev/tap0 - -You can connect to the VDE switch's management channel using: - - $ vdeterm /tmp/mgmt1 - -To run qemu: - - $ qemu-system-arm \ - -serial null \ - -serial mon:stdio \ - -nographic \ - -M xilinx-zynq-a9 \ - -net nic,model=cadence_gem,macaddr=0e:b0:ba:5e:ba:11 \ - -net vde,id=vde0,sock=/tmp/vde1 - -m 256M \ - -kernel build/arm-rtems5-xilinx_zynq_a9_qemu/rcconf02.exe - -== Issues and TODO - -* PCI support on x86 uses a quick and dirty hack, see pci_reserve_map(). - -* Priority queues are broken with clustered scheduling. - -* Per-CPU data should be enabled once the new stack is ready for SMP. - -* Per-CPU NETISR(9) should be enabled onece the new stack is ready for SMP. - -* Multiple routing tables are not supported. Every FIB value is set to zero - (= BSD_DEFAULT_FIB). - -* Process identifiers are not supported. Every PID value is set to zero - (= BSD_DEFAULT_PID). - -* User credentials are not supported. The following functions allow the - operation for everyone - - prison_equal_ip4(), - - chgsbsize(), - - cr_cansee(), - - cr_canseesocket() and - - cr_canseeinpcb(). - -* A basic USB functionality test that is known to work on Qemu is desirable. - -* Adapt generic IRQ PIC interface code to Simple Vectored Interrupt Model - so that those architectures can use new TCP/IP and USB code. - -* freebsd-userspace/rtems/include/sys/syslog.h is a copy from the old - RTEMS TCP/IP stack. For some reason, the __printflike markers do not - compile in this environment. We may want to use the FreeBSD syslog.h - and get this addressed. - -* in_cksum implementations for architectures not supported by FreeBSD. - This will require figuring out where to put implementations that do - not originate from FreeBSD and are populated via the script. - -* MAC support functions are not thread-safe ("freebsd/lib/libc/posix1e/mac.c"). - -* IFCONFIG(8): IEEE80211 support is disabled. This module depends on a XML - parser and mmap(). - -* get_cyclecount(): The implementation is a security problem. - -* What to do with the priority parameter present in the FreeBSD synchronization - primitives and the thread creation functions? - -* TASKQUEUE(9): Support spin mutexes. - -* ZONE(9): Review allocator lock usage in rtems-bsd-chunk.c. - -* KQUEUE(2): Choose proper lock for global kqueue list. - -* TIMEOUT(9): Maybe use special task instead of timer server to call - callout_tick(). - -* sysctl_handle_opaque(): Implement reliable snapshots. - -* PING6(8): What to do with SIGALARM? - -* <sys/param.h>: Update Newlib to use a MSIZE of 256. - -* BPF(4): Add support for zero-copy buffers. - -* UNIX(4): Fix race conditions in the area of socket object and file node - destruction. Add support for file descriptor transmission via control - messages. - -* PRINTF(9): Add support for log(), the %D format specifier is missing in the - normal printf() family. - -* Why is the interrupt server used? The BSD interrupt handlers can block on -synchronization primitives like mutexes. This is in contrast to RTEMS -interrupt service routines. The BSPs using the generic interrupt support must -implement the `bsp_interrupt_vector_enable()` and -`bsp_interrupt_vector_disable()` routines. They normally enable/disable a -particular interrupt source at the interrupt controller. This can be used to -implement the interrupt server. The interrupt server is a task that wakes-up -in case an associated interrupt happens. The interrupt source is disabled in -a generic interrupt handler that wakes-up the interrupt server task. Once the -postponed interrupt processing is performed in the interrupt server the -interrupt source is enabled again. - -* Convert all BSP linkcmds to use a linkcmds.base so the sections are -easier to insert. - -* NIC Device Drivers -- Only common PCI NIC drivers have been included in the initial set. These -do not include any system on chip or ISA drivers. -- PCI configuration probe does not appear to happen to determine if a -NIC is in I/O or memory space. We have worked around this by using a -static hint to tell the fxp driver the correct mode. But this needs to -be addressed. -- The ISA drivers require more BSD infrastructure to be addressed. This was -outside the scope of the initial porting effort. - -== FreeBSD Source - -You should be able to rely on FreebSD manual pages and documentation -for details on the code itself. - -== BSD Library Source - -== Initialization of the BSD Library - -The initialization of the BSD library is based on the FreeBSD SYSINIT(9) -infrastructure. The key to initializing a system is to ensure that the desired -device drivers are explicitly pulled into the linked application. This plus -linking against the BSD library (`libbsd.a`) will pull in the necessary FreeBSD -infrastructure. - -The FreeBSD kernel is not a library like the RTEMS kernel. It is a bunch of -object files linked together. If we have a library, then creating the -executable is simple. We begin with a start symbol and recursively resolve all -references. With a bunch of object files linked together we need a different -mechanism. Most object files don't know each other. Lets say we have a driver -module. The rest of the system has no references to this driver module. The -driver module needs a way to tell the rest of the system: Hey, kernel I am -here, please use my services! - -This registration of independent components is performed by SYSINIT(9) and -specializations: - -http://www.freebsd.org/cgi/man.cgi?query=SYSINIT - -The SYSINIT(9) uses some global data structures that are placed in a certain -section. In the linker command file we need this: - -------------------------------------------------------------------------------- -.rtemsroset : { - KEEP (*(SORT(.rtemsroset.*))) -} - -.rtemsrwset : { - KEEP (*(SORT(.rtemsrwset.*))) -} -------------------------------------------------------------------------------- - -This results for example in this executable layout: - -------------------------------------------------------------------------------- -[...] - *(SORT(.rtemsroset.*)) - .rtemsroset.bsd.modmetadata_set.begin - 0x000000000025fe00 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000025fe00 _bsd__start_set_modmetadata_set - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe00 0x8 libbsd.a(rtems-bsd-nexus.o) - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe08 0x4 libbsd.a(kern_module.o) -[...] - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe68 0x4 libbsd.a(mii.o) - .rtemsroset.bsd.modmetadata_set.content - 0x000000000025fe6c 0x4 libbsd.a(mii_bitbang.o) - .rtemsroset.bsd.modmetadata_set.end - 0x000000000025fe70 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000025fe70 _bsd__stop_set_modmetadata_set -[...] -.rtemsrwset 0x000000000030bad0 0x290 - *(SORT(.rtemsrwset.*)) - .rtemsrwset.bsd.sysinit_set.begin - 0x000000000030bad0 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000030bad0 _bsd__start_set_sysinit_set - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bad0 0x4 libbsd.a(rtems-bsd-nexus.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bad4 0x8 libbsd.a(rtems-bsd-thread.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030badc 0x4 libbsd.a(init_main.o) -[...] - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bd54 0x4 libbsd.a(frag6.o) - .rtemsrwset.bsd.sysinit_set.content - 0x000000000030bd58 0x8 libbsd.a(uipc_accf.o) - .rtemsrwset.bsd.sysinit_set.end - 0x000000000030bd60 0x0 libbsd.a(rtems-bsd-init.o) - 0x000000000030bd60 _bsd__stop_set_sysinit_set -[...] -------------------------------------------------------------------------------- - -Here you can see, that some global data structures are collected into -continuous memory areas. This memory area can be identified by start and stop -symbols. This constructs a table of uniform items. - -The low level FreeBSD code calls at some time during the initialization the -mi_startup() function (machine independent startup). This function will sort -the SYSINIT(9) set and call handler functions which perform further -initialization. The last step is the scheduler invocation. - -The SYSINIT(9) routines are run in mi_startup() which is called by -rtems_bsd_initialize(). - -This is also explained in "The Design and Implementation of the FreeBSD -Operating System" section 14.3 "Kernel Initialization". - -In RTEMS we have a library and not a bunch of object files. Thus we need a way -to pull-in the desired services out of the libbsd. Here the -`rtems-bsd-sysinit.h` comes into play. The SYSINIT(9) macros have been -modified and extended for RTEMS in `<sys/kernel.h>`: - -------------------------------------------------------------------------------- -#ifndef __rtems__ -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - static struct sysinit uniquifier ## _sys_init = { \ - subsystem, \ - order, \ - func, \ - (ident) \ - }; \ - DATA_SET(sysinit_set,uniquifier ## _sys_init) -#else /* __rtems__ */ -#define SYSINIT_ENTRY_NAME(uniquifier) \ - _bsd_ ## uniquifier ## _sys_init -#define SYSINIT_REFERENCE_NAME(uniquifier) \ - _bsd_ ## uniquifier ## _sys_init_ref -#define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ - struct sysinit SYSINIT_ENTRY_NAME(uniquifier) = { \ - subsystem, \ - order, \ - func, \ - (ident) \ - }; \ - RWDATA_SET(sysinit_set,SYSINIT_ENTRY_NAME(uniquifier)) -#define SYSINIT_REFERENCE(uniquifier) \ - extern struct sysinit SYSINIT_ENTRY_NAME(uniquifier); \ - static struct sysinit const * const \ - SYSINIT_REFERENCE_NAME(uniquifier) __used \ - = &SYSINIT_ENTRY_NAME(uniquifier) -#define SYSINIT_MODULE_REFERENCE(mod) \ - SYSINIT_REFERENCE(mod ## module) -#define SYSINIT_DRIVER_REFERENCE(driver, bus) \ - SYSINIT_MODULE_REFERENCE(driver ## _ ## bus) -#define SYSINIT_DOMAIN_REFERENCE(dom) \ - SYSINIT_REFERENCE(domain_add_ ## dom) -#endif /* __rtems__ */ -------------------------------------------------------------------------------- - -Here you see that the SYSINIT(9) entries are no longer static. The -\*_REFERENCE() macros will create references to the corresponding modules which -are later resolved by the linker. The application has to provide an object -file with references to all required FreeBSD modules. - -The FreeBSD device model is quite elaborated (with follow-ups): - -http://www.freebsd.org/cgi/man.cgi?query=driver - -The devices form a tree with the Nexus device at a high-level. This Nexus -device is architecture specific in FreeBSD. In RTEMS we have our own Nexus -device, see `rtemsbsd/bsp/bsp-bsd-nexus-devices.c`. - -=== SYSCTL_NODE Example - -During development, we had an undefined reference to -_bsd_sysctl__net_children that we had trouble tracking down. Thanks to -Chris Johns, we located it. He explained how to read SYSCTL_NODE -definitions. This line from freebsd/netinet/in_proto.c is attempting -to add the "inet" node to the parent node "_net". - ----- -SYSCTL_NODE(_net, PF_INET, inet, CTLFLAG_RW, 0, - "Internet Family"); ----- - -Our problem was that we could not find where _bsd_sysctl__net_children -was defined. Chris suggested that when in doubt compile with -save-temps -and look at the preprocessed .i files. But he did not need that. He -explained that this the symbol name _bsd_sysctl__net_children was -automatically generated by a SYSCTL_NODE as follows: - -* _bsd_ - added by RTEMS modifications to SYSCTL_NODE macro -* sysctl_ - boilerplace added by SYSCTL_NODE macro -* "" - empty string for parent node -* net - name of SYSCTL_NODE -* children - added by SYSCTL macros - -This was all generated by a support macro declaring the node as this: - ----- -struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name); ----- - -Given this information, we located this SYSCTL_NODE declaration in -kern/kern_mib.c - ----- -SYSCTL_NODE(, CTL_KERN, kern, CTLFLAG_RW, 0, - "High kernel, proc, limits &c"); ----- - -== Core FreeBSD APIs and RTEMS Replacements == - -=== SX(9) (Shared/exclusive locks) === - -http://www.freebsd.org/cgi/man.cgi?query=sx - -Binary semaphores (this neglects the ability to allow shared access). - -=== MUTEX(9) (Mutual exclusion) === - -http://www.freebsd.org/cgi/man.cgi?query=mutex - -Binary semaphores (not recursive mutexes are not supported this way). - -=== RWLOCK(9) (Reader/writer lock) === - -http://www.freebsd.org/cgi/man.cgi?query=rwlock - -POSIX r/w lock. - -=== RMLOCK(9) (Reader/writer lock optimized for mostly read access patterns) === - -Note: This object was implemented as a wrapper for RWLOCK in the rm_lock header file. - -http://www.freebsd.org/cgi/man.cgi?query=rmlock - -POSIX r/w lock. - -=== CONDVAR(9) (Condition variables) === - -http://www.freebsd.org/cgi/man.cgi?query=condvar - -POSIX condition variables with modifications (hack). - -=== CALLOUT(9) (Timer functions) === - -http://www.freebsd.org/cgi/man.cgi?query=callout - -Timer server. - -=== TASKQUEUE(9) (Asynchronous task execution) === - -http://www.freebsd.org/cgi/man.cgi?query=taskqueue - -TBD. - -=== KTHREAD(9), KPROC(9) (Tasks) === - -http://www.freebsd.org/cgi/man.cgi?query=kthread - -http://www.freebsd.org/cgi/man.cgi?query=kproc - -Tasks. - -=== ZONE(9) (Zone allocator) === - -http://www.freebsd.org/cgi/man.cgi?query=zone - -TBD. - -=== devfs (Device file system) === - -There is a minimal implementation based on IMFS. The mount point is fixed to -"/dev". Note that the devfs is only used by the cdev subsystem. cdev has been -adapted so that the full path (including the leading "/dev") is given to devfs. -This saves some copy operations. - -devfs_create() first creates the full path and then creates an IMFS generic node -for the device. - -TBD: remove empty paths on devfs_destroy(). - -=== psignal (Signals) === - -TBD. Seems to be not needed. - -=== poll, select === - -TBD. Seems to be not needed. - -=== RMAN(9) (Resource management) === - -http://www.freebsd.org/cgi/man.cgi?query=rman - -TBD. Seems to be not needed. - -=== DEVCLASS(9), DEVICE(9), DRIVER(9), MAKE_DEV(9) (Device management) === - -http://www.freebsd.org/cgi/man.cgi?query=devclass - -http://www.freebsd.org/cgi/man.cgi?query=device - -http://www.freebsd.org/cgi/man.cgi?query=driver - -http://www.freebsd.org/cgi/man.cgi?query=make_dev - -Use FreeBSD implementation as far as possible. FreeBSD has a nice API for -dynamic device handling. It may be interesting for RTEMS to use this API -internally in the future. - -=== BUS_SPACE(9), BUS_DMA(9) (Bus and DMA access) === - -http://www.freebsd.org/cgi/man.cgi?query=bus_space - -http://www.freebsd.org/cgi/man.cgi?query=bus_dma - -Likely BSP dependent. A default implementation for memory mapped linear access -is easy to provide. The current heap implementation supports all properties -demanded by bus_dma (including the boundary constraint). - -== RTEMS Replacements by File Description == - -Note: Files with a status of USB are used by the USB test and have at least -been partially tested. If they contain both USB and Nic, then they are used -by both and MAY contain methods that have not been tested yet. Files that -are only used by the Nic test are the most suspect. - ----- -rtems-libbsd File: rtems-bsd-assert.c -FreeBSD File: rtems-bsd-config.h redefines BSD_ASSERT. -Description: This file contains the support method rtems_bsd_assert_func(). -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-autoconf.c -FreeBSD File: FreeBSD has BSP specific autoconf.c -Description: This file contains configuration methods that are used to setup the system. -Status: USB - -rtems-libbsd File: rtems-bsd-bus-dma.c -FreeBSD File: FreeBSD has BSP specific busdma_machdep.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-bus-dma-mbuf.c -FreeBSD File: FreeBSD has BSP specific busdma_machdep.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-callout.c -FreeBSD File: kern/kern_timeout.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-cam.c -FreeBSD File: cam/cam_sim.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-condvar.c -FreeBSD File: kern/kern_condvar.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-copyinout.c -FreeBSD File: bsp specific copyinout.c ) -Description: Note: The FreeBSD file is split with some methods being in rtems-bsd-support -Status: Nic - -rtems-libbsd File: rtems-bsd-delay.c -FreeBSD File: bsp specific file with multiple names -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-descrip.c -FreeBSD File: kern/kern_descrip.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-generic.c -FreeBSD File: kern/sys_generic.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-init.c -FreeBSD File: N/A -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-init-with-irq.c -FreeBSD File: N/A -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-jail.c -FreeBSD File: kern/kern_jail.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-lock.c -FreeBSD File: kern/subr_lock.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-log.c -FreeBSD File: kern/subr_prf.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-malloc.c -FreeBSD File: kern/kern_malloc.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-mutex.c -FreeBSD File: kern/kern_mutex.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-newproc.c -FreeBSD File: N/A -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-nexus.c -FreeBSD File: bsp specific nexus.c -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-panic.c -FreeBSD File: boot/common/panic.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-rwlock.c -FreeBSD File: kern_rwlock.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-shell.c -FreeBSD File: N/A -Description: -Status: USB - -rtems-libbsd File: rtems-bsd-signal.c -FreeBSD File: kern/kern_sig.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-smp.c -FreeBSD File: N/A -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-support.c -FreeBSD File: bsp specific copyinout.c -Description: Note: the FreeBSD file is split with some methods being in rtems-bsd-copyinout. -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-sx.c -FreeBSD File: kern/kern_sx.c -Description: Status: USB, Nic - -rtems-libbsd File: rtems-bsd-synch.c -FreeBSD File: kern/kern_synch.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-syscalls.c -FreeBSD File: User API for kern/uipc_syscalls.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-sysctlbyname.c -FreeBSD File: User API for sysctlbyname(3) -Description: -Status: - -rtems-libbsd File: rtems-bsd-sysctl.c -FreeBSD File: User API for sysctl(8) -Description: -Status: - -rtems-libbsd File: rtems-bsd-sysctlnametomib.c -FreeBSD File: User API for sysctlnametomib -Description: -Status: - -rtems-libbsd File: rtems-bsd-taskqueue.c -FreeBSD File: kern/subr_taskqueue.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-thread.c -FreeBSD File: kern/kern_kthread.c -Description: -Status: USB, Nic - -rtems-libbsd File: rtems-bsd-timeout.c -FreeBSD File: kern/kern_timeout.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-timesupport.c -FreeBSD File: kern/kern_clock.c -Description: -Status: Nic - -rtems-libbsd File: rtems-bsd-vm_glue.c -FreeBSD File: vm/vm_glue.c -Description: -Status: USB, Nic ----- - -== Notes by File == - -altq_subr.c - Arbitrary choices were made in this file that RTEMS would -not support tsc frequency change. Additionally, the clock frequency -for machclk_freq is always measured for RTEMS. - -conf.h - In order to add make_dev and destroy_dev, variables in the cdev -structure that were not being used were conditionally compiled out. The -capability of supporting children did not appear to be needed and was -not implemented in the rtems version of these routines. - -== NICs Status == - ----- -Driver Symbol Status -====== ====== ====== -RealTek _bsd_re_pcimodule_sys_init Links -EtherExpress _bsd_fxp_pcimodule_sys_init Links -DEC tulip _bsd_dc_pcimodule_sys_init Links -Broadcom BCM57xxx _bsd_bce_pcimodule_sys_init Links -Broadcom BCM4401 _bsd_bfe_pcimodule_sys_init Links -Broadcom BCM570x _bsd_bge_pcimodule_sys_init Needs Symbols (A) -E1000 IGB _bsd_igb_pcimodule_sys_init Links -E1000 EM _bsd_em_pcimodule_sys_init Links -Cadence ? Links, works. ----- - -To add a NIC edit rtemsbsd/include/bsp/nexus-devices.h and add the driver -reference to the architecture and/or BSP. For example to add the RealTek driver -add: - -SYSINIT_DRIVER_REFERENCE(re, pci); - -and to add the MII PHY driver add: - -SYSINIT_DRIVER_REFERENCE(rge, miibus); - -The PC BSP has these entries. - -Symbols (A) - pci_get_vpd_ident - -=== Cadence === - -The cadence driver works on the Xilinx Zynq platform. The hardware checksum -support works on real hardware but does not seem to be supported on qemu -therefore the default state is to disable TXCSUM and RXCSUM and this can be -enabled from the shell with: - - # ifconfig cgem0 rxcsum txcsum - -or with an ioctl call to the network interface driver with SIOCSIFCAP and the -mask IFCAP_TXCSUM and IFCAP_RXCSUM set. - -== PF (Firewall) == - -It is possible to use PF as a firewall. See -[https://www.freebsd.org/doc/handbook/firewalls-pf.html] for details on the -range of functions and for how to configure the firewall. - -The following is necessary to use PF on RTEMS: - -- You have to provide a +/etc/pf.os+ file. The firewall can use it for passive - OS fingerprinting. If you don't want to use this feature, the file may contain - nothing except a line of comment (for example "# empty"). - -- If some filters use protocol names (like tcp or udp) you have to provide a - +/etc/protocols+ file. - -- If some filters use service names (like ssh or http) you have to provide a - +/etc/services+ file. - -- Create a rule file (normally +/etc/pf.conf+). See the FreeBSD manual for the - syntax. - -- Load the rule file using the pfctl command and enable pf. Please note that the - pfctl command needs a lot of stack. You should use at least - RTEMS_MINIMUM_STACK_SIZE + 8192 Bytes of stack. An example initialisation can - look like follows: - ----- - int exit_code; - char *params[] = { - "pfctl", - "-f", - "/etc/pf.conf", - "-e", - NULL - }; - - exit_code = rtems_bsd_command_pfctl(ARGC(params), params); - assert(exit_code == EXIT_SUCCSESS); ----- - -=== Known restrictions === - -- Currently PF on RTEMS always uses the configuration for memory restricted - systems (on FreeBSD that means systems with less than 100 MB RAM). This is - fixed in +pfctl_init_options()+. - -== Wireless Network (WLAN) == - -The libbsd provides a basic support for WLAN. Note that currently this support -is still in an early state. The WLAN support is _not_ enabled in the default -buildset. You have to configure libbsd with the -`--buildset=buildset/everything.ini` to enable that feature. - -The following gives a rough overview over the necessary steps to connect to an -encrypted network with an RTL8188EU based WiFi dongle: - -- Reference all necessary module for your BSP. For some BSPs this is already - done in the nexus-devices.h: - ----- - SYSINIT_MODULE_REFERENCE(wlan_ratectl_none); - SYSINIT_MODULE_REFERENCE(wlan_sta); - SYSINIT_MODULE_REFERENCE(wlan_amrr); - SYSINIT_MODULE_REFERENCE(wlan_wep); - SYSINIT_MODULE_REFERENCE(wlan_tkip); - SYSINIT_MODULE_REFERENCE(wlan_ccmp); - SYSINIT_DRIVER_REFERENCE(rtwn_usb, uhub); - SYSINIT_REFERENCE(rtwn_rtl8188eufw); ----- - -- Create your wlan device using ifconfig: - +ifconfig wlan0 create wlandev rtwn0 up+ - -- Start a wpa_supplicant instance for that device: - + wpa_supplicant_fork -Dbsd -iwlan0 -c/media/mmcsd-0-0/wpa_supplicant.conf+ - -Note that the wpa_supplicant will only be active till the device goes down. A -workaround is to just restart it every time it exits. - -=== Known restrictions === - -- The network interface (e.g. wlan0) is currently not automatically created. It - would be nice, if some service would create it as soon as for example a USB - device is connected. In FreeBSD the names are assigned via rc.conf with lines - like +wlans_rtwn0="wlan0"+. - -- wpa_supplicant hast to be started after the device is created. It has to be - restarted every time the connection goes down. Instead of this behaviour, - there should be some service that starts and restarts wpa_supplicant - automatically if a interface is ready. Probably the dhcpcd hooks could be used - for that. - -- The current wpa_supplicant implementation is protected with a lock so it can't - be started more than one time. If multiple interface should be used, all have - to be handled by that single instance. That makes it hard to add interfaces - dynamically. wpa_supplicant should be reviewed thoroughly whether multiple - instances could be started in parallel. - -- The control interface of wpa_supplicant most likely doesn't work. The wpa_cli - application is not ported. - -== IPSec == - -The IPSec support is optional in libbsd. It is disabled in the default build -set. Please make sure to use a build set with +netipsec = on+. - -To use IPSec the following configuration is necessary: - ----- -SYSINIT_MODULE_REFERENCE(if_gif); -SYSINIT_MODULE_REFERENCE(cryptodev); -RTEMS_BSD_RC_CONF_SYSINT(rc_conf_ipsec) -RTEMS_BSD_DEFINE_NEXUS_DEVICE(cryptosoft, 0, 0, NULL); ----- - -Alternatively you can use the `RTEMS_BSD_CONFIG_IPSEC` which also includes the -rc.conf support for ipsec. It's still necessary to include a crypto device in -your config (`cryptosoft` in the above sample). - -The necessary initialization steps for a IPSec connection are similar to the -steps on a FreeBSD-System. The example assumes the following setup: - -- RTEMS external IP: 192.168.10.1/24 -- RTEMS internal IP: 10.10.1.1/24 -- remote external IP: 192.168.10.10/24 -- remote internal IP: 172.24.0.1/24 -- shared key: "mysecretkey" - -With this the following steps are necessary: - -- Create a gif0 device: - ----- -SHLL [/] # ifconfig gif0 create ----- - -- Configure the gif0 device: - ----- -SHLL [/] # ifconfig gif0 10.10.1.1 172.24.0.1 -SHLL [/] # ifconfig gif0 tunnel 192.168.10.1 192.168.10.10 ----- - -- Add a route to the remote net via the remote IP: - ----- -SHLL [/] # route add 172.24.0.0/24 172.24.0.1 ----- - -- Call `setkey` with a correct rule set: - ----- -SHLL [/] # cat /etc/setkey.conf -flush; -spdflush; -spdadd 10.10.1.0/24 172.24.0.0/24 any -P out ipsec esp/tunnel/192.168.10.1-192.168.10.10/use; -spdadd 172.24.0.0/24 10.10.1.0/24 any -P in ipsec esp/tunnel/192.168.10.10-192.168.10.1/use; -SHLL [/] # setkey -f /etc/setkey.conf ----- - -- Start a ike-daemon (racoon) with a correct configuration. ----- -SHLL [/] # cat /etc/racoon.conf -path pre_shared_key "/etc/racoon_psk.txt"; -log info; - -padding # options are not to be changed -{ - maximum_length 20; - randomize off; - strict_check off; - exclusive_tail off; -} - -listen # address [port] that racoon will listen on -{ - isakmp 192.168.10.1[500]; -} - -remote 192.168.10.10 [500] -{ - exchange_mode main; - my_identifier address 192.168.10.1; - peers_identifier address 192.168.10.10; - proposal_check obey; - - proposal { - encryption_algorithm 3des; - hash_algorithm md5; - authentication_method pre_shared_key; - lifetime time 3600 sec; - dh_group 2; - } -} - -sainfo (address 10.10.1.0/24 any address 172.24.0.0/24 any) -{ - pfs_group 2; - lifetime time 28800 sec; - encryption_algorithm 3des; - authentication_algorithm hmac_md5; - compression_algorithm deflate; -} -SHLL [/] # cat /etc/racoon_psk.txt -192.168.10.10 mysecretkey -SHLL [/] # racoon -F -f /etc/racoon.conf ----- - -All commands can be called via the respective API functions. For racoon there is -a `rtems_bsd_racoon_daemon()` function that forks of racoon as a task. - -Alternatively IPSec can also be configured via rc.conf entries: - ----- -cloned_interfaces="gif0" -ifconfig_gif0="10.10.1.1 172.24.0.1 tunnel 192.168.10.1 192.168.10.10" -ike_enable="YES" -ike_program="racoon" -ike_flags="-F -f /etc/racoon.conf" -ike_priority="250" - -ipsec_enable="YES" -ipsec_file="/etc/setkey.conf" ----- - -ATTENTION: It is possible that the first packets slip through the tunnel without -encryption (true for FreeBSD as well as RTEMS). You might want to set up a -firewall rule to prevent that. - -== Problems to report to FreeBSD == - -The MMAP_NOT_AVAILABLE define is inverted on its usage. When it is -defined the mmap method is called. Additionally, it is not used -thoroughly. It is not used in the unmap portion of the source. -The file rec_open.c uses the define MMAP_NOT_AVAILABLE to wrap -the call to mmap and file rec_close.c uses the munmap method. diff --git a/rtemsbsd/arm/include/arm/lpc/probe.h b/rtemsbsd/arm/include/arm/lpc/probe.h new file mode 100644 index 00000000..54f9ebda --- /dev/null +++ b/rtemsbsd/arm/include/arm/lpc/probe.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_LPC_PROBE_H +#define _ARM_LPC_PROBE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int lpc_eth_probe(int unit); + +int lpc_ohci_probe(int unit); + +#ifdef __cplusplus +} +#endif + +#endif /* _ARM_LPC_PROBE_H */ diff --git a/rtemsbsd/include/bsp/mv643xx_eth.h b/rtemsbsd/include/bsp/mv643xx_eth.h new file mode 100644 index 00000000..7d122cd6 --- /dev/null +++ b/rtemsbsd/include/bsp/mv643xx_eth.h @@ -0,0 +1,397 @@ +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifndef MV643XX_LOWLEVEL_DRIVER_H +#define MV643XX_LOWLEVEL_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MV643XXETH_NUM_DRIVER_SLOTS 2 + +struct mveth_private; + +/* Create interface. + * Allocates resources for descriptor rings and sets up the driver software structure. + * + * Arguments: + * unit: + * interface # (1..2). The interface must not be attached to BSD. + * + * driver_tid: + * optional driver task-id (can be retrieved with BSP_mve_get_tid()). + * Not used by the low-level driver. + * + * isr(isr_arg): + * user ISR. + * + * void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred): + * Pointer to user-supplied callback to release a buffer that had been sent + * by BSP_mve_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg' + * and a flag indicating whether the send had been successful. + * The driver no longer accesses 'user_buf' after invoking this callback. + * CONTEXT: This callback is executed either by BSP_mve_swipe_tx() or + * BSP_mve_send_buf(), BSP_mve_init_hw(), BSP_mve_stop_hw() (the latter + * ones calling BSP_mve_swipe_tx()). + * void *cleanup_txbuf_arg: + * Closure argument that is passed on to 'cleanup_txbuf()' callback; + * + * void *(*alloc_rxbuf)(int *p_size, unsigned long *p_data_addr), + * Pointer to user-supplied callback to allocate a buffer for subsequent + * insertion into the RX ring by the driver. + * RETURNS: opaque handle to the buffer (which may be a more complex object + * such as an 'mbuf'). The handle is not used by the driver directly + * but passed back to the 'consume_rxbuf()' callback. + * Size of the available data area and pointer to buffer's data area + * in '*psize' and '*p_data_area', respectively. + * If no buffer is available, this routine should return NULL in which + * case the driver drops the last packet and re-uses the last buffer + * instead of handing it out to 'consume_rxbuf()'. + * CONTEXT: Called when initializing the RX ring (BSP_mve_init_hw()) or when + * swiping it (BSP_mve_swipe_rx()). + * + * + * void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len); + * Pointer to user-supplied callback to pass a received buffer back to + * the user. The driver no longer accesses the buffer after invoking this + * callback (with 'len'>0, see below). 'user_buf' is the buffer handle + * previously generated by 'alloc_rxbuf()'. + * The callback is passed 'cleanup_rxbuf_arg' and a 'len' + * argument giving the number of bytes that were received. + * 'len' may be <=0 in which case the 'user_buf' argument is NULL. + * 'len' == 0 means that the last 'alloc_rxbuf()' had failed, + * 'len' < 0 indicates a receiver error. In both cases, the last packet + * was dropped/missed and the last buffer will be re-used by the driver. + * NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header + * is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes) + * is appended. 'len' accounts for both. + * CONTEXT: Called from BSP_mve_swipe_rx(). + * void *cleanup_rxbuf_arg: + * Closure argument that is passed on to 'consume_rxbuf()' callback; + * + * rx_ring_size, tx_ring_size: + * How many big to make the RX and TX descriptor rings. Note that the sizes + * may be 0 in which case a reasonable default will be used. + * If either ring size is < 0 then the RX or TX will be disabled. + * Note that it is illegal in this case to use BSP_mve_swipe_rx() or + * BSP_mve_swipe_tx(), respectively. + * + * irq_mask: + * Interrupts to enable. OR of flags from above. + * + */ + +/* Direct assignment of MVE flags to user API relies on irqs and x-irqs not overlapping */ +#define MV643XX_ETH_IRQ_RX_DONE (1<<2) +#define MV643XX_ETH_EXT_IRQ_TX_DONE (1<<0) +#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16) + +struct mveth_private * +BSP_mve_create( + int unit, + rtems_id tid, + void (*isr)(void*isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +); + +/* + * Clear multicast hash filter. No multicast frames are accepted + * after executing this routine (unless the hardware was initialized + * in 'promiscuous' mode). + */ +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp); + +/* + * Program multicast filter to accept all multicast frames + */ +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp); + +/* + * Add a MAC address to the multicast filter. + * Existing entries are not changed but note that + * the filter is imperfect, i.e., multiple MAC addresses + * may alias to a single filter entry. Hence software + * filtering must still be performed. + * + * If a higher-level driver implements IP multicasting + * then multiple IP addresses may alias to the same MAC + * address. This driver maintains a 'reference-count' + * which is incremented every time the same MAC-address + * is passed to this routine; the address is only removed + * from the filter if BSP_mve_mcast_filter_accept_del() + * is called the same number of times (or by BSP_mve_mcast_filter_clear). + */ +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr); + +/* + * Remove a MAC address from the multicast filter. + * This routine decrements the reference count of the given + * MAC-address and removes it from the filter once the + * count reaches zero. + */ +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr); + + +/* Enable/disable promiscuous mode */ +void +BSP_mve_promisc_set(struct mveth_private *mp, int promisc); + +/* calls BSP_mve_stop_hw(), releases all resources and marks the interface + * as unused. + * RETURNS 0 on success, nonzero on failure. + * NOTE: the handle MUST NOT be used after successful execution of this + * routine. + */ +int +BSP_mve_detach(struct mveth_private *mp); + +/* Enqueue a buffer chain for transmission. + * + * RETURN: #bytes sent or -1 if there are not enough descriptors + * -2 is returned if the caller should attempt to + * repackage the chain into a smaller one. + * + * Comments: software cache-flushing incurs a penalty if the + * packet cannot be queued since it is flushed anyways. + * The algorithm is slightly more efficient in the normal + * case, though. + */ + +typedef struct MveEthBufIter { + void *hdl; /* opaque handle for the iterator implementor */ + void *data; /* data to be sent */ + size_t len; /* data size (in octets) */ + void *uptr; /* user-pointer to go into the descriptor; + note: cleanup_txbuf is only called for desriptors + where this field is non-NULL (for historical + reasons) */ +} MveEthBufIter; + +typedef MveEthBufIter *(*MveEthBufIterNext)(MveEthBufIter*); + +int +BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it); + + +/* Legacy entry point to send a header + a buffer */ +int +BSP_mve_send_buf_raw(struct mveth_private *mp, void *head_p, int h_len, void *data_p, int d_len); + +/* Descriptor scavenger; cleanup the TX ring, passing all buffers + * that have been sent to the cleanup_tx() callback. + * This routine is called from BSP_mve_send_buf(), BSP_mve_init_hw(), + * BSP_mve_stop_hw(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_tx(struct mveth_private *mp); + +/* Retrieve all received buffers from the RX ring, replacing them + * by fresh ones (obtained from the alloc_rxbuf() callback). The + * received buffers are passed to consume_rxbuf(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_rx(struct mveth_private *mp); + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr); + +/* Interrupt related routines */ + +/* Note: the BSP_mve_enable/disable/ack_irqs() entry points + * are deprecated. + * The newer API where the user passes a mask allows + * for more selective control. + */ + +/* Enable all supported interrupts at device */ +void +BSP_mve_enable_irqs(struct mveth_private *mp); + +/* Disable all supported interrupts at device */ +void +BSP_mve_disable_irqs(struct mveth_private *mp); + +/* Acknowledge (and clear) all supported interrupts. + * RETURNS: interrupts that were raised. + */ +uint32_t +BSP_mve_ack_irqs(struct mveth_private *mp); + +/* Enable interrupts included in 'mask' (leaving + * already enabled interrupts on). If the mask + * includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + */ +void +BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Disable interrupts included in 'mask' (leaving + * other ones that are currently enabled on). If the + * mask includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + * + * RETURNS: Bitmask of interrupts that were enabled upon entry + * into this routine. This can be used to restore the + * previous state. + */ +uint32_t +BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Acknowledge and clear selected interrupts. + * + * RETURNS: All pending interrupts. + * + * NOTE: Only pending interrupts contained in 'mask' + * are cleared. Others are left pending. + * + * This routine can be used to check for pending + * interrupts (pass mask == 0) or to clear all + * interrupts (pass mask == -1). + */ +uint32_t +BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask); + +/* Retrieve the driver daemon TID that was passed to + * BSP_mve_setup(). + */ + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp); + +/* Dump statistics to file (stdout if NULL) + * + * NOTE: this routine is not thread safe + */ +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f); + +#define MV643XX_MEDIA_LINK (1<<0) +#define MV643XX_MEDIA_FD (1<<1) +#define MV643XX_MEDIA_10 (1<<8) +#define MV643XX_MEDIA_100 (2<<8) +#define MV643XX_MEDIA_1000 (3<<8) +#define MV643XX_MEDIA_SPEED_MSK (0xff00) + +/* + * Initialize interface hardware + * + * 'mp' handle obtained by from BSP_mve_setup(). + * 'promisc' whether to set promiscuous flag. + * 'enaddr' pointer to six bytes with MAC address. Read + * from the device if NULL. + * 'speed' current speed and link status as read from the PHY. + * + * Note: Multicast filters are cleared by this routine. + * However, in promiscuous mode the mcast filters + * are programmed to accept all multicast frames. + */ +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int speed); + +/* + * Update the serial port to a new speed (e.g., result of a PHY IRQ) + */ +void +BSP_mve_update_serial_port(struct mveth_private *mp, int speed); + +/* + * Shutdown hardware and clean out the rings + */ +void +BSP_mve_stop_hw(struct mveth_private *mp); + +unsigned +BSP_mve_mii_read(struct mveth_private *mp, unsigned addr); + +unsigned +BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v); + +unsigned +BSP_mve_mii_read_early(int port, unsigned addr); + +unsigned +BSP_mve_mii_write_early(int port, unsigned addr, unsigned v); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h index 92125619..37008cc6 100644 --- a/rtemsbsd/include/bsp/nexus-devices.h +++ b/rtemsbsd/include/bsp/nexus-devices.h @@ -54,7 +54,6 @@ RTEMS_BSD_DRIVER_SMC0(0x4e000000, RVPBXA9_IRQ_ETHERNET); RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL); SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus); SYSINIT_DRIVER_REFERENCE(ti_scm, simplebus); -SYSINIT_DRIVER_REFERENCE(ti_pinmux, simplebus); SYSINIT_DRIVER_REFERENCE(am335x_prcm, simplebus); SYSINIT_DRIVER_REFERENCE(am335x_pwmss, simplebus); SYSINIT_DRIVER_REFERENCE(am335x_lcd, simplebus); @@ -109,6 +108,20 @@ RTEMS_BSD_DRIVER_XILINX_ZYNQ_SLCR; RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM0(ZYNQ_IRQ_ETHERNET_0); RTEMS_BSD_DRIVER_E1000PHY; +#elif defined(LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_H) + +#include <bsp/irq.h> + +RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0(ZYNQMP_IRQ_ETHERNET_0); +RTEMS_BSD_DRIVER_E1000PHY; + +#elif defined(LIBBSP_AARCH64_XILINX_VERSAL_BSP_H) + +#include <bsp/irq.h> + +RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0(VERSAL_IRQ_ETHERNET_0); +RTEMS_BSD_DRIVER_E1000PHY; + #elif defined(LIBBSP_ARM_ATSAM_BSP_H) RTEMS_BSD_DRIVER_USB; @@ -152,6 +165,22 @@ RTEMS_BSD_DRIVER_MMC; SYSINIT_DRIVER_REFERENCE(ofw_regulator_bus, simplebus); +#elif defined(LIBBSP_ARM_IMXRT_BSP_H) + +RTEMS_BSD_DEFINE_NEXUS_DEVICE(ofwbus, 0, 0, NULL); +SYSINIT_DRIVER_REFERENCE(simplebus, ofwbus); + +SYSINIT_DRIVER_REFERENCE(ffec, simplebus); +SYSINIT_DRIVER_REFERENCE(ksz8091rnb, miibus); + +#if IMXRT_IS_MIMXRT11xx +SYSINIT_DRIVER_REFERENCE(ehci, simplebus); +SYSINIT_DRIVER_REFERENCE(imxrt1166_usbphy, simplebus); +SYSINIT_DRIVER_REFERENCE(usbus, ehci); +RTEMS_BSD_DRIVER_USB; +RTEMS_BSD_DRIVER_USB_MASS; +#endif /* IMXRT_IS_IMXRT11xx */ + #elif defined(LIBBSP_ARM_LPC24XX_BSP_H) RTEMS_BSD_DEFINE_NEXUS_DEVICE(ohci, 0, 0, NULL); @@ -159,6 +188,31 @@ SYSINIT_DRIVER_REFERENCE(usbus, ohci); RTEMS_BSD_DRIVER_USB; RTEMS_BSD_DRIVER_USB_MASS; +#elif defined(LIBBSP_ARM_STM32H7_BSP_H) + +#include <stm32h7xx.h> + +RTEMS_BSD_DEFINE_NEXUS_DEVICE(stmac, 0, 0, NULL); +SYSINIT_DRIVER_REFERENCE(ukphy, miibus); + +static const rtems_bsd_device_resource dwcotg_res[] = { + { + .type = RTEMS_BSD_RES_MEMORY, + .start_request = 0, + .start_actual = USB2_OTG_FS_PERIPH_BASE + }, { + .type = RTEMS_BSD_RES_IRQ, + .start_request = 0, + .start_actual = OTG_FS_IRQn + } +}; +RTEMS_BSD_DEFINE_NEXUS_DEVICE(dwcotg, 0, RTEMS_ARRAY_SIZE(dwcotg_res), + dwcotg_res); +RTEMS_BSD_DRIVER_ST_SDMMC(0, SDMMC1_BASE, DLYB_SDMMC1_BASE, SDMMC1_IRQn); +RTEMS_BSD_DRIVER_MMC; +RTEMS_BSD_DRIVER_USB; +RTEMS_BSD_DRIVER_USB_MASS; + #elif defined(LIBBSP_I386_PC386_BSP_H) RTEMS_BSD_DRIVER_PC_LEGACY; @@ -205,6 +259,15 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus); RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL); SYSINIT_DRIVER_REFERENCE(ukphy, miibus); -#endif +#elif defined(LIBBSP_BEATNIK_BSP_H) + +RTEMS_BSD_DEFINE_NEXUS_DEVICE(mve, 0, 0, NULL); +SYSINIT_DRIVER_REFERENCE(ukphy, miibus); + +#elif defined(LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H) + +RTEMS_BSD_DRIVER_PC_LEGACY; + +#endif /* LIBBSP_POWERPC_MOTOROLA_POWERPC_BSP_H */ #endif diff --git a/rtemsbsd/include/bsp/st-sdmmc-config.h b/rtemsbsd/include/bsp/st-sdmmc-config.h new file mode 100644 index 00000000..b4415991 --- /dev/null +++ b/rtemsbsd/include/bsp/st-sdmmc-config.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BSP_ST_SDMMC_CONFIG_H +#define BSP_ST_SDMMC_CONFIG_H + +#include <stdbool.h> +#include <stdint.h> +#include <stm32h7/hal.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct st_sdmmc_config { + /** + * Number of data lines. Can be 1, 4 or 8 + */ + uint8_t data_lines; + /** + * Polarity of the DIR pins. See "SDMMC_POWER" register in the + * STM32H7xx data sheet for that. If you don't have the lines, you can + * use any value. + */ + bool dirpol; + /** + * Possible OCR voltages. Should be something like + * MMC_OCR_290_300 | MMC_OCR_300_310 depending on card supply. + */ + uint32_t ocr_voltage; +}; + +/** + * Get hardware specific configuration for SDMMC. + * + * This function can be overwritten in the application to adapt to another board + * configuration. The sdmmc_base points to the base address of the SDMMC + * instance. The cfg structure is set to zero before calling this function so + * that only the necessary fields have to be initialized. + */ +void st_sdmmc_get_config(uintptr_t sdmmc_base, struct st_sdmmc_config *cfg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* BSP_ST_SDMMC_CONFIG_H */ diff --git a/rtemsbsd/include/machine/_kernel_if.h b/rtemsbsd/include/machine/_kernel_if.h index 08086658..16733fe3 100644 --- a/rtemsbsd/include/machine/_kernel_if.h +++ b/rtemsbsd/include/machine/_kernel_if.h @@ -41,6 +41,20 @@ MALLOC_DECLARE(M_IFADDR); MALLOC_DECLARE(M_IFMADDR); #endif +extern struct sx ifnet_detach_sxlock; + +struct nvlist; +struct ifcap_nv_bit_name; +int if_capnv_to_capint(const struct nvlist *nv, int *old_cap, + const struct ifcap_nv_bit_name *nn, bool all); +void if_capint_to_capnv(struct nvlist *nv, + const struct ifcap_nv_bit_name *nn, int ifr_cap, int ifr_req); +struct siocsifcapnv_driver_data { + int reqcap; + int reqcap2; + struct nvlist *nvcap; +}; + #define ifr_buffer ifr_ifru.ifru_buffer /* user supplied buffer with its length */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ diff --git a/rtemsbsd/include/machine/_kernel_socket.h b/rtemsbsd/include/machine/_kernel_socket.h index e9acc744..3acee460 100644 --- a/rtemsbsd/include/machine/_kernel_socket.h +++ b/rtemsbsd/include/machine/_kernel_socket.h @@ -46,6 +46,7 @@ #define MSG_SOCALLBCK 0x00010000 /* for use by socket callbacks - soreceive (TCP) */ #define MSG_MORETOCOME 0x00100000 /* additional data pending */ +#define MSG_TLSAPPDATA 0x00200000 /* do not soreceive() alert rec. (TLS) */ #define CMSG_ALIGN(n) _ALIGN(n) diff --git a/rtemsbsd/include/machine/frame.h b/rtemsbsd/include/machine/frame.h new file mode 100644 index 00000000..936ffd88 --- /dev/null +++ b/rtemsbsd/include/machine/frame.h @@ -0,0 +1 @@ +/* EMPTY */ diff --git a/rtemsbsd/include/machine/rtems-bsd-cache.h b/rtemsbsd/include/machine/rtems-bsd-cache.h index 73b55e25..e292b216 100755 --- a/rtemsbsd/include/machine/rtems-bsd-cache.h +++ b/rtemsbsd/include/machine/rtems-bsd-cache.h @@ -45,7 +45,7 @@ #if defined(LIBBSP_ARM_LPC24XX_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && defined(LPC32XX_DISABLE_MMU)) /* No cache */ #elif defined(LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H) || \ - defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H) + defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || (defined(LIBBSP_ARM_LPC32XX_BSP_H) && !defined(LPC32XX_DISABLE_MMU)) || defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H) /* With cache, no coherency support in hardware */ #define CPU_DATA_CACHE_ALIGNMENT 32 #elif defined(__GEN83xx_BSP_h) diff --git a/rtemsbsd/include/machine/rtems-bsd-commands.h b/rtemsbsd/include/machine/rtems-bsd-commands.h index c471d283..a517ed7b 100644 --- a/rtemsbsd/include/machine/rtems-bsd-commands.h +++ b/rtemsbsd/include/machine/rtems-bsd-commands.h @@ -50,6 +50,8 @@ int rtems_bsd_command_arp(int argc, char **argv); int rtems_bsd_command_ifconfig(int argc, char **argv); +int rtems_bsd_command_ifmcstat(int argc, char **argv); + int rtems_bsd_command_netstat(int argc, char **argv); int rtems_bsd_command_nvmecontrol(int argc, char **argv); diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h index 75b744a4..97cdb625 100644 --- a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h +++ b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h @@ -5041,7 +5041,6 @@ #define t_functions _bsd_t_functions #define t_functions_inited _bsd_t_functions_inited #define ti_am335x_clk_devmap _bsd_ti_am335x_clk_devmap -#define ti_am335x_pinmux_dev _bsd_ti_am335x_pinmux_dev #define tick _bsd_tick #define ticket_altqs_active _bsd_ticket_altqs_active #define ticket_altqs_inactive _bsd_ticket_altqs_inactive @@ -5056,10 +5055,6 @@ #define _timeout_task_init _bsd__timeout_task_init #define timevaladd _bsd_timevaladd #define timevalsub _bsd_timevalsub -#define ti_pinmux_padconf_get _bsd_ti_pinmux_padconf_get -#define ti_pinmux_padconf_get_gpiomode _bsd_ti_pinmux_padconf_get_gpiomode -#define ti_pinmux_padconf_set _bsd_ti_pinmux_padconf_set -#define ti_pinmux_padconf_set_gpiomode _bsd_ti_pinmux_padconf_set_gpiomode #define ti_prcm_clk_disable _bsd_ti_prcm_clk_disable #define ti_prcm_clk_enable _bsd_ti_prcm_clk_enable #define ti_prcm_clk_get_source_freq _bsd_ti_prcm_clk_get_source_freq diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h index 09bcecf1..f2f1b91f 100644 --- a/rtemsbsd/include/machine/rtems-bsd-kernel-space.h +++ b/rtemsbsd/include/machine/rtems-bsd-kernel-space.h @@ -55,6 +55,12 @@ /* General define to activate BSD kernel parts */ #define _KERNEL 1 +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IN_HISTORICAL_NETS + +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IFCAP_NOMAP 0x4000000 + #include <machine/rtems-bsd-version.h> #include <machine/rtems-bsd-kernel-namespace.h> diff --git a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h index ff545dc0..50a43873 100644 --- a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h +++ b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h @@ -243,6 +243,28 @@ extern "C" { SYSINIT_DRIVER_REFERENCE(mmcsd, mmc) #endif /* RTEMS_BSD_DRIVER_MMC */ +#if !defined(RTEMS_BSD_DRIVER_ST_SDMMC) + #define RTEMS_BSD_DRIVER_ST_SDMMC(_num, _base, _dlyb, _irq) \ + static const rtems_bsd_device_resource st_sdmmc ## _num ## _res[] = { \ + { \ + .type = RTEMS_BSD_RES_MEMORY, \ + .start_request = 0, \ + .start_actual = (_base) \ + }, { \ + .type = RTEMS_BSD_RES_MEMORY, \ + .start_request = 1, \ + .start_actual = (_dlyb) \ + }, { \ + .type = RTEMS_BSD_RES_IRQ, \ + .start_request = 0, \ + .start_actual = (_irq) \ + } \ + }; \ + RTEMS_BSD_DEFINE_NEXUS_DEVICE(st_sdmmc, 0, \ + RTEMS_ARRAY_SIZE(st_sdmmc ## _num ## _res), \ + &st_sdmmc ## _num ## _res[0]) +#endif /* RTEMS_BSD_DRIVER_ST_SDMMC */ + /* * USB Drivers. */ @@ -371,6 +393,33 @@ extern "C" { #define RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM1(_irq) \ RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xe000c000, _irq) #endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM1 */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0) + #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(0, 0xff0b0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM0 */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1) + #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xff0c0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM1 */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2) + #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(2, 0xff0d0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM2 */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3) + #define RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(3, 0xff0e0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_ZYNQMP_CGEM3 */ +/* + * Versal has a similar GEM as the CGEM. This should work for now. + */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0) + #define RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(0, 0xff0c0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM0 */ +#if !defined(RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1) + #define RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1(_irq) \ + RTEMS_BSD_DRIVER_XILINX_ZYNQ_CGEM(1, 0xff0d0000, _irq) +#endif /* RTEMS_BSD_DRIVER_XILINX_VERSAL_GEM1 */ /* * Designware/Synopsys Ethernet MAC Controller. diff --git a/rtemsbsd/include/machine/rtems-bsd-program.h b/rtemsbsd/include/machine/rtems-bsd-program.h index f71ac9cd..3062c1a2 100644 --- a/rtemsbsd/include/machine/rtems-bsd-program.h +++ b/rtemsbsd/include/machine/rtems-bsd-program.h @@ -60,6 +60,12 @@ rtems_bsd_program_call_main_with_data_restore(const char *name, int (*main)(int, char **), int argc, char **argv, void *data_buf, const size_t data_size); +void * +rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg); + +void +rtems_bsd_program_remove_destructor(void *cookie, bool call); + void rtems_bsd_program_exit(int exit_code) __dead2; @@ -198,7 +204,7 @@ rtems_bsd_program_free(void *ptr); #endif #ifndef RTEMS_BSD_PROGRAM_NO_FREE_WRAP - #define free(ptr) rtems_bsd_program_free(ptr); + #define free(ptr) rtems_bsd_program_free(ptr) #endif __END_DECLS diff --git a/rtemsbsd/include/machine/rtems-bsd-user-space.h b/rtemsbsd/include/machine/rtems-bsd-user-space.h index 28d5dd5a..93113b9c 100644 --- a/rtemsbsd/include/machine/rtems-bsd-user-space.h +++ b/rtemsbsd/include/machine/rtems-bsd-user-space.h @@ -41,6 +41,7 @@ #define _RTEMS_BSD_MACHINE_RTEMS_BSD_USER_SPACE_H_ #define __FreeBSD__ 1 +#define _WANT_FREEBSD_BITSET #include <rtems/bsd/local/opt_inet6.h> #include <machine/rtems-bsd-version.h> @@ -49,6 +50,9 @@ #include <stdio.h> +/* REVIEW-AFTER-FREEBSD-BASELINE-UPDATE */ +#define IFCAP_NOMAP 0x4000000 + #define O_CLOEXEC 0 #define O_DIRECTORY 0 diff --git a/rtemsbsd/include/rtems/bsd/bsd.h b/rtemsbsd/include/rtems/bsd/bsd.h index cec14ac4..9e524719 100755 --- a/rtemsbsd/include/rtems/bsd/bsd.h +++ b/rtemsbsd/include/rtems/bsd/bsd.h @@ -92,28 +92,6 @@ typedef struct { rtems_status_code rtems_bsd_initialize(void); /** - * @brief Initializes the libbsd and starts a DHCPCD task. - * - * The libbsd is initialized via rtems_bsd_initialize(). If this is - * successful, then the loop back interfaces are created. If this is - * successful, then a DHCPCD task is started at the least important priority. - * - * The default devices of the BSP are initialized. Support for - * - IF_BRIDGE(4), - * - LAGG(4), - * - multicast routing, - * - UNIX(4), and - * - VLAN(4), - * is enabled. - * - * No RTEMS shell commands are registered. - * - * @retval RTEMS_SUCCESSFUL Successful operation. - * @retval otherwise An error occurred. - */ -rtems_status_code rtems_bsd_initialize_dhcp(void); - -/** * @brief Configures the lo0 (loopback) interface. * * @return Returns an exit code, see also <sysexits.h>. diff --git a/rtemsbsd/include/rtems/netcmds-config.h b/rtemsbsd/include/rtems/netcmds-config.h index 81f6c92e..28961bae 100644 --- a/rtemsbsd/include/rtems/netcmds-config.h +++ b/rtemsbsd/include/rtems/netcmds-config.h @@ -32,7 +32,11 @@ extern rtems_shell_cmd_t rtems_shell_PING_Command; extern rtems_shell_cmd_t rtems_shell_PING6_Command; extern rtems_shell_cmd_t rtems_shell_IFCONFIG_Command; + +extern rtems_shell_cmd_t rtems_shell_IFMCSTAT_Command; + extern rtems_shell_cmd_t rtems_shell_ROUTE_Command; + extern rtems_shell_cmd_t rtems_shell_NETSTAT_Command; extern rtems_shell_cmd_t rtems_shell_DHCPCD_Command; diff --git a/rtemsbsd/include/x86/bus.h b/rtemsbsd/include/x86/bus.h deleted file mode 100644 index 2427ae51..00000000 --- a/rtemsbsd/include/x86/bus.h +++ /dev/null @@ -1,1109 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-NetBSDE - * - * Copyright (c) KATO Takenori, 1999. - * - * All rights reserved. Unpublished rights reserved under the copyright - * laws of Japan. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer as - * the first lines of this file unmodified. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* $NetBSD: bus.h,v 1.12 1997/10/01 08:25:15 fvdl Exp $ */ - -/*- - * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, - * NASA Ames Research Center. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/*- - * Copyright (c) 1996 Charles M. Hannum. All rights reserved. - * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Christopher G. Demetriou - * for the NetBSD Project. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _X86_BUS_H_ -#define _X86_BUS_H_ - -#include <machine/_bus.h> -#include <machine/cpufunc.h> - -#ifndef __GNUCLIKE_ASM -#error "no assembler code for your compiler" -#endif - -/* - * Values for the x86 bus space tag, not to be used directly by MI code. - */ -#define X86_BUS_SPACE_IO 0 /* space is i/o space */ -#define X86_BUS_SPACE_MEM 1 /* space is mem space */ - -#define BUS_SPACE_MAXSIZE_24BIT 0xFFFFFF -#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF -#if defined(__amd64__) -#define BUS_SPACE_MAXSIZE 0xFFFFFFFFFFFFFFFFULL -#else -#define BUS_SPACE_MAXSIZE 0xFFFFFFFF -#endif -#define BUS_SPACE_MAXADDR_24BIT 0xFFFFFF -#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF -#if defined(__amd64__) || defined(PAE) -#define BUS_SPACE_MAXADDR_48BIT 0xFFFFFFFFFFFFULL -#define BUS_SPACE_MAXADDR 0xFFFFFFFFFFFFFFFFULL -#else -#define BUS_SPACE_MAXADDR 0xFFFFFFFF -#endif - -#define BUS_SPACE_INVALID_DATA (~0) -#define BUS_SPACE_UNRESTRICTED (~0) - -/* - * Map a region of device bus space into CPU virtual address space. - */ - -int bus_space_map(bus_space_tag_t tag, bus_addr_t addr, bus_size_t size, - int flags, bus_space_handle_t *bshp); - -/* - * Unmap a region of device bus space. - */ - -void bus_space_unmap(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t size); - -/* - * Get a new handle for a subregion of an already-mapped area of bus space. - */ - -static __inline int bus_space_subregion(bus_space_tag_t t, - bus_space_handle_t bsh, - bus_size_t offset, bus_size_t size, - bus_space_handle_t *nbshp); - -static __inline int -bus_space_subregion(bus_space_tag_t t __unused, bus_space_handle_t bsh, - bus_size_t offset, bus_size_t size __unused, - bus_space_handle_t *nbshp) -{ - - *nbshp = bsh + offset; - return (0); -} - -/* - * Allocate a region of memory that is accessible to devices in bus space. - */ - -int bus_space_alloc(bus_space_tag_t t, bus_addr_t rstart, - bus_addr_t rend, bus_size_t size, bus_size_t align, - bus_size_t boundary, int flags, bus_addr_t *addrp, - bus_space_handle_t *bshp); - -/* - * Free a region of bus space accessible memory. - */ - -static __inline void bus_space_free(bus_space_tag_t t, bus_space_handle_t bsh, - bus_size_t size); - -static __inline void -bus_space_free(bus_space_tag_t t __unused, bus_space_handle_t bsh __unused, - bus_size_t size __unused) -{ -} - - -/* - * Read a 1, 2, 4, or 8 byte quantity from bus space - * described by tag/handle/offset. - */ -static __inline u_int8_t bus_space_read_1(bus_space_tag_t tag, - bus_space_handle_t handle, - bus_size_t offset); - -static __inline u_int16_t bus_space_read_2(bus_space_tag_t tag, - bus_space_handle_t handle, - bus_size_t offset); - -static __inline u_int32_t bus_space_read_4(bus_space_tag_t tag, - bus_space_handle_t handle, - bus_size_t offset); - -#ifdef __amd64__ -static __inline uint64_t bus_space_read_8(bus_space_tag_t tag, - bus_space_handle_t handle, - bus_size_t offset); -#endif - -static __inline u_int8_t -bus_space_read_1(bus_space_tag_t tag, bus_space_handle_t handle, - bus_size_t offset) -{ - - if (tag == X86_BUS_SPACE_IO) - return (inb(handle + offset)); - return (*(volatile u_int8_t *)(handle + offset)); -} - -static __inline u_int16_t -bus_space_read_2(bus_space_tag_t tag, bus_space_handle_t handle, - bus_size_t offset) -{ - - if (tag == X86_BUS_SPACE_IO) - return (inw(handle + offset)); - return (*(volatile u_int16_t *)(handle + offset)); -} - -static __inline u_int32_t -bus_space_read_4(bus_space_tag_t tag, bus_space_handle_t handle, - bus_size_t offset) -{ - - if (tag == X86_BUS_SPACE_IO) - return (inl(handle + offset)); - return (*(volatile u_int32_t *)(handle + offset)); -} - -#ifdef __amd64__ -static __inline uint64_t -bus_space_read_8(bus_space_tag_t tag, bus_space_handle_t handle, - bus_size_t offset) -{ - - if (tag == X86_BUS_SPACE_IO) /* No 8 byte IO space access on x86 */ - return (BUS_SPACE_INVALID_DATA); - return (*(volatile uint64_t *)(handle + offset)); -} -#endif - -/* - * Read `count' 1, 2, 4, or 8 byte quantities from bus space - * described by tag/handle/offset and copy into buffer provided. - */ -static __inline void bus_space_read_multi_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int8_t *addr, - size_t count); - -static __inline void bus_space_read_multi_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int16_t *addr, - size_t count); - -static __inline void bus_space_read_multi_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int32_t *addr, - size_t count); - -static __inline void -bus_space_read_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int8_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - insb(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: movb (%2),%%al \n\ - stosb \n\ - loop 1b" : - "=D" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory"); -#endif - } -} - -static __inline void -bus_space_read_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int16_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - insw(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: movw (%2),%%ax \n\ - stosw \n\ - loop 1b" : - "=D" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory"); -#endif - } -} - -static __inline void -bus_space_read_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int32_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - insl(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: movl (%2),%%eax \n\ - stosl \n\ - loop 1b" : - "=D" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory"); -#endif - } -} - -#if 0 /* Cause a link error for bus_space_read_multi_8 */ -#define bus_space_read_multi_8 !!! bus_space_read_multi_8 unimplemented !!! -#endif - -/* - * Read `count' 1, 2, 4, or 8 byte quantities from bus space - * described by tag/handle and starting at `offset' and copy into - * buffer provided. - */ -static __inline void bus_space_read_region_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int8_t *addr, - size_t count); - -static __inline void bus_space_read_region_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int16_t *addr, - size_t count); - -static __inline void bus_space_read_region_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int32_t *addr, - size_t count); - - -static __inline void -bus_space_read_region_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int8_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: inb %w2,%%al \n\ - stosb \n\ - incl %2 \n\ - loop 1b" : - "=D" (addr), "=c" (count), "=d" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsb" : - "=D" (addr), "=c" (count), "=S" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_read_region_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int16_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: inw %w2,%%ax \n\ - stosw \n\ - addl $2,%2 \n\ - loop 1b" : - "=D" (addr), "=c" (count), "=d" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsw" : - "=D" (addr), "=c" (count), "=S" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_read_region_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int32_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: inl %w2,%%eax \n\ - stosl \n\ - addl $4,%2 \n\ - loop 1b" : - "=D" (addr), "=c" (count), "=d" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsl" : - "=D" (addr), "=c" (count), "=S" (_port_) : - "0" (addr), "1" (count), "2" (_port_) : - "memory", "cc"); -#endif - } -} - -#if 0 /* Cause a link error for bus_space_read_region_8 */ -#define bus_space_read_region_8 !!! bus_space_read_region_8 unimplemented !!! -#endif - -/* - * Write the 1, 2, 4, or 8 byte value `value' to bus space - * described by tag/handle/offset. - */ - -static __inline void bus_space_write_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int8_t value); - -static __inline void bus_space_write_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int16_t value); - -static __inline void bus_space_write_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int32_t value); - -#ifdef __amd64__ -static __inline void bus_space_write_8(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, uint64_t value); -#endif - -static __inline void -bus_space_write_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int8_t value) -{ - - if (tag == X86_BUS_SPACE_IO) - outb(bsh + offset, value); - else - *(volatile u_int8_t *)(bsh + offset) = value; -} - -static __inline void -bus_space_write_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int16_t value) -{ - - if (tag == X86_BUS_SPACE_IO) - outw(bsh + offset, value); - else - *(volatile u_int16_t *)(bsh + offset) = value; -} - -static __inline void -bus_space_write_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int32_t value) -{ - - if (tag == X86_BUS_SPACE_IO) - outl(bsh + offset, value); - else - *(volatile u_int32_t *)(bsh + offset) = value; -} - -#ifdef __amd64__ -static __inline void -bus_space_write_8(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, uint64_t value) -{ - - if (tag == X86_BUS_SPACE_IO) /* No 8 byte IO space access on x86 */ - return; - else - *(volatile uint64_t *)(bsh + offset) = value; -} -#endif - -/* - * Write `count' 1, 2, 4, or 8 byte quantities from the buffer - * provided to bus space described by tag/handle/offset. - */ - -static __inline void bus_space_write_multi_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int8_t *addr, - size_t count); -static __inline void bus_space_write_multi_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int16_t *addr, - size_t count); - -static __inline void bus_space_write_multi_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int32_t *addr, - size_t count); - -static __inline void -bus_space_write_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int8_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - outsb(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsb \n\ - movb %%al,(%2) \n\ - loop 1b" : - "=S" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_write_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int16_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - outsw(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsw \n\ - movw %%ax,(%2) \n\ - loop 1b" : - "=S" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_write_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int32_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) - outsl(bsh + offset, addr, count); - else { -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsl \n\ - movl %%eax,(%2) \n\ - loop 1b" : - "=S" (addr), "=c" (count) : - "r" (bsh + offset), "0" (addr), "1" (count) : - "%eax", "memory", "cc"); -#endif - } -} - -#if 0 /* Cause a link error for bus_space_write_multi_8 */ -#define bus_space_write_multi_8(t, h, o, a, c) \ - !!! bus_space_write_multi_8 unimplemented !!! -#endif - -/* - * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided - * to bus space described by tag/handle starting at `offset'. - */ - -static __inline void bus_space_write_region_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int8_t *addr, - size_t count); -static __inline void bus_space_write_region_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int16_t *addr, - size_t count); -static __inline void bus_space_write_region_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - const u_int32_t *addr, - size_t count); - -static __inline void -bus_space_write_region_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int8_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsb \n\ - outb %%al,%w0 \n\ - incl %0 \n\ - loop 1b" : - "=d" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsb" : - "=D" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_write_region_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int16_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsw \n\ - outw %%ax,%w0 \n\ - addl $2,%0 \n\ - loop 1b" : - "=d" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsw" : - "=D" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "memory", "cc"); -#endif - } -} - -static __inline void -bus_space_write_region_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, const u_int32_t *addr, size_t count) -{ - - if (tag == X86_BUS_SPACE_IO) { - int _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - 1: lodsl \n\ - outl %%eax,%w0 \n\ - addl $4,%0 \n\ - loop 1b" : - "=d" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "%eax", "memory", "cc"); -#endif - } else { - bus_space_handle_t _port_ = bsh + offset; -#ifdef __GNUCLIKE_ASM - __asm __volatile(" \n\ - cld \n\ - repne \n\ - movsl" : - "=D" (_port_), "=S" (addr), "=c" (count) : - "0" (_port_), "1" (addr), "2" (count) : - "memory", "cc"); -#endif - } -} - -#if 0 /* Cause a link error for bus_space_write_region_8 */ -#define bus_space_write_region_8 \ - !!! bus_space_write_region_8 unimplemented !!! -#endif - -/* - * Write the 1, 2, 4, or 8 byte value `val' to bus space described - * by tag/handle/offset `count' times. - */ - -static __inline void bus_space_set_multi_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - u_int8_t value, size_t count); -static __inline void bus_space_set_multi_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - u_int16_t value, size_t count); -static __inline void bus_space_set_multi_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, - u_int32_t value, size_t count); - -static __inline void -bus_space_set_multi_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int8_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - while (count--) - outb(addr, value); - else - while (count--) - *(volatile u_int8_t *)(addr) = value; -} - -static __inline void -bus_space_set_multi_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int16_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - while (count--) - outw(addr, value); - else - while (count--) - *(volatile u_int16_t *)(addr) = value; -} - -static __inline void -bus_space_set_multi_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int32_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - while (count--) - outl(addr, value); - else - while (count--) - *(volatile u_int32_t *)(addr) = value; -} - -#if 0 /* Cause a link error for bus_space_set_multi_8 */ -#define bus_space_set_multi_8 !!! bus_space_set_multi_8 unimplemented !!! -#endif - -/* - * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described - * by tag/handle starting at `offset'. - */ - -static __inline void bus_space_set_region_1(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int8_t value, - size_t count); -static __inline void bus_space_set_region_2(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int16_t value, - size_t count); -static __inline void bus_space_set_region_4(bus_space_tag_t tag, - bus_space_handle_t bsh, - bus_size_t offset, u_int32_t value, - size_t count); - -static __inline void -bus_space_set_region_1(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int8_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - for (; count != 0; count--, addr++) - outb(addr, value); - else - for (; count != 0; count--, addr++) - *(volatile u_int8_t *)(addr) = value; -} - -static __inline void -bus_space_set_region_2(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int16_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - for (; count != 0; count--, addr += 2) - outw(addr, value); - else - for (; count != 0; count--, addr += 2) - *(volatile u_int16_t *)(addr) = value; -} - -static __inline void -bus_space_set_region_4(bus_space_tag_t tag, bus_space_handle_t bsh, - bus_size_t offset, u_int32_t value, size_t count) -{ - bus_space_handle_t addr = bsh + offset; - - if (tag == X86_BUS_SPACE_IO) - for (; count != 0; count--, addr += 4) - outl(addr, value); - else - for (; count != 0; count--, addr += 4) - *(volatile u_int32_t *)(addr) = value; -} - -#if 0 /* Cause a link error for bus_space_set_region_8 */ -#define bus_space_set_region_8 !!! bus_space_set_region_8 unimplemented !!! -#endif - -/* - * Copy `count' 1, 2, 4, or 8 byte values from bus space starting - * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2. - */ - -static __inline void bus_space_copy_region_1(bus_space_tag_t tag, - bus_space_handle_t bsh1, - bus_size_t off1, - bus_space_handle_t bsh2, - bus_size_t off2, size_t count); - -static __inline void bus_space_copy_region_2(bus_space_tag_t tag, - bus_space_handle_t bsh1, - bus_size_t off1, - bus_space_handle_t bsh2, - bus_size_t off2, size_t count); - -static __inline void bus_space_copy_region_4(bus_space_tag_t tag, - bus_space_handle_t bsh1, - bus_size_t off1, - bus_space_handle_t bsh2, - bus_size_t off2, size_t count); - -static __inline void -bus_space_copy_region_1(bus_space_tag_t tag, bus_space_handle_t bsh1, - bus_size_t off1, bus_space_handle_t bsh2, - bus_size_t off2, size_t count) -{ - bus_space_handle_t addr1 = bsh1 + off1; - bus_space_handle_t addr2 = bsh2 + off2; - - if (tag == X86_BUS_SPACE_IO) { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1++, addr2++) - outb(addr2, inb(addr1)); - } else { - /* dest after src: copy backwards */ - for (addr1 += (count - 1), addr2 += (count - 1); - count != 0; count--, addr1--, addr2--) - outb(addr2, inb(addr1)); - } - } else { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1++, addr2++) - *(volatile u_int8_t *)(addr2) = - *(volatile u_int8_t *)(addr1); - } else { - /* dest after src: copy backwards */ - for (addr1 += (count - 1), addr2 += (count - 1); - count != 0; count--, addr1--, addr2--) - *(volatile u_int8_t *)(addr2) = - *(volatile u_int8_t *)(addr1); - } - } -} - -static __inline void -bus_space_copy_region_2(bus_space_tag_t tag, bus_space_handle_t bsh1, - bus_size_t off1, bus_space_handle_t bsh2, - bus_size_t off2, size_t count) -{ - bus_space_handle_t addr1 = bsh1 + off1; - bus_space_handle_t addr2 = bsh2 + off2; - - if (tag == X86_BUS_SPACE_IO) { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1 += 2, addr2 += 2) - outw(addr2, inw(addr1)); - } else { - /* dest after src: copy backwards */ - for (addr1 += 2 * (count - 1), addr2 += 2 * (count - 1); - count != 0; count--, addr1 -= 2, addr2 -= 2) - outw(addr2, inw(addr1)); - } - } else { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1 += 2, addr2 += 2) - *(volatile u_int16_t *)(addr2) = - *(volatile u_int16_t *)(addr1); - } else { - /* dest after src: copy backwards */ - for (addr1 += 2 * (count - 1), addr2 += 2 * (count - 1); - count != 0; count--, addr1 -= 2, addr2 -= 2) - *(volatile u_int16_t *)(addr2) = - *(volatile u_int16_t *)(addr1); - } - } -} - -static __inline void -bus_space_copy_region_4(bus_space_tag_t tag, bus_space_handle_t bsh1, - bus_size_t off1, bus_space_handle_t bsh2, - bus_size_t off2, size_t count) -{ - bus_space_handle_t addr1 = bsh1 + off1; - bus_space_handle_t addr2 = bsh2 + off2; - - if (tag == X86_BUS_SPACE_IO) { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1 += 4, addr2 += 4) - outl(addr2, inl(addr1)); - } else { - /* dest after src: copy backwards */ - for (addr1 += 4 * (count - 1), addr2 += 4 * (count - 1); - count != 0; count--, addr1 -= 4, addr2 -= 4) - outl(addr2, inl(addr1)); - } - } else { - if (addr1 >= addr2) { - /* src after dest: copy forward */ - for (; count != 0; count--, addr1 += 4, addr2 += 4) - *(volatile u_int32_t *)(addr2) = - *(volatile u_int32_t *)(addr1); - } else { - /* dest after src: copy backwards */ - for (addr1 += 4 * (count - 1), addr2 += 4 * (count - 1); - count != 0; count--, addr1 -= 4, addr2 -= 4) - *(volatile u_int32_t *)(addr2) = - *(volatile u_int32_t *)(addr1); - } - } -} - -#if 0 /* Cause a link error for bus_space_copy_8 */ -#define bus_space_copy_region_8 !!! bus_space_copy_region_8 unimplemented !!! -#endif - -/* - * Bus read/write barrier methods. - * - * void bus_space_barrier(bus_space_tag_t tag, bus_space_handle_t bsh, - * bus_size_t offset, bus_size_t len, int flags); - * - * - * Note that BUS_SPACE_BARRIER_WRITE doesn't do anything other than - * prevent reordering by the compiler; all Intel x86 processors currently - * retire operations outside the CPU in program order. - */ -#define BUS_SPACE_BARRIER_READ 0x01 /* force read barrier */ -#define BUS_SPACE_BARRIER_WRITE 0x02 /* force write barrier */ - -static __inline void -bus_space_barrier(bus_space_tag_t tag __unused, bus_space_handle_t bsh __unused, - bus_size_t offset __unused, bus_size_t len __unused, int flags) -{ -#ifdef __GNUCLIKE_ASM - if (flags & BUS_SPACE_BARRIER_READ) -#ifdef __amd64__ - __asm __volatile("lock; addl $0,0(%%rsp)" : : : "memory"); -#else - __asm __volatile("lock; addl $0,0(%%esp)" : : : "memory"); -#endif - else - __compiler_membar(); -#endif -} - -#ifdef BUS_SPACE_NO_LEGACY -#undef inb -#undef outb -#define inb(a) compiler_error -#define inw(a) compiler_error -#define inl(a) compiler_error -#define outb(a, b) compiler_error -#define outw(a, b) compiler_error -#define outl(a, b) compiler_error -#endif - -#include <machine/bus_dma.h> - -/* - * Stream accesses are the same as normal accesses on x86; there are no - * supported bus systems with an endianess different from the host one. - */ -#define bus_space_read_stream_1(t, h, o) bus_space_read_1((t), (h), (o)) -#define bus_space_read_stream_2(t, h, o) bus_space_read_2((t), (h), (o)) -#define bus_space_read_stream_4(t, h, o) bus_space_read_4((t), (h), (o)) - -#define bus_space_read_multi_stream_1(t, h, o, a, c) \ - bus_space_read_multi_1((t), (h), (o), (a), (c)) -#define bus_space_read_multi_stream_2(t, h, o, a, c) \ - bus_space_read_multi_2((t), (h), (o), (a), (c)) -#define bus_space_read_multi_stream_4(t, h, o, a, c) \ - bus_space_read_multi_4((t), (h), (o), (a), (c)) - -#define bus_space_write_stream_1(t, h, o, v) \ - bus_space_write_1((t), (h), (o), (v)) -#define bus_space_write_stream_2(t, h, o, v) \ - bus_space_write_2((t), (h), (o), (v)) -#define bus_space_write_stream_4(t, h, o, v) \ - bus_space_write_4((t), (h), (o), (v)) - -#define bus_space_write_multi_stream_1(t, h, o, a, c) \ - bus_space_write_multi_1((t), (h), (o), (a), (c)) -#define bus_space_write_multi_stream_2(t, h, o, a, c) \ - bus_space_write_multi_2((t), (h), (o), (a), (c)) -#define bus_space_write_multi_stream_4(t, h, o, a, c) \ - bus_space_write_multi_4((t), (h), (o), (a), (c)) - -#define bus_space_set_multi_stream_1(t, h, o, v, c) \ - bus_space_set_multi_1((t), (h), (o), (v), (c)) -#define bus_space_set_multi_stream_2(t, h, o, v, c) \ - bus_space_set_multi_2((t), (h), (o), (v), (c)) -#define bus_space_set_multi_stream_4(t, h, o, v, c) \ - bus_space_set_multi_4((t), (h), (o), (v), (c)) - -#define bus_space_read_region_stream_1(t, h, o, a, c) \ - bus_space_read_region_1((t), (h), (o), (a), (c)) -#define bus_space_read_region_stream_2(t, h, o, a, c) \ - bus_space_read_region_2((t), (h), (o), (a), (c)) -#define bus_space_read_region_stream_4(t, h, o, a, c) \ - bus_space_read_region_4((t), (h), (o), (a), (c)) - -#define bus_space_write_region_stream_1(t, h, o, a, c) \ - bus_space_write_region_1((t), (h), (o), (a), (c)) -#define bus_space_write_region_stream_2(t, h, o, a, c) \ - bus_space_write_region_2((t), (h), (o), (a), (c)) -#define bus_space_write_region_stream_4(t, h, o, a, c) \ - bus_space_write_region_4((t), (h), (o), (a), (c)) - -#define bus_space_set_region_stream_1(t, h, o, v, c) \ - bus_space_set_region_1((t), (h), (o), (v), (c)) -#define bus_space_set_region_stream_2(t, h, o, v, c) \ - bus_space_set_region_2((t), (h), (o), (v), (c)) -#define bus_space_set_region_stream_4(t, h, o, v, c) \ - bus_space_set_region_4((t), (h), (o), (v), (c)) - -#define bus_space_copy_region_stream_1(t, h1, o1, h2, o2, c) \ - bus_space_copy_region_1((t), (h1), (o1), (h2), (o2), (c)) -#define bus_space_copy_region_stream_2(t, h1, o1, h2, o2, c) \ - bus_space_copy_region_2((t), (h1), (o1), (h2), (o2), (c)) -#define bus_space_copy_region_stream_4(t, h1, o1, h2, o2, c) \ - bus_space_copy_region_4((t), (h1), (o1), (h2), (o2), (c)) - -#endif /* _X86_BUS_H_ */ diff --git a/rtemsbsd/include/x86/x86_var.h b/rtemsbsd/include/x86/x86_var.h index 2028d739..5819205a 100644 --- a/rtemsbsd/include/x86/x86_var.h +++ b/rtemsbsd/include/x86/x86_var.h @@ -1,145 +1 @@ -/*- - * Copyright (c) 1995 Bruce D. Evans. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the author nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _X86_X86_VAR_H_ -#define _X86_X86_VAR_H_ - -/* - * Miscellaneous machine-dependent declarations. - */ - -extern long Maxmem; -extern u_int basemem; -extern int busdma_swi_pending; -extern u_int cpu_exthigh; -extern u_int cpu_feature; -extern u_int cpu_feature2; -extern u_int amd_feature; -extern u_int amd_feature2; -extern u_int amd_rascap; -extern u_int amd_pminfo; -extern u_int amd_extended_feature_extensions; -extern u_int via_feature_rng; -extern u_int via_feature_xcrypt; -extern u_int cpu_clflush_line_size; -extern u_int cpu_stdext_feature; -extern u_int cpu_stdext_feature2; -extern u_int cpu_stdext_feature3; -extern uint64_t cpu_ia32_arch_caps; -extern u_int cpu_fxsr; -extern u_int cpu_high; -extern u_int cpu_id; -extern u_int cpu_max_ext_state_size; -extern u_int cpu_mxcsr_mask; -extern u_int cpu_procinfo; -extern u_int cpu_procinfo2; -extern char cpu_vendor[]; -extern u_int cpu_vendor_id; -extern u_int cpu_mon_mwait_flags; -extern u_int cpu_mon_min_size; -extern u_int cpu_mon_max_size; -extern u_int cpu_maxphyaddr; -extern char ctx_switch_xsave[]; -extern u_int hv_base; -extern u_int hv_high; -extern char hv_vendor[]; -extern char kstack[]; -extern char sigcode[]; -extern int szsigcode; -extern int vm_page_dump_size; -extern int workaround_erratum383; -extern int _udatasel; -extern int _ucodesel; -extern int _ucode32sel; -extern int _ufssel; -extern int _ugssel; -extern int use_xsave; -extern uint64_t xsave_mask; -extern u_int max_apic_id; -extern int i386_read_exec; -extern int pti; -extern int hw_ibrs_active; -extern int hw_mds_disable; -extern int hw_ssb_active; - -struct pcb; -struct thread; -struct reg; -struct fpreg; -struct dbreg; -struct dumperinfo; -struct trapframe; - -/* - * The interface type of the interrupt handler entry point cannot be - * expressed in C. Use simplest non-variadic function type as an - * approximation. - */ -typedef void alias_for_inthand_t(void); - -bool acpi_get_fadt_bootflags(uint16_t *flagsp); -void *alloc_fpusave(int flags); -void busdma_swi(void); -vm_paddr_t cpu_getmaxphyaddr(void); -bool cpu_mwait_usable(void); -void cpu_probe_amdc1e(void); -void cpu_setregs(void); -bool disable_wp(void); -void restore_wp(bool old_wp); -void dump_add_page(vm_paddr_t); -void dump_drop_page(vm_paddr_t); -void finishidentcpu(void); -void identify_cpu1(void); -void identify_cpu2(void); -void identify_cpu_fixup_bsp(void); -void identify_hypervisor(void); -void initializecpu(void); -void initializecpucache(void); -bool fix_cpuid(void); -void fillw(int /*u_short*/ pat, void *base, size_t cnt); -int is_physical_memory(vm_paddr_t addr); -int isa_nmi(int cd); -void handle_ibrs_entry(void); -void handle_ibrs_exit(void); -void hw_ibrs_recalculate(void); -void hw_mds_recalculate(void); -void hw_ssb_recalculate(bool all_cpus); -void nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame); -void nmi_call_kdb_smp(u_int type, struct trapframe *frame); -void nmi_handle_intr(u_int type, struct trapframe *frame); -void pagecopy(void *from, void *to); -void printcpuinfo(void); -int pti_get_default(void); -int user_dbreg_trap(register_t dr6); -int minidumpsys(struct dumperinfo *); -struct pcb *get_pcb_td(struct thread *td); - -#endif +#include <machine/x86_var.h> diff --git a/rtemsbsd/nfsclient/nfs.c b/rtemsbsd/nfsclient/nfs.c index d6f43305..e9e83abb 100644 --- a/rtemsbsd/nfsclient/nfs.c +++ b/rtemsbsd/nfsclient/nfs.c @@ -397,71 +397,36 @@ DirInfo dip; /* Macro for accessing serporid fields */ -#define SERP_ARGS(node) ((node)->serporid.serporid_u.serporid.arg_u) -#define SERP_ATTR(node) ((node)->serporid.serporid_u.serporid.attributes) -#define SERP_FILE(node) ((node)->serporid.serporid_u.serporid.file) - -/* - * FIXME: The use of the serporid structure with several embedded unions to - * split up the specific NFS request/response structures is quite a hack. It - * breaks on 64-bit targets due to the presence of pointer members which affect - * the overall alignment. Use a packed serporidok structure to hopefully fix - * this issue. - */ +#define SERP_ARGS(node) ((node)->serporid.serporid) +#define SERP_ATTR(node) ((node)->serporid.serporid.attributes) +#define SERP_FILE(node) ((node)->serporid.serporid.file) typedef struct serporidok { fattr attributes; - nfs_fh file; union { - struct { - filename name; - } diroparg; - struct { - sattr attributes; - } sattrarg; - struct { - uint32_t offset; - uint32_t count; - uint32_t totalcount; - } readarg; - struct { - uint32_t beginoffset; - uint32_t offset; - uint32_t totalcount; - struct { - uint32_t data_len; - char* data_val; - } data; - } writearg; - struct { - filename name; - sattr attributes; - } createarg; - struct { - filename name; - diropargs to; - } renamearg; - struct { - diropargs to; - } linkarg; - struct { - filename name; - nfspath to; - sattr attributes; - } symlinkarg; - struct { - nfscookie cookie; - uint32_t count; - } readdirarg; - } arg_u; -} RTEMS_PACKED serporidok; + nfs_fh file; + diropargs diroparg; + sattrargs sattrarg; + readargs readarg; + writeargs writearg; + createargs createarg; + renameargs renamearg; + linkargs linkarg; + symlinkargs symlinkarg; + readdirargs readdirarg; + }; +} serporidok; +/* + * The nfsstat is an enum, so has an integer alignment. The serporid contains + * pointers, so has at least a pointer alignment. The packed attribute ensures + * that there is no gap between the status and serporid members on 64-bit + * targets. + */ typedef struct serporid { nfsstat status; - union { - serporidok serporid; - } serporid_u; -} serporid; + serporidok serporid; +} RTEMS_PACKED serporid; /* an XDR routine to encode/decode the inverted diropres * into an nfsnodestat; @@ -493,7 +458,7 @@ xdr_serporid(XDR *xdrs, serporid *objp) return FALSE; switch (objp->status) { case NFS_OK: - if (!xdr_serporidok(xdrs, &objp->serporid_u.serporid)) + if (!xdr_serporidok(xdrs, &objp->serporid)) return FALSE; break; default: @@ -2040,7 +2005,7 @@ char *dupname; rtems_clock_get_tod_timeval(&now); - SERP_ARGS(node).createarg.name = dupname; + SERP_ARGS(node).createarg.where.name = dupname; SERP_ARGS(node).createarg.attributes.mode = mode; SERP_ARGS(node).createarg.attributes.uid = nfs->uid; SERP_ARGS(node).createarg.attributes.gid = nfs->gid; @@ -2093,19 +2058,19 @@ static int nfs_rmnod( return rv; } -static int nfs_utime( +static int nfs_utimens( const rtems_filesystem_location_info_t *pathloc, /* IN */ - time_t actime, /* IN */ - time_t modtime /* IN */ + struct timespec times[2] /* IN */ + ) { sattr arg; /* TODO: add rtems EPOCH - UNIX EPOCH seconds */ - arg.atime.seconds = actime; - arg.atime.useconds = 0; - arg.mtime.seconds = modtime; - arg.mtime.useconds = 0; + arg.atime.seconds = times[0].tv_sec; + arg.atime.useconds = times[0].tv_nsec / 1000; + arg.mtime.seconds = times[1].tv_sec; + arg.mtime.useconds = times[1].tv_nsec / 1000; return nfs_sattr(pathloc->node_access, &arg, SATTR_ATIME | SATTR_MTIME); } @@ -2134,7 +2099,7 @@ char *dupname; rtems_clock_get_tod_timeval(&now); - SERP_ARGS(node).symlinkarg.name = dupname; + SERP_ARGS(node).symlinkarg.from.name = dupname; SERP_ARGS(node).symlinkarg.to = (nfspath) target; SERP_ARGS(node).symlinkarg.attributes.mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; @@ -2231,7 +2196,7 @@ static int nfs_rename( nfs_fh *toDirDst = &SERP_ARGS(oldParentNode).renamearg.to.dir; nfsstat status; - SERP_ARGS(oldParentNode).renamearg.name = oldNode->str; + SERP_ARGS(oldParentNode).renamearg.from.name = oldNode->str; SERP_ARGS(oldParentNode).renamearg.to.name = dupname; memcpy(toDirDst, toDirSrc, sizeof(*toDirDst)); @@ -2297,25 +2262,25 @@ sattr arg; } const struct _rtems_filesystem_operations_table nfs_fs_ops = { - .lock_h = nfs_lock, - .unlock_h = nfs_unlock, - .eval_path_h = nfs_eval_path, - .link_h = nfs_link, + .lock_h = nfs_lock, + .unlock_h = nfs_unlock, + .eval_path_h = nfs_eval_path, + .link_h = nfs_link, .are_nodes_equal_h = nfs_are_nodes_equal, - .mknod_h = nfs_mknod, - .rmnod_h = nfs_rmnod, - .fchmod_h = nfs_fchmod, - .chown_h = nfs_chown, - .clonenod_h = nfs_clonenode, - .freenod_h = nfs_freenode, - .mount_h = rtems_filesystem_default_mount, - .unmount_h = rtems_filesystem_default_unmount, - .fsunmount_me_h = nfs_fsunmount_me, - .utime_h = nfs_utime, - .symlink_h = nfs_symlink, - .readlink_h = nfs_readlink, - .rename_h = nfs_rename, - .statvfs_h = rtems_filesystem_default_statvfs + .mknod_h = nfs_mknod, + .rmnod_h = nfs_rmnod, + .fchmod_h = nfs_fchmod, + .chown_h = nfs_chown, + .clonenod_h = nfs_clonenode, + .freenod_h = nfs_freenode, + .mount_h = rtems_filesystem_default_mount, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = nfs_fsunmount_me, + .utimens_h = nfs_utimens, + .symlink_h = nfs_symlink, + .readlink_h = nfs_readlink, + .rename_h = nfs_rename, + .statvfs_h = rtems_filesystem_default_statvfs }; /***************************************** @@ -3149,7 +3114,7 @@ rtems_filesystem_location_info_t old; rtems_filesystem_current->location = old; } rtems_semaphore_release(rpa->sync); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } diff --git a/rtemsbsd/powerpc/include/linux/crc32.h b/rtemsbsd/powerpc/include/linux/crc32.h index 0edaaa07..a86ac4a0 100644 --- a/rtemsbsd/powerpc/include/linux/crc32.h +++ b/rtemsbsd/powerpc/include/linux/crc32.h @@ -28,7 +28,7 @@ #define _LINUX_CRC32_H #include <linux/types.h> -#include <sys/libkern.h> +#include <sys/gsb_crc32.h> #ifdef __cplusplus extern "C" { diff --git a/rtemsbsd/powerpc/include/linux/genalloc.h b/rtemsbsd/powerpc/include/linux/genalloc.h index 9df0e430..c7ecd7bc 100644 --- a/rtemsbsd/powerpc/include/linux/genalloc.h +++ b/rtemsbsd/powerpc/include/linux/genalloc.h @@ -88,7 +88,7 @@ gen_pool_alloc(struct gen_pool *gp, size_t size) chunks = (size + (1 << gp->gen_chunk_shift) - 1) >> gp->gen_chunk_shift; mtx_lock(&gp->gen_lock); - blkno = blist_alloc(gp->gen_list, chunks); + blkno = blist_alloc(gp->gen_list, &chunks, chunks); mtx_unlock(&gp->gen_lock); if (blkno == SWAPBLK_NONE) diff --git a/rtemsbsd/powerpc/include/machine/legacyvar.h b/rtemsbsd/powerpc/include/machine/legacyvar.h new file mode 100644 index 00000000..8683a0e5 --- /dev/null +++ b/rtemsbsd/powerpc/include/machine/legacyvar.h @@ -0,0 +1,2 @@ +/* See freebsd/sys/x86/include/machine/legacyvar.h */ +#include <x86/include/machine/legacyvar.h> diff --git a/rtemsbsd/powerpc/include/machine/pci_cfgreg.h b/rtemsbsd/powerpc/include/machine/pci_cfgreg.h new file mode 100644 index 00000000..1bfa468e --- /dev/null +++ b/rtemsbsd/powerpc/include/machine/pci_cfgreg.h @@ -0,0 +1,2 @@ +/* See freebsd/sys/x86/include/machine/pci_cfgreg.h */ +#include <x86/include/machine/pci_cfgreg.h> diff --git a/rtemsbsd/pppd/rtemspppd.c b/rtemsbsd/pppd/rtemspppd.c index cf237a81..c001eff5 100644 --- a/rtemsbsd/pppd/rtemspppd.c +++ b/rtemsbsd/pppd/rtemspppd.c @@ -71,7 +71,7 @@ static rtems_task pppTask(rtems_task_argument arg) /* terminate myself */ rtems_pppd_taskid = 0; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } int rtems_pppd_initialize(void) diff --git a/rtemsbsd/rtems/program-internal.h b/rtemsbsd/rtems/program-internal.h index da817130..2104c064 100644 --- a/rtemsbsd/rtems/program-internal.h +++ b/rtemsbsd/rtems/program-internal.h @@ -60,6 +60,12 @@ struct program_allocmem_item { LIST_ENTRY(program_allocmem_item) entries; }; +struct program_destructor { + void (*destructor)(void *); + void *arg; + LIST_ENTRY(program_destructor) link; +}; + struct rtems_bsd_program_control { void *context; int exit_code; @@ -68,6 +74,7 @@ struct rtems_bsd_program_control { LIST_HEAD(, program_fd_item) open_fd; LIST_HEAD(, program_file_item) open_file; LIST_HEAD(, program_allocmem_item) allocated_mem; + LIST_HEAD(, program_destructor) destructors; }; struct rtems_bsd_program_control *rtems_bsd_program_get_control_or_null(void); diff --git a/rtemsbsd/rtems/rtems-bsd-cxx.cc b/rtemsbsd/rtems/rtems-bsd-cxx.cc index aadb25af..f6ec78e6 100644 --- a/rtemsbsd/rtems/rtems-bsd-cxx.cc +++ b/rtemsbsd/rtems/rtems-bsd-cxx.cc @@ -31,6 +31,7 @@ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS +#undef _GNU_SOURCE #include <machine/rtems-bsd-kernel-space.h> diff --git a/rtemsbsd/rtems/rtems-bsd-init-dhcp.c b/rtemsbsd/rtems/rtems-bsd-init-dhcp.c index e0a298f7..d91f439b 100644 --- a/rtemsbsd/rtems/rtems-bsd-init-dhcp.c +++ b/rtemsbsd/rtems/rtems-bsd-init-dhcp.c @@ -38,6 +38,7 @@ */ #include <rtems/bsd/bsd.h> +#include <rtems/bsd/modules.h> #include <rtems/dhcpcd.h> #include <sysexits.h> @@ -76,7 +77,11 @@ rtems_bsd_initialize_dhcp(void) #define RTEMS_BSD_CONFIG_NET_PF_UNIX #define RTEMS_BSD_CONFIG_NET_IP_MROUTE + +#ifdef RTEMS_BSD_MODULE_NETINET6 #define RTEMS_BSD_CONFIG_NET_IP6_MROUTE +#endif + #define RTEMS_BSD_CONFIG_NET_IF_BRIDGE #define RTEMS_BSD_CONFIG_NET_IF_LAGG #define RTEMS_BSD_CONFIG_NET_IF_VLAN diff --git a/rtemsbsd/rtems/rtems-bsd-racoon.c b/rtemsbsd/rtems/rtems-bsd-racoon.c index c7ea3594..e6e6205c 100644 --- a/rtemsbsd/rtems/rtems-bsd-racoon.c +++ b/rtemsbsd/rtems/rtems-bsd-racoon.c @@ -75,7 +75,7 @@ racoon_task(rtems_task_argument arg) } clean_up_args(args); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } rtems_status_code diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c index 23ee15db..8ffaa914 100644 --- a/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c +++ b/rtemsbsd/rtems/rtems-bsd-rc-conf-net.c @@ -103,7 +103,9 @@ cloned_interfaces(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) "ifconfig", aa->argv[arg], "create", NULL }; rtems_bsd_rc_conf_print_cmd(rc_conf, "cloning_interfaces", 3, ifconfg_args); - rtems_bsd_command_ifconfig(3, (char**) ifconfg_args); + if (rtems_bsd_command_ifconfig(3, (char**) ifconfg_args)) { + return -1; + } } return 0; @@ -377,7 +379,7 @@ defaultrouter(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa, bool memset(&sin, 0, sizeof(sin)); memset(&rti_info[0], 0, sizeof(rti_info)); sin.sin_family = AF_INET; - inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr); + (void) inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr); r = rtems_get_route(&sin, rti_info); if (r == 0 && rti_info[RTAX_GATEWAY] != NULL) { @@ -710,9 +712,9 @@ run_dhcp(rtems_bsd_rc_conf* rc_conf, rtems_bsd_rc_conf_argc_argv* aa) } dd->config.priority = priority; - rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv); + r = rtems_bsd_rc_conf_find(rc_conf, "dhcpcd_options", dd->argc_argv); - if (dd->argc_argv->argc > 0) { + if (r == 0 && dd->argc_argv->argc > 0) { dd->config.argc = dd->argc_argv->argc; dd->config.argv = dd->argc_argv->argv; } diff --git a/rtemsbsd/rtems/rtems-bsd-rc-conf.c b/rtemsbsd/rtems/rtems-bsd-rc-conf.c index 36f90a1d..88d98c3e 100644 --- a/rtemsbsd/rtems/rtems-bsd-rc-conf.c +++ b/rtemsbsd/rtems/rtems-bsd-rc-conf.c @@ -260,12 +260,14 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, */ length = strnlen(text, RTEMS_BSD_RC_CONF_MAX_SIZE); if (length == RTEMS_BSD_RC_CONF_MAX_SIZE) { + free(_rc_conf); errno = E2BIG; return -1; } copy = strdup(text); if (copy == NULL) { + free(_rc_conf); errno = ENOMEM; return -1; } @@ -286,6 +288,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, lines = malloc(sizeof(char*) * line_count); if (lines == NULL) { free(copy); + free(_rc_conf); errno = ENOMEM; return -1; } @@ -335,6 +338,13 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, if (timeout >= 0) _rc_conf->waiter = rtems_task_self(); + if (_rc_conf->name == NULL) { + free((void*) _rc_conf->lines); + free((void*) _rc_conf->data); + free(_rc_conf); + return -1; + } + /* * Create the lock. */ @@ -343,6 +353,7 @@ rc_conf_create(rtems_bsd_rc_conf** rc_conf, free((void*) _rc_conf->name); free((void*) _rc_conf->lines); free((void*) _rc_conf->data); + free(_rc_conf); return -1; } @@ -714,6 +725,7 @@ rc_conf_worker(rtems_task_argument task_argument) rtems_chain_node* node = rtems_chain_first(&services); int r = 0; int error; + bool rc_conf_verbose; /* * Check for a syslog priority before any services are run. @@ -748,6 +760,8 @@ rc_conf_worker(rtems_task_argument task_argument) if (r < 0) rc_conf->error_code = error; + rc_conf_verbose = rc_conf->verbose; + /* * If there is a waiter signal else clean up because the waiter has gone. */ @@ -760,10 +774,10 @@ rc_conf_worker(rtems_task_argument task_argument) rc_conf_destroy(rc_conf); } - if (rc_conf->verbose) + if (rc_conf_verbose) printf("rc.conf: finished\n"); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } int @@ -793,6 +807,7 @@ rtems_bsd_run_rc_conf_script(const char* name, if (sc != RTEMS_SUCCESSFUL) { fprintf(stderr, "error: %s: get priority: %s\n", name, rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return -1; } @@ -805,6 +820,7 @@ rtems_bsd_run_rc_conf_script(const char* name, &worker); if (sc != RTEMS_SUCCESSFUL) { fprintf (stderr, "error: worker create: %s", rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return -1; } @@ -814,6 +830,7 @@ rtems_bsd_run_rc_conf_script(const char* name, (rtems_task_argument) rc_conf); if (sc != RTEMS_SUCCESSFUL) { fprintf (stderr, "error: worker start: %s", rtems_status_text(sc)); + rc_conf_destroy(rc_conf); errno = EIO; return - 1; } @@ -869,7 +886,7 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose) if (r < 0) return r; - rc_conf = malloc(sb.st_size); + rc_conf = malloc(sb.st_size + 1); if (rc_conf == NULL) { errno = ENOMEM; return -1; @@ -892,6 +909,8 @@ rtems_bsd_run_rc_conf(const char* name, int timeout, bool verbose) fclose(file); + rc_conf[sb.st_size] = '\0'; + r = rtems_bsd_run_rc_conf_script(name, rc_conf, timeout, verbose); free(rc_conf); diff --git a/rtemsbsd/rtems/rtems-bsd-shell-ifmcstat.c b/rtemsbsd/rtems/rtems-bsd-shell-ifmcstat.c new file mode 100644 index 00000000..c8fbd4a1 --- /dev/null +++ b/rtemsbsd/rtems/rtems-bsd-shell-ifmcstat.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rtems/netcmds-config.h> +#include <machine/rtems-bsd-commands.h> + +rtems_shell_cmd_t rtems_shell_IFMCSTAT_Command = { + .name = "ifmcstat", + .usage = "ifmcstat [args]", + .topic = "net", + .command = rtems_bsd_command_ifmcstat +}; diff --git a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c index 4af789cc..3f705975 100644 --- a/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c +++ b/rtemsbsd/rtems/rtems-bsd-shell-wpa_supplicant_fork.c @@ -53,7 +53,7 @@ new_wpa_supplicant_task(rtems_task_argument arg) free(params->argv); free(params); - rtems_task_delete( RTEMS_SELF ); + rtems_task_exit(); } int rtems_bsd_command_wpa_supplicant_fork(int argc, char **argv) diff --git a/rtemsbsd/rtems/rtems-kernel-epoch.c b/rtemsbsd/rtems/rtems-kernel-epoch.c index f4fff0ea..642b5854 100644 --- a/rtemsbsd/rtems/rtems-kernel-epoch.c +++ b/rtemsbsd/rtems/rtems-kernel-epoch.c @@ -60,6 +60,10 @@ struct epoch_pcpu { rtems_interrupt_server_request irq_srv_req; }; +#ifdef PER_CPU_DATA_NEED_INITIALIZATION +PER_CPU_DATA_NEED_INITIALIZATION(); +#endif + static PER_CPU_DATA_ITEM(struct epoch_pcpu, epoch); static SLIST_HEAD(, epoch) epoch_list = SLIST_HEAD_INITIALIZER(epoch_list); @@ -345,15 +349,29 @@ epoch_call(epoch_t epoch, epoch_context_t ctx, void (*callback) (epoch_context_t)) { Per_CPU_Control *cpu_self; - struct epoch_record *er; struct epoch_pcpu *epcpu; + struct epoch_record *er; +#ifdef RTEMS_SMP + ISR_Level level; + Thread_Control *executing; + + _ISR_Local_disable(level); + cpu_self = _Per_CPU_Get(); + executing = _Per_CPU_Get_executing(cpu_self); + _Thread_Pin(executing); + _ISR_Local_enable(level); +#endif - cpu_self = _Thread_Dispatch_disable(); epcpu = PER_CPU_DATA_GET(cpu_self, struct epoch_pcpu, epoch); epcpu->cb_count += 1; er = EPOCH_GET_RECORD(cpu_self, epoch); ck_epoch_call(&er->er_record, ctx, callback); + +#ifdef RTEMS_SMP + cpu_self = _Thread_Dispatch_disable(); + _Thread_Unpin(executing, cpu_self); _Thread_Dispatch_enable(cpu_self); +#endif } #ifdef INVARIANTS @@ -407,7 +425,7 @@ epoch_call_drain_cb(void *arg) struct epoch_record *er; epoch = arg; - cpu = _Per_CPU_Get(); + cpu = _Per_CPU_Get_snapshot(); er = EPOCH_GET_RECORD(cpu, epoch); epoch_call(epoch, &er->er_drain_ctx, epoch_drain_cb); } @@ -421,6 +439,7 @@ epoch_drain_callbacks(epoch_t epoch) uint32_t cpu_max; rtems_id id; rtems_status_code sc; + rtems_interrupt_server_request req[CPU_MAXIMUM_PROCESSORS]; #else struct epoch_record *er; #endif @@ -429,6 +448,7 @@ epoch_drain_callbacks(epoch_t epoch) mtx_lock(&epoch->e_drain_mtx); #ifdef RTEMS_SMP + memset(&req, 0, sizeof(req)); cpu_max = rtems_scheduler_get_processor_maximum(); for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) { @@ -441,8 +461,15 @@ epoch_drain_callbacks(epoch_t epoch) for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) { sc = rtems_scheduler_ident_by_processor(cpu_index, &id); if (sc == RTEMS_SUCCESSFUL) { - _SMP_Unicast_action(cpu_index, epoch_call_drain_cb, + sc = rtems_interrupt_server_request_initialize( + cpu_index, &req[cpu_index], epoch_call_drain_cb, epoch); + if (sc == RTEMS_SUCCESSFUL) { + rtems_interrupt_server_request_submit( + &req[cpu_index]); + } else { + panic("no interrupt server for epoch drain"); + } } } #else @@ -457,4 +484,12 @@ epoch_drain_callbacks(epoch_t epoch) mtx_unlock(&epoch->e_drain_mtx); sx_xunlock(&epoch->e_drain_sx); + +#ifdef RTEMS_SMP + for (cpu_index = 0; cpu_index <= cpu_max; ++cpu_index) { + if (req[cpu_index].action.handler != NULL) { + rtems_interrupt_server_request_destroy(&req[cpu_index]); + } + } +#endif } diff --git a/rtemsbsd/rtems/rtems-kernel-init.c b/rtemsbsd/rtems/rtems-kernel-init.c index 7112914e..b0779277 100644 --- a/rtemsbsd/rtems/rtems-kernel-init.c +++ b/rtemsbsd/rtems/rtems-kernel-init.c @@ -135,7 +135,9 @@ rtems_bsd_initialize(void) sbt_tickthreshold = bttosbt(bt_tickthreshold); maxid_maxcpus = (int) rtems_scheduler_get_processor_maximum(); - mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO); + if (mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + return RTEMS_UNSATISFIED; + } sc = rtems_timer_initiate_server( rtems_bsd_get_task_priority(name), diff --git a/rtemsbsd/rtems/rtems-kernel-thread.c b/rtemsbsd/rtems/rtems-kernel-thread.c index 8e3344ef..f06999fb 100644 --- a/rtemsbsd/rtems/rtems-kernel-thread.c +++ b/rtemsbsd/rtems/rtems-kernel-thread.c @@ -280,13 +280,6 @@ rtems_bsd_thread_start(struct thread **td_ptr, void (*func)(void *), void *arg, return eno; } -static __dead2 void -rtems_bsd_thread_delete(void) -{ - rtems_task_delete(RTEMS_SELF); - BSD_PANIC("delete self failed"); -} - void kproc_start(const void *udata) { @@ -312,7 +305,7 @@ kproc_create(void (*func)(void *), void *arg, struct proc **newpp, int flags, in void kproc_exit(int ecode) { - rtems_bsd_thread_delete(); + rtems_task_exit(); } void @@ -340,7 +333,7 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, struct thread **new void kthread_exit(void) { - rtems_bsd_thread_delete(); + rtems_task_exit(); } int diff --git a/rtemsbsd/rtems/rtems-program.c b/rtemsbsd/rtems/rtems-program.c index 204ed248..1ca8e3b9 100644 --- a/rtemsbsd/rtems/rtems-program.c +++ b/rtemsbsd/rtems/rtems-program.c @@ -224,6 +224,18 @@ allocmem_free_all(struct rtems_bsd_program_control *prog_ctrl) } } +static void +call_destructors(struct rtems_bsd_program_control *prog_ctrl) +{ + struct program_destructor *node; + struct program_destructor *tmp; + + LIST_FOREACH_SAFE(node, &prog_ctrl->destructors, link, tmp) { + (*node->destructor)(node->arg); + free(node); + } +} + int rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) { @@ -251,6 +263,7 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) LIST_INIT(&prog_ctrl->open_fd); LIST_INIT(&prog_ctrl->open_file); LIST_INIT(&prog_ctrl->allocated_mem); + LIST_INIT(&prog_ctrl->destructors); if (setjmp(prog_ctrl->return_context) == 0) { exit_code = (*prog)(context); @@ -262,10 +275,48 @@ rtems_bsd_program_call(const char *name, int (*prog)(void *), void *context) fd_close_all(prog_ctrl); file_close_all(prog_ctrl); allocmem_free_all(prog_ctrl); + call_destructors(prog_ctrl); free(prog_ctrl); return (exit_code); } +void * +rtems_bsd_program_add_destructor(void (*destructor)(void *), void *arg) +{ + struct rtems_bsd_program_control *prog_ctrl; + struct program_destructor *node; + + prog_ctrl = rtems_bsd_program_get_control_or_null(); + if (prog_ctrl == NULL) { + return (NULL); + } + + node = malloc(sizeof(*node)); + if (node == NULL) { + return (NULL); + } + + node->destructor = destructor; + node->arg = arg; + LIST_INSERT_HEAD(&prog_ctrl->destructors, node, link); + return (node); +} + +void +rtems_bsd_program_remove_destructor(void *cookie, bool call) +{ + struct program_destructor *node; + + node = cookie; + LIST_REMOVE(node, link); + + if (call) { + (*node->destructor)(node->arg); + } + + free(node); +} + void rtems_bsd_program_exit(int exit_code) { diff --git a/rtemsbsd/rtems/rtems-routes.c b/rtemsbsd/rtems/rtems-routes.c index 6663e8d4..0b5250f0 100644 --- a/rtemsbsd/rtems/rtems-routes.c +++ b/rtemsbsd/rtems/rtems-routes.c @@ -85,8 +85,10 @@ int rtems_get_route(const struct sockaddr_in* sin, struct sockaddr** rti_info) } s = socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC); - if (s < 0) + if (s < 0) { + free(buf); return -1; + } rtm = (struct rt_msghdr *) buf; rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); diff --git a/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c index c24732cc..da64922f 100644 --- a/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c +++ b/rtemsbsd/sys/arm/freescale/imx/imx_rtems_gpio.c @@ -27,7 +27,7 @@ */ #include <bsp.h> -#if defined(LIBBSP_ARM_IMX_BSP_H) +#if defined(LIBBSP_ARM_IMX_BSP_H) || defined(LIBBSP_ARM_IMXRT_BSP_H) #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -303,4 +303,4 @@ EARLY_DRIVER_MODULE(imx_rtems_gpio, simplebus, imx_rtems_gpio_driver, imx_rtems_gpio_devclass, NULL, NULL, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); -#endif /* LIBBSP_ARM_IMX_BSP_H */ +#endif /* LIBBSP_ARM_IMX_BSP_H || LIBBSP_ARM_IMXRT_BSP_H */ diff --git a/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c new file mode 100644 index 00000000..b8e3a188 --- /dev/null +++ b/rtemsbsd/sys/arm/freescale/imx/imxrt1166_usbphy.c @@ -0,0 +1,227 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (c) 2013 Ian Lepore <ian@freebsd.org> + * Copyright (C) 2023 embedded brains GmbH & Co. KG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <bsp.h> +#if defined(LIBBSP_ARM_IMXRT_BSP_H) + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * USBPHY driver for Freescale i.MXRT1166. Most likely works with the whole + * i.MXRT11xx family. + * + * Based on USBPHY driver for i.MX6. + */ + +#include <rtems/bsd/local/opt_bus.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <machine/bus.h> + +#include <dev/extres/regulator/regulator.h> + +#include <fsl_device_registers.h> +#include <fsl_clock.h> + +struct imxrt1166_usbphy_softc { + device_t dev; + struct resource *mem_res; + regulator_t supply_vbus; + USBPHY_Type *regs; +}; + +static struct ofw_compat_data compat_data[] = { + {"fsl,imxrt1166-usbphy", true}, + {NULL, false} +}; + +static int +imxrt1166_usbphy_detach(device_t dev) +{ + struct imxrt1166_usbphy_softc *sc; + + sc = device_get_softc(dev); + + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (0); +} + +#define BUS_SPACE_PHYSADDR(res, offs) \ + ((u_int)(rman_get_start(res)+(offs))) + +static int +enable_vbus_supply(device_t dev, struct imxrt1166_usbphy_softc *sc) +{ + int rv; + phandle_t node; + + node = ofw_bus_get_node(dev); + if (OF_hasprop(node, "vbus-supply")) { + rv = regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", + &sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get \"vbus\" regulator\n"); + return ENXIO; + } + rv = regulator_enable(sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable \"vbus\" regulator\n"); + return ENXIO; + } + } + + return 0; +} + +static int +imxrt1166_usbphy_attach(device_t dev) +{ + struct imxrt1166_usbphy_softc *sc; + int err, rid; +#if IMXRT_IS_MIMXRT11xx + uint32_t usbClockFreq; +#endif + + sc = device_get_softc(dev); + err = 0; + + /* Allocate bus_space resources. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + err = ENXIO; + goto out; + } + + /* Enable VBUS Supply if a regulator is given */ + err = enable_vbus_supply(dev, sc); + if (err != 0) { + goto out; + } + + sc->regs = (USBPHY_Type *)BUS_SPACE_PHYSADDR(sc->mem_res, 0); + +#if IMXRT_IS_MIMXRT11xx + /* Enable register clock */ + CLOCK_EnableClock(kCLOCK_Usb); + + usbClockFreq = CLOCK_GetFreq(kCLOCK_Osc24M); + + /* + * Set the software reset bit. It will be implicitly cleared when + * setting up the clock in the next steps. + */ + sc->regs->CTRL_SET = USBPHY_CTRL_SFTRST_MASK; + + /* + * Enable PLLs. + * + * FIXME: Hacky way to find out the module. + */ + if (sc->regs == USBPHY1) { + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, usbClockFreq); + } else { + CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, usbClockFreq); + CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, usbClockFreq); + } +#else + /* Not implemented */ +#endif + + err = 0; + +out: + + if (err != 0) + imxrt1166_usbphy_detach(dev); + + return (err); +} + +static int +imxrt1166_usbphy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Freescale i.MXRT1166 USB PHY"); + + return (BUS_PROBE_DEFAULT); +} + +static device_method_t imxrt1166_usbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, imxrt1166_usbphy_probe), + DEVMETHOD(device_attach, imxrt1166_usbphy_attach), + DEVMETHOD(device_detach, imxrt1166_usbphy_detach), + + DEVMETHOD_END +}; + +static driver_t imxrt1166_usbphy_driver = { + "imxrt1166_usbphy", + imxrt1166_usbphy_methods, + sizeof(struct imxrt1166_usbphy_softc) +}; + +static devclass_t imxrt1166_usbphy_devclass; + +/* + * This driver needs to start before the ehci driver, but later than the usual + * "special" drivers like clocks and cpu. Ehci starts at DEFAULT so SUPPORTDEV + * is where this driver fits most. + */ +EARLY_DRIVER_MODULE(imxrt1166_usbphy, simplebus, imxrt1166_usbphy_driver, + imxrt1166_usbphy_devclass, 0, 0, + BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); + +#endif /* LIBBSP_ARM_IMXRT_BSP_H */ diff --git a/rtemsbsd/sys/arm/lpc/if_lpe.c b/rtemsbsd/sys/arm/lpc/if_lpe.c index 40ac162e..87ca9ff7 100755 --- a/rtemsbsd/sys/arm/lpc/if_lpe.c +++ b/rtemsbsd/sys/arm/lpc/if_lpe.c @@ -1,1428 +1,1769 @@ -#include <machine/rtems-bsd-kernel-space.h> - -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD +/** + * @file * - * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> - * All rights reserved. + * @ingroup lpc_eth * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * @brief Ethernet driver. + */ + +/* + * Copyright (C) 2009, 2022 embedded brains GmbH * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); + +#include <machine/rtems-bsd-kernel-space.h> + +#include <bsp.h> + +#if defined(LIBBSP_ARM_LPC24XX_BSP_H) || defined(LIBBSP_ARM_LPC32XX_BSP_H) #include <sys/param.h> -#include <sys/endian.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> #include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> #include <sys/module.h> -#include <sys/lock.h> -#include <sys/mutex.h> -#include <sys/rman.h> -#include <sys/bus.h> #include <sys/socket.h> +#include <sys/sockio.h> + +#include <sys/bus.h> #include <machine/bus.h> -#ifndef __rtems__ -#include <machine/intr.h> -#endif /* __rtems__ */ #include <net/if.h> -#include <net/if_arp.h> #include <net/ethernet.h> +#include <net/if_arp.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> #include <net/if_var.h> -#include <net/bpf.h> +#include <dev/mii/mii.h> -#ifndef __rtems__ -#include <dev/ofw/ofw_bus.h> -#include <dev/ofw/ofw_bus_subr.h> -#endif /* __rtems__ */ +#include <rtems/bsd/bsd.h> -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> +#include <arm/lpc/probe.h> -#include <arm/lpc/lpcreg.h> -#include <arm/lpc/lpcvar.h> -#include <arm/lpc/if_lpereg.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/lpc-ethernet-config.h> +#include <bsp/utility.h> -#include <rtems/bsd/local/miibus_if.h> -#ifdef __rtems__ -#include <machine/rtems-bsd-cache.h> -#include <rtems/bsd/bsd.h> -#endif /* __rtems__ */ +#if MCLBYTES > (2 * 1024) + #error "MCLBYTES to large" +#endif -#ifdef DEBUG -#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ - printf(fmt,##args); } while (0) +#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + #define LPC_ETH_CONFIG_TX_BUF_SIZE sizeof(struct mbuf *) #else -#define debugf(fmt, args...) + #define LPC_ETH_CONFIG_TX_BUF_SIZE 1518U #endif -struct lpe_dmamap_arg { - bus_addr_t lpe_dma_busaddr; -}; +#define DEFAULT_PHY 0 +#define WATCHDOG_TIMEOUT 5 + +typedef struct { + uint32_t start; + uint32_t control; +} lpc_eth_transfer_descriptor; + +typedef struct { + uint32_t info; + uint32_t hash_crc; +} lpc_eth_receive_status; + +typedef struct { + uint32_t mac1; + uint32_t mac2; + uint32_t ipgt; + uint32_t ipgr; + uint32_t clrt; + uint32_t maxf; + uint32_t supp; + uint32_t test; + uint32_t mcfg; + uint32_t mcmd; + uint32_t madr; + uint32_t mwtd; + uint32_t mrdd; + uint32_t mind; + uint32_t reserved_0 [2]; + uint32_t sa0; + uint32_t sa1; + uint32_t sa2; + uint32_t reserved_1 [45]; + uint32_t command; + uint32_t status; + uint32_t rxdescriptor; + uint32_t rxstatus; + uint32_t rxdescriptornum; + uint32_t rxproduceindex; + uint32_t rxconsumeindex; + uint32_t txdescriptor; + uint32_t txstatus; + uint32_t txdescriptornum; + uint32_t txproduceindex; + uint32_t txconsumeindex; + uint32_t reserved_2 [10]; + uint32_t tsv0; + uint32_t tsv1; + uint32_t rsv; + uint32_t reserved_3 [3]; + uint32_t flowcontrolcnt; + uint32_t flowcontrolsts; + uint32_t reserved_4 [34]; + uint32_t rxfilterctrl; + uint32_t rxfilterwolsts; + uint32_t rxfilterwolclr; + uint32_t reserved_5 [1]; + uint32_t hashfilterl; + uint32_t hashfilterh; + uint32_t reserved_6 [882]; + uint32_t intstatus; + uint32_t intenable; + uint32_t intclear; + uint32_t intset; + uint32_t reserved_7 [1]; + uint32_t powerdown; +} lpc_eth_controller; + +#define LPE_LOCK(e) mtx_lock(&(e)->mtx) + +#define LPE_UNLOCK(e) mtx_unlock(&(e)->mtx) + +static volatile lpc_eth_controller *const lpc_eth = + (volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE; + +/* ETH_RX_CTRL */ + +#define ETH_RX_CTRL_SIZE_MASK 0x000007ffU +#define ETH_RX_CTRL_INTERRUPT 0x80000000U + +/* ETH_RX_STAT */ + +#define ETH_RX_STAT_RXSIZE_MASK 0x000007ffU +#define ETH_RX_STAT_BYTES 0x00000100U +#define ETH_RX_STAT_CONTROL_FRAME 0x00040000U +#define ETH_RX_STAT_VLAN 0x00080000U +#define ETH_RX_STAT_FAIL_FILTER 0x00100000U +#define ETH_RX_STAT_MULTICAST 0x00200000U +#define ETH_RX_STAT_BROADCAST 0x00400000U +#define ETH_RX_STAT_CRC_ERROR 0x00800000U +#define ETH_RX_STAT_SYMBOL_ERROR 0x01000000U +#define ETH_RX_STAT_LENGTH_ERROR 0x02000000U +#define ETH_RX_STAT_RANGE_ERROR 0x04000000U +#define ETH_RX_STAT_ALIGNMENT_ERROR 0x08000000U +#define ETH_RX_STAT_OVERRUN 0x10000000U +#define ETH_RX_STAT_NO_DESCRIPTOR 0x20000000U +#define ETH_RX_STAT_LAST_FLAG 0x40000000U +#define ETH_RX_STAT_ERROR 0x80000000U + +/* ETH_TX_CTRL */ + +#define ETH_TX_CTRL_SIZE_MASK 0x7ffU +#define ETH_TX_CTRL_SIZE_SHIFT 0 +#define ETH_TX_CTRL_OVERRIDE 0x04000000U +#define ETH_TX_CTRL_HUGE 0x08000000U +#define ETH_TX_CTRL_PAD 0x10000000U +#define ETH_TX_CTRL_CRC 0x20000000U +#define ETH_TX_CTRL_LAST 0x40000000U +#define ETH_TX_CTRL_INTERRUPT 0x80000000U + +/* ETH_TX_STAT */ + +#define ETH_TX_STAT_COLLISION_COUNT_MASK 0x01e00000U +#define ETH_TX_STAT_DEFER 0x02000000U +#define ETH_TX_STAT_EXCESSIVE_DEFER 0x04000000U +#define ETH_TX_STAT_EXCESSIVE_COLLISION 0x08000000U +#define ETH_TX_STAT_LATE_COLLISION 0x10000000U +#define ETH_TX_STAT_UNDERRUN 0x20000000U +#define ETH_TX_STAT_NO_DESCRIPTOR 0x40000000U +#define ETH_TX_STAT_ERROR 0x80000000U + +/* ETH_INT */ + +#define ETH_INT_RX_OVERRUN 0x00000001U +#define ETH_INT_RX_ERROR 0x00000002U +#define ETH_INT_RX_FINISHED 0x00000004U +#define ETH_INT_RX_DONE 0x00000008U +#define ETH_INT_TX_UNDERRUN 0x00000010U +#define ETH_INT_TX_ERROR 0x00000020U +#define ETH_INT_TX_FINISHED 0x00000040U +#define ETH_INT_TX_DONE 0x00000080U +#define ETH_INT_SOFT 0x00001000U +#define ETH_INT_WAKEUP 0x00002000U + +/* ETH_RX_FIL_CTRL */ + +#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST 0x00000001U +#define ETH_RX_FIL_CTRL_ACCEPT_BROADCAST 0x00000002U +#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST 0x00000004U +#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST_HASH 0x00000008U +#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH 0x00000010U +#define ETH_RX_FIL_CTRL_ACCEPT_PERFECT 0x00000020U +#define ETH_RX_FIL_CTRL_MAGIC_PACKET_WOL 0x00001000U +#define ETH_RX_FIL_CTRL_RX_FILTER_WOL 0x00002000U + +/* ETH_CMD */ + +#define ETH_CMD_RX_ENABLE 0x00000001U +#define ETH_CMD_TX_ENABLE 0x00000002U +#define ETH_CMD_REG_RESET 0x00000008U +#define ETH_CMD_TX_RESET 0x00000010U +#define ETH_CMD_RX_RESET 0x00000020U +#define ETH_CMD_PASS_RUNT_FRAME 0x00000040U +#define ETH_CMD_PASS_RX_FILTER 0X00000080U +#define ETH_CMD_TX_FLOW_CONTROL 0x00000100U +#define ETH_CMD_RMII 0x00000200U +#define ETH_CMD_FULL_DUPLEX 0x00000400U -struct lpe_rxdesc { - struct mbuf * lpe_rxdesc_mbuf; -#ifndef __rtems__ - bus_dmamap_t lpe_rxdesc_dmamap; -#endif /* __rtems__ */ -}; +/* ETH_STAT */ -struct lpe_txdesc { - int lpe_txdesc_first; - struct mbuf * lpe_txdesc_mbuf; -#ifndef __rtems__ - bus_dmamap_t lpe_txdesc_dmamap; -#endif /* __rtems__ */ -}; +#define ETH_STAT_RX_ACTIVE 0x00000001U +#define ETH_STAT_TX_ACTIVE 0x00000002U -struct lpe_chain_data { - bus_dma_tag_t lpe_parent_tag; - bus_dma_tag_t lpe_tx_ring_tag; - bus_dmamap_t lpe_tx_ring_map; - bus_dma_tag_t lpe_tx_status_tag; - bus_dmamap_t lpe_tx_status_map; - bus_dma_tag_t lpe_tx_buf_tag; - bus_dma_tag_t lpe_rx_ring_tag; - bus_dmamap_t lpe_rx_ring_map; - bus_dma_tag_t lpe_rx_status_tag; - bus_dmamap_t lpe_rx_status_map; - bus_dma_tag_t lpe_rx_buf_tag; - struct lpe_rxdesc lpe_rx_desc[LPE_RXDESC_NUM]; - struct lpe_txdesc lpe_tx_desc[LPE_TXDESC_NUM]; - int lpe_tx_prod; - int lpe_tx_last; - int lpe_tx_used; -}; +/* ETH_MAC2 */ -struct lpe_ring_data { - struct lpe_hwdesc * lpe_rx_ring; - struct lpe_hwstatus * lpe_rx_status; - bus_addr_t lpe_rx_ring_phys; - bus_addr_t lpe_rx_status_phys; - struct lpe_hwdesc * lpe_tx_ring; - struct lpe_hwstatus * lpe_tx_status; - bus_addr_t lpe_tx_ring_phys; - bus_addr_t lpe_tx_status_phys; -}; +#define ETH_MAC2_FULL_DUPLEX BSP_BIT32(8) -struct lpe_softc { - struct ifnet * lpe_ifp; - struct mtx lpe_mtx; -#ifndef __rtems__ - phandle_t lpe_ofw; -#endif /* __rtems__ */ - device_t lpe_dev; - device_t lpe_miibus; - uint8_t lpe_enaddr[6]; - struct resource * lpe_mem_res; - struct resource * lpe_irq_res; - void * lpe_intrhand; - bus_space_tag_t lpe_bst; - bus_space_handle_t lpe_bsh; -#define LPE_FLAG_LINK (1 << 0) - uint32_t lpe_flags; - int lpe_watchdog_timer; - struct callout lpe_tick; - struct lpe_chain_data lpe_cdata; - struct lpe_ring_data lpe_rdata; -}; +/* ETH_SUPP */ -static int lpe_probe(device_t); -static int lpe_attach(device_t); -static int lpe_detach(device_t); -static int lpe_miibus_readreg(device_t, int, int); -static int lpe_miibus_writereg(device_t, int, int, int); -static void lpe_miibus_statchg(device_t); - -static void lpe_reset(struct lpe_softc *); -static void lpe_init(void *); -static void lpe_init_locked(struct lpe_softc *); -static void lpe_start(struct ifnet *); -static void lpe_start_locked(struct ifnet *); -static void lpe_stop(struct lpe_softc *); -static void lpe_stop_locked(struct lpe_softc *); -static int lpe_ioctl(struct ifnet *, u_long, caddr_t); -static void lpe_set_rxmode(struct lpe_softc *); -static void lpe_set_rxfilter(struct lpe_softc *); -static void lpe_intr(void *); -static void lpe_rxintr(struct lpe_softc *); -static void lpe_txintr(struct lpe_softc *); -static void lpe_tick(void *); -static void lpe_watchdog(struct lpe_softc *); -static int lpe_encap(struct lpe_softc *, struct mbuf **); -static int lpe_dma_alloc(struct lpe_softc *); -static int lpe_dma_alloc_rx(struct lpe_softc *); -static int lpe_dma_alloc_tx(struct lpe_softc *); -static int lpe_init_rx(struct lpe_softc *); -static int lpe_init_rxbuf(struct lpe_softc *, int); -static void lpe_discard_rxbuf(struct lpe_softc *, int); -static void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int); -static int lpe_ifmedia_upd(struct ifnet *); -static void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *); - -#define lpe_lock(_sc) mtx_lock(&(_sc)->lpe_mtx) -#define lpe_unlock(_sc) mtx_unlock(&(_sc)->lpe_mtx) -#define lpe_lock_assert(_sc) mtx_assert(&(_sc)->lpe_mtx, MA_OWNED) - -#define lpe_read_4(_sc, _reg) \ - bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg)) -#define lpe_write_4(_sc, _reg, _val) \ - bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val)) - -#define LPE_HWDESC_RXERRS (LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \ - LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \ - LPE_HWDESC_RXNODESCR) - -#define LPE_HWDESC_TXERRS (LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \ - LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR) - -static int -lpe_probe(device_t dev) -{ - -#ifndef __rtems__ - if (!ofw_bus_status_okay(dev)) - return (ENXIO); +#define ETH_SUPP_SPEED BSP_BIT32(8) - if (!ofw_bus_is_compatible(dev, "lpc,ethernet")) - return (ENXIO); -#endif /* __rtems__ */ +/* ETH_MCFG */ - device_set_desc(dev, "LPC32x0 10/100 Ethernet"); - return (BUS_PROBE_DEFAULT); -} +#define ETH_MCFG_CLOCK_SELECT(val) BSP_FLD32(val, 2, 4) -static int -lpe_attach(device_t dev) -{ - struct lpe_softc *sc = device_get_softc(dev); - struct ifnet *ifp; -#ifndef __rtems__ - int rid, i; - uint32_t val; -#else /* __rtems__ */ - int rid; -#endif /* __rtems__ */ - - sc->lpe_dev = dev; -#ifndef __rtems__ - sc->lpe_ofw = ofw_bus_get_node(dev); - - i = OF_getprop(sc->lpe_ofw, "local-mac-address", (void *)&sc->lpe_enaddr, 6); - if (i != 6) { - sc->lpe_enaddr[0] = 0x00; - sc->lpe_enaddr[1] = 0x11; - sc->lpe_enaddr[2] = 0x22; - sc->lpe_enaddr[3] = 0x33; - sc->lpe_enaddr[4] = 0x44; - sc->lpe_enaddr[5] = 0x55; - } -#else /* __rtems__ */ - rtems_bsd_get_mac_address(device_get_name(sc->lpe_dev), device_get_unit(sc->lpe_dev), sc->lpe_enaddr); -#endif /* __rtems__ */ - - mtx_init(&sc->lpe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF); - - callout_init_mtx(&sc->lpe_tick, &sc->lpe_mtx, 0); - - rid = 0; - sc->lpe_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (!sc->lpe_mem_res) { - device_printf(dev, "cannot allocate memory window\n"); - goto fail; - } - - sc->lpe_bst = rman_get_bustag(sc->lpe_mem_res); - sc->lpe_bsh = rman_get_bushandle(sc->lpe_mem_res); - - rid = 0; - sc->lpe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); - if (!sc->lpe_irq_res) { - device_printf(dev, "cannot allocate interrupt\n"); - goto fail; - } - - sc->lpe_ifp = if_alloc(IFT_ETHER); - if (!sc->lpe_ifp) { - device_printf(dev, "cannot allocated ifnet\n"); - goto fail; - } - - ifp = sc->lpe_ifp; - - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_softc = sc; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_start = lpe_start; - ifp->if_ioctl = lpe_ioctl; - ifp->if_init = lpe_init; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; - IFQ_SET_READY(&ifp->if_snd); - - ether_ifattach(ifp, sc->lpe_enaddr); - - if (bus_setup_intr(dev, sc->lpe_irq_res, INTR_TYPE_NET, NULL, - lpe_intr, sc, &sc->lpe_intrhand)) { - device_printf(dev, "cannot establish interrupt handler\n"); - ether_ifdetach(ifp); - goto fail; - } - - /* Enable Ethernet clock */ -#ifndef __rtems__ - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(3)); -#else /* __rtems__ */ -#ifdef LPC32XX_ETHERNET_RMII - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(3)); -#else - lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL, - LPC_CLKPWR_MACCLK_CTRL_REG | - LPC_CLKPWR_MACCLK_CTRL_SLAVE | - LPC_CLKPWR_MACCLK_CTRL_MASTER | - LPC_CLKPWR_MACCLK_CTRL_HDWINF(1)); -#endif -#endif /* __rtems__ */ - - /* Reset chip */ - lpe_reset(sc); - - /* Initialize MII */ -#ifndef __rtems__ - val = lpe_read_4(sc, LPE_COMMAND); - lpe_write_4(sc, LPE_COMMAND, val | LPE_COMMAND_RMII); - - if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd, - lpe_ifmedia_sts, BMSR_DEFCAPMASK, 0x01, - MII_OFFSET_ANY, 0)) { - device_printf(dev, "cannot find PHY\n"); - goto fail; - } -#else /* __rtems__ */ - if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd, - lpe_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, - MII_OFFSET_ANY, 0)) { - device_printf(dev, "cannot find PHY\n"); - goto fail; - } -#endif /* __rtems__ */ - - lpe_dma_alloc(sc); - - return (0); - -fail: - if (sc->lpe_ifp) - if_free(sc->lpe_ifp); - if (sc->lpe_intrhand) - bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand); - if (sc->lpe_irq_res) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res); - if (sc->lpe_mem_res) - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res); - return (ENXIO); -} +#define ETH_MCFG_RESETMIIMGMT BSP_BIT32(15) -static int -lpe_detach(device_t dev) -{ - struct lpe_softc *sc = device_get_softc(dev); +/* ETH_MCMD */ - lpe_stop(sc); +#define ETH_MCMD_READ BSP_BIT32(0) +#define ETH_MCMD_SCAN BSP_BIT32(1) - if_free(sc->lpe_ifp); - bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res); - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res); +/* ETH_MADR */ - return (0); -} +#define ETH_MADR_REG(val) BSP_FLD32(val, 0, 4) +#define ETH_MADR_PHY(val) BSP_FLD32(val, 8, 12) -static int -lpe_miibus_readreg(device_t dev, int phy, int reg) -{ - struct lpe_softc *sc = device_get_softc(dev); - uint32_t val; - int result; +/* ETH_MIND */ - lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ); - lpe_write_4(sc, LPE_MADR, - (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT | - (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT); +#define ETH_MIND_BUSY BSP_BIT32(0) +#define ETH_MIND_SCANNING BSP_BIT32(1) +#define ETH_MIND_NOT_VALID BSP_BIT32(2) +#define ETH_MIND_MII_LINK_FAIL BSP_BIT32(3) - val = lpe_read_4(sc, LPE_MIND); +/* Events */ - /* Wait until request is completed */ - while (val & LPE_MIND_BUSY) { - val = lpe_read_4(sc, LPE_MIND); - DELAY(10); - } +#define LPC_ETH_EVENT_INIT_RX RTEMS_EVENT_0 - if (val & LPE_MIND_INVALID) - return (0); +#define LPC_ETH_EVENT_INIT_TX RTEMS_EVENT_1 - lpe_write_4(sc, LPE_MCMD, 0); - result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK); - debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result); +#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3 - return (result); -} +#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4 -static int -lpe_miibus_writereg(device_t dev, int phy, int reg, int data) -{ - struct lpe_softc *sc = device_get_softc(dev); - uint32_t val; +/* Status */ - debugf("phy=%d reg=%d data=0x%04x\n", phy, reg, data); +#define LPC_ETH_INTERRUPT_RECEIVE \ + (ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE) - lpe_write_4(sc, LPE_MCMD, LPE_MCMD_WRITE); - lpe_write_4(sc, LPE_MADR, - (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT | - (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT); +#define LPC_ETH_RX_STAT_ERRORS \ + (ETH_RX_STAT_CRC_ERROR \ + | ETH_RX_STAT_SYMBOL_ERROR \ + | ETH_RX_STAT_LENGTH_ERROR \ + | ETH_RX_STAT_ALIGNMENT_ERROR \ + | ETH_RX_STAT_OVERRUN \ + | ETH_RX_STAT_NO_DESCRIPTOR) - lpe_write_4(sc, LPE_MWTD, (data & LPE_MWTD_DATAMASK)); +#define LPC_ETH_LAST_FRAGMENT_FLAGS \ + (ETH_TX_CTRL_OVERRIDE \ + | ETH_TX_CTRL_PAD \ + | ETH_TX_CTRL_CRC \ + | ETH_TX_CTRL_INTERRUPT \ + | ETH_TX_CTRL_LAST) - val = lpe_read_4(sc, LPE_MIND); +/* Debug */ - /* Wait until request is completed */ - while (val & LPE_MIND_BUSY) { - val = lpe_read_4(sc, LPE_MIND); - DELAY(10); - } +#ifdef DEBUG + #define LPC_ETH_PRINTF(...) printf(__VA_ARGS__) + #define LPC_ETH_PRINTK(...) printk(__VA_ARGS__) +#else + #define LPC_ETH_PRINTF(...) + #define LPC_ETH_PRINTK(...) +#endif - return (0); +typedef enum { + LPC_ETH_STATE_NOT_INITIALIZED = 0, + LPC_ETH_STATE_DOWN, + LPC_ETH_STATE_UP +} lpc_eth_state; + +typedef struct { + device_t dev; + struct ifnet *ifp; + struct mtx mtx; + lpc_eth_state state; + uint32_t anlpar; + struct callout watchdog_callout; + rtems_id receive_task; + unsigned rx_unit_count; + unsigned tx_unit_count; + volatile lpc_eth_transfer_descriptor *rx_desc_table; + volatile lpc_eth_receive_status *rx_status_table; + struct mbuf **rx_mbuf_table; + volatile lpc_eth_transfer_descriptor *tx_desc_table; + volatile uint32_t *tx_status_table; + void *tx_buf_table; + uint32_t tx_produce_index; + uint32_t tx_consume_index; + unsigned received_frames; + unsigned receive_interrupts; + unsigned transmitted_frames; + unsigned receive_drop_errors; + unsigned receive_overrun_errors; + unsigned receive_fragment_errors; + unsigned receive_crc_errors; + unsigned receive_symbol_errors; + unsigned receive_length_errors; + unsigned receive_alignment_errors; + unsigned receive_no_descriptor_errors; + unsigned receive_fatal_errors; + unsigned transmit_underrun_errors; + unsigned transmit_late_collision_errors; + unsigned transmit_excessive_collision_errors; + unsigned transmit_excessive_defer_errors; + unsigned transmit_no_descriptor_errors; + unsigned transmit_overflow_errors; + unsigned transmit_fatal_errors; + uint32_t phy_id; + int phy; + rtems_vector_number interrupt_number; + rtems_id control_task; + int if_flags; + struct ifmedia ifmedia; +} lpc_eth_driver_entry; + +static void lpc_eth_interface_watchdog(void *arg); + +static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e); + +static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e) +{ + rtems_status_code sc = rtems_event_transient_send(e->control_task); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); } -static void -lpe_miibus_statchg(device_t dev) +static void lpc_eth_control_request( + lpc_eth_driver_entry *e, + rtems_id task, + rtems_event_set event +) { - struct lpe_softc *sc = device_get_softc(dev); - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - -#ifndef __rtems__ - lpe_lock(sc); -#endif /* __rtems__ */ - - if ((mii->mii_media_status & IFM_ACTIVE) && - (mii->mii_media_status & IFM_AVALID)) - sc->lpe_flags |= LPE_FLAG_LINK; - else - sc->lpe_flags &= ~LPE_FLAG_LINK; - -#ifndef __rtems__ - lpe_unlock(sc); -#endif /* __rtems__ */ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + e->control_task = rtems_task_self(); + + sc = rtems_event_send(task, event); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + e->control_task = 0; } -static void -lpe_reset(struct lpe_softc *sc) +static inline uint32_t lpc_eth_increment( + uint32_t value, + uint32_t cycle +) { - uint32_t mac1; - -#ifndef __rtems__ - /* Enter soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX); - - /* Reset registers, Tx path and Rx path */ - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET | - LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET); - - /* Set station address */ - lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]); - lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]); - lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]); - - /* Leave soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX)); -#else /* __rtems__ */ - /* Reset registers, Tx path and Rx path */ - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET | LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET); - - /* Enter soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX); - - /* Leave soft reset mode */ - mac1 = lpe_read_4(sc, LPE_MAC1); - lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX | - LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX)); - - /* Reinitialize registers */ - lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(0x7)); - lpe_write_4(sc, LPE_MAC2, LPE_MAC2_PADCRCENABLE | LPE_MAC2_CRCENABLE | LPE_MAC2_FULLDUPLEX); - lpe_write_4(sc, LPE_IPGT, 0x15); - lpe_write_4(sc, LPE_IPGR, 0x12); - lpe_write_4(sc, LPE_CLRT, 0x370f); - lpe_write_4(sc, LPE_MAXF, 0x0600); - lpe_write_4(sc, LPE_SUPP, LPE_SUPP_SPEED); - lpe_write_4(sc, LPE_TEST, 0x0); -#ifdef LPC32XX_ETHERNET_RMII - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX | LPE_COMMAND_RMII); -#else - lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_FULLDUPLEX); -#endif - lpe_write_4(sc, LPE_INTENABLE, 0x0); - lpe_write_4(sc, LPE_INTCLEAR, 0x30ff); - lpe_write_4(sc, LPE_POWERDOWN, 0x0); - - /* Set station address */ - lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]); - lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]); - lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]); -#endif /* __rtems__ */ + if (value < cycle) { + return ++value; + } else { + return 0; + } } -static void -lpe_init(void *arg) +static void lpc_eth_enable_promiscous_mode(bool enable) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - - lpe_lock(sc); - lpe_init_locked(sc); - lpe_unlock(sc); + if (enable) { + lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_UNICAST + | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST + | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST; + } else { + lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_PERFECT + | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH + | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST; + } } -static void -lpe_init_locked(struct lpe_softc *sc) +static void lpc_eth_interrupt_handler(void *arg) { - struct ifnet *ifp = sc->lpe_ifp; - uint32_t cmd, mac1; - - lpe_lock_assert(sc); - - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - return; + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; + rtems_event_set re = 0; + rtems_event_set te = 0; + uint32_t ie = 0; + + /* Get interrupt status */ + uint32_t im = lpc_eth->intenable; + uint32_t is = lpc_eth->intstatus & im; + + /* Check receive interrupts */ + if ((is & (ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN)) != 0) { + if ((is & ETH_INT_RX_OVERRUN) != 0) { + re = LPC_ETH_EVENT_INIT_RX; + ++e->receive_fatal_errors; + } + + if ((is & ETH_INT_TX_UNDERRUN) != 0) { + re = LPC_ETH_EVENT_INIT_TX; + ++e->transmit_fatal_errors; + } + } else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) { + re = LPC_ETH_EVENT_INTERRUPT; + ie |= LPC_ETH_INTERRUPT_RECEIVE; + ++e->receive_interrupts; + } + + /* Send events to receive task */ + if (re != 0) { + (void) rtems_event_send(e->receive_task, re); + } + + LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te); + + /* Update interrupt mask */ + lpc_eth->intenable = im & ~ie; + + /* Clear interrupts */ + lpc_eth->intclear = is; +} - /* Enable Tx and Rx */ - cmd = lpe_read_4(sc, LPE_COMMAND); - lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE | - LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME); +static void lpc_eth_enable_receive_interrupts(void) +{ + rtems_interrupt_level level; - /* Enable receive */ - mac1 = lpe_read_4(sc, LPE_MAC1); -#ifdef __rtems__ - (void)mac1; -#endif /* __rtems__ */ - lpe_write_4(sc, LPE_MAC1, /*mac1 |*/ LPE_MAC1_RXENABLE | LPE_MAC1_PASSALL); + rtems_interrupt_disable(level); + lpc_eth->intenable |= LPC_ETH_INTERRUPT_RECEIVE; + rtems_interrupt_enable(level); +} - lpe_write_4(sc, LPE_MAC2, LPE_MAC2_CRCENABLE | LPE_MAC2_PADCRCENABLE | - LPE_MAC2_FULLDUPLEX); +static void lpc_eth_disable_receive_interrupts(void) +{ + rtems_interrupt_level level; - lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7)); + rtems_interrupt_disable(level); + lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_RECEIVE; + rtems_interrupt_enable(level); +} - /* Set up Rx filter */ - lpe_set_rxmode(sc); +static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e) +{ + volatile uint32_t *const status = e->tx_status_table; + uint32_t const index_max = e->tx_unit_count - 1; + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + #endif + uint32_t produce_index; + + /* Disable transmitter */ + lpc_eth->command &= ~ETH_CMD_TX_ENABLE; + + /* Wait for inactive status */ + while ((lpc_eth->status & ETH_STAT_TX_ACTIVE) != 0) { + /* Wait */ + } + + /* Reset */ + lpc_eth->command |= ETH_CMD_TX_RESET; + + /* Transmit descriptors */ + lpc_eth->txdescriptornum = index_max; + lpc_eth->txdescriptor = (uint32_t) desc; + lpc_eth->txstatus = (uint32_t) status; + + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Discard outstanding fragments (= data loss) */ + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + m_freem(mbufs [produce_index]); + mbufs [produce_index] = NULL; + } + #else + /* Initialize descriptor table */ + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + desc [produce_index].start = + (uint32_t) (buf + produce_index * LPC_ETH_CONFIG_TX_BUF_SIZE); + } + #endif + + /* Initialize indices */ + e->tx_produce_index = lpc_eth->txproduceindex; + e->tx_consume_index = lpc_eth->txconsumeindex; + + /* Enable transmitter */ + lpc_eth->command |= ETH_CMD_TX_ENABLE; +} - /* Enable interrupts */ - lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR | - LPE_INT_RXFINISH | LPE_INT_RXDONE | LPE_INT_TXUNDERRUN | - LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE); +#define LPC_ETH_RX_DATA_OFFSET 2 - sc->lpe_cdata.lpe_tx_prod = 0; - sc->lpe_cdata.lpe_tx_last = 0; - sc->lpe_cdata.lpe_tx_used = 0; +static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait) +{ + struct mbuf *m = NULL; + int mw = wait ? M_WAITOK : M_NOWAIT; + + MGETHDR(m, mw, MT_DATA); + if (m != NULL) { + MCLGET(m, mw); + if ((m->m_flags & M_EXT) != 0) { + /* Set receive interface */ + m->m_pkthdr.rcvif = ifp; + + /* Adjust by two bytes for proper IP header alignment */ + m->m_data = mtod(m, char *) + LPC_ETH_RX_DATA_OFFSET; + + return m; + } else { + m_free(m); + } + } + + return NULL; +} - lpe_init_rx(sc); +static bool lpc_eth_add_new_mbuf( + struct ifnet *ifp, + volatile lpc_eth_transfer_descriptor *desc, + struct mbuf **mbufs, + uint32_t i, + bool wait +) +{ + /* New mbuf */ + struct mbuf *m = lpc_eth_new_mbuf(ifp, wait); + + /* Check mbuf */ + if (m != NULL) { + /* Cache invalidate */ + rtems_cache_invalidate_multiple_data_lines( + mtod(m, void *), + MCLBYTES - LPC_ETH_RX_DATA_OFFSET + ); + + /* Add mbuf to queue */ + desc [i].start = mtod(m, uint32_t); + desc [i].control = (MCLBYTES - LPC_ETH_RX_DATA_OFFSET - 1) + | ETH_RX_CTRL_INTERRUPT; + + /* Cache flush of descriptor */ + rtems_cache_flush_multiple_data_lines( + (void *) &desc [i], + sizeof(desc [0]) + ); + + /* Add mbuf to table */ + mbufs [i] = m; + + return true; + } else { + return false; + } +} - /* Initialize Rx packet and status descriptor heads */ - lpe_write_4(sc, LPE_RXDESC, sc->lpe_rdata.lpe_rx_ring_phys); - lpe_write_4(sc, LPE_RXSTATUS, sc->lpe_rdata.lpe_rx_status_phys); - lpe_write_4(sc, LPE_RXDESC_NUMBER, LPE_RXDESC_NUM - 1); - lpe_write_4(sc, LPE_RXDESC_CONS, 0); +static void lpc_eth_receive_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_event_set events = 0; + lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg; + struct ifnet *const ifp = e->ifp; + volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table; + volatile lpc_eth_receive_status *const status = e->rx_status_table; + struct mbuf **const mbufs = e->rx_mbuf_table; + uint32_t const index_max = e->rx_unit_count - 1; + uint32_t produce_index = 0; + uint32_t consume_index = 0; + + LPC_ETH_PRINTF("%s\n", __func__); + + /* Main event loop */ + while (true) { + /* Wait for events */ + sc = rtems_event_receive( + LPC_ETH_EVENT_INIT_RX + | LPC_ETH_EVENT_INIT_TX + | LPC_ETH_EVENT_STOP + | LPC_ETH_EVENT_INTERRUPT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events); + + /* Stop receiver? */ + if ((events & LPC_ETH_EVENT_STOP) != 0) { + lpc_eth_control_request_complete(e); + + /* Wait for events */ + continue; + } + + /* Initialize receiver or transmitter? */ + if ((events & (LPC_ETH_EVENT_INIT_RX | LPC_ETH_EVENT_INIT_TX)) != 0) { + if ((events & LPC_ETH_EVENT_INIT_RX) != 0) { + /* Disable receive interrupts */ + lpc_eth_disable_receive_interrupts(); + + /* Disable receiver */ + lpc_eth->command &= ~ETH_CMD_RX_ENABLE; + + /* Wait for inactive status */ + while ((lpc_eth->status & ETH_STAT_RX_ACTIVE) != 0) { + /* Wait */ + } + + /* Reset */ + lpc_eth->command |= ETH_CMD_RX_RESET; + + /* Clear receive interrupts */ + lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE; + + /* Move existing mbufs to the front */ + consume_index = 0; + for (produce_index = 0; produce_index <= index_max; ++produce_index) { + if (mbufs [produce_index] != NULL) { + mbufs [consume_index] = mbufs [produce_index]; + ++consume_index; + } + } + + /* Fill receive queue */ + for ( + produce_index = consume_index; + produce_index <= index_max; + ++produce_index + ) { + lpc_eth_add_new_mbuf(ifp, desc, mbufs, produce_index, true); + } + + /* Receive descriptor table */ + lpc_eth->rxdescriptornum = index_max; + lpc_eth->rxdescriptor = (uint32_t) desc; + lpc_eth->rxstatus = (uint32_t) status; + + /* Initialize indices */ + produce_index = lpc_eth->rxproduceindex; + consume_index = lpc_eth->rxconsumeindex; + + /* Enable receiver */ + lpc_eth->command |= ETH_CMD_RX_ENABLE; + + /* Enable receive interrupts */ + lpc_eth_enable_receive_interrupts(); + + lpc_eth_control_request_complete(e); + } + + if ((events & LPC_ETH_EVENT_INIT_TX) != 0) { + LPE_LOCK(e); + lpc_eth_initialize_transmit(e); + LPE_UNLOCK(e); + } + + /* Wait for events */ + continue; + } + + while (true) { + /* Clear receive interrupt status */ + lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE; + + /* Get current produce index */ + produce_index = lpc_eth->rxproduceindex; + + if (consume_index != produce_index) { + uint32_t stat = 0; + + /* Fragment status */ + rtems_cache_invalidate_multiple_data_lines( + (void *) &status [consume_index], + sizeof(status [0]) + ); + stat = status [consume_index].info; + + if ( + (stat & ETH_RX_STAT_LAST_FLAG) != 0 + && (stat & LPC_ETH_RX_STAT_ERRORS) == 0 + ) { + /* Received mbuf */ + struct mbuf *m = mbufs [consume_index]; + + if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) { + /* Discard Ethernet CRC */ + int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1 - ETHER_CRC_LEN; + + /* Update mbuf */ + m->m_len = sz; + m->m_pkthdr.len = sz; + + LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz); + + /* Hand over */ + (*ifp->if_input)(ifp, m); + + /* Increment received frames counter */ + ++e->received_frames; + } else { + ++e->receive_drop_errors; + } + } else { + /* Update error counters */ + if ((stat & ETH_RX_STAT_OVERRUN) != 0) { + ++e->receive_overrun_errors; + } + if ((stat & ETH_RX_STAT_LAST_FLAG) == 0) { + ++e->receive_fragment_errors; + } + if ((stat & ETH_RX_STAT_CRC_ERROR) != 0) { + ++e->receive_crc_errors; + } + if ((stat & ETH_RX_STAT_SYMBOL_ERROR) != 0) { + ++e->receive_symbol_errors; + } + if ((stat & ETH_RX_STAT_LENGTH_ERROR) != 0) { + ++e->receive_length_errors; + } + if ((stat & ETH_RX_STAT_ALIGNMENT_ERROR) != 0) { + ++e->receive_alignment_errors; + } + if ((stat & ETH_RX_STAT_NO_DESCRIPTOR) != 0) { + ++e->receive_no_descriptor_errors; + } + } + + /* Increment and update consume index */ + consume_index = lpc_eth_increment(consume_index, index_max); + lpc_eth->rxconsumeindex = consume_index; + } else { + /* Nothing to do, enable receive interrupts */ + lpc_eth_enable_receive_interrupts(); + break; + } + } + } +} - /* Initialize Tx packet and status descriptor heads */ - lpe_write_4(sc, LPE_TXDESC, sc->lpe_rdata.lpe_tx_ring_phys); - lpe_write_4(sc, LPE_TXSTATUS, sc->lpe_rdata.lpe_tx_status_phys); - lpe_write_4(sc, LPE_TXDESC_NUMBER, LPE_TXDESC_NUM - 1); - lpe_write_4(sc, LPE_TXDESC_PROD, 0); +static struct mbuf *lpc_eth_next_fragment( + struct ifnet *ifp, + struct mbuf *m, + uint32_t *ctrl +) +{ + struct mbuf *n; + int size; + + while (true) { + /* Get fragment size */ + size = m->m_len; + + if (size > 0) { + /* Now we have a not empty fragment */ + break; + } else { + /* Skip empty fragments */ + m = m->m_next; + + if (m == NULL) { + return NULL; + } + } + } + + /* Set fragment size */ + *ctrl = (uint32_t) (size - 1); + + /* Discard empty successive fragments */ + n = m->m_next; + while (n != NULL && n->m_len <= 0) { + n = m_free(n); + } + m->m_next = n; + + /* Is our fragment the last in the frame? */ + if (n == NULL) { + *ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS; + } + + return m; +} - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +static void lpc_eth_tx_reclaim(lpc_eth_driver_entry *e, struct ifnet *ifp) +{ + volatile uint32_t *const status = e->tx_status_table; + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + #endif + uint32_t const index_max = e->tx_unit_count - 1; + uint32_t consume_index = e->tx_consume_index; + + /* Free consumed fragments */ + while (true) { + /* Save last known consume index */ + uint32_t c = consume_index; + + /* Get new consume index */ + consume_index = lpc_eth->txconsumeindex; + + /* Nothing consumed in the meantime? */ + if (c == consume_index) { + break; + } + + while (c != consume_index) { + uint32_t s = status [c]; + + /* Update error counters */ + if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) { + if ((s & ETH_TX_STAT_UNDERRUN) != 0) { + ++e->transmit_underrun_errors; + } + if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) { + ++e->transmit_late_collision_errors; + } + if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) { + ++e->transmit_excessive_collision_errors; + } + if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) { + ++e->transmit_excessive_defer_errors; + } + if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) { + ++e->transmit_no_descriptor_errors; + } + } + + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Release mbuf */ + m_freem(mbufs [c]); + mbufs [c] = NULL; + #endif + + /* Next consume index */ + c = lpc_eth_increment(c, index_max); + } + } + + e->tx_consume_index = consume_index; +} - callout_reset(&sc->lpe_tick, hz, lpe_tick, sc); +static int lpc_eth_tx_enqueue( + lpc_eth_driver_entry *e, + struct ifnet *ifp, + struct mbuf *m0 +) +{ + volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table; + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + struct mbuf **const mbufs = e->tx_buf_table; + #else + char *const buf = e->tx_buf_table; + uint32_t frame_length; + char *frame_buffer; + #endif + uint32_t const index_max = e->tx_unit_count - 1; + uint32_t produce_index = e->tx_produce_index; + uint32_t consume_index = e->tx_consume_index; + struct mbuf *m = m0; + + while (true) { + uint32_t ctrl; + + /* Compute next produce index */ + uint32_t p = lpc_eth_increment(produce_index, index_max); + + /* Queue full? */ + if (p == consume_index) { + LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m); + + /* The queue is full */ + return ENOBUFS; + } + + /* Get next fragment and control value */ + m = lpc_eth_next_fragment(ifp, m, &ctrl); + + /* New fragment? */ + if (m != NULL) { + #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA + /* Set the transfer data */ + rtems_cache_flush_multiple_data_lines( + mtod(m, const void *), + (size_t) m->m_len + ); + desc [produce_index].start = mtod(m, uint32_t); + desc [produce_index].control = ctrl; + rtems_cache_flush_multiple_data_lines( + (void *) &desc [produce_index], + sizeof(desc [0]) + ); + + LPC_ETH_PRINTF( + "tx: %02" PRIu32 ": %u %s\n", + produce_index, m->m_len, + (ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : "" + ); + + /* Next produce index */ + produce_index = p; + + /* Last fragment of a frame? */ + if ((ctrl & ETH_TX_CTRL_LAST) != 0) { + /* Update the produce index */ + lpc_eth->txproduceindex = produce_index; + e->tx_produce_index = produce_index; + + mbufs [produce_index] = m0; + + /* Increment transmitted frames counter */ + ++e->transmitted_frames; + + return 0; + } + + /* Next fragment of the frame */ + m = m->m_next; + #else + size_t fragment_length = (size_t) m->m_len; + void *fragment_start = mtod(m, void *); + uint32_t new_frame_length = frame_length + fragment_length; + + /* Check buffer size */ + if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) { + LPC_ETH_PRINTF("tx: overflow\n"); + + /* Discard overflow data */ + new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE; + fragment_length = new_frame_length - frame_length; + + /* Finalize frame */ + ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS; + + /* Update error counter */ + ++e->transmit_overflow_errors; + } + + LPC_ETH_PRINTF( + "tx: copy: %" PRIu32 "%s%s\n", + fragment_length, + (m->m_flags & M_EXT) != 0 ? ", E" : "", + (m->m_flags & M_PKTHDR) != 0 ? ", H" : "" + ); + + /* Copy fragment to buffer in Ethernet RAM */ + memcpy(frame_buffer, fragment_start, fragment_length); + + if ((ctrl & ETH_TX_CTRL_LAST) != 0) { + /* Finalize descriptor */ + desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK) + | (new_frame_length - 1); + + LPC_ETH_PRINTF( + "tx: %02" PRIu32 ": %" PRIu32 "\n", + produce_index, + new_frame_length + ); + + /* Cache flush of data */ + rtems_cache_flush_multiple_data_lines( + (const void *) desc [produce_index].start, + new_frame_length + ); + + /* Cache flush of descriptor */ + rtems_cache_flush_multiple_data_lines( + (void *) &desc [produce_index], + sizeof(desc [0]) + ); + + /* Next produce index */ + produce_index = p; + + /* Update the produce index */ + lpc_eth->txproduceindex = produce_index; + + /* Fresh frame length and buffer start */ + frame_length = 0; + frame_buffer = (char *) desc [produce_index].start; + + /* Increment transmitted frames counter */ + ++e->transmitted_frames; + } else { + /* New frame length */ + frame_length = new_frame_length; + + /* Update current frame buffer start */ + frame_buffer += fragment_length; + } + + /* Free mbuf and get next */ + m = m_free(m); + #endif + } else { + /* Nothing to transmit */ + m_freem(m0); + return 0; + } + } } -static void -lpe_start(struct ifnet *ifp) +static int lpc_eth_mdio_wait_for_not_busy(void) { - struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc; + rtems_interval one_second = rtems_clock_get_ticks_per_second(); + rtems_interval i = 0; - lpe_lock(sc); - lpe_start_locked(ifp); - lpe_unlock(sc); + while ((lpc_eth->mind & ETH_MIND_BUSY) != 0 && i < one_second) { + rtems_task_wake_after(1); + ++i; + } + + LPC_ETH_PRINTK("tx: lpc_eth_mdio_wait %s after %d\n", + i != one_second? "succeed": "timeout", i); + + return i != one_second ? 0 : ETIMEDOUT; } -static void -lpe_start_locked(struct ifnet *ifp) +static uint32_t lpc_eth_mdio_read_anlpar(int phy) { - struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc; - struct mbuf *m_head; - int encap = 0; + uint32_t madr = ETH_MADR_REG(MII_ANLPAR) | ETH_MADR_PHY(phy); + uint32_t anlpar = 0; + int eno = 0; - lpe_lock_assert(sc); + if (lpc_eth->madr != madr) { + lpc_eth->madr = madr; + } - while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { - if (lpe_read_4(sc, LPE_TXDESC_PROD) == - lpe_read_4(sc, LPE_TXDESC_CONS) - 5) - break; + if (lpc_eth->mcmd != ETH_MCMD_READ) { + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; + } - /* Dequeue first packet */ - IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); - if (!m_head) - break; + eno = lpc_eth_mdio_wait_for_not_busy(); + if (eno == 0) { + anlpar = lpc_eth->mrdd; + } - lpe_encap(sc, &m_head); + /* Start next read */ + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; - encap++; - } - - /* Submit new descriptor list */ - if (encap) { - lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod); - sc->lpe_watchdog_timer = 5; - } - + return anlpar; } -#ifdef __rtems__ -static int -lpe_get_segs_for_tx(struct mbuf *m, bus_dma_segment_t segs[LPE_MAXFRAGS], - int *nsegs) +static int lpc_eth_mdio_read( + int phy, + void *arg RTEMS_UNUSED, + unsigned reg, + uint32_t *val +) { - int i = 0; - - do { - if (m->m_len > 0) { - segs[i].ds_addr = mtod(m, bus_addr_t); - segs[i].ds_len = m->m_len; -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(m->m_data, m->m_len); -#endif - ++i; - } - m = m->m_next; - if (m == NULL) { - *nsegs = i; - return (0); - } - } while (i < LPE_MAXFRAGS); - return (EFBIG); + int eno = 0; + + if (0 <= phy && phy <= 31) { + lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy); + lpc_eth->mcmd = 0; + lpc_eth->mcmd = ETH_MCMD_READ; + eno = lpc_eth_mdio_wait_for_not_busy(); + + if (eno == 0) { + *val = lpc_eth->mrdd; + } + } else { + eno = EINVAL; + } + + return eno; } -#endif /* __rtems__ */ -static int -lpe_encap(struct lpe_softc *sc, struct mbuf **m_head) -{ - struct lpe_txdesc *txd; - struct lpe_hwdesc *hwd; - bus_dma_segment_t segs[LPE_MAXFRAGS]; - int i, err, nsegs, prod; - - lpe_lock_assert(sc); - M_ASSERTPKTHDR((*m_head)); - - prod = sc->lpe_cdata.lpe_tx_prod; - txd = &sc->lpe_cdata.lpe_tx_desc[prod]; - - debugf("starting with prod=%d\n", prod); - -#ifndef __rtems__ - err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); -#else /* __rtems__ */ - err = lpe_get_segs_for_tx(*m_head, segs, &nsegs); -#endif /* __rtems__ */ - - if (err) - return (err); - - if (nsegs == 0) { - m_freem(*m_head); - *m_head = NULL; - return (EIO); - } - -#ifndef __rtems__ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap, - BUS_DMASYNC_PREREAD); -#endif /* __rtems__ */ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - - txd->lpe_txdesc_first = 1; - txd->lpe_txdesc_mbuf = *m_head; - - for (i = 0; i < nsegs; i++) { - hwd = &sc->lpe_rdata.lpe_tx_ring[prod]; - hwd->lhr_data = segs[i].ds_addr; - hwd->lhr_control = segs[i].ds_len - 1; - - if (i == nsegs - 1) { - hwd->lhr_control |= LPE_HWDESC_LASTFLAG; - hwd->lhr_control |= LPE_HWDESC_INTERRUPT; - hwd->lhr_control |= LPE_HWDESC_CRC; - hwd->lhr_control |= LPE_HWDESC_PAD; - } - -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd)); -#endif -#endif /* __rtems__ */ - LPE_INC(prod, LPE_TXDESC_NUM); - } - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +static int lpc_eth_mdio_write( + int phy, + void *arg RTEMS_UNUSED, + unsigned reg, + uint32_t val +) +{ + int eno = 0; - sc->lpe_cdata.lpe_tx_used += nsegs; - sc->lpe_cdata.lpe_tx_prod = prod; + if (0 <= phy && phy <= 31) { + lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy); + lpc_eth->mwtd = val; + eno = lpc_eth_mdio_wait_for_not_busy(); + } else { + eno = EINVAL; + } - return (0); + return eno; } -static void -lpe_stop(struct lpe_softc *sc) +static int lpc_eth_phy_get_id(int phy, uint32_t *id) { - lpe_lock(sc); - lpe_stop_locked(sc); - lpe_unlock(sc); -} + uint32_t id1 = 0; + int eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR1, &id1); -static void -lpe_stop_locked(struct lpe_softc *sc) -{ - lpe_lock_assert(sc); + if (eno == 0) { + uint32_t id2 = 0; - callout_stop(&sc->lpe_tick); + eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR2, &id2); + if (eno == 0) { + *id = (id1 << 16) | (id2 & 0xfff0); + } + } - /* Disable interrupts */ - lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff); + return eno; +} - /* Stop EMAC */ - lpe_write_4(sc, LPE_MAC1, 0); - lpe_write_4(sc, LPE_MAC2, 0); - lpe_write_4(sc, LPE_COMMAND, 0); +#define PHY_KSZ80X1RNL 0x221550 +#define PHY_DP83848 0x20005c90 - sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; -} +typedef struct { + unsigned reg; + uint32_t set; + uint32_t clear; +} lpc_eth_phy_action; -static int -lpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +static int lpc_eth_phy_set_and_clear( + lpc_eth_driver_entry *e, + const lpc_eth_phy_action *actions, + size_t n +) { - struct lpe_softc *sc = ifp->if_softc; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - struct ifreq *ifr = (struct ifreq *)data; - int err = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - lpe_lock(sc); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - lpe_set_rxmode(sc); - lpe_set_rxfilter(sc); - } else - lpe_init_locked(sc); - } else - lpe_stop(sc); - lpe_unlock(sc); - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - lpe_lock(sc); - lpe_set_rxfilter(sc); - lpe_unlock(sc); - } - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); - break; - default: - err = ether_ioctl(ifp, cmd, data); - break; - } - - return (err); + int eno = 0; + size_t i; + + for (i = 0; eno == 0 && i < n; ++i) { + const lpc_eth_phy_action *action = &actions [i]; + uint32_t val; + + eno = lpc_eth_mdio_read(e->phy, NULL, action->reg, &val); + if (eno == 0) { + val |= action->set; + val &= ~action->clear; + eno = lpc_eth_mdio_write(e->phy, NULL, action->reg, val); + } + } + + return eno; } -static void lpe_set_rxmode(struct lpe_softc *sc) -{ - struct ifnet *ifp = sc->lpe_ifp; - uint32_t rxfilt; +static const lpc_eth_phy_action lpc_eth_phy_up_action_default [] = { + { MII_BMCR, 0, BMCR_PDOWN }, + { MII_BMCR, BMCR_RESET, 0 }, + { MII_BMCR, BMCR_AUTOEN, 0 } +}; - rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT; +static const lpc_eth_phy_action lpc_eth_phy_up_pre_action_KSZ80X1RNL [] = { + /* Disable slow oscillator mode */ + { 0x11, 0, 0x10 } +}; - if (ifp->if_flags & IFF_BROADCAST) - rxfilt |= LPE_RXFILTER_BROADCAST; +static const lpc_eth_phy_action lpc_eth_phy_up_post_action_KSZ80X1RNL [] = { + /* Enable energy detect power down (EDPD) mode */ + { 0x18, 0x0800, 0 }, + /* Turn PLL of automatically in EDPD mode */ + { 0x10, 0x10, 0 } +}; - if (ifp->if_flags & IFF_PROMISC) - rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST; +static int lpc_eth_phy_up(lpc_eth_driver_entry *e) +{ + int eno; + int retries = 64; + uint32_t val; + + e->phy = DEFAULT_PHY - 1; + while (true) { + e->phy = (e->phy + 1) % 32; + + --retries; + eno = lpc_eth_phy_get_id(e->phy, &e->phy_id); + if ( + (eno == 0 && e->phy_id != 0xfffffff0 && e->phy_id != 0) + || retries <= 0 + ) { + break; + } + + rtems_task_wake_after(1); + } + + LPC_ETH_PRINTF("lpc_eth_phy_get_id: 0x%08" PRIx32 " from phy %d retries %d\n", + e->phy_id, e->phy, retries); + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_pre_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_pre_action_KSZ80X1RNL) + ); + break; + case PHY_DP83848: + eno = lpc_eth_mdio_read(e->phy, NULL, 0x17, &val); + LPC_ETH_PRINTF("phy PHY_DP83848 RBR 0x%08" PRIx32 "\n", val); + /* val = 0x21; */ + val = 0x32 ; + eno = lpc_eth_mdio_write(e->phy, NULL, 0x17, val); + break; + case 0: + case 0xfffffff0: + eno = EIO; + e->phy = DEFAULT_PHY; + break; + default: + break; + } + + if (eno == 0) { + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_action_default [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_action_default) + ); + } + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_up_post_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_up_post_action_KSZ80X1RNL) + ); + break; + default: + break; + } + } + } else { + e->phy_id = 0; + } + + return eno; +} - if (ifp->if_flags & IFF_ALLMULTI) - rxfilt |= LPE_RXFILTER_MULTICAST; +static const lpc_eth_phy_action lpc_eth_phy_down_action_default [] = { + { MII_BMCR, BMCR_PDOWN, 0 } +}; - lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt); -} +static const lpc_eth_phy_action lpc_eth_phy_down_post_action_KSZ80X1RNL [] = { + /* Enable slow oscillator mode */ + { 0x11, 0x10, 0 } +}; -static void lpe_set_rxfilter(struct lpe_softc *sc) +static void lpc_eth_phy_down(lpc_eth_driver_entry *e) { - struct ifnet *ifp = sc->lpe_ifp; - struct ifmultiaddr *ifma; - int index; - uint32_t hashl, hashh; - - hashl = 0; - hashh = 0; - - if_maddr_rlock(ifp); - CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - - index = ether_crc32_be(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f; - - if (index > 31) - hashh |= (1 << (index - 32)); - else - hashl |= (1 << index); - } - if_maddr_runlock(ifp); - - /* Program new hash filter */ - lpe_write_4(sc, LPE_HASHFILTER_L, hashl); - lpe_write_4(sc, LPE_HASHFILTER_H, hashh); + int eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_down_action_default [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_down_action_default) + ); + + if (eno == 0) { + switch (e->phy_id) { + case PHY_KSZ80X1RNL: + eno = lpc_eth_phy_set_and_clear( + e, + &lpc_eth_phy_down_post_action_KSZ80X1RNL [0], + RTEMS_ARRAY_SIZE(lpc_eth_phy_down_post_action_KSZ80X1RNL) + ); + break; + default: + break; + } + } } -static void -lpe_intr(void *arg) +static void lpc_eth_soft_reset(void) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - uint32_t intstatus; - - debugf("status=0x%08x\n", lpe_read_4(sc, LPE_INTSTATUS)); - - lpe_lock(sc); - - while ((intstatus = lpe_read_4(sc, LPE_INTSTATUS))) { - if (intstatus & LPE_INT_RXDONE) - lpe_rxintr(sc); - -#ifndef __rtems__ - if (intstatus & LPE_INT_TXDONE) - lpe_txintr(sc); - -#else /* __rtems__ */ - if (intstatus & LPE_INT_TXUNDERRUN) { - if_inc_counter(sc->lpe_ifp, IFCOUNTER_OERRORS, 1); - lpe_stop_locked(sc); - lpe_init_locked(sc); - } - else if (intstatus & (LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE)) - lpe_txintr(sc); -#endif /* __rtems__ */ - lpe_write_4(sc, LPE_INTCLEAR, 0xffff); - } - - lpe_unlock(sc); + lpc_eth->command = 0x38; + lpc_eth->mac1 = 0xcf00; + lpc_eth->mac1 = 0x0; } -static void -lpe_rxintr(struct lpe_softc *sc) +static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up) { - struct ifnet *ifp = sc->lpe_ifp; - struct lpe_hwdesc *hwd; - struct lpe_hwstatus *hws; - struct lpe_rxdesc *rxd; - struct mbuf *m; - int prod, cons; - - for (;;) { - prod = lpe_read_4(sc, LPE_RXDESC_PROD); - cons = lpe_read_4(sc, LPE_RXDESC_CONS); - - if (prod == cons) - break; - - rxd = &sc->lpe_cdata.lpe_rx_desc[cons]; - hwd = &sc->lpe_rdata.lpe_rx_ring[cons]; - hws = &sc->lpe_rdata.lpe_rx_status[cons]; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(rxd, sizeof(*rxd)); - rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd)); - rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws)); -#endif -#endif /* __rtems__ */ - - /* Check received frame for errors */ - if (hws->lhs_info & LPE_HWDESC_RXERRS) { - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - lpe_discard_rxbuf(sc, cons); - lpe_init_rxbuf(sc, cons); - goto skip; - } - - m = rxd->lpe_rxdesc_mbuf; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len); -#endif -#endif /* __rtems__ */ - m->m_pkthdr.rcvif = ifp; - m->m_data += 2; - - if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); - - lpe_unlock(sc); - (*ifp->if_input)(ifp, m); - lpe_lock(sc); - - lpe_init_rxbuf(sc, cons); -skip: - LPE_INC(cons, LPE_RXDESC_NUM); - lpe_write_4(sc, LPE_RXDESC_CONS, cons); - } + int eno = 0; + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct ifnet *ifp = e->ifp; + + if (up && e->state == LPC_ETH_STATE_DOWN) { + lpc_eth_config_module_enable(); + + /* Enable RX/TX reset and disable soft reset */ + lpc_eth->mac1 = 0xf00; + + /* Initialize PHY */ + /* Clock value 10 (divide by 44 ) is safe on LPC178x up to 100 MHz AHB clock */ + lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10) | ETH_MCFG_RESETMIIMGMT; + rtems_task_wake_after(1); + lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10); + rtems_task_wake_after(1); + eno = lpc_eth_phy_up(e); + + if (eno == 0) { + const uint8_t *eaddr; + + /* + * We must have a valid external clock from the PHY at this point, + * otherwise the system bus hangs and only a watchdog reset helps. + */ + lpc_eth_soft_reset(); + + /* Reinitialize registers */ + lpc_eth->mac2 = 0x31; + lpc_eth->ipgt = 0x15; + lpc_eth->ipgr = 0x12; + lpc_eth->clrt = 0x370f; + lpc_eth->maxf = 0x0600; + lpc_eth->supp = ETH_SUPP_SPEED; + lpc_eth->test = 0; + #ifdef LPC_ETH_CONFIG_RMII + lpc_eth->command = 0x0600; + #else + lpc_eth->command = 0x0400; + #endif + lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN; + lpc_eth->intclear = 0x30ff; + lpc_eth->powerdown = 0; + + /* MAC address */ + eaddr = IF_LLADDR(e->ifp); + lpc_eth->sa0 = ((uint32_t) eaddr [5] << 8) | (uint32_t) eaddr [4]; + lpc_eth->sa1 = ((uint32_t) eaddr [3] << 8) | (uint32_t) eaddr [2]; + lpc_eth->sa2 = ((uint32_t) eaddr [1] << 8) | (uint32_t) eaddr [0]; + + lpc_eth_setup_rxfilter(e); + + /* Enable receiver */ + lpc_eth->mac1 = 0x03; + + /* Initialize tasks */ + lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_INIT_RX); + lpc_eth_initialize_transmit(e); + + /* Install interrupt handler */ + sc = rtems_interrupt_handler_install( + e->interrupt_number, + "Ethernet", + RTEMS_INTERRUPT_UNIQUE, + lpc_eth_interrupt_handler, + e + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + /* Start watchdog timer */ + callout_reset(&e->watchdog_callout, hz, lpc_eth_interface_watchdog, e); + + /* Change state */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + e->state = LPC_ETH_STATE_UP; + } + } else if (!up && e->state == LPC_ETH_STATE_UP) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* Remove interrupt handler */ + sc = rtems_interrupt_handler_remove( + e->interrupt_number, + lpc_eth_interrupt_handler, + e + ); + BSD_ASSERT(sc == RTEMS_SUCCESSFUL); + + /* Stop task */ + lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP); + + lpc_eth_soft_reset(); + lpc_eth_phy_down(e); + lpc_eth_config_module_disable(); + + /* Stop watchdog timer */ + callout_stop(&e->watchdog_callout); + + /* Change state */ + e->state = LPC_ETH_STATE_DOWN; + } + + return eno; } -static void -lpe_txintr(struct lpe_softc *sc) +static void lpc_eth_interface_init(void *arg) { - struct ifnet *ifp = sc->lpe_ifp; - struct lpe_hwdesc *hwd; - struct lpe_hwstatus *hws; - struct lpe_txdesc *txd; - int cons, last; - - for (;;) { - cons = lpe_read_4(sc, LPE_TXDESC_CONS); - last = sc->lpe_cdata.lpe_tx_last; - - if (cons == last) - break; - - txd = &sc->lpe_cdata.lpe_tx_desc[last]; - hwd = &sc->lpe_rdata.lpe_tx_ring[last]; - hws = &sc->lpe_rdata.lpe_tx_status[last]; - -#ifndef __rtems__ - bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE); -#else /* __rtems__ */ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(txd, sizeof(*txd)); - rtems_cache_invalidate_multiple_data_lines(hwd, sizeof(*hwd)); - rtems_cache_invalidate_multiple_data_lines(hws, sizeof(*hws)); -#endif -#endif /* __rtems__ */ - - if_inc_counter(ifp, IFCOUNTER_COLLISIONS, LPE_HWDESC_COLLISIONS(hws->lhs_info)); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; - if (hws->lhs_info & LPE_HWDESC_TXERRS) - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - else - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - - if (txd->lpe_txdesc_first) { -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag, - txd->lpe_txdesc_dmamap); -#endif /* __rtems__ */ - - m_freem(txd->lpe_txdesc_mbuf); - txd->lpe_txdesc_mbuf = NULL; - txd->lpe_txdesc_first = 0; - } + (void) lpc_eth_up_or_down(e, true); +} - sc->lpe_cdata.lpe_tx_used--; - LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM); - } +static void lpc_eth_setup_rxfilter(lpc_eth_driver_entry *e) +{ + struct ifnet *ifp = e->ifp; + + lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0); + + if ((ifp->if_flags & IFF_ALLMULTI)) { + lpc_eth->hashfilterl = 0xffffffff; + lpc_eth->hashfilterh = 0xffffffff; + } else { + struct ifmultiaddr *ifma; + + lpc_eth->hashfilterl = 0x0; + lpc_eth->hashfilterh = 0x0; + + if_maddr_rlock(ifp); + CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + uint32_t crc; + uint32_t index; + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + /* XXX: ether_crc32_le() does not work, why? */ + crc = ether_crc32_be( + LLADDR((struct sockaddr_dl *) ifma->ifma_addr), + ETHER_ADDR_LEN + ); + index = (crc >> 23) & 0x3f; + + if (index < 32) { + lpc_eth->hashfilterl |= 1U << index; + } else { + lpc_eth->hashfilterh |= 1U << (index - 32); + } + } + if_maddr_runlock(ifp); + } +} - if (!sc->lpe_cdata.lpe_tx_used) - sc->lpe_watchdog_timer = 0; +static int lpc_eth_interface_ioctl( + struct ifnet *ifp, + ioctl_command_t cmd, + caddr_t data +) +{ + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int eno = 0; + + LPC_ETH_PRINTF("%s\n", __func__); + + switch (cmd) { + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + eno = ifmedia_ioctl(ifp, ifr, &e->ifmedia, cmd); + break; + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl(ifp, cmd, data); + break; + case SIOCSIFFLAGS: + LPE_LOCK(e); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ e->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) { + lpc_eth_setup_rxfilter(e); + } + } else { + eno = lpc_eth_up_or_down(e, true); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + eno = lpc_eth_up_or_down(e, false); + } + } + e->if_flags = ifp->if_flags; + LPE_UNLOCK(e); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + LPE_LOCK(e); + lpc_eth_setup_rxfilter(e); + LPE_UNLOCK(e); + } + break; + default: + eno = ether_ioctl(ifp, cmd, data); + break; + } + + return eno; } -static void -lpe_tick(void *arg) +static int lpc_eth_interface_transmit(struct ifnet *ifp, struct mbuf *m) { - struct lpe_softc *sc = (struct lpe_softc *)arg; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc; + int eno; - lpe_lock_assert(sc); - - mii_tick(mii); - lpe_watchdog(sc); + LPE_LOCK(e); - callout_reset(&sc->lpe_tick, hz, lpe_tick, sc); -} + if (e->state == LPC_ETH_STATE_UP) { + eno = lpc_eth_tx_enqueue(e, ifp, m); + lpc_eth_tx_reclaim(e, ifp); -static void -lpe_watchdog(struct lpe_softc *sc) -{ - struct ifnet *ifp = sc->lpe_ifp; + if (__predict_false(eno != 0)) { + struct mbuf *n; - lpe_lock_assert(sc); + n = m_defrag(m, M_NOWAIT); + if (n != NULL) { + m = n; + } - if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--) - return; + eno = lpc_eth_tx_enqueue(e, ifp, m); + } + } else { + eno = ENETDOWN; + } - /* Chip has stopped responding */ - device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n"); - lpe_stop_locked(sc); - lpe_init_locked(sc); + if (eno != 0) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + } - /* Try to resend packets */ - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - lpe_start_locked(ifp); + LPE_UNLOCK(e); + return eno; } -static int -lpe_dma_alloc(struct lpe_softc *sc) +static void lpc_eth_interface_watchdog(void *arg) { - int err; - - /* Create parent DMA tag */ - err = bus_dma_tag_create( - bus_get_dma_tag(sc->lpe_dev), - 1, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */ - BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_parent_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create parent DMA tag\n"); - return (err); - } - - err = lpe_dma_alloc_rx(sc); - if (err) - return (err); - - err = lpe_dma_alloc_tx(sc); - if (err) - return (err); - - return (0); + lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg; + + if (e->state == LPC_ETH_STATE_UP) { + uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy); + + if (e->anlpar != anlpar) { + bool full_duplex = false; + bool speed = false; + + e->anlpar = anlpar; + + if ((anlpar & ANLPAR_TX_FD) != 0) { + full_duplex = true; + speed = true; + } else if ((anlpar & ANLPAR_T4) != 0) { + speed = true; + } else if ((anlpar & ANLPAR_TX) != 0) { + speed = true; + } else if ((anlpar & ANLPAR_10_FD) != 0) { + full_duplex = true; + } + + if (full_duplex) { + lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX; + } else { + lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX; + } + + if (speed) { + lpc_eth->supp |= ETH_SUPP_SPEED; + } else { + lpc_eth->supp &= ~ETH_SUPP_SPEED; + } + } + + callout_reset(&e->watchdog_callout, WATCHDOG_TIMEOUT * hz, lpc_eth_interface_watchdog, e); + } } -static int -lpe_dma_alloc_rx(struct lpe_softc *sc) +static int lpc_eth_media_change(struct ifnet *ifp) { - struct lpe_rxdesc *rxd; - struct lpe_dmamap_arg ctx; - int err, i; - - /* Create tag for Rx ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_RXDESC_SIZE, 1, /* maxsize, nsegments */ - LPE_RXDESC_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_ring_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx ring DMA tag\n"); - goto fail; - } - - /* Create tag for Rx status ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_RXSTATUS_SIZE, 1, /* maxsize, nsegments */ - LPE_RXSTATUS_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_status_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx status ring DMA tag\n"); - goto fail; - } - - /* Create tag for Rx buffers */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - MCLBYTES * LPE_RXDESC_NUM, /* maxsize */ - LPE_RXDESC_NUM, /* segments */ - MCLBYTES, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_rx_buf_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx buffers DMA tag\n"); - goto fail; - } - - /* Allocate Rx DMA ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_ring_tag, - (void **)&sc->lpe_rdata.lpe_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_ring_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_ring_tag, - sc->lpe_cdata.lpe_rx_ring_map, sc->lpe_rdata.lpe_rx_ring, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_rx_ring_phys = ctx.lpe_dma_busaddr; - - /* Allocate Rx status ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_status_tag, - (void **)&sc->lpe_rdata.lpe_rx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_status_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_status_tag, - sc->lpe_cdata.lpe_rx_status_map, sc->lpe_rdata.lpe_rx_status, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_rx_status_phys = ctx.lpe_dma_busaddr; - - - /* Create Rx buffers DMA map */ - for (i = 0; i < LPE_RXDESC_NUM; i++) { - rxd = &sc->lpe_cdata.lpe_rx_desc[i]; - rxd->lpe_rxdesc_mbuf = NULL; -#ifndef __rtems__ - rxd->lpe_rxdesc_dmamap = NULL; - - err = bus_dmamap_create(sc->lpe_cdata.lpe_rx_buf_tag, 0, - &rxd->lpe_rxdesc_dmamap); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Rx DMA map\n"); - return (err); - } -#endif /* __rtems__ */ - } - - return (0); -fail: - return (err); + (void) ifp; + return EINVAL; } -static int -lpe_dma_alloc_tx(struct lpe_softc *sc) +static void lpc_eth_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct lpe_txdesc *txd; - struct lpe_dmamap_arg ctx; - int err, i; - - /* Create tag for Tx ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_TXDESC_SIZE, 1, /* maxsize, nsegments */ - LPE_TXDESC_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_ring_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx ring DMA tag\n"); - goto fail; - } - - /* Create tag for Tx status ring */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - LPE_TXSTATUS_SIZE, 1, /* maxsize, nsegments */ - LPE_TXSTATUS_SIZE, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_status_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx status ring DMA tag\n"); - goto fail; - } - - /* Create tag for Tx buffers */ - err = bus_dma_tag_create( - sc->lpe_cdata.lpe_parent_tag, - LPE_DESC_ALIGN, 0, /* alignment, boundary */ - BUS_SPACE_MAXADDR, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - MCLBYTES * LPE_TXDESC_NUM, /* maxsize */ - LPE_TXDESC_NUM, /* segments */ - MCLBYTES, 0, /* maxsegsize, flags */ - NULL, NULL, /* lockfunc, lockarg */ - &sc->lpe_cdata.lpe_tx_buf_tag); - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx buffers DMA tag\n"); - goto fail; - } - - /* Allocate Tx DMA ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_ring_tag, - (void **)&sc->lpe_rdata.lpe_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_ring_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_ring_tag, - sc->lpe_cdata.lpe_tx_ring_map, sc->lpe_rdata.lpe_tx_ring, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_tx_ring_phys = ctx.lpe_dma_busaddr; - - /* Allocate Tx status ring */ - err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_status_tag, - (void **)&sc->lpe_rdata.lpe_tx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT | - BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_status_map); - - err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_status_tag, - sc->lpe_cdata.lpe_tx_status_map, sc->lpe_rdata.lpe_tx_status, - LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0); - - sc->lpe_rdata.lpe_tx_status_phys = ctx.lpe_dma_busaddr; - - - /* Create Tx buffers DMA map */ - for (i = 0; i < LPE_TXDESC_NUM; i++) { - txd = &sc->lpe_cdata.lpe_tx_desc[i]; - txd->lpe_txdesc_mbuf = NULL; -#ifndef __rtems__ - txd->lpe_txdesc_dmamap = NULL; -#endif /* __rtems__ */ - txd->lpe_txdesc_first = 0; - -#ifndef __rtems__ - err = bus_dmamap_create(sc->lpe_cdata.lpe_tx_buf_tag, 0, - &txd->lpe_txdesc_dmamap); -#endif /* __rtems__ */ - - if (err) { - device_printf(sc->lpe_dev, "cannot create Tx DMA map\n"); - return (err); - } - } - - return (0); -fail: - return (err); + (void) ifp; + + imr->ifm_status = IFM_AVALID | IFM_ACTIVE; + imr->ifm_active = IFM_ETHER; + + if ((lpc_eth->supp & ETH_SUPP_SPEED) != 0) { + imr->ifm_active |= IFM_100_TX; + } else { + imr->ifm_active |= IFM_10_T; + } + + if ((lpc_eth->mac2 & ETH_MAC2_FULL_DUPLEX) != 0) { + imr->ifm_active |= IFM_FDX; + } else { + imr->ifm_active |= IFM_HDX; + } } -static int -lpe_init_rx(struct lpe_softc *sc) +__weak_symbol int lpc_eth_probe(int unit) { - int i, err; + if (unit != 0) { + return ENXIO; + } - for (i = 0; i < LPE_RXDESC_NUM; i++) { - err = lpe_init_rxbuf(sc, i); - if (err) - return (err); - } - - return (0); + return BUS_PROBE_DEFAULT; } -static int -lpe_init_rxbuf(struct lpe_softc *sc, int n) +static int lpc_eth_do_probe(device_t dev) { - struct lpe_rxdesc *rxd; - struct lpe_hwdesc *hwd; -#ifndef __rtems__ - struct lpe_hwstatus *hws; -#endif /* __rtems__ */ - struct mbuf *m; - bus_dma_segment_t segs[1]; -#ifndef __rtems__ - int nsegs; -#endif /* __rtems__ */ - - rxd = &sc->lpe_cdata.lpe_rx_desc[n]; - hwd = &sc->lpe_rdata.lpe_rx_ring[n]; -#ifndef __rtems__ - hws = &sc->lpe_rdata.lpe_rx_status[n]; -#endif /* __rtems__ */ - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - - if (!m) { - device_printf(sc->lpe_dev, "WARNING: mbufs exhausted!\n"); - return (ENOBUFS); - } - - m->m_len = m->m_pkthdr.len = MCLBYTES; - -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap); - - if (bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_rx_buf_tag, - rxd->lpe_rxdesc_dmamap, m, segs, &nsegs, 0)) { - m_freem(m); - return (ENOBUFS); - } - - bus_dmamap_sync(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap, - BUS_DMASYNC_PREREAD); -#else /* __rtems__ */ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len); -#endif - segs[0].ds_addr = mtod(m, bus_addr_t); -#endif /* __rtems__ */ - - rxd->lpe_rxdesc_mbuf = m; - hwd->lhr_data = segs[0].ds_addr + 2; - hwd->lhr_control = (segs[0].ds_len - 1) | LPE_HWDESC_INTERRUPT; -#ifdef __rtems__ -#ifdef CPU_DATA_CACHE_ALIGNMENT - rtems_cache_flush_multiple_data_lines(hwd, sizeof(*hwd)); -#endif -#endif /* __rtems__ */ - - return (0); + device_set_desc(dev, "LPC32x0 Ethernet controller"); + return lpc_eth_probe(device_get_unit(dev)); } -static void -lpe_discard_rxbuf(struct lpe_softc *sc, int n) +static int lpc_eth_attach(device_t dev) { - struct lpe_rxdesc *rxd; - struct lpe_hwdesc *hwd; - - rxd = &sc->lpe_cdata.lpe_rx_desc[n]; - hwd = &sc->lpe_rdata.lpe_rx_ring[n]; - -#ifndef __rtems__ - bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap); -#endif /* __rtems__ */ - - hwd->lhr_data = 0; - hwd->lhr_control = 0; - - if (rxd->lpe_rxdesc_mbuf) { - m_freem(rxd->lpe_rxdesc_mbuf); - rxd->lpe_rxdesc_mbuf = NULL; - } + lpc_eth_driver_entry *e = device_get_softc(dev); + struct ifnet *ifp = NULL; + char *unit_name = NULL; + int unit_index = device_get_unit(dev); + size_t table_area_size = 0; + char *table_area = NULL; + char *table_location = NULL; + rtems_status_code status; + uint8_t eaddr[ETHER_ADDR_LEN]; + + BSD_ASSERT(e->state == LPC_ETH_STATE_NOT_INITIALIZED); + + mtx_init(&e->mtx, device_get_nameunit(e->dev), MTX_NETWORK_LOCK, MTX_DEF); + + ifmedia_init(&e->ifmedia, 0, lpc_eth_media_change, lpc_eth_media_status); + ifmedia_add(&e->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&e->ifmedia, IFM_ETHER | IFM_AUTO); + + callout_init_mtx(&e->watchdog_callout, &e->mtx, 0); + + /* Receive unit count */ + e->rx_unit_count = LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT; + + /* Transmit unit count */ + e->tx_unit_count = LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT; + + /* Remember interrupt number */ + e->interrupt_number = LPC_ETH_CONFIG_INTERRUPT; + + /* Allocate and clear table area */ + table_area_size = + e->rx_unit_count + * (sizeof(lpc_eth_transfer_descriptor) + + sizeof(lpc_eth_receive_status) + + sizeof(struct mbuf *)) + + e->tx_unit_count + * (sizeof(lpc_eth_transfer_descriptor) + + sizeof(uint32_t) + + LPC_ETH_CONFIG_TX_BUF_SIZE); + table_area = lpc_eth_config_alloc_table_area(table_area_size); + if (table_area == NULL) { + return ENOMEM; + } + memset(table_area, 0, table_area_size); + + table_location = table_area; + + /* + * The receive status table must be the first one since it has the strictest + * alignment requirements. + */ + e->rx_status_table = (volatile lpc_eth_receive_status *) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_status_table [0]); + + e->rx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_desc_table [0]); + + e->rx_mbuf_table = (struct mbuf **) table_location; + table_location += e->rx_unit_count * sizeof(e->rx_mbuf_table [0]); + + e->tx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location; + table_location += e->tx_unit_count * sizeof(e->tx_desc_table [0]); + + e->tx_status_table = (volatile uint32_t *) table_location; + table_location += e->tx_unit_count * sizeof(e->tx_status_table [0]); + + e->tx_buf_table = table_location; + + /* Set interface data */ + e->dev = dev; + e->ifp = ifp = if_alloc(IFT_ETHER); + ifp->if_softc = e; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_init = lpc_eth_interface_init; + ifp->if_ioctl = lpc_eth_interface_ioctl; + ifp->if_transmit = lpc_eth_interface_transmit; + ifp->if_qflush = if_qflush; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; + IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1); + ifp->if_snd.ifq_drv_maxlen = LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1; + IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = sizeof(struct ether_header); + + rtems_bsd_get_mac_address(device_get_name(e->dev), unit_index, eaddr); + + /* Create tasks */ + status = rtems_task_create( + rtems_build_name('n', 't', 'r', 'x'), + rtems_bsd_get_task_priority(device_get_name(e->dev)), + 4096, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &e->receive_task + ); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + status = rtems_task_start( + e->receive_task, + lpc_eth_receive_task, + (rtems_task_argument)e + ); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + + if_link_state_change(e->ifp, LINK_STATE_UP); + + /* Change status */ + e->state = LPC_ETH_STATE_DOWN; + + /* Attach the interface */ + ether_ifattach(ifp, eaddr); + + return 0; } -static void -lpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +static int lpc_eth_detach(device_t dev) { - struct lpe_dmamap_arg *ctx; + /* FIXME: Detach the interface from the upper layers? */ - if (error) - return; + /* Module soft reset */ + lpc_eth->command = 0x38; + lpc_eth->mac1 = 0xcf00; - ctx = (struct lpe_dmamap_arg *)arg; - ctx->lpe_dma_busaddr = segs[0].ds_addr; -} + /* FIXME: More cleanup */ -static int -lpe_ifmedia_upd(struct ifnet *ifp) -{ - return (0); -} - -static void -lpe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) -{ - struct lpe_softc *sc = ifp->if_softc; - struct mii_data *mii = device_get_softc(sc->lpe_miibus); - - lpe_lock(sc); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - lpe_unlock(sc); + return ENXIO; } static device_method_t lpe_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, lpe_probe), - DEVMETHOD(device_attach, lpe_attach), - DEVMETHOD(device_detach, lpe_detach), - - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - - /* MII interface */ - DEVMETHOD(miibus_readreg, lpe_miibus_readreg), - DEVMETHOD(miibus_writereg, lpe_miibus_writereg), - DEVMETHOD(miibus_statchg, lpe_miibus_statchg), - { 0, 0 } + DEVMETHOD(device_probe, lpc_eth_do_probe), + DEVMETHOD(device_attach, lpc_eth_attach), + DEVMETHOD(device_detach, lpc_eth_detach), + DEVMETHOD_END }; -static driver_t lpe_driver = { - "lpe", - lpe_methods, - sizeof(struct lpe_softc), +static driver_t lpe_nexus_driver = { + "lpe", + lpe_methods, + sizeof(lpc_eth_driver_entry) }; static devclass_t lpe_devclass; - -#ifndef __rtems__ -DRIVER_MODULE(lpe, simplebus, lpe_driver, lpe_devclass, 0, 0); -#else /* __rtems__ */ -DRIVER_MODULE(lpe, nexus, lpe_driver, lpe_devclass, 0, 0); -#endif /* __rtems__ */ -DRIVER_MODULE(miibus, lpe, miibus_driver, miibus_devclass, 0, 0); -MODULE_DEPEND(lpe, obio, 1, 1, 1); -MODULE_DEPEND(lpe, miibus, 1, 1, 1); +DRIVER_MODULE(lpe, nexus, lpe_nexus_driver, lpe_devclass, 0, 0); +MODULE_DEPEND(lpe, nexus, 1, 1, 1); MODULE_DEPEND(lpe, ether, 1, 1, 1); + +#endif /* LIBBSP_ARM_LPC24XX_BSP_H || LIBBSP_ARM_LPC32XX_BSP_H */ diff --git a/rtemsbsd/sys/arm/lpc/if_lpereg.h b/rtemsbsd/sys/arm/lpc/if_lpereg.h deleted file mode 100644 index a40bf8b5..00000000 --- a/rtemsbsd/sys/arm/lpc/if_lpereg.h +++ /dev/null @@ -1,210 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _ARM_LPC_IF_LPEREG_H -#define _ARM_LPC_IF_LPEREG_H - -#define LPE_MAC1 0x000 -#define LPE_MAC1_RXENABLE (1 << 0) -#define LPE_MAC1_PASSALL (1 << 1) -#define LPE_MAC1_RXFLOWCTRL (1 << 2) -#define LPE_MAC1_TXFLOWCTRL (1 << 3) -#define LPE_MAC1_LOOPBACK (1 << 4) -#define LPE_MAC1_RESETTX (1 << 8) -#define LPE_MAC1_RESETMCSTX (1 << 9) -#define LPE_MAC1_RESETRX (1 << 10) -#define LPE_MAC1_RESETMCSRX (1 << 11) -#define LPE_MAC1_SIMRESET (1 << 14) -#define LPE_MAC1_SOFTRESET (1 << 15) -#define LPE_MAC2 0x004 -#define LPE_MAC2_FULLDUPLEX (1 << 0) -#define LPE_MAC2_FRAMELENCHECK (1 << 1) -#define LPE_MAC2_HUGEFRAME (1 << 2) -#define LPE_MAC2_DELAYEDCRC (1 << 3) -#define LPE_MAC2_CRCENABLE (1 << 4) -#define LPE_MAC2_PADCRCENABLE (1 << 5) -#define LPE_MAC2_VLANPADENABLE (1 << 6) -#define LPE_MAC2_AUTOPADENABLE (1 << 7) -#define LPE_MAC2_PUREPREAMBLE (1 << 8) -#define LPE_MAC2_LONGPREAMBLE (1 << 9) -#define LPE_MAC2_NOBACKOFF (1 << 12) -#define LPE_MAC2_BACKPRESSURE (1 << 13) -#define LPE_MAC2_EXCESSDEFER (1 << 14) -#define LPE_IPGT 0x008 -#define LPE_IPGR 0x00c -#define LPE_CLRT 0x010 -#define LPE_MAXF 0x014 -#define LPE_SUPP 0x018 -#define LPE_SUPP_SPEED (1 << 8) -#define LPE_TEST 0x01c -#define LPE_MCFG 0x020 -#define LPE_MCFG_SCANINCR (1 << 0) -#define LPE_MCFG_SUPPREAMBLE (1 << 1) -#define LPE_MCFG_CLKSEL(_n) ((_n & 0x7) << 2) -#define LPC_MCFG_RESETMII (1 << 15) -#define LPE_MCMD 0x024 -#define LPE_MCMD_READ (1 << 0) -#define LPE_MCMD_WRITE (0 << 0) -#define LPE_MCMD_SCAN (1 << 1) -#define LPE_MADR 0x028 -#define LPE_MADR_REGMASK 0x1f -#define LPE_MADR_REGSHIFT 0 -#define LPE_MADR_PHYMASK 0x1f -#define LPE_MADR_PHYSHIFT 8 -#define LPE_MWTD 0x02c -#define LPE_MWTD_DATAMASK 0xffff -#define LPE_MRDD 0x030 -#define LPE_MRDD_DATAMASK 0xffff -#define LPE_MIND 0x034 -#define LPE_MIND_BUSY (1 << 0) -#define LPE_MIND_SCANNING (1 << 1) -#define LPE_MIND_INVALID (1 << 2) -#define LPE_MIND_MIIFAIL (1 << 3) -#define LPE_SA0 0x040 -#define LPE_SA1 0x044 -#define LPE_SA2 0x048 -#define LPE_COMMAND 0x100 -#define LPE_COMMAND_RXENABLE (1 << 0) -#define LPE_COMMAND_TXENABLE (1 << 1) -#define LPE_COMMAND_REGRESET (1 << 3) -#define LPE_COMMAND_TXRESET (1 << 4) -#define LPE_COMMAND_RXRESET (1 << 5) -#define LPE_COMMAND_PASSRUNTFRAME (1 << 6) -#define LPE_COMMAND_PASSRXFILTER (1 << 7) -#define LPE_COMMAND_TXFLOWCTL (1 << 8) -#define LPE_COMMAND_RMII (1 << 9) -#define LPE_COMMAND_FULLDUPLEX (1 << 10) -#define LPE_STATUS 0x104 -#define LPE_STATUS_RXACTIVE (1 << 0) -#define LPE_STATUS_TXACTIVE (1 << 1) -#define LPE_RXDESC 0x108 -#define LPE_RXSTATUS 0x10c -#define LPE_RXDESC_NUMBER 0x110 -#define LPE_RXDESC_PROD 0x114 -#define LPE_RXDESC_CONS 0x118 -#define LPE_TXDESC 0x11c -#define LPE_TXSTATUS 0x120 -#define LPE_TXDESC_NUMBER 0x124 -#define LPE_TXDESC_PROD 0x128 -#define LPE_TXDESC_CONS 0x12c -#define LPE_TSV0 0x158 -#define LPE_TSV1 0x15c -#define LPE_RSV 0x160 -#define LPE_FLOWCONTROL_COUNTER 0x170 -#define LPE_FLOWCONTROL_STATUS 0x174 -#define LPE_RXFILTER_CTRL 0x200 -#define LPE_RXFILTER_UNICAST (1 << 0) -#define LPE_RXFILTER_BROADCAST (1 << 1) -#define LPE_RXFILTER_MULTICAST (1 << 2) -#define LPE_RXFILTER_UNIHASH (1 << 3) -#define LPE_RXFILTER_MULTIHASH (1 << 4) -#define LPE_RXFILTER_PERFECT (1 << 5) -#define LPE_RXFILTER_WOL (1 << 12) -#define LPE_RXFILTER_FILTWOL (1 << 13) -#define LPE_RXFILTER_WOL_STATUS 0x204 -#define LPE_RXFILTER_WOL_CLEAR 0x208 -#define LPE_HASHFILTER_L 0x210 -#define LPE_HASHFILTER_H 0x214 -#define LPE_INTSTATUS 0xfe0 -#define LPE_INTENABLE 0xfe4 -#define LPE_INTCLEAR 0xfe8 -#define LPE_INTSET 0xfec -#define LPE_INT_RXOVERRUN (1 << 0) -#define LPE_INT_RXERROR (1 << 1) -#define LPE_INT_RXFINISH (1 << 2) -#define LPE_INT_RXDONE (1 << 3) -#define LPE_INT_TXUNDERRUN (1 << 4) -#define LPE_INT_TXERROR (1 << 5) -#define LPE_INT_TXFINISH (1 << 6) -#define LPE_INT_TXDONE (1 << 7) -#define LPE_INT_SOFTINT (1 << 12) -#define LPE_INTWAKEUPINT (1 << 13) -#define LPE_POWERDOWN 0xff4 - -#define LPE_DESC_ALIGN 8 -#define LPE_TXDESC_NUM 128 -#define LPE_RXDESC_NUM 128 -#define LPE_TXDESC_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwdesc)) -#define LPE_RXDESC_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwdesc)) -#define LPE_TXSTATUS_SIZE (LPE_TXDESC_NUM * sizeof(struct lpe_hwstatus)) -#define LPE_RXSTATUS_SIZE (LPE_RXDESC_NUM * sizeof(struct lpe_hwstatus)) -#define LPE_MAXFRAGS 8 - -struct lpe_hwdesc { - uint32_t lhr_data; - uint32_t lhr_control; -}; - -struct lpe_hwstatus { - uint32_t lhs_info; - uint32_t lhs_crc; -}; - -#define LPE_INC(x, y) (x) = ((x) == ((y)-1)) ? 0 : (x)+1 - -/* These are valid for both Rx and Tx descriptors */ -#define LPE_HWDESC_SIZE_MASK (1 << 10) -#define LPE_HWDESC_INTERRUPT (1U << 31) - -/* These are valid for Tx descriptors */ -#define LPE_HWDESC_LAST (1 << 30) -#define LPE_HWDESC_CRC (1 << 29) -#define LPE_HWDESC_PAD (1 << 28) -#define LPE_HWDESC_HUGE (1 << 27) -#define LPE_HWDESC_OVERRIDE (1 << 26) - -/* These are valid for Tx status descriptors */ -#define LPE_HWDESC_COLLISIONS(_n) (((_n) >> 21) & 0x7) -#define LPE_HWDESC_DEFER (1 << 25) -#define LPE_HWDESC_EXCDEFER (1 << 26) -#define LPE_HWDESC_EXCCOLL (1 << 27) -#define LPE_HWDESC_LATECOLL (1 << 28) -#define LPE_HWDESC_UNDERRUN (1 << 29) -#define LPE_HWDESC_TXNODESCR (1 << 30) -#define LPE_HWDESC_ERROR (1U << 31) - -/* These are valid for Rx status descriptors */ -#define LPE_HWDESC_CONTROL (1 << 18) -#define LPE_HWDESC_VLAN (1 << 19) -#define LPE_HWDESC_FAILFILTER (1 << 20) -#define LPE_HWDESC_MULTICAST (1 << 21) -#define LPE_HWDESC_BROADCAST (1 << 22) -#define LPE_HWDESC_CRCERROR (1 << 23) -#define LPE_HWDESC_SYMBOLERROR (1 << 24) -#define LPE_HWDESC_LENGTHERROR (1 << 25) -#define LPE_HWDESC_RANGEERROR (1 << 26) -#define LPE_HWDESC_ALIGNERROR (1 << 27) -#define LPE_HWDESC_OVERRUN (1 << 28) -#define LPE_HWDESC_RXNODESCR (1 << 29) -#define LPE_HWDESC_LASTFLAG (1 << 30) -#define LPE_HWDESC_ERROR (1U << 31) - - -#endif /* _ARM_LPC_IF_LPEREG_H */ diff --git a/rtemsbsd/sys/dev/atsam/if_atsam.c b/rtemsbsd/sys/dev/atsam/if_atsam.c index ff8219f4..21a28fcd 100644 --- a/rtemsbsd/sys/dev/atsam/if_atsam.c +++ b/rtemsbsd/sys/dev/atsam/if_atsam.c @@ -42,6 +42,7 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/mbuf.h> +#include <sys/sbuf.h> #include <sys/socket.h> #include <sys/sockio.h> #include <sys/kernel.h> @@ -49,7 +50,9 @@ #include <sys/bus.h> #include <sys/sysctl.h> +#include <net/bpf.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_var.h> #include <net/if_types.h> #include <net/if_media.h> @@ -66,6 +69,7 @@ #include <rtems/bsd/local/miibus_if.h> #include <rtems/bsd/if_atsam.h> +#include <rtems/bsd/bsd.h> /* * Number of interfaces supported by the driver @@ -90,46 +94,40 @@ /** The runtime pin configure list for GMAC */ #define BOARD_GMAC_RUN_PINS BOARD_GMAC_PINS -/** Multicast Enable */ -#define GMAC_MC_ENABLE (1u << 6) -#define HASH_INDEX_AMOUNT 6 -#define HASH_ELEMENTS_PER_INDEX 8 -#define MAC_ADDR_MASK 0x0000FFFFFFFFFFFF -#define MAC_IDX_MASK (1u << 0) - -/** Promiscuous Mode Enable */ -#define GMAC_PROM_ENABLE (1u << 4) - /** RX Defines */ #define GMAC_RX_BUFFER_SIZE 1536 #define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC -#define GMAC_RX_SET_OFFSET (1u << 15) -#define GMAC_RX_SET_USED_WRAP ((1u << 1) | (1u << 0)) -#define GMAC_RX_SET_WRAP (1u << 1) -#define GMAC_RX_SET_USED (1u << 0) -/** TX Defines */ -#define GMAC_TX_SET_EOF (1u << 15) -#define GMAC_TX_SET_WRAP (1u << 30) -#define GMAC_TX_SET_USED (1u << 31) #define GMAC_DESCRIPTOR_ALIGNMENT 8 /** Events */ #define ATSAMV7_ETH_RX_EVENT_INTERRUPT RTEMS_EVENT_1 -#define ATSAMV7_ETH_TX_EVENT_INTERRUPT RTEMS_EVENT_2 -#define ATSAMV7_ETH_START_TRANSMIT_EVENT RTEMS_EVENT_3 - -#define ATSAMV7_ETH_RX_DATA_OFFSET 2 - -#define WATCHDOG_TIMEOUT 5 /* FIXME: Make these configurable */ #define MDIO_RETRIES 10 #define MDIO_PHY MII_PHY_ANY -#define RXBUF_COUNT 8 -#define TXBUF_COUNT 64 #define IGNORE_RX_ERR false +#define RX_INTERRUPTS (GMAC_ISR_RCOMP | GMAC_ISR_RXUBR | GMAC_ISR_ROVR) + +#define RX_DESC_LOG2 3 +#define RX_DESC_COUNT (1U << RX_DESC_LOG2) +#define RX_DESC_WRAP(idx) \ + ((((idx) + 1) & RX_DESC_COUNT) >> (RX_DESC_LOG2 - 1)) +RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 1) == + GMAC_RX_WRAP_BIT, rx_desc_wrap); +RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 2) == + 0, rx_desc_no_wrap); + +#define TX_DESC_LOG2 6 +#define TX_DESC_COUNT (1U << TX_DESC_LOG2) +#define TX_DESC_WRAP(idx) \ + ((((idx) + 1) & TX_DESC_COUNT) << (30 - TX_DESC_LOG2)) +RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 1) == + GMAC_TX_WRAP_BIT, tx_desc_wrap); +RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 2) == + 0, tx_desc_no_wrap); + /** The PINs for GMAC */ static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS }; @@ -139,11 +137,13 @@ typedef struct if_atsam_gmac { uint32_t retries; } if_atsam_gmac; -typedef struct ring_buffer { - unsigned tx_bd_used; - unsigned tx_bd_free; - size_t length; -} ring_buffer; +struct if_atsam_tx_bds { + volatile sGmacTxDescriptor bds[TX_DESC_COUNT]; +}; + +struct if_atsam_rx_bds { + volatile sGmacRxDescriptor bds[RX_DESC_COUNT]; +}; /* * Per-device data @@ -156,17 +156,16 @@ typedef struct if_atsam_softc { struct ifnet *ifp; struct mtx mtx; if_atsam_gmac Gmac_inst; + size_t tx_idx_head; + size_t tx_idx_tail; + struct if_atsam_tx_bds *tx; + struct mbuf *tx_mbufs[TX_DESC_COUNT]; + size_t rx_idx_head; + struct if_atsam_rx_bds *rx; + struct mbuf *rx_mbufs[RX_DESC_COUNT]; uint8_t GMacAddress[6]; rtems_id rx_daemon_tid; - rtems_id tx_daemon_tid; rtems_vector_number interrupt_number; - struct mbuf **rx_mbuf; - struct mbuf **tx_mbuf; - volatile sGmacTxDescriptor *tx_bd_base; - size_t rx_bd_fill_idx; - size_t amount_rx_buf; - size_t amount_tx_buf; - ring_buffer tx_ring; struct callout tick_ch; /* @@ -190,13 +189,12 @@ typedef struct if_atsam_softc { struct if_atsam_stats { /* Software */ uint32_t rx_overrun_errors; + uint32_t rx_used_bit_reads; uint32_t rx_interrupts; - uint32_t tx_complete_int; uint32_t tx_tur_errors; uint32_t tx_rlex_errors; uint32_t tx_tfc_errors; uint32_t tx_hresp_errors; - uint32_t tx_interrupts; /* Hardware */ uint64_t octets_transm; @@ -243,6 +241,8 @@ typedef struct if_atsam_softc { uint32_t tcp_checksum_errors; uint32_t udp_checksum_errors; } stats; + + int if_flags; } if_atsam_softc; static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc); @@ -251,27 +251,6 @@ static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc); #define IF_ATSAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) -static void if_atsam_event_send(rtems_id task, rtems_event_set event) -{ - rtems_event_send(task, event); -} - - -static void if_atsam_event_receive(if_atsam_softc *sc, rtems_event_set in) -{ - rtems_event_set out; - - IF_ATSAM_UNLOCK(sc); - rtems_event_receive( - in, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, - &out - ); - IF_ATSAM_LOCK(sc); -} - - static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp) { struct mbuf *m; @@ -356,11 +335,10 @@ if_atsam_miibus_readreg(device_t dev, int phy, int reg) static int if_atsam_miibus_writereg(device_t dev, int phy, int reg, int data) { - uint8_t err; if_atsam_softc *sc = device_get_softc(dev); IF_ATSAM_LOCK(sc); - err = if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, + (void)if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, (uint8_t)phy, (uint8_t)reg, data, sc->Gmac_inst.retries); IF_ATSAM_UNLOCK(sc); @@ -403,63 +381,53 @@ if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck, static void if_atsam_interrupt_handler(void *arg) { if_atsam_softc *sc = (if_atsam_softc *)arg; - uint32_t irq_status_val; - rtems_event_set rx_event = 0; - rtems_event_set tx_event = 0; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + uint32_t is; /* Get interrupt status */ - irq_status_val = GMAC_GetItStatus(pHw, 0); + is = pHw->GMAC_ISR; - /* Check receive interrupts */ - if ((irq_status_val & GMAC_IER_ROVR) != 0) { - ++sc->stats.rx_overrun_errors; - rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_RCOMP) != 0) { - rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT; + if (__predict_false((is & GMAC_TX_ERR_BIT) != 0)) { + if ((is & GMAC_IER_TUR) != 0) { + ++sc->stats.tx_tur_errors; + } + if ((is & GMAC_IER_RLEX) != 0) { + ++sc->stats.tx_rlex_errors; + } + if ((is & GMAC_IER_TFC) != 0) { + ++sc->stats.tx_tfc_errors; + } + if ((is & GMAC_IER_HRESP) != 0) { + ++sc->stats.tx_hresp_errors; + } } - /* Send events to receive task and switch off rx interrupts */ - if (rx_event != 0) { + + /* Check receive interrupts */ + if (__predict_true((is & RX_INTERRUPTS) != 0)) { + if (__predict_false((is & GMAC_ISR_RXUBR) != 0)) { + ++sc->stats.rx_used_bit_reads; + } + + if (__predict_false((is & GMAC_ISR_ROVR) != 0)) { + ++sc->stats.rx_overrun_errors; + } + ++sc->stats.rx_interrupts; - /* Erase the interrupts for RX completion and errors */ - GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); - (void)if_atsam_event_send(sc->rx_daemon_tid, rx_event); - } - if ((irq_status_val & GMAC_IER_TUR) != 0) { - ++sc->stats.tx_tur_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_RLEX) != 0) { - ++sc->stats.tx_rlex_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_TFC) != 0) { - ++sc->stats.tx_tfc_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_HRESP) != 0) { - ++sc->stats.tx_hresp_errors; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - if ((irq_status_val & GMAC_IER_TCOMP) != 0) { - ++sc->stats.tx_complete_int; - tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT; - } - /* Send events to transmit task and switch off tx interrupts */ - if (tx_event != 0) { - ++sc->stats.tx_interrupts; - /* Erase the interrupts for TX completion and errors */ - GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0); - (void)if_atsam_event_send(sc->tx_daemon_tid, tx_event); + + /* Disable RX interrupts */ + pHw->GMAC_IDR = RX_INTERRUPTS; + + (void)rtems_event_send(sc->rx_daemon_tid, + ATSAMV7_ETH_RX_EVENT_INTERRUPT); } } -static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) +static void +if_atsam_rx_update_mbuf(struct mbuf *m, uint32_t status) { int frame_len; - frame_len = (int) (buffer_desc->status.bm.len); + frame_len = (int)(status & GMAC_LENGTH_FRAME); m->m_data = mtod(m, char*)+ETHER_ALIGN; m->m_len = frame_len; @@ -467,7 +435,7 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) /* check checksum offload result */ m->m_pkthdr.csum_flags = 0; - switch (buffer_desc->status.bm.typeIDMatchOrCksumResult) { + switch ((status >> 22) & 0x3) { case GMAC_RXDESC_ST_CKSUM_RESULT_IP_CHECKED: m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID; m->m_pkthdr.csum_data = 0xffff; @@ -481,21 +449,17 @@ static void rx_update_mbuf(struct mbuf *m, sGmacRxDescriptor *buffer_desc) } } -/* - * Receive daemon - */ -static void if_atsam_rx_daemon(void *arg) +static void +if_atsam_rx_daemon(rtems_task_argument arg) { if_atsam_softc *sc = (if_atsam_softc *)arg; struct ifnet *ifp = sc->ifp; - rtems_event_set events = 0; - void *rx_bd_base; - struct mbuf *m; - struct mbuf *n; - volatile sGmacRxDescriptor *buffer_desc; - uint32_t tmp_rx_bd_address; - size_t i; + volatile sGmacRxDescriptor *base; + struct if_atsam_rx_bds *rx; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + size_t idx; + struct mbuf **mbufs; + struct mbuf *m; IF_ATSAM_LOCK(sc); @@ -506,47 +470,37 @@ static void if_atsam_rx_daemon(void *arg) } /* Allocate memory space for priority queue descriptor list */ - rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor), + base = rtems_cache_coherent_allocate(sizeof(*base), GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(rx_bd_base != NULL); + assert(base != NULL); - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; - buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP; - buffer_desc->status.val = 0; + base->addr.val = GMAC_RX_OWNERSHIP_BIT | GMAC_RX_WRAP_BIT; + base->status.val = 0; - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1); - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2); + GMAC_SetRxQueue(pHw, (uint32_t)base, 1); + GMAC_SetRxQueue(pHw, (uint32_t)base, 2); /* Allocate memory space for buffer descriptor list */ - rx_bd_base = rtems_cache_coherent_allocate( - sc->amount_rx_buf * sizeof(sGmacRxDescriptor), + rx = rtems_cache_coherent_allocate(sizeof(*rx), GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(rx_bd_base != NULL); - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; + assert(rx != NULL); + sc->rx = rx; + mbufs = &sc->rx_mbufs[0]; /* Create descriptor list and mark as empty */ - for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < sc->amount_rx_buf; - ++sc->rx_bd_fill_idx) { + for (idx = 0; idx < RX_DESC_COUNT; ++idx) { m = if_atsam_new_mbuf(ifp); assert(m != NULL); - sc->rx_mbuf[sc->rx_bd_fill_idx] = m; - buffer_desc->addr.val = ((uint32_t)m->m_data) & - GMAC_RX_BUF_DESC_ADDR_MASK; - buffer_desc->status.val = 0; - if (sc->rx_bd_fill_idx == (sc->amount_rx_buf - 1)) { - buffer_desc->addr.bm.bWrap = 1; - } else { - buffer_desc++; - } + mbufs[idx] = m; + rx->bds[idx].addr.val = mtod(m, uint32_t) | RX_DESC_WRAP(idx); } - buffer_desc = (sGmacRxDescriptor *)rx_bd_base; /* Set 2 Byte Receive Buffer Offset */ - pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET; + pHw->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO(2); /* Write Buffer Queue Base Address Register */ GMAC_ReceiveEnable(pHw, 0); - GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0); + GMAC_SetRxQueue(pHw, (uint32_t)&rx->bds[0], 0); /* Set address for address matching */ GMAC_SetAddress(pHw, 0, sc->GMacAddress); @@ -554,306 +508,230 @@ static void if_atsam_rx_daemon(void *arg) /* Enable Receiving of data */ GMAC_ReceiveEnable(pHw, 1); - /* Setup the interrupts for RX completion and errors */ - GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); + IF_ATSAM_UNLOCK(sc); - sc->rx_bd_fill_idx = 0; + idx = 0; while (true) { - /* Wait for events */ - if_atsam_event_receive(sc, ATSAMV7_ETH_RX_EVENT_INTERRUPT); + rtems_event_set out; - /* - * Check for all packets with a set ownership bit - */ - while (buffer_desc->addr.bm.bOwnership == 1) { - if (buffer_desc->status.bm.bEof == 1) { - m = sc->rx_mbuf[sc->rx_bd_fill_idx]; + sc->rx_idx_head = idx; - /* New mbuf for desc */ - n = if_atsam_new_mbuf(ifp); - if (n != NULL) { - rx_update_mbuf(m, buffer_desc); + /* Enable RX interrupts */ + pHw->GMAC_IER = RX_INTERRUPTS; - IF_ATSAM_UNLOCK(sc); - sc->ifp->if_input(ifp, m); - IF_ATSAM_LOCK(sc); - m = n; - } else { - (void)if_atsam_event_send( - sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT); - } - sc->rx_mbuf[sc->rx_bd_fill_idx] = m; - tmp_rx_bd_address = (uint32_t)m->m_data & - GMAC_RX_BUF_DESC_ADDR_MASK; - - /* Switch pointer to next buffer descriptor */ - if (sc->rx_bd_fill_idx == - (sc->amount_rx_buf - 1)) { - tmp_rx_bd_address |= GMAC_RX_SET_WRAP; - sc->rx_bd_fill_idx = 0; - } else { - ++sc->rx_bd_fill_idx; - } + (void) rtems_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT, + RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out); - /* - * Give ownership to GMAC for further processing - */ - tmp_rx_bd_address &= ~GMAC_RX_SET_USED; - _ARM_Data_synchronization_barrier(); - buffer_desc->addr.val = tmp_rx_bd_address; + while (true) { + uint32_t addr; + uint32_t status; - buffer_desc = (sGmacRxDescriptor *)rx_bd_base - + sc->rx_bd_fill_idx; + addr = rx->bds[idx].addr.val; + if ((addr & GMAC_RX_OWNERSHIP_BIT) == 0) { + break; } - } - /* Setup the interrupts for RX completion and errors */ - GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); - } -} -/* - * Update of current transmit buffer position. - */ -static void if_atsam_tx_bd_pos_update(size_t *pos, size_t amount_tx_buf) -{ - *pos = (*pos + 1) % amount_tx_buf; -} + status = rx->bds[idx].status.val; + m = mbufs[idx]; -/* - * Is RingBuffer empty - */ -static bool if_atsam_ring_buffer_empty(ring_buffer *ring_buffer) -{ - return (ring_buffer->tx_bd_used == ring_buffer->tx_bd_free); -} + if (__predict_true((status & GMAC_RX_EOF_BIT) != 0)) { + struct mbuf *n; -/* - * Is RingBuffer full - */ -static bool if_atsam_ring_buffer_full(ring_buffer *ring_buffer) -{ - size_t tx_bd_used_next = ring_buffer->tx_bd_used; + n = if_atsam_new_mbuf(ifp); + if (n != NULL) { + if_atsam_rx_update_mbuf(m, status); + (*ifp->if_input)(ifp, m); + m = n; + } + } else { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + } - if_atsam_tx_bd_pos_update(&tx_bd_used_next, ring_buffer->length); - return (tx_bd_used_next == ring_buffer->tx_bd_free); -} + mbufs[idx] = m; + rx->bds[idx].addr.val = mtod(m, uint32_t) | + RX_DESC_WRAP(idx); -/* - * Cleanup transmit file descriptors by freeing mbufs which are not needed any - * longer due to correct transmission. - */ -static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc) -{ - struct mbuf *m; - volatile sGmacTxDescriptor *cur; - bool eof_needed = false; - - while (!if_atsam_ring_buffer_empty(&sc->tx_ring)){ - cur = sc->tx_bd_base + sc->tx_ring.tx_bd_free; - if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) { - eof_needed = true; - cur->status.val |= GMAC_TX_SET_USED; - m = sc->tx_mbuf[sc->tx_ring.tx_bd_free]; - m_free(m); - sc->tx_mbuf[sc->tx_ring.tx_bd_free] = 0; - if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_free, - sc->tx_ring.length); - if (cur->status.bm.bLastBuffer) { - eof_needed = false; - } - } else { - break; + idx = (idx + 1) % RX_DESC_COUNT; } } } -/* - * Prepare Ethernet frame to start transmission. - */ -static bool if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m) +static void +if_atsam_tx_reclaim(struct if_atsam_softc *sc, struct ifnet *ifp) { - volatile sGmacTxDescriptor *cur; - volatile sGmacTxDescriptor *start_packet_tx_bd = 0; - int pos = 0; - uint32_t tmp_val = 0; - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - bool success; - int csum_flags = m->m_pkthdr.csum_flags; + uint32_t head_idx; + uint32_t tail_idx; + volatile sGmacTxDescriptor *base; - if_atsam_tx_bd_cleanup(sc); - /* Wait for interrupt in case no buffer descriptors are available */ - /* Wait for events */ - while (true) { - if (if_atsam_ring_buffer_full(&sc->tx_ring)) { - /* Setup the interrupts for TX completion and errors */ - GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0); - success = false; + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + base = &sc->tx->bds[0]; + + while (head_idx != tail_idx) { + uint32_t status; + ift_counter cnt; + struct mbuf *m; + + status = base[tail_idx].status.val; + + if ((status & GMAC_TX_USED_BIT) == 0) { break; } - /* - * Get current mbuf for data fill - */ - cur = &sc->tx_bd_base[sc->tx_ring.tx_bd_used]; - /* Set the transfer data */ - if (m->m_len) { - uintptr_t cache_adjustment = mtod(m, uintptr_t) % 32; - - rtems_cache_flush_multiple_data_lines( - mtod(m, const char *) - cache_adjustment, - (size_t)(m->m_len + cache_adjustment)); - - cur->addr = mtod(m, uint32_t); - tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED; - if (sc->tx_ring.tx_bd_used == (sc->tx_ring.length - 1)) { - tmp_val |= GMAC_TX_SET_WRAP; - } - if (pos == 0) { - start_packet_tx_bd = cur; - } - sc->tx_mbuf[sc->tx_ring.tx_bd_used] = m; - m = m->m_next; - if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_used, - sc->tx_ring.length); + if (__predict_true((status & GMAC_TX_ERR_BITS) == 0)) { + cnt = IFCOUNTER_OPACKETS; } else { - /* Discard empty mbufs */ - m = m_free(m); + cnt = IFCOUNTER_OERRORS; } - /* - * Send out the buffer once the complete mbuf_chain has been - * processed - */ - if (m == NULL) { - tmp_val |= GMAC_TX_SET_EOF; - tmp_val &= ~GMAC_TX_SET_USED; - if ((csum_flags & (CSUM_IP | CSUM_TCP | CSUM_UDP | - CSUM_TCP_IPV6 | CSUM_UDP_IPV6)) != 0) { - start_packet_tx_bd->status.bm.bNoCRC = 0; - } else { - start_packet_tx_bd->status.bm.bNoCRC = 1; - } - _ARM_Data_synchronization_barrier(); - cur->status.val = tmp_val; - start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED; - _ARM_Data_synchronization_barrier(); - GMAC_TransmissionStart(pHw); - success = true; - break; - } else { - if (pos > 0) { - tmp_val &= ~GMAC_TX_SET_USED; + while ((m = sc->tx_mbufs[tail_idx]) == NULL ) { + base[tail_idx].status.val = status | GMAC_TX_USED_BIT; + tail_idx = (tail_idx + 1) % TX_DESC_COUNT; + status = base[tail_idx].status.val; + + if (__predict_false((status & GMAC_TX_ERR_BITS) != 0)) { + cnt = IFCOUNTER_OERRORS; } - pos++; - cur->status.val = tmp_val; } + + base[tail_idx].status.val = status | GMAC_TX_USED_BIT; + if_inc_counter(ifp, cnt, 1); + sc->tx_mbufs[tail_idx] = NULL; + m_freem(m); + + tail_idx = (tail_idx + 1) % TX_DESC_COUNT; } - return success; -} + sc->tx_idx_tail = tail_idx; +} -/* - * Transmit daemon - */ -static void if_atsam_tx_daemon(void *arg) +static void +if_atsam_cache_flush(uintptr_t begin, uintptr_t size) { - if_atsam_softc *sc = (if_atsam_softc *)arg; - rtems_event_set events = 0; - sGmacTxDescriptor *buffer_desc; - int bd_number; - void *tx_bd_base; - struct mbuf *m; - bool success; + uintptr_t end; + uintptr_t mask; + + /* Align begin and end of the data to a cache line */ + end = begin + size; + mask = CPU_CACHE_LINE_BYTES - 1; + begin &= ~mask; + end = (end + mask) & ~mask; + rtems_cache_flush_multiple_data_lines((void *)begin, end - begin); +} - IF_ATSAM_LOCK(sc); +static int +if_atsam_tx_enqueue(struct if_atsam_softc *sc, struct ifnet *ifp, struct mbuf *m) +{ + size_t head_idx; + size_t tail_idx; + size_t capacity; + size_t idx; + volatile sGmacTxDescriptor *base; + volatile sGmacTxDescriptor *desc; + size_t bufs; + uint32_t status; + struct mbuf *n; - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - struct ifnet *ifp = sc->ifp; + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + capacity = (tail_idx - head_idx - 1) % TX_DESC_COUNT; - GMAC_TransmitEnable(pHw, 0); + idx = head_idx; + base = &sc->tx->bds[0]; + bufs = 0; + n = m; - /* Allocate memory space for priority queue descriptor list */ - tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor), - GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(tx_bd_base != NULL); + do { + uint32_t size; - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; - buffer_desc->addr = 0; - buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP; + desc = &base[idx]; - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1); - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2); + size = (uint32_t)n->m_len; + if (__predict_true(size > 0)) { + uintptr_t begin; - /* Allocate memory space for buffer descriptor list */ - tx_bd_base = rtems_cache_coherent_allocate( - sc->amount_tx_buf * sizeof(sGmacTxDescriptor), - GMAC_DESCRIPTOR_ALIGNMENT, 0); - assert(tx_bd_base != NULL); - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; + ++bufs; + if (__predict_false(bufs > capacity)) { + return (ENOBUFS); + } - /* Create descriptor list and mark as empty */ - for (bd_number = 0; bd_number < sc->amount_tx_buf; bd_number++) { - buffer_desc->addr = 0; - buffer_desc->status.val = GMAC_TX_SET_USED; - if (bd_number == (sc->amount_tx_buf - 1)) { - buffer_desc->status.bm.bWrap = 1; - } else { - buffer_desc++; + begin = mtod(n, uintptr_t); + desc->addr = (uint32_t)begin; + status = GMAC_TX_USED_BIT | TX_DESC_WRAP(idx) | size; + desc->status.val = status; + if_atsam_cache_flush(begin, size); + idx = (idx + 1) % TX_DESC_COUNT; } + + n = n->m_next; + } while (n != NULL); + + sc->tx_idx_head = idx; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &base[idx]; + sc->tx_mbufs[idx] = m; + status = GMAC_TX_LAST_BUFFER_BIT; + + while (idx != head_idx) { + desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | + status; + status = 0; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &base[idx]; } - buffer_desc = (sGmacTxDescriptor *)tx_bd_base; - /* Write Buffer Queue Base Address Register */ - GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0); + desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | status; + _ARM_Data_synchronization_barrier(); + sc->Gmac_inst.gGmacd.pHw->GMAC_NCR |= GMAC_NCR_TSTART; + ETHER_BPF_MTAP(ifp, m); + return (0); +} - /* Enable Transmission of data */ - GMAC_TransmitEnable(pHw, 1); +static int +if_atsam_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct if_atsam_softc *sc; + int error; - /* Set variables in context */ - sc->tx_bd_base = tx_bd_base; + if (__predict_false((m->m_flags & M_VLANTAG) != 0)) { + struct mbuf *n; - while (true) { - /* Wait for events */ - if_atsam_event_receive(sc, - ATSAMV7_ETH_START_TRANSMIT_EVENT | - ATSAMV7_ETH_TX_EVENT_INTERRUPT); - //printf("TX Transmit Event received\n"); - - /* - * Send packets till queue is empty - */ - while (true) { - /* - * Get the mbuf chain to transmit - */ - if_atsam_tx_bd_cleanup(sc); - IF_DEQUEUE(&ifp->if_snd, m); - if (!m) { - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - break; - } - success = if_atsam_send_packet(sc, m); - if (!success){ - break; - } + n = ether_vlanencap(m, m->m_pkthdr.ether_vtag); + if (n == NULL) { + m_freem(m); + return (ENOBUFS); } + + m = n; } -} + sc = ifp->if_softc; + IF_ATSAM_LOCK(sc); -/* - * Send packet (caller provides header). - */ -static void if_atsam_enet_start(struct ifnet *ifp) -{ - if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; + error = if_atsam_tx_enqueue(sc, ifp, m); + if_atsam_tx_reclaim(sc, ifp); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - if_atsam_event_send(sc->tx_daemon_tid, - ATSAMV7_ETH_START_TRANSMIT_EVENT); -} + if (__predict_false(error != 0)) { + struct mbuf *n; + n = m_defrag(m, M_NOWAIT); + if (n != NULL) { + m = n; + } + + error = if_atsam_tx_enqueue(sc, ifp, m); + if (error != 0) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + } + } + + IF_ATSAM_UNLOCK(sc); + return (error); +} static uint8_t if_atsam_get_gmac_linkspeed_from_media(uint32_t media_subtype) { @@ -975,23 +853,55 @@ if_atsam_tick(void *context) callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc); } +static void +if_atsam_setup_tx(struct if_atsam_softc *sc) +{ + sGmacTxDescriptor *base; + struct if_atsam_tx_bds *tx; + size_t i; + Gmac *pHw; -/* - * Sets up the hardware and chooses the interface to be used - */ -static void if_atsam_init(void *arg) + pHw = sc->Gmac_inst.gGmacd.pHw; + GMAC_TransmitEnable(pHw, 0); + + /* Allocate memory space for priority queue descriptor list */ + base = rtems_cache_coherent_allocate(sizeof(base), + GMAC_DESCRIPTOR_ALIGNMENT, 0); + assert(base != NULL); + + base->addr = 0; + base->status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT; + + GMAC_SetTxQueue(pHw, (uint32_t)base, 1); + GMAC_SetTxQueue(pHw, (uint32_t)base, 2); + + /* Allocate memory space for buffer descriptor list */ + tx = rtems_cache_coherent_allocate(sizeof(*sc->tx), + GMAC_DESCRIPTOR_ALIGNMENT, 0); + assert(tx != NULL); + + /* Set variables in context */ + sc->tx = tx; + + /* Create descriptor list and mark as empty */ + for (i = 0; i < TX_DESC_COUNT; ++i) { + tx->bds[i].addr = 0; + tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i); + } + + /* Write Buffer Queue Base Address Register */ + GMAC_SetTxQueue(pHw, (uint32_t)&tx->bds[0], 0); + + /* Enable Transmission of data */ + GMAC_TransmitEnable(pHw, 1); +} + +static void +if_atsam_init(if_atsam_softc *sc) { rtems_status_code status; - - if_atsam_softc *sc = (if_atsam_softc *)arg; - struct ifnet *ifp = sc->ifp; uint32_t dmac_cfg = 0; - uint32_t gmii_val = 0; - if (ifp->if_flags & IFF_DRV_RUNNING) { - return; - } - ifp->if_flags |= IFF_DRV_RUNNING; sc->interrupt_number = GMAC_IRQn; /* Enable Peripheral Clock */ @@ -1000,7 +910,6 @@ static void if_atsam_init(void *arg) } /* Setup interrupts */ NVIC_ClearPendingIRQ(GMAC_IRQn); - NVIC_EnableIRQ(GMAC_IRQn); /* Configuration of DMAC */ dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) | @@ -1011,20 +920,17 @@ static void if_atsam_init(void *arg) /* Enable hardware checksum offload for receive */ sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_RXCOEN; + /* Use Multicast Hash Filter */ + sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN; + sc->Gmac_inst.gGmacd.pHw->GMAC_HRB = 0; + sc->Gmac_inst.gGmacd.pHw->GMAC_HRT = 0; + /* Shut down Transmit and Receive */ GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1); - /* - * Allocate mbuf pointers - */ - sc->rx_mbuf = malloc(sc->amount_rx_buf * sizeof *sc->rx_mbuf, - M_TEMP, M_NOWAIT); - sc->tx_mbuf = malloc(sc->amount_tx_buf * sizeof *sc->tx_mbuf, - M_TEMP, M_NOWAIT); - /* Install interrupt handler */ status = rtems_interrupt_handler_install(sc->interrupt_number, "Ethernet", @@ -1036,30 +942,143 @@ static void if_atsam_init(void *arg) /* * Start driver tasks */ - sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096, - if_atsam_rx_daemon, sc); - sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096, - if_atsam_tx_daemon, sc); + + status = rtems_task_create(rtems_build_name('S', 'C', 'r', 'x'), + rtems_bsd_get_task_priority(device_get_name(sc->dev)), 4096, + RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_MODES, &sc->rx_daemon_tid); + assert(status == RTEMS_SUCCESSFUL); + + status = rtems_task_start(sc->rx_daemon_tid, if_atsam_rx_daemon, + (rtems_task_argument)sc); + assert(status == RTEMS_SUCCESSFUL); callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc); + if_atsam_setup_tx(sc); +} + +static int +if_atsam_get_hash_index(const uint8_t *eaddr) +{ + uint64_t eaddr64; + int index; + int i; + + eaddr64 = eaddr[5]; + + for (i = 4; i >= 0; --i) { + eaddr64 <<= 8; + eaddr64 |= eaddr[i]; + } + + index = 0; + + for (i = 0; i < 6; ++i) { + uint64_t bits; + int j; + int hash; + + bits = eaddr64 >> i; + hash = bits & 1; + + for (j = 1; j < 8; ++j) { + bits >>= 6; + hash ^= bits & 1; + } + + index |= hash << i; + } + + return index; +} + +static void +if_atsam_setup_rxfilter(struct if_atsam_softc *sc) +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint64_t mhash; + Gmac *pHw; + + pHw = sc->Gmac_inst.gGmacd.pHw; + + if ((sc->ifp->if_flags & IFF_PROMISC) != 0) { + pHw->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } else { + pHw->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } + + ifp = sc->ifp; + + if ((ifp->if_flags & IFF_ALLMULTI)) + mhash = 0xffffffffffffffffLLU; + else { + mhash = 0; + if_maddr_rlock(ifp); + CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + mhash |= 1LLU << if_atsam_get_hash_index( + LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); + } + if_maddr_runlock(ifp); + } + + pHw->GMAC_HRB = (uint32_t)mhash; + pHw->GMAC_HRT = (uint32_t)(mhash >> 32); +} + +static void +if_atsam_start_locked(struct if_atsam_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + return; + } + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + if_atsam_setup_rxfilter(sc); + + /* Enable TX/RX */ + pHw->GMAC_NCR |= GMAC_NCR_RXEN | GMAC_NCR_TXEN; } +static void +if_atsam_start(void *arg) +{ + struct if_atsam_softc *sc = arg; -/* - * Stop the device - */ -static void if_atsam_stop(struct if_atsam_softc *sc) + IF_ATSAM_LOCK(sc); + if_atsam_start_locked(sc); + IF_ATSAM_UNLOCK(sc); +} + +static void +if_atsam_stop_locked(struct if_atsam_softc *sc) { struct ifnet *ifp = sc->ifp; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + size_t i; - ifp->if_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - /* Disable MDIO interface and TX/RX */ + /* Disable TX/RX */ pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); - pHw->GMAC_NCR &= ~GMAC_NCR_MPE; + + /* Reinitialize the TX descriptors */ + + sc->tx_idx_head = 0; + sc->tx_idx_tail = 0; + + for (i = 0; i < TX_DESC_COUNT; ++i) { + sc->tx->bds[i].addr = 0; + sc->tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i); + m_freem(sc->tx_mbufs[i]); + sc->tx_mbufs[i] = NULL; + } } @@ -1070,7 +1089,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; octets = pHw->GMAC_OTLO; - octets |= pHw->GMAC_OTHI << 32; + octets |= (uint64_t)pHw->GMAC_OTHI << 32; sc->stats.octets_transm += octets; sc->stats.frames_transm += pHw->GMAC_FT; sc->stats.broadcast_frames_transm += pHw->GMAC_BCFT; @@ -1092,7 +1111,7 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) sc->stats.carrier_sense_errors += pHw->GMAC_CSE; octets = pHw->GMAC_ORLO; - octets |= pHw->GMAC_ORHI << 32; + octets |= (uint64_t)pHw->GMAC_ORHI << 32; sc->stats.octets_rec += octets; sc->stats.frames_rec += pHw->GMAC_FR; sc->stats.broadcast_frames_rec += pHw->GMAC_BCFR; @@ -1120,24 +1139,159 @@ if_atsam_poll_hw_stats(struct if_atsam_softc *sc) sc->stats.udp_checksum_errors += pHw->GMAC_UCE; } +static int +if_atsam_stats_reset(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + int value; + int error; + + value = 0; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error != 0 || req->newptr == NULL) { + return (error); + } + + if (value != 0) { + IF_ATSAM_LOCK(sc); + if_atsam_poll_hw_stats(sc); + memset(&sc->stats, 0, sizeof(sc->stats)); + IF_ATSAM_UNLOCK(sc); + } + + return (0); +} + +static int +if_atsam_sysctl_reg(SYSCTL_HANDLER_ARGS) +{ + u_int value; + + value = *(uint32_t *)arg1; + return (sysctl_handle_int(oidp, &value, 0, req)); +} + +static int +if_atsam_sysctl_tx_desc(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + struct sbuf *sb; + int error; + size_t i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) { + return (error); + } + + sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); + if (sb == NULL) { + return (ENOMEM); + } + + sbuf_printf(sb, "\n\tHead %u\n", sc->tx_idx_head); + sbuf_printf(sb, "\tTail %u\n", sc->tx_idx_tail); + sbuf_printf(sb, "\tDMA %u\n", + (pHw->GMAC_TBQB - (uintptr_t)&sc->tx->bds[0]) / 8); + + for (i = 0; i < TX_DESC_COUNT; ++i) { + sbuf_printf(sb, "\t[%2u] %08x %08x\n", i, + sc->tx->bds[i].status.val, sc->tx->bds[i].addr); + } + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} + +static int +if_atsam_sysctl_rx_desc(SYSCTL_HANDLER_ARGS) +{ + struct if_atsam_softc *sc = arg1; + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; + struct sbuf *sb; + int error; + size_t i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) { + return (error); + } + + sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); + if (sb == NULL) { + return (ENOMEM); + } + + sbuf_printf(sb, "\n\tHead %u\n", sc->rx_idx_head); + sbuf_printf(sb, "\tDMA %u\n", + (pHw->GMAC_RBQB - (uintptr_t)&sc->rx->bds[0]) / 8); + + for (i = 0; i < RX_DESC_COUNT; ++i) { + sbuf_printf(sb, "\t[%2u] %08x %08x\n", i, + sc->rx->bds[i].status.val, sc->rx->bds[i].addr); + } + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} static void if_atsam_add_sysctls(device_t dev) { struct if_atsam_softc *sc = device_get_softc(dev); + Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *base; struct sysctl_oid_list *statsnode; struct sysctl_oid_list *hwstatsnode; struct sysctl_oid_list *child; struct sysctl_oid *tree; ctx = device_get_sysctl_ctx(dev); - child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + base = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); - tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "regs", CTLFLAG_RD, + NULL, "if_atsam registers"); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdesc", CTLTYPE_STRING | + CTLFLAG_RD, sc, 0, if_atsam_sysctl_tx_desc, "A", + "Transmit Descriptors"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxdesc", CTLTYPE_STRING | + CTLFLAG_RD, sc, 0, if_atsam_sysctl_rx_desc, "A", + "Receive Descriptors"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "imr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_IMR), 0, + if_atsam_sysctl_reg, "I", "IMR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "isr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_ISR), 0, + if_atsam_sysctl_reg, "I", "ISR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_RSR), 0, + if_atsam_sysctl_reg, "I", "RSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_TSR), 0, + if_atsam_sysctl_reg, "I", "TSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "nsr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NSR), 0, + if_atsam_sysctl_reg, "I", "NSR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncfgr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCFGR), 0, + if_atsam_sysctl_reg, "I", "NCFGR"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncr", CTLTYPE_UINT | + CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCR), 0, + if_atsam_sysctl_reg, "I", "NCR"); + + tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "stats", CTLFLAG_RD, NULL, "if_atsam statistics"); statsnode = SYSCTL_CHILDREN(tree); + SYSCTL_ADD_PROC(ctx, statsnode, OID_AUTO, "reset", CTLTYPE_INT | + CTLFLAG_WR, sc, 0, if_atsam_stats_reset, "I", "Reset"); + tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "sw", CTLFLAG_RD, NULL, "if_atsam software statistics"); child = SYSCTL_CHILDREN(tree); @@ -1145,12 +1299,12 @@ if_atsam_add_sysctls(device_t dev) SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overrun_errors", CTLFLAG_RD, &sc->stats.rx_overrun_errors, 0, "RX overrun errors"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_used_bit_reads", + CTLFLAG_RD, &sc->stats.rx_used_bit_reads, 0, + "RX used bit reads"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_interrupts", CTLFLAG_RD, &sc->stats.rx_interrupts, 0, "Rx interrupts"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_complete_int", - CTLFLAG_RD, &sc->stats.tx_complete_int, 0, - "Tx complete interrupts"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_tur_errors", CTLFLAG_RD, &sc->stats.tx_tur_errors, 0, "Error Tur Tx interrupts"); @@ -1163,9 +1317,6 @@ if_atsam_add_sysctls(device_t dev) SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_hresp_errors", CTLFLAG_RD, &sc->stats.tx_hresp_errors, 0, "Error Hresp Tx interrupts"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_interrupts", - CTLFLAG_RD, &sc->stats.tx_interrupts, 0, - "Tx interrupts"); tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "hw", CTLFLAG_RD, NULL, "if_atsam hardware statistics"); @@ -1175,40 +1326,40 @@ if_atsam_add_sysctls(device_t dev) NULL, "if_atsam hardware transmit statistics"); child = SYSCTL_CHILDREN(tree); - SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_transm", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets", CTLFLAG_RD, &sc->stats.octets_transm, "Octets Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames", CTLFLAG_RD, &sc->stats.frames_transm, 0, "Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames", CTLFLAG_RD, &sc->stats.broadcast_frames_transm, 0, "Broadcast Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames", CTLFLAG_RD, &sc->stats.multicast_frames_transm, 0, "Multicast Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames", CTLFLAG_RD, &sc->stats.pause_frames_transm, 0, "Pause Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes", CTLFLAG_RD, &sc->stats.frames_64_byte_transm, 0, "64 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes", CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_transm, 0, "65 to 127 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes", CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_transm, 0, "128 to 255 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes", CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_transm, 0, "256 to 511 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes", CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_transm, 0, "512 to 1023 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes", CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_transm, 0, "1024 to 1518 Byte Frames Transmitted"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_greater_1518_byte_transm", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes", CTLFLAG_RD, &sc->stats.frames_greater_1518_byte_transm, 0, "Greater Than 1518 Byte Frames Transmitted"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "transmit_underruns", @@ -1237,49 +1388,49 @@ if_atsam_add_sysctls(device_t dev) NULL, "if_atsam hardware receive statistics"); child = SYSCTL_CHILDREN(tree); - SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets_rec", + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets", CTLFLAG_RD, &sc->stats.octets_rec, "Octets Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames", CTLFLAG_RD, &sc->stats.frames_rec, 0, "Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames", CTLFLAG_RD, &sc->stats.broadcast_frames_rec, 0, "Broadcast Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames", CTLFLAG_RD, &sc->stats.multicast_frames_rec, 0, "Multicast Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames", CTLFLAG_RD, &sc->stats.pause_frames_rec, 0, "Pause Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes", CTLFLAG_RD, &sc->stats.frames_64_byte_rec, 0, "64 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes", CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_rec, 0, "65 to 127 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes", CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_rec, 0, "128 to 255 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes", CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_rec, 0, "256 to 511 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes", CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_rec, 0, "512 to 1023 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes", CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_rec, 0, "1024 to 1518 Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_byte_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes", CTLFLAG_RD, &sc->stats.frames_1519_to_maximum_byte_rec, 0, "1519 to Maximum Byte Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames", CTLFLAG_RD, &sc->stats.undersize_frames_rec, 0, "Undersize Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames", CTLFLAG_RD, &sc->stats.oversize_frames_rec, 0, "Oversize Frames Received"); - SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers_rec", + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers", CTLFLAG_RD, &sc->stats.jabbers_rec, 0, "Jabbers Received"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frame_check_sequence_errors", @@ -1311,49 +1462,6 @@ if_atsam_add_sysctls(device_t dev) "UDP Checksum Errors"); } - -/* - * Calculates the index that is to be sent into the hash registers - */ -static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val) -{ - uint64_t tmp_val; - uint8_t i, j; - uint64_t idx; - int offset = 0; - - addr &= MAC_ADDR_MASK; - - for (i = 0; i < HASH_INDEX_AMOUNT; ++i) { - tmp_val = 0; - offset = 0; - for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) { - idx = (addr >> (offset + i)) & MAC_IDX_MASK; - tmp_val ^= idx; - offset += HASH_INDEX_AMOUNT; - } - if (tmp_val > 0) { - *val |= (1u << i); - } - } -} - - -/* - * Dis/Enable promiscuous Mode - */ -static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable) -{ - Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; - - if (enable) { - pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE; - } else { - pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE; - } -} - - static int if_atsam_mediaioctl(if_atsam_softc *sc, struct ifreq *ifr, u_long command) { @@ -1380,8 +1488,6 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int rv = 0; - bool prom_enable; - struct mii_data *mii; switch (command) { case SIOCGIFMEDIA: @@ -1389,17 +1495,31 @@ if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) rv = if_atsam_mediaioctl(sc, ifr, command); break; case SIOCSIFFLAGS: + IF_ATSAM_LOCK(sc); if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - if_atsam_init(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) { + if_atsam_setup_rxfilter(sc); + } + } else { + if_atsam_start_locked(sc); } - prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0); - if_atsam_promiscuous_mode(sc, prom_enable); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - if_atsam_stop(sc); + if_atsam_stop_locked(sc); } } + sc->if_flags = ifp->if_flags; + IF_ATSAM_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + IF_ATSAM_LOCK(sc); + if_atsam_setup_rxfilter(sc); + IF_ATSAM_UNLOCK(sc); + } break; default: rv = ether_ioctl(ifp, command, data); @@ -1416,7 +1536,6 @@ static int if_atsam_driver_attach(device_t dev) if_atsam_softc *sc; struct ifnet *ifp; int unit; - char *unitName; uint8_t eaddr[ETHER_ADDR_LEN]; sc = device_get_softc(dev); @@ -1437,13 +1556,6 @@ static int if_atsam_driver_attach(device_t dev) memcpy(sc->GMacAddress, eaddr, ETHER_ADDR_LEN); - sc->amount_rx_buf = RXBUF_COUNT; - sc->amount_tx_buf = TXBUF_COUNT; - - sc->tx_ring.tx_bd_used = 0; - sc->tx_ring.tx_bd_free = 0; - sc->tx_ring.length = sc->amount_tx_buf; - /* Set Initial Link Speed */ sc->link_speed = GMAC_SPEED_100M; sc->link_duplex = GMAC_DUPLEX_FULL; @@ -1486,17 +1598,20 @@ static int if_atsam_driver_attach(device_t dev) */ ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_init = if_atsam_init; + ifp->if_init = if_atsam_start; ifp->if_ioctl = if_atsam_ioctl; - ifp->if_start = if_atsam_enet_start; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + ifp->if_transmit = if_atsam_transmit; + ifp->if_qflush = if_qflush; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 | - IFCAP_VLAN_HWCSUM; + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTAGGING; + ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP; - IFQ_SET_MAXLEN(&ifp->if_snd, TXBUF_COUNT - 1); - ifp->if_snd.ifq_drv_maxlen = TXBUF_COUNT - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); + ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* * Attach the interface @@ -1504,6 +1619,7 @@ static int if_atsam_driver_attach(device_t dev) ether_ifattach(ifp, eaddr); if_atsam_add_sysctls(dev); + if_atsam_init(sc); return (0); } diff --git a/rtemsbsd/sys/dev/iicbus/rtems-i2c.c b/rtemsbsd/sys/dev/iicbus/rtems-i2c.c index b965f248..0fb14d1a 100644 --- a/rtemsbsd/sys/dev/iicbus/rtems-i2c.c +++ b/rtemsbsd/sys/dev/iicbus/rtems-i2c.c @@ -87,9 +87,11 @@ rtems_i2c_attach(device_t dev) len = OF_getprop_alloc(node, "rtems,i2c-path", &sc->path); if (len == -1){ - device_printf(sc->dev, "Path not found in Device Tree"); - OF_prop_free(sc->path); - return (ENXIO); + len = OF_getprop_alloc(node, "rtems,path", &sc->path); + if (len == -1) { + device_printf(sc->dev, "Path not found in Device Tree"); + return (ENXIO); + } } if ((sc->sc_iicbus = device_add_child(sc->dev, "iicbus", -1)) == NULL) { diff --git a/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c index c37bd868..21420689 100644 --- a/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c +++ b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c @@ -270,10 +270,10 @@ lpc_tsc_init(struct lpc_tsc_softc *sc) TSCWRITE4(sc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); } -static void -lpc_tsc_ev_close(struct evdev_dev *evdev, void *data) +static int +lpc_tsc_ev_close(struct evdev_dev *evdev) { - struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data; + struct lpc_tsc_softc *sc = evdev_get_softc(evdev); uint32_t tmp; LPC_TSC_LOCK_ASSERT(sc); @@ -283,12 +283,14 @@ lpc_tsc_ev_close(struct evdev_dev *evdev, void *data) TSCWRITE4(sc, LPC32XX_TSC_CON, tmp); lpc_adc_module_disable(sc); + + return (0); } static int -lpc_tsc_ev_open(struct evdev_dev *evdev, void *data) +lpc_tsc_ev_open(struct evdev_dev *evdev) { - struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data; + struct lpc_tsc_softc *sc = evdev_get_softc(evdev); LPC_TSC_LOCK_ASSERT(sc); diff --git a/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c b/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c new file mode 100644 index 00000000..ec3c9cdd --- /dev/null +++ b/rtemsbsd/sys/dev/mmc/st-sdmmc-config.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <bsp.h> + +#ifdef LIBBSP_ARM_STM32H7_BSP_H + +#include <bsp/st-sdmmc-config.h> +#include <dev/mmc/mmcreg.h> + +void +st_sdmmc_get_config(uintptr_t sdmmc_base, struct st_sdmmc_config *cfg) +{ + switch (sdmmc_base) { + case SDMMC1_BASE: + cfg->data_lines = 4; + cfg->dirpol = true; + /* + * FIXME: Also the evaluation board could switch to 1.8V, the + * control for the level converter isn't implemented in the + * driver yet. So only signal 2.9V. + */ + cfg->ocr_voltage = MMC_OCR_280_290 | MMC_OCR_290_300; + break; + } +} + +#endif /* LIBBSP_ARM_STM32H7_BSP_H */ diff --git a/rtemsbsd/sys/dev/mmc/st-sdmmc.c b/rtemsbsd/sys/dev/mmc/st-sdmmc.c new file mode 100644 index 00000000..4b202952 --- /dev/null +++ b/rtemsbsd/sys/dev/mmc/st-sdmmc.c @@ -0,0 +1,859 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * STM32H7xx SDMMC controller. Documentation: ST RM0433 (Rev 6), Chapter 54 + * + * According to Linux DTS, the SDMMC is compatible with an ARM Primecell PL18X + * with peripheral ID 0x10153180. + */ +/* + * Note: This driver is inspired by the NetBSD pl181 driver written by Jared D. + * McNeill. + */ +/* + * Note regarding DMA on STM32H7: + * + * The STM32H7 SDMMC doesn't have an interrupt for few received data (less than + * half the FIFO size). So especially for short responses, it is not possible to + * use the SDMMC without DMA. + * + * On the STM32H7 SDMMC there are two DMAs: One IDMA integrated into the SDMMC + * and one MDMA that is a general purpose DMA. MDMA can only be used on first + * instance of the SDMMC. For the second instance, the trigger signals are not + * connected. + * + * The IDMA of SDMMC1 can only access AXI SRAM, QSPI and FMC (where SDRAM is + * located). The IDMA of SDMMC2 could access memory in other domains too. + * + * MDMA is designed to be a companion for IDMA. It seems that ST thought of a + * very specific software structure for that. It can be either used to change + * the IDMA buffer addresses (which would allow some kind of scatter gather + * functionality with fixed buffer sizes) or to refill IDMA buffers from some + * RAM that can't be accessed directly by the IDMA. Take a look at ST AN5200 Rev + * 1 "Getting started with STM32H7 Series SDMMC host controller" for more + * details. + */ + +#include <rtems/malloc.h> +#include <rtems/irq-extension.h> + +#include <bsp.h> + +#ifdef LIBBSP_ARM_STM32H7_BSP_H + +#include <stm32h7/hal.h> +#include <stm32h7/memory.h> + +#include <bsp/st-sdmmc-config.h> + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/condvar.h> + +#include <pthread.h> + +#include <machine/resource.h> + +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmcbrvar.h> + +#define ST_SDMMC_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define ST_SDMMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define ST_SDMMC_LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), \ + "st_sdmmc", MTX_DEF) + +#define SDMMC_INT_ERROR_MASK ( \ + SDMMC_MASK_CTIMEOUTIE | \ + SDMMC_MASK_CCRCFAILIE | \ + SDMMC_MASK_DTIMEOUTIE | \ + SDMMC_MASK_DCRCFAILIE | \ + SDMMC_MASK_ACKFAILIE | \ + SDMMC_MASK_RXOVERRIE | \ + SDMMC_MASK_TXUNDERRIE | \ + SDMMC_MASK_DABORTIE | \ + SDMMC_MASK_ACKTIMEOUTIE ) +#define SDMMC_INT_DATA_DONE_MASK ( SDMMC_MASK_IDMABTCIE | SDMMC_MASK_DATAENDIE ) +#define SDMMC_INT_CMD_DONE_MASK ( SDMMC_MASK_CMDSENTIE ) +#define SDMMC_INT_CMD_RESPONSE_DONE_MASK ( SDMMC_MASK_CMDRENDIE ) + +#define RES_MEM_SDMMC 0 +#define RES_MEM_DLYB 1 +#define RES_IRQ_SDMMC 2 +#define RES_NR 3 + +/* Maximum non-aligned buffer is 512 byte from mmc_send_ext_csd() */ +#define DMA_BUF_SIZE 512 + +#if 0 +#define debug_print(sc, lvl, ...) \ + if (lvl <= 1) device_printf(sc->dev, __VA_ARGS__) +#else +#define debug_print(...) +#endif + +struct st_sdmmc_softc; + +typedef void (*st_sdmmc_dma_setup_transfer)(struct st_sdmmc_softc *, void *); + +struct st_sdmmc_softc { + device_t dev; + struct mmc_host host; + + struct mtx mtx; + rtems_binary_semaphore wait_done; + int bus_busy; + + struct resource *res[RES_NR]; + SDMMC_TypeDef *sdmmc; + DLYB_TypeDef *dlyb; + rtems_vector_number irq; + + uint32_t sdmmc_ker_ck; + struct st_sdmmc_config cfg; + + uint32_t intr_status; + uint8_t *dmabuf; +}; + +void st_sdmmc_idma_txrx(struct st_sdmmc_softc *sc, void *buf) +{ + BSD_ASSERT( + (buf >= (void*) stm32h7_memory_sdram_1_begin && + buf < (void*) stm32h7_memory_sdram_1_end) || + (buf >= (void*) stm32h7_memory_sram_axi_begin && + buf < (void*) stm32h7_memory_sram_axi_end) || + (buf >= (void*) stm32h7_memory_sdram_2_begin && + buf < (void*) stm32h7_memory_sdram_2_end) || + (buf >= (void*) stm32h7_memory_quadspi_begin && + buf < (void*) stm32h7_memory_quadspi_end)); + sc->sdmmc->IDMABASE0 = (uintptr_t) buf; + sc->sdmmc->IDMACTRL = SDMMC_IDMA_IDMAEN; +} + +void st_sdmmc_idma_stop(struct st_sdmmc_softc *sc) +{ + sc->sdmmc->IDMACTRL = 0; +} + +static void +st_sdmmc_intr(void *arg) +{ + struct st_sdmmc_softc *sc; + uint32_t status; + + sc = arg; + + status = sc->sdmmc->STA; + sc->sdmmc->ICR = status; + sc->intr_status |= status; + + /* + * There seems to be some odd combination where the status is zero but + * an interrupt occurred. In that case, the task shouldn't wake up. + * Therefore check for status != 0. + */ + if (status != 0 && + ((status & SDMMC_STA_BUSYD0) == 0 || + (sc->sdmmc->MASK & SDMMC_STA_BUSYD0END) == 0)) { + rtems_binary_semaphore_post(&sc->wait_done); + } +} + +static int +st_sdmmc_probe(device_t dev) +{ + device_set_desc(dev, "STM32H7xx SDMMC Host"); + return (0); +} + +static int +st_sdmmc_set_clock_and_bus( + struct st_sdmmc_softc *sc, + uint32_t freq, + enum mmc_bus_width width +) +{ + uint32_t clk_div; + uint32_t clkcr; + + clkcr = SDMMC_CLKCR_NEGEDGE | SDMMC_CLKCR_PWRSAV | SDMMC_CLKCR_HWFC_EN; + clk_div = howmany(sc->sdmmc_ker_ck, freq) / 2; + if (clk_div > SDMMC_CLKCR_CLKDIV >> SDMMC_CLKCR_CLKDIV_Pos) { + clk_div = SDMMC_CLKCR_CLKDIV >> SDMMC_CLKCR_CLKDIV_Pos; + } + clkcr |= clk_div << SDMMC_CLKCR_CLKDIV_Pos; + + switch (width) { + default: + BSD_ASSERT(width == bus_width_1); + clkcr |= 0 << SDMMC_CLKCR_WIDBUS_Pos; + break; + case bus_width_4: + clkcr |= 1 << SDMMC_CLKCR_WIDBUS_Pos; + break; + case bus_width_8: + clkcr |= 2 << SDMMC_CLKCR_WIDBUS_Pos; + break; + } + + sc->sdmmc->CLKCR = clkcr; + + return 0; +} + +static void +st_sdmmc_host_reset(struct st_sdmmc_softc *sc) +{ + sc->sdmmc->MASK = 0; + sc->sdmmc->ICR = 0xFFFFFFFF; +} + +static void +st_sdmmc_hw_init(struct st_sdmmc_softc *sc) +{ + st_sdmmc_set_clock_and_bus(sc, 400000, bus_width_1); + sc->sdmmc->POWER = 0; + if (sc->cfg.dirpol) { + sc->sdmmc->POWER |= SDMMC_POWER_DIRPOL; + } + /* ST example code just set it on. So do the same. */ + sc->sdmmc->POWER |= SDMMC_POWER_PWRCTRL_0 | SDMMC_POWER_PWRCTRL_1; + /* + * Wait at least 74 cycles; lowest freq is 400kHz + * -> 1/400kHz * 47 = 117us + */ + usleep(120000); + + st_sdmmc_host_reset(sc); +} + +static void +st_sdmmc_board_init(void) +{ + HAL_SD_MspInit(NULL); +} + +static int +st_sdmmc_attach(device_t dev) +{ + struct st_sdmmc_softc *sc; + int error = 0; + static pthread_once_t once = PTHREAD_ONCE_INIT; + bool interrupt_installed = false; + + sc = device_get_softc(dev); + + memset(sc, 0, sizeof(*sc)); + + if (error == 0) { + sc->dev = dev; + } + + if (error == 0) { + ST_SDMMC_LOCK_INIT(sc); + rtems_binary_semaphore_init(&sc->wait_done, "sdmmc-sem"); + } + + if (error == 0) { + int rid = 0; + sc->res[RES_MEM_SDMMC] = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, 0, ~0, 1, RF_ACTIVE); + if (sc->res[RES_MEM_SDMMC] == NULL) { + device_printf(dev, + "could not allocate sdmmc resource\n"); + error = ENXIO; + } else { + sc->sdmmc = (SDMMC_TypeDef *) + sc->res[RES_MEM_SDMMC]->r_bushandle; + } + } + if (error == 0) { + int rid = 0; + sc->res[RES_MEM_DLYB] = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, 1, ~0, 1, RF_ACTIVE); + if (sc->res[RES_MEM_DLYB] == NULL) { + device_printf(dev, + "could not allocate dlyb resource\n"); + error = ENXIO; + } else { + sc->dlyb = (DLYB_TypeDef *) + sc->res[RES_MEM_DLYB]->r_bushandle; + } + } + if (error == 0) { + int rid = 0; + sc->res[RES_IRQ_SDMMC] = bus_alloc_resource(dev, SYS_RES_IRQ, + &rid, 0, ~0, 1, RF_ACTIVE); + if (sc->res[RES_IRQ_SDMMC] == NULL) { + device_printf(dev, + "could not allocate interrupt resource\n"); + error = ENXIO; + } else { + sc->irq = sc->res[RES_IRQ_SDMMC]->r_bushandle; + } + } + + if (error == 0) { + /* + * FIXME: This memory should be in AXI SRAM, QSPI or FMC. In the + * configurations for our BSP, the heap is either in AXI SRAM or + * in the SDRAM. So that is OK for now. Only assert that the + * assumption is true. A better solution (like fixed AXI SRAM) + * might would be a good idea. + */ + sc->dmabuf = rtems_heap_allocate_aligned_with_boundary( + DMA_BUF_SIZE, CPU_CACHE_LINE_BYTES, 0); + if (sc->dmabuf == NULL) { + device_printf(dev, "could not allocate dma buffer\n"); + error = ENOMEM; + } + BSD_ASSERT( + ((void*) sc->dmabuf >= (void*) stm32h7_memory_sram_axi_begin && + (void*) sc->dmabuf < (void*) stm32h7_memory_sram_axi_end) || + ((void*) sc->dmabuf >= (void*) stm32h7_memory_sdram_1_begin && + (void*) sc->dmabuf < (void*) stm32h7_memory_sdram_1_end) || + ((void*) sc->dmabuf >= (void*) stm32h7_memory_sdram_2_begin && + (void*) sc->dmabuf < (void*) stm32h7_memory_sdram_2_end) || + ((void*) sc->dmabuf >= (void*) stm32h7_memory_quadspi_begin && + (void*) sc->dmabuf < (void*) stm32h7_memory_quadspi_end)); + } + + if (error == 0) { + pthread_once(&once, st_sdmmc_board_init); + } + + if (error == 0) { + sc->sdmmc_ker_ck = HAL_RCCEx_GetPeriphCLKFreq( + RCC_PERIPHCLK_SDMMC); + + sc->host.f_min = 400000; + sc->host.f_max = (int) sc->sdmmc_ker_ck; + if (sc->host.f_max > 50000000) + sc->host.f_max = 50000000; + } + + if (error == 0) { + st_sdmmc_get_config((uintptr_t)sc->sdmmc, &sc->cfg); + if (sc->cfg.data_lines == 0) { + device_printf(dev, "config not found!\n"); + error = EINVAL; + } + } + + if (error == 0) { + st_sdmmc_hw_init(sc); + } + + if (error == 0) { + error = rtems_interrupt_handler_install(sc->irq, "SDMMC", + RTEMS_INTERRUPT_UNIQUE, st_sdmmc_intr, sc); + if (error != 0) { + device_printf(dev, + "could not setup interrupt handler.\n"); + } else { + interrupt_installed = true; + } + } + + if (error == 0) { + sc->host.host_ocr = sc->cfg.ocr_voltage & + ((1 << (MMC_OCR_MAX_VOLTAGE_SHIFT + 1)) - 1); + + sc->host.caps = MMC_CAP_HSPEED; + if (sc->cfg.data_lines >= 4) { + sc->host.caps |= MMC_CAP_4_BIT_DATA; + } + if (sc->cfg.data_lines >= 8) { + sc->host.caps |= MMC_CAP_8_BIT_DATA; + } + } + + if (error == 0) { + device_add_child(dev, "mmc", -1); + error = bus_generic_attach(dev); + } + + if (error != 0) { + /* Undo relevant parts */ + if (interrupt_installed) { + rtems_interrupt_handler_remove(sc->irq, + st_sdmmc_intr, sc); + } + + free(sc->dmabuf); + + /* + * FIXME: Should free resources but RTEMS doesn't implement + * bus_free_resource(). + */ + } + + return error; +} + +static int +st_sdmmc_detach(device_t dev) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(dev); + + /* Always attached. So this is not necessary. */ + BSD_ASSERT(0); + + (void)sc; + + return (EBUSY); +} + +static int +st_sdmmc_update_ios(device_t brdev, device_t reqdev) +{ + struct st_sdmmc_softc *sc; + struct mmc_ios *ios; + int err; + + sc = device_get_softc(brdev); + + ST_SDMMC_LOCK(sc); + + ios = &sc->host.ios; + err = st_sdmmc_set_clock_and_bus(sc, ios->clock, ios->bus_width); + if (err != 0) { + return (err); + } + + if (ios->power_mode == power_off) { + /* + * FIXME: Maybe a reset of the module is necessary instead. But + * the ST samples use a power off too so it should work. But + * power saving hasn't been tested during development. + */ + sc->sdmmc->POWER &= ~(SDMMC_POWER_PWRCTRL); + } else { + sc->sdmmc->POWER |= SDMMC_POWER_PWRCTRL; + } + + ST_SDMMC_UNLOCK(sc); + + return (EIO); +} + +static int +st_sdmmc_wait_irq(struct st_sdmmc_softc *sc) +{ + int error = 0; + + error = rtems_binary_semaphore_wait_timed_ticks(&sc->wait_done, + RTEMS_MILLISECONDS_TO_TICKS(5000)); + + if (error != 0) { + error = MMC_ERR_TIMEOUT; + } else if ((sc->intr_status & + (SDMMC_STA_DTIMEOUT | SDMMC_STA_CTIMEOUT)) != 0) { + error = MMC_ERR_TIMEOUT; + } else if ((sc->intr_status & SDMMC_INT_ERROR_MASK) != 0) { + error = MMC_ERR_FAILED; + } + + return error; +} + +static void +st_sdmmc_cmd_do(struct st_sdmmc_softc *sc, struct mmc_command *cmd) +{ + uint32_t cmdval; + uint32_t xferlen; + uint32_t int_mask; + uint32_t arg; + void *data = NULL; + bool short_xfer = false; + + debug_print(sc, 1, "cmd: %d, arg: %08x, flags: 0x%x\n", + cmd->opcode, cmd->arg, cmd->flags); + + xferlen = 0; + sc->intr_status = 0; + + sc->sdmmc->CMD = 0; + /* + * There should be a delay of "at least seven sdmmc_hclk clock periods" + * before CMD is written again. The sdmmc_hclk is the clock that is used + * to access the Registers. Some more registers are accessed before the + * next CMD write. So that should be no problem. + */ + sc->sdmmc->MASK = 0; + sc->sdmmc->ICR = 0xFFFFFFFF; + /* + * Make sure the semaphore is cleared at this point. There can be an + * error case where a previous command run into a timeout and still + * produced an interrupt before it has been disabled. + */ + if (rtems_binary_semaphore_try_wait(&sc->wait_done) == 0) { + device_printf(sc->dev, "Semaphore set from last command\n"); + } + + int_mask = SDMMC_INT_ERROR_MASK; + + arg = cmd->arg; + cmdval = (cmd->opcode & SDMMC_CMD_CMDINDEX_Msk) | SDMMC_CMD_CPSMEN; + + if ((cmd->flags & MMC_RSP_PRESENT) != 0) { + if ((cmd->flags & MMC_RSP_136) != 0) { + cmdval |= SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_WAITRESP_1; + } else if ((cmd->flags & MMC_RSP_CRC) != 0) { + cmdval |= SDMMC_CMD_WAITRESP_0; + } else { + cmdval |= SDMMC_CMD_WAITRESP_1; + } + int_mask |= SDMMC_INT_CMD_RESPONSE_DONE_MASK; + } else { + int_mask |= SDMMC_INT_CMD_DONE_MASK; + } + + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + cmdval |= SDMMC_CMD_CMDSTOP; + } + + if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) { + cmdval |= SDMMC_CMD_CMDTRANS; + } + + if ((cmd->flags & MMC_RSP_BUSY) != 0) { + int_mask |= SDMMC_MASK_BUSYD0ENDIE; + } + + if (cmd->data != NULL) { + uint32_t blksize; + uint32_t dctrl = 0; + xferlen = cmd->data->len; + + if (xferlen > MMC_SECTOR_SIZE) { + blksize = ffs(MMC_SECTOR_SIZE) - 1; + } else { + blksize = ffs(xferlen) - 1; + } + + debug_print(sc, 1, + "data: len: %d, xferlen: %d, blksize: %d, dataflags: 0x%x\n", + cmd->data->len, xferlen, blksize, cmd->data->flags); + + BSD_ASSERT(xferlen % (1 << blksize) == 0); + + data = cmd->data->data; + /* + * Check whether data have to be copied. Reason is either + * misaligned start address or misaligned length. + */ + if (((uintptr_t)data % CPU_CACHE_LINE_BYTES != 0) || + (xferlen % CPU_CACHE_LINE_BYTES) != 0) { + BSD_ASSERT(xferlen < DMA_BUF_SIZE); + if ((cmd->data->flags & MMC_DATA_READ) == 0) { + memcpy(sc->dmabuf, cmd->data->data, xferlen); + } + data = sc->dmabuf; + short_xfer = true; + } + + dctrl |= blksize << SDMMC_DCTRL_DBLOCKSIZE_Pos; + if ((cmd->data->flags & MMC_DATA_READ) != 0) { + dctrl |= SDMMC_DCTRL_DTDIR; + rtems_cache_invalidate_multiple_data_lines(data, + roundup2(xferlen, CPU_CACHE_LINE_BYTES)); + } else { + rtems_cache_flush_multiple_data_lines(data, + roundup2(xferlen, CPU_CACHE_LINE_BYTES)); + } + st_sdmmc_idma_txrx(sc, data); + + sc->sdmmc->DTIMER = 0xFFFFFFFF; + sc->sdmmc->DLEN = xferlen; + sc->sdmmc->DCTRL = dctrl; + + int_mask &= ~(SDMMC_INT_CMD_DONE_MASK | + SDMMC_INT_CMD_RESPONSE_DONE_MASK); + int_mask |= SDMMC_INT_DATA_DONE_MASK; + } + + sc->sdmmc->MASK = int_mask; + sc->sdmmc->ARG = arg; + sc->sdmmc->CMD = cmdval | cmd->opcode; + + cmd->error = st_sdmmc_wait_irq(sc); + if (cmd->error) { + sleep(10); + device_printf(sc->dev, + "error (%d) waiting for xfer: status %08x, cmd: %d, flags: %08x\n", + cmd->error, sc->intr_status, cmd->opcode, cmd->flags); + } else { + if ((cmd->flags & MMC_RSP_PRESENT) != 0) { + if ((cmd->flags & MMC_RSP_136) != 0) { + cmd->resp[0] = sc->sdmmc->RESP1; + cmd->resp[1] = sc->sdmmc->RESP2; + cmd->resp[2] = sc->sdmmc->RESP3; + cmd->resp[3] = sc->sdmmc->RESP4; + debug_print(sc, 2, "rsp: %08x %08x %08x %08x\n", + cmd->resp[0], + cmd->resp[1], + cmd->resp[2], + cmd->resp[3]); + } else { + cmd->resp[0] = sc->sdmmc->RESP1; + debug_print(sc, 2, "rsp: %08x\n", cmd->resp[0]); + } + } + + if (short_xfer && cmd->data != NULL && + (cmd->data->flags & MMC_DATA_READ) != 0) { + memcpy(cmd->data->data, sc->dmabuf, xferlen); + } + } + + st_sdmmc_idma_stop(sc); + sc->sdmmc->CMD = 0; + sc->sdmmc->MASK = 0; + sc->sdmmc->ICR = 0xFFFFFFFF; +} + +static int +st_sdmmc_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(brdev); + + ST_SDMMC_LOCK(sc); + st_sdmmc_cmd_do(sc, req->cmd); + if (req->stop != NULL) { + st_sdmmc_cmd_do(sc, req->stop); + } + ST_SDMMC_UNLOCK(sc); + + (*req->done)(req); + + return (0); +} + +static int +st_sdmmc_get_ro(device_t brdev, device_t reqdev) +{ + + /* + * FIXME: Currently just ignore write protection. Micro-SD doesn't have + * it anyway and most boards are now using Micro-SD slots. + */ + return (0); +} + +static int +st_sdmmc_acquire_host(device_t brdev, device_t reqdev) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(brdev); + + ST_SDMMC_LOCK(sc); + while (sc->bus_busy) + msleep(sc, &sc->mtx, PZERO, "stsdmmcah", hz / 5); + sc->bus_busy++; + ST_SDMMC_UNLOCK(sc); + return (0); +} + +static int +st_sdmmc_release_host(device_t brdev, device_t reqdev) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(brdev); + + ST_SDMMC_LOCK(sc); + sc->bus_busy--; + wakeup(sc); + ST_SDMMC_UNLOCK(sc); + return (0); +} + +static int +st_sdmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_VCCQ: + *(int *)result = sc->host.ios.vccq; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + /* + * Note: At the moment of writing this, RTEMS ignores this + * value. So it's quite irrelevant what is returned here. + */ + *(int *)result = (SDMMC_DLEN_DATALENGTH_Msk) / MMC_SECTOR_SIZE; + break; + case MMCBR_IVAR_TIMING: + *(int *)result = sc->host.ios.timing; + break; + } + return (0); +} + +static int +st_sdmmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + struct st_sdmmc_softc *sc; + + sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + case MMCBR_IVAR_TIMING: + sc->host.ios.timing = value; + break; + case MMCBR_IVAR_VCCQ: + sc->host.ios.vccq = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + return (0); +} + +static device_method_t st_sdmmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, st_sdmmc_probe), + DEVMETHOD(device_attach, st_sdmmc_attach), + DEVMETHOD(device_detach, st_sdmmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, st_sdmmc_read_ivar), + DEVMETHOD(bus_write_ivar, st_sdmmc_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, st_sdmmc_update_ios), + DEVMETHOD(mmcbr_request, st_sdmmc_request), + DEVMETHOD(mmcbr_get_ro, st_sdmmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, st_sdmmc_acquire_host), + DEVMETHOD(mmcbr_release_host, st_sdmmc_release_host), + + DEVMETHOD_END +}; + +static driver_t st_sdmmc_driver = { + "st_sdmmc", + st_sdmmc_methods, + sizeof(struct st_sdmmc_softc) +}; + +static devclass_t st_sdmmc_devclass; + +DRIVER_MODULE(st_sdmmc, nexus, st_sdmmc_driver, st_sdmmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, st_sdmmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(st_sdmmc, mmc, 1, 1, 1); + +#endif /* LIBBSP_ARM_STM32H7_BSP_H */ diff --git a/rtemsbsd/sys/dev/mve/if_mve.c b/rtemsbsd/sys/dev/mve/if_mve.c new file mode 100644 index 00000000..517484ee --- /dev/null +++ b/rtemsbsd/sys/dev/mve/if_mve.c @@ -0,0 +1,2389 @@ +/* RTEMS driver for the mv643xx gigabit ethernet chip */ + +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* + * NOTE: Some register (e.g., the SMI register) are SHARED among the + * three devices. Concurrent access protection is provided by + * the global networking semaphore. + * If other drivers are running on a subset of IFs then proper + * locking of all shared registers must be implemented! + * + * Some things I learned about this hardware can be found + * further down... + */ + +/* +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif +*/ + +#include <bsp.h> + +#ifdef LIBBSP_BEATNIK_BSP_H + +#include <rtems/bspIo.h> +#include <rtems/error.h> +#include <bsp/irq.h> +#include <bsp/gtreg.h> +#include <libcpu/byteorder.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <bsp/mv643xx_eth.h> + +/* CONFIGURABLE PARAMETERS */ + +/* Enable Hardware Snooping; if this is disabled (undefined), + * cache coherency is maintained by software. + */ +#undef ENABLE_HW_SNOOPING + +/* Compile-time debugging features */ + +/* Enable paranoia assertions and checks; reduce # of descriptors to minimum for stressing */ +#define MVETH_TESTING + +/* Enable debugging messages and some support routines (dump rings etc.) */ +#undef MVETH_DEBUG + +#define TX_NUM_TAG_SLOTS 1 /* leave room for tag; must not be 0 */ + +/* This is REAL; chip reads from 64-bit down-aligned buffer + * if the buffer size is < 8 !!! for buffer sizes 8 and upwards + * alignment is not an issue. This was verified using the + * 'mve_smallbuf_test.c' + */ +#define ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + +/* Chip register configuration values */ +#define MVETH_PORT_CONFIG_VAL (0 \ + | MV643XX_ETH_DFLT_RX_Q(0) \ + | MV643XX_ETH_DFLT_RX_ARP_Q(0) \ + | MV643XX_ETH_DFLT_RX_TCP_Q(0) \ + | MV643XX_ETH_DFLT_RX_UDP_Q(0) \ + | MV643XX_ETH_DFLT_RX_BPDU_Q(0) \ + ) + + +#define MVETH_PORT_XTEND_CONFIG_VAL 0 + +#ifdef OLDCONFIGVAL +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + | MV643XX_ETH_SET_FULL_DUPLEX \ + | MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD \ + ) +#endif +/* If we enable autoneg (duplex, speed, ...) then it seems + * that the chip automatically updates link settings + * (correct link settings are reflected in PORT_STATUS_R). + * However, when we disable aneg in the PHY then things + * can get messed up and the port doesn't work anymore. + * => we follow the linux driver in disabling all aneg + * in the serial config reg. and manually updating the + * speed & duplex bits when the phy link status changes. + * FIXME: don't know what to do about pause/flow-ctrl. + * It is best to just use ANEG anyways!!! + */ +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + ) + +#define MVETH_SERIAL_CTRL_CONFIG_MSK (0 \ + | MV643XX_ETH_SERIAL_PORT_ENBL \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_SC_MAX_RX_MASK \ + ) + + +#ifdef __PPC__ +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_4_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_4_64BIT \ + ) +#else +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_16_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_16_64BIT \ + ) +#endif + +/* minimal frame size we accept */ +#define MVETH_MIN_FRAMSZ_CONFIG_VAL 40 + +/* END OF CONFIGURABLE SECTION */ + +/* + * Here's stuff I learned about this chip: + * + * + * RX interrupt flags: + * + * broadcast packet RX: 0x00000005 + * last buf: 0x00000c05 + * overrun: 0x00000c00 + * unicast packet RX: 0x00000005 + * bad CRC received: 0x00000005 + * + * clearing 0x00000004 -> clears 0x00000001 + * clearing 0x00000400 -> clears 0x00000800 + * + * --> 0x0801 are probably some sort of summary bits. + * + * TX interrupt flags: + * + * broadcast packet in 1 buf: xcause: 0x00000001 (cause 0x00080000) + * into disconn. link: " " + * + * in some cases, I observed xcause: 0x00000101 (reason for 0x100 unknown + * but the linux driver accepts it also). + * + * + * Here a few more ugly things about this piece of hardware I learned + * (painfully, painfully; spending many many hours & nights :-() + * + * a) Especially in the case of 'chained' descriptors, the DMA keeps + * clobbering 'cmd_sts' long after it cleared the OWNership flag!!! + * Only after the whole chain is processed (OWN cleared on the + * last descriptor) it is safe to change cmd_sts. + * However, in the case of hardware snooping I found that the + * last descriptor in chain has its cmd_sts still clobbered *after* + * checking ownership!, I.e., + * if ( ! OWN & cmd_sts ) { + * cmd_sts = 0; + * } + * --> sometimes, cmd_sts is STILL != 0 here + * + * b) Sometimes, the OWNership flag is *not cleared*. + * + * c) Weird things happen if the chip finds a descriptor with 'OWN' + * still set (i.e., not properly loaded), i.e., corrupted packets + * are sent [with OK checksum since the chip calculates it]. + * + * Combine a+b+c and we end up with a real mess. + * + * The fact that the chip doesn't reliably reset OWN and that OTOH, + * it can't be reliably reset by the driver and still, the chip needs + * it for proper communication doesn't make things easy... + * + * Here the basic workarounds: + * + * - In addition to check OWN, the scavenger compares the "currently + * served desc" register to the descriptor it tries to recover and + * ignores OWN if they do not match. Hope this is OK. + * Otherwise, we could scan the list of used descriptors and proceed + * recycling descriptors if we find a !OWNed one behind the target... + * + * - Always keep an empty slot around to mark the end of the list of + * jobs. The driver clears the descriptor ahead when enqueueing a new + * packet. + */ + +#define DRVNAME "mve" +#define MAX_NUM_SLOTS 3 + +#if MV643XXETH_NUM_DRIVER_SLOTS > MAX_NUM_SLOTS +#error "mv643xxeth: only MAX_NUM_SLOTS supported" +#endif + +#ifdef NDEBUG +#error "Driver uses assert() statements with side-effects; MUST NOT define NDEBUG" +#endif + +#ifdef MVETH_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#define TX_AVAILABLE_RING_SIZE(mp) ((mp)->xbuf_count - (TX_NUM_TAG_SLOTS)) + +/* macros for ring alignment; proper alignment is a hardware req; . */ + +#ifdef ENABLE_HW_SNOOPING + +#define RING_ALIGNMENT 16 +/* rx buffers must be 64-bit aligned (chip requirement) */ +#define RX_BUF_ALIGNMENT 8 + +#else /* ENABLE_HW_SNOOPING */ + +/* Software cache management */ + +#ifndef __PPC__ +#error "Dont' know how to deal with cache on this CPU architecture" +#endif + +/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency + * management works for cache line sizes of 16 and 32 bytes only. If the line size + * is bigger, the descriptors could be padded... + */ +#if PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32 +#error "Cache line size must be 16 or 32" +#else +#define RING_ALIGNMENT PPC_CACHE_ALIGNMENT +#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT +#endif + +#endif /* ENABLE_HW_SNOOPING */ + + +/* HELPER MACROS */ + +/* Align base to alignment 'a' */ +#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1))) + +#define NOOP() do {} while(0) + +/* Function like macros */ +#define MV_READ(off) \ + ld_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off))) +#define MV_WRITE(off, data) \ + st_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off)), ((unsigned)data)) + + +/* ENET window mapped 1:1 to CPU addresses by our BSP/MotLoad + * -- if this is changed, we should think about caching the 'next' and 'buf' pointers. + */ +#define CPUADDR2ENET(a) ((Dma_addr_t)(a)) +#define ENET2CPUADDR(a) (a) + +#if 1 /* Whether to automatically try to reclaim descriptors when enqueueing new packets */ +#define MVETH_CLEAN_ON_SEND(mp) (BSP_mve_swipe_tx(mp)) +#else +#define MVETH_CLEAN_ON_SEND(mp) (-1) +#endif + +#define NEXT_TXD(d) (d)->next +#define NEXT_RXD(d) (d)->next + +/* REGISTER AND DESCRIPTOR OFFSET AND BIT DEFINITIONS */ + +/* Descriptor Definitions */ +/* Rx descriptor */ +#define RDESC_ERROR (1<< 0) /* Error summary */ + +/* Error code (bit 1&2) is only valid if summary bit is set */ +#define RDESC_CRC_ERROR ( 1) +#define RDESC_OVERRUN_ERROR ( 3) +#define RDESC_MAX_FRAMELENGTH_ERROR ( 5) +#define RDESC_RESOURCE_ERROR ( 7) + +#define RDESC_LAST (1<<26) /* Last Descriptor */ +#define RDESC_FRST (1<<27) /* First Descriptor */ +#define RDESC_INT_ENA (1<<29) /* Enable Interrupts */ +#define RDESC_DMA_OWNED (1<<31) + +/* Tx descriptor */ +#define TDESC_ERROR (1<< 0) /* Error summary */ +#define TDESC_ZERO_PAD (1<<19) +#define TDESC_LAST (1<<20) /* Last Descriptor */ +#define TDESC_FRST (1<<21) /* First Descriptor */ +#define TDESC_GEN_CRC (1<<22) +#define TDESC_INT_ENA (1<<23) /* Enable Interrupts */ +#define TDESC_DMA_OWNED (1<<31) + + + +/* Register Definitions */ +#define MV643XX_ETH_PHY_ADDR_R (0x2000) +#define MV643XX_ETH_SMI_R (0x2004) +#define MV643XX_ETH_SMI_BUSY (1<<28) +#define MV643XX_ETH_SMI_VALID (1<<27) +#define MV643XX_ETH_SMI_OP_WR (0<<26) +#define MV643XX_ETH_SMI_OP_RD (1<<26) + +#define MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port) (0x2448 + ((port)<<10)) +#define MV643XX_ETH_TX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_TX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_TX_START_M(queues) ((queues)&0xff) +#define MV643XX_ETH_TX_STOP_M(queues) (((queues)&0xff)<<8) +#define MV643XX_ETH_TX_STOP_ALL (0xff00) +#define MV643XX_ETH_TX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(port) (0x2680 + ((port)<<10)) +#define MV643XX_ETH_RX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_RX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_RX_STOP_ALL (0xff00) +#define MV643XX_ETH_RX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_CURRENT_SERVED_TX_DESC(port) (0x2684 + ((port)<<10)) + +/* The chip puts the ethernet header at offset 2 into the buffer so + * that the payload is aligned + */ +#define ETH_RX_OFFSET 2 +#define ETH_CRC_LEN 4 /* strip FCS at end of packet */ + + +#define MV643XX_ETH_INTERRUPT_CAUSE_R(port) (0x2460 + ((port)<<10)) +/* not fully understood; RX seems to raise 0x0005 or 0x0c05 if last buffer is filled and 0x0c00 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_IRQS (0x07ffffff) +#define MV643XX_ETH_KNOWN_IRQS (0x00080c07) +#define MV643XX_ETH_IRQ_EXT_ENA (1<<1) +/* defined in public header +#define MV643XX_ETH_IRQ_RX_DONE (1<<2) + */ +#define MV643XX_ETH_IRQ_RX_NO_DESC (1<<10) +#define MV643XX_ETH_TX_Q_N_END(n) (1<<((n)+19)) +/* We just use queue 0 */ +#define MV643XX_ETH_TX_Q_END MV643XX_ETH_TX_Q_N_END(0) + +#define MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(port) (0x2464 + ((port)<<10)) +/* not fully understood; TX seems to raise 0x0001 and link change is 0x00010000 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_EXT_IRQS (0x0011ffff) +/* Recent (2013) linux driver mentions both bits 0x00110000 as 'link change' causes */ +#define MV643XX_ETH_KNOWN_EXT_IRQS (0x00110101) +/* TX queues 0..7 */ +#define MV643XX_ETH_EXT_IRQ_TXN_DONE(n) (1<<(n)) +/* We just use queue 0 */ +/* defined in public header +#define MV643XX_ETH_EXT_IRQ_TX_DONE MV643XX_ETH_EXT_IRQ_TXN_DONE(0) +#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16) + */ +#define MV643XX_ETH_INTERRUPT_ENBL_R(port) (0x2468 + ((port)<<10)) +#define MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(port) (0x246c + ((port)<<10)) + +/* port configuration */ +#define MV643XX_ETH_PORT_CONFIG_R(port) (0x2400 + ((port)<<10)) +#define MV643XX_ETH_UNICAST_PROMISC_MODE (1<<0) +#define MV643XX_ETH_DFLT_RX_Q(q) ((q)<<1) +#define MV643XX_ETH_DFLT_RX_ARP_Q(q) ((q)<<4) +#define MV643XX_ETH_REJ_BCAST_IF_NOT_IP_OR_ARP (1<<7) +#define MV643XX_ETH_REJ_BCAST_IF_IP (1<<8) +#define MV643XX_ETH_REJ_BCAST_IF_ARP (1<<9) +#define MV643XX_ETH_TX_AM_NO_UPDATE_ERR_SUMMARY (1<<12) +#define MV643XX_ETH_CAPTURE_TCP_FRAMES_ENBL (1<<14) +#define MV643XX_ETH_CAPTURE_UDP_FRAMES_ENBL (1<<15) +#define MV643XX_ETH_DFLT_RX_TCP_Q(q) ((q)<<16) +#define MV643XX_ETH_DFLT_RX_UDP_Q(q) ((q)<<19) +#define MV643XX_ETH_DFLT_RX_BPDU_Q(q) ((q)<<22) + + + +#define MV643XX_ETH_PORT_CONFIG_XTEND_R(port) (0x2404 + ((port)<<10)) +#define MV643XX_ETH_CLASSIFY_ENBL (1<<0) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_AS_NORMAL (0<<1) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_2_Q7 (1<<1) +#define MV643XX_ETH_PARTITION_DISBL (0<<2) +#define MV643XX_ETH_PARTITION_ENBL (1<<2) + +#define MV643XX_ETH_SDMA_CONFIG_R(port) (0x241c + ((port)<<10)) +#define MV643XX_ETH_SDMA_RIFB (1<<0) +#define MV643XX_ETH_RX_BURST_SZ_1_64BIT (0<<1) +#define MV643XX_ETH_RX_BURST_SZ_2_64BIT (1<<1) +#define MV643XX_ETH_RX_BURST_SZ_4_64BIT (2<<1) +#define MV643XX_ETH_RX_BURST_SZ_8_64BIT (3<<1) +#define MV643XX_ETH_RX_BURST_SZ_16_64BIT (4<<1) +#define MV643XX_ETH_SMDA_BLM_RX_NO_SWAP (1<<4) +#define MV643XX_ETH_SMDA_BLM_TX_NO_SWAP (1<<5) +#define MV643XX_ETH_SMDA_DESC_BYTE_SWAP (1<<6) +#define MV643XX_ETH_TX_BURST_SZ_1_64BIT (0<<22) +#define MV643XX_ETH_TX_BURST_SZ_2_64BIT (1<<22) +#define MV643XX_ETH_TX_BURST_SZ_4_64BIT (2<<22) +#define MV643XX_ETH_TX_BURST_SZ_8_64BIT (3<<22) +#define MV643XX_ETH_TX_BURST_SZ_16_64BIT (4<<22) + +#define MV643XX_ETH_RX_MIN_FRAME_SIZE_R(port) (0x247c + ((port)<<10)) + + +#define MV643XX_ETH_SERIAL_CONTROL_R(port) (0x243c + ((port)<<10)) +#define MV643XX_ETH_SERIAL_PORT_ENBL (1<<0) /* Enable serial port */ +#define MV643XX_ETH_FORCE_LINK_PASS (1<<1) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX (1<<2) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL (1<<3) +#define MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL (1<<4) +#define MV643XX_ETH_FORCE_FC_MODE_TX_PAUSE_DIS (1<<5) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX (1<<7) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1<<8) +#define MV643XX_ETH_BIT9_UNKNOWN (1<<9) /* unknown purpose; linux sets this */ +#define MV643XX_ETH_FORCE_LINK_FAIL_DISABLE (1<<10) +#define MV643XX_ETH_RETRANSMIT_FOREVER (1<<11) /* limit to 16 attempts if clear */ +#define MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII (1<<13) +#define MV643XX_ETH_DTE_ADV_1 (1<<14) +#define MV643XX_ETH_AUTO_NEG_BYPASS_ENBL (1<<15) +#define MV643XX_ETH_RESTART_AUTO_NEG (1<<16) +#define MV643XX_ETH_SC_MAX_RX_1518 (0<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1522 (1<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1552 (2<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9022 (3<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9192 (4<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9700 (5<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_MASK (7<<17) /* bitmask */ +#define MV643XX_ETH_SET_EXT_LOOPBACK (1<<20) +#define MV643XX_ETH_SET_FULL_DUPLEX (1<<21) +#define MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD (1<<22) /* enable flow ctrl on rx and tx in full-duplex */ +#define MV643XX_ETH_SET_GMII_SPEED_1000 (1<<23) /* 10/100 if clear */ +#define MV643XX_ETH_SET_MII_SPEED_100 (1<<24) /* 10 if clear */ + +#define MV643XX_ETH_PORT_STATUS_R(port) (0x2444 + ((port)<<10)) + +#define MV643XX_ETH_PORT_STATUS_MODE_10_BIT (1<<0) +#define MV643XX_ETH_PORT_STATUS_LINK_UP (1<<1) +#define MV643XX_ETH_PORT_STATUS_FDX (1<<2) +#define MV643XX_ETH_PORT_STATUS_FC (1<<3) +#define MV643XX_ETH_PORT_STATUS_1000 (1<<4) +#define MV643XX_ETH_PORT_STATUS_100 (1<<5) +/* PSR bit 6 unknown */ +#define MV643XX_ETH_PORT_STATUS_TX_IN_PROGRESS (1<<7) +#define MV643XX_ETH_PORT_STATUS_ANEG_BYPASSED (1<<8) +#define MV643XX_ETH_PORT_STATUS_PARTITION (1<<9) +#define MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY (1<<10) + +#define MV643XX_ETH_MIB_COUNTERS(port) (0x3000 + ((port)<<7)) +#define MV643XX_ETH_NUM_MIB_COUNTERS 32 + +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO (0) +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_HI (1<<2) +#define MV643XX_ETH_MIB_BAD_OCTS_RCVD (2<<2) +#define MV643XX_ETH_MIB_INTERNAL_MAC_TX_ERR (3<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_RCVD (4<<2) +#define MV643XX_ETH_MIB_BAD_FRAMES_RCVD (5<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_RCVD (6<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_RCVD (7<<2) +#define MV643XX_ETH_MIB_FRAMES_64_OCTS (8<<2) +#define MV643XX_ETH_MIB_FRAMES_65_127_OCTS (9<<2) +#define MV643XX_ETH_MIB_FRAMES_128_255_OCTS (10<<2) +#define MV643XX_ETH_MIB_FRAMES_256_511_OCTS (11<<2) +#define MV643XX_ETH_MIB_FRAMES_512_1023_OCTS (12<<2) +#define MV643XX_ETH_MIB_FRAMES_1024_MAX_OCTS (13<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO (14<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_HI (15<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_SENT (16<<2) +#define MV643XX_ETH_MIB_EXCESSIVE_COLL (17<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_SENT (18<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_SENT (19<<2) +#define MV643XX_ETH_MIB_UNREC_MAC_CTRL_RCVD (20<<2) +#define MV643XX_ETH_MIB_FC_SENT (21<<2) +#define MV643XX_ETH_MIB_GOOD_FC_RCVD (22<<2) +#define MV643XX_ETH_MIB_BAD_FC_RCVD (23<<2) +#define MV643XX_ETH_MIB_UNDERSIZE_RCVD (24<<2) +#define MV643XX_ETH_MIB_FRAGMENTS_RCVD (25<<2) +#define MV643XX_ETH_MIB_OVERSIZE_RCVD (26<<2) +#define MV643XX_ETH_MIB_JABBER_RCVD (27<<2) +#define MV643XX_ETH_MIB_MAC_RX_ERR (28<<2) +#define MV643XX_ETH_MIB_BAD_CRC_EVENT (29<<2) +#define MV643XX_ETH_MIB_COLL (30<<2) +#define MV643XX_ETH_MIB_LATE_COLL (31<<2) + +#define MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(port) (0x3400+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(port) (0x3500+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_UNICAST_TBL(port) (0x3600+((port)<<10)) +#define MV643XX_ETH_NUM_MCAST_ENTRIES 64 +#define MV643XX_ETH_NUM_UNICAST_ENTRIES 4 + +#define MV643XX_ETH_BAR_0 (0x2200) +#define MV643XX_ETH_SIZE_R_0 (0x2204) +#define MV643XX_ETH_BAR_1 (0x2208) +#define MV643XX_ETH_SIZE_R_1 (0x220c) +#define MV643XX_ETH_BAR_2 (0x2210) +#define MV643XX_ETH_SIZE_R_2 (0x2214) +#define MV643XX_ETH_BAR_3 (0x2218) +#define MV643XX_ETH_SIZE_R_3 (0x221c) +#define MV643XX_ETH_BAR_4 (0x2220) +#define MV643XX_ETH_SIZE_R_4 (0x2224) +#define MV643XX_ETH_BAR_5 (0x2228) +#define MV643XX_ETH_SIZE_R_5 (0x222c) +#define MV643XX_ETH_NUM_BARS 6 + +/* Bits in the BAR reg to program cache snooping */ +#define MV64360_ENET2MEM_SNOOP_NONE 0x0000 +#define MV64360_ENET2MEM_SNOOP_WT 0x1000 +#define MV64360_ENET2MEM_SNOOP_WB 0x2000 +#define MV64360_ENET2MEM_SNOOP_MSK 0x3000 + + +#define MV643XX_ETH_BAR_ENBL_R (0x2290) +#define MV643XX_ETH_BAR_DISABLE(bar) (1<<(bar)) +#define MV643XX_ETH_BAR_DISBL_ALL 0x3f + +#define MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(port) (0x260c+((port)<<10)) +#define MV643XX_ETH_RX_Q1_CURRENT_DESC_PTR(port) (0x261c+((port)<<10)) +#define MV643XX_ETH_RX_Q2_CURRENT_DESC_PTR(port) (0x262c+((port)<<10)) +#define MV643XX_ETH_RX_Q3_CURRENT_DESC_PTR(port) (0x263c+((port)<<10)) +#define MV643XX_ETH_RX_Q4_CURRENT_DESC_PTR(port) (0x264c+((port)<<10)) +#define MV643XX_ETH_RX_Q5_CURRENT_DESC_PTR(port) (0x265c+((port)<<10)) +#define MV643XX_ETH_RX_Q6_CURRENT_DESC_PTR(port) (0x266c+((port)<<10)) +#define MV643XX_ETH_RX_Q7_CURRENT_DESC_PTR(port) (0x267c+((port)<<10)) + +#define MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(port) (0x26c0+((port)<<10)) +#define MV643XX_ETH_TX_Q1_CURRENT_DESC_PTR(port) (0x26c4+((port)<<10)) +#define MV643XX_ETH_TX_Q2_CURRENT_DESC_PTR(port) (0x26c8+((port)<<10)) +#define MV643XX_ETH_TX_Q3_CURRENT_DESC_PTR(port) (0x26cc+((port)<<10)) +#define MV643XX_ETH_TX_Q4_CURRENT_DESC_PTR(port) (0x26d0+((port)<<10)) +#define MV643XX_ETH_TX_Q5_CURRENT_DESC_PTR(port) (0x26d4+((port)<<10)) +#define MV643XX_ETH_TX_Q6_CURRENT_DESC_PTR(port) (0x26d8+((port)<<10)) +#define MV643XX_ETH_TX_Q7_CURRENT_DESC_PTR(port) (0x26dc+((port)<<10)) + +#define MV643XX_ETH_MAC_ADDR_LO(port) (0x2414+((port)<<10)) +#define MV643XX_ETH_MAC_ADDR_HI(port) (0x2418+((port)<<10)) + +/* TYPE DEFINITIONS */ + +/* just to make the purpose explicit; vars of this + * type may need CPU-dependent address translation, + * endian conversion etc. + */ +typedef uint32_t Dma_addr_t; + +typedef volatile struct mveth_rx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t buf_size; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + void *u_buf; /* user buffer */ + volatile struct mveth_rx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ + uint32_t pad[2]; +} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthRxDescRec, *MvEthRxDesc; + +typedef volatile struct mveth_tx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t l4i_chk; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + uint32_t workaround[2]; /* use this space to work around the 8byte problem (is this real?) */ + void *u_buf; /* user buffer */ + volatile struct mveth_tx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ +} __attribute__(( aligned(RING_ALIGNMENT) )) MvEthTxDescRec, *MvEthTxDesc; + +/* Assume there are never more then 64k aliasing entries */ +typedef uint16_t Mc_Refcnt[MV643XX_ETH_NUM_MCAST_ENTRIES*4]; + +/* driver private data and bsdnet interface structure */ +struct mveth_private { + MvEthRxDesc rx_ring; /* pointers to aligned ring area */ + MvEthTxDesc tx_ring; /* pointers to aligned ring area */ + MvEthRxDesc ring_area; /* allocated ring area */ + int rbuf_count, xbuf_count; /* saved ring sizes from ifconfig */ + int port_num; + int phy; + MvEthRxDesc d_rx_t; /* tail of the RX ring; next received packet */ + MvEthTxDesc d_tx_t, d_tx_h; + uint32_t rx_desc_dma, tx_desc_dma; /* ring address as seen by DMA; (1:1 on this BSP) */ + int avail; + void (*isr)(void*); + void *isr_arg; + /* Callbacks to handle buffers */ + void (*cleanup_txbuf)(void*, void*, int); /* callback to cleanup TX buffer */ + void *cleanup_txbuf_arg; + void *(*alloc_rxbuf)(int *psize, uintptr_t *paddr); /* allocate RX buffer */ + void (*consume_rxbuf)(void*, void*, int); /* callback to consume RX buffer */ + void *consume_rxbuf_arg; + rtems_id tid; + uint32_t irq_mask; /* IRQs we use */ + uint32_t xirq_mask; + int promisc; + struct { + unsigned irqs; + unsigned maxchain; + unsigned repack; + unsigned packet; + unsigned idrops; /* no counter in core code */ + struct { + uint64_t good_octs_rcvd; /* 64-bit */ + uint32_t bad_octs_rcvd; + uint32_t internal_mac_tx_err; + uint32_t good_frames_rcvd; + uint32_t bad_frames_rcvd; + uint32_t bcast_frames_rcvd; + uint32_t mcast_frames_rcvd; + uint32_t frames_64_octs; + uint32_t frames_65_127_octs; + uint32_t frames_128_255_octs; + uint32_t frames_256_511_octs; + uint32_t frames_512_1023_octs; + uint32_t frames_1024_max_octs; + uint64_t good_octs_sent; /* 64-bit */ + uint32_t good_frames_sent; + uint32_t excessive_coll; + uint32_t mcast_frames_sent; + uint32_t bcast_frames_sent; + uint32_t unrec_mac_ctrl_rcvd; + uint32_t fc_sent; + uint32_t good_fc_rcvd; + uint32_t bad_fc_rcvd; + uint32_t undersize_rcvd; + uint32_t fragments_rcvd; + uint32_t oversize_rcvd; + uint32_t jabber_rcvd; + uint32_t mac_rx_err; + uint32_t bad_crc_event; + uint32_t coll; + uint32_t late_coll; + } mib; + } stats; + struct { + Mc_Refcnt specl, other; + } mc_refcnt; +}; + +/* GLOBAL VARIABLES */ + +/* Format strings for statistics messages */ +static const char *mibfmt[] = { + " GOOD_OCTS_RCVD: %"PRIu64"\n", + 0, + " BAD_OCTS_RCVD: %"PRIu32"\n", + " INTERNAL_MAC_TX_ERR: %"PRIu32"\n", + " GOOD_FRAMES_RCVD: %"PRIu32"\n", + " BAD_FRAMES_RCVD: %"PRIu32"\n", + " BCAST_FRAMES_RCVD: %"PRIu32"\n", + " MCAST_FRAMES_RCVD: %"PRIu32"\n", + " FRAMES_64_OCTS: %"PRIu32"\n", + " FRAMES_65_127_OCTS: %"PRIu32"\n", + " FRAMES_128_255_OCTS: %"PRIu32"\n", + " FRAMES_256_511_OCTS: %"PRIu32"\n", + " FRAMES_512_1023_OCTS:%"PRIu32"\n", + " FRAMES_1024_MAX_OCTS:%"PRIu32"\n", + " GOOD_OCTS_SENT: %"PRIu64"\n", + 0, + " GOOD_FRAMES_SENT: %"PRIu32"\n", + " EXCESSIVE_COLL: %"PRIu32"\n", + " MCAST_FRAMES_SENT: %"PRIu32"\n", + " BCAST_FRAMES_SENT: %"PRIu32"\n", + " UNREC_MAC_CTRL_RCVD: %"PRIu32"\n", + " FC_SENT: %"PRIu32"\n", + " GOOD_FC_RCVD: %"PRIu32"\n", + " BAD_FC_RCVD: %"PRIu32"\n", + " UNDERSIZE_RCVD: %"PRIu32"\n", + " FRAGMENTS_RCVD: %"PRIu32"\n", + " OVERSIZE_RCVD: %"PRIu32"\n", + " JABBER_RCVD: %"PRIu32"\n", + " MAC_RX_ERR: %"PRIu32"\n", + " BAD_CRC_EVENT: %"PRIu32"\n", + " COLL: %"PRIu32"\n", + " LATE_COLL: %"PRIu32"\n", +}; + +/* Interrupt Handler Connection */ + +/* forward decls + implementation for IRQ API funcs */ + +STATIC int +mveth_init_rx_desc_ring(struct mveth_private *mp); + +STATIC int +mveth_init_tx_desc_ring(struct mveth_private *mp); + +int +BSP_mve_dring_nonsync(struct mveth_private *mp); + +static void mveth_isr(rtems_irq_hdl_param unit); +static void noop(const rtems_irq_connect_data *unused) {} +static int noop1(const rtems_irq_connect_data *unused) { return 0; } + +static rtems_irq_connect_data irq_data[MAX_NUM_SLOTS] = { + { + BSP_IRQ_ETH0, + 0, + (rtems_irq_hdl_param)0, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH1, + 0, + (rtems_irq_hdl_param)1, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH2, + 0, + (rtems_irq_hdl_param)2, + noop, + noop, + noop1 + }, +}; + +/* LOW LEVEL SUPPORT ROUTINES */ + +/* Software Cache Coherency */ +#ifndef ENABLE_HW_SNOOPING +#ifndef __PPC__ +#error "Software cache coherency maintenance is not implemented for your CPU architecture" +#endif + +static inline unsigned INVAL_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbi 0, %1":"=m"(*(cache_line*)d):"r"(d)); + return (unsigned)d; /* so this can be used in comma expression */ +} + +static inline void FLUSH_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbf 0, %0"::"r"(d),"m"(*(cache_line*)d)); +} + +static inline void FLUSH_BARRIER(void) +{ + asm volatile("eieio"); +} + +/* RX buffers are always cache-line aligned + * ASSUMPTIONS: + * - 'addr' is cache aligned + * - len is a multiple >0 of cache lines + */ +static inline void INVAL_BUF(register uintptr_t addr, register int len) +{ +typedef char maxbuf[2048]; /* more than an ethernet packet */ + do { + len -= RX_BUF_ALIGNMENT; + asm volatile("dcbi %0, %1"::"b"(addr),"r"(len)); + } while (len > 0); + asm volatile("":"=m"(*(maxbuf*)addr)); +} + +/* Flushing TX buffers is a little bit trickier; we don't really know their + * alignment but *assume* adjacent addresses are covering 'ordinary' memory + * so that flushing them does no harm! + */ +static inline void FLUSH_BUF(register uintptr_t addr, register int len) +{ + asm volatile("":::"memory"); + len = MV643XX_ALIGN(len, RX_BUF_ALIGNMENT); + do { + asm volatile("dcbf %0, %1"::"b"(addr),"r"(len)); + len -= RX_BUF_ALIGNMENT; + } while ( len >= 0 ); +} + +#else /* hardware snooping enabled */ + +/* inline this to silence compiler warnings */ +static inline int INVAL_DESC(volatile void *d) +{ return 0; } + +#define FLUSH_DESC(d) NOOP() +#define INVAL_BUF(b,l) NOOP() +#define FLUSH_BUF(b,l) NOOP() +#define FLUSH_BARRIER() NOOP() + +#endif /* cache coherency support */ + +/* Synchronize memory access */ +#ifdef __PPC__ +static inline void membarrier(void) +{ + asm volatile("sync":::"memory"); +} +#else +#error "memory barrier instruction not defined (yet) for this CPU" +#endif + +/* Enable and disable interrupts at the device */ +static inline void +mveth_enable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + val = (val | mask | MV643XX_ETH_IRQ_EXT_ENA) & mp->irq_mask; + + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), val); + + val = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + val = (val | mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), val); + + rtems_interrupt_enable(l); +} + +static inline uint32_t +mveth_disable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val,xval,tmp; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + tmp = ( (val & ~mask) | MV643XX_ETH_IRQ_EXT_ENA ) & mp->irq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), tmp); + + xval = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + tmp = (xval & ~mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), tmp); + + rtems_interrupt_enable(l); + + return (val | xval); +} + +/* This should be safe even w/o turning off interrupts if multiple + * threads ack different bits in the cause register (and ignore + * other ones) since writing 'ones' into the cause register doesn't + * 'stick'. + */ + +static inline uint32_t +mveth_ack_irqs(struct mveth_private *mp, uint32_t mask) +{ +register uint32_t x,xe,p; +register uint32_t rval; + + p = mp->port_num; + /* Get cause */ + x = MV_READ(MV643XX_ETH_INTERRUPT_CAUSE_R(p)); + + /* Ack interrupts filtering the ones we're interested in */ + + /* Note: EXT_IRQ bit clears by itself if EXT interrupts are cleared */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(p), ~ (x & mp->irq_mask & mask)); + + /* linux driver tests 1<<1 as a summary bit for extended interrupts; + * the mv64360 seems to use 1<<19 for that purpose; for the moment, + * I just check both. + * Update: link status irq (1<<16 in xe) doesn't set (1<<19) in x! + */ + if ( 1 /* x & 2 */ ) + { + xe = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p)); + + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p), ~ (xe & mp->xirq_mask & mask)); + } else { + xe = 0; + } +#ifdef MVETH_TESTING + if ( ((x & MV643XX_ETH_ALL_IRQS) & ~MV643XX_ETH_KNOWN_IRQS) + || ((xe & MV643XX_ETH_ALL_EXT_IRQS) & ~MV643XX_ETH_KNOWN_EXT_IRQS) ) { + fprintf(stderr, "Unknown IRQs detected; leaving all disabled for debugging:\n"); + fprintf(stderr, "Cause reg was 0x%08x, ext cause 0x%08x\n", x, xe); +/* + mp->irq_mask = 0; + mp->xirq_mask = 0; +*/ + } +#endif + /* luckily, the extended and 'normal' interrupts we use don't overlap so + * we can just OR them into a single word + */ + rval = (xe & mp->xirq_mask) | (x & mp->irq_mask); + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: mveth_ack_irqs 0x%08x\n", rval); +#endif + return rval; +} + +static void mveth_isr(rtems_irq_hdl_param arg) +{ +struct mveth_private *mp = (struct mveth_private*) arg; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mveth_isr\n"); +#endif + mp->stats.irqs++; + mp->isr(mp->isr_arg); +} + +static void +mveth_clear_mib_counters(struct mveth_private *mp) +{ +register int i; +register uint32_t b; + /* reading the counters resets them */ + b = MV643XX_ETH_MIB_COUNTERS(mp->port_num); + for (i=0; i< MV643XX_ETH_NUM_MIB_COUNTERS; i++, b+=4) + (void)MV_READ(b); +} + +/* Reading a MIB register also clears it. Hence we read the lo + * register first, then the hi one. Correct reading is guaranteed since + * the 'lo' register cannot overflow after it is read since it had + * been reset to 0. + */ +static unsigned long long +read_long_mib_counter(int port_num, int idx) +{ +unsigned long lo; +unsigned long long hi; + lo = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + idx++; + hi = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + return (hi<<32) | lo; +} + +static inline unsigned long +read_mib_counter(int port_num, int idx) +{ + return MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); +} + + +/* write ethernet address from buffer to hardware (need to change unicast filter after this) */ +static void +mveth_write_eaddr(struct mveth_private *mp, unsigned char *eaddr) +{ +int i; +uint32_t x; + + /* build hi word */ + for (i=4,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_HI(mp->port_num), x); + + /* build lo word */ + for (i=2,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_LO(mp->port_num), x); +} + +static inline int +port2phy(int port) +{ + port &= 0x1f; + /* during early init we may not know the phy and we are given a port number instead! */ + return ( (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*port)) & 0x1f ); +} + +/* PHY/MII Interface + * + * Read/write a PHY register; + * + * NOTE: The SMI register is shared among the three devices. + * Protection is provided by the global networking semaphore. + * If non-bsd drivers are running on a subset of IFs proper + * locking of all shared registers must be implemented! + */ +static unsigned +do_mii_read(int phy, unsigned addr) +{ +unsigned v; +unsigned wc = 0; + + addr &= 0x1f; + + /* wait until not busy */ + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_RD ); + + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + if (wc>0xffff) + wc = 0xffff; + return (wc<<16) | (v & 0xffff); +} + +unsigned +BSP_mve_mii_read(struct mveth_private *mp, unsigned addr) +{ +unsigned rval = do_mii_read(mp->phy, addr); +#ifdef MVETH_DEBUG + printk(DRVNAME": BSP_mve_mii_read(%d): 0x%08x\n", addr, rval); +#endif + return rval; +} + +unsigned +BSP_mve_mii_read_early(int port, unsigned addr) +{ + return do_mii_read(port2phy(port), addr); +} + +static unsigned +do_mii_write(int phy, unsigned addr, unsigned v) +{ +unsigned wc = 0; + + addr &= 0x1f; + v &= 0xffff; + + /* busywait is ugly but not preventing ISRs or high priority tasks from + * preempting us + */ + + /* wait until not busy */ + while ( MV643XX_ETH_SMI_BUSY & MV_READ(MV643XX_ETH_SMI_R) ) + wc++ /* wait */; + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (phy<<16) | MV643XX_ETH_SMI_OP_WR | v ); + + return wc; +} + +unsigned +BSP_mve_mii_write(struct mveth_private *mp, unsigned addr, unsigned v) +{ +#ifdef MVETH_DEBUG + printk(DRVNAME": BSP_mve_mii_write(%d): 0x%08x\n", addr, v); +#endif + return do_mii_write( mp->phy, addr, v ); +} + +unsigned +BSP_mve_mii_write_early(int port, unsigned addr, unsigned v) +{ + return do_mii_write(port2phy(port), addr, v); +} + + +/* MID-LAYER SUPPORT ROUTINES */ + +/* Start TX if descriptors are exhausted */ +static __inline__ void +mveth_start_tx(struct mveth_private *mp) +{ +uint32_t running; + if ( mp->avail <= 0 ) { + running = MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num)); + if ( ! (running & MV643XX_ETH_TX_START(0)) ) { + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + } + } +} + +/* Stop TX and wait for the command queues to stop and the fifo to drain */ +static uint32_t +mveth_stop_tx(int port) +{ +uint32_t active_q; + + active_q = (MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) & MV643XX_ETH_TX_ANY_RUNNING); + + if ( active_q ) { + /* Halt TX and wait for activity to stop */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port), MV643XX_ETH_TX_STOP_ALL); + while ( MV643XX_ETH_TX_ANY_RUNNING & MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) ) + /* poll-wait */; + /* Wait for Tx FIFO to drain */ + while ( ! (MV643XX_ETH_PORT_STATUS_R(port) & MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY) ) + /* poll-wait */; + } + + return active_q; +} + +void +BSP_mve_promisc_set(struct mveth_private *mp, int promisc) +{ +uint32_t v; + + v = MV_READ(MV643XX_ETH_PORT_CONFIG_R(mp->port_num)); + if ( (mp->promisc = promisc) ) + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + else + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), v); +} + +/* update serial port settings from current link status */ + +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; +uint32_t v = mp->promisc ? 0x01010101 : 0x00000000; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) { + MV_WRITE(s,v); + MV_WRITE(o,v); + s+=4; + o+=4; + } + for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i] = 0; + mp->mc_refcnt.other[i] = 0; + } +} + +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_MCAST_ENTRIES; i++) { + MV_WRITE(s,0x01010101); + MV_WRITE(o,0x01010101); + s+=4; + o+=4; + /* Not clear what we should do with the reference count. + * For now just increment it. + */ + for (i=0; i<sizeof(mp->mc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i]++; + mp->mc_refcnt.other[i]++; + } + } +} + +static void add_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( 0 == (*refcnt)[hash]++ ) { + val = MV_READ(off+slot) | ( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +static void del_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( (*refcnt)[hash] > 0 && 0 == --(*refcnt)[hash] ) { + val = MV_READ(off+slot) & ~( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + add_entry(tabl, hash, refcnt); +} + +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + del_entry(tabl, hash, refcnt); +} + +/* Clear all address filters (multi- and unicast) */ +static void +mveth_clear_addr_filters(struct mveth_private *mp) +{ +register int i; +register uint32_t u; + u = MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num); + for (i=0; i<MV643XX_ETH_NUM_UNICAST_ENTRIES; i++) { + MV_WRITE(u,0); + u+=4; + } + BSP_mve_mcast_filter_clear(mp); +} + +/* Setup unicast filter for a given MAC address (least significant nibble) */ +static void +mveth_ucfilter(struct mveth_private *mp, unsigned char mac_lsbyte, int accept) +{ +unsigned nib, slot, bit; +uint32_t val; + /* compute slot in table */ + nib = mac_lsbyte & 0xf; /* strip nibble */ + slot = nib & ~3; /* (nibble/4)*4 */ + bit = (nib & 3)<<3; /* 8*(nibble % 4) */ + val = MV_READ(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot); + if ( accept ) { + val |= 0x01 << bit; + } else { + val &= 0x0e << bit; + } + MV_WRITE(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot, val); +} + +#if defined( ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM ) && 0 +/* Currently unused; small unaligned buffers seem to be rare + * so we just use memcpy()... + */ + +/* memcpy for 0..7 bytes; arranged so that gcc + * optimizes for powerpc... + */ + +static inline void memcpy8(void *to, void *fr, unsigned x) +{ +register uint8_t *d = to, *s = fro; + + d+=l; s+=l; + if ( l & 1 ) { + *--d=*--s; + } + if ( l & 2 ) { + /* pre-decrementing causes gcc to use auto-decrementing + * PPC instructions (lhzu rx, -2(ry)) + */ + d-=2; s-=2; + /* use memcpy; don't cast to short -- accessing + * misaligned data as short is not portable + * (but it works on PPC). + */ + __builtin_memcpy(d,s,2); + } + if ( l & 4 ) { + d-=4; s-=4; + /* see above */ + __builtin_memcpy(d,s,4); + } +} +#endif + +static int +mveth_assign_desc_raw(MvEthTxDesc d, void *buf, int len, void *uptr, unsigned long extra) +{ +int rval = (d->byte_cnt = len); + +#ifdef MVETH_TESTING + assert( !d->u_buf ); + assert( len ); +#endif + + /* set CRC on all descriptors; seems to be necessary */ + d->cmd_sts = extra | (TDESC_GEN_CRC | TDESC_ZERO_PAD); + +#ifdef ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + /* The buffer must be 64bit aligned if the payload is <8 (??) */ + if ( rval < 8 && ( ((uintptr_t)buf) & 7) ) { + d->buf_ptr = CPUADDR2ENET( d->workaround ); + memcpy((void*)d->workaround, buf, rval); + } else +#endif + { + d->buf_ptr = CPUADDR2ENET( (unsigned long)buf ); + } + d->u_buf = uptr; + d->l4i_chk = 0; + return rval; +} + +/* + * Ring Initialization + * + * ENDIAN ASSUMPTION: DMA engine matches CPU endianness (???) + * + * Linux driver discriminates __LITTLE and __BIG endian for re-arranging + * the u16 fields in the descriptor structs. However, no endian conversion + * is done on the individual fields (SDMA byte swapping is disabled on LE). + */ + +STATIC int +mveth_init_rx_desc_ring(struct mveth_private *mp) +{ +int i,sz; +MvEthRxDesc d; +uintptr_t baddr; + + memset((void*)mp->rx_ring, 0, sizeof(*mp->rx_ring)*mp->rbuf_count); + + mp->rx_desc_dma = CPUADDR2ENET(mp->rx_ring); + + for ( i=0, d = mp->rx_ring; i<mp->rbuf_count; i++, d++ ) { + d->u_buf = mp->alloc_rxbuf(&sz, &baddr); + assert( d->u_buf ); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + + d->buf_size = sz; + d->byte_cnt = 0; + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + d->next = mp->rx_ring + (i+1) % mp->rbuf_count; + + d->buf_ptr = CPUADDR2ENET( baddr ); + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_rx_t = mp->rx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->rx_desc_dma); + + + return i; +} + +STATIC int +mveth_init_tx_desc_ring(struct mveth_private *mp) +{ +int i; +MvEthTxDesc d; + + memset((void*)mp->tx_ring, 0, sizeof(*mp->tx_ring)*mp->xbuf_count); + + /* DMA and CPU live in the same address space (rtems) */ + mp->tx_desc_dma = CPUADDR2ENET(mp->tx_ring); + mp->avail = TX_AVAILABLE_RING_SIZE(mp); + + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++,d++ ) { + d->l4i_chk = 0; + d->byte_cnt = 0; + d->cmd_sts = 0; + d->buf_ptr = 0; + + d->next = mp->tx_ring + (i+1) % mp->xbuf_count; + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_tx_h = mp->d_tx_t = mp->tx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->tx_desc_dma); + + return i; +} + +/* PUBLIC LOW-LEVEL DRIVER ACCESS */ + +struct mveth_private * +BSP_mve_create( + int unit, + rtems_id tid, + void (*isr)(void*isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +) +{ +struct mveth_private *mp; +int InstallISRSuccessful; + + if ( unit <= 0 || unit > MV643XXETH_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, MV643XXETH_NUM_DRIVER_SLOTS); + return 0; + } + + if ( rx_ring_size < 0 && tx_ring_size < 0 ) + return 0; + + if ( MV_64360 != BSP_getDiscoveryVersion(0) ) { + printk(DRVNAME": not mv64360 chip\n"); + return 0; + } + + if ( tx_ring_size < 1 ) { + printk(DRVNAME": tx ring size must not be zero (networking configuration issue?)\n"); + return 0; + } + + if ( rx_ring_size < 1 ) { + printk(DRVNAME": rx ring size must not be zero (networking configuration issue?)\n"); + return 0; + } + + mp = calloc( 1, sizeof *mp ); + + mp->port_num = unit-1; + mp->phy = port2phy(mp->port_num); + + mp->tid = tid; + mp->isr = isr; + mp->isr_arg = isr_arg; + + mp->cleanup_txbuf = cleanup_txbuf; + mp->cleanup_txbuf_arg = cleanup_txbuf_arg; + mp->alloc_rxbuf = alloc_rxbuf; + mp->consume_rxbuf = consume_rxbuf; + mp->consume_rxbuf_arg = consume_rxbuf_arg; + + mp->rbuf_count = rx_ring_size; + mp->xbuf_count = tx_ring_size; + + if ( mp->xbuf_count > 0 ) + mp->xbuf_count += TX_NUM_TAG_SLOTS; + + if ( mp->rbuf_count < 0 ) + mp->rbuf_count = 0; + if ( mp->xbuf_count < 0 ) + mp->xbuf_count = 0; + + /* allocate ring area; add 1 entry -- room for alignment */ + assert( !mp->ring_area ); + mp->ring_area = malloc( sizeof(*mp->ring_area) * (mp->rbuf_count + mp->xbuf_count + 1) ); + assert( mp->ring_area ); + + BSP_mve_stop_hw(mp); + + if ( irq_mask ) { + irq_data[mp->port_num].hdl = mveth_isr; + irq_data[mp->port_num].handle = (rtems_irq_hdl_param)mp; + InstallISRSuccessful = BSP_install_rtems_irq_handler( &irq_data[mp->port_num] ); + assert( InstallISRSuccessful ); + } + + if ( rx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_IRQ_RX_DONE; + if ( tx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_EXT_IRQ_TX_DONE; + + mp->irq_mask = (irq_mask & MV643XX_ETH_IRQ_RX_DONE); + if ( (irq_mask &= (MV643XX_ETH_EXT_IRQ_TX_DONE | MV643XX_ETH_EXT_IRQ_LINK_CHG)) ) { + mp->irq_mask |= MV643XX_ETH_IRQ_EXT_ENA; + mp->xirq_mask = irq_mask; + } else { + mp->xirq_mask = 0; + } + + return mp; +} + +void +BSP_mve_update_serial_port(struct mveth_private *mp, int media) +{ +int port = mp->port_num; +uint32_t old, new; + +#ifdef MVETH_DEBUG + printk(DRVNAME": Entering BSP_mve_update_serial_port()\n"); +#endif + + new = old = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(port)); + + /* mask speed and duplex settings */ + new &= ~( MV643XX_ETH_SET_GMII_SPEED_1000 + | MV643XX_ETH_SET_MII_SPEED_100 + | MV643XX_ETH_SET_FULL_DUPLEX ); + + if ( (MV643XX_MEDIA_FD & media) ) + new |= MV643XX_ETH_SET_FULL_DUPLEX; + + switch ( (media & MV643XX_MEDIA_SPEED_MSK) ) { + default: /* treat as 10 */ + break; + case MV643XX_MEDIA_100: + new |= MV643XX_ETH_SET_MII_SPEED_100; + break; + case MV643XX_MEDIA_1000: + new |= MV643XX_ETH_SET_GMII_SPEED_1000; + break; + } + + + if ( new != old ) { + if ( ! (MV643XX_ETH_SERIAL_PORT_ENBL & new) ) { + /* just write */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + } else { + uint32_t were_running; + + were_running = mveth_stop_tx(port); + + old &= ~MV643XX_ETH_SERIAL_PORT_ENBL; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), old); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + /* linux driver writes twice... */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + + if ( were_running ) { + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + } + } + } + /* If TX stalled because there was no buffer then whack it */ + mveth_start_tx(mp); +} + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp) +{ + return mp->tid; +} + +int +BSP_mve_detach(struct mveth_private *mp) +{ + BSP_mve_stop_hw(mp); + if ( mp->irq_mask || mp->xirq_mask ) { + if ( !BSP_remove_rtems_irq_handler( &irq_data[mp->port_num] ) ) + return -1; + } + free( (void*)mp->ring_area ); + memset(mp, 0, sizeof(*mp)); + __asm__ __volatile__("":::"memory"); + return 0; +} + +/* MAIN RX-TX ROUTINES + * + * BSP_mve_swipe_tx(): descriptor scavenger; releases mbufs + * BSP_mve_send_buf(): xfer mbufs from IF to chip + * BSP_mve_swipe_rx(): enqueue received mbufs to interface + * allocate new ones and yield them to the + * chip. + */ + +/* clean up the TX ring freeing up buffers */ +int +BSP_mve_swipe_tx(struct mveth_private *mp) +{ +int rval = 0; +register MvEthTxDesc d; + + for ( d = mp->d_tx_t; d->buf_ptr; d = NEXT_TXD(d) ) { + + INVAL_DESC(d); + + if ( (TDESC_DMA_OWNED & d->cmd_sts) + && (uint32_t)d == MV_READ(MV643XX_ETH_CURRENT_SERVED_TX_DESC(mp->port_num)) ) + break; + + /* d->u_buf is only set on the last descriptor in a chain; + * we only count errors in the last descriptor; + */ + if ( d->u_buf ) { + mp->cleanup_txbuf(d->u_buf, mp->cleanup_txbuf_arg, (d->cmd_sts & TDESC_ERROR) ? 1 : 0); + d->u_buf = 0; + } + + d->buf_ptr = 0; + + rval++; + } + mp->d_tx_t = d; + mp->avail += rval; + + return mp->avail; +} + +int +BSP_mve_send_buf_chain(struct mveth_private *mp, MveEthBufIterNext next, MveEthBufIter *it) +{ +int rval; +register MvEthTxDesc l,d,h; +int nmbs; +MveEthBufIter head = *it; + + rval = 0; + + /* if no descriptor is available; try to wipe the queue */ + if ( (mp->avail < 1) && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + /* Maybe TX is stalled and needs to be restarted */ + mveth_start_tx(mp); + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->u_buf ); +#endif + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second mbuf and fill the first descriptor + * last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + nmbs = 1; + while ( (it = next(it)) ) { + if ( 0 == it->len ) + continue; /* skip empty mbufs */ + + nmbs++; + + if ( mp->avail < 1 && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + /* Maybe TX was stalled - try to restart */ + mveth_start_tx(mp); + + /* not enough descriptors; cleanup... + * the first slot was never used, so we start + * at mp->d_tx_h->next; + */ + for ( l = NEXT_TXD(h); l!=d; l=NEXT_TXD(l) ) { +#ifdef MVETH_TESTING + assert( l->u_buf == 0 ); +#endif + l->buf_ptr = 0; + l->cmd_sts = 0; + mp->avail++; + } + mp->avail++; + if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) { + /* this chain will never fit into the ring */ + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + mp->stats.repack++; + /* caller may reorganize chain */ + return -2; + } + return -1; + } + + mp->avail--; + +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + + /* fill this slot */ + rval += mveth_assign_desc_raw(d, it->data, it->len, it->uptr, TDESC_DMA_OWNED); + + FLUSH_BUF( (uint32_t)it->data, it->len ); + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, head.data, head.len, head.uptr, TDESC_FRST); + + + FLUSH_BUF((uint32_t)head.data, head.len); + + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + h->cmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +int +BSP_mve_send_buf_raw( + struct mveth_private *mp, + void *head_p, + int h_len, + void *data_p, + int d_len) +{ +int rval; +register MvEthTxDesc l,d,h; +int needed; +void *frst_buf; +int frst_len; +void *uarg; + + rval = 0; + +#ifdef MVETH_TESTING + assert(head_p || data_p); +#endif + + needed = head_p && data_p ? 2 : 1; + + /* if no descriptor is available; try to wipe the queue */ + if ( ( mp->avail < needed ) + && ( MVETH_CLEAN_ON_SEND(mp) <= 0 || mp->avail < needed ) ) { + /* Maybe TX was stalled and needs a restart */ + mveth_start_tx(mp); + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->u_buf ); +#endif + + /* find the 'first' user buffer */ + if ( (frst_buf = head_p) && (h_len > 0) ) { + frst_len = h_len; + } else { + frst_buf = data_p; + frst_len = d_len; + } + + uarg = (head_p && ! h_len) ? head_p : frst_buf; + + /* Legacy: if h_len == 0 but head_p is not then use that for the user arg */ + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second (optional) slot and fill the first + * descriptor last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + if ( needed > 1 ) { + mp->avail--; +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + rval += mveth_assign_desc_raw(d, data_p, d_len, 0, TDESC_DMA_OWNED); + FLUSH_BUF( (uint32_t)data_p, d_len ); + d->u_buf = data_p; + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot with raw buffer - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, frst_buf, frst_len, 0, TDESC_FRST); + + FLUSH_BUF( (uint32_t)frst_buf, frst_len); + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + + /* first buffer of 'chain' goes into last desc */ + l->u_buf = uarg; + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + h->cmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( needed > mp->stats.maxchain ) + mp->stats.maxchain = needed; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +/* send received buffers upwards and replace them + * with freshly allocated ones; + * ASSUMPTION: buffer length NEVER changes and is set + * when the ring is initialized. + * TS 20060727: not sure if this assumption is still necessary - I believe it isn't. + */ + +int +BSP_mve_swipe_rx(struct mveth_private *mp) +{ +int rval = 0, err; +register MvEthRxDesc d; +void *newbuf; +int sz; +uintptr_t baddr; + + for ( d = mp->d_rx_t; ! (INVAL_DESC(d), (RDESC_DMA_OWNED & d->cmd_sts)); d=NEXT_RXD(d) ) { + +#ifdef MVETH_TESTING + assert(d->u_buf); +#endif + + err = (RDESC_ERROR & d->cmd_sts); + + if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) { + /* drop packet and recycle buffer */ + newbuf = d->u_buf; + mp->stats.idrops++; + } else { +#ifdef MVETH_TESTING + assert( d->byte_cnt > 0 ); +#endif + mp->consume_rxbuf(d->u_buf, mp->consume_rxbuf_arg, d->byte_cnt); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + d->u_buf = newbuf; + d->buf_ptr = CPUADDR2ENET(baddr); + d->buf_size = sz; + FLUSH_DESC(d); + } + + membarrier(); + + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + + FLUSH_DESC(d); + + rval++; + } + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + mp->d_rx_t = d; + return rval; +} + +/* Stop hardware and clean out the rings */ +void +BSP_mve_stop_hw(struct mveth_private *mp) +{ +MvEthTxDesc d; +MvEthRxDesc r; +int i; + + mveth_disable_irqs(mp, -1); + + mveth_stop_tx(mp->port_num); + + /* cleanup TX rings */ + if (mp->d_tx_t) { /* maybe ring isn't initialized yet */ + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) { + /* should be safe to clear ownership */ + d->cmd_sts &= ~TDESC_DMA_OWNED; + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + BSP_mve_swipe_tx(mp); + +#ifdef MVETH_TESTING + assert( mp->d_tx_h == mp->d_tx_t ); + for ( i=0, d=mp->tx_ring; i<mp->xbuf_count; i++, d++ ) { + assert( !d->buf_ptr ); + } +#endif + } + + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_STOP_ALL); + while ( MV643XX_ETH_RX_ANY_RUNNING & MV_READ(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num)) ) + /* poll-wait */; + + /* stop serial port */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)) + & ~( MV643XX_ETH_SERIAL_PORT_ENBL | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE | MV643XX_ETH_FORCE_LINK_PASS) + ); + + /* clear pending interrupts */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + + /* cleanup RX rings */ + if ( mp->rx_ring ) { + for ( i=0, r=mp->rx_ring; i<mp->rbuf_count; i++, r++ ) { + /* should be OK to clear ownership flag */ + r->cmd_sts = 0; + FLUSH_DESC(r); + mp->consume_rxbuf(r->u_buf, mp->consume_rxbuf_arg, 0); + r->u_buf = 0; + } + FLUSH_BARRIER(); + } + + +} + +uint32_t mveth_serial_ctrl_config_val = MVETH_SERIAL_CTRL_CONFIG_VAL; + +/* Fire up the low-level driver + * + * - make sure hardware is halted + * - enable cache snooping + * - clear address filters + * - clear mib counters + * - reset phy + * - initialize (or reinitialize) descriptor rings + * - check that the firmware has set up a reasonable mac address. + * - generate unicast filter entry for our mac address + * - write register config values to the chip + * - start hardware (serial port and SDMA) + */ + +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr, int media) +{ +int i; +uint32_t v; +static int inited = 0; + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Entering BSP_mve_init_hw()\n", mp->port_num+1); +#endif + + /* since enable/disable IRQ routine only operate on select bitsets + * we must make sure everything is masked initially. + */ + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), 0); + + BSP_mve_stop_hw(mp); + + memset(&mp->stats, 0, sizeof(mp->stats)); + + mp->promisc = promisc; + + /* MotLoad has cache snooping disabled on the ENET2MEM windows. + * Some comments in (linux) indicate that there are errata + * which cause problems which would be a real bummer. + * We try it anyways... + */ + if ( !inited ) { + unsigned long disbl, bar; + inited = 1; /* FIXME: non-thread safe lazy init */ + disbl = MV_READ(MV643XX_ETH_BAR_ENBL_R); + /* disable all 6 windows */ + MV_WRITE(MV643XX_ETH_BAR_ENBL_R, MV643XX_ETH_BAR_DISBL_ALL); + /* set WB snooping on enabled bars */ + for ( i=0; i<MV643XX_ETH_NUM_BARS*8; i+=8 ) { + if ( (bar = MV_READ(MV643XX_ETH_BAR_0 + i)) && MV_READ(MV643XX_ETH_SIZE_R_0 + i) ) { +#ifdef ENABLE_HW_SNOOPING + MV_WRITE(MV643XX_ETH_BAR_0 + i, bar | MV64360_ENET2MEM_SNOOP_WB); +#else + MV_WRITE(MV643XX_ETH_BAR_0 + i, bar & ~MV64360_ENET2MEM_SNOOP_MSK); +#endif + /* read back to flush fifo [linux comment] */ + (void)MV_READ(MV643XX_ETH_BAR_0 + i); + } + } + /* restore/re-enable */ + MV_WRITE(MV643XX_ETH_BAR_ENBL_R, disbl); + } + + mveth_clear_mib_counters(mp); + mveth_clear_addr_filters(mp); + +/* Just leave it alone... + reset_phy(); +*/ + + if ( mp->rbuf_count > 0 ) { + mp->rx_ring = (MvEthRxDesc)MV643XX_ALIGN(mp->ring_area, RING_ALIGNMENT); + mveth_init_rx_desc_ring(mp); + } + + if ( mp->xbuf_count > 0 ) { + mp->tx_ring = (MvEthTxDesc)mp->rx_ring + mp->rbuf_count; + mveth_init_tx_desc_ring(mp); + } + + if ( enaddr ) { + /* set ethernet address from arpcom struct */ +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Writing MAC addr ", mp->port_num+1); + for (i=5; i>=0; i--) { + printk("%02X%c", enaddr[i], i?':':'\n'); + } +#endif + mveth_write_eaddr(mp, enaddr); + } + + /* set mac address and unicast filter */ + + { + uint32_t machi, maclo; + maclo = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + machi = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* ASSUME: firmware has set the mac address for us + * - if assertion fails, we have to do more work... + */ + assert( maclo && machi && maclo != 0xffffffff && machi != 0xffffffff ); + mveth_ucfilter(mp, maclo&0xff, 1/* accept */); + } + + /* port, serial and sdma configuration */ + v = MVETH_PORT_CONFIG_VAL; + if ( promisc ) { + /* multicast filters were already set up to + * accept everything (mveth_clear_addr_filters()) + */ + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + } else { + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + } + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), + v); + MV_WRITE(MV643XX_ETH_PORT_CONFIG_XTEND_R(mp->port_num), + MVETH_PORT_XTEND_CONFIG_VAL); + + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + v &= ~(MVETH_SERIAL_CTRL_CONFIG_MSK); + v |= mveth_serial_ctrl_config_val; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), v); + + if ( (MV643XX_MEDIA_LINK & media) ) { + BSP_mve_update_serial_port(mp, media); + } + + /* enable serial port */ + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + v | MV643XX_ETH_SERIAL_PORT_ENBL); + +#ifndef __BIG_ENDIAN__ +#error "byte swapping needs to be disabled for little endian machines" +#endif + MV_WRITE(MV643XX_ETH_SDMA_CONFIG_R(mp->port_num), MVETH_SDMA_CONFIG_VAL); + + /* allow short frames */ + MV_WRITE(MV643XX_ETH_RX_MIN_FRAME_SIZE_R(mp->port_num), MVETH_MIN_FRAMSZ_CONFIG_VAL); + + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + /* TODO: set irq coalescing */ + + /* enable Rx */ + if ( mp->rbuf_count > 0 ) { + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + } + + mveth_enable_irqs(mp, -1); + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Leaving BSP_mve_init_hw()\n", mp->port_num+1); +#endif +} + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr) +{ +int i; +uint32_t x; +unsigned char buf[6], *eaddr; + + eaddr = oeaddr ? oeaddr : buf; + + eaddr += 5; + x = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + + /* lo word */ + for (i=2; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + x = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* hi word */ + for (i=4; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + if ( !oeaddr ) { + printf("%02X",buf[0]); + for (i=1; i<sizeof(buf); i++) + printf(":%02X",buf[i]); + printf("\n"); + } +} + +void +BSP_mve_enable_irqs(struct mveth_private *mp) +{ + mveth_enable_irqs(mp, -1); +} + +void +BSP_mve_disable_irqs(struct mveth_private *mp) +{ + mveth_disable_irqs(mp, -1); +} + +uint32_t +BSP_mve_ack_irqs(struct mveth_private *mp) +{ + return mveth_ack_irqs(mp, -1); +} + + +void +BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + mveth_enable_irqs(mp, mask); +} + +uint32_t +BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + return mveth_disable_irqs(mp, mask); +} + +uint32_t +BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask) +{ + return mveth_ack_irqs(mp, mask); +} + +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f) +{ +int p = mp->port_num; +int idx; +uint32_t v; + + if ( !f ) + f = stdout; + + fprintf(f, DRVNAME"%i Statistics:\n", mp->port_num + 1); + fprintf(f, " # IRQS: %i\n", mp->stats.irqs); + fprintf(f, " Max. mbuf chain length: %i\n", mp->stats.maxchain); + fprintf(f, " # repacketed: %i\n", mp->stats.repack); + fprintf(f, " # packets: %i\n", mp->stats.packet); + fprintf(f, " # buffer alloc failed: %i\n", mp->stats.idrops); + fprintf(f, "MIB Counters:\n"); + for ( idx = MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2; + idx < MV643XX_ETH_NUM_MIB_COUNTERS; + idx++ ) { + switch ( idx ) { + case MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2: + mp->stats.mib.good_octs_rcvd += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_rcvd); + idx++; + break; + + case MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO>>2: + mp->stats.mib.good_octs_sent += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_sent); + idx++; + break; + + default: + v = ((uint32_t*)&mp->stats.mib)[idx] += read_mib_counter(p, idx); + fprintf(f, mibfmt[idx], v); + break; + } + } + fprintf(f, "\n"); +} + +#ifdef MVETH_DEBUG +/* Display/dump descriptor rings */ + +/* These low-level routines need to be synchronized with + * any Tx/Rx threads! + */ +int +BSP_mve_dring_nonsync(struct mveth_private *mp) +{ +int i; +if (1) { +MvEthRxDesc pr; +printf("RX:\n"); + + for (i=0, pr=mp->rx_ring; i<mp->rbuf_count; i++, pr++) { + printf("cnt: 0x%04x, size: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n", + pr->byte_cnt, pr->buf_size, pr->cmd_sts, (uint32_t)pr->next_desc_ptr, pr->buf_ptr); + } +} +if (1) { +MvEthTxDesc pt; +printf("TX:\n"); + for (i=0, pt=mp->tx_ring; i<mp->xbuf_count; i++, pt++) { + printf("cnt: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x, mb: 0x%08x\n", + pt->byte_cnt, pt->cmd_sts, (uint32_t)pt->next_desc_ptr, pt->buf_ptr, + (uint32_t)pt->u_buf); + } +} + return 0; +} +#endif + +#endif /* LIBBSP_BEATNIK_BSP_H */ diff --git a/rtemsbsd/sys/dev/mve/if_mve_nexus.c b/rtemsbsd/sys/dev/mve/if_mve_nexus.c new file mode 100644 index 00000000..be9433da --- /dev/null +++ b/rtemsbsd/sys/dev/mve/if_mve_nexus.c @@ -0,0 +1,935 @@ +/* RTEMS driver for the mv643xx gigabit ethernet chip */ + +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm <mdharm@momenco.com> + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle <ralf@linux-mips.org> + * MontaVista Software, Inc., Dale Farnsworth <dale@farnsworth.org> + * Steven J. Hill <sjhill1@rockwellcollins.com>/<sjhill@realitydiluted.com> + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* Nexus port by Till Straumann, <till.straumann@psi.ch>, 3/2021 */ + +#include <machine/rtems-bsd-kernel-space.h> +#include <bsp.h> + +#ifdef LIBBSP_BEATNIK_BSP_H + +#include <sys/types.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/ethernet.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <rtems/bsd/local/miibus_if.h> +#include <stdio.h> +#include <bsp/mv643xx_eth.h> + +#define DRVNAME "mv63xx_nexus" + +#undef MVETH_DEBUG + +/* Define default ring sizes */ + +#undef MVETH_TESTING + +#ifdef MVETH_TESTING +/* hard and small defaults */ +#define MV643XX_RX_RING_SIZE 2 +#define MV643XX_TX_QUEUE_SIZE 4 +#define MV643XX_BD_PER_PACKET 1 +#define TX_LOWWATER 1 + +#else /* MVETH_TESTING */ + +#define MV643XX_RX_RING_SIZE 40 /* attached buffers are always 2k clusters, i.e., this + * driver - with a configured ring size of 40 - constantly + * locks 80k of cluster memory - your app config better + * provides enough space! + */ +#define MV643XX_TX_QUEUE_SIZE 40 +#define MV643XX_BD_PER_PACKET 10 +#define TX_LOWWATER (4*(MV643XX_BD_PER_PACKET)) +#endif /* MVETH_TESTING */ + +/* NOTE: tx ring size MUST be > max. # of fragments / mbufs in a chain; + * I observed chains of >17 entries regularly! + */ +#define MV643XX_TX_RING_SIZE ((MV643XX_TX_QUEUE_SIZE) * (MV643XX_BD_PER_PACKET)) + +/* The chip puts the ethernet header at offset 2 into the buffer so + * that the payload is aligned + */ +#define ETH_RX_OFFSET 2 +#define ETH_CRC_LEN 4 /* strip FCS at end of packet */ + +#ifndef __PPC__ +#error "Dont' know how to deal with cache on this CPU architecture" +#endif + +/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency + * management works for cache line sizes of 16 and 32 bytes only. If the line size + * is bigger, the descriptors could be padded... + */ +#if !defined(PPC_CACHE_ALIGNMENT) +#error "PPC_CACHE_ALIGNMENT not defined" +#elif PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32 +#error "Cache line size must be 16 or 32" +#else +#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT +#endif + +/* HELPER MACROS */ + +/* Align base to alignment 'a' */ +#define MV643XX_ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1))) + + +#define IRQ_EVENT RTEMS_EVENT_0 +#define TX_EVENT RTEMS_EVENT_1 + +/* Hacks -- FIXME */ +rtems_id +rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg); +#define SIO_RTEMS_SHOW_STATS _IO('i', 250) + +#define MVE643XX_DUMMY_PHY 0 /* phy is defined by low-level driver */ + +struct mve_enet_softc { + device_t dev; + struct ifnet *ifp; + device_t miibus; + struct mii_data *mii_softc; + struct mveth_private *mp; + struct mtx mtx; + struct callout wdCallout; + rtems_id daemonTid; + int oif_flags; +}; + +static struct mve_enet_softc * ifaces[MV643XXETH_NUM_DRIVER_SLOTS] = { 0 }; + +typedef struct MveMbufIter { + MveEthBufIter it; + struct mbuf *next; + struct mbuf *head; +} MveMbufIter; + +/* Forward Declarations */ +struct mve_enet_softc; + +static void +mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr); + +static int +mve_media_change(struct ifnet *ifp); + +static void +mve_set_filters(struct ifnet *ifp); + +static int +xlateMediaFlags(const struct mii_data *mid); + +static void +mve_ack_link_change(struct mve_enet_softc *sc); + +static __inline__ void +mve_send_event(struct mve_enet_softc *sc, rtems_event_set ev) +{ +rtems_status_code st; + if ( RTEMS_SUCCESSFUL != (st = rtems_event_send(sc->daemonTid, ev)) ) { + printk(DRVNAME": rtems_event_send returned 0x%08x (TID: 0x%08x, sc: 0x%08x)\n", st, sc->daemonTid, sc); + rtems_panic(DRVNAME": rtems_event_send() failed!\n"); + } +} + +static __inline__ void +mve_lock(struct mve_enet_softc *sc, const char *from) +{ + mtx_lock( & sc->mtx ); +/*printk("L V %s\n", from);*/ +} + +static __inline__ void +mve_unlock(struct mve_enet_softc *sc, const char *from) +{ +/*printk("L ^ %s\n", from);*/ + mtx_unlock( & sc->mtx ); +} + +static int +mve_probe(device_t dev) +{ + int unit = device_get_unit(dev); + int err; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_probe (entering)\n"); +#endif + + if ( unit >= 0 && unit < MV643XXETH_NUM_DRIVER_SLOTS ) { + err = BUS_PROBE_DEFAULT; + } else { + err = ENXIO; + } + + return err; +} + +/* + * starting at 'm' scan the buffer chain until we + * find a non-empty buffer (which we return) + */ +static __inline__ struct mbuf * +skipEmpty(struct mbuf *m) +{ + while ( m && ( 0 == m->m_len ) ) { + m = m->m_next; + } + return m; +} + +/* + * Record a buffer's info in the low-leve driver 'iterator' struct. + * Also scan ahead to find the next non-empty buffer (store it in + * the iterator's 'next' field). This info is needed because we + * want to know if 'this' buffer is the last (non-empty!) one + * in a chain. + * + * On entry 'it->next' identifies 'this' buffer and on return + * 'it->next' points to the next non-empty buffer. + */ +static MveEthBufIter * +nextBuf(MveEthBufIter *arg) +{ +MveMbufIter *it = (MveMbufIter*)arg; +struct mbuf *m; + /* If 'this' buffer is non-null */ + if ( (m = it->next) ) { + /* find next non-empty buffer */ + it->next = skipEmpty( m->m_next ); + /* record 'this' buffer's info */ + it->it.data = mtod(m, void*); + it->it.len = m->m_len; + /* if there is a non-empty buffer after 'this' uptr is NULL + * if this is tha last buffer in a chain then record the + * head of the chain in the uptr (for eventual cleanup + * by release_tx_mbuf()). + */ + it->it.uptr = it->next ? 0 : it->head; + return (MveEthBufIter*)it; + } + return 0; +} + +/* + * Initialize the iterator struct + */ +static MveEthBufIter * +initIter(MveMbufIter *it, struct mbuf *m) +{ + /* record the head of the chain */ + it->head = m; + /* initialize 'next' field to the first non-empty buffer. + * This may be NULL if the chain is entirely empty but + * that is handled correctly. + */ + it->next = skipEmpty( m ); + /* Fill with first buf info */ + return nextBuf( &it->it ); +} + +static int +mve_send_mbuf( struct mve_enet_softc *sc, struct mbuf *m_head ) +{ +MveMbufIter iter; +int rval; + + if ( ! m_head ) { + return 0; + } + + if ( ! initIter( &iter, m_head ) ) { + /* completely empty chain */ + m_freem( m_head ); + return 0; + } + + rval = BSP_mve_send_buf_chain( sc->mp, nextBuf, &iter.it ); + + return rval; +} + +static void +mve_isr(void *closure) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_isr; posting event to %x\n", sc->daemonTid); +#endif + BSP_mve_disable_irqs( sc->mp ); + mve_send_event( sc, IRQ_EVENT ); +} + +static void +mve_stop(struct mve_enet_softc *sc) +{ + BSP_mve_stop_hw( sc->mp ); + /* clear IF flags */ + if_setdrvflagbits(sc->ifp, 0, (IFF_DRV_OACTIVE | IFF_DRV_RUNNING)); +} + +static void +mve_set_filters(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +int iff = if_getflags(ifp); +struct ifmultiaddr *ifma; +unsigned char *lladdr; + + BSP_mve_promisc_set( sc->mp, !!(iff & IFF_PROMISC)); + + if ( iff & (IFF_PROMISC | IFF_ALLMULTI) ) { + BSP_mve_mcast_filter_accept_all(sc->mp); + } else { + BSP_mve_mcast_filter_clear( sc->mp ); + + if_maddr_rlock( ifp ); + + CK_STAILQ_FOREACH( ifma, &ifp->if_multiaddrs, ifma_link ) { + + if ( ifma->ifma_addr->sa_family != AF_LINK ) { + continue; + } + + lladdr = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); + + BSP_mve_mcast_filter_accept_add( sc->mp, lladdr ); + + } + + if_maddr_runlock( ifp ); + } +} + +/* Daemon task does all the 'interrupt' work */ +static void +mve_daemon(void *arg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) arg; +struct ifnet *ifp = sc->ifp; +rtems_event_set evs; +struct mbuf *m; +int avail; +int sndStat; +uint32_t irqstat; + +#ifdef MVETH_DEBUG + printk(DRVNAME": bsdnet mveth_daemon started\n"); +#endif + + mve_lock( sc, "daemon" ); + + for (;;) { + + mve_unlock( sc, "daemon" ); + if ( RTEMS_SUCCESSFUL != rtems_event_receive( (IRQ_EVENT | TX_EVENT), (RTEMS_WAIT | RTEMS_EVENT_ANY), RTEMS_NO_TIMEOUT, &evs ) ) { + rtems_panic(DRVNAME": rtems_event_receive() failed!\n"); + } + mve_lock( sc, "daemon" ); + +#ifdef MVETH_DEBUG + printk(DRVNAME": bsdnet mveth_daemon event received 0x%x\n", evs); +#endif + + if ( !(if_getflags(ifp) & IFF_UP) ) { + mve_stop(sc); + /* clear flag */ + if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING); + continue; + } + + if ( ! (if_getdrvflags(ifp) & IFF_DRV_RUNNING) ) { + /* event could have been pending at the time hw was stopped; + * just ignore... + */ + continue; + } + + if ( (evs & IRQ_EVENT) ) { + irqstat = BSP_mve_ack_irqs(sc->mp); + } else { + irqstat = 0; + } + + if ( (MV643XX_ETH_EXT_IRQ_LINK_CHG & irqstat) && sc->mii_softc ) { + /* phy status changed */ + mii_pollstat( sc->mii_softc ); + } + + /* free tx chain and send */ + if ( (evs & TX_EVENT) || (MV643XX_ETH_EXT_IRQ_TX_DONE & irqstat) ) { + while ( (avail = BSP_mve_swipe_tx( sc->mp )) > TX_LOWWATER ) { + IF_DEQUEUE( &ifp->if_snd, m ); + if ( ! m ) { + /* clear active bit */ + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + break; + } + sndStat = mve_send_mbuf( sc, m ); + if ( sndStat < 0 ) { + /* maybe not enough space right now; requeue and wait for next IRQ */ + IF_PREPEND( &ifp->if_snd, m ); + break; + } + } + } + if ( (MV643XX_ETH_IRQ_RX_DONE & irqstat) ) { + BSP_mve_swipe_rx(sc->mp); + } + + BSP_mve_enable_irqs(sc->mp); + } + + mve_unlock( sc, "daemon (xit)" ); +} + +static void +release_tx_mbuf(void *user_buf, void *closure, int error_on_tx_occurred) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +struct ifnet *ifp = sc->ifp; +struct mbuf *mb = (struct mbuf*)user_buf; + + if ( error_on_tx_occurred ) { + if_inc_counter( ifp, IFCOUNTER_OERRORS, 1 ); + } else { + if_inc_counter( ifp, IFCOUNTER_OPACKETS, 1 ); + if_inc_counter( ifp, IFCOUNTER_OBYTES, mb->m_pkthdr.len ); + } + m_freem(mb); +} + +static void * +alloc_rx_mbuf(int *p_size, uintptr_t *p_data) +{ +struct mbuf *m; +unsigned long l,o; + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + + if ( m ) { + + o = mtod(m, unsigned long); + l = MV643XX_ALIGN(o, RX_BUF_ALIGNMENT) - o; + + /* align start of buffer */ + m->m_data += l; + + /* reduced length */ + l = MCLBYTES - l; + + m->m_len = m->m_pkthdr.len = l; + *p_size = m->m_len; + *p_data = mtod(m, uintptr_t); + } + + return (void*) m; +} + + +static void +consume_rx_mbuf(void *user_buf, void *closure, int len) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)closure; +struct ifnet *ifp = sc->ifp; +struct mbuf *m = (struct mbuf*)user_buf; + + if ( len <= 0 ) { + if_inc_counter( ifp, IFCOUNTER_IQDROPS, 1 ); + if ( len < 0 ) { + if_inc_counter( ifp, IFCOUNTER_IERRORS, 1 ); + } + m_freem(m); + } else { + m->m_len = m->m_pkthdr.len = len - ETH_RX_OFFSET - ETH_CRC_LEN; + m->m_data += ETH_RX_OFFSET; + m->m_pkthdr.rcvif = ifp; + + if_inc_counter( ifp, IFCOUNTER_IPACKETS, 1 ); + if_inc_counter( ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len ); + + if (0) { + /* Low-level debugging */ + int i; + for (i=0; i<m->m_len; i++) { + if ( !(i&15) ) + printk("\n"); + printk("0x%02x ",mtod(m,char*)[i]); + } + printk("\n"); + } + + mve_unlock( sc, "rx_cleanup" ); + (*ifp->if_input)(ifp, m); + mve_lock( sc, "rx_cleanup" ); + } +} + +/* Translate IFFLAGS to low-level driver representation */ +static int +xlateMediaFlags(const struct mii_data *mid) +{ +int lowLevelFlags = 0; +int msk = IFM_AVALID | IFM_ACTIVE; + + if ( (mid->mii_media_status & msk) == msk ) { + lowLevelFlags |= MV643XX_MEDIA_LINK; + + if ( IFM_OPTIONS( mid->mii_media_active ) & IFM_FDX ) { + lowLevelFlags |= MV643XX_MEDIA_FD; + } + + switch ( IFM_ETHER_SUBTYPE_GET( mid->mii_media_active ) ) { + default: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: UNKNOWN SPEED\n"); +#endif + break; /* UNKNOWN -- FIXME */ + case IFM_10_T: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 10baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_10; + break; + case IFM_100_TX: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 100baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_100; + break; + case IFM_1000_T: +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: 1000baseT\n"); +#endif + lowLevelFlags |= MV643XX_MEDIA_1000; + break; + } + } else { +#ifdef MVETH_DEBUG + printk(DRVNAME"xlateMediaFlags: NO LINK\n"); +#endif + } + return lowLevelFlags; +} + +static void +mve_init(void *arg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*)arg; +struct ifnet *ifp = sc->ifp; +int lowLevelMediaStatus = 0; +int promisc; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_init (entering)\n"); +#endif + + if ( sc->mii_softc ) { + mii_pollstat( sc->mii_softc ); + lowLevelMediaStatus = xlateMediaFlags( sc->mii_softc ); + if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) { + if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); + } else { + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + } + } + + promisc = !! (if_getdrvflags(ifp) & IFF_PROMISC); + + BSP_mve_init_hw(sc->mp, promisc, if_getlladdr(ifp), lowLevelMediaStatus); + + /* if promiscuous then there is no need to change */ + if ( ! promisc ) { + mve_set_filters(ifp); + } + + + if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); +} + +static void +mve_start(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); + mve_lock( sc, "mve_start" ); + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + mve_unlock( sc, "mve_start" ); + mve_send_event( sc, TX_EVENT ); +} + +static int +mve_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct ifreq *ifr = (struct ifreq *)data; +int err = 0; +int f, df; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_ioctl (entering)\n"); +#endif + + mve_lock( sc, "mve_ioctl" ); + + switch ( cmd ) { + case SIOCSIFFLAGS: + f = if_getflags( ifp ); + df = if_getdrvflags( ifp ); + if ( (f & IFF_UP) ) { + if ( ! ( df & IFF_DRV_RUNNING ) ) { + mve_init( (void*)sc ); + } else { + if ( (f & IFF_PROMISC) != (sc->oif_flags & IFF_PROMISC) ) { + mve_set_filters(ifp); + } + /* FIXME: other flag changes are ignored/unimplemented */ + } + } else { + if ( df & IFF_DRV_RUNNING ) { + mve_stop(sc); + } + } + sc->oif_flags = f; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if ( sc->mii_softc ) { + err = ifmedia_ioctl( ifp, ifr, &sc->mii_softc->mii_media, cmd ); + } else { + err = EINVAL; + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if ( if_getdrvflags( ifp ) & IFF_DRV_RUNNING ) { + mve_set_filters(ifp); + } + break; + + case SIO_RTEMS_SHOW_STATS: + BSP_mve_dump_stats(sc->mp, stdout); + break; + + default: + err = ether_ioctl(ifp, cmd, data); + break; + } + + mve_unlock( sc, "mve_ioctl" ); + + return err; +} + +/* SIO RTEMS_SHOW_STATS is too cumbersome to use -- for debugging, provide direct hack */ +int +mv643xx_nexus_dump_stats(int unit, FILE *f) +{ + if ( unit < 0 || unit >= MV643XXETH_NUM_DRIVER_SLOTS || ! ifaces[unit] ) + return -EINVAL; + if ( ! f ) + f = stdout; + BSP_mve_dump_stats(ifaces[unit]->mp, f); + return 0; +} + +/* + * Used to update speed settings in the hardware + * when the phy setup changes. + * + * ASSUME: caller holds lock + */ +static void +mve_ack_link_change(struct mve_enet_softc *sc) +{ +struct mii_data *mii = sc->mii_softc; +int lowLevelMediaStatus; + + if ( !mii ) + return; + + lowLevelMediaStatus = xlateMediaFlags( mii ); + + if ( (lowLevelMediaStatus & MV643XX_MEDIA_LINK) ) { + BSP_mve_update_serial_port( sc->mp, lowLevelMediaStatus ); + if_setdrvflagbits( sc->ifp, 0, IFF_DRV_OACTIVE ); + mve_start( sc->ifp ); + } else { + if_setdrvflagbits( sc->ifp, IFF_DRV_OACTIVE, 0 ); + } +} + +/* Callback from ifmedia_ioctl() + * + * Caller probably holds the lock already but + * since it is recursive we may as well make sure + * in case there are other possible execution paths. + */ +static int +mve_media_change(struct ifnet *ifp) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct mii_data *mii = sc->mii_softc; +int err; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_media_change\n"); +#endif + + if ( ! mii ) { + return ENXIO; + } + + err = mii_mediachg( mii ); + + return err; +} + +static void +mve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) if_getsoftc( ifp ); +struct mii_data *mii = sc->mii_softc; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_media_status\n"); +#endif + + if ( mii ) { + mii_pollstat( mii ); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + } +} + +static int +mve_attach(device_t dev) +{ +struct mve_enet_softc *sc; +struct ifnet *ifp; +uint8_t hwaddr[ETHER_ADDR_LEN]; +struct mveth_private *mp; +int unit = device_get_unit(dev); +int tx_ring_size = MV643XX_TX_RING_SIZE; +int rx_ring_size = MV643XX_RX_RING_SIZE; +int tx_q_size = MV643XX_TX_QUEUE_SIZE; + + sc = device_get_softc( dev ); + sc->dev = dev; + sc->ifp = ifp = if_alloc(IFT_ETHER); + sc->daemonTid = 0; + sc->mii_softc = 0; + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_attach (entering)\n"); +#endif + + mtx_init( &sc->mtx, device_get_nameunit( sc->dev ), MTX_NETWORK_LOCK, MTX_RECURSE ); + callout_init_mtx( &sc->wdCallout, &sc->mtx, 0 ); + + if_setsoftc ( ifp, sc ); + if_initname ( ifp, device_get_name(dev), unit); + if_setinitfn ( ifp, mve_init ); + if_setioctlfn ( ifp, mve_ioctl ); + if_setstartfn ( ifp, mve_start ); + if_setflags ( ifp, (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX) ); + sc->oif_flags = if_getflags( ifp ); + + if_setsendqlen ( ifp, tx_q_size ); + if_setsendqready( ifp ); + + mp = BSP_mve_create( + unit + 1, /* low-level driver' unit numbers are 1-based */ + 0, + mve_isr, (void*)sc, + release_tx_mbuf, (void*)sc, + alloc_rx_mbuf, + consume_rx_mbuf, (void*)sc, + rx_ring_size, + tx_ring_size, + ( MV643XX_ETH_IRQ_RX_DONE + | MV643XX_ETH_EXT_IRQ_TX_DONE + | MV643XX_ETH_EXT_IRQ_LINK_CHG)); + + if ( ! mp ) { + rtems_panic("Unable to create mv643xx low-level driver"); + } + + sc->mp = mp; + + BSP_mve_read_eaddr( mp, hwaddr ); + + if ( 0 == mii_attach( sc->dev, + &sc->miibus, + ifp, + mve_media_change, + mve_media_status, + BMSR_DEFCAPMASK, + MVE643XX_DUMMY_PHY, + MII_OFFSET_ANY, + 0 ) ) { + sc->mii_softc = device_get_softc( sc->miibus ); + } + + sc->daemonTid = rtems_bsdnet_newproc("MVE", 4096, mve_daemon, (void*)sc); + + ether_ifattach( ifp, hwaddr ); + +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_attach (leaving)\n"); +#endif + + ifaces[unit] = sc; + + return 0; +} + +static int +mve_miibus_read_reg(device_t dev, int phy, int reg) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); + + /* low-level driver knows what phy to use; ignore arg */ + return (int) BSP_mve_mii_read( sc->mp, reg ); +} + +static int +mve_miibus_write_reg(device_t dev, int phy, int reg, int val) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); + + /* low-level driver knows what phy to use; ignore arg */ + BSP_mve_mii_write( sc->mp, reg, val ); + return 0; +} + +static void +mve_miibus_statchg(device_t dev) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_miibus_statchg\n"); +#endif + /* assume this ends up being called either from the ioctl or the driver + * task -- either of which holds the lock. + */ + mve_ack_link_change( sc ); +} + +static void +mve_miibus_linkchg(device_t dev) +{ +struct mve_enet_softc *sc = (struct mve_enet_softc*) device_get_softc(dev); +#ifdef MVETH_DEBUG + printk(DRVNAME": mve_miibus_linkchg\n"); +#endif + /* assume this ends up being called either from the ioctl or the driver + * task -- either of which holds the lock. + */ + mve_ack_link_change( sc ); +} + + +static device_method_t mve_methods[] = { + DEVMETHOD(device_probe, mve_probe ), + DEVMETHOD(device_attach, mve_attach), + + DEVMETHOD(miibus_readreg, mve_miibus_read_reg ), + DEVMETHOD(miibus_writereg, mve_miibus_write_reg), + DEVMETHOD(miibus_statchg , mve_miibus_statchg ), + DEVMETHOD(miibus_linkchg , mve_miibus_linkchg ), + + DEVMETHOD_END +}; + +static driver_t mve_nexus_driver = { + "mve", + mve_methods, + sizeof( struct mve_enet_softc ) +}; + +static devclass_t mve_devclass; + +DRIVER_MODULE(mve, nexus, mve_nexus_driver, mve_devclass, 0, 0); +DRIVER_MODULE(miibus, mve, miibus_driver, miibus_devclass, 0, 0); + +MODULE_DEPEND(mve, nexus, 1, 1, 1); +MODULE_DEPEND(mve, ether, 1, 1, 1); + +#endif /* LIBBSP_BEATNIK_BSP_H */ diff --git a/rtemsbsd/sys/dev/nvd/nvd.c b/rtemsbsd/sys/dev/nvd/nvd.c index 897b0af6..4a8e8ac1 100644 --- a/rtemsbsd/sys/dev/nvd/nvd.c +++ b/rtemsbsd/sys/dev/nvd/nvd.c @@ -43,6 +43,7 @@ #include <stdatomic.h> #include <rtems/blkdev.h> +#include <rtems/thread.h> #define NVD_STR "nvd" @@ -164,10 +165,9 @@ nvd_completion(void *arg, const struct nvme_completion *status) static int nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req, - uint32_t media_blocks_per_block) + uint32_t media_block_size) { uint32_t i; - uint32_t lb_count; uint32_t bufnum; BSD_ASSERT(req->req == RTEMS_BLKDEV_REQ_READ || @@ -178,13 +178,15 @@ nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req, req->status = RTEMS_SUCCESSFUL; bufnum = req->bufnum; req->bufnum |= bufnum << NVD_BUFNUM_SHIFT; - lb_count = media_blocks_per_block * ndisk->lb_per_media_block; for (i = 0; i < bufnum; ++i) { rtems_blkdev_sg_buffer *sg; + uint32_t lb_count; int error; sg = &req->bufs[i]; + lb_count = (sg->length / media_block_size) * + ndisk->lb_per_media_block; if (req->req == RTEMS_BLKDEV_REQ_READ) { error = nvme_ns_cmd_read(ndisk->ns, sg->buffer, @@ -208,28 +210,27 @@ nvd_request(struct nvd_disk *ndisk, rtems_blkdev_request *req, static void nvd_sync_completion(void *arg, const struct nvme_completion *status) { - rtems_status_code sc; - - if (nvme_completion_is_error(status)) { - sc = RTEMS_IO_ERROR; - } else { - sc = RTEMS_SUCCESSFUL; - } + rtems_binary_semaphore *sync; - rtems_blkdev_request_done(arg, sc); + sync = arg; + rtems_binary_semaphore_post(sync); } static int -nvd_sync(struct nvd_disk *ndisk, rtems_blkdev_request *req) +nvd_sync(struct nvd_disk *ndisk) { + rtems_binary_semaphore sync; int error; - error = nvme_ns_cmd_flush(ndisk->ns, nvd_sync_completion, req); - if (error != 0) { - rtems_blkdev_request_done(req, RTEMS_NO_MEMORY); + rtems_binary_semaphore_init(&sync, "nvd sync"); + + error = nvme_ns_cmd_flush(ndisk->ns, nvd_sync_completion, &sync); + if (error == 0) { + rtems_binary_semaphore_wait(&sync); } - return (0); + rtems_binary_semaphore_destroy(&sync); + return (error); } static int @@ -240,11 +241,11 @@ nvd_ioctl(rtems_disk_device *dd, uint32_t req, void *arg) ndisk = rtems_disk_get_driver_data(dd); if (req == RTEMS_BLKIO_REQUEST) { - return (nvd_request(ndisk, arg, dd->media_blocks_per_block)); + return (nvd_request(ndisk, arg, dd->media_block_size)); } if (req == RTEMS_BLKDEV_REQ_SYNC) { - return (nvd_sync(ndisk, arg)); + return (nvd_sync(ndisk)); } if (req == RTEMS_BLKIO_CAPABILITIES) { diff --git a/rtemsbsd/sys/dev/stmac/if_stmac.c b/rtemsbsd/sys/dev/stmac/if_stmac.c new file mode 100644 index 00000000..7e3e07c7 --- /dev/null +++ b/rtemsbsd/sys/dev/stmac/if_stmac.c @@ -0,0 +1,1009 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 embedded brains Gmb_h (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <machine/rtems-bsd-kernel-space.h> + +#include <bsp.h> + +#ifdef LIBBSP_ARM_STM32H7_BSP_H + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/gsb_crc32.h> + +#include <net/if.h> +#include <net/ethernet.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <machine/bus.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <rtems/bsd/local/miibus_if.h> + +#include <stm32h7xx_hal.h> + +#include <rtems/bsd/bsd.h> +#include <rtems/irq-extension.h> +#include <rtems/score/armv7m.h> + +#define RX_DESC_COUNT 64 +#define TX_DESC_COUNT 256 + +#define STMAC_LOCK(sc) mtx_lock(&(sc)->mtx) +#define STMAC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) + +#define STMAC_TXLOCK(sc) mtx_lock(&(sc)->tx_mtx) +#define STMAC_TXUNLOCK(sc) mtx_unlock(&(sc)->tx_mtx) + +struct stmac_desc { + __IO uint32_t DESC0; + __IO uint32_t DESC1; + __IO uint32_t DESC2; + __IO uint32_t DESC3; + struct mbuf *m; +}; + +struct stmac_softc { + ETH_HandleTypeDef heth; + uint8_t mac_addr[6]; + struct ifnet *ifp; + struct mtx mtx; + device_t miibus; + struct mtx mii_mtx; + struct mii_data *mii_softc; + u_int mii_media_active; + u_int mii_media_status; + struct callout tick_callout; + int if_flags; + + /* RX */ + struct stmac_desc *rx_desc_ring; + uint32_t rx_idx; + bool rx_do_alloc; + + /* TX */ + struct mtx tx_mtx; + struct stmac_desc *tx_desc_ring; + uint32_t tx_idx_head; + uint32_t tx_idx_tail; +}; + +static void stmac_init_locked(struct stmac_softc *); +static void stmac_stop_locked(struct stmac_softc *); + +struct mbuf * +stmac_new_mbuf(struct ifnet *ifp) +{ + struct mbuf *m; + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m != NULL) { + m->m_data = mtod(m, char *) + ETHER_ALIGN; + m->m_pkthdr.rcvif = ifp; + rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len); + } + + return m; +} + +static void +stmac_tick(void *arg) +{ + struct stmac_softc *sc; + struct ifnet *ifp; + + sc = arg; + ifp = sc->ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + return; + } + + mii_tick(sc->mii_softc); + callout_reset(&sc->tick_callout, hz, stmac_tick, sc); +} + +static uint32_t +stmac_rx_desc3(uint32_t idx) +{ + + if (idx % 16 != 0) { + return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V); + } + + return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V | + ETH_DMARXNDESCRF_IOC); +} + +static void +stmac_rx_setup_desc(struct stmac_softc *sc) +{ + uint32_t idx; + bool do_alloc; + ETH_TypeDef *regs; + + do_alloc = sc->rx_do_alloc; + sc->rx_do_alloc = false; + sc->rx_idx = 0; + + for (idx = 0; idx < RX_DESC_COUNT; ++idx) { + struct stmac_desc *desc; + struct mbuf *m; + + desc = &sc->rx_desc_ring[idx]; + + if (do_alloc) { + m = stmac_new_mbuf(sc->ifp); + BSD_ASSERT(m != NULL); + desc->m = m; + } else { + m = desc->m; + } + + WRITE_REG(desc->DESC0, mtod(m, uint32_t)); + WRITE_REG(desc->DESC1, 0x0); + WRITE_REG(desc->DESC2, 0x0); + WRITE_REG(desc->DESC3, stmac_rx_desc3(idx)); + } + + regs = sc->heth.Instance; + WRITE_REG(regs->DMACRDRLR, RX_DESC_COUNT -1); + WRITE_REG(regs->DMACRDLAR, (uint32_t)&sc->rx_desc_ring[0]); + WRITE_REG(regs->DMACRDTPR, + (uint32_t)&sc->rx_desc_ring[RX_DESC_COUNT - 1]); +} + +static void +stmac_tx_setup_desc(struct stmac_softc *sc) +{ + uint32_t idx; + ETH_TypeDef *regs; + + sc->tx_idx_head = 0; + sc->tx_idx_tail = 0; + + for (idx = 0; idx < TX_DESC_COUNT; ++idx) { + struct stmac_desc *desc; + + desc = &sc->tx_desc_ring[idx]; + WRITE_REG(desc->DESC0, 0x0); + WRITE_REG(desc->DESC1, 0x0); + WRITE_REG(desc->DESC2, 0x0); + WRITE_REG(desc->DESC3, 0x0); + desc->m = NULL; + } + + regs = sc->heth.Instance; + WRITE_REG(regs->DMACTDRLR, TX_DESC_COUNT -1); + WRITE_REG(regs->DMACTDLAR, (uint32_t)&sc->tx_desc_ring[0]); + WRITE_REG(regs->DMACTDTPR, (uint32_t)&sc->tx_desc_ring[0]); +} + +static void +stmac_init_locked(struct stmac_softc *sc) +{ + struct ifnet *ifp; + ETH_TypeDef *regs; + + ifp = sc->ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + return; + } + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + stmac_rx_setup_desc(sc); + stmac_tx_setup_desc(sc); + + HAL_ETH_Start(&sc->heth); + regs = sc->heth.Instance; + + /* Enable interrupts */ + SET_BIT(regs->DMACIER, ETH_DMACIER_NIE | ETH_DMACIER_RIE | + ETH_DMACIER_FBEE | ETH_DMACIER_AIE); + + mii_mediachg(sc->mii_softc); + callout_reset(&sc->tick_callout, hz, stmac_tick, sc); +} + +static void +stmac_tx_reclaim_all(struct stmac_softc *sc) +{ + uint32_t idx; + + for (idx = 0; idx < TX_DESC_COUNT; ++idx) { + struct stmac_desc *desc; + struct mbuf *m; + + desc = &sc->tx_desc_ring[idx]; + m = desc->m; + desc->m = NULL; + m_freem(m); + } +} + +static void +stmac_stop_locked(struct stmac_softc *sc) +{ + struct ifnet *ifp; + ETH_TypeDef *regs; + rtems_interrupt_server_entry server_entry; + rtems_status_code status; + rtems_name name; + uint64_t t0; + + ifp = sc->ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + return; + } + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + regs = sc->heth.Instance; + + /* Disable interrupts */ + WRITE_REG(regs->DMACIER, 0); + + /* Disable the DMA transmission and reception */ + CLEAR_BIT(regs->DMACTCR, ETH_DMACTCR_ST); + CLEAR_BIT(regs->DMACRCR, ETH_DMACRCR_SR); + + /* Wait for DMA done */ + t0 = rtems_clock_get_uptime_nanoseconds(); + while ((regs->MTLTQDR & (ETH_MTLTQDR_TXQSTS | + ETH_MTLTQDR_TRCSTS)) != 0 || + (regs->MTLRQDR & (ETH_MTLRQDR_RXQSTS | + ETH_MTLRQDR_PRXQ)) != 0) { + if (rtems_clock_get_uptime_nanoseconds() - t0 > 500000) { + break; + } + } + + /* Disable the MAC reception and transmission */ + CLEAR_BIT(regs->MACCR, ETH_MACCR_RE | ETH_MACCR_TE); + + /* Make sure no receive interrupt is in progress */ + status = rtems_object_get_classic_name(rtems_task_self(), &name); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + if (name != rtems_build_name('I', 'R', 'Q', 'S')) { + status = rtems_interrupt_server_entry_initialize( + RTEMS_INTERRUPT_SERVER_DEFAULT, &server_entry); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + rtems_interrupt_server_entry_destroy(&server_entry); + } + + stmac_tx_reclaim_all(sc); +} + +static uint8_t +stmac_bitreverse(uint8_t x) +{ + static const uint8_t nibbletab[] = { + 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 + }; + + return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); +} + +static uint64_t +stmac_hash_bit(const uint8_t *eaddr) +{ + uint32_t crc; + + /* + * Thanks to the excellent documentation from STM it was easy to figure + * this out. + */ + crc = 0; + crc = crc32_raw(eaddr, ETHER_ADDR_LEN, ~crc); + crc = stmac_bitreverse((uint8_t)~crc); + return ((uint64_t)1 << (crc >> 2)); +} + +static void +stmac_rx_setup_filter(struct stmac_softc *sc) +{ + struct ifmultiaddr *ifma; + struct ifnet *ifp; + ETH_TypeDef *regs; + uint32_t macpfr; + uint64_t machtr; + const uint8_t *eaddr; + + ifp = sc->ifp; + + macpfr = ETH_MACPFR_HMC; + if ((ifp->if_flags & IFF_PROMISC) != 0) { + macpfr |= ETH_MACPFR_PR; + } + + machtr = 0; + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + machtr = ~machtr; + } else { + if_maddr_rlock(ifp); + CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) { + continue; + } + + eaddr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); + machtr |= stmac_hash_bit(eaddr); + } + if_maddr_runlock(ifp); + } + + regs = sc->heth.Instance; + eaddr = IF_LLADDR(ifp); + regs->MACA0LR = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | + (eaddr[3] << 24); + regs->MACA0HR = eaddr[4] | (eaddr[5] << 8); + regs->MACHT0R = (uint32_t)machtr; + regs->MACHT1R = (uint32_t)(machtr >> 32); + regs->MACPFR = macpfr; +} + +static int +stmac_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ + struct stmac_softc *sc; + int error; + struct mii_data *mii; + + sc = ifp->if_softc; + + error = 0; + switch (cmd) { + case SIOCSIFFLAGS: + STMAC_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if ((ifp->if_flags ^ sc->if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) + stmac_rx_setup_filter(sc); + } else { + stmac_init_locked(sc); + } + } else { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + STMAC_TXLOCK(sc); + stmac_stop_locked(sc); + STMAC_TXUNLOCK(sc); + } + } + sc->if_flags = ifp->if_flags; + STMAC_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + STMAC_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + stmac_rx_setup_filter(sc); + } + STMAC_UNLOCK(sc); + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + mii = sc->mii_softc; + if (mii != NULL) { + error = ifmedia_ioctl(ifp, (struct ifreq *)data, &mii->mii_media, cmd); + } else { + error = ether_ioctl(ifp, cmd, data); + } + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +static void +stmac_init(void *arg) +{ + struct stmac_softc *sc; + + sc = arg; + STMAC_LOCK(sc); + STMAC_TXLOCK(sc); + stmac_init_locked(sc); + STMAC_TXUNLOCK(sc); + STMAC_UNLOCK(sc); +} + +static void +stmac_media_status(struct ifnet *ifp, struct ifmediareq *ifmr_p) +{ + struct stmac_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + STMAC_LOCK(sc); + mii = sc->mii_softc; + if (mii != NULL) { + mii_pollstat(mii); + ifmr_p->ifm_active = mii->mii_media_active; + ifmr_p->ifm_status = mii->mii_media_status; + } + STMAC_UNLOCK(sc); +} + +static int +stmac_media_change(struct ifnet *ifp) +{ + struct stmac_softc *sc; + int error; + + sc = ifp->if_softc; + + STMAC_LOCK(sc); + if (sc->mii_softc != NULL) { + error = mii_mediachg(sc->mii_softc); + } else { + error = ENXIO; + } + STMAC_UNLOCK(sc); + return (error); +} + +static int +stmac_probe(device_t dev) +{ + + device_set_desc(dev, "STM MAC"); + return (BUS_PROBE_DEFAULT); +} + +static void +stmac_rx_checksum(struct mbuf *m, uint32_t desc1) +{ + + if ((desc1 & (ETH_DMARXNDESCWBF_IPCE | ETH_DMARXNDESCWBF_IPCB | + ETH_DMARXNDESCWBF_IPHE)) == 0) { + uint32_t pt; + + pt = desc1 & ETH_DMARXNDESCWBF_PT; + if (pt == ETH_DMARXNDESCWBF_PT_UDP || + pt == ETH_DMARXNDESCWBF_PT_TCP) { + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | + CSUM_IP_VALID | + CSUM_DATA_VALID | + CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } else if ((desc1 & ETH_DMARXNDESCWBF_IPV4) != 0) { + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | + CSUM_IP_VALID; + m->m_pkthdr.csum_data = 0xffff; + } + } +} + +static void +stmac_rx_interrupt(struct stmac_softc *sc, ETH_TypeDef *regs) +{ + struct ifnet *ifp; + uint32_t idx; + struct stmac_desc *desc_ring; + struct stmac_desc *desc; + + ifp = sc->ifp; + desc_ring = sc->rx_desc_ring; + idx = sc->rx_idx; + desc = &desc_ring[idx]; + + while (true) { + uint32_t desc3; + struct mbuf *m; + + /* + * Make sure the interrupt is cleared and no longer pending + * before we read the descriptor status. + */ + regs->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS; + regs->DMACSR; + _ARMV7M_NVIC_Clear_pending(ETH_IRQn); + + desc3 = desc->DESC3; + if ((desc3 & ETH_DMARXNDESCRF_OWN) != 0) { + break; + } + + m = stmac_new_mbuf(ifp); + if (m != NULL) { + uint32_t mask; + uint32_t set; + uint32_t len; + + mask = ETH_DMARXNDESCWBF_CTXT | ETH_DMARXNDESCWBF_FD | + ETH_DMARXNDESCWBF_LD | ETH_DMARXNDESCWBF_ES; + set = ETH_DMARXNDESCWBF_FD | ETH_DMARXNDESCWBF_LD; + len = desc3 & ETH_DMARXNDESCWBF_PL; + if ((desc3 & mask) == set && len > ETHER_CRC_LEN) { + struct mbuf *rx; + + rx = desc->m; + stmac_rx_checksum(rx, desc->DESC1); + rx->m_len = len; + rx->m_pkthdr.len = len; + (*ifp->if_input)(ifp, rx); + } else { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + m = desc->m; + } + } else { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + m = desc->m; + } + + WRITE_REG(desc->DESC0, mtod(m, uint32_t)); + WRITE_REG(desc->DESC1, 0x0); + WRITE_REG(desc->DESC2, 0x0); + WRITE_REG(desc->DESC3, stmac_rx_desc3(idx)); + desc->m = m; + + _ARM_Data_synchronization_barrier(); + WRITE_REG(regs->DMACRDTPR, (uint32_t)desc); + + idx = (idx + 1) % RX_DESC_COUNT; + desc = &desc_ring[idx]; + } + + sc->rx_idx = idx; +} + +static void +stmac_reset(struct stmac_softc *sc) +{ + HAL_StatusTypeDef hal_status; + ETH_TypeDef *regs; + + hal_status = HAL_ETH_Init(&sc->heth); + BSD_ASSERT(hal_status == HAL_OK); + + regs = sc->heth.Instance; + + /* Set descriptor skip length according to struct stmac_desc */ + MODIFY_REG(regs->DMACCR, ETH_DMACCR_DSL, ETH_DMACCR_DSL_32BIT); + + /* + * FIXME: Just use a random value. It is not clear which clock is used + * here. + */ + regs->DMACRIWTR = 0x80; +} + +static void +stmac_interrupt(void *arg) +{ + struct stmac_softc *sc; + ETH_TypeDef *regs; + uint32_t dmacsr; + + sc = arg; + regs = sc->heth.Instance; + dmacsr = regs->DMACSR; + + /* This is almost always a receive interrupt */ + stmac_rx_interrupt(sc, regs); + + if (__predict_false((dmacsr & ETH_DMACSR_FBE) != 0)) { + regs->DMACSR = ETH_DMACSR_FBE | ETH_DMACSR_AIS; + + STMAC_LOCK(sc); + STMAC_TXLOCK(sc); + stmac_stop_locked(sc); + stmac_reset(sc); + stmac_init_locked(sc); + STMAC_TXUNLOCK(sc); + STMAC_UNLOCK(sc); + } +} + +static void +stmac_tx_reclaim(struct stmac_softc *sc, struct ifnet *ifp) +{ + uint32_t head_idx; + uint32_t tail_idx; + struct stmac_desc *desc_ring; + + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + desc_ring = sc->tx_desc_ring; + + while (head_idx != tail_idx) { + struct stmac_desc *tail_desc; + uint32_t desc3; + + tail_desc = &desc_ring[tail_idx]; + + desc3 = tail_desc->DESC3; + if ((desc3 & ETH_DMATXNDESCWBF_OWN) != 0) { + break; + } + + if ((desc3 & ETH_DMATXNDESCWBF_LD) != 0) { + struct mbuf *m; + ift_counter cnt; + + if ((desc3 & ETH_DMATXNDESCWBF_ES) == 0) { + cnt = IFCOUNTER_OPACKETS; + } else { + cnt = IFCOUNTER_OERRORS; + } + + if_inc_counter(ifp, cnt, 1); + + m = tail_desc->m; + tail_desc->m = NULL; + m_freem(m); + } + + tail_idx = (tail_idx + 1) % TX_DESC_COUNT; + } + + sc->tx_idx_tail = tail_idx; +} + +static void +stmac_cache_flush(uintptr_t begin, uintptr_t size) +{ + uintptr_t end; + uintptr_t mask; + + /* Align begin and end of the data to a cache line */ + end = begin + size; + mask = CPU_CACHE_LINE_BYTES - 1; + begin &= ~mask; + end = (end + mask) & ~mask; + rtems_cache_flush_multiple_data_lines((void *)begin, end - begin); +} + +static int +stmac_tx_enqueue(struct stmac_softc *sc, struct ifnet *ifp, struct mbuf *m) +{ + uint32_t head_idx; + uint32_t tail_idx; + uint32_t capacity; + uint32_t new_head_idx; + uint32_t idx; + struct stmac_desc *desc_ring; + struct stmac_desc *desc; + ETH_TypeDef *regs; + uint32_t bufs; + uint32_t desc2; + uint32_t desc3; + struct mbuf *n; + int csum_flags; + + head_idx = sc->tx_idx_head; + tail_idx = sc->tx_idx_tail; + capacity = 2 * ((tail_idx - head_idx - 1) % TX_DESC_COUNT); + + idx = head_idx; + desc_ring = sc->tx_desc_ring; + desc2 = 0; + bufs = 0; + n = m; + + do { + uintptr_t size; + + desc = &desc_ring[idx]; + + size = (uintptr_t)n->m_len; + if (__predict_true(size > 0)) { + uintptr_t begin; + + ++bufs; + if (__predict_false(bufs > capacity)) { + return (ENOBUFS); + } + + begin = mtod(n, uintptr_t); + + if (bufs % 2 == 1) { + desc->DESC0 = begin; + desc2 = size; + } else { + desc->DESC1 = begin; + desc->DESC2 = (size << 16) | desc2; + idx = (idx + 1) % TX_DESC_COUNT; + } + + stmac_cache_flush(begin, size); + } + + n = n->m_next; + } while (n != NULL); + + if (bufs % 2 == 1) { + desc->DESC1 = 0; + desc->DESC2 = desc2; + idx = (idx + 1) % TX_DESC_COUNT; + } + + new_head_idx = idx; + sc->tx_idx_head = new_head_idx; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &desc_ring[idx]; + desc->m = m; + + desc3 = ETH_DMATXNDESCRF_OWN | ETH_DMATXNDESCRF_LD | m->m_pkthdr.len; + csum_flags = m->m_pkthdr.csum_flags; + if ((csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | + CSUM_UDP_IPV6)) != 0) { + desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_PAYLOAD_INSERT_PHDR_CALC; + } else if ((csum_flags & CSUM_IP) != 0) { + desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_INSERT; + } + + while (idx != head_idx) { + desc->DESC3 = desc3; + desc3 &= ~ETH_DMATXNDESCRF_LD; + + idx = (idx - 1) % TX_DESC_COUNT; + desc = &desc_ring[idx]; + } + + desc->DESC3 = ETH_DMATXNDESCRF_FD | desc3; + _ARM_Data_synchronization_barrier(); + regs = sc->heth.Instance; + WRITE_REG(regs->DMACTDTPR, (uint32_t)&desc_ring[new_head_idx]); + return (0); +} + +static int +stmac_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct stmac_softc *sc; + int error; + + sc = ifp->if_softc; + STMAC_TXLOCK(sc); + + error = stmac_tx_enqueue(sc, ifp, m); + stmac_tx_reclaim(sc, ifp); + + if (__predict_false(error != 0)) { + error = stmac_tx_enqueue(sc, ifp, m); + if (error != 0) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + } + } + + STMAC_TXUNLOCK(sc); + return (error); +} + +static int +stmac_transmit_no_link(struct ifnet *ifp, struct mbuf *m) +{ + + (void)ifp; + (void)m; + return (ENETDOWN); +} + +static void +stmac_qflush(struct ifnet *ifp) +{ + + (void)ifp; +} + +static int +stmac_attach(device_t dev) +{ + struct stmac_softc *sc; + struct ifnet *ifp; + int error; + struct stmac_desc *descs; + ETH_TypeDef *regs; + rtems_status_code status; + + sc = device_get_softc(dev); + + sc->rx_do_alloc = true; + mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); + mtx_init(&sc->tx_mtx, "stmac tx", MTX_NETWORK_LOCK, MTX_DEF); + callout_init_mtx(&sc->tick_callout, &sc->mtx, 0); + + sc->ifp = ifp = if_alloc(IFT_ETHER); + ifp->if_softc = sc; + + regs = ETH; + sc->heth.Instance = regs; + + descs = rtems_cache_coherent_allocate(sizeof(*descs) * + (RX_DESC_COUNT + TX_DESC_COUNT), 4, 0); + BSD_ASSERT(descs != NULL); + + sc->tx_desc_ring = &descs[0]; + sc->rx_desc_ring = &descs[TX_DESC_COUNT]; + + rtems_bsd_get_mac_address(device_get_name(dev), device_get_unit(dev), + &sc->mac_addr[0]); + + sc->heth.Init.MACAddr = &sc->mac_addr[0]; + sc->heth.Init.MediaInterface = HAL_ETH_RMII_MODE; + sc->heth.Init.RxBuffLen = 1524; + + stmac_reset(sc); + + if_initname(ifp, "stm", device_get_unit(dev)); + ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; + ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6; + ifp->if_capenable = ifp->if_capabilities; + ifp->if_hwassist = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6; + ifp->if_transmit = stmac_transmit_no_link; + ifp->if_qflush = stmac_qflush; + ifp->if_ioctl = stmac_ioctl; + ifp->if_init = stmac_init; + IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); + ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; + IFQ_SET_READY(&ifp->if_snd); + + error = mii_attach(dev, &sc->miibus, ifp, stmac_media_change, + stmac_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, + MII_OFFSET_ANY, 0); + if (error == 0) { + sc->mii_softc = device_get_softc(sc->miibus); + } + + ether_ifattach(ifp, &sc->heth.Init.MACAddr[0]); + + status = rtems_interrupt_server_handler_install( + RTEMS_INTERRUPT_SERVER_DEFAULT, ETH_IRQn, "stmac", + RTEMS_INTERRUPT_SHARED, stmac_interrupt, sc); + BSD_ASSERT(status == RTEMS_SUCCESSFUL); + return (0); +} + +static int +stmac_miibus_read(device_t dev, int phy, int reg) +{ + struct stmac_softc *sc; + uint32_t val; + HAL_StatusTypeDef hal_status; + + sc = device_get_softc(dev); + hal_status = HAL_ETH_ReadPHYRegister(&sc->heth, (uint32_t)phy, + (uint32_t)reg, &val); + if (hal_status != HAL_OK) { + return (-1); + } + + return ((int)val); +} + +static int +stmac_miibus_write(device_t dev, int phy, int reg, int val) +{ + struct stmac_softc *sc; + HAL_StatusTypeDef hal_status; + + sc = device_get_softc(dev); + hal_status = HAL_ETH_WritePHYRegister(&sc->heth, (uint32_t)phy, + (uint32_t)reg, (uint32_t)val); + if (hal_status != HAL_OK) { + return (-1); + } + + return (0); +} + +static void +stmac_miibus_statchg(device_t dev) +{ + struct stmac_softc *sc; + struct mii_data *mii; + u_int active; + u_int status; + ETH_TypeDef *regs; + uint32_t maccr; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->miibus); + active = mii->mii_media_active; + status = mii->mii_media_status; + + if (sc->mii_media_active == active && sc->mii_media_status == status) { + return; + } + + sc->mii_media_active = active; + sc->mii_media_status = status; + + regs = sc->heth.Instance; + maccr = regs->MACCR; + + if ((status & IFM_ACTIVE) != 0) { + if_settransmitfn(sc->ifp, stmac_transmit); + + if ((IFM_OPTIONS(active) & IFM_FDX) != 0) { + maccr |= ETH_MACCR_DM; + } else { + maccr &= ~ETH_MACCR_DM; + } + + if (IFM_SUBTYPE(active) != IFM_10_T) { + maccr |= ETH_MACCR_FES; + } else { + maccr &= ~ETH_MACCR_FES; + } + } else { + maccr |= ETH_MACCR_DM | ETH_MACCR_FES; + } + + regs->MACCR = maccr; +} + +static device_method_t stmac_methods[] = { + DEVMETHOD(device_probe, stmac_probe), + DEVMETHOD(device_attach, stmac_attach), + DEVMETHOD(miibus_readreg, stmac_miibus_read), + DEVMETHOD(miibus_writereg, stmac_miibus_write), + DEVMETHOD(miibus_statchg, stmac_miibus_statchg), + DEVMETHOD_END +}; + +driver_t stmac_driver = { + "stmac", + stmac_methods, + sizeof(struct stmac_softc) +}; + +static devclass_t stmac_devclass; + +DRIVER_MODULE(stmac, nexus, stmac_driver, stmac_devclass, 0, 0); +DRIVER_MODULE(miibus, stmac, miibus_driver, miibus_devclass, 0, 0); + +MODULE_DEPEND(stmac, ether, 1, 1, 1); +MODULE_DEPEND(stmac, miibus, 1, 1, 1); + +#endif /* LIBBSP_ARM_STM32H7_BSP_H */ diff --git a/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c b/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c new file mode 100644 index 00000000..243470ca --- /dev/null +++ b/rtemsbsd/sys/dev/usb/controller/dwc_otg_nexus.c @@ -0,0 +1,166 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <machine/rtems-bsd-kernel-space.h> + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/rman.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> + +#include <dev/usb/controller/dwc_otg.h> + +static int dwc_otg_detach(device_t); + +static int +dwc_otg_probe(device_t dev) +{ + + device_set_desc(dev, "DWC OTG 2.0 integrated USB controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +dwc_otg_attach(device_t dev) +{ + struct dwc_otg_softc *sc; + int err; + int rid; + + sc = device_get_softc(dev); + + /* initialise some bus fields */ + sc->sc_bus.parent = dev; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = DWC_OTG_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + sc->sc_mode = DWC_MODE_HOST; + sc->sc_phy_type = DWC_OTG_PHY_INTERNAL; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + return (ENOMEM); + } + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->sc_io_res == NULL) { + goto error; + } + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + goto error; + } + + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (sc->sc_bus.bdev == NULL) { + goto error; + } + + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + dwc_otg_platform_init(sc); + + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, + &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + sc->sc_intr_hdl = NULL; + goto error; + } + err = dwc_otg_init(sc); + if (err == 0) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + goto error; + } + + + return (0); + +error: + dwc_otg_detach(dev); + return (ENXIO); +} + +static int +dwc_otg_detach(device_t dev) +{ + + (void)dev; + BSD_ASSERT(0); + return (0); +} + +static device_method_t dwc_otg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dwc_otg_probe), + DEVMETHOD(device_attach, dwc_otg_attach), + DEVMETHOD(device_detach, dwc_otg_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +driver_t dwc_otg_driver = { + .name = "dwcotg", + .methods = dwc_otg_methods, + .size = sizeof(struct dwc_otg_softc), +}; + +static devclass_t dwc_otg_devclass; + +DRIVER_MODULE(dwcotg, nexus, dwc_otg_driver, dwc_otg_devclass, 0, 0); +MODULE_DEPEND(dwcotg, usb, 1, 1, 1); diff --git a/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c b/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c new file mode 100644 index 00000000..464c9ba4 --- /dev/null +++ b/rtemsbsd/sys/dev/usb/controller/dwc_otg_stm32h7.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <machine/rtems-bsd-kernel-space.h> + +#include <bsp.h> + +#ifdef LIBBSP_ARM_STM32H7_BSP_H + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> + +#include <dev/usb/controller/dwc_otg.h> + +#include <stm32h7/hal.h> + +static const stm32h7_gpio_config dwc_otg_pins_dm_dp = { + .regs = GPIOA, + .config = { + .Pin = GPIO_PIN_11 | GPIO_PIN_12, + .Mode = GPIO_MODE_AF_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_VERY_HIGH, + .Alternate = GPIO_AF10_OTG2_FS + } +}; + +static const stm32h7_gpio_config dwc_otg_pin_id = { + .regs = GPIOA, + .config = { + .Pin = GPIO_PIN_10, + .Mode = GPIO_MODE_AF_OD, + .Pull = GPIO_PULLUP, + .Alternate = GPIO_AF10_OTG2_FS + } +}; + +void +dwc_otg_platform_init(struct dwc_otg_softc *sc) +{ + + (void)sc; + stm32h7_gpio_init(&dwc_otg_pins_dm_dp); + stm32h7_gpio_init(&dwc_otg_pin_id); + stm32h7_clk_enable(STM32H7_MODULE_USB2_OTG); + stm32h7_clk_low_power_disable(STM32H7_MODULE_USB2_OTG_ULPI); + HAL_PWREx_EnableUSBVoltageDetector(); +} + +#endif /* LIBBSP_ARM_STM32H7_BSP_H */ diff --git a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c index c7c09bb4..d02bba98 100755 --- a/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c +++ b/rtemsbsd/sys/dev/usb/controller/ohci_lpc32xx.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include <dev/usb/controller/ohci.h> #include <dev/usb/controller/ohcireg.h> +#include <arm/lpc/probe.h> #include <arm/lpc/lpcreg.h> #include <arm/lpc/lpcvar.h> @@ -90,7 +91,7 @@ __FBSDID("$FreeBSD$"); while ((lpc_otg_read_4(_sc, _sreg) & _value) != _value); \ } while (0); -static int lpc_ohci_probe(device_t dev); +static int lpc_ohci_do_probe(device_t dev); static int lpc_ohci_attach(device_t dev); static int lpc_ohci_detach(device_t dev); @@ -107,12 +108,20 @@ static int lpc_otg_i2c_wait_for_transaction_done(struct ohci_softc *sc); static int lpc_otg_i2c_read(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t *value); static int lpc_otg_i2c_write(const struct usb_otg_transceiver *self, uint8_t reg_addr, uint8_t value); +__weak_symbol int +lpc_ohci_probe(int unit) +{ + + (void)unit; + return (BUS_PROBE_DEFAULT); +} + static int -lpc_ohci_probe(device_t dev) +lpc_ohci_do_probe(device_t dev) { device_set_desc(dev, "LPC32x0 USB OHCI controller"); - return (BUS_PROBE_DEFAULT); + return (lpc_ohci_probe(device_get_unit(dev))); } static int @@ -473,7 +482,7 @@ lpc_ohci_resume(device_t dev) static device_method_t lpc_ohci_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, lpc_ohci_probe), + DEVMETHOD(device_probe, lpc_ohci_do_probe), DEVMETHOD(device_attach, lpc_ohci_attach), DEVMETHOD(device_detach, lpc_ohci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), diff --git a/rtemsbsd/sys/net/if_ppp.c b/rtemsbsd/sys/net/if_ppp.c index 709f13e0..e134dc76 100644 --- a/rtemsbsd/sys/net/if_ppp.c +++ b/rtemsbsd/sys/net/if_ppp.c @@ -313,11 +313,12 @@ static rtems_task ppp_txdaemon(rtems_task_argument arg) frag=0; /* initialize output values */ - sc->sc_outfcs = PPP_INITFCS; - sc->sc_outbuf = (u_char *)0; - sc->sc_outlen = (short )0; - sc->sc_outoff = (short )0; - sc->sc_outfcslen = (short )0; + sc->sc_outfcs = PPP_INITFCS; + sc->sc_outbuf = (u_char *)0; + sc->sc_outlen = (short )0; + sc->sc_outoff = (short )0; + sc->sc_outoff_update = false; + sc->sc_outfcslen = (short )0; /* printf("Start Transmit Packet..\n"); */ diff --git a/rtemsbsd/sys/net/if_pppvar.h b/rtemsbsd/sys/net/if_pppvar.h index fdfb56df..bd11bcbc 100644 --- a/rtemsbsd/sys/net/if_pppvar.h +++ b/rtemsbsd/sys/net/if_pppvar.h @@ -117,6 +117,7 @@ struct ppp_softc { struct ifqueue sc_freeq; /* free packets */ short sc_outoff; /* output packet byte offset */ + bool sc_outoff_update; /* outoff needs update in pppstart */ short sc_outflag; /* output status flag */ short sc_outlen; /* length of output packet */ short sc_outfcslen; /* length of output fcs data */ diff --git a/rtemsbsd/sys/net/ppp_tty.c b/rtemsbsd/sys/net/ppp_tty.c index 80d4fee1..2e850dc7 100644 --- a/rtemsbsd/sys/net/ppp_tty.c +++ b/rtemsbsd/sys/net/ppp_tty.c @@ -124,7 +124,7 @@ int pppread(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args); int pppwrite(struct rtems_termios_tty *tty, rtems_libio_rw_args_t *rw_args); int ppptioctl(struct rtems_termios_tty *tty, rtems_libio_ioctl_args_t *args); int pppinput(int c, struct rtems_termios_tty *tty); -int pppstart(struct rtems_termios_tty *tp); +int pppstart(struct rtems_termios_tty *tp, int len); u_short pppfcs(u_short fcs, u_char *cp, int len); void pppallocmbuf(struct ppp_softc *sc, struct mbuf **mp); @@ -557,7 +557,7 @@ pppasyncctlp( * Called at spltty or higher. */ int -pppstart(struct rtems_termios_tty *tp) +pppstart(struct rtems_termios_tty *tp, int len) { u_char *sendBegin; u_long ioffset = (u_long )0; @@ -567,6 +567,13 @@ pppstart(struct rtems_termios_tty *tp) /* ensure input is valid and we are busy */ if (( sc != NULL ) && ( sc->sc_outflag & SC_TX_BUSY )) { + /* Adapt offsets if necessary */ + if ( sc->sc_outoff_update ) { + sc->sc_stats.ppp_obytes += len; + sc->sc_outoff += len; + sc->sc_outoff_update = false; + } + /* check to see if we need to get the next buffer */ /* Ready with PPP_FLAG Character ? */ @@ -644,8 +651,25 @@ pppstart(struct rtems_termios_tty *tp) /* write out the character(s) and update the stats */ (*tp->handler.write)(ctx, (char *)sendBegin, (ioffset > 0) ? ioffset : 1); - sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1; - sc->sc_outoff += ioffset; + /* + * In case of polled drivers, everything is sent here. So adapt the + * offsets. In case of interrupt or task driven drivers, we don't know + * whether all characters have been sent. We only get feedback via + * rtems_termios_dequeue_characters() function which is the one that is + * calling us. + */ + if (tp->handler.mode == TERMIOS_POLLED) { + sc->sc_stats.ppp_obytes += (ioffset > 0) ? ioffset : 1; + sc->sc_outoff += ioffset; + sc->sc_outoff_update = false; + } else { + if (ioffset > 0) { + sc->sc_outoff_update = true; + } else { + sc->sc_outoff_update = false; + sc->sc_stats.ppp_obytes += 1; + } + } return (0); } diff --git a/testsuite/dhcpcd01/test_main.c b/testsuite/dhcpcd01/test_main.c index c1d97a3f..b982bfc9 100644 --- a/testsuite/dhcpcd01/test_main.c +++ b/testsuite/dhcpcd01/test_main.c @@ -44,6 +44,7 @@ #include <rtems/dhcpcd.h> #define TEST_NAME "LIBBSD DHCPCD 1" +#define TEST_STATE_USER_INPUT 1 static void dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env) @@ -67,9 +68,7 @@ test_main(void) { rtems_dhcpcd_add_hook(&dhcpcd_hook); - - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/dhcpcd02/test_main.c b/testsuite/dhcpcd02/test_main.c index 221b096f..cbfd0846 100644 --- a/testsuite/dhcpcd02/test_main.c +++ b/testsuite/dhcpcd02/test_main.c @@ -40,12 +40,13 @@ #include <rtems.h> #define TEST_NAME "LIBBSD DHCPCD 2" +#define TEST_STATE_USER_INPUT 1 static void test_main(void) { - rtems_task_delete(RTEMS_SELF); - assert(0); + + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/evdev01/init.c b/testsuite/evdev01/init.c index fe588ff4..5a8b0beb 100644 --- a/testsuite/evdev01/init.c +++ b/testsuite/evdev01/init.c @@ -341,7 +341,7 @@ evdev_scan_task(rtems_task_argument arg) } } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -401,7 +401,7 @@ err: } } ktask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -484,7 +484,7 @@ err: } } mtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -561,7 +561,7 @@ err: } } ttask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/foobarclient/test_main.c b/testsuite/foobarclient/test_main.c index d55b55c6..71b4774d 100644 --- a/testsuite/foobarclient/test_main.c +++ b/testsuite/foobarclient/test_main.c @@ -272,8 +272,7 @@ test_main(void) foobar_register(&question); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/ftpd01/test_main.c b/testsuite/ftpd01/test_main.c index 7ec17b96..cc7e8a00 100644 --- a/testsuite/ftpd01/test_main.c +++ b/testsuite/ftpd01/test_main.c @@ -79,8 +79,7 @@ test_main(void) rv = rtems_initialize_ftpd(); assert(rv == 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE diff --git a/testsuite/include/rtems/bsd/test/default-network-init.h b/testsuite/include/rtems/bsd/test/default-network-init.h index 3afa20fa..b367d956 100644 --- a/testsuite/include/rtems/bsd/test/default-network-init.h +++ b/testsuite/include/rtems/bsd/test/default-network-init.h @@ -255,7 +255,11 @@ Init(rtems_task_argument arg) #define RTEMS_BSD_CONFIG_NET_PF_UNIX #define RTEMS_BSD_CONFIG_NET_IP_MROUTE + +#ifdef RTEMS_BSD_MODULE_NETINET6 #define RTEMS_BSD_CONFIG_NET_IP6_MROUTE +#endif + #define RTEMS_BSD_CONFIG_NET_IF_BRIDGE #define RTEMS_BSD_CONFIG_NET_IF_LAGG #define RTEMS_BSD_CONFIG_NET_IF_VLAN diff --git a/testsuite/loopback01/test_main.c b/testsuite/loopback01/test_main.c index 1b5c0064..51d5e5b2 100644 --- a/testsuite/loopback01/test_main.c +++ b/testsuite/loopback01/test_main.c @@ -133,7 +133,7 @@ static rtems_task workerTask(rtems_task_argument arg) if (close(s) < 0) printf("Can't close worker task socket: %s\n", strerror(errno)); printf("Worker task terminating.\n"); - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } /* @@ -230,7 +230,7 @@ static rtems_task clientTask(rtems_task_argument arg) clientWorker(arg); sendClientEventToMasterTask(arg); printf("Client task terminating.\n"); - rtems_task_delete( RTEMS_SELF ); + rtems_task_exit(); } /* diff --git a/testsuite/mcast01/test_main.c b/testsuite/mcast01/test_main.c new file mode 100644 index 00000000..ad6c45b7 --- /dev/null +++ b/testsuite/mcast01/test_main.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @brief Example code to test multicasts. + * + * ping 224.0.0.1 + * + * Linux: + * + * echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts + * echo 1 > /proc/sys/net/ipv4/ip_forward + * ip route add 224.0.0.0/4 dev eth0 + * echo "Hello, world!" > /dev/udp/239.1.2.3/1234 + */ + +/* + * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <machine/rtems-bsd-commands.h> + +#include <rtems/console.h> +#include <rtems/shell.h> +#include <rtems/dhcpcd.h> + +#define TEST_NAME "LIBBSD MCAST 1" +#define TEST_STATE_USER_INPUT 1 + +#define MCAST_PORT 1234 + +#define MAX_PACKET_SIZE UINT16_MAX + +static const char mcast_addr[] = "239.1.2.3"; + +static const char hello[] = "Hello, world!"; + +typedef struct { + in_addr_t addr; + char name[]; +} iface_binding; + +static void +set_mcast_route(char *iface_name) +{ + static char net[] = "224.0.0.0/4"; + int exit_code; + char *route[] = { + "route", + "add", + "-net", + net, + "-iface", + iface_name, + NULL + }; + + exit_code = rtems_bsd_command_route(RTEMS_BSD_ARGC(route), route); + assert(exit_code == EXIT_SUCCESS); +} + +static void +mcast_rx_task(rtems_task_argument arg) +{ + iface_binding *ifb; + struct ip_mreq mreq; + int sd; + struct sockaddr_in addr; + int rv; + char addr_buf[INET_ADDRSTRLEN]; + const char *ip; + ssize_t n; + + ifb = (iface_binding *)arg; + set_mcast_route(ifb->name); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ifb->addr; + + ip = inet_ntop(AF_INET, &addr.sin_addr, addr_buf, sizeof(addr_buf)); + printf("mcast: join %s using interface IP %s\n", mcast_addr, ip); + + sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + assert(sd >= 0); + + addr.sin_port = htons(MCAST_PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + rv = bind(sd, (const struct sockaddr *)&addr, sizeof(addr)); + assert(rv == 0); + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = inet_addr(mcast_addr); + mreq.imr_interface.s_addr = ifb->addr; + + rv = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + assert(rv == 0); + + addr.sin_family = AF_INET; + addr.sin_port = htons(MCAST_PORT); + addr.sin_addr.s_addr = inet_addr(mcast_addr); + n = sendto(sd, hello, sizeof(hello), 0, + (const struct sockaddr *)&addr, sizeof(addr)); + + if (n != (ssize_t)sizeof(hello)) { + perror("send"); + } + + while (true) { + socklen_t addr_len; + char buf[MAX_PACKET_SIZE]; + + addr_len = sizeof(addr); + n = recvfrom(sd, buf, sizeof(buf), 0, + (struct sockaddr *)&addr, &addr_len); + if (n >= 0 && addr.sin_addr.s_addr != ifb->addr) { + size_t m; + ip = inet_ntop(AF_INET, &addr.sin_addr, addr_buf, + sizeof(addr_buf)); + printf("mcast: received %zi bytes from %s\n", n, ip); + + addr.sin_port = htons(MCAST_PORT); + addr.sin_addr.s_addr = inet_addr(mcast_addr); + m = (size_t)n; + n = sendto(sd, buf, m, 0, + (const struct sockaddr *)&addr, + sizeof(addr)); + assert(n == (ssize_t)m); + } else if (n < 0 ) { + perror("recvfrom"); + } + } +} + +static void +start_mcast_rx(const char *iface_ip, const char *iface_name) +{ + rtems_status_code sc; + rtems_id id; + iface_binding *ifb; + size_t len; + + len = strlen(iface_name) + 1; + ifb = malloc(sizeof(*ifb) + len); + assert(ifb != NULL); + ifb->addr = inet_addr(iface_ip); + memcpy(ifb->name, iface_name, len); + + sc = rtems_task_create(rtems_build_name('M', 'C', 'R', 'X'), + RTEMS_MINIMUM_PRIORITY, RTEMS_MINIMUM_STACK_SIZE + MAX_PACKET_SIZE, + RTEMS_DEFAULT_MODES, RTEMS_FLOATING_POINT, &id); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start(id, mcast_rx_task, (rtems_task_argument)ifb); + assert(sc == RTEMS_SUCCESSFUL); +} + +static const char * +get_value(char *const *env, const char *key) +{ + size_t len; + + len = strlen(key); + + while (true) { + const char *s; + + s = *env; + + if (s == NULL) { + return ""; + } + + if (strncmp(key, s, len) == 0 && s[len] == '=') { + return &s[len + 1]; + } + + ++env; + } +} + +static void +dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env) +{ + + (void)hook; + + if (strcmp(get_value(env, "reason"), "BOUND") == 0) { + start_mcast_rx(get_value(env, "new_ip_address"), + get_value(env, "interface")); + } +} + +static rtems_dhcpcd_hook dhcpcd_hook = { + .name = "mcast", + .handler = dhcpcd_hook_handler +}; + +static void +test_main(void) +{ + rtems_status_code sc; + int enable; + int rv; + + rtems_dhcpcd_add_hook(&dhcpcd_hook); + + enable = 1; + rv = sysctlbyname("net.inet.icmp.bmcastecho", NULL, NULL, &enable, + sizeof(enable)); + assert(rv == 0); + + sc = rtems_shell_init("SHLL", 16 * 1024, 1, CONSOLE_DEVICE_NAME, + false, true, NULL); + assert(sc == RTEMS_SUCCESSFUL); + + exit(0); +} + +#define DEFAULT_NETWORK_DHCPCD_ENABLE + +#include <rtems/bsd/test/default-network-init.h> + +#define CONFIGURE_SHELL_COMMANDS_INIT + +#include <bsp/irq-info.h> + +#include <rtems/netcmds-config.h> + +#define CONFIGURE_SHELL_USER_COMMANDS \ + &bsp_interrupt_shell_command, \ + &rtems_shell_ARP_Command, \ + &rtems_shell_HOSTNAME_Command, \ + &rtems_shell_PING_Command, \ + &rtems_shell_ROUTE_Command, \ + &rtems_shell_NETSTAT_Command, \ + &rtems_shell_SYSCTL_Command, \ + &rtems_shell_IFCONFIG_Command, \ + &rtems_shell_IFMCSTAT_Command, \ + &rtems_shell_VMSTAT_Command + +#define CONFIGURE_SHELL_COMMAND_CPUINFO +#define CONFIGURE_SHELL_COMMAND_CPUUSE +#define CONFIGURE_SHELL_COMMAND_PERIODUSE +#define CONFIGURE_SHELL_COMMAND_STACKUSE +#define CONFIGURE_SHELL_COMMAND_MALLOC_INFO + +#include <rtems/shellconfig.h> diff --git a/testsuite/media01/pattern-test.c b/testsuite/media01/pattern-test.c new file mode 100644 index 00000000..4b00cb73 --- /dev/null +++ b/testsuite/media01/pattern-test.c @@ -0,0 +1,337 @@ +/** + * @file + * + * @brief Command to fill a file with a pattern. + * + * The written pattern is a simple 32 bit counter which makes it easy to find + * problems where buffers are not written to the correct location or where some + * blocks are missing. + * + * This file can be either compiled as part of RTEMS or as a stand alone Linux + * or (most likely) FreeBSD application. To compile it as stand alone, just use + * "gcc pattern-test.c" + */ + +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __rtems__ +#include "pattern-test.h" +#endif /* __rtems__ */ + +#include <arpa/inet.h> +#include <err.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + +static const char * const usage = + "Fill a file or a device file with a simple incrementing 32 bit pattern.\n" + "Use with:\n" + " pattern [fill|check] <file> <size> <block_size> [<start_value> [<output>]]\n" + "Arguments:\n" + " All numbers can be decimal (e.g. 256) or hex (e.g. 0x100)\n" + " <size> and <block_size> is in bytes\n" + " <start_value> start with this counter value\n" + " <output>: Only relevant for check. Can be: \n" + " <nr>: print the first <nr> errors\n" + " \"short\": Print all errors but short form\n"; + +static int +check_and_process_params( + int argc, + char *argv[], + int open_flags, + int *fd, + size_t *size, + size_t *block_size, + uint8_t **block, + uint8_t **read_block, + int *max_errors, + bool *short_output, + uint32_t *start_value +) +{ + if (argc < 4) { + printf(usage); + return -1; + } + + if (argc > 4) { + *start_value = strtoul(argv[4], NULL, 0); + } else { + *start_value = 0; + } + + if (short_output != NULL && max_errors != NULL) { + *short_output = false; + *max_errors = 1; + if (argc > 5) { + if (strcmp("short", argv[5]) == 0) { + *max_errors = -1; + *short_output = true; + } else { + *max_errors = strtol(argv[5], NULL, 0); + if (*max_errors <= 0) { + warn("Can't use <output> parameter."); + return -1; + } + } + } + } + + *size = strtoul(argv[2], NULL, 0); + if (size == 0) { + warn("Couldn't convert size or size set to 0"); + return -1; + } + + *block_size = strtoul(argv[3], NULL, 0); + if (block_size == 0) { + warn("Couldn't convert block_size or block_size set to 0"); + return -1; + } + + if (block != NULL) { + *block = malloc(*block_size); + if (*block == NULL) { + warn("Couldn't allocate block"); + return -1; + } + } + + if (read_block != NULL) { + *read_block = malloc(*block_size); + if (*read_block == NULL) { + warn("Couldn't allocate read_block"); + if (block != NULL) { + free(*block); + } + return -1; + } + } + + *fd = open(argv[1], open_flags, 0666); + if (*fd < 0) { + warn("Couldn't open file"); + if (block != NULL) { + free(*block); + } + if (read_block != NULL) { + free(*read_block); + } + return -1; + } + + printf("File: %s\nSize: 0x%x\nBlock size: 0x%x\n", + argv[1], *size, *block_size); + + return 0; +} + +/* Write addresses to the block. Keep word boundaries intact. */ +static void +fill_block(uint8_t *block, size_t size, uint32_t start) +{ + uint32_t pattern_size = sizeof(start); + uint32_t start_offset = start % pattern_size; + uint32_t value_h = start & ~(pattern_size - 1); + uint32_t value_n = htonl(value_h); + uint8_t *val = (uint8_t *) &value_n; + + if (start_offset != 0) { + size_t to_write = MIN(pattern_size - start_offset, size); + memcpy(block, val + start_offset, to_write); + block += to_write; + size -= to_write; + value_h += pattern_size; + value_n = htonl(value_h); + } + + while (size >= pattern_size) { + memcpy(block, val, pattern_size); + value_h += pattern_size; + value_n = htonl(value_h); + block += pattern_size; + size -= pattern_size; + } + + if (size > 0) { + memcpy(block, val, size); + } +} + +static int +command_pattern_fill(int argc, char *argv[]) +{ + int fd; + size_t size; + size_t block_size; + uint8_t *block; + uint32_t start_value; + int rv; + + rv = check_and_process_params(argc, argv, O_WRONLY | O_CREAT, + &fd, &size, &block_size, &block, NULL, NULL, NULL, &start_value); + if (rv != 0) { + warnx("Error while processing parameters.\n"); + return rv; + } + + for (size_t current = start_value; + current < start_value + size; + current += block_size) { + size_t write_size = MIN(block_size, size-current); + ssize_t written; + fill_block(block, write_size, current); + written = write(fd, block, write_size); + if (written != (ssize_t)write_size) { + warn("Writing failed on block at 0x%x", current); + break; + } + } + + free(block); + close(fd); + + return 0; +} + +static void +print_block(uint8_t *block, size_t size) +{ + for (size_t i = 0; i < size; ++i) { + if (i > 0 && i % 0x10 == 0) { + printf("\n"); + } + printf("%02x ", block[i]); + } + printf("\n"); +} + +static int +command_pattern_check(int argc, char *argv[]) +{ + int fd; + size_t size; + size_t block_size; + uint8_t *block; + uint8_t *read_block; + int rv; + int errors = 0; + int max_errors; + bool short_output; + bool last_was_error = false; + uint32_t start_value; + + rv = check_and_process_params(argc, argv, O_RDONLY, + &fd, &size, &block_size, &block, &read_block, + &max_errors, &short_output, &start_value); + if (rv != 0) { + warnx("Error while processing parameters.\n"); + return rv; + } + + for (size_t current = start_value; + current < size + start_value; + current += block_size) { + size_t read_size = MIN(block_size, size-current); + ssize_t received; + fill_block(block, read_size, current); + received = read(fd, read_block, read_size); + if (received != (ssize_t)read_size) { + warn("Reading failed on block at 0x%x", current); + break; + } + rv = memcmp(block, read_block, read_size); + if (short_output) { + bool is_error = (rv != 0); + if (last_was_error != is_error) { + warnx("%s: 0x%x", + is_error ? "ERR" : "OK ", + current); + } + last_was_error = is_error; + } else if (rv != 0) { + warnx("Pattern wrong in block at 0x%x", current); + warnx("Expected:"); + print_block(block, read_size); + warnx("Got:"); + print_block(read_block, read_size); + ++errors; + if (errors >= max_errors) { + warnx("Too many errors. Refusing to continue."); + break; + } + } + } + + free(read_block); + free(block); + close(fd); + + return 0; +} + +static int +command_pattern(int argc, char *argv[]) +{ + if (argc < 2) { + printf(usage); + return -1; + } else if (strcmp(argv[1], "fill") == 0) { + return command_pattern_fill(argc-1, &argv[1]); + } else if (strcmp(argv[1], "check") == 0) { + return command_pattern_check(argc-1, &argv[1]); + } else { + printf(usage); + return -1; + } +} + +#ifdef __rtems__ +rtems_shell_cmd_t shell_PATTERN_Command = { + .name = "pattern", + .usage = usage, + .topic = "files", + .command = command_pattern, +}; +#else /* __rtems__ */ + +int +main(int argc, char *argv[]) +{ + return command_pattern(argc, argv); +} +#endif /* __rtems__ */ diff --git a/testsuite/media01/pattern-test.h b/testsuite/media01/pattern-test.h new file mode 100644 index 00000000..5d89e16c --- /dev/null +++ b/testsuite/media01/pattern-test.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TESTSUITE_PATTERN_H +#define TESTSUITE_PATTERN_H + +#include <rtems.h> +#include <rtems/shell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern rtems_shell_cmd_t shell_PATTERN_Command; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TESTSUITE_PATTERN_H */ diff --git a/testsuite/media01/test_main.c b/testsuite/media01/test_main.c index 91e0dd5a..001c80a8 100644 --- a/testsuite/media01/test_main.c +++ b/testsuite/media01/test_main.c @@ -53,6 +53,8 @@ #include <rtems/shell.h> #include <rtems/telnetd.h> +#include "pattern-test.h" + #define TEST_NAME "LIBBSD MEDIA 1" #define TEST_STATE_USER_INPUT 1 @@ -123,7 +125,7 @@ telnet_shell(char *name, void *arg) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); env.devname = name; env.taskname = "TLNT"; @@ -199,6 +201,8 @@ early_initialization(void) #define CONFIGURE_FILESYSTEM_DOSFS +#define CONFIGURE_FILESYSTEM_TFTPFS + #define CONFIGURE_MAXIMUM_PROCESSORS 32 #define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 4096 @@ -240,7 +244,9 @@ early_initialization(void) &rtems_shell_NETSTAT_Command, \ &rtems_shell_SYSCTL_Command, \ &rtems_shell_IFCONFIG_Command, \ - &rtems_shell_VMSTAT_Command + &rtems_shell_IFMCSTAT_Command, \ + &rtems_shell_VMSTAT_Command, \ + &shell_PATTERN_Command #define CONFIGURE_SHELL_COMMAND_CPUINFO #define CONFIGURE_SHELL_COMMAND_CPUUSE @@ -261,6 +267,7 @@ early_initialization(void) #define CONFIGURE_SHELL_COMMAND_MV #define CONFIGURE_SHELL_COMMAND_RM #define CONFIGURE_SHELL_COMMAND_MALLOC_INFO +#define CONFIGURE_SHELL_COMMAND_MD5 #define CONFIGURE_SHELL_COMMAND_FDISK #define CONFIGURE_SHELL_COMMAND_BLKSTATS diff --git a/testsuite/netshell01/test_main.c b/testsuite/netshell01/test_main.c index 81941d0b..b967cb87 100644 --- a/testsuite/netshell01/test_main.c +++ b/testsuite/netshell01/test_main.c @@ -162,7 +162,7 @@ test_main(void) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); rtems_shell_main_loop(&env); exit(0); diff --git a/testsuite/nfs01/test_main.c b/testsuite/nfs01/test_main.c index 2312040a..21d744d1 100644 --- a/testsuite/nfs01/test_main.c +++ b/testsuite/nfs01/test_main.c @@ -63,8 +63,7 @@ test_main(void) NULL); } while (rv != 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_SHELL diff --git a/testsuite/openssl02/test_main.c b/testsuite/openssl02/test_main.c index 32e9c03a..11229e58 100644 --- a/testsuite/openssl02/test_main.c +++ b/testsuite/openssl02/test_main.c @@ -46,6 +46,8 @@ #include <sysexits.h> #include <unistd.h> +#include <rtems/bsd/modules.h> + #include <machine/rtems-bsd-commands.h> #define TEST_NAME "LIBBSD OPENSSL 2" diff --git a/testsuite/pf02/test_main.c b/testsuite/pf02/test_main.c index 92073165..dfb9847b 100644 --- a/testsuite/pf02/test_main.c +++ b/testsuite/pf02/test_main.c @@ -132,7 +132,7 @@ telnet_shell(char *name, void *arg) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); env.devname = name; env.taskname = "TLNT"; @@ -167,6 +167,7 @@ test_main(void) { rtems_status_code sc; int rv; + rtems_shell_env_t env; prepare_files(); @@ -176,9 +177,7 @@ test_main(void) rv = rtems_initialize_ftpd(); assert(rv == 0); - rtems_shell_env_t env; - - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); rtems_shell_main_loop( &env ); exit(0); diff --git a/testsuite/ppp01/test_main.c b/testsuite/ppp01/test_main.c index 078e96e2..b6e9d4d7 100644 --- a/testsuite/ppp01/test_main.c +++ b/testsuite/ppp01/test_main.c @@ -211,7 +211,7 @@ telnet_shell(char *name, void *arg) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); env.devname = name; env.taskname = "TLNT"; @@ -272,8 +272,7 @@ test_main(void) rv = rtems_pppd_connect(); assert(rv == 0); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } RTEMS_BSD_DEFINE_NEXUS_DEVICE(ppp, 0, 0, NULL); diff --git a/testsuite/tcpdump01/test_main.c b/testsuite/tcpdump01/test_main.c new file mode 100644 index 00000000..0f31cdde --- /dev/null +++ b/testsuite/tcpdump01/test_main.c @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @brief Tests the tcpdump command. + */ + +/* + * Copyright (C) 2022 embedded brains GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <vm/uma.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> + +#include <assert.h> +#include <ck_epoch.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/bsd/bsd.h> +#include <rtems/console.h> +#include <rtems/libcsupport.h> + +#include <machine/rtems-bsd-commands.h> + +#define TEST_NAME "LIBBSD TCPDUMP 1" + +typedef struct { + rtems_id main_id; + int sp[2]; +} test_context; + +static test_context test_instance; + +static void +epoch_cleanup(void) +{ + rtems_status_code sc; + + sc = rtems_task_wake_after(CK_EPOCH_LENGTH); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void +init_addr(struct sockaddr_in *addr) +{ + int ok; + + memset(addr, 0, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + ok = inet_aton("127.0.0.1", &addr->sin_addr); + assert(ok != 0); +} + +static rtems_id +start_task(rtems_task_entry entry, void *arg) +{ + rtems_task_priority prio; + rtems_id id; + rtems_status_code sc; + + sc = rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_create(rtems_build_name('T', 'E', 'S', 'T' ), prio - 1, + 32 * 1024, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start(id, entry, (rtems_task_argument)arg); + assert(sc == RTEMS_SUCCESSFUL); + + return id; +} + +static void +test_tcpdump_help(void) +{ + char *argv[] = { + "tcpdump", + "-h", + NULL + }; + rtems_resource_snapshot snapshot; + int exit_code; + + rtems_resource_snapshot_take(&snapshot); + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + assert(rtems_resource_snapshot_check(&snapshot)); +} + +static const char raw_packets_file[] = "packets.bin"; + +static void +run_tcpdump_write_raw_packets(rtems_task_argument arg) +{ + test_context *ctx; + FILE *file; + FILE *saved_stdin; + FILE *saved_stdout; + FILE *saved_stderr; + int rv; + char *argv[] = { + "tcpdump", + "-n", + "-i", + "lo0", + "-w", + RTEMS_DECONST(char *, raw_packets_file), + NULL + }; + int exit_code; + rtems_status_code sc; + + ctx = (test_context *)arg; + + saved_stdin = stdin; + saved_stdout = stdout; + saved_stderr = stderr; + + file = fdopen(ctx->sp[1], "r+"); + assert(file != NULL); + + stdin = file; + stdout = file; + stderr = file; + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + stdin = saved_stdin; + stdout = saved_stdout; + stderr = saved_stderr; + + rv = fclose(file); + assert(rv == 0); + + sc = rtems_event_transient_send(ctx->main_id); + assert(sc == RTEMS_SUCCESSFUL); + + rtems_task_exit(); +} + +static void +test_tcpdump_write_raw_packets(test_context *ctx) +{ + char *argv[] = { + "tcpdump", + "-n", + "-r", + RTEMS_DECONST(char *, raw_packets_file), + NULL + }; + rtems_resource_snapshot snapshot; + int in; + int out; + int rv; + char c; + ssize_t n; + struct sockaddr_in addr; + socklen_t addr_len; + rtems_status_code sc; + int exit_code; + + rtems_resource_snapshot_take(&snapshot); + + rv = socketpair(PF_UNIX, SOCK_STREAM, 0, ctx->sp); + assert(rv == 0); + + start_task(run_tcpdump_write_raw_packets, ctx); + + init_addr(&addr); + + in = socket(PF_INET, SOCK_DGRAM, 0); + assert(out >= 0); + + rv = bind(in, (const struct sockaddr *) &addr, sizeof(addr)); + assert(rv == 0); + + out = socket(PF_INET, SOCK_DGRAM, 0); + assert(out >= 0); + + c = 'x'; + n = sendto(out, &c, sizeof(c), 0, + (const struct sockaddr *) &addr, sizeof(addr)); + assert(n == 1); + + c = 'y'; + addr_len = sizeof(addr); + n = recvfrom(in, &c, sizeof(c), 0, + (struct sockaddr *) &addr, &addr_len); + assert(n == 1); + assert(c == 'x'); + + /* The tcpdump pcap read timeout is 1000ms */ + sc = rtems_task_wake_after(rtems_clock_get_ticks_per_second()); + assert(sc == RTEMS_SUCCESSFUL); + + c = 'q'; + n = write(ctx->sp[0], &c, sizeof(c)); + assert(n == 1); + + rv = close(out); + assert(rv == 0); + + rv = close(in); + assert(rv == 0); + + sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + assert(sc == RTEMS_SUCCESSFUL); + + rv = close(ctx->sp[0]); + assert(rv == 0); + + exit_code = rtems_bsd_command_tcpdump(RTEMS_BSD_ARGC(argv), argv); + assert(exit_code == 0); + + rv = unlink(raw_packets_file); + assert(rv == 0); + + epoch_cleanup(); + assert(rtems_resource_snapshot_check(&snapshot)); +} + +static void +test_main(void) +{ + test_context *ctx; + FILE *file; + int rv; + + ctx = &test_instance; + ctx->main_id = rtems_task_self(); + + /* Fill dynamic file pool in Newlib */ + file = fopen(CONSOLE_DEVICE_NAME, "r+"); + assert(file != NULL); + rv = fclose(file); + assert(rv == 0); + + /* + * Stop interferences of uma_timeout() which may need some dynamic + * memory. + */ + rtems_uma_drain_timeout(); + + rtems_bsd_ifconfig_lo0(); + epoch_cleanup(); + + test_tcpdump_help(); + test_tcpdump_write_raw_packets(ctx); + + exit(0); +} + +#include <rtems/bsd/test/default-init.h> diff --git a/testsuite/telnetd01/test_main.c b/testsuite/telnetd01/test_main.c index 00916843..d7e50a97 100644 --- a/testsuite/telnetd01/test_main.c +++ b/testsuite/telnetd01/test_main.c @@ -50,7 +50,7 @@ telnet_shell(char *name, void *arg) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); env.devname = name; env.taskname = "TLNT"; @@ -75,8 +75,7 @@ test_main(void) rtems_status_code sc = rtems_telnetd_initialize(); assert(sc == RTEMS_SUCCESSFUL); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_SHELL diff --git a/testsuite/thread01/test_main.c b/testsuite/thread01/test_main.c index 9d5e5bba..48b21452 100644 --- a/testsuite/thread01/test_main.c +++ b/testsuite/thread01/test_main.c @@ -109,13 +109,10 @@ wait_for_worker_thread(void) static void non_bsd_thread(rtems_task_argument arg) { - rtems_status_code sc; test_curthread(""); wake_up_main_thread(); - - sc = rtems_task_delete(RTEMS_SELF); - assert(sc == RTEMS_SUCCESSFUL); + rtems_task_exit(); } static void diff --git a/testsuite/usbkbd01/init.c b/testsuite/usbkbd01/init.c index 0ea2d2b7..9800b871 100644 --- a/testsuite/usbkbd01/init.c +++ b/testsuite/usbkbd01/init.c @@ -101,7 +101,7 @@ usb_keyboard_read_task(rtems_task_argument arg) rtems_message_queue_send(omid, &msg, sizeof(msg)); } rtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -141,7 +141,7 @@ usb_keyboard_open_task(rtems_task_argument arg) printf("keyboard device closed\n"); } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/usbmouse01/init.c b/testsuite/usbmouse01/init.c index 5bf732d8..56518d67 100644 --- a/testsuite/usbmouse01/init.c +++ b/testsuite/usbmouse01/init.c @@ -103,7 +103,7 @@ usb_mouse_read_task(rtems_task_argument arg) } rtask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void @@ -143,7 +143,7 @@ usb_mouse_open_task(rtems_task_argument arg) printf("mouse device closed\n"); } otask_active = false; - rtems_task_delete(RTEMS_SELF); + rtems_task_exit(); } static void diff --git a/testsuite/zerocopy01/test_main.c b/testsuite/zerocopy01/test_main.c index 3e7b7b7b..3a03c12c 100644 --- a/testsuite/zerocopy01/test_main.c +++ b/testsuite/zerocopy01/test_main.c @@ -190,7 +190,7 @@ telnet_shell(char *name, void *arg) { rtems_shell_env_t env; - memset(&env, 0, sizeof(env)); + rtems_shell_dup_current_env(&env); env.devname = name; env.taskname = "TLNT"; @@ -240,8 +240,7 @@ test_main(void) sc = rtems_task_start(id, network_flood_task, (rtems_task_argument) bc); assert(sc == RTEMS_SUCCESSFUL); - rtems_task_delete(RTEMS_SELF); - assert(0); + rtems_task_exit(); } #define DEFAULT_NETWORK_DHCPCD_ENABLE @@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.0.19" -REVISION="1f3c580272b15a03d2566843c5fe872a" -GIT="61ee22b598cf80e260beb64e475966f58b304d0d" +VERSION="2.0.25" +REVISION="767522112be77f8585812fcfaa08e805" +GIT="39ef33e48380a2db38a0eae40a3a4a2c954f4450" INSTALL='' -C1='#6' -C2='#.' -C3='#%' +C1='#+' +C2='#*' +C3='#)' cwd = os.getcwd() join = os.path.join @@ -168,6 +168,6 @@ if __name__ == '__main__': Scripting.waf_entry_point(cwd, VERSION, wafdir) #==> -#BZh91AY&SY9 ½´\¥ÿÿÿ³DPÿÿÿÿÿÿÿÿÿÿÿm (¬#%00e€(b÷/mЀ#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%öÞúzómmkîÖZžÞæ—²U#.´[iÚ¾îõ¹±µ–ú:¥'kd‹î7{ÛïoºµÛ¶ÖµK“¹]Øvµ'ÛßO¾ö¸gv÷s½÷¶N)#.Q+Ù½íp·µÞ]/{mš»<{guÒ‹@Ýó{§«vo;¾ø;ŸvÎiîáÉî44lºï{ß|ûëËßÚU9íõÝŸÝšî·¯y½÷½x#%#%@(}ìh#%` ï²€=âwfí˜:4§[aÓ¹½ y©³A¦€‡¸ê»´ö4=¦öìd+Ö¨ª)ífš0#6 *”¢”{ƒ’Q$#%#6#%YJzÚV½ƒ%çÍíîæ}»j½êºa’ïjÚØá3TTf˦Iv×]s}6óu7¾÷z:#.ÎxûÝW_=³ÛzÖåZ;Û»ÍÝÛ}Þsy¾¯³»ß{¸ûížÖîiÉÔ÷Ëzõ¶§rç>^æû}ñ¾íóÛa zúz{x¹-ìz̀ݳֲ4÷³¼ÛÞ÷fÞÏs¬ô`u¡ÒãF°Û"’EE7w¼#%(J*’OCÀ#%lîè•=÷n{Ywº¹½»ÐdÜöï>}Ü[vÇGÐ`>†ÛEmk®¥ÑMãW`×LæyÛãÚ^u½#%}÷aŽît;o¶û}|{mçO—vpòǼ΀Šo·*WµíƘܶ2>öí®®òÏŸwÖõ»]´î;ºjîí9QÚt×·Zt“ž>÷»Ü®¨ÜÙu÷‡ß|}í«×ܬ‰&ÞíN«îq\îû½:¾Ã.ñŽ^ÙéyaC§¶Ó¶Û›—³®÷Ð¥&nCzî¤|öilöͶíÝîвë[¸˜÷»³Ûºû5ÖkoX«èqØ—nöÐlîõÞÞ/`{´žó½wÌ>ï†Ðê¥PJªPTT´jB¹7uÐv&i¶vÜèº]vú‰Ý›:kÛkÔØëUUܘ{x÷š)º¼»HЃºÚɯfw€#%7ZI#%#%;Ü^êåÉÎó²û3ÊF-Ÿm½ÜözÔíÝ™Üì_U.us[›¶õv]]ó¸|˜Hû†,ÍqÑ»ÝÁæñ{c;úŒçvåJº{+¹éÔ²õ§l÷+ªWg¡»`úùÛÙ;{n#î²Ç]÷}çww»{»»xîvÛ#.¾Øú½mÝEfù‡"sž¼ï¯¼ùÚÀ°ËlÐú$/g¬íÑ”x<ïÜë°VØ6EÇ‘µÞ®ÚGxO{×>öø½ïwÞ´#%À*×Û;Fªµíí7¶;g<ÂÀëu´oSk´ÝëÓmï 9.ó’®Ú×ݬ»Ý¾çwÅówwtåvë¥Þç:#.[.ëwc Ó‚àéÕhÁ”gu“½Þ^ïŸnØu¼ÓÐŒ…/=ÝÚ4#%•c¹éî½Mö{ÜàìJ…U )½{xW»´×`ï{®ñÝÊäÅÖÃV¶•U[}÷IzovƒuÇH*Ší·u$ëIÒÖªîúãÞncÚ[ÓµŽÛ× &Æ»»}öøø‡½óí·³acÜ| ìÁ“G»îñÌͼÜ&:·»}½í6µîÐíç;ï¹ßvÛRÀîï·Ÿw}-âòó[ãnh€ &€#%&€ £CBa0š#.#%‰ä™PÏTz†Ðš#%õ§’{SÔõA) B @†@©©á$zzI☠é4#%#%#%#%#%#%#%‚D! M5TÿMŠoU<ÔôzTý(òÔõ)é¨mG¨#%#%Ñ£@#%#%'ªRD!4dÐODõ$ÙOFž¡£õM#.€ò@#.Sj#%#%#%#%#%#%$!#%&@bh&M#%M#.4SÓiˆŒ4 ¢ #%h#%#%#%I¨ˆ L˜#Až€&Œ¦=&”õmêˆ{SIé” h#%d4#%È#%—ÿÓ?£Uirê\ÕQWwk¹úµZvhʃ>5Zu!LA³ ,L•Q*"€)æ°Pcõ§óü Zü“•5OùÓ¦¸ üæJt¦î£Ä‘8•WoT"¢]^SÌÆ´_3ý†os2ìÀlDs‚4;m»qE6›®ñ‘ÎÊ´ÚMí˜ÖØ`Vd…â]jíâ—˜•5o‚# åñ÷UŒI7wD¼áÌKUå¦/žÝ~«oÔBäE"ªSº»U¦ÖÖ5k3khÈ‹ €7#%H*-E Õ:¥”$HŠ& ˆ" HåP° !ß#%hŠ–‚¡#%ª$@dPÉ@mªfL…’ŒÍCLÓd@M$Rj6Ñ35–25)F”Sm&‰š$ÐJ2ZšQ°h[Fi,ši¢Z!#.F)iM€F¥#.1eM£DRl–¨Š–ZSMhˆYi#.š ƒ(ÌÌcRF£Qµ&ƒJl†BjcI@˜hÒËHÆR›F¦›"[M¶³U´i˜’ÆfLÈM&²m¦ÛM65%)-5±–¦Û3fZL“1‘f´ÄÑd¢‚™l‰B!Qf‘´R`4TH!`ؤ©„f•Jb0lB ‘XhdFIR&B4±ClÍ’&)BFRÊËf$@ÒYŠY5“ccE#6‘d†K) -)¦ ±%&EDš2hhɉIF‘($@VAMEˆ¢fRÒ¦`›É؉1MÍ‚ab6¤Ø–V‹$”›‚$´”PRD•’%¤Q0EaÆIL# ”&RJME™±£XHÒjH’b iM$"H²[bË2ŠFÌ’‰²™3bdE(Í‘˜&U4Ä‹)A³Q`,i¦†Æ"5+%’6(”R"šIˆ&¤a³LcI¡ &¤–Q”Ôi3HÚ("šh¢kP,&YA¤²2‹%&DÐM2‘*4f6”)1¨6ÄÊA! "“1F4,Ë ŠÙ&TÌÕ˜£l¥±(‰™#.H¦!HŒØСYM$QF¤É&ÉØÆJšF¦h±b2’™˜²2³i„¬D`ˆ¢JM52i™!Œ¢)#.š1Sb‹5*R’š)#.‘“2)´…HÅ(ÊHR"‹E$˜’M)´i5DŠFÀ˜Œh&Ti¦DÚ#.ƒ,„Òe‹DÈY‘¦ÉJb”D!f™ k6lmd°$4–Ld5EPZ XR$Ècc1 Â2ƒRXÔY¤J&ŒE©M(Z˜hI™IŠ$2ɲ#)‚E,Ñ¥1„5„²e"$4M¶¦¶´`™BjfŒ¦F&”RLȈ¥6À¥š,Å&‘”%–TÙ¢-L¬-”bC؈RˆšD#HJm~Ãk¢¦©aˆÍ¢ŒlVƶ*6L¥4ÔR¤š44´…¨ØFÉ5Y†¡¦c(ÉS*H¶D‰™¦[%´Q‰IKQµ…£&˜D±5˜±acR#6e¥™2eI´*Ò4TÙB+dË"©TÒT›63š¶,Y“L¬‰S,¦ÖJ²ÙJSLl’‘µ)¬±%¤2RÔZ ªŒ!¨Õ£%AEVMd£jŠÅEH”kDÑ$m‹hÅŠÔl2ѵ€´™0°U@i€”R £F2™&•¦±Æ¦&±‹d‹i Õª–µ–* ”ÍKY4IR1!M¤lY"¶¥Y¶1Jm*–R¦YaZšDF¶•K"e5MM¶„”Êk,Xl²k+,…lÓ ¨ÒD…bÃ4˜‰!´…-£T%¢†KQY&š6LšJ,ˆ&ËŠl¤#."+,‚E1”ÌJ”lÓ4AE¢Ó$&Fm6“F6ÆÈS¦™cE%bHÌÔ#!4LÔQ¢JdR–P5BTÀ4˜Ó J™²Bj-#636£)F̤’Œ£!HbÊkÃi4F5&Éa$YP…-4)Äš4Y”©””²•˜d¬aš,‘±jE¨Õ5£4ECF¥)6b¥fQj4 2ÆA¦&Ä•%I”1Ršcd¤Ía)ME`Û0Ù¤²lf“E˜‰CJ2Zfµ‘hTÍ2–H’l£0D2hªih¬Òl”Ä,LDÒRBAh´[ Q¨ÔQŠÆ-–ÃI†’M#6LŒRˆ#b-f›C2ˆ6*2%,¤©‘Ñ´k£TI¢„Ë5EŠ$Ôš£PdðŒj”¡kA¥4Ë!E$Š¦Ih‚5Eˆ±JÛ"¥F±¤ÁdÑÌ¡jQHfJ$©±(‰•j#V#.2,Y*6Œ)‹4¢Ù2V-&)+)¶D¶M¡¢T’™R¢Âƒccd£dÑ&#¶HÔRLÁ¬ÉƒA#6)³6&"šÆ¨©•“Y‘¤)¤šŠ¢Š&›RV-”’ÆɈ£F‚4˜2JÔˆÑie£he¨’ÑjŠ±”£i5² …Q¶5%)ЙÈ̌ƚ‘LÙ&kDlU%‹hÔ•,«&´LÚ1lj £cc$UE2©¥XÚ6¶1mI˜ÊQ f²±-F+iQµ´©QJV”f*šB(4X"J&ØÖ,Ê’+I[M²“+FbbÑIE[l¶¬R&T*&$¢1PÀ"CFŠi&ÔÃ%XÛØ´Í[F5¬´•42ÖÊY6ÔÔÛ!mI¦¬ME26’QA°ÖjMI™²Êˆ B¢LI&RLˆÈÄd"Ù-¢Ñ‚±“þn··ýÔ¥C)þB51jÄÿTqôe+(a(Æh”‡÷!þ§ ûbåÕ LÿL$hÒlùNzí¿Îó±½9¿‹øºô·¦SUIPª?ë[ŒlÈŸÎÿ·ÖYLâsÈ †ßùë;L(0\:8À¥‰pÁI äˆd˜*ƒ‰Ãß±¢rý*¤çÿœ3ÿ³ôZÑÿÊI(R×+ˆ½”g™,$b/Í0ÿ-‹npz%ZM‡¼íõâ8ßwB¹ÁŒ¨d~ËÕ‹NØœ“ÆZþ,3cRR(l“ŠrrXˆþúݳf¤áV…cÈÌ`I¥Û"lÆLhƒcôzç““G]“¼î˜Þ¹w§¥^¥ã\³Ë/âuçåu™Í\‚LŠëP¦"W‹4LªvcK™@¥JAF&Ä“M‚ê×bÈ‹Ò3ˆ(êñç]¨eñnY½74h6m`Ù—(c2@sªªzoþÝ(‘ 7p‡Êc#6Gv…)†ì,`¼rÂü®R[ŒR}ßÒëdzçgsÅéšÐN×VÄQL0¦Ð¤Y;ùd«Ïô8$ÚÍÏ£˜ØÐØžj¤Ï(%?·râƒþ°Ãµ“ºÀ¡…†JÅxžß÷k¶Œ¶cÂx-›2KËw%€•¬C°UyáyÜ–íÞw^QŽr車esn–CQW6éçqllO²ëå5¼Z4EA¯»ºŸ?_ÆW‹ò-ȦWÅȶÆåÈIºÕø¼u‘¨Û?Go*„Èyùæ|m<™b#4L9cuPEX°ž«©#.¹t@#6A!´ä#ñÆ#.¯)ÎãÂHˆÃz”^+›Ý\Ѩ8šë•ÞwcrŽ•Ë™(Ë®é$s™5öø‘ZA„ÿhÿŒJrÈ.YÚlCÙ’,0!MðÛtÉÈe«#6ýR‹ÁÃ79ô,ýפ¾ìÉþ˜FtÃÏxV¬@Gô¡€dž%lþ «p’—<kìÄ9 c£ðÐÃ|¶Ç™“#tE]HP0îT[æÈ؃Ù_#¨½—zírH ȦwâÔŠŽÍW†7mxþVYê?)Ärvj³oQžwE¦m„e#.¿ÂdÔ‚™ç™×¶b²àþN!ïe §&¢”øuû/ÑÏKà`ö§nínœšùT)"*¤#6d9#c*w&éƒbÛ†ÉðfR6zš®:'ï.àûì¥k,YL#6N*}±ÆyÞ>ÇãŠUË"õÖ“UO«pi)8ª—â”+ñ §Ó¬,‰±Èj[þv¼‹lQôÇôa™1Kèë¯Ç×FÌ÷¿SªÄ7Ím‰Z½”*‰£NŒ¼Pâ-#6¬AË@‰*•{iX<ÙZøW÷»(Éðâ»EEE¼nY5ÆÕ͵þ—ìw’>ÇS6½•ß‹«¦öøÿM‡fhŠF¨%2+zmýW!†ŒbSQIðîÜ®÷uVé(i%"÷@Xš'+¬¥&D§)J²‹šå¢¶/]öï^¯"R¬•k¹bÑ_·-×»±öõÆ*Š|ý{ü=o÷îèë˜ØÔl›|ÏÀÞ1¨4–üŽ‹>õÝŒ>¬žýóL—ÖȲ"4£~"Uä¨ræPaŒLÖ¶jϹ$Â![ÙùzÛ²†F²lWßî«âøùÑÊW=ì¼4D‰Áa«l—AHÒPŤ'Ò'mBµ§…C×Gƒ¶éE2„R]U¥"»wÕ¬ü*ÔðХ߲œœ²ôî²Á‹¨ÀSE#6lÃQMZH"JuœëJ»*”Ó#6I¢L²íãe—¥Â„S²ŠŸ$+b¨¬N¬<ÙkH](©É#6<*žoЗçAJõÂü/•;q°óap×^Ro^X„jÅÄ"_gñ‡Ë<TC—ª¼s×y›¬Xq&ýqK\:05a–áeF1PXäiMö÷3g{O4Ýur™gúkßÙóô€oÄï3Ã)#åªiÔö¢'”־ʣôwr³éº8;+¹+Ÿ†oìÑ®Há m^L¯q#.1¼öÊIr%ÉnCMÏ}‡×–ìƒ6q¸v~„ØÆ5.›|båÙåA„Å#ƒ™S—<*Jtªð¡áô\äáDäÑö%*Éú9ß<}/²Á"#.“L3¦ÙúÀýv½ëåŽ+’ù§T-±·,»í¶g)õïDš!;SñŵaÊà=±3œÏDKA´](´¸åHvÈÍRß#v/6P½E#Ç…‡8qêΰâKè(q<åÅð¹’ý>¼ê÷Œ:+#%ñ²(\HÎpÊ6ç–´zmï*'Ü©×1Ù»á?_kŒ#.êV£„ÊFyûeo–Ðæ”$ê?•ßjfA›vß¾ÚœNü£ƒz…$…!¨.fä³Ü¡Ï\ˆiÌ©Ê#.#¥ŠÉE0F:E&©¯}Ùž…µAîg]°éˆÃp?X7d1€á\NB=}ÙILƘ#6P#6íç;¢gä˜ý=«»†"ž<ú¹áïïRî:I2Éðˆ\õq.Vž)gÉ…,Õ8`¬=jPŽûí‰ÞèqëZ®#.þø¯,nM´B8÷8»è¸×µÛº†ä1ž‡i‹l÷õm±Ï׺|š5Ì=Zí鵦üLuÙ $‘(w0©WX uÒ‘Bd˜N>ù¢Ü¸I¸ì¾½i“íE•éËÒêwë+Õ^{å5ÁZU{Š)ŠãÉÂé¨c¼T˜ì˜í§ @^†Ç¡=4´îç ʉZÛoý4^Û1¶Ã‰±öíªÆË ˜ò-—îûâû™§õoÉÜn¬æU>õéºiúK|e˨†J/±ßåÇ}m}w*IeQ92e@êÛíhQAA9¼CrºõÝO¡¸Ëðuu5¤&÷JÃF}w/Å¡j©jŠÿ}…(© Ÿ+þ«¯½û¿éºÚ|~þëÇT•öµö°ù¥±G‘R{¨«EŠ ¢€úªS>,õ‰÷öæß»åj»çíj܇±†¿•™Yõ¿¯œõâ‚ú·+$‰þ.hÚ>n·‚ªôj£]–P‚ê©¨Æ ×¶{âÝûïg˜œå mk4µ“O§k{Ñ”ÇÛEË£õ;n”dm¥AyÑQŸ#6ÚäúÓ«f‰Í²ãX—8T¾NL\ð—ôP苘1 øˆ63œYî y²úUM3¦ùÝ#.X˜|è¤TWù“7ìÍÇΊD^jÀEŒR,Œ)9Õß¹ÊÈ·)óÜÑŒûúuO³~™¾$_¶ÏŠ ÊsãÆ4?ŒZu>žì]1yÅøÚüžkÓEµÛX)>óOÖâm8s´Ì‚ëéÞO–z\àÚÄÚ¸;â¦R¸Á1ŠŠ~FS_tí“Rn(‘ØPñ¼b˜¤Rf“WYüÊ¡êô„cì×ùúù«ÜËÎP¼B´ ú’Y×…é¤DQQe0¨x~‚Û{*©nf £ªwiX‡T¯)œ’6-Ï;Ë£êw³$3Ê¡¶Ö}•TÛÉ|-ºz²8ï5›`BüéÝââêÖh¢’…ƒÒ¡¸É~¦™J‘‹Î€¤Z+¯¦9r¬èœlÿV´xð¨#6h6ÀêD¶x0¥Ë)AX›³£*0²¹ë¯_#.Ç·†È÷zìe¥HÙÓ,›¿µñïñ²5AòõÁÞŸŠ{žeÔË7Súþ¡&yÖYñ¯¥³LÅSÂœ¹¾Ù?•”¾ŽÙƒÊ-„y¯69^èŽ}ž¦pà†BI‘iK‡&±nÎǨŸþ³…éÝs»Î¹ßŽe²‚ð,¼&2:LäßÏå[‹«¸óxHÇ®*–Ø×ÒU9„åé1䌟DL5;þ²ÏiîÐå‚„QT nUÙ‘`ÒŸ#ÍoJò4†1Sºëïv馫ÚÕéa離Mô4O|®–I${U½órà¼0‡;0©*ÃzG/½7ƒ+–#%Qb ]’öÙK\.ª¸ÌÖ‡÷) I9q×ëa¾¡—¼òó#.ÑýýïÞ5К¾ä¬×w*8ÙA—'<ß:¥˜bÊï¬2×ÂxÕQn:ú,öTl8:<ôáyï9ëCÛ¢M\¡"#.JvX¢=Êóðw¹Ô%g±œCÍ•P´GhaÑû£™ªºÅH…”ö)¬¾U.ÎõÛ™Âî‰A=†”sÈ«Ôs›H]#.¤ú$¢8.‹\Ó³Mìòõò³Þ› TÑTaÓMš}ªP‚ «½È¥>?‚¿½¨èØkÕborR#Rñ/ŸÌç*3ÔÆѦø–Ú4u©ð±o‰c‡ÈÞéñÇ#.vnV»pÁáã)r‹³Z”,ª£Ž.O€êÀ’·}zwîÝlnª¥Ÿ.LQøy×@EHiwýÐçç=Q›q§"%V2&0+VŒq¬¨w=Pë¦uÁ7ºV^-Eu©QQ'çU0D}ÏéÞ™Ni×ôñ$¤úÊ'1ÎK¯U=_“lS#6´íŠ0ã×D1zßÆǾ+âdpÚˆô±æZ?wéžx½\`<+½—f—Jøã×~WKËÕ¸îáÝ8n©$z«G»à<c½Ì/Y€á÷ˆÓ vfˆz3ó¹L´cz7kù%ûÚEΛY¥ñ¹-J`]ÞBvP\ÃÚïŸHm2LˆMßwðrÈ÷4›:ðÅ’µ¼`¡‚¯\Ô½u}˜<(ñf`¨Åˆ³÷ôͽ‰UQ9c:ªO+%‰ŽöPiP/º°<H¬§`±È6,I<Y–e"4ECØ@f‹zÝ«šÉbFŽõ™G¡B>q¡z<À®÷±¢—Ù è‘ì#Œá?Ÿ—™}ûI”¡ÓL»_KÃ'ë—B¶Øÿ#É+ù¢ËÖò„|ÑúåµömŽ±‹×ª©ü°Œá˜Ç’7û"ònþ½bôŠ#sh¡›¶¢~¥?~´›V]6k)0úô£ ¦ÙUBÕñ °Uˆ¢,õ'«@ž5ûÜ+ÏÕlŸ±X6µÉ nŒ.ö{pùèü}´:ÑÚð†y¥ªŸbsf19%¢ØñZ³æ#Ô#6 ‚ ê,&Ñž3|#6y€"ú.$#.#.æuµÅ.÷ éÙד»*{gd¾ŠevKfûQοí¶ÒÌð7N ›hÄe hP"1b±êL`ÅMm¬"ã-ÅX]#6=™©Õ)˜«Î#.äÓ2¿+]øºtÉg“:Ô00Y½UDà’ÆŠÛHœIå#LÇÍ’6\j¾}›Þ#.‚Û»Æ6Ñ=U S¿ÑÏÞštÌ4såÙº"ïg³.¾‰t(ÄÉ:qþ²c1û¦³^¹ÙÈÕýÉn*fb’¢fƒÛŠãš;èXhêöÛ÷Ç1•£a¼s>¬=N#.ä÷Áý(ûqÃÙ†fT`ÒalÚÞœ~2å8ŒÀr¥‘Ÿ¦!:Y@àe¨Öj¡Á¥N¬Íec±;8`i‰j%ÉÓcW?äDM¶@0L‚¸¤\Þ'k‘KŽ«®NºËϧcV*±FÖ˜Òßãñð³Ô9O—äÇ»éìåôÁõ*5,<:Ø’|EI$¹$G4¡Õ{âS§#%bŸeÙ‚‡TöŽ¯äìÚÈQ·Ø!û€PveòóêÞ6‚^—ø\F´÷fX=”*´nïú>Q/ãnNQŠž#64…¯¦E¼×^Æ~ú“’`GÈ'0’õÍ\P”DØLÍÕ7؃Ku„ÑohsyÛÝQ¼ã*Sšçsƒ;5XvPÍf8uŒÁ®[˜ÝÒS¬ÝÏ’³•ÔÍ4”¹²†s?…'ÚÝiÎ;¾n)tYG.å¯K|íMÆ×6AâÖuü×8'¶¿UÊÒ»*se¦Ü™L)e‹8«|Šãý×W#%}O`×|AÊ>ƒbm€fò†\sIÑO##y3£Bé#.⊠}ÏÕ¡n«r£CDb+ÂÛ`ì™úÛn¨>O½Õqòh‹¼’×59·b¢ fŒ~~˜V#+bç±^Ug•:^óŽ×ÿxºTq(Ū¿1™y{T‘áâzÒÔ²?[좧sˆÔ5YÿÆgk’—qjûWXvã_WmMÆñuÍZ3¥Q_{=’žìã³—¦(5#<IÛD9Ü÷3Ï^—’y%0I†jÂÉ[J3NMíT¦nPÿG†4„–_oñï=BÚÓK¤JÍ¿‹˜ï¹¹oŒÕa^ݶxe@eˆëªã8GHºê½ÙGÄÆŠÀú"ûÞ° £‘’wñ†ýž~Sa¢œv®4L§Uz°ÎÞÛß²+f”y´Þ¿#./wWdË«^úPÄ´¤áYKAq:;åG2jT wÌCˆ{¿‡ÚœC¥™k5PuÝrª0àÒs¢l+«3Û¬#Û8]Å‹‘Tœˆò»Ó:—úûâ$KÈr™‰0Êâ(\ñ>ˆ‡„Âæ©CÞØ ¥Î]«m9¹ÙãHˆ X1ºÈ½7¸ÙŸdÉ-”K†OèÜû¿—ŽqÛDË‹âíÜ×s²žó#%$ÌØ@²š%"£ÏiñÁGn¦·—›šÿ¦ågsáºÖÉÑosã¢Ñ+-¯m%b”"¹mofŽŠÁfm{pYpÐØoÆZ”̸Â4¸b³Øúxì^S™ÎuÜÛzVþîlbݵ Ï°POæÀŠ:›»ˆ¾Zn(Ô#{›7)×¹LùσãÕñÁ”€÷YÓ¡á‡õ"6zXø£°ébc™G“ë-þ7J-YSÕ–#.™[òw_#6}P$RFÂ/þª:üó²§HèCþCo:“Të–=49yz¿ÃÆÊræÄ•{´R4IÖÌom¨3 ]•F“m;Àuc£tÇloz‰Á`(]ô¿5Œ¨å¥÷!œ.º¿»ZUǤe(®™¯ù‚Ê<V+ÎŽ3¾Òÿ™Ó²uèæ:»bà-¯°F–|¦W“ìºS·§Œµ‚]ëÑöá« Ø·M(ŒW–ù¯:ŒýuÊ:jÝÚÌ£H¹EÑ£8`ñÅتá¨à³ýk £Ã7c¤ü¶<Î’W‚=ëS¼qé4§5#.-ڃ߾›öÝE3ÂcÈär¨Äžù§L¢^3ýÞ®§r"ßM¼}1(gËDôÑ%¯PÄIƒ¶ôÄ`ƒ³”CȲ-G%nG5—lV°‰À(#6QEc—ærÚéͬ١C8’z³‡`Cß½uˆ.7ªY`ÞD,UGÊRÌÑvp«œOøg[\þ–]EÅš@Bcvß8èˆÕñ]Ñ·7ˆŒBov?Uëì”?ë†oŒ;2ëðÿ…¾>^ŽR÷ F““Ö\«ÈÞ¬Ãô²†Ct½FŽ«X 8Ž,Ò;hŸ˜Xj¾$8ÍûYùüxpå»È6püà ·Ý¤$ÀA7ýc€%#%%"A/üÕê¢c–å9Xãßá]5GóÚY"E5É#ÁÛ/*9/„QÉ?Bu˜jÑm-Ã&é;ýz|ž¯‚££Õ¨l@'ñ(¢¹*öúi|GÀS«êÓ©ÝœKð!7ÕmVÐÒ×>ƒø³9€&u ŽÈ„ƒ#% ’ûÿÕ»Ñþ-¢îÕáøÇîäévÞ»¥ #.cc¢¼¢ ÎD×Ì@?Ç4#6†¡o轶j0–ê**ÂŽ¸ÙsóîÅÇ#.„¾º"MŒ\îª"ˆ¤‘ò©¹#%gíÞœá4yR™b(Ñ>@€òtÆ¡R¿šÒ]eÑ·äsvÓ Én(+VpäÿE®‘+ü'6¹†ðN°¢3¤Ä[(ZŠaÉ»I{–Þa½x=™Ž9"1ˆ[ëJ¯lê1H#.ªñŒŠ%OÙŒ#%”ì#%ÄcÏm¾ÿ¶ç•aùã¤þÑI‡ç³ñåú#.5™ðׯ_ó~µ?èÌO“HÅGTUÙÀM¿ƒðý¶y´»õ¨5ц6¥—Ãcj™b²ø§¹ƒƒ$|_Cºuž0€^~c_ñd°"wƒ#%°>בj>ðõ—ER=Sþñ¶s|º5ŸÛå€Ãm¶fÕ¿.Õ4|œåÝj]“³‘¨:;£únŠ>’Ÿñ:ÿn@ é)ª×fËw#.·l¾¡ßþ0}÷îâ£(WÏÎüoNßÛès’{G4ÆÞ|ö0t9×bbÎ?GÝ÷k_ÁÉfª ž"@îkò•X/E÷;¥fL¡¦¿qößÕ5¼8±“d¢*¡â{/Ìë¿D4áÅtÉšûàûoötgS³wÓ>¬XB§<ÈIÁM•mGý—ùu…9£Š>¿zsáþ¸'^Í¡c®¶7Bªƒ+Ã…À3ïr˜\«poª_P–ÜâÛô=‚TÁÖ“æõ§º»¶4ZMI:îäÙœ³[Ê+YòÈ{Q”Óho³…`#67]±áÄRd1ÌU…²[d"ˆ—”b$#6Jk!¬gÛªLk Š$,¸ûÌ£Ö[ËúžP&¶Ï•¢U´’†ð¼U1#ÅË;§¿&b±A…1dy%*üüóŒî€¨"/Z7—cûÿuËBˆsrœ™§¿-‘œ$ýþ~…mLÀ:z®X»U ÄDq#6“Ïß©s <=Uƒ¨~L …¯R›5¸}ŠÛVIé³íLgï› ¦aóÓáåvº˜ÿ_E—F–– -™áG8˜Ÿ“?ûmRGÒ=bSfqÖ7¶ÆAi+sèü"Ê|;O¦$÷#I±aiñÓíÁº×®[Hå[#.º“œu€ÆÆŽ²X[È—¡J×<óÙT¢ Þº™ÆÕQjª@J’ßü.Å#%…W&û9œo…èîD@¾Àß#.âë…cû§˜A,BCG:l¡úkLuÐ#6”‘Õž?; éõæk¬‡©÷‘ž#.qÌvÊÐ3:^LŸUÈk4Ž#6`#.~•H‰5ðÏÙž>Ãõ›÷þ}¾Ó¯‘[ŠhyѺBJ2\¡~¬dâe#/¯ÿa7·§4ãYUáCÇMs÷þ{¿ÍcB'$Å;Ç©·8¦ø¦#.{,µj€weß#.røâ^˾<0fxþîÍÎ5&Ī¦š6P·cQǘ"5Uô†÷êŸE7¥ã˜y–l)p™Ú7úµ!$—Ó8>ž{K2®rãyòUçD#¯lüw¯åz€ã=wÊ‹Ý#6Dn…¸}^æ¡}ªé«)D¥ÐŠ”‚]âuq·à¿çÛ³2±³…®\ÚS{ûìSXãÅY%qßÀn©ÇA#°óÑUá]=@tÔ³žÅj&†Â»Æ5&fîŒðpK‰ ¶˜Sd&vEÚ}´zGYÌ*B#6 ìÃQ>Gêp¬vþ—Û…m}M‚ææ‰ï¤É*Ý0ƒÞþÓpì÷Æûè|A²vB0;½–k[Èé#6¦h˜"#_ ßzéòøÍ3ã 2&ˆBžÑ…5Â6‡…ܦ2,,»¹Ç|زØR¢"ÈQˆæY¼° ÆÒƒ•ÇLkG>'ôjŽ1* Èéô‹uüi‚S1ëŒu\SdIiÆTˆ°Š²O¨6ƘڱÔá‘¢»µázl_gêÿ8hx”MöX0-ôî ê#ôéŠsüKˆòÿ5ö¨»?#%Ýyx®)–˜À’7 y#%í2òw‰ ÐPc± Æ#%tÀ`=!#.fîÁN¯%Ò[^v…'k³Q÷Þ´R“̧`ÆzÆ) û¼nuí¶#ÎÃí?.ÒIpJ[¥iB¡×*,ÂÒÅ š=ÕÕ`„ìQ×pgc¼Iâèez«ôC,Á†f{Ê2ѪRuš„Æú 빈7©Óyå_æ>£vöt…µÆ|ú9´VKœâR˜;#6(<äwãC)ª((F‚Ïv0ñ2hÊÐŒ–ˆDÊ#0+C“9°z“˜¼•]>ÄlÑJYb¦„ÒTœ-¨¦Å¾bàèÁFrÐpõŽ*(~¨Ñäû£sÐÌꆄÊ4 u»y!jŽÐ#8±sy#.bƒn=>tzËÌh†Á¡ÈÌ\åê¦å…bPõYÄËþ:¹Â—nTÝÐ`¨é“Õñ턱ֽnz1»DR‘Å+‡GèobŽÊØÛÑ£ŒU$2ÖåŸ#tóëñØó^¸émÖ½¹ªO8«,›}YEω{^ÜIfxOÐpLÂÅG•#.pßú¿‡¸ÊYE³>vKUÓç¼çûˆ}šgا©Ú§^ÏÜøÌÍ+#%®[Ÿaœ:9Þšœ9ÉÃd×+àB~e.wµ†¼qalñwE#%{j›,dº*1—®íwyæ]®îëO‘˜ÀFŽn‘snZ*4x’m1|F¢ ˆªÝ4s¡ÊŒýÖž{Ζ¹,äl D {#%ÓDhM´ÔD)$CTÅVƨʣ}íÁ14LT.%"¸ã#%l@'D188Ö÷#.A³èòñ®$Õ^¾0Ʊ³\´h$‡{Þ®#.NUzJ);I?×$è±ØíJö»‹? v#.ê‹Ùö‰T«T¾î°¦‡ãaÆ»EÀø5vÈbãlyA}·É¹»Ròš¢C÷)K.Ç(…ösN\0p¨¨S"Ðoœ*ñ“!WTðCyp2b2„ˆè/mû¬kÙÓY¢Kn9‚†¦µJ:¸Ü³\ü{oM—h]¥ÂŠfŒXn#6àÛ¯d Ó™Â/QƒèØ…E®‘KNÀá®WöÙ=þ=^u¶Ã?×›íÆÊ“5çãœ+8~Ñ…ZF0oÑ›48Ã@òð_¾*Çc0Z4ÛþþJ˜°q …áF‹Ã=’tÌÐÔ%Îln¶b–Š†æ˜¦Àé˜JÞ(BwyÉ.ÅBjܘx†HÃQ•TÊ8¦4c#6Å|mñd@ñ˜#%†ŠÒ奉c‹6PºËÈaFĈ`Ÿ]ÿw&ÅççqÕ2ü2GµÐQˆ¯ŠÛš1R^+¦¯LZ¾•yïÛn@m2(ʼn£-eFK`²(euDÕ8IwN`;ª–4,œ:$VbV66ÄPEBD 2ÙN—ßYö;üó6é}rÛ=ùk²#6_³S0¢³*¸‰B¬¥yÔy¥,ôS°X¾.‡9€¶¤Ëuª‘ë@+ë#%)|ðÆÔÔ@zæX"% F#%Às´õ‹&Á;Æzüiûç|ú¶&í•Ÿïöíˆ0t 6‚5à1”L Þ=Ùôãõ¹H9ív ¥RÜð¿4‡Þ{m›ÇnpíÔ+ãÍ=ŒSº!tî#6oÅgcì—tn€…™©t,–Ülu„VFÆ”±#.Ù%Æ·©ƒ_NÃÕsËÏù*\¶µ8r&Aö„²ÕÚ¨N6˜‘÷KY¾#¥ö’ZÔŸŽÌì:‚w¬9§ƒUf ‰Þj Íñ0“ÀF•‡+IŽuœ,—<H¿ý…ÍF;ù]õ½ =ci?kÓ]w›©XNÚ“ÃC$#."oáo”+wÖÓý6Ì<ärÿ‡gŽ·²ln{²ïé£ú/ä#%Ưۡ¬Ï‰”ªÜåŒô¥ázY].Lè®k¥èÐkš#.Ï·…g/!¡5ªJ ±R%KA¾Ç³8Ávœk#.î#ƒßxÄEûÈ¿•÷þÎÈ•Úìv`‡‡ÁÕåEjÜ¡ˆøØ¢Öˆö%ˆ#d¶{äs1Ü“‚QãËðÓø—ÉÏe·’&¸r¦¶Äj¡ýÓ;•¤5wÙ1#.Ä)rÁ·÷íÌiò/:'D\ÿl–Ì€m#.¦2!Š1Mäñ&¬ˆ¬ƒF)š˜èdXUK¤@md”2,ÈtëE„4'ÖFÁ°5’‘F:Ep[ÌÙRMÔ0õoïº5"HJ)#.’í¤#6)$Õ¤o.Ø…)ÚŠ"0Ñ¥bÈèQ„Ѹ&bÄÌ´‰cˆu·^†ØŒj5 ÔF=3#.M‰C˜]˜o/Õïâ÷~ÞaÆ„DbšÃ'™¢Y鱂‡˜ÍCB&g×LF[o×QLi‚†•Â4ŽòìÏ˃GdÛ78¬Hý]Ñãˆg(q5ÆL¯;•ÆÎEêRiü\©Dyb9iȦaš<µL£Õö½w±Í£ÅBM"1¸È¥—Yi]Ã\†¶…‚Ýb»/•Ì]Nç†úséÇp#fâ‘›Œz܈òëÑF߉Äå .¡‚IÛµË#.oQævyÙŠxq=L”°¤XÁMÀ7E+_î9x:ãOƒF#äøT`¡’œÜ³µq›„ùCåÊjm·}#6±;¤½d‘JHµ!MSxžLÍÁÁˆàé#6\³•*"ÿ©©eã‡;ߎÏxý#.–q#Ö§i¸>NcUGEæÿÏ^ۤɎà˜4ó$„Á;>)Í™n$"˃ýȵ7æÍ–Ñ ÷šÈÙf1¡ýê(v Ž h«éC5ל`ò ã{Ô R#ƒ7(HTZ'ÖcJ:7Zf6NGÌQä8@ª‘sIÊۤ܎jì’Ú#%;qŒÂnwjÙ˜Q±¡3¿Áaªê©i·Š(Vj¡Š(bõëVðÖ\ÓâM´0kD>§¶U‰„zpNzb#.ý:ë8á“âJï}ŸC•ñ•Ov(ÀäO˦Ãÿ¡˜6ˆ8H^&ì×¥±Ó³?òtÇy™ë°#%#ûý¶¼·6é³]Vâ¡3‰ée›Á×GY`IÁ´Ú·%®Š ¼ï|HsϺš˜ÛE“H:u™Å) ²,Fj0ËsVºY#W6éщ(4ÐÅ"´4Yˆ„aŒdtªÙ#6‚#}´‹Ü¶`Ø5ŒMõøf¤ëÀºfmjÎ(#.árµ¯VŸ¢¿í;'\ãS—´)=ǸöòÒ#%'Ü€”#%›½ƒzÜ\‹n@|ʼîmÛ¿6ôÙ ²êö¶¾7Ç¿ eÊçó)vaÒ¡ÃHF‚Á‰ XÏÛwêÖ?!¿òÑŸŸ©›Ââýj—Ìžx‹OuƒÖwð4r }ðÛãæáŸ7>ñ§Æ(8Ñ_Ì]`#÷˜úv#%ƒHÑ=©“UŠ°Q0…þrm¾H%x‘; Öõ‹™ý¹….?½VI|gxûœˆq¨[$›î}PT=]‘#6Lk+ƒì ™•Ïãý¯²K»d9r_ Ÿ¹bD³åAˆÍÝðü(òìù· #6#føåðÝÿ¯c»ì²BŒÜÖÝÕÏtê~jðêêçßlúü3~i»Ónˆn>ûy¾{çñ~ùë]—‹ Þn¨`"1€Ø{ô°õ¯ƒ©Y.HGs$‘å<šÆë½\GŸ<ŸDºáëߧ=~ÏÕþÓCrqy!áã^H³U$ÅÕ2Änà cèúÔb3AÙîù½ùa.ÁGc@õ¼#6\Q\RÒ9¼ôUäÎ)u”Û¯ëqVÂRVÜ°¶·úûJ#%Gh¡ïÿÒ#%f—÷,ÛQ¾ß·èøvKx#% œè¡”#6€”»—wE·B¸cÖáÄîâ§Ï—¹øj<}Åz¤4:CíÖï÷7¥d€B‹b3Ôíh"™µ%“D˜€Í•,J/Îã14i±WöÑÛv¢e»véRŒIKûR…IXñ¥·õ‹jý_È^Ö„ÒVÅ&.Ìýü²Ü2SQui¶ø]ˆ!C†û^~7²^4snë«u*䔩󫺱5øuåw?çÔÕû%`¢åÑ©ÉãüjRD>=>ÿòu>œi>ÊÊ:##69dc|›ŒVþénmü/®ž~çú•´J:3#Þ'$!éþÝPY]oR®ˆm½$òZ¯&Ö¯SQUQF@ö#Ûá aøè}ΑÙCui±œb)f¢y&¨ƒŠ•D éƒ*þn&NSìÓ°%ö$¨áTò¹^%\…™ýÖwbIlI{VTe—ùä§îjÅ‹çƒÞ•¥Ô:±…Ø1†’µ&óWR÷ñþÑùùÜ>2…ÿ#.#ú6q4¿~¿•ù·X30~>êí{P?œëF(¢,Ö>_#6à3~ž‘Úˆ#6 ïž0\îr›fžÁ«#6CõÃü j1T,jƒ¥q¾#6Û6Od'@h'Á¤¢$„K“ð²åª¯1¶ œ½šô†±ùË=rÿˆ2·æðWÕPÅŒùw©1P°æ(óÚ‰ùPŸ™´†îΔVåRh!0ÁV“ñJ@-I[ú¤\T(–#6N=>$oŠ¿ƒïŸ-#ëüþoÌ}÷~?©¿SÄ>áŸçú}+ãä™SÖyà-[O¯?8Ñ[úF©î»ÏDÐÏcÒÿ½tz/Œƒº~˜ùÓïá7|çé6`¶œ¹‚ð‡ÍVúö«Ãñ˹G8©ç°híýqpä"#.ó$`{z—EÂêVsãbÆG˜¹ÄL¯“<„OeпõIGá=}}›’ô,k±Þá<öv}²#.¹—iÒå•ýº{ã¶+¦p±ß³0éý¹@ÒÍîÎÃ{¶uÁ#uwDa n'ª@ÆpTÏÝc#æP”OR˜1ß@y÷^¥ïPÊí8jßeÖÑnvµåô¸aËA¸cüU&dã#6V]#ÛêG²S "‡Í÷ÙKÎw-ÃÑ4¾Pdé9ïÏåp.Q¯„Ÿ~9r—a#.xR^ös›eH»q(â>ƒýºë9ÕÜ{Ϭý°±øï½áäÞQEV§ì±p2”/Xu|¶]æ¢c=!U%äi?Cù…CØcX¦ËìÆýýøPRv²Ä…¥24q`Þam;“ÿƒŸÕÛ¾×Î_\P Wë¾ëÝ2ÒÓÄ«>îþë¤îº~š¤>¶–=TM@òjj*©¢³ÒB''%=-–uûûHkû8ܪpÿXŽËؼ‡¡&>]°CÒp}!é8‘BvþZ>Ö-øÛ꘶*äĄǸ„$M®pƒ9@øI‡Hg2q#.SýuFfŸÑýÿ›3ñyXx{Pî-ô2Ñ°oõIöO¶ž–$˜¬'Ñ¿ÆŠâ4ÊјŠÂCcíÔÓ×å óí5±y¿oœü|zÚ1QÈøÎóK,,$o_»Cd œÌˆbWά¯Ÿ–¥éŠÕáZ¿V3XÀŸÓÐôüŠ¹CŠE.}•<åÔtÖšžv¹W‹:ìN#K»aÝÙ<ïíáËXÿZ0þ:4°4n'ô)¿©cç£*(ÑZ1F ©ûÍIpŒ[Å ]E}¹ýæ™æzï ‘½Ô‰ˆÈ‡@x#Muaͧøô÷{»þyú¾ošÓé6låÕTÏG7°èôÓ6”Õò}Y]ûü¤~zú›)ùí`¯]š$Æ‚–×=ù\¹µYÖïS"Ëàîýßê#õoÛ}¶?=º;~Îícvφ\·X!kÊôCÍ~8ED´Õí«ù¿]mRáã>^Õçá&÷_#68êpbo×—›;ëvدµZSqŽ§iûöÇ÷oöjíôgËÈm‡ysrËG÷t{œvǃƒh²à¢á`×æÒúôÏG7…m óSžÜpa»£:H~ êþRöÄj9\RAï·î~^ÌîéçÇ]%=†"Ëù™é»‡íxâ¦É hæ^†LG’ëe#.·s-”ëÝÑ3Ú÷ÙÌêÏæ|Û%”n³¬|ðjãWuzµ}F×péÌœ°çþS¶1†š¶¯‘ãLSèyµìÑ}˜í{ù`-¼-§z9Ðó%[¶5¡v»º½2Î;§'D‹?/Ã?À؃}öÛfª]wT¸(9=E)#.]ÙYY‰xôpaòÆÁ¿E3Æ£Ü0¹ÏѼF%Ì.lAÀ>2.sºUÍ?¯Öç½FŽ6e2w?.ì·Å¿–=ÛüÇn¢n–;å"^¯Nù]¦ëûi=Ü[ºó´<Js‘CÆÉÝác!»…-9ý6GŸ£M\RD‡8½;ä¬Dèà¨Xhÿk¬1ÛÝîöj|G#àef®zu?|{?ˆb죩]÷<qáó„Ø2óyúW»î4öùüãØì˜sfö_IÄdaÀíÔ2²#í‘·Ù#.ˆªú«¾û+ôçñé¾ÎïÒ-«alÙÞ¿(Œô/£#.lŸ<tìß+{t¤3.—üÂó/°Ú 2´@ÎôcL|>y8bºwc|{a²rÜÞÁÐ#šŸgŽwùv[…qýª$R¿S›ú>]ÚwæÿÔtf¶³×ÖžOåð{£<î¾ßÇKjØtcú ®ê]·yïìDZ¡¯Ú’ÿ°Ëà” F«àËÕ¤pøzŽì¡Aó:Ð}Ù"¼p+ˆQÕ‡“ß_«ÐÃ^‰/±m&îù<‡7;›(|]o”ÎÇO%~Wû™ß2lSœ–7µ|r¯ýæ†ï»,èá†vENJÞá^[#=6{}ŸÂ¶uýŽm™`žóùÌ~ÃýîÑÛŸ]nåÌ)ÖÿîËÕè¯m¶ÿ£Ïª£ò©#6ù¿GFqŸÂ^FÌÒìò7ðü4µ?w·ö¡(?1œ½è„ŒŒ®ßçõ`¶úO·¯¯Ûï)ÍÓ["†ïÆŽH#%õ"ÕËÆÐ2Æ P~kZ#%RE¦)#%éŒ:³»“ÿ£O2ƒŸìô„ý½£•<Ý~¯Ïî·•üsŸ?·åõuåÚ%÷‹›WwPì»4ô¿IÛø×~÷%IôiÓtêüÚoæòšº³¢qüµiAŸ£ªÇçÃ@ïîøgÕÓùj¸aõv{À>ÑÀu.Þ?üB…Í´t¶¿®gºç*¯õhÛDAþóSö䓈yGÇbeÎ>Í_ÝøþÌ)Ón¢¹8h¾>#.[¿g®øÔjý ¿º;¾œœ0v½=†áÑ›Éñ¯Ÿìá¡>1û4Šðx÷ gP#÷h÷óñÙÀpý_Yõ¥©Ü;ü|PåVˆQó•ñ=€ê?~‡u±ßåmAì#6`¿Ç™«–+¿÷¶ÔºÇ%ð#.ý‰Ã“¬óS³÷wt÷/vÎná#Ͷ?!b¿ƒpv ùlîâ¹ù;òžêÌ™/Ð|S¶ž9¢ ãþ?»"(H;G¾÷*Œt<&/»éÖ†ûçë’GK÷Dj`’à–õç9K#%#61ÜT'&D¯žñª¤½Œ[-2è+˜`†IBfH&ùÚ’~;†A΋wù´Åð8,†È¿{#Y^‰ýÙátÁôMÏŽý®7Ü}·»,°§EÜÜpà…#.±J‘ÔÁ€ÐúìŽR#ÕÃîá4¶LÜhâ+µw».©™À’Xê%äqh– !ô¦ÏÇ~žIèù}A7Œ®«wèüº!À²h;ÐÁà·-˜>ðÐB)ؼ³»¥JÒˆ®Ç“>;ÚëZÁZ;ž|%9µdC–PSx½óƒsZËËgDås£wr×+)°±¶1/1B+SéÏkÍÞ<k«ïQ}=:ÕѧBxóÃ&ýÇï"÷´¹8KÎ.óÉÞiYÉê¤kÍÍê” BI×eŸWRÍ “÷zž4åµ%fˆjŽ|x¥¹‚ê΃0Ϩ]¸RÃbo£<±Ã<âñrgóa^ç£Ç¼;y-¾çYð‰&aÈI$¤í"…:¶¾ 8w”Ôu—™²æ\ËÆv³i¯ožM°Õ¥ž„|Yïæ1xÑÀ{]°‚2Çã×^—þa»›Pëø-z×ã¾4-‘]»l¥_3T¿˜*|õ„)8óÌõ:„aΓÉ>·_!†(£«F€+~›4sgì¶W×M«°ìÆç/Ïö´©ÝŽß¶£Ní3¡hžl‰“3û‹Ý-ÌØm=O‹£Ûóp>Ø#&þQ¬½0â/Sß3 þ>mM©‡ñw“÷ì\•ý\}ÿ“—$Ùwü÷y¡ÔR‰$ìØú|¡ô_ÓIØÈDí'¶1ulu³LׄãÏú[=8á'x¿ƒôúè3ú†A¼#%Éé/SØC¡I%yb1ÖFÉ¥Góü™*”å˜bG‹ÑºüCª öIO4|žÿHï½cÀøòŽ»ÚcY|uŠ·‹>Sþÿ—æ#`ó²{ÑÊÌáþBÔfeÌ©cv²Á¨9G…?G¥F5¨4iTlTŽ Ô¢¥nØ€Ðæ7#6ªÒƒÀ¢¢,tþÍÚ›HÁ¶¸}=3†´Ö¤On`˜a´ŽÅ-ŽÀ¦°Aȱ¬a¦ILA«"hhn„)"–4±[VWŒc€ì'Ë#.Ss¬6ÐÚ]&6)":ýº[ÅÖ¡É \’6ö‹B{qZ8dÚu‰ýÿ¶ø[Ñã"?›9ÌgBPP}‡¨üÖÊS<¦³ =a‹5þrÐÍAÉ]E··cˆXw\Š1´™ac0]WëøoýOSø\ýVæœ#¯#±E4DCÄHèé@ŒÖbï¡i7¨1lXT¥Q&FÈCV ±6Æ›f’J¤ƒÿj*P´Y¶Oà9þ¬éšq·ß›åñãrÌӛܙïëûpì—A¾öFþÞŒi|œ-'Ôl³D±€œçj·UuwåËu¾_…ãŸÉfºòöWV¯Ùãõlò:ŸŸëôeñÖ1ýèæÕ»ê(¦áLS·ñþ>ܬù±ý9Hþÿ<ƒ·ˆjÄU±Ýmù}Zbçvù–U²«&¸ê²·ü³×†íú¡[GmC……fŸj#6#Ò$pçwÔ]uGÜ<Ý~ßÓø¯Ó§Ù£O«¸¢Z„xûþ#%8g#65U6ü;ܶ>®aL~ØY¿w?VPèÁwÙu~Uwö¹\ç3Ü®“šÀÂÐÃÉÑkøÿ.¬&5)n—|?±½¿>¯&¿án®nàM>äM¹¬ý“ûôwL[%û"ûBßæ<ëά#6)_@è¸à\S¼|8¿ÁéÎØÄÑ›^F+rEµh‘[J%c§l”õX*5¢gtõu¼^´yçœÛÆ9ÍfRÉ¢((Œ©[\0°Ä£#6’ œÙiéÈ¢ Ôch¥b…€È£°µÂÂ44W VR²ÙÿÒ£FÁ÷R\æŒmFAKTQeBÑlB†#.°Q’ƒtêMæîy3‘wä%r(*`¥2ƒshÙÀXœ;Ýg6®¶JhÇTChÆ%™8Ì+:ª-!ñiÜtu,¥µÌà’‡#.óÅ!ÚÐ@AFšþ§YÙ¬†žuD³Žmé¾#.äQ¡±‰‹$I•8‰ì~UÒI‘#ü,D¢Ö–%\ȪrF‚(3š*ˆÂ®ÔÊŒp#®²R1Œlž°b¹ÓòJ®x3¸ø!U†$‚’IsJCÙõC‹ID&é½’ôAŒô;/ôôÆNô˜}€Ë ´íäë-ûLH ¬^Þ@ u3ÇD>Õ÷z“´ö#.QùUôhÉz°÷ý”OT=¾÷góxáˆ$g±S—óPü-ΊÃáÉj»#6ý£×£ŸLPL*ø–ý§f¶£Ô§ü!½ëÉ3§F9–û§ùj~¸îîh¨xÊìƒÍcÔºåøËãïÏ›""'NõOU87«6«üVûú„ÖY´¹ÄÞÏÇñ}NO#ûjÒõ@Tõ¹€â9Øs‘•ûÊvA#%_œï†${Ó«”ÔŽVì6TÖÙõ¯’=óø~ç–{àäm·l’_/ÊÙµÇÒBÐ’Ä–'Ÿp€âqCó9Ë‹è‡æ÷¯!üyI£×<³ì©«.V|¾ŸÇÕ!‰æª£#6u¬êyÄC¸î'·>Åþ¹õüܯ±öÝAe;6LèǨ4«9ÎCH¡QH2t2˜wT9"+1r_uLUv¹ä“h—ì¯F¬ÿÂÅê‘€wf\„F±>ž™[î÷QâZ%T»…#6ÏÕ¬ªŠÿM[hÅe”£$#ˆÜ‚±#.D¿ºÅlÅ˦ómòØÊŠŠB‚YQYCFF¶Ï7®_ËäÇÝã³áê_{HUuí;wùüÿ§OÉ“Aó¬º!BAT‹‚1žsa虑sÊ,¿] WÚh&6p¦41-6—·n›ÑHÚ)¬1=j ±o\ËA„y©Y¦=Zﶊ°Ì#6Ô«ÉgAèpE30ËBƒH¨Á‰”V¢À*ø¹¢hâÒñ/mˆŒM}RdCÝ7ˆÌy#6Ú´s¶jdßå$ –8ÛAmb{€„&‚ãì͆¬o*ž©H¥›·86AUƒ„Œ‹³àrYb‘ÍZ¨¬ÛÕ§hCZ¸ÆÛ…‘2SSDÍ#.H#6J lXdL1aˆ4KDã#.‹G‚í3³u#.Ðá1¶“¡×l°lôÝXÊ-“m(Uz§ûÿñhÉäŽø1†µ—š(UX¬0âØ”©DƒlópÈ#.Ìi@¢OlƒÂÕ·åtS¼'Þ30<sÁ„l˜Pt⢲oŸ»ø¿“Õæ#6” !B#6‚-ê»o.O‹„6öÚº<AÿlqgºŠØÏwìèõfñf4ÙËÆbýÖ}æSuƒT0Ÿ‰’‡44V\áÿwáîÞ:ŸÙìÛé{yзc•(ž~Qïɨ<Þ O û‡Š¹ñÓͫчJt‚zAíÜ'ku#%ùšÇ"#,,‚6űaiUSõÒªˆ4ã÷]áv,ÄDjGH¸¸ÂðâÒŒi§O®|´°Ud—N¤G#"DÔÂfýV5[ÒDê*#.m4&’Wç§(¡ßÅÆ_ˆt5³…‡¥ˆ3MéСÅA!ðy½ÀÐ’ÑK¥¢#.Tˆ£¡·ÆóÌU:¼]*«EPˆTå·—ôùdì×k»¤kmó0¦T¨J5Ël$2~ßØ?sÏêê¿g¯Ì:?.\`<<øâï‹«@»6žñ™>\G¯øøJ½µ`CT<ʤ2+r‚i÷MŸ„š -+A‘¸·Tc£´øìz^Ƕò—ÅG=Úæ4Rë«Ùî65ÚaŽtHÇ‹µ0 óF‡ÞG[q·º¨ÊN𮋳 þü¥ÅŸ™ˆ¶<ª¢‘¨6Óˆ®Ç!#6^¹"XÞ<(²ÀÐœk]o3ÆÌÀãH›5KœK¸´pLÊ,°¡+&°,”Êp#.#Za¿*V>#%ÐÎýhÂ8p™˜Í†K§9æe¼‹õ².l‘m¬8ÁV6>d4#±C§›à¶——ÔAܺÉ&Fèé ᦦ€¬ÒKbíV4˜Î Ø=ÕÝ ¸Døn$DN÷»A£PÓŒ+gb†XÌ‘Œ®çFPá2-îc¶+Pƒ¥ò)2ÉÂ2™+ç*³v»ö—w?Ãzwò_Ûü9ô[ß™£oƒ×ûiþnþAý— èyÔÆÓýb'j¼ç3Xtß1ám¦,¼3êtäzÉù~îìa#.6uqöMÛ‰ó¦q9°gì,ªÄÊ;÷ ÁÍS¹(v-ª ù`kb—ô»6ßV۾ϱ£)‹`åÈf=NÜRäòAUHtF¨ÚE’ëž1’œÕOSxWxe¶ãjh¨ˆô©Siyiºf6äÜ‘R®©n1v6«D˜E¼ Œqì’T'wÜÏ€.›gÊh#i™£^ÞM¼h½óé¶3`ìQ±q’&Úri3haÔ”9†ÙÆbX±AO}`PwÍUí«3Û¡~ÕoÒ_ÇúFÅš°¤<N=¦,Ü9}Ð'pø”F_smŒÕA—#6#.¬:M³±¸Œ-µÉ°àzÌ! 1IË(Dâ8ìMpî@Τ2ìl+ñ"V½†Üq·ž*t;$î§iœŽ¨Åo›;n3†Ù†-ÏŒu#.põQ…boyN×?u°ƒL…#.¹}#.IŽƒM¤$Á„ x¶ï‹)—Ï®v¼îÑŽ±nú¼…Ë@:kï¯#.K¨iccndë‹ÔªÞAåj«[É•P]ÃK©)pU7ŽUØ"wW;´ÇƒˆÁ’ß’ÛÄ!ÜÔr7 •JQoùŲmÛZé\¨ÂIAƆàˆQÞ#6:26#.Š]î;ñ´ïC®â¶£¨ë³º ¬›ÌK%c¸Tnq°QÍ峺mñÙºâã!X ÷Ðk¶Ö·ÚåÇF=ˤV¬v„NŒSuÕÑÆs'&vl©”4µrÍb< vj—N›19ÆbÉÃ"°Ë0NrcZ6ŒßW$œãdŠB\ÜÖŠëwl#6Ó¨cÇ.Ûldå¤É6µ&†Dm<ÍŽc/³Ür±±5ß ÕoÓ:Ù›D$âoµ}ä«‚ÅÇy溽<¸üþY·íhÕŠ@óμžKaNâÒW•GiÝ/_³ym§,qGr0÷GÂÜœFŒDdªõç›V׆¾†’¿òÜdIÄäF6$ˆ«,Ÿ©…5·\m4üŠb-ßöÐTM^í¶"R§".f哽ÿ¾ºáwu…œ¹RM|an‰¸ê!Ô¥•“ÄAl©·KÆ<F–°´‘¼)ÁF§P6ø¤,½¬-®eß4ð¡“RàoNYyÉ\#6wæïÆ·¶¨Å€ÎînŠ,ÿ…ÒÛ§Gt•ïŸJ“L˜L™ kÞ8!ÉLº÷m?oõxtîåöÅa^øñ×4@üÃe¥ŠnÓ8¹búÇUÖµÕim;'Âyƒhz7,íÎ+¿‘öÉ«þÇyDCá÷˜šT–ToƒÁ89I°šY¾ç´<„ß\±`%dn½;,ù²BÂå68ãu6«7“Ž'}ÆôÛ^÷–YtNÃWdÓ;=ä/fRͲºòc p®FiùUóískˆ[áü3¦†¯²ŠŸ77И{¬1F.™LÍÔ4éç¤<³§n#%·SœáGFÓƒ9“fài©CÓzÒ¯,6õbi#6‹aÝì®îÊómàò7ù¾ÅÚOf#.5Í`ùfz×¥q„aôËÊǵ¦ô»u×[AŒ(g×zÿ”dÅGߨrt‡5:Ú'à®üc´vP¢¨$‹ÞìÛ³Fáàtx‚‘Ãù]L@¢Ùù›¨öVA¬^–yw†17sŽGqì€AÙƒ™ÉdfÒÒ_—Ä£¯Ãó7n·hÓ'²8u,Ü.¤ñyƒ¿0t¾óO`šD…Tò#63ɤ@ºÃjúP3‚<#.þ”‹‘FRØ/(?€7óGŠá:ix¤ú{¹D€ç#%²yþ¼#âôØž¢€8‹=lè{ý}å¥r¸¸éñ¾Šáç\Íï>’ïø2Ý䀹ÐiߥþCÒKíWnר>áüpbx_9³å¬×WÎŽ@žTÛBðÑŠZ·¼Bì–”b>ß_§çcOVz|ymx#6fǼYJ˜…²-™Ê 0#6@ŸŠwßwvOóÓ®ËÒëÎ!‘Þöƒ‚xnpŒF€°Q¨#%Š²ÝjŠ«ÀÎÇ·e#1T˜´ËRV¡Ö1(3‡È40° cpç¯T‡ô沪ÎA›œÏ¨9zâ"3áÅ‚NÇíí<·¹“ycÐœr—v>óZªQà:¹ý탂%ÖД~ND|²EÊÉ´à÷Ô%¸< &ÙÚq4öÁ…)UyøèÌùÿ_ó–Æu«޼?ßàzg nõ¼;qËô;剚ރ†Ò„&LÅSå1ºúeÃyg²)IVì–\—.B„m0J°þ’[qƒkdÅ°ó#´-©`®Rb]8ð3ŠÏcÝÇÊûO¥Ÿ]ÝB“$ÄKÁÇ|\ÀÈ›|jÌÅg}'ò$j•a±áêȵ¿<4Æ=6=&¹|§‹ð»®6…]ºÔOvé¸pX»Ü=ôTŸÖV*yumžì…+¾â1e¼#µöÚgDtž,DãFQiwqlÞŸäÀþµ#.س^O¢ƒàô.é¤îÝϪ’‡-ÇIÐ÷]ÔAõõ…îÆQ72„I8€ïxL’joLk.ËŽˆ¢MDz=ýtï&½ó:oŸ$48;!Cªpóa}j‹ bÝ›94gÖ|«¯3ß½›µ4#.ÒOè%èÛÔØ¡ñ–ÓÞ:Î<4ÅôE#.54òì3R‘¬úTW-Çðk¥³øyçÈcUéN-<#.'çik“é;!:-DŒ¤§ 7)%#.M;î·$®ƒÂ÷h.Ø–CpíJi“˜5Ðéï¬Zñ™9þ®ß#%ÿ‡7±CðÉêpC ‚Ï#ò‰Á´ð ü¹“ßrÞÓ¬Grɧ嶖“9¶šE„“J¥™Í•aÕ…ë¶ú`xf¤-0è´%ùk‚ö2#Ž¼üx#.|^š:+Њ „ÒÉùÌÛS9lVŠo•azâ3!m²<…¦y©CÞo{vþ|eŽûüÿdQ茌¶F÷T½Ä½Ïá~;¬wÚjl.r<0XîÍ1`àžœ1ß¹iê1æйºKj„¶Ó§\ž¾¯‡å;åd“ý|†:ó»OeÑ¡ÌìÉâcs§ãò²Íc↛Zøô<«LÁ“QÖE-¿”Æa H•àòµ0·Nõ3\JܘrSs t UýÒ>\}[ˆìäåúžŽÞc‡ríàXZ.ê,¥¯Ô#Îfi?ú'î¤è—DÁµÊŠ©æÔ÷½`£Ñ@ˆ¨úã´#6ˆoÍÇ~ÏhÊ0*Ü×ã:Ö hïÿKC¼i´Î|½_ðÍÓÃ_.¼71¼§½Âu"˜¹r—Fxô⠘Šç#LþÒ¹Æky™—óé“>Q†9¬€Àȳ÷›#%hÍÊÈ(²…Ÿ¨ˆž+fLmìßãh”}ƒãÄuÚŽŽBN[ëR fJé{ 2ñªA·Pjפ/ñté‚£YÚÚ$ùyPñŽÖ–øêú뢕‹@…)Õ§)O”~U$BO3âud¾‰Û1/5¿<’k«æ‡U«xd›í¬½ìu¶ø¦ta>y6œ¡™û5œÍNîQápNHèºeò¸è>–·D.•÷*EdÌn2,Y—(迃f—ñ/¥sµÒ›ñ˜D‰µÒ‹Óô’ÌØ—›Ð¦wN—¯®ÖIóíîç#.¤PBbÛV¦u]í‡Un=Eœlã‹·ï¿>JªE·":Ç”Ì[~ÊÚ‹Ä&ÂOš¨ÁÄ)ƒ%&UrfIJNgq¾aCF¦p—zP‡™†vß·SC…:l#ƒ:=ɪ/¢6<ç0DU¼F9‘Š¦îÅR3–}m¡ð6‚£y)¤8Õáà7Õ¿QúÜñeìî#$†ã\¦Æ§ò^q1®J=f3£ÕÍkÆÝ$·LùÎã{ˆ–¦Ö=Jì‹mŸ&@׆vµÑ|^m÷"Xî<ºãcèšQÕ¨ŠR„HyrÕòá#6̪\hlæÏ„„#6Qɺ*h9·¹Œ52g(eG=¦!ƒB-½FäÈ= *\¨½[=Ù4³Ê…ܧIm¯ƒÐŠã¹Fo%ë*œ¡açgþsé7s”¾›ð¤ß>üç:¶è$qCñntl ûÜÂŒ=³D”OåùÅ@à}Äýó;Â_+'ç» EFžÍÇD_½@Ê´Î/HÌ(¹%“üñãÖÛ~!ÞʨÁõsÉMžªG4E8ŸˆyKåÒØÚ„¾ôc6Ãä´ÜàÎû6vâv¥l½Ë#.‡g“t>";¢ïm¼Ùu¼,ÉvõutÁ=;2hä!0ßH¿(âÞ}´s^G ap%N=¬c¥çµQ»<dZf˜ÛêïÞWˆå—=¾ïy±[g|̉¥“ÿi¼å¤2ÖÉüLøÅ7Š¯#.¿n˜ßF0‰r ðI8TÝÌŒ·6µD$‰M/#6oñ@üјHÜ¡—ÈYe—V×ÛrÁ‘D¨ïDÔ_‰MºpàütC¯‹y°ý:Ç1_G’ ¦h.:üzÑÓ«‚“&~ÐbªÒqëM—N6kÕmcí99º×ļkg‡X3sS§¶‡–„ûtc˜¼/+ÍìF!ä÷øŸ†2xìåX?]¤BM#%èìùRÔöÁ3. s®)©³ù3F~~!Äi9þ×8ÁIütgHC©B³Ê @é›Fº×E¸éÆÉÔtÎxêýbT{ÈIZkŽšÎŸérÎ[J(7ÕZÇפґ–é½¥“°ô4%ngW³U¡p‰zÜÆš.D…c·Fó\Ïå¢ê ÑsT»f«ø†W#6/á9=jÑ´S¹e‹EØàö¹ÑãÈB#%ÈX±uÏÊÖŠ¬N,&6#6B‹õ=p]Ù#6fˆ§|:×D¹aêF/þ¿©+6Í|Õ‡,2Ø'7;5mÌ©ÙÂû]Ü`x¿2°²kŒ\U_v‹¬X)æûêëò°,àµ{wï…ñYžÞ%åq\jµ|ßxèªÌÕ,`œRWn²Æ°øÙÍËM0º‰ <X5…U’Ûo6<-¨Ç)d^÷Ýb#%ûðçÚ°Šò”®[£±õ¾·>Iz]¸lð«°S·#6eŽh´³$'°´¬çíHf#6^Uî…Ѓt.‡.k„“¤ö#.п4âî6zÛXÛPÅ¥½“\9šÛ1mðR´t}yUQ¯}[;î¨Æûáožs„˜-÷À•<¤¦TvZ§•H¤sÄ•éïã寻Ë&?Áß™Æf}ôèÁ>WÞ³ÅøÕO‡Ùwbe'½øëåÆqžçŒtÕÑ_I&5q¶–"æ¹Öçgõj2àE°™©£•½"*[WÕn¬2¾ëAá¦ÐM*’aFkmö0*áe"ë²°I@.µq|ž%hy±gt>›ûõGÌ0ÍxQ“¨ZQ]~¹„…LfÏ9ÖÑhjÀ’®“'»YžtX?PØçÜôTðQäj?¬½Èo]YQµfÐ[DÔ³áGçtP?ÒóèþØÎeϤ;ûmð÷\ã4•§á˜‘x§5¨íÍKèÒ@¥ÛY ü–—°¡•³([¥uã`£„"ñ%K ¢%b5j!®#TRé,Ы©’ªö`A¬5w,åu1ž…¶K#6¯uÌÍ+;¼òzÜÇEºâko¡.¦ùÕÏVÍ߇Ç>ëä(úXòV÷9Cg–Œ ôuª<²w]ÇÞ7ÆðÛþë+™Þ#6–~J¢ÖÞož8Ð#!žõþ³PJ†#%)W&8Ø9¢/ï•®Îo7v°Ðv¸-õ 6âm¾Žæ¤C+«WA’z)zÄêéVµ{„Ñuå|NÉK›¦Ò¥tßËòܹÆ"‘½E¦/séÑWa›Æº\T:Ü©ÝÆðñ#%̹ÖovJ±$o¨e›@„¦ëtLE#.õHáÐ’æxizÎ@pžßyƒŠ¦M>úgŒV0P´¡ã/ïÚWFŒê ªÆê°ÊgR0y¹ö\÷›Ü+nS×GJÊÕ–^I¸ºY©}ªëÜ轄BÎYC™;-|ŽCÄDfVšsÖ#%Û5·\a?Æ ÖÖv•s>×_Òë5Fó¨÷ypfÝuÓÊ#-/Í\¿Tz>#»¸dqÄËÍKÎ{e¿"ϽFòæŒÁ³Áµ@œ}#6#6pÂi†1šIc¯1Ž¡òª¯ÇÃ|èŽIm'T\&IËlgï™´µp/jº~?†5óÎvÝ ææܲÓ5‘bŒ7fÓnº™N3[al@}sÙ…_XƒFÉÆÛ„ß'mÂ6)k4ïÞ',Ö#6Ö•KdÒUÅCÞî… £¬ô#6F6¡H‰Dp¢JÊ÷ÖÚWS€“ä²R²sýÙí~¥axÆ«aázß5Ä7¾m.mí*E¨$CAlæ¦BIm*øÒ¡ò˳5®Ø45ÖJ·jºï9}æžÖÏxôHü5Z¸QŒ<Wž÷¾»N³VDÕhU¡|ãf+ÏÆ<é¹v„€^-~]ðBäA`Äæ‡n˜ð¬W…éÒô>·W«¯ê‚ñ‘B¢(PŽ%¶ÃD7K#..mØÎLjئ+Ô,#e3m{wlˆ4º/}úî}¼ÎYðsEÞC9H2ƒ¶±ƒtflkA²´ÕM1‹C,áî…`âl„™Ø±„ëkù´OIiN•}¥áù#6nÆ#%ààÍA^†³(ÆÀl˜!Ê.±o7Bù[8fX»HíÚ€Wzò2ÔÕså@ç¬É“¯ËjÎc¯oN=,qkóPxÙ¡ÀË·qÊX8~ =Þ¡O½íÏ’‹Ó¯tÌqÁ8=þø´¼Âžûx–’ÔE†@é#%Åmp‰•‘ÀO˜NÐ’³Ïaj¬ôe“³ÊÈT†I*ƒxzÊÔ¢ˆŽ4äãlûGüGþÞtОq͇¯5K¯ìëâì·VžÉxdÔÊ*Wž6—XËSm³¾‘$ðH/᦯°FŽì¤øS‰Ùà‚<?z̈GP-’;yZæOµD5Ë·gºë«iTÖêœÏéÄ2 Á¿ryº~NKûôøÌGˆâkgÑêv|¿Wç—¿êþÆú~xøLη²1«¨\8û}ÑÓ®ŠÌW¯Î6—¼jS…;TǯOmæÜuW<ß±©38z†%~çrý·–›ÙÄ¢3̦ۤïàåq^˜—úðN¥gÑ'u±k//u65—>Q²{Æh‰e!wº(¦±Ïš]ºÈ—c!‡ïƒÂëOZÏ_…ÍÓy©1L6ðéšP¾·«>ºÃezÑa¦ö_j¸ï©~Z^¢iJáÝtQaè[›9ò-QÍïäzí$o“‰±ýL—÷æsaÕHR1€ÒTÕ ×¡Áâè*aæu®|b¶ž‡µïææyÌ„…Ä“{Sê-p5cÞ§wsÕ_L›ÜÂG‚›ËÖôöû©åŽ¿¦àc}±Œ —«ûû5I³©jJ#fع¾”ÎM$rÂKLîg¡sß9a--JÉ–,°ã¾›õ½`¤Ç|ír]xä+n/ÑÖÕœELm¹‡˜‰‰›Èb3ÍO]aÓ(£ì!^\C᛽¦Õ-(r"„ÇFgRÒ¸nXم꣥ü1½„+/!“ÆšÃ6d#6P ÕdØIá‚ÇT”ÁHS{ÚÔbÿ3Ÿ1棆…N¬ù\¤ë€`- xŠf<Ž3·¡Ù#6 gî®Þ|Ûc$âSâ¹@ág"Ä ÀÝ:9Òà»Ý.mÚ†—¾|×[6âjžg5sÍ¢ðºàÙ½KÚ=WæˆÐ#%U)Aµý³MU7/”Îà³E³V¶û³õb>|ÔÔsDD°#6T2À¦yÁq¢Ð²#6èü¥ŸÒk1~6ñ,ŸÑå%Øâèûgû—K°¶ˆ"hÔi2KúŸ;e#6é f¼¬T¡ÅX]Ó¦h”vÍÖ°NZPqîWP¦õáÐä²I¼|Æág¬í÷ži ã¢\ÒªÊaîa·Øé8ºibP» ”Jr¬ÑÑkLØ0óöÖë¢vÝ\ê£q!ö•ytŸØ :÷Ôܵo‚û<£gŒÏ=Éo6¼IÞbì·låHÜCZýn±ù¯Ö7Fn²“žd»Ê¿_¦Ö„VM‚ü£cîƒ:v¹+F;³Ü¬)&#.M—'ÁÂ#.›Ë‚¯£¶Œb»ÀYÊw:$ƒ†±å½À=ëZ×û°' &hhmZžñX\Ë,€ÊŠÙ9CÆçò¤!9¹0rÊÜ=ÞµmŒ©3¾¶[…ƒ¿è‰ZÁW©TƒŒ·ÈÞÝ+(a19Å‚‘Ìáë<¥Ñéæ¡ÜvÅF³Ôè©xñ‚è~ÅGÁB UU(;}ný½º¯œà•Þ¹™Áµzoô]4Œ‰B:žÐ=.®atÏ$³ôÒeJûœä™í` r=q`ÉiCÂJ¿ç¼ø”D¡ùâÓÁ#.BY`œ‘åfYB¢òó¿Ay ±U /v¶ /—C—3¯¿±òÔ$¬sék:!ßr=®.ê«Y8=}«œ[SdІ*„¼¡÷M¹õ(œö´¯RÔ˜xÁ5&œ¾GÔ†¢¨)´èÉÄp¼Ió{ ã™.xGtÆ¡žÿ',©iAxdÙt½×ôY»7ÓFšs<dçWÙÕÈߣlõúÅ`)N}ÊèÍ™fK¸H™}öC¡ó"qÝQ†™/Džq×hÞ%Y“NuðùËûaØÓüf6¦œ‘O}_+Ës$õÞ;œöçzìm$$L·LÑáz¯bòþøËžš*'%$i:tÙ™cŒ£yªâs‚–Qi':-'‰™·(Þ`„b6†<-‹ã8ž¨ó.£uÐé¶ïËmòÚ]{¿ÜÞg·j9 ¨ š^æJª'ÎæÀo}Ò‡¬ÑÐñD-‘‹W8g{Èm´“ˆqná†êkÏZ9æË9ò›ïŒùÖ, CŒü`ÅNŒ`[£½Â™y#6Z=Ôs'´WïÏ'dë¤\¬ù¦¯ÇUÖ|MÛµÃÑŠé{ùËzürE÷õntœwšæi0²J:Ìñímã¢û åOäzã‰-¹|o)#XÔW·—>ÅÓÐs=o¼[DØЂunÄ2¨æƒû‹`Ü1˜ÛNAö;ñoó$³¶‹›¸x‹<¡³EüqÊíêK‘/–¬ÙßbÐK¬„/aÑt÷h$&c„%n×çl#«'¬s¹â™(éT¿·iاۤªËò¡^v*ø1Ö.)òFÅ<eZÝØxA)³NTÿ(ÃöAБ?”>?NŠá_§X#6ãûbo1QìÐb¼DzÞÒ¸Û¾nB¬htRߌM~Þ¹Ÿ¼çhæ>(â= 6º†6Mº1@ì|]½fÏ.×ösÛž(õ::l¬züø³jîOÁk)¾pVzקk÷û±jÔ3pV×>IÎf¥$™Jý€?LV2e*¤Xpå¹³‹nžÈ(#2òY¸zk0Ö9êŸ<õ-w¨õ°u¡Íˆ¶lÂnIv(˜"`‘îTaŠÙߦ:b×däB®zí†GmÚå»#6õÈyh3å„Œ³#.¡™Œæó‡vÏ=ýB´Ùîïj¸+8;å>N¼Ù8›dÃøݺÌu]:D£~·/Ò®*©z½pIs>•Ó¾Ä¶7¢ð¾ìæ|¡Ð.ÝC×o“ü<©Oz¬Û5*ôÚÐŒ}&E§xãÑŠ»ó!½SbGeœžFI-ÉU#%¦$Ô³I Ù™lM,–01Œ*Fe`÷ S~S G>»2@×ã¾~ï3¬ZÂË/ÒsÛ¤¸zµkÓÆ.xï|~ñÍ‘•D¤#%¼Û鮈u™¯ÍÐ*±§Z#.ßËn¾‹€P>¡…uxi¬ëÍÔ¨&„'a¼S9—2)igkHÏíð|·¯`çÛÜ¡à‚iÖÉÛS{Æ`'Œ+æU<:êI,öæÙ7¿_ǃVñ5î‡O/Iú~!®ìàœZ§ºÜÁáÈR¸÷pùqyàì^Km›ž$?«ÿr¢‡u“à;46CR;,VÒ¤èøu'Ž/•0Î?í5¯{íÀ™iÎÚÞÜsü"Vîu¾ jOð3ˆýKÉOäýN“w™³ª¯ÂÛ¦/½6¨ .¡#6×X”rpÑoHì~!ÑRKz¹£%dn(è)!ëßžnÈ€r†lšÔˆ£§‹G’ÜŸ#D’ÎŽ¸N5›çþwP$È~žåtÔŸNBeåkµîM„ ‚ÏÙènæ²1Â7_êÝóÞ¹¾?5õü;{Óº'àãÕøÏŒ¥¡{ùq(#%`@)ïõ(õ§áôáÔŠç°Ö9MvŽ#Õ(JL+G;I@À€ðèz¼xÔ¾(H)æ„gëxTWÆ®òØ]—÷ào»ýEkù©\ë¹lIi?K¶Wò¨Ø”‹Rm“cÚ-«®¯ñþ5ù³Lˆ–ˆ…>(2Í›ÊþbFmp–ß‹gË~:×ô@ß4>$Ë_#p¼$’ŠÅ"‘AƒY ä&©×‡™z½:œžÛ¬-&OŸÈÌySµgÎ]–ÜžlÈþÔÃfÁõ¤™^¥«Û¾÷Ê“Ao¼?“ñxüGŠ5‘ý7B÷ØH‡Ó\™ü¯íN9†1‚¤IY‘[Gtþ#%²©‚¨qHTi*7+¡þÜõÿ—‰Ëò?¯YC‹‰xþöaÏ(¾zÿ~[Ûá_ÚÏ]>Nt©"rNK(ö·ËçzÔöKíˆd¿»æïžðù´!Ú‡Eá¹tî›V}ëªÚÃÞíèÝnCiü˜]÷ùY??ÎýŽ…ªz¯nÇN¶:*F`6$´t%5®Ææ󳃊\ª#%±|´ãÓ¹ƒþ1“ì?£¾í/ÒÊY¡°ê8KË,H|ñ“‰"~¯Ð©¡ýj!Š#.K©wk|Yž™²äèYû=v&‚Sú7åí}f“›¥h¯P#6ªÌ·Š Ϩ:ý_Býš>¼•s<7E‰a§VNKÎõú]›(ú¶ÅÆQØ/ð~¶w+E@âõL›0½Æ\üƒª"XÉBÌÜ!œ?_þG7‹L˜zß„çmßÂÇ>¯+e§m¿—Á÷¡Øf c(QÆŠ#6‹"šýu9¡Ñ#.P?KóO¹ êL¥09]#%n‡?Ÿøí®@¡/T¨#%ž½t xÎè†"RµœKš Tg¹‘L°ÇJæhZ‡\j ¨µ+r¼ Lt#Èæ"#%oŽŠ®¡òËÒ(~†—ª#%¾± ´é§ëãcßÁmÃfÿU#%èõ@ÝEôÿR ðºØŒÄŸxi¯Ðþ•ëôBañŠ¾`Ù8ú&ØH¨Ù6óòP¿Ÿˆ€XøcÚi®à†í´ÇK™÷$qý4¹a‹ß-)Á>ÏN99¨“Õ‰5v8ñ:je`a"ÅeOXï2€ƒ~ƒZhCq‹ yžÏWiƺ(T¾¦Û¿ol|ïøH,ézþë–(`L$¼{ †9gÕWR|1€¿{ÕŸ¹Ýø™@Äyi<×Þ€ÿ$ïèLJòS(ò‚ýäOgÏIëŽÕ¥OSIïô–õ#Ñzüco*(B¢Êvæý?Ít,¡xë©TÏ[]3«X(~Š¤ž©R”#6¥`UMKõ·;ã6Á¢(ܣ؊y·½?=ÆÝа섀>Š©˜aI\öñ}V$øÁ4º2qÇm¿Êe#.fqpÚi”ÌkZÔ·ü(b§RfÌ%ݵEÔR1#µ¤Ûp2„‚\4SywE9gŸ+ìôŸÐmÒü!Œô¯#Àè€ (&ë]Ì#%¹Öië…uQºâý:vêÙ®¨ÙJË5Iší4ôýn½2‘×m’pŒè«¡ön³DAñ|],ʈ‰[#6Øy`ïÄ#6€ŒPIµoßÕwÊîŽ,ßlN¹¬ç·Ü"‡c;þú†Y³:=Ÿ±Z&W¨å#6‚Çf½Y¦ÈÈ`¢ÃÏÅ3×8ÈŠ7K±´-•T"EÃvÉFQ`¹Š\ÏP.Dp1C¯f¶Öoe¹_}g¶âúà«âc'‰ŸKš³’T¢©Ö¨`¢XD$¡›\æ#6P8ÌüøÀ·Ì£€¾ý—tb•ÓÎOIan0aÓÖ€LŠ bBç#.°‚ ¶ö]\ñ²òBET dØÇ(°so¾)g®Ñà#.4FÌ·e¦`1ÜL`òyJœIÅG§³Â¥ô ï#?øYÖzKéÄ2÷#.ÍÔþ:·Ü7ÃßüÕaÖNQżÿi˃2}}Jo£Èú¯.þ³ÊÍýÇÐïL_fs@ˆvô#6€Ä¥Í7–;Î_{Éž‘Fy"]yXºmý~,@yÆNr+(kîØ®™ñ3zL;,Ÿ"ì¼BN3$ü÷>åq6׿yðö]|'<@¶HZ°¦ „Ýè[ï*¹süÚÍæðX¤ßï”@¤U-0žàeœ‹{¶Ú3ÀÂÓ“ƒíTÆií’7q¢‘I¼y»Î…—'íýVç?rÕëHƒŒõ!d=1#3tÿe–®ÎÝ~AN{º4N¡úÓ‡× ‚I5Ù(åtD# øŒ uÕ™Ü`ÔQP+&Ö—á`KH³ZŒhð!É î¾ÉA ê(‘e<ýd’¼þ½eý÷ímÊ5^äÞ¢)?̸Úþñçš–6`éØÂàäâ{î£Æák¼dVÔ™2S„Ä;¬7ÊN¤@‘`wdÃ@à¢`s¤˜á59—_E·OR« _ªzŽq…›Ks^GlÞ<ЈŠ¹\¡±BÖ³žBdF&!Úë\[S®~zÞd'¼ˆ’¢$6›ÒÙ¢½ÉŸ6NX |¼¡AÀ(噘øïÃrã;XËq-î¨ùöˆÎrI{@Î{"{íLÕźޜ“5y\ˆJ‹Eã›%9×!}ÞCøI(ès=>§X.~Xp^‰éó{ì¨k±âøa§Ë%–&â’’Ñþ„Ww,/òmì¥z¹€Nãõ‡5ÇŽ/mKŸ¦$jìá +ÓK6L²8ª~í1‚o'š\8¸`À-€ #!C<×® lˆá2ô×8ëõÍÙXÃN¾eÌü®R«•b/!ö¥E7Â5ƒˆpdp«¬BÜqÖ;»8]c5Ù½= L=«ô7#%í¾XžïÃ%3#6‰¥,4«ã¤"»¹¼±¦ÿò€uoŠ8‘KÔ<׬(¸§E¤;UZÙÃoXÍ:I¬‚ßMejyyBþBœT¥îú#6=‘q¨<ô,;¢ qÌ·¡Lá-á¨rjß~Ä‚=%³Š¿uû:RcŒ’x+#.ÃVe(Jź•¨C°×¦m"¯ƒ‘1IÃÉ2*˜Ü¼ùÎÔ‡FÆ2…?Ù0¨k˳ߔïÚ?Éðý\ðßCc£{ ž±¥õ‚>Ô儯p/UM†óè<g;e#6,Û¡¬…(àÆz¶bâJ ü¼¢‰®:…Öò7ã œlG1ÑŸát¸ù|±<ŸXÛ'M1çIñà/T½]‘)œÏo9ìð_Urb,ˆÝOèL{Sá_mTܶ§ã³ZY:Tú9°Ïo¿¥ÁÆš‚³(u™l;g}šm@óß‘¿²YµCfMjdÕó<#.x³:O)ºm‘÷/·ú4@[Zô’;Š÷hÙ‹hhP¯;wœìÁÒ‚Vñ¸¥_F”m¥?ºø2_P|M9Ü©|r]¼½–ko{†<x–rÎdLf¡œ,z7±¿ßg>îPxÝ,lû5=¹‡™ËJÔé{CTj‚Çh÷ñ•bìãšc°«¾DƒiÊî:L™$eR‹I¦ÆÚtî×ãÄtç)†¬ëÈß™·žÜŽÑ~“tÔÆã‚Ãœç£#.p²†}wÍ)@))ë 2d¨…t‘`!.3ØYbô(%œD®‡#%ûtdb#ŠÐ7B™Ê©—Ÿ'Ø«ÁYúcþõCµg›ï‰îé¬ÔMRaÿ2[þ4¤ ]g†bÒ•»woÕNÝ«Ï,.1D¦•XËûØö¾ÛTÓ<’€5ÿ'ƒË.¸¿¢ª×?Øä*£b¯iöÅW-«cµ@UTÙ #%AýJÌ#%î?øó>ÝÚô¯á[íê{}¾¤aªô·ï«éE „aûÄEÃû(ÈõÍüÏŽðÝøÉöý–Áä?²ÔðNšxè`ß·¥"lÆPÞ·G?`lû=Úø ®j¹Ë—\ॣ4ʤˆAàP(AÍ&…à¾Ê0Ax2E¼«¸Móˆr“¹Ù2I#6Q0’D%9èÀ÷¯0(^Cg~‚ÁÛ•RT:F¯gŽw÷Ù}}v®‰ÄT$/d*>Q¾•4C´ãÝÞ¨¦ÙˆÇVÍT«Å„Kv#.ïÙÊ[Tú_‘üúl6q™½„Ì:ÑAéé-‘¾ìE.ôñ x4‚à‘†ŽÎ2²#.±ñÞ‹|Â¥ÀÿóääqÚ¡K¥Èj#Åìðý3˜© 2r·2¢ «êÈš#6Óù¬<7ÐêžY’Ö1Ö‘$“â‚Íh>GÛTÌx~2‹ããéŸÊô@‰î*%Ô39hþZš[ÃÏù`tgø·Ø’j«Úcü© Àïð÷Ãz¾ô! °,¹f°ÇÙ”èd"Ô@„2 v¨0é³k(!ïh[YÙR|"€h/ #.(³(00Ýú¸ …˜Mm”°¶>˜+dʈ¦²’ÆDTKƒ¥ÊGE´@ˆÅ2†.¨&c¯u…ù8#aj¥6MIF›EÜ.–3+ˆ;bÁ–$K*˜Ù’”³T”HBn|4D3Š¿Á#6Ù½3¡ç$ú{•OôÙ«ÂÿHô³€i"Q`UK‹È–…s;$>ª¾5ñÌP({èlpùÈ?)ã¨,ý¾r˜b"Zô§KA±Æ’;5WÚ˜øO´~‹Ë~ËýOî„Nâ¡{Zc#1¼¦Ìň…P'OäŠ)Ï·ž±ŠÎ£¼M!+½E>÷TöƒÃßô9¶s¨ÎšÞ?—ä#%“`cnh?½öÝ+ŸBGG%om_}Žþ ¤M¼Q'ÀwG«ÏwE&h¢æÝnï%óªpmœÜ"&ñõëw&©$·)¢rÛu©í³Ùí«ûÉíööñp)\uÆUüdá{‘KãqÚÛãE¦é]RÎ*8#.ñ6lTxÌ2Œi†˜§DéÃL©Ýåj22JË‘ïuPݹš3Tj²‡©ÿ:§Å¦%tCAç0Û³ô=f[ŽªŠ®Û/0MksvLoTõwe1ð C¹{…ÉR‰â_qý“Zk‹ác>Õaü¬ûìfоáÜlJéæBBé€r'·;<[ŒKå€îñÛuïºä±f’È«%3J„a1b‘{Ûì=G\‹ULj#ERX城ncsÁÜr†sVÓmš;ÃX;MÆV Š]±#.Z²·›mÔ2$ †g-$F„ÄÆñ žLŠ%ð.ª_–L©§Ç6íßQSÔxÝš#.I»!¨œM#.”F×Ϫ‡MC€êvB¶ð#%æ!qŽI*™Øt‘§mŒå´Æ¡t:CÖ\$×@0!ª†#6%ÀB‰eV‰7vd”‚À°ò®cÀAÖ©Fvt×FýeÐm6Û¨ëô_^qöÀ©3_éävjᙎ:¤ö7T%â#.aû ŸŽÈ3ÅÀã#’Dõ<d!'š=Ws=Ñá"ƒ{Üð±ür3‚»xXããÑèL“#Ì{ë€FÅ0ñœ“¹<ït²'ö(v²¢Uº¥\±ªÞËs[%k·ÁÖU’ÖÅZåµsl›UÊ®’@ÁK¢¼›ªÆ!E T$UhÁ^³¢“eeÍq0Q›j„¹»6x©$ëÛAICt“×M4iÁ‚šŽ‘@ƒš\!£Ž{í!Êxtš®á™,ØècÄß“³¡x ltH±h<û“yî̯wy£¥|®3¼¥ðìp§Õ+4ðŸ‚)ÒIÍ ŒHŒ‚ÂE‘@;Ð4['H!³¡ljÁQÎ^58uä9^çSi|£E_¬ŠPE‹Üš4æÃixj’–Rd’F[ÏI)-|݇E#.ÉBœ–ÿJÖ¢}•÷Ø ˆ“ÊS0Ž+Ÿ–Zc8VêrØQ„5†TQ¨[u-¶*£guï«^2÷4¯yt£K»kvË_\h¼•G}þ÷{ÇÎNÍߨV I ’tU¯Ìb*ÂäÚ§’Œ‡VKðfá,#†œlìÇŽs·©¿®üwB1‹óQ”"¬ÝØÊN¿]S}|x×?¿ç=,%xo:!ÀI*såÑoÉ‹1¬J:ò¤´¡eÒB Û#.#%,þ$÷ü9g»ŸnòyϨ7#%&êuÆ$3â6;ˆ7ØR71)™¥‡Tð$Ž¸¤¶§#6ø¹!ÅHTZaH$´"%;úQҬ߃(Z„©"²(MPB0ÛËŽw@NÕeóñÙ{]=Üd[»ÁrÈnÀÇVoÔR@<†‹$„D DÉG׶·-"+joaÖà eSUXò'Ò‚ˆòB‚«ÐÆ,ÚºK•Ô¸g¯/7vj»ºRK]#.Ìeâ+µžÿ‡|½xzª”p®ބERÇ“Piƒg>á,üPCª†Ä{Æ–ò½˜½«XÜ©¦_=Þq¤¡A g6ОO¢¦‰_*’P!6E†3Ë87Xd]ä#6æmóŽ±2M#%áF³™k#U@T¢yÂý3Ø›÷o#ÖÉÐÁfÞî,Ü ›;èΗm0T&â&4÷5I$ÂpŽº~¶4Æ1ržÍ/€‡ë›#6íÙj¼wüšXëËh:nx–fø|ñLJgXC§ÒPt/yÒŸ¿áìÍørŠ#6°\ÈÆØÍ(‰< UTȵ\[#%^™H#.}qš¨ÛVFX+1á÷D¨ûºÕÓBà„#..”rªü‚˜m¾È½Í—¡—½Y]ŽZ† I„¸8ÓLÓA5À×ÏN·Ó0$ÑŽ–mÅ#6æ#.€Xõæa÷îÙâ.êôå“\ºHÜŽ9T¥w_–¥v¢E”F`0HƒÊ¡E%xÜÂ+º¸öî2<õç<Œ-“#.áê}›Ï¤Ûy£¶™ÙÜT[§Ï%)Ý7°40¶ãòâæÙO:§dXoY£t¢«º{{»õÆU;ªR%£D P–ÈÌ0ì¼Y3#%€Û*VÃØq@iãAF®L`àîë´'©!ç#´ ÏAVì#º(®RÐBVcÙ3±EÊí’ a „›¶)ÙXª`_€žôÉ X¸h£Dì;£Ûe¼ô‰ŒŽ'¡Ž4QÁJ#6Q`¹”(T"’@ çK¨ ®¬`²ÇînÆ´ÝïZ:uY¹í©ŸÆtFýð›ìÂ-¢˜RTˆÐfµs™K>÷Bo¾Hç¼~YÅuúýsPÓÊÑ¿±ï ˜9:ô–Y†&Û˜:öcU:çÙ›ëBûrÎ0º>È^i…[d6p;÷Š6¡’O‹ðƒ#.¿c>ëU o*&L:ÜÁÀŒ–.$ó³ôLx㤴»§Õ~àÂïÉ9Àë'tldæ…!¤1#.—@Ìäu*n&H÷QT*ÖËCî.졉=9"E‹.Ôœ+u˜e×̪¤HÈA$}á{;~æJ²\¨MâÔDF`aÝGnJB†PÁŒ=Ù#.“ݱâcL¸¡Bž"Yáî¢ýYÏÒj@=½Ï<èq¾í!É‹ùÆÞf—l¹c\ƒü1GVø¦l.o6ÈêûlinDµÄ®ÌCÆD¬ïÁKøµˆÃÖ)Uf[(8?ìBª± §^fÀ] tJëM/œBóC]™éƒ¯…è×»i¡^|ÇE7u[d¥g¥ÊZ;KÚ`¤ÆóÈ3;†óæï`n…節qi‹8šöדOB!2ÌØCP;Èä{Ž]¡GÊ‚¢¼È0†QvDÝ9¼‘¥ºËY=†s³¤íEÃBî4¶:˜vAèC2.û\•|ÓioÝv®ZFQ4ZOí²i2e67î1kÓkð/™#.§]Ž6@‘³4xÊ3töÕÑøželG•Í†²ÍÑ’@R† ]ˆ)˜NýþüueVL5š‡Ú)·é.´É©ü sà7§@4/:UÛ‘Ë]u‹C<äR\€wÂ'UWÞ:çQ“vbª§F†HRÉS©Ð,wÀ×ß=uÃYƒV\<wWa‘"ä¢wÕ÷€Èߤ#ö˜’®qœ”¿£å„.›#8%€vÏÏíωIúu-?µ÷ðž·8>ë2‡“Rª™MÄ£8{ôíåŽíxÍ]z¨Bì<bc«öz:yÿfáÃçG•ŸËhäŸbV…©÷b‰ ÒÖéZ‰Ù‹ÙUð%ž¯óè™Q>V'׿_ÝSÈ2È6½–_g¸ñ¢Öåä#%˜Æð§:¬É©£_ÑÆxÖyßÔ#%ŒÒéDÙ‡¯Š~ªJ†ó$ƒ?Ù~~é†8f>†îÑ=G/‰…Þe%A7Üüq,[Y§â¿o¹€b΢oªd¡ÝŠãà¶4€CÄ™À?Æaûý?ËÉoôÝÝã‰è”Ÿßþ$Ý6aH*2AHe,-3ôWðþ>—|ðoÃpûgŠrñßã‹jËntKç;ÓøÂß%ÂòJŸMY¨TßzÊüúQ÷óÌ<u`ÔáXXCWKqŠ_æA?žÛÇoMWœŒ*ôˤ,«éýŸ£õÿ/7ö0oöÿ‹Ûöþ±/Ý8üq¤¬”I&Éêe…UšÕωk›ôR2ˆXIlW+YsÎC?Û äÚ¡Gþ€€ŠeÝžYg ÜÕ|ÆÚ#%$Ç¡µ¨Ãhý››“[<cOgöNÚk¼Mbf"\ 0íÝ{PÖõÏ?oÈGðª¡)©>p?îüy÷?=BVüsɼcýNG8(´5Y‰yPYú¾ÎÞξ½¢jö«vÚÙÜ1¸ýË}+sk…6édص2½yÛù¯$Ü¡i)_?ï_Œ3÷ÓÇíüûàüéC6ª?·LG÷³û_Àȯ«ï5‹Mu¹=!£~*+LOú¤k2¨‘bÄ2ðÏVÞ 7üDÏDa{ª';âûƒ (#jº£ª€%3t»¬i×¥¦•A‰QÇŽ®¬2ÕYhqßž´w/ù‡øƒã¦Z7¤Åö#6uÑiM«ZÎ0—XL¡ëŒ!…9¼zóü€ª ²zYÏ¿hnÎHr`§fá5©¯N$fá"ßæßV Þ±Æeî>¥?ë.3/«ð>KÔ6½ŸM(Ÿ9ž—Ož‰ÊK#6=mcÒï¾´'ª¬€§J¦1EæwŸ³g¶ËO…õR—¿Õó°"ûå m"À„Œ@¦D¡È_ßØ_éú«ÕÈqè4é|Ÿ`òßR쎉+´j`ŠQ‰(‰âL”&‡ПK#.FM=‡3–ÿ§úñ3£I¢Sø¦Ø¥cÒš ÈȹÄþƒ~î>ÜþËq•U×-Ã^ËuèàH19ôêaY?¸NN·Ñ¯¨]ó#.—y·Ûßù `2É/ÈDÏà‰ê>Ÿ #6m*€ÕHç<\2ê=|.mcÅwž~÷‡w§`PòMêÆ9^Þj¼=ÐXÂŽfulDRžÀç,±þBpˆRH‚ú‡Ð݉™îµ˜)_Å®ðߦ.üîÏÏôúýà|Iõü¸“ò»ãšMJîëáEŸO×ñ"aÚO‰A$ÉF‚ï5ýéúxqçÐû¦„Fwd;zçСËÓ².'œgòêç©äâ|9Êlrå€ó×ê¯l¢:vGÐi2] ™³ @ñ¦Š®3×#M»½‹.À;Gâ¿9BG8ý¯ý\žŽý»D=®h¯^þòÖØøÉýò=k‹â<RÂÐäõ†©‹€DD`_êåî‚"xKyy‘+·75¼â]*ß:Îp#%"t-8¹j°ä €®Ä»ÓëRiö4úi÷>áú¿ÀØñ<ÎzüãËçò%LÑQû>\ÿ3pÓ°êvÇ1gío¡¨Ócj‹ÛÚï^ÿø½\¯:³Hr!i ~ZT×°-î(ÿL½þ›rø†'›™@Ù·Uì:k¶+cJ©Òò]ô8<Ãr¾É¹Ð#6È„Ô˜²¤ƒìüØõ^ˆcÕgqÁ”oTÔL#6$8ˆ=Ìò<Ë1c„‹Š4+?G£âòó¿x#.âÎ\ÝZs ÜŸ™ÀàF’)¡EŽhsŽ•ƒ.²`.z´ñò„Ù0öªÞÒʹQR~m+í»0†Ç._¯ÛË—ÑåÇC«nè5 ÏutßWv#60蔪Z.0òë𷵬žËðSŠF§NOƒµN®#.ÐûEÁ¤Ÿe˜ûøÑ3¦2J¦|;ìïMmQ³ ïºvj†x¼rïJœ¹¾Ý‘~“Ѐpr)‡c§~¢;wo«j£ï‹ïÎö1žsÑõñTh#0Ò!ãóR³UW•×}Úh¥bÍ exƒËá"æ|PH[ǧ:àJžäô/°qŒDC“×I¦™u„ÃGµ_z§ó8Xzgü‰Ï—N×È:ÎÝÓ¿¿x£aF÷›Ó¤ïÅø̺€["·½ò½ejøM†§Û™AéA&¶²¶®6)BЉˆ;XG꧕('ꢵ¬IB¬×(~ a{ŸÕpß5×.êµ55‡½.>#b·—´Þç=ÿ©]oðÃG§Tmü#%AÁP1@zsÍnál?]ÞªzxkÝ@ˆ2 eó*+hÛ:¬¿w›ìݩΆÜ~‚ TJ–^9¿ Í‘4hB&¨ï¿#.xbðPýÀ;Ô¢Ÿ^Æýú}¡‡›¥Ø€K“Ú}´!IŸ+ê@=ªçŸÏ@-êá)"„-±ñ³êa”å7&#.J+Ç¢ñå™ÏL½pæ#.uÚ…ýŸÏó,fƒò¼5à‡(ÌTMB³î9ál¡0á5ŠÕ’:¦s‡V6z=oÝür¼úÙ,åƯ÷~\Ý¿£Œm½ñW#úLv‘ÅÙÏ#.‚{£PP·6Ú).zEËÃÂ{ï»ý›Üan!Ì@ï\Nø¯*}Wÿ†±›þÉ3ÄÒS+ÏPhÅu·¯cãßm’ÙÓ‡Ú]’<ÑÒõyÅÕ'ëïztW{ÛÚ³—êøÞ`Ì«í-XLm#¸‡F—u¢ÓZ±ä¾µïŒÿ9S9œí¶}È8ß+äªnæÓ^ßn$ÊdE[Ó'}ìt”¯íøѵK¥Q´Ûmï·“mˆs³‡ñVhïU¹á _@6½o‡ÞíöO¢êCU˜sª†V“Ãú«:ÇJM,²tnOƒäÛ伎r'Jh±Ï“ªŒÁ¬ˆW•è*˜• IJŽTy`¸ñ´,xÂÆPèL*3(#.ÛàsÇÍ(Ç5Ã.wà~>>j;›A^ZÕŠÂ0MÆÇ¿š*vˆaFš¸7Ï#%÷Ðgˆ}lÌÒÌÁì-a”Ôƒz3urî#.œ‚÷?̶›#6!î8P¹òˆ-ã¡lÕ]EïÇ®ÎZØð™[¥•ðRӇºÝâ“H‘53û5qµ‘ÓÝ¥ƒ¡ƒ(¨*‚—Sv†Akë«Ü´iXE¦ë%qaÅn_–#%ÑEò¸ÙeÑ.’g}‚H#lkÓ>§.¿ÙÔ¯-º(ž2}T|pðÔ=ˆæ ÊÓm.C:)]úÝ«ËÛyï§ÌåÝÖÇð …0«¥7§aÝçYјóÉî®úÁž™ÛEg'>±Ež®LNïˆzûë1Åè§âßÒ(íÓ\x¬ëµÎ©êwƒõÆiYئóãÊe0wóÔ‡%<Gœz¦÷Z¿±a”úÝÊéFK^Ôœ%zd¿M½5:îRª®cw×{Wô©hØOú“¨—o¯œ¾½löyšâÍ$®¨¯zÀ0§mCÓgÀpP®¥hîÒöUA|ÊÀÝ!=yy©DzäsÎ9UI#ºêI¹#6ÉWeý¯gÍóz8V“)tÇáFöƒ®úBÜXöãfñÎþG|Âøoí.lì&)ÄpæôëøMSÓŸô5#.2ŠsQ"„—Þw"×ju÷5ªNþº7Îåí}uKfs=}éØŠzÚ_CŽ¿dlO¶¹²ªrú%Ÿ§Øçß#¤¿|*¦›góßsg~®ÉD\͈zw”¼Ö~h˜é¬»{ˆxHgPî„6ßÂ÷¬8¹ãŠß³Þt[iÖ(N…ÒIÝô—‰Ð‘Œ¿ÅÜSm©Áw—tî‹sÄ:·'³?‘¬g6ムÉÄz)÷=n™çЯ[JDqDQJhµŸ<Â^·gPáñ%mÝäú»·n·=3¼ítd2ïäËÉAíTdýúÌù^sŸÙo–R×j=µì\å,c¾-#%¯Ã³Žx©©; WÍxÙzë†X\µÜéó™3Õ] ΰ»«—K^ûÕGª1Õª÷ć¡–k{¦1Bϧñ êœY±£]é~·<ëí¬‹«%HºîmÆ”2¡™´¸U\ E\¨¥D#6Ö©ýè¥=ÿE̒㶉ÛËrëïÇ‘Yòûáã%»¾f=ÞéMŸÞ³žKÚtZÓÈ'l8•¿8{žß6TF=Eò|"#.ßßUGÙVmÏ‘iûY'.þ÷Æ ´n¤(¦Ôh³ä\ŠjdäW¹MØ£Ç×G¦XÒ|^–uS‚&Æ ú]^Ñ]s}§GPW齑ɵùÛ]¹>íîæ˜~çÚã¯+}ß Ùî"•6ÃõµÁU=c¥Ú8j~>Ï?sÓ×Òs…Å"Cqv…?·åN=3 ÏDü™ƒÆs\ùÿ‚¤õ¤=§¤y-&*ŽJý†öãÃ[ÔÛw™bÄØñéûDfNÖHZZÛóJQù«¿¯bÑÔ|¥¨Áv¼êšŠ±¤z(ó±V‹ÖÁtAµêq¶#.èÄ‘ø6ÔÔrÑþú#¸\ØÇÖgÞü},#6ÛNjBÅp®~ÇBYÅ7ŸàÅË;æßO¶ï‡Ãß«…Ä* Â=µ³\F’˜v©ểÁRÚMܯ©”øeàE\G)×ÈLÅ<ýŸ™];8iÉú‘Æ££í–]Ä ËĺÏYŽ\6Çg#%U¤{’Ø[†»¦.Iœ<:Øg‹#6¨Q.AQÁƒ´7漯#üá#.¬h;û!ƒ]žX–0ÇÏçò¸m‡gÝš1Ž9ÍÿžèmúH¾_H‡–GYÓ¼Ï?N××wíQ:)þ$ø/KyÎi6¡+žvŽJŠ¢ÕE‰B¹@¸ÊƉðˆP¢úz…¢,Š¡–ªÙm–¿¢„ÛiKˆ»eïAxý1_®ùS4`Š÷³…¿wÙ“»N¶{PÔôÖ÷ÅTXPD]Î*çDïCEžüYc¤ô¤ãð^FO¸Žëz]ÕÏéæ’cpÈÚUÊUÐ@œ,#%ŒÂpHmUWùa±ƒµ®*|ÛH¦Îˆ}Úu÷'Ø2›Ô¤æŸŒ¼…ìGC9BSu&3æÖ5ñh÷=3m"±|Ø\!€XPŠ‘.{ØyÝH¸›¹[¹BJ+ÅGPÍS#.O2,,<Ó¿f;r‘÷ú½M`ª)…®¢Ñ@9b\m@ü @@]¶ÐئD #_K®cR(ä?µ‰#QQÄpFñ#%¨ªêªÄÄ:UÙ#%œ¨"ñÓmÝ„²#.ž·4맯É(¾Î^¬(üEJBë…‹23º ¬W¾\îp®4«ÎJê#.¬ËqÂ&zÐB/ò‰!<Ë9#.ølc´“Àw£ôMzME‚×®#.CÁEEÁEà#%‚<så£ñhõyp³R¸„øðá#6ÌÞO}Ž¢„åE§=•ï½ï–8Å»—ðàÙø{q!† ‘¯ÑÅ=ÜÆ¢Û©ˆ)êCöà?Ës_îì‡]#6„ŒêT?ˆ€–@-!öðó÷ÎÌ/™¾¥™Mþ¢Jk覱{Z-@?÷U§œÕë¹™üGê-›ÇŸ·Õ@{ª¼cnÎL‡§Q-ðÿúöòÎÿ”ÛeÐFçÛAùk9ý3xÇx/‡À4¾ÔPÅHDÀÒ¯ §¼z@ìÓüUÿ‰¼x´#%žo2‡2iíé…¶z—® kÒ$~ÇÙcÒ5õ«ŸoÕöü}œù°RH1蹈çèÚ4£}çKêçÎL¥ŸvJjþ©J%ÿñ ìøÐ}ÝAbAr€ðQ1«˜o£Ú·X„#66ÅH.>qÈ|ñ$T½‘Š"~ßÀ$ééí#.YqrA@f#%ûW°@Cë:ÑЊi&s7Ïôì¾&BOCüf+uÙòj” Ù$ßœº×Ïíz³¾EêOæÚ$>ùhÕn¡á™t÷HŒ†ú§¸!#*1"Ú.Nç¸Û;]\xVÙöùú¤šd^ ƒwÏ|gO6ÃϺí;ŒJæ0&é5Ó#%}¡8Ur/$€ß…3‘ì„>Üx¿,CŠq¨;™†•@v/#%å@]¹C9P¤grÏþ¸(9Á Èvkš$§9T¬Æ4˜h ÷~ÿm*ñ8꘻ˆÈ`#%X}¹lJ‚‡^¾ü. #.JCVgˆ‚5t^‚ˆ7!߆÷ eøÆe#%~zT)AY`ʼNµÂùíUö)>Sæ׋rYXP\¨¢!Å&‚NŽ0#6±ƒzY*¥MtÛ ìt‰šÊl,tgÎçg2ˆj—bOåN@–Âq7´ñ&¿FŠ)¶¦=((œ{;›‚P'+ª×Ãw”V_ðÀëá?tf“¸Ÿ@¢ÛnóX.Ž3Q%x]]Ïv[ÒÉk¥×¡TÔ4¢¦ª%…e^ROkl(FX8g&øw†å!£?géâÊ#.#íYÍïj‡~†âi›e•Ä×u'³Ý˜e;g•ÂTüLìq1Ý ¯#¨(òáá#ŒÉ,p†“çt‡Aè•5+ÙldèwäÕÒ{øÞv‡åÆ;Úõm”P•'@î}ˆço=pÊ2H#6OG™ê_™0P&Ú‰J#%íËTO¦\ýæÀ¢j›hDp¿É“ꡧКy©9Sr³xêÚ-ÇhvÔdŠ#.Y|ÓãÃPmSÀˆ_¶V?L_"'(PvÖR«6ã$¾Z'Žxõ×=÷#.A='áTP‘)þ%¦}ýjW¿Íþ\èô´Ò¨õÑ3Ê¡â̸|ÅZ~c¡ÞFÊ0‡V‡(·ÙÛiúÿ+Þíôüt|~!s#.Ë@Å¥ÑZÃñxä·˜aƒ……%»©±ªþ½GŠK1à*ÒpÁþ'Â=6ŸqK½•¶±/öþ®x ©®óÒ½±dRl/U¬ôdÂAÆr³#6+óÁ…G‹Ì&IŸòê“nçôBs\g"ìé_žKâéœ?ŠŸ§èMað{4•„ÃCɸü¤ý³þ~öÜ…ÚázNö}Ëê˜9„)˜Jë³ÒïIÆ€f¸<Êw²Á»¤‹Æ=,µ¶&M•!.üpVí±ðNtÞe°Ø²”Ó¦p„1•Ð˜àƒøôžzrżùX—wÇ+8So£°y1Ï,ð#.›0#йò½ÇÈB9:ƒ-{~Ã1[rµã[öÁå/{Ã"“ V«Ñ*&dÛëqì¶Y-Ï.õâÒÝpœO5Æw‹;-¹’¸ò ¦JÅff Ç»¼Á¤à°Õç–H‰|WÉ¡œ!I¥Æý1eEÖ¿Ö9†AŸtP_ªÈåÐêk‹WHV#6%±¥ìå¬1^ˆ´#0Ó)åªùº°Eóg*ímŲÜëèûßÁV–Bo^Eæñ4ûyÖ°‡.éÄFŸ±¾z4÷ö`€¡(Ù§¢œW'¨f鶹Jb•‚®òÑ;¥:ZÑ;n‘»Íçe¤°,5÷¿_„¡==:©8¼á*è›çñŽ–_ozâ|qä“5v]QsÚö.2ß#6ïÓœ‡Ó~ýá*ÜEÛ‘"cƒ¬|T=ù<³#€‚ˆ#6àœÆ`—Ñ-¶™ŠP¦#.…ÀLï6– ÏÜѺ Gà¨ëÔTt6k®^m¾×¡Îu¾íˆr%ÄD>Ú‡òQŠMŸã~pÝŒ, ¬&»œ_Ag¨šŸ²„Naæ{¼'##6ØÊ°àY•€È‡#søýø²\¿ú-´Ø~mÞÂɱ*ƒ6i¨I@¡¯PUG¶/G$YYzÑÎR0úgÏüŸ—î–å¯Cæ“ô~6·ÌS™PÔŸ^ÎÓ¤é#.Ózý¯kHmiÛm.•3Ìx‡K¦ÜÈÿ~®%Ä"Þ+G;L¡ïncï–53áõ>¬pxq¸dê}ƒŽ†¬=ÓÓ·UýöA¶Ÿ—vr„v¤’9’“4‹>ü‘L¸½p¯´°©gL{Ùþµt6É{<]^ÒÞœ‚™ÓXÈÁ\€‚ ¸ALäo¢—Ïóü¢’Ôð8ºÜP6;Ak…Ù#.†›njú«xõm©ÁäXµEÕ-—">/Ñ£§5ô¦`åä[R÷6qì./Ó#%®Prö9¤¡ÏTXŒù8H^ôѬPòÖ~•Zª¨‚S~§!¡™<פýUîO'›Uú¤–0°*û¡Â!5Éåúõ…`ØY—³¿ÂÁà$µD£#%¡<矘 A8¤y¦ç½â±F*$‚P’IåeÉ#%úZ;dž>žý{/ón\ö»A8*Ž#vw3…*ü5`,rØ5¦#./¾ºËÜŽ 4W‘i•Z¢‰œG˜éŸ!uá&(ÒÇ_q”Då®gˆ·3$g²a:¬ï,ÕMˆ{ÂkÌû!†ÚR [€#.Mßâ–i a½6`3š"»—½§ïØÈNÐ+Ùbv™‡§ˆêëdë—ŠZO9ª$-]»°Û–ü"üfÕÃõ¸p‚ELÂçìîiUHšE¹é±e.™ÂC¼â‡gA¬6y¢gÃë]±éwÁ÷Uîåã½}¿)¯#.A²ÌëðÀ`lAºÁ×dí9Èãjá*Ù݆渻²§°ñ,‘ƒ«hïÜ£dwÌ.°‚»d€åšy„r ‹%(sãeäFzeÚXí¼#.Kó¿i†CÜÚ'~Z6´–#±sY‘”JÉaL”Jƒ˜¦#%39F¸¨™Q" ^Då6{PSS3yÝL†Ý'n˜'´sûõ*ãšz‚¶ƒ«cT¸ˆ#.2!r(PhPÚë37nPᙌF;E6‹ƒ#%ò-®}“˜]…âRkß ×ø°Œ‘ç>Ðt©Ž[â¾Ú²Ù¯kYëõÉ×,oÅѲZf°Øâ'̆ü¼üFæ"_ê•ßv8í<[8›¢XæË«É;ýˆÚru06fåÚ¹$£”ìH½_¿ÅÌÖ_ÈB'ÑsÁ¾Wì$Š“³m#6E]}Ñ#.~j4aÙ£ Mfù‡ ‘À\ö=‚°¶²„„ÔJqå΂{ŠôÅOª÷Üþ§Òj›AéTPº¦Êƒæ}Ëã1¨ò%¦ÿ£Ì÷"‡ÛÍ¢1#6HsMžÇ¡±Q¯5óßâ¬Ì½9ÆÂ>uRÂ#}ì3va¤gìvŽ‘m¹€ZUÀ#ª³qÆîëƒVØX®H2ÚÏßÙ#.ÁÖ›@¨$7›°íc<ƒŠ!"†·ô2¯È.îöö!Ò‡5‰ÐÀ„¡LŒ÷³á±Ú¶æ…™Ÿ`ÆVØäºÑb#6#6õϧë(¢Ý‹a'9ú#6«`Ž‰„4Z†¢ >P4i á~q\KAÔ!nAÌ‘)™ÁÑYÅɼLèÈÉ6Þ¼44mŠ§3r !±é郓Z-*5Þ/!*kâÔ€VäD—K¸ù°;"Ön[z€2bŒˆèé}ˆÚÖ½ÖJ"ÃX¢žÌvh{X£è¹Øf¬b¡Z–ëó‰Šè¾´ò+AŽu~üâOÚ°Éqm©Þ„:¤Œ·C®aïÄ,«&q\È™A̶¿n<l4(ÉÙÛÄW†ŸÎ”"oí?wŠ©zg\.Äf¾]%k€Ù’¢t<æÐ#.c–Êá tgß›r#%s-¤ ÷çmhÅ»]§ˆN4(ãZs´cÎ.ßx*sŸgi5‘ÃÌJI@íod/(&_ÊX·éåøC¥¿ZO¹V?0Ìd<—‰Ö‘¼ÌĉË0æ–•;bù¶ëðzàUö‡Kd3‹³ Ê—áh@Ù€Qm´c`‰ñíYÀíÌÀNò‰1ª8Ø”´œ+n5µ#˜ðF¼]²"†RO]WQ5#.ê/Ùªß~”aêÔàí#%鉣µâÓ˜ê(-ˆ”+ƒGcÒÐÝ`|^D]ÿ¤¿³ÄœÁ#6El\q´`!zI¡¼<)MG~©oÖxEß¿øÍF'Qn#%xTL9ó¸- 4‚€ÜÜÜÝOÂxp8Yx¶@Ø‘o\D÷Níuœ§WL7COÀ»îZ¨#6iY|ŸÊäVú—AÓ]&[óêæ¥À_5·i>sE/#6¸¹€%5ÿ/ÃN¿ÃÝìï(öþlâò9Ðs7Îåú§óˆh£ógTH‚€ŸðYg}ç_Aò0¢VÅSNÄ‚HQ–'Äá×õÀ£UGJÙB3qØ÷dZšÞgÅÒЇq÷q¥¢×‹_aÜ8#%Iü~JÚž79ÀëÒ6ò`á|û6¯/ YÚr´hÄú§]Ù3óþKÕ⻳«—Ä|3ü|cÈužyb3f‡ïíÃø‡ÝCx-®–X3ÐPX‰ú†t?QÃœˆL#6Ô28~±s;{þ|%UVvþ,¿Æp‚gºÿ…ÀY²Tì*”Br)SŽ0e–Çûï¯Éþp>VÇù¸îü¡ü_ášbÖ?©BÈrøð½C9%_ý¹$TÅIÿUP©þ·÷êYE»üZÕôAêa#°^ˆØ”Xá©8Š1#%hÿ]¡0;5gûYý¼³Ö÷vØà|ávçv…†ßë›ûîšÖáþйގc€Ë«±_wNóx©©A3CaÚ— ï:Æ]Û¸…xn4©‰ã×Ìê{3 ®íH¸éÍßYTBºâüT£Ca²lÀ–ºª<ƒý¼ƒ;ª§`xµÍG°{‘Ú€ÀÞõ{ƒ¯œaô{ý.Aô¯³í4A#.PÈû=¨íŠÚ+þ˜ªYÄ; ]ÃäNÁ5%–‘Ñ…î\þK¿ÞÍ5”9€Í=¶Ïâ}™ F²†¡®B Ötüû™§ }ËÔ:Ð$A7ýß¾ºÏîû]MéÎÉ㈠D|_÷*ØUÙý./Žœó,Õ!nÿP=`dÄ_¼çœâÜ&\’B7dªõ{ ²Aåý—ˆŽ qÈP#%)X†u„¬¯# #\>Ïæþ¯G¿å+÷[q7¡ˆç˜b<œ*„™Ïw÷ý=OŸâGÔûôŸH|ï=áãøN®A ÊL’ª´RÅßzáóHò?yˆCÔ”“àVøþí¼…ò>!”0нÈ^‡ûÆÁ„.4ñ6×å#.ý?ipvóÙ7ìj¢Ñ X…˜%.S–7pÂM”ÞbÀò6£ƒ4¡ÖyÂCaΚ#.Ì·¨¬Qv ´†&ËPÿ—<#6ÃvT‡«±}ž‚ûXðÒ¥W–‡Ò`_Ýä }ÝâÄì9Åwœû[wmZ?¼“C`ðã tÌ{àsõ›Û?OqÂöžVÆȨ7RÁ°Æ.¸È’(H0µ˜p½#%o{‹‚íx-)F,=žÂÙ²"œÃÌ„ä—°íÀÚæ9Níw.i…c’vL0ìòÆ‚Ð*ÚÃz}pAZ€sŸmy^®šROÅö…îNÿïòðYGÔT%X›|ËBáá5¦ý4¨TÎ(ÔXÐ#ýeµeþô=]Hê#%ºwZÁÀH“_ž6£áúl¯ïwç£èÃ0%ï»=¡iìÁ'åƒÇ`w‚Ž%#{†ì;r6,#.øÖ,ć¤zdx`¿‡ý9p©CYÐÒTe*ÞiD¢-&¶Ö H<>Rg#65Y$îŸ?.ÔäxRR°Áîú=T²U}ø@ú1ME~ kmmÙG“åô¨óƒ&FØèÃefæj²’~G³M¬2ëôê‚YAÁwYÃâÖ÷ ™#%yM©o®#̉ôlBÉÕ@"Ð$eæ ˆÄ|³5:IF~Œ—¸"Ì-¨É>¤÷x›¸ºŽ"#.?â#6ª[pÆñ”·æ4ï* *˜D¨ uåsØmos¬!ÖÃfM€ð*fÀo5йdãÆ¿ç=1…”V·ñÚÀÓömçÊkû8…6ú„l£\ðgŒbÝ’K!UU;¤’I#6ðïlJüý¸f¶˜Çí86#îu︔öoz©žàÖóׄɄà<è:YhÜðyfÊ–çñΙSlK6ÉÆd–.Õåè‡SøÌöü½Aô–~Ã!#6ü§ù‘|N3ôÿ›»òï;§ÔO‘Úy K~ô¡…×Ä°´—SêœúfÄ=ºÔ#.óévdÖõò?IÆÉõáUê6ü‰“ƒôû”[~@ðˆ@X#6‚Ä`ÈÃ/õ{ü>Ÿ¨ #6èQÚ@Äöl„²Î¦§\Ã)%±·P7À`«³¹ð×…÷C.ŽÐup È`güŠûV}¹Î•|¥F1°bcC„2æ²Ëœæ‡0ž}"£‡A°o‚#. '#%ØÐhü÷‚ìÚ¹&F÷7yGÃÌ2M†Š:Œ„ÖúD(3ü_‹ý}Hlû·§qƒE#.2+ÏM¦äûøæƒÖ039!×ÚFå—9-Eö V#6„‡`d’€êxïk #%ÔOÁ-=fŒŠq/¾Êƒõ¥*`k|X}yÅ^Eí“!l<—FDYºÓA ±¨TD â)#–¢š3S:€™™š‘i°q`wr$îï+à™±›">,Œòq$D©L#6„3ÎÏõk’NðÍÔk‡Ä^!rÈÌC¹éi Ñ93C<µ™½Ø2.¦„#.Y$ˆà ¸ûŸÙÓñŸoXÿËw^º#%r€ÿ™úõˆUvAýù+ŽRWáóÆVoë¸q®ž½v¤ÞŒ”™zíÛ$cÁú»e’XjÿÇý1ž-.õ„XÊf‰]dmffžµ¨Tä!Ô}™§§Å´5N*näl³+”¤CEš‘×’I$™ª5ŠLTËMIikm§™«š²Öjãzef׌º†:õ„'«^kA«ÐÖ·¢šféu†G¼ÇV:ŒÒÄeA)2¬gøY(Î:ü¾Ù#6ŒX#6ª4YH¹„ŸŽ²¨é™ô0 ˜A‘P~_bÑjTmå»2sxqnaÑX„î;ÚŸ%³ïöÞÙýµ£ƒ˜t€Gí^^ÈžÂÆÛýÁ éŒó˼ÉI~}~ÝÕ¼QRü¾ÔúÑ÷}‚q§‡#%è!À´>Ò^„¶Cwl9¨; Œ„‚6ð›IÝ·;¶¯¬ó·Þd]¶d>a؈ZjH M)ßQl‰ÐªïVY¬$ˆ €r¶PRm82d?ðÛc;ŽÆxézZªÎÀ«àU¶#AIE5º&W¾f^%àù§Sš«-5RkÓ„¾]ZÍÌ¿ŸÂ19l=1IAJ~ËöêuEð4+Õ.^‡¿%6‰èßرfÆó£æŽg§-Ô¦ÝɽŠI"H±’~Å÷WN3v¥7(Š)sp‚ƒ*ˆKûû;®Î϶þÒÀû“´ÛïŸ3Ž£ßødc_dDôvö´€’»~¢µQ‹ý![~Æ1m—ؾñ!túíÈÿ©©¿)èlHÅ3ê+ì:ŸöK<¾¢ªQRR”Š"›äÙL„Ù°LÔËsMw¶`¡º îÍieBñ@0bRòK:R¢d|~“n|šâšWnƒGô „ݽ}‘8_¨à<'¯aòV¼Qîó5¯·o\W‹÷Û^0'h7 2u%‘¸Fmîòf±0/ÍÊÀ·ió¸ò@£P.£ùbúÈ+‹?NÎTáõPäDs#%>!ø½Þ@Y68þçxÉ\ż.Ü?€ú-¸QÙþ*°™DjIôßç½a-êzÄRÏ¢ˆ*%ö›Q<ìr0}y,ýÂz ú>Ç[„žäv€ãòƒ`õ#.€²Â½ú&Ì2(Abã:’6ƒ¿ô9œv¾ðû`ç¾É±9ßÙú_¨ì>†Ê§8»ÀþÉù€ö»6x'ã7ˆu'€{Å;H©A5twµñíPI”É(Ö?xyX"DNþ}+»ÅÞy‡sF±èæÆÀY—€3m;0#6ŒJŸÊÌ#%âaAŒö÷D<…‚É„Y(‡Ìò ¯À.pW÷Îapõ÷lòÕs½8¾?~†ç3PGÌpC© :¨3yC³ŠÐÕ?P«¯™»/id+ä?ÇGÞÙúÉõõɬ÷V7LUQX•®4=áù2»Ý–£¯ë‚ø|áçð*úÄ‚êkCÔ¦j˜„‡£’I'±â©æ>·Ò »ø5UŠÈA°4d€>Ÿ/ЯãH¤ŸWb)ÙZ]CÝZ{Ž_\£ßö¨0G±Lùru‹pÖÖÆZžÿG}6\ÒšT…Éá žåõú÷£°20®UöÊ’°8MAÕñ#.7û;³ƒ¬{Š¯Ïàtc$Ÿ:F†Þ,!î:€ìÎ:`âÁbqe¨s꾸äædQÒPÁ“NêƒËJCì2U‘ÕZˆ*>RQÀÑИ†©üèeLæHãc;väáÁbNˆTm÷T†Í&†t”K£Pññø©†tóUŒ‚Hª%HYÇW¯>nÛC-Žmu‹r6ÑêE±½„¹Þô+šn}|=i³Ÿ'2)˜E^úÏS<a‘$Ed@¡$´ZЫFþÎ)Ìí76,BĤJ(~¾'QvVôb•JÁøN|À=ɯ»ß<OZ'ˆIâh<Þ°ö#%`íßÂs#.dòUî6šûcÁ-£È3#%§Mª¥¹ãïù¼_Øû«óåïè/˜_Çk/qcu¨²{¤·´?×?ž#%#49'öJŠ<ªJÿ¡Ä†Ã”5m᥈8`«:m _\‘¸Í©R•JJQ¸kúºu÷OIÝ'lòµ°~3áGb˜â=hòäÒøŸj=ußËã$½!°ÔÃïLý®äwšžò!³Ý(TöC’ÇÙDåÄã÷~C—Ï¡\Èৠ‹T £T¦!S䧊r>Ð2üY"æC}Àí•iòJ;)œ.Ù¹ž½ð˜ h_Ÿ™é籄¾üåw¼!,Œ#6|õ»ùN}Cƒà=åË)BB¡háô"þã3ì• kEµ,ã‚e”'?`vðßù\à£öíç¶>ã‰(}M|)kì`éþ¹áaª]Ë•àa½E½h#ãòF%LæV #%ÚI0%‰¾ Z\Baнìh%RÀ2` øÐlHvE6Œ˜‚(¤\D½€d󈸂HÅH"8‰ERG5Ö ™½Áù²AC€ DÖ55-~£¿Àú}ý#.>(#.ØÈðòÅÓ<³1™Z*¦°°'Qb¨r#%*ãIé}¨kOH~)Û´ÑLAG§QbY()9gb4 ÁˆGô~×ôs¢×3Ü“×fH„z3J³íü`‘œV߶PÁý+_>q°Ù#64X”Ì¢3|»Í¯’r«Ò¸n‘[]0ÙP˜‚²ÙmÝyæ]<(©h–‘ïô›“Wóúý?f’þÐ3MãʳìóžAì};`%õñºéîfú‰#%–½'¼ŠYÍõù½HÃ[±#hqŽÃkóÔ6CôƒH&·j ˜ªY1#€(#.D†B2`ëJhL€é·E(ˆÒŽ*XZÌO/'œ ;+Ó³xBJ<ò9œ/;3§¨^-ûi^Cµ#6@Õ£wëÜŸ¢}”RN5Â7ö†n9ÐõD#%ØpHo(ë u}›J6öÄ+pòi:ºØç¡<½s¶Üíö[c˜Û’.Ó à49°¶¨<ïªÊ<Ïgã³ÙÛƒ;Âä=Åa¨]lùŒ‰úÇ#.ÒÒá)Š#6(£ß½d©ý`~¯YíˆIæIRJìóî9\§ÃÞúîŸ8ÿ‰#6Ù6zC¬÷|ý“>4Ò~Á1ØŠŸÂ¡ð&Il™LatɆ¼,#ZÅò4|˜kf_Ñ‚½š ´¸¹K†\UÔ0½—¼$…´UO°ßÈùq¢úHhG®Ž¬’:Š#žY9!|úÿróŒ=HÍŠ@p2¡ «B#؉ë·×îüèt*KHÍÁÙ·Z<ƒ°ïC$"4˜ëêëç–?}z¢3ŒÔŸx'îM'oÝé#%ø÷ÿ´‹;)âB0®ƒôo%dLƒ›,6f(·—Öߤ¯J”¼u.î¯[ž<r±!‚J‘™´Sâ_í#6Ïíªãø ùßïÅ#%¯P–C2?¢?½öýfYÌ1?I#6¯Úz³:ÃÀÉå”Öh¢ˆ"ƒ’•VŠ£ÌwŒg#6„þ¬)ʲüöTÜ0¯Á=iÚ ÏŽ‰™…Bi0‡Ö“%ö§gpõ c%ÌU!ˆ1¤1*D\?S:PzΟ ®þçæ–Ë—ÉHJMŠ´ŠRÁd¸¸1?;‚ÁcÀÌÝ°.<Îb§ ‡Ö;nçÎN[»›Ñê Ýžõ{Cízƒ!RسãõÙ. S#6 žŠê=‡,”è>½¾zDZN°ëÀàô·•NV’2KÀ.*Ð~› àGX5ºž!¶Å/K›¬Û…Ø©Ó€yâzK¨Š*üÌ`…–êAn߉lä¦ï/g«”*|[•B‰³‰¿âUBP„ Ÿ£âÍù·4„¸Å„á8'Pf,ÍŠ¥d‹Ä1éÿk‰üš;Ô¾Ö#Õ„žQÒRƒQŠñ{ùmzIIJ…0êĉ´Ã–Å…ãd…å1*ç0%v_η?nTÖtÎ#lš1绸UgP=`Š‡*•IùI²8¢º'ʨ´§F¥‰ëPØA¶_NáAJŽ`µ}ÿ¨|ýÅÓÚ/O€>9X<ü|z`î7Éíö¤Š»ÍýVèŸt¹ úd¨HÁ^mQÔЯ€ŒÒkK™ÖH¾‚ƒºëÓ.pâìDéGb\3>:ZA*à¡=^Wû<eÀ|ä 9 âp]û-³•¦hêØyƒš¡¥ÌŠšÁÐðN£{q¤bB:8jð´AKF#6#}븟O`y½JÝü‰Ñ<÷ à]ÈÈJ¬Êª%¤[9‡_Ôöì;#.HÂ.Ò‚PŽó÷áùOžÜ¶ÕÜ’“ÌúˆQœóμ8*eåàx_Xú(ÉA=ÐIÓ퇌É\½èÖÈ,Råí <nÝÔn3Ûò'ÙÙéóøö–_gùUJè â¯Wô‹(X¨7>àý<ž?ÔÕ =>Âcp`¾Æø™ÏðAv}Ï°¦òO"¾€<ªŠƒR†{Ë€¦8r?ŠOësðo¶S·à~4Îj·¹£0gþðˆ¢¢™+dã(€Z‹@·˜A}îlí…rÔåŸØޟ̤]×Ý®¥éÎ#%5I#%M$—ee5hPmØ؇5o¢àLÝpxûÂoJ#.0I Ò,6©‚BÈXõZªPýÑ÷w˜*÷o6`>Ϲ#.¨,~謙ðˆ!"Æ"?» Þ`]Q{“jòNo.ä˾‡0,bd% ¯pA°kó:Î.îz³h*;¾ŸÏ»kõê'©uÑÔ9V/N|Ž¢½%œbªdÏ¡‘?*=…ym+Pw!Ýd#.Œ[*›p… ½ÈÁa°ln7â^€öÂ9„íLÀйp´4#6ú}>¿|{Vuï-¯MŠ«¼ô©)mï¥Ývó·n°EÿQõ½Ä3t6ËnáññõÙºÈÀ®Þn]¯C9Q›pë’]q¾#.ãvË´[ÒQ¦ßô‡ßõ‡…ÌšD÷Fˆ§œ-hja°»^¹B†‡VͶl؈Å#%PX°V0PPÓ®¹ø:ä nî¡zgžxDeñŠÝÚb‡Ÿ³ÇÐ>ˆyÊék¦ô>z{h}Ás1óAˆdÑä–H>Ôéf X}„Ô¾îÍžÌ#¼ð8&`ÛwÐ6pŸÑF¾ý…BBM¬#6I#6çá~Èëž H7V%íü/Äÿxên=@çE¶¡ï‚=jÛQQEV$[jUUD:ü‹•z⤊^*eV“Ø”!ªú¿¨Ì&&r¶l|ô—‡D\Üç´X†€Öi¡š?«Xiê%x¯<íæ®ézîÒfSo—[Ö¨ ¬’s4üåú¿ä'÷åcyÜÕÕáP”Ðv‚SÓú0Ñ"#òÈðÍ‘~éº{ç´×Dª_*9äÕ¸S^t œ§(MÉg#ärö=“§ßÆò}²„)NVú¿A>?Òe ³èÎýPã/#.Yl{d+×V=µpø£µ0têÔ¹2ý0‘‡â{Qnw™vï÷ê8KÎÛ¨àzÊk0Ó2¡#%óØéüú»#.qÉTÕK£ë‡±+WU‚шù÷ÜxÉ°ý*(D¾Ý!Q4ãH“À¢¤çæT,P@X$àY´5eHr€ëþ;lýÐèllWTKw{ºZÄš¬û?‘ü£øN=‰ªg±‹\ļ3à1ê¢Il“¸ù݃ã'0´`Œ°+Ãm÷@#.ÉY†Çî½Éc÷µóy§#êú’éàãœ/ê#%B§ôëé._0þjà%Æ/ò9Q»ür#6Wúÿ=…rý™¥ˆ“âÀ¡(‰#6`1—M3-“ÊS¼ómÛÛËÄï)sª[!÷Œ™D–TL¡Y¬ß¨è™Žhq9"ºT"Õ#6+èAá©…¤©˜¥q„¬©æŸøÚŸàúY¡š…ûfgñ ²¤(kf:ñ£ðåØŒÅCù¬”#. çXÑa¤Å¨klmYçqÎ3Ö~73™®Ô.¦Æ%½Q²’: v-°—r· ê0#6ÄlÖ¯H`³}gÏ¡‘t¼¨Lýïä|~Gôÿ‡ìÛnîv<ÁÌ!ìÊù#.‹ÓÕwÊŸ†,ý†Ã°õ*ŒUŠ0BA_Üá÷KÒ¡‰(æI:§*•‚þM?µ™Í®ßÅÁ5›ä=½ÖN}¼NÍ\ë׳ž&‡í?z Ф´Y-…„¤*¨õÃ~‘#%·žöµ¦ÚSŸõ’ Qk„#%Z…z³#%åÝÈW;ÀÏ„§m`ŽðŒÝ%#%&„âUêï1ó~{Žýq×ÙúùT{,eÃMÙ"bª!œÖŸÍÌ"qùz„Nº|úw¿1ˆÔe”É»D@’å€Ày#6#°2;×₾÷%þ÷2zTXAÄ#%2€8Æð*5@åï'»ˆn„Êl„’V#6~Ž\bHQ^ÿ½èž¿,[>G;H=iêPÈJÞ§b¨$ ¦ð¯ñ”LÒú˜.½ÕÎ`ð4ƒÑm°É(4^?b——S*èdðpéÿ7f§Mó ÜÖñ?A‰£7DË$Ë—tÆûëÌ›dÞ>o#%dˆ„*6<¾Þ<Î= ͸_èyËa°aúðLfë9›ßm‚ÚíŸãÆxjt3û±]]î~N.5¿U4†K.w¤Í…ÒG…QëæìøŠæ°ÞŠi}‚kCeúÉî.§ZÆ8=6t™¸Yr#6„%Ý7"—sPDÖL1ܱ þ’‡^'y·=Ÿë;â*Slb¬lˆ{¼*þR{„\2ˆî@¯çk·¨˜ªØPîᬅâðDçG½#6@<0GzÂò£|~Hø¤þ‡zN† ˜ØûcGÿÍý]ŽbïÓcy×÷Ò…Ø!éÅ!ù‰Æ?ƒí·oÙÜÙ?ÉåTQŽÜ)ÞQñ6ÓÒáÞú_7Ûƒàò‹à#.èðüª½ÚŠpÉê7?v¿~óAÞJî„îç'bœ—û,íÜz@®‘”^í@q(M¶9T*²õ†Ã.Évm‡¶Âºˆ9ψY t Æ"ñÌ&÷Ä;:„×ì<݇Lò0âÃñ{ü×R¬þ/$œúý3Ò#6?¿>þ_Óu,š6æšsl»sÄl˩Ň0â•Õ躨ƒsŸæG#ä.ñuÁgg¹mPÿk¼§l$XóüE‘æv˜Q?™â[òÏÞßÆIGG”HNåwÒSƒîýçâ-‚¹³\Áx„µ”9ù¯ˆs£†{©¤`öù¤J÷ªÕ:^¶‰B4žœË?ÕzfêÞÿ•§„¥¢®¿)#.!£“†a’€û1¬KÚëõÌó€ê†2_‘ã[¬{™lAnµLL#6#.µ› qÍ¡S'[o”½ÂÓGf¬(`ƒ{JÂñw–óç¦$ÛÇRuœŽ:g<®ÏðÛ]3êød¼çŒCpêÖ“4¦8óe2övÏw]Lâ0W½k³8h[¡åÁ,¶ †”{ö}/l\_G–H½£¤á¦#6ù^'…òlŸæ¢å®#6—ÊÙ.rƒ‚w ë0Þáü~-¼ï>]¸ã›ÉÀswsÜ&…ÑÜÝÛe‰ìü§]È]2ý3„Ë4Ù—–£UtÏ[žmjpªž¤9-ÐÁ@AbŒ#6™£AkãÕÓlÔ•ùúøoTqŽ˜4°—gÒ3,Θw<vâ‘/£Ã±ü—3#6¤39ߊ \ëa7gÖâ¸<Ž«I…º×$ #%‚!¼|_á1wç—£6ôtŠ‹3kì'6¥Œe¶>É1~¯&#Ùî+ïUÜ¿îÌ×}.µŒŠðƳӦ³K7wMïTD_UŒÒr{RniÐU!Üñȸ>mòi?]“Ðúsyü®ç:áéõ#6Œõ›3Pÿ[~´ì—ù¯§¹OšÏ.pìà?h‹^UÚfÞ|ð‰ 7䆨 DN&¥6iQ-9z-¶bêwþ{£K–Ý4d@òuL:Ø€ÝrŽËç}ÖÓ¶æ8\à4›Ýœ&dOË‹x”tð£ÕŠ1ÕoêïSßþþt¾þßã}¿FZ÷|iwïòÃ\EÂ^ÃÑq¼.ÎúyµŒß³Ì#F×›šcró°Ê1J?„yŸU`< zŒôê9Ÿ=(ÇV#%Û»#%øÓY]¦˜ÆÉNIör+Í0‰Õ‡”#%)aN^/X#÷$ 2ÂOaA~eœoa#%†aÓÈÅNÍ¿GõŽ‡ðãηÁ8•†ÝóÎ$÷ÍKs{üœtJä^ôeìQú¤ê(LÌ#.!Ž~ž=.gm “iijÞ]¨¦b¸MÐ6Eî@ýŒ´‡Þv`w‡aY<<ëà›;% ‚Ó3!.áé»IÕ35Ç3å·®N†úÇÓ(¯§ëƒù6‰c9vòD[µ4;ȘQT4¢Æ’R.²aV&¤BœÓÕ¯Nh¬Fz *åQH¼Õj´$fU"Â67U’<y„bןžb® »T†#.‹Zd˜‹K©ƒ¨å9Ì‘‘?¼’TRëøq©û߯:ÌÑÜYE‚zh€À‘O¥òúÑ'Â}ßϯú¶´ã´!hRéÿgÉ8_‡ú+ƒvá4¼þxÓËDçèûý]g#.ZŠgùPô˜}šý;ñšlÈ°†± IôKû«ôlpýŸHN#öþ?bpà€~á¹#%—$!!ûß#%Ü•Ul?Úz˜Søól]¸~·a¶ÄÛɘkä öjk#6vè¼]ý#.çˆÓHIH‘Šÿ{µ?¢ËÀ;uM·YMQÃÈÓ[q“Ƀ[N¹´ƒ€&ÚôunµÝW€x¾0óÉ籈×p4-œçPÖ\IÁ¸7\¶Öò¨ªñô¦Ü‚ Béǽÿ9Á¸t<#%<‡3§ê#. 2¿ÛÐs½k•!(Ú*]𒪨°©A+hX™˜’Æ?“Úvÿ)Èi0h~~ë[v[‘¹a,îÍìw ÚÌÖM€N0Ï|¼Ùª<ÝД'Qä{jWÛö[¾¿‰9`±Mà®P³(yÝ9qõEX‡—ós!GWQ®Ú-ñg4'™io/R„ á—«®Pï¡ØùØk„•¬ÈŽBé;"kƒ£s_¤‡}5fŠcfS.ê²ëƒ€Ã2 `D §ÂSIˆ0&„b1"áJ5ih¤˜>fRàH¢Í«‡N¶s8„ˆ¬ Fe…qWžäz’§d¾5àÖ…!;xÀÞ>ï†ç†eÕeB-!Øœïm§0M.Î"Éô¤à7ñ¡NõbÈ>}¾X3ýä…:²íäÖNÅÍ÷ü7º[Sò„aéߦ#HáòÇéwo¦¢Ì³ÙÛ´!€;Z-"¡ø‰è’zbS Á#6ª…°ÐÕ’–²A±¨ÛAp¥i²e¢TA Ð@h=BµËŒº¿ f?´5û†»›q&}Ûrd^Wð?üßk{K5™ Ú,_Öï{¬/+Yüt1Q!%¥*‰d`ü¿ÚŸ@R¾Ä9AžäF"ÕÝ·fÛžPàw†£|}¯ôh„ {·g=×·½¬HER!AÓË´Ößè,?»ßØ=†ËȪŠÄDy_7xÿ<íî,ö‡+ɲ.eñÖàÈÔhX؉‚+VBG=«ÝŠÛV =¼R7–Áý.ÎùrõT'(f¡Úh»V7Ê‘¤–sW´Ð’Ž¦À&’áõQN!ëMpqZ%·d岘—nƒ¶;±'Ú%1†LhˆUQú#6l}i à ó"™e¿¦<BˆÈ|½†3®Ü<<t¹Rð`6¸Ç&ÀvîÛvŸyÂ8Û`«pBº¶DÆd“kOVR\:¤+‰;ï#¡s°îÝÈãLºÅ€xórÇúÝkå.̇xžÔ©¸ë„4Úgu“"0úË!we*ñt5æ—:ÝÎ&e%•ßãD‹¬sì·.¬é5Ĭ^r)Ãg[Äb.;a“3¤ðžœX8:^Ó·¿Ÿ¡Û&Éë“Ïs^½ÛÃõwG•$fž>šWâw}wλw̸á‘ŶòÕš˜Y$6ÎÖa7Åç4E#v8œ–¹omæf¸À¯C'am²H|MÜ4!vkÿ!VBÞ†ðÙ:Æ Å]å&@Éî[ÝÈù1àô#.sžµ3/MC4XÅ#6ÅE)ë‹nØáã´'a‚Ú[–\†£Àº½ŽfÖ@ƒ¬næ¨|?¢©£_w³Ý¨Ï¦ÐàY”ÎÀlô2K™‡?*#%a8xIéç×¾=¾øs¿AÜrø€‡‡fA#6¤–±á·GÃM§o#.F¾™Üϵš*wQ¥íè¥T—ˆ”ñ˜›`Ñö7€õF4kÈAËð#.2ìª0hXøT¬ZÔß´åb µØ=H<àXoA#.!rqíg#%[:yléTËs©ÓT#q¿`Z×Í“^!|´Bl¶Ç¸ò´©Ê=1Ž#ÖÑ8šÆÎ$'`äQ.º%ì+¶&†À÷{¡èzy…‰5 °ÔmyÁ,Tí6¾$pd‡—ø(Q13(%&ŤÂBß²°Ôla¥²HrÏ#.æcÎ<BÙɤôÏÔÞ»«[°ËZPX·wouMÁÈ̪÷LÎi‚]©C£0=ƒ°X¨îuà]ˆñ»$8dï;ˆu!‚ÉÒ@0äPT®a¬xoà„fv‡fåWl×€Œ G–¿àªˆQp@©Öö›¡’”MÇ“qG{tØ&ðºSlƒ* z½39Ã=NùÌÌÈðØy"¥ˆw'ÐUÒq5¶Æé¹v/¡’Ëà24@·øl'âlȳ§w@'©gyÂvk–Ìùµ‹œÒ¢¬d¤Í„4ôÎ¥ë|í#6£Ðà«5t¶õMÚ]î‘<#6i5À{WÓêB<ᤜ‡”16;€¤ ²D`ª#6†ÍɈšN^DYuQX’¥ŽÚhgLm¤»2îxNKaÊ£R+q õ(Xø8œLÎÎoE”¤hÂ.JhÚBHN_C]àYÑÉ.ƒžÅuáŽ-,\à@ Ø+ǯ53¢,UæeÇ™™2æffXÛ1¬«2ÌÉ%Xì<õcˆûªjñl$0n˜Æ&z¸íü'qÚÌQ¶*WÕÜFÞz>7³wœ&–¥éÖû½C›9Û#%!zˆlžP?V[¸·=¾Û»ÊÄsÍÑÖ™Ò„&w·áÁ"TK¯=k9çÓäôzp¼m™)ðièÖšÜæÄÙ3=êFC¾hYCS8G+}9•ÇÏ5®ÍGÞ˜sÔÁÐi“·d%(ô#6×nÜÉBiÄhŸáIw°P¨¡%Õ,±h¨¢^4˜{ûw+Ñ#6Æ]Án™³ÍÊÔJ,!25.!bÌÎ’[úsHƶg6|kë‡P奕èŽÐÐɪ×e‚ìëÁéë#.dÓ’C¸ÌG)¢”AkotcC~[^²Ù#%D¹ÞìÎBI(×¼®Ò´U6U;â´øÀ¸}=‡»Ï|#%Ö«Õ¬´”ÑÂ7:ðäUC#6ŠS¥Áµ;’ȉèáÎ\6Fj«U*F7Û`žr¹á˜t¯äXÈ‚ë@¾v…^µšG1)ã^ï4¡N)LvàYÎò`']ÑL„ª5m²Ý3ã}´Y¼5ÞB@-º·¦ü¦ÏFµ&¶€á®) l‚ÐÑÛµŽm,¹r,±aòãH }3V%O2‘(ö›–„Ðà£J(‹ÐâHk2fdçqªºDôòÚy"èN‰5«Û¹ˆ]Æ#.¡¬Ï7Á£0¡:þÿç‡06î쪻BÌÙµRÉÊ$Ç‘‡cHÃáŠÂûJ$ètU&WD9æÏ‚š*Ë!çÈÜåÞb<BÑ#.#.¤E¡Fy ha½¶E· 38aú-á'/JZ*šV¨i4œ`hRq!]#6ÐΡ«0Q¢e.‘ÿ_B†"ûÉ¥#:+R*‰F䨎ãºq=r@õœ¾_ 9Û!ß/¦MHYˆLÓÓ‘gq.Ôt=vü7]Xu&I¯Ù„(Cy¨¼°jÏ4±K¨d;Ä¢¥Ú…©#.üJ”$E1ΦÚ1‘d‚€k±€×´†³[&ᨡÁÄrGÌXÐ!™"|&“4уIŒƒr`ô<4˜ÒËMkW€gÅ7&»Ó6ínS./ÌçÇÚÖÞ°‡F2õN;@ÕDf¹â“»ãUëÐûŽÀ%NÐn_~L•À ‚DH²Óf©˜¤ß|oŒšR½oªó æ85IF+:ƒ5_`d{ÎuȦ¾¿\–·\g#_-7‘èõ–ì{Œèìî"æ³#YCi#.‹×ØÊW¬¹7R¹ÉØÎ!‡‹±E¹—*.I¦›#6’‚Éw×¢½³áp$Ç‹$‹#.Îxy|FÀPL’Z#.š°’(Ì—‡nÛm&C¶#.F´"뼡%Ï·z9¼ÒjÄ›6¦„™Þ0ð¾oHCøŒÇÇš‰Š¥7L!3 „0q‚Ä#%Æ;{‚[‚þ@o $ÜÙVI6“-Ãp%A<劂O××'ŸP£ÿ¯¹Mu#%†I|E>½Êõž…8q¼D¨-ê½0ÒƘ»²ô:¢äåê=¦‘™&-O醥éÏÛgÆ úîí²]+<GHk%Ù¢˜*>ñ¬K‡¿·M>i#.$×ûwã_ÜŸóÐÑŒ‰¸$ëÌgñ\W«§Ñ3nWVGM§OUªy#.ñ”ÜY7#.ï·»·=h=BÐR´”‡gÌß»´_t=V[(@²2N. c ›!áHôØ>((Q6ÿÅ7Ý*Š…=Š°eHÐÿoµéÅr.#6ë\HîÚæzyŸðCÿ?²e˜³ý“€‡BÔVKÓÝò<`PA>B¢*£!>Ÿy5©÷Tõ.¥(¶fÉ(ε<U”vöú|G¼z¨lô!á×gï"ûN`´é~aõúCäD#6F ‰[Ûº‰7÷«nÓ6R[êž‚P!½ÊPE.xùš®5†#.+i}˜2AA‚ˆûwR@ŠH¡A)Ò¢+4Õ#6˜ÚÊ fkÙƒ:·oNqï$$$¥¯b¸Îô”yØöpÕ$µ@© ‚Ò(øì¡Þ§’pm`<lr°#.ú¯±òÁY±W,ï$ˆÍø àž””ôŠ]CP÷À;?8¤D™v#6#bBÙ-’hÐ I÷ì{ÄB"@ˆæ|§®\¦€ú¢Æ*X4GZ‘A#.ʛÙx‚{i#.¤Pd ‚##%=ÀA`|x_äCR³¥éó4èÔ£`$‰kB1KºíÁ#6Xƨ1tI‡ÅÊ»{m|®Õé¶#%¬Pº5ƒ80.˜HÜÌö¶D[¡·¯¸ê’{-Ïþ0~ðî6#.ÐÀÛœuHKÓÁȬ;‡Æè+ù(ª‡—°0ˆ#6AÍ£3¬%„_²Ÿ\$Í•>‚&@!åÌh!7#%Ü#.[Ó]ì¸F/ˆ5û"•µöi^øUÍÔdwéÖn\¶qüŠ/ ‚êÌpá<`bˆé·¤Éê5e6Û}åcj墩i,ÚAE`d;ˆá;`‚þ興è«UÛ4ÖÖéW¼ªÙ¬”àc‹òz9$ž»u[}’ÌnS(î—ÁG¦"uÆäMö#%²¶×/lM²`Î3ˆ™ €Iˆ‡BÅ…‘ÆêÜ£ÉPbî0*z*RI‹z»·M·7©½MÍv%»´^uå»ùÞy·‹›¦;¹ÕÍË%ewh䉽#.„ºEhÒÐ% Ô¦¯>6õ|,µ•ŸÁ}lƒ@›$~×Ú#%Üà=Ó$.4¡°†eÏE‡Y $$gñ¸|o'¯Øÿ&_xr¤¹ße{ÚƒŒ²qõøç#Øéêô/Í9xóÏsLXËü‚ðDo™Ã7æzF%€A\Gv‚íg–}¡õ¨ÄhKmBˆx#6I!yb°hÚùëí*×O–é¦Ïî§{h“Vø¶¹[sU>þ³Ö2šf00$Y§Ó‘Ä·+?߈ó÷ã–ì#%OPÐ?ÂUHïjÍgy诨·«f#.¼ç•™r£k¸,“‹CÇ•sŒ(lÈd×XT‘˜„¡…#.-¡¦êTÞè`¦–—`0¹þf( äC.¸hïâÜ_<rµXzèý¦.[÷ƒ9WlÑþDÝÇ~++yK”ÜD„Ýèó5Û=ÇÝåó†Ýæ°‡_uZ¬ÎûÛItlìßi!,òîh~aÑCåð#.ïvœ±ß“!@»‡#.W8Gµä´®M[q‘³ó'ôÿ5çc‰yΉ˜èóó\s‚ÚU’Á´ü³1,;næ)šÈ£D£lè¢{Â#6üýE„ª•RŠNÞϤ:oN‹EŽµbz©5RöóžnJ¯\E k}ð„Gp0³5+"¾•N¤zŒ¥|JNq±z©$MÅkÔÃâÁ/H…çƒx8˜ˆSø{Qy´¶"ØØ›}¹ú+<·‹1™#k²*âªh–ü·ÖÐñÓãïí„()de1*#úå¢#ј{<¤©#6B~´0„úÑPPa¶ÉV‹d¢6jÆ’l¢JŒV++-«M²ZI[Ò[YQ¦ÌÐ Š@ŒAdDõ”ü8üú—ÒZ§U¾¹G]WHsÞ"zådHª2#%È õúÐú „˜à×ùuÜßÄgõ¯"ÝV7Õ6‡?e¹¡€ðÐØ늣šˆ5ŒhXdi½ú#.ˆ«,Š_Â*†ƒ›”é’i›â{ÒWÍíï—BI*@<&k(}¼Hšt¯•MÍ#.%€hkI”‚/¤Ò²fIš¯©ÓrÝw‚§º*(»w*R Z(íxÇÕ˜N{Òup£&šÝ¨$[j]8l JÄ*ʽ‘mî°uA}žCË•¬ ê•##%©QM¢:ö捻*$g¸À½3ÂtBE ÁP‰'H¤!‰-EjæÜÙ+šÜ¦l)S(ªf#.EI¤)¨ˆ‚€¢“ç™÷O°h!ØÜÓ&²Ð8@#% HAdA•NÔUÔ£Êúø>|Çí½÷z»Õ‹r¦š7ËHdaÅÂ&ï’€fA=/Q…yžwßv) ß#%ÖHˆ}0!ÂÓã!ì?çîÿ—ðûoçôŸü6*‡œ9Ã}ŸâHß$Áßég‹/ƒŠb¬5B^!Õ i >ÐŒüÝhFŠD"°¢#誨d|M×½÷ÇNV2]Q5J…:4¡x¡²¢(.QÒHMŸnš]E>¶,xC6tŒ‰h/לá†É»²Ô{hÍDhËþ8_£÷ùóÄ“õÒ+µ˜A#.¬Zb‚¡žÿiÞ0"$œqÝ»1ñ;“£n²èÓxIx5S²ù2ûõ#.Ou‚ *}–À©9,Ê1üKûEåžè')®Á²#% ÈB £H싶0›º=IÐøõœþÚ;B³q@lŠi8"d3¿!û±Üx#.ARúì:ýØp´xÔ…ˆ$‘¶¯SÓÖû·¿„'ˆCµ¥ß£sƒì¡ßlQñ»aúpð:r‰S²^uÍz놳ÃéÊdT$Ö`ý²À ”Œ>·†D=‡ÕNž3%c£xIîÝøµÏʼiă=ÏI#.-3}ûüÉr¯ÏõÉÀÃÄ#%ÿ4vvu„#%ÍN‘‘vº÷n¼Bq†ˆYƒ¼ê…(›lÊSiLúÚ¥Äcí&b‰3ãOx±“»#.k#6P2C+Ž´ãLjAçXô9¢”pªq¯ïf÷³®zçíblS¡-§œÚá&+šíXRka\¹ÙrÇŒ`„X#%ˆŒ žâ’¢¡ë ò£À;Ÿx‹¦h}§œ6uò:«1[w~ÿòÑ ³?Ä7¯(iHPjÊj¨¨4£QPuQöt?_‡ïbɱù*îíw(Qa–DçéýM ™ªEbyf:öõ¯ŒìôeÕË8C8èPX‰{il[ÍI~#6!OLcìš¾––@#9OÓçVvÐøX79° #¡Ž@úA¢•V/5MP4€0#.»Bgð¸k7ŸÃ¿ß^×ο}Ò‡¯Êùñ‹(=–côôþÁ~‡'‹¢!ÈD£v^ÿŽ#6îóÊ@²²ZÐXzñÍ}fT°Þ.Rá>çæÞ™¤SÚ¶8‘ècs(Ÿ:óž‹Á¿>z œ,>cº¸½¾1Þt*U¶¼G¡ÔLc3úZo¥î«ÂÁÞµÑáè㥷;WeÓÇc¶¹åpçqF;jÌt,½ÇÙ¤mÖÙ*a\£·¶|kžµÐ/ç¦ÅоÍhŒ¯›ËDP$S¡x7ãmš£"äŠ[lmp`¯h8x–ÁÁ}7:ÙדlíA¦ÕÃÃ|v#6õw;60ný1ljmß>øµ;Q‰šcµnÊ$[ž9µœÚŒ5)Ü‹QžaH$ì£h8ŠâæfÏ{xÅlÔÝ.£Á°_+À£ÐÑðøbsaצóëÇ#%YbdSÎdÆ#.pûŸs{¼Il0ÜûÙR&ÌêAÇ#6ñÿBHjëP‘¹A골)œHTøL3#%!+R¨Ï¢VAH©JÙêÄcv>8}¥hà÷·2Ö|ÊöçÛ!ˆ¿#%ù˜ì÷;vÀ+…Ã"x%õéÑ×Ûc@¤«x@¥Ñ ºï¡Ÿ8`Ŷoê¡ùÁS’'+ä9Ùýâ6*êvòñ‘š¹lØ6‹Ÿ×ÆÛywPõ˜¤/ÊfïWÇ Œ„¬ï¡ëµ#.W#žÃòú¾ ædôn;vêÝFl[Ú ÷†w6%‰9‘ªM–ÛpZód½™òK[?i`ë¿WúX†šAžÔ„†Ó}{–n#.˹ÏeA–Ç=ÎB:£‰ îÎhá#¯$ïR¥ˆ"Ã!ÙÙã>|÷É8Žf#.(¤SôܾöÒcC|¸á6RGdõLÅõý}Œô=H )G¯†÷&r ‚0Á˜ô€z•s:ñ—•wdêßË-㡧"Ö$3!~I$ß¿Ð4!•#(í¿‹ Ó]àôöklÑü™1v.ÂŽsϺ&®]Cj¤„Jöm7†ÊöÚ´õŠšúAôöh†)(r¡ÊV‡T’u©£Ý55#hý~=Yäk©"N(Dï·Ká%OÔŽœLzJ“"À^q\øëÑ•X0¸°&ÃaÅ?Õ³h„FE5|øeëëܼó¨_ ë'_·&µîP5' ;ûè`õVSªS¼Ž–ªˆ*·Us²43d¶º\ÙIÝ]Mœ‹’ÆÚš[]¾TŒR5 ‚²0ƒ¹`¨ÈX¬%JBŒ¡i)¹d¥ÖpìnæDp¹”f²áݳ ²XÒŠÄé½Vù—µôh´55Šf¬Ê‘E$M¯ê ï!åÄ£c#6låÑ1¥gkÏ~ÓcíÝèginÿu[ÇPüúᆤ„ ×#6Ê´—é+¥dÖm¿JâÐÂO}Žˆgº„#%¨‚#%ÆÙCÜ#6„tü¥È|lûžäðv+ªÁÂ"®Ý ÙS)D¨ìr«–4óI£#gm‡±°æ´Sà¨P©Í×p„ã²µma·Ø¢žÏÎu„mwñž`W¹ ¤vëC0‚Ù_·«#%b ž˜n*F¢[föUŽŽÃÛ¦š¸í<¶‰ÞNHO–Nù'ùîHÙ¥#6¬…#.¬meQ€Fˆªd°QB$J”Œ`ÈÓD©*X²R’ A¡iP‰bB‹>&í'5mÌSÀ‡aõP#.ž3¼ù«¯Uõ_\ÊƃnëµXÚ€Iét÷ÄAÓ¹jr‰ir%§Ã^[šõpÉ!1ßй×ÑXZÚ§TÙóYüÕfǦ³¶Kò`^üBþ“™:â"€^%GPß:D×-nóD¿\“iKÛ6ªª§£Ú!ÚE$H#%;±Ä+†¦ûãÛþ:Á‰Ù(gœëw'`v2›OA¸ºÚ¡bˆb$(cÕ¨‡Bô6HÐçªõ&™:å„Á[ýBðn‘6šLÙY¢’&oáoÃa gZæ4ÅS$.vøü}^Gwxù?LD(:íÔl†Ú£Ø|û»^ys*½ oèo’è¡ýþ{o´Ò)”4±"ˆ‚EƒÖŠ"Îg™ÑŒf¯:ºJåM.î›8ˆgÕô8è$€ÍI¬´e¨Ùòˆ”7Ú!ôäþF›9Îòué¬ÌV°WT…vgˆ÷ÙîNž^»Þ×·t) Õc=#j7®çkÏgÔKm&ðŠßÌåܹÁ5.”SêÔÆɸŸk4JApN:lŠ§eÏÛƇm2LÆ?¯]°ËYØ¡¶“¯Áª¤úCÝÂûªC<sLTšCvê‚ÃA ´»†Ê/>!!hl5ç©\¬Ð™J\¯×¥ dHÂdê43d=BÂÁH#¢‘¸pK5Qz 5ÑZt%Î^E†A‚@–@Èœ_àæ÷e{>ñ¢ ³<Ô ‰²Æw+¸s, "õ€5#%B «Piuô+55ƒÁ‚Ú4ÍÖo.Æ#*H) ŒBˆFõ‘ˆ®¦`Ș`¡âÏY¥Âz¢ØÃɘj8MË!KI} ‹A#.``(3ŠU€Àõ¶Ô&¢M•·ÊêëlU”j0À%Ô¥†-Ù_›œá烞;^Q=õ\¿)ŸI#6w¬©”H¹`Íöò€¨Åïå‘|ÙF×mÐA;8ck³èÚ{Γ{44‡ ”JàÉÜ™ÕÏ€}LPCæyBM±dY¾‡«²›ábyOÄ’ýäµ’ý™ˆƒ~šÉ¹úšØà›Õ#.¼C"¶‡I<Û;úÏÕ‹{FÊinlü~}|ßáñBûÅÉ{øºF¾ dìàÉ#6çîà±!!1134ã¹c÷t³Ê'HœC…˜-&Å#6°ªhvÏWqþD€ÈAˆèªt@‹QÙÃÇD¹Ö ‚[!äåRÉÒs'tÃ-‚T#%g£è雕*ª“ÒY“Kt¥ÅF"(«UV#6Ñ0&(÷&T¼Ç0}zšƒŸO‡²‰Qj#Æ%…ŒN¼ƒ±€¤nˆ²Ÿ™(VMBòÑsýì¬ãb¨LcC@! ƒ.¤ð¥t˜1'?]‡ˆŠ& Õã¶ØÀÀ¨‘i‹®(DŽ÷¨æçÌâŽõy¤Z¬Ý*BèÀ½ej·–Mdø¨Fí¢FtHFØÑ}ÃZ0À"'•¦?ÑâÊõŠhØÒ;ðdYC*Ï;C¿ P…Ct_ À#%2Nÿµª©°¢dÔÔH<ü%ÝqHMÝI\uª´’ßßÎ ŒäLĦæêpÚMÒ<ëç#.†ÚÊ8ôã%˜fL‰QÇ&јÅ\‰*…å‹01ñxaj€ ;‰”ž‹ùžª{è™<(…pÑŽ®=¬¢ÿHy!ŽwD0íµÞÇ•˜ÔAc&™´²–b‚˜£Š¤Ø.5sSIý›1ôj2¶ŠEH¢ƒ¥·ÆÏ,k¬Õ=ÍñÚÍÍ2äÁEœ¸¨(ʽˆ„ :¡9ˆZkµ‘©´öO<שMFØÚË!Lc`0Ò3s@ï}Y÷N#6late<˜pÖ0÷J"DzØØÓˆßyCr6±Ë#%=l+§j¹0jÒOþ¯|×f>-=q¦ûÊß:Û¿ƒ×¢ó1Èø¹¨õ’’ ^¼P“\aa£Oö¶³r@^õéu4¨’ãº} ˜xÚc4uÆÙàâ×Xgds°•;îæ"ÆÖ,_ãMþºl„áÇtï¡“ÛKV¬7LÅ¡8ƒ 9ŒÀ„²T(5T‰H†„™ ‘¨Œp_;KŠ¦ùeÉYJ9-Uà AˆìXÛIò–.#.áï#bì’lÃIm£€õ˜U±ØdáA´”Ã)jѹ ÒMF…Ý4m¦¤’j!6I&̓³¦&1>ì!áÞ¦°ãm.[¼R>Ø]™Ã]+ÙÒí.ì]¥ñ•éx·´®jñašJ¥-#.ÐÑÇ#6†ƒ%±dA‹af“ÂÈ[&fVî£ãøIå.Û¹ð‡Ç9ÝML™Í5#›Ô’#|¼™[A‹Úæèu˜‰_kØAÆ:œQB3³eÀÔ/ð°ÎÊF ws+¡-+¬œpä.Q‚S&h%mK‡ƒOÖ¨fö¬¾ÒŠ‡·šñßÝä:yGÈ`ÓÂ$F…½"šˆ„ˆ»|hÙàP;#.aHê‰Å4¦”K_kà@Çip[6‰6y3÷ˆóxpBÿFýfÔLâ[6ãknk׫‰™®TãÝ¥¹„ÊÊ—ÎúNº¬Ó 2I+FÂmá‚ØÙkå>)4+.b‹$馬ú‰¨Ë¶?m…ÃS>^O¹ðã³*0üÉÍhæŸ'£~;j–0æÕº5-Nîše¶F¯Á³9HßGIÚÉr,=ä°4!{nî8nØýÐë’Ùë œ~Æí:gg¿˜Ðƒ`È-@Tùÿ/¯Þ?D/«¹ÕíاÁB °!D±¡DÐE‰Ï% #.&3í÷R’ŽÖâƒ6æ¦÷0Ü Éu*:hë$‘ÌM4h@I&…M/WX(,¹§fh#6l°#%„ÌSÎ`°£X n¦yÓzÚ¹‹©i¥RJùkïVr2L”7käÜr˜E¤¼Ö¦EÇRJðÉ·ÂwXU:½–c7ãweû‰ÚŠ(AÓ×Lo®ÜXçû¡¸×-•=®1&òXËÙL9Éž‰! ’Ù}Á¹IǾä\y$þÉW¦G†~ÃE4ªm©•ëS_ÉþßÌ G®#%ˆ£sºvYBog•?’~ËA%*j×Ú¹âÛˆ,#6ѵ¨¶¼TjæÕ¶5¨ÖÕÍmͶ-V6«¥¥ÖévCÙç.'Q÷¸ÁE5bõG9;#.¶ïY4¥•(ö±Ô÷u;ƒk®ŽÛœ+¦¤ùï4šÖ¿™D’4$‚S&e3JÓ,É)I¦)5E¤ÍAúΤÂZÒ1Q4Ô¡’š"P¢´±¯~Ü¡“ZLY%™[4)“4Ì‘˜hEQC”M½ÝDFÒY)˜¤”–PÃT%¢Ò£EL¦FhÆL•¦)¬T„Ê&ƒ%!MA”¨ÌA¦ƒLÚcH` ‹4ÑÜS¹&óg¶§äe/è#6b|[|X&Aï~lU_SBݘ×Çøf̈þ¢˜;708ëY!,”Ìm¯Òfvòús†Á©Ù9`ÄÞ¬ü8Î#.©‹4czKÖãj\ ‚0Ž¬Î]iÙ¼¼Ú(’5÷Ðz—àmÚn.Ö›¨—,mDñs0³Ž¥xÂxâN;Z-_ßK¡# ý‰}úYJÒ»0teŒ^ÎÝfw†’~âÅçãîùA{*ÂïÃñŒŽƒbm°ÇŒªd© …ïúûZø^Ûo³_‘bc5 £#f²V*(“i–`!Faå³><9ôí0ݽò¬X ›#6"ƒtXaÛô3³ÙŸyèNUMæçaëÔY„b’™Â†+ÏNbC4®}û0_ê®[ç/c´y‡OÀôW¿—¼‡}#êk#6xn4ëO–åûÒ3Ï=GQá’íPœê‹+™|ÎàÉ ÒV_£ãfåÄw&}z^Ë –p{"r>Mõ£ÓG]‡eUY´#.q¨ E)¢AT‘$/rdÇ¥oKý}eÂGÝ2û¶ÛùåßXÇÆiKôôˆ£¬Ê%x4ûmÁ÷cÇ—$I*#1â8-¹¬-ߣ¼8ó#+ígÇö£‡dÎCìÖ“÷µ« ªIm¦úõöÃ{N¹”`g"â–PR¨EΨ¸)µ›xãcB?+ç#6cguVÖˬ!çE®PÓŠ#6Ú+G0Âè'#66[ïG8 ápI5ö,nm_&·É‚Ôè2…ò˜í¶£·ŽWÂc.[õ"^7L³§<1YCŒu-Í4î'¼Ñ¯$º41²Úîg’Œ ®„Ä©OSååšä‡½ðæ^R°öœA虚O?M"… CòqôÂdìNIÈDBª‹´ÊxŒK'²Ùª ÕµÞŽ¢gd6Eõ)×FÛÀ¹m¹™—¼2ÙIb=OiÑ·,(ŸŸÕ5X@ý\µêž{w÷7g FÊHC–ï‰:ڷשÔ'®½]Æðëì±õz¼†— Ãíúëïúv“¢B¤†xà÷Ç}IÁ\³Âw¢â‡vÏ·§Ó–æÖ_¯¥ø? 畧ЪX‰@w Üóßœž5Ìíí†á#6áí)~¿.wðzŒàDà ¢Ÿn”GkBkjl8ògâm»]š•%};àf.äIG^fP–så3 jÓ5'B;Cq¬ ™™#6;뀛<rÝ &\?#€E„C8(p+x#.jÝ_âN®ú9pÜh>?/æåüÛ>é5ú³0ù²Yþ–¥7Q6È£Šö0¦Ä¦ai™ªF—˜»ÔÎELš¢ƒÖ¡•ÂŒœ‘u§÷5˜Ú30ÞW>ØÙ‹šÓ©œê<Ô4̵¶Í<¼ØÍ3‘©t,ņq4hÔQSRJbã5¬D‹(E…Æð¶ÒçVL›WUޜŵ¬ æß´¥(%©ñ?”ÕKľ$–.›¥âÃ1^{¶S{ÛeóL:Ø"ža‚*cŠ“óuo\ a\MLËhz²IÆ´lÆV#6¶Â'¿S³Œ;ÙŒï¬îç;WGÚv Òá8ƒacgµ9t!éî^Ñ´åd»Ìê5+6É5#.ÂÉ,0&àbÛNJÒ:¤é°~g–]øì`ɽ/Ü,7#6ÁŒR[Ì&*_¨€ÊÐõ¡‰êìí§ž4ÍTo#.ª?02!ðnÐ6#.ÂΩª²æÑóÄï4Á&:ÏJFìÀ9]Ѥ]Ò¬ua®y:†š0jæó2w67¶ŒLªL¹‚$ôgç}õ‹´n£ð@ußpBƒŒí…´ñÖ=,æYí?9Š@ôÓRP±áɬ@ºZßlAsø;oˆ‚Nñ©¦™«¬•qbÐ×gÉÙã6ô¯fl-&¢<ºF!ÏOB|VvÌïDDÆÇr¯/“,¸Ú‹z"VnöÍfrg£ÑñSëqH·:Óº¶6´=V_è-³eßÇ®ÚƱã‚æ·mˆO¸Œè¢õ¸t“¤÷)0–ešÔj§=1åŠÎØÐà’F§1žÜWVÕ£i–7îæ)¬Rãžw¾_<ã0Ý®È^ÏP•Üd>ÃÜllg:Žûà5Úã%¬GQÄî²a…3™§¦è:Ó;1¢dÆ9Ž°„LJ¡n5´š£Íš']ºß—ÊßQt×\PÄ##.'yJX5Ñ\F™Ö¸2L‘/Fò.ZX]ÔG¡‚Œ(¸jS}5#6jåVͶã]*—ïXTäÊ•Š°óqdßûªÚöFSB™cÕ\llÏ^sÇ9*Në¡Ü,ßž]Ì•öO,ºK+çThu¿(vÎ[‚=e´Æ™™¹CAibu†S"“q0V^£ Zk¿kO+gX›]Ž°ÎNe6¼™_y#”n˜uÁÕÎkƒ¦©Éct9)¯Y6ª9àZ€nl8\e¢‘±¿#ƒ¿˜A#M|fS®±*CP7ð˜vB¦šEVöU;"áÖ“€X.\†[DY\('¹Ä>›;Öï8‚óghh:yYz™ä×&YNÚ‹0,¶iQ!³é¥ØXlhÍÍÌpÑÓNVŸNÚ]#. ºÑH.q¼†Å:¦ÌR‚IFÈ¡“bÓ<P¹ƒ‰Ô™ÂÀµ)>Û4=¸ÍÃ#6Y¬ŽÛëEÎ&)Ä+æaõ—Æ0[3A”@¡®bXßQÝëí*1‚¤X‚1qI—N~.2ߩݨÛháÛá£Ô7á‰)” ¬neïpOVêÑÙRQ9B&PÆ7Ês‹iõncºÍ7Ûƒu³U·6vu†blâÂçìw‡Ûsðüa½evâæfcyž5Ù9ݬe|[Èu`z§Û™L>8¦²‡·¹O/¦ÅR=:„%›)Ç’:Ó{¸ÜÖZÍQëF72Ìf2û"t[¸—LLLb|¾ZÏÔáFc™êEæðó8™—‡S7G”´ÖQ²´ƒÓgórâ’Êܨ;ç¿©Ç1™£Wœ•· i©#\Z%±pƒˆð²ÌÝUX6嵡s2úBº9‰“†H4qÆ:jn¬21ÉO)&PmV†Ìâë\—[™lÖfõŽ™5,yU–LbÇ0vBŒ ¢&¨©ÒªC¤‰r# šSH‹^éi!°Ó×ÆÊ»I>ívÃ8YÙªüµW›a²EZN©Øχ–+aì'gj0šŽ²ÕUÎ☙ìrÓŠÖ*L¾#6$§Ê 3vðä{Á¨·É"aNû+â(0àe#6Bu öó›Rô`h©µÃ€ŠpPI©š,#. f˜F’èã#6ëÚ°äÚ¸c¸M™©_{RÆ£eé\½1åf2åR;ÎZ©a®šf%‡Ã±Âb"âzÿºïÍ·9V¶ßé2#.¾¦¡#º@DE»ÎuBI‘@aàÞÔs‰¯#]Öhè Ph“}”’‚•èuz1‘Uõç €tÍ®NŒzzOQ(‹¶Twqh÷Ä঴üšÄõRù` »pZPLÒ;Ì7CN]ƒ"ÌŽÍ€äü#6.s׫ZÃ’‰4ý½Ø'»õâΠшä^î‹le,Tm®t• ‘쉌SR™3›@±0îÃt¬y\)&Œ#.:b8ô“!ËK@õ´]NÍc4Ü‚Ó@rÖàš²e53Al2ȈbiA-’™µR‘rîšvÖºS%#6#¥ÍÜ ¶ói"2ÐÀ…°œõ3Sd”ÂÅtvNI-"ÚS#.ó['£‘2ä`éRqfà˜d5eëRªŒ#.XsL%q®i¶L§¤n2éÍc*±<.Gâ(N¶çc²ž¥i¶m£¦TéÂ&pL*ˆ[#%ŒXŒ*tv™‰‚mœÀåŽÈB$I”3¥/ObYÚD³»w\ñ#.»dÃ펋xå/ºï#.Ó‰\lÜêÎ#6eÊ47Uç3}Ç/™ÒÙlpNZÖ)£¢Ájx#u±¡öMXNRÎÒ¬˜³ˆÎ ÔÁ|`5Œ`pËè²3Pò†à‚f—8¬&„7PÐÖÆn°Öj–›;Ã'žCSž…™_¿&™ÓŒPiIÛ+]#%ODö®÷Â,zÜÎùèe̲¤9i 7R#6tÈðS0@e°usÇùM׉#6KÕ5B˜E¥!UAIHX.Ëb004’ÚÏêÀóbŒ;àG±.NâˆÑÔGI–F1T*©ÒNï_»;QwW‹˜m3ŠoüòÊA8ÐTX$GaJt®Þ†Á˜`g£é®á™ N Ž¡}[aY¢¹I‡°êˆÒB5RÙÂ[#%†—l8Øqˆ+CŽûÈ¡¢#.F+2fisÄØÜ l³3Y†ÅÖ¸©Hˆu#.Íî‘„ÌI‘awmwš#6nò“aæqÁÊŒWÃ;‘ÂeUô88JD61C#%CGfú1ÃgÆ$˜±ÆNØÃɆµ#.fŠšåî äi ’q¥º£ Ã0é#.(à›™œ hLÉåJII4ƒdpjŠì•ËrÁ·i3fÕ̺ÇEÐÙ™9—Fm0²jJG8ÒgtÓ^Rg~V@.·uXiØl4I˜%ÝlK9Ã00¡Ü[Xn&uA#%Š0ŒIF ÄúÈ%#6¯qCÿ$R5Á@w³;ÀiSh¦²…Ê#┈1Aÿ6¾*?g€|zœ(† íW¹ *I"Hƒ#$d‚#%‚|jÇØ?´È3$䈌@q:áÔ:7H¤ýhuF¤ˆ@„&È4€¨±TC8Ì •î#%ƒ»-=Ùk<Œ÷˜º›~¸X$Ÿ*vʪL>ô ÷ó¼JÓhiÁ×/‰Ô'‰Ø³‡—7®R™ÆM߬·YEr,;Bó®_}®P«4ó¸ëá›&A±´Æ\©s'M•.áiR¸k5†…ÜÖBh7„nÂ20+aÀ7èu%¶eŽxn„X!ÑÕöõ#%c=¤×Àæ^çõµ’§4pu\0RÈpBÿev#6h•"J¨”ÔËkñ¦[m&òRjµ*ÕᚈPÙŒR€² q¶˜X¡1H_§‡D‰˜#.o=#6“JAµµ‚ÍBp5㊡Œš\¡íö÷¬C$;×á8·SoWÇ4íUú‚*ˆ#%nùnR6._¡S—WwUÙn»³.nºn¨-sd¯å¯5<¿Fîȵ¾*ä}î³ÓÁAáÖ<‰`P¡ý÷©¬ŽÙLv*Žôr†ð8«#6#%„†|ž(u/¼ˆqœz0g-ZŠ"½(àŠ{̸°'ìò@7>™GÔ»ìo?6©2.œþ“0Vè<œŸ²¾Ü?Ø|~rÚ=¹—ÈFÙÙwE¹µÈM44¨ÈÀ,ˆœ;Si#67‹ –P¼o„ÆnF¿½µ°bhÐ1D)¦ ‹õªS@£¦n0—.Ö”™Ò.eÆùA2Ac¿´z¸äf$YÞЂQ#.cG÷±3þ!¬:?+xø…Ë–%êá¹A.‡¦9FòKPí?Vþ?_¶×äUi´&RkF±£Q[FÔIDL“`Í3-‚‹lXÅ|ÿZ0Pˆì´O€ïö¸ñÒ´TîRºÔ᳧zä™K¼º:ŽØk5vˆz€4DŸ+ZÂ"kÂÑ’ÄDŒFÓI0Ž#%¬D‘¨í #”E¶V-ÄÞƒ‡ âêxÝïf§PD`@Ë[΋Iß3_Ùk̹<æÑÛÕŠ~«Qb*H²j}šJ²a_x;wÙ$”œŽ›l[^•ÖAõ4ÑLÔdÂËp‘çbˆ7úN(ŠÒcKi"ÈF'¸„Üf5jå]*¾M½KÓoB5LâÕÙ1‹fR–@l@ÓE`›Œ+K†³D#0ŽQÆR&lcƒ8®åB+ Ö×ËHŸyÂ˱žJEƒ†ˆHÐq ²@Q¶“Uú©oƒ#.ê;˜‚eK¾ü'Ÿô(ï‰éz–”#.s’ddÞí¨»‹¼è‘%Ï$¡ˆïÏë&ðÙ»#.†PÔ…HåïT¥Èõ¢®î°]Æc(i“°·Ej#.^&Ž²Ð†åZHšp0…“8‹ˆW‹‹ª.¢cc#%mÆLN¨–L…uÈÍY^sŲñüÞaÅ#%ò=§w‘Kbm´’lc+A)aLDHÀÆ-Ÿ°5Iq0]i>â k…@ /_¢Àmˆ!š* I,Ëñ ‡Ý›¼;âdç¬D©™_&,ŠúMŽ“õÊŠ6Áý{ˆ8QÌ`o¨¹U¹¾NJÚ## ‘qöe]²gÞRšrE0;>Ç/¼Ú#Ú:í¦Æ>µîÅÇmcz"4ˆŠïU#.è§2†]õÈl0ÒjøáiiZµ}†køw3†FC“®•zª‰¶i!KábÇgØÌ:„'5˜’bCèVmÓc{”^ZÖH8 “‡ ÉË¿+#%Ó¾æÖZøA”ËIÃ4Áùz¶^ym£Á¬ÂÉ'Gi±†êØá9E$åPš«%*‰ ÀUŒeYNÜBº1B)#6×6¦Ô9sypYµ.ݘUfylM‘¥ç—x¸‡kÈDÔëi¦lïÊ‹]ôŽ†ÁúÑ ¡C‡|ÐÁÅF–1#6hÊ7n®ÆbC#%ktE.J Ô˜DbTJ¨š Q$Ø£J#.L„@Àc¤œ#%-P$PRÄ[¼ t`luqlg¨LÍ#6Ð?Æ@ ®åB<5ퟨ;+Æ>ER+ýÜÉŸz¹M8¤Qb{(=Gø+t Ãï ~L>±ÿWTŸª/‚¢ÒkºÓÏó<1G¦A¢|A]fsÕ10#!ëGšú™"‰‚d„’FHÅ"½†«bƨÚɶ¬ZÚM«Š„ "BCÒ÷©š5Ï?=x•T\½KŸJåÈûŸ ó[ʾ§ôÀikE/³Šâk|t¶u+W…þ n.¬…¨ê¬ÞÔrØr$®E#.“{:iÃYÞ%lšeäʵS0:2¥KŠYk8ißjÓm&ìÙ&èdñv5-zÔbŠºSc³AÎ##6pšHÒ.™KÄӃƶð¤¶Ê>-ÌÞI†SŒ1d¼ÔèËÍ<»SâKÛ¦3‡Û’$•Šiȉ3̸jMÙsy†ô@#DmãŽü¼Õ‹Á(ÑŒíju¾N&H†mŠr¦=fÎþW˜ÑÇàËNÔë‡5ÀÈå‡ËµÝÀÆi>ŠSæ)1jnÀI™Ž‘®$ÌÜC#:¨y>ÖÞ*¨i½ðyXÛz›Mp“\ÎJùš}e)œãï3Ã&=¾¢¸É&B°‚âðÇ"íž–7•5¬´ÝÎ+c¤ØàdÌrpQ©»M=íW0ƒarr1Æ@Æõ«t¥XE€D‹LXÄ%‚ѵeÌôö²ÒBÌ´¼›Ë•ôÖkUã\§›©ê^y^¦¶¼¼âƺÜÛiÖíª ,ŠÜÔsK¡B4ªä!_ÁšhR‡M¢†:—«#ÿRDõìÍ”¤Dkm^|œ•ÝÅUÚì›"hƒI°’1Ž<¥jÆY*dÂK èíƒ0š]#6»jí&°n•ÝuÝÅ&ñ\µëµÉ–§›®ó*òlLi[©Ãc]¦£hÒT¼e?äÐÉH‘Ã…(¦Ñ³R¤me-¦U$…”™f±¶5¦¤S5ÔµÒ´²–™5¥™SUFŸ6¾^xMEª*f‹VЕlÒ„T„7–öy™¼túccQ”Õ©£â™`IPÀ€¡Q6››@‚—bQ‘Q‘P´X!º+çÚD='qí°z³ÄõŒ.‡kÓWéaKÞ9S@Ð×î6ýy‡†´Oƒ;‘Øngq ¾ Oz³,§E™ÀËÂqhöëÏKS-Àº#%DŒFßÝOXÅ¥*jXÃɪÓm'ÓO#»;ÎLÀMÉÞ¦l1JœSí"[ÕKŒR¥U(ÑJ01!¤Uý¨¦ÐŠa•#%¨Çj#%¨)"ìØn¢¶Í¸—eµsÆòèéf´Í:ð²ÿfÂs©÷ pÝ#%H…5¦µ.Gf`#6 ò®µ2™ñÞ‰ eRD£T5ïÓ×ûûyk#.Žæñ#.ãÀÇ=AõÕê4\¸Twíé©Øm?2s¦O^ßn’õU„\ˆ«#%rãÑâuJÿo‘¢Æéˆ%òÅ€´AS¶(†L!¯$yB—`w?6Èl„¨íx3ÝŸb…ÍÁ‡L8o\šårEœÎD¹S¤,`&Q¶7ühÄ´t{Î+Áoœn/1~»bµxÙ#%à.jßÞ¡ö ~Ɉ0Tˆ„ªþ—¯æƹ¾óíèlodð8YGçÙ|fQ¯ãûô0*mùÊ‚¨<„ h%µj6¢ùýÕ²Eâì(–’eQ‰’MÍmr,¡–‰¬“Þûµk“M¥c)m&Ê6-")¦Y,¦JUÅ,µ”gå]aM«)˜lÚ1&ÑFÚ¦µMÓŠ+"µ+ºéµ }í®Öí^Ý»*b(2#6em¥)IkjhÆ¥Wµó¹Æ¯~í’lŠ-cd¶¶ÙH›en]ihÒeRMKmçáM‰´Úl¡3#6š°ÛilÚMìêKlcãnŠÍ–ó«¯;’R2hk–êÍRW‚ë5x®Ø”ĪÂFÛÂk±ŠË[L7ô®ßõLUšq¼ó¾T•ÔÇŽîRÈ´,£›^›?/Ëá>Ïw>Bmôs'`˜ÆèOeÅ3$˜SF¯<XqIƒFÎzŸTX@÷Äñ˜…ª’×æmãœ÷_NñW{<{1’MEœC=”#%.b¤3ª#‚E À‰$T B 6Ø£Ç^–S² ¤"-³JÙ¦Ú÷mªé¶±oƒ²³KÍÚå5so›º×”ª¢#6²QdFAôR*fSJl)«iJ)’‘Ô,¢…E’Ö–ÍFÌRÓ3ÚE E¥&©Kmi¶i›Y5°Òš)R›|Vå(“-EeQ-%£(ÓJHQ¡´Û)M"jLÑ°Ó1”b‚Äa£+eØÙ2IdhXÔš±j”ª#6Š”ÙR”ÉT•¢’©Y#jQ#.¨µ›M!BJLX)2“ ¦I¦J–j¦ØƨŠ²$mE,LÚ“$‹6Ö¥šÉ“F”’Ò›l²¶¤Z÷Õk»VÒ²ÛM¤²Ô†’ÞòÚ×M›6µ)ª²V¯f¯6{*¹zÍV-¶R–ÁmFÕ¤J*mm£ZúáÖÞcí³€^ù6g]!‡ n5Æx§“{á·ç5ª÷è`fþÆpb‚›Ézì7ùWkN-ÝðÞW ¸ˆˆîmE<³¤•!‘à#%¤yFäoDz¥ª$„>Xqå×⢵žåE÷Äô^éè†øì\$1gÁÚaž>Ž®ìk£g]7bÙ 3meÌz4º:¶š.L@ݤ¡áÈ"¥+´Ä»/šö7ñ_‡K3ü½©=Úßn9=½™C#6?q°Å‘@P \cha!¼†„ )Ìæ]28ÐƘ‘›à¡ª ƒ(ÁW_@pÄ$•ûŽ -΄¸>õÒlÕsuæ°ÙåÈ)P;+ôø ž:ó‚lŽPùÀ‚.3ôу¿ŽI÷¾×‰ÅLFz%;8ã»m§f¸ñ¼õЫ´Ô¬ovÄÅ.šy‡Ëg ØPÝM±"Ê~¤}þ…b\åö2eÏàØ¥WCó|såÇéл\Lu#6“8TóT>µK‘ƒîªtJZ~ÙVêøPŸvÙÂíͳÏHdÖ°éè#ÅÄØÚ =õ øs«~O†Øõl£qGFù¬Öû#6¤Èƺj–µ¤7Ò6Wi0½«#._Èé}/[onÓ€‹ @òî#%#%‹lj¨Ê²N"Q@R+¥rÔ'8#6Èo(Õàž ‚"2! ÖDëê¥Ñxšíb¹Õu^¬U˜°‰=³¾_vóÒ…‘Xdé[ž&ya–&\T•CA¸œ!Š$Œ*¨c#.ª “SLÖ`O¼I.!&*x³VÏz§@â]7ù=ý«¨á×èv#.çWµ±ç]{|)LcÜšÃPó €fÛÊÖs²šD\OúÙÀ«–‚Îx…#6((S)øbî#úÛËR’dA]%Ù@ÿgìºùaZÞåe±F6ÐÊÊH‘F–Œæª&cD2LŒ#l#6Ðh{ÄC5U.mÈÁ¶“ÄH)fáu'6ÑÆ¡‰†±ÅXƒVFA4^Z®¨ÆÎjv–©g6& b S´·»RÅF[¢ÃK#6ª‘@Q®—I–ŠL*cð¨Râ‘¿J“T#.t#%6:^ôtqs•èÕ(ñQ €jÔÎQ§×hXÔz´A;Pƒl|߶¨4"ÑJÔL©ª‰‹hJ¸XÌîÁò€›`‘8ŤMýàž5çñæk±—°÷§ŒÁÜæ" k;CXx†j/zø/#£Pì|…4ÉL`ZhÚ-AXAKëíÚÛ[EìÔ×9b$>#%ý0¸ Ìèª=V.ná·kmÿ>sê ý¹gźÆS®i{HÔ·t0Jcˆ*ßÁ$çL¼•·ŸZ«ùß$!›Ã¾$ë”ÚwËIƒ;àtn”l øÇ-i¥!I¡#.njßRY²’VTÊÁ[õðð™l…c0¶õ«ìXlA‘4„(Fë¹ËÈ/n•÷Odçèʃô½teú‹&YùA16ºa2‚e8 ÔÁõQv!h+åËbêÅó¿r@Ã…/¥)Æ®)iQÒÆâO±¹9&íBø>ÿ|€Ép©*ÚYKmj”i!´ƒ:I$š””žmDVCCpš€P±Èc&P1èó€´âÚs½s¯gÙ™².Û!›H #6áPªR3ÂxQȵßxÇ™Š¥ƒcQ¢Á:÷Ts‹hiß5¶JÆKF¤„ÉÃ'3ÔGÃœyÍãsKMûé:Ÿ]û`‚ª‡§ ÃÂ2‰UKÕ•JâòÌ«ŒÏZ§N7~R±î(: @"¡ÌȡⶶQfíf´Ä‘ÒiDÓ´ThAŒ´~Œù~ÿNè>‡—èëTšPå›úÇQpº¢ˆóG‚Çšpó ¥€CaFE<Q#%,ˆ2ªœä°®Gíâ&±û¼á9Ťð;õ1A¾¼uŒÈŒhœ)¹¢`Ûk„NÑD’=ó§#.ùüq ¹Ž[tcv¦¢â#.iÉÆLjÆD.Ã1ì˜ÆVí¡¶&q±"J5†Òë`̆/¸:ž”¯l’F#6¾”R¯OQz‹#%ñ=;NÝÖo+fy*wЙ¹çF|û Å"TX¥BSM4…DB —¥×*¯rïšJZÀeE–¤É@òrÎf§„PÜ\/èÆQø#%»¶’(tWx.ǨÚP QÕ‚¤BÉUN#("»ðX¨2#%›LâF›M`”'°fT(#.à2–pâ§.=ZpË×v3œ{×raY &&´Ú²4î¤G]:\„clç¹¾,u/Ëß xµî¹anGÇå¬$46×PŠÁDùkŠ‰’It¼îÂ2žv¹ÍÄÀŽ+¹,Òçžu ñ<xϹ½eäŒmŽ "#%$lŒá`Ü1R Œü{n×z¨¢3D¦ë2Ó™#.äÅž†êí¤¯dò“ê߆¹nŒh/‘!Ý]ñžåîî¶ØnéŸ-ü:»+~A8uˆëÈN3‹5åŠP«Ãp¼Cmí$/R«4²¦ÆîÁ,Ã1=;ÓPp¥'#.¾>Œú UGLc0õâu{2=AÑ+½ëÜ('_¹HCX=˜DàmÆq–zEn€ÆÅ$ONÃ-Ž’¥¯Ë¡e¤šÈ#6¤ÍSY}Ÿ—ƒ“¹‚YîêúÑ#6I£åíª†¸%Ý£HÐÍ?]‰$E/]jôåå˼n¤ÛìnÈ™”HNõæ½Z½jé%»mj©RC¤Š@…àmQcš°^:wn) «—1KÙ)yš#6†QŠÒ€4šlß·ºw7¥´D9Š;¢¾âênïÕ¢ìÑꙧëÛÄtN(Àºü5šîIåÖïãrBÌ©[æq‚_S¾®¦Rg¿s%_Æ—¥àM~%Db+)'æhwJM¬0…«ö5¯YFqtªÁÏé‚ "懲‚:Ê•½Õ4Ñ´L¢‹Z®Q•M4šjW]6èrbK$ج[FßVäbÄVkãJ*¯KÒõ-kÆ嬳oßýþñ¶Éki¶[V½*ÝrttÊ7ĸeÀ ®h'kåŠÐ³)ôãK —]¬Îe´"„¶JJˆPÉB7&®r°„2ý(-„Øo)’€<(˜ û7Îu¡¬²h%,3al!™â ¦±hv>¹3Ù]´l4‚„}»ÒX(¸ÐÞì½qOt”T5(¿åiÀ’¤(‰º#8+!iH@ÛaSòÕ*ŒµVzPT*éd“W˜fTp9°ÌPð5 ëÖàtïôQìꪅ¨…E#5ÝvÆÓ"£I´Ul#%&°ÝíÝ+Èt00p‘²Fù`Åì&á—,Ü}C…pÀ×t,Våp¨CšA2¬ ÛÑo¢|¸¾ÀÞï'´Pg=b,‹q¤ B€=cik=A’~x#6gLüqöÿË€.×HšÂà\‹h]IÕíø1…Œ‘#%¨%扨&?¿dÒI¡ÏÊçÕ~¶ÔØ;Îáâ¨Ë#.ÃÀa a‘Qúùh€ŸFÍpA24‘»Mšjÿ-ŸF?=+áÿ‡ûRâl~C~˜œƒ$4Kç‚^ˆn«&߃Byï‹(íê`=4ñ…x(«C’~¿´ŽA@TTвÏa81#6êå{œ êÒB|<Luß»¤/6ìÙýžðéÙÔ;ÂŒûî„€pt6žøSTSbGz¿Cé¿Êw.;=#%žd#%RIí #6J¥*#6¡DYX@¨©PPfŽôlY‚!x¥EdBD “b[’Š3\0ù_a»ÌË÷¡ƒôq¸Á™+£b‚ÓIbÁNQ²Õ~ƒ«0ŽGqÍ9Ÿe±ìSQù~ý£Ëj±DŸPõ$›SÒžÔ£C N/P¼läV_Òþ íUAd‚:ƒ C—â[!Üak%ªEIH®^E¿Wcâ|s¼R¨ÄÅgßêóåÅ„àX!`≫r'ÈàPë;OyÜbPWGçÄ>'¯—¯ï`Ãæy~6×Üß=a§,»rÞtï£ÔŸHÄT̉I9¨™¦¤¯V–¾Tª‹SRÙÍTþËÁ˜I¦ƒúËúPцjˆ,TU#6©3û®ÜSE,#6@¼•I#6e,%Žì†€Ze¡Cð@ÃQ>9yñþ_›¼ï€Õ-’6#ÆD“ca]‚ªñË<>ÙZcm¦É\ïg:F0¶ 0#BÌ £&D®¤ÍeµÄ±¡Gf–4'ÔF4ÆÒ„qI‰Žlb-ªÍå†LQ”X(‰‹J¶™*™Ef°‹-œH#.Ô’™a4J@fµ@¤ ±I2ÈPµ›„´Ù’œbƒb$]’ÌÐLïR`MS$’•‹µZLibm-äB1ŒŒŒç+–=B`HVZM¨“F`L%¥ª¤mV>Lä±¼xa¶EXVÆ¢ÄÞª²¬ ÕÖAÉF¶áÃ4gJ’Z¢‹,j¨j¥0¤…'6Ñ´®è±\·6Årܾ#.ãc^»½.4<ºí¾-“¹1Šç4\3•¨lêApÌjÓ´KŒ&D`:3±ók0Ó,$i¾K†µbc‡,P¸yA®5&Ψ hÕQ.šHº5@€ák[R²°ðÕ5O<#.ŽC:Q¨€6%Üe{îË]ƒúÓN×Ò!£ Ñ uP2Ëñ»h|€úim²O:“4MiãÁÖÆïØøÈxÕ,#>¸Òc!I¬ÆñÛmwºòeºÍ7YºÐ«áùqüô”Â1Ó ÍÍÓ+p8ñö¼þ#.áߣ .H(ôMTØØ?9ø Ñ0€À«¢¯`1 «þ8÷‘A%Q#FååljT²j,ªê£TI’ñn’ª0±¬QrÕ]JƬQ¶ÜÝm’ÓDbd”@b-eAÔn*¬Z•$A$TɸúÓøC )È°íDÔ@QHXÄHE¤Ç¼ð?ÙÔX#.Gꂤ@ƒÆYÁáõ×g¿Â¬{S(±ƒk—Ùæf¹!¡çò«W²®]¦Ûnºë[ÎerZOºÂ¡.TP¤IËúìÀ&#ƒŒé¹‰ÍuÕñVã1 Ng°þ["ALàlBƒêŽPoÕTBD‘WáU‚š¼ZƒPø¯4²mdÛÎÝJT²«šæ·¯¥Vò̈¢Ù+Xl…TzšÅŠê»ª-zjö»×‘/]nË\Õ|š×šZƯ7uY‰…$¶¤Õ©3J’ö÷í¶ØšA¦@¨¢G¶2<㥩Xý|º!ÔÑò‘kk™E{”\±È5¨p¯Ê‘•E.I#%}3@¡„‘„ËÁ±“,€erIcÇQ4×´Õ0NK2#.É#%½Žçì”è¤0#qÒòœMŽ›ÇnXq[‘l'µ}Ò@aš%Ç¿¯3Ëš‡74Â&iGÊ<Ø!DW‚bUTŠUðË›Â[åHŒ=TvU Äh¸¨Z7aq“P׈ñ0»¹aÌ°oËŽuÍ>¨½ ¥ECOˆbÈ€(¿ˆ|³”Mî®d=` T6qÓr.À;Q$!îa^Ò¥#.-T””iKJTÕcj´×¼®ØÓUšk)êWQ´ŒE%D/å‰Ðñ9™‘çÙê°Ä@º5]°;c{nÑ÷µJ*ÎdBdñaöü’6µñ³'¤.ÿ¤ˆý¾v™íFqp£FC²êNº–3ìBKŸQé£4Ó]®“#6¤Ó‹‡ý#.ËÌg8¨¼±†bCÖ°¡ :ëŒÃPó¨êÎÉ&&42i•´PËZÇ0Û#%G{ª"M18=ɉÝGÜ`‡+hƒ}ñ¡È*Mbð]þáñOC×ÝXd’I"BMçQýQ¹“‘ì<ÏÖônå¦F^Ϻú¹!}zæ±Ë`™ÉûdšWß#"‡%54I7L0µš…°3'øþ|ˆ@å¹ÉY¶çA#6?KämɃ:Ž’®MœÓûlp(¡©ÐÌ“Ò¸×V¹ "ÄëˆÔ§VlÃ@,×j°nÅF•@ˆ$+û™RMŠ6SHDÀ˜B0 D¢¥Ø#P³N5X©Õ4O4D1öÍ—á=ü'§1ÀÁ5š5Ò¤”{Û:ðeQÃýi`êñ×K”ã;4Ï×ú´1$ÐÑеÊc£çF^Ó#Ž¼ô=¥ØΤú¡©“§A>iéTSíMZʸk2Pù|Tà€‘¨ðÛ‡’„‰Â5ˆ¶PajJL–i˜¶¦¶–¤ØÖŒŒe5ù¥tA&‰•2ÒÏØjܶ¦e©²-,Í`•³LKlÛl6³Lµ™m–$µ‹J"©µ3f«4M5M±UQli$’1H³$#.GäiGåL"±£æÙ8î¸l–Ɖš¸Èéáü)D M´VµÔåV¢ÚÅ®¦µ®kvo^aQƒoѴצÕû»ËA'"vC¿¯†€œ#%#%ÛFÈD*TK[›R]Kí´·µUøˆm~B²#ùKÐ=Q_fÚÈH†EQ ÆGúZ#>Õ†–aõóØø„á¼ådzApQÙ¸»h¥âB9¨!áOGŠk§!€' %JŠ¾ØÞ!¡ìNHlá`G´>ÒB ‰zAm¨#%û¢Ü‚%íKtŠD©ìByìçx‹Æqä;ÏÓ×8Ï<Iµ#%CÁÕPÌÅŒ¼Ð“yzÔ´àµ!¶7$¹=ˆÉ6Ý*ax—2š^³30xã—è—pþ#6XXPµÈ…p>W·ÑëU8;€¡¤g‘ê&L‘x‹Ñ;ˆQÄ7V«ZÖ‹œKš“è6˜x#6ÅéØ¡›¼ƒ¸€p[1v›*®š@âƒ7̨‡·Ý£Bò8#. Ò\Ð;Ÿ¤:háŸÜz°žp!¬a#.DV„††‰#„mbL¿ê¯(Ó4tì!#6°I£±…Oú;N¦Ï”Rbå.¡÷Ã-+?£VF0¥VªêãìŠ~}мáö™mFÂiîAÁÆQ:ïÄ•ƒ#%>°Ñ#&’!ƒ¦-ŒcöðtL”‚‰!/Ù¯9ãvþWà¼è¥æÔö€ršÍù[ë9+ÁS:~“r˜âúýt&ø-tœõw–¼¤¦åÆåÒñL ,˜SÝðð4Ç]«Pø¦äº‘C#6(,H„QX€ŒPí‹‚ÇéÓ9À“óx§ï¥½#%2O‡ùæ&ѬH›€ø8Õ–Y!D1ž4P)#.h¤4–ÉÓL*vƒ#6HÛLMûþ¯ŸéIJB#.™žüX11¾"lN9anTG§d#6숒6«Æ%Œ `–6‰ØÄ´wc&µi︷l¼C#.@6dÏZ«Hfh…ãëU)%E`Ò‰„c JO´'ñTŠ¨â*cm¬hæF‰&(}q#%çDBŸ ;sR; SÎTyzg?~—<s/T– œ#.\/lëã:Þo\™qŠFˆ•(P¹¨µî€„âð8' ¬J‰Œô*Æ›‹›%S ×õ-ªl;w£DÑ@äàPƒ@4ÆBˆ‚¤PEÐBÈ”ÅPe'‚Î#6zx¨†vi=>~¼® QÐu€}Þ&`†]|Ñ÷N^@ÃÕUBÅf€ÔUЪ`EZó}MI¢#ðGðGù$=è‘ŽÞ;}%¡&Zi‚Á]ä4¸¸fÇfÙ«EC–n¢ÉBvP§`¾Cqh—„Š\5&+&ÉWsLŠjFà|Ûùfwâ{àrTPœÙ#6`R#.Õ!æÊ%•‰r)(#6a<,<Ï[Þ‰Kž:MåÊd¤.óÏ!Y;0•UQT»¸Y“™¡õñ²‹"‡½®$D0øBR0Š!ÔÜD½.Øbo’ìÀ#Š]œÌš T`Áѱ†É"žˆ.)¢†èÉdrf5Âó"^¬ž¿îi‰hˆžòœŠÝÝ%|Á#6‡ÓeOuB¥´àÈ©PÚW.T²¹[#.½i?†îÍL#%Ez¤#6©36¶~ôâüÞ*Ž”(›Âý4Ƕ¿Í»CVnÙ=è¾»VâHfmÚ¨·aYeÖ”je 5VWE·[Ñ$F¥”¤ŽÂB°ƒNÄy3ó, /¼ŒXŸ¼™½p †8¬§gdpŒ¨:©´>j"ìO=H£EëeäÎh ïË‹“§ÊgؤHÞ’–%?^uÝF§·ÙéV§mTS±ÄºÕ+nj„ÜBðôÕ#6´×9Íâ„BröÓ,à¾81rÂsÚU:âæìbúékÿgÖË:¼µÜ‹|ÖƲ:Š#6K%$—,è`˜#%*¨#%ADP€ÐÈŠÒ¥h€#ͨ¨In1€l+p<;ŠTÈ)í ˜©Ù¿Ó:þ°UyónêïæÔe³#6ÔeëÇ·VÜÞQöº1ÈßÌÕ×ë2Q#%dd!C‹ÇBŽbuðE!¤G³v#6Ð($«U‰`ѽ„ÀsƒqKòƒÙï Ñ£šÄv\5Ê ÎÐHÒž%A#%‚‰Î|É:LòOB§¯ÆSÌ‚N¦#64 / LdM´¼íÍr¼ó¦ëÏ5wŽÚëm¦ÖJI¶ÒËÇ’›BHTˆƒ*‰F2ÀD#6üÿ_¸÷J(-ƒ!þÚnêÖwÁrPl ´ W1¢Šª`Gñúnî¿«Âb£ Œb´Fϸ•:Ä6Ö4kR&'Ó&{‹_¿´)·w¶õÚV7t\-2á˜Ö5bB<* ˆ\K‰lH2Ym@¡&©€ª"Ó-PÒËf{½•nçˆØé¦{ÚG.®ÊÅz¢ç±¦&#.œ¼zPšÕ1`W¨¬PÙ²‡2¸ÛO!-Ý0éà›È”fÙF¢D$ #fKÀ0‡#.`—ömZhmmèØ•áͳŒX"bͲ9PÃ(¼X†m–ÌG0Õ.à£MË쀿˜â§iÅLüI!1²Éb;EP DüÀSqS¿•c²vwñÄä'Ñ祥ÎUñ<þÞzªßòNu؈›'úû¤ÓIŒáÂfÊbWGq'NÄöP”!×#%ÄI¾åmøÛ^ef’Êd‰–’ÒjØÕ)}ʾÖÚL®»U?OÇ—ŒkÆÛ•Êæ°µ0}½ÃÞ^“xÇëaÎ6<îXJ.mèÚ¦Òv¨"«Om¬Õ¨}Pr)N=\w†žü²Ëˆ«Ÿ(ײ|J+±±òT-ªä`UíèvÙïIðƒ{¨óÎ!WžâÛoUx$˜§áUýÇêYÐYy¾áa©<–·ØÍn6˜I6=Î q.ê`˜Â÷ývÇ]tú¦øo¹ƒ+œM`sZõQ4$n@âtÂpÅ&/m…ÇãîHF1מwë߸Æ,bÅè¬ÞBðì&Ù3‘°kŒÕ&˜†72^A#% •·4ß¹çLÌÑL´K3Jj í>E/Œ»ÛêÛçÌÚA!~ï,v7ÞÛ¯é#sµzø%ªì2t‹·‘ƶ€BʵŽ-úð*¨ð ¹FÇò,¬ðç" sž^ÓÅC^´GÌñUGÝ‚!p6m=ßmÃÝŒ#6NÓ0¨^b!”d%]±‚-Ê.ÛJZÊ0O¾^!‰¡ÖÂ0ןbí‹-s¦µy䃉Õe‹´SZ·°òüX›K¾fsMÖï%ç8Tb»üè\²?}{Þ>%ËÂça‹8ð‰øqgêd;1Cð™)æÁ… €ªT'çd%íBnpŸêá·>Ž¡˜¼»f>¥ó‰èÄyKp#ÃææG³´ÕsmGyç…cQ®»·¦Ûš%¶Ì\U#.¡$‰HH0‘Q·™KAŠCo·UïУç»Rˆƒ*·µsà$.?çû‹õà¶KªbÓ\µ…‚ň„aÇY2J2ÄTX’ (·VyÛ¹ÖîuÞªZÛ¦Û#.1A¼A(ˆ‰ð ¬ ^3@¤dâÑnÀÕÁ£ò—ðt£àyžˆ“Åù¿r=‚÷‹·EI&þŸö01ü›„$Õ¥³º[›Ðö"û·'Ã4Š|·låËMÓGÎòçQÝOg”C_˜ù´4èÓwA0Cf±eÂEÃpÑIå¨GGè ö’à$ëªU˜èt§ÝÅÈòZˆ¿0óhäk>1"°Õ—,”W%ëFþä;ùq56z{þÎ@ @qôÙ(”yCScpô_{J!0£:tª4‘“™QZ$†¸ê×\×v庺ͩ¦¬¦ÛNݵu&ÄÚÛ4¶«ªØÑ\¦^.^]ÚVòµ¾l²„@ Å#%C@US_B„Ä«ËÚ”5ê)ALjºr·S9¬¹† 5öTX“lzï€ÀÊM‡†u>#.3ïk¤ÁÖøûí#ÂÈÉ j÷â¿«G„í#.Ã0¾Ãn”º·JYUiKí€:ÍËažÛp¶<h6Ú`g5!‘5‘È0°ØzùpÕŒH¥²”!ÍíÀuþÁë¾pS-VÞM¹”dmÐL¯Ü†á¸6uð|#.ÊäÊÉÜ@à{&ÿ}v›uˆˆÄE'™sj>¦þZ2nÇË׬-å®Wߢæ#%ß`+Âގɬí'm'·õ#.|¹_úuØÑTF#6Ì5å*oµµX~ÍóÂ\èÄgi[á4Gëk®“]ä9*éitQ™0#6ñ'êwÉúgúÿ§îoÛ`§ñ!(RÄæY}_’bû.“»áÁ5Å(,D‡ÙéPºRð¹ßSX§%DAÀµ¶&Œ‚¤?3Y{ÀZ$*Áöm™˜k_ªèzÔ.¹#mäÐl¨AAì[œv,€aÈ$ºvÀ¥‚+ã'C‡-®H4(an~#.ÐÅ¡´ÁëX> 7¼Pˆ“Å£Ÿ›ã“Fs¼ðE’Ó£pè&¦‘—K¨€ü~iÿKn<£ª{6±€g™TSE_çõð¾ÌÏžRI'EÔ,Ìï‰#.™–ümQºC^[ò†#6Èì<¶’èÝPi¥ÀûqL…ñ=Lo³#%{BhÙë Ҭܿ¶lÁs-†…¯¢jgÎ0‰‡€xAËÓ¥¶ßgLeXd¬b‘…û`Y)0(ÏQ3‡I!…áô®ÔùašÃŒý¡ø̽™:ТIÅÔeÁÙ)ýÕ‚Ô<=±®’áÐÁá†c),·[I_òÀ3r^”B8iý5³V-êWBÆCGµ÷cHgM‰ardæ7ÝÍO1U;žæf,Ý1!„Èkq.‚ìaì£P0ëO¯võѤÈgõá§NJ±ÐèO5Ĩw¿€y‡ˆr²þÊçõm|µåê#%ĨƒÂ#%p+ÓUC'‘ÕG Ìñä¦j&¨ª™ßË– ¡!ÞÀ[©#%¦B‡½‘OE«¨¶Ö+Q´Z5mdÆ©5£Y,mb¶@¢²*TEMŒ2…Èì5W‡Ùn—¹\ê4—"Ô–* AR‡æÀá¹Uz,.ã`!i ÅòìÚp:6ßvýøg7nYÁÞúËÝ;«Üd(]Š)5«=:™šC÷«‚Œ7ã„-\õûn}ªcâÀuÓWCÈ2ùmFm’CEli î°2d–S ~Õê(‡åíOyÖ!¤ØwF¿?´"„Œ5jùíÌ<ÁCi²”R‘ÚPl.@ä#6ê%ºéçÓ5„ÊU¨ü=¹Ñ®%O™×?#6\¢p`v㋜NJɆ@;$™Ðs]‹_2B‘ªPôÐöVâ¢ßÌ0`Cm¶¬×7¶Ú9FD‡·7|Y´&©ªVÍ,[þ}¯]tÁ(L:’"W. {Ž§¶@,ϬGüÛoÖͽ”(TÐJÀÕs’ª¤ ó£äI/#6½y³ªîfx²&qK¨*.‘qß<¡ˆ¥¥LÒ®%–Å#6eq}Kw4(#ÔæH€ Ÿò*EZ#66°Ö‰Ÿ-5¸éW¾`[É@DêƒFq#.በ†ÌªMZ¡Pwgåõ{ö²Ÿ7>Gcúìcšò0Ä5™µÕÔâ»×võo1“kpÄ&Á|ÙôO«Y…ñ–¦ãQè‡î>Ž#.zÞ“®ùŇ'èkj—©a¨‰„²ÐÈTʥȔläÏ$ÍíB…mÊ=·e×àšY bø¿]ع`±#%˜ ª£‹.êLãžÅÓ?ɧtÐnòu#%žNÐê„Ø€L8»1²üÁð¢zÏpÝ#.ž½…Ôêôn•‘êÌÇʪKq·t/Bª+5Ö˜kYfŒ…2ÉUBÆÐÀù¾Cˆ®YIô ÙMIÀǵ܄$“Á¨˜Ø:¤Â*š›ËuÖηL«w]¥#jff±PÑh×]uôÕ<®óõ§½æèQfðÀÆÀnBhT¬ci0xÈ`hpTZÔJÄ0`Ä28Ü«hãÕJ!ÁÑS£‘ ÉLkLD²üaÐ6™Þ(Ú߃pHìÃWÖ™û%°Dûo!H¤HI¬j¬¦ºUÍôMÌ‹6J´¥\é23Í×Y2¤ …r»ˆàd¨Å ¢‹Š#6&†Û¤ 3PEÐî÷ZH ¨ÌÝZ@Œë²Ä0ÂíV›52‘ ¥l¥f†Sµ$‘e6›,Û3$ÈÛe-/]Û˵riÝË›rAfë®[sNݽåÍy¼ï"h¡RÔ3–ùýw³Ù›šf¡å輸÷íï¼öÕæ"¢FJ°ë¦2†–šÙˆ¬ˆËAÁ\µh@Ò4EÙcÄôõf1ºÔ6î“#.j*Ócq¡¨ÑÈ0À×ÙUkH°[yÐ÷´IòãÁ¤óßQÆM¦Òp&äbÓ\3PÍÖ†úÀ4Õ½1ΙóEåêÞnÕŠ zÚéEF¾ÊËIµð[¤ö#6<qêšTU¶ÓuÛ5!(£ƒdbå´±#k7‘+®6½ª\®¦øµ]ï™\†÷¾’¥Y•â²"°£i1yŒic$I˜Ùi%ehCÑV‚Žc"sz(ˆ¤ˆÝT‚&0ÝÜD1§~/êˆÔaáˆenÃ)±²á™i©Ä3aCwc®<¥j[J\Mb0s.#.ç¶ÖÆÖ¤2Eõ5Ÿß`i’l(A£§‡£‰G½„MÙ·¥2AžTÍn¦©Üž]ÏDO•µ3JiAÓ«Ê_¬Ý»8Ÿ+ɳskhÁÇgjF—V4Ò.#%ÖcKlÆöõT•MžX…,A(SæX‘ôšçzGí ºó2Ìÿ#FœÜ4ĺf#.„ndÇÕ|PÇí÷k#zLö1Âf˜Ý—Öd6róo?v:WGÆŽ.Š5Fcmäå…tÈWÓÇ°…š1¯‰ÝqƒÆŒM!j´•M¶Vo‰¥Øz¦øã5 ¤§d†„˜vÎUjhÙµ†•#.\"ª9lT¡µ=³C#-‹ÃqÓéîó}&°Æ0#.7B¬¡û÷ Bì¤]”¬‹ˆ&Áê$åÜ,eeU»cõ ¹¸GãO8a¤)¡ ä¥P4/—–ººëtÖ›{¯5E]5fTŠƒ@Â& Â6:é `"0lŒU,P]§DÂQ %Ħÿ¸ „tàtë=A×n¸Ø nìÁìüô³,8¤è×æ¤EþÕiqu#%T#.Pæûqb¾e4~¹(ÄHægÞ·›fŽ='³¦ÓO¾ï×q6ø¿yÃ&ÒVý?²ç‹ª+lMHŸÜúõCÔÞ±\öÍ#.hÅ#3ÚÚmáîÝÔÉK#{{ºNÅËÖåyºjð=šy½\ÖM\l¹Ù‘K*̵ðòJpð÷uÐôÐïÛjØÍ-¿kÙ»£qFb!$‡-ÌÌÛ“¨Ù¼è®¹ãÌÛ„˜F6qÇ8hPhÂ,CöºÂ7ÌÕãµCdÌqÎám#±•M¥„ÙBº¯¹yÐà½ëš|÷3¿y‚!È7稺(4Q®ÛX0“wù¾ƒ,h#6?äè‰!“‰¸\£Êo/£÷õgg^i¹ã‚íÓj#.M,=6ͪ뚷ê³xFpSÑ4¦ì¨‰–€FÛ,„š~¦JôÖ±ÝrðÓ+°iá³Ýdý_>÷Ž„ÖéM¨#6kf©l#6Ì놡Å)”êýáˆ{Á„=óùœû£‚{ vÜé „R$T@›‡Ð# *Ì…°´%Ôl–#%ú H‚š›*GåB XtL„,³:jU(älŠFÒªŽ‚®Õ`]›[†ÖéµIµwŽ‹b×r髵·6îÜÈrêñµðË%‹H´ŒT©«œwUÝÚ±Z•¦¾+Uã[É[Iº@•%rØ¡#6BKI€`‡šW(0ú6nün°££a‘I}ø«Jì¹X½9*–Šae(¡Q#P1EŠZÀôÉŒ.p»Rշӛ͗Æ×ZïrÐÕâ¢%&±’X‚Ñ%¨OsË}2†±P”BTBˆe„EªR–¤_ÂëQM”ZÅf&i¥E±¶5™ŠE¨¶6³+Ó5I²Y”LɉTÑDmLɲlklÕçjº7ैŠ9Ÿ?ÉïÐÐ>ߺDE‰OŸjÚóÔMª+ RRËð9ì¿âŸ‹ÆýÝ^lxêþeËŸÝåEÓ¸°zÃÅ8ÓÕÝóa‡T9—ùhìž !¥ŠÖüíM6´”ÊѾ‡7ѽ]Bͨûf·äV·#6µâ×OÜêêF™æî¢6Ó»³®µÝ¤¥¹ŠŠ«Knk—5]¥+ZV²½vìÖj(ÄJE¦®ÏøHf;ÙYÈŒI–b2_l(†‚Ñ$˜‚D@ Ä:o',À’ÕPBMˆÈ4p#6¡a#.§gW¶¤¡Pm+œÄ4ÚÁ €™—Nκk‘fñÖ¶!4ž€õ…>ûµ>ÖùñkÑ6wć–äÁí0Ϻ¼è¨…Ì]hʈ°Ÿ,Ó`ûO]Œ‰ÿ3]¸™·7µLÌf7?,lc]Ì“MBbAŒ1H¬µ4Ýi†d8w&ßNÓ)Ä”v»Æ#.Áàu?²æñçãµì! 5éá1°<¦ˆt2b²tÙðVþ²ðæ8ÀU;¬"Ç‘¶C’}åtÈŽÏë“|w½µ^]™3aÌ8fùã—=ù6´ì§Í„%µ ~¹åšÑ¶»zÊW\=ÙXÛÔZh¸Ïa†°Ó(±Æ™må÷<H°UýÒvЬbE“È#6NÁÛ±=ôL~+"‡õ†È„eÛ)·‚È~ÏðV<0T‡&*ž“2ÊuJÚ©X¡à†÷BxQGš`…×Øhœ Ÿ-“T–ŠÑªKo•/©ZºˆB2$"cü„\¯IÛ\â£rh¶·’{:²Ulm¨Ú¹Uˆ#Ÿ”U#%Ÿiy?Ò3vµÇ‡$eù»#%ˆb Zü¬ßàTy„/o\½Õ.@Úº[%´×]§·-lfmBÑl@Eá1Á¢#DBùRÈ8$j©À\0z¦Fü¡€¸ á[oZµ¾‚[[fIŒl•m\ÓX¾>Ù*¼î«ˆžË¼UÚDFÒæ»É×>µž¹éw“W–Ù’€“€œãKÁhã*7j*”™A#6´¶³V¥(1Ä*ÛAÍXU¬!R#. aS#6$D•Q#6#6ŠÆ,ÊŽXI˜…vU‡ï€DdЄÂ%¢¤€äÄã0õ‡ázâhÌ RƒLcAA‡¬–T#.9?³¿Ïïç*®·€(øhyÒ]aöàŸsÖÐa`ªsêpÄ%X¬gÕ™3nš#.Ç#.³ÈÂêH9F1˜ÖKQ·nžíùîÑ™ÛÌmµ¶¾©kU~[Â` #.Ä;@¸ˆâ@HŠnüÝ¡ã¦Qð!¼*‰ï'æ£[£Cõmþè¡/C ;¡ø&©“òƽq‡j¯ù#%AD^G9ŸÐÀÓz*‘J#.Š¬¤,XcÍ#6ŽÇ+Ìf¡„zœ#%fθåC8>yóvoÖ¢ŽJ¹ÛïFØØé”8ꦈbCDÐ#%ÅEœ7áÃGÐ*‰¨á%ÎÞÇ¢tºÄ÷Å4Èn#6½¡1*ÃM63âqMí€!œý!ȵ¥ð¨W¬|TaøµX#»#hLCZ¢«xbi(6³‚Š±tŠ\°èYl…ˆEX#%$™—p‰4b9&K“`È°y~5D¼_¼ #%¡ôˆ^3Äê;HÓ× XïYU[ï"zGEM“pùC›Ý‘¶Íêц|J§NqÂIß.k6#.éï?XC߶ óð&‚©UMÛWJ(Õ]Ûeº»±š¿ºÄB¬€%Ÿò\;é(B¢ÔÈîLgÇõÙüÞôÛWÎôNÝWu\v¶Âegd!Q‹ò–Bt?g>YAh7vgyÝ\º2嫆”uÝâòœÜËÍÅfË"i$l&Ê–£ÆÛ´Û&‹S60xÜMæÜ®UÍÜw^vo.Üu×l£•ÝÛ¤W/ñäª9¦Ëy<Ëurîk2Ƈnój•hÈó¶Úæå®›VKhÔ¥ŠÎ·)³,›'ŠÝÝݧ5wft×$RÑœáÊ»eγQhÑÊÔX¥"µDh‚´m› ¹¸#6XTbzà„vA؉CH#%ömõoÊ´;TÄ< 8#%îGû;ƒ¾Š#P„ ¡A;X‰alExýª*ŠoN™¨xþÉÂ{S´pAÀúJA¸!áE ±#%ã›êwŒ'¬+Ó}†Ïu¯‰¥hÂ%öz3…#6ËïÜŸ›´æm:Ïr#%r"1Œ’ ”ÌÒùíêæÛVükZæ+kº¬ÿ¤¹gú£ #6Æ#%™Îžî6S!5²jÅjÐ¥¡–·½5{Ì|‹1À P÷€A!µ%AbÄB’VçjœuÝÔ²±c#.ݵêZðc^úªli¤#.÷„5w!,8‘*àN߉D l&’PƒtI¨ÅiéÓ&Iš#.ˆ½Ö|ý·\{úµ¯UÑæÝÛ½(ÒDH"^‚b#6#.)k‹`‰E•Tb¤"M(0'ñn\ÄÈ)OŠŠVuç#6'÷ð=•øØ 1†„HA„cŠQAû†j=•€Ôáa3‹¼D#%;" T€ÒÍ–ÖiµJ¢¶•fËMô¡ˆ!·/¤¸Ž‡Y@sÑV2#Í©ZdÍQ¶Ú…DüÏ&1‰½#.@ §Ë†HH"Zf0šÚÿmFkB¦km…ˆ7€ÉLÍv¦vÓÆk‹±_T,£T ÂÊ$6#%þDûOPp%*¼CrW¿ªúÖó6óô÷}w~›¢|—ïd`ŸB2¿˜¦gËñꟙ´Cå#%a‚aÌHôÿ¹Xˆ4)RkU‘»M¶ ÈfÈOÆÑv`iñÎŒlD‘HI'2's¤Ñ’õ¡²yˆ?Àáýb€@cÈoSÑ5·câX¢^ü-ëpqû˜ZÇ#%‰ÄÄ#.IÇY%¢ˆiQ8ð$MÊ$4ªB£ŠQ!.pK#.dÔþÕâòºmsWI*å^ï&ÅÄ*6VjBTBW@#6Ö#.c0LK½àŒ1†åËBá-$‹ok#%´Â "JM!hKEÌ©#~NÍŠªð»/8Áo:¶f¾$öû^à쪰pÐÖ„ 331(Ä`m¥ÁØx]âO,’½).àdË0L\Ò²u"Áb…0«‹ÔÁh BX’T°€²+"¢7*© H4uÍ’it52LòêUÖRnݸ6Âhüt#ùÓw2¢H×`ôÂò*ù{Ntp~m£fE…DAÙ‰`=aPP¬&i»PE±*Ð#6E#KT-ѺZÆž^Šv0ù lûa£ï€ƒÄ‘Œ$µb ÉS¯t·\ªvݪ–Êš[§.ê¾»mµõR¶¿’¶×ºØ+ìŠf‘MÄ”± f#.‘¨-RHE9À³‰ŠÐÂѹÑxOd#.Ášyþg2Å>?åDÚñç—ì[Ýa3FÖAÊR±1m6¥ÒqŸÏ#.¾r¯©†×PäqŸPì†4`!‘ÄF˜ÁDm6›¬‰¶òÉ’c‚„$‘¦ÖcÁ¨ñ©˜ÛvHEJèGŒ+ú2LÌRØ&Ç.-S9;7¶£´3P–—´¹”-2µ‹–š}¦Ó’òxPëצè!àDûšOË[ˆm4;CìAÓA?{@~¨ìFêøʈ;gèª#. #°êÞ#6&àÍdT#%¤‚‚O]#.0UXû%H€ð+Qè¨÷#6'dÑg"æ”Ø;¾#%âßh§#%–6‡ÕáüÓ†#.š¶ö!þ]ßzLÉ¿¿¯¥$уHX*fO‚'Æ"°›[>PAÍ#%GÉQMAû={”}ᧃvY¬¯ÑõÖ)Ñu©A_°d FÛϲ½¿†Šc[Ü%•—oEÓ,y†÷¢ˆJ2&óo†tÜÖ‚;¤¼6^o´uç‚A›(77ü“)– µ¶÷Y…ÄLx‹l+Ûš¨SÃù5ä4wòò$;8SV›âT:—<‘t(ñH˜Èª†',ð³™RÚ€ÒâRgpo¤ˆŒÂÉ2"ZBÇË0/‚x†ã~æŸÇTCîûÖþIõ‚h&"´#6bƒéC;¾¯3Ž¯«ÚÉÖÙ’>úWš‰ûc~ÓÊqÖ'lcØÆöœ·0´U>’L#»²8US›3H—¸¨wâ® B?‹ï‡kzþˆú&)¸E–m[p•´!oqÎJЗYÅ{™ÙL7£L€ÁA€Œ¸¦ÓŽV0>²BG.#ÄDDJª® )æŒM+I’¦Ík¥¦=i4b¦—sìž‹'4ƒ³>é}2ksá²HÎó|DaXéw‡kÎú}e°&f´R‹˜ƒÚyiÚΔóKÛù=iæÙ°y/âu+‰`Çxö$ÝÀû0}]‚ÐJ3ñdáË-^Ýø϶Û9@Ѽ#.3]7üºÊê@óÀ¶ñXTÕ˜¬Fôõ{¹Œë²›Q…2fqLd–¦ñ!»o³!‡ ÄtÞ8<µ†çÂôÈ6c8Cg¥B~óQMãøÙKºŒËù®ÇC‰mª¹"9n¨¢H´<J•#Öر–#%׋1Mn ¥ÊéÝÔxaöŸOÚŸÖNÿuQá´ÿb¨Ìñz` œ74*¨9‡N¾›tÌ Sõ–z¡Ö…Ë8 zJãì;EøxW´‘±3Cd¦””6JöˆHŒå¼Òa¯`&C! íŸó<µ,]I˜ZðÇá€Òß:}P#6f27¢– T‘Í6Eún§m¯FØØ™áŽOu—m×êoÙœºSV#6ÌÔýìÞ×éÀÔ’¶I&³#ø}3 lýCÖå¿YãšµÏ]Ú6·©d·‹jsÝ…M)gV\ÕÛÒÕÞ,¯ßë'õ‰r8ÃÒPòC5…U4v!yc:Îpôk¼¶••ÿd^קÃD~„»÷??[TôšðŽE¶»š’wª•;ÀkŽ½WÆÎÊ]!A†ùp£Îrm°d+Û #L`„Œ± ´|P HšÆ¹†ç—_®gXo,›¹iž¹ªË\ XyËú)ò¦á4·†§`ž8ÏkÆïgBŽC¹HÎçÕåNw3‰Uv0/ÏnñÑ8ª¡3'»=·„1Mw™É™1‚Ù}ûÒ2xÃER…¦§‘±PL¿@ã×¢LQRI5=û¼x_ZC‡#.eÒG§ï‘üØke(¤ÞàÒ„ F@½Ù¹€ŸuôWnqÈËÙþ/£ò色§Ù(oÒ`Ä‚H¿I/#% $;ÁðH|‘ëü!ô€ÿ8¬¾$DYDˆ‡vâõtŠŸFþX*G²»ÏÅ^“Ò™ÌÝ@E8*mì81P}°‡f>ÃÞ²âôž“s#.Anã"Ý3-½GCÔtä.9#¿å¼:öŸ´Ô>‰ÄÇpwÇÉÄP+m&™#%f¶FÖ´ÑÛd92hÅŸ$ÑEy#6,:@ä¡í’(:xÔw‡u#6Š©6Ï쯟T®Î®£Æ>ÞHˆ{:¨óÚrz¨öY5zº¹[(úù³¨å•gõ8Àu¢ÚO…±Ú¨¦ ¢0ôV#% FB$š‡¯°Þå<jØÀOfçDšŽ¡Ð+ö8ÍÆ#%àA)hBÀÆ+ Ä®qk_g?ë?¯à̬¤‹ÝOÞS/P@JFÒQ?µ•ªù±À2:‡ß·gˆdªH5R#£Ê‡ˆ;ãM羄Á#%óë‚xN_¼n1¬ÙlX+¤Žg¹·ù_•4k3>÷Vgl6éÎ×Ó©ñCqš×ÐEúW9³‘v@ÓѨa´Nø¥7ì⇲ñvVù¦…éì€?ÔTÆ'»Ÿe¿Ü»Ù;}`£9úì=§„¦ò•…A+©/þnw‹®B’v>û¶çU;QUΆ¤”RtÐ:`#A‹'BÑ1Œ«¾ùa+Ð1 MpAH!ŸþO«mmCPç¢6îÐ*Á#ñÈr=ñ?£?ùûKÑTHØ?ÚáUŠ¦7ÊIÈ—Û—X”ÇrxiÞ7•ÎåÆš˜:tà€^³Í£Á!žKòó©,8AüÏzßóÄw(I ìäh·ÑQúöùw,ù.™1øµZw®íb!&t4!Ç.ü–ú2EŽ”)OkR¶Ä4³¬?eBF1³öeÑ$í8xnÕnùx-ˆPúÉŒé^û,ÇãXFKPÍ<œÇ+•wOÞ×j¤Ñ¬E¶Ëf³MRÍbZRµMR(gfí–sʵ$‘Èu.kOU;ÏïHÝEUõÔ«@È¥~Ïíýש?£ú~dm”eI£0h šm‰¨ŠLBLh”b#M*FJ&…!&Í“a#6ˆ±Šü$åÅögŒxɳ'€«:û“7Ü›o'6úÝyŸ”R’M›vÌ ÜÆ0Øh4MÌÝ4¢¤aKæÞ7t¶zh%*éMð·åÆ^&y}Gå1ÑԦ׃M6`¥·m—8™@¨…{*E#R«5sQƒí‹“øø˜ÞŽ¨Ã&D¡+fCÐÄÆÛxÎ&cz\/Ó†À[]4šªœ„>n¨¤ôÜŸh#.ØL“ ïÖ9obkMNËÒuÕ.Âjõ!2̺ju^ëØÖ8ì3ŸÛë¨fèÎ[ŽG —ç_¸3ýO8†ž°Õ Úm𦶰9ãT’/è„™áž3°ûç™#6ƒRi¾m#%F÷"¢Ö[©Œ”¥m¦œiG#%0NPÈ©-D‚„#61u2H€âˆë—QUË;®JnVÝ®ésuu®ÈïkÎòYšÛ‰×·‰7®«—+lÆ4›hÆ@î’1ˆAš *ˆ‚•º%*»@Â’Žë&M’æ˘ª#%¦_å¬2H¢ÈAB,$Ö™WŠ¸rérÎësk$¢3—*çFד2 ËLEø:UÊA-H¡K29ÇXar„—ŽR×eFÉ‘?6¢teÌeÄÑs4Ñø©…~÷ð/c…ž ò—hâÓX!˜Ë#6Tr!žS“Ñ©/Þ:ãk‚Ãz)\Q„Ñ!TÁ`U«Š¢7*â͈Òll«/c\(›Ž!¡6BÒˆ’¼«³5A”«¥¨Šž;Í·rÊ.»xÖúÇ®î×uQ”õíQ°i…¢I¥Âħ%4Ъ!h¨&MŽ9Q%Œ²ÛSM©·LÆ<D†”ÓÄÜ$‰AƒVeCbz’Ū‰×*:R\£³Û+µ‚(m„UÅ¡:‰WTŠ7"B¡\+-£u´6›6R†Ê–ó Uj…¦<«•5C`À5·—&Á¼éåŽnãÀnUŠ.à»*.ÔÝTZ%ÚÔ&#.*A±Ñ©§¼Å¶e™ÕÞŒøÁŒÆ1š'3²9Daƒ[#.iHH7SlYe «UÆ«A–@¬’;)ucÔN}fÍ=,$‚î|ùWoŽÃAŽR<®Ó&ÂQAg–Û#©Â‹\\CÑz!¦©¦GK1dR0q¨²yܦ8BÙ¼1’EâMjJ#.ÁŽB•mÌKH'…p¬ƒJ¼—šŒŽá*8ÀpìŒ#0o”àÅ-ZN5F†A4¨·Bæˆ 2+±Ðj!B ›SR1F/4Ìâ†0Ê#.býßR ÷Øsȱ˜Tnͯ›7†nnø÷Xµä¢ÉʾNLâ!ILsC#&D™VE¶Fi›„‚¨(a S#6Hƺ-¡2@aZM'"1A‚Æî<j¨í˧¥ÁlUòÃÃÛæM8K‰˜¤A³¡ˆØ¡)‘"ÞZ9fñJ6Þ˜ º‡ÐÑœê äd'̓4Ǹj¨ºN0ü˜fâÆiûi—©²™°°(0ÓbÉQ”ÑHD5qîe‘éån#.nèÀ™O\m Õà*%oŸC#.«ÊŒM’ï‹ìu¦÷Â…wÀjïˆT™•¯¿&Cr—@Þ±¨Š¨„ÄpŸ•6Êäøz$×&p²±@‹V[i¡YDæË(F )96pÒ¡&4©J¸(©0Ö\±‚’…0¸†(ƒæ!B†ÐQ!bJŠ£"¤Á€4‰ŠÕƒ´‰ÈÂÓGâMI)Ǹ9pd!U!Ì[¼;†îíâ+Îòé‹Q’×b‚„ŸžŠQÁHµCTˆEÍIÀ;¤’‚Ú‹mŠ¦Í¦l²Œ"µj …”Cñ`§ë.ÿ3#6ä4êJðüû¯Ì("Å#6ˆˆH°=ùÀ=Rc·.£÷e‹}·ªŠ>üR˜ÕDôÑF¿œ•Y—Ú>REB#%!‰ŒT£AÜ‘Båµé{T}µ*Àì=UíŒ&„æë§ÎÎ00Ò °W!P‹'Û:D¯µg<¼ÕF r†™„O#.4P÷¨ì¬p£þÛ2FÄÙÝÛn®®›[{k®”;¸[ºU0Ir…Y…ƒvÊk^(¦µR˜¡LdÁ’ RËuB¦æ»³qP Ù(f3Т#‰omB‹7û¾¥°·°ø\¼ÌÚÒ㌊L6-Öýš"ò»-ëÛ¹Vçt$YÐI€ÛâóÝkXäœ$Y¬Ùæj±ˆtª/œvA»ˆ>#.¬—½HíÓÒ1ø¶GÛ«ÃD¯P‚VÑõ(þ¯àˆ© N½¤€L¯ÈÏa‚“‰‰r×(‰œ$ìÝÉÐ’³àw ïRb4w‡^×»C¾/…ìX,^¨bK”\«Ù½-!®ÓlßMvª|ºõö!üȦ7@Ï_]ßÌØuåÜ(7Ñúþ©ØÁ#%Ë·I#ú;}+¾?}œn‡*œ>—Û±ÏÒ[ì×{oѤvÒBÅÛ³É~¹¼È?ÈùŒc(šñG‘© ÔU#6„+¿æ‚d礇¼ì½¦ÐðÉßz†R‰Ê'j[br¬#.v#%ôηUŠšûʸT71wVfºËmŽEßÒXJ§^v×# ïEC°yƱ,• ìA2±A„ETù°’á™òNÊR¨QeTÎÞ‰rá˜\ö9Ú5‘jªË#.&t0ßá‰cZb8z¤0À—mE„þf³ÈßçHœŽ°Mq4›¤Š9 #.þš ÃUHr¢’\ªévÜV^ßÈò¼QkÚr¶Kh·²ÖìKrîêÛôK\6¼UÆŽnVéµ·MY#.cnm&Úæ²k—-µÍÞír,m“jQª+—‹;«î-Ã#.âw²Ë»$çžv÷07Šü<¢ŒÍ}Zk¾˜•˜zÓo´=H^Êî"Øκ’!7@A"ä#6´Þ”=¯íÂäD´‘ƒ»¿vú®_†o™ªƒ‹¾ªýQy>ð)Gä#6çøÑá"É4ÖÂ=$±òSè2w@d#%“Gßè—q¢1¤(š‰IBHå%ŠJ*¨Nîòá^T#6Z\# x Ùb¡†*©""(¤›[u#.mt›¦µø~{oF®Ü¨Rd%ÛÝäT]Ñ *½Q@–Q¤ÖHѶÛ%[¿™~k÷!D,#6 éB?õ@Ô#%šÿ$CÏ¥'¾ûHjÆMöFÒ¶FñÇÙ{ZPuðP甼P#%;²f_Œî@O÷A$‘;ý&Ô¬›¡Ôu`¯ª¤ª£Ù¨3`Ð: H#"&Q)òT(uqe-LÍ’IJ1idŒ´i66hmIEbÆÊRRJdÚ4)µ´Eª*ØÖÔkjZV™jZ52¥bÖ‹ci5³[ϺÏn·$cu¬È²#.Ç"6W"Dûne6˜ÛaPÊAÇ$r®tëÓ£Ò®Ï=tªò®<ìiq¨ò¤©¥’D£O c!&$Ü*+hlŒc ¬H˜É-a"#6@£`èÁi†“#.‰H…4ECJ¤P*( B*Zë0$(¾B¡ Ô#6Y2?Íü5°Á#%™ô©DØ%%¦îéݘÜ:ÓõÚô·W®ìÛVÀ(@d‘‚$–f2Óȸ@M°%Ä^û³„B3ã`ûÇNÙ"vÔôôm÷wc¾²³Ù”„…QRN {ABH,O½¸VÆÈŒÜÐö5OÈ¡äØf2vp®§W_—[—,1Z%ê/-l@Ò¨ˆŠ±±Ü¨Mâ xFß—¤jœ¯§1p‰V!TÌH!PáJu”0UR8Gõa£ÜœµõN¦’„µFénãɼ•ÖòË©R™”/Ú]êµsQµ±hÕZŽê®Á¥±ìóp–9‡ávº?®HEÊoz¤ÑïpÛ2#6e_ìÓjÎ`»8C®+dßX{Ap–Â!ê>ÿï(ü±YD©Ë³eJ[LTz÷F"Ë`ç`΃՚‚RúÕ ÿ™ùiÏJ—Šê%áMY¢ªS|š²|‰š®#6‘u¡‘àÖ½usFpÙ"}rC¼"Él³Êª<ÂoבøÏM™Ïr§ý–æØè„#Æ X}¨vµºt¡;?;#'•ËRùi@Ü¡ÍM¸Ôã|]?ÙÒί^¡ÔB}¨#.#%ù`ø€¨ÖîAå—Íã°Ï/Ñžÿ|hÒl\1º**;»3Ú*¥â‘TþÈñÅ4¢ØìçÓ~ö3aƒ¦)¸÷GZ³*¥›z—ÁÑÇUƶÜ` ÂpÞ]ÎÚ£]u\ÊJã‰Q¢ë‡òUÓ3÷˜‘ðÁÆ&Òe¥”ÌI#‰ÆåÛJ{ií'[lüÇ¿Üo5ÕÛÄîÙ¥º'Q#%ä3!O¨"e¨BG½V×ÒH#´'53×ÕîZ×#.ÃP„˜ñçRn tÈCñžxm÷¦‰¾Ù¶Uúûïy/‘ãV°A„<(}ZÒÙ81K²‰$=·E4œ¢Bl‚:E’æÊSlµÉÆW_û×¹/#.ÑÛ‡QuÔîí®:väì³G¸\§”µí{KXî{îV¾¬#%¤LÒ#6Bk¬¨7â¶(/%ÀèÞà÷Ô¦Çb-¬Ä¢õÂÕÄÀÂFÅÆixå$Z³™F®;ûó–©vÂqÙÔ!Äg ÷7tVBZÇS¼•/yÀo#ß«½Þ%™&‡s©X½*77/²cMž±Ár˜øW^3Ý$"“Q?:yÛ„6"£ŠéÅƺ–{åøÉÐ#.ôŠ¡¡”ÌVË~ügƒÉg‰smm9)¸®jWMöÓÃm‹f±“ 虘·â®8kÃÓ[é‘þ]»±6årXöN”VRTâÌYÍZª¥;¢øùžgrª³s BÄyo…,pѾa& q<£“jf)‰ÐÙnÆÛ›ÐÓE¥Å—•›f5Šw©±ŠÑÄâV\º)2›Æƒl`œqÑáDë¾x¶ŒÅf^–a›TûåkĆŠWnëh6“³m<pæyßý_ZÝ>Ø—[ÑÆçhNI/$“ËÆfš7Ž)¤ÖŸô•öÍGŒb$C¥×ÃŒêC–C:ÚÒsŠ/}Ñw§Ê€ò¶DÉJŒnÛTóQíjP÷xÍ÷‡“bëZ:>춖Êü¹Ÿ9õÌW_J×°¦ŠéyƒiÄÆx’»uƒw}²™×:üç´ç”¼Žî'÷u{õõŒa*Ž½^W‹W0ïoèÌÐâò¹˜ô³8ÎÜVx˜«¶®œ§|ʼntªCVo…Ž†#6åUvga*Fñêòª‹éïlg¢/Ç=˜’Ã5ÅéüÔÂln÷.úþöb¢</Deìóիô<¥sïŸ>NÎ lr²¶AÅ׬Æ<EúûcÛÔÑìôþÝ!˜ ‚"D^æy¾8Ü£ŽËB6KÛ8£,g ‘:d©D÷˜ÓìLíoY°¼0PÚŒ/iÈÚyÔsdÛ)Ž…7°²›žTîÃ÷ä&ªhdrå#6%#.-í/Š×Cð&d«©.]ê™'O8çmÃ7£žUãŒIð,¦`œW£2êçµ8ÏNÀÅî$`.èXÌ@ð=Z¢Ã„ì÷m„R†˜m³³£f3ƒg#„RcÀÞQä¼b3x0óš˜ð½0„_”$gB†È›0íÚº‰9¥#6¨ÜÑ)¯±Ò'¬Ï•Ç– keØ yŒc0À)Õ€t™±…(ßÛíÖ ÙdJݧÍ-®¯®ŽAåÖÇ“¯0É<¼–^G¨&ê@1[õ c®UNÃcAÈn>Ð o#.°†(ÐÈ#×LC÷ªó#%€@:óš#6`FãaÀP™˜‡a»ô:£L6ñÕýxõ»Æ`vŒž¥›¤•`qG=æ26ð•9è•ÖkSAC«#%6ª„sÊ^æz±¿v‰•‘ Z3Kˆ—3.ªÞ&˘sxÃ\ã#6?#6«_Dd\¨¡PʸG`{»u½§D¯OÆ.þîO'6÷ûÞ[ž‡“°ìkÅÖÈ,½ÓÀý* “„GÄÂg ¾T©œ"Vsïδ##ËlºNÁƹÓkFÒéaùC·”úÏòghæz¢:§ç™ŽŠ–”žç—>ýcšŠETÚQSSNFüb((±SöÛœM—Äe²Ú{Að]‘Ák]zVÔ4o““Œ{#.”Uû»ð*#%Å9ÇGÖ“ôÚÏ6(¯Û#.oÏ϶;3\4‘Š¡³G>FÎD(Öѽò‹çÇÑp²9–žfª7 Zæ0ÙºÞÝCë¡ã’šÖ‹NÚ21D‘ª!ƒŸ.e-lZ'ÚUbÕõQÃØ¡™ç~%‰@½vÝiVkQ-29¾ysæ83DñÅì^[)X×ÕÚÉš"Dv:e£;F#6Ç~›•Ù¸°ëíÓÓwÃŒãWoÕqÜÌ‹²¿';¢Cs:ö#.aº:ë¶Y¹Tû›èœ¯[–ãÔÇ4v4n s©»x÷ÑÏ;èÝ2÷~öç‰sw~/´j¦<“£\W‰t·Œåóm‚åG‘^5&ŽÉoä wªŸ7AÍÄ%å#¾Þ0Må8pqäñ<³~7SÛ»å߃Ló‘Î¥mZ¡\dõQ†Ð{x09hRÄ¥ˆµ¥«#%}¥ë„ô—‰”D4·–bÍ…èŒ3¨ç’š®E’=¸¼ó‡ ;Hœðà>.Ãx[CUZØ=íô7,Èh¨·tz=<7~†O›œ í†fBñEo7ê9q À°ï‹ˆLBõ«•³üñ6°§«Y܉±Y…„;W¸Žœ–Îå›\¹Þq.ï6…)sf\=®Ë#ÕƒV4(êvµÐ<³Ê@Ç”,,¸Œg ¢(Ôyzw-•HÕ]B¡¡¯E´öq3¤:f,öÒ'!Ñð ì@â†1HóFÔó vM\;€© É3@Úõì.Úg$$VtIm-äSLÛ0w0ÈÁ'‰#%ÕY"aÃßôèU×]õûC#.˜`fá˜+u¸mæò?‰N®°Cw"³0ñ²)qO‘n"xXŽ ‚í¸NÛ&Äy~‘å(ä·°ÇèpÅ`\r ‡Pž <ß‚ð(;ïÍVßáL{U#.>¿ãxKf°ÂwÀõüƒå'™y;`kàýpÃhST«CÙç2°gÏ¥_V«GªG±8v'$ODMáÀÖN}®;O‘[Tb,ŠÈÃäÒ¡E€~ ¯Á°e…D–aì»Ímlš…s˜Ô¸ª’“#I#6±Kº’Ì0ªm&@•<Æ…(Ó`YF91!²²"¨DÈ¢–‚“EXB!$JÆÕ½+F·5¨nZÊèZ©”‹"(ØÁci¬¢ÅFPTÒŠˆ¢aQFR¥‚0ø!\Þg¬âþ&xØ…µ±Î@LñÜî#…# ¹vÐ…á¤é$c9ÍbŠž”Õé¼mãnEɱ*µ¯¹m¶‹V®UQUUé¹UײóÇÐÜŒÍýÐçP:þ~–µÑøC5÷]cÇ-–§AKxæ*Æ1ƒK+udˆ×/¨ÒÁí¸i¶í®oÜ#.hÇHiþc£f-+°£W ¾ù¥SáîÀkN4bJ³ÄòÖ:¸`@¥,×–cÉ –(2ƒtHqbePDiòãOR;Æ]ÓxÖa%‚dH°!V“7V˜Á·Ù(š&ÓicRE,%ÝEcbi¦4C®Z*=Ê[ÌgA§Âu‹wL¤t’d½>ùlÀö›L!P9l›ª#6@Y0šÂfœ‘L7zUàÅ3R·X›€QŽÆ6ëØËÖ¥4ÊÑÛŽv²ÍîFjh£MÔU¬zmkZ¦A¸Lk.<“µ†µcÔèç9«eo†LÑ$D”MqP\`SZ¨X³LÐ*±c‚ñcix1”2ëB‚ÕC$Î¥¯Õ¾5©Ï9;·hÓaYÎS¼JÎ!ª#6A,ØTP”2„ÇcLhÇ»*ëµs½µPAôãn²‡U"^Ck*œêæVú†•“%+XIwÜ&]J&0ghnÀot‚ÇßhsiH“°N'a§ïšiÏ]P\‰ªR8ÙFö0TjŒ?ªãÞºÓÖ“cSˆ‚ò£œµ.¨CZF“Làèd¬q¨Ô"Ú7*ª#6”*à Ѹ”Ðá¤0m0m15+‹‹FÞn-¼™,²»zça›[ ¹tùÐC 2´CdT¨£]ž&09ÜEZÙ*å#.hFe¡›±—mÝF?Sã5@‚ŠLãRùg%¥¶È|4k ¸Ô €ˆÚhŽ¦ª~¬¶ÆŠä$¼*7Ä®é@ì€zi¤„<˜D¨°]8&è .ï®r#.V†FÚqÉ#——B–¡ZYm r˜m†2Lä„ÊlbWfJÌæÚ¢&n\c§ˆF±V=¼§^1Q¼ÁèÞi•®›m·Xˆ9¢VßãÞŸßÞŠ= GÕy˜úe”}#611(÷"Ñ„Ž·e0Qsâšz±ƒXr8c™MAø¹¢@‚œ=6ñalÊSŽ!vH#.0ÕŒÏ ¶–‹€û0\d#3ÉÜ$d9ö¥›:é¨WbTF¡£†MP1#BÌðCV#6æe´Î-ͳZ"ÛhDF¤¨b0fµ8Ò†¸Â±¶FxØ7‘¨Ôƒ{ÔË ¦A¡6Á&'"Ë9#.+µU°ÍNß!UíÏ*ª™Û2‡s—£Û1ó•M`ÁC!0çr‡c$’Ûã½n‹ç€¿¦Ì+¾îÝiL™5–MÓõ6¯ÚÙH•$ä”´•¤$ˆz—O™®;ÅÝóèºbCóHwÀ©ÚªwUŒØÖÀ4†*SmÙüçƒ U+ {}E*ûXSÔÓ^#.“ñqº²±eÓ¦3™~sÈ+?2CÁûàÿ›Ð9¯¥Ag*Î*JAFä#.o³·Ä³Bx«ÇzQ€#È›»û<*±¿®væA§ öœšÁ›Kˆ?~Cóyßì¨×\Ubƒeª•Áå˜ó>\f÷={p1¼$a¦î“»¹0ˆ¿{w@y9;šbƪ¦[ƒ'º0’ÇotÜòä<×Ñ(õÈø¦k´àµÕ÷¤qÁΑHû®øŽ›ü!‘¬í£Q:ǧ å@Hµð†ç©!Í’5iŠ Wj™KV——\Õ‹m]7‹hlPPH¯Ð†¬»öÐÖiä#ÔD~èì9ߥ}~•Œvý"°R(±H#%VZÛ#61´Œ˜Æ&1¤D©(Z¦UŠil[%¤¶¬RQ¶$¬š4(¦Ê£Í¡M5#JM’Q³JHIQÓhD¤KFHS4T¦È¥SF’a¶lÂ%(I1«X!#%øÞ:úx—ôÌÔžg´ê9šÉSÛŒº¬î“¹[Ç4UyÜÐ~ðWæòs5þW‘$$g1$8)×ÃÂÚµâW#o®å$î—VÐElO"¨>œ€#.¶ƒ˜4~‘ ¼Hga¡„’hf6ؽØLKn£’R&¾~§Ûzk³pCõ%ßÚ(2ÆH\Àù9q]‡=½haѹS(ÂBY‰èâ°ª¦$ã…ß´ùû½šÂùïâsþŒCJ‘FÃÅqy§üÚµù¯×ÑÖÇ‹{!Ãë: ûo?“WÝjßµ¨’Û&ÑXªKXÑ÷Ösi±©B“Q£±õÝ«™C!b”R©bXK2ïÈ0'uàEŠ$fPÉ#6>ÿ£"Ù›‹)ƒí¬÷ó.#.ƒ>+„¡Q…¡¦b0ÜürèÕcD>n.ž5ª(àÉX¦¤ŽRb"©¢˜ªŠD.éÊf’¡”+@6lxœËRXÐV,Iˆ.¾Þ1Ó{¯.íГ³šöó«ÌÑŒ¦»6ö•å&ñsD¬ÊÜ·/×–³PQÀº.a#%ÅÝÞ!”`ŠÐ¡GLŒŠÄˆÄ½G¾ôÓIà{è5O‹†EÉŸ'&u1p6e¤J'LHQb`àËVAd¦¢ ŒbŒ¤…(Œó©#.LáGô`6 kgì›Î¼Þ6ór1J¤NŽ2,=¤4,°pˆœ¦˜„| d<ÀÂÛ«6ìjHÚÛElmŠ#6#%ÚÔ(ƒ"3@pBh_²*ÁUY‘U¦#6!ìßÕºLŽ@ ¦‰@¥+†Å-= #%½]”ô¿~7c¿#´…`>ϾÈ÷÷ò3Œþ¨ž‰Cft½B$#!¿ö?‹"dê…„|êÙ¾~h%¡Ð{Ñæ#.‚’Ÿ,(`šä塤|ÈØ”n2¦‰”LÓÈ3ÏÐ@„>€ã7ïØV†Æ’iy¼Â°÷ýÞ=ßµöR|ª2Zûf‹5ïðñÆUo¿[52UQJ–m¯ZÔÆÊJýKï*G;ó&¦¥„uÊqµm¢òêg™*ñ4-o^…°n2nF-tiªD§7C5Æ6Ó&TS`Ò&Œ!*Q›±A÷Ärê]uâ+NA–«B/¯Ô{,¢tòOئÊúÓ’rµÉF¯Aï J+«)è(° Éë;øNsê>«íó]ÊMË°#.ŠšL†|ÊÛéRæ!Á)’Bª9£^¤ÿjfÅ<Ì;¬#6Aî0xw;Þõ†HA)4g˜fzúœqŽòFõcÏ á„ØE¡kõ|¦!»°uªMG&~逸²#%Q;úŒ¿ÜtŠ@d‰"Ȉˆ*¦#.>¸ô/}RŽâb_cóÖìT#D¥:Ζ£Ê±rÖîõý“'5>‹ç|â²âè}.šàý_Tl×=ÎÜÔ²]4X`C$0ÇVêã9'HØv'F³ñIÚj&¥g™õ@>3¹“ŽÐí€#%pàJrê`{ä7s@òq{ÊA/˜”UÒ\zge)^±²=sDþXÜÆ1×Æ£ÔŒå°nd[wof²XŒ‘²¾çg)P’6B·Þ\+ÉÚEµÜ,3âÍ„JS»uUtDÀý19Wž53ÂpTlÐÈÓ#.¤c€h‚` ñ‘0pP#.@ŸÐnªÁ`È%ÿV TU*2ìÈ/&YÛr-Þ¨»5!€PÔÊ0E#Q¤H›a¸’6P";^-ˆš@â–YÇ1Ìnì5Í—™¡#%1”,½!‡sˆYÚŒñ¾¤flzÙ¹Ôé¥ÁciDã&‘Ho?/3²Ð½X2Ž(uÇÌ°´ÃdÝzYg$äÝ&Á5åDˆEPÚ¥HŒA±è~“3#%‚ˆ Lˆ™ªÀC}ŠYf‚-"¼¤}q¥£ÌàIQ%¼†Ø^FZTz³µšHÅKÅ"@Sp¤u^<o.ÈB3@Dmï½àâ…×j”ta¦lrÞX@z‡QfkQ‚Á¨‚À½Aƒ(Ö÷Šóò¯n7ÛžW üï¬"ù:ÈÏÓ7Ò`ÀR7{ƒ¦BdXg&ðS¨f%¤Š‰” É0ð»,í‹ýî“•¶ÜD¶uS2ºcre38ºt#%™aÁ%ç»#6Lp²O§pß’X9¤à»WD¢ƒvkb”Ô+LÄ#.–܈#.»³BñC7<è‰D©#6H2#%ë¸ïšµ‹!£!PÅ X#6ÉŠH žºÄyŽ-•_F¨é<´ŒöÝaš•˜ñ¿n«ÍÛµ±S†o1ï[ÍœVWÁ¦e 2äptc¯,ã2®i²‹í˜Žs¤°ðŽ4F!ÇöR¨±AV,ñ³á‡‰$Eïæm;ÖcHo!Ó@3k9LJ_™'LÁ›YÓB¯iè1@'*kMž.³ˆÆ£6™‰6“ A»m¶ÒÁL\RE΢ŒFe¥%o˜Ý“*—,už›`Á´¾Ñ‘9¥É1260ìo)¯i#.&”÷Ã@V«¦Ä$l‚tšÊvÛck¦—r˜F)œÀKE ÙM‰,vLí.\º{Õ—[>!m´Áiå¬Þc#4Î!äkû}av˜ÚߤœljW¸ä'7qÖ$[¬vŠÚÜßLU»ÆD<´ÑjlìïôH=fUÌœL3™aè’'£>4‹O¬ÈÐŒ!Zõ‰S5{ÓX¦¹‡—CÎ&—;±8k¦îŸ\@r¯2n&‘='I©Zã¤êBÅ FÕ—„,¹\¬*ÍV׳p™`—\ñ©HfJ•Y#%êë㚨‡"ß‘kkÌ›%ñO+vv&º?grà{Þºç(äUÐä×I¤ X¬E#65³6É›!ÅdhmÌóÀé^]04©qs:ÙÁÇ0@ëæE}h·¨nœLeÁÃ}8 ܃^-1³ñ|2£Ãµ÷@¸p´V×›;"'›‚å¡„±•X%ôŒ–ºí#%í5`+IB27©t§#.NZÐÅ©º†B¸–°Aø±b+m:+¤‚BN\¯…mgY¬ñ[6L†ÎÀVælåŒa²DgÑ@iµDбP<ŽýÿB㌱§4ülÔ?nŠMÖÎð„àM$ÕY'¯BÐòtoi´\Ûñ¥Ø+~8ᥢ]-YÃ[Ôtͤp‰?UÿÂQm—2áLØqEÍ#.q™ƒíñØåÓ<<4x¨1 (3Hr›ŽÞ<9²óëktò ¦Æ ‡r³òëz]YÒ1Íñ,ÌŠsÕ¦)³åÈFµ.ÉzˆÂ—y|²ÂÒ+d׳„ÛFó~BÀ™aud¤Ö †l”ŇÇ-Å…µ¾ù‚3XSC´=‹‰±ùq«óëth:j=iu]†Šp3B„Àtž§¯_ m}i¢2HÛ bvvÏxâ«$3@ä½Ì·gnªÕ ³ ŠP—JPÍHÆÜq!¦YÛ%j®`Þˆ"–#I¢#P/¿=¹fgØMœÁC´;,LMµM4Yp'Ò*Úúµ˜·”ÉSìÎxÔEë¹Y¦§vM€Y@ôðSäÌŽœ™è‰åå#6ݹpf#%ÊÏÚ;tíyA&w©b¥‡OLìO#.&Âé?8L89ÞÝ“M7ã%†8Ž9Ê¡Ø-u#. ³/L]Ÿ¯\Õ¿%‚Ú.cxŽ¨b‰4&µ!Ò`,fÄlLM‚á#6˜‰Ó£p:5ÒHÄÄÌü¤Žd‘A¶¢„Hø0Ì‘Áö4EqT¡ªk&§xÝÛ3ÅÖè)ïe£(Ã1YÚ`¢ÇŽ6!Ý]ÛÑ.$#. €àT–0%DH!¹±Z¸å9Ç´ÒQ PP4ˆwŒšÈi8,ÑHŠê%Fl!6aeÈ0à\*hÛiD5Üè8hé™–*¢P ƒK#6¦æÃ@ÀÀALCÄD‘6D“áS*¸è†wB]èÊ4,¨6"ŠwA³½ò3bà‡"¨)ØÙ˜Òi½‘ƒ†ûøU‘’«Hªå¶ÎMlEÜ 1 ˆÃØ#6š6v4#60gSP#.F†¸H¶WU%0MEó& 83ÐíÄ„3œmÂt대ÉHrºâÄ¢ œBB¢ê™•¯3[Ò^6¼¯_#.óˆ±c%%¾²yyôŸCk£U@Ê(¨ˆ°×¦Ž½Èš“n‡£¤ž±°f÷ò C°5#6,"B@MbªPê ‡á¿öôl½€Èë:½u‹yÑ\ƒ²íšeX§—ëïk—°Õy+'J#.?Ö>£MÃ܇)üŽøx¨âÙàm&-£™×B¸kMµ7ⓃIfŒsQ‡fE°Šë»ºf$‘$#6DXÍf¿šY¯§·š#%ó×W%U®ÔEüðMÑÎ^hÂò¨I#%ê¯JS°x|k0ÂJh(¨Gf‚uaÐÆzífˆ?“nôÓ'àÒ«„¨“¹*3#¼¹R'W((q#HªÕÝe’iŽè3³%<ªÙ쪌JÅrØãz#ÈÄ6¹–7oSñkJêF9ÌTÍ@Z¿0TV(NAqKìÍËVè+ÈämÎÜa·4}o{ž«;Ù¬å¦66”˜µ²¶[[:ÁÖ®¿¢>¡DHª{hˇà}*s9e¾Dʳ:¯«d{÷ÅT°3ñD#.¶CvN]GÃL—@SÞ¡#%4„Ž©C»¶Çá^Wf“·DîΗZ²º÷áâÔRñ7p#6ªVŒˆÚX"kÕáË»[Í$2%ñ5;Îéo7v†È®òï^vÉåÒ¹Å×RÉ#6A‘Ü0@4Ћ5*ˆF ´8iQ¡øé3 iŽöGZK(4‡]-ÌUDÖ7v)<´MF¢¡ÇgVƒ#34u*ÌÙbQ „GFùèë쯷»È!ð†<ýZ÷H|ÁpDäA#%¦Ï¡¦3ƒõ†j‘¬¼•dú.ÖÚ1<#P„$xîamˆ…ÁV†ŠÄ®ÉÉL+ÃàÀØšÁ$p ¶”(“Ÿ(7'§¨&µ9w²#6nc±ì³êžlæ!ÜúìçÙ#.:ª&Œª§LYUå™ùû¦ƒ÷×!ŸG´šÈ(|#%ÉÂmr,A-Ùa…NKÎMN8ÓR0;»n[5'Ã)Lª×³¨^b>%¹C3é8]6±uLDÔ`X€ B!:©O¬i悘ç³n%Ú’"öa\ÐØq¥ÑÃK×è‘£ñLôQ#.i‹1Ç¿†0ÚÉ0µö~tm6V,ŒV"$ú‰¬á†sÈkÐÒŒ˜Ê¸ öò…0'º)HH2DO¢"öµ”ZÍiHJÔߪXDVBEGE„ìE—„0cUÆ„e5@Àm©n\0P‰`„M`¯8#%ì#%IîËÑ•…*Bé—øahš<2½Æ¦Z\ÜìŠÈ¤°ƒ² H’€:¯ƒäßbš•X1¿8Íõ#6 #%HŠ’*¾ß–[·|4ÅO4oÕå}#›9†Ñth¼g#.`±Þ»Ž´Å¸3©Õñ*\žY9)h„¢‡#65½P4a£ePM£2X’l+%Ê«EE¨íD‰‘·LNA‰GUÕÂë×yåmÊ^¶²VÌÝv¬V`•%šQV@IÅN†ýWPË3:È$#.EG^2S4#%Ü¥…¤M—2,Às4µ§âPNÞ0:=G§‹Í,0!&*Ç·§ä=ÔÌÃaÂօз;sHc²iÞfÍŸŽtê1ëa°SàßÚ½&ñØÒ 7!,HÈ#%—5ëÃ@ä%驾EsRÏr¢Íýï_kWÏ:ó¶¶*'õÍn¥‰†šHƒ[ïh4×·¢`wAuuÚá#.,‡q89 ‰JþÏáÃ;«¯³‡3€¯ÅO8~kÔ1 Ü$9Ê%¯«y£6ŠZhI¥EG!‘+Ž]£fvßæoíÕ˜8”™`±Òùn¸¿¾¶^Óötš†Y[ÕôãBa»Ö›iAà~_X>ÈH‚&³ì‚‰ÔXb„#.4Ñ©î„5ë%ïë!rKïl}9úÓì='p>ýÉÔ(Ž^7/$‰ˆ¤´š’jYiŒVIJ#6«bÖa´&ÄŠ*kLmµ&ߺÑZ¸Îk¡£Y³k‡s¼ßÛ<“ÕXªå‘˜ûµ£aÞ¢þs”Ôæ: ÇÅ#.#.¾®¾Š+Ÿ˜±;öòÀMI@\Ùô‘²z°/‡aÙ°nâ#%žÕNÑ=ǾꉑäKÁŸFzùÂFC?6ëñ(ŽukµÃ Ž…XE{šJ•ƒ9&ärÔP|üi¶ŠöÔä4Ø-J[hyªÃ´ƒmše;“;ð´#.MÓIÓqA’q«)\»’ÄùÑe®qÄAäÒå˜CDƒ8:ß 7$™l·}uf¶™ÛŽ6c„6ZAI¡%!pj[´-ÑÅ[åÍü¬+cTdë‹ÔçL¨Í¶§fˆÜ€¤#%PYl±’Ú!VYCrhM#.ÑÞîôÅN‚¡êÐ0LoäÅ£JÔ1”ÊȲnÅF´ýJÖ†bÕk#.¥ø?{5²b]%OZZl}Æ0 ¸äE!òó÷΀Ù,"÷•Ég4 )@w}* uAÔ ²$ALpIF wIÏž/ƒ©‰tÊð¨A°O€!##Æ Žj‚U6¼ãÕt±ð<4O‡ ¢+ÙÚt÷ a‚ŠÛ‹L;ÏX#6ɨF §ßæý#%oÙ”1d”@Ç!”ƒ Tñ{õŸWnÿWòmî'y‰üµÍŒó6@gö8A°ÄZ‘´³0?yüä<vuøl¼ôªÄfPx"%7³0Ü'´ws7uÿê"çð’Žš¾ÞöÝ—fÇÓ500.=Ô–l×Àþê:;Xæ¾·ÞQPñã@?LSa#%G1×#6ÜÕAf˧f¡€Xùè£C&3#ÅÔlDhÒKºº #.‘¦WÑÞaû]Û\«›œõæóÏ]ų„†É”Sd5Q‰y7s]¼‹&ÆѶñs\·Æ+»·‹yåv•¤å[y“oÑ[¶Ís!ÅSõ$S`6™mÙó÷þŠ#6*¾%|}¶<L³=AÞIYªa<Í>›é#%Å<Ô®Êû>Ê,U!¬Ûf,ÚÍ´õµ¿5÷øµj÷û"Ñ-62)E’4²’©³jü¿tj7ßÕ~îú#. £D1SQ¶*¥)JÚüz§h’—µ¼'Ì‹x„"#%Z"göBRÖÃÖ1ȸ0ćÐ0–HrHNTŠ$è>ä#.D1¦î¹²)±¬-f¬Q¬lÈÅc+™E²Á‰ëÎò*ˆ„(@Š'](Š]ê4{qM_îòýé©çÜOsèùfaב$ðWÀ šõ‡#%£'¸}—ËeL|ó*j¹#6ý{.³ƒæɃxv÷’,!Ú°3œ…ó˜ÈIÀôªyæ8öÑUãoË{Ø,E˜E'F™ñßt €ž&ny¤À˜ÞB Q¶Äb‘püÈt‡î"€Û@ÖôTÖ¡ódH˜E9§¹Z€¡ ½B!JŠS˜Újh(‘"jKNíÒkTÛRiIª[R’Tšl‘‘@njm¯a0¨ ÿNR‚(”+Ô\/*SÞR¬‹e¬Ë/n£.žÃgUPû£ãÓdtÎ=v£9é.k¯Ni`MÄljh™ú([;³RÑÖ*È5õXV²Oµ8Ð"Å…k°ÞÚ4ì#.y@^K?ÞÑïW-ï%ë¼lcúé#nìˆzK™š!ö¦ ¸X÷¢Ð‡§øþàêüûo–—çãvMÅ)¦%I²5¶1R`ÔFÕ&Û#6Z7æm¿+W‰°ÑZ(FAUdQ$ïî9ú»,ì–i!`¡P*…ˆû¤6+°0rBF"EHÒ(ÄÉ…Š)¢hlÚQ¬,#%”†ýh—Æ}qÏzíÙ/uÝJQþÀ!"kWš±mF- Ú”5F„«6X‹X«QšZÊßÓ[¯3†:zº›üYݽ´ªË%$ÜkìÇègÍ#6Ñü5–#êŒ1KŠL5VÊå˜F·ÓLðкùÀÆ—àIô÷$È«miGlý/$²ÙÛ3"‹*hc<jÖ†Óƒý90Š(„8AK"…Ð2Ú·Én³I©¢•-ë6ÛNìµÝÙ^5æòµÕ6‹&·¥\•ówY›³2«®nÚŠ¹Ú‚KdYµywcMk»«»¤ÙRTÈ”ØÖóº·škΨ¬$IS‚•a!ƒHˆ6Ôb£cÞMi„^›×—j”ÒÙMI–UéZêÞuuçj¼lm(Êe¬¶R×Ü·k»tYc)&A¢aˆ£ÑN™¡àΓ#%É#6ÛÐqV„m—3ø{äš‚è5H±dÜ>²øQ¶EF*‚d‚ÈÇ#%ª@ Sã’¡¬þ¤Ê[Fh¥È“(1P¹{äÁ5×\É&§4)DÚ›7Õ¥GôØ46àYêÓ’äö¢ìþ3OêÁ `>¸ˆ¯pÏ?~ðøfŒEº#6ݧ¾÷#R÷fìˆfKËÇXZ¦#.Yë9¾îã~07"ß^GÏõ–™“¤M#.UJœmG/¤lír$a®îB™1ßèSJë@ë#³o(ƒ$QGÊ)ļ.#.õêLo.Eä`%Áæ1‰¢GP5¦Õñóç/ml·p†B#%|²$Ÿxk‘(#6™(iι8>J¦ç¸iߎ„>ÔÒ9åÈt(a…-Q¢Þ–3oÐÕùUbÙ›õ`|¢‡B hÚ)I¨i¦×£|#.âöA¯qØÔùKŒ˜5Ó"÷gëtód›±BKûòVµw¬¢ö|jë‚P¤Ot~ÃìJ'`O8)¹A°.F´'RK`¨¤,n#¨f4äÈlƽQÁ€ÑÔÂ!³n¶`¢úC#’ØÇwšÆ½!ȱ|*逊DŘaM ‚¶”FŠ›ÕÕšWáœuzC<óËû²œkGG ŽÄêîéÕ‚]Üãjþ?70ÚEy£ÉZ$£Y¨Ï¨ÑÐǽÁäuMmÐ/÷Ð…ÄZè8@¾?jã‚8“ù‹ç¯fƒYÁêF4dýŒ—-DEC=µvNÅ|ªÖÞm¨:ƒ´Pi\Xì"ð#.%t¢#‹Ì¸ÝѨU׎1Qߪ…@ãƒ[8AÀÓ Êö;÷—çKŽ¤B4Dbžƒ:[nXŠ#.édRS(´@Ír:†‘ä|Xr×c'pždl€A±ï47mà¶Ô~¤…f1Þ“‚BG¸Ô_h½GCˆô"W¾s;/~†D#.îfYÚÛÊÚ{aµÝ×oÊ÷}þëiäk×í¸*‰>ÜD-(?„LþdÓ*z©Q£fÜ@ ݨþQD¢¸ß³Ñ2Tm`ô=åWì#%ÇßwïØÁ²>uLä‘™›™˜ÿl#%#%b#%?›Ùÿs¿ñõÿ¿ü¿éýŸôÿ¹íÿóÿûýíÿóîÿ/óÿëþ_òÿHþ]º>_¿ýÙ|¾ß÷Oþÿëëÿ‡þ?ðøÿãáÿ~#ü¿ãÿ/óþïü?ùÙÿ‡ú|åÿ/ôÿ=¸G£üúÿÓËæùGô_ôêüß_Õ¥P÷SÿLBÄ?iþ,ϳüêÈDÊy;"™e\?·ûäE7¨;ˆ‹‡©sÁþ±L@ZüÄm4 þË#%öªŠÂ)40)*ÿšþþǹÝÔI I™ŸF·ÊÙVôh #%âQ³ßÌôjB»M¢Ï¡E˜SYˆ8ˆÛ,×gØ+,€ƒþûö¦C¦Ò‚æ¨x¶æñ#.Öâ×ýÀä÷² ƒ¢îÇ‘^A‰:Õó»0ÉæfØÝ@„#Ì%*ãþU•Êlé·üiÙ\¾ÅŸÙ»Æøyö¡ÃÉvǧ‡¿„eÔ«jÿ݆떅¥&P"ƾrÿïUËrž#.ð7Ï_û«u”a:X˜„Ès+u‘ž#6Ù™ýþ.ÀÀ7µ*jH†þ”¦xii”Qš]d*ši‚—eaËœTÁŒ0ÙÿŠeTñhÞ¶ñïÕ¥ƒpvŒîm©TœYnœ44¼BÒÙl`…ÕÁËsO6e7ª¨5mÚ8øq훓Mmƶ9T¨¨ëÌFdMmT˜I†qx“Ý,ØY.䃆5¨mõyõx*ŧ7ŠFÎîs¢kCºFêâ¬#y{¢ï4êÈh2ùÃá’f´Øäì¹a²…qS1a ïH²F#¸yí‹ÈdŒ¸ÐŽH?F:oÌÌü4ÃÃ)×Цë^EúîÞK¨ŒëN‘±£\-˜ÑNàS4'>:`«é”ßw´ÿJ±·ý4#.pó‹À@ÄžGßF;tk5SÞ’æ ш넞‰‡ gà w#6Hp,†#6¹^>`L³¬5¬R Eò™„ "IEq¸Ov˜]œ.fB¦æ<õÙ3PEATV*AB“9wt¸¦cß·Y˜ÌÊ2 ¯kº\69‰¤U¥€ >È!<NÁ(¿K61$@ 0P’2îHqw”;ó\¨xƒH1Mï}Ÿlı38bÿûKuÊÉ€ž–¬Æ\¬îÙ ,F#6"`–ºT70]#.úwóbs„ ‰‚$LjÄÔJƒlkY¥3[FÔm’¨ÌÒDÅ…‘S_EÛWZªø|:Ö¾¸‚:?àE3€ÿt#%¿]Tµi¸#I(¼co¦üÛªözÜíSlÖÕïéñ#%Mþ}OÒÂFA~©o;|mq>§^²æF4?ícS9öɬצåU1Š"ü«Ç»'_û6Ä+W] 扄8uîKºƒxteæ߶}C3¿¾z„g,r.;À}²“½©½Ç0À×–fÜT9†P^]*zgâ_§Æ³ò8ÎŽQHÔ¼=‰ #6@C&9›Ò’#.³QÁOª‹#.D'ÒáG½\áUò»µŠšP®›CGݼòöœ²áž˜¾¦ÕÍ-ø¾v챯¤ƒ[êk˜Ø´|nmcãm½(ÕìµoSEªMAj½óZåUìÞÝy»¶ë€]#6.æäQ‰#. ©‘’| P?ç!xnãdN2Apã'PQ±Ôí±s¼p]º¡f!‘OVݤ—¿š7áUÁbÇá*J9…¨H{ÿî°÷cÍÔ¼(5É@$fáÌR¿æû'wØÊ=<ÑÜŽ§ÛÈ_.EúØqPôÇX¡ÿ’#"‹(]¨î;µ#D”)Qb(ÁmLÍtÖêÕÙjûËf¤ÅÍh é! H¤€ £þßÃ3ÏëÿÏئ¨¤"Žþ~˜™áã(ø{fYr0Ûm‹CòNÀ.!xN¦ çå,A‘<?à“‡áÿ¨ —ýþ&^â|YY?÷ùB-Þ6áÍtçüŠ¤ÿÿ&EÐý hNí'»ÿ›cÿ`[Óúl¿‰·@³¼%ËAú¼Oìÿ»æÿÜíe÷øCâ.ÃGéʽ1ÍÀ‡ê'¼waÕBžÏ+“Üã!Éä,—yý>^#y;³9ø¾Šÿѵú½gþs(ž?l-A!ñÿÔ¥ÝóG#%‰#%(ö#6”CÛ¢ OùI[¥BY=‹”+¨›4ÃÔkéýÕsø]¯üžB_Fœ*ÀN#.ÿ6pŸñäÑÎJíâqTE3‚o¦«…ÆY̘wÈâfÓ§m3 ÊÊÇðç,Má†/-„±¿Ñ8¨–¼§0aw<}8fJÃ$É o§‚ì]aðx`HŠ$Y@zõnÒ=®Ù i„ÿ~éŽ1éÿµ^#.Jz(ÎLPÛæcè–ªÆ"ÿ“â|xã‡ü†œgê?I¼Ëò~ÿð"‰ÿü]ÉáB@ä&öÐ +#BZh91AY&SY#*s†#)Êwÿÿ°#)Pÿÿÿÿÿÿÿÿÿÿÿeà(Â(€0Í0çŒb¼{¤Æ@#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)÷Ý×}=y”£îâìl=½Á½µ«C(4šÓ··ÞÓÞ³ è>ò·›lÙb#îÒi÷·5ï;µ‘zë´hm2_<>ûåÁ;íëo9tz—QRQ^îë{†íÞÖZS»vÏvn«Vá¤|óÏKz×Ùï–÷G)Ú¦=Û×;ï¯{<w`dñï‹koÃvànû¼âïsœ`Ð#):ÐÐs»#)<t#+0ßf‚x¶{·°z{w´½Üô·ßo°i #)ãZ=2zŠ‡ªÐR‚Û QJR°wc€PP¡TR‚÷¸)QU"-€4S×@êUBŠéZì¶óæö÷]ÕÙöÜojÏoxÛÍÕ©3#*TThm2*Uš·Üõ;h÷Þäðè;ÍÙvڵϷ^½Ý‡<9›»½Þ³íóæö=ؾ¾µõíŸv·sm»]Mïé=ªc¹»åîÞ÷×ÑヒÚöÎŽì¦J%FP!RAÖ´ÞÍÛ+nºÝŽžïw¢öí¼ö÷½çÊëƒZWMÆN°íœÙH’Švó”ÊzÐ #+UD½Øg è¼Ü%/lï{sÕØ[m{ƒJ>Ü#*ß{_ÜÏ}Þ”H#)´´Åmk®úoEË#)%Þ°rM€o¼xyæôìo‘ͽìô-nŸ}ç½ç_uÁêñק ›íÊ£Ó^ÊÔpiÒù›¸w‹>o¾¼ënîÜÛ—tÕÝÚnúÎÞåØúæ÷%³çjuÚî^Ý^¯=Ã=·×wΥؒlÕ4k#*mõîôï°û³Å×-³n肵Ñé«fî¸ïmÛçÔR›^îí\kS7Ϲ¾¼ï¶;ìÛžöí÷==k{‰½—R¾÷uëc_YGgŽÛÝ×uõÏ=é¾D#)7Ó—Ï;Ð.|ÃîðhX:©E€Û!°³#+æËvrT[뒽ݗ`vèí= ¼Ç»Téו{¶Ø®•SŽºžÞ=æR-×m½GBë5¦ÛÛ#)#)žç]¨#)[Ü^¯sÝzyìönñ¤æ½ëÞ^Eî;Ó²Þöˆò½ÝÒjtºŸ{¸|˜öΈ…O‰®:"öàóÞÇ›^ûg¹¹ÓÀ)W£Ò•íÝ0÷·W¨™ƒ¯rxiôú£ï§AÝk뵇>ÛžÇÞ³3înï7ß;›í»»»xît°Ûíƒî÷yîínŽÇ½»ŽàæÛb³×E“`Ø3EhÞÏYÛ£)꧞¶®‚¶Á²©Ïê•Øì/<7}µÚÞ}ÎÀ^ÕªŸ\zR}ÝöôèéÝÑÆ•ÝmÆ#+»MÚÕÑÙÕ÷îw9ÚÏs#+ûk/a½w»¹Åëvî4×J¬”èÐíã2]ØÞ¼èåLêðn·T;PãG{Îò÷Ÿ}®À{[ÈÈ#*ŽYµm¡::#)J¸ò·›]z›ì÷¹ÀØP(¥Ù·{”ó»[lì\nòu—[P¦UU–÷M—wb–ëÀH¢ÝµÔífí¥F»¶éî4÷¹.Æ4î7zu"ïXÛzÒ9s×Öí|CÞùöÛÖ`OcàB=±š=×Ýâ'·›´P¶½í½™uÝÏ}Ï}t:§Ïg5í¥`wwn:xº½áo;ÎŽZ¾5ñw#*4@#)™#)@M#)&L#)O%4Ò4ÓÒ#)hõFj=Ó) B&4Äñ1SÄô“SÙ&SÒz™='¨4#)#)Ð#)#)#)H„!4C@#+i¦$Ÿê¦ñ$üG¢žž¨õÊF#*¨Ó@#)#)h#)#)#)IꔑÒF“MOj&¦ôô‡¨mOSj#)é@úˆ=@#)#)#)#)#)’€ #)L€˜M ¦2##)Ô4É¢z&(Ð#)#)4#)#*#)ÐI¨ˆ ‚hhCL©²O%<ÔOjŸ”ÊhõzOG¨€2#)#)#*#)òÿúF«]rOã9µ©7ôjµ×ñÕkõêµÞÙ‰%ˆ0S=êµ×dÕi“yU®»h¬ª“ᶒ¢ £""à1=gÍ$=¿1ðÔc#+{Ø xƒñ™‡):µ*±„©æe[Ý\Uz™VXÅbŲ‘Cóš¼é^õµp® :Á]ªðˆ„‚4‰½¡PÝ*_vü6¶:U4ËÇ\\¬=:|\QurõŒ<cª-ÞžbqX|DÝã =à”Ã&ýýºþb'¸¶Óm–Ó*¬®î¥U¥µX¶™V5U‹jŠ¨ÛkXѵE¥µ½÷Y5µW³Z¢ñ#)‘±=‘Rˆ©a*(XYJ#)H•ÕV›kj«~#*m[j™“!d£3PÓ4ÙF •h™š„K#)š”£J)¶“aDÍdš FKSQª6#*hÍ%“I¢Z!#*FJDÚSA Ñ©cCLYShÑ›%¢ÔE K-4ÒÚ bZCf‚`Ê33Ô‘¨ÔZƒ$ÐiMÂ(Mš4” †‹,´Œ`E)´ji²%´Ûk+[F˜ÑK™3!4D˜2ɶ›m4ÚÔ”l¦Zkc-M¶eJZL“1‘f´ÄÑd¢‚™l‰B!Qf‘´R`4TH!`ؤلf•Jb1„($D‘-ˆÉ#jDÈF–"fhd‘1J0Ì‚–VX31"’ÈÄRɬ›,Q0‹"É–R4m)¦ ±%&EDš2hi™1 ©(Ò%¤+ ¦Æ‰"ÄQ3)iS0Jd†lD˜¦†fÁ0±ÒlK+E€I)65ZK‘&¢D£AŠ& ÀJd”Â0™Be$¤ÔY›5„‚“RD“ˆKJl!E‚Ø€“Y”R6d”M”È؉›")FlŒÁ2©¦$„YJa³Q`,i¦†Æ"5+%’6(”R"šIˆ&¤a³LcIˆBI©%”e5LÒ6Š¦š(šÔ‰–Pi6FB1d¤Èš ¦R%FŒÂfÒ…&5Á™H$$Rf0"Å‚%’a1A$Ê™š³m”¶%3!©Ä)›+(QP‘E"“$›$cc*i™¢ÅˆÊHRf`SŒ¬Úa-ˆŒII¦¦M3 ¤1”E!³DTØ¢ÍJ”¤¦ŠCddÆLŠm!E’1J2’ˆ¢ÅQI&‰$Ò›F“Q¤H¡¦6ÄcA2¢Í2›A£c0ešL£cH™2 ”Ù)LRˆ„,Ó!-fË ¶JÉ`2Hi,˜ÈjŠ ¨$MaH“!ŒÄƒÊ#*IcQf‘(š1¥4 Qja¡&e&(Ë&ÈŒ¦ ³F”ÆÔÉ”ˆÑ*´ÛkF ”&¦hÊdbiE$̈ŠÐjSl#+Y¢ÌRiH–YSfˆ¨´ÊÂÙF$(¦ÄB”DÒ!BSkùM®ˆJ›ÒÛ¢ Œ1´QŠØŠ“)M5©&#*-!j6²MFÖDa¨i˜Ê2FÊ`%Iȃ134Ëd¶Š1))j6¢´dÓ ©¬Å‹S-,É•&ÐTl«HÑSe“,Š¥RÉS#+lØÌfjØÑdElšedJ™e6²U–ÊR–d[dh”¨±Me‰-!’–¢Õª1!¨Õ£%AF«&²QZŠÅEH”kDÑ$m‹´bÅj6hÚÆÀZL˜Q‚ªL¢M1´É4-ˆ¶4m15Œ[$XÛDµRÖ²ÅC!2™©“D•#4ÚFÅ’+jU›c¦Ò©e*fË#+ÔÒ"5´ªY”µ54lBÚS)¬±a²É¬¬²²ÌPPÊ$HE"ņi01$Ch4&1#+[F¨J%Eš¢²M4l™4”Y#*–)ÙH6DVYJQ”ÌJ”lÓ4AE¢Ó$&Fm6“ccd)„SL‰#+1¢’‹#*1$dÔ#!4LÔQ¢JdR–P5BTÀ4˜³ J™²Bj-"ŒÍ¨ÊQ³)$£ hÈR²›b"˜m&ˆÆ¤Ù,"‚ÉXBŒ–Š†bM,ÊTÊJYJÌ2V0‚ËH؆5"ÔjšÑš"¡£R”›#*1RÆÙ”–£@‘“ LdÄØ’¤©2…)¦6I‰šÂRš`Û0Ù¤²lf“E˜‰¦”RZdmY¦…LÑ)aD‰!Ê2‚!“F©¤lQ¢³I²S±1II £h¶£Q¨£ŒZ3-†“#*$š,™¥FÅZ#+Í6,†e$1²RÊJ™m@F±ª5Dš(L³TPX ‚MIª5L1Æ©JXÚ#*)¦Y#+(Ñ$U2KDª,EˆÊVÙ(ª-&‹&ˆ¦eRŠC2Q%M‰DL«Q°h©‘±dÔmSiE²d¬[ZLRVSl‰l›CD©%2¥E„ ƒcQ²Q²h“ˆÛ$Hj)&`ƒVdÁ …F”Ù›Mcj*edÛ24…4“QHEQ(Z’±l¤–6LE(#Iƒ$H–Z6†Z‰-¨«J6“["Z±©(±H¦„ȦFdf4Ԉġ©›$ÍhŠ¤±m’¥•dÖ‰›R-DlldŠ¨¦U4£hØÛc©3J!,ÖV%¨ÅbÍ*6¢6•*)JÒŒÅB4ÒY@Ñ¢ÁTi6ƱfT‘ZJزm”™Z4kÓŠLhÚ6¶ÙmX¤L¨TLIDb¡€D†ÒM©†2M´m‹lZf£ÖZJške,›jjm¶¤ÓV&Œ¢ˆHJ(6ÍI©™›,ÔEbI2’dFF#!d¶‹2I4ßû¿ý€¾ï•ÿÇ[›¿í½3þS܈ê²*Jaêý¨‡’R,õ±DB{$e±j©'†X;ë#+ÿ¶ƒ#¾?ÀÂð $I6|'=vßÐó±½9¿OéëÒÞ™MÜrª=ÖÛ2'ù_ñäYLâväPCo²³¸E‡G±.)$M·¹‰Ã>f‰ÏñU#ÿGùƒ?ù~{Z?òJµÊâ/e¸+(‘ ¾Ç4Ñþ{ÜàõJ°Ãàvûño»¡\àÆT5¶ÃÃ5bÓ¶'$ñ–¿‹#*R(¢†É8¡·'%ˆÁýõ»fÍ……£ÈÌ`I¥Û"lÆLhƒcõhÉÉ£®ÉÞwHLo\»ÓÒ¯Rñ®YÎå—ùŽ¼üŽ³9«I±u¨S «Åš&U;1¥Ì¡LU)„¤ŠI1H°ÞíºP®‘€‰‡[•xó®ØeñnTŒÞ›š4V´"ÁS8͆@Ö÷.9_ûXk#)àá#*ó)ŒX)Ú¦ºòhñËïv¹IlF1IùÿޢܼYÜñzf´µÕ±S)´)þY*̶Ñ"Ìi^©@Ã"‘ÆnDçD”á÷éWŠ÷†¨XœÐ¶(D%òþgËý×F[1ÝD!²Ð9³$¼nä ÛâÄÕ–F4›iêY#+“qÆA³¸¦W6éd5sn¼v$ØØŸ]×ÊmxÚ,EAoÏÝO§¯ÖW‹ì[‘L¯‹‘mËÿ9 ƒòxë#Q¶~=¼¨"!çç˜Añ´òe¨Rh˜rÆê Š°XOUÔ†Ž\º2MÊM£;®QéÊ7Õß~z»»\¯nò^+›Ý\Ѩ8šë•ÞwcrŽ•Ë™(Ë®é$s™cé¢KÿTþÒS–ArÈ6ÓbÈD‘a#+hg†ØËËV,öQJ>´š~-Ò¶í.¾ofd4x ‰_îQI»)饖«PÅCüè`ìÀQ³ûVá%.x×Ûˆs@ÇG㡆Î[cÌÉ‘º!$Āšê‹|Ù{kädu¶ï\’(3)£^··vj¼1»kÇóÙg¡øaN#“³U›x“:8XMÙ†ØFPÑÉýðû&¤Ï<§^¹ŠÍwÔPCàÊANME)ðëöߣž—ÀÁîNÝÚÝ95ó¨RDUH„ä,Œ©Ü›¦tj#+M4+É1e)Õ–Ód|h*ô¡ã¡e³,YI#+N*}±ÆyÞ>×åŠUË"õÖ“UO¯pi)O$WàÊø„ÓéûrH›†å¿ÜÃ××oL[1þ[¬3&)}]uøúèÈÇCßAbæ¶Ä^¨ŒJFmǶ®Aâp¶˜=8fwŠ^w0ø²µð¯õ{(ÊšÒ…A@P#+m–™5ÆÕ͵þ‡ì÷’>·S6½•^41XýÞ?¶Ã³4E#T™Š¾çÇû/6½5ŒcšŠO‡væn#)(5Qžë#)£6Ïi‘ÞÒç²à”\×-±zï˽z¼ˆFiVJ‡µÜ±h¯Ü–ëÝØü½qQ¡7Ó׿ÃÖñO~‹FÉ·ÐûÍãƒIo±Ð¨°R:§eè!íÅx鵘OrTÅB™)¦jKGñ¨ræPaŒLÖ¶jϽ$ô¥+?+³òu·edÛð÷DvU»½Ð´’ºp²ðÑ$XS†Í²]#ICœ›HÕsÖžM]·J)”"’ê)Ù¥®úµ‘¿Zž»øVS“–^ÖX1a#+p(¡M˜j!i«L‚$¦õœï0J»*‘£ÃDbá‹MZûÒ—ucg²~,'~6ÅQXXy²Ö"<4º:QS’xU<ßb_3²Šå|ìá®Ñ3VoÎMëËX¸„³ú¡ó犈rõWŽ`cï su‹$ß²)kG›AÃFš5Ú”ØdÌ(Ì)¿/s1õ÷´óMƒLOýæoÛòõ€m¡¹ÄîÃÏ$|2Ì¥@Œ¡ïj"*i_mQû{»{p}X‡aùRi“Ç¿WìѤ-´#*©ÉÍ{ˆiçºRJñ¥ÉnCMÏ…‡×–ìƒ1nÇ#+ÈÁ61K¦Â¹vB9&Ò#*Xe5úèÙÆÔyÕ:”éUáCÃÙs“…“GÚ…$ä¾»Z×qZ‡#+9‘JQo`·ÐéX¹Oq\‹åN¨Yœñ'mg2||Ý#ïӌ֒ÈþöÜ\KÉŒP6@{"i:O8–ƒhºQXPüŽÑlŒÕ-ù‰±y²…èú)<!ÌÉŽ“©1¥óN”8žrâ÷ÜÉ~NZ½ãpödn®$g¸esËZ=M]¨ò}Ê‘¡ýs‘ë¾ð ý®07¨UX¾k”‰Ó¾vT9¥ 0àD*Ó?<cµ4fé\[Š¥FyÆõ#+I#+CPXÍÉg½Cž¹Ó›O~™/gÊÊÉE0F:E&©¯}Ùž•Èk^œáÓ†à~NX#*É`8FW_vSr(´è=Fj/~wfgÔÑù²þ[TÎÅ<ytr ÃÛ´iK¸é$Ë'¾!réE·ã s7ïÂ41¯Å¢6¸gl&?H Ç×\â÷[¸áÕWÿ|-“–F·&Ú!z]ô\kÜíÝÆÃrÉêvµmŸ cmŽ~Óçh×0ôzíëµø˜ë²I",P>îaS×¥)F´ÚÇãš6¢#*΃`w©Dï ”T ÝfĈÎ(»W\ÞN0›“í!oǺêŽ3»º„hö4{:Œ#*ñ#+¸øM-;¹È2°âV¶ã[ÿr‹Ýf6Øq6>ݵXÙdȶ_»ï‹îfŸÕ¿'q¸v³™NúøS#Yöbùd6CTEMØßÛ'Õã×è×k7éÑT^D¤AèëEõtE¾§Ü®½wSæÜe÷º(A¢P¦3ë¹~-UKTWúXRŠ™ò¿éºü¿ý¶Ÿ/úñÕ%}ÍE}Ì?Bñ‰ó»o·®ñ`¢*~§no“=b~¹·ïùÚô¨ð»šUÈgóLZ{ŸvØìÅkêܬ’'ù5·Xýð黄—n#†9ìÄ<$’0 1,áÂ|Ý®>¸ÙL¶T¼å0$Õ4ÔºÓÒùÔBQ³”‰ü\†‡?‚л´£#m*ÒŠŒøÖ×'Ñ;0ÔO'IXÏÂb«´W·°äÅÏ ‚„|‚.aûâðXŒËO˜àLTÍéC]Íø Â`áGÙE"¢¿«4dfýš\}(¤Eàf¬F"‘daIήü´©¥Yç¥dÂ|wjŸfýs|4H¿mž#*A”çÇŒ0h´ë-+‡mènnΛÔrÛòy¯=×m`p¦;öš~—iÄë–Ó2§Ÿi>5Îe 2}–¥Ö™;v¦dMŸ¥¨ù&;ÝÅááÛã¸D;/[×0Êe¨ÌõÇàg#š<Ð/¶ŠGƒ5¸SÓ³k˜~ {bÂõ¢Ø ú’Y×…é¤DQQe0¨x~r‚Û{*©nf Øøg·sèÉð„kÃ#bÜ󼺟S½™!žPm¶³ìª¦ÞHóæ´éêÈã¼ÔVmò§w‹ˆ~W«5FA´?8.†¥úš6e*DF/:‘h®¾˜åʳ¢q³ý½hñáPÐmÔ‰lðaK–R‚±7gFTaes×^¾o#*‘Ò÷àbì±,¥7KªÊß\Ï«fÜËFá5L´*éù“÷9æ]L³u·óôŒ,aï?æÐÎÊÙu9s}r~û)|ݳ‹Ñ-Áð‡7ÒïŸgÖ"+EÅ3|E£[³±è'ÿyÂôî¹nó®[ñÊ[(/ËÂc#¤ÎMü¾5¸º;7„Œuv†kBoyùqòª#Š¤:¤*!šãü…žãß¡Ë¢¨@ÜÚì…2,Sæ\&¹X±@•E©¢$@Ájô°Œ÷SѦú%ѯ9Ar*¨[Úï›—á„9Ù…H±T›Ò9ÿ#*|PוÖ+ŽJÔemA×ÂŽûis…õWô9šÂÐä^å!UdT±Ä ¨q‘¹pöæ÷¹ûÔ¡)M¡_DÏO߇7•ª9;¤Ò™&)¡/)ãUE¸ëêYùL6”î8:<öp½wœõ¡íÑ+‰q°.~ŽÇäô°ô?“á߯•Æ´üwZ·ÙQgí¾YgoKR!e=Šcë—ê¥ÕÞºø“ŸÛ›oä¶Ú`èéΓq©+¥µ\”GÓkšvji7XÁÀœÔH0U4UtÁSfŸrƒD^8Ç·™üß˧oy±áóã=˜‚ÍO*÷9ßú1‰btE†WZ»»#*&5Oçׇ‡NzŽåÜ\패gÆv“Ç“:T‹³Z”()ÇÃ'Àu€®•\ð><¼û:è<&.Ò#*iEíƒÙϪEÙöP~úáPØë9*±‘18pÎóM‡µú0ôÞ¸Ätëm«Ž²HøŠ][ÑŸVµu”·ì©üûS)¡#*:þI)D}èá‘ÈU}g]_“lS#+´íŠ0ã×D¶ v>wNšÛb8mDzØÆó-¿òžx½8À}¦^©i»~8õß•ÒÁrõn;¸wCéÖƒ‹•O'Dô׶øæ1;ÜÂô˜x< •”փܛ÷*E''Ni<·HôD}¡*¼Áq½Él‚SìòÁ°Ýç¥víãsx±-¼kÁÈ##ÜÒlëËJÖñ‚†#+½sRõþö`ò4£Å™‚£"Ïæé›{ª¢rÆ6uTú®¸¾•ïÈnä²°<H¨ËÈvA²„‘ÂË»å6<8b?©š-íts¬PZk%‰|e… øçÆ…éJÓüj6J¸É8Mü„ÃËêëóñåf_~²eèt³"߈d¢/ø ¦¸ÆF=ªDŠaø3eëyB>Hý2Úú¶ÇHÅ‚”Î~·\¸n‰GKùTä·÷ç²¥Crh¥fí¨Ÿ,ù8Œæ`Û{åÌÂ3Ѹc0qÖ£QËãAdXŠ"ÏDôÐ+–j_Á¢Ü}•º¯ÅÐYŒ‚ØÏžÜ~Ú?OmŽ´v¼ážij§ØœÙŒNIiÏ_qßη÷§ow…Ò¡¿G8K‘åU¦ý€Ž›?dÈ~ó:Úâ—k†³Ržú¨v æ´•ÜÕ ì®Élßj9µú»¶Û0üÞ#)†éÁ3mpŒ¡-#+F,V!ý Œ©µ„\bŸ«¡G·5:¥3yÁ¢šfXøµÐO‹§LÜòbÛE\É:b£Em¤MÉå#LÄã¢Çtˆ”бÞîDk³Æ6Ñ<ë¤8™äû!û¬7ÍWËÝRçz-s½žÜºú%У$éÇü„Æc÷Íf"6½’.u¡ËùÙ^E¤J)*&h=Ø®9£¾…†Ž®ŠøÓXKa i†±å›C«Bâ¼hJüB¬lßÙlĨÁ¤Âè黌?bª<M`¨¨šÿI0^T€ðj#*Dæ¤GN6Ï6³YXÅlGÓg¤Ff¤¡J¢P±F^>¼Râ€ÉQUÕ)YSWŽÝÛ›Äír)qÕuÉ×YyõljÀbkxs#<ý°ØÀêòMüy°w2Ô¨tÔ°õõ±$ù#+’IrHŽR‡Uï‰NœŠ|ÿž.@˜ËîÕbX÷Ô}Á¾Ð#)A0#)A©»ù²¸n½/ò\FÄ÷ä°z(T 9hÝßçù„¿}¹¹F*y”i>+_\‹z.½Œþz™Òñ‡“Ò>“J÷j«Ä3ŠžPS„}4›&Q¶Ïhsy[ÝQ|£*S&; Ù4 ÙöP¡!šÌpëƒ\·„iÛrjÕø¶×‹£Œêh=~í9ÛqmõÞ"1R;ëj–QùkÎß;FfãPÙ@·OXý°Û#eÙ~E‚‰`©iF!Å1!ä8¥–,âò«ø]\ö=ƒ]ñ‡(úŒa‰¶›ÎqÊNŠy™Í¡HÀi/U#)Ãó|7?^•ºÊ#*0qˆ®ûm€|42eoæmÚàù>÷UÇͦ.óKdÔ}&ݪˆ%”côõB±Û=Šò«<©Ô÷œw?ú‹¥GŒZ«ô—‘g¹PD†ù¬da´ÌÍÈGŠÐ´#Næ¨j²=¾ù¬pH.]Ũsë]!ÛŒD}µ7Å×*´gJ¢ðýÞ)ÆÉãHg#U(+mV¼)¦UÎñÛl\*!"·Ž¬,•´¡c8ÔäÞÕ@á›^¾e\7ErûvóÐ-4ºD¬Û÷¹ŽÜšHü– `6ãˆn—#)ÛQ¡v“Øýå¥ÚOËqñ1¢°>ˆ¾÷¬Óº÷âI7ïÏ“ô£*a#>êfÜ0“Õ†v÷^øÍ)X+4£Í¦õøéxÓº¸*aO‰¿tl1Vc;ee`àã=xwËc£8É™AnËÌ‚NˆPÊNKYªƒnÃôÞÌŽZnt¥vföíöÒbäU'A#+ã“ÑK—£s3…ü‡%ʘ“, æþ"…ž'¸‘ðƒƒQ¼Õ({Û¹eÚ¶Ó‘K–ÏD@JÁÖEé½Æ̱#+hYʆS¹-Ø.6-sÊØá0ç(<1ÄìqÛ‹‚‚ȤÕ&X³5lS‰û9¿ž”;õµ¸\½Ü×ý—+=hË‘~-lÝù6ˆòYn{j(ŠRdW=Íå†QÓ1Ü+–Í׬¹´60æÕ«H’“¦Y.!\1Yƒ©m…„aÇk¹n²Ã¨íŮԸmÇ&mŠHIõ 'ó"š¿7îÄ”Ôh©2¬ŒªÅçªÁÀÄŽ¨ÆíŽ0€ù,êÐðÃûÈ…Í,|QØu11åGÕürßñߦZ]SÆ´Æ#*y[òöã¥O¦d‚@ˆÀøP%ÿÕGW–vT鎉?å‚z(x&gAaÉBC‡?öñæÙN\ø’¯vŸï#LlÇ܃Bí»@BØt´ÞRµùü2~WjÝÜÕ½›gºú@ßÆŽšÆTrÒûJ&ùæs/³KH–}ÒöDdü#+W•g}¥ÿQϪuææ:;bà-—Ô#K/+Ãì.tíçß`—zó}¸jÂv-ÓJ#ã|וF~šäŽz·EõÚ„ÝÏC›œñ´¯¬‚œõMUÃYÁ´}Ë £Ë–çp Ð.p~A¶™ºf—<ºO†–ëAí¾›õÝEã;4JbNC•F$öštÊ%Ú_6ŽÈÆ!š4«qŒ¡£=2´ÓL”:½c&Ü8Sm€¼¢E‘j9+r9¬»jµ„N@RfV9ý’ü¦ºìm\½¼Ü‰O†þ'ª*±Å?¸·íÕÛŽŠ±T19b7;·š®q?ÛBÚçõ0Öê+ýùHLoÝ鿤Dk÷Ý×»/!œpäE‰åzùÀð#þp$œ×#*[ÿºþïw¿¹Æˆ½ÓÈÀW§º®Ïæº.:üƒª=–°(q™¬wQ>±aUÞÀÝb‹}÷ão#)#)‡¿¼¾½‘r ŸŸõ`ÁM3#*¿»]#û\·)ÍlqŠðñ®ª£ú-,‘"»$‘æ.ÛqQÈq|"ŽIú“¬|üüã^›h‘nlÛ¨ðø5y½Ÿ#*GO³XÚ!ÝÅW·Õ;¢=¥:¾Í:Ñaˆ¼:ðA¹TX…ä^¡þ#*NàÁµîBrAH€ 'Óú®ãøZ.žïKbúá“¥ÝzÅñ×@;@2Ùì-`üþ¼¯‡rö/öåïѤ_R4#)ýôþÕ¼Ú¿ú>×!Öÿ‚‰fnÈB$Ãxw Hƒó>u7##)ýÛÖ0o2ÒíÚp½_Ùû#*Ïç„DuH??=¤»·Êï•ÝίH=²àPYc¸ô?ösÆËZkûÞÿVåþ3¸£]êd¹è¨‰.ò¦(æS#*ùòô³íÈסؔÌ5íM#*áÂ:“U¥Ø@´¶8~xŠf„—Ö#)4§_Li€ÆçyÁpŠÛ«à‡Éí•o¥—ü¿2 ÿ<B‹@ÄTUÙĎ΃ÛõdðFõ\1cW6Ú–_#*ªeŠËâžæä„É×þ)û}Ëçc0t?I¯üÂL–d@ƒ¼\€€¹ðF¼†µx‚˦©.º-.xÀÛ9¾}:ÇÕñyà0Ým™ÏñoÓçÀÚ¦ƒÓ›œ¢;íK³v‚5‡G|]ÑGÒSþggðÌè!…5Ñû,Û`æèþm×m¾¡ß(þ}÷ïâ£8U}=Æôîþ§9 „z#+è³q»WöÈÛïõúõj÷wÑuƈ;Zü¥VÂiõcœæ֣܈mÃë?L|‡Ö2l”EIHà4ŠÙé1ÄR¤ÁáëäaÑ-È¥ÔA%Y¢êÅ„*sÈ„œÙVÔ×FS›¡{üçÝËoîç³==œ¢Ž(ì6™$,Ñ‹Ì-'òd{¹Vàïd¿0–íÛõM‚TÁÖ“q{Ì*H(Ñé©"'CýÜ›3–kyEk?ÂÊ•ô½¬}¾’ÿ“ëÞa.|ý¨”m°ù;wÞ#*§Û>Æ2a™º&„0Ö›:|–½Ç—¿×0·ÈÎdYq÷™G¤·õéÁ0…¶xÔ«%[I(në½T$~W”ëãâ!>ë·hjI6õâ·Èò›7Ã#ôo.ÇöþË–…æå93O~6Fp“ö÷ñø+jf_´4 eÝd(K….ë?§2Íàóî«—åÚhfDjë0é[‡Ô ½µd˜šÛ>±TÆoí¬X[0ùçïñvý?ÅÌèî¶}QUÊ6”®ùq—>¥?½kÐ#Ìv%6g#{`,d’·>oÂ,§Ã´ùâOrbè5)ïÏëÁº×¦[Hä‡ØmÔ¯¤†°°ÐÃýôt’ÂÞD¼Í·òÏ–û6.ë¸w8wk3{eùÿ£–º¦Ýûu“UÇÒWKèÓõ"ÕpGÅxºáXÿƒÒÄ$Tèf~ÉË’e$uño†ï½„õ|cŸýÑ.9®ë߯Ìï0zr2}yÍ#‚˜}Åä#)8Ot¹ Q[ñ]ñYÔ.…´*}`#)ƒ™ÎHYrØ-ÞŠdpó¢:Ë_êíRzNša£ä¡#+#+Ï×øÝþk9&)Þ=#*¹c„CWdÑƬ!TÎYÜ—Çö^ópÁ™áûxFv<–ºGtIPï°EG`ˆÕWÕ˾íÓq—9á´‡'¸Líý’Kçƒ:(äBûd¡4Z L1á-õ®`,•õ‘hTƒƒ;±ß›‰Öø›ÍW÷½ék”AÁr4‘#©ß¬uš3_ÓÆùËéIÀííÛb˜ÊÀèæŽýêÉ([ŽýÃuN: ‡_5ï/ Z#*a{ÿ¶±c±Ä¯QLšÄ69øGׂtToËN7}F"í^ê=#°âˆ*B#+ íÃY9ŽõñÃàZX`¼P!¾N`XžÐPŠL“P°"Ó=ŸÖnžøß}ˆ6NÈFaתÍb(VÇBœiÅÂÂwÞº|þSLøÁ(‰ „)í#‰®ÉÖ»ÂF‘j4ˆ™¦Š[W~³bËaJˆ‹!F#™fWAB-(0¹\tÁÖ±¤s ÕñÅöëTäŽnšSû3‡–ºõyfl+¼F2¤E€ÄI&fûƒ3s—ž†Õ”'¾6‹Éü‡ù@Ú¤ßaEƒßNê¢o–³ãü+´ý_íWÅéö¹ÓÏæGßàT¦xìÆÞ^¸´‘òb‡À'ÝÀGp Æ b@1@00{Ëa(zÂÍÝ¢”âkˈ‰t†öÛ¡¡IÚì¨øbÂ÷‡—#*ê€FW´HqFYT_…"Ú,P~ÕµZI.3©Ku-(T:åE˜ZX¡2}uØ!;vÜØæïHÝïU®˜ça“=åjÖ†¡1‚sŽ©3#+½ÓQ}²•þcáJ7oQ÷HR'Eã¿9jàøÆâ÷y?šî’¸»…(=$yq¡”Õ#Qg»(^ɧ;B¶2Z!(ŒÀlæÁêNEäªé¹ö Õf˜ÚRμÛÑ:]yìë‡é£™8·<”X‡Hç΢‡ètny™PЙF”:ݼ!ª#*œ‡lÆB¥œI:¥š:è˦Â849‹œ½BÃœ‘ºØV%UfeÔ±oߣœ)vä fìƒG<ž\%Ž•ésÍÚ"”„Ž)Xæì:?õ(çؼÚ8ÅQ²C-nP°7/.#*KÓÕ/.—tÍ•ëÈtnª5<±VY6ú²‹žòö½x“ ð¹Â0±Q↸ñý¾¦Â)™ñ£<¾?~BóüÈ}•3ìSÑ;TëÕõ£ùöÒÓoA ü|*Ã(tô=5¸ƒ›¶Ís¾Ä'Ö¢…Îæ(#W2Ì4U>ÿjWzòòmç]„K×v»¼ó.×wu§ŽÈÌ`#G7²™) ,ѯ-q¡æКUtÚc{ù^}œßv3—ÊúN^÷’Ê–Â+H…$ƒ`Õ1U…1ƒlj'XÐ×Ë^¸,[fˆ©‘¦n8Ã#)H³ƒèrdCÀ5Û–âG·.+õ#*×J lÇ-$ #aÞ÷«ƒS—£‚MÝ8°Ê^M^ŸÌ÷Úïy³òȃ¾´^Ï´J¥Z¥òÀšH(\Xb·0í`CÔ[!‹±âë¾N'—¿r`?S…Ú¡Í-÷óÍ*#)ªÈ´ç#+¼dÈUÕ<ÞD\˜Œ¡ý¢í¡ßu{:k4ImÇ0PÔâFÇÃÁΉΧ—ÃÖï–ùE¬©¨úUÓäL#áÒêeVßI6µNu£°ìûúùÎJZv#*r_#c¾àó¡ó˜E±÷Û.Š ?FVÄÀX¸7é@ˆŸ!#)F¾8ÊF´rê͵óh^øEXìf£Mºâb£.Ôh»gºN^Þ¶5#*9ºÎëf)d¨n)Šl™„â„'xÐñXX’)œ²Þk0˜™‘¤$`Q1¬aX£@ÞѤK"ŒÀ4V—-,KX!3e¬° e…0Hóãö'aÇÇKêÇé±|»7è<à¤6M¹£%쮚½1jûŠóß¹¶4È£&ŒÚ(ÞÁ4d¶"†XTMPÄ—tæÝT±¡dáÑ"°+j±°1¶"Q„$@‘C(çmõ—c¿Ï3f—Ö-¯ßÓݶ#+_»[0¢³*¸ B¬¥yÖz%,è¦5`±|]“mH!–ëU#Ú€WÛ#)Rùᩬ€õÉ`ˆ–0ì@í~Ám'c۳̟ÆxO²ô߶ sþ‹tDèქ´{£^aˆ„¢eóoÑ©?Ÿ7srtZ&ìR¥º 5ýr§z´}~[to¡_(æ{4ö°qNø…Õ¼)½JÎÓí–Ún€…™©t,–ÜXë¬)b²KoS¾‡¥Ï/?ÓR嵩Ñ2´%–®Õ@*q°ÄÀÔ²ZÍñ﬒֤ûög`ùT½`‘Í=µVbž¡„$§ÞÂO<Â+v“8Y,á݇ß~¢&#+?½]°Dé©—„á¤Ú í7R0°µ'v†HDߺß(9Öï§øÛ0ó‘Ëý½^:^ɱ¹îË¿žá5~¼ÍfbU©Ü`z;Þl®—&z+šézµ#*s@¼û¸¬i£ÌkxMª’ˆ,V A…’ÐKsÄí#nø©.ÝŒ‘‰aÛ÷Žß±oý»0ðÆé¶;pCÇ`ìyQZ·(A+#)”ZÑı`Œ–ÏqþS™Žäœ._«OñÉÏ^BvB";3Ö)á¡HÏï|a¶Î¾šŠFâ¹VàÛû—Ù˜Óä^TNˆ¹ýÞ¹-˜%#)ÚLdCŽ&òx“VDVA£N”ÍL@ô2,*¥Ò 6²Jd :u¢Â€ë#`Ø)c¤QG¼É#*•$ÝCMþBà&¤Ò+J‡,µÆˆ£áÆ;§Î"7bøõÕ3^îÙïué{¼ÒöÆZU‘,q¶àëÐÐ;F¤ˆÇ¦a¢A‰±!ˆbÓ%Ù†òý/wîæ¡™¡7¸†ý±…×@ä¡»iÈ[dikðÛÂݼm¿ME1¤w.#+WÒ;K³<8G'Ž©¶nX¬HýÑ߈g(q5ÆL¯+•ÆÎEêRiü©4xÄriȦa‚<j™F?«ëzíc;›G·U¶‘ÜdRˬ´¶<>ôáBâ±£s¹‹©Áâ|ðß>~ÌpÇ6n)ɱèÇÈ.œÔmø@°nP’è$ºÜ°ÖõgW˜§€GÐÉK#+EŒÜpäRµþÛ–`é>#*L…F#+)Èý‹:×I±ˆO”>\¤zR«0«ñCµÊKÖI¤‹R×7‰ÙçɹœØ”äÛ›êT¨‹þ–¥—Žêí}ú½ãô6YÄŒOJ¦à÷¹9Uדÿ,}:î“&;`ÓÌ’ìø§6d]¸‹,öâÔß|?H-£AìuȹHƇ÷ª4¡Ú‚:¢¬ts¥ lV¹âé+Té‡F:Ä}W”½v}›ü˜Ût<ÅÎÂÖ™“‘ô”y#)Ð*¤\Òr¶ù7#•vÉm#)ü~¼ts»V̶4&`à×ùl5]UL«IŠ(Vj¡Š”1zõ«xk.iò&Û0âúžÙTn&éÁ9ëƒioé×XqÃÔxQ½Ÿ3’øJ§»`r'ã~Saÿ«0mp¼J%ŸlÅ//Lg•û>¶„#) “*Ä#ìM4×5~ø(LÄ´²Íà룫šÀ“siµÎ‡&ëæ§ ªÚËA7cÎ¥7þÐÕœ‹šN.-R6‰6£·5k¥’5phˆ’QVˆ©#*RK܉jºQc*¶CÊÁPDo°[H½Ëf#*„Ò†K_vjN¼¦fÖ¡Ìâ€Þ5¨5–iUÂeúNQ×8Ôèö'´öž°ž@ûP€w—AoÉ[¬ÌN=û媼ô[¿´¿ìòñJü¹Ñûž¯klñ|{øî>PÃ@¿K³J5Óth,šŒþ+¿^Ñû‡Ý§GGZ6¨¤!òªZS<ÐêYßÀÑÈ%øÃo_.ûú8#*^"ƒñíÅÖ?‰«j€þ©ð¦u·+•C ²q›_S~ܾ¹Þa?¿*—ÙVI|gâ^H¿žãñ–øóØܧŸq\f#*ep}‚”2¹ð<cùöCÍb´G7úA÷,ŽywŒ@–]ð_Ëð.ߟzuj#)Q!–^ñ¿Ãõíw…–HQ›žÛºú.OÍ^n¾·ýü-ŸgŽ_TÀßëÒýçå«GÓ³iûü~í®Í¢È7£®Œ`6Ÿ#*L=‹âêVAÀEƒ‹Â;Ü’G<Ûû½œ}'DŸDºáü'u±îúð CRzi æ#+¤#™T£0dbýÿZŒ0 ÷|ŸOÑ–ìv4[Æ#*?d˜ÔqXW7ž¡™œRë)·_Ôâ„F¤¹alÿgø»2#)Ghøí#)F±éñ2À¢“O?×ÛîÝ?˜#)R•PˆJ@J_ÏÕmϦö8q{Ǽ¨ñÃ;¦7õ…ç$HfG §3¢**9S°¨QlFzZ¦mIdÑ& 3eK‹ó8ÌMlUýjôvݨ£X5RŠh€TI\Ki_ب뷇¸Ý³ŸÞuêMæÜõ›“#*¿ü8å¸d¦¢èÓmïv …,ûï²æPXÁTR™*Š @I)R çWt/bkñéÊþþ¿SWì•r‹46„§lç·øìÔ„_—óq<ï“ð¬Bdšëêö™|›ŒVþånV5õ¿7øRÏÔÉ¢PÑ™.ð)92«ýšeAêUÑ#*·¤‘µÒŽò® (aÁTUS!<añëÐ?Ä~½Âó;hn£m6#ŒE,Â/‹8h+‘H(#*ŸÙêmmþÎ~ óºaÿ:Eë„úɃ*ÛW—÷î77£,Òüu/óÙïR”Î4ÿ!DºZÒ[0ÒV¤Þjê^þ?¨ãs8kV¾åýÇlV#*Ï)í,®GG%Äâ@?ÖŒQDY¬|¾4pǧï¿ì¾9XdRÎÛ(ñY¥•Tsg ŽCV"†ééKQˆ*¡cT+÷ýµÉâü‘ð @ý²¡C`ŽäÓzÃ"Š„MD¾íÁaƒ,~räd¿Ú[óxOÊ#(×ãîlYCq!7iCôD~Éh&©¤ÊŠ¼“÷Rä@pÁV“ôJd–͹ÿ±³†ŠÛe/oÊÂ7ǃáügËók£çúÏwåúþç~¸û¼¾¯«ë|=kæçLéí=0‡-§Û§¤jŒýCdÇøÝê¢igOŠöCñ]^¼%0î·¶5#WÐÆ núÚlÁm9ó…æ‡ÓVû·+ÃñÏÁG@R-Ñ`ÓÝû"à:D& ^ßé¸]JÎ|lXÈó—8‰•óè‰íºþ¹(ý3ÙÙÓy¹/BÆ»]ò è³ðˆ]Ì»N—,fïðÓá#*ÒmT®ý¹¯ßœ#*-àá]8;od7W|F†òp*°9jÊž6Û’Ã@T_½0c¾€òí½KÞ¡¼QiKû1*ÒÃâ£Moäà#*‡-à@;¯¹ y—ÄB"˜ñÑŒ÷Ӕ˯xÿ_¸Ÿ)™úi£þ>™¥òƒ'QÑ~;rœÒ}øçÊ]¤5áH@U|8Lf-È€ð7J‘ÞñŽ#ë?¾‹‘˜v0ûað÷^ðño(¢¿hVø(¸Z£0yØöþ¯o¯¤Ìøžœ6ºTrÿØl9ç—DÉåÔÞußÇÇ#)j#+NöX´¦S#*n…Æ#+ŠÆè©ãû¼7:Mvõ° Wí¾ëÝÈ-5#+ÃNt |`™2žJ¤Ò¤£ÕDÔ6°æ¢ªš+0mD(pRsSÖÙè_ÑÜ¢B~û*õõÀëC„y+ðˆeyPd+Ê‚J´§ôÓø0ÓoÈßšbÝkµ'oÞ‘Mï¦#*/½SÁ5á¢Ên䟦¨ÌÓú·ñÁ“?!•‡‡±Ü_¾± Iz¯²}´õ±$Åa>þTW¦Vˆ4Æ(H"¨€4š¼¿µBïUWÀ%Ägrõù¶4#+b£‘ó–XXHà¿Ž–ÉA9Ùį©X“¯TCA˯צkûÝODZW(qH¡O€#*ÒP- Àn¡H‘¢¢¢=©9ñ=<§•ýœ9kìFÇF–Äþ•7ô Ñ*å%¢T-¢#*DK)·œ:T—żÔWןÜižiÕ®ú Ýs²?¬À<†)—]üÿÏ«¿Ùáôï槳èú.>³”¶ò늪o«ŸÚuúé£bgòýVY\8yÈûëìlç鵂½vé|ÓJ[]çrå®ÎÇ{_àÞÃúøn¾Û¢Ý=ßw~Á¿oÅ.[ìµåzaè¿"¢Zªö×ýÏ×cT¸yOŸ¹z9¤×{áGoMû3ôhrŽÝ÷+Joï"ÑÖí_ŽèÿÝ}Þù¢ðï>\³Óþ=>÷Ñæpm6\\,'vz5>½SÓÏã[h=è·‹ØoéÐ’¡=ŸÒ^èƒîG!#)ËŠ@i>þÂ+Û¡Ý]줧´ÄY!ô=7ãñ~wŽ*l–™qçédÄy®¶PÝw:ÙNÍñm8ó½¯}›.dé§^ŸCæÙ¬£uƒé€×ÆŒ4õû5ý¦×qêÉ9ðéþ“¶1†ª¶¿•ãTÏ¡å¯fˆÝkè4ÇkßÇmëiÞŽt= Vîh]²î†¯L´ùÉÑ"ÆÑÇôhø[oîÂëÙ[ï”Á€ÛÈ¥(ý}ùÙJ‰ytö°ù±ÁñóÛËŸÖ;uç4kÏâc#+`|LvR®–TÌýÏ-?Íð9ïQ§™ãÇ ;›ÓË›–ù8rÃnïAÛ¨›c†çyÈ—³×¾VéôÞ{©=ü[Û44<T0è",‡“»›ŠÆC5-:=vGŸ£U\RD‡Ð8½<$¬DéæT,4ÿÒëw{ýºÄø˜×W=:Ÿ¾ù}þt4«¿ŽþšÆ^OBöþžïGœ|.͇,†—Òq˜p;E5ŒìˆÃ·lmöÃj*¾ªïÆÊýš<š¯³£KûµjØ[6w¯Ê#=+èÃc'Ó!Ïîá;»^Éu?è™}ÆÕ•¢‡£cäúdኪjßñîŽÙK{{HŽTû¼³¿Í¶Ü(£Gïa2–|v'FKõ|¼5ógÿAÕ˜½¶›ÿ7jy¿§·âŒô:û-M¯qÓüò”½p?¼+»W}þ¬;òïhmø€óLa÷û§8·2÷l¾>ͼ4Bƒæu ûô"¼q+ˆQÙ‡£óWìÀu0Ù¦Kð£å×Ìï•óÁØtt¹³‡¿ªMóØéæ¯Îÿ‘׌›èAåí_,«þ‘¡¿ðÏB8a¡‘S’€GŽ`x8W–ØÏUŸ·ùVÎϾÑÏ·<è?¸ýIûù]§»FÊÝÊ p§cÿÇ?gª½ÖÛüÊN¸~ŸÜG£ô…•®wN£Æ^fÉ¥Ûæ#*ü¿F¦§ð÷~ô%¸PŒ=î1B,Xןúß—˜aúyyiŠÿЩ´Þ¢WÞ39Ôéw_/+<Ä~_ AõÕE€(Xb˜Ã¯C¹?òúµs¨:~ïXOßÜ9SÑÒ0ùõýÿ7Éw>´ŸWÅ/gf}Â_ˆ±™µ÷õŽÛ±ôOSõß ß§ÔݹsD[lêÙ²ÕÙ·ëÙG Ô…Û©—è^ô厮Ë?»–‘äðø§ÛÙûµÜ0û{þîœs±èßÍåP¹nM³óLÏ.ûáý,gëÕºÄAþKôS÷Ü“yÇËbhèvÏñü¿nê·O×\Ü4Ž/ß·à¾5ÅFÏRpçŽÿ·7³Wi¸tÃéó|Ëéû¹´§È?oðÅGæÑ€#ùL8|‚Ƕl?-ŸO8ßÈ”~ϼü)byŸœkè€G*´B¬¯•öy-Ëc»ÂÚ‚¸h)€uÿ&f®X®ÞCõ6Ô¥ApoïNnvÙé§ðòuù˜]·Ÿ¦ù.}Òù‹ý-ÌìôÛßÅ[O'þéï¬I—ÖÄù'm<óEX#Ëüÿ†DPw/„)ò*Œtœ<f/»í؆ûçÍÙΑÔýñØ Ÿ°¨§Ã¢@æm,#)(ÇyPœ™¾šÆª’÷0alµK¤®C2œu®8ek—hltx׺bô+Ör4.l?¤"r:Jì•Œë ¦¢i^ŒxiŸ’Ì°ù|w—»FŒiÕG\8©CnR¤Dv0`5>»°r‘Î?‹¸É-©—(¸Šï^g`EÃÙ38KA¼ŽC¨nÔ>ÔÛúøiÛ螟œ'Ïöoü…ÿ~˜sØMƒºX<æ±ÒÚì/#*" ËÊå˜+]òiDWsÌdãѲְVŽèù)óÛOÆQ/ÜÆþ/ŒààÖ²òÙÄQ9\ônîZæ²›có"µ1þ´ö¼Ýãƺ>õÍóÏ¥]päÛÔæÇì?!O]PB:®ús…G}ëeaµP¥u²™Ñ3eˆ¤Ò*ú¹qþc‡/`¯øÿ“'–{ÒViÎô„µÏ· ºô ÈhÖ.à-¼ÞœÀTh–8hœ^"®Mœ+àñ§ãxúõ7žÛîužù&C„”ÄP¢G×ßX ?=ßUê<ˆÍ®pBç/ÞÍæ½~Y6Ã>f4W‚7¢Ã˜ y‡Æí¤A–?.Šõ?ìùõŽÏŠÑaǤQ=º<!·JÙÚºÊUó5Kù§ÕXIÒ“9ïS¬A Qí„›þ9ö:ù1Ezt ú¬ÓÏ£¶Ù_]UþÎ÷œ¿WàÒ¤ríw#*Õ·êDóæL™ŸÞXùofÃqë|\í?ÓÌ~(#&q°Á0â/]¾:êôlmŒ<Ç‹¼ßÇréu¼¯íåêgüÙüúuܯôÝè„`=Šv¤“v×ÓáŪþªNÆB'i=ш{«c˜ó•á8ô~Nž¼p“©äþPÕðThöÃxDä½oa•$•åˆÇaü¶Í*?»ädªS–C<ž÷âQ²JyGÍó÷X7¯–QÌ#).ymÁ¨6—Ç`+wéÑ?òüßA†lŸ:9Oò*?pf@ç9œå%”› Y`Ô‚#ƒÂŸF5¨4iTlTŽÔ¢¥nØ€Ðæ7#+ªÒƒÀ¢¢,tÿíM¤`Vjîå5fYš¨èЈD0QJ…ÑŠ[M`ƒ‘cXÃL’˜ƒVDÐÐÝRE,h5b¶¬®9ÇØOŸ#*Ss¬6ÐÚ]&6) ºýº[xºÔ90$½¢ÐžìVŽ6bí¾ôxÈÙœæ3¡((>¿ÉÔ~kÖS<¦³ =a‹5ÿ$´3QÉ]E··cˆXwBñR‘dK¢Â`z¿¯òÖqõÿ|&àÏôò"4i[F2DHèé@ŒÖbï¡i7¢C$M¨R%dl!#*YRM±¦Ù…¤’©$sþlˆm»gí:>Ý ”ãoÏ—ÍåÆ噧?ÆšþÁ?χÉ>“}ìþ8ÒØ$2p´iŸY²Í2ÆsªÝu×äÏ–û}ÇGžÍ•åð×^¿Ûåø6‰yÇ©VÏ¿óz³xìF_·ª.êèÙÃí2N¹'åü±}º,ú²ýZ$ªAÜÂ1lx[~{4˜ÅÎîô,«eVMq×eoùç²9ñ5Ç0¶Žë„J0Ê}È(H’AáߘºêÀz;=ß«ò_·W·N¯gyD´A˜¯Þ8s8#)á /ÔQMŠ¦ï™À]q×ðt#+ed,á¿£¯8táÁ¬‰:ן÷YþG½ì÷¬a75¡…Á‡›ªçòþ—_0©Ku;âýîüz5ù¶+uój»˜š~›²³öÏñÓß1l–@L/í‹í ô¿J°(¤}Ý 8=u¢ãýQNñðâþÏ^vÆ&ŒÚò1[ª-«DŠÚPÈ9+¥®–#+Œ0‡q,j‡Ó[»¦KAi¦ˆ$J‘D›·||×£^·^¡|š©R§§"ˆƒPlchŠ1J1BÀdQØZáa++)YlÿWÖ£F¡ã*\æŒmFAKT£Q[*‹£86ÁFH2#*Ó©7›¹äÌ#+F5ß”MÈAS(Á„˜0£FÌ&bpïuœÚºÙ) lcQ#*¤c̉†fUø‚4î::RÚæpIC†ù₉mh £üͧ`2ãzXUÖ»]b¥0Q‰‹$I•8‰ì~UÒIGù E,I´W2*œ„ÐEsEQUÚ™QŽde¤cÙ=€ÓDérf†Î Ü|ªÃAI$¨åJCÙo!AŠ¼–„Ý"÷²^ˆ1ž—báÞ¾¨ÉÞ³¸a/úNîN²ßÎdjEböóž:aù×ßìNãÚ5ÇæTRÝ:s^¼>º)쇻çvà ÕSž‰º¹"°÷?ä±]u>ñìÏ–ˆ ˜UÅÉâ0H#Ç°=z™6Ê)R#k“¡Þ©»”BƒžÏÝšV=A{®_y}~m‰²""tçñ^©ë§3zò×aŸ…¿6ÁÐôþãU¦Žq8³óüŸiÒù?¾¡ýoQdOeÏp‡K‚Ar2ÉNâ 50Èhû6tMHåçnãeD ~µé¹µ÷z‹Ôª¥ÈI%™UU®ðQ8$whM 9,HÙbzy¼‚B>SÄâ‡èþéË‹è—åó¯!üz=‚÷Øþ#+‘-Õc®>M¨É¬’[ 5þÔ<SÐ÷Ûd$Ü>’]þ¬ù¿L5òøo¶ê)Ù²gF1ý#*/¾²XnFDy»:|V51ï°9$, ½0¾¦*»œòI¸HKöŠ×§#*740±zä`ÛŸ!°O¿¯®zï÷ûÔkF …Tf@ ’žÙ"ªáï&ˉw\í{÷[ÎÓu_×¥]`OóåÓy¶‰yteŠ(È7b–¤dkîô{eý>\ÿËç·ßÚ4þ˜UÃ!”ûûþhyž Å“Q#+#+¤XŒŒõë›RfDQÌr¡Kò„ØOÐlfrñ¦41-6—·n›ÑHÚ)¬1=j ±o\ËA„y©Y¦=Zﺊ°Ì#+ÑFy#â¨=¦fhPi12ŠÔX_;š&Ž-/ö؈Ä××&D+‚剕.œ!&ˆíš™7ùI%‚Î6Ðmlst+¥#*ß¼Œ†hŒ5`£yTðeLzDm,ݹÁ²#+¬ä$d]Ÿ#)Ë’ËtŽjÐøÕEfÞ;BÕmÆ6Ü,‰’šÝëq3cRbˆlfÐrP¡™&%‚L"#)´nåµG[#Hê¡Âcm'[C®Ù`Ø2麰!05i™ÈgNªéÿ=Pp…©$Xj§0C^ž¼eÌ]wî:õÔw¦P(“Û Ö¿+¢á8Fñ™ãž)JÁ`ÙÂÒ´ÛãûÏâØþOØTáÀ1ÞË·räø¸Cw€G”;ð†÷MIg瘸(!Áàƒ‹1¦È&^3òÙ÷MÌ8aŒýæˆ=AÀ¯/qÿ~3Aм}ÛåÌqµBÝŽt¢zømG¿6 ôûA>Ãò#+èÇW-~¬:S¤Òh v¸-ž #*áî..T–AbŒX°´ª¯Ï“U44ã÷Þ1nEtˆHé7^ZQ"TéöO£KVItêDqH2$HÝMCŒÁß®ÍEĉÔTíšÃ~¡£DžÝÞg‹¶ l=½±zM7§B‡v<„WÁæôk@KEÓÈ´A¢#+‚q64(¸ÃXĸÜæÕÒª´U…N3éý”uÆb!П:¦”ŠM¸Û(ŽoÊÛ†V~[ÿ¼ŸÙ×~ßgœtþþ\`<}âï^‘vZ¼Ióâ<=ŸÏÆUî«¢‰ýП‰T§ÞÂ#*Óî›? 4.ZVƒ#qnÎ&:Ç”øíxöö›ÚÀgNŽ|æ4Rë«Úî½æé˘cŽmcÒ´Â@ƒÌ}äu·{ªŒ¤ï#+è»2ïÊXHÜQàÈØÙùÌE±å\b0A¶œF:7„)zÑ6ñáE–„ãZëyž6fDÙª\â]Å£‚`ÆQe…AY5d¦C`1T‚ùAX„oª$e*QM5CEZ¬-Ý#*ÿH®YÚÃŒccæCB;:y¾#+ÛiypMDˬ’dnŽ’`ºh#+Í$¶.ÕcIŒçš6Œ]Ú„O–âDDï{´5#*8±¦w(eŒÉÈÚèèÊ&E½À¬vÅ`j0t¡E&Y#FS%|åFX3äÐg~v™nÔ÷tË„.1z~8æ[Ê3ʦ6˜þ‘ͪóœÍ[¦ùm†²ðÏ©Ó‘é'Çêä™ÁÌŽp8QZãÇmùá¢hø*®åûÐ`æΩܔ;ÕPVü°M!”_ã§[m¾·mŸcFSÁÉÈf=¼Rà¸@“mˆóc®1´¬òÌѤ_C¸5-Æ«¯:úÙ¶ØÆ?8¢ðÎ<o¦j6äÜ‘R®©n1v6«D˜E¼ Œƒ$M±ÕBws=ÀºmŸ(#* ¦fzømãEï˜×=¸ça¢‘¼—43“¦—B#JHçXÌX()ð¬-óU{jÌÞÜ5¶ßÍBôÁ±ý¦Åš°¤<Ci‹7t Ü>—ÜÛc5P ‘0Ú±ÇI¶q·kYäp8Íé0„$Å',¡ˆã±5ùC:˱°`¬Åü‰X~ÖqÆÞX¨aÐì’yØiœŽˆÅ{q¬¸Ã}Cç¹ÃÎFÜõ£#+ÄÞòî~ëa™]]Íú’7›HIƒñmßS/—LíyÝ£#ZÝõy–€t×ß^—@ÒÆÆÜ>QÒcVò+UZÞLª‚î]IK‚´*ùNþjì;«Îºc…ÁÄ`ÉoȶÃ@ŒA‚Í@G!¸xL¨ÚR‹x·Î-“nÚ×:ä¢>é%·‚!Dvx(æÈØ6)v¸íÆÓ½»#+6ÚŽƒ®®ê#+ɼIJV;…Fç¯-ÓoŽ¨¥Ó#+ÀO¾ƒX°Ü#*¶µ¾×.:1ì¹ÅjÇhDèÅ7M]g2r3³eI(ijäÍb<@ìÕ.6bsŒÅ“†6Ea–`0œäcZ6ƒ7ÑÉ'8Ù¢—+šÑ].íaZu`Øäí¶ÆNM žIµ©42 Ãiæls}žã’ÆÄ×ncV¿<ëfm“‰¾µö’®ÐÌ!(nÇ,~>éµáÜ)2pç:䤡KÄ9S!mDø¨ë;¥éí¼¶Ó–#*ÖŽ)¸‘‡º=öäâ0„b#%To¯,Ú¶¼5ó4•ÿŽã"N'"1±$@Xùdý)ºci§ä)ˆx·×AQ5{¶Ø‰Tˆ‹™‡ygWþK®gQYË•$×½F蛎‚JYY1D1–Ê›t»ã¼ikIÂœTi#+uoŠBËÚÁñÚæ]óO#+3¥"‹Û0EPû)<TG›ê$&I7~WEÏÓIÑÏ#*|qƒhÁŒPšp¾%ц=;fõÓýw;¹}1XW¾;Å÷Í?(l´±MÖgW,_1øèºVº--§`„øO0mB&å¹b°ñ©lš¿ëq÷”D>y‰¥IeFø;“ƒ<™A¦²?Ïh&‰ ¾¹âÀJÈÝzvØ#*ò-d…„ Êl#26«7“Ž'}ÆôÛ_–YtNÃWdÓ;=ä/fRͲºòcRá\ŒÓò«åÚæ×·Ãù³¦†£²ŠŸ'7И{ì1î5HtΚáØž~¶Ç¥_iM[AÍJ6E;àñ¬Ü#*1µ(zïZUå†Þ¬M1¡¶2„’ƒ.–¬±¡°DûN‹A2Dàá½\69Ø/ZãÂ%ë—•ŽkMév뮶ƒ8ux¼üá#+‹ÂÖð-2‰ºtl_¸Æ.Ž#*€A$Xð¯fß¡j7§È<YGé|N€QJ}æ—ûJ‰ÜÚŠ§–a"âõØo… ƒ³g'Õl¨éì/ÓïQÝæ£wívr{#€‡bÍÁ‚ëO@wÔ/ÄÓÚ&‘!EU<â‚̆I¨@»kúÐ3‡ã÷9ÝHÚ bÅz2Ç‘8z³óGËV¾¼õ{•ï½D€è#)²z¾Ü#ãì°"#È EÌèüÿi\£v.:¼¯¢¸zW&Àø®Íx|nó@\è4ïÔÿ1i%÷+·kÔxß蘞ÎACíùë5Æ•öQÈʃ„—È0›FB뙌01Ú2ÏŸ¿ñÁ¿ÃmþîßÞ¾"™åà,¥#)Ì…LôÍÊ 0~Iß}÷÷©ÓýÚ—e—¥÷œC#¾vñÞá!`£X#)$;¤#¾×6C¿ÈU®*?#*±£vÑÉg~ GT$Ä'ÏðƒC#)–7ŠõÈQnVC¹@°9È2è3ë^ȈŒpùÀIÔýcg“{™7Œy“ŽIvcí5ª§ô#)s£ŸÐ.¸1x"]m GääGÇ$\¬›Nj‚¥ÁàI6ÌÓ‰§¶)J¨{ÏÃF`?Úý?Æ[…\åÝþmÑÂDyÁÚ·ãñ¥Å)¢‰xØ0L™Š§ÊcuóʆñžÈ¥%[²YpF\¹#+Q´Á*ÃøÛŒ[&-‡™¡hKrI‰pCŒ…G©îãã}OÂϦî¡I’b%àã¶.`ä&ß³1CÀ™ßIü5WÕ[Ãæ‘Ÿ#+áÝ®ºIHB¥§Ù4,²tJÄzÅÂqæØÜUÛ]nk»tà0X»Ü=ôTŸÔV*y:¶ÏvB•ˆ_aG˜²ÝÑÖúí3¢9Ï#+–"q£$@‘LT&¯¾!u½“<ÖaàoWƒzi™UK‘OIC–ã¤è{Œ®Ê úý!{±”MÁÑÇM¤"ˆ9æµå2I©¬#+Xv\sG™'ÙÝtï&½Aé’’yê½Y±GfXFÞ©ˆèì:f ‡94gÒ|«§)ÖïÚÍÚšç'ôômê^èÑ8ßéO¶zßLãÚwæLY¬ÔÈ„´ðV}Url„¯DÚk$ÿ4óÈJhTψƒ‚ž‡eçik“è;#ŠþIqÏ1þ#6úÈÛO´:U$øUü’ørðÆŠæ8ðü/v‚í‰d7'hèSK…Ì'p»k㔜-÷a¹ôt(Ä$x°2˜1åÈ~HœO#+?½p™= Î[ÖuˆáÎLš~;ii1#›aAI¤XI1áAóΛ*ãÓl9óÀðÍHZaÑhJò×ìDG9|8#*õÓžŽjô"ˆ!4²~U¹›cö‘•3–Áôh¦øÖ¦#2к£À´Ï5(r¹ëìÝd#µzü¬ñ¤‰4"·T½Ä½Óã~;D¬xZjm9ËQï…ðäÞí§ÆˆG±2¹'¨tBw#+Ï¥þ‹Ïb_É;ä‚2Iÿ#᎜ ·iÀÜŒ¢"´B¬È樻³„㼄t&§pÃ7Í1Ñ$RÛðùLf†2TOsÅ©…ºw§™®)nL9)ˆÄÈÈ_ÎGË«q\œ¿CÍÛºlpî]¼EÝE”µß¹øˆò£ÕšO¼ãýã÷RsKš`Úå5Tójû^°Qæ DT}1ˆÚD7æㇶÏhÊ0*Ü×ß:Ö h#)íúæZi4QÃRÆZØR:¯¤À$:h§2€`UéžÄ6ÖeV1É#*vÄYÆ©–ëÇ"ÌýerÌïò!ðþ\ø3â0Ç*Ë+YQ_ÀFéuž-Å”,ýdDó™±··‡•¢Qö“Ùhtsc%[ìR fŠê{ 3òªA·ÐkÙ¨ªë¢Ó• Œ%d®PñŽ¶–øèú颕‹@…)Õ§)Oˆû¢ªH„žg¼êÉ}¶b^j~8y$*×GÍ«VðÉ7×Y{ØémðLèÂ|ò6œ¡™úµœ¦§w(î¸V§$s\òù\sK[¢ÑÆ©vÃæ!sYP¢·N+å3Ç¥|Õm¿”sÎ áß&×:/OÎK3mäô)Ó¥ééµ’|ºû¹tÀp&`ȃѧ4995‘’…l2®„Û™{Ü#*-\ÌædèÛð6VÔ^!6x–óŸUr“í·Xm´üD6sž*kçÓb'&ëx\Ö®]éBfÛŽ¼)ÓaŒÑîMQ|ѱå¢ÜÝŸxêºÇvb©ƒ»ñTŒå£ci|#* ¨ÆY—<…ü•ƒhYÁoÒá…ÞËÙÜFI#*Ç°”ØÔþKÊ&5È£Òc:=Ö»íÎKtÏœáž7¸‰jmcЮ¨¶ÙøÑ )’*DÜw(:¤‡Š@*…as¯6¾‰©Py#+‰¶Û$ûE)}ë'Æï}'nkcg6|$ PàòŽFè© åor8'jeä;MF‹ílr½FôÐ= *\¨½:ÛEÙ´²8‚بz’i=ÊðŠã±Fo%ë*œ¡açgþ3ç7s”¾{ð¤ß>üg:¶æ$qCñnsl û\ÂŒ=³<U"¸x/ÊO` •,'è{¨´•YE]*4íöéXóAƒ§ÅÃÆûyæÙÁâ¿nÇ¥·Ø‡{*£Öç…6z)Ñâ~!å/;cjûQŒÛ’ÓrÁölíÄíJÙ{–Ï&èy%ÝØ‹´”Ùu¼,ÉvõutÁ=ZÏjø}¦o¢ÿÀ¿8âÞ}´s^G ap%N4e °iQ‘Ieâ]©„–¢üÈRÆQãËÌس¾fDÒÉá߬FÞRÒkdýæ{⛽Wv߯<o£ ˜D¹qÉÂ¥cŽ‰fFVÛ›[ãhròÄ$Þ”:éùÚÿWÐØæä?ëQÇ÷åVܘ2(ƒu¡ðäÔOLnû‡ß¢|ɇçȶ9E|ÞH1™Í+¯³®N½j",evY®sBSóF:ÓeӇɚôŠH6±öœœï]|ñ×icàÍƵL#+¥ž e¡>ÜØå#)vƒåy=ˆÄ<žàßïÆNý\«ë´ˆI _*ZžÀ"Á&eÁ.Pjâš›?ߟÏÜû¾rÚbgýIX¯È¢ê+äS\Õ}ÞuFŠ/ê«>Í‹¦ìu]dÀë:FNxëýbT{ÈIZkŽªÎŸ$érÎ[J(7ÕZÊõR:چ齄óv¦„ÉÕÁl×h[…¤0¢^·1£´ÜD…e-×FóÜÏå¦ú ÓsT»f«ùW#+/期«šÑLõYf#¡òxÃ#½áåÐ!#)tÎÖ“ïnŽW4Ubsa1´RXŸµèèÙß“¡L¢)áŽÕÕ.Xmz†Q‹ÿ·ÉjJÅÚQÁgÏXoÃ=¢ss²¹*vÅn± »ËÏâ¬*pƒW[¦Ú¬óþ:]~p6œáLqi1Þ³={ËÊâ¸Õjù_hæªÌÕ,`œRWsÇÀŠÇü§}²ù*šÅFÀª²[,çÚڌ2–SXBX€>Ǽ9ÁFmd"ÊR¢Ý-ó¹òKÐÙk÷#*ž6:#+váLñÊ-,’ØZØtw&Aâ%å^è]7Jéråpu™¦G‹à8Âü§/q°X4VتÆÚ†-.› éÌÖÙ‹EðR´t}yuQ³…[Cî¨Æûáo¦s„˜-÷°&žRS*:SʤR9q%yßù£ã¯³ÆL.ܧ™ö§"!ÙÃÊ̦æÐð9B†Õ²üë(ÊâéYµô×åI|m¥ˆ¹\ët3úõ™slLÔÓÊÞ¡-«ƒk·^ßp´mV‚iT“#+3X»¯±W(ñ]‚Jl_'‰AZŒYÝ/§#*qƳ2¼(ÍÔ1¹QŸ†Ùš8Yž]”Òò{¯>Dob|»ú,Ô÷ïKÓƒýƒÚk"§Š3Qý…îCzìί-%´ÍK>qn‡AåôÂZW\e'(âʺé¿då2^$õ°‡'¨œÛ»*_F’.ÜÈM¿¹¢ä½…Lî¡BÝK²¶#+:x’¥ÐQ±µ’â5Å.’Í#+ºy*¯fÃ_zÎW@]¬ë™†Ñ€¨»ÆÝ33JÎÏ<K˜æ·\Mmó%Öªuqx{6^qŒto¾B¥¯%os”4ìZ°~„tÇŒ—a÷ñ¼6ÿ²Êå;ÁRÏȪ!mäùã2h½´ÅÔ¡€#+GMÉq°r¿Âsvƒy»¹†“¹Áhg¬I·“môw="™]Zº“ÓHìØ&Ñ¥)W<0r±Î9çI[ Ädn}ÍèµmKTYyQú ár÷¼:5vyWK«Š‡CŸ™ ñ•;øÞ ù×BÍîÍV$Šp(Ë6€XJo·LÄPßP´Ž#+Ù.w ™Õ9û±Â{}æ*™Ý§Ï·<ñšÆ#+”<eý¶Ï<lÀNª#+¬n«™¼¦ˆóB‘ƒÍϲç¼Ø^[sžÊ:Tlv¬³óMÅÒÊ—Ú®½Î‹ØH!a-”9“¶×Èæ<„Fei«E`#*³[vFü¢#*ma»qX×'ÚëúdÞ+1ªwŽÛ®šyS¤e¥ùUËñµG›â;;†IÙ㉟/E˜7ÎO~‚Ϫ\Ò¬f#*ž-®ãêP Rîl(«TEE&’XìÈë‘ÇŽ‰UWšññ_:#’[‰× ’s¤s¿}vÌÚZ›‘zÜmÏïû±·Ë9Þtƒ•Í¹e¦n"7EoËTv8#+g8šÛ{fjë¢Ì*øbÄ6hÀDï6Ü&öÝ„,RÖjátq¨¼0¥'HÖ dšJ¸({ÝÒ´u‚>¡HÆÔ)#¨ŽIY_#+ÛJêp|–JBÖNaỞ×á`ÎVŒj¶kÖøÙ²!¾y´\¹piR-A bg=3KiWÆ•–}¹Zí£KQíd«v»®Ðå¬Âï¡óÖÙïiv«W#+1‡ŠíòÞ÷×YÖjÈ—|•C¨_(ÙŠÁ³óG¦›—`ÈH·Œ¯£UÈ\ƒ,œÐíÓŠð½:^‡ÑÕêëõ8àÔŒÊB„q-º]#*òÃS›~4±â3Sƒ„j²™n{w46Ä]¾û¶_bó9hÁÍx•å ÊÜÆ#*Ó“cZ#*µ¦ºjŒZèt+d$ÎÅŒ'[_Èç¦z¨éÑZ°´ÀC1MøÀ¨+ÒÖgØ#*“¤#EÖ-æè_+g–.Ô;· żŒƒµµ@Á\÷#+59ÂBþpœ!M6i€Pgä,2¢1ýÒT¥ƒY)ø€÷z>÷·/#+,:νÓ1Çàöö‹KÈSáoÒ@úˆ°Ì@¡Î2²8 áÕ)ÜÒVyí-U~œóv‰C#+É%PoYZ”QÆœœ6Ï©¥ÿ¿ÍîšÃŽ9°ãôåQTº~®îËuj™/šÙEJôFÒëjm£ÒxR$‚ ãd5UáöÑÝ´ƒ_#+q#ƒ<‚<ŠÌˆGX-š;Zä/µD5Ë·Wºé«iTÒèœÏèÄ2 Á¿bÖÁëh´Cªk3dÖ9ñt{¨±ëmª!îô…¬ßË|Ìëxú£±Ê…÷ÙÍ:ç/f/Óå/x™N;Tǧ?]æÜuG<Ÿ±©3+#+¡·û^×ìw)ŸÝÊZsÉDmÛy™L?ÝÈ港LKý|©RÔŽ#*¡à]lZËãKÝMÍeÁÏ”lŸ0Ê"DYH]ånª)ßòOë·IáÏÏ:¿|žZzÛ]~77Mæ¤tÄs%YIQĘN¦u¾T¢¬ÈLÚFÃÌ`o¦ÑµKæKÔM)U¼;®Š,=xý•ƒŸ“ùmDt¬8upŽq qc%ãMù¹°ì¤)Àl*j„î€Ôàñt0ô:×>1[OSÚ÷ô; d„…Ä“{“79È·ƒß»Ó»žŠùäÞæ;û+o,ÛaéïöÓË0Cuüqoœà¨L½_Øã³T™Û:–¤¢6m‹›á ršE˜ËKÞ_Þsû)ªÖ{gÖ6ß,±e‡>tá±ë&<'k’è+Åœö./ÓÚÕ‚TÃ÷Ûô‘!#yFG{Sà¬6:e#)”}¤+Ë€é|1Ò·´fê—N<ˆ‰1Ó“©i\7¬lÂõQÖþloa#+ËÎdñª°Ë$˜c×Aš |zåÕºt¨äǤjÿR<×íGžÎ{¼ª}~>LRú´,9¡ø³½÷½»óùcÁ¢C;¯»£-ñ’r)ñÜ©ÆÎrÄ ÀÝ:9Òâ»UÝNmúƧ¾}[6âjž‡5tM¢ð» Ùx/pö_”ft€J6ç÷LY5TÞ¾pGC‚ÍÍ{îÛˆúr¦³”DK#)¡•C,#+h¼:- ð0!CÜ$trŽY¤…øÙÄi°NWœ—a‹°÷Sø-tœnªšµZLš¿±ô×CôÊÝa.Ž›U*BñVÄul˜J;FûX#*Ç -È8÷«¨S‚ót¹,’sšáA‹<EYë{A¾sÏ!L²¸Klª²˜{˜nŶºN.šX”.éB‰NUƒš:i›þŽêÝ`´BNÁ›«¡Tj#k-RÃEïÎèØ9QÐr>È‹ìòž3<¸{’ÞÝs£3û¿‹ûr÷ë;XÏÄû§zí̽ƒDfáÛ)9æK¼«ïuÑ:mhEdׯÌl}ÐgN×)‚À£„<ÙïV¤˜56\Ÿ6^xü0¥}]´`2+µÌœgs¢@x8lkÜÞ°Å¿ñp”Aa¥¢–&=}jŽ,ùxâ=xÅÞ<K–|+ûÛݶ¦9«µ/+°,÷|ÛYRg…l·†Ð%k^µR0~ï3{µ¬¡„,ÄçR9Ü>Ê]>¾y#+çtTl0-AÒnŠ¼ýCÍ‹>ˆ±”„D’ƒúÏŸÝü{øúuv&çÉœ_±ÖÿUÕHÈ”#©êÝ#Öêä.Ûæ–Ž“ZL©_~1º/Ûe½úkeÎl}ÚXqaœÿGWð ¹8$ýià†¥,°NHŒs€³<áÓ#+yú¤¼ƒ#+ت„^í@^™.—.N¾þ×ËX’±Ñ©®4釅Èö¸»®eý#)öw.mM“Bªò‡ß6èÖ¢sÜÒ½KRBaãÖš³ù_Ruþ¢0r8z\AÝDŸW°NxïÒº¡i£Uþ~Œ©iAxb@© is‘=ãÀ†G!¹QD¤¹ŽbJ)ËÑ–"é#FB´§W2Ò3fY…RîDHÒ®œwÇÝ{#lü/ëÅúeþë3-vŽ"U™4欤·EPMwŽ¢‡ìgNu.¾Çb-ÂÚ¿I!"`}¸’fŽëÑ|«—öÆ~ݼ÷5—Hô^žœw˜ÇlÏDm»ö^Bn±b3F”ÛŸÞ³Š’˜Þðj!l¢ìÒ ö°Þ/¥VÔ¤k$™Ã%²Us±Áâׂ |&i{™)þÔOJÌl+ç¬}X}6z^V…¶sk1ƒ†‡¼†ßI0X‡ã>7p¤ôÎbÛ` -ÐèeùÖ, CŒü`Æ×[È©®Ì{¥£ÎŽRzÅ~Üò:§\âågÉ5~:¨e\æ³ÞnÝ®ŒPßUïå-éðÉÛѸFµÎqÚk”ÓZ9ád*”t™ãÖÛ¿5ôƒ’žÈôÇ[r|o)#XÔW·—>‹Ÿ˜æz^x¶‰±¡ê݈eQʃû#*‹`Ü1˜ÛNAõ;ñoò$³®‹›¸x‹<Cf‹øc•×;Ô—#Z_f³g~AÚ-¹ñwÒ·ž¾D‰ ‘·sô6‰×›Ö:ñLÔ]9½Ë¯P_qÒNK>¹¼¬O_3EÅ>HاŒ«\àMœÜ_Ñ™~¨:'ñÝÏ“\•ùó€®?œMæ*=ZWxKÂù×c×ÉÈU‚b-Š[ñƒ ¯×Ó3öœ¶ž`ƒàŽ#Îk¨cdÛ£ÇÁÓHdIZ©Vám…ÊA„‚9ÛW5~!ÛŒä[åg¥yõ¿ov"ÍZ†n#+ÚçÂs”Ô¤‰oéìé‡ÆaÓƒ¡ÑÀÿêøG‘ÁŽzö·ÝòCÛ &™(B~ðž[ÃP:mPžÌG~ýÖÔÓíQ@E#äTa’ÛåÕ#G]›Ñ8s>—_X¡xhs›rëxÐgÆ2Ì4j†f3›™(¶x!ðê¦Ï{Ux+Y8;å>w^`Óq6ɇï»s%˜è,ºt‰Fý._\T Rôzà’æ |'+Ÿm‰loEá}™Ì÷þ®dxáÐCbù?ÃÊ”ù‹ÕfÜ©W®Ö„cé2-Ixãà‹»ò!½bGeœžÚ(Ô®"LI´'š¶Kbjd±ˆÈ¤d¬ä#+oÃR`äèÙb&d#*}ûgìò:C qÐì!¼eùÎzó—Fy÷Ä7Ž÷ÇãŸÑ¹YTJ@;šè‡Xþ]‰M;oþ›¶tÚîúØQ_—UgX¦žµA4!;MàbšÄºÇ#OC<_ˇjtnïPàA4‹ëdí)½ã 'Œ+éU<Ê•#*$–‹rÛ7¿g˃^‘ïkÝ®^ÃöûÆË´pejžûrBÇ¿›çÅäwjòm›Þ$?³ÿ‚£™¨;ìŸ0n—f1U#¶#+Ú”?´òÅò£¢¦ò5½¦µìûp&Z~ÚÞÜrý±"ÝΗÄ#*IáÞqŠð§ò~‡9Œ;ÌÙÑWÝmÑÚ›TÆ—B!WÆosÝãËæ}™ì1+°ð/8ç2·z³RB+×ÃM €tg¡mAH ºY4rЖh|K::Òq¬ß,cøËZ„ &Có÷(;&¤úr/ï°…@tØB˜,ý¾¦æ_@×™Žºÿg? €ð2åòú/¯éîïNøŸ‡WË>2–•ðåÄ #)§ÏìPìOÑõãÖŠç°Ø9Mw#Ù(JL+G;Q@À€ðè{<¸ÖK ! Až½(AF#©õƒãWyl.£%ýç8Ãöÿ¡v¿œ•Î»–Ä–“õ;e6‰HÚ“l›ªå¶åµuÕ§¯@ðbq2"Z"@ú4 Ë6o+ùü,q‰µÂ[~-Ÿ,øk_Ï|Ðù‰–¾FåxI$"‘AƒRIæ&©×‡™z½:›†r:L-&OŸÄÌx§jÎ-œ»-±<Ù‘ýp®™j9I2½KW²}¯&‚ßh'Ùáó7#+&-žû¡{ì$Cì®È#)GÌþä㘋`ªˆ$-Qµ]Ê¿º8,ªdU,…F’£rºÞ›¿eEž'ÛP Øåo±(Í¡mÿd“^çü€»‚‹ªHœ“’Ê=D-òùÞƒ=’ûb/çòwÏhYäb›zêÞ96½]µ‡ÎîÝ0nCqýÌ.ü|ìŸÒ#*®…ªzïnbb5i£¦¤dÔÓҔػZCŸÒÎ)rª%‹ça{à®èRˆö9#MLЩ‘; $=‰Bj}¥HøÓCåÃ嬉°‘?GØ©¡ý*!Š#*K©wkSî¨ÅÈ?ƒÜ˜ê„#+}s³˜íÜ"—kU‚€@UVeÀUœ7íËoÔ¿v¿‡ñ%\ÏÄ«B#*Ór\E³¨dн0_±Ùè³|\eÁ‚ÿ'ígsÚ*ƒ“Ôj2l…î2é稈yc%3p†€ýŸæsy5Iø„ç˜FÛ{òá k‡çܨ¡XÊr¢’¢È¦~úw@ß8§Ý>öÀ{“)LW@¡Ïñÿ%ðB!àkRw!%"_VºU<'lC&ˆVnÂÎp#+d÷°Õ)4`/Žµ‡\дž,¤†¥Ô“D¡¡°á» †ìxpƈ#+Tv‘½µ%ƒÙg#)ùL¢#OF³»i“µÒÅ„þˆC®ÀžÏ‹¬„½Öl£öNºJõøÂañŠ¾`Ù8ú&Ø`“Fvk¾}N¿ãÞ =øõšk¸!„zí13H쇶7*¥NëT] ]ê£v̯>[0nƒÝ®…ÎiÑGTqâtÔÊÀÂEŠÊ®Àoj¢Ê#)¤’P Ì°‚ÒL{Ww-%éfá…ÉíU½íuÏîª%í:ûïFÍÀ`¯o—LïøÕÀ”žØÀ_¹êÏØîŽ]¦P#qŒ´€lÏQ#)}î«"Ýb³“ì^<#Ìë£{÷o9¬ã*]U×»â_‚ãõßì…‘mdÉÔ(ß„ˆ9_7¢¬™J^UÁAÞªBzJ” Pµ+ªj_©¹Üï¶#*FãŸ$z¨Ó‡/[O2F•PÍU3Œ) K—^÷ÑbO|4Òè´ÉÇ1¶ÿ ”5¶q‰ººXG]uÖ/q<2‰´Û=ªc-ƒQuŒHí@i6Ī"Àm!¼Š;¢œ³Ï•özè6é~Æ}uç<§VF²&¡«½€:ÍBªë£»##*Z·kÛ²¨Ù¢¼Š\DAN=†W<„1”ˆJ¡`Qf¡YAá¾7MÇñõ°*"%l+a僿 (1A&×Ç]ß3ºhàÂrr^õ{ñ䨡ØÎÿ¡–lÎgîV‰•õÔ#+@žæD*šõf›#!‚‹O‚f[ïÕž×oeùn+݉FS?Ÿ¶qœ@ýÓs#ÎÐàH˜Hj )2q¶Þø|7¤vžoŽ$ìLÇõ“62½×5g$&¨ESPÁIÉ4J«ÎŽ`¥ŒÏÓŒ}#)z¹…÷í»§®®€ªz‹qƒ®Ä†„ïd-.Šð/Ôˆ¼úÝyüuåÕIQÏXçnÅ,ø-pÐ#)ÕDl–ìõL‚‚#0®´šq'#+žÎê—΃´ŒÿØΰ›Î_N!^À–Áîì˜\:BnÛè/Š™•ícÆ<ÂÔ`IßQCmßQóy¿Eã·¤ò@ù¿°ù ç‹êÎi€îäC·˜T%.TÞ1ÚrûÞLóˆîòye› TŸ›DgˆÏF›œÃÎÊû¶«…æ|F‘ŒÞ“Ï7ȃ;#o§HՌʼn?MϹ\E#*«Ï4¥0½B)œhBÐ-L »Ð·àUrçúõ›Íà±I¿á(HªZa<!ÀË94œ.¶Ç #„1§'Ö©ŒÓÚ#*$nãE"#+’xòw.O×øÛœ¾Å«Ö‘èBÈzbF;¦fçþË-]z|Bœ÷shœCó%§®Bæ[ÝÆqãžÆJÁÕ)ç¾—Á\l[›†ùstG˹ ‘Ë+Qƒ`-$9$ÙÛ(!=e,£Ë¢÷#ôsÞåÛ\{uÜ£UîAè*Oò.6¿´yåRÆÌN¸ƒ™-ó_•ÔxÞ-w”ŠÚ“³6JsLC¾Ã|¤á*Dª… pGÍ#*ÃAÎŒòMni×Ío¯B«®zzëÍ2‡@ÂÍ£5¹¯#ºoˆ€ÄEÜ®PØ¡aËaÑ!¶# #*0îu‚.-×?Eo3à ÄIQÃq½-š,ŒÙñƒf傇ÍÊÁG,™†úØ–¨EKn% vouGˬFs’H3ÖsÕ|k‘mß~ÍØð$䙨ÊäBTZ/üy(й‹îÌ.D’×SÍcèt‚çã‡ay§§ÍﲡýnÄS‹ß†Ÿ,±7”–ˆ—ó"»9ax§“hU+ÑÌ0ÇékŽü^º2—/ž$jêá +Ýpe™œU?~˜Á7ΓÍ.\0`ÀP#+q7’n%ƒ(B”Rç‰óžŸqTç¸^Ûâý=ßGï^9ºwÆéá\›soã{”I#*;Ï[°ûŽ±ÙÙÂë¨^ççdym_ƒp®»å‰ìü2S0¨‘ùÒÃJ°N:B+³›Ëoä|`^¿4¤mÑCÍ{‹ŠqtZ@•zöpÛÖ1Œ$Ö@o"Á•0ÞC½Y$S¥¢imIçô(ø²g€{w,Rîú¼Õ#+ZÞ‡÷ìH#Ñs[9•û¯ÛÒ”98̉'‚°Þ5æ¥ Z·ÒÊ€Ä;š¦Ò*ø9: ¬ ‘TÆåçÎpØtlc*ÑæSüæÑ#*yv{ñûmwTø~Žw_ÄØÂ(ÞÂBg¤zVöÄFërÂWˆ¸ª¦Óyõ€Ža›ÇÊX#+·Ãa#+QÁŒõíÅÄ‚€È??8¢lŽ±u¼Ä8é'È.EÝ›8p‹íI¦‚Ý-,€£ÈÔ¨CŠ)yOW‚ú+“dFêÇ>õÕMËj~Û5¨Õ'J‚ßW>-ò·õ¸8ÓJ°V`ųs-‡tï³Hmȯ]pkâÐeØÊfõF©äctÕmèÚFhMÛrúÿ‚ˆk^`’GQ‘^í#ïm#*#+tå¶3˜:PJÞ7”«èÃ0ò¸§øßKêO‰§C•/Žo·‹oOßšÛºáŽýå‡Ü³”ˆlÔ3‘ã$m¬]Øɘ˜9#+ùæT@æÀã5¢MÌE#+*€CÙÆý\ötÚ(Þ°sÝ°¡Ã=Å%b¦Í(´‘laM§^ý~\GNr˜jμ‹ß‘nS™Q—Oæ¾áIÂjcÁaŽ®sብ»#åéôë¼áÀ8o¿’;êF¼“G“¦‰[¿‚‡Å1)ŒùOç!\Húڎμÿ\ïo$î¥éÍö*Ð’žÄ?ÙPbJk<èG+¢‘D„qþd1üæåhºÎÅ¥+vîß°*Ý…Æ(”ÂR«ü¿Ùí{ò©¦x¥#)iþ>÷Ž]q~º¨LþÏñŠ¨Å„+Ü|Ì“ïôwÖYÂgßAUOv€ þ0OH19ùq<r{×ëöC¡'Sߟ^X¨ª¤$ÿ#*ð- C÷È‹‡õP#’òÍüφðÝö?‡×õ[ˆþ«SÀ=iá¡€3~¾t‰³@/zÝô_?^Îb„×*¹ËÍK®p#+Rєʢ2BxÙâ„/öQ€‹»)YÛÆ»„ß8‡1>ýþ—F:Þ9Ž¿V»ã^’"p…ç¿ãÕ,_©Ñ*",õ{|s¸ç¸ÖËëë°åtN" )!{!Qð*ô©¢§îõE6ÌF:¶j¥a‘Y·‘1Žk{¿„ûÏëۉDZÖyJ GJ €ãÀyùËdo~#)ú‘K›|ûÂ]Í`«éhÃGgYØøïE‹>ARá?´xp˜˜å½B—N#œl#Éí¯êœÅM“•¹Õ_VDñb|ÿ–É×¥wۨݙCƒ ‚°Uûƒê.VÁ÷frõýŠ†;ÿ+Ÿ¡ý_/LþOPçOyQ(¼#)zXÚlD¿×òÿùÿ'ü?Ïþ¯ö}ß÷þÿ«ÿ‡ÿµûsÿgÿoüáøûoûÍõÿ£ý?7«ýÑû}ß÷ÿ·þÿ›ÿú?ÓˆÿÇþÿò¯üñÿ/ú¿ãÛþïõYÿ³þùy>ÃÏôWÏþÿ÷¿ïÿwûáÛÿøÿÇø~`#)ŸÆÿo÷ãþfäþ«XUª?žËSûÌXq|¿ö¦¡ýÿêåÚ¯üˆCû"—ÿQè.Y¬0~tfS¡’£Q#)rPÈ\€ ýª‡D:D,Ãñb¢e:¢97ÖØ¡Ë4°#)H‚ú€Ò€»2‚a¥ˆé#)Áˆ+#)‰²–Õð‚¶L¨Šk),dEI¸:\¤tQºPY#ʸ2 ™Ž½Öø\°†5R&¤£M¢îK•ŒÄ±`ŒK%•LlÉJYªFDƒ%ÔPÎ*ÿß¡mŸæžÿ:/úUÿ—ÒÏ/îéÿXÿK8õøÚ,è“=§u* bwòy‰îËüÛÖ7üôl8#+Ùp\†ÿéBÿ¶±§èsx$–š»A¦ÀdCa5±³æÀ!Ù^däîË¢ß÷g¢Še®Ð½1‘˜ÞSfb¶ºxÞ¨ãžæhÔ>ªo-cGhšBWzŠ}î©íw¿÷\Û9ÔgE»ÇýNÉ&ÀÆܨ>Ô½¹×/2GGom_fÉ=´Ó3rèÃÂ@z³ÈH6i¡°R™(•V ±n‚pmœÜ"&ñõëwa!šH´q™s£·ˆ~þ!°/1üxÛˆ‚¸ëŒ«û¤á{ÑKãqÖëFÞ|!YÓ'¢YÅG¾&ÍŠ†‹ˆe'u×ìᶡÅðõZŒŒ’mè~á–Dw`w³FjVPõ?ÚãΩòi‹¨ö$³ÇgWæv˜ll<Í㢲ë›õÓÌøÃòYDéÝtZe1ð!!ÇÞ‡ƒ=£ÎBÖšØc™ÿ¥á#*q{ìiÂÚ¬?8k?ã`Ë6…ïîÓbW?"È?ìÀ9Ùž-Æ%ŽâÀvø+FM1f’ËY)šT³ZfJbEïo°õr,r4UU1¤”UBÎÝ}˜j#*ÚO{r;#\C9ìpr.S³p˜7ØÍÉ0GY|è‹HÆȘDˆ°ìHg“$" E=å‘_„M-6iòÇ#*»wÔTõ7fƒRnÆ2AðØÙDmqLú(tÔ8‡d+owZ€KBl‘ ÅS;’4í£±‘þ<¡›'vÐÞd‚Òf„-04 ê È(xÂ#+’mŽ©¤V H…ƒ®·YÖ©Fv5Ñ¿YF´E#*¶è:¼ï¯8û Tšcëàtœ\µSo +!á :,jθ‘ÚÎ04âàq‘É"z^2“É‹¹žØ†p‘Á5VD9?øh4šUºòöt¬ŒXži×PMŠ`ã9'rx#ÞédN(ìPíeDT#+H¦ì·5²V±«|eY*Ø«\«\Û%Zæ«¥µz²/:ïÆó»×uÊ«èùôØ)4`¯HÅÑI²³ÊâdMñøWÎUt×/°>—§¬È;¾˜â½@A¥£ÎèpC§áŠøðY¿f“…©j4•9<Ìx›òvt/#*Ž‰-Ÿro=ù•ïão"tkçP¡å/‡c€µ>¹Xñ èg„øøÜO¹¶ú–É´VÆÌÛE_N¢Ù9Á#*œËcV#+Žrñ©Ã« Á£ÎY‘_1ôrUñÈ¥X¹ÝÉ£@Ǫ«ƒ–©)e&I$e¼ñT’’ׂ“$ædS’ßçZÁÐO²¾Ûx”Ì#«6>7šÂ+u9l(Öƒ‰BÃ*(ˆÔ ˆÈ튄¨ÙÝ«{×àmk×û·ÀlYHªÔJX%PIR$;ï€<6t:ûý®6÷”[·@<X$&IÐ;¾6ë!Š°¹6©ä£!Õ’Ä<†ø2‹‚ ³2¥çˆgŽu:íß Ž;9£È¡æ£(AVn„ØÊN¿]S}|x×?ãóž–¼7à$•9òè·äŘÖ%yRZP²é!Pm†À€ß'ÃãË=Üûw=Gpn#)MÔêŒI gÄlvo°¤z#)höñ%ˆR³Ó7ÛP5ò†ˆ‘"Ó#+@Y%¡(9ßÒŽ•gLš%¨J’+"„ÚÕ##*¼¸çtì™nð×;2Å`,!†UHwÍÌ œ¾]!¦É!#)Q'rQĵí±ËHŠÚœv8(äSUXò‡Ô‚ˆòB‚«ÐÆ, P—+ו׃<òóq×f«»¥$¨.†æ…â+µžÿ‡|#ì¸ëÑT£€UÖ<Éš•#*A¦#*œ÷Ø{„³ðAªì4·‹Ù‹Øú5ÉH4‚ùní»¡Ä=BœÛBy>Šš%|êIB@D5dXc<³ƒuEÞ@¡:ž“o”u‰’dŽlx"gwÓŠ›äåyNჟ.dzY:,ÛÝÌ%›3g`}Ò〦#+„ÜCÊeoP;»Èøc».¨L„!²ÊÊo@ÿ?Êl+Þ2ÇÁ‚ûR`]Q“‘w4lÉQLHR³%„Âx›Êý~]ØÉãFÀ”KBmTŠ„Ì©7ﲓ»¬ËÎô£¨¼d=ßfÂÖ4î{÷†‡·ˆCœ>è•gJºu¨K€w—J9*¿L6ßT^æËŽ°Ëƒ^Œ®Ç&¡¨a Dn4Ð(#+;öñž÷!ÅvŒÚÀå‚@ Ay&6c×™‡ß»gˆ»«Ó–Mré#r8ê’©Jî¿-JíD‹(Œ"À`‘—kŠJñ¹„WuqíÜdyëÎx,Ax0—¥‘ïàqÇŽ4ž Ù¼©T94¦„òò)Hþ齡…°ŸŸ‹›e<ê‘`y¼â—FéEWt÷ww댪wT¤KFˆ‘´¼N8½‡#+ KF„d‰&¿d§¹1ƒƒ»®Ðž¤'œŽÒƒ==[¬Ç7ßœŸ/7Ço³œÞ}²x¾ìPîˆpíŠ|;ªm¬U0/ÄNøz$΄ج\4Q¢v.ŒékyéOCh£‚”*¢Ár2(³ƒ%*øNœãçÛ¥vÿ]pû9äéÕ:¬ÜöWqÙG¾;ÏÍöaÓL(ðûW:궂öÁË}k]®]¿-bz}>kÒ¸¨iä´mê{ÌŽf|å†Ç{=V#*'&#)NÃàÁlbJ§b{³œKåLã«ì…è˜U¶AÓ`wLj£jm1Åy Ãwÿ&}Öª@ÞTL˜v9ƒ¬\I^‚ý:‚ÿF}rÒîœ_MûC¿$éÔ&NèØÈ1Ò…@Ù"1#*—@ÌäuàÜà&„{¨ªku¡÷—vPćžœÑ"Å—êN5ºÌQ“\À\ªD¸„A7Þ·»þ¦†')Cqµ#)µ¢r£Ž#+Dh‰žRÖHz4y˜Ó.(P§ˆ–wûè…?kÀVsóšokÏ0 åP懧!3„”@$î‹Q¯Må ZæølŽ¶Æ–äKQƒ.›1X~kBÖ¹s2ìWyS„V#`¥T:l àüŸ¹#+w›Hë²#+€u¶h~»ÃÂ^æAâ~¸ßs†ß]²•éÑЯ>c¢›Ã:²R³Òå-¤Â¡Ø MÀSvÆ›ô-@@ânXêÊŒŽÃ>UÕØÓ̈L³6ÔŽòÅùã…×hQÆr ‡(¯IÊ.È›§KÉf$náï´Ö¸6W&¬'²Ä8@í!™}®J»#*!‹’B ŒCL‹I³lšL™MÝmî1–4ÚG±œ6v8ÙFÌÑã(ÍÓÝfçÐèZ烉À¹ˆEP{š% rœ`0ÛŸá]íì˜j15=´SoÎ]i’?'ñž¹ÚÏ øPÍ#)P×K-<*x,ƒÔ”l÷Õ}C®u7aXª¥:´0!±,xDC˜@ÜÐmì¾·é±F¯§Ã“ú–$Ícy;œùÀÄoΑú†LIW8ÎJ_δHMúáœ#*¥ÓÔŸû~Xê*ve¡þùéÖ÷Ì\øÙ”<˜zd4y§Óo>ÜúpβNÿÆu#)KˆX?ÊxÄϧþç£@êíÿn°Ùð£¶Çü÷;Ç0>ä#+'áŠA¥JÝ/qóÎ.H¾²pý_Ž™•åb}[õýµ<€,€k×eñ†qK-n^@ ׌o#+sªÌ&¨˜ûé¸)G<ãñ#)bi´Ç³Ù'\AøÀðÂyŸp¨>˜óö˜cÍó8v‰ìê9¾&y”•ÝÿcñımfŸˆ>–ý¾Ö‹: ¾‰’‡v+‚ØÓ#)ÂÄ™À<Àû¿§÷Ùÿžüùüó5¼fîØÁ þù ºe… ¨É!¢.XZgzþù<îÿõà߆áõOåã¿ËÕ–7:%‡ð;ÿxêÂߣ±!)'_›Î‡\åî|y9ý0#*Q ÿäô̈jçn1Küçþræ…[³ŽÞz¯?¸.wd ö¶¢€'Áÿoú|ÿõûÑÿ‡ýrÿ¶Gþå™QøÿäÐX:´•’m®f•ŒçÊ|"¢¿Š·ê¬éHZ-ÊåjEœíœã_Q§h¬H#+ó#)H( eÙž_*4í3#)GDwœ#)G#)"kíO—®^¸#)C,?=ƒ3”A‡xt|'ñŠ©QêÞÿ¦ˆ@ïÆÃ|}"Ï¢¨•D*ä??oæúº9φ`ŽpFCÇ÷éÑÂçþ^G3¤Z¬Èa¤&ny½EÛu®ãkʽÛ#9˜‚Îrº ÁÔ°}¤%¤'6·ø3n–M‹Qô«×¿œòO7B¤3fâŸä÷{½spõ‚ÀRJ£ïƒ|îø}ÀÞ@¾Ÿy¬Ó]nGêÍñEZbᑯl‡-‹ËÍ¢À@wv=1…ïˆKî€ xªè"ŽÊ#)”ˤ~£P¦Î!’&ÇÕ-A™Q¢¯õQâ:^GÜî#)@Ü¿ÛÔûÁ¸7ÏjLjæJŒm¸[Rlvãeˆ(W£n¿pã'y”"¬êƒH¶îÏ߃¦l Ó]úÔoÙÀ„2LδûY~šÅµC375_[PÆøïb l?,ïþ&œGʼؽþuÜm{~šaO”ÏeÓÂ'¢CJ2„>—3¶ÖÒ²ä3Š*£ {G·ÊÞ½žË>Ê#ð*”·ü¿#)Á;9 Éôh`‡6P#+°!#)‘(|³ø6§OýO£#Æò»ýñ:ïBoL½NãÕ:?mwgÕ_«²ÃQ´‘_‹ÓCÝÂq3"åân6êú¿ùÏ®;âm†P6CöCKÒÆyT¡"Å“VÖtçÙó×ó¾Æª¼[ëÕÂE¤”NZØV@L~q98:ßNαwÐ6Ýçáo‡îÀg›Àly‹Ï¤ò_¶~N³èâ4lmCÕ<î{=²\ÝœíÑ´y-¿Ñêðz,ýˆ•µ#)æ#+¯{™Æ2UûnÿKÌXžçõØ}¼¶œ ½jijY™µôîfs(§ï/6G÷—ݤ4¢¸nÿ¥ÆÉkåyih‹„Pa*††©ò\̨ü™™Å·¹›s RžÀ€ïîé#)u„îì¨O9)W©#+ç]ÜÍEŸgßñ‡"aàO•Š‰s…G³¯²oêøx|D#*–3ÆKÏÝüšèêdéU=Ï#*éò”%º†¶ÛåÄuÜt<ŸGYL¸=´èË~ë<‘”‡nðÿš:wÓ¤zÂ#*®#+ ô*–/ÊHœÁç„ÔÛåøÚP‡ú—ì(HëÅÿ³¥èïãÀCäsE{øò§®¼±ûRÿFWâý«ìÏ^D·õºÄ… äH0y1×úAÚv÷W®!ú¶+õîê±Û÷j]óÛÿC+cî†èg’(+îP"@>±Ó#):»uóÉî¶~wÂâ=Lõ#*°_*º«ƒ¡ˆ‰¿ß˜¢y¾a¾`{;÷yG¯ÙéJ™¢§~Qëק-#dy9K‚"€1s8l#*H⺥zÈìÎc „A¬üu¨»pZæQÿ¾¯fíúd>Q…‰éèP›·ë¿hëp;•S®è}½”yšf.Y(øbæ£4¦äEsò©ff#*!¾ê¯±6(»,Éyb'?†ÝjŒ4põ|ª‚]íLÕ«qÃ~ŽÍ:ÐpO–L#)̃=ÜÛŸ˜òþé:EÍyPŠî9õµ¢W[wAþ,0d(Sy âÏ3Fïf LNÏ£«;y°ù4[q 7ºyôqXjÆÂ]S›v4äHuˆŠ3^·ºQLB"$…rå.kªöíߦ}·®¿vÍ×ØÖª†“MÂdž±VËßè3í¾…BADR!æ)FÍ÷ÖSm¶az«”8Ê5èÕ‚"4˜lòùî²F›lx»\C#c`‘YJ=šÞ]™ øA*w'°z>Æ1Om&š´÷œˆ™kØ*Êò§#*Üx‡-ÁÈííöuvNØwÚØèeŒ2óÕN¾@æ@+[ª(M]dð÷ÍAŒ7ÄFI÷ÓΕöAp)#)â¬TˆDENø»²Fr‰Ï*s`Ž›…#*Žùe:„DHyj"&]€#óôuý½º¸¢í5ü€;]%†ñ'¿²yfoî¿£Vüÿ¯o5èì~íßmÃXg–b¯@«°CZ 6¯§x ‡?Pÿù¤v_dîÑwjs¡·´ƒ†TT;‚s.l¸ï¿xt]'ŸõmÛeÚ½½¡atÓp.¸C+i‹Á@ ßÈùýŒ,ü77ôŸ¸0ôíu#)%Þãî ùJLùßRîW<þzhøŸW I!mÏŸ™†s”ܘ5(¯«Ç¤`g=Röà 5×köÿ‡Ö±š×xkÁQ‘Q5#+зø/+å›Ñ&Ÿ#*PîêÉÌš‡Hìªü¿Õ˜Ã×É/Å<&Ÿì÷j-{cñGlUÈþs¤qvs¼C`pÅÃç,*)G<›nh9YX¶ÕW•`Ñ5(‹x¢âwÇlÕUySê¸'üŒßõÉž&’™^ZƒF+¥½z6úè„–È^|>Ò褑äŽw«Î.©?Og§Ev½½k9~˜Þ$Ì¢úËCHî!Ñ¥Ùh´×b’úW´gøÈzÌlçXu³îAÆù_Rò˜¹îþË"e2"链öé)_ÏáFÔa.uDuÓm·µ¼›lC¯o—ÎÊEÄcâïΈµ[±ðâûÝÂÉôØN¤1µY‰B¨`åa <?¥ºô,u$ÒË'Fäø>MÂKÈè"t¦›ù0a#+¨È5‘#+ò½&æLKÜ MbŽUy`²[âhXñ‚Š„£2€ÝÞS¢>YF9\3è~äã裰¹±´å±X¬##)Ülªúa×¼±àÚÓÉᲫcËoÇxʯð¹PrƒÄÔ‹‚3uòï#* ‚õ‰ ¥r,HeƒXËoˆ‚Þ9–ÍUÐ^zÉIÿSÂeN–ÞÏI—ÊÅnñI¤B&¦V×#Ÿ»Ò ¾pæâqy¦æC®zY]³²¯rѤa›¬•Å†7¸M~¦XEÊãe—DºBI¡ö ±¯TúܳýÓqΗu’å·É¤åK9D¹[6eÈgE+¿K¸Uã×yí§ÌåÝÖÇí …0«7ŸQÝçYјòÉî®úAžyÛEg'/H¢ÏG&'wÄ=}µ˜â‹óS‰qoçuç®;ÖuÖçTõ;Aúc4¬ƒêSyïâe0vòÔ‡"ž#Ê=³{_xÖ¿nX°ÊÝ"ùâ%.Èm™ýÔ_¦þšw)UWG±»ë½*ñÎ%ðûšo>Õég«Ì×h9Ñìòzíú7)½¿’êsÙ>µhîá®ÕA„KÕ‰7ÌSnˆz)D‚Tä^™ÕÒGã¶m¨(y#*©ñþšŠmTZ”“¢‘£ñ£rÐußH[‹ÌlÞ=/ævmo³¦(áPaN#‡7§_¶hâžœÿó¨i”Sš‰$¾ã±ºÓ¨í¹RwóѾw/ké¨Ò[3™é숧¥ñC(SåaÒÎ×;`÷If‹uyØu¼)'ÐÆGU*º<x;IÞ&íÞfÆMIûß?S2ùõÌE>rÊØΡÝm¿mïXqrãŠß³Út[)Ö(@ñäßIw ËüÅ6ÑywNè·;²L;\ÄâÒ” X‡Äz©ø=n™èÒ¯^l9$˜d-”ÏwûãÈrÞ]j[ÓñÝ(Š·ìbeW8…˜s_¦c‘A¶‚‡*$=“³„¥.Ø=¤…èÐ-®zÄ"c´¿Î:"¦¤íX‚=œW+À~Û×d0zÀŠç²çOœæÉ¢¨;)pè;˜ä«îú}±Ö¹véÚìñ½=aɤÄi.œ½#+T¤„#+è!·—ؽêðLJ%HºîÊ”;pwJö88Ö#*šÅJhÑ‹ä=‰œ~‰—¿e̒㶉ÛÆå×ÛgÇÛ-Ýó1åäðYþ|佧A¥<ðyæVý!ð{}YQxù‡}ò{âÚªª¬Û—‘iûY''~¯ŒÈømsœ&äh³ä\ŠjdäW¹MØ£Çæ£Ó<i>/K:é`g0hâÕíÓ7Ötq~€{Ù¯ÊÚíÇú]ÄÒÜû\uïç¶ûþl¡³ÞE*m‡èk‚ª2#*v#*;ÎjÊúûºx9së6Áx•R \G²Ø ø}bqç™|j'âÌóšt>ï–d='ˆì´˜ª7+óûpÇŸ}ºÞ vî2ʼn±ÛÊ!öˆÌl´µùJ?AøŸ£·òèZ9” ùŽÄHþÁ^…Me?ÚÒ=`ˆ”yÚ«Eì`º ÚÒ,¤E^"ÄGº>æÚšŽ@¿÷¨ŽÁp KcIŸgãêy`P¸†ÚsRŽ(¨úÝ aþôÞ_sCrZãóûeßø{µp¸…A8Cò¶tQ‘5ÄjÄÐ~·«2'uKi7pR¾¶Sáš±™>ß5GõßS>ßÐkÏÙ¦Dç¡mñÕóªo™ ËĺÏIŽNÆØêà#+±´r@ü(ù{¹èæ “8xö0ÑP¢\BàQáÁú[ÄXþ#+§½BˆiLxrDÛ)‡f”³øû¢KA‰‡úM„éås_Ëáp»»··€!)uÓ¼Ï/Ÿ[éƒ;õ¨ÿh/KyNi6¡+ž[G$èoÁܵQbP®p.2±¢|b龞Áh…„"¨&µjÚþt&ØãJ\EÛ/zÇåúaÝ•.Q!"ð›_[®ÿ”åï¤$=ØK»$ñø…Ήކ‹<;ù24ÇIëIÇà¿][ý9Ï»Ùo«íôèòÞtÍÒ¯N*º 0æVE´G\.ï©Œ’OgÄ»~#+ÉVƒò³òŽèýf:ê¥'4ø°ôeä(ä‡b<êÊf—=¨PžÖežŒ<\¦¬…¤ö¡x(¦`BB*Dú0aéu"âoåo(e(¯%c:–FЮ‘a`pqæú±Ôp»”ˆôw‰²¨¦²ì;¢€pĸÛ"@üÐ"J9$I¯HQïŠ6½ãT*Âꘊ“,ÈÌ0™ 2Íî´B'}Q„<P.Txé¶îÒYÏÍ:Ä)ìóJ¯¼r¨á„£ñ•)®,È, Î낱^‰s¹Â¸Ò¯9«¨73-Æ(™æKA¼#ÄI æYÈo·luèQ\ƒµŸ|×DZ.zâÔ>D…#) ý1¿=?°~qìóáfµpÿ辬úú¥jt¥Kd¶‹{ø×ÓÆ™:ŒÄW·—ëÁ³ïö~ÒC#á³ÑÉ=ÝóYë×ð?‡ùìçþ“ŽE"Ãôr,Ÿ´€Bðj~¾ÏÚúd;¿34V?2¦?Pµ+î©YÅÚTI#)þÛVžˆÿõö\Ìý‡é-›Ç§Ýê =õ^1‚9a+7>”0âËÿÁüÏþÆׄGÿ3ˆfæ&“÷8L§ûy}ôøêùÿÏ2Wæh´¨† ê¬B?À}ÁÙ§ûÕÿ‘¼xh=„ÿGÁΚû„z†Cãmßñã÷A.zDÿµ÷žêgÔ7v+Ÿæöü•éÐ;I ˨^æ#§¨0WjÔ<bøO«Ÿ8-3”J~ÅÝ)«ú¤H(—üƒÊƒ»äAøvˆÊÁDæ\¬{Ç5^Ö_j(Û•!—¤rLF I$/`#)bˆŸ¿ôœSìϾ\Úóâä‚€Ì4ùÂöˆ#+oükÜÓaFf¹#*/“Ðþãºêù?¾¥(6$§‘…÷¿é÷=Yß*õ«ëÞ$?i×n±ã’é)ï ‘´`9ÊŒH¶‹“¹í6ÎÇW¶}F~@~ô†P`€H‚#*ßMñ=D>ëµmR1+‘7I¬˜ó„檻5R@é#*ú) h”!ùðnÅâüñqN5'…’ç]$î¯"ôKøQkÓõgÛ§?ú=EDõsŽÑmr„‰)ÐD„ÕVcL4{ÿº•xœuÌ]ÄhŽ,`c.[ ¡×¯Ãˆ#)U¯'ˆ’5ôÞˆ7¡á‡ gùFe#)~z”)Aa`A‰Ö¸_!}ª¾Õ$z§f-ÈnfAaA¾ÉŠ› äAß/p’F`öØcòÌ’ÂÉÎ&k)°°EÑŸ+œÊ!ª]‰?e9#)XÉÄ޳Ěü4P)Mµ1ç@øçDãÕÜÜÛú9]½û¼¢²ÿv^ùû#4ÄúÕJXG2Átñš‰+Àšëï{³à–Ke(Ö½#+¦±©0½Q,+*ÚJé׉b$€XŠY-$R%Ë»‹(4g7½ªøn‰¦m–W]”ÆÏfa”ì8qœuW Sñ>²D1Ê¢+™!ôä:‚ѯ*µ9jW$±Â3VŸf)ƒÑ*lW¶Ø™¹ž(¶ößyÚ“!Œv¬ã¶QBTTr"ÚhŒ£4€¤ðÄz¥ñI‘Í‚€A6Ð4JPoZ 8ë}3èð6#)ý3TÚJ#…þlÞU#*>”ÕÏIÌ{•œ^án;ƒ· {$PkÏáçŸmaµÏ!|"ÙØýQ|ˆœ¡AÝYJ¬ÛÌBùiž:#Ù]Ü4Ü õ‰QG8ïÐËÎ,¨ü¸Ó^žÉû·ÑÚ@´2ªô8äìpš”¯QVŸ”s;HÙFêÐåõú»m?Oßu„`W|ÆýárÖ@#)‚HŵÓWÃòxä¯8Ã%…%¿±ªýÿŠ$–cÄU¤áƒý^oˆz´î?kÁ•·1sü¿×uŒˆÒ#Ïn:gÏ]Ë5|›üOÁg-¶ËVó©ü“SW3»M‹+ïê³Ä"4kŒä]]!c+óÉ|]3‡îSóüX{ë^%a0åàf–x8y =1\`–…ÚázNö}Ë¡ê˜9„)•×h¥Þ²-#)Êàó)ÜF"ªIŒtÀ@Ò*JL:T…ûûqÁ[¶Ç½9Ïy–ÃbÊSN™ÂDÆK*1A`aê»â}×Ü‚#V‹D#*þIeÉiïõ÷8†Ze¦²ÈÑè\ù^ãç! î Ïný“†EmÎ׌MoݦéPˆ<&ÖÆG‚·¦jÇŠ&dÛéqê¶Y-Ï«½¥ºá8žkŒïuZ7s$qäL4•ŠÌÌAv#yƒH%ØXjÇBuãÃ3t·gÏLÅù‚iq¿TGÛQu¯Ãàã0èD_$k‡¶:zàÔÔ¥‚ˆìgj{9i+AÇDZ#*ˆÓ)åªMG¢Òé3•}£Þ£Ç¾wUµs¬vöâõK¹y¼M>ÞKu¬!ËÃ:q§ò7Ï6žÞŒ&I£¾®6uÙüTÁ3öÏúpø$e)ŠV#+¸lÏLî”ék3©REUДZ˜•D#*}¯ÓÚPŒž~}œ^p•sMñûã—×Ùq=øƒrIš».¨Ž™ë{?Ž Ë{ß·>Yž0ý»BU¸‹¯!"cƒ¤ZnÏP÷áàíÐ.¥¹fý[è´ú%¶Ó"”)ƒapûÍ¥€tºQªC:ËÅ÷…G3fºäómï.¸ôYÖû´rä&Kˆˆ}µáDJ)6u÷mÃv0° Hpð#*Œÿu-}~ŽáÌü´Bƒ°ô yVÆU‡Ì¬#)ŒE©ìì{ p ]ù|-*u¾Òɵ*ƒ,¦ }…›ðžrÅVì³òY‘‡Ú>Yôÿ±ó\¿l™1è|Ò~Xc}E:‘òE#*ÁÃíô‹;8ؼø‡Ãî{ZCkNØirrTÏ8í—=¹Kýšºš·…#+QËiƒºt"íÌ}’Ʀ{¾§ÍŽî7Øþ¸›ÔÔ^ŽÙæoæaÛ©å"žç±¤j$ŽrRf‘kÝ¢)—'û }#+–tÇ©#*ŸêWLé¶K¡åŠ«Ú[Ï«S;uåÝ yaqZ‚š¦—Ïïù…%àqu¸ lv‚<÷³d-#*6ÞÕõÖñëÝSƒÈ±j‹®[nD|^ÿNž¬¯¥2PsgäÝ&<à¥W¥./Âc.M;>/déMçî¬y«?RUTA)ÃSÒÌ—$½tîO“ϪíRJ°¾°0`»í ÖùÀÃ^¢‘²#)m…žŸo‡ƒÄIj(‰úß«õüC}f¿#*ñ‚úäÐNϲeQb©<¬¹ #)‚p¥£ºIåêðÙ¶ÿ@¶åÑk´ƒz¨â7ès8m½uõêq/ÁêH2×>t~6~ÏŧBö‘É*µE@9Õ[>RëÂLQ¨®¾â (‰ËdÏnL‘žÙ„·®ßàFºí@ìd„ßd0ÝJDpæÖÞIe!$82È#*f˜®õðiüûYè;€¯mˆ!ÚFL=xíGWc'd¼’ÔzuQ!jî߆ìøaã6¨ñ†±ÃšQI ©`º;{ÚUR&ƒ‘nFº¬YKªpÅÑxTRâ’µáÎÉBê~ÓX:^ðzmVÅýñ’PG<òuø`06 ß`ì²vqµp•lïÃ#*&s\]ÛSÚx–hÁÕ´xïQ¶;Âf`A]Ò@sÊy‹„s ‹%(tceäFz¥¡ÚíXî¼#*kô¿q†cÞÚg~zw4–#ñsqág ò÷·‡3±Û'ZðhÛ«ç»i9”áÑ Êlö ¦¦Méu3µÚ žáÑóëUÇ)ë#+ÚN½Râ 4°Ì…Ì¡A¥Cl¬Ìݽ\"S#†ÞáMÂàÀ<‹k£lÀçax”˜^ù¿É„d:0ž“¤`ô¦óÐöÛ-šõµžŸL2ÆüYÌà\Û%¦kccˆœ"òòɪ"Ì5e±î8Õ˜:È"‚—55¬w«„¦«È§'CaunNÕÈ’ŽIØ‘z?oƒ™¬0¿x„OšÓrà|¯ÔIR¤êÛBŸÂ®¾È†¿%rúÆD·Õhr ÏcÚ+k(HAA¡ÊƒqN<ºOy^¨©æÎ /yß¿£Ù#+ÑSx##*jŠeAú *ù‡{Æm˜r:ÍaMÌ€ bàÊ&’ÀifWÍçBùVd½ZÒ>…RÂ#…ì2íÃP.ÑÚí=BÛriW#)l$; .…½ö†¥ª¹ TÈ,{«?Ÿ¶ƒ6PH½ÆŽÓ‚¾ÓQ 5¿¤A•~Qw|è'·´xÙ”:³µ:J”Ä1Óƒ>k]N¬¡fO°c+lr]h±{'Õ}”GÑnÅ°“œý%U°GD‹B-CQ¨uPs_ WÔÐu[„Ps¤Jdàè¬âäà&.B$M·§#*#*b©ÌÜ‚HlyùàÁá·=Ýä2M“¼Zpê܈•²êwFçlZÃmËoXBCQ‘O±b×¾ÉDXkSÛŽÝ/kq6Ð#V©PKtÆùÄÅsúTK+AŽu~ÙÆkÞ÷áñm7©Þ„:¤Œ·Ã²aïÄ,«&q\‘$@µÉ¯×ŽçK#*J2uvïݧ¼3¥›úÇÙùÜî©´H.b2¾e%k€Ûš¢t”=Ð#*cžÚá¤tèá—n^`»¿$ Mu^Qî`£—¾m¶üÍclRŽ5§:Æ<¡Âí÷‚§0yuv“Y<€D¤”ÖøõBñËø–-źy~éoÒ“îUÆÌÂÈó^u¤Do3q"’Èò–¥;Íbù¶ì0zß‚´.žØhÍt¸hBÜ‹m£O—rÎv@0¼ð(“ã‰KI¶ã[R9dkÅÛb(e$òUÕuXà¢ýºàîêF®½nÒ hi¡Ö4LBc¬ ¶"P¬d#*¯KG`™‡Ãë/íò'8B‘Z®@Ü0ÇÁð঳Ã\¸k#)¼"ˆpáÆ÷à#5žÅE¸ã` sépZ€Ò#+sóóõ¿ áÌp²ñl°(3"ÞȉïÛ*+9O¾¨o†¯…x\µTJjY³_$#+·Ôºzç2ߟG5.ù-ºÉòœy©xwí,)³û¿FŸ£ßíÿ8÷}zät ço¥ËõOèÑGסQ̉'ü+Nxá;}sé±Ù¶ªkò>I¡VsåsÝîBŒiT,ˆŽ•[²…g$ã±ïȵ4MFñ`{~> Ž„;Ï¿-¼ZûñÌ'òùkjy\ç³PÝÊN#)B1ùßÛ¹yy‚Ì#*Õ£N'Ôe:ïÍŸ£÷/_’îÞø£ùyG˜ê<¥xÃâ“p¼¼þƒpü!`o²]øEè(,DýcB¬ç†VD#*&j?`ǽŽœþ§ª«ý™aÃï#)JúýؽÀY²Të*”Br)SŽ0e–Ç÷3Ý_‡úÐøÛÝÇwåÙðýsBO܇õ…ô$A€ašúAÃngüÍ¡¤FÿÈÕT*¹ý¤º–@íþ]jùÁêa#«`¼4‰E€.“ˆ£`õÚÀóP¶ÎÏè-ãž··²Çç…Ûººoîºk[‡ûªœ3s¹Ç—GZ0¾î}ÆñSR‚f†Ã±.AÞu»·q#+ïÜiSÙâŒrk¼òÔ8”*¬™ðï„Æ8BWŒ_™J0T6(Ì kª£È?ßÈ3ºªu—ƒ]*=cÚŽÔë÷£Ú]1‡¿Ýèr’úþ³AÐÕZ;"µÿdT,+Þ.áð'Pš%–‘Ñ…î\þK¿ÚÍ5”9€Í=M¶ÏØú²@e#*C\„™Óù¶73O =‹Ð:Ð$A7Ÿgð.³¿²¬¦äè²wúKõÇÅÿj…]ŸÞq|tç˜1f©wùƒÕüU„ÿEô]£å¥-–GÃo==Cd¥_C™. Y@#)¥`Ê•ätk‡ÝýÏøý_?ÌWð¶ârõ0kmd#*š£ÙPŠ6}œunÀTí` ie•Ã蘇ñ½Ý Ði#+vBÛ1>â’m>“StKçKP}Ej¿÷éÖIÖzÃ.Þ^ä/Cý£`Âx„‚kò†þÄ\½;&ýTZ4°A)rœ±»†l¤nô‹#)Á°Ô#*쥬ò„†Â#*œô™nQX¢í$òÅÌÝj×<#+ǃ*CÕؾÖÏAþV|4©UÆå¡õå÷ù‡“¨!ÍìNä+¼éímÚšâ»VÀiö’j9(K¦cᇬÞÙü]ÇÔqÆóÎÂØÙêXK"œX°YH—q; ® t¥Áv¼”£H¿YlÙNé¹±©{NÜ#*®c”î×ráÁ6†ŽIÙ0ÁÁűB¯€t‡æ“ø É% w¿¥{qWM)'Ùõ…îNïíñïY@ô• VÛ6ù†¾kMú´¨TÎ(ÔXÐ#ýcNù ù O´#)@#+éÜ]k"M~˜ÚŽw겿Åß~Õ´Ä‘û±#ÚžÌ~X;ðux!×÷F2Œï¸n÷#abÀßbÌHzG©FFààŸû#'#+”0E#*%Ažræ”J"ÒkmbƒÃôÐÊ×5ˆj"²H§äŸ?br;é)X`öûý4®U{íðïÅ5ù€ÖÿmÙGsãï?yòƒ&G&mfækBôdŸ‹Ù®l2ëì!Ô²ƒ\"ty\ŸŸ·MP¦g7;¾üÏ_s,ûu†vÉNÿÐà ‚ŒGÚyé6:š$£OnKà,͆$«Ô'¼i=¾'‡YÐ!Óïýaµ¬¶â¿sÈ#+RÜc¸¨$ªa %—•Ëô›`êu)”]#)æS‡ü%ÃSžE›æÝÿĹH„™s™Iàh„½ÖÒÜ1©özÿƒGz)K¶¦Üµ×JŠº™G9Îj•UG,ê²KÏ—fÕɘÇìx6#í.M{ðé=†{ÞŠg¸6=!ØáÔFA„#*Ù‡<üŒ¡°è:L×H;Þ53ijlœfIbíQM²ó‡C÷_oÇÒ"ÏÔd!_”þèIÌÛïÝýÝ<Ÿ`úÎ'ˆ”±'ó%.¾%… ¬ºŸ9ºÿzlCÛ‡`´EzÄ`½|ÀŒcdúìªõ A°ðÿäL¨+˜b['æ#+´‚K¹Æéµ±gë¹?Ùú¾ÙûCcj¯Úz\[B™ÔÔë˜e$¶#6ð)ƒR<AÝychî‹òn´ƒ…€ƒåg+ô‘ý5z›V4iDPHŒŒ(Åã8²ç9¡ÌçŸH¨´µˆ6(#)4N± Ñù÷‚ìÚµ&F÷7yGÃÈ2M†€ºY‰¬!ò Óìû?ÛІߩúùöH4PÓ î¥6›“í㚯PÅÌä‡Waå—9-Eö V#+„û‡Xd’€êxïk #)ÔO½,÷—#*‹!±ßn›“Ý#+TÀÖø°úgy¶L…°ð\9I»ÈAÐÌ)„8€È娦ŒÔÀ&feõ"Ù¸q`vò$íî+à™±š¢þfF{fUXSQ#+D×[ŸÛÁ.7~°Þq9'gœ\²3îz$:'1æ`¨g–³7¨-£"êaˆ@Ð5’HŽ#+™Èþ®¿¸éëÙrÝW£ò@#*¨\?£ðÍUÊJIúpVËšåßËý½ô6ýþk§¯]©7£ #*DEAAëÃUWFoü¿;Æ!¹á’ÒïQ¸EŒ¦h•ÖFÖhÖiëZ…NA²@ÇÙš|nKhjœTÝÈÙfW)H†‹5#¯ $’LÕšÅ&*eÖ¦¤´µ¶ÓÌÕÍYk5q½2³ëÆ]CzÂÕ¯5 Õˆhk[ÑM3tºÃ#Þc«FYF5ÈÍ6Ó¯í[”½½}ßØý®ÌjrѵDEúóRàL°þ»Ðef“ú‰¨õnv#*Ü·äœþ<[œtÖ!;Æn÷'ËjôfÔwÄQ4@zÀ«á#+î.•*00(E,nʸˆB)ò(@v÷ûéGÛõÆž˜‡RÓô»×hÞ„CŸeK&¡ ÌdTE0Ñï\ÝÆ%¡ÿYò‡ï,»lÈ|‚&#©´Ô6@šS¾¢Ù¡UÞ¬³X0IA#)ä)l ¤Ú>PdÉ:Û·ÆrhY+{‹„’I¶#Wyô]ãÉÎ:¹e%5º&V‘ŒKDux#*¸w6Þ¡Ãn®4îàsø”øñ‰ËaèŠJ#+Sõ_³Sª/y¡X>‰p2ó{²Shža›ú‚Ö,ØÞs|‘Ìát应Áö}Zû™´6aYû7箜fíJnP.nS)QMÝ´äÃ&a‡{w†#)xˆ—‘;Gƒ<y¤ÅÞ¿€_ˆWQAgÇâ¢|Q:‡è7¨#*¯ÔŽ6©†é6Oa!8zuî+æpüo¼k.%GÚ+àp=xâØóúÊ©EIJR(Šo“i#*q⓼o½5ÜBÙ‚†è€»5¥„KÅ#)Á‰JÅ,éH)‘ó|#v#*r#*í+»PÑý õ“äé‰?#©:½Œ“Ì@±Z#+(e,ÄO½H‡óE-í†ôN²²7Î®ï ¶kñܬ«pvŸ=À/’uåˆaGé ®,ü¶p§_ʉ Âj#)"ܯéùÏVúü–»›ý ùõöŸæ?§§QB#1ãü9Y» ý•B¿Ž?&Jõ„·©ëK>ŠZ ¨—ÚmDó±ÈÁõd\³ö‰èƒèúÝn>i=Èì9ÇäÁêe„9{ôM˜dP‚Ä"¼èH}#*wçs8í{ÍáöAÏ|?’cr»¯ô?IÖ8CeP}”»XÀýÓóíxîÝàŸyÄC¥<#)#Þ)Ú@‘«£À#*Ïj‚L¦IF±ûƒÊÁŠ zwóë®ÿyæ]ÍÇ£ÔiËhBµqàGŒïâSi*›oôŽ¦ˆ†»N]²D‘YdR2>X9úÂε€é€w '¸È gŽ«ÉÅðû´7;ø˜Z‚>HtÁ„€ê Íå¾*?©ú€¨}]]%þì½…¯ÿx_dWê'Ô>§Õ&³ÛXÝ1UD bV¸Ð÷åÊïnZŽ¯®ßôCËàUõ[ˆÔÖ‡¥LÔ1 <FI$ž´ª§úŸ@&ïáÔ!V+ ]°4d€>Яà‘I>Ž´S®;µº¶´öœ¾™GW ÁÕ3çäê5…[¥ÚØËSßãÅà_B#*—/¥šT† ßà‚ãôú·£°20®Uõ*ZÉ3#*ûî#*º|½5‘œeÙõ•_Á¬ðc$Ÿ=#C‚ïvö#)uç0q`±8…‹IjúoyÇî99™s”0d$Ûz…àòûLàˆjb¢#J””p2nF!ª~ô2¦s$q±»rp‰`±'G/öR¢qÙØÖ¡CˆJOg³îÌ@Ä<ÕDc ’*‰RqÕëÏ›¶ÐËFc›ÄŒnFÚ=¶7°—;žet¦çÕÃÔ›:y9‘LÂ*ðß<5Lñ†$Ed›˜P’Z-hU£_é; ½¹r%"QCôò:K°õ¨Õp »lªV#*pã:t#)÷ Nÿ„ò>0O:ÄÐ>|Þ ë#)ÁÙ¿„éd@<U{M¦¾ÈÿðKhòÀ)àSj©Eîx{¾w‹úŸm~Œ½ÜÂ¥óøb#*aÅî,nµOl–öûgóÀÒãDMIþ9QG•IB¿ô8Ør†«xib˜*ΛHÃ#)ob£J”¤ªRUjþŽ}]³ÐvÉÙ<mlqð£Lq="/דKâ}¨Dô¨–þošIzC[a©†Š?â™ûÈï5=¤Cg¶P©ì‡%®õ—ßøœ§3ìF¹;ÁOA2©AE!P˜…OŠ›|‘õŸÙš.8\XÖ¨ƒW˜ âQÚ$N»¦æzø@mr`%¥~®w§¦Ë}¹Êïx ¢Yùkwòœ:¤èð±í0¡JP‡¨P¨Z8}¿¸ÁŒû%h¢"€9»Bñö¯NÉ°€—?·&ûŽ¢põ5óS*×ØÁÒ1ýsFð¶šÃ7.W†õõ¤F%ÇæF%LæV #)ÚI0*ÔÉœKˆ@Ì9ö²mªU¸Q”*Í€¨nÈý#+7Œ™‚(Á“†/`<à® ’1RŽ"QTÑv(&op~9¡Äƒ¢kš–ú±ÌðÉÚ÷õ| 4#c#ÃËcîÔÎ¥h,PMad'Qb¨r@)c)$Sö¡ú6'¨?”îÜjS3XQêÖX–J#+Ndؘ1H„GìG4éÚ-s=é8Ú¯T‰ÊmW¾…/¤4UXSà}w‰Th¥‘YPX-™”Fo—y[ä£\ªô®¤V×L6T& ¬¶ZUq*l¢›ajb†'öáØWÃÓõç<qíŠpˆ2ϳ)âÇÓ¸öPPo_£§¹˜2é$Zôžò)g7Õäô##*nÄŽA ¦:ͯѨ#+l‡ïH&·j ˜ªX@ŒFDa #+2 €K8.p©‡#)C¦tRˆ}(⥅¬ÄðRò” :ë¿“³xBJ<²:NyÓÐ/À–ýH´/!Ú… jÑ»‚unOÑ>º)'á›û74=#)6Ê:ˆ#)_fÒ½‘#+Ü<šNŽ€v9èÏTì·M¾»lsrEÚdÍ…´…@Ðaå}VQé=ydëìÁ‰:àO¬¬ÊLIsÜI÷¶— LPQEýë%Oðü™=gÍ€¾áiZ/w¡Û‚žÿsêº|ñþe6z‹è÷ø·¹ë©PüH_Œgü$MŸx´*ÒÓ3Ó&jBð°Œ!†ö(ö,’¬’Hû\ô`ú÷ɺœ;õkRZÀ51 ÐfT´<cl„¹I$}FþGLJÈ6„z¨èÉ#¨¢9å“’Ï«øצ2 ô 6)ÀÊ„8&¬ X>¨ÏW·ó¡Ì©-#7^Ýhò³¹ˆÒc«£«¦%~Ú(í !ìIøCäýÉêåñíTõôÿš<eÆ”ð!W1÷ô´“‘“˜wÄ‘‰–w3Ô†%´ UA‰S³³KÈ´††¦ÑO‰|O¬kü€ §Ø#*«ø ùßîÅ#)ïP–C2?½àúñ”»ô‘§úý‡§ì8'Qƒ³ÍÑEE%*G ïÎ ý8S•uúl¨2/Àa_©=‰Ú ˜óJÀbŠÌ*I€äûR{\©,Æq'TU, •V½Û) B)ñWñH‹‡æ Aõ8Ó¬çò#+îí{ùél¹|”„ tØ«H¥!Bâà ÄûÁÜæfèEqé:D3O@?¨,_³®}ÂrÝÚlÞA#)Üö«ØcÐ#+FÀªÉpHª˜PLôWQçÖrÉNa“êÛå¬zÔê ë8yTåi#$¼âïY:ÀÔñ#*¶)yÜÝa6Ü.É!¿XyâzK¨Š*üÌ`…–ýpçëÿR(ÛÉMގ߃”*|›ÕB‰³‰¿âUBP„ £§âÍòïh q‹ ÂpN°ÌX?ŸJɉ“3î!ñ_Ó£½Kíb=0“Ê:J@j1^/.o *T‘5¨UʇT†dK9lQˆ^ºõ’‰ˆaW,À”IÕ*ÜýyPr¬éœGéßFÆ|øw#+mÖ¨r©TŸœ›£•Ñ>UE¥:5,OZ†ÀZ#*²úwâ#+Ts‚Ôa÷þ¡óö%j:¾`|s°x<9xõ`î7˜Éíö¤Š¼#*ý6êO“g"w`¤P°jI&éTp2߬„rs©3:ÉÀWÀ îºó˦]ˆœâ²ìK†gϧ;H%CÂÜ'®§*÷w–Ë€>ùÈ;@ rAÄà»ö[g+LÑÕ°"ôƒš¡¥ÐEM`èw§A½¸Ò1!#*p Õà#h"–H‰@Àäo½tÛ‰óB{ÉèPíîõ½^àøì=[66"N ëiUÀz-ý\N<gCÙ°ë5#»J B;Ïá‡â}rÛTrZO!ØìBŒç–ußÁS(/¼bú‡ÎŒß>ñ¨xÌ•Ëàl‚€¢—/lD$ð»wQ¸ÏlPÊŸ__£Ëæì-:>¿ô 4-Ò€xB½ÔH",¡b Þû€gõrxÿ#T€ôûÉÁ‚¯½YÞDç¡ùì>’8ydTó**n¥÷–%,"pä~Ô6(ŸÍϵ¾’¾Óî å™êlÌ_è¿ÏÎÎŽ7£‡ÍãÃœƒwà~ŠíÿÕoÇá{Þœ³âÓø©uä7k©zs€#*D’D¦’#+˲²š´*mØØæ-ôh ƒà÷`>¤rBÁ#*éA«’#*!óa´#*lu84BÀG :•R‡ì³¸ÁW¸»zY€ú~Ä6¨±û"²g „‹,ˆÿ4¸:€º¢ö¦Õä/.Ô˺‡0,Bd% ¯hA°kò:Ž.îY´wÇónÚà}Z†Éé]ttU‹Â„_ ¯;’Î ±U2H?á¡‘?zÊñÚV íC¶È»Æèì¡,ä`Ž0Ø67ñ#+/@zÃaŒBv%¡EÂÐÐ+ãèõ{ {š³üw¾}²dP¯‚(ŒšA*Š—Jn°EÿŒúÞâºe·pŒGôñõÙºÈÀ®Þn]¯C9Q›pë’]q¾#*ãvË´[ÒQ¦ßó‡ßõ‡…1ìÍ<¡kCS#*…ÚõJ0Ä8"ï/ÅåñµzÌU1¡š(¯~ûºñ÷ºä nî¡xÏ<¼"2¤š+wiŠŸ_‡˜ùÃÊW;X}¡òÓÙCí™’C ."²À=‰Û='È3àpFP¬}ñ„Ô¾Þ½ž¼#¼ï8&`Ûw¼là?:8z¸”Š/(…AJïõã©8ògàðI€œˆÊLoü÷â ÔÜz΋mað‚=jÛ$„@Ë;]wwe}ßؼÝçÃ×+!kÓüuü7mQ8ú¿€ÃcÝBaÊñA£ðÃD«BOoÝ®˜p¤Ã]ŽÐ˜EŸÎ°6ÓÔJÓalU6Ù dˆcc.ª @M„[™§æ:ÔOíÅçpOO…BSAÚ]OOçÃDŠÈ.?#Ã0Y?cÍû¬áCU*9äÕ¸S^t œ§$u#*¯´Ûã8ÈoùøÞO®R…)ÊßœÔpîþÓ(Mžÿ®ýRÑŒ¨xóÜöÉW¦¬{*áñGr`ãÓ©rd/ú!"Cò=¨·;Ì»wûu%ç#+¿ñj8²šÌ$4̨g@¸ö:>®Ã\rU7©6Ÿ$ù….ýÖÆw‰[øÞS Z‡Ö……—Üh¹Ò‰§Â&/—ŸÄˆ¢€«#)a`“d<ªào¥Ð NñÛgí‡P±±]Q,EÝîê¶DŽÈôýÇÝcòN_Þjìd!ËLÄ»³ØcÐvfh@À’ƒsÒ{@è¹ë]ÁhDƒ,ŠðÛ}ÐrVa±ûorXýý}/Jr=ßB\~{ÎaõXCò*^Þ¢åôîW.1s•´!É!å [ëõ0oÂõ,–õ|º]e®jfót̶#*‚#*]É*EE’ËF¬˜eRÙÀC,D%”%DÊÚš®¦å_hq=[VkAU¡E`<50´•3®0••dzØ&ÔþÏ¥š©Qÿ¸f8M•!C[1׈0µÿ€~½ˆÌQ-1ùª *ÄóÀ÷¼°SÃVo¶„ÏYú\Îf»Pº›–ô#*DÈRHè2ì[h®îVäý&XšÀö`io¡úpKÊ÷”ÏßþGâOøþ–ìè±âdfWòp+jõußó'èÖ¿´ÚwµQ’±FH+ü\"þùzÔ12É'C«dåR°_ÜÓüìÎm¶þN ´ß!îðåüæœûxš¹×¯g<L!å?™PhRZ,–ÁˆBRTzáŽ?P€[Ï‘khíIÓû ¨¹Â ~§ˆ ‡ÔqåAåÛ:å½´þ¬jrõò4ÉwNÏλÌ|Ÿ—aߦ:x~ž*=V2á¦êŽ1UÖeŽãœïgòcô„N??2°‰×O—N÷æ#ÑŒ²‚9F‚ˆCýØœ¡;C#¾+ïr_åìžµVPq#) „1¼#+…AE9{‡×Ä7Be6BIH+Ÿ·ƒœbHÕ|?/ßõçn,únQ<ýX¾†U¯ê¢ÌŒ·‹ðÕDñ¿RfïŸîýÞ{ŸîLÁAè/³!¿,ð'ɤóèÔ}èÅ/õL«™’C¹Àø»0|E:o‘#)øìæ·‰ùŒMº&Y&\Ó{í¯"m“|¼<Ș„*6<}|¾ù<Ž=`͸_àÄW&Ã`ÃóܘÍÖr 7´Y¶h¶‡á©Ó3ûŽÜkÒ=ÏÌâá£[ô¦ÉeÎô™°ºHð¡èuÌX+óŒeÌ º ¦ùŒš„ˆùäD™gZÄx>£ix|šÃõkÈy!Õ->;2 ı þ²‡‰^'Ásþ¼"*St^¬lˆoº<¥ÃÜ3ˆï@Ïèk¸(˜ªØP4îæØBñx"s£Þ 'Àá*7É槒Oéw¬éa* îº4/ÿoölrŽ]Úlo:¾ÚP»=¤?W×nÏ«µ²1ýÜB‡bᤌÈ*£¤¡±fv5§™Ã Ø„èŽtx~J½²íE8dõŸÃ_Ãy ï%wBwsDÀ§#eþÑžNÑ¿PÄh‹Ý8”#+,jí{(*]{aÁçÚñïÈnªöÚWYJ}ù…GJb0âo~ ôq½±¾ÀcÑØtÁWþæXþšèUŸ¹ä‘r¢Ž}²oëåÑÛãzo¥ƒ&¹DY«-·8B¹u¸¢ñb8§…*ôÝb Þçú#)ÆQÌù‹€üAðÙÛï[T?Üï9Ý =d|í0¢(ºCÄx:'æŸÎßÎIGG”HNåwØS‹îý+éä-‚¹²¸5‚ñk~WÄ9ÑÃEÔÔ0{}#*%{ÕU:V^¶‰B4ž§äÑÕý—®ŽïùšxVsÓcßœ† ÑÍà @uTfköºýc·Dà;!Œ—åxØëæ[[±SƒufÈrÒ©›óÜái£¨ù— ˃¥a€»:»E8W+ç)#+QAE`ÓÁwRwgÑðÉyO†áÕ&iLq‡fÊeêížÎºÄ`¯e®¬á¡n‡—²Ø€Qí³ézââù¼¡('ntZa¡´¯âx_&ÍñzŠV˜*]0{fXå..Ž{·pHëY†÷Ø«ímåyï׎0ì0'w1#*Âh\ÝÍݶXë@þS®Ä.y~yÂešlâ±2o›¦zÜókS…Tõ!Én†"ELÑ µñêé¶jJˆmÜÃz#ŒsÁ¬:êúFe™Óç~¼@Ò%óqìT³l‚)œïÉP.…°†ôlq\g]¤ÀÂÝ_òI#)ˆ#) ˆpååø¾‚ï¾^œ¸# ÈÄTY–ÎÒ^¯ŒgßT‰˜¿G“ëöÆ&Ê“°¿žf»g¥c"¼#±ñºéÉë7wÍïTdFê´œ““Ú“sH†‚©´¿¢Y©/åë˜ËõÐù<ÏŸ;Ïåw9×O¨Tg¬Ù˜z‡úB| ü#vÂí&w(µQ@Vh39T,4ć#)á[C !P@ˆØ§°¢&Ýj$EÇ?U×L~¬+äüøJ·ÚêÈä`P)Ì@ %ÄbxÃ#GR¢6Ù†¨÷iœXOÆ5ñ(ëð£ÕŠ1Ñl}ê{¿áÒ } õþdêí…nïTwñóͲ"á/ié¸Þo5¾¾{mF#øîô.̹朡„#+"ªO€¦cʯMÄB2Éd)°~Yr»Ðrš#)û= ˆ„\ËåÈR&fî°.²A” )@–åäøÁ!ž¸ñL:÷Vúõ³A$)‡ˆr…Rì¡ÍIÇ£ÈzG3öñå[àœJÃnùå‰=¦¥¹Bÿ'¹ÙF:”~2t&fHG/Ÿ;™ÛBdÇ’P»vâ¦LÈd„Ýd^äÜËHpíå1Ôz<½_Þ›;%¦€€óÌÈK¸yîÒ@”DƒX®vtÕ!p}f¼bbÎ!ü|X[[EÈ%%O‹v¦‡`9ìlÂ=îuôgÎ3‘ÑÏ‹ZbjD)Ï={5eˆÒ/Ca%BqævßÑ`ì5$fªE¤nnL/«$ytįOLÅ\†É´É1V—S#+ÕñäëˆB>rˆŠ„‡/Íb§Ê{_#)ŸŒ¬s,Q`‡è Š|ìËêÿ\|ßöýþŽÏéÿ=iÇhBФ+Õþï8_‡úëƒvá4¼þxÓÆ)ÓèôzºŽ´49¿ÙóEÀÄî…Jûþku¹j‹"&l ƒâZ¤ý_}ÏÝûПú‘óˆ ñMa§¥//íÆE½5UVóž¦ÔÁ„þLųq@sÙ·8@u—1`6ìð2ߺ›“`S»P#)Qw÷œ#*…¦’•#"1þ×jE—€vj›¢¥Q¯¬Ë;I|r#*nu:æÒ#)\›kÑÕº×u^âøñLšC@8j"ÑÜôYq'àÝrÛ[Ê¢«ÃfÑN=Ïø‡3¼¼'>#Ü€<4Ç_<Pwâ¸4£G"HbzÕ#*ê™8i|BšHàÓt×ð\žã·÷v˜Ó‡çïµ·e±¹–ÌT¹aë íW¼ëCX B5–ç6±ƒ›º„è<OgíJû>»wWñ¡g# ,)¼Êˆâ'C·gsˆy~‡œÈQÕÔk¶‹|eB™mc¿ÈȆ‚xeÁèë’ô;+#*pÂ}‹Ö3ez&;1r°íöŒú"•B&ªqdŽ³³ôàð$ÃÆ#*@ cX`Œ‰¡ŒD€¸RAšZ,¬à<vz ÁÚ4®úØÝ„} gƒ1V!{QiM¥öŽäà¶5¯v÷iòØî(‡—E‡£*¼• @Œ-iI-Òí,ŸaëNa¼'™#+x+@ õnô@smÑú #+uçÝɬ‹—è÷u^én³û£(¿†¨C›èæww®¢ÌôYݸA€q*„$‡ÖC¶Ûv¢:àURZ&FqiÅꉶ"áJÓdËD¨‚A €Ðz#+ÔC.2êþ “0ü¿pÀ‚D(ÜšµßÃæÞ&Eå~ÿù?§ÚÞÒÍfh6‹ú½ïu‰yXÚÏâó¡¥yÒQAú¨–FÇúÝ©ï#+WÖ‡(@ôH1®í»6Üò„¼5ãîÔ ÑPÇ»vpÁ“Ý{{5€I°*D(9øìbõ–ôþƒÙØl¼Š¨¬DG•ówï½ÅŸ0íÂØ•åÊU.J`¿pÎËFÎŽCšÁ¶"`ŠÃ•‘År÷â¶Õˆw…<˃ú]Òåê¨$NPÍ!C´Ñv¬o•$¨4òmú›‡¡ÈÚ¨ü¡!ÚÎ0îúcKNÄ5CSXª©È–voMT0|D¢æ!ÉJª?9Myšh0È<ˆ¦@™oçŽð¢25¡Œë¯Æn@"j^À·äØÝÛnÁsð8Gln+»‹ê+ÊTîÑp*v ÙÓðîhýGvîG-2ëàgçscüýkã.̇xžÔ©¸ãh)´Îë&D0aõBîÊUâèj#Ò—:Î&e%•ÝáD‹¬së·.¬é5Ĭ^r)Ãg[Äb.;a“3¤ðžœX8:7ƒo<û(‡yú»ó·®·…Óv9@îŠR´’¿»ë¾uÞ÷ÇŽy[o-Y©…’Clíf|^sDR7c‰Ék–öÞfkŒ#+ô2pöÛ$‡ÀÝÃBV¿òiÔ-áøn퓤`œUÞPòaDžÕ½ÜË,x<Ã\éئ…é¡èf‹¡X¨¥=qmË`áã´&hl#*nX r:Žòêõ¹›XXM±»š¡ðþŠ¦}¾¿n£>¡À³)€Ùæd—3#)a8wIçåÓ¶=Þý{ùoÌw¾ !ÇaÖ*’ZÇ|.Ýý6œ5ùçs<JÖh©ÛFfÞŠQEAyx‰O‰¶#*Sw@ä#*£^j_€iŸeQƒBÇÀ ÍbÖ¦æËH¥®ÁêAçÃzi“k5„lLç㳜H ES-ΧMPÆýakœ)5âËD&Ëaœpë+Jœ£×â=‘+‰¬lêÒBuŽEâë¢^»bhlo¶o? ±&¡6¯LÀENÃ`á0òI“D¬W¶ñxÆÂa;´X(‚`YXj60ÒƒÕ$9gvò1åNž!läÒzg¼Uû‰Q#+ P6Í{Ú"!W¬ÄfáDõ 'a¡Ôf°vŽœ©÷c‡ƒ=§ˆ„t—¡"™šZ[}áÀzu€ÇRÓ˘#)rxdˆ3·‡ôU0P“$#+Oaº)DÜpá™7w7M‚o¥6È-ÁKáܘ–®‡†äSP¶È¸BHï$ùǶ»Úòˆ=ßaɳ~#+4ÈÎŽŠ<}|¹M™tîèà5,ï8NÍrÙŸ6±sšTUY) sa#*=3©zß;B¨Æ8<*Í]-½Sv—{¤O#BƒMp/‰ ¹2]¤Ú†ÆhtPEQ‚¨*4po&&’†rò%j˪ŠÄ”…0ì7—)‡#*ײ=§sF!ÀÞg&ø(Pò(Xø8œLÎÎoE”¤u3XÉMHI Ëæ×xªF…¥zy™ëÊ´]N‡:9pû~o-fSlUæeÇ™™2æffXÛ1¬«2ÌÉ%Xí™è.Åü5[ZöÝm#*ˆ;´qÆg§]¼ÝówNÖb´iR¾ÿqD¥’“<8šZ—ŸKìõ#+lä7¶#)Bô6;ì®±ãCÄóõô/†¼ê°ë|ùé“®e((Œª*qå@¤©—^zÖsϦ&#ÉèôáxÛ2SàÒæžñ#*ÈÄÙ3=ªFC¶hYCS8G%¾œÊÖãç•k«QÇ·vfº92uìE±£Ð+]»s% ¤¢v|»…%Üq¢¥Ýt·Æò:ìJcè¼¾ç»JñÔú¸‹AX˸-Ó#Vy9Z‘©E„&F ÅÄ,Y™ÒKb_NiÖÌæÏaÝcê´¹{* ØŠq¦X.μž°ÖM9$;ŒÄrš)AD¶÷F47åµì Q—þ`PNs¤ãª**Ñá^bsð›É!Êéá§ÂÃå‘îÏð¾#*€kUèÖZJ÷háœËwò*¡ˆÈE#+©‡ RàÚɘ>ŸZ“Ûw}ß"tvé˃^YxòÆZ‰Û¯½°èã˜_€»IWf«QßÌJx×£{Ëq#t:eƒ‹9o&uÍ-ÈL!ñZ£ZNV¼xn4:( [ÍæF¦:©s¬ÚVðá(¼#+#*daÃ…ÓZ#‹.\‹,X|üiö.d_1úΊÂhpQ¥Eèq$5À™32s¸Õ]"zuÂhšuÂL‡Z+[^Âèì"H‘½÷ÌÍ¡…_E{_çÑæ^ßY-aLËn1¦Ã¹6W&¤Ì’Í šc9ê0IÐèªL®hs£>#+`HçAëÁ£²Ü-ÐÚDZa“À#*Â\'Œœ#)×RŠËÈ4ÎòPj…†ž‹xIËÒ–Š¦• i‹kn»°XèÜa’±Û»€ÀšG-¯9!üré‘úÖ÷äÓ=ÇlërTÇqÝ8ž¹ v;= ×QÅL_n+5Eªè!‰‰Tß`ºèæyùXwò4ÜAuaÕ#*!ÃäÀD¢Cs#*†àƒ5ÖT‰¥‡p”T»P¢t!¿‰Ma!}Ôé”b›I#*€qÉÆpã#*f¶MÃQCƒˆäJoH^Œé™–X°ÄK‘a#+ Ô©ƒÐðÒcHs-5^Á¯ƒ:F¹õuF¡‰ nÜ·ù'b<,!ÑŒ½SŽÐ5Q‘®\Rw|j½#€ZqØ;v#)cDx¢M]ôQ”¥’ÆÑ4Ùªf(G1ÐЄH.ò,‡™Ò85IF+:5_`d{ŽšäS_OªK[ª3‘¯–ˆÈóz‹#+õ½¦tuö¡‘sY‘¬¡´†Åêëe+Ô\›©\äëgÃÅ؇"„2åEÉ3Ý Â–úòÌÞ«ôH…ó†Ê‚O„ÑJ°{ $ˆ÷1íë×ZB!»·B¾ü@òæÒ&9Í—c|<¼Y^~166«l ¨t›V¦„”—™_ÓÂøVN×›òòÕË<ÞÆÐÄ0\`±#)1PÈCnW¾(#*¤›‹*É&Ò`%·mĨ G”±@PIúyÑ»#ë0Lÿúܦ¼€d—ÁYôܯYèTÊ’˜S²}¬âœkl‚奵¿¸üNZM£lìug膥å×Ûë›5§>›»l—:¹ž#œ'¢ ´‰8*ñxdø¤;D#÷Ϋý#+Äd#+X/ef3Òø®+Ñ6#*‰ÜÂÀé²éÝò¾MZà†8ßg_fzÐyAJÒR]û«ÏEÞDžiÛbÑ¢a€ #*‚;!ÝHóØ=è(Q6ÿ’o!²UJJœP—#+!`Ê‘ =þßkÓŠyw¸+ñ#+O»×†z”_ü¨ôûfY‡ü?×8tEd·rïP#)€%/Ø„ƒ·]µ¿?ë[ZŸž§©v”¢Ù›&Qjþ&Û:û>Ï·õmúƒÌ”#*Cg þ«?iØt‚Ó¥úCéôÄšR“T¶™5JDÄíÝòZHÅDBSÃaJ7¹J¥ÏÑUÆ¡pÀ•hÛÂñP¦‰?‘òï{×-X¶+>¹*ˆ®´ÓT*ck(%™¯nxwsèHï`{É!!%-zÕÆw¤£ÊÀ>ÿÕÈõI#*P*A¨ ´Š>;iDG¥èN-¬Ž‹#)ߦñ;,—û•rÎòIêÖo^¨x¤)…="%ÔšÃã#)ìaIf]‚ˆØ¶Kdš4~;H‰#™òž¹r‚€ú"Æ*X4GZ#*ʛäȼA=´†Ò0"A€àà °>|/òR³¥éŸ´z5(Ø‘-hF)w]¸¤c ŠTI‡ÅÊ»{m|®Õé¶#%«X¡tkp`]0‘¹˜ò¶D[¡¯¯´áYQá—¾ÇÖ‡3dvxHIÕÐnbtÑ:2WÄŠ¨y{C#)X€¤0ô zLÎ –~ª}P“6T÷¨à<¹&à«zk½À7Åñ¿¾ÈV××¥w›áEÍÔdwiÔn\¶iüŠ/˜EÕ˜áÂ(x@*Ä #)4ÛÎdô2›m¾¥cj墩i,ÚT„Qõ†C¸Ž² /킀ȊH""«"ˆ#)±)ÀÇâóä’zÑmöKnS#+;eðQè‰B6ÒÕE“êKVlâUÝFÖ‚nbåŒ%È°†#)Ü Ýu«ãy^YëäWøäùŠe‘5êx×¢¥$˜·«»xôÛp½Mênk±(ÝÝ ’ðë¨×6ïñ<óo7Lwr®nY++»G$Mèh”%Ò+F–À”2 ©Lÿ‡>6õ|,ÚÊÏâ¾rlƒ@›$~ÕØÜà=³$.4¡°†eœÏ ÄET`{ æOfãòŸÕ“·í'‡!KöQÁ¨8Ë!G_–r=Žžž¥ù§ ïyîi‹ˆb^‘ð-ó8fá|ÏHHÁä°+ˆïÒ]§TóѤt¾µ)m¨Q#)ðI$/,V#*_=}…ZéñÝ4ÙþŠw¶€90Uo‹mµÊÛš¶4ûºÏXÊi˜ÀÀ‘fŸ,Ž&t9YþؼG¨#)N~캷¤`?¬ª‘ÞÕšÊóξ‚Þœ9˜ÛÓ<h\Ë•]ÁawD$‘-¶·1( éÁžhY<!Pp2DVb†4¶†›©S{¡‚šXR\l€Âçø±@1"u@Ø;Gâ÷á㕪ÃÕGñ¹oßå]“GõùÍÜwâ°â·”¹MÁ¤HMÞ~F»u'´üž1?°Û¸Öà«í«Uƒµß{`K /p]ëg^ûI g—kCó†·EŒW¼;÷Ÿ Ý«£.üÙ#+ýüxê¹Â=ï%¥rjÛˆ<Íž?Ÿúnó±Ä©ç¢'z<¼—°[J²A6ƒ?–I‰aÛw1KcŒ(ãQ(Û9„¨ž³ï‚¿?¯¢ˆBUJ©E'g_È9ïNƒEŽ¡bzi5RötÏ'!Wª" Ð5 º";…™©Y‘Eô*t+Ðe+æ):cbõQXs-PóBNÚ„éÁUšýÝð½-ˆ¶6&ÁŸn~5ž[Ř̑µÙqUDŽü·ÖÐðÓæ÷vB4²2˜•ý2Ñßnµ¥*ø@¸]t$ 5Š´Û%Z-’ˆÙ«I²‰*1X¬¬¶¬M6Éi%lkImdeF‹À‚)#)V1‘Ò~_¿ÏйmnžëüÚ<j¼¿¡}P;XI ‚W©|#)°ƒ“#)‚`;Ð>öøŒãàµãd[ªÁú°&ÐÿUvæB†ÃCc®*Žj Ô21¡aY¤Fñ6(¬°©~ø#)˜VlFTÛHmkŽÜ¢÷VˆüñHªcQÎ$Ê4¶h›x&‘4é_*›šJ2-#)Á0±_@/¥dÌ“5^=žåºïOlQQv›•)-v‚ÎÆ|5#)éÏzN®dÓ[µ‹mK§#*”˜‰I…Uü¨{X—áõ×ÖζÅÓŒ¶Ú¹Í¯žÖú>s²ÎÕÉE,÷#)X~6$#*ñH’@`Å~yk•ŒIj+W6æÉ\Öå3aJ™ES0j*Mlm!ÎÉ(¨‹~mû{ëw«ÞWÞ–rÁš†Ð8@#) HAdA•NÀ#)5(ò¾¾—Hýw¾ïIù=8·*i£|´†F\"nø¨dÐôxW‘Þ'h=×b’€@O¶°ùB4‡OšCÖÍÝÿ¿ëþ/ÍèÿñØ€žp|ç#*ôB">–! ŽŽÛÛÏ]£O\À©»zjûíÓm¼#)÷çH0còu!h QŠÂˆœ‘›?9ã3<‰î÷zÓitÑÓŒ‹•iyþ¸ ¹D9!6}Zit¨GøýM¯û¿ÆÃó0ð›É?ZOve%â°ôn®ÈeÑKâ”ë!oÝIÓÎvî9‰'ïÒ+µ˜A#*¬Zb‚¡žÿià1픉Œ;+.ýú‘©9¶ê!N}„—ƒŒPá×æÐ_Û®ÍoŠÉ•>å¸*NK2Œ~ò¿‘G[ P>X늢QC .ØÂoêzS¨ùuœÿ%¡Ù¸ 6E4œ2T†]%rNEX7³8û#+^´'øI” ëb¬½ý“yáóŸg@ü"O#)‡bÿc¿Fçʇ}±GÆí‡çÃÀæUÊ|œ>/×ëütt#+ª ðƒÄ˜?|°#¡1÷¨Añ>çWæÔ<óQ(hvïÅ®_ï§#*9W#*gN¥Ù³È½ËŸôÜÚ0Ç#)?¾"»:ú‚#)f§8È»]H{w^ ìP,\ï:¡J fÛ2”ÚS>â„×âbÛãFûz›ãbrÛáãØÇZq¦5 §#+¥Pžˆ NC>+ü3wf³¬âí!’t2å´ó›BüÐ"b±ÅÇ33Š·u‹(7êCÄ€È#)BÀ" í)**¡ñ;Àµ÷ºf'¾”òˆ†Î®GEf«nß×üÔC¾D„,Ïç?£¿2¨Æšª*#*(ÔDæª>Þ‡ˆü‌ÇÙWwk¹CE†X“_‡íþÃtP ™ªEbxæ:¶õ/„ëóË£–pMP×b‚ƽ¯7îqÖBèˆUjúÚY#)Œ<äE?‹Î¬í¡‡p¡äaîLxGCôE#)¥d8H#)Ä™E&ŸF|ë°²ö¯ÐÊ@ÃrÙŒ5@5™7uß2ղʇT;wÄ?u¤edÅ °wûûwéňîíÂè!KÉÕ/ì®ÿï„Þµ±ÄC!Ú¡×…mÑ0ÁÑ1Y6&#*#+Y½Î²3 //23q0-ŒsÏèi¾toº®èeÐ íZÀèîôqÎÛŽ}kªçߎ‘×\¹.ãŽ(Ç]YŽe—¸û4ºÛ%B0.HëÃížúåÒ¹…ñ¼óغ:qîf‡£¡´EE0º7ƒ~6Ù®2.H¥¶Æ×#+ôa`)“’'óÜégNFÙÚƒM«‡ 3†ølèî#+vl`ÝùâØÔÛ¾}¢ÔíF&hŽµ»(tدýË¿ª†µ;Ñj4L)‚>Š0ƒˆ®.fm¦Q}ét*;›#*|–!G™£ßïÄæç=çÓŽ#)²Äȇ”Éà7÷>ç÷w“¸a¹{3¶N8ˆN\uŸÃ™×í *ôCÀ÷{#+‡»Ê~Yù¤r€BYa b3ê–H)¡}¾ÌF7kÕ¥Çbþ›8JÇa^XòRìž ÷—ãèšr@vLæxztu÷DÔØ+Aé*Þ)ghŠ&™íÜ•œ0bÛ7ôPü°Tä‰ÊùFËØFÆ™²ÛQ8o%“ƵÅ>kÚÌv(M!ê€;2#+!·Ÿ#)ˆFB@ÖwÐõZ†«‘Ó°øúk2û¯p#*Û4n;uèÝlKÚ öîlKrÕ&„JfÀš¹²v5¡w&¸Ý8W¤à€moˆmR"Lytì6Y¸7,Lnç.ªƒ-Ž]ŽA}"…C¶PdÜä§^DZª•,AFÎÏösß$Lâ9˜4¢‘O¥âüVD`Œvi¢´,ªn¨OmcÝîàcÐõ"€¥úõYq‚/7í@îŠæuc/í>©ÑÓ·N„ØÛ´»ÔLuŒztú‰i9M“®ýí§L!v†óõüÌklÑûÙ1,Š%ç…òôa¿oq.ªÂ¾\Ž€üMµø¦qš(|<¥ÜiF#+¨r¾n:¤“¨Õé©©GéíèȎ漉qB'}s¾TïËx%'›4ôf뮹gw³‹l6Yøý;6ˆDdSWÏÃ/WVåé΢A|Cª^Ìš×¹@þ½iÐ}ÌšÎtµY½…—T`„ #+b‚ÈÐÍ’Úése'uu6r.KiKk·êyrÝÍ×uÚ¼Q„ËFBÅX㈆†Q(ŠnY)v{¹‘.ce¬¸wlÈ,–E´„"±#)Â:7\0mI’jkʬÊ-¶|÷õÏÉZà1›#)¢¨B£$4 |–,ãÛ`ªuÛÉ“nìVá;~ïâJëp”üK”‹(ÐÔ… XŒ#$õPz¢ª¼n› žp#+@"¥Ð<²#)¢&éø—!ñ³î{S½Ø®¨Š»t$Nî I‡@g“Ð.ˆ/Õ#*i™AÙòã/caÍh¦1ÁP¡SšÍŠ×„»™BˆMe†º>5ªåHìÛ}àW¹ ¤vëC0‚Ù_³«#)b žˆn*F¢[föUŽnÃÛ¦š¸í<v‰Ü‘NHO–•uô¾ˆ2Æ]k§†xnëE«šêó.óÈ€¢"T¤c£M7BJ b”‘#)Š#*@$K#+QcÖj•#*ÒKBCœÁâ{¨Ic©¸–ÖˆQpätL¬X6î»U¨¾¿Zþ’h:Ö-´WƒEŸFþ®éÍzpÉ!1¿˜pÛ>«Bíe›Ù¡ì˜ü–åÍŽMgl—äÀ½ø…ýF2uÄE#)¼K:”ÜüšSç-Þb‚Wê’m)c[bfÕUTù7¨Áê&a&"A#)íÇ®¦ûãÙû«'\¢=Yx‡)´ôÛ‹¨Ã0(…FB¤grB˜ó ª'9=Þzìë}]Ý¢oìÅX±dM¦†ÇS6Vh¤‰›ø[ðØhÖ¹1T„á¯?³hN ä(Kd*EŠ£¤xìÖ™Ã1Uï§Ñhå1EúzíóÇ#fCDÚňˆ$X=h¡,æyÆj󫤮U1ULå§ú¾gÄ’©5–•¬µ >"qIñ·£y¼ëš9ñÁÖúo©$¬ÔªB»3Ä{ì÷§@Ï]ïkÀá[ºj‚‘=cj7®çkÏgÔKm&ðŠßÌåܹÀ¸.”SëÔÆɸŸs4„JApN:l™ÍEÏ×Ƈm2LÆ?¯]pËYÔ¡¶“§½Á‰”•R}Aïá}Õ!ž9Φ(šCvê‚ÚÃA ´»†Ê/>!!hl5ç©Vê‚d)p.¿N”E# “¨ к XX)sC‚ ðnÍ@Ôd„#*sV rË (°È îHÈ‹ûœÞì¯WÃ>4Dg‚1 •Ö$~ÑÌ°$‹Ô#)Ô#+‚A¥×̬ÔÖhÒk7Y¼»Œ© ¥¨LBˆFõ‘¤WS#*#*0dL0Pqg°ÒÖˆá="ØÃɘj8NÆXRÒz‹A#*``(3ŠS@F“Q¦ÊÛåuu¶»Ë¯„Æ.¥,0ðÙnäy~Îs‡žxížB&}ØŸ¾$qìM¶EädÜN#*†°€°æûy@TbørȾL£k¶è š3›O—3àpd›Ù¡¤9#)üâPNäή|ëb„yÖ‹’ÈȲ:²;¸Ô¶»ë6âæ]ÅÝWd…ÙǦûOºi@,òIÆÎÖ#*•[ä6q>rÉ.0å\3kÛá¯Âzûõ(d½Üf£_°màÉ~úéöðX˜˜™š#*)³Qc÷ô³Ê'HœC…˜-&Å#+©TDhvÏOiþd€ÈAˆæªsF¶¼ÞZ%αÙ0g,#+–N£’wÁÌ3Ú%@…î{nÝÝËòo,¾n<âó ’ Ï€«h˜{Ó*^#+c˜>½MÿÃóaë¢TZˆñ‰ac$œ/ è\)¢(DOÖ”#+«¦¡yh¹‚©~¬¬ãb©#+¦1¡ #*ÐA+$ð¥t˜1'?]‡ˆŠ& ÕçÎó‘P#*"Ó\P‰ïAÒçÒq·z½(Ò“…ÌlÄ&7eú¶¦×É =‚ÅZFtHFØÑ}ãZ0À"'•¦?Æ=Ä4Ä·“QC¸¼ôl©çV}Gd¡GH¾Ã#)âT“Ãîjªn(šÂ2§’^„ÝÔ•ÇZ«@yðÍüªÎF‘)†ù:œ6“t:ùGka¶²Ž=8ÉfY’]•ürmŒUÈ’¨^X³à>w†¨#+¼™Iêõ½T,øQ2xQ#+á£\z›¬¢þÆHy!ŽwD0íµÞÇ•š™-겑µ#M$g!S•`Ób™¥>Êb1èÔX2¶ŠEH¢ƒ¥·ÆÏ,ã…Ãc«¬õkmy3OF£‚kÙصj#+2¯j!FN¨C˜…¦±ûY†žÉç–õ)hÛ²ÆÈS`6ÒcHÍͼôϺpTƒc£*òaÃXÀ[Ý*Ee;±±¦¾ò†ämc–#){Zv‹“#)Æ$ñŸÑß5Ù‹WL–ï fIÑõÙ¹’ÇwX‰§U0Hzq"LYq…†?͵›’÷¯;©¥D—“ìéÃÆÓ£¦6ϺC;#–ÂBTï&ï嘌#*¬Ø¿Æ›ý„Ù ÃŽ&çÛC'¶–Xn™‹@vEÜE#*b6H’ª¨B 2&.A#P;áZ¾v—MòË’²”rZ«Á@ƒØ±¶“ä,\/ËÄ¥ qJª“D¢MC°ÁsA›¦ŒÐáA´¢a‚% f´nh4“Q(Q#*ÓFÚa#+a&¢d˜lØ;7-2Ëtp£#+ ©1h¥ÖÒE“„Ú2V“ƒ"""ÈÃX´0dLÒU)hn†Ž8Q4-‹"X 1f“ÂÈ[&fVî£á÷Iâ]·sߎYÝML™Í5#•ê I¾^LÆŠÎ ÄÞÏ7C¬ÄJ=èÖ9‘°ƒŒt8¢„gf ˨_ØÃ;*1h¤ 'æbÇãÓG—”+Æ\{HƉ#*“* @›RáÁ Óôª#)Ù°í„k/ I0—GåÇØÙ̺>|ä†Æ¥½hYJˆÑª)r! .ï6øÓXD.š=ZΈ´À6ïá8#× >ÇeÑ€šÈó44Ó-”›åÏÏWßüút^aª¯gÂtš“×z‰dMtSŒ[w–é ••/ž4©ívÌf©]€Î6„0…•ÀàðÔÉTSâ“B²æ(²N»5gØAF]±ú!¶&¦|¼‹#+ˆ.{+Empµó½d6&ºMæ+&Rhú‘9jwpDÓ-²0>-™ÊD†ú9ÎÖK‘gÀº#*¡B;üànèý°ê’Ùë œ~mÆí9™ggähA°dN9X¦ß›ÑÔN„|ö•ß£±ÝB °!D±¡DÐE‰Ï% #*&3ï÷Ò’¯ŠÁC…‡K0ÒÂ#)•F$ÔI“:É$sMÐeXŒ<5\ldµ¾µ°aÑ#)!yåÍ6:à1@iØ5‰i²$#)„ jMÁ„Œ%#*Úù7‡è»›&&ÀY—¤=˜5{‹Q#À{þ³¿»/ÞNäR@I¶u®Ìô•ç¤ß9‘ YŠ^ï|•6¤b]*#)4&¥Ì¢ðÕÕ‹ÁøÜ&Z+NòNr?#+¹¨e)Á‰ù™,̸¨bœ¥ú;õ}Àƒ<Ð Œ@TnwÎÒÊxÓ<éì>x’HB¦~eÏÜA`VEµâ£W-[cZÚŠÚ¹¹²È€2(A!CBì<5_œ¸œˆ_¹Æ#+)«ª:¤ë6Û¹W*¦@š33Òp:K<C<¦šu¾ç ø6HEDê!‰$hI¦LÊf•¦Y’R“LRj‹IšƒùN¤ÂZÒ1Q4Ô¡’š"P£ZX׿nPÉ&,’‰LšÉ‰šfHÂTPÄ¥Cêî¢#i,Š‰†)%%”0ÕÉA¨´¨ÑD”Â(ÍÉ’´Å5Š™DÐd¤)¨2•ˆ4Ði›LcIMïîî)Ü“y³ÛSˆò²—ì#+’²{'t{?++UWÐÆ(}Ù|?uÙ‘Ò;!·äÈ;8H¨Ü*9>ã•á†s³¬#*ôç#*ƒS²rÁ‰¼YúqœS(hÆô–Þ™å#)àŒ#«3—7ZuïïKEF£>ê6%øv„Û‹‡m¦ê%ËA<Ì,ã©]‚õ\vidZ¿Æ—CZFú“|wë‚.µvc±êÌnúðµÒ6—óˆö—_`?ø¡¿YBß~+˜Èè6&ÛpÀÈʦAáJšØýZø^Ûoů±bc- £1³Y6ŠŠ#d´Ë(„a;3ãçŸQ†íè·bÀLØQ×4àíø3³ÙŸcÌÍ7›œa‡¯AfŠJg9W–œ8Ć62i\û®[é®å/c°zCŸÄH`MþÒ”›WS·aèéOŽÕûóÇ=G"Ã!v¨NŠ¢ÀJè/™Ö4JËô|,ܸŽäÏž†›rÅ%œ¨†Ü‹}bóÑ×D!×UVm\jQJA(U$I#)‹Ø™1ç[ÒÿWW×p‘öL£~ݶþYwÖ1ðšRüüâ(é2‰F^#*>Ûp}1ŒcÇ"$•o7Û|^ZàèÜžrXL´g/.Û>^?Î#+ø‰³ƒû5¤þ#*jè*’[i¾½°ÞÓ‡îe¸¥…†*t*.#+mfà8ÅXèPÎù†úVÚó¦Ç(iÅl£˜_€€zÐ Q›g¾ôrÁ‚Iú¯›WÅò`µº L¡|dŽ»j:÷ä½órß¡ñºe8yaŠÊc¡ni§q=æxK›C Í®Æyapü& EJzŸ3\ˆ{ßeá%+iÄi™¤òï¤Ò(P”?KÇÕ GdêpÚNB"T]¦SÄbY=¶ÍP¡#)Æ®ôu;¡²/©Nº6ÞËmÌ̽á–Ê"lG¡ì9 ve…óúf«§–½SËnþÖìîäÈÙIrÝð'SVú5:„õW§´Þ]v>OˆÁãA8}_Eq½ÿAÓi9¤*HgŽtwÔœË<!öyÜC¬íÙõsùe°ÇïZËôs ßâ9Z{ÕK±(Ñ;^ùÉ禷;{¡¸B¸{Š_§—;ø½Fp"aQO»JRGkBkbl8ògâm»]š•%|¶;€Ì]È’*ŽŽ¼&P–så3 jÓ5'2;Cq¬üŽù™™#+;«€›<2Ý 'Ê÷ÞÂX°ˆgE%p@ûkùS§¾ŽŽÌ‡§«õlÏ5|*³öã=©W_ÜʳK…h•#“ëhŽŒc#+ãZ‘Ž7•™o´6Ìš¢ƒÖ¡•ÂŒœ‘u§÷5˜6™óq™†ò¹÷FÌXÜÔnLçQ桦e¶iåæÆiœK¡d´ÅâhѨ¢¦¥0ŠáWb$YB,.7…¶—:²fÚŠ‡zsÖ°'›|JÒ” –¦wÄþSU/ø“éº^,3ç±ûe7½±ÖQ~ɇ[#)Dgá¦ø Á2¦8©?7VõÀ–ÄÔ̶‡«9Ñ©¨¶pi•‚°„ÉðÔÇÎì§y+;ö“ðCµs}§j#*.Sˆ0¶6{P#“¤éOrðf§+%ÞgQzœÔC#*Cp²Kf¸¶Ä“’´Ž©:l¦³Ë.üv0dÕA:oØ,œÄ7#+ÁŒUEÅAbåúYžŽÎiç–™ªáµGßÆAËœ>#*ÚÁ¸YÑ5V\Ú0>x’æ˜$ÇIçHݘ5êΙwJ±Õ†B¹äêh`Á«›ÌX;!÷67¶ŒLjL¾ =ù_mbí¨Ç.›îPq°¶ž#ZÐ'¥œË=§å˜¤:ØÑ5%ÜšÀ„öÄ0ÿ{¶ñ01€l´“¼G¯ji¦jë%\X´5Ùòvxãͽ4V4ØZMDxç‡<üÉïYÛ0gµ;iʼ¼bL²ãj-è‰Y»Û5™Éœ^ŒW7ÅN·Å#:LèNèvØÚÐõYšÛ6Pñ<zm¬kHüpbo†Ü„üÎÅŸ¥Ã¤¹C¨ÆD¤ÊY}Ö«hÕNyãÆ(#;cC‚HMœÆzñ\¤j9¼ow1Mb—ò½òùåŒÃsz»!z½BT;AÆJÓì>#ccYÐÔw߬n×(}q|oWÄî²a…3™§¦è:Ó;1¢dÆ9Ž°„LJ©n5´š£Íš']ºß—ÊßQt×\PÄ##)^Nò”:±¡µÑ82%pd™"^!ä\´°»¨(2CAl(¸jS}5\7š¢¨HÂIÓlª^mK‘a+a[æâ*ɾ÷UµìŒ¦„!2Æ(1ª¸ ØÙž¼§ŽY5‡«òíd7ç—s%}“Ë.’ÊùÕoÉͧ)8ÇRÚcLÌÜÐcZXƒ!”ŤÜL—¨Ç2@ª&ßhƒ”Cà†–I¶5$äËÁmÉ·wŽHÝ0냦`á\ö§%‹C’šõ“j£–3fàHâA–ÌÃìí ˆÐ—#üÁÄ5ï™NºD©#*@\Ü@;ÂaÙ#+ši[ÙT싇ZN#)]`¹rmep œnçô‘¼%–Ì>ÎPiI#+4óÉ6I‡ƒjvÅ0,¶iQ!³é¥ØXlhÍÍÌpÑÏNVŸNÚ]#* ºQH.q¼†Æ.¶bÔJ2¦E›6™äz…Èr'i3…jR}¶huhfá…,ˆÖGmõ¢çHBºfY|c90ÌÒB P×±!¾j1»½ý%EàÖ‡N74BbeÓŸŽK„Œ„7èîŒÔm´píƒpRj¯âã#*'Œ5xîs/ps[Dìr·‘1„Qvkwg·JÃ]Þq•ÛÂEBIìÙB –HÄ9ú”amoaq&õ•ØS‹™™ŒLMæxÔ;dçv±•ð#*o!Õé8fÜÈm¢qãKf‰¹¼s%O6;ª‘éÔ!,ÙH¹D»¨#+¤®%'©ˆE@ª‰IæQ(îšQnâ\ñ11ˆyññÖxƧ#+3§¡[›ÃÌáæTB¥Uqx‡Úk ¨ÙZGžÏä‡-é,ʃyóÓã.‰vÆX¨Ì¼!$œª‹UH–ÔÑ-‹„ÌG€5–fêJ½1·[ƒs2ú™Bº9‰“†H4qÆ:jn¬21ÉO)&PmV†Ìâë\—[™lÖfõŽ™5,yU–LbÇ0vB‰ÓW5£YÓÖ™fBÞ0Ì £Ý+.ì2çÎêç#+ªAâÎ1¬“BåPÜÒÕi:§c=ÞX‡°¨Âj8rËUW8Šbvg±ËN+X©0Nø4a©Ë9Þåqä{Á¨·É"aNû+â(0àe#+Bu öó›Rõ`h©µÃ„E8($ÔÍN–3L#Itq…FõîXrm\1Ü&ÖÌÔ¯½©cQ²ê¤EÒÂ%*‘ÚrÕK#*tÓ1#>"<4[»K5ýl^«¼;6ÜåZÛ|g®È95{ÛdæŠmP)MÂÝç#)º¡$Ì 0ñoj9ÆÄÙ‘²‹ë4tP)Q´IÂÊI@ìÍæxúªÄ°… iÙ4J¥L©Ù‹¶TwdZ>h€!#)4Ãß“B£Xž¡#+_,nÛ¦Pí„&RÌá‰Ë°dY‘Ù°Ž¥Ü±Ñ>á!ÿÐá‘W<ÖDwÀ6é"ðd^»åÉDk¡H¤Š1Ršd-œXZ‰‡vCϤ0é‹C€ÊÇgys( AÍ1š®Âej‰–³7Õ)©ša–D@ÈÓJ l”ͪ”‹—viÛZéN”(XŽ”S7p‚ÛͤˆËCa9ëZf¦É)!…Šè윒ZE´¦æ¶O7"eÈÁÒ¤âÌ<Á0ÈjËÖ¥:3T#*°æ˜Jã\ÙÁsC…U¶xò¬eV'—'qøƒ#+¹X¡àèÑ¢Ó@¥Nœ"g¨…ØÊãc8zŒöf\»#)CÔPà†È1Œ6`ÚuFòjlAé;H–wnË—Û¶L>Ð8è·ŽHy}×hn|Jãfå«n#+eɪó™>ã—Êt¶C›Ê¡WŽ4TÎ…õJ.ô½ßc¢vk\8RÎÒ¬˜³ˆÎ ÔÁ|`5Œ`pËî¸ÅG5D´Lbð¥ñ®¬]CC[¸.Œa½n–œ!“ŠÏ!©ÏaA¦WíÉ„˜™ÓŒPiIÛ+] gw6÷¾ü2ÇÌï—æf²„9i 7U@88û±³0…6šD¼rî+„Ýx¤½WÁ\Ö+Ùr»º¹kǦ©-Nô\)–Ä``i’ÚÏêÀè’âãA.NâˆÑÔGI–F1¶ÂHÀcfÒíåxüÚæË•c¬ik#¿í*Fï%‚Dv ‡JíèlËRQ]+y¦—"€9 aÔ4ë8†æy¬r¡˜2„6(kX›mlá.@!µÛlyˆkCŽûÈ¡¢#*F+2fisÄØÜ l³3Y†ÅÖ…²U)¡ÎQÓ#)e2É•%d#)rá¹™#+9ŽÅ‡32£ðÆŽäp‚UC=N A‚#*ŒBÀÑÙ¾ŒpÙÂ+q‰%ÛʈÝÒÐÎhÎ2\fÐÜ$#*†DN4”;œÄÅ1Í‹ FÃ9²4SlÁdÌž)Iy#pt(q“3a4ìnf:Í«™t5Ž‹³aÁ¢ê23i…“RR8Æ‹Æv´±={›f¤Í¨Ûss!€¦D‰w[ÎpÌ#)ÅC¨@Àî#)bú†âgT£Ä‘„bõ€% °‚P¡ú·?ÈI„MDÐìÎðTÚ)¬¡rˆûR‘(#_é×ÅGǼ=ý …ÔŠö´I$IddŒ’BO}Q¸úöcœ€Q€"'T:F韡ˆÔ‘Blƒ@Q(#+‹@D3ŒÀ™#)ÀP.àH;²Óß–³ÈÏ‹ i°p‡é…‚IñNÙUI‡Ã>”ÜâV›Cûuœü®Ëâu âv,áÅ¥ÍÁ«”¦kFñ“wë-ÖQEÜ‹h^uËïµÄL«4ó¸ëá›&Rc'j_ÃXGåxÎœk—’ÛWg`ÐÉÍ`f!æƒxFì##¶<fó¡-°[,pCÃp„"ÁŽŒo· ë úÍŵ²Rän„¹ÂK”A#ÈpBÿ:ì‚ 4Ñ*D•Q*4Ëkñ¦j¬›ÉIªÔÚÕåðQ#+"ïðŠPT!ÓÔ&)óïæ‘2éЩ 4¤B¨,Ô'^8ª(É¥ÊÏgpÄ2C¹~ò‹u6ô|Ù§b¯À ‚*¹EUÝónRZ¾•Ë÷Õ9uwu]˜Öë»2æë¦ê‚×6Jþ槗ïîì‹[äÕìvˆê¡àK…ï½#)\vÊc±Tw ;7Àüø#)ä$3äñC¡}ÄCŒãÌ¡ƒ8‘jÔQæaGSÜ`=¥?Htsáßב´}+¾ÆóðÕ&EÃò‡ô™‚·Aääý-•ö`Ùþ'Íðc–Ñì̾B6ÊzîçfiZLPOB0@cK.ªÃµ6£xº eÀöøLfà´kü;[&ÐdBšb±oQ*¥4#+:\sq„¹v´¤Î‘s.7Ê ’ýƒÑÇ#4"Î愈k;ϵ‰˜‡ë"Ûñ·‡€\¹b^®”èz#”o$±‘57Îý‹ù=~ã_b«M¢IL¶*±¢Ñµ£j$Æ¢&I°f™–DQm‹¯§ã©$Be¢|±Ç†•¢§h‚•Ô§#*œû—$È2]åÑÔv@Y«°CÒæ‘¢#÷Ý؈šð´Ad±#´ÂL#À+$Ej;Håm U‹qy€ÍÂx:žosשÔ2ÁÔôÑi;¦kþD{Œ5î|¨á™)ƒKõXQ¤‰s>5a¼$ô€'-ZDŒW‰@Hm<ùä®2¼3+¬ƒêi¢™¨0É…–á#ÎÅH7üGEi1¥Ž´‘d#ÜBn^šÕʺU|›z—¦Þ„j™#+°.ÉŒ[2”²bš+ÜaZ\34B1c@ÔsùÎ2‘3cÅw,!hF¶¾X#+Dü]ˆðªDˆÁÇÆäh8Y (ÛIƒ@U~ªXž8ªM;¹ˆ&T»ïÂyþš;byÞ¥¥\ä™7»j.â"††ôC‰µË}ëÚc* 6È5$*G/z¥.G5ÝàÛÜD¢Ñ;tfõ$^&Ž’ІäŒ6ÚfjÃLâ*qLuquEÔLhF1IoÄGTK&Bºäf¬¯:ql¼? ã#)=¡ìIæz{J˜(À*À-!VD˜S04ªýÓx#F%ûÆ…¬*’ðU÷Ø#*±D3‚0cº#*ƒ4þ$À!°|µ÷KxÅz[úÌÇòý4g<0æFÖ:N%E`Ö™ú7p£˜ÀßQr0«s|œªÚ## ‘qöe]²cûÊCNH¦§gØå÷›DÒ˜oÃ8ªßÂæÃwhº"4ˆŠïTØuñ£L4úãAÈÑ´-«ã…¥¥jÖAl"¿–á$á‡2k³B¨TLí³H#+g‘NÇ6:Ç™ÍÅ®(láã¡Y·MŒ{”^ZÖH8[΃3™<Ÿ#)÷¾æÖU¸ehÃå ¨ýúN¦Ã¥‘œ¤Í¹=†Œ’(Jv›nN<mmƒåPš«%*‰ ÀUŒeYNÜ9#+)áÕaŒ:°Ö@mQõ'u¡=p®pì’ëSɱ4\CbF”ñqÖ=‰©}lô‹7Ë;VôÆ„ub‚ÂŽl El0,±ˆSFQ»uv0È3[¢)e x#*À„aL*˜d@!L@£*#*Ì#)¸_zÀ-$BE,$˜: bmã7ì–k¸B㬊Ð?ä PWr¡ö…ÏÒuà×ÔH…£2gÞ®SN)XžÊQþ#+Ý4@õº°.yø³¡±_#+zÛf¸c±Néç‹Çû&@Qéh‡ßWYœõLL#)ˆÈ{澦H¢`™!$‘’1Mõk^6¶þzŶ‹Uj-m "#+H$‰ Cܦh|êž^Zðܹz—>#+•Ë‘ö>!ã5¼¡ïè#*-h¥õq\MoŽvÎ^øA¸ÕY[QÑY½¨äØr$®E#*“{9éÃYÞ%lšeäʵS0:2¥K‹ßl4ïµi¶“vl“t2Vø»–¯V1FÛÜlc6h9Äq£šHÒ.™KÄӃƶð¤¶Ê>s7’a”ã6ÍWIT%³/4ò嫈’öçŒáöäD´¬SNDIžRá©7DMÌ—C€é‡ÚsF7Êš€ˆ¼Œ1N·ÉÄÉÍ£qNv˧º£~Q&˜Ç؈ƒh5)ßa=`C§‡8·Š´Û§ E2±J|Å&-MØ 31Î5Ä™›µ3žµfgS´œb¨i½ðyXÛz›Mp“\ÎJùš}d£3œ}æxdÇ·ÔW!2„dž9lð4(ÚwM‡ƒc&úU/“›dØ¢™¨sLêgïgjRP@Æc$"1a¹9ã czÕºR‚,"ˆÁ`‹L1É`´mYGË]¾qµK¥ŒŠ¥%!Ð##+\¨ŒP´0hH¥ƒhmQª’†” "D‹"·5œÒèP@@`Œ’~äË V¬,0ÜqJÓʹ¢ƒÔƒ€ØØ"í²8ÆqUv‡;bì×MŠŽî¯I#ÊV¬e’¦HÜ!²ŽæVÁ‰ÒÍ75t*ê×i5ƒt®ë®î)7Šå¯]®L´í<Ýw™W“bcJÝMní4H¤‰,ÊÍø)80¥Ú6x¯m¸·Œ¥´Ê¤²“"lÖ6Æ´ÔŠfº–ºV–RÒÉm,Êšª4ú5òóÂj6¨0X©š-[IŠ± B*BËzüŒÞH:#+ü£cV©@YKj@æÁD8¦EØd0 (TM¦æÐ ¥Ø”D ACœAìnŠùDv‘AÚ{,œñ=$#¡Øóý²–œr,¦¡¯Úmúsýj‡œ'>bŠè ï@¶a€´èµ¬¸Á‡fn¼´±°51bÜ HÈhûìê$Ì•5,aâÕi¶“ŽiŠ§‰ÛæØÜCЩ6¥O‚}oßd@êŠ\b•*©Fˆ„‚ÔBàÅ,þ Z2"S¨F;¤†ÒïÜò„ðüiâw®œo.Ž‘†kGìÔeÃ]‹}yº,n§å#)ת 2!MiK‘٘ˆ<«…L¦Ez鄤0©"@ŠÑª÷éêýŽý¼µ†Çsdx†ñàc‰ž újõ.\*;öóÔì6Ÿzi¿p¿C~–g%éT2r "¬ËŽ-Ž#rý9™,æå!3`ZI!æÈ!“kÉ*ð—¤*“ k„˜8噧&¶(\¨Ž:aÂë%e²;N_#6Yð f”Ký[Z‰8&›ajt'áwM¬íÈ 3 ¤š·÷(}JªD#)Œ"¡Þøú¾v5Òû¯™±¼“¼áež*²øÌ£_¯ôä](’:~B’IBv{[Vñ¥ã²Z¨€þÿº£VH¼]…ÒLª12I¹®E”2Ñ5’b·¾íZäÓi´e-¤ÙFŤE4Ë%”ÉJ¢˜¥›P)F~EÖÚ³LÃfÐÌi‰6Š*ÔÖ©¢zqEdV¥w]6¡/«iÚÍ^Ý»*b(2#+f¶ÒŒÖ”¶¤ÚÄš1iUû«UÖ¾—"XÕïݲM‘E¬l•¶D’&ÙµË-LªI©m¼ó¯)Q-6›(L¦¬6Ú[6“{:µ©mŒb¢<mÑY²ÞuuçrJU¦M#*rÝYªJð]f¯Û˜•XHÛxMvM 0E#$Æÿ“—¦eViËç|©+-ýÀ°,£œèÈø|;+ÑÕ¦á’j4ªÖÝ í¸¦d“¡×Ùª-Dhå2ãîé|R"bOk”º¨¤=¬–ÓN‰ÒZe·,XˆŒ€e×#)jID‡tËKM¥”ÒÚ6Ô¤ÕHr²o#*®CÉ€¤QE$Œ’$TÊ*Qs”ÄbBÍ%@jæßGu¯)ªém±W65¨k}…”Š™‡1Ò›#+jÚRŠdéjß+e·6ª‹%-š˜ÒÓ3ÚE E¥&©Kmi¶TͬšØiM©M¾+r†I–¢²¨›IA(Ê4Ò’hm6ÊSHš“4l4Ìe ±hVÊ)±²bKBƤÚÆÚ”ª#*EJl©JdªJÑIT–’6¥Ú‹Y´Ð²2RcAI”˜Jdšd©fªmˆÕVD¨¥‰›Rd‘fµ©f²dÑ¥$´¦Û,¬„ ™ %R#+@`©„4†’ÞòÚ×M›-ZSUe‘,Ä#)¨‰v 2"±ªRØ-¨Ú´‰U¦¥€"´mæ?Puœ÷Ìèq°‹:é2:B}ÛÍŠ÷ÃoË5ªöÐÀÍýöpb‚›¦uëòÎ9J&£'ºÊôq¹ÃM¨§–t’¤Èï#*G‡xR<£r7‰á×RÕB,8ñ¿ïø g¸E}ñ<ïtó†øìT$1g½¸ƒ’ÈÅ~Žý˜×6ιîų@fÚÊ ˜óivz.•Mù”£v’‡‡ Š”®Óì¾H+ѽ7Àe§ý¬†ß;Ž³ºëÒsq!~FDÉ2lÀm \cha!¼†„#)S™ÌóÙsšL–oðõt˜%)ºõu«¼‘B +÷Êy€T·E p}ª™³UÍÔtÍa²)ËR u#+*Wèî)A=|5Hqf‰÷ŒÀ‚.3î£#*L“ð}Î6dXšs1š"„$]º‰nû{Ó&¼ÍolË¡=8¼½7¬]Î(³€Üêp׳]Hû<ÊĹÉö2RÃeÝùŸ·èšìßçdBS1Ô*LáPT_×PúÕ.F¾©Ñ)iûYá`'ëqŽg#*¶`E’FtªM*CzµX^7:µ½ß£ª£›º±jSI£|Ök}„ˆÙ®ÑFrp[ö‡œB¢\†™<äH¥ ÑÓLÔUU"„€!˸#)#)V-±ª£Ûé•w]·!êpövÈVyF¨·Tô„‘ Z‹ÜÉ´ÉØœpc±¾üÝ÷iVUÆD`üßSŽ½‡¾ÂHƒ#)Y;+s™…8e‰†’‘Çfw†„m}Ù²¯weŒp¶tìÆcoM>ƒWÌhJ~=;6û„@=ã¿ÅïçÚºÎ6óv#*çG±±Ý][{éLcÚšÓPô„P,Àý;nÉÞù »!&'ù¿ÅÀ«–‚Îx…Q®\‰úþ»ÌŸÌyìíË^›)6†#*¥iÿÖþ¾XV·¹YlQ´2²’$QŒhÑœÕRhÖ4C DÈÂ6À‡¼D5$Öê-pÁ´#*‘‚9m&ˆRÍ(¦7#JhÃ\Ã#*cŠ±¬Œ‚h¼°Š³†7#*»KTŠ3›ˆlLÐÝeq‹±Å,TCD.ÚXUTŠÆº]$˜*Z´<R)0©Î¡HkŠFý*LàH’·€í—MÜ\åz5J<BGàµó”B)Á5Ú5NÄ Ú×ÒƒÏÄÈÒƒ4çCÜR4j’±Ì#+pvàøÄ]©Æ-"jí8s*Wg¯qž†'Ó¦Ná\„$€Ãw0'M;}§£z·"˜Ø·7;†±#+²•@TFñAj#+Àº#+_gmoÐ?«Z¶‹Ù©®rÄH|ú~p¸ ÌêU‹7pÛµ¶ÿŸ¦}ÒNg•B ¤Ožø·>÷è¨1£ñMnx$œé˜W’¶âÓâëUc^o#ŽMÉ! y§h^õ Q‰¡²ÒÛˆoŠkÙŒ»±“4!¸L «}IfÈzIYS*#oÓ½#*àþœ›ãCòü¸{öcÂô2>þÅHT#áÓJ)Xùö˜`üh˜øæb¾Ú!ºE›¡X R$ÕÒS$™>]ˆZ¾<¶.¬_;ö¤8RñN0$‡™µœÅýS䘵àü>¶&µÂ¶ïm%ãü¼¦šm£jõiå&4jRRy´zj"²„Ô…Ž@£2>8#*¸>Öw8æ篌ì’ä1vb]00ŽÌÍ…PȵHÏ à=G"Ö÷’3Tm´âp Ëéí©Ü¡\}js¢f‹F¤„ÉÃ'`²Ãa¨‰«Zôt0Q¥ii¿}'Sé~éUŒDì=9”Jª^¬¨‚P'–e\fzÕ:q»ò•yì°êD#)€É ÉÁê÷Nbœ¥CŽ“J&˜õ & @C8„D»Ï¾½¿w~è>g—áÚ©4¡Ë7õŽ¢áuEÕËiÃÎÒ`€sCgÀ|#*#)Q0jHõ¢Â¹»ˆšÇïó„瓘ú(7׎±™…74Lmu¢‰Ú#¨€MCgÓ—@îíËïÄ&ä¶æÆíMD#*ÄÓ’5Œ˜ÕŒ-8ŠvÄÏI±Ô9:ÌbÚË‘(ÖK€!¨™1ÎÓ€8rUb*ú¨¥^ž²õâzvœ3»¬ÞVÌòT.4ï;¡3sÎŒø9ö Ðb‘*,R¡)Œ`f‹"DB —¥×*¯zïšIÚÚJXB“%ÉË9˜FœCqp¿£GàîÚH¡Í]ໃiH¢tGV#+‘$iU8Œ@ ŠïÁb È#ꢱ˜$¢»™‹„ \Yu«JIK‹NzîÆszîLo;®îõ›ØƒœSÍ>÷5&#*æÁH©µR*Šª†ÌZôD´†‰I4ªuÔ›3‘¦†ÚêXˆÚ|µÅDÉ$ Ë"de<ís›‰.W[rY¥Ï<ëAâxñŸkzËÎä' "#)$lŒá`Ü1çéÛv»ÕEš%0ÐXpe§2Q0{º–Ý„'Cü[Ñ›cÛ[J;¥PaŸ*C·Z&»ã=ËÚ#Ûm°ÝÏ>[øtuÖ ë࣯ Y8Î,×–)B¯-ÂñXµLTªÍ,©±»°K¯áÔ˜Šé¨8RŽ„6øyçͨ:c¯£Ù‘éi]ÏP¦áA:¸´íÊBúëÂ'n3Œ|sÒ+tÄF,Âzvlt•-~}-$Ö@U&j˜*ÈÃíü°h9;˜%žþ©ëD(YzwÜ°À—€jN,0â©‚e캒EÖ—®µzròåÞ7Rmõ·dLÊ$'zó^^µt“c7m«ww@5*Ha(’™´Qcš°O;·„Õ˘¥ì”¼ËUCV(ÅiH@M6ïÛß;›ÒÚ"Žè¯´º›»µh»4z&cèúvñŠD §¬¹½‹íðœú³¹RãMpÜuH§ÞwÞêf“=û™5~´½/kë]f"±‘‚’~¶‡t¤ÚÃZ¿kZõhÕ“j¬ŒÃý©ÁP:U2dfÃ`mEouM4m(¢Ö«”iTÓI¥¥ºè¹·C“Y‹hÛó*ÜŒXŠÑ|iEUéz^¦Ö¼nZË6þóûÞñ¶É«i¶km¥é::byôÐÅ) *p%—Î\TâMHùÍÃ#*Kƒ8 –BЊRâ2DZq1'+C oЂØM†ñ2P‡rËHD}¯…ç:ÈÐÖY4‰–°´š¤•wfa#*r½5ølmØ›=›œ €IŸ>l#*“¼Ühov^¸§¶J*”_ô„rÖ-0¢v&èŒà¬…¥!m…OÊ©Teª³Ò‚¡WK$š¼Ã2£Í†YÖp€ëÖàtîó£!ÙÓU#+#Q#+ŠD‰UKLŠ&ÑTju¶Ûªµôïfé^#¡„k„*7Ë/a7¹fä{ª+†»ab·(ôÁ ”‚dYQ·½óãÅõ†÷y8ൢƒ:uˆ²-Ɔ€‰²#)ó,¥¬ôI÷íà)œM3ðÇ×þü#)vºDÖä[Bê‚NgÀÙŒ(4dˆA(þèš¿ë°ä³CŸ•Ï®ým©°wÃÅQ–‡€¢,#*„Aéä ¢{ök‚ ‘ªCb%ò58þ\4ùj™m¯³ü÷¢9üGákè D‡T]°õdÞˆn»&ߥ¡=ŇœwuŒðºyB¼U¡Í?o“ÜG àìì}/Ž?Š$D‡'&\^¾ˆo¯Ì£¦ûºBónÍŸÕðC¼! ˆÏ¼äP:Í€ç¾Õ؃â#µ_›Íô_å;^Hž$PRI‘Dë #+J¥*#+¡DUYXD¨©PPfŽälY€¡x@ ‘,‚ä‰%f¸añ¾¤Nìk3.?Þ†=Ž·3Rº0V(,i,X)ÀÊ6Bý¹î\r;”é>«SìSQù~Ý«Ëj±DŸ@ô$›SОģC˜N/P¼läV_Ôþ€å$¤ ÂPÀ‡eø–ÈwGÜÉj‘BÒR+†—‘D‡o×ØøŸ,Âïª11Yøz¼ùqa8X8¢j܉ó8:ÎÓœdŠÓaí¨#u›¼…AÚ5m,WÀ»5¦ XiË.Ì·œû¨ô§Ü¤NÒ*^D’"tÈa±æ5ðÙ°¾¤—TY*¥6È.j§ó³,?6a&šòìuCFª ±QV$6kùy ´ÐDj6€dÀîÈaX¡†Z? °QÖú){~å]j¬iRD@‚›]C–*H¢†0PžrÜ5uÉ8b¤™EX¤®wγ##*ÛŽ‰fQ“"#+×Rf²ÚâXÄj°+HìÍ46„úˆÀf˜ÚPŽ)11Ò#*ŒµYÆTdÅE‚ˆ˜´«a)’©”Vk²ÙÁ”€ÝBS ,†‰H춨„)&Y#+³p–›2SŒPaD‹²X„Æõ!l –’•‹#*m&4‚±6–ò!ÀÆFFs•ËÚ!0! «-&‰•hÌ „´µƒtªÒÁ ‘¼–7 6È«#+Ãp´X›Õ¬«L°rt5¶ŽÞÝ{{öÛÁŠòwvwnk–¹sV6•Ý+–åQ\·/ƒxĘ׮ïK.»o‹dÁ®IŠç4\3•¨lêApÌjÓ´KŒ&Di:3±öu˜i–4ßÃZ±1£–(\'(2ƤÙÕ#*ª0%ÓI@ 8ZÖÔ¬¬<5MSÏXÛ3¥‰b]ÆW¾ìµØ?±4ì}0œÆA,¾çmˆ*[l“ʤÀ…ÍZxpu±»õ>KϦ4ÀˆšÌoªÝçn¼™n³MÖn²‹«áùqý”Â1Ó ÍÇžVàqãìz~#*áÝ£ .H(óMTÖÔ?<û Ñ0€À«Š¯X1 «ûãÜE•DˆFåæ¶5*Y-UvØÆ•d¼[¤ªŒ,k\µWR±«m·7[d´ÓX™%ƒYPuŠ«#)–¥II¼²n>”ýaäXv¨j ¤,b$"Ò?»yÞ}_A`5~ôUXŒ³ƒ¿é®¿w}XõŽ¦Qc×/«ÈÍrCCËPbP¤ŠPBªT`T"¯ÆáP—*(R$åþ0 …ˆàã:njÄ€çºêù+q˜„§3Ú‡-£›ˆ’" <MˆP}¥!ñ½UD$I~X)¡Å¨5#*!å,•²mçn¥*YUÍs[Æ×Üm¯,ÈŠ-’Ŷ†ÈUG©¬X®«º¢×¦¯k½yõÖìµÍWÉy¥¬jówQE˜˜RMjMZ“4ÚKÛß°Û`i˜I#)#+Dt dyÇKR±û9uC©¢ÞE˜$Ê+Ü¢åˆìƒQJW#+ü©….I#)}@!x!ˆâ&X‹ˆ b6 íƒb"Ù™ R¼€;›ŽÙ#)&¥@¶ÊŸGðQì#b0d•0›AþÂîmù½ôžCrÊؤ‹a=‹í’#)ëÑ.=ÝYž=ô• oi„L¥8ô`…«Á1*ªE*ègÏÆá/nÔˆÃÕS²¨N#EÅBØÝ…ÆMC^$Ø`1v rÿCòdgW+×0úbõAJŠ†¯ˆbÊ#)h¿ˆ|¯¢&÷W2°ª8êÜ‹°ÔCÉ{†ÁØWƒ´©Fí»ŠJ4¥¥*j±µÚkÞWliªÍ5”õ+¨¬(±F0XDd•¿¯¡âs2Ó·}ƒèÕv@ìí»GÞÕ(P]«šŒ`îÐ>^å8ʵáQíû‹¿Õá ~'ÇG<n¸Ñ캓®¥Œûƒ’ÃåÔzhÍ4›h¦ATšqpÿá¹<ÆsŠ‹Ëf$1#*k#+CT¦ï®0fŽ—#*î¸I#¬¥ª(ecƒm€#½Õ&˜ŽäÄŒî£î0aC•´A¾øÐ䱇½wû„Á<ޮتÀê$’IhLUG÷£s'#Öyç»–™zþÛêä…õëšÇ-‚c¾œW*ùÖ,®ð©NPYT.ÂÔZjBÀÌŸëý¼‚@å¹ÉY¶çA?Bô3Ãuå\›9¦#õØàQCS¡™&>^ÌJ8×V¹ "ÒuˆÄjS«6a k‚907ÓM‡ƒrb&'üD¦Å0Ù#*d†IPŠ0¡l0F¡fœj±RF¨¦y#+!®l¿ îäi=ŽÍÈ5Ï$w¡¾L±Ã\jð…‚f÷bäv¬C<¡ÿŸ÷òZK&$HànÙdÁá&l½¡‘Ç^zÂŒìgR}ÔÉϘŸ:z#)CÒûV€r®Ì”>?#)Õ8 $j<#)¶Æáãj4[Ö"ÙƒRRd³Lŵ5´µ&Æ´d˜Êkõev “DÊ™igìµnm©™jl‹E3X%lÅSÆÛ5T6²Ìµ™m–"Ö-(Š¦ÔÌMš¬Ñ4Õ6ÅUE°¥ ͦÏ}WÊþfêÛöû&¿Nãç~EÓ–üÙlµ$"jp+#ÕãúÛ¤µ¹¶ŠÖºœÛjµ‹]Mk\ÖìÞ#+¼Â£ßÁ´×¨¡µ°IÁiÌîêá '@6Ѳ6æ®s·6(¤º—å´·¶Ùê’ ø]‘Ê^”èŠúöÐFBD2$N8À¤‡ÒÑ0)ö¨0üÙ‡“‚|Ù1q@a>ÎVGœ{æÅ1jéC¿ž~ þ‡N€'( %J€² whzÓ¡#*úØ‘óÐQ!•(dñI-$$)#+@ý‘nAö¥ºE"#+Tõ¡<Bös¼E‹cï>3Ë=»¶\ׂÁ‹#)%ªÇ S">³¿Ê¥§m©#*±¹%Éê"¶éMà\˨¹”Òõ™™ƒÀß¿<º«€ÏïRÂÀк…®D+ñ½½þJ§'`4Œñ=#¤VNÂOy„£°9×Wwj2jÃéï6˜x#)!ÅçÖ¡›¼ƒ¸€p[5v›»ËAÕ‚o/“Ç•7# Hp¡’m`q>»#*Sø9m+¥.â!Fb+BCCD‘Â6°L¿Ð¼£LÑÓ°„,ÜXšË•;Þçì:>1J=h}¿”º‡Û¹œ~n¶kIêµP¿²)øIötSBó‡Úeµ §¹'s«ý|a®#)p® ¬-Aç®×_Ï‘ÑŠQ’÷ýÚðãž7oåñ¡çE/6¡ p)¬ß‘µ¾°³’¼3§ñäÀŸ'×ë )&©#+§YA””²’„´ ,˜Sßñð4Ç]«Pù&云kÑe•ˆeIŠß…׬z _;ª‘#*ûxƒoMt#)‡Oñÿ»È ²å˜µ1¦¬²É‚ ‘Œò¢´Ž!n[$#M0©Ø)#m$_/w·ìÁ£Ý)œùb`H‹¥E)ƒ—°·*#Ó²vDIUãÆVŠ´@Z-QzÖal¢¸£Á5«OšâÞv=òñ5#)Ù“=UZH_#)ÁÛæÙ,H ™B™ˆÊ†ðŒPjp´ ûjETq1¶Ö4s#D‰>¸€i‚‘@ ¯L`)ªv@§G—çðÒçžeê’Àá5ÀÕÂöΰŽ3æñ¡µÉ—I#+5ˆàÂ3‹Œ@!ádêuhAë†ð-íÐÍ-ç-1¡‡4X…”þñdîxžža,H2:#+0ˆÒ‘0…B’†Å4#*K …‘)Š¡ ÊNMœõx¨†vi=>~½Ö(è:À>ß0C.¾jú]½¾$Núª$dG ”É!ETB›G|¶«bªŽúOb~)¦]ÈÁàfÑ·ôÄ!,ƒç4FDZŸ&NAóŸBÜ`5±Ý®lÔtfë,”'e[_e·ç·¶ÛÛƽ#já©0‘QØSAˆSR0×èßÓ3Ãå’¢„æÈRBn©FPÉ,¬KØ¢¢±õù^gïD¥ÓÇI¼¹L”…Þyå#o¼é"½ $M¶[QR]Áªo(²(|Íq"!‡Äª‚‘¢(‡YÐÆ]ÚÐi¦u¢Ó#|½-0"`ÁÓdƒKùÈbz ¸¦Š£$mŠq£IÎ×I2íâ05“ÙâÍ3±-±äS‘.î’¿80ˆûiÝqâÐÛ*JåÊ–W"€Ka·'óník#)þ„‚D‰•º¨EOy©$($4#*²¨q[ñ¶BqK â¼]FžCl Áã–&M›eÖ”je 5VWE·[Ñ$F¥”¬ŽÂB q‡’?5î‘À2·wbÄý¤Íè€hGràE<3ƒ¢§U6‡ÍD]‰ç_Û1"bvÁ'Pe;dÙeƒgÍÙÒRħêλ¨Ð#4öû=*ÔíªŠv8—Z¥mÍP›ˆ^OMWñ«MsœÞ(@d'/m1òÎãƒ,'=¥QC®0òÖ³´B×oô~ÔéÕÓå®ä[æ¶5‘ÔPPÒZ³cyq»UÝÕR‘Eµ^²Ýu4»^3bÑ(Þ–)²DTqŒa[áÜPxê¦AOi#),ÀH®Íþ™ÕõŠ#)vgœoˆ€åþm½0€WöWÛ®0Ýçà5¹°ÈßÒjêõ™( 2#)²ˆ!Åã¡GHGR:Dz÷`‚JµX–#*ÚØ@\#)q8—¼Rü`õúh#*}]^HjÛ¼íÓ2jÜYÖ“8›‹ê}Þ—V¹žIæ©êð”Äò “¡ŠMèÈ’B$ÆÆ’b…°j •Äˆ&äÛM¬›mRm´²ñä£ZT…Hˆ2¨”c,@ ÏÏú=ç¼ðB°d?è¡vîi|%À›Bn%I¸û-³ú½ë[Cb¶CCàU³-¤0Â3U;¥ià]¾=¡M»½·®Ò±»¢ái—Ʊª4 öš¹I^eæ^5%ªæÞ^;WKj˜#+¢-2Ð0å,¶f«(E¶›»ÎŽšg½¤rêé/óåEÎ!cLL9xô¡5ªbÀ¯P#X¡²ed9diä%»¦<yŒÛ#hÔH„$lÉx0á¬þ½«@#*½¼9 öñ‹B,\*…°ii@eàÄ3l¶b8†©wn_\þ\ç;*gâjI –KÚUŠ8è#)ʘˊž|ë³°¿ 6'!>ŸM-.rÞïg¨^ÀCôÎu؈›'ù·;I¦“Ãá8š'½ÜIÏ#)}t%u@1$D8×y•šK*5’¶&Z6KI«cT¦µùÕ~VÚL®»U=ÖÙlX[$¤)#+a 0?OBz ‹ÃòïK˜Øò¹e(¹·›j˜8 ã´UXz;-f¬…Cß5"”ãÌå×ËFžì²ËŠ«Ÿ×®|ÅÖØøª7ׂ02¶Wº¨;nõ²| âê<³ˆU¶åÛoUx$˜§Á#*ýk¢È¶‚ËÍðŸ#*Iä·Ø¹¨Úa$Ñ8UA‰vSÀ&·Ólw×?™Í¹ï†û)ôÄÖH"Ð7ªˆ ¡#rž†)1{l.¸ßop0ˆ²B1Ž¼ó¿VýÉFyä#c¼ù½ð7ÄCD‰¤0Iƒ®%¬:EGs“|AAÓo-/Ù3¤m]'щ)MA=gÁKá.öú¶Å¹ò6H_³Æ:›ïmÓô¹ÖŽ=;’Õv9ÅÛÈìÖÐÈ@ëóxâFýØ?™åJª@(/#+6¿‘eg‡9ë máÔ7¢õ¢>G‚ª>Üé³iëíúîؼ`Rv…@ñï£!*íŒnQvÚRÕ»f…ï‚ÄMü–O6Bdm@8LÊŒÒZ¼òAÄê²ÅÚ)ÛØy~LM¥÷Á¶²"&V¶Ð\s!JD»‚„H@zp.¥É†çÂÓ‘çÀ†‡.äǬJ¾ÁÙÍ[Õ#+y°aB`*• û {P›œ'û¼6àGÑÔ1…åÛ09ô_(žŒG”·1Œ>Na{{D-¨ï<ð¬j5ÝÛÓmÍ:mVÌ\U#*¡$‰HH0‘Q·‘KAŠCo³UïÌ£éݨ#*Á•([¼Ú¹„õÇüÿy~¼ÉuAb ,Zk€¶°º,D#&:É’Q–"¢ÄH0n°lP±Hà¤p˜”M$…PofK>E„AÃ!š#'‹v®#*”¿sóÈóDïÁéôcÚ/yê»tT’a¿èÿ_ÎÜ 9&-ÒV)7‰ëEöîO†iøîÙË–8›¦•å΃¶ž=q#*~ÁóhiѦî‚`†Ì5bË$ &ᢓËPŽoÍAë%ÀIÓTª)1¡Ð`Fœ#w#ÈIj¶"ü’óhäkÝ‘XHš²Œå’Šä½HßÚ‡XŸ&¡ÏO‡ÛÈ>«%2jlqî‡ÁìqŘƒ‡N•F’#rs*+A°×ZëšîÜ·WYµ4Õ”µgnÚº“d•¶Í5ªê¶4W)—‹——v•¼o£,Ñd#)ˆ#)d›:0Ô¦ªlê(† }~z¥+MzŠPS†nœÔÎk.Ea„ŠQµöÐ,#+I¾=—Å”›¾¸?ø5Ò`ë|~‘ádd„µ|Ø…‚_Û“”œƒcPÔÇ–Õ&üÚ‘Aª#+Ú¤“sc£ðÿ^álxÐl6%´ÀÎjC"j“PÈ4FXl=œ‡ƒ¸jÎÆ$RÈZ±`„#4g·Õú7¨Nè9S-VÞM¹”dmÐL¯Oj†³¯ƒÞnW&VBÞ~ÐlÈõÎ>êî7í Qˆ#)Ï2æô}M÷ïBÆMØð=Z‚¼uJøtyÜÁ€ëwÛÏ©ày•'¥·õ|ù_ûºìhª#f€òQuÌ:eaú·Ï s£¥lá1Gèk}8¯ª¨«¥¥ÐIDdPÀ+ÄŸ©ß/ëý¿×ÿWðo²À-OæDàÎSßRú¿$Åö]'v5ÂkŠPX‰·çˆ±²ð¹ßSX§%DAÀµ¶&Œ‚¤?9¬½à-`û6ÌÌ5¯ãºµ„.HÛy4#)jP{çÆÝ‹ B˜oQp<AuòÃÌñ#*áN‡PûŸŠó1hm0zÖ¨#*ÀïV¢ÙáÑ¿â±’‰ÎóÁKNØššF\ì‚îù'íJ›^ƒ¦¡EI…P¨¡]ãæ½Õˆñ$ÉÍuK39gpÖ³fÝx¶ÈQ-žÈcB’b8L6dfêƒM.ÛŠ`D/ˆaí#+bð@!Kž4UÌtóxäÁ¦Ðbñ´7âÄa‡€xAËÓ¥¶ßgLeXPó.„ÄzàY)0(Ï€¢g’C»è>|¥ƒ.œ× Øþ!õ·;ô‹Â‰+"dÌŠ¡òÈÿšaB;)™ç=‡^yÌàö¤‘ÀcK-ÒÒLÅþü7"ðŒ¢ÃOèš´xÞ¥s,LWÇëÑÓkŒC;”ò9y^ýáÖy[u¬Øvªdl‰pIÀf–—mAk<hÌŒ:ÓëÞòÜ݉(6ëÊcz0èRR5|m¼µV]áÒ2ÉRÈýûÊéú6¾:òô bÐA„a¨‚•èª„!“Èè£fxrS4ST#)jëÛqÈS¦’KRPNŸ…±xÚ-XFÛXFÑhÕ¶4[ ‹RkF²XÚƵ*´«^5mÊ£msY#ºfÙƒ]ÎŽíúe÷æ݈Á v"• Œ)CñÀá¹Uz,.ã`!i Ä<zöœm·Ý¿vÄMÄD–pw>¢Å7NÚö™#+bŠMjÏF¥nYÇLgöQ³nÔ-Tôúî}jcàÀtÓW3Àd7ñµM¶H0`¹yào#*Ðíu#ÑÌ1ç'qB~ï8}“aÛüÞÀŠ0Ôy+å·0ò#*¦ÊHHˆ^Ìz„döŽü"÷{õ`{rÃø}¸sdÃ¯Ò m“gø;6Q80;qÅÎc½dà ’Ìæ9‹Ç¦Å¯‘!HÕ(zh{+qQ@ïˆf?¬!¶ÛVb£9µHC!•?E) ªj•³Kÿ~·®º`”&#*I·€Žy$ Œw»mõºÙ¡—®…#+ƒº X®™*ªJ Ê>$‘ñï«×“:.æg‹"g±ª‚¢éMñÆŠZTÍ âYlP¦WÒuÜ Ò ŒWS™ X(‚‚U £a[#*aA˜†°þN{-{æg#)]!7#)‰Õ8ŒâÃ2A#*™U š*µB 8îÏÍõ{ÝïK=yz¸Ñtg™ƒf#!A@Ò…STU7I20Š9Áð‡uxâ÷/Œµ7D?qôpkØô“’Añû»k ï—TæÝâ×ik"9ŽqE8QŒs,86iÁ-2wC³c8wÇF¼‹€‹àý6bå‚Äb‚ªŽ,x»©3ÏžÚêŸßm\ë¯YáôûAú~{ì#)¹^‡DÛà ÅQ ê=£t8üx˜î÷ž3jÐøjgïªo²½˜ªQW<èÒ!¦uˆå¨¥Eª¢FD€ óNCˆ®YIô ÙMIÀÓÚîBU„Á¨˜Ø<aRa¦¢¢„v®«w]¥#jff±hhÚ!#*‹Ð—òʃz”Y¼(TP$ÆIe¢#p”`48*j¥â0bnVÑÆj £HptkFTĨÂ$HrEiˆ–A” КÓ;Å[ð$‡1¯dOÊ®è‡!únfUDTD1j²šéW7©¹‘fÉV”«&Fyºë&T…(´#+åv7ÀÉ[Q‹AD#%£[( LÑÜo##+a‚#+ŠË+&†Y¶©FÑkA4š”#)¨5+e+4%1±‹RI&…6›,Û3$ÈÛe+/]ÛËriÝËš¹ ³u×-¹§nÞòæ¼Þw‘4P©jË}>»Ùì͆ÀE³#+YK¥’óâŠ"”¡é¼Ðr#*£uœSl5X<'Z4A vXñ==YŒcnµ#*»§dÃZŠ´Çƒ#*}•V±„‹·‘jÚ$ùqàÒyð¨ã&Ói@¸r1i®†(fëC}`j˜ÓqÒŸD^^æíXª #+×®”QH×ùYi6¾tžÕäôÓ›3,–«ÛºÍUY)¡Ò.YA@Ë1³pÉ°Úá£!*‚›$#+Ò1#+E¬45-—w%¦WŠÈŠÂ¤Ä1än0-¥Œ‘&`Ùi%ehCÑV‚“SšØÆ1±&;"ÐÆf;jcß».qÂ#Q†7†!•»¤n¸LÉšœ@ã67v:ãÊV¥´¥ÄÐWˉ¼÷ZØÚÔ†H¾¦£Óûì#*2M…4tðõq(÷°‰»6ô¦CÈ NyVëu5Hÿ*ÕÜó@qø¶¦iM(9ôyCKô€u»¶âgåbY#ÃVnmm8álíHÀá€jÆšEÃ`ÌhÛ1½½U”ÄÙç¤)b#*¡M$}&¹Þƒv]ö1̳?ÄÑ„§7#*0á.™ƒa™1ô_#)Ç©úþs¥²÷ÓSb<4$²àwOÄô°¿.y™~nžl¶Ç\èë‘I–XåW²ÙŠ-ôÅd1ì!fŒkâD7\``ñ£HZ%Sm•›âir=S|qš„ÒSŠ^5©aÛ9U¨E£fÖT5pŠ©#¶*PŽMOF,ÐÈÁ‹bðÃÜG¬ùû¼ŸI¬1ŒDPÅ#*Ы(é»)e ë â °z‰9R·A™UnØÆýnnÆQøÓÎh ¡ ä¥P4-¼µÕ×[¦´ÛÝy¨Ú*é«6¤TCH˜ƒØè0c¤,%€ˆÁ²B1T±Av D$—›þЀrÓϨô‡T}šàC`»¯¯å¥™aÅ'4î¿4H‹ýjÒâêZ¨¡Ì1õâÄ?„Êhý4Qˆ‘Ì>Ͻoz4që=½6›Ÿuß¡ÄÛâýç›I[ù]ÏTáX‹bjDþçפ=Ö+žé¡á£F'÷6#*¼=ûº™)doowIعqû¯7M^³O7«šÉ«—;2)eY–¾INþššúí[¥·ë{7tn(ÌD$å¹™›ru7•Ó<yp“ÆÎ3¸ãG#*#+#*Eˆ~×XFùš¼p6¢À¨b™Ž9ÝÒ;TÚXM‘$+ªî[—Ùr§ÁÛs;÷ö0D9üºšƒB£]v°a&íò}˜Ð&~ÒŒn^Œ>‡ÒÎG‘Xg~{–õÏϬ©Oƒ9ñÁ¯..Ýš„’Å>òÿt’L˜õa#Ü)»*"e ¶Ë!†æŸŸ†©’½5©ïÇ)N‡K¨Ÿl®ò7ðü<-ä6lC¤eÀ~홦¶j–À¬Î¸jR™N¯àùÀCá?SŸ|pO|Â{ÀÝ!0ŠAdŠˆ ÷ˆÈŠ³aDl- u%‘>ˆ¢š›*GãB Y#)Ý!Κyw“¸»wM;Æòy[ÇJ⫳kpºmRm]ã±b¬my×.šºµ;s!ίQO²X´‹HÅJš¹Çu]Ý«Ôª4×ÅZ¼[y6´›¥[ªëÏ7ŒV4mmâÚ5^¬|Y\ ÃèÙ»ó4ÝaF=FÂ9"’õ]+®åbôä*Z(Q…”"…4Éu¶ìÚ©µçš©ö‹f^ª.!{,4„¬¤H…¡ ÍkµíµäÍÙ^–«Öµy«¢÷–úe#*b¡)„© …@ËTj¥)jEú]j)²‹X¬ÆfšT[cQ©˜¨ØÔZ‹ck2°hÑ3T›%™D̘•J(©™6@Ímš¼íWK;)@¢"¤gÙá™ ~Ý÷‘G!O§Uµç¨4•¨¬%I"H)eø;/öO³Âý½Lxêþ~»·?ÑãEÓ´°õ‡Šp8#§;«»æè(s/ò£²xI%µ¥ŠÖüÍM6´”Íhß77Ïzº…š5Qùf·ØÖ·#+µâ×Oézº‚Ѧžnê#m;»:êÝÚJZÛ˜¨ª±¤¶æ¹sYÕÚnÎiZÊõÛ³Y¨‰#)#+R-4}6Û!˜î7eg"1&Yˆ~%Å_t(†‚Ñ$˜‚D`b#*‹€‰7“–`Ij¨ ‰#*È´p#+¡a#*§_G¶¤¡Pm+½Êlr„50^5+´¹†)Â6!4žoª@§Ýv§¼u¾\Zó›;¢HCÇqÉó3¾¼è¨…Ì]hʈ°Ÿ<Ó`û6l<ê£ú™ÃZÆ ºK1Œ&~VX8ØÆ2»™&š„ă#+b‘X3jiº8$ÓÉpîM¾¦S‰(íQ‰qàu?ªæñåßì! 5éá1°<¦ˆt2b²sÙðVþ’ðæ8ÀU;¬"Ç‘¶C’}¥s—ceòw¼o0<&…1d¼ØeÌ"ò9Æ.{òmiÙO“P–Öû'–kFÚíì)[hítKEH³*Ó¸Áƒ†¥“#*1.ïg¹àšE‚¯ð“¶…c,žDèÀRs†Îû…fGaºò²ˆ#+&Õ6g-Ÿ³îñ¡þšÇ†*C“bô™”SªVÕK5¼î„ï5QG’P…Àõ†š'7í5¬›dµlmF©-¾TûêÁDB‘!÷0?ÌAÊô‘Î#+>§Åmo$öu¤ªØÛQµpA,[w±€+oeªåi÷«Ä8coEò¤ @Ã×åfÿ Éð…íê—º¥äm]-’Úk®ÓÛ‹¶3#+"…¢Ø€‹Âb-‚8#DFˆ-37lZRE ÀÑ÷=šüA€!·Â¶ÞªÖù’ÚÛ2LFÉ«jæšÅñöÉUçu\Dö]â®Ò"6—5ÞN¹øzç¥ÞM^[f2@IÀ@Îq¥à´q•µHÌ …FÚ#*[FÙ«R”âm ˆf¬*Ö©ÆÐ0©…#+’"J¨…#+…#+ÅcÈeG,¤ÌBÞ0«ßDdЄÂ%¢€ƒ“®aìÎõÄј@¤'˜Æ ‚ƒxÐ8_ùxõåõgb1ˆ¤/+=]çè?'Üõ´X*œúŽx„«Œú3&mÓYƒ·vgÒd…$£Ìk%¨Û·Onü÷hÈèUN$E#)‘;KHFI$„$ h‡`UH OÇ°<3Ëè> t…PúGì£;Q‘÷éþ? <!l‹Å5@üÓTÁùF½Q‡…ÓþxEäs™ý¬#*7„Œd<–2qO_»}ÏÍ“OŽ¡N?,ß|Ç:Áë9hTOç@Z‚u:ˆ¤d.Hв€(?IF¸[.^2#)1Qg#*øpÁÑô#+¢j8Is·¶³µ‚ó³ /º)¦CpQÿ.Š˜–0ÑŠ'¡µšè€A7öÄÎg\#¾n=±´Û#+xŒ8°ÙÊåØ7Ì¢€l]"•Pô)ZE&Û#) Š¸yZÂÂiÄrL—&Á‘`òû•!†OÚ#)à“3R½ûö¾±<þž ¶7÷ÖÅS‰ãÖ©ô>Ê›&áò‡6»#m›Ñ£ø•Nœâ3„“½*z€QCøƒLà_!¤òeAws®m«¥j®í²Ý]ÚF!Ò”1«" gü×êJ¨µr;Sñý6|.;Å#*’òš¨#+’‘b£D!Q‹ó–Bt?w>,Yi^½çžygyÝ\º2嫆”uÝâòœÜËÍÅfË"i$l&Ê–£ÆÛ´Û%(¦l`ñ¸š%͹\«›¸î¼æ^MÇ]vÊ9й]ݺErñoJ£šl·“Ì·W.æ³,hvï6©VŒ;m®nZéµd¶JX¬ër›2Édñ[»»£tæ®ìΚäŠZ3œ9Wl¹ÖTZ4rµ»i¶åÝ-·_áy^j#)ÜÜ,*=P#*Â; ìD€!¤#)zöúwƒ@åØg¦b0#)v£û»Cº€‹#P„ ¡A;‰al@ExýjŠoNy¨x~©Â{AS°qè)à‡| ‚ÄŽo¦=¾ž ¯Eö=¶¾&• —Ø!çŸp¤û(P ü¾íÉøö&Ó¨öƒ`#)7 2#Õ¨Êfi}6õr«[ñb£ )TØ\³ýQEc#)Lç?o&¶½”–5²jÅjÐ¥¡„&€ÀÐañ>IŽ‡À #*©*+—âW›ÇjœuÝÔÙX¨²ªC"Bб‹—c 1<BŒÞä%‡%ZëøBY(0vm¾‰ôé“$ÍÄ^äâ!#x#)° ÝJ#*éL`Ú)#+5ézk[ÔÕz‘V·‘@Ò‘f¸¶eÈQ@´ƒLF*†£œý›—12#+SæAJμ¡DþÞ¬2¿€¦0ЉŒb±J!ƒÞëš]`5¸C¤5¦qwŠ#)\Kb¦«+RÍ–ÖiµJ¢”€1`‘Þ†"&ܾEÄt:ˆ‚ñÛf5“fÔ-2f¨Ê¡$Q #)éò<XÆ&ô5#)$vŸj(¤Ô–™Œ&¶¿ÀÛQˆ’@#X„.8Ü"þ¨:dkµ3²ž3T]`˜ƒ#NqS]ŠÐ÷Ìw’M@˜QÏÙ“¦Q3eËgm–Ñ=ï@ƒÁÖ†0Oz3?^÷é÷Wã}wŠ¢Õ¤#)áXq„ÔýŒðT6ÚcÈ3‰4 ;YÏ04¤'åh»04øÈgF6"H¤$“ƒ‡8èç‡ÇCè]ñ‡ðüÃa~!òy,Òr<À-%ÇØCM>ZÇ#)‰ÄÄ#*IÇY%¢–#+U„"mA#‰#)l(DÓtRŒ„]0KDdK#+”ÐAŒ`FÝi&ÅÄ*6VjBTE+ k±˜3¦ÙPƆ٘î\´.ÐRH¶ö°AL R$¤Ð‰!@’XÂ(&eIòvlI$–ÑTÉ8ŠÂ‚«ÞâOoµáPîÊ«3h}Á“” H\U3‚Ô*”\ÍcÂï@zyd•ëõŠÔ4Ö#*7¨ÛÚ<í'ŒW5ÞRŽ¼ø^¯²#*ˆ- ȈH€£qR¡@”¨(!´Üþ]!³lnqÒèîŠë)7nÜá4}Ô#ù“wIRBB@Ž»¢‡‰WËØtÑÁùÛFЦå’× Àz ¡XLÓv iQÊÀˆHm-P·FékxyÓ±‡Î°óÃG݉#Ihä©Õº#*#)%- X#)± Ñ*@t*©È ©øCm{ˆÒ¾¸¦i]½ZvÍÁÌ#PZ¡ŠtÀ³‰ŠÐÂÐE¹ÍxO\MÁšy'KŒ(HÈzŠÈ(‰µãÎ?ܶn°™£kª–ª¬¢Ø˜¶ˆ@sjÝ'ü°Ðkå*ú˜mpEg/Í<î½5ê´¹ÚæJÔ·y›o!,™&8(BIa]f<™·d„T®„’ÿL1‹þ}†Ù#)â®0‡'ŽîF¼>ãò-A*ËÊ«m°¯+X¹i§Öm9/'…½zaa¢áHÄî> ñÖÔ6šõ é‚ Ÿ¿ ?Dv#u|%D³óÕØtopãdPª.Òµiúýnʪ¦þK«Kò„¨ˆsT{B®h³‘sJloÛ#)qo€KSè;äþiÃÍ[zÐÿNïµ&GcÔßÖiI4A¤,3EŸ³·Í‘E„ÚÙñˆŽ`€ê>"ŠjÔñëÜ£í=<²Ì}#+ý¿JÅ:.µ(+÷”¶ÇEòì¯GoᢘÖ÷ eeÛÑtËaƒ>¢Œ‰¼Ûã75‡ Žé/#*—›íà†PfÊ#*ÍÿI”ËZ[{¬Ââ&6ŶíÍT)ˆáˆüÍy#*ü¼‰ÆNÕñ*žVÆȺx$LdUC#*–xY‰L©m@iq)3Š¸7Ò#*D#)ÆadÆ-!cã˜?—½;ÄCq¿sb‰Útd‡äûVÞ¬WÜÈ·d”’ óS»°Ùœîòc³$}´®%9õ<Æýf‰ÇH±SÚrÜÂÑT`ùÉ0Œ>îÈáUNlÌ "^â¡ßŠ¸%ýϾëú#昦>ÁYµmÂVÐ…½Ç,¡.³Š÷²þ]²˜oF™‚ƒ3q‹N8L-a}d,„ŽYÍ<cÆ2H#*#*‚0oPŹ°ÑÍk¥¦=i4b¦—sìž‹'4ƒ‡Ÿ£Çe*ý,sNèÞñ#Hè„,I8£h«Óë-0ŠQszÏ&¬çO4½{Öžm›#)·’þB¸–vRMѼ°ÇÑØ-£?N™jöíÆ}vØ)Êàišç¼o㤮„<nõMÖ^YEû}\5Œ¤eÆ@æjmÑ!Ù%©¼HnÛíÈaê17Ž-a¹ó^™Ìgló¨OÚb*)»þë ©wQ™%Ôæq-µW$G-Âu4B!‰@6‡‰R¤z›"Àñf)¡#*ı¹];zë>³àÉúÓúC‰ßìª1ûvŸëUÞ/OßȆæ…U ïçÕÏn™*~’ÏD"ºÐ¹gA\}y·È¿®óŠöñ6&hl€sÒ’‚& ^Ñ |·šL5íI2Ðùlÿož¥‹¢É3Aà\0CÝçOªLÀŒè¥¨$D3M‡AFþ{©Ûkä ›<3±Éî£ríµº?¿nrè}l<X+5 Çð`~ö¸÷Τ•²I5½j¿›é˜·ƒíJIõ¶ÅÃâª!0$D–‰eh#žü*h$m®¬¹«·¥«¼YϨãøH—#Œ=%$3XUSGRgŒgYÏF»Ë`IY_øâ÷=>#õ eß½ùû¤ï¤×„r-µÜГ½T©Ø¾a®:ô¾6p&Pƒ{wÇh|QØcÑÏ! ™Åi¼@Rö¡dN¢íAV)‰GJ-ùtúfu†ñ‘ów-*zf§-paäG'óS⛄ÒÝÓ>Ì?Ÿ#+R” ÜÜѹñåmçQ•v0/ÏnñÑ8ª¡3'»=·„1Kø)ê–`Ėȃ§¶Æ§N9ß#*Tò´Öô!†1]OCã´ÑJ®óÕÏÙ× zð1SGÐ#+·,62”RopiB#0^ì¹ÀŸ…ô³áÆÁÌé:L½Ëïüºbl©õJÃò°bA$_‘ ¼#)$PîG½!ñG¨/ð‡ÈùÅ`Ýð Š$€$""@ C»qz¸ŽÄ~þX*G®»²½¡2™º€ŠpzຎcÐñØC¯QîNÉq#)ùƒs#*Aná#ó-¼GCÒsä.6c’;þ7ÇñÂǾq1Ú qñq.ùt–rîÀ SîÕµmó‰h<4¶ÓkñfÐDW‘@¢Ã~9({¤Š¾5ÝB¢ªMóû«ììJíçÜu;×ǶH@ùwQÍ`ÄDÉGK Ÿ>X¶ÅG›¬ä1‚ÇÊ¢ci>Çb¢˜Šƒ"vɤ`¹‡Uš›*Ó^y…ø»³àHàà0×ê‚^:=¡#)¥ìIKB#)Æ1XH ås‹Zû9ÿ+úþfee$^ú~ò˜Ñz‚Åd—z[-Úé@Ðî'Ó—`i! Jª‘ PÔ<Aßo=Ô&»Åë#ßç9™àq¼Ùçð£.J¦„O%~ËÒš5™›ufvÃnœí}:ŸrCq3Cú¢‚ÿus›8d#*1úa(«GÐÒ"Àú°tý’€Ýù¬BÉ.Á; è!†Wóµ¿<û:+_5#*<GIµk+Ø´#+‚WB_ûºo\„7$ë}×lΊv6¢«;;Ë…(:Ùç°óÀNb™XÅêÁŒ‹¾ùð•èÐ&¸ ¤Ïϳõxžt.9B¿?!(6¿>ųøš?·–}‰‘°µÂ«Ln3”’9‘/·.±)Žä÷ïÜ;¦õŒªÌ©Dæýúà³{%à˜Úû÷Ù[™¡óÑŸ¿åˆîP’AÙÈÑoª¢_˜]HÄúwL˜üZ;×u‰±55(øtЇ»°;èÉR:R¥:íeT‘@ÙÖª¡#Ùú²è’vœ<7eú1*2‡Í,˜Î•ë;rÌ~jÂ2Z…J‰,JŒr¹Wtýê»U&b-¶[5šj–kÒ•ªj6©³ñîÙd#*Ï*Ô’D7!й¬\»ºîêøuŸ(Fè¢(z¨‡®¥Z#)°Eîü9Ì`gú?O‡™#)›FeRDhÌš(&›D¢j"““%ˆÓJ‘’‰¡HI³dØP‘£$é]»'Ž6 'È‹¤^d’;Èzâúã·‡6ú]y”R’M›vÌ ÜÆ0Øh5$˜l²AšQR0%óoº[=4È ˆ®Ú8o‰‹ùñ•€ü‡“‚’G4ÓÁ߇+kí÷›0‰9ùvg\vruÙ{R·ŸÉû½2÷ÑæÎL£(JÙÀô11¶Ýó‰˜Þ—ïÃ`-®šMU_@µi–0bVÇ ‰šÇ-ìMci©ãŠ‡ÙU'ß߀nk±Ù¤ec‚á¬j˜TC§Ûë¨fèÎ[‰‘À ËóÏÚróˆië©ß#+kkž5I"þX@É—ýoò猉Þðù‘T“Mópµ$-e±*˜ÉJVÚiÆ(âKBjEIj0 Q‹¬i’@´FÝQUË;®JW+n×t¹ºº…µ’êÅd#IJ4fÑŠÅÄC#+Û1&Ú1;¤Œ`f¢#+€âà¥n‰J„.Ð0¤£¶É”´×6\ÅP2ÿÂ#)(²PˆÂMi•x«W.—,î·5d”Frã>òÖÅ\èÚòfT#+ÌEùHSËH"Og¹£°i-±¼knâq¶H܃oͨspÅ!4\Í4Eý´Â¿ƒù‹Øágˆ<„eÄÚ8tÖHL%ÀB„•ˆg”äôFªKýã®6¸*:„mä1›bCb%mä„`tDy¶>ñm#I°y²¬`½ppl¢¶n8„F„Ú11!9¦ÞZ4ÉcŠ#*‚'Žómܲ‹®Þ5¿#)õÝv»¨Œ§³jƒHÌ-M#*Œ @áüJrSM#+¢Š‚a´ØãŽ(’ÆYm©¦ÔÛ‰¦c"ÃJibnD Á«²¡±=#*ÉbƒUDë•V).QÙ„m•ÖÚÁ6Â*âÐaD«ªE‘!P®•Ú1§CiºÓe(l©o0•Q†¨ZcÂaUƒJŒ0lCezïOv9»*A)V(»‚쨻SEÝÄ´¹j @űѩ§¼Å¶e™ÕÞŒøÁc®„¬—µvCg@TF4±pÖ”ŠÔƒqs”¥`«UÆ«A–@–Úì2¦AÕŒ#V„‡«M3OK »Ÿ.UÑ×€ÀÀÊAÇ•Úbb¤ØJ ˆ,à’ºÒ|¢µÀuÄ=7¢jšdt³EÁƶ¢Éçpf˜á3fðÆIkRPnr¨ãnbZ@Á<(Ó„}dU¬—šŒŽá*8˜` wÈÂ3ùNXÕ¤ãT`hdJ‹t.hŠ"³d‚j!B!›SUPj8ŽöÎŒ@ÉŒ3ƒ@F¿oЈ/¢‹”à#XšGdÕm5œÊÐ&ÄQ‘@ݤÎ*Ò”ÁW:Ô22dI•@PËdI¦™¸H*‚†Š(ÑšhË#+Й °£B©¤äA(0XѽÀGUý¹tôÅnÒ\LA]ª²Ðà†¤‚IqЩDÚYcŽY¼R±éŠ¨}#*œ¸ƒ‘<Ÿ6`Ó᪢é8ÃðA6á(¥â =M„8@ ¤†’£)¢ ˆµqîe‘éån#*nèÀ™OdnDœDóêaµyQ‰²]ò}Ž´ÞâXP¯¬Xˆ–”ãîÑÜ¥Ð7¬j"ªB$5çf‰lŸ7ªMrg+µe°FÓ"&˜Ï&©8±xtí¸Œ¶ª$ˆ’Z!LìaMHÒZ#*Š(ƒæ!B†ÐQ!bJŠ£"¤Á€524ÚK7 )"#)8ZhýpÞPŸÅÌ;zÒR%R”Å%ZÕ+wvñ?›çytŨÉk½ut·ïuÄõr;§rV;å|-ôWàÚJj-¶(Z›6™²É“Vú6ÝÐBÊ!÷X§é.ÿGšnÛ45*êv‹²ÍÂüÀ2‚,P¨ „‹ÝœÔ Yé’n‡Lß銤€Œ¦jC;Ðü(£‡âµZ˜r'¹Y "#)P€Â1©F‚¹"…ËkÒörAöÔ¤¨‡y±‡tÏ/}ˆ^T$IB\Œ*dû§H•÷0,ç—š¨Á.PÓ0‰â¦’#+;+$(ÿÓ`fHØ›;»b1±ÕÕÓkomuÒ‡w`ÂwJ¢Æ .P¡‹0°nÙ@b˃¥ÀÖªQ³)Œ€ø2CöŠYrT PjnkºeËF()fÉC×BˆŽ$p½µ#+.<ßîûÂÞÃápd͵61.ÎòX× QÞÈnO¶ÍŠño#*o»‹;˜#*¾/=”ÍIÂEJÍžf«üA¨¨½Ùî ÷¶²^õ#·L_HÄpæm:/§W~‰^‘£éP5ýÃR@[EÒrŸy¯%C°Ë‚ðPÃTP‡‘‡'BJÏ܃¼MIˆÑÚ}ý[^Ýè½÷±`±z¡‰.Qr¯f,C^&Ù¾šë9TøõkëCøO‰LK g¯ªïâÔueÚ‚7Ñú~¥ØÁ#)Ë×)%í·½·•ç#)Ó–«Ç2Ðai¾§Äþÿ¼™ñ|DG¾˜Þ™†–ÞAÆÝKÇÉîeÅød"I¯yé’#*EP¨@B»þtìÛHûÏY…À|,fcœ¿n³& N.È=ÌFÕåX,Ù:V*mî(zà PTÜÅÝZµšë-¶:#+@'?‘`{ñ*yÛ\ŒP„èȇ`ò;b\”Ø‚eb‚Š©ö0’á™óNÊR¨QmTÎÞ¤¹Q¤Uñs´k"ÕU– Lèa¿ÃÆ´Èj樤0À—mE„ýMg‘¾#*Α9H<\f·µÉ±´&Ä›#m¿è1Éóë\’åWH»¶â²öþŸÊñFɽ§+d¶‹{5·b[—wVßÁ-pÚñTk9¹[¦ÖÝ5d5¹´š·5“\¹U¹S»Ý®E²mAŠ5¼W"ÒÒ5AüKpÃD8ì²îÉ7cu=#*Q_€g”@âQ™Zú4׌œÄ™‡©6ûÌÉÙ]Ä[Ð7RD"†èŠ#D@<QV›Ò‡±þ,D@»I;»·oªå÷æù¨8»ê /Ñ#î~ ®|÷}ȇë"Щ6ðŸÏI,|DÂ:Á†“ª#+E#)ªÁv<ùUéL) Q‚"FÒP’74‡ Ÿè0'Õ"Òà0XŽÈdk0#)6¨µm¤·jÐÖ×I°X€œöyíÀŠ…&B]½Þ‚¢îˆªªü-ªYF“Y#FÛl "³ÀùÑD,(Ž çB?Ø#)Ÿô€&¿ËîçIÕn’ŒZ¡X0™/ÉCÑE!+Ê•<ïkJ®#+1 KÄQз&e÷ ‰ÿ$ƒ §w Ú•“t:ì•ùÒ‡u¸ùnÆ¢ä„XKݮߛm]m_ÛÙKS3d’R…ŒZY#-MšRQX±²””’™6#+VÚ#j[Ú¶¥¥i–¥£S*V-h¶2ГB¿aŸ*Ý$‚cnµ™¶C\ÑÏM®/Û]öùêõzò‘TyC)Ü`Gpo2ä7b4À¸ÔyR@ˆTÒ€É"Q§1Ž“n‰±6FÆAX”‘1¨åŒ$AB¨l-0ÃRÔBŠV!"Œ¤¤ P!#)‚-u4_!P‘j,`ÅŸ_êÎÁpAÇm)h@'.ºjnîÙÃ>Õv½A ½Sm("!bBch‰C¬@M°%Ô^별B3ã`û‡NÉ"vTôsmöö㺲³×”„…QRN {AD‘!ìÛ‡ôllˆÍÍkTþE&˜SG]Ÿ¡º0±öô††€–\Z#\b»‰Œl2× ÷(–!„mùq€Q#+@†©Áú|ük.ë1ìb>f‘€ÆlÑú¬QjªGþœ4{S‚v!Ü©ÐÉ#*Îk¦Ü;Ç™¼•ÖòË©R™”/Ü]êµrѵ±hÖ„$ª@©!pil@züœ%Žü3¡í×Gù€Q‚¦1TšUw¸p̤B™GÓùtÙê˜K*Pt—Êo¤= ¸Kaõo÷~X¬¢TåÙ²¥-¦?§dW<`ã•Ìõ‰ºx¹¤Ý7Þ)ÿKóÕ¢4•/!Õª¡0#+Yš*¥07É«'È™ªà«Œò*Ðû÷üÛÎþÞ'#*’'×$;Â,–Ë<ª£™Î²ë‰ídçr§–þ«slsB߬>Ô;ZîãuÄt~—Ǻ)ã‚øaº õ=â¿òigW¯Pê!>æ#)샱š^(quÔ¨ryqTOŸ›´i6.Ý–ÝYíRñH„ªTwâšQlvróßµŒØ`çŠn=ÑÒ¬ÁŠ©fÞ¥ðsqÕq·0œ7%Üëª5ÓXô̪qĨÑuÃøUc2íÐ|oq@²°™‰$q8ܳ=U2Ím³òoq¼×Gnó»f–èœ<r¢!ÈfBž%PDËP„e[_9 Ž°œÔÏOG¹k\7#*BþU&à—<„?29æìÙM|®%l«õw^ò._Â`‚N"Dh}ZÒÜœ¥ŠYD’뢚M¬Ò:@$¹°¥6Á\œeuÂÝ[’QÉ7ÕnJèvv× G»rvY£Ü.Iå-@ûBŽHQ¶3üØHœh‘`1ÚÂ4]…A¼ÚwíÑA.Ü:.}Jmv"ÚÌJ/\-\L!$aŒ\f—ŸVXã«ëI§~Ûûg-Rí„㳨CˆÏ1î9ªn欄µŽ‡i*^ó€ÞG¿G{¼K2MçB±zT.W7/²cMž‘ÁrŽã˜÷×N|óÝ$"“Q?*y[„6"£ŠçÅƺ{Kñ“˜èô873C(!˜–ý¸Ï…ž%͵´äL¦â¹T®>{í§†ÛÍc&AÍ31oÄ#*\pׇ¦·ÒiÈþÿz„¦Fx ]YsCÊ—C¶é¦ žžI$;º/€O™å;•U›#ÆøQaÌ"Ç#*è`—cÄr6¦b˜#*•ì;w;è\kg#*Û/+6ÌkïSb1£‰Ä¬¹tRrÎËÛÂAÛŽõ2yÊîuëž-£1Y—¥˜fÕ>ùZï!¢•ÛºÚ#*¤êÛO9ž[ÿŸÒ·O¶%Öôq¹Ö’KÉ$òxÌÓFñÅ4šÓâ^’¾¹¨ïŒDˆtºwqHrd3'8¢÷Ýz|¨6È™)QÛmî¹\zá@êAqÙ=ˆ0zˆ¯Jè1žKRØ_—)îóŸLÅtï;RzŠâ»[ám8ƒ8ÏÕפ6îûe3®Züç´ç‰yÜOÏÜžý=#J£§G•ÞÕº›6de6 k›L£*YYã#+b®´JÚºró%Ò©#*YÕìð¼àywyï=pô¡'Fñèòª‹éïlgš/Ç=˜‘ašâô‡~U0›½Ë¾¿¹˜…æŽëÍ™{0àÞµ«ðyJçÚ|¹]‘—%•².IŒw‹ôõǯ ú;ö#*ü[–qØwb÷3ÍñÆåvXÂq²XÆÝiÅc8LŒiÓ%J'¼ÅŸ3Ís°ß4.¯Ör6žDtÃ6Êc™Mê,¦å³ÊØ~܈ðÕMŽNP¢PÒÞ²øs1¿fJºàeÞ©’tóŽ[hƼÜ|òTlŽ1'¼¶)˜'<u,áGf(kA2Ê”¸;¸[x„¸pņåÚjç’^Ä#*æ¤u鮡4Á³‘Â)1Üoáw$ÄfðV"³c³RëÏ!Î=Ôìb#6.†º¡»WQ G4¡Uš%5ãØ!šCäözà½t ;@©l¼ƒF1˜`èÀ:LØ”oìökAa†Ü+a˜2pu姃œ§è©<9Àzp±6Ìö¼Wœ:FÅ®]šp#+Aà pL·é@ÇM*ÆÆ7ƒpÜ}¡@Ü$É$gL"kØÎLCø*ó#)€@>#+ )„@#„Aµ#—-ãôqØlߣúqéwŒÁˆa¸s¸ã3¨iaHN(ç¼ÆFÞ§N‰]Fµ4:0ƒaš (@ˆ<eîg«÷h™YPš´º s2êâl¹‡7Œ5Î0£ðªµôFA5ÁèŠ ëDv·³[ØsJô|ÒríÜtm”NÜïí-Ï3ðìk½ÖÈ,½ÓÀüê2p‰øLäWÉJ™Â%g>ÙÁv„dym—9Ø8Õ³IÛ6£”˜¤*fíN²#+á*5®¼†¼¶¹®/32{ž\ûôŽU"Š©´¢*¦¦œøÄPQb§ë·,M—Äe²ÚzÁï]QÁk]9ÖÔ4nû““Œz‚FQWîíÀ¨LçßZKµ´óBŠý°Öüüû`c³5ÃIª4q“ç;B×u´o|€ï|»ñ-#™iå5Q½j×(Ãdvéxwu®g~E5´db‰#T@Ç/¥-lZð¶é/yÂŨáìPÌó¿Ä ^›4n´«5¨–™ß¼¹‹sN™¢xâö/-”¬kèíd͈¢:œòÑ£c·=ÊêÜXtõç牻\!œjãmú.;‘uWã´ ˜ÁÓ¨k vGMuË7%O¹¾‰Àºt¹n=r£©£uý˜«Ç¸Žny_6æYí»Îòæîü_XÕL0xNq]åÒÞ3—Ͷ•ßx™“GT·Ÿ#¼Tù8bW—‰öï‚o)Ä{ƒÉ›îº.<»ðr žr9Ô«T+OC*0Ñðúw-#+X”±÷4µ`#)Ï´½pžƒRñ˜Â¨†–ñ˜³ay£ê9r)ªàtY#Û‹Êp¨Ò§<8‹°ÞÐãUV¶{@þå™#*î7£'víÌÉòsƒð:á™»ÐüÍúŽ\h0,;¢â£}jålÿ<M¬)èÖv¢lC–aaÅí#§å„3¹f×.wK»Í¡C#+\Ù—c²Èô`Õ¤#*#+:o,ò1å.#ÂhŠ5.W!N岩«¨T0kÁG‘m=œL陋=Ô«I°j:º HJh¼²›Ó&·”Ø…—3˜ëP"ºÀ9O‹$åQìŒ]i-¥¼S¹ÐÓ6Ì2¨I#)š2·ˆ=¥÷Ï´Ý\IK#)}M@§£Ìþ%:û#*ÞŠÌÃÊÈ¥Å>e¸ˆAáb:·`PnQšßÚVù€¼t#*~Ê#*s¨òæ!tÛV\Äò~À g6û;Ã4q[1êUT4úŽuÝ-˜)1„ïëù‡Ì2òvÀ×ÁúC#*¡MR—_’°gÓί«U£Ñ#)£¨=idO8›Ã¬œû\vŸ2¶¨ÄY‡Í¥B,‹#)ü_ƒ`Õ…D–aí»Ímlš…s˜Ô±U%&F’b—uBDá Ø¢d SÌhXЂ6”c#I#*•‚U@’ ³x"–‚s@ˆACUkzVnkQZܵ•ÓlÖ(v¦R,ˆ ‡u‚¦¨5*2‚ Æ”TE#+Š2•($…FñÊ3='ð3ÆÄ-¬!™È#)É€ýÛ„p°R.Ú¼4$‚¢,g9¨¢§§+ÓxÛÆÜ‹“cm*µ¯Î¶[E«W-TkU^›•]{2×ô7#3t9Ô¿—…¥t~Í}×XñËe©ÐRÞ9Š±Œc`ÒÅ#+ÝD"5Ëçj4°{nmŽÚæðÍÀÖŒt†Ÿç1h‘]…2¸™÷Í*ŸvlëZq£U˜Ž'–±ÕÃ)f¼³M±A¤« ÊC‹*‚#H¯—z‘Þ2î›Æ³‘,"E#+´™º´Æ#*¸æÈaDÔi6›K’)a.ê+M1¢@%rÑQîSüO1#+|'X·u²¡ÒHW£>úlÀö›L#¢Ù7T€²`&5„Í81"Ìvî\¦jVãp#+1ØÆÝ{zÔ¦™Z;ñÎÖY½ÈÍMi¢ŠµMkTÈ7 glÓÔ–ÃZ±êts‹œÕ²·Ã ‚6Ò ¸È†<Á³‰`"Í3@ªÅŽÅ¥àÄVPË+m†IK_¦øÖ§<äîÝ£L…d39Nñ+;rDŽHcB÷Aê„@Ô ˜ãŒi÷cÅ]c®w¢ªC1¶ã!sR%à4²©Æ®eo˜iY2R³ .õ³7 —R‰ŒÚ›°Ý ±Ç·ÔV¹´¤IØ'tRÙ«#*wÍ4ç‹®¨.DÕ)l£|˜Dä4<ÿRãÞºÓÖ“cSˆ‚ò£œµ.¨CZF“Làèd¬q¨Ô"Ú7*ª#+”*à Ѹ”Ðá¤0m0m15+‹‹FÞn-¼™,²»zça›[ ¹tùÐC 2´CdT¨£]ž&09ÜEZÙ*å#*hFe¡›±—mÝF=]q›„DËš&Zþ㙢ðà•¬½ÍˆòÞа¨iSq—W²©U[UO$¼*7Ä®ôéif›:˜pÌw`ôäcL…"½®ûb†[¥cMUSW³E¥…(V›UÖ‚ÇZ1ŒÐµ¢r3LäÅ=š&g6Õ3rã<@Ê5Š±ÑÅ›òıqɦ3ËN•Um!C4@ŠÛý]éþ®ôQíj>«ÌÇÓ(£è Qˆ±¯sA_-š$u»)‚‹¯(h1êÄn#*`9ÐáŽe4<iäæ‰#+pôÛÅ…³)L8…Ù 4ÃV3<“m-÷aZ¸2ÈF;g›¸HÈmá&47,ÍÜJˆÔ4pɪ$bˆYžjÁ\̬Mnñ¢g%MfHT06™ %‰Tø¦r±$!htq`8XqƒFŠ\ŠX¬ï¬Ä5–tc`ÎÜqœ˜RcõÖ®“éxú’I/’>.¢v“O«æçñj¥,Hˆ,F#+Q؆ˆv2I-¾;Vè¾\üóþaüÝœae2m–MÓíÚ¿oe&¸}ývÝ®HIô®Ÿ9®;ÅÍóÂÕ'Øb ÒNÕSº¬fƵn¤1R˜ËnÏÞq(0‘UQ²»ÔR¯¹…=M5àÙ?G²5{©•ðšîà’O{Öx§9òPÕöv‡5ô b¨,åVÈe&8O—Ÿ°'êØ}`oìé#+2;GŸ«Ë×Užž/ždJpšÛ×'iäõµ±®‚¦ôh?wÆÿœöd£¬PlµR¸<³gÏÆos×n7„Œm#*H1É"a~öî€òrwnÍ›»žÿUdöÆXãá\÷<¹BùÊ= r;àƺi![k«ð„Hãƒ"ûðûŸÒÌge‰Ô<øOE¨÷7=ˆ`–•ènb¶ìÛIIWj™JBHF!6$‚#*V’`@H”*#+{ÐÕ€7~ÊÉ<Dz‡èân·m{»kׯ¿ùá¢ÄbÕ5CLµ¶ci1ŒLcH‰RPµL«ÒضKImX¤£lIY4hQM•F!›BšjF”›$£f”’¢41¦Ñ‰H–Œ¦h©M‘J¦Œ-$Ãl0Ù„JP’cV°AUoî7Þùôð/虨Ng°è:MHd)ì‹F]o9ÚÎÇÝ4Uy\ÐvðWçy9šÿ+È’3‡‡#)œêáßmzæ%r6ú®RNÙum´ƒ/Ràzñ@#+ mC À½jŒp%ÁV[1¶ÅîÂb[pÝÌ”‚Ù0eô}0~1xʲ@ûáks9QBF+ ¯nà>.AÀÜQÖtíêCÈ¢™FÌO>!¶òI!pNƒ]ä{ý9¢¿WQ¹éíŒÕF©©ˆNÜÙ~ʈ\ÛóÑ«µØæ§P#+š?ˆ3Vüm¨ÕD–Ù6ˆŠÀ˜RmcGÝZµÍ¦Æ¥#+MFŒkb3ð]«™VZ•ŠJ¤}ia, Ë¿À·€ÈÈB3(d…‡³"Ù›”Á÷Vtö`ÏŠá(TGaE¨i˜Œ7?,º5XÑ“‹§CªŠ82V)©#”˜ˆ¢Ûf#*UE"#+tå³FÉÐË!`#+@ÐqÅÜ“ I‰çÉ%±jouåݺvs^Þuyš1”×FLˆX#-(`£Ä%$¦ýÔ&j#+8EÁጠ¸»»Ä2‚lZ(à‚ ‘‘X‘—ªèøC¾÷µï>ê#*F“äá‘`rgÅäÁn¦.Ì´‰Jû©EˆÜ×I‘j#L!(ÊHRˆÏ:ÔÎn`ºÐü«b9×›1·›‘ŠU"tq‘aî!¡o@ƒ„Då0„Ä#ß!éon¬ #±©#kmA±¶(*¶kH‚"Àpä#+K†Hä[Å’D’I#€U†*!ëßѺLŽ@i¢d)Já£qKO5„ˆ^žÆÊz·±Ý‘ØB°WÛ#)ä{»¹8Æ~´¨žr†ÌçzdHFBÿn/ë~̉“ª:zjÙ¾^H%¡Ì{‘Ò‰ª¡ÓuËÖ:DŠx µ%BÉ„bce+kS4ñóó Bðæ›÷ì+C",dæö¢Ðñørðý«Òå1*í጗Yøxxã*Š7ßš™*¨¥K6×jãe%=WÞTŽwçLMKê”ãjÛD½CHÄŽºŒšW°Ø»øäûo#ÌÒ`=¦"Þ¦©œÝ[S[ÆÚdÊŠlA`‰£5%4ÝŠº [—Bë¯yZrµZ}^“×`ÕŸˆz8®Å6WÒœ“•®J5yžê¢Žìš<§ ¢ÀHƒ'¬ïàYÃN}GÕ}¾k±éøɹv±SIÏœ¾….b'zFIJ¡S‘ê5êOöflSÌÁ°ãºÀ¤ãcµØï/¬2B I£<ƒ3ÕÌTãŒwj7éV<ñ@M„Z¨þ?œ¨nìj“2`ÃîÝô#qõÀÊ%»¡ÐA÷{NqHR,’""#+©ƒO¤z¾©Gq1/±û5´mƒPq#à{ì=ØJaoÏ÷þǵÂGæÎ3†’x%Éôºkƒøþ¨Ù®{¹©d*ºh°À†U0ÚÝ\C’t€‡btÅ^~I;Lƒ ©XÕï=¡½;éòî/Ú#)'‡‰?e‰óh·ÂÙO;ʤÂA/˜”UÒ\yge)^±²=sDiù~{ÜãŒuñ¨õ#9d,™ÝÛÙ¬–"]Ò!ntr• #d+}å»»H¶»…†|Y°‰Jwa¢*®ˆ˜ž'*óƦxN#+š„G x„(€` Y#)wÄfAƒ‚€€jþSuVA/úB¢ hœäI†ù9Qw.äÖì뀰8e˜$R5)&Øn$”ŽÅpÉc#*Ò„ÚQF´Â¡0&¹9팈@ÂËÒp,@2ÎÔgõ,3cÎÍÊvÒAÓK‚Æ4‰"q“H¤·Ÿ—™Údœ:äK'[àY†˜l›µ¯K,䜒¡ãaF¼¨q¢*T©€p(6<ÏÚf`P1)‘5B08o±lÐE¤W”®<mlçaÁœ¤•\e!…‹uLîÖîFaÁ #*‰ X'W»¸ÁÇ$DHÉawÃn:vèïÁÅ®Õ(èÃLØå¼°€õ¢Éªt2BC¢#)bõ£[Þ+ËÅ>7Û—%È–úÁ’/‘ÒF~y¸v“‘»ÜE2"Ã97€š@³1-$TL NI‡…Õgl_éÿ’NVÛqÙÕLÊçÉ”ÌâçÌ|e‡—–¶ê)1ÂÉ>}ƒ~D°r¤àº×4¢ƒvkcõ#+Ä1e·2áƒnìмPÍÏ:"PÔ…$€uÜwÍZ…ƒ"À4d)/BV²b’ °Šg®q'^c«¹š5@·Iå¥lgºëÔ¬Çûµ^nÝŠœ3yzÞhõâ²¾#*3(I—#ƒ£ìíy`•p“HöXÈßlÄs 5‡…vA[ÄâñèØ韧—¾ü:fÓ½f4ˆÖò0æmg8pâÂpëàÓ$é˜3k:{OAŠ9 6q8v5´ÌNÂRd7m¶ÚX)‹‘Ê’(Lè®ZŠ1–”•¾cvLª4f\±ÖyíÃdɽ>ñ¡9²äLG&FæŽ%5í!¤Òžù8ÐdZ®{‘² Òvk)Ûm®š]ÊHŠg0ÑH6SDâK“;K—.žõeÖψ[m0Zyk.eŠF«—Mý:’-“_g|X™á[§pBsqáWbEºÁÇXÍñ4Æ´Nòò2bš-Mâ>‰£ŒÊ²Ã‚Y“‰†s-š00¹æ§ ¬S®pUœ1ízD©š½é¬S\¡åÐó‰¥Ëv'#*tÝ“ëˆJñs&âiÒtîÔ‹\sH`Za#*YxBË•ÉaVj¶»&àdKŒØZ{§pwreè¨âÈP·_.u«aw<‡×móMs±\œjJ݉€®oÕÞà{Þºg(ä*ær5Îih+BFìIJfÁÈ8¬#*¹ž\šÕåÓJ—)ÖÎ9‚Xw0rô :^¡¹ñ1–;õNå5âÓ!?'Ã*8;_}®fà´Žøk(D$N 6)ËC f•`ãf(i®»@;MX#+ÄRP„M#)Æ“¼)ÃS–´1jn¡®å·öÁºZHº+¤‚BN\¯…mgY¬ù%C]ÞÅV…@§„#*4ÑœèLkå#)¼»"È@x;vüe9§àcf¡øétR^›=k4Q&ªÉ=š‡“£{M¢æߊm‚ãb5éhÚÎÜæ.ƒ¦m#„Iøßíû¥öÙs.›"—!ybi¤ü|òqζò°ø#+ƒ„˜l×Ƨwu#)i×ÁÖ¯®ó\uK!®~~·¥Õ#ßrÌÈNz´ÉXÍQLÍ;T_!5qUŠÔ¾®èj齜&Ú7›ð, –FJMjfÁ‰LX|2ÜX[[ï˜#5…1 !#+ÆÃعb£á«0tÔLb 6Òê#+»#*àf… €é\&8æ¹>LJd†½áõÒw+mÆ4,¶(»ÜËuvèZ0¡ÉÄ (K(f…¤cn8é>{rko)Þ¨ †˜ij!¡A¡¦ ñÅàÇQ4ZsжÖ&&Ú¦‚,¸ÓÖß™®`—s¢ÙÅD^±Û•‘Êk;¦¸Ë ©MNNpŽËW4W*aÛ“€ëPÐ&~±×ŸX¼ £;ï¬Qažœv3†“atŸ¤&ïnɦ›ñ’ÃGåÐ캆„‹Yƒ®.ÏÙ®jß’ÁmáÌé1ðà ÞÅÄS{qßb: !0dG4lä]+T£p:5Ò‰‰™ù¤Žd‘A¶¢„HòCªhEáAFH"¸ªPÕ5“S¼–”ºÄ8:ðšÑ”a˜¬áÍ0QcLjNîºà<ÙSlð°;*0%DH!¹¡.æ¢ì+TК÷™‚Y0#*#*L#)ŒN’.jdë2#*¹¢‘ÔJŒØCSÕâ„5&²Ìš,•œMÃV6feŠ¨”`àQ®ç›ã:š½V™iE®Û]\Q$ÅÇxTÊ®:!Зz2*#*ˆ¢ÐlïC |ŒØ¸#aȪ#+v6f4šodADà ġ¾þdd„jÂҥ͕MŒ„P6' HD†€6NÃ4#+’0`éFp†Í$Á50J€uÑœQ´3#)€¦ˆÜ`Îq·é:Á# 2:ÎÚ@Æñ…¨Ø#+S&pÃI`³Øâˆ^0Àgi¢¢‘H±DŸByyôŸÆÖ9Fª”QQ`1¯Má#*áËcêð_PØ3{¹!Ö! &°U#+‚(tCïßú‹yì½€ñ:ŽUbÞTW òKóvÒ³S%Éùú¥gy3>Fø[JÞPÇðE£MÃ܇)üç|=º¾6¥MmºåTÞchS”uá÷H…s#3A7bÙÒÃ$†8lªŠ\¹$ r‘¯çK5òìé^zêäª õZˆ¿š º2˹DJöR* UzRƒÃåY†SAEB;4«€ö0ã×k4AüvïM2p#**¸J‰;’£2;Ë•"påp2‚‡24ŠP}öQ-çµ#*{4GâV‘}’&™2xäïvÇàÄms,:nÞ8§èÖ•ÔŒ8"s˜¨!š€´#*~°¨¬Pœ‚â—Ù›–Í,%ì5J¾Ø’ÓÑòWoÒxzœ¦BBLÉTEÅ£ÃÆ u/¨UL}*ˆ‘TöQ—_Àù)ÌèÏ„‰htß^ØìCÝ#)Àϲ m²²rè>d¼‚žå¤$uJ%U+'L,P`Æ¥#+5Q¡(ßÀäNN5J¦dÝÀ*v©Z0b#i`ˆŒ+q“µ¼ÒC&J-âjwÒÞní6ÒfH –$ÇH0Ž6BÉ#+AvaˆHX€i #Ôª i Z4(Ðû©3 IŽæG\“H2?=Ãjæ¸ï§(b¹«Â4jÂ%Ö…#34u*ÌÙbQ „GFùçÕ×;goˆCáwÚµîùј"rN€€YÈúc/è#*XA‘¬·“ŸEÚÛF'„j#)ðƒ¤Ì5™‘Í#+´4V%vH˜†Ja^0kbk‘‚ÚPƒ< ÜPžŽ€šÔäqÞÈ)¹ŽÁw²Ó¹óg1ç×g>ÈhÑÕQ4eU=jbʯ,È_Ýäô¸r²ÉÂ}¼C8~è¾&à·e†9/958ãMHÀîí¹lÔŸeV¸¥˜…Bóïq-ÊŸ(Ó…ÓkTÄMAÔˆÎ!²¢‘÷z†ž‘ÇN͸<WjH@ˆ×…sCaÆ—G#*5|zg¢ˆkLYŒ}ý»±†Ð^H`1…¯«ó£-²±db±'±(ùÎg<†½#*(É+€ZS{b”„ƒ$D÷ÁZE%³kZRmM÷z‘XDVBEGE„VìE—!F[L!‹3p„¸(·.(D€°B&´W¦#);#){róÈ¢‘¥H]2ÿ"ìCg~UËÐÞ!Õ‡vØ9Î,‘a0‘"HBê½ü;þˆt¾µ5*±c~qšï+‚‚ šþ8Û·áž*xp®ù¾¼o¤p€›6t†Ñth¼g#*`¡Þ»Ž¤Å½û9†³{[#‰AŽZHSÌ0Ѳ¨ ¦ÑÀ™,I6’åU¢›kÉo'žk».™gt¶ç;x«…×®óÊÛ”½ZÉY¦ëµd,Á*K4¢¬€“ŠœÍú®¡–fuHŠŽ¼d¦l‚®å,"l¹‘f™¥>Åìã›Ðz8½)a 1V=œÿÛKà6-h]s·1´†;&ælÐÉùgN¢ó#*‚˜ÏþÀìæÙ7ŽÀ6‘¹âQW"#)‘16ìã sôÔ…ß2½©g½Qfúö08D$Pu¨S @‘!2)#)Î&½Ü!0PGkT<†ìUZž,Ö–C¸œ„ÄŒ¥?ßãÃ;«¯»›h Wä§Ã0ü¯PÄ'xè(–¼e[Ì…´ÒУÆo8œJ-9—‡—:—ÂTãî8ùõ9ac©¡JñÝq›å.ÇòæBÀØhÓÇ=ÜD¡6“½ºgÑê× Ö}PQ:P€A©R†IJך'Œ|DÀ¸é,ü5øÃõóÑ_väŒVÈ éG/—’DˆÁR)›I©%¥–˜Åd” ª¶-f&Л(©1jÒmû«Œæº5‹¡Üï7Ä6ÏôÇ*¹df¾ÝhØw¨‡»¦oñšœÇD´ùÐÛéêæ#+¹ùÏo,8Ù‘dEÍŸ"6ONïãÒ{¶î( ìTìÚ{®™NB¼ÛfA$ŒÆŽ}÷âQê×s‡I*°Š74” +–Ô´½A#)ñ7ËáM´W®§!¦ÁjRÛCÍQÇH6Ù¦S¹3¿@0ÔÝ47 G²•Ë¹,O6PjàaWDÙ6fÑ ëEƒ·ÂDÉ&FZеŒ)ƒäuã˜á#*¤ÀÖRhIHnbítqVùsF?+#+ØÕ:âõ9Ê\1¶ÔìÀÑ€#+-–2[BU‹(nC- ¡u¡±ùõ»ÝÞˆ©ÁïT>1`!}î¦L˸Á,Å¥LPæìTkA¿ãV´0X³«Xpm/À°ûÙ“c"úYXS“ù2áÐèÆÄ`:û=/0l–Q{Šä³»¥#+P]¿%¸ï,‰B#ÚC¿‘„æ²tôâø:—L¯#+‰Ðð0"0!Ò1sUb©Ó¸èºXøú'ÃÌ¡¯FÿAÈÄ’K@9& *'IÞF/X¢£C^óXIÑ{ÒˆPE¤ä2t#+ž!o~Ãêíßêý;{‰ÞbG?>£9±žfÈìó¾Ó‡"v‹R6–fï=aE½ž;o=J±(#+<Q¿ŸÛÞ'¸wó·öC{÷ÉG=__sn»³c蚀‡U…ËÛIvÍ|í¡£©ÜÇ5ô…¾ÒŠ‡ˆ7ùE6sq@ ]à½4l±:»5Š¯/¦Š0$2c2<]FÄF&]ÕÐHl™¦WϼÃöû¶¹W79ëÍçž»³„‘²RŠl†¢ònæºyM£mãs\·Æ+»·‹yåv•¤æ¼É·ðV¡•v*Ÿ¼‘M‚@PÚe·gÏï;ü%U|Jøûlx˜/fzƒ¸’³TÂyšyßH)éP::ß4d#)„‘«(ÅIu´Þ=b!ŸQ ’A"ÈÂ)E’4²’©³jõùãQ¾î«÷wÌÒ#+4Cš¢¨Úi¦cm¯Çjvˆˆ&Il?“`l!¯êF¡+aêä\X]O#aM°GmBB.ò}ª*øJõ¦î¹²)±¬-f¬QQ£fF+X©”[,ž¼ïm&µ*mJmcª”E.õ#)½¸¢&¯÷øþüÔôö“Úùüs0ëÈ’w«ÞMzÀQ“Ú>gÇeL|ù•5\……~—YˆAòdż:Ä{‰ì#*XÎH.yÌ„Ê/yèòÌq좫ٻ°±…!"ŠN3ä?Ít €ž&ny¤À˜ÞB Q¶Äb‘pû oMýw#+‚€Û@ÖóAÖ¡ñÉ‘0Št§µZ€¡ ½!BŠS¼|·WFšÔ–Û¤Ö©¶¥"”’ ÐdÓdùé¨ 85¶Ù´˜U«)#*aJ€ë.•)ï)VE‡²Öe—Cý†¾Š¡÷GÇž4Æ!ûøkáaÃûŒ5†W§4°&âcÄ´LüVuÅ3Wch« ×ÒµÚ}èÁÆ€›M¢%Í 9ßjõ-òÀß&ÄÕ£è{Æ÷>rõÚ61ýT‘Ç+õ`ËÁ„{½?áWû@ ðWRC¤i„IŠSLJ“dj)lb¤Á¨ªM¶´oÕm¿#W‰°ÑZ(ÚM2€Ø•,ôôÛç[(ËD$#+B©‘„<ÔЫ |æØ@è*F…F&L(÷ÑMCfÒa`¡7ë@ïô}0ÏzíÙ/uÝ[imoçÔkWš±mF- Ú”5F„«6X‹X«RÆ „ø’3†:t6ù3·kiU–JHœËÆÿl}ð(€!£õÖX¦0Å.)E4¤»ÂƒžQFzžQ%Û馗àIôx$È«miGlþ'‡šYl홑#*€Å…„«‰9æí‚ÇûŸ=‡ECˆ&íÝWMãô-Öi54R ±4›a‰ˆ’&¢ª€4‚ÉéW%E¼ÝÖfì̪뛶¢®v ’Ùm^]ØÓDÚîêîêÒl©*dJlhV@U#*V$©ÉJ°ÁÀ·k¡¹o"o%i„^•ëˈµJil¦¤Ë*ôuo:º¼óWŠŠÊ3LÛe²–¼îå»]ۢ˗Mtšå®Üö©Ùšì˜HVØF€íÄZ´`&ÍOÜ5åUY¡t¤X²nBøQ’ñ°˜){Z6gªTÛW9d¢ck?žô™KhÍ¡ø¤ÊAT.F¨’oÅ#)9 vŽÝÎpéÓ\MÎp,ôi‚Ér{Qv~×/มpõBK°‘îˆyü7‡Ç0ôźsÛéŒI¼ž†Ú¶éƒ•)¶uF±±á¼·âBÝÚñË‘ñüȦF$Ç©CUR§WÊtÎÑoÎ÷¼QŒ-õx™˜zä)ØaöíådŠ(â¹E#8—…Á¾½Iâå¯â½„#)5:‘0î¡5$ÑÇÃ3U‘wà°þ̤—øæÍ8;x_°jùœþ©+.ëIéìcìE±<@yr#+RÕ!®*F<¾ÀÖ±ÛU‹foÕñŠÈ%£h¥G|6×U:žóPj” ~VoÜv•#*ñÞpÅ`×L‹ÝŸ¦žl“v›³ôhœKxP¼¿„³4pcoGê>ÖìâÌ ç7$ä5¦-XêI0TR'·!À4¶ª/pÖšÄëj hêa×»[0Qüá‘Élc·É#+Ù]©´±':µ@¶¤YzêÛÓ\ñJmå5ÓuÖÜŽWC%Û‡Ôq ä<e~äe"hóƒÉK©8>YãÉÈ[9Ï|Çîù¹‚Öè|l‡pYyL(7Æ|5NˆÞöÁò<-šX ýî8 M[‡Ñó.8#‰1YÚˆËÁäŒpÃ÷2\µ†{h$ìŠùÕ¼›Pnh Òþ¨6±ØEì’ºQÅæFÜn‹hÔ*ëǨïÆ…@ãƒ[8AÀÓ Êö;÷—çAÇÒ!"1OAªs¶Ü±2ÒȤ¦Qhšät Á‡-u1ßp–dl0Ày±»o¶£ï=(ð1Žäœl=¦¢ûEè9œG™È"¸=г™Úxû´2 os+:ίmåm=ÚŒí¯Êöý¾Ûgâç«TO®àd ¤úñ´ ýñ3ød„L¨-é¡ršCp…@C·Qü…Šã~Ï4ÉPUµƒÌóOftë#)OfYvë35Ãǵ^úUsb¡2ýë#) ?ýÉ$ÿâR†V¼wÎÕާцRÄÜá™íúN±LL§#)¤p`óõHŠo!Pv‘#*%η°S+VEÀö´2²Z¿@CbŽïQnóí¿—ŸØ÷;º‰$ 33çò¶U¿ÕwUðï$rφB@;3Õ©.í6ˆkC’2tBÚ9DmžG}ü/dp¬°‚uÇfý©é´ ¹ª¡mÒñ#*ÖâƒÕè'º/ÐŒÊ{†qäW„bA€µ|îÌ3ñ‹À;:hž÷d\:_ìÈ`Ï6l€Ctè~+—Ú³õ_Ëðò=íC‡’íLáCáÂ2éJêóøCxŠ#+,]d†'ŠD$í¯”‡oóUËrOü#*Ê7ãKýšÝe¥‰ˆL‡1Ò·Yàþs#5¯×Íà{R¦¤ˆo½)ž:Zef—Y#+¡¦†#+]”#*‡—šTÁŒ0ÙÿŠeTò´o[x÷ìÒÁ§îS¯sÅ#;µ^ûlÝÃL++UŒº¢X9aðiæ̦õ@£µ»G‡Ù¹4Ñ#)fÜkc•AšŠŽ°lÄa¦DÑ!vÕI„œ^$ö–l,—rA‚C šÔ6ú¼ú¼bÓ›Å#gw9h‚Ð„8ƒ+Þ^è†{Í:‡²¾~YŒ£ø&l&Ï6ì¹°ÙB¸©˜°w‰$Y#‹\ž{bò#)c.4£™èÇB#*ùLÏÇL<2t)«zׂƒ}vo …iÒ64k…³)Ü#+fì‰ÓÇL}2›îöB±³è h3‡œ^iIäqÝôac·F³U1]é.`AHŒIàˆ˜z†|ØK¸RCd0UÊñóüy=ûŸ#*¸ax1¥wfLò¹ƒ¶à‹ÏÛ³¯]×8óÔ,glÍ+P=X¢#+•ðÝÓÂâ™vÝf``3(HÈ&½®épØæ&‘V† ÷‹ „Pð:¢üìØPÄ‘@$HÁBHË´e!ÅÞAyð–á(8caÆ—Ð0û¿¸ŒÌáÿ{MÂüì;Œ¹Yݲ@XŒ DÀ'-t¨n`ºô6îébtÂ#+D„Õ´¤LjÄÔJƒlkY¥3[FÔm’¨ÌÒDÅ…‘S_;¶®Ú¨Ï::"füO˜Še#)úȇß#)/ΪZŠ·,Rزs=r€Ë‰M@±=ý?ÝãÉùÏÍ##*"¿L·¾k]O¥×¬Á¡ðcS9öɬצåU1Š#€•sóèèéüÍäÛ\ç•pè-Ü—uñËþëû¨mŸPÌïð€^¡Ë‹Žðl¤ïjoEwÊ [å%®ð†=Ç•N›æ7©¥<X?Íí럨N#+3ÉšgY,ØÕL#+š<³}<*¶j8# ©‹#*D'ÒáG½D᡽ÑqIâJò5–¿=7§ÈÙmJS{¦8r›W4 °RlÉ8²¢E…¡E]o¾×1±hø6ÜŒW¶ô£W²Õ½MTš6‚Õ{æµÌ@iªß ¦ë³°X6*<0Ý0HF ¤22OŒ#+÷¼7q²§ ¸q“¨(ÖêvعÜ8.ÝP³#+ЩÝ˸ǚ7áUÁbÇã*J9…¨DôÿÞ‹öA]Kƒ\‘dFnÅ+óýS·»êez=(îGSìä/"Œ ýl¸¨yͬPþÈŒŠ,X mv£¸íÔP§6kk%µ2šéÕ«³jú–&ÍI¯WѪ×Û$ѨªRÀäw³ÝðÌòúPÿõ)ª)£¿§ÑÑ;ü%c¦¦eò²Ó÷=g0@ÂfóxÈ{˜}íˆÈÿgÞ—‡ßýÀ€í €âïÂ&¼l=PH,Š#+,Ÿçò„Zÿ·žœÿ¾U'ú?¯"‡=d12„÷ÿÇô½>í·ñ6éx¹h?ÃÈþßëô~§iô/Ï㌻#*<Ú+:³qM:ãÉïøuЧ·Îä÷„ùG²y‹%ÞŸ_ŸˆàNøŒ÷§þV'—ˆÕûÄOAQä $»ùiwDüeàp#+?dYðêÉ×ÿ6•occ-3{(WQ6éÿ›Ø#³ÞUßæf'üuâ¥Ä”ù6Qû*Qþå³Ò»xœULà›çªáq–s¦ò8‡ÇÕ´éÛDè2²±ûye‰¼#pÅå°–7áá'ו´æ.ÇŸÁȬ2LMf¬yÙpx`HŠ$Y@zõïÔ=²Ù j‰ýÞÍÏ«_ŸûŸ©®E=Tg6(‡ÚgÝ-UŒF7°;·m¾Ïö™m÷Ž_—øy ¨ÿÿrE8P#*s† #<== -#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAl3aW3cACgkQSbTGfAUn\neqruqA//Y9oJ46ZR8W7YB/e45bfrYxGbN7NnkvkwSPNziObYur+n1QpQEOaPTn/U\n5kFtPWHXRJzaG/A9poKn7pl1Xd7Edcu1aalfoEazZbuD37VOxIp9lnrefCAeICqj\nGv0SD96Zac91CbA+b20Q4xnqxKMi3LSI4NPjfFGy62FkSk3MS4p6Rdp0/WAKwwNj\nw7WEjQCNmLb37z+FGSzXg28aljYeteBZEthsVmGJ5QqVwMBwgj2+y5FOTzFfxmqB\nrWgjFYS0l85kgYRZv9yzdNmFs5SScwafwpT8Xmdr49tFn/+0LxXyRxX+rdODgrpV\nY4EOiQz0fd6mMMnaTDXlLSXls3JyVYmbTjeNL/9gcHmnStzJ851CJQfyQg7A+JoC\nc7nz0HbiFyTgB+PUZr1OhGj3A7287o8XQ0tqR3oa7jXIOX0OynrGplMQKr++0jE1\nBgKzjLoE9CTbjkQfICLG+aUy3S1ZyDk/BcO+5+Ytbru+qXuDsIgAdVosMfNSv9jJ\nXvOINsbRMekdejYMZv8fIkn5OEjCFHVhNpobEsCb768bjB3p7alQGECBvjHCm6dy\nXZPzl9cBMWIXcBjPTS+GZj+PIXGcu76pbsx6HBHWf+uJ+4xgOsUCVu//0AV09jvA\n0MjtLWwQ8mdRH6Wt4hsp4HKtSvQrhmljf2OnuYBgaFmcdJkN1zI=\n=C0oT\n-----END PGP SIGNATURE-----\n +#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECzlystnjLqtCPS4PIr4MYv+/pUgFAmOxj14ACgkQIr4MYv+/\npUgg7A//X6agCAo9x3REEDFk4GTbW8nMkH7gD+ixNnMNcDF96pmp1f3Qdkm4DU19\nscpa1IPN5ik9xeU4Xs7+SifSlJrT1h9gj2bCFIPWWVlXjCXbn52mU4lijQ4iyaV7\nwLuD4ebG8UU9QK5gP1U2hERdTbkuOxuhQijTijDpfOuUnR1N5YoZmONszgYvYmAL\nr7Zr+Cuc+0HiEdsgMUabp1LTyY+urkZJoHcOTMe0QoyrTvM/CFgB4V3ppw9006VQ\nV4XdoAXEkNNfdJUE1s+4SrQxWT2AC2n9X7vjxbqNwfglzrujf862JrloOjybffdg\ngMPXwLpfT8TrVHFSs5aQK++8YhgwcgDz4Biqt/aq68iM424XnUYSwkzZTowGM/A7\nhyM7Jh+Mp/Of10pIx6PBbjjhbh3dH4OUtFv48uYLh1V95ICQ538UypMfBOK9mEPL\n8zpyTbQ8U873Ri+awOcpoJ4738gZNHAbEHZ3Ctr+g7eoayNlFMENG8bMrLwOkiUQ\nLZVXzNPtPs/OFe0rYu6okVONs8MLmuArnIy7v21Ti73Dtc4ABP6V4CFfc3AGqO+l\n3SmEb5r31h+iXvc4vdp33tESohxUFosIgMGjD8ZMyVFWy78wgp5I9qSbSPdVhl60\nF8WGX/ydvcf6D8NDZqjrsRyFtxKn6xLRQgUI+6DKsNLwRzJBVas=\n=mYk7\n-----END PGP SIGNATURE-----\n diff --git a/waf_libbsd.py b/waf_libbsd.py index 9ac5bf35..f1088584 100644 --- a/waf_libbsd.py +++ b/waf_libbsd.py @@ -1,7 +1,10 @@ +# SPDX-License-Identifier: BSD-2-Clause +"""LibBSD build configuration to waf integration module. +""" + +# Copyright (c) 2015, 2020 Chris Johns <chrisj@rtems.org>. All rights reserved. # -# Copyright (c) 2015-2018 Chris Johns <chrisj@rtems.org>. All rights reserved. -# -# Copyright (c) 2009-2015 embedded brains GmbH. All rights reserved. +# Copyright (c) 2009, 2015 embedded brains GmbH. All rights reserved. # # embedded brains GmbH # Dornierstr. 4 @@ -9,35 +12,30 @@ # Germany # <info@embedded-brains.de> # -# Copyright (c) 2012 OAR Corporation. All rights reserved. +# Copyright (c) 2012 OAR Corporation. All rights reserved. # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function -# Python 3 does no longer know the basestring class. Catch that. -try: - basestring -except NameError: - basestring = (str, bytes) import os import sys @@ -48,6 +46,10 @@ import builder import rtems_waf.rtems as rtems + +BUILDSET_DIR = builder.BUILDSET_DIR +BUILDSET_DEFAULT = builder.BUILDSET_DEFAULT + windows = os.name == 'nt' if windows: @@ -55,12 +57,16 @@ if windows: else: host_shell = '' +def _add_flags_if_not_present(current_flags, addional_flags): + for flag in addional_flags: + if flag not in current_flags: + current_flags.append(flag) + # # The waf builder for libbsd. # class Builder(builder.ModuleManager): - - def __init__(self, trace = False): + def __init__(self, trace=False): super(Builder, self).__init__() self.trace = trace self.data = {} @@ -82,12 +88,11 @@ class Builder(builder.ModuleManager): return sources def generate(self, rtems_version): - - def _dataInsert(data, cpu, frag): + def _dataInsert(data, cpu, space, frag): # - # The default handler returns an empty string. Skip it. + # The default handler returns None. Skip it. # - if type(frag) is not str: + if frag is not None: # Start at the top of the tree d = data path = frag[0] @@ -96,6 +101,10 @@ class Builder(builder.ModuleManager): # Select the sub-part of the tree as the compile options # specialise how files are built. d = d[path[0]] + # Group based on the space, ie kernel or user + if space not in d: + d[space] = {} + d = d[space] if type(path[1]) is list: p = ' '.join(path[1]) else: @@ -104,14 +113,14 @@ class Builder(builder.ModuleManager): d[p] = {} d = d[p] if cpu not in d: - d[cpu] = { } + d[cpu] = {} config = frag[0][2][0] if config != 'default': if 'configure' not in data: - data['configure'] = { } + data['configure'] = {} configTest = frag[1]['configTest'] if configTest not in data['configure']: - data['configure'][configTest] = { } + data['configure'][configTest] = {} data['configure'][configTest][config] = frag[0][2][1] if type(frag[1]) is list: if config not in d[cpu]: @@ -137,15 +146,24 @@ class Builder(builder.ModuleManager): self.data = {} - for mn in self.getEnabledModules(): + enabled_modules = self.getEnabledModules() + for mn in enabled_modules: m = self[mn] - if m.conditionalOn == "none": + enabled = True + for dep in m.dependencies: + if dep not in enabled_modules: + enabled = False + break + if enabled: for f in m.files: - _dataInsert(self.data, 'all', f.getFragment()) - for cpu, files in sorted(m.cpuDependentSourceFiles.items()): - for f in files: - _dataInsert(self.data, cpu, f.getFragment()) - + _dataInsert(self.data, 'all', f.getSpace(), + f.getFragment()) + for cpu, files in sorted(m.cpuDependentSourceFiles.items()): + for f in files: + _dataInsert(self.data, cpu, f.getSpace(), + f.getFragment()) + + # Start here if you need to understand self.data. Add 'True or' if self.trace: import pprint pprint.pprint(self.data) @@ -156,19 +174,23 @@ class Builder(builder.ModuleManager): for cfg in self.data['configure'][configTest]: if configTest == 'header': for h in self.data['configure'][configTest][cfg]: - conf.check(header_name = h, - features = "c", - includes = conf.env.IFLAGS, - mandatory = False) + conf.check(header_name=h, + features="c", + includes=conf.env.IFLAGS, + mandatory=False) elif configTest == 'library': for l in self.data['configure'][configTest][cfg]: - conf.check_cc(lib = l, - fragment = rtems.test_application(), - execute = False, - mandatory = False) + if conf.check_cc(lib=l, + fragment=rtems.test_application(), + execute=False, + mandatory=False): + conf.env['HAVE_%s' % l.upper()] = True else: bld.fatal('invalid config test: %s' % (configTest)) - + section_flags = ["-fdata-sections", "-ffunction-sections"] + _add_flags_if_not_present(conf.env.CFLAGS, section_flags) + _add_flags_if_not_present(conf.env.CXXFLAGS, section_flags) + _add_flags_if_not_present(conf.env.LINKFLAGS, ["-Wl,--gc-sections"]) def build(self, bld): # @@ -204,23 +226,45 @@ class Builder(builder.ModuleManager): defines += ['%s=1' % (o.strip().upper())] # - # Include paths - # - includes = [] - buildinclude = 'build-include' - if 'cpu-include-paths' in config: + # Include paths, maintain paths for each build space. + # + include_paths = config['include-paths'] + if 'build' not in include_paths: + bld.fatal('no build include path found in include-path defaults') + buildinclude = include_paths['build'] + if isinstance(buildinclude, list): + buildinclude = buildinclude[0] + inc_paths = sorted(include_paths) + inc_paths.remove('build') + inc_paths.remove('cpu') + includes = {} + for inc in inc_paths: + includes[inc] = include_paths[inc] + # cpu include paths must be the first searched + if 'cpu' in include_paths: cpu = bld.get_env()['RTEMS_ARCH'] - if cpu == "i386": - includes += ['freebsd/sys/x86/include'] - for i in config['cpu-include-paths']: - includes += [i.replace('@CPU@', cpu)] - if 'include-paths' in config: - includes += config['include-paths'] - if 'build-include-path' in config: - buildinclude = config['build-include-path'] - if not isinstance(buildinclude, basestring): - buildinclude = buildinclude[0] - includes += [buildinclude] + for i in include_paths['cpu']: + includes['kernel'].insert(0, i.replace('@CPU@', cpu)) + includes['kernel'] += [buildinclude] + + # + # Path mappings + # + if 'path-mappings' in config: + for source, target in config['path-mappings']: + for space in includes: + incs = includes[space] + if source in incs: + target = [target] if isinstance(target, + str) else target + i = incs.index(source) + incs.remove(source) + incs[i:i] = target + + # + # Place the kernel include paths after the user paths + # + includes['user'] += includes['kernel'] # # Collect the libbsd uses @@ -231,16 +275,17 @@ class Builder(builder.ModuleManager): # Network test configuration # if not os.path.exists(bld.env.NET_CONFIG): - bld.fatal('network configuraiton \'%s\' not found' % (bld.env.NET_CONFIG)) - tags = [ 'NET_CFG_INTERFACE_0', - 'NET_CFG_SELF_IP', - 'NET_CFG_NETMASK', - 'NET_CFG_PEER_IP', - 'NET_CFG_GATEWAY_IP' ] + bld.fatal('network configuraiton \'%s\' not found' % + (bld.env.NET_CONFIG)) + tags = [ + 'NET_CFG_INTERFACE_0', 'NET_CFG_SELF_IP', 'NET_CFG_NETMASK', + 'NET_CFG_PEER_IP', 'NET_CFG_GATEWAY_IP' + ] try: net_cfg_lines = open(bld.env.NET_CONFIG).readlines() except: - bld.fatal('network configuraiton \'%s\' read failed' % (bld.env.NET_CONFIG)) + bld.fatal('network configuraiton \'%s\' read failed' % + (bld.env.NET_CONFIG)) lc = 0 sed = 'sed ' for l in net_cfg_lines: @@ -255,10 +300,10 @@ class Builder(builder.ModuleManager): for t in tags: if lhs == t: sed += "-e 's/@%s@/%s/' " % (t, rhs) - bld(target = "testsuite/include/rtems/bsd/test/network-config.h", - source = "testsuite/include/rtems/bsd/test/network-config.h.in", - rule = sed + " < ${SRC} > ${TGT}", - update_outputs = True) + bld(target="testsuite/include/rtems/bsd/test/network-config.h", + source="testsuite/include/rtems/bsd/test/network-config.h.in", + rule=sed + " < ${SRC} > ${TGT}", + update_outputs=True) # # Add a copy rule for all headers where the install path and the source @@ -266,7 +311,8 @@ class Builder(builder.ModuleManager): # if 'header-paths' in config: header_build_copy_paths = [ - hp for hp in config['header-paths'] if hp[2] != '' and not hp[0].endswith(hp[2]) + hp for hp in config['header-paths'] + if hp[2] != '' and not hp[0].endswith(hp[2]) ] for headers in header_build_copy_paths: target = os.path.join(buildinclude, headers[2]) @@ -274,10 +320,10 @@ class Builder(builder.ModuleManager): for header in start_dir.ant_glob(headers[1]): relsourcepath = header.path_from(start_dir) targetheader = os.path.join(target, relsourcepath) - bld(features = 'subst', - target = targetheader, - source = header, - is_copy = True) + bld(features='subst', + target=targetheader, + source=header, + is_copy=True) # # Generate a header that contains information about enabled modules @@ -296,12 +342,13 @@ class Builder(builder.ModuleManager): output += '#define RTEMS_BSD_MODULE_{} 1\n'.format(modname) output += '#endif /* RTEMS_BSD_MODULES_H */\n' self.outputs[0].write(output) + modules_h_file_with_path = os.path.join(buildinclude, module_header_path, module_header_name) - bld(rule = rtems_libbsd_modules_h_gen, - target = modules_h_file_with_path, - before = ['c', 'cxx']) + bld(rule=rtems_libbsd_modules_h_gen, + target=modules_h_file_with_path, + before=['c', 'cxx']) # # Add the specific rule based builders @@ -311,20 +358,20 @@ class Builder(builder.ModuleManager): # KVM Symbols # if 'KVMSymbols' in self.data: - kvmsymbols = self.data['KVMSymbols'] + kvmsymbols = self.data['KVMSymbols']['kernel'] if 'includes' in kvmsymbols['files']: kvmsymbols_includes = kvmsymbols['files']['includes'] else: kvmsymbols_includes = [] - bld(target = kvmsymbols['files']['all']['default'][0], - source = 'rtemsbsd/rtems/generate_kvm_symbols', - rule = host_shell + './${SRC} > ${TGT}', - update_outputs = True) - bld.objects(target = 'kvmsymbols', - features = 'c', - cflags = cflags, - includes = kvmsymbols_includes + includes, - source = kvmsymbols['files']['all']['default'][0]) + bld(target=kvmsymbols['files']['all']['default'][0], + source='rtemsbsd/rtems/generate_kvm_symbols', + rule=host_shell + './${SRC} > ${TGT}', + update_outputs=True) + bld.objects(target='kvmsymbols', + features='c', + cflags=cflags, + includes=kvmsymbols_includes + includes['kernel'], + source=kvmsymbols['files']['all']['default'][0]) libbsd_use += ["kvmsymbols"] bld.add_group() @@ -334,32 +381,30 @@ class Builder(builder.ModuleManager): # if 'RPCGen' in self.data: if bld.env.AUTO_REGEN: - rpcgen = self.data['RPCGen'] + rpcgen = self.data['RPCGen']['user'] rpcname = rpcgen['files']['all']['default'][0][:-2] - bld(target = rpcname + '.h', - source = rpcname + '.x', - rule = host_shell + '${RPCGEN} -h -o ${TGT} ${SRC}') + bld(target=rpcname + '.h', + source=rpcname + '.x', + rule=host_shell + '${RPCGEN} -h -o ${TGT} ${SRC}') # # Route keywords # if 'RouteKeywords' in self.data: if bld.env.AUTO_REGEN: - routekw = self.data['RouteKeywords'] + routekw = self.data['RouteKeywords']['user'] rkwname = routekw['files']['all']['default'][0] rkw_rule = host_shell + "cat ${SRC} | " + \ "awk 'BEGIN { r = 0 } { if (NF == 1) " + \ "printf \"#define\\tK_%%s\\t%%d\\n\\t{\\\"%%s\\\", K_%%s},\\n\", " + \ "toupper($1), ++r, $1, toupper($1)}' > ${TGT}" - bld(target = rkwname + '.h', - source = rkwname, - rule = rkw_rule) + bld(target=rkwname + '.h', source=rkwname, rule=rkw_rule) # # Lex # if 'lex' in self.data: - lexes = self.data['lex'] + lexes = self.data['lex']['user'] for l in sorted(lexes.keys()): lex = lexes[l]['all']['default'] if 'cflags' in lex: @@ -373,23 +418,23 @@ class Builder(builder.ModuleManager): lex_rule = host_shell + '${LEX} -P ' + lex['sym'] + ' -t ${SRC} | ' + \ 'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' > ${TGT}' if bld.env.AUTO_REGEN: - bld(target = lex['file'][:-2]+ '.c', - source = lex['file'], - rule = lex_rule) + bld(target=lex['file'][:-2] + '.c', + source=lex['file'], + rule=lex_rule) if lex['build']: - bld.objects(target = 'lex_%s' % (lex['sym']), - features = 'c', - cflags = cflags, - includes = lexIncludes + includes, - defines = defines + lexDefines, - source = lex['file'][:-2] + '.c') + bld.objects(target='lex_%s' % (lex['sym']), + features='c', + cflags=cflags, + includes=lexIncludes + includes['user'], + defines=defines + lexDefines, + source=lex['file'][:-2] + '.c') libbsd_use += ['lex_%s' % (lex['sym'])] # # Yacc # if 'yacc' in self.data: - yaccs = self.data['yacc'] + yaccs = self.data['yacc']['user'] for y in sorted(yaccs.keys()): yacc = yaccs[y]['all']['default'] yaccFile = yacc['file'] @@ -397,7 +442,8 @@ class Builder(builder.ModuleManager): yaccSym = yacc['sym'] else: yaccSym = os.path.basename(yaccFile)[:-2] - yaccHeader = '%s/%s' % (os.path.dirname(yaccFile), yacc['header']) + yaccHeader = '%s/%s' % (os.path.dirname(yaccFile), + yacc['header']) if 'cflags' in yacc: yaccDefines = [d[2:] for d in yacc['cflags']] else: @@ -408,19 +454,20 @@ class Builder(builder.ModuleManager): yaccIncludes = [] yacc_rule = host_shell + '${YACC} -b ' + yaccSym + \ ' -d -p ' + yaccSym + ' ${SRC} && ' + \ - 'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < ' + yaccSym + '.tab.c > ${TGT} && ' + \ + 'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < ' + \ + yaccSym + '.tab.c > ${TGT} && ' + \ 'rm -f ' + yaccSym + '.tab.c && mv ' + yaccSym + '.tab.h ' + yaccHeader if bld.env.AUTO_REGEN: - bld(target = yaccFile[:-2] + '.c', - source = yaccFile, - rule = yacc_rule) + bld(target=yaccFile[:-2] + '.c', + source=yaccFile, + rule=yacc_rule) if yacc['build']: - bld.objects(target = 'yacc_%s' % (yaccSym), - features = 'c', - cflags = cflags, - includes = yaccIncludes + includes, - defines = defines + yaccDefines, - source = yaccFile[:-2] + '.c') + bld.objects(target='yacc_%s' % (yaccSym), + features='c', + cflags=cflags, + includes=yaccIncludes + includes['user'], + defines=defines + yaccDefines, + source=yaccFile[:-2] + '.c') libbsd_use += ['yacc_%s' % (yaccSym)] # @@ -428,56 +475,54 @@ class Builder(builder.ModuleManager): # specific files for those flags. # objs = 0 - sources = sorted(self.data['sources']) - if 'default' in sources: - sources.remove('default') - for flags in sources: - objs += 1 - build = self.data['sources'][flags] - target = 'objs%02d' % (objs) - bld_sources = Builder._sourceList(bld, build['all']) - archs = sorted(build) - for i in ['all', 'cflags', 'includes']: - if i in archs: - archs.remove(i) - for arch in archs: - if bld.get_env()['RTEMS_ARCH'] == arch: - bld_sources += Builder._sourceList(bld, build[arch]) - if 'cflags' in build: - bld_defines = [d[2:] for d in build['cflags']] - else: - bld_defines = [] - if 'includes' in build: - bld_includes = build['includes'] - else: - bld_includes = [] - bld.objects(target = target, - features = 'c', - cflags = cflags, - includes = sorted(bld_includes) + includes, - defines = defines + sorted(bld_defines), - source = bld_sources) - libbsd_use += [target] - - # - # We hold the 'default' cflags set of files to the end to create the - # static library with. - # - build = self.data['sources']['default'] + for space in sorted(self.data['sources']): + sources = sorted(self.data['sources'][space]) + if space == 'kernel' and 'default' in sources: + sources.remove('default') + for flags in sources: + objs += 1 + build = self.data['sources'][space][flags] + target = 'objs%02d' % (objs) + bld_sources = Builder._sourceList(bld, build['all']) + archs = sorted(build) + for i in ['all', 'cflags', 'includes']: + if i in archs: + archs.remove(i) + for arch in archs: + if bld.get_env()['RTEMS_ARCH'] == arch: + bld_sources += Builder._sourceList(bld, build[arch]) + bld_cflags = sorted(build.get('cflags', [])) + if 'default' in bld_cflags: + bld_cflags.remove('default') + bld.objects(target=target, + features='c cxx', + cflags=cflags + bld_cflags, + cxxflags=cxxflags, + includes=sorted(build.get('includes', [])) + + includes[space], + defines=defines, + source=bld_sources) + libbsd_use += [target] + + # + # We hold the kernel 'default' cflags set of files to the end to + # create the static library with. + # + build = self.data['sources']['kernel']['default'] bld_sources = Builder._sourceList(bld, build['all']) archs = sorted(build) archs.remove('all') for arch in archs: if bld.get_env()['RTEMS_ARCH'] == arch: bld_sources += Builder._sourceList(bld, build[arch]) - bld.stlib(target = 'bsd', - features = 'c cxx', - cflags = cflags, - cxxflags = cxxflags, - includes = includes, - defines = defines, - source = bld_sources, - use = libbsd_use) + bld.stlib(target='bsd', + features='c cxx', + cflags=cflags, + cxxflags=cxxflags, + includes=includes['kernel'], + defines=defines, + source=bld_sources, + use=libbsd_use) # # Installs. @@ -494,36 +539,48 @@ class Builder(builder.ModuleManager): if 'header-paths' in config: headerPaths = config['header-paths'] cpu = bld.get_env()['RTEMS_ARCH'] - if cpu == "i386": - cpu = 'x86' for headers in headerPaths: - # Get the dest path - ipath = os.path.join(arch_inc_path, headers[2]) - start_dir = bld.path.find_dir(headers[0].replace('@CPU@', cpu)) - if start_dir != None: - bld.install_files("${PREFIX}/" + ipath, - start_dir.ant_glob(headers[1]), - cwd = start_dir, - relative_trick = True) + paths = [headers[0].replace('@CPU@', cpu)] + # Apply the path mappings + for source, targets in config['path-mappings']: + if source in paths: + i = paths.index(source) + paths.remove(source) + paths[i:i] = targets + + for hp in paths: + # Get the dest path + ipath = os.path.join(arch_inc_path, headers[2]) + start_dir = bld.path.find_dir(hp) + if start_dir != None: + bld.install_files("${PREFIX}/" + ipath, + start_dir.ant_glob(headers[1]), + cwd=start_dir, + relative_trick=True) bld.install_files(os.path.join("${PREFIX}", arch_inc_path, module_header_path), modules_h_file_with_path, - cwd = bld.path) + cwd=bld.path) # # Tests # tests = [] if 'tests' in self.data: - tests = self.data['tests'] + tests = self.data['tests']['user'] + enabled_modules = self.getEnabledModules() for testName in sorted(tests): - test = self.data['tests'][testName]['all'] + test = tests[testName]['all'] test_source = [] libs = ['bsd', 'm', 'z', 'rtemstest'] for cfg in test: build_test = True - if cfg != 'default': + for mod in test[cfg]['modules']: + if mod not in enabled_modules: + build_test = False + break + if build_test and cfg != 'default': for c in cfg.split(' '): if not bld.env['HAVE_%s' % (c)]: build_test = False @@ -533,11 +590,11 @@ class Builder(builder.ModuleManager): for f in test[cfg]['files']] libs = test[cfg]['libs'] + libs if build_test: - bld.program(target = '%s.exe' % (testName), - features = 'cprogram', - cflags = cflags, - includes = includes, - source = test_sources, - use = ['bsd'], - lib = libs, - install_path = None) + bld.program(target='%s.exe' % (testName), + features='cprogram', + cflags=cflags, + includes=includes['user'], + source=test_sources, + use=['bsd'], + lib=libs, + install_path=None) @@ -1,28 +1,32 @@ -# -# RTEMS Project (https://www.rtems.org/) -# +# SPDX-License-Identifier: BSD-2-Clause +"""RTEMS LibBSD is a transparent source build of the FreeBSD kernel +source for RTEMS. + +To use see README.waf shipped with this file. +""" + # Copyright (c) 2015-2016 Chris Johns <chrisj@rtems.org>. All rights reserved. # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. # # RTEMS LibBSD is a transparent source build of the FreeBSD kernel source for RTEMS. @@ -41,89 +45,43 @@ except: import sys sys.exit(1) -import libbsd -import waf_libbsd import os.path import runpy import sys -try: - import configparser -except ImportError: - import ConfigParser as configparser + import waflib.Options +import libbsd +import waf_libbsd + builders = {} -BUILDSET_DIR = "buildset" -BUILDSET_DEFAULT = "buildset/default.ini" - -def load_ini(conf, f): - ini = configparser.ConfigParser() - ini.read(f) - if not ini.has_section('general'): - conf.fatal("'{}' is missing a general section.".format(f)) - if not ini.has_option('general', 'name'): - conf.fatal("'{}' is missing a general/name.".format(f)) - if ini.has_option('general', 'extends'): - extends = ini.get('general', 'extends') - extendfile = None - basepath = os.path.dirname(f) - if os.path.isfile(os.path.join(basepath, extends)): - extendfile = os.path.join(basepath, extends) - elif os.path.isfile(os.path.join(BUILDSET_DIR, extends)): - extendfile = os.path.join(BUILDSET_DIR, extends) - else: - conf.fatal("'{}': Invalid file given for general/extends:'{}'" - .format(f, extends)) - base = load_ini(conf, extendfile) - for s in ini.sections(): - if not base.has_section(s): - base.add_section(s) - for o in ini.options(s): - val = ini.get(s, o) - base.set(s, o, val) - ini = base - return ini - -def load_config(conf, f): - ini = load_ini(conf, f) - config = {} - - config['name'] = ini.get('general', 'name') - - config['modules-enabled'] = [] - mods = [] - if ini.has_section('modules'): - mods = ini.options('modules') - for mod in mods: - if ini.getboolean('modules', mod): - config['modules-enabled'].append(mod) - return config def update_builders(ctx, buildset_opt): global builders builders = {} - buildsets = [] if buildset_opt == []: - buildset_opt.append(BUILDSET_DEFAULT) + buildset_opt.append(waf_libbsd.BUILDSET_DEFAULT) for bs in buildset_opt: if os.path.isdir(bs): for f in os.listdir(bs): if f[-4:] == ".ini": - buildsets += [os.path.join(bs,f)] + buildsets += [os.path.join(bs, f)] else: for f in bs.split(','): buildsets += [f] - for bs in buildsets: - builder = waf_libbsd.Builder() - libbsd.load(builder) - bsconfig = load_config(ctx, bs) - bsname = bsconfig['name'] - builder.updateConfiguration(bsconfig) - builder.generate(rtems_version) - builders[bsname]=builder + try: + builder = waf_libbsd.Builder() + libbsd.load(builder) + builder.loadConfig(bs) + builder.generate(rtems_version) + except Exception as exc: + raise + ctx.fatal(str(exc)) + builders[builder.getName()] = builder + def bsp_init(ctx, env, contexts): # This function generates the builders and adds build-xxx, clean-xxx and @@ -143,6 +101,7 @@ def bsp_init(ctx, env, contexts): for y in contexts: newcmd = y.cmd + '-' + builder newvariant = y.variant + '-' + builder + class context(y): cmd = str(newcmd) variant = str(newvariant) @@ -158,49 +117,62 @@ def bsp_init(ctx, env, contexts): commands += [str(cmd)] waflib.Options.commands = commands + def init(ctx): - rtems.init(ctx, version = rtems_version, long_commands = True, - bsp_init = bsp_init) + rtems.init(ctx, + version=rtems_version, + long_commands=True, + bsp_init=bsp_init) + def options(opt): rtems.options(opt) opt.add_option("--enable-auto-regen", - action = "store_true", - default = False, - dest = "auto_regen", - help = "Enable auto-regeneration of LEX, RPC and YACC files.") + action="store_true", + default=False, + dest="auto_regen", + help="Enable auto-regeneration of LEX, RPC and YACC files.") opt.add_option("--enable-warnings", - action = "store_true", - default = False, - dest = "warnings", - help = "Enable all warnings. The default is quiet builds.") + action="store_true", + default=False, + dest="warnings", + help="Enable all warnings. The default is quiet builds.") opt.add_option("--net-test-config", - default = "config.inc", - dest = "net_config", - help = "Network test configuration.") + default="config.inc", + dest="net_config", + help="Network test configuration.") opt.add_option("--freebsd-options", - action = "store", - default = "", - dest = "freebsd_options", - help = "Set FreeBSD options (developer option).") - opt.add_option("--optimization", - action = "store", - default = "2", - dest = "optimization", - help = "Set optimization level to OPTIMIZATION (-On compiler flag). Default is 2 (-O2).") - opt.add_option("--buildset", - action = "append", - default = [], - dest = "buildset", - help = "Select build sets to build. If set to a directory, all .ini file in this directory will be used.") + action="store", + default="", + dest="freebsd_options", + help="Set FreeBSD options (developer option).") + opt.add_option( + "--optimization", + action="store", + default="2", + dest="optimization", + help= + "Set optimization level to OPTIMIZATION (-On compiler flag). Default is 2 (-O2)." + ) + opt.add_option( + "--buildset", + action="append", + default=[], + dest="buildset", + help= + "Select build sets to build. If set to a directory," \ + " all .ini file in this directory will be used." + ) + def bsp_configure(conf, arch_bsp): - conf.check(header_name = "dlfcn.h", features = "c") - conf.check(header_name = "rtems/pci.h", features = "c", mandatory = False) - if not rtems.check_posix(conf): - conf.fatal("RTEMS kernel POSIX support is disabled; configure RTEMS with --enable-posix") + conf.check(header_name="dlfcn.h", features="c") + conf.check(header_name="rtems/pci.h", features="c", mandatory=False) if rtems.check_networking(conf): - conf.fatal("RTEMS kernel contains the old network support; configure RTEMS with --disable-networking") + conf.fatal( + "RTEMS kernel contains the old network support;" \ + " configure RTEMS with --disable-networking" + ) env = conf.env.derive() for builder in builders: ab = conf.env.RTEMS_ARCH_BSP @@ -210,25 +182,28 @@ def bsp_configure(conf, arch_bsp): builders[builder].bsp_configure(conf, arch_bsp) conf.setenv(ab) + def configure(conf): if conf.options.auto_regen: - conf.find_program("lex", mandatory = True) - conf.find_program("rpcgen", mandatory = True) - conf.find_program("yacc", mandatory = True) + conf.find_program("lex", mandatory=True) + conf.find_program("rpcgen", mandatory=True) + conf.find_program("yacc", mandatory=True) conf.env.AUTO_REGEN = conf.options.auto_regen conf.env.WARNINGS = conf.options.warnings conf.env.NET_CONFIG = conf.options.net_config - conf.env.FREEBSD_OPTIONS =conf.options.freebsd_options + conf.env.FREEBSD_OPTIONS = conf.options.freebsd_options conf.env.OPTIMIZATION = conf.options.optimization conf.env.BUILDSET = conf.options.buildset if len(conf.env.BUILDSET) == 0: - conf.env.BUILDSET += [BUILDSET_DEFAULT] + conf.env.BUILDSET += [waf_libbsd.BUILDSET_DEFAULT] update_builders(conf, conf.env.BUILDSET) rtems.configure(conf, bsp_configure) + def test(bld): rtems.test_uninstall(bld) + def build(bld): rtems.build(bld) builders[bld.libbsd_buildset_name].build(bld) |