From 39d0d0173e740a5594bad34b78b6df4799760177 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Sun, 8 Sep 2024 16:37:55 +0300 Subject: [PATCH 001/348] csp_rdp_queue: Fix enqueue of wrong value Resolve a segfault caused by passing the packet pointer by value instead of by reference. --- src/csp_rdp_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp_queue.c b/src/csp_rdp_queue.c index ceb7576c1..70ce3cef1 100644 --- a/src/csp_rdp_queue.c +++ b/src/csp_rdp_queue.c @@ -21,7 +21,7 @@ static int __csp_rdp_queue_flush(csp_queue_handle_t queue, csp_conn_t * conn) { csp_buffer_free(packet); } else { /* put it back */ - ret = csp_queue_enqueue(queue, packet, 0); + ret = csp_queue_enqueue(queue, &packet, 0); if (ret != CSP_QUEUE_OK) { /* something is really broken */ break; From f6467afbbc39a23f9204a94564cfabb2b6dfbb9b Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 24 Sep 2024 18:19:03 +0900 Subject: [PATCH 002/348] github: workflow: build-test-zephyr: Remove Python 3.9 Zephyr 3.7+ requires Python 3.10 or later. Remove Python 3.9 from the CI test. Ref: https://github.com/zephyrproject-rtos/zephyr/commit/9d1b36126535c611617 Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-zephyr.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index b5518dc27..0c8e969e6 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -16,7 +16,6 @@ jobs: - qemu_cortex_m3 - mps2_an385 python-version: - - '3.9' - '3.10' - '3.11' From cac6e700e61664a60e60da0f60906bd1e37525d7 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Wed, 25 Sep 2024 11:51:40 +0900 Subject: [PATCH 003/348] csp_bridge: Remove unnecessary error message csp_qfifo_read() only returns either CSP_ERR_NONE or CSP_ERR_TIMEDOUT, so logging for CSP_ERR_TIMEDOUT isn't necessary. Signed-off-by: Takumi Ando --- src/csp_bridge.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/csp_bridge.c b/src/csp_bridge.c index 061aaefe2..72d0be934 100644 --- a/src/csp_bridge.c +++ b/src/csp_bridge.c @@ -29,10 +29,9 @@ void csp_bridge_work(void) { return; } - /* Get next packet to route */ + /* Get next packet to bridge */ csp_qfifo_t input; if (csp_qfifo_read(&input) != CSP_ERR_NONE) { - csp_print("Failed to receive packet from router input queue\n"); return; } From 8d88b0a79ed42c201536cd545b127b113fb50712 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Wed, 25 Sep 2024 14:58:26 +0900 Subject: [PATCH 004/348] examples: Add CAN to UDP bridging example csp_bridge_can2udp bridges CSP packets from CAN to UDP. Signed-off-by: Takumi Ando --- examples/CMakeLists.txt | 2 + examples/buildall.py | 2 + examples/csp_bridge_can2udp.c | 132 ++++++++++++++++++++++++++++++++++ examples/meson.build | 7 ++ wscript | 5 ++ 5 files changed, 148 insertions(+) create mode 100644 examples/csp_bridge_can2udp.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6fce8b760..3e070795a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,12 +2,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_executable(csp_server_client EXCLUDE_FROM_ALL csp_server_client.c csp_server_client_posix.c) add_executable(csp_server EXCLUDE_FROM_ALL csp_server.c csp_server_posix.c) add_executable(csp_client EXCLUDE_FROM_ALL csp_client.c csp_client_posix.c) + add_executable(csp_bridge_can2udp EXCLUDE_FROM_ALL csp_bridge_can2udp.c) target_include_directories(csp_server_client PRIVATE ${csp_inc}) target_include_directories(csp_server PRIVATE ${csp_inc}) target_include_directories(csp_client PRIVATE ${csp_inc}) target_link_libraries(csp_server_client PRIVATE csp Threads::Threads) target_link_libraries(csp_server PRIVATE csp Threads::Threads) target_link_libraries(csp_client PRIVATE csp Threads::Threads) + target_link_libraries(csp_bridge_can2udp PRIVATE csp) endif() add_executable(csp_arch EXCLUDE_FROM_ALL csp_arch.c) diff --git a/examples/buildall.py b/examples/buildall.py index 17cce4db5..0f9b2b8e4 100755 --- a/examples/buildall.py +++ b/examples/buildall.py @@ -12,6 +12,7 @@ def build_with_meson(): targets = ['examples/csp_server_client', 'examples/csp_server', 'examples/csp_client', + 'examples/csp_bridge_can2udp', 'examples/csp_arch', 'examples/zmqproxy'] builddir = 'build' @@ -26,6 +27,7 @@ def build_with_cmake(): targets = ['examples/csp_server_client', 'examples/csp_server', 'examples/csp_client', + 'examples/csp_bridge_can2udp', 'examples/csp_arch', 'examples/zmqproxy'] builddir = 'build' diff --git a/examples/csp_bridge_can2udp.c b/examples/csp_bridge_can2udp.c new file mode 100644 index 000000000..95fb66525 --- /dev/null +++ b/examples/csp_bridge_can2udp.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEFAULT_CAN_NAME "can0" +#define DEFAULT_UDP_ADDRESS "127.0.0.1" +#define DEFAULT_UDP_REMOTE_PORT (0) +#define DEFAULT_UDP_LOCAL_PORT (0) + +static struct option long_options[] = { + {"can", required_argument, 0, 'c'}, + {"remote-address", required_argument, 0, 'a'}, + {"remote-port", required_argument, 0, 'r'}, + {"local-port", required_argument, 0, 'l'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; + +/* Overwrite input hook to print packet information */ +void csp_input_hook(csp_iface_t * iface, csp_packet_t * packet) { + char dst_name[] = "UDP"; + + if (strncmp(iface->name, "UDP", strlen(iface->name)) == 0) { + strncpy(dst_name, "CAN", sizeof(dst_name)); + } + + csp_print("%s: %u(%u) --> %s: %u(%u), priority: %u, flags: 0x%02X, size: %" PRIu16 "\n", + iface->name, packet->id.src, packet->id.sport, + dst_name, packet->id.dst, packet->id.dport, + packet->id.pri, packet->id.flags, packet->length); +} + +static void print_help(void) { + csp_print("Usage: csp_bridge_can2udp [options]\n"); + csp_print(" --can set CAN interface\n"); + csp_print(" --remote-address
set UDP remote address\n" + " --remote-port set UDP remote port\n" + " --local-port set UDP local port\n" + " -h,--help print help\n"); +} + +static csp_iface_t * add_can_iface(const char * can_name) +{ + csp_iface_t * iface = NULL; + + int error = csp_can_socketcan_open_and_add_interface(can_name, CSP_IF_CAN_DEFAULT_NAME, + 0, 1000000, true, &iface); + if (error != CSP_ERR_NONE) { + csp_print("Failed to add CAN interface [%s], error: %d\n", can_name, error); + exit(1); + } + + return iface; +} + +static csp_iface_t * add_udp_iface(char * address, int lport, int rport) +{ + csp_iface_t * iface = malloc(sizeof(csp_iface_t)); + csp_if_udp_conf_t * conf = malloc(sizeof(csp_if_udp_conf_t)); + + conf->host = address; + conf->lport = lport; + conf->rport = rport; + csp_if_udp_init(iface, conf); + + return iface; +} + +int main(int argc, char * argv[]) { + char default_can_name[] = DEFAULT_CAN_NAME; + char * can_name = default_can_name; + + char default_address[] = DEFAULT_UDP_ADDRESS; + char * address = default_address; + int rport = DEFAULT_UDP_REMOTE_PORT; + int lport = DEFAULT_UDP_LOCAL_PORT; + + csp_iface_t * can_iface; + csp_iface_t * udp_iface; + + int opt; + + while ((opt = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { + switch (opt) { + case 'c': + can_name = optarg; + break; + case 'a': + address = optarg; + break; + case 'r': + rport = atoi(optarg); + break; + case 'l': + lport = atoi(optarg); + break; + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case '?': + /* Invalid option or missing argument */ + print_help(); + exit(EXIT_FAILURE); + } + } + + /* Init CSP */ + csp_init(); + + /* Add interfaces */ + can_iface = add_can_iface(can_name); + udp_iface = add_udp_iface(address, lport, rport); + + /* Set interfaces to bridge */ + csp_bridge_set_interfaces(can_iface, udp_iface); + + /* Print interfaces list */ + csp_iflist_print(); + + /* Start bridge */ + while(1) { + csp_bridge_work(); + } + + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index fda70e25b..0996b0d3d 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -19,6 +19,13 @@ executable('csp_client', dependencies : csp_dep, build_by_default : false) +executable('csp_bridge_can2udp', + ['csp_bridge_can2udp.c'], + include_directories : csp_inc, + c_args : csp_c_args, + dependencies : csp_dep, + build_by_default : false) + executable('csp_arch', 'csp_arch.c', include_directories : csp_inc, diff --git a/wscript b/wscript index 12c9787f0..db0857178 100644 --- a/wscript +++ b/wscript @@ -251,6 +251,11 @@ def build(ctx): lib=ctx.env.LIBS, use='csp') + ctx.program(source=['examples/csp_bridge_can2udp.c'], + target='examples/csp_bridge_can2udp', + lib=ctx.env.LIBS, + use='csp') + ctx.program(source='examples/csp_arch.c', target='examples/csp_arch', lib=ctx.env.LIBS, From 74e2be15678f0ab4d7fcd9d2f0e804311640ea11 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Sun, 29 Sep 2024 08:30:29 +0300 Subject: [PATCH 005/348] csp_rdp: Fix random connection hang due to overflow Fixes an issue where the RDP connection is left in an OPEN state when `conn->rdp.rcv_cur + 1` exceeds the `uint16_t` maximum value (65535). The value was not properly wrapping around to 0, causing the connection to remain open. This issue occurs randomly as the SND ISS is generated using `rand_r`, but always manifests when `rand_r` returns 65535. This fix ensures proper wrapping of `rcv_cur` to prevent the connection from hanging. --- src/csp_rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index a860d86fa..eb0707dd2 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -549,7 +549,7 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { goto discard_close; } - if (rx_header->seq_nr == (conn->rdp.rcv_cur + 1)) { + if (rx_header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { csp_rdp_protocol("RDP %p: Received RST in sequence, no more data incoming, reply with RST\n", (void *)conn); conn->rdp.state = RDP_CLOSE_WAIT; conn->timestamp = csp_get_ms(); From 38d6df7541a1cdb2437d7a205f9d5aacca2c4ee3 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Fri, 27 Sep 2024 11:57:01 +0900 Subject: [PATCH 006/348] examples: csp_bridge_can2udp: Add --protocol-version option We'd like to use both CSP version 1 and 2 in this example. Signed-off-by: Takumi Ando --- examples/csp_bridge_can2udp.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/csp_bridge_can2udp.c b/examples/csp_bridge_can2udp.c index 95fb66525..c5c6ae782 100644 --- a/examples/csp_bridge_can2udp.c +++ b/examples/csp_bridge_can2udp.c @@ -13,11 +13,14 @@ #define DEFAULT_UDP_REMOTE_PORT (0) #define DEFAULT_UDP_LOCAL_PORT (0) +extern csp_conf_t csp_conf; + static struct option long_options[] = { {"can", required_argument, 0, 'c'}, {"remote-address", required_argument, 0, 'a'}, {"remote-port", required_argument, 0, 'r'}, {"local-port", required_argument, 0, 'l'}, + {"protocol-version", required_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; @@ -38,11 +41,12 @@ void csp_input_hook(csp_iface_t * iface, csp_packet_t * packet) { static void print_help(void) { csp_print("Usage: csp_bridge_can2udp [options]\n"); - csp_print(" --can set CAN interface\n"); - csp_print(" --remote-address
set UDP remote address\n" - " --remote-port set UDP remote port\n" - " --local-port set UDP local port\n" - " -h,--help print help\n"); + csp_print(" --can set CAN interface\n"); + csp_print(" --remote-address
set UDP remote address\n" + " --remote-port set UDP remote port\n" + " --local-port set UDP local port\n" + " -v,--protocol-version set protocol version\n" + " -h,--help print help\n"); } static csp_iface_t * add_can_iface(const char * can_name) @@ -86,7 +90,7 @@ int main(int argc, char * argv[]) { int opt; - while ((opt = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "v:h", long_options, NULL)) != -1) { switch (opt) { case 'c': can_name = optarg; @@ -100,6 +104,9 @@ int main(int argc, char * argv[]) { case 'l': lport = atoi(optarg); break; + case 'v': + csp_conf.version = atoi(optarg); + break; case 'h': print_help(); exit(EXIT_SUCCESS); From 96c12d5a8ba6cf246fee2a1f166b19691d5a9d67 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Mon, 3 Jun 2024 16:08:20 +0200 Subject: [PATCH 007/348] Add documentation for CFP in CSP version 2 --- include/csp/interfaces/csp_if_can.h | 45 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/include/csp/interfaces/csp_if_can.h b/include/csp/interfaces/csp_if_can.h index 7cb364529..9df662884 100644 --- a/include/csp/interfaces/csp_if_can.h +++ b/include/csp/interfaces/csp_if_can.h @@ -22,6 +22,39 @@ * field serves the same purpose as in the Internet Protocol, and should be an auto * incrementing integer to uniquely separate sessions. * + * For networks configured as CSP version 2, the CAN identifier is divided into: + * + * - Priority: 2 bits + * - Destination: 14 bits + * - Sender id: 6 bits + * - Packet count: 2 bits + * - Frame count: 3 bits + * - Begin flag: 1 bit + * - End flag: 1 bit + * + * The \b Priority represents the CSP prio field. Placing this as the first bits in + * the transmission ensure that packets with high priority is priotized on the bus + * due to the nature of CAN arbitration. + * The \b Destination field represents the CSP node of the receiving node + * The \b Sender holds the least significant bits of the transmitting interface, + * i.e. the local address when a packet is forwarded by a routing instance. + * The \b Packet \b count is an incrementing value for every CSP packet + * The \b Frame \b count represents the frame fragment index for the particular packet + * The \b Begin \b flag is set for the first CAN frame in a CSP packet + * The \b End \b flag is set for the last CAN frame in a CSP packet + * + * In addition to the 29 bit extended CAN header, CSP utilize four bytes in the + * first CAN fragment in every CSP packet as: + * + * - Source: 14 bits + * - Dest port: 6 bits + * - Source port: 6 bits + * - CSP flags: 6 bits + + * The \b Source holds the CSP node address of the origin of the CSP packet. + * The \b Dest and \Source \b port represents the port numbers for the transmission. + * The \b CSP \b flags holds the CSP_HEADER_FLAGS. + * * Other CAN communication using a standard 11 bit identifier, can co-exist on the wire. ****************************************************************************/ #pragma once @@ -89,18 +122,6 @@ extern "C" { CFP_MAKE_ID((uint32_t)(1 << CFP_ID_SIZE) - 1)) -/** - * CFP 2.0 - * - * PRIO: 2 - * DST: 14 - * Sender id: 6 - * Sender counter: 2 - * Fragment counter: 3 - * Begin: 1 - * End: 1 - */ - #define CFP2_PRIO_MASK 0x3 #define CFP2_PRIO_OFFSET 27 From 5046c89b1008370794252ce5bcded6a1bb43a7eb Mon Sep 17 00:00:00 2001 From: Thomas Lykkeberg <62430938+Lykkeberg@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:53:40 +0200 Subject: [PATCH 008/348] socketcan: Deal with EAGAIN correctly --- src/drivers/can/can_socketcan.c | 49 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index b5c456574..d04982ab2 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -1,3 +1,5 @@ + + #include #include @@ -59,9 +61,14 @@ static void * socketcan_rx_thread(void * arg) { struct can_frame frame; int nbytes = read(ctx->socket, &frame, sizeof(frame)); if (nbytes < 0) { - csp_print("%s[%s]: read() failed, errno %d: %s\n", __func__, ctx->name, errno, strerror(errno)); - sleep(1); - continue; + if (errno == EAGAIN) { + /* This is acceptable, since something interrupted us, try again */ + continue; + } else { + csp_print("%s[%s]: read() failed, errno %d: %s\n", __func__, ctx->name, errno, strerror(errno)); + usleep(1*1E6); + continue; + } } if (nbytes != sizeof(frame)) { @@ -105,15 +112,37 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat .can_dlc = dlc}; memcpy(frame.data, data, dlc); - uint32_t elapsed_ms = 0; + uint32_t waiting_ms = 0; can_context_t * ctx = driver_data; - while (write(ctx->socket, &frame, sizeof(frame)) != sizeof(frame)) { - if ((errno != ENOBUFS) || (elapsed_ms >= 1000)) { - csp_print("%s[%s]: write() failed, errno %d: %s\n", __func__, ctx->name, errno, strerror(errno)); - return CSP_ERR_TX; + uintptr_t pdata = (uintptr_t)&frame; + uintptr_t pend = ((uintptr_t)&frame + sizeof(frame)); + size_t length = sizeof(frame); + + while (pdata < pend) { + int written; + + written = write(ctx->socket, (void *)pdata, length); + if (written < 0) { + if (errno == ENOBUFS) { + /* If no space available, wait for 5 ms and try again */ + usleep(5000); + waiting_ms += 5; + } else if(errno == EAGAIN) { + /* Acceptable, since something interrupted us, try again */ + continue; + } else { + waiting_ms = 0; + } + + if (waiting_ms >= 1000) { + /* We finally got tired of waiting, give up */ + csp_print("%s[%s]: write() failed, we have been waiting for CAN buffers for too long (>1000 ms)\n", __func__, ctx->name); + return CSP_ERR_TX; + } + } else { + pdata += written; + length -= written; } - usleep(5000); - elapsed_ms += 5; } return CSP_ERR_NONE; From 51c81501f505449c38e780837205c4406b4f7154 Mon Sep 17 00:00:00 2001 From: Thomas Lykkeberg <62430938+Lykkeberg@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:35:05 +0200 Subject: [PATCH 009/348] socketcan: Also deal with EINTR --- src/drivers/can/can_socketcan.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index d04982ab2..b90233d92 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -61,7 +61,7 @@ static void * socketcan_rx_thread(void * arg) { struct can_frame frame; int nbytes = read(ctx->socket, &frame, sizeof(frame)); if (nbytes < 0) { - if (errno == EAGAIN) { + if (errno == EAGAIN || errno == EINTR) { /* This is acceptable, since something interrupted us, try again */ continue; } else { @@ -127,11 +127,12 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat /* If no space available, wait for 5 ms and try again */ usleep(5000); waiting_ms += 5; - } else if(errno == EAGAIN) { + } else if(errno == EAGAIN || errno == EINTR) { /* Acceptable, since something interrupted us, try again */ - continue; + waiting_ms += 5; } else { - waiting_ms = 0; + csp_print("%s[%s]: write() failed, encountered an error during write(). %d - '%s'\n", __func__, ctx->name, errno, strerror(errno)); + return CSP_ERR_TX; } if (waiting_ms >= 1000) { @@ -140,6 +141,7 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat return CSP_ERR_TX; } } else { + waiting_ms = 0; pdata += written; length -= written; } From ca547b5de1c15576a75406b87c1c4dd3494c7eb5 Mon Sep 17 00:00:00 2001 From: edvard Date: Wed, 29 Nov 2023 09:38:00 +0100 Subject: [PATCH 010/348] SI-1390 enable tcp keepalive on zmq --- src/interfaces/csp_if_zmqhub.c | 44 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 903a477d3..43a9a1aec 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -252,19 +252,37 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add drv->subscriber = zmq_socket(drv->context, ZMQ_SUB); assert(drv->subscriber != NULL); - /* If shared secret key provided */ - if(sec_key){ - char pub_key[41]; - zmq_curve_public(pub_key, sec_key); - /* Publisher (TX) */ - zmq_setsockopt(drv->publisher, ZMQ_CURVE_SERVERKEY, pub_key, CURVE_KEYLEN); - zmq_setsockopt(drv->publisher, ZMQ_CURVE_PUBLICKEY, pub_key, CURVE_KEYLEN); - zmq_setsockopt(drv->publisher, ZMQ_CURVE_SECRETKEY, sec_key, CURVE_KEYLEN); - /* Subscriber (RX) */ - zmq_setsockopt(drv->subscriber, ZMQ_CURVE_SERVERKEY, pub_key, CURVE_KEYLEN); - zmq_setsockopt(drv->subscriber, ZMQ_CURVE_PUBLICKEY, pub_key, CURVE_KEYLEN); - zmq_setsockopt(drv->subscriber, ZMQ_CURVE_SECRETKEY, sec_key, CURVE_KEYLEN); - } + /* If shared secret key provided */ + if (sec_key) { + char pub_key[41]; + + zmq_curve_public(pub_key, sec_key); + /* Publisher (TX) */ + zmq_setsockopt(drv->publisher, ZMQ_CURVE_SERVERKEY, pub_key, CURVE_KEYLEN); + zmq_setsockopt(drv->publisher, ZMQ_CURVE_PUBLICKEY, pub_key, CURVE_KEYLEN); + zmq_setsockopt(drv->publisher, ZMQ_CURVE_SECRETKEY, sec_key, CURVE_KEYLEN); + /* Subscriber (RX) */ + zmq_setsockopt(drv->subscriber, ZMQ_CURVE_SERVERKEY, pub_key, CURVE_KEYLEN); + zmq_setsockopt(drv->subscriber, ZMQ_CURVE_PUBLICKEY, pub_key, CURVE_KEYLEN); + zmq_setsockopt(drv->subscriber, ZMQ_CURVE_SECRETKEY, sec_key, CURVE_KEYLEN); + } + int keep_alive = 1; + /* Time in seconds a connection must be idle before keep-alive packet send*/ + int idle = 900; + /* Maximum number of keep-alive probes to send without ack before connection closed */ + int cnt = 2; + /* Interval in seconds between each keep-alive probe */ + int intvl = 900; + /* Publisher (TX) */ + zmq_setsockopt(drv->publisher, ZMQ_TCP_KEEPALIVE, &keep_alive, sizeof(keep_alive)); + zmq_setsockopt(drv->publisher, ZMQ_TCP_KEEPALIVE_IDLE, &idle, sizeof(idle)); + zmq_setsockopt(drv->publisher, ZMQ_TCP_KEEPALIVE_CNT, &cnt, sizeof(cnt)); + zmq_setsockopt(drv->publisher, ZMQ_TCP_KEEPALIVE_INTVL, &intvl, sizeof(intvl)); + /* Subscriber (RX) */ + zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE, &keep_alive, sizeof(keep_alive)); + zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_IDLE, &idle, sizeof(idle)); + zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_CNT, &cnt, sizeof(cnt)); + zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_INTVL, &intvl, sizeof(intvl)); /* Generate filters */ uint16_t hostmask = (1 << (csp_id_get_host_bits() - netmask)) - 1; From ee5e737c1187bca938da4dc2d6679d463c77eae2 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Tue, 1 Oct 2024 11:24:00 +0200 Subject: [PATCH 011/348] zmqhub: Silence unused variable --- src/interfaces/csp_if_zmqhub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 43a9a1aec..076d9157e 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -63,6 +63,7 @@ void * csp_zmqhub_task(void * param) { while (1) { int ret; + (void)ret; /* Silence unused variable warning (promoted to an error if -Werr) issued when building with NDEBUG (release with asserts turned off) */ zmq_msg_t msg; ret = zmq_msg_init_size(&msg, sizeof(packet->data) + HEADER_SIZE); From afaa95c22b42a413d582da9284200d7485eab973 Mon Sep 17 00:00:00 2001 From: edvard Date: Tue, 25 Jun 2024 08:23:44 +0200 Subject: [PATCH 012/348] null check pthread queue to safeguard client apps with runtime csp init --- src/arch/posix/pthread_queue.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index 878e21039..ce35cf414 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -170,6 +170,11 @@ int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) struct timespec ts; struct timespec * pts; + if(!queue){ + csp_print("csp not initialized\n"); + return PTHREAD_QUEUE_ERROR; + } + /* Calculate timeout */ if (timeout != CSP_MAX_TIMEOUT) { if (get_deadline(&ts, timeout) != 0) { From 227ea20b591fb69c612f4232b87dd5c036fc5acc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lab Date: Thu, 7 Mar 2024 14:28:54 +0100 Subject: [PATCH 013/348] bugfix: pthread_queue: computation free items --- src/arch/posix/pthread_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index ce35cf414..ff10b9a2f 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -218,7 +218,7 @@ int pthread_queue_items(pthread_queue_t * queue) { int pthread_queue_free(pthread_queue_t * queue) { pthread_mutex_lock(&(queue->mutex)); - int free = queue->size - queue->items; + int free = (queue->size / queue->item_size) - queue->items; pthread_mutex_unlock(&(queue->mutex)); return free; From 04514cd1e79732d03ca89a18d61d7546112cd21f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lab Date: Tue, 18 Jun 2024 15:11:28 +0200 Subject: [PATCH 014/348] eth: Add null pointer check --- src/interfaces/csp_if_eth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index adbaeec00..d2cf0af08 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -175,6 +175,11 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei /* Add packet segment */ csp_packet_t * packet = csp_if_eth_pbuf_get(&pbuf_list, packet_id, task_woken); + if (packet == NULL) { + iface->drop++; + csp_print("eth rx cannot get csp packet\n"); + return CSP_ERR_INVAL; + } if (packet->frame_length == 0) { /* First segment */ packet->frame_length = frame_length; From bc97b0c88d9d008d1f1fec697f8843b5832e3655 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Thu, 6 Jun 2024 20:12:15 +0200 Subject: [PATCH 015/348] counter: Unify usage of rx_error and frame --- src/interfaces/csp_if_can.c | 5 +---- src/interfaces/csp_if_eth.c | 10 +++++++++- src/interfaces/csp_if_kiss.c | 7 ++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index 3619f8580..180a81513 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -85,8 +85,6 @@ int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t break; } - iface->frame++; - csp_id_setup_rx(packet); /* Copy CSP identifier (header) */ @@ -298,7 +296,6 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t return CSP_ERR_INVAL; } - iface->frame++; csp_id_setup_rx(packet); /* Copy first 2 bytes from CFP 2.0 header: @@ -345,7 +342,7 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t /* Check for overflow. The frame input + dlc must not exceed the end of the packet data field */ if (&packet->frame_begin[packet->frame_length] + dlc >= &packet->data[sizeof(packet->data)]) { csp_dbg_can_errno = CSP_DBG_CAN_ERR_RX_OVF; - iface->frame++; + iface->rx_error++; csp_can_pbuf_free(ifdata, packet, 1, task_woken); return CSP_ERR_INVAL; } diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index d2cf0af08..2f67a528d 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -138,10 +138,12 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei /* Filter on CSP protocol id */ if ((be16toh(eth_frame->ether_type) != CSP_ETH_TYPE_CSP)) { + iface->frame++; return CSP_ERR_INVAL; } if (received_len < sizeof(csp_eth_header_t)) { + iface->frame++; return CSP_ERR_INVAL; } @@ -152,22 +154,26 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei csp_if_eth_unpack_header(eth_frame, &packet_id, &seg_size, &frame_length); if (seg_size == 0) { + iface->frame++; csp_print("eth rx seg_size is zero\n"); return CSP_ERR_INVAL; } if (seg_size > frame_length) { + iface->frame++; csp_print("eth rx seg_size(%u) > frame_length(%u)\n", (unsigned)seg_size, (unsigned)frame_length); return CSP_ERR_INVAL; } if (sizeof(csp_eth_header_t) + seg_size > received_len) { + iface->frame++; csp_print("eth rx sizeof(csp_eth_frame_t) + seg_size(%u) > received(%u)\n", (unsigned)seg_size, (unsigned)received_len); return CSP_ERR_INVAL; } if (frame_length == 0) { + iface->frame++; csp_print("eth rx frame_length is zero\n"); return CSP_ERR_INVAL; } @@ -187,11 +193,13 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei } if (frame_length != packet->frame_length) { + iface->frame++; csp_print("eth rx inconsistent frame_length\n"); return CSP_ERR_INVAL; } if (packet->rx_count + seg_size > packet->frame_length) { + iface->frame++; csp_print("eth rx data received exceeds frame_length\n"); return CSP_ERR_INVAL; } @@ -209,7 +217,7 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei if (csp_id_strip(packet) != 0) { csp_print("eth rx packet discarded due to error in ID field\n"); - iface->rx_error++; + iface->frame++; (task_woken) ? csp_buffer_free_isr(packet) : csp_buffer_free(packet); return CSP_ERR_INVAL; } diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index b1b1e35f9..1dcef80c3 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -128,17 +128,14 @@ void csp_kiss_rx(csp_iface_t * iface, const uint8_t * buf, size_t len, void * px ifdata->rx_packet->frame_length = ifdata->rx_length; if (csp_id_strip(ifdata->rx_packet) < 0) { - iface->rx_error++; + iface->frame++; ifdata->rx_mode = KISS_MODE_NOT_STARTED; break; } - /* Count received frame */ - iface->frame++; - /* Validate CRC */ if (csp_crc32_verify(ifdata->rx_packet) != CSP_ERR_NONE) { - iface->rx_error++; + iface->frame++; ifdata->rx_mode = KISS_MODE_NOT_STARTED; break; } From 4507a5efb45f63fe8d29efab3a300b726efc8532 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Fri, 13 Sep 2024 16:46:28 +0200 Subject: [PATCH 016/348] eth: stabilize ethernet interface implementation --- include/csp/interfaces/csp_if_eth_pbuf.h | 28 ++-- src/drivers/eth/eth_linux.c | 6 +- src/interfaces/csp_if_eth.c | 48 ++++--- src/interfaces/csp_if_eth_pbuf.c | 158 +++++++++++------------ 4 files changed, 111 insertions(+), 129 deletions(-) diff --git a/include/csp/interfaces/csp_if_eth_pbuf.h b/include/csp/interfaces/csp_if_eth_pbuf.h index de9408d2d..4290ed314 100644 --- a/include/csp/interfaces/csp_if_eth_pbuf.h +++ b/include/csp/interfaces/csp_if_eth_pbuf.h @@ -35,20 +35,14 @@ #include -#define CSP_IF_ETH_PBUF_TIMEOUT_MS 2000 - -/** Packet list operations */ - -csp_packet_t * csp_if_eth_pbuf_find(csp_packet_t ** plist, uint32_t pbuf_id); - -void csp_if_eth_pbuf_insert(csp_packet_t ** plist, csp_packet_t * packet); - -csp_packet_t * csp_if_eth_pbuf_get(csp_packet_t ** plist, uint32_t pbuf_id, int * task_woken); - -void csp_if_eth_pbuf_remove(csp_packet_t ** plist, csp_packet_t * packet); - -void csp_if_eth_pbuf_list_cleanup(csp_packet_t ** plist); - -void csp_if_eth_pbuf_print(const char * descr, csp_packet_t * packet); - -void csp_if_eth_pbuf_list_print(csp_packet_t ** plist); +typedef struct { + uint16_t rx_count; /* Received bytes */ + uint16_t remain; /* Remaining packets */ + uint32_t cfpid; /* Connection CFP identification number */ + uint32_t last_used; /* Timestamp in ms for last use of buffer */ +} csp_eth_pbuf_element_t; + +void csp_eth_pbuf_free(csp_eth_interface_data_t * ifdata, csp_packet_t * buffer, int buf_free, int * task_woken); +csp_packet_t * csp_eth_pbuf_new(csp_eth_interface_data_t * ifdata, uint32_t id, uint32_t now, int * task_woken); +csp_packet_t * csp_eth_pbuf_find(csp_eth_interface_data_t * ifdata, uint32_t id, int * task_woken); +void csp_eth_pbuf_cleanup(csp_eth_interface_data_t * ifdata, uint32_t now, int * task_woken); diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 590526795..04ba7fe98 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -98,7 +98,11 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int /* Open RAW socket to send on */ if ((ctx->sockfd = socket(AF_PACKET, SOCK_RAW, htobe16(CSP_ETH_TYPE_CSP))) == -1) { perror("socket"); - csp_print("Use command 'setcap cap_net_raw+ep ./csh'\n"); + char exe[1024]; + int count = readlink("/proc/self/exe", exe, sizeof(exe)); + if (count > 0) { + csp_print("Use command 'sudo setcap cap_net_raw+ep %s'\n", exe); + } return CSP_ERR_INVAL; } diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index 2f67a528d..000654292 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -132,7 +132,6 @@ void csp_eth_arp_get_addr(uint8_t * mac_addr, uint16_t csp_addr) int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t received_len, int * task_woken) { csp_eth_interface_data_t * ifdata = iface->interface_data; - csp_packet_t * pbuf_list = ifdata->pbufs; if (eth_debug) csp_hex_dump("rx", (void*)eth_frame, received_len); @@ -153,9 +152,9 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei uint16_t frame_length = 0; csp_if_eth_unpack_header(eth_frame, &packet_id, &seg_size, &frame_length); - if (seg_size == 0) { + if (seg_size == 0 || seg_size > CSP_ETH_FRAME_SIZE_MAX) { iface->frame++; - csp_print("eth rx seg_size is zero\n"); + csp_print("eth rx seg_size of %u bytes is invalid\n"); return CSP_ERR_INVAL; } @@ -165,40 +164,43 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei return CSP_ERR_INVAL; } - if (sizeof(csp_eth_header_t) + seg_size > received_len) { + if ((sizeof(csp_eth_header_t) + seg_size) > received_len) { iface->frame++; csp_print("eth rx sizeof(csp_eth_frame_t) + seg_size(%u) > received(%u)\n", (unsigned)seg_size, (unsigned)received_len); return CSP_ERR_INVAL; } - if (frame_length == 0) { + if (frame_length == 0 || frame_length > CSP_BUFFER_SIZE) { iface->frame++; - csp_print("eth rx frame_length is zero\n"); + csp_print("eth rx frame_length of %u is invalid\n", frame_length); return CSP_ERR_INVAL; } - /* Add packet segment */ - csp_packet_t * packet = csp_if_eth_pbuf_get(&pbuf_list, packet_id, task_woken); + csp_packet_t * packet = csp_eth_pbuf_find(ifdata, packet_id, task_woken); if (packet == NULL) { iface->drop++; csp_print("eth rx cannot get csp packet\n"); return CSP_ERR_INVAL; } + if (packet->frame_length == 0) { /* First segment */ + csp_id_setup_rx(packet); packet->frame_length = frame_length; packet->rx_count = 0; } if (frame_length != packet->frame_length) { + csp_eth_pbuf_free(ifdata, packet, true, task_woken); iface->frame++; csp_print("eth rx inconsistent frame_length\n"); return CSP_ERR_INVAL; } - if (packet->rx_count + seg_size > packet->frame_length) { + if ((packet->rx_count + seg_size) > packet->frame_length) { + csp_eth_pbuf_free(ifdata, packet, true, task_woken); iface->frame++; csp_print("eth rx data received exceeds frame_length\n"); return CSP_ERR_INVAL; @@ -208,12 +210,11 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei packet->rx_count += seg_size; /* Send packet when fully received */ - if (packet->rx_count < packet->frame_length) { return CSP_ERR_NONE; } - csp_if_eth_pbuf_remove(&pbuf_list, packet); + csp_eth_pbuf_free(ifdata, packet, false, task_woken); if (csp_id_strip(packet) != 0) { csp_print("eth rx packet discarded due to error in ID field\n"); @@ -226,17 +227,13 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei csp_eth_arp_set_addr(eth_frame->ether_shost, packet->id.src); if (packet->id.dst != iface->addr && !ifdata->promisc) { + csp_eth_pbuf_free(ifdata, packet, true, task_woken); (task_woken) ? csp_buffer_free_isr(packet) : csp_buffer_free(packet); return CSP_ERR_NONE; } csp_qfifo_write(packet, iface, task_woken); - if (eth_debug) csp_if_eth_pbuf_list_print(&pbuf_list); - - /* Remove potentially stalled partial packets */ - csp_if_eth_pbuf_list_cleanup(&pbuf_list); - return CSP_ERR_NONE; } @@ -250,27 +247,26 @@ int csp_eth_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int fro return CSP_ERR_NONE; } - static uint16_t packet_id = 0; - csp_eth_header_t *eth_frame = ifdata->tx_buf; - - csp_eth_arp_get_addr(eth_frame->ether_dhost, packet->id.dst); - - eth_frame->ether_type = htobe16(CSP_ETH_TYPE_CSP); - memcpy(eth_frame->ether_shost, ifdata->if_mac, CSP_ETH_ALEN); - csp_id_prepend(packet); + static uint16_t packet_id = 0; packet_id++; - uint16_t offset = 0; - const uint16_t seg_size_max = ifdata->tx_mtu - sizeof(csp_eth_header_t); while (offset < packet->frame_length) { + + csp_eth_header_t *eth_frame = ifdata->tx_buf; + + const uint16_t seg_size_max = ifdata->tx_mtu - sizeof(csp_eth_header_t); uint16_t seg_size = packet->frame_length - offset; if (seg_size > seg_size_max) { seg_size = seg_size_max; } + eth_frame->ether_type = htobe16(CSP_ETH_TYPE_CSP); + csp_eth_arp_get_addr(eth_frame->ether_dhost, packet->id.dst); + memcpy(eth_frame->ether_shost, ifdata->if_mac, CSP_ETH_ALEN); + csp_eth_pack_header(eth_frame, packet_id, packet->id.src, seg_size, packet->frame_length); memcpy(eth_frame->frame_begin, packet->frame_begin + offset, seg_size); diff --git a/src/interfaces/csp_if_eth_pbuf.c b/src/interfaces/csp_if_eth_pbuf.c index cdf54ca5c..4097247af 100644 --- a/src/interfaces/csp_if_eth_pbuf.c +++ b/src/interfaces/csp_if_eth_pbuf.c @@ -6,119 +6,107 @@ #include #include -/** Packet buffer list operations */ +/* Buffer element timeout in ms */ +#define PBUF_TIMEOUT_MS 1000 -csp_packet_t * csp_if_eth_pbuf_find(csp_packet_t ** plist, uint32_t pbuf_id) { +void csp_eth_pbuf_free(csp_eth_interface_data_t * ifdata, csp_packet_t * buffer, int buf_free, int * task_woken) { - csp_packet_t * packet = *plist; - while(packet) { - if (packet->cfpid == pbuf_id) { - return packet; - } - packet = packet->next; - } - return packet; + csp_packet_t * packet = ifdata->pbufs; + csp_packet_t * prev = NULL; -} + while (packet) { -void csp_if_eth_pbuf_insert(csp_packet_t ** plist, csp_packet_t * packet) { + /* Perform cleanup in used pbufs */ + if (packet == buffer) { - if (*plist) { - packet->next = *plist; - } - *plist = packet; + /* Erase from list prev->next = next */ + if (prev) { + prev->next = packet->next; + } else { + ifdata->pbufs = packet->next; + } -} + if (buf_free) { + if (task_woken == NULL) { + csp_buffer_free(packet); + } else { + csp_buffer_free_isr(packet); + } + } + } -csp_packet_t * csp_if_eth_pbuf_get(csp_packet_t ** plist, uint32_t pbuf_id, int * task_woken) { + prev = packet; + packet = packet->next; + } - csp_packet_t * packet = csp_if_eth_pbuf_find(plist, pbuf_id); +} - if (packet) { - packet->last_used = csp_get_ms(); - return packet; - } +csp_packet_t * csp_eth_pbuf_new(csp_eth_interface_data_t * ifdata, uint32_t id, uint32_t now, int * task_woken) { - if (!packet) { - packet = (task_woken) ? csp_buffer_get_isr(0) : csp_buffer_get(0); - } + csp_eth_pbuf_cleanup(ifdata, now, task_woken); - if (!packet) { - /* No free packet */ - return NULL; - } + csp_packet_t * packet = (task_woken) ? csp_buffer_get_isr(0) : csp_buffer_get(0); + if (packet == NULL) { + return NULL; + } - csp_id_setup_rx(packet); + packet->last_used = now; + packet->cfpid = id; + packet->frame_length = 0; - /* Existing cfpid and rx_count fields are used */ - packet->cfpid = pbuf_id; - packet->rx_count = 0; - packet->last_used = (task_woken) ? csp_get_ms_isr() : csp_get_ms(); + /* Insert at beginning, because easy */ + packet->next = ifdata->pbufs; + ifdata->pbufs = packet; - packet->next = 0; - csp_if_eth_pbuf_insert(plist, packet); + return packet; +} - return packet; +void csp_eth_pbuf_cleanup(csp_eth_interface_data_t * ifdata, uint32_t now, int * task_woken) { -} + csp_packet_t * packet = ifdata->pbufs; + csp_packet_t * prev = NULL; -void csp_if_eth_pbuf_remove(csp_packet_t ** plist, csp_packet_t * packet) { + while (packet) { - csp_packet_t * prev = 0; - csp_packet_t * p = *plist; - while(p && (p != packet)) { - prev = p; - p = p->next; - } + /* Perform cleanup in used pbufs */ + if ((now - packet->last_used) > PBUF_TIMEOUT_MS) { - if (p) { - if (prev) { - prev->next = p->next; - } else { - *plist = p->next; - } - } + /* Erase from list prev->next = next */ + if (prev) { + prev->next = packet->next; + } else { + ifdata->pbufs = packet->next; + } -} + if (task_woken == NULL) { + csp_buffer_free(packet); + } else { + csp_buffer_free_isr(packet); + } -void csp_if_eth_pbuf_list_cleanup(csp_packet_t ** plist) { + } - /* Free stalled packets, like for which a segment has been lost */ - uint32_t now = csp_get_ms(); - csp_packet_t * packet = *plist; - while(packet) { - if (now > packet->last_used + CSP_IF_ETH_PBUF_TIMEOUT_MS) { - csp_if_eth_pbuf_print("timeout ", packet); - csp_if_eth_pbuf_remove(plist, packet); - csp_buffer_free(packet); - } - packet = packet->next; - } + prev = packet; + packet = packet->next; + } } -void csp_if_eth_pbuf_print(const char * descr, csp_packet_t * packet) { +csp_packet_t * csp_eth_pbuf_find(csp_eth_interface_data_t * ifdata, uint32_t id, int * task_woken) { - if (packet) { - csp_print("%s %p id:%u Age:%lu,%lu,%lu flen:%u\n", - descr, (void *)packet, - (unsigned)packet->cfpid, - (unsigned long)csp_get_ms(), - (unsigned long)packet->last_used, - (unsigned long)(csp_get_ms() - packet->last_used), - (unsigned)packet->frame_length); - } else { - csp_print("Packet is null\n"); - } + uint32_t now = (task_woken) ? csp_get_ms_isr() : csp_get_ms(); -} + csp_packet_t * packet = ifdata->pbufs; + while (packet) { -void csp_if_eth_pbuf_list_print(csp_packet_t ** plist) { + if (packet->cfpid == id) { + packet->last_used = now; + return packet; + } + packet = packet->next; + } - csp_packet_t * packet = *plist; - while(packet) { - csp_if_eth_pbuf_print("list ", packet); - packet = packet->next; - } + return csp_eth_pbuf_new(ifdata, id, now, task_woken); } + From 8ae4ece914a4fd73c18ee67d738d46d9599d581c Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Mon, 12 Feb 2024 23:12:45 -0500 Subject: [PATCH 017/348] crc32: Accept a void pointer as input --- include/csp/csp_crc32.h | 4 ++-- src/csp_crc32.c | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/csp/csp_crc32.h b/include/csp/csp_crc32.h index 83f30aed2..78fe02fb3 100644 --- a/include/csp/csp_crc32.h +++ b/include/csp/csp_crc32.h @@ -42,7 +42,7 @@ int csp_crc32_verify(csp_packet_t * packet); * @param[in] length length of memory to do checksum on * @return checksum */ -uint32_t csp_crc32_memory(const uint8_t * addr, uint32_t length); +uint32_t csp_crc32_memory(const void * addr, uint32_t length); /** Initialize the CRC32 object prior to calculating checksum. @@ -56,7 +56,7 @@ void csp_crc32_init(csp_crc32_t *crc); @param[in] data pointer to data for which to update checksum on @param[in] length number of bytes in the array pointed to by data */ -void csp_crc32_update(csp_crc32_t * crc, const uint8_t * data, uint32_t length); +void csp_crc32_update(csp_crc32_t * crc, const void * data, uint32_t length); /** Finalize the CRC32 checksum calculation. diff --git a/src/csp_crc32.c b/src/csp_crc32.c index 2cf416afd..480f7ca64 100644 --- a/src/csp_crc32.c +++ b/src/csp_crc32.c @@ -51,14 +51,16 @@ void csp_crc32_init(csp_crc32_t * crc) { } } -void csp_crc32_update(csp_crc32_t * crc, const uint8_t * data, uint32_t length) { +void csp_crc32_update(csp_crc32_t * crc, const void * data, uint32_t length) { + + const uint8_t * data8 = (uint8_t*)data; if (crc) { while (length--) { #ifdef __AVR__ - crc = pgm_read_dword(&crc_tab[(crc ^ *data++) & 0xFFL]) ^ (crc >> 8); + crc = pgm_read_dword(&crc_tab[(crc ^ *data8++) & 0xFFL]) ^ (crc >> 8); #else - (*crc) = crc_tab[((*crc) ^ *data++) & 0xFFL] ^ ((*crc) >> 8); + (*crc) = crc_tab[((*crc) ^ *data8++) & 0xFFL] ^ ((*crc) >> 8); #endif } } @@ -73,7 +75,7 @@ uint32_t csp_crc32_final(csp_crc32_t *crc) { return 0; } -uint32_t csp_crc32_memory(const uint8_t * data, uint32_t length) { +uint32_t csp_crc32_memory(const void * data, uint32_t length) { csp_crc32_t crc; From 551931fad7fc68bd124d68ce22747ddf45cad3ae Mon Sep 17 00:00:00 2001 From: edvard Date: Wed, 13 Dec 2023 12:22:13 +0100 Subject: [PATCH 018/348] bugfix: csp_send_direct missing dupl. on L3 rewrt. Copy idout before iflist_get_by_subnet This fixes L3 broadcasts not going out on all ifaces in same subnet Before when dst got set to max nodeid it would break next iteration. --- src/csp_io.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index dec585881..6d8f008be 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -85,6 +85,9 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route csp_packet_t * copy = NULL; int local_found = 0; + /* Make copy as broadcast modifies destination making iflist_get_by_subnet the skip next redundant ifaces */ + csp_id_t _idout = *idout; + while ((iface = csp_iflist_get_by_subnet(idout->dst, iface)) != NULL) { local_found = 1; @@ -102,12 +105,12 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route /* Apply outgoing interface address to packet */ if ((from_me) && (idout->src == 0)) { - idout->src = iface->addr; + _idout.src = iface->addr; } /* Rewrite routed brodcast (L3) to local (L2) when arriving at the interface */ if (csp_id_is_broadcast(idout->dst, iface)) { - idout->dst = csp_id_get_max_nodeid(); + _idout.dst = csp_id_get_max_nodeid(); } /* Todo: Find an elegant way to avoid making a copy when only a single destination interface @@ -115,7 +118,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route * Is this even possible? */ copy = csp_buffer_clone(packet); if (copy != NULL) { - csp_send_direct_iface(idout, copy, iface, via, from_me); + csp_send_direct_iface(&_idout, copy, iface, via, from_me); } } From a326adb9756e46d427c0ce4b1a48c8f01abfc665 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Sun, 23 Jun 2024 15:34:14 +0200 Subject: [PATCH 019/348] Report buffer error as tx_error instead of drop --- src/csp_qfifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_qfifo.c b/src/csp_qfifo.c index 476d51315..ffb30d291 100644 --- a/src/csp_qfifo.c +++ b/src/csp_qfifo.c @@ -51,7 +51,7 @@ void csp_qfifo_write(csp_packet_t * packet, csp_iface_t * iface, void * pxTaskWo if (result != CSP_QUEUE_OK) { csp_dbg_conn_ovf++; - iface->drop++; + iface->tx_error++; if (pxTaskWoken == NULL) csp_buffer_free(packet); else From afbe183433c3bd1c7ea951d0702211b016086a19 Mon Sep 17 00:00:00 2001 From: kivkiv12345 Date: Thu, 22 Aug 2024 14:55:39 +0200 Subject: [PATCH 020/348] csp_promisc_enable() returns CSP_ERR_USED when already enabled --- src/csp_promisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_promisc.c b/src/csp_promisc.c index 223bdc0c6..60e313139 100644 --- a/src/csp_promisc.c +++ b/src/csp_promisc.c @@ -15,7 +15,7 @@ int csp_promisc_enable(unsigned int queue_size) { /* If queue already initialised */ if (csp_promisc_queue != NULL) { csp_promisc_enabled = 1; - return CSP_ERR_NONE; + return CSP_ERR_USED; } /* Create packet queue */ From d04d0af6e8da572941ad6a6ee769cf98ad65b78b Mon Sep 17 00:00:00 2001 From: edvard Date: Wed, 11 Sep 2024 13:40:13 +0200 Subject: [PATCH 021/348] csp_io: Faster loopback Avoid scanning the list for the loopback interface and duplicating packets for a faster (zero copy loopback interface) --- src/csp_io.c | 6 ++++++ src/interfaces/csp_if_lo.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/csp_io.c b/src/csp_io.c index 6d8f008be..4c0393a64 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -85,6 +85,12 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route csp_packet_t * copy = NULL; int local_found = 0; + /* Quickly send on loopback */ + if(idout->dst == csp_if_lo.addr){ + csp_send_direct_iface(idout, packet, &csp_if_lo, via, from_me); + return; + } + /* Make copy as broadcast modifies destination making iflist_get_by_subnet the skip next redundant ifaces */ csp_id_t _idout = *idout; diff --git a/src/interfaces/csp_if_lo.c b/src/interfaces/csp_if_lo.c index 7db823ce7..dba171479 100644 --- a/src/interfaces/csp_if_lo.c +++ b/src/interfaces/csp_if_lo.c @@ -20,4 +20,6 @@ static int csp_lo_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, i csp_iface_t csp_if_lo = { .name = CSP_IF_LOOPBACK_NAME, .nexthop = csp_lo_tx, + .addr = 0, + .is_default = 0 }; From eab4ddcb5c9e81af5235557cd25bb1dcc97a4c2d Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Fri, 13 Sep 2024 16:45:19 +0200 Subject: [PATCH 022/348] meson: Define PACKET_PADDING_BYTES correctly --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 70d83a33a..055a87d38 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ conf.set('CSP_CONN_RXQUEUE_LEN', get_option('conn_rxqueue_len')) conf.set('CSP_CONN_MAX', get_option('conn_max')) conf.set('CSP_BUFFER_SIZE', get_option('buffer_size')) conf.set('CSP_BUFFER_COUNT', get_option('buffer_count')) +conf.set('CSP_PACKET_PADDING_BYTES', get_option('packet_padding_bytes')) conf.set('CSP_RDP_MAX_WINDOW', get_option('rdp_max_window')) conf.set('CSP_RTABLE_SIZE', get_option('rtable_size')) From 9a17e5b1d983168364c23983e1ef0e3e32956b81 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Fri, 6 Sep 2024 17:36:48 +0200 Subject: [PATCH 023/348] promisc: Send outgoing packets after adding CRC This unifies the interface compared to incoming packets --- src/csp_io.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index 4c0393a64..659c8a884 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -223,13 +223,6 @@ void csp_send_direct_iface(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * } -#if (CSP_USE_PROMISC) - /* Loopback traffic is added to promisc queue by the router */ - if (from_me && (iface != &csp_if_lo)) { - csp_promisc_add(packet); - } -#endif - /* Only encrypt packets from the current node */ if (from_me) { /* Append HMAC */ @@ -255,6 +248,12 @@ void csp_send_direct_iface(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * } } +#if (CSP_USE_PROMISC) + /* Loopback traffic is added to promisc queue by the router */ + if (iface != &csp_if_lo) { + csp_promisc_add(packet); + } +#endif } /* Store length before passing to interface */ From f748243918f90dd6e97308b1359cbee18e5c6115 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Tue, 5 Dec 2023 09:26:58 +0100 Subject: [PATCH 024/348] route: Accept packets to all the nodes addresses This allows pinging a node on all its assigned addresses regardless of which interface it arrived on. Previously CSP would only accept traffic to the address associated with the interface of the incoming packet. sendto_reply: If the packet is received on the L2 broadcast address, reply with the physical interface of the response. --- include/csp/csp.h | 5 +++++ src/csp_buffer.c | 3 +++ src/csp_io.c | 18 ++++++++++++++++-- src/csp_route.c | 4 ++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/csp/csp.h b/include/csp/csp.h index 103f41824..583bc2d29 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -73,6 +73,11 @@ const csp_conf_t * csp_get_conf(void); */ void csp_id_copy(csp_id_t * target, csp_id_t * source); +/** + * Clear csp id fields after creating new buffer + */ +void csp_id_clear(csp_id_t * target); + /** * Wait/accept a new connection. * diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 52499e278..d6fcca181 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -1,3 +1,4 @@ +#include #include #include @@ -51,6 +52,7 @@ csp_packet_t * csp_buffer_get_isr(size_t unused) { } buffer->refcount = 1; + csp_id_clear(&buffer->skbf_data.id); return &buffer->skbf_data; } @@ -69,6 +71,7 @@ csp_packet_t * csp_buffer_get(size_t unused) { } buffer->refcount = 1; + csp_id_clear(&buffer->skbf_data.id); return &buffer->skbf_data; } diff --git a/src/csp_io.c b/src/csp_io.c index 659c8a884..cafd82881 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -75,6 +75,15 @@ void csp_id_copy(csp_id_t * target, csp_id_t * source) { target->flags = source->flags; } +void csp_id_clear(csp_id_t * target) { + target->pri = 0; + target->dst = 0; + target->src = 0; + target->dport = 0; + target->sport = 0; + target->flags = 0; +} + void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * routed_from) { int from_me = (routed_from == NULL ? 1 : 0); @@ -390,7 +399,6 @@ void csp_sendto(uint8_t prio, uint16_t dest, uint8_t dport, uint8_t src_port, ui packet->id.dst = dest; packet->id.dport = dport; - packet->id.src = 0; // The source address will be filled by csp_send_direct packet->id.sport = src_port; packet->id.pri = prio; @@ -406,5 +414,11 @@ void csp_sendto_reply(const csp_packet_t * request_packet, csp_packet_t * reply_ if (opts & CSP_O_SAME) { reply_packet->id.flags = request_packet->id.flags; } - csp_sendto(request_packet->id.pri, request_packet->id.src, request_packet->id.sport, request_packet->id.dport, opts, reply_packet); + uint16_t dst = request_packet->id.src; + if (request_packet->id.dst != csp_id_get_max_nodeid()) { + reply_packet->id.src = request_packet->id.dst; + } else { + reply_packet->id.src = 0; + } + csp_sendto(request_packet->id.pri, dst, request_packet->id.sport, request_packet->id.dport, opts, reply_packet); } diff --git a/src/csp_route.c b/src/csp_route.c index 2e4b76f3b..18c5f7cd2 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -134,9 +134,9 @@ int csp_route_work(void) { input.iface->rx++; input.iface->rxbytes += packet->length; - /* The packet is to me, if the address matches that of the incoming interface, + /* The packet is to me, if the address matches that of any interface, * or the address matches the broadcast address of the incoming interface */ - int is_to_me = ((input.iface->addr == packet->id.dst) || (csp_id_is_broadcast(packet->id.dst, input.iface))); + int is_to_me = (csp_iflist_get_by_addr(packet->id.dst) != NULL || (csp_id_is_broadcast(packet->id.dst, input.iface))); /* Deduplication */ if ((csp_conf.dedup == CSP_DEDUP_ALL) || From 1353ba52f5cf8d86090cdf09527909ceeb818e2c Mon Sep 17 00:00:00 2001 From: Thomas Lykkeberg Date: Fri, 17 Nov 2023 09:55:31 +0100 Subject: [PATCH 025/348] rdp: Added csp_conn_is_active() Allow the sending task to monitor the connection status without calling csp_read on the socket for a timeout. This relates to closing of connections in RDP and keeping a connection alive. --- include/csp/csp.h | 14 ++++++++++++++ src/csp_conn.c | 12 ++++++++++++ src/csp_rdp.c | 25 +++++++++++++++++++++++++ src/csp_rdp.h | 1 + src/csp_sfp.c | 2 +- 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/csp/csp.h b/include/csp/csp.h index 583bc2d29..398654897 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -272,6 +272,20 @@ int csp_conn_src(csp_conn_t *conn); */ int csp_conn_flags(csp_conn_t *conn); +/** + * Return if the CSP connection is active + * + * Active in this context means if the protocol layers is connected and no time outs has happened. + * Especially if a connection is marked as a RDP connection, the active state means that the + * RDP layers are connected and no time outs have happened. If the RDP layer has a connection timeout + * or of the connection is closing, the connection is inactive, and ready to be closed. + * + * @param conn connection + * @return true if the connection is active + * @return false if the connection is in-active + */ +bool csp_conn_is_active(csp_conn_t *conn); + /** * Set socket to listen for incoming connections. * diff --git a/src/csp_conn.c b/src/csp_conn.c index 9b013ef8d..6cc497613 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -406,3 +406,15 @@ const csp_conn_t * csp_conn_get_array(size_t * size) { *size = CSP_CONN_MAX; return arr_conn; } + +bool csp_conn_is_active(csp_conn_t *conn) { +#if (CSP_USE_RDP) + if ((conn->idin.flags & CSP_FRDP) || (conn->idout.flags & CSP_FRDP)) { + /* This is for sure an RDP connection */ + return csp_rdp_conn_is_active(conn); + } +#endif + + /* Non RDP connections are always "active" */ + return true; +} diff --git a/src/csp_rdp.c b/src/csp_rdp.c index a860d86fa..00ee15c4b 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -32,6 +32,7 @@ #endif + static uint32_t csp_rdp_window_size = 4; static uint32_t csp_rdp_conn_timeout = 10000; static uint32_t csp_rdp_packet_timeout = 1000; @@ -726,6 +727,11 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { } } + if (conn->dest_socket == NULL) { + /* User space has taken control of the connection, so RDP needs to "monitor" + * the connection by timestamping each time a packet is received in OPEN state */ + conn->timestamp = csp_get_ms(); + } /* Store current ack'ed sequence number */ conn->rdp.snd_una = rx_header->ack_nr + 1; @@ -1024,3 +1030,22 @@ void csp_rdp_get_opt(unsigned int * window_size, unsigned int * conn_timeout_ms, if (ack_delay_count) *ack_delay_count = csp_rdp_ack_delay_count; } + +bool csp_rdp_conn_is_active(csp_conn_t *conn) { + + uint32_t time_now = csp_get_ms(); + bool active = true; + + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + /* The RDP connection has timed out */ + active = false; + } + + if (conn->rdp.state == RDP_CLOSE_WAIT || conn->rdp.state == RDP_CLOSED) { + active = false; + } + + return active; + +} + diff --git a/src/csp_rdp.h b/src/csp_rdp.h index 87802ce82..931030e27 100644 --- a/src/csp_rdp.h +++ b/src/csp_rdp.h @@ -10,3 +10,4 @@ int csp_rdp_connect(csp_conn_t * conn); int csp_rdp_close(csp_conn_t * conn, uint8_t closed_by); int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet); int csp_rdp_check_ack(csp_conn_t * conn); +bool csp_rdp_conn_is_active(csp_conn_t *conn); diff --git a/src/csp_sfp.c b/src/csp_sfp.c index 1e845b658..b78ce4651 100644 --- a/src/csp_sfp.c +++ b/src/csp_sfp.c @@ -54,7 +54,7 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t } unsigned int count = 0; - while (count < totalsize) { + while ((count < totalsize) && csp_conn_is_active(conn)) { sfp_header_t * sfp_header; From 08ba42a997ad63b87be23577ef51efaee0ff37a8 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Wed, 18 Sep 2024 17:08:57 +0200 Subject: [PATCH 026/348] rdp: mend race condition that caused dual acks --- src/csp_rdp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 00ee15c4b..34f0f6196 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -122,6 +122,13 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, packet->length = 0; } + /* Update last ACK time stamp + * We do this early to minimize race condition between read() call and router task csp_rdp_new_packet() */ + if (flags & RDP_ACK) { + conn->rdp.rcv_lsa = ack_nr; + conn->rdp.ack_timestamp = csp_get_ms(); + } + /* Add RDP header */ rdp_header_t * header = csp_rdp_header_add(packet); if (header == NULL) { @@ -159,12 +166,6 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, /* Send packet to IF */ csp_send_direct(&idout, packet, NULL); - /* Update last ACK time stamp */ - if (flags & RDP_ACK) { - conn->rdp.rcv_lsa = ack_nr; - conn->rdp.ack_timestamp = csp_get_ms(); - } - return CSP_ERR_NONE; } From ded5c5a5eda44ff87bc5a2a75e027f76b901f307 Mon Sep 17 00:00:00 2001 From: edvard Date: Thu, 19 Sep 2024 13:19:07 +0200 Subject: [PATCH 027/348] rdp: timeouts allow retransmit during close-wait Allow the timeout handling to execute during close-wait This includes retransmission of segments that are still left in the outgoing queue. --- src/csp_rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 34f0f6196..a4b61a63c 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -442,8 +442,8 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { if (conn->rdp.state == RDP_CLOSE_WAIT) { if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { csp_conn_close(conn, CSP_RDP_CLOSED_BY_PROTOCOL | CSP_RDP_CLOSED_BY_TIMEOUT); + return; } - return; } /** From d67ec133cc6a7633863bdf8b46b2c8b40880cda3 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Tue, 24 Sep 2024 09:46:36 +0200 Subject: [PATCH 028/348] rdp: treat all outgoing messages as ACK packets This will reduce the ammount of chatter by not triggering ACK timeouts in the same direction that is transferring data. --- src/csp_rdp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index a4b61a63c..b1f466902 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -121,14 +121,15 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, return CSP_ERR_NOMEM; packet->length = 0; } - - /* Update last ACK time stamp - * We do this early to minimize race condition between read() call and router task csp_rdp_new_packet() */ + if (flags & RDP_ACK) { conn->rdp.rcv_lsa = ack_nr; - conn->rdp.ack_timestamp = csp_get_ms(); } + /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp + * We do this early to minimize race condition between read() call and router task csp_rdp_new_packet() */ + conn->rdp.ack_timestamp = csp_get_ms(); + /* Add RDP header */ rdp_header_t * header = csp_rdp_header_add(packet); if (header == NULL) { @@ -478,6 +479,9 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { /* Update to latest outgoing ACK */ header->ack_nr = htobe16(conn->rdp.rcv_cur); + /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp */ + conn->rdp.ack_timestamp = csp_get_ms(); + /* Send copy to tx_queue */ packet->timestamp_tx = csp_get_ms(); csp_packet_t * new_packet = csp_buffer_clone(packet); @@ -939,6 +943,7 @@ int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet) { packet->length, (unsigned int)(packet->length - sizeof(rdp_header_t))); conn->rdp.snd_nxt++; + conn->rdp.ack_timestamp = csp_get_ms(); return CSP_ERR_NONE; } From 82648177d52caaeffa723c645fe14e798325095e Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Thu, 26 Sep 2024 16:05:57 +0200 Subject: [PATCH 029/348] rdp: sending task will retry if connection is open The sending task will now wait for the connection to be closed internally instead of a single semaphore wait. --- src/csp_rdp.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index b1f466902..3cbc4edb3 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -271,11 +271,10 @@ static inline void csp_rdp_rx_queue_flush(csp_conn_t * conn) { } rdp_header_t * header = csp_rdp_header_ref(packet); - csp_rdp_protocol("RDP %p: RX Queue deliver matching Element, seq %u\n", (void *)conn, header->seq_nr); /* If the matching packet was found: */ if (header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { - csp_rdp_protocol("RDP %p: Deliver seq %u", (void *)conn, header->seq_nr); + csp_rdp_protocol("RDP %p: Deliver seq %u\n", (void *)conn, header->seq_nr); if (csp_rdp_receive_data(conn, packet) != CSP_ERR_NONE) { csp_rdp_error("RDP lost packet internally, stream corrupted!\n"); csp_buffer_free(packet); @@ -308,11 +307,7 @@ static inline bool csp_rdp_seq_in_rx_queue(csp_conn_t * conn, uint16_t seq_nr) { csp_rdp_queue_rx_add(conn, packet); rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *)packet); - csp_rdp_protocol("RDP %p: RX Queue exists matching Element, seq %u\n", (void *)conn, header->seq_nr); - - /* If the matching packet was found, deliver */ if (header->seq_nr == seq_nr) { - csp_rdp_protocol("RDP %p: We have a match\n", (void *)conn); return true; } } @@ -322,8 +317,11 @@ static inline bool csp_rdp_seq_in_rx_queue(csp_conn_t * conn, uint16_t seq_nr) { static inline int csp_rdp_rx_queue_add(csp_conn_t * conn, csp_packet_t * packet, uint16_t seq_nr) { - if (csp_rdp_seq_in_rx_queue(conn, seq_nr)) + if (csp_rdp_seq_in_rx_queue(conn, seq_nr)) { + csp_rdp_protocol("RDP %p: Already exists in RX queue %u\n", (void *)conn, seq_nr); return -1; + } + csp_rdp_protocol("RDP %p: Add to RX queue %u\n", (void *) conn, seq_nr); csp_rdp_queue_rx_add(conn, packet); return 0; } @@ -370,7 +368,6 @@ static void csp_rdp_flush_eack(csp_conn_t * conn, csp_packet_t * eack_packet) { } } } - static inline bool csp_rdp_should_ack(csp_conn_t * conn) { /* If delayed ACKs are not used, always ACK */ @@ -695,7 +692,6 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { /* If duplicate data packet received, send EACK back */ if (conn->rdp.state == RDP_OPEN) csp_rdp_send_eack(conn); - goto discard_open; } @@ -737,6 +733,7 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { * the connection by timestamping each time a packet is received in OPEN state */ conn->timestamp = csp_get_ms(); } + /* Store current ack'ed sequence number */ conn->rdp.snd_una = rx_header->ack_nr + 1; @@ -754,7 +751,6 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { /* If message is not in sequence, send EACK and store packet */ if (rx_header->seq_nr != (uint16_t)(conn->rdp.rcv_cur + 1)) { if (csp_rdp_rx_queue_add(conn, packet, rx_header->seq_nr) != 0) { - csp_rdp_protocol("RDP %p: Duplicate sequence number\n", (void *)conn); csp_rdp_check_ack(conn); goto discard_open; } @@ -899,17 +895,16 @@ int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet) { return CSP_ERR_RESET; } - while ((conn->rdp.state == RDP_OPEN) && (csp_rdp_is_conn_ready_for_tx(conn) == false)) { - csp_rdp_protocol("RDP %p: Waiting for window update before sending seq %u\n", (void *)conn, conn->rdp.snd_nxt); - if ((csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout)) != CSP_SEMAPHORE_OK) { - csp_rdp_error("RDP %p: Timeout during send", (void *)conn); - return CSP_ERR_TIMEDOUT; + /* Wait here for RDP to become ready or the connection to be closed */ + while (1) { + if (conn->rdp.state == RDP_CLOSE_WAIT || conn->rdp.state == RDP_CLOSED) { + csp_rdp_error("RDP %p: ERROR cannot send, connection closed by peer or timeout\n", (void *)conn); + return CSP_ERR_RESET; } - } - - if (conn->rdp.state != RDP_OPEN) { - csp_rdp_error("RDP %p: ERROR cannot send, connection not open (%d) -> reset\n", (void *)conn, conn->rdp.state); - return CSP_ERR_RESET; + if (csp_rdp_is_conn_ready_for_tx(conn) == true) + break; + csp_rdp_protocol("RDP %p: Waiting for window update before sending seq %u\n", (void *)conn, conn->rdp.snd_nxt); + csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout); } /* Add RDP header */ @@ -1044,6 +1039,7 @@ bool csp_rdp_conn_is_active(csp_conn_t *conn) { if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { /* The RDP connection has timed out */ + csp_rdp_error("RDP %p: Timeout no packets received last %u ms\n", (void *)conn, conn->rdp.conn_timeout); active = false; } From 65a1f1eed9f194f9fa116b5a79b15b0cf05387ae Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 2 Oct 2024 19:14:34 +0900 Subject: [PATCH 030/348] csp_rdp: Remove trailing whitespaces Trailing whitespaces from previous commits were causing linelint to raise warnings on unrelated PRs. This commit removes those whitespaces. Signed-off-by: Yasushi SHOJI --- src/csp_rdp.c | 3 +-- src/interfaces/csp_if_eth_pbuf.c | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 9dc9a4c5f..12456ee9d 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -121,7 +121,7 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, return CSP_ERR_NOMEM; packet->length = 0; } - + if (flags & RDP_ACK) { conn->rdp.rcv_lsa = ack_nr; } @@ -1050,4 +1050,3 @@ bool csp_rdp_conn_is_active(csp_conn_t *conn) { return active; } - diff --git a/src/interfaces/csp_if_eth_pbuf.c b/src/interfaces/csp_if_eth_pbuf.c index 4097247af..3d7473645 100644 --- a/src/interfaces/csp_if_eth_pbuf.c +++ b/src/interfaces/csp_if_eth_pbuf.c @@ -109,4 +109,3 @@ csp_packet_t * csp_eth_pbuf_find(csp_eth_interface_data_t * ifdata, uint32_t id, return csp_eth_pbuf_new(ifdata, id, now, task_woken); } - From 5b3f0b3b6366c02d2048df9d7fabf98e73e74062 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 12 Sep 2024 02:49:07 +0900 Subject: [PATCH 031/348] drivers: can: zephyr: Prevent potential null pointer dereference Rearranged the assignment of the interface name to occur after the null check on the device pointer. Although `device->name` wasn't necessarily accessed before the null check, it could have been in certain scenarios, which posed a risk of null pointer dereference. The check is now performed first to ensure safe access. Signed-off-by: Gaetan Perrot --- src/drivers/can/can_zephyr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/drivers/can/can_zephyr.c b/src/drivers/can/can_zephyr.c index 7730f6ad1..37d161705 100644 --- a/src/drivers/can/can_zephyr.c +++ b/src/drivers/can/can_zephyr.c @@ -138,13 +138,15 @@ int csp_can_open_and_add_interface(const struct device * device, const char * if int ret; k_tid_t rx_tid; can_context_t * ctx = NULL; - const char * name = ifname ? ifname : device->name; + const char * name; if (device == NULL) { ret = CSP_ERR_INVAL; goto end; } + name = ifname ? ifname : device->name; + if (rx_thread_idx >= CONFIG_CSP_CAN_RX_THREAD_NUM) { LOG_ERR("[%s] No more RX thread can be created. (MAX: %d) Please check CONFIG_CSP_CAN_RX_THREAD_NUM.", name, CONFIG_CSP_CAN_RX_THREAD_NUM); From 682e59c2309d211af1662a4df29320f6c172a33e Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Wed, 2 Oct 2024 15:33:03 +0200 Subject: [PATCH 032/348] Add host machine condition for supporting FreeRTOS compilation FreeRTOS is not posix compatible --- src/drivers/meson.build | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/drivers/meson.build b/src/drivers/meson.build index a21d56cf7..72ad370cb 100644 --- a/src/drivers/meson.build +++ b/src/drivers/meson.build @@ -7,6 +7,8 @@ else conf.set('CSP_HAVE_LIBSOCKETCAN', 0) endif -csp_sources += files(['eth/eth_linux.c']) -csp_sources += files(['usart/usart_linux.c']) -csp_sources += files(['usart/usart_kiss.c']) +if host_machine.system() == 'linux' + csp_sources += files(['eth/eth_linux.c']) + csp_sources += files(['usart/usart_linux.c']) + csp_sources += files(['usart/usart_kiss.c']) +endif From 7d8743843afb085a4940864a0f0549f24efdfa3a Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 8 Sep 2024 12:42:55 +0900 Subject: [PATCH 033/348] csp_queue: Define csp_queue_handle_t with a concrete type The commit cc71d1ad4156d63e6fee1ce2a10825fca394268c fixed an issue with the argument order. However, the underlying problem, besides human error, was that GCC did not warn about type mismatches. This occurred because `csp_queue_handle_t` was typedef'd as `void *`, which is a generic pointer type. GCC does not warn when passing any pointer type to a `void *` argument or vice versa. This commit changes `csp_queue_handle_t` to a concrete type instead of `void *`, allowing for better type checking. Signed-off-by: Yasushi SHOJI --- include/csp/arch/csp_queue.h | 6 ++++-- src/arch/posix/pthread_queue.h | 5 +---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/csp/arch/csp_queue.h b/include/csp/arch/csp_queue.h index fdd0d3960..c2c24ebdf 100644 --- a/include/csp/arch/csp_queue.h +++ b/include/csp/arch/csp_queue.h @@ -21,14 +21,16 @@ extern "C" { #define CSP_QUEUE_OK 0 #define CSP_QUEUE_ERROR -1 -typedef void * csp_queue_handle_t; - #if (CSP_FREERTOS) +typedef QueueHandle_t csp_queue_handle_t; typedef StaticQueue_t csp_static_queue_t; #elif (CSP_ZEPHYR) #include +typedef struct k_msgq * csp_queue_handle_t; typedef struct k_msgq csp_static_queue_t; #else +#include "arch/posix/pthread_queue.h" +typedef pthread_queue_t * csp_queue_handle_t; typedef void * csp_static_queue_t; #endif diff --git a/src/arch/posix/pthread_queue.h b/src/arch/posix/pthread_queue.h index c5810b2c4..c9334fd5c 100644 --- a/src/arch/posix/pthread_queue.h +++ b/src/arch/posix/pthread_queue.h @@ -8,12 +8,9 @@ Inspired by c-pthread-queue by Matthew Dickinson: http://code.google.com/p/c-pthread-queue/ */ +#include #include -#include - - - /** Queue error codes. @{ From 8fc609a06eb2a93d348b0bddc76b5a72b160cad6 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 7 Oct 2024 11:37:19 +0200 Subject: [PATCH 034/348] Use opaque pointer instead of include from source dir --- include/csp/arch/csp_queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/arch/csp_queue.h b/include/csp/arch/csp_queue.h index c2c24ebdf..bb56cc0ed 100644 --- a/include/csp/arch/csp_queue.h +++ b/include/csp/arch/csp_queue.h @@ -29,7 +29,7 @@ typedef StaticQueue_t csp_static_queue_t; typedef struct k_msgq * csp_queue_handle_t; typedef struct k_msgq csp_static_queue_t; #else -#include "arch/posix/pthread_queue.h" +typedef struct pthread_queue_s pthread_queue_t; // Opaque pointer typedef pthread_queue_t * csp_queue_handle_t; typedef void * csp_static_queue_t; #endif From 872f6cb1850d07f39d35085ef6ee5813f10d77ae Mon Sep 17 00:00:00 2001 From: edvard Date: Thu, 14 Dec 2023 12:36:47 +0100 Subject: [PATCH 035/348] rdp: Removed EACK The EACKs are part of the RDP RFC. However, for satellite communication the link may be degraded by EACKs because they are rapidly generated and otherwise transmitted out ot sequence. (1 ack per packet) This messes with the rdp_options and we have found this to be both confusing and detrimental to reliable communication. Especially when dealing with half-duplex links. This commit simply stops generating EACKs and relies instead on the normal packet timeout and retransmission procedure. It has been tested for almost a year in production at Space Inventor. --- src/csp_rdp.c | 92 ++------------------------------------------------- 1 file changed, 2 insertions(+), 90 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 12456ee9d..33b97b334 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -167,49 +167,8 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, /* Send packet to IF */ csp_send_direct(&idout, packet, NULL); - return CSP_ERR_NONE; -} - -/** - * EXTENDED ACKNOWLEDGEMENTS - * The following function sends an extended ACK packet - */ -static int csp_rdp_send_eack(csp_conn_t * conn) { - - /* Allocate message */ - csp_packet_t * packet_eack = csp_buffer_get(0); - if (packet_eack == NULL) return CSP_ERR_NOMEM; - packet_eack->length = 0; - - /* Loop through RX queue */ - int i, count; - csp_packet_t * packet; - count = csp_rdp_queue_rx_size(); - unsigned int space_available = 100 - (packet_eack->length + sizeof(rdp_header_t)); - - for (i = 0; i < count; i++) { - - packet = csp_rdp_queue_rx_get(conn); - if (packet == NULL) { - break; - } - - /* Add seq nr to EACK packet */ - rdp_header_t * header = csp_rdp_header_ref(packet); - if (space_available >= sizeof(uint16_t)) { - packet_eack->data16[packet_eack->length / sizeof(uint16_t)] = htobe16(header->seq_nr); - packet_eack->length += sizeof(uint16_t); - space_available -= sizeof(uint16_t); - csp_rdp_protocol("RDP %p: Added EACK nr %u\n", (void *)conn, header->seq_nr); - } else { - csp_rdp_protocol("RDP %p: Skipping EACK nr %u\n", (void *)conn, header->seq_nr); - } - /* Requeue */ - csp_rdp_queue_rx_add(conn, packet); - } - - return csp_rdp_send_cmp(conn, packet_eack, RDP_ACK | RDP_EAK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + return CSP_ERR_NONE; } /** @@ -326,48 +285,7 @@ static inline int csp_rdp_rx_queue_add(csp_conn_t * conn, csp_packet_t * packet, return 0; } -static void csp_rdp_flush_eack(csp_conn_t * conn, csp_packet_t * eack_packet) { - - /* Loop through TX queue */ - int i, j, count; - csp_packet_t * packet; - count = csp_rdp_queue_tx_size(); - for (i = 0; i < count; i++) { - - packet = csp_rdp_queue_tx_get(conn); - if (packet == NULL) { - break; - } - rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *)packet); - csp_rdp_protocol("RDP %p: EACK compare element, time %" PRIu32 ", seq %u\n", (void *)conn, packet->timestamp_tx, be16toh(header->seq_nr)); - - /* Look for this element in EACKs */ - int match = 0; - for (j = 0; j < (int)((eack_packet->length - sizeof(rdp_header_t)) / sizeof(uint16_t)); j++) { - if (be16toh(eack_packet->data16[j]) == be16toh(header->seq_nr)) - match = 1; - - /* Enable this if you want EACK's to trigger retransmission */ - if (be16toh(eack_packet->data16[j]) > be16toh(header->seq_nr)) { - uint32_t time_now = csp_get_ms(); - if (csp_rdp_time_after(time_now, packet->rdp_quarantine)) { - packet->timestamp_tx = time_now - conn->rdp.packet_timeout - 1; - packet->rdp_quarantine = time_now + conn->rdp.packet_timeout / 2; - } - } - } - - if (match == 0) { - /* If not found, put back on tx queue */ - csp_rdp_queue_tx_add(conn, packet); - } else { - /* Found, free */ - csp_rdp_protocol("RDP %p: TX Element %u freed\n", (void *)conn, be16toh(header->seq_nr)); - csp_buffer_free(packet); - } - } -} static inline bool csp_rdp_should_ack(csp_conn_t * conn) { /* If delayed ACKs are not used, always ACK */ @@ -478,7 +396,6 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp */ conn->rdp.ack_timestamp = csp_get_ms(); - /* Send copy to tx_queue */ packet->timestamp_tx = csp_get_ms(); csp_packet_t * new_packet = csp_buffer_clone(packet); @@ -689,9 +606,6 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { /* If duplicate SYN received, send another SYN/ACK */ if (conn->rdp.state == RDP_SYN_RCVD) csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_SYN, conn->rdp.snd_iss, conn->rdp.rcv_irs); - /* If duplicate data packet received, send EACK back */ - if (conn->rdp.state == RDP_OPEN) - csp_rdp_send_eack(conn); goto discard_open; } @@ -739,8 +653,7 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { /* We have an EACK */ if ((rx_header->flags & RDP_EAK)) { - if (packet->length > sizeof(rdp_header_t)) - csp_rdp_flush_eack(conn, packet); + csp_rdp_protocol("RDP %p: Got EACK\n", (void *)conn); goto discard_open; } @@ -754,7 +667,6 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { csp_rdp_check_ack(conn); goto discard_open; } - csp_rdp_send_eack(conn); goto accepted_open; } From b74b25ec3757a4c8c39a54d25f4d9d8de731f6e9 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 7 Oct 2024 11:44:51 +0200 Subject: [PATCH 036/348] doc clean: removed references to eack Also cleaned the struct for rdp --- doc/basic.md | 1 - doc/protocolstack.md | 2 +- include/csp/csp_types.h | 1 - src/csp_rdp.c | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/basic.md b/doc/basic.md index 60ad6d00b..9489d19fe 100644 --- a/doc/basic.md +++ b/doc/basic.md @@ -62,7 +62,6 @@ Definition of a buffer element `csp_packet_t`: the CSP id to different endian (e.g. I2C), etc. */ typedef struct { - uint32_t rdp_quarantine; // EACK quarantine period uint32_t timestamp_tx; // Time the message was sent uint32_t timestamp_rx; // Time the message was received diff --git a/doc/protocolstack.md b/doc/protocolstack.md index 4317347b2..1a58f0b33 100644 --- a/doc/protocolstack.md +++ b/doc/protocolstack.md @@ -118,6 +118,6 @@ a few additional features: - Packet re-ordering - Retransmission - Windowing - - Extended Acknowledgment + - Extended Acknowledgment (deliberately not supported) For more information on this, please refer to RFC908 and RFC1151. diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index dee5fd463..a26a4dc23 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -121,7 +121,6 @@ typedef struct csp_packet_s { /* Only used on layer 3 (RDP) */ struct { - uint32_t rdp_quarantine; /*< EACK quarantine period */ uint32_t timestamp_tx; /*< Time the message was sent */ uint32_t timestamp_rx; /*< Time the message was received */ struct csp_conn_s * conn; /*< Associated connection (this is used in RDP queue) */ diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 33b97b334..497d8c772 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -837,7 +837,6 @@ int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet) { } rdp_packet->timestamp_tx = csp_get_ms(); - rdp_packet->rdp_quarantine = 0; csp_rdp_queue_tx_add(conn, rdp_packet); csp_rdp_protocol( From faa46fd1f0625293558cc22e76842eb790ace92a Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 24 Sep 2024 17:34:55 +0900 Subject: [PATCH 037/348] cmake: freertos: Add informative error message for unsupported build FreeRTOS is currently not supported by the CMake build system. As far as we know, Meson works with FreeRTOS. This update adds an error message to inform users that CMake support requires community contributions. Signed-off-by: Yasushi SHOJI --- src/arch/freertos/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/arch/freertos/CMakeLists.txt diff --git a/src/arch/freertos/CMakeLists.txt b/src/arch/freertos/CMakeLists.txt new file mode 100644 index 000000000..21478ec90 --- /dev/null +++ b/src/arch/freertos/CMakeLists.txt @@ -0,0 +1,3 @@ +message(FATAL_ERROR + "${CMAKE_SYSTEM_NAME} is not yet supported by CMake. Instead, use Meson. + https://github.com/libcsp/libcsp/issues/524") From 79b2294e8b68c802902005bd400d62460a7ad7da Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 7 Oct 2024 12:18:35 +0200 Subject: [PATCH 038/348] iflist: add now returns void The add itself is always sucessful. If adding an invalid interface or null pointer it will silently fail. But the user will very fast notice that the interface has not been added. So run time error checking for interface adding is not needed. --- include/csp/csp_iflist.h | 3 +-- src/csp_iflist.c | 10 +++++++--- src/interfaces/csp_if_can.c | 4 +++- src/interfaces/csp_if_i2c.c | 4 +++- src/interfaces/csp_if_kiss.c | 4 +++- src/interfaces/csp_if_zmqhub.c | 12 ++++++------ 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/include/csp/csp_iflist.h b/include/csp/csp_iflist.h index d6ee975f0..4e23b7ec0 100644 --- a/include/csp/csp_iflist.h +++ b/include/csp/csp_iflist.h @@ -15,9 +15,8 @@ extern "C" { * Add interface to the list. * * @param[in] iface The interface must remain valid as long as the application is running. - * @return #CSP_ERR_NONE on success, otherwise an error code. */ -int csp_iflist_add(csp_iface_t * iface); +void csp_iflist_add(csp_iface_t * iface); /** * Remove interface from the list. diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 448a2c162..3ad071f6b 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -152,7 +152,11 @@ csp_iface_t * csp_iflist_get_by_index(int idx) { return ifc; } -int csp_iflist_add(csp_iface_t * ifc) { +void csp_iflist_add(csp_iface_t * ifc) { + + if ((ifc == NULL) || (ifc->name == NULL)) { + return; + } ifc->next = NULL; @@ -165,7 +169,7 @@ int csp_iflist_add(csp_iface_t * ifc) { csp_iface_t * last = NULL; for (csp_iface_t * i = interfaces; i != NULL; i = i->next) { if ((i == ifc) || (strncmp(ifc->name, i->name, CSP_IFLIST_NAME_MAX) == 0)) { - return CSP_ERR_ALREADY; + return; } last = i; } @@ -173,10 +177,10 @@ int csp_iflist_add(csp_iface_t * ifc) { last->next = ifc; } - return CSP_ERR_NONE; } void csp_iflist_remove(csp_iface_t * ifc) { + if (ifc == NULL) { return; } diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index 180a81513..9a73a7fe0 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -486,7 +486,9 @@ int csp_can_add_interface(csp_iface_t * iface) { iface->nexthop = csp_can2_tx; } - return csp_iflist_add(iface); + csp_iflist_add(iface); + + return CSP_ERR_NONE; } int csp_can_remove_interface(csp_iface_t * iface) { diff --git a/src/interfaces/csp_if_i2c.c b/src/interfaces/csp_if_i2c.c index bb66e01bf..ffb3228b7 100644 --- a/src/interfaces/csp_if_i2c.c +++ b/src/interfaces/csp_if_i2c.c @@ -66,5 +66,7 @@ int csp_i2c_add_interface(csp_iface_t * iface) { iface->nexthop = csp_i2c_tx; - return csp_iflist_add(iface); + csp_iflist_add(iface); + + return CSP_ERR_NONE; } diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index 1dcef80c3..a9c55e59a 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -205,5 +205,7 @@ int csp_kiss_add_interface(csp_iface_t * iface) { iface->nexthop = csp_kiss_tx; - return csp_iflist_add(iface); + csp_iflist_add(iface); + + return CSP_ERR_NONE; } diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 076d9157e..7976d1803 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -209,13 +209,13 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr assert(ret == 0); /* Register interface */ - ret = csp_iflist_add(&drv->iface); + csp_iflist_add(&drv->iface); - if (ret == CSP_ERR_NONE && return_interface) { + if (return_interface) { *return_interface = &drv->iface; } - return ret; + return CSP_ERR_NONE; } int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport) { @@ -329,13 +329,13 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add assert(ret == 0); /* Register interface */ - ret = csp_iflist_add(&drv->iface); + csp_iflist_add(&drv->iface); - if (ret == CSP_ERR_NONE && return_interface) { + if (return_interface) { *return_interface = &drv->iface; } - return ret; + return CSP_ERR_NONE; } From 21828db39513191dfd06bbe3dea2533a4a31299d Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Tue, 8 Oct 2024 08:05:55 +0200 Subject: [PATCH 039/348] interface: can: Re-Fix off-by-one error in csp_can2_rx overflow check Imagine having configured a max MTU = 8 bytes For simplicity, we say that sizeof(packet->header) = 0 bytes address of packet->frame_begin == packet->data == 0x0 A packet is received over CAN, with dlc = 4 bytes. First CAN frame then writes to frame_begin[0..3]. Frame length is increased to 4, so that second CAN frame, also containing 4 bytes writes to frame_begin[4..7] With the previous commit, the test executed when the second CAN frame is received, as &packet->frame_begin[packet->frame_length] + dlc >= &packet->data[sizeof(packet->data)] --> &packet->frame_begin[4] + 4 >= &packet->data[8] --> 4 + 4 >= 8 --> Statement is true, thus fails with CSP_DBG_CAN_ERR_RX_OVF Therefore, the check should be 4 + 4 > 8 to allow the last byte of the packet to be written --- src/interfaces/csp_if_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index 9a73a7fe0..66988c75e 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -340,7 +340,7 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t } /* Check for overflow. The frame input + dlc must not exceed the end of the packet data field */ - if (&packet->frame_begin[packet->frame_length] + dlc >= &packet->data[sizeof(packet->data)]) { + if (&packet->frame_begin[packet->frame_length] + dlc > &packet->data[sizeof(packet->data)]) { csp_dbg_can_errno = CSP_DBG_CAN_ERR_RX_OVF; iface->rx_error++; csp_can_pbuf_free(ifdata, packet, 1, task_woken); From 6e7d3405c66fb34e8a08372bf4133410aba83a75 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 8 Aug 2024 06:10:42 +0900 Subject: [PATCH 040/348] zephyr: Kconfig: Replace POSIX_CLOCK POSIX_CLOCK is deprecated in Zephyr v3.7. POSIX_CLOCK still works but will be removed in v4.0. https://docs.zephyrproject.org/latest/releases/release-notes-3.7.html#zephyr-3-7-posix-api-deprecations In libcsp, we use clock_settime() and csp_clock_get_time(). Thus, we select `POSIX_TIMERS` and `POSIX_MONOTONIC_CLOCK`. Signed-off-by: Yasushi SHOJI --- contrib/zephyr/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index 44672e8e7..08780701c 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -5,7 +5,8 @@ config LIBCSP bool "Enable Cubesat Space Protocol Support" - select POSIX_CLOCK + select POSIX_TIMERS + select POSIX_MONOTONIC_CLOCK help This option enables the Cubesat Space Protocol (CSP) library. From 5e02b22221cdb4ad6ac5ed292015f0361033399c Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Tue, 15 Nov 2022 22:59:45 +0100 Subject: [PATCH 041/348] csp_buffer_get_always: A new method From now on, csp_buffer_get will reserve two (2) buffers for critical incoming messages. These remaining buffers can be used only by calling csp_buffer_get_always() - This should be reserved only for such places where not getting a buffer would be detrimental to the reliability of the system. An example is the CAN input buffers. A failure to retrieve a buffer here means the system gets non responsive. We handle this type of error by calling csp_panic() a new hook thats just added. This function must not return, and should reboot the system or the program running CSP to recover responsiveness of the system. --- contrib/zephyr/samples/server-client/main.c | 2 +- doc/basic.md | 4 ++ examples/csp_arch.c | 4 ++ examples/csp_server_client.c | 2 +- include/csp/csp_buffer.h | 12 ++++ include/csp/csp_hooks.h | 2 + src/csp_buffer.c | 76 +++++++++++++++------ src/csp_init.c | 8 ++- src/interfaces/csp_if_can.c | 8 --- src/interfaces/csp_if_can_pbuf.c | 5 +- src/interfaces/csp_if_kiss.c | 4 +- src/interfaces/csp_if_tun.c | 3 +- 12 files changed, 91 insertions(+), 39 deletions(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index 49747bd6c..a08e1da6c 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -108,7 +108,7 @@ void client(void) { } /* 2. Get packet buffer for message/data */ - csp_packet_t * packet = csp_buffer_get(0); + csp_packet_t * packet = csp_buffer_get_always(); if (packet == NULL) { /* Could not get buffer element */ LOG_ERR("Failed to get CSP buffer"); diff --git a/doc/basic.md b/doc/basic.md index 9489d19fe..a5eff80cb 100644 --- a/doc/basic.md +++ b/doc/basic.md @@ -48,6 +48,10 @@ version is for task-context and interrupt-context. Using fixed size buffer elements that are preallocated is again a question of speed and safety. +if you use `csp_buffer_get_always()` instead csp will panic if there is +not enough available buffers. This ensures that incoming hardware +always gets a buffer, or the system will reboot. + Definition of a buffer element `csp_packet_t`: ```c diff --git a/examples/csp_arch.c b/examples/csp_arch.c index 8cd3f4ba0..6be5d827f 100644 --- a/examples/csp_arch.c +++ b/examples/csp_arch.c @@ -8,6 +8,10 @@ #include #include +void csp_panic(const char * msg) { + csp_print("csp_panic: %s\n", msg); + exit(1); +} int main(int argc, char * argv[]) { diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 2ded19ec4..6d13b9662 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -108,7 +108,7 @@ void client(void) { } /* 2. Get packet buffer for message/data */ - csp_packet_t * packet = csp_buffer_get(0); + csp_packet_t * packet = csp_buffer_get_always(); if (packet == NULL) { /* Could not get buffer element */ csp_print("Failed to get CSP buffer\n"); diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index d9cce32ef..93eba98e3 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -19,6 +19,12 @@ extern "C" { */ csp_packet_t * csp_buffer_get(size_t unused); +/** + * Get a buffer or get killed (from task context) + * @return Buffer (pointer to #csp_packet_t) + */ +csp_packet_t * csp_buffer_get_always(void); + /** * Get free buffer (from ISR context). * @@ -27,6 +33,12 @@ csp_packet_t * csp_buffer_get(size_t unused); */ csp_packet_t * csp_buffer_get_isr(size_t unused); +/** + * Get a buffer or get killed (from ISR context) + * @return Buffer (pointer to #csp_packet_t) + */ +csp_packet_t * csp_buffer_get_always_isr(void); + /** * Free buffer (from task context). * diff --git a/include/csp/csp_hooks.h b/include/csp/csp_hooks.h index 2f2d57aef..5e90249eb 100644 --- a/include/csp/csp_hooks.h +++ b/include/csp/csp_hooks.h @@ -21,6 +21,8 @@ void csp_shutdown_hook(void); uint32_t csp_memfree_hook(void); unsigned int csp_ps_hook(csp_packet_t * packet); +void csp_panic(const char * msg); + /** Implement these, if you use csp_if_tun */ int csp_crypto_decrypt(uint8_t * ciphertext_in, uint8_t ciphertext_len, uint8_t * msg_out); // Returns -1 for failure, length if ok int csp_crypto_encrypt(uint8_t * msg_begin, uint8_t msg_len, uint8_t * ciphertext_out); // Returns length of encrypted data diff --git a/src/csp_buffer.c b/src/csp_buffer.c index d6fcca181..4b3790a97 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -1,4 +1,3 @@ -#include #include #include @@ -6,6 +5,8 @@ #include #include #include "csp_macro.h" +#include +#include /** Internal buffer header */ typedef struct csp_skbf_s { @@ -35,31 +36,30 @@ void csp_buffer_init(void) { } } -csp_packet_t * csp_buffer_get_isr(size_t unused) { +static csp_packet_t * csp_buffer_get_actual(int reserve, int isr) { - csp_skbf_t * buffer = NULL; - int task_woken = 0; - csp_queue_dequeue_isr(csp_buffers, &buffer, &task_woken); - if (buffer == NULL) { - csp_dbg_buffer_out++; - return NULL; + /* Get buffers remaining */ + int remain; + if (isr) { + remain = csp_queue_size_isr(csp_buffers); + } else { + remain = csp_queue_size(csp_buffers); } - - if (buffer != buffer->skbf_addr) { - csp_dbg_errno = CSP_DBG_ERR_CORRUPT_BUFFER; - /* Best option here must be to leak the invalid buffer */ + /* Respect if remaining is lower than the reserve requested */ + if (remain < reserve) { return NULL; } - buffer->refcount = 1; - csp_id_clear(&buffer->skbf_data.id); - return &buffer->skbf_data; -} - -csp_packet_t * csp_buffer_get(size_t unused) { - + /* Now fetch a buffer */ csp_skbf_t * buffer = NULL; - csp_queue_dequeue(csp_buffers, &buffer, 0); + if (isr) { + int task_woken = 0; + csp_queue_dequeue_isr(csp_buffers, &buffer, &task_woken); + } else { + csp_queue_dequeue(csp_buffers, &buffer, 0); + } + + /* We might be out of buffers */ if (buffer == NULL) { csp_dbg_buffer_out++; return NULL; @@ -165,3 +165,39 @@ void csp_buffer_refc_inc(void * buffer) { int csp_buffer_remaining(void) { return csp_queue_size(csp_buffers); } + +/* CSP will use every remaining buffer in an attempt to allocate a packet + * buffer. Including the last two, that is normally reserved. + * This can be important for ensuring a node is always reachable, so a + * failure to allocate will cause a panic and a reboot */ + +csp_packet_t * csp_buffer_get_always(void) { + csp_packet_t * packet = csp_buffer_get_actual(0, 0); + if (packet == NULL) { + csp_panic("Out of buffers"); + while(1); + } + return packet; +} + +csp_packet_t * csp_buffer_get_always_isr(void) { + csp_packet_t * packet = csp_buffer_get_actual(0, 1); + if (packet == NULL) { + csp_panic("Out of buffers"); + while(1); + } + return packet; +} + +/* CSP will try to reserve the last two buffers for calls which can take it, + * examples are client funktions that are allowed to fail and have adequate + * error checking. Or services which are allowed to timeout of memory becomes + * sparse. */ + +csp_packet_t * csp_buffer_get(size_t unused) { + return csp_buffer_get_actual(2, 0); +} + +csp_packet_t * csp_buffer_get_isr(size_t unused) { + return csp_buffer_get_actual(2, 1); +} diff --git a/src/csp_init.c b/src/csp_init.c index 5910b65c8..c1389a0b9 100644 --- a/src/csp_init.c +++ b/src/csp_init.c @@ -1,14 +1,18 @@ - - #include #include +#include #include #include "csp/autoconfig.h" +#include "csp_macro.h" #include "csp_conn.h" #include "csp_qfifo.h" #include "csp_port.h" #include "csp_rdp_queue.h" +__weak void csp_panic(const char * msg) { + return; +} + csp_conf_t csp_conf = { .version = 2, .hostname = "", diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index 66988c75e..880f31acc 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -60,10 +60,6 @@ int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t if (packet == NULL) { if (CFP_TYPE(id) == CFP_BEGIN) { packet = csp_can_pbuf_new(ifdata, id, task_woken); - if (packet == NULL) { - iface->rx_error++; - return CSP_ERR_NOMEM; - } } else { iface->frame++; return CSP_ERR_INVAL; @@ -274,10 +270,6 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t if (packet == NULL) { if (id & (CFP2_BEGIN_MASK << CFP2_BEGIN_OFFSET)) { packet = csp_can_pbuf_new(ifdata, id, task_woken); - if (packet == NULL) { - iface->rx_error++; - return CSP_ERR_NOMEM; - } } else { iface->frame++; return CSP_ERR_INVAL; diff --git a/src/interfaces/csp_if_can_pbuf.c b/src/interfaces/csp_if_can_pbuf.c index 454e3c80f..cf7641693 100644 --- a/src/interfaces/csp_if_can_pbuf.c +++ b/src/interfaces/csp_if_can_pbuf.c @@ -49,10 +49,7 @@ csp_packet_t * csp_can_pbuf_new(csp_can_interface_data_t * ifdata, uint32_t id, uint32_t now = (task_woken) ? csp_get_ms_isr() : csp_get_ms(); - csp_packet_t * packet = (task_woken) ? csp_buffer_get_isr(0) : csp_buffer_get(0); - if (packet == NULL) { - return NULL; - } + csp_packet_t * packet = (task_woken) ? csp_buffer_get_always_isr() : csp_buffer_get_always(); packet->last_used = now; packet->cfpid = id; diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index a9c55e59a..e529a4cc0 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -93,9 +93,9 @@ void csp_kiss_rx(csp_iface_t * iface, const uint8_t * buf, size_t len, void * px break; } - /* Try to allocate new buffer */ + /* Always allocate new buffer */ if (ifdata->rx_packet == NULL) { - ifdata->rx_packet = pxTaskWoken ? csp_buffer_get_isr(0) : csp_buffer_get(0); // CSP only supports one size + ifdata->rx_packet = pxTaskWoken ? csp_buffer_get_always_isr() : csp_buffer_get_always(); } /* If no more memory, skip frame */ diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index 321aded13..6225af10e 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -17,7 +17,7 @@ static int csp_if_tun_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packe csp_if_tun_conf_t * ifconf = iface->driver_data; /* Allocate new frame */ - csp_packet_t * new_packet = csp_buffer_get(0); + csp_packet_t * new_packet = csp_buffer_get_always(); if (new_packet == NULL) { csp_buffer_free(packet); return CSP_ERR_NONE; @@ -118,3 +118,4 @@ void csp_if_tun_init(csp_iface_t * iface, csp_if_tun_conf_t * ifconf) { csp_iflist_add(iface); } + From 19b7394f2b5bea5a16b6d7eb7c2d23064df42f6e Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Thu, 10 Oct 2024 13:00:23 +0200 Subject: [PATCH 042/348] csp_sfp: buffer overflow check Relates to PR #503 --- src/csp_sfp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/csp_sfp.c b/src/csp_sfp.c index b78ce4651..89c43e2af 100644 --- a/src/csp_sfp.c +++ b/src/csp_sfp.c @@ -49,7 +49,7 @@ static inline sfp_header_t * csp_sfp_header_remove(csp_packet_t * packet) { } int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int totalsize, unsigned int mtu, uint32_t timeout, csp_memcpy_fnc_t memcpyfcn) { - if (mtu == 0) { + if (mtu == 0 || mtu + sizeof(sfp_header_t) > CSP_BUFFER_SIZE) { return CSP_ERR_INVAL; } @@ -59,7 +59,7 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t sfp_header_t * sfp_header; /* Allocate packet */ - csp_packet_t * packet = csp_buffer_get(mtu + sizeof(*sfp_header)); + csp_packet_t * packet = csp_buffer_get(0); if (packet == NULL) { return CSP_ERR_NOMEM; } @@ -81,7 +81,7 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t conn->idout.flags |= CSP_FFRAG; /* Add SFP header */ - sfp_header = csp_sfp_header_add(packet); // no check, because buffer was allocated with extra size. + sfp_header = csp_sfp_header_add(packet); sfp_header->totalsize = htobe32(totalsize); sfp_header->offset = htobe32(count); From f2694b460116eccfeeedc1a7a91ddd50c6b7fe0c Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Mon, 14 Oct 2024 10:03:34 +0300 Subject: [PATCH 043/348] csp_queue:arch:posix: Fix incorrect free space calculation Corrects the free space calculation by removing an unnecessary division. The queue::size variable represents the queue length, not the buffer size, so the current approach miscalculates the available free elements. --- src/arch/posix/pthread_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index ff10b9a2f..ce35cf414 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -218,7 +218,7 @@ int pthread_queue_items(pthread_queue_t * queue) { int pthread_queue_free(pthread_queue_t * queue) { pthread_mutex_lock(&(queue->mutex)); - int free = (queue->size / queue->item_size) - queue->items; + int free = queue->size - queue->items; pthread_mutex_unlock(&(queue->mutex)); return free; From 55471e4bc6db75ffc09c27c13a1e8b35d00cfa78 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Sat, 26 Oct 2024 10:16:41 +0300 Subject: [PATCH 044/348] csp_rdp: Prevent NULL pointer dereference Adds a check to ensure successful buffer allocation, preventing potential NULL pointer dereference when buffers are unavailable. Introduces the 'csp_buffer_copy' function to improve safety and handle buffers more reliably. --- include/csp/csp_buffer.h | 10 +++++++++- src/csp_buffer.c | 22 +++++++++++----------- src/csp_rdp.c | 29 +++++++++++++++++------------ 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index 93eba98e3..62b2a18df 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -60,7 +60,15 @@ void csp_buffer_free_isr(void * buffer); * @param[in] buffer buffer to clone. * @return cloned buffer on success, or NULL on failure. */ -void * csp_buffer_clone(void * buffer); +csp_packet_t * csp_buffer_clone(const csp_packet_t * packet); + +/** + * Copy the contents of a buffer. + * + * @param[in] src Source buffer. + * @param[out] dst Destination buffer. + */ +void csp_buffer_copy(const csp_packet_t * src, csp_packet_t * dst); /** * Return number of remaining/free buffers. diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 4b3790a97..808d149bc 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -130,22 +130,22 @@ void csp_buffer_free(void * packet) { csp_queue_enqueue(csp_buffers, &buf, 0); } -void * csp_buffer_clone(void * buffer) { - - csp_packet_t * packet = (csp_packet_t *)buffer; - if (!packet) { - return NULL; - } - - csp_packet_t * clone = csp_buffer_get(packet->length); - if (clone) { - size_t size = sizeof(csp_packet_t) - CSP_BUFFER_SIZE + CSP_PACKET_PADDING_BYTES + packet->length; - memcpy(clone, packet, size > sizeof(csp_packet_t) ? sizeof(csp_packet_t) : size); +csp_packet_t * csp_buffer_clone(const csp_packet_t * packet) { + csp_packet_t * clone = NULL; + if (packet) { + clone = csp_buffer_get(0); + csp_buffer_copy(packet, clone); } return clone; } +void csp_buffer_copy(const csp_packet_t * src, csp_packet_t * dst) { + if ((NULL != src) && (NULL != dst)) { + (void)memcpy(dst, src, sizeof(csp_packet_t)); + } +} + void csp_buffer_refc_inc(void * buffer) { if (!buffer) { diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 497d8c772..eb9c18700 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -388,18 +388,23 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { /* Check timestamp and retransmit if needed */ if (csp_rdp_time_after(time_now, packet->timestamp_tx + conn->rdp.packet_timeout)) { - csp_rdp_protocol("RDP %p: TX Element timed out, retransmitting seq %u\n", - (void *)conn, be16toh(header->seq_nr)); - - /* Update to latest outgoing ACK */ - header->ack_nr = htobe16(conn->rdp.rcv_cur); - - /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp */ - conn->rdp.ack_timestamp = csp_get_ms(); - /* Send copy to tx_queue */ - packet->timestamp_tx = csp_get_ms(); - csp_packet_t * new_packet = csp_buffer_clone(packet); - csp_send_direct(&conn->idout, new_packet, NULL); + csp_packet_t * new_packet = csp_buffer_get(0); + if (new_packet) { + csp_rdp_protocol("RDP %p: TX Element timed out, retransmitting seq %u\n", + (void *)conn, be16toh(header->seq_nr)); + + /* Update to latest outgoing ACK */ + header->ack_nr = htobe16(conn->rdp.rcv_cur); + + /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp */ + conn->rdp.ack_timestamp = csp_get_ms(); + /* Send copy to tx_queue */ + packet->timestamp_tx = csp_get_ms(); + csp_buffer_copy(packet, new_packet); + csp_send_direct(&conn->idout, new_packet, NULL); + } else { + csp_rdp_error("RDP %p: Failed to allocate packet buffer\n", (void *)conn); + } } /* Requeue the TX element */ From 537e90c6de566bf03164b57a4a7ceaa2c8ac3693 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 31 Oct 2024 14:56:14 +0900 Subject: [PATCH 045/348] csp_packet: Remove unused timestamp_rx variable Remove the unused member variable `timestamp_rx` from the `csp_packet_s` struct. In a previous commit, removing a different member variable shifted the layout of the struct, changing where assignments could cause unintended overwrites. This change made it easier to surface the overwrite issue involving `timestamp_rx`. The assignment to `packet->timestamp_rx` in `csp_id_strip()` represents a genuine bug and a regression in protocol version 1. Signed-off-by: Yasushi SHOJI --- doc/basic.md | 1 - include/csp/csp_types.h | 1 - src/csp_id.c | 1 - src/interfaces/csp_if_can.c | 3 --- 4 files changed, 6 deletions(-) diff --git a/doc/basic.md b/doc/basic.md index a5eff80cb..c2f4b66df 100644 --- a/doc/basic.md +++ b/doc/basic.md @@ -67,7 +67,6 @@ Definition of a buffer element `csp_packet_t`: */ typedef struct { uint32_t timestamp_tx; // Time the message was sent - uint32_t timestamp_rx; // Time the message was received uint16_t length; // Data length csp_id_t id; // CSP id (unpacked version CPU readable) diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index a26a4dc23..c2b7e4c6d 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -122,7 +122,6 @@ typedef struct csp_packet_s { /* Only used on layer 3 (RDP) */ struct { uint32_t timestamp_tx; /*< Time the message was sent */ - uint32_t timestamp_rx; /*< Time the message was received */ struct csp_conn_s * conn; /*< Associated connection (this is used in RDP queue) */ }; diff --git a/src/csp_id.c b/src/csp_id.c index ee5549576..e6957b701 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -187,7 +187,6 @@ void csp_id_prepend(csp_packet_t * packet) { } int csp_id_strip(csp_packet_t * packet) { - packet->timestamp_rx = 0; if (csp_conf.version == 2) { return csp_id2_strip(packet); } else { diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index 880f31acc..d5b25d0ac 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -148,9 +148,6 @@ int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t /* Free packet buffer */ csp_can_pbuf_free(ifdata, packet, 0, task_woken); - /* Clear timestamp_rx for L3 as L2 last_used is not needed anymore */ - packet->timestamp_rx = 0; - /* Data is available */ csp_qfifo_write(packet, iface, task_woken); From bedd0db81c134d6289ebb1681cbae28d5cb55669 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 26 Oct 2024 05:43:23 +0900 Subject: [PATCH 046/348] Add the fist unit test This commit adds unit test frame work and the first unit test for the issue fixed by the previous commit for csp_queue_free(). The framework used in this commit is "Check"[1]. [1]: https://github.com/libcheck/check Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 9 ++++++++ tests/CMakeLists.txt | 8 +++++++ tests/main.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ tests/queue.c | 46 +++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/main.c create mode 100644 tests/queue.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ff0447087..17f8373e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND else() message(NOTICE "No libsocketcan found") endif() + + pkg_search_module(CHECK check) + if(${CHECK_FOUND}) + message(STATUS "Found ${CHECK_MODULE_NAME} ${CHECK_VERSION}") + set(CSP_HAVE_CHECK 1) + else() + message(NOTICE "No libcheck found") + endif() else() message(NOTICE "No pkg-config found") endif() @@ -100,6 +108,7 @@ target_compile_options(csp PRIVATE ${CSP_C_ARGS}) add_subdirectory(src) add_subdirectory(examples) +add_subdirectory(tests) if(${CSP_ENABLE_PYTHON3_BINDINGS}) find_package(Python3 COMPONENTS Development.Module) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..9720cfd9a --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +if(CHECK_FOUND) + add_executable(csp_tests) + target_link_libraries(csp_tests PRIVATE csp ${CHECK_LIBRARIES}) + target_sources(csp_tests PRIVATE + main.c + queue.c + ) +endif() diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 000000000..5600675a5 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#define DEFAULT_PRINT_VERBOSITY (CK_NORMAL) + +Suite * queue_suite(void); + +static struct option long_options[] = { + {"verbose", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; + +void print_help() { + printf("Usage: csp_tests [options]\n"); + printf("Run libcsp unit tests.\n\n"); + printf("Without any option, it will run all tests.\n\n"); + printf(" --verbose print verbose message\n" + " -h print help\n"); +} + +int main(int argc, char *argv[]) +{ + int number_failed; + SRunner *sr; + int opt; + enum print_output print_verbosity = DEFAULT_PRINT_VERBOSITY; + + while ((opt = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { + switch (opt) { + case 'V': + print_verbosity = CK_VERBOSE; + break; + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case '?': + /* Invalid option or missing argument */ + print_help(); + exit(EXIT_FAILURE); + } + } + + sr = srunner_create(NULL); + srunner_add_suite(sr, queue_suite()); + + srunner_run_all(sr, print_verbosity); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/queue.c b/tests/queue.c new file mode 100644 index 000000000..dc96c95d4 --- /dev/null +++ b/tests/queue.c @@ -0,0 +1,46 @@ +#include +#include "../include/csp/csp.h" + +#define DEFAULT_TIMEOUT 1000 + +/* https://github.com/libcsp/libcsp/pull/707 */ +START_TEST(test_queue_free_707) +{ + char item[] = "abc"; + + int qlength = 10; + int buf_size = qlength * sizeof(item); + char buf[buf_size]; + + csp_queue_handle_t qh; + csp_static_queue_t q; + + /* zero clear */ + memset(buf, 0, buf_size); + + csp_init(); + + /* create */ + qh = csp_queue_create_static(qlength, sizeof(item), buf, &q); + ck_assert_int_eq(csp_queue_free(qh), qlength); + + /* enqueue */ + ck_assert_int_eq(csp_queue_enqueue(qh, item, DEFAULT_TIMEOUT), CSP_QUEUE_OK); + ck_assert_int_eq(csp_queue_free(qh), qlength - 1); + +} +END_TEST + +Suite * queue_suite(void) +{ + Suite *s; + TCase *tc_free; + + s = suite_create("Queue"); + + tc_free = tcase_create("free"); + tcase_add_test(tc_free, test_queue_free_707); + suite_add_tcase(s, tc_free); + + return s; +} From 4222b70086bc11d270fa2371d69032c1a1b861a7 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Tue, 15 Oct 2024 14:14:51 +0900 Subject: [PATCH 047/348] examples: Remove unnecessary externs csp_conf is already externed in include/csp/csp.h. Signed-off-by: Takumi Ando --- examples/csp_bridge_can2udp.c | 2 -- examples/zmqproxy.c | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/csp_bridge_can2udp.c b/examples/csp_bridge_can2udp.c index c5c6ae782..a232d1be1 100644 --- a/examples/csp_bridge_can2udp.c +++ b/examples/csp_bridge_can2udp.c @@ -13,8 +13,6 @@ #define DEFAULT_UDP_REMOTE_PORT (0) #define DEFAULT_UDP_LOCAL_PORT (0) -extern csp_conf_t csp_conf; - static struct option long_options[] = { {"can", required_argument, 0, 'c'}, {"remote-address", required_argument, 0, 'a'}, diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index 709106c49..4b1777a6b 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -8,7 +8,6 @@ int csp_id_strip(csp_packet_t * packet); int csp_id_setup_rx(csp_packet_t * packet); -extern csp_conf_t csp_conf; int debug = 0; const char * sub_str = "tcp://0.0.0.0:6000"; @@ -72,7 +71,7 @@ static void * task_capture(void * ctx) { csp_print("Packet: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %" PRIu16 "\n", packet->id.src, packet->id.dst, packet->id.dport, packet->id.sport, packet->id.pri, packet->id.flags, packet->length); - + if (logfile) { const char * delimiter = "--------\n"; From 3c44572e00a11d1b82af79e248753bc37de20630 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Tue, 15 Oct 2024 14:44:32 +0900 Subject: [PATCH 048/348] examples: Add CSP version option Bugs specific to CSP version 1 are often overlooked. This commit adds a version option to all the examples to avoid these bugs. Signed-off-by: Takumi Ando --- examples/csp_client.c | 7 ++++++- examples/csp_server.c | 7 ++++++- examples/csp_server_client.c | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/examples/csp_client.c b/examples/csp_client.c index 37625a31a..d33bc4b54 100644 --- a/examples/csp_client.c +++ b/examples/csp_client.c @@ -57,6 +57,7 @@ static struct option long_options[] = { #endif {"interface-address", required_argument, 0, 'a'}, {"connect-to", required_argument, 0, 'C'}, + {"protocol-version", required_argument, 0, 'v'}, {"test-mode", no_argument, 0, 't'}, {"test-mode-with-sec", required_argument, 0, 'T'}, {"help", no_argument, 0, 'h'}, @@ -80,6 +81,7 @@ void print_help() { if (1) { csp_print(" -a
set interface address\n" " -C
connect to server at address\n" + " -v set protocol version\n" " -t enable test mode\n" " -T enable test mode with running time in seconds\n" " -h print help\n"); @@ -139,7 +141,7 @@ int main(int argc, char * argv[]) { int ret = EXIT_SUCCESS; int opt; - while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:C:tT:h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:C:v:tT:h", long_options, NULL)) != -1) { switch (opt) { case 'c': device_name = optarg; @@ -164,6 +166,9 @@ int main(int argc, char * argv[]) { case 'C': server_address = atoi(optarg); break; + case 'v': + csp_conf.version = atoi(optarg); + break; case 't': test_mode = true; break; diff --git a/examples/csp_server.c b/examples/csp_server.c index e124c28dd..87c1cd7bc 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -106,6 +106,7 @@ static struct option long_options[] = { #endif {"interface-address", required_argument, 0, 'a'}, {"connect-to", required_argument, 0, 'C'}, + {"protocol-version", required_argument, 0, 'v'}, {"test-mode", no_argument, 0, 't'}, {"test-mode-with-sec", required_argument, 0, 'T'}, {"help", no_argument, 0, 'h'}, @@ -128,6 +129,7 @@ void print_help() { } if (1) { csp_print(" -a
set interface address\n" + " -v set protocol version\n" " -t enable test mode\n" " -T enable test mode with running time in seconds\n" " -h print help\n"); @@ -184,7 +186,7 @@ int main(int argc, char * argv[]) { csp_iface_t * default_iface; int opt; - while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:tT:h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:v:tT:h", long_options, NULL)) != -1) { switch (opt) { case 'c': device_name = optarg; @@ -206,6 +208,9 @@ int main(int argc, char * argv[]) { case 'a': server_address = atoi(optarg); break; + case 'v': + csp_conf.version = atoi(optarg); + break; case 't': test_mode = true; break; diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 6d13b9662..5c34c456c 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -138,6 +138,7 @@ void client(void) { static void print_usage(void) { csp_print("Usage:\n" + " -v set protocol version\n" " -t enable test mode\n" " -T enable test mode with running time in seconds\n" " -h print help\n"); @@ -148,7 +149,7 @@ int main(int argc, char * argv[]) { uint8_t address = 0; int opt; - while ((opt = getopt(argc, argv, "tT:h")) != -1) { + while ((opt = getopt(argc, argv, "v:tT:h")) != -1) { switch (opt) { case 'a': address = atoi(optarg); @@ -156,6 +157,9 @@ int main(int argc, char * argv[]) { case 'r': server_address = atoi(optarg); break; + case 'v': + csp_conf.version = atoi(optarg); + break; case 't': test_mode = true; break; From 5ee4b0a9639fdfd82cfec6f96b2891ef0505c845 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Tue, 15 Oct 2024 14:49:37 +0900 Subject: [PATCH 049/348] github: workflow: build-test: Run tests with both CSP v1 and v2 This commit adds support for running tests with both CSP v1 and v2. Signed-off-by: Takumi Ando --- .github/workflows/build-test.yml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 1ab5cad58..165984032 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -18,6 +18,9 @@ jobs: CXX: g++ - CC: clang CXX: clang++ + csp_version: + - 1 + - 2 runs-on: ${{ matrix.os }} steps: - name: Setup packages on Linux @@ -57,30 +60,30 @@ jobs: run: | socat -d -d -d pty,raw,echo=0,link=/tmp/pty1 pty,raw,echo=0,link=/tmp/pty2 & sleep 1 - ./build/examples/csp_server -k /tmp/pty1 -a 1 -T 10 & - ./build/examples/csp_client -k /tmp/pty2 -a 2 -C 1 -t + ./build/examples/csp_server -k /tmp/pty1 -a 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_client -k /tmp/pty2 -a 2 -C 1 -t -v ${{ matrix.csp_version }} pkill socat - name: Run KISS Server Test run: | socat -d -d -d pty,raw,echo=0,link=/tmp/pty1 pty,raw,echo=0,link=/tmp/pty2 & sleep 1 - ./build/examples/csp_client -k /tmp/pty2 -a 2 -C 1 -T 10 & - ./build/examples/csp_server -k /tmp/pty1 -a 1 -t + ./build/examples/csp_client -k /tmp/pty2 -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_server -k /tmp/pty1 -a 1 -t -v ${{ matrix.csp_version }} pkill socat - name: Run ZMQ Client Test run: | ./build/examples/zmqproxy & - ./build/examples/csp_server -z localhost -a 1 -T 10 & - ./build/examples/csp_client -z localhost -a 2 -C 1 -t + ./build/examples/csp_server -z localhost -a 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_client -z localhost -a 2 -C 1 -t -v ${{ matrix.csp_version }} pkill zmqproxy - name: Run ZMQ Server Test run: | ./build/examples/zmqproxy & - ./build/examples/csp_client -z localhost -a 2 -C 1 -T 10 & - ./build/examples/csp_server -z localhost -a 1 -t + ./build/examples/csp_client -z localhost -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_server -z localhost -a 1 -t -v ${{ matrix.csp_version }} pkill zmqproxy - name: Setup vcan0 @@ -96,10 +99,10 @@ jobs: - name: Run CAN Server Test run: | - ./build/examples/csp_server -c vcan0 -a 1 -T 10 & - ./build/examples/csp_client -c vcan0 -a 2 -C 1 -t + ./build/examples/csp_server -c vcan0 -a 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_client -c vcan0 -a 2 -C 1 -t -v ${{ matrix.csp_version }} - name: Run CAN Client Test run: | - ./build/examples/csp_client -c vcan0 -a 2 -C 1 -T 10 & - ./build/examples/csp_server -c vcan0 -a 1 -t + ./build/examples/csp_client -c vcan0 -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_server -c vcan0 -a 1 -t -v ${{ matrix.csp_version }} From 147538afa9336a9b27752375c7f8ead043d34d37 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 19:52:13 +0900 Subject: [PATCH 050/348] drivers: eth: linux: Fix empty initializer ISO C before C23 doesn't allow an empty initializer braces. Add `0` to silence it. Signed-off-by: Yasushi SHOJI --- src/drivers/eth/eth_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 04ba7fe98..541405f9f 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -32,7 +32,7 @@ int csp_eth_tx_frame(void * driver_data, csp_eth_header_t *eth_frame) { const eth_context_t * ctx = (eth_context_t*)driver_data; /* Destination socket address */ - struct sockaddr_ll socket_address = {}; + struct sockaddr_ll socket_address = {0}; socket_address.sll_ifindex = ctx->if_idx.ifr_ifindex; socket_address.sll_halen = CSP_ETH_ALEN; memcpy(socket_address.sll_addr, eth_frame->ether_dhost, CSP_ETH_ALEN); From 8524721a96a9d8f0887fab8048b75e7a92c8d08f Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 19:46:04 +0900 Subject: [PATCH 051/348] examples: csp_arch: Fix empty initializer ISO C before C23 doesn't allow an empty initializer braces. Add `0` to silence it. Signed-off-by: Yasushi SHOJI --- examples/csp_arch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/csp_arch.c b/examples/csp_arch.c index 6be5d827f..75b54db37 100644 --- a/examples/csp_arch.c +++ b/examples/csp_arch.c @@ -17,7 +17,7 @@ int main(int argc, char * argv[]) { // clock - csp_timestamp_t csp_clock = {}; + csp_timestamp_t csp_clock = {0}; csp_clock_get_time(&csp_clock); assert(csp_clock.tv_sec != 0); csp_print("csp_clock_get_time(..) -> sec:nsec = %"PRIu32":%"PRIu32"\n", csp_clock.tv_sec, csp_clock.tv_nsec); From 004713db1b084a65c75e794ac27884cd047a9096 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 20:43:54 +0900 Subject: [PATCH 052/348] examples: csp_server csp_client: Fix a function with empty argument Add explicit prototype to function declaration to resolve deprecation warning. Signed-off-by: Yasushi SHOJI --- examples/csp_client.c | 2 +- examples/csp_server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/csp_client.c b/examples/csp_client.c index d33bc4b54..ba1d78ae1 100644 --- a/examples/csp_client.c +++ b/examples/csp_client.c @@ -64,7 +64,7 @@ static struct option long_options[] = { {0, 0, 0, 0} }; -void print_help() { +void print_help(void) { csp_print("Usage: csp_client [options]\n"); if (CSP_HAVE_LIBSOCKETCAN) { csp_print(" -c set CAN device\n"); diff --git a/examples/csp_server.c b/examples/csp_server.c index 87c1cd7bc..eff0d8120 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -113,7 +113,7 @@ static struct option long_options[] = { {0, 0, 0, 0} }; -void print_help() { +void print_help(void) { csp_print("Usage: csp_server [options]\n"); if (CSP_HAVE_LIBSOCKETCAN) { csp_print(" -c set CAN device\n"); From d29df2afa6e1584a04ce72772f4aed242ccaaa93 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 20:35:33 +0900 Subject: [PATCH 053/348] interfaces: csp_if_eth: Fix a function with empty argument Add explicit prototype to function declaration to resolve deprecation warning: csp_if_eth.c:80:15: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes] 80 | void arp_print() | ^ | void Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index 000654292..711abe831 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -77,7 +77,7 @@ arp_list_entry_t * arp_alloc(void) { } -void arp_print() +void arp_print(void) { csp_print("ARP CSP MAC\n"); for (arp_list_entry_t * arp = arp_list; arp; arp = arp->next) { From 65c4619c242a4b204ec5925976709af562ced0e6 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 19:52:38 +0900 Subject: [PATCH 054/348] waf: Enable -Wpedantic flag To ensure compatibility and avoid non-standard features like GNU extensions in libraries such as libcsp, we enable the -Wpedantic flag. This flag helps enforce adherence to standard C by issuing warnings when extensions are used. Since we rely on the token-pasting operator, which isn't standard in C, Clang generates a warning for it. As this feature is necessary and simplifies the macro, we suppress the warning in Clang with -Wno-gnu-zero-variadic-macro-arguments. Signed-off-by: Yasushi SHOJI --- wscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index db0857178..0ab81fd63 100644 --- a/wscript +++ b/wscript @@ -77,8 +77,10 @@ def configure(ctx): # Setup CFLAGS if (len(ctx.stack_path) <= 1) and (len(ctx.env.CFLAGS) == 0): ctx.env.prepend_value('CFLAGS', ["-std=gnu11", "-g", "-Os", "-Wall", "-Wextra", "-Wshadow", "-Wcast-align", - "-Wpointer-arith", + "-Wpointer-arith", "-Wpedantic", "-Wwrite-strings", "-Wno-unused-parameter", "-Werror"]) + if ctx.env.CC_NAME == 'clang': + ctx.env.append_value('CFLAGS', ["-Wno-gnu-zero-variadic-macro-arguments"]) # Setup default include path and any extra defined ctx.env.append_unique('INCLUDES_CSP', ['include', 'src'] + ctx.options.includes.split(',')) From a0ba5783024af06156d686bf9fcedcdd73f560a8 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 19:56:35 +0900 Subject: [PATCH 055/348] meson: Enable -Wpedantic flag To ensure compatibility and avoid non-standard features like GNU extensions in libraries such as libcsp, we enable the -Wpedantic flag. This flag helps enforce adherence to standard C by issuing warnings when extensions are used. Since we rely on the token-pasting operator, which isn't standard in C, Clang generates a warning for it. As this feature is necessary and simplifies the macro, we suppress the warning in Clang with -Wno-gnu-zero-variadic-macro-arguments. Signed-off-by: Yasushi SHOJI --- meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 055a87d38..29edb7327 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('csp', 'c', version: '2.1', license: 'LGPL', meson_version : '>=0.53.2', default_options : [ 'c_std=gnu11', 'optimization=s', - 'warning_level=2', + 'warning_level=3', 'werror=true']) cc = meson.get_compiler('c') @@ -65,6 +65,9 @@ csp_c_args = ['-Wshadow', '-Wwrite-strings', '-Wpointer-arith', '-Wno-unused-parameter'] +if cc.get_id() == 'clang' + csp_c_args += '-Wno-gnu-zero-variadic-macro-arguments' +endif subdir('src') subdir('include/csp') From 7ca7cd53f05e412a327d67287289951136c2b428 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 19:57:01 +0900 Subject: [PATCH 056/348] cmake: Enable -Wpedantic flag To ensure compatibility and avoid non-standard features like GNU extensions in libraries such as libcsp, we enable the -Wpedantic flag. This flag helps enforce adherence to standard C by issuing warnings when extensions are used. Since we rely on the token-pasting operator, which isn't standard in C, Clang generates a warning for it. As this feature is necessary and simplifies the macro, we suppress the warning in Clang with -Wno-gnu-zero-variadic-macro-arguments. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17f8373e6..6b5156a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,8 @@ endif() add_library(csp) set_target_properties(csp PROPERTIES C_STANDARD 11) set_target_properties(csp PROPERTIES C_EXTENSIONS ON) -target_compile_options(csp PRIVATE -Wall -Wextra) +target_compile_options(csp PRIVATE -Wall -Wextra -Wpedantic + $<$:-Wno-gnu-zero-variadic-macro-arguments>) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release MinSizeRel) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) From f74ee417ea5450254bfe7ef0109fe62aeda367af Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 1 Nov 2024 21:38:35 +0900 Subject: [PATCH 057/348] waf: Update to v2.1.3 Versions of Waf prior to 2.1.2 had an issue[1] with Python 3.12 and above. This was resolved in v2.1.2, and we are now updating to the latest v2.1.3, released on 2024-10-28. [1]: https://gitlab.com/ita1024/waf/-/issues/2441 Signed-off-by: Yasushi SHOJI --- waf | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/waf b/waf index e4c12266b..9eae75eca 100755 --- a/waf +++ b/waf @@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.1.1" -REVISION="073e1606e6ae84d01a398086bd4053ad" -GIT="0d2f819d1e1127a8dc53fd61755ff4fb065bc411" +VERSION="2.1.3" +REVISION="9bb4ce978ce08636ef790135d3c297a6" +GIT="d89cab923fcd82a576a8fa44ae4a01f0136854bd" INSTALL='' -C1='#.' -C2='#&' -C3='#$' +C1='#5' +C2='#1' +C3='#-' cwd = os.getcwd() join = os.path.join @@ -98,7 +98,11 @@ def unpack_wafdir(dir, src): err("Waf cannot be unpacked, check that bzip2 support is present") try: - for x in t: t.extract(x) + for x in t: + if hasattr(tarfile, 'data_filter'): + t.extract(x, filter='data') + else: + t.extract(x) finally: t.close() @@ -168,6 +172,6 @@ if __name__ == '__main__': Scripting.waf_entry_point(cwd, VERSION, wafdir) #==> -#BZh91AY&SY,•zx;ÿÿÿ´#$PÿÿÿÿÿÿÿÿÿÿÿÍà(B€°…0u@b(óÞ鯍#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$÷§l§M²ŒÆÕµ»î÷e±õ…ZîËk×{Þó4ÚmlÚ–ÚzÚ×Y+foZû:÷{£k»Þå.Ië«–ÔÕ¥3ç¾÷×rõ¯{.vÓ#.A×E;³Îõ\^ÛÛ§M8½ÛlÒíÞ¾·||2^½ó½ÓVO]C¼×ŸKÞ}ëÛzölvïl¡£éÞ±¾v{âù‡^öïíÞÚÛïwÝãÓîeJöe¾÷×€#$#$#$Ðϱ  z–#$Ì”Ü#$Ñ.`S]°Ó¹´:¦Žã¸Úš#$èèwa¹­néè§IèØ-LØèW ±M»‚†T( ITªP=Ø:ªR*¤@(©#$ÐBB›ovÓÝà{ÙÑ>ù½îë¸ù±Þ¯5ª{zë›ÞõY’™¬©FÙ®ì%)]–¾ž×G<ûÝàmÞ>û£¯¾ßfúßZ6ò­÷<Þöïw=žû¹ç¯·w¯½ßsï¶÷4íõÏò÷›Ýums{î»Õ·ß_xõ±º>½®«¡»( @vdÌ#$%NÍÝÝ.õÜô×w^çw,ôãÍ¥°·nr5†­{1*•=¹ÊÆ€UT ±¤@ªz@{{Þïa¥+ÛÞ»Ô÷wKÞîl#.i »ÞgÌǧ]/± ‘m¤"˜õ÷¼ûxnÅòÃÐ4ÞÀîÌØ¾wxöƒ¦¼ñ×#$}óÖp[ÖãîÅö£½Ï|¾vå]#.¨§}÷žTúvçËÔnEí·¡Ýå˵½qÚµÜÝÓp¹tæÓgvwÛ{[Ofæsî÷½.wkë{e{ÝáÅo}Õç—Ëmµ’&ÀS}¸UÝ5Óç°§Â2ûfÓŽ ”zjOu×3Û»}ð¡[»nÉW»»ml|óo¯;ÛÛ§Ü·v}÷Ýñ_n&=íuk-ÜuqUˆu«Ç¹¹“(äÑœ].›Ð€9ËÏ7¼=}ÝìñëC`:2#$¡@¨©z¦¤WZ[îóGº­J[4^(õo%ÇNš™c=ÛY×[ÍÛÛ³²é¢œuÊ{x÷š(îõÛ¶‘¡hìÍénnÜÞ#$#$ÎçS #$U^‹ÛÚËsSxó»1”Œ›××½}ð{·®ì¹=Ü[›mr„qÜÌ×]_7ÜŽlÚ*úfÂl9ȈX®g¶håÞ÷×=î{Ì{¯€J.ÁvWŠÜõmÔ{YÝï6÷7S…^ÍhÕᧉº:ûnFúÄÌÚß;»w;}ÝÞî_ÜÛ1žûx.÷]Û²î>.O[2œöòúûïªÑšÐ-˜#$>öë¥Û“(o¯}}…Vº¾¸.•<õYÔôml·-=š·Þw´öï¼@6u®CŽ”º÷nñÐÓ4î×½íÜ»ïÆ½é¼õÖ-”{ÉÞw]º;¹¯—0Îò·œõ][t•†€ö{ÜgmÎë/^twaÞæ0½á½îªÑƒt9»7¼w—½ò7Ø£ÜÞ@ÓÐ8vlõmÑÞà#$ì{‰5vëíÆ„€ì)ÐÐ+ #&i·°§{¹nØ;Ön¼í66êRÈog»zÞéÛn»¹n·Ðï^:@(¡Ôë#$âÓJÙrkb¾ó9ìæ•žåol’Þã^ =éÛ¯l>íÃÄ=ïŸ+Ö`^Ô| z-ˆdi½}Ï—½p*¦9{³é×ok-ÕcÓÖO};mõS#&×}¹v|]|õ.ò{Ðèwƾh€ Ä#$&#$†š2&™4ÔÚ&#.cHSÔ=Cjˆ6 Õ=&Å6H%4A   d&j??SÓJg’‡”hxSÔzƒ@Ð#$ #$#$ ‘@š ¦@A4Ó*Ÿ€7’m55µPhzž P#&#$ €#$#$#$ =R’ §¢&Ñ='¨h§úš™OjOPÐÐÓMF†¨ƒ@#$#$#$#$#$#$$!#$#$€ ¡¢hÄM ´ÓD0™0šOCI²¦#L€#$#$#$4#$“Q@@#&§äÑSñ4Õ==L£ò™¨Ó)é‰éOÔš#$#$€#$#$5ÿ¤?Á ÌIh QR ­D¯RyÌ‹bD&)ëU®ê´kei=µZ®jÚ¯ÜT#$¾Bi?ñcÇøþz\üúŸœ{›–õ£wмV'÷Œ8–0“½ºrf­Õ=Õ;çÒ{Ïå^äB®#$6DBAð@MÈ GC—:bZy©§R‡‰…OTðöïWW=."1…à›ÅââLUÄDU^&/¡0ÇëÒ9üÄ?á†ÔV͵6Ó6͹]ªÒ¶¬m£m¬ËU’¶ÚKUlkm¬T[O±Ö¢•U¾´"ŠP$ÄÂ$DlEDM~â­ÒÖò¶¬ÖÕ캯:-€dÑBEDD¥AFF"ÉFf¡¤“M‘2FE’£m3P‚XÈÚR(¦Ú6&h ’˜ŒÔÕ#.Š£`жŒÒ&’4KD!LŠ2R&Òš #$JÆ¥ M1eM£DRl–¨£K-4Ò´b!e¤6h& £31‹IAj ™±¥6C¡6cI@¤4Y²Ò1¥£SM‘5¥kf¶Ú4ÌIc3&dˆ’ ²m¥¶šVÔ”•šMM¶3jm³52Òbf"ƒP%$²¬ÄѤ¢‚™M‘R6„B¢Í#h¤ 4TH!`±I³4ØÐTÈ؄!E"Hˆ’dFIcBd#  #&³42H“ #J)™²²Á™¢DÆ’ÈÄRɶM(˜IdjHd²‘£iM0M’JLD2hi™1 ´”iˆŒHI#.Th’,E2•%3ÆŠÉ Ø‰1M Í‚ab6’ÒT™JÈF€É$„›$ÚK‘&¢D£`ŠE˜#PÈbIL# †)%‹2£E±‚’¤‰&!–” $["H²[bË2ŠFÌ’‰²™2¢d‘J3df ›f˜’e"a²Ñ`$¤ƒJhlb#R²Y#b‰E!2)¤˜‚j@F,È4”0‰Y”¨Òf‘X  Ši¢‰mL%€Ä¦2Œ›1(²RHš ¦@ÐÔXÌ$JʘØ6ÆD¤™Œ‚1`‰²C,%²L´Ìµ˜¢©J¢h‰™©˜…"3hP¬¡Š’(£)$›$ccMM"Ó4X±¤32LR3i°ekd`ˆ¢K%š™4Ì‚ÆQ†Í¦ÅjT””Ñd,Œ˜“"›HQdŒRŒ¤…"(ت)$Ñ$šShÒj6I##&1°&#"f£M2›C(ØÌ„Ó2ŒFÆ‘2dA)²R˜¥Y¦BZÍ–l˜ÒX ˆi,˜ÈmAPHšÂ‘&BŠŒÄƒ†ƒRXÔY¤iDÑ+HšPÅ%©"ÂJe&(Í%‘L)f)Œ!¢ƒBl™HÒ%V›khÁ2„ÔÍLŒM#$Ó""´”Û–h³šFRY³dTZfÑLRSb!J"i!e6¿ˆÚèe©ˆ‚0ÄeF(ÆÆÛ¨¨Ù2”Óa#.T“F’i´…hØFÉ5YÚf2Œ‘e0„©#Yƒ12•™lÍh£’–£j)#.Æf˜MKLX°±d$ËM¦L…*JѲ­#F¦Í­“,Š¥RÉRÙ±˜ÌÕ±¢ÈŠÙ4•„©–M¬•e²”¥™h*F€ÉHÚ‹ÖX’Ò)µ¨5¨¦Z2TVM%¨¬lT‰F´M2ERh£DQ­FÃ-XØ#.É“#.0mbÀi€”R £ÈZ&%#.Ò+¶4m1+¶H±¶ˆ-mRµ³EC!2™©k&‰*F)&›HÙ4FÖ¥¬‹c¦Ól²–ÌÙbV¥˜‰-´ªYJjšš,BÚS)¬Ø°Ñ*RÙYE!µ–cI ¨Ù"B-"ņi(b$H†ÐbDÆ&LÛPÍ –(dª6’i¢Œ™,”Y#&–)ÙH"Yd¥JbP¤±³LÐ1!FÑi’#6›IŒcll…0ŠY’‚J HhÆŒ”PÓFMAb1!©1$™‹IL’”h,©@Õ S#$dhÒcfš‰6HME£!Q™µM2’MFCHbÊmˆŠa¬š4jM“aÙ#K £$j(P)™‰4h³)S))e+2 •Œ ³bÈlC‘lQª6[FhІJRl4ÅKba“j(% 2Æ”“bÉRT’†5)‰¤È™¬%)¨Ö#&±(i,›F‹b&šQIi‘µdlЩš e,(‘’T£4‰’Š¥#b-šM’˜…‰‰’’$“)&F‹E°•E¬bÑ™l4˜Y$С²f61MFÅQK6,†e$(dÆÉ¥)*dEX´k£TI¢„Ë5EŠ$Ôš£PdÀ £T¥#V-4¥™d(¦Q$U2M¢Õ"Äe5TŠš+b“%$Y4Ì¡jDd#.2jlC6DÊÚˆµƒEL‹&¢±†˜³J4ZL•ŠÛIE%²›dJÒlCFT‘e*4 L66J*MXŒFÙ"Ch¤ÌÁ¬É‚Á5SS63Kcj6e²U$i M$ÔRE…©+ÊIcdÄQ¢„ˆÁ’V¤E–Z5 ­j5Q¶MFR¤¶È‚£ljJ,R)¡2dfA1M‘”53fL¶Œlm’ÆÚ5&Ë6²[DËR-DlldЍ¦U4­Š£bÛ´™Œ¥–k+ÔcXÙ¦¶#iR¢”Ô[(ÌT#M&&P,QIh²kbŒ²¤¨ÚÅ“l¤ÊѢ؆™6ŠLh±cjªmk‰•#.‰‰(ŒT0´›)M$¨ªaŒ“mbÛ™«hƵ–’¦†ZÙK&Úš›d-©4J±4eDhÈÚBbƒa¬Ù)¼»vl³QlHi‘DiÛ! ¤b@¾?ÜÃýò»'öcýWl5ªÏú¡{ÍKõ4>¸ÊgIóåMíÏõ¿êõ1 —?½FATÎÛ¹†ÈØÓ¥Cý—QfD·ûhÂL'eR ºZJ£{ìeŇ ÿĵ3‰ºY—n÷V—Z‹‘AIpŸ©rD°±-0Q±ÉÚo‰ì~Ïìù^»ŸñµŸòÆ^Ÿæõmzÿ±V'¹éšÊv6˜êUÑR˜ÚÖPÿÔÑ­Jî‘@FzxàšéŰ´­fÖ#&Gì®Ó»5¨±ÊârNùkø°Õ"M#&’6žL°Q›f=4ìˆcm¡²O;¢ZM¶š$D"ƨ¤0mxNwDFÔ£+ÖØF&Õ]r»ÎìnQÒ¹s&hªe4Ä^oéP&h?ëÔUß%Û|œÅ§½×lz•Λìøã.™9ŒµGϹA_]JRL~ÖŒm[÷_[Ý™#&(‰_ÛE&ì4më­–ªª,?Ò…‚öjnþ„ÕqûjK\Œô¯»9掯††œöǨɘÝWT-@ýQp›&AÝuò²:4në°µÉ2ƒ2š‹V*‚…áÛ^?¢Ë<Ï»#.qZ¬ÛÄÔgvp×DZaCG/ï‡Ù5 ¦yæt½rô²ü\Añe §&¢”øuû/ÑÏKà`÷'nínœšüj\×ú‚zm\µõEO&íö/šöø9i·-¸R|”žmW÷À—q÷Ùe³,YI#.N­+ŠUDqYé‹ûŸ¯:«–Eë­:¹-#.‡Ó7pi)$WÞÊ÷„Óêý^4dˆlr–ÿ¡‡§®Þ”cs¦?Êu†dÅ/¯®¾Þ¨Yè|h-#&ó[`¥ÕìDl`˜*P¬DÒ©tC)ZªbZLñ©W¶•ƒÑ•¯…7eSZP¨( b’›[A„[F£T”tå`²„Šÿ±A|Z2¡ZÐPÅc÷müVNÌÔPnm(`úþ?Øùµél±Fi¨¤øwnW{ºµº]6"û<ê¼—ºúyÞË›Ún{. Es\¨Ûbõßvõêò!¥Y5k¹bÂÃðQ£Q}tRÅ (*0ãFšâZžýÝrŠ‹FÉ­ó~&ñJ°Džm#.¨°Xê— ‡·ÇM°j_9N)*-Jˆ_:[B}T›w„#Íkf¬ûÐ ƒúÚ¥TAX‹BcKÖu¤aç YÖÁ¸Å<{Rã‚fX×:\Ûäñ·\—.˜åo‹Ä‰ÝD‡=iáPÐõ×'mÒŠe¤¶’Ò‘]»è¹ùU©á¡BïáYNNYzwY`Ål&¢ùÝt_¾¼_-&¹x^M?|Q£»Dbá‹mZâ„ÕDÙÙEO­#.á\¶*ŠÄêÃÍ–±âÀ݇„"òalž|™~#^pŒc>ûáN¼XtA)°›\»Ýs,1PìaÇoŸðsÚx†ʪ»œÀÆ5Ú@æëI¿81x´4cW·ÏËʱT™3 RF”ßwqÉÕX%’4PTÁŸòÃ.t¾<¨ ¢zU^ÕÉ 9ª§VY”¡… ¡ïj")\~.çëßÃÂOœ¹‘sÒ*a­06§'4ÛL’g¬¤— ßyq!¶‰gºÃêËx ˜=Äx=Ý~Mqxé‘­Xß²ôÅDgFnÃVM~u6LmGS©N…eÎNNMjR£ÞŸ¯óǾ¸:˜pZa?X®Â×£â_ãŽ+’¹§T,Îx“ÃYÌŸSé½I4B(v²~X¸¬‘j8!<Øj÷žöÉhq³ 0z ó;ª,Òimø7bå”/S¥#Ç…‡8qå0â‘¿›t¡Ä󗾿Kóú¹Õïb°;!҆ú0‹†Q“ÃZ=#&]†_µXÁ·õLvG©Ûð üúÜ`n&Åï¦éãë+`x4×2¨ü¾¼c·2ä²×müm¢”g¿¿\äzT¤tFÇ1G×Ã$oâš'I×{¢2½ ztÂ:E&©¯…Ùž–ÕÁvÁºC”‹–rC•Ää#×Û•cNAe”E$S·”ŠcðËùmS8 SÇ—W6“" =Þ²—qÒI–|BÏX×͢ò‘Ø"}l)fÉÇaÚ¥ï¾Øá¾zSGKZ®#&þè[',nMŒPlÔâí¢ã;ÒïFÁE#&*ƒ´x72ž.èaTkðÒŸCF¹‡“×oM¬o¼Ç]Ÿ“¥’H_wHTõçJO*Œ4ÚÇãš7#&Ñùfï£ùMj™>äP¨^^ ßSÃY^ºôß)®#.Ò«âQK%Ç“„ ÓPÆ'x¨1Ù1ÛN€¼9#&BÑ饧w9C+Žòµ·ßú¨½lÆÛ\VƒÃ†mº¢"©ZËê©òL¾í9·…£…œÍjv׺™ϳË!²¢*nÆþÙ>}ý>:ëfüº%IÝ¥èÌÆDn´SMKêq#.åu뺟Fã/ÅÕÔÛFÚkn f8ké«ÝnHÜlÉþÝØ£!/Êÿšë‹÷ÿÄëiõ~·Ž¬)~梾Ԧ}ÌO¥Ú¿?]â#ADj?3s|™êßÃì»dûþ»U¾•sJ£¹ þtŧ¹÷ß: ¾z•’Dßão®cº[º$¼8ŽX¥vÔ¡#.©¨® gÂS>U5¿•œËiKÎ J#.*—‚”ô¨„£w)ù9q-ŸÀî=úQ‘¶•ëEFvÖ×'Ñìx6±ÄÏIXϾb©©ÓÌàÅÆÒþ:ñüqp3ØH>"#&Œ¼µžâ—T¾N­ï7ßÐÕÉB}‰LV?£4dfýt¸úQH‹§Kã(XŠE# NuwáE¥ÏÃ{‡ÑÄÙŒûûº‘ß·\ã‘Æ/Ûg‘¢¨2œôçƒÒ¦[Žî—Sr©'uJg¬£IøÖ3ß¹mvÖÕÃÔùwš~·!Zç32 ¯¯„Ÿ5MÑÛdTÁ‡3«é‘i“¯ZfDÙïj>IŽöqwxu˜vSЉ“2ñ£M?Hm¤TÖüóø#&hz‡¦h†;¨¤x³¯^Ý®~HVÍ5¢Ø U­%œ4˜4ˆŠ*,¦Ñ(-·¶ªñfæMY-Ui;ô¬CªW•œ’”šQÒölzãxlÇ:¶г#.VÓcÈäÉ®^YwŠ*Í4¡~têâkGÒýWY¢ŠÖ‹Xô¢n2_£Fì¥HˆÅç@R.mo¯¦g>u•ŸêÖ5MؖϹe(+vseEçŒuòм5”+¶& „@é§ÝíÁcÏ~0Z7 ªe¡PŒoF7^ÇÉ?¥Æ…ÔÆŽÇöíýÂ.3˜ÆyÑ¡œ;²>úræü3T?òÁkæí˜?‡œê˜Dò|¡Ñõ]òô¬à5"+E«°èo¥åá?ûާuÊÝJ×ñ̶PcËÂ"’Š'<àoU#.éÄÎÆIâ¨ôISpdYŠ2¶b8ª@cªB¢­ÿig¼øèsÁB(ª76»!L‹”ù— „.DV,b#.7SDH‚Õéað§¥Ê"t¢PývùÊ ‘UBÞÕo=11"ñ'J+lM¦ÛK¬g¬çûçÞÜý–dðÑ9:EdQâ{ͳ½†W÷9š¥¡Ì^å #.e™RÇvrV•üÚ¡›á°bñsùJ”ÛÂúÓ=?~\åe\3}*–a‹+¾°Ë_ rß94¢“ÍÚ²£bá¡=8O„c®‡¶D­åÆÀ·Mùpæ?7¥…Bù¦'c}|bSRãwM¬žâ_õŽ“U&ý¢¤BÒ{ÇÛ/‡UKÁÞ¼>’uûómý mœÂò*õFãRWSk>É(Ž ª×4ìÖÒ)g%Œ ß\ÌU4UuÁAºÕ|¢"‘s¸9§ÓóCZÍžƒ¸Ó®©Èä¤F¥â_?ªÇ½¹£mó-´:´uÔ=ÖyŒã™‘Ãáô¼Sì“u‹ËÇ¢"uÄž^léRÒ|$ s0õ’’kÒƒ‰ìÛÝéçË1ã2»H5®‹ÛÓË'X&Z`)9~º#$ðì¯#.MÚcU#.µc"cN%1öçu‡µù°òà×8ެ­ª™#'K“©~¬ÕÖRß²•—Ûé•åe-Aß÷hc•ý‰Ê&ÜÊÓ^Âë×EGæÉ¾)Ö†Û“œÂ¼ùAVj%äþnw™¨áˆïcMæZ?wÑ:bòãõ™·‡6ýùò§¾Æ"Árö7ÐúvPqr¬ótOVµ«óÄßZ¨ÄÂõ˜ˆ< •”փܛùU"““§4ž[¬z¢>´èJ¯0\or[ ”Àº¼´ì 7y½»y\Ú(–ÎÞ5#. FG¹›:ñÍ’µ¼`¡‚¯\Ð^¿Å^x<(ñf`¨ÅYÝT‡öu¸T‡wdq3•„Þ6:KéA¥@¾ÊÀðIIª2ðÌX‡d"G?Nï”Ø<ðˆþ¤h·ÊèçX ´ÖKžø4ÊA#.*¨>ñ‚¢•§øÔl•q’p›ú„Åþ~>Ve÷í&PpÈÖd[ï ”þXà'1éR#úfÿ¡^w€D#åùå$¡:I/ÆÓ#&¿×zðŒX,jÃ÷<ƒHßh¼½õ‹Ò(,lá®M¬«7«Tüê”ýúÒmXtÕ¬`¤ÃêÒŒ&›e1Úì¼q£îLb(‹>/¯Ç¾ŠýÍB¼üñÉü #.Áµ®†ŒdR 5Ä)#.?™È¯Œ]^µ$8»­ÅõT"O±9¡ŒNibr×Üwt­þ Ó·Cu¥Ã‹Ñ9"_ÛnnQŠž#.4Ÿ+_D‹xÝ{ýU'4ÀŽ ˜Na%ùïX`jÈä”É:§Á#&ž.Y|+D£\Ù~V÷TGo(Ê”ÆÉ³o‚vM6}”3ŽäÔȳ‡XÌå¼#vÆFœšµ[lÇÇ}U‘º+ÄÁÝ7ð¤ø·kZÝØ¤4’ï¬F,‘b¨Ó”ÚNƒM’DR¯¡’„Pâ¾¶E‚‰`©iF8⥖,ä­ïWﺸë{[¾`ª>&0ÄÛ#$ÍâqÊNŠy˜2y´#Bé#&%⊠}¿%Ï×¥n«rÑ¡¦&"¼–ÚðøhdÊßµ·kƒäûÝW6˜º>c°‰·j¢å ~=0©Ì‚$#&±{د-Yäj/g®;ŸýEÓ£‰F,j~eäWåTe鱘$dÈG‡ØÔ.r4î|1´5YÿÂv­ð8 $§qm}‹´;sˆ€¯­ÛSj8{®•hΕD-áü^9ÆÉá™ Þš#.åUÃ%I†úß’utõѾ„ôK`,“,Ù…’¸J3•NmïT:5íâUÃtIeöþýç°[Zit‰Y·ñ¹Žü´‘ù­@ÀmÇðÊ€]u\gç]ü®:¼K@Õþ4SCWèðà=ø’š&U‘•…#®·H–ø¿ªÇOiŸ×ÇÛåDéEô¸|QØt±1åQæûKãºP4ULɱo“cÞB¢ òÀlH¿÷Q×ç•6ÇD‚é!#$³% y ‚ftªù¿ÇÆÊróbJ½Ú¸;fû¨8³ÐhAUÛTp å bfšR1ÓÉ1ß6£ù;›EÅ›'·X6µÏÏiÓç[Kë¯F2uÕf\û1­,ðçšÊQ]5_óE”w¬#.W•3¾Òù?i$•áÑ:ós=]±p/ ,äïT¼^WJvóïÖ°K½y¾Ü5a;é¥8˜ñß7åQV¹ËÔˆaG‚唜5 Ô_7¸ó†]‰kgŸ$Õ÷>;šôÔšýÞ}ÎÕòäõ7’¼ˆôZSÃôõšSš†–ï@½ûé¿uÜXþyÙ¢SIÈåQ‰gÎÃŒƒ tûë[ÎMæÃ!F‰õz„TjÕ ür´—Ytmú{™ôé¶ZŠ#.ÕƒÜ9¿Õk¤JÂ>ã–Ɇò¥™v Õ@Š °‰ŒUèð£Á.þì>ý‹\ÒùÐUëùÖ¤Õf&0(•:}ø¤3a˜Jí35ìÀ1Dyí·ÛõÜò¬?Lo?¼W3­ÝŸ~_·øi¬Ï†½xçþ?º§ý‰òcêH#$¢º&M¿sîýöy:&­A«: ÆÖ¡ðØÚ³XL­Yâ²¾W-Óúïí% ⇠ú¦Ïù#&Vv†0?¤³3Õ/#&QËWRŸ_Ž›÷küÞugeÎp÷»á§Xúþoaºë2Õ¿>ù\ÇöØ=Zžõáza¥Ú¨£p|¸Köá}eOæw @é i¶›¹-6#èå×nÛªð û±äåQœ*¾ž‡ãzwVßóB $$#&­|P[ªì„Fùä&,ãòû~Ýkö—Ê#.f» †0ƒ?õ#.ƒæ×ë='F¶5ÒnD+‡Ø~¬}O#$á¹k6J"ªMCˆÔ•³Ôc;ˆ¥I€x\ØtCÆäRê ’¬Ñf,©Ï"pSe[Qÿaþ=XuNn…ïóŸnvÿ=YåçÈVÇÇv ^ñÅw+5)‡ZÈå0ªÞÝí—è³@…³·óS SZMÅï0d©FC!½Kº…Xþ½ lzO%³lÔ6[J¥C‘‹²›7ûøZÂO .xy Y ³ªÂŽÝ-Â2DKÊ1%5Ú4òlYYH qeÇÞe’Þ?Ô÷N „-³Æ¥Y*î»ÆXŸ]— ðs‹I©‹#É)Wæï·@Ðè#.€¢(D^¯r û|ðr1fQTz‡9ë éÅDäJåÉoaÕ‚ ¾»–/#.¤Šà*N·?—bæyzë;Ðu2`L -z”Ù­Ãè ½µd•‚»á¥µíljþÚÅ…³®¿;´…Ûìf#Fíg“´6X$á)]ðã.|Ôý+XDèÏysœy^Û@lH-%n|ß„YO‡iôÄžäÅÐkSßü¿f#&Ö½²ÚG(}ÆÝAÍõ€ÖÂŽ²X[ÈN‘Jé–šÑ&¤(7®Æq±TXª…$·;g@!Ž#.%h³Æø^Žæ"ö÷Þ.¸Uñþ9%ˆH©Û3öÊ8lˆH\ÍòÛ÷°–Ÿf-nõ¾ò2¾.¼ävΰœÃO&O©gY¤Àü•&‰5îÏÓ;<|ÏÚtü@á?kwºÓ‡¯™[Êbz(ß%k$±š¹Âþº4›ã2‘—Œ[ý„ÞÞœ4ãYc/#.~¡Ê¤½~¥ÝèMü'%NãØß¦)¾IƒorËV©‚©œó¸7/ŽEì¼kêã‡3Ïø¸Hk78Ô›ª”Pí°EGÆ‘ªúC{òŸ*oK¿0èC›Hr÷ ¢7Ÿ±¡ $¾¬Áõym,ʹËåȪó¢×¶~[×ó^ 8Ï]ò¢÷B‘¡G¦YgY_žÙ–h´B‡B*R w‰Ï•¸ß˜þ®1º{,ëIÀíîðئ2«#¢;÷¼¢Å¸ïÜ%SŽ‚Ga×Ô½ñåöå Ö¿ûê`Y7¼cQs`hnêg…Ç|˜Õƒ°ïËTï2ÊýŸn¼1ØF(‚Â@žÜ5“˜ò(ŽÕ¥† Å#$dউð‚„Rdš…n˜AîY¸v{ã}ôh¼aÖ„D‘:o›±ð(f °Lc¾6ÂõÓçõÍ3å 2$‚÷ )¨×TëH£M"&rÑK]¼êªñ—‚HÚòöÏ[ËЃ×]#n‘r¸éƒ­cHã¼þR W·úëXr£¸ÿf`aó×^,Í…wˆÆTˆ°Š xfnróàmYBqüq´=äþ_øØ+d M…#¯BBDˆZwó àCú;3}Ÿ¦¹oû(Ýñ£öÞ~’ÿ„À}wÛÉ6uvç”óL@ LÂVÊV ŽÀ „º€È=ø#.°}è#&gÜ'>&¼¼CUVC‘¶èhRv»*>°½áåÃ@z •íQ‡NUáH¶ŒÜH?zÜ­1Æv»RÎeC¯Qä-0´±Bh”ku‚±GmÁ†`Nðá †WG;Õa¦:2 2{Ê*Õ¬(ò#&#.ì,*'8ê“0«Ý§:ó!Ê¿Ä|)Fíê>é;¢ñߤµp>Âõñ%àm»Om‡6C~¨üÑ+eI#&æ¦Ið¿Ñ€Ó¡[-‰”F`V‡6vN{Ê’ÅäªéµõAM©MÖhe#JJ¤áhE6-ò,Uë <÷£“ÛµÄàáy™PЙF”:ݸBÕ:b7aÆEÍÐkl“OeŒæ49„phr39z„)‡9FëaX”=U™‘Ô±oѨvá@ÍÙ•÷=ØKkÒ磴E) R±ÑØuø·¸£§[qÆ*’kr…¹òéí¯$z`;%åÖ½yªO8²‡ŠIF'¼½­à¿Fx\„@!‰{xŽö5ȳõô2“K61”³ÇÇðÈ^°‡Ð¹¦}Šzªuèú˜éÂfiW€ÅrÜû ¡ÑÎôÔäç7lšç|ˆO±E1íaf¼sˆNñMÑ@Ú€…bË-:F!õÝ®ï<˵ÝÝiã²3ÑÍÒ76墣GˆO鉳Õ8‹wé–™¿žñ__+å5KÒ:7E i€i¢4&Új"’#&¦©Š¬)ŒŠ2£hÁžÌöâDÁ¥BâR ÆãŒ0±#$z@bpj5½ÃPlžŸ æMÕéœôÙ¶lÛŠäF‰HÚĵùT`qÊŠNÒL’‡¶ëÓœ†Dµ¢ö}¢U*Õ/XSCï°ã]Ž"ÂKÊVÈbãlxÁ}·É¹»Rñ–ªt¤?r¥Ú¡Í–ûù拆#$ªÈ´ç#.¼dÌ*ꂞo".LFTÔ Flñ‡œv醴4(eC%ƒé #.Dú4üùøEå,±‚3­4(”¶×n—á× eIšç¿8VÜ?hǺ­#7èÍšá©2S­‘.ihÓliŠ …ÚÑàï‘Z«9Nn³†:YŠY*Šb›¡“{—G„%™Ô—z£m#—RÇØÒ4&Ž W¡¦¡Å1˜ÕbŒ{íGö² Èb#&ËÇLÙBë) °£F Ažîÿƒ= Øý}w|š%òìÜè<à¤6I)‚Å(PÂôÅ«ëWžý¶ä`ÕdQ‹F•‹{$«Ý·ìÁd|0ÇŒVǨꥌK'‰•âÜÞ"¯B×Sk$jkßð*I×,t¥Ûâ,“ªƒTtç¢È)~ÍlÀ§OȾHÝn{ï™\ #.ƒ ÝR¹0‰J•±fMýM¼¹VàÛû—ݘÓä^TNˆ¹ý}²[0J`lÁŒˆ`]äï&¬ˆ­DѧJf¦ zRéY!C!™NŽ´XCR@pdl£%"ŒtŠ(â1ïZÙV0Q˜†YøÝ €šH­(FêŽ4¢ŒK‡îßZâÅñëªesÝàÆ÷–îºô´n ˜±3-(˜j#&«CnFu *5 ÔF=3#& ##†˜ˆñd Ò]OÏÎãuã—1C3BobøÆP\pq!ƒÈLh¡A©ÈÔ×u5:W ³ÐLŒGt\ Ÿƒ:µç‘)`V#.z5×ð›ã áçk={ÖrÇx5Æ/9º]6r0jD&“òv‚¹G–#–œŠf'ò#®‡ÇôýŸ shïP’f :"–]e¥±áæÍô#&ð…‚â±^ ã¼»Öpf|ï#&ôçÎ8c¶)›lz܈ñëÑFß‰Ä øV$º†J'n×,5½G‘Ùçv)àûOS%¬)à¦à‡"•¯ó¹f¸ÓàÑñ˲>O•dq’ܳ¹ŠMŒB|¡òå56ü>‡x‡ŠÎR^²H¥$Z¦É¼NÏFmƒƒ[ÅÒ¹fã*"ÿ©©g§º—<V;ö|gòkg3/ÖŸy¸=îNcUFëÉÿ×þ¬ýž$ÉŽà˜6y’@Ê`ßæì‹·E–‰?Ï0͇zǘ|p-ã`ð5ê¼IìlE£e5P`³r2Ì­MØk|b‘‰Fªxq¡ðˆú«){lû7ïÆÛˆq׸»"³ož§HÊÏ:ÊýSR$ûbc3/Ï›ö¿^Ùòæø ú}œðs³V̶4&`ÐÏéº3mË“*4‘¢†[†*PÅùyJúðªßà.vaÄ!õ½²¨âœž˜ü+RcaáFöz¯„ª¸V(ÀäOÇü¬\f#&¦‰B–[f)yzcþG•ûŒèû`nP#$&L݆®ëÇ»w­íÆò3骶Óàå^·åÊN?^',jC£½õ-!|óð¦¦vÕdÖOýyÆ´lIµËrÖºT‘«–émܪº[M-ݦ`Ñf#ŠVc#˜5l‡{A¾weNðÐÀ lÌ$]þ›g^CÓlhïq$¡°ão7{74ÖÞº:1ØÎ 5e=¿XT>gÌöñÒ 'Þ ”€›¼ù–üÕºÌÄãôtÞ/KÀkrmû8ð@¿4hÃé>¸:#&g™ÐòsoA§žjô)vc©C†°:®ƒi XÏßwön¸qýÚttö °¤TŽâï‚£ŒÏ:z$-=öiäÞjäóCo_.ý?G>‘¯Ì(%Æ vr¾d?²gù{Óiì÷?¡éi%ZUŠ5…§õÉòA+2'a:Þ–ËþÔ—g÷CÓd§Æ~“â«ç#|7ÚٙN§Å#Üæ¯¥pk8é4¾||#.â[£ÃŠý`Œû Â=¢ð!‡n•ü?(qëøîLÀE¼]äÎ6wf§wYd…¸[ÛÓËtê~ééw³u’éò}rw¢=ò«—^Ãú{ÿF¾j×gp²#&ãÕ F0Ork`1ë_4ÃéY‡&.HË{$‘å<»ûý\ÃÏ¢P¢]pþºØöýx‰àœPïñ¯$Y,i ]S,Fì¸[èú`1€OÑîøü/Xž=”#.«Š)TeÍeªúÒæSNÉóuTDiÖÝu–ÀoìýZ¿B€šè(êÿ䀘¾ÏjÊ5&¿³÷ÿ?vŸ¸€‰ÎŠ @¨ K¹yìµô¿‡#&è}ÃâTxaÒ“ºûÔØïOÉžÛÿ.d©S"‹" *#.H£=N­S6¤²h“²¥‰Eúî3F›!z;nÖ6,·s®Ý*å¼ðÿ(P4—ö𛓸[Wý{? À´$îŠP#‹@>NúeFˆà7·lŽô´ CD16¿Mô»ÙF¯¹Wtíe-s\eH'¥]н©®ÿWN[gïÓ÷ìlý²°Qr‰hÓ¶uî£öF /·oÓüœOMò~ŠÄ&P‰F솨ãbÅoæ-UÍ~GÙO?‹Æþ•·ºé¬3£(`¦òÎHCÕþýX(ERL ]¶ô“c[¥_t«æ­^EPÀIЦˆžP‰ùû2â>ܦ^MÇ*¨ÛMˆÄãK0…Í5H[ñaO;v„ã"ýZƒQà[Zýæ}AN˜[Çü·vïDú‰ƒ*¾¯ôn77£,ÒûÚu/Κ/Ú¥)œ`øŸÚÒêXÂä…µ–KâØ«lÛýç£}®v« oµ@iÛƒsGÀÂîÆzÀE¥dþðšKûacOKì§[ÓáeÍâ ;…Š¢‚ ޳Ì&?0ÌØð$#.r–Ï:zúÐ7½£Y°o0‰¶„IοÎÂçötëµÇØúqBlü¯¥èô2T•5#.ÃÉ•(&,§•T£aƒ”£•DÔC*ë$|KKîdpúÇøß_lÿÏÁNNŸ‡}ÔöHì½KÈy’cæõÛ='gÒ)):d½É¿Ñ§ïa¦ß{}³E\˜”8÷„†½„åæ$F ²#.(`Ъ$'û?Êñ>€BU‚^:PÁ¸@×Åš1°oR}sì§¥‰&‹ ñßãEqehƒLb„‚a#{n“O_”'ÊC¬ÖÀðvž¯.ÆyLrŸ)ÞvÙaa‰×õi`,Ó8§31 Jÿ„•z`‹—Â#&–Ýtó"?¯¡íúvI*Aº+Ÿ’§œ»-¥§®mâλ.í‡wdó¿·‡-cýÃøhÒÀѸŸÌ¦þhw*M+µ²Z… PÉL!rK÷½ñ%Â1o#$€U ¨·äÏòšgšu뾂F÷X(Œ€xtä¢Ä¢å§«nwoÇ¿’^¯‡Â¶¯¤ã-œzbªƒÙèåû½«oªºu¦Ï¢]¶šã¿Ä‹ìõ6róÜÁ^»t>Y ²š.ÎÕÆÍzzŸê½žÞßÒÝÞ£øïݶ?…ºû~ÞÍÃVï–\Ûìµåz!ã~1ÂX8;UmæÇ›®VCã̼ü$Ѻ 2åÖðÎ#.¦í™øæä;ºKò+Iݤ\:tþ¦Ûíßì×ÙèÏ->UQ`w›-™éþ|þçÑààÚl¸(¸dÝ~:°¯Dôóy+m9íÇÚö¤GëOWóÉò#€eÁ 4Ÿ{ïŠöféôsc®³¦ÃmüGÅé»/›ô¼pSd„±å<ì1k­”3Ý*Û^®I6<Î׃í¹Sž{tùß&ÍekÒ>¨#&|(ÃGG¯Wç6»FI͆Ÿç;cj«w}¤n}{4Fë_Aøj–è]8‹Ü·èèyұѭÛùã^‰ió“¢E§—3x6ÛZÓU–vDõX/`6ò”§º°×ÝÉ[Düº{˜}1°oÕM1¨À*asž'èÞ#æ 0g1½ÎëW4¾ßSžõt击L]ÇÓÍÃM¾]üØnäôº‰·ró‘zýœ—Û¢ãéÈöÙNN-ŽízU 9È‹!ãdîßбߢ–»#ÏÑÆ®)"åðé™éß%b$h|¼U4ÿ±l1Ýîöêtàcus7[ÝÉ÷t¥ôÆ:Uߥã'Ä&±—ŸÑνŸ¤Óåñô‘Ù°åÈhÛ}'™†ó´SXÏæˆÇ·tmù 7"«ê®ûìÏóið×}œÚŸà;ö ¬ qºŽqöy„i©}îdøÇP`t}¼gosÒ.·ÝóKí´€Ê¨Æxy~0v“‹*oÂØ÷Ãlw¯°uhŸÛã}þ}–áE?z‰¯ÏTåÅ{þŽO#&}Yÿç:³Úpüý‰ãüþOtg§ö? ¿gçÚíÛÎÏå¢s~ÏÞ†0íw:=š{çÞÐáÇÞ{M=Ô¤ÄTpÊ,½¼‚>ý[¸h…ÅÖƒôhExå+ˆQÛ‡žŸe㥆½_o´}:ø;é}0vý{³»¦mð4µÓЯÑgÎÎêù“œ—ýγÆô=_¿ïÏB8_¡‘S•@#ÿ0 …y¶Æzlù}¿Æ¶uþ‹G6ÌðOõŸ‡Ãâ¿èvžÝ+wT ág[ÿÇ?_¦½¿Êár•N_ÙãéÝ?È!#.=¬5/sºµ#&^Yx¶†– ¿òÿ‹Îø½û¿²(Ê…{ŒP‹5åñÁX‡à<útÁËõõ…Iª½ìK9ÔèÖƒp*‰ÒDº¸yZf£ð÷ˆ]T^#$REæ0˜Ï¯S¯ëú°ÙÎÀþEHÙ÷zÂ~íÚÏGHËã·ô|>‹º0ѨúýÒêìÏ´Kõ ›ñÙÖ;nÇÑ1©úŽïY¿O©»ráEm³«fËWfß³eüþƒRv¤N_Ö½è;Ê#&=v8MÃÉî—g_îÕh¿íîùGÌ?HÞ:GŸ_ ýƒÄ.[GCl”±ì¹ÿÊ kþÍ;j€(?Á}©íßDôgÌ>íãøþ_Ò½7iüÕÚá¨o÷ñjÝû½wÆ£`¨ÛèNñßùópÁÛ5v‡L>¯7½|ÿ£B|ã÷¨Pr6w‚${‡C‡Ù,Ü?Pž¿‡(âÎ?oÜ}©TïO7(ÕŒ9U¢}E|Àu‚œ’™ýÞÔþ‘ ¦×ù35rÅqYšè7^âßÜxÿšß’]ŸÏ»£¹yœ|,òãª?Ab¿­¶»|ü;{U´ðå=µ‰2úØžùÛO4ÑGÖðþÛ…‘$AáÛA†ƒt…ÖýšÐÝt÷÷ò¤t¾J€"äôöz¡-Í¥€î*•‘!ç¨|lT—µƒ ¥ª]s¡’P™’ ½Ôy+f@H®ôs@.áŒ$ƒ/Ί¢rÔ«³ Y×ÁLTÒ¼ÙoÕä³?–Ñž6Þ¼¼A¤ Ô6Å*DGK'×^À÷$Nø%]TÃ|œF£±®"£Ñ#(O#‚Š‹M§¬oF üSgörSgš}^7#&Víóò~ÕÕ¡ü@ä&a¾ŽÅ­Â#$²‡p±·Ó‘øà(b ¾Ê|®WÌ+YÁõ­(ŠáÎyÌ”9z3àÖZÖ#.ÍÝúrÒkdˆrʪ#\ád¢85¬¼¶qNWM»–º,¦ÂÆØÄ¼Å­LŠ_Âó7Xñ×Wܨ¾®jèÓ†á;qèÙdßÀþ#²Cäá/G¶V¼rvÒ– dëH׃›Õ( BIä­~`®¥œ'ïö¼iËrJÍÞ–©â-Õ¡?1#&ÅÜ·›Ó” F‰c†‰Åâ*äÑè¾@›‡ÔaWZïS[uÍ/|Sø©ñçìùØ¿ŸW·N­ÅÒA%@ßÁÀ#&~²ŠlU7{¸8 ®:éìçÊØÛ¿“Ÿ«8tc½¬‰:×›÷YýÏG½ì÷¬a75¡…Á‡—¦çòÿ;¯˜TØ¥º]ó~Mòþ®üÛ?ºøj»4ûÑ7egå?Õ§ºbÙ,€˜_Ê/´-þpƒÎ:½#.À¢‘÷t#$å˜JàtS¬{q‡Ÿ;cFmw1[ª-«DŠÚNG%cµÃÆÁQ¬!ê2ƨpQ-1»l–Å…4Â1ˆ€Š#.#(­®XbV„7d#.Îl©éÈ A¨61´E¥P°dQØZáa+’5Y#.Ëg÷úThÂ8>2ªÕÎhÆÔdµJ5…²¡h¶„lJF2#&Ó¤›ÍÜðf#íÈJ&ä ©‚”`ÈB#&ÌQ£f(&’0yͽ¦^–†¶JhÇTCiı¬ !ÆB³¥Qi> ;Œ£ e-®g´8o˜ àQ-­(H±#.´×öµ` 4ó¤mñÌÎêð£ñAAIeC…N"G»…Dt„ÒdþÃA*5¥‰6*æEG!4®`¢Q…]i…F8‘–‘Œcdó•ÎÁš8'qðB« I$’£˜‚P‡²ú¡AЇ´&é½’ôAŒôôä¼D=R›z‹þàeû99ZáeߤÈÔŠ#.Éíæ#.Sh}«¯¯éÌù´E®NO(Á ÀõêdØs(¥H®N‡z¦îX…„!<=ž3»4¬z‚÷\¾òúü4B&Ȉ‰Ó›Ê½3×N#&ëË]†~K~Ãh£O=nq83òþð´é„Át¹E=u{€â9Øs ‘–#$vA#$] ï~{æ×Ë0¤p‚y›°ÙPø}j8¦Ô—äô¥UK’K2ª«]Ü¢pHîК@rX‘²Äóïïð<$ƒðþu¤ü!Pt¿/¥yDCæÇÔ o}Þ©Ý60éãßµ95’KcD¿Óô,Y%Ó©Å HÈ‘#ePã(ïÎÍEĉÒ* m4 ’Wé§(¡ßÂã/Ä:ÙÂÃÃib¤ÈãÓT8ˆÇcÈE{ž/F°4¤´]<‹DD¨'S¡·ÆóÌ*vsjéUA¢¨D*rÛËø<²vkµÝÒ5¶ù˜$<¥BPXY®„j£Éøiäþƒ÷¼þ=Wô{}§òåãáçÇ{úô‹²ÕÞ2O†#Éíþ>YW¶¬`Ї™Bô†EaÎB#&Óí›>ù4.ZV¦FâÝ#ltvª¨Çs„ÊæX&È-:0¦,Š#¢CcPq‘¦cF<]i„˜24ûHën3§,1i“¼+¢Û þì¥ÀFÆÏÐbVÇ•sˆÂµÚpXèÜr¥êôaÛÇ…F0О&­­Éžl© S‚ ‰Ã‘nÔ`yˆl(B#.Í`I0Ê!p Ö˜oÆ•€(Fú¢FR¥ÓPCª !¤YJ(KïLŒ*å‘m¬8ÁV6™#&êPèÌßm´¼x& —ŒÞ¤šÑÒAÃlF€©™&„ás ˆ›íbƒ®'à¨ìµ!:ÞÍ8iÖ«M3±C/•J6B1¨ÛgS©”9Lˆm>7©¡^E&Y#FS%}2«7k«ì»¹þ£¿’þŸ^rHBwˆ™ |íúqÐ]Ë x~˜=ÔwÎq|'9M[¦ù m#&eáŸS§= ýŸ¿Á‹CXƒ:¸áÛˆóåœEŽÙ#.üHå© ^(#. êŽèqg#.T;†¸°Š‘üÉòloõ3ï}÷Éi¤”o!èvòÈñy ª¤:£T lc±'<34G©Ðî#&KqªëΟ6m¶1Æ(»†óŽûèÍFÜ›’*UÒ–ã÷cj´I„[À˜È2DÛµ\„ìsêô¶| 6‚6ˆ£^ÞM¼d½ótÛ°v(ظxÀT¸jnɱ#.d¡Ì5¼E€±b‚'Ôð$a$ûeâpšc;\(_¹Bf­Íëš(ͱH x4U=¦,Ø8çáÅ!— OÁ¾æ* A"f6±ÇI·v/Ì-O&ãŒÞsBLRs§L˜z†CŽÅÖä‰ êƒg€ÉX/§ÀÃDJÃñA·<í㊆É'†˜ÙÚ;#íÆ°vàÃ}@Ö竇”ŸÀLQ¢±7¼§kŸr:l1¦WéÛK¹Ï™Û#.(¦äÙ¶BL@Ç´Ã/—[Ö±³F:Ƶ³êò-#$鯾¥Ð4±±·”M. wÓ0òµU­äʨ.¡¥Ô”¸+B¯”ïæé "wW;´Ç ƒˆÁ’ß’Û#& A†‡sPÈÉÓ)5ÂQoùŲmÛZé\¨¶$H5¸ÐÜ#.;+ÂhÁvۆ婸ðãiÞ‡^ö£¨ë³º‚²o1,•ÿD¼È\ps¸QÒøÓiÓqR틎…`'ßA¬XpÛZÕCŽ‹÷.±Y±Ú91MÇK£ŒæNLìeN©Æ¦¾Y°!ƒÊcP6¢µYctNf)ÎLkB¦Ã›¬"HÎ6J) bæ²WK»`XVCm7ÛlÙ8iòM­I¡‘O3c’ÈF256Jƒ²Áà#&.”ëR¨”%AN~ãDqwL „¡¼œ~^é´¼\) ÆC­mÒž‹ä\@…½/Ùqžõ÷ÏÛÖ˜8Z9£‘<©ÃÝŸ 98Œ!ˆÉUkË6­ºÝ6{¥›»ÐnñØÂÑDzrœés¯žúæ§€òÙw÷ÐTM_MÛœÍ+DF&aßšhXÿN/¢ðu…¬¹RM{Ôar‰ºÄB¤²²jˆ5S9m*—K¾:FËXZHÚà‚Š5¸Î=-¡šq(YW>[TËæ¦2gJEþ]ô¤›±÷R$y*‡ó}­’ÑV,wôÓ&M]4\ð×Ç6Œ#.$…¡”8¡¿šßúÊ‚ÊUTBø¾&ŽM}óD¬óLáCXv™Î]áŠè>º¬u¼õZU;„ à æ#&¡è^#.°S;tÍd8çi|š¿ëq÷gwÃï16«Nú‡òOd嶃f¼¥xÎËF×Ç—~d4øëÙ¿Ø{7A‹Äw “B.\ãž-.U6áK«ÉZ¦©‡iÉÉn$/VUfnøM=Í5ŽæŠäfÙãWÍö¹µÄ-ð èѲêX|Üâ#Baî°lǸ;%HtÎšáØŠ®¾ÕLz¾;ȰrÕ¢ŠQÕš“fà´Â]µìãDwpáÍ8šcB«aßîk»³>˜qGˆ‰¿Øøsi=Ø4×S>?çÖ½²¸Â0€YÊ®hÁ¿bíׯ [`”Æ3íÖ„ûŒ˜£¨ûõNæ§g‰ø=xñ9â´U‘`{½›~…°oçOx2Ž?Îù#$0>(bŸKûJ‰ÜÚŠ³…‘o¦²œ`˜s9>ËeGOa~Ÿ¡Go²ÝµÚµÉìŽk7 ­<=ß`t¿Q§È&‘(­XPÙ “P#.•Üuµãù åßúÓ<‡@Ë7;©T X¯NXó&ñâ-5Ï!i^£“'~gÑ\= ÈØ!òò;5âðe»ÆçA§~Çø˜ÔRKÊI–&ð8-ÈC†‚[rÇÛ¨ÑÓ׃³pl[h†ŒRÕ¸½âÈK)Õ^Ž_ óO»Þ¾™åÞ,¥#$Ì…ÐF…-›”2 Ù@ƒ€òNû�NŸé©v p¸¦ƒ#¾,x’7íøIŒQ‰=@éÇFµö¾Ë3‰ù‹ÅÇdDx‹bã,QIZ&}$#Ã;•º;"¦s¼tÀ]ÝnOð[„Z=ªÁÎAŸI§`r興¨.•'ûG;O-ìɼ±Â]˜ûŒæ©ý ‰ƒ£ŸÊ'ëf/”§[BQñr#ã¢.VMNuARÖàð6šê‚,ã4¦ÐPf·­|üúÐk5zO2þžgÅq„ø È …+¾¢Ae»#²4Ÿ$s;¥N¶‰í¢DD‘LÔtês&á<Ö`>¥?ñ4½4Ъ¡Še0à¦éŒ›³'™‡ãî¯ëß,Á¬kdtÚB$ƒ›€íyL’jo,k.Ëžˆ¢N¸ö[.¯&½é’’c¦ŸÍ›x0âÂ6ôLGWГ0L8ÙÉ£>sõ×^g[¿…–nÖÒ7J >"z6õ#,S>/óe´øuŒw?±œuE#&74òì3R‘°}uѲ½SjÖéÊ?É]t•zS›NMM?+KY'ÒuGûü㟕þ4r’PÕEA-î‡wÝš Þ+(°í˜]±,†áÚ;'0k¡Ó}ç ÇRtþŽß?Ï7±CòÉìpCÁ#žGåƒiá@'øp¹“Ý&L·´ê‹ܲiù^–S9¶šDÕXw¶|6ÔvÐÎ눴|5*äÊ;’Дì×ìDGyøp=ë¯MèEBidü×ÃÇFTÎ[ÌÉõ´Û|o+ש h^hh®{ˆPɘ³jag¶ÑlЂ1¨—_•ž4”CR,¾¥î%è]Óäç=€Ù>Ü›ƒ \ #¤8žùêßûž†0òh\Ý%µB[iϦO_GÇ/‡å;åd“ü™ uæ vŸšèÐæ#&öa;„¾çO/–#Xø!¢ëh}ûôÞ)¯‡ríàXY.ê,¥ååä~B=(ìÍ'ÞqÿPã}Ôè˜6‹Š”Xÿn+žrä–å\ýUq´¨üÑ,{¶{Fj ÀŸsQYÌ#&#$½¿×ÈïF2Îû|c‡5,I“ ‘Yg]“)Õ#òCmfUcã;Ó}Æs¹Õ2Ýo”¯ð™Û™ÞäCéùî¸3ácšË+YQ_ÀFçu–ßKÒD®vvryZîÀuØ/ŽLd«oñ\cˆ,¯sÏ!mQt†2¤NûFÝ®ë¾è…œ¨ô`A+s€,ñ}í-ñÕõ×E+#.S¤[¹JoÎ~ù»’!'›¯*‹%ôNÙ‰y¨øáä«]4:­[Ã$ßUVaö:Ø|S:0Ÿ<›NVÐÌýšÎf§w(î¸V§$tåúiô¹¡ôµº!öö®²úˆ]”(‡ðçy1_9ž}+è«mñå9‡7w·0Æyæ(½<ÑF-¼ž”ÏTé?§¦ÖQîÇonX`ÜC01‘1æÍÓ.òÞ¨ïÞó^ÒZü1v÷¿>*ªE®Dtþ/umø+j-ðØIâ[Î}UÈ“í³šl§†Æ1½M}:xʗɺÞE«—zP‡™†vã· Òl#ƒ:=“T_DlykA(Ɉð\4q ±©äŒU1w~J‘œ´lkŸh*2–Ï!B?%`Úp^£õ¸aw²öwƒCqÆ®ScSù¯)®n+n†I·=U®ûu£™õ¬4ouÔÛcЯ[lŸovµÑ{ÜU#&ø"ͤw“Õs[·£OæŠR„ ‰5X«>C|!Y•KMQbIÊÂc<ڃ͆æð€à…¤îBÅiˆbЂ oQȃù‡ a%K•ê9Û“Gyx-‚”é-kÜô"·ìVo%ë*œ¡aÉÙÿš|¦îr—Ë~›çÍ¿šs«n‚GtŽpä6þp£ØÃ4MÚ/—õqѳp8c¨ cøb÷zÜþ0ûx>â÷h¹Ñ"ÑÛ·Ùæ‡#&ˆ"1Þ©•i¨^‘ QrJ#B¹þ˜ˆ/[n<ñöT•8>·ýëàÞL?Jn~oÒ‰(r߯íz»!2»,×9¡)yC#&±KËG±3Ö£»‰­Ÿ&nµð/Ù;Æ ÜÕ0*–x–€ñ¾ÝX뢀ï&Æiy½ˆÄ<žàçðÆ2xðçÈG‡!&€tx>ÊZ¬ì’¦\éÖñMMŸØ7¯æ©2Gùñ5¶^èý‹˜œ°pb~¾fÓ:}¿C¡MK¦ì5ÒÄšC¤d环Ú%G¼„•¦¸ê¬éöΘ*Üå´€ —_W:ÊôšJ:˜]'v8OGß ½N–èÞû³[÷^,lEÄ8X™,ÑÛ/"B²•·Fô\Ïè×mö ×{X]‹´UlÿÏ. -˜òÒOƒXæ¼S!ÁVX¹¢rÅír5£Í B#$é'¨¸õsÜÑU‰É„Æ¡HQb~×§3å¿;ã\â+á®--²·ÁC(Áÿ×è±%UØ"7¬ùë[òØ'J=ü޶™2wIn[LW¼ñ~*ÂÃQ*®·V«¬X©æýz†ˆBÎp®]ã0¾ 3áã;Å..uZ¾o¼tUfj–0N)+¹ãˆ°øÙÍË;í—ÑÍbj#&ÁUf¶ÚpÊÚ¶»9Ë:,á ëËóvLŽs^霮—í;uÛ¥eº²Ç<Þ=ç·îÃðS·+3Ë8´³HOû6«£#&§W‘4‡ˆ—•{¡| Ý+£Kæí#.&PI d9a†‰Ä lc`ÕWcmC4¸97AÓõÑx[}EøÄKßdèÂFF$wªŒáci}Ö »­Â7z鯱£‚ã‹KY‹dhõ.Wò/¹Áû!ø74úôÈÇô÷æs˜øË£ùc¹Yã¼ê§ÒÃËbe'Áøëã¾c&OlϺxní·éI½ÒÄ]:ÝLýòçÔ¢èŒÙ«¯¦îá U.±Á¬·nßp´]V‚iT“#.3X»ï±W (ñ]‚J­ldéõgú±gv>||uÇÌ0Êà£7P0ÆåF~·f&,Ôo»"è+ê¸5d«C'»,™žuX;«µðºfÝ…ÎCrêÆM§,ósMK>qnW@¼*è„´.˜ÊNQ½•uSn½Q|¥ x›Õ `sr‰É»óôiRë ³÷´\—0©Ô([ëmø6›Åvéna`ôõH®ˆõÃtËé“Î×I;Ô@…üKö¯Ò5ž½N¹Û©~Ñ€¸çßÃ34¬îópÛGàl6Â1àå4{RÇ–ƒ£Ë— GUwßJ#.Æú¤ýn\#\>ðVþ•…g–ŽËÀ~#Œq#&Ç?¦ ‰Þ#.–~J¢,ü¥@ëHŠçƒ^}ŸýY:ëÕgi|=z0MÑÑ¢à4j˜ËÉI»aÄáÚÂZ×…qžÑ&Äßq£tR!‰•Õª‡AÉ#&3†ÍaIÉ… ÜN†c¢Ë#ÁhétÝtž‘üx~‡ï?HçWHø¹ÅzÜ_Çñ²¹é±Ùió.·Y¡C¡ÏÁ ñ;w^ ù—JÍîЫE6‡ÔX÷©çÓ'l3öÜ}³~ù¾œdú©/LýøÀpžÖ®acÞŠ¨í8Ý+$øø™@°ÛGrXü'cKVò¬²ÂÖI‰Hr=îqµÕµÏ¡ytë¢{¨èѯ:-YhóÎTçÝ·^^zÌâ È™yòPäÃ~\ÖW™ù£O·¦‰V8º«vé½1CmXoÞV5Íö»#.Vn+7kÎÁê¤êN‘ÃKóWÞ£[Ü,?gxEŒ²FÏE¯áÎwJ/~eŸEò>\`ÙâÚ N=#."©vü§|EE&’XìÈë‘dž‰UW…ãšùÑ’ÜN¼\(IÖ2³ð‚’ÔT‹×hÛŸÝöão–s¼é76öZŒø£Lu@Gc‡ð¦s€É­°‡¶fš+…'n,A£O4p<†Û…Û°…ªZÝ[ïÕ†¥iY4É4•̓œÏ^e £¬ð¬mB‘:ˆßD”-î¶êÃS€“ä²R²síÜÖ¿ 2°¼cU°ð½o{dð£IËŸbÕ! ¶óR)döT¼F•Ó§³E—?pÖØÙ_Ki¶ûØÕ˜ÖAc|ad޹¾pb[ IŸ¢0ãdl¦Ô“àZóBÔ:ÅÄlÅ`ÙøÇ7.Å’E¼_ÇK!‰X19!ìÓŠð½9Þ‡ØêôuøPÔŒÊB„r–gæ·h’èsm®¨Z/#„j²™m{AâÕå7p¸ÙÝþò£:£Ý%JZ2 d«av°®¬)n‚ÑSß/ÆÄ`ø|"Ĺó!ßóéôQ èÙsÄ^€­ú&æV¬qÇ­àS¸¤$­(/q©‰.Æ9硟ŒQR#.¡nKë–Ó˜>Ÿ\Ç©£úûÍÓBxqÇ48ýxŠwe"óeùÊ#.`^Éa3±ÕmFâîš`][N=!w’‘$ðH/a²¯°FŽÂl!N$ogG‡ñ]"ÒGÞ(ñiù#§2Éཪ!®]»=Ñ×VÒ¨¶QyQ*wè›Ð¥Ar &_t · ˆtÑc&lÚŽ«BKʰuVP³¥ª!òyÅfûlÜç:un-À({.Ž>ß(€:mÖ^Ì߯Ê.^ñ2œ7vJ¦=zûUopã¬z8áÕøÆ5&eaQ#&¿Úö¿Gr™ø†ŒnâQvQ˜sî8uû:q~™·1Á/ mÚ(^6ñ¸Ù„kƒ©µ­¸9ògÄrDH‹© ¼­ÕE5Œ´•×| pç錆¿CÅv§Û]¾55Mæ¤tÄ0ÛôâzlxéoŒæ*TëtÅ#$[HëujIƒSeÉòpƒe?<¾HÕþžê0ÚæÎ3­Ï‘#$á°xÆç¤ ±Å®Ï,##$Fi¹ÒÅ‘#&3­µÂ‡ íeŽ6ÑäQ[7(€äx^ŠÁ„)75¦.Yg‘g»Ø°,?ÕÕÈþK ãuøhæ˜ÃÃFu d oÚÌAÓ;Îß6åsñX²ÙI9H=€ó‰õyz$(jwÅTí!¨…ÊÃG˜-ð ½ÎH*Y3;×pÕÿo|6YF€BWŠäÎ#&³Øëmþ­®²™(GcպǭÖP]·ÆZ:Mi2¥}î1º/ãt"ú>í¯:1øëaɆsüýè ç~w±K#.1@#Õ6®Hlmi:‰Éã¦"í:a×#.úzß™yDF·©Ì… Œ7µ€½Æ]±Ç½òäV:õ³y¬Fk »¤Ó³¤ï"ä4TÙ4!Š¡/(~‰¶²%-Û+Ôº’™#.éMü -!‡fHŠ%¼‡V‡uÃ-æp¬',¸l] V®lZ©iAxb@©Ais‘=ãÈC#ܨ¢R76À’… —­ø‹¦*j¥9÷­#6i‚ÅÜH%}™C tÆ…)=ñçŒ5Ég&{0Åi‹£kŸ!1Ef³’ß %äZ{CÑõPü#.ß^±Äéº@»ݘÃ;l~cå#$òD¬¹áïxÐvWÅ¡&ä—U6DRad¤geÖ ÜÑÊN¼ŠÙ“i,3‹Sš5ýµ‰Ê_Ë“›¢–ÓÄ1Ùj½ïž»jNèö.£¢ð:qÏn¼ç6Ë¿“ó¬›Ì¯ôt¨ š^æJw>÷'´…Íè>ªé›û0ùlõ¼ÿ&‡ãZŽÞyÌ¡µR-™eÓQþzóáÆÊëœÅ÷Àf"0Ôø¾2Ö÷LMû[(#?:/{­ÏŒdTŽ÷#.f6!KG­$÷EwÊû”Ýñt^m¦’‘è5‚ƒ‘~#.ùæ÷ÁR ¯xN‹ãÎ[×ß“ÇÕ¸FsÒuã5ÌÓZ:ad*”u™ßÚÛ¿ní’ðÎÑíNw(Ãt|ñ)#J9˜x1——>Å×Ðs=¯Õ{§#&¡u‡bTt ên[ŒÆÚr©ß›™%ž.náà#.-±%{á|jø:Tbñ'€$N9Éж‹A5[`ï;é¿P™ #&NîGêlc³CÕ£¥ì)¡Eӛܻ0uÔß~’p¯Ož^W¿5Áxèg´s‰·Ù£‚Þ4­aØxA)³±&/ùàsOàƒ±"8|7^ŒDúôÀNÿà‰¼EG¹ Åvõ™{ÂúWŽØöòr`˜‚Ðâ)oÆ &¿oLÏÜs´#&Þ*I>hæ<à7º†7AÂ$N#.•®D•Ê•æºÛ5‹”‚É.®ZÒg6¸F»+Ùƒ°Ï+‡jÎÐÖóÏ3G¤Á;TP@HÌ},Žï4l…5ºý0D ãÂöëÛXdnÏàtEzä<´òÂYf5LÌÆsyûgx6|:B´Ùîíj¬ÆÂÖNÙOSn#&cFå‚_v'CGjªL'\Ýt—¥P%Í¿³ß‹ð>S¿†ˆ–ÆöNלç³úüKØ:öiån}ýáOyy¬Û‘•yíi­2v§xÆ<˜»¿Ñ6$ÚßZ;¶,ÍEÄ@/…MPÒy­´ü·¤70,×&8G«Érλrítº@Ï®wí¨Ž\ùiìê8Y £EB’M+¦;)9zø÷v\ñé|~YñÉ[©YM@{.F æ´#.æga>Ôÿ¦Ý}7#$ {ÿ3#.ëñÓÂÊY$Ë­PQNÓ€#.$d_k¶âúéhí^>hHmïçAÒ­’²6Íà 'ƒëéU<3 i$³·“~¿†#&ZG½¯t9øûÉïÛv€N ­aì» ðä)ßröpøåÍÖ¼¡n·‘âCü_üÍ@ñóüÄoï¯(Ÿn×kf£¶*íÊAÛ#&É厑üƒãÉ[få4±ˆcq§¹Ýïo›>'YGÖ:íE§,”Pgè7Ÿ¥úÌaÝN«E^pølþ1}©¶ ´¿dŠFV¥œu[Ì;_tT’Þ¤3Ë‚ù4‡%áh[%ERQë#&TÇ#$tèÒ· ¶Èо™´³·C¢f‘YMÖ‡kÓm:&ºû®hÚ±+¯©³¹™j·²{õï¸!P6€f ?“ÐÇÄjÈÂø[w§|Î@xzãôùï¯áÛÜÑ>Áãû'ÂRÒ½|x#$0 ú½j#$ü“ðû#$òt¢¹ì6Y®áÄ{% I…hçj 0 <:Ï k%„”Ñž½(AF~×Sæ]å°ºŒ—÷ào»ý…Cú‘#.hªID‚<=W"Í”+I¶MŒUbÚºêù}ÿ(ñbq2"Z"@ûô ÊxiõÂüŠþLXç3s„·[>˜3òØ¿¦ ¡ò&Zù›…ã!EŒ ¤P`ÀÔ#$‡ÌMS¯Bõzu6?ät˜ZLžÿO‘˜ñ§lâ›#&ã8Á„y§œš[Ñh2$ÁÊYzH ëåõ§+G#$f½`|=0ïp(ÔpôñË4å¶Ègû6ðc¨~î>‡ÕÐÎÇmQVŠ-Qµ]Ê¿?¾ ,ªdU,…2¡A,Öóúß/ôì6øÏÇ0¢‰¶õoÖ¥-–îÁ£íð¯õyk§¹Î•$NIÉe¢–ù|öâÁéìu#©æàªìYb 9C#¢ðä]\ƒ•µèÞºø ìÀ-üº ÑÜ{ ¿OÑõ ߵеOUíÀ˜ZFè驵$4ô%6.Ö—7¡œÏTKÅ„aóÁ\ ø’ˆõâ㯾ÝöaRüÌÂÍÖµ:ãÅ8è¬æ<A¶_¶t5š'»V£Hç#Í–¨—9DkáP1ŠnH?˜Aê˜ën:ýuÇÅ{{Œ7Eèû½¸ UVe¼à,A£Xv:þµûµ|Ÿ¬•s<­7=‰ag:‡&…æ zýŽÏD}|‘q”w üŸµÛhªmzfM˜•ï3çg1P¢ÐýÛ?Ôæòi“cîœò@Ûna“°ÙW4ì¯ów¾ô:ÌÔ×¹œ(ãE%E‘#&P÷§æÎŒ6d?côO½ êL¥0:]#$pC¯ÕþËkPPäfPþn5PFÐå¼U}{hd=¬#&"¶œ‹š PÏ{ZRhÀ_Š3³Qˆà± 啌0\TvC6˜ÐøÂ­ŽœÂÎCðbéõ@Ö12ˆkþž:Îÿ3'kÙªåÄþhC¶)éþKØèºz˜“ï#&5ø¿}%züa0úâ+|²qôͰÁ"Sƒ8ãÜÛíï»_f¾˜É4ÒË‚zp²C¥„Lƒ‹‰{8þ›\°Å”óä¿]ñÉÍDž¾I«·uQljÓS+ +$óyÕ¦60`ÖšÜb¤¼Ogm«´ã\*_SmÒo?šA^ÊåáZÀ“¯Y 7gÊ®¤÷c"¿g¹?{¸Ž{1„ˆ‡vÓRûŸ­úqÆå h>¾Uv™TAàë gñá½øT>†z½Ï%U™¸(ÔJ_:áߨYÑ»øà%zÿ‡LŽÈ÷õßJNlkÉà þr1|TQ° ÜSÛgúÛÎñ›`ÑnTïEóozœÏ=11/:{‰É€ù«¶flåL sÛ½õXŸ|Ò:­2qÇkÛ„Êf¶Ûp÷#&¦yLƵ­HxÂ!™´Û<…¬X[Ä…‰±4Äù€ms€,BA, ¼Š;¢œ¶gÊû='ò›t¿c>ÊôO3¦Î¼ÍAp Zî^ #$ ›Ø_°[c¢V;(îÉG]ü›'»j£hEy2! ¶ˆ‚œ½†W< #&ì$”æªÄ*Qh¡YAãÂ7Õ'¸îëPþj"%l+a^lñ€`#mœ8uÝô»¦Ž ''%ïW¿bªT)fNÿÆ¡lÙ‡G³÷+DÊû* O{$#.@ÖmÓ$Hä#&ãðcât'ùöΑ¼}Óß üðìJ6eszúö×ml®öt/—ã¢T ™†&XÐÛo@ü>ÕãÁú>8ªfµ1Ãý…NèW¸†ÂnMP"ЧZ¡‚“‡m¥‰Øôã‹(AP<ÐýYD·ÄW†qIêç#.§¨°­ðaÓØ€LJPÈüÃm ‚u)×Ï*ÜHLBD¤c”Zm®ù7ÏfÒxç³Gwèxùè ðØé~¦5‰;·²ê¿Uê#äxÑú#.³|=›=÷ê‡M$-pÛ‹‰Ìª& èé Èñäm!à2rSu’½ÅyqØNŒQÈ(‹¾8Ú„ñã'Ü9Ú³눢 ØD³kÐ ÃZ´{‘IœÖÞ³Ýà¾Ë‹"dˆá;´ Üõ‰†3{ ä›¶Ê’|Úù:Tú´á¢ÝÍýn¦–`¬ÁŠfàËféßfÛ:ç`¦ô`²`‹% HŒ õ<±º[z6‘›xn_oÖˆ ^`I##$„?ašчZQÜIW—€q¿…ÙÙnºpK¯@ÒQ o(Ø”ÿ>s’ú" ÞiÎåèèZç×铵#$½û ;öù@ÁGIبhƒ×CžìuûîLmÈ„ÅÈWÆôÄ8f´I¹ˆ¡EPêÛ­ÙÐ{¨­ÉŽºŠãW´¤¡ŒCV”J¢!:¨MÓÇÙä+åœM׃ZäÖ·#.s*2à)áp Ý4¸ágX6f1çí÷ó†÷xã^Œ×AƒG¾áiWä›<„teu~Å×0¶x"ép`VlŒC?&:{¢_iŽ’«/F‡à«BJGÔ‹þ5ÅžÆ'MCQ5m†ó¦¡?Ü#B!˜´¥nÝÛ÷·jóÏ+ÌÄJI)UþOêö½²©¦x²€4ÿ{Ç#®/ßUçƒ?«üBª1U{¶+ü]hj“?#.#.ª{´@O÷Á=@|IÕçÄòÊpæ}Õáâ•ú }Âý¯o»ÔŒ -^—ܯæ«ð«ÐB?tˆ¹?ª”þóøÿòàÿA£øý¿e±æ'Á•/šRß ðSÏ£È !#${–â’êÏ;˜lé²÷¯N½ûîÇMR–ÉÆ% ëÎÇ\µËq¢àɪ5<¶W2±tØ·K?þ·F:Þ9Ž¿f»å^¢"p…çÇÙ×,_Ʃʛâ,õ{|s¸ç¸ÖËëAƨ8#.€¤…ê…G¼ª6Ò¦ˆvÞåE6ÌF:¶j¥J¤ÒgÌj›~Ê6_›~Ãü™Üß²Ãyˆ%P#¥CqÜ<¼¥²7»#$}K£|»Â]Í`†÷$a£«Œ­A¶>;QbϤ*P; ‡ï?OÛИåäP¥ÓŠs#&„y=µüg1S@Dœ­Ì¨¨*ú²&’„úÿw÷ðNδN»a»2‡A`±~ðø+@ùaÝãñ’ú¾v!ü^ÞÎü|ç܇¨¦ÛˆaÚäŒÿNf–|}æÖÍÕUF„µ¥ÿÔ™Œÿ|7+ïBâž&°²åšÃ¯Ö!m#.u*5%#&È#$™¨0é³k(!Õ4 9†5µbÉž‰`#$‘ r-œ,Paº¶Ô…À,Ä>‚På±Â—÷cê‚¶M(Šm),dEI¸:îRº€`P„@ˆgºÀYRâJ‚h;xX]ÎØC©@“ZQ¦áw‹¥ˆfLf î‚A–$K#$8Ù’—ÓVèC2A…ÁìÑTÎ%_ç¡m‡¶xÇä‹úÕÿ_ªÏ?ǧë?S8ö{®‹4:¬­©Ãe†!bwxV-Ç$_]_÷ÌP({èlpQé1š„w‰¸°qt 1ÊÆÝnA™®¶‘Lâ%ßC#$‡mz ÉxïÙ±ý€î*½¦23ÊlÌ k‰\ÝÀ?DE•ÒÇÆÞ[g7¶ÑÖ&•ÞÑOµÕ= îó˶Íê5e»ÇëÃD@:l3rع/u·JçÌ‘ÑÁ[ØWÙc¿x)oKp™6«õ«ÏwE&h¢æÝ\ï%¨N¦sL"&ñõëw&©$·Då·0ò‹ÖÏOZ¸¸ž¾½{àB8ÆÈ¿¶Nµ½ô¨Kl0>›×VQÉG|M›TxÌ4XŒL¤îºà$¥L³z4¾Nm”JJªâä{ĺ¨q@ïfŒÕ¬§4õCŸ:uè‡L–Eà†ƒÎ`;7ƒõ;Ì6 ¶fñÙYuÖ»w¬YŠ×Zæì˜Þ©êî”ÇÂ@‡‚ÌÞ#&ª6%J‚S:Ñù ¡DÜÂW´Ø'´Aé`Ë6…ðïãbTóë$!~˜"{r³Å¸Ä³ÔY==’J4•)("0D‰‰3J–kLÂS>ÇŸŠýkðûF˜%UQ•#$‘¢©,rÏÇ7±¹àî9C9«i¶Íá©ÓZ»ƒqÒÉ éL¢ZëfÃ;’i#¼¾”E‚$ c‚¦"(lHg‹$" E:–E}ÑšZlÞ¾ýpÛ·&¢§¨ñ»4“v1!L;#&Ž4F×Ï¥š‡Ð6B¶ð@ÝjT- °K,ì:H˶KšÑþ;…Ũv'vÐÞeÁÍ&¸ˆâ#&(a°¤©Ö@¡±U¢MÝ™% °,<«˜ðœ$…Ü6áG=eÄm#$vÛ™×è¾¼ãí T„Óo¡ÅÆÈ¦ÕJá%dCc¸ƒ}…#Ð`™Þ+t©Š±$8ç‰5âŸX6Ò5’RÉ-‰@ÁÆþ•Ò¬Û¦†p½ REdP›Z „a³—î ³#-þ:åueаX#&£ ªð›˜2ÈÃ#$9™ú9óQÖ=¥öÁ¿-ñƒï‘À¤EMU{¹„ñ÷ ¢<Рªô0bÁ‚1‘†TB¶Ó´ª7˜dŠ‚ènhO:+ª^ÿ#&ð¶]Ç^Š¥®ÑæLÔ¬x±A¦#&œ÷Ø{ gà‚éÍÍoÔLLï î|fIÉÀÓ0mºP‡˜ÈQÅ´'ƒæ©¢W×RJ!¬Q!ŒòÎ#&Öq„蜻|£°LÓ@tssÀˆaÜN*o‹•îž^Hó²tÀ=›zô#.fàLÙØ&t¸à+i‚¡7ò™îj’I„á.²~ŒiŒbå=š^àÿcç›á›ÆX÷£_jL%Õ96ï™*)‰#.Vd°˜zÍM}ݜ#$.ÐÚ¥T$€{¡“’ëòÁIÝÖ¥çŠQà+o,‡¿íØZÆÏŽðÐöñs‡Ý£ìíWNµ pã@ÒéG*¯È)†ÛéÄàöTx›yÇ98q˜&` —¸Q1í¥ÔAC‡†ÞSàä8®À•5å‚@°BsŒlǯ»vÎ$gU—»—,w— ‘ÓT•JWuùjWh$YD` , ”úv®)+Ææ®êãÛ¸ÈîXèÆ˜7Õ¡]À÷Ÿ‡S¶{³6½¬óè¢VaÓˆç’}ov"_lDÚ é ü!tœâR¥”Wt÷÷w댪wT¤ FR HÚ#.^'^ÄP%¥#$Å‚ð÷Dž4jäÆù­v„õ!<äfŠî#&¢…îŠß)ÐBVãÙµEË‹ö‰\FFX—/—Æ>ór&æ#égØßNhˆ‰#&h=¡Ë|wÚKc{ à—{Á®C««ÕÝÓb7ÍN·#.Oɾ¿œ]®¼d¸üë^Éá{ôAjêX´sÙ]Çeøòñ›ìÊ#&u0£Ãîu\ëªÚ Û-ïœ_|¸÷Í¢oæâu±E´ä×©î ˜àäÏœŒÓâ›ÝbL[m¹ #¡õ(-ˆ¬Ý—nòçq›î¡}ÆeNÊâ‘@_KfŠH*„æÐx3!Rñ+ñ°Ýö³é'¿dæ’¿¾`‘O€úÞ{}ÂKý»z¨TÛwcUûƒ Ã$ꉸL±±c 4†a#&.™ÈëÁ¹ÀM÷QT*5ºÐü »(bCÏNh‘bˆu'ÝfGuô°è¢Ë®£þü#&–}Ë¡Ã#$@Þvd¤(e ÃÜÝÙ>O|룚Jx‰gˆSö¼Efõç5 ÞמšœF/ÃK¡Í#&íNQ3„”@##$wA¨×§!D ¨¨ˆ)B/ZlinE1uÙˆbØÉö;?jã’–Gñ¾³¦¤  Ù"UÞÞɆ£PåiIy›qéšxE>þú,õÎÖmô­#.'µ„ùb€ÐªR·KÜ}!Àä+c¯öÿNC¸¿Hðé¶Î¾L[#$–zì¾PÊ)qáE­ÇÍ:ñÁNuY„Õ¿s´‚?›®‘ù0 M6Ì{žÉàÔBg3>Ð+öëÛôeGq/¨íyç]ðœe•¿Gš½ÿlïßfÖÎg{Ù¹ø‡ˆ×ØÓvDfÏ ¨ÐA%À†h ûGóŸ½ù¹]î_¯¾å¡’[Zþûùšå|—»\¡ hÕðGo\H½~?v°ÍW Ò`pM$Y¦}b¬@ùƇ‹DaÏŒF9!?S)àë+¹7ž¾Øsü¾Î“‹@ŸçW¢L)›°!¼ìð![V6èiäô‰‡t)ïþŸîùÿGßýßèÏú?¤Óú¦©Fz¿éÿd.•þÕZHŽ;m—Ƚw ".2òÀ0ÿ6«`Ô>oïò‚é\Ržg#.Þw¬Öuùh´¯o8$Zªˆ|Af`C5‡Þ‡ð~?>:ç¿4G8ЧÏ¥¾š@Lcûö›Î¢ÐÕfC#&!3só~Mü{Ë/ϵÆ+9»ôJäl·Ô·6·ðʺi*5GÍ^¼êþp±”-%+çüwÞc ÷ þS?,~Þ'êã¡£Ë?Ϫî3ù~`ñ…òûMfší¹èÍïQZaÅT¦J¢E‹Áwè«rO˜™Á÷5DŒ[`g!â+ Š:fað諬kÚ54Ò¨1*4õ]ïËFª»¢z¢D~ç·/“úü½ExÍ|DÆ6 R*eÉkÚÇÖ}º¤pm—~Y`ã#&ÑCW#.GT]U»mÇ8¶‚|Ð&¢%ñhݦ‘YAÂÍ Z$;”™¦€ïõ¤e.‘­y#&¯gÓBŒ)ò™çt<"z$4£(C»™ÛmyTNä3Šj£ ‹G¿ÎÞÝ~ë­=ô3Ô|-i Ìcìù…Çžè~2ˆ7Æ€„XDˆÈ”¹ üý¡Õþß³Ño:ì=>²‹Ü€ªþ®Aè9\»k-#.íÔàŒQÄ#$ò¦JaA¼ŽS#&FM=‡C–ÿ‘§ú“úYч$чÿmŠXÏJE!(J$Js2ÝL¼[ªº‹aK-’PH1:uìa$/‹ƒ­ôìèüÛ|wÛÝûİd—„aÄ"kÌp#$z=~Ñ#.n*ªsž,<:ôðôssíKü}¾Hðåö¹P~š%SÏ3ÈýU¥ƒ ìZ³E.B@ù=¡Ì®Çú#.ßtˆ°Ê–Ê®3QÝe¤pýöÜwèË~еû…õ°ŸÍö÷‹ó~ÅúÜ¥ªþ·(W?ð¢Ï«î÷"a‰=þD $‚PE‚ï=ýéû8{»¤¨I|ˆSR#?µGW<Ï[5‡ºs¿œióôíÀcÓyÑ|Ý$fO#ÛV‘ê¿óÙ>éNc¯?Vám6¦½CÎ9Çsåe‹Ù½T†>O•ÝÀwØ¿YBG*ÿg3ÑßÃp‡¹ÍíßžŠÛŒbâø‹øê­ÅßĶɜ¸e#$#.T:Wlòûÿò1ûš€s°95zfë— Ø„† Ô(²/î;±œÏ.>o­2Wnû;ªÝþS“8óž!ÃNoNM-òÀGWŸ;}YTç <^:uyÇ›Ïà–™¢¦0èôáõiØû/ß©…?,ÌÁî¼"'JõUÖþŠÏ¢¼Ð#&€ÈŽônøz6mÊCæX›•P#&:´ß¬tkÚz.PÑïü›½|áÎïñôº^°Þáf$6/»ùtªðCÊ#$2Hi,‹ÏŸæú8óÞ Ü–pãÍ£B#&©î—+€‚:l»V½jÅZ*ºÁ|™˜À+uD››ñ¡;Ö€ª¡ˆ4ú9ﻫ}/ éÉ×,ž:[*’Fi„]Òø‘Òfõ™§K3Ë˱s:=/ž1Îm—5¤VìQÌxuíð1ç8ò JD!ÚÊ5ïÏaM¨å{_Š^Ä8e—£P?(0D% E|‘ï¥<ûü°ÝS/ÍF&NÛ ÁZc5{žªúåÕ¥´ùÖ?°sB!Éí¤ÓT½h‚ïpNåPÿ(åN¸ñ[€äuö{úørì¶%碜ýÝôN€;7¨=ŽËÛÖ#$…–Ü¢æÃ¥ï 1´‚çòÀCQÀÆãV¾®+! ”yÑ*× Ùi6sÞeÉñÓÊ9P|Æ(#$AY ¦UóCê_>Zý»ëfB#$Bð¡ =ð<ð›cõóûeëÑÉ €`@ÃHTVÉ?»Æ·zïÇëÍΆ¬>Â#$n¥—ÿ®sdM:@P‰#&¢ëõ߃Á@øÖDÿFÆôç«ìžw@2…UþcóÔ|JPø¾Âõ+¿U#$¸{§W I%º³îa¢”£“¥ãÕ€ôŒCÌçº_$9Ã]°]ÛýßjÂH?…n¸õ§ÙÞ!xüW•ôÍè“O†¨wu á9“Pé*¿/±ÍzœÂÇ«Ã\Údc[1‹ààº\ظ(8©Þ;Ëd£½á]¤³“Ž&ÒëÚ.^{9ÇóÞã tæ wŠ.'~139ú©ç‚?¶oŸëIÇIL¯=A£#&]mëÔdm·ÒÉIn…éO´º)’:^¯8º¤ý}á{zÖrýq¼I™rû;HÖHîÀÉ/#&š¬T_ZƒÝ=5þÕ‡¡ƒJ7sëg܃ò¾*¥å1sÝýË"e2"­êðûÁI.?Jaëï­G4_¢œK‹H£Ã®¸ñ¬ëÂâdÑŽîAûg3âë‡Ö«ñò™Lüõ!Чˆ9õ<*ïu¬xÅkøå‹  ïÖ!Ÿ£”B]Æ%z²_«oV§]ÊUUÑì®»äzÕòj_¯!RÎTèÐÀn~¨#&jçÙ0.#&¼Å€ÃõSÉp‚nùŽ"ê•Ø­¯]±UAŒ‹*Àá1^M^ªQ£f¾ÕM峤Ûüzk#&acÌHÉÈ–Ú|þÚŠmª.]'E"2ûœ}$ õà§ï:S_üNùÙ|Ñ\pî˜Ý‰qæôëõš8§¯ö_ô·®ùs6ˆsˆ‘(t¿ȵÞG&µH^º7Îæ6¿#&F’ÙžÖ|:§b-ïh&3;¯á_|×¶×eTåôK?MþØ?%ü°®i«vßËŽ ;£uS8ôï6ïä³gÉÊž›[±¸‡„†uèCoí|VKž8­øÛ¢ØëN°áB¯éªëg d¼u¡#‹¸¦Ú>S‚ï.éÝçŒ:·'Ážüf2­> ''¹úmýÔý4¾ÏG£gå1Â¥BLzsÌ|sK‡xIEËü·‡w"­ü MïÒ!@‰x5û¦9(# £O¾ã¶§>8|çñºŒ²¤h×=b‘1„Z_‡g,45'jÄíå\¯ûoWì8=`EsÕs§Îsa‰ÑbÊÅœ•´£ºùz›Ú&­:Úà%ÏOXE2i1Gv1w"Móþ.°¥#.̆Ü_EïW‚@]ѵ¸ÊXLÌ…ä/+‚A”òìéËL—à.È©þ(¥>̒㶉üc.#&g?v<‹ÛË–îù˜óóx,þØ|äŸAMT²¾AæC™[ô†{æßK*.||¤äùZwŸKvZ<t†gVAâå\Vr¸p²”Sz4Yò.E6™!{”䎡“Fr+ÒœÓ\T¬ETs9ñ£<é›í:8Š‚¿ {Ù›\ù[=¸þwq4ƒõ[D«tô»¾û Ï‰SÝôÎT?l\±(Ðíž ´#.pÔ—¯—¹ã×Ìç ŠD¼XB÷¥t1tðT#&#&1“ÀFãF@3œÉzªÂé8#&nµ²3(Uµ~Ÿ~ôãŽÚÞ «w ™bÄØðòˆ}¢3'kÏÈ%¶Žþì~lð"ð³,¤q#.œh+Ω¬ gü­ z(óEZ/[ÑÔ£P¶²K $Â#Ý{oM#fžy¢;…À-Ì}S>÷çèòÀÛÎέ³AiË&oàq¤GZú3Çç 9§_ÓcÛv¢Ÿ~›î!PMðòî®|éÈšâµ#.ˆeØ~oT2'uKi7pR¾l§Ã5Ëyu:ñDÑ]Ïä¿d*À35³azí –]Ù2ñî³Úc—#&±àà#.±´r@ü(ú{ºhè[ivý½à.ÜÙÇ1ó:ÂAD•墎/L‘6ˆsйÑ;AÑgqކWtùÕŠ\& >”ó2x4GuýNëéõÅ2´iÒ¶•|_|Ÿ½Ÿl©uݤY&~´t0ÏYÍéøâ‹ÁÒZªþŸ,e»®Ç¸Št5SQ ;0sCdw¨q(Y¡Aðg†•/Ò‰Œ”hÅ$ö¡VQLÀ6+…´›|ºYéubä&îkw èC)EyTu ªY5s‰ˆP+Ÿ'Ÿ™÷—Û%͚ѡ-sᤸÛ)/T#.b#_#.Jl&uI"Ûx(tA¾/©*O约 †´4c#$#–šÝ›b#&³ÖC½Í(D¼4Ùw!<®A­¶lW-³=^h@Úû¸øh°^ùGÞB!uÂÅ™-#$K;¦#.Å{¥Îç#.ãJ¼æ® øTÌGGÄ9UЦ¢IF2’ÍSA-öÎ;Hë#$ÅãSŸ¦h",¾­ƒª}h$, >¸èÏOä?XöxáfJáþ$ígî¼…ˆ¸ÕÖ£#$âˆD1ÑjõéŒc#.H «Ž_¯ÏÃÛü$Ž&G§ßʪ¹z¨A=ýÂÙ²1 ¢~å±§÷tNy‹ÛèY?„€Bè…¤>î]ÞŸ”¬NÓ…J+œ©ú µ+ú? —¦nÒ˜"÷ÿu[ògõ}0jp=eèC¡>ß³ò(»ç^ö"uÝu9ŸVHPßôÆh›àC¨U‚#$6PQãÔ ‘þù‡ìàÃËâ4¿¢Ò Zƒ®±þùâþÏõ±üÓòêíû_ðôº»D:#ÜÛ}kÇݵé7~O“ˤlëW>ï·Ùóý}¬’ ºEîb!‚»V¡åŠ7ê:ŸW>pZg(”ý«ºSWõ#.s²/Û>iñý´ÿ?¼¬Â&ú :ç·<öbÎ]yÂÍ‚c×SZîþcà>§ª9¹%²OßúÂA?=Ñá¯>0yP†?Jö#&q`Yñ‡Ê±GÅ?¼¸9Ìû½´;Ä@pÔ>ÑÐâ¿LÜK#.QEö»ëùž¬ï‚õ«ìÜ$?\´l·PðÇCyˆÈo¨õ„ $J”(#.#H%©­5$°½j|F#$A&P`€H‚#&ßMñ<ø <>ëµmR1+‘1Z†ï ŠìÕIœ7áM{D¡¾öê^/ËãPy†•@v/#$åÛ”3•#.Fqä–ôòð#.Ps‚1˜ë×(H’9Èš±ß¿=ó¾Äaëÿ/ôq&o×'_™hR0“#$“#$”=Œ¹lJ‚‡^Ÿ. #&JCÓný#&_©=>®¡*‰¥ü7¸K?Æ3(ñ Ò¡J ºË#$ÊËMVîçËYË…WáS!¯²}»¹ÛÒnµ“8›é ˜©6Pè$èã#$«1‡¥‘Œ×M°bÇK!ŒVSa`‹£>W;9”CT»úÓ39c'zÏkðÑ@¥6ÔÇã¥WvÜd6þ€NWU­ÕWù/TB—Ïçi@ª‚³#$³uR–Á`ºxÍD•àMu÷=ÙïK%²”k^…SXÔŠ˜^¨–•m%tk³ ²ÁÃ9ü7)Ýú~nó`Ó5IíÒˆñ›7‡X¢œé«–‚r¶å³xéÜùnÜR(6eŸÉÍ=oãÁs~ÊäDpŒšV¿\_"'(PvVR«6ó !„µO-ꮫ^~n¥Å.‡)D*S¿Jùy¿ÙÒA M*€Oe<êšL¸|Çwžâ¡$DHS¡áz5ª#.;Ÿè…a˜»cm' \Ç"Ìb‡bê¶Ø~/çûùÆW¹, Kc j°§õ°ð.…]'Œèõ{‡§»ß¬Z:Û{•·|¿„Ï ‚€•ÞgÖè‰MkÑGèpÉ„`v“xÈ~Ü(¬ÎØ0äíÓkbÊü:)ÆØ"4oËm‡öyÚŽÛ¿›s^n™ÃùTýƒß9õi*Ó?PñmÿJ?’rÇë‡íhÖ†ZñåIÞϽt¿Bæ¦BW[¦—zˆ¶4+ƒÎuÑ–#&Ý$^1émkl:L„¡Ä*Bþ_8+uÔù8xöÌ\.6iæ£P+ ——b­Êô>ëñ _^‹D#&þ–\ÚYÑYr{;™á|â9ê~¨n.Ñ #&).|ñqó„ŽgTiäѶpÐVÜíxÈÖþH<¥ïbAä›cÐVôÍXñ­•Uõ\{-–KsÇ»Ú[®‰æ¸É¼YÙo°†ÜÉÜyÓŒNg=Ø#yƒH%ØXjÇRëÇÅ™ºÛ³ç®bü†C$ii¿TGÝQ•¸úÆá˜œ"/’ 5æ:z᛫¨#&KØÎ‹ÙËIXb :"ÐhŒF™O-Rj=—Iœ«µ·ñÛßN«jéà^vâôw+5sO·’Ým„9pá8ƒMt#.Üv:„£W*+EN+›ÜÁÎéwËdŽs£míß··žºg[s;n‘»Íå褕§d#.Ú±†­® ‰#&:o.ÂQ%÷\©Ö88_í6;+T89Ï„ð×ËXQs±„“rãu²¡N2’á‹?T‘øu&:¢Óaêü^]œ]‹rÍû7Ô²$ú%¶Ó"”)ƒapûÍ¥€tº5H`GQx¾ðhèl×\¼Û{Ë®=Ÿ[qÃGABdŽb!÷Û*b“«MükŸ#&Æ¡„,bý7þú7[zü;Ç3ñ¢Üç¤úAäa[V 2°ÄZž¾·²ßÈÒ¨ùmö–M©Te5ó”#.[JóaGaxÒÅÆLy?§ŒbÐùLùÅ÷Ü¿t™1½šOÇîé˜ßHN’>H¡¸á÷ùE\l^Ù"™ í[„gên†è›Lÿ¢ Ø”÷¥„ÓhÓ(B0rjµC#ÑS˜ Ñ‚¹qZ‚šAäôRùþŸ¤R[+­Å#$ƒ`ƒ´ç¸]˜Ó ²]íOU.­ÔÖp-ˆ¯–Û…‘7ÏéÓÕ•ôëì €û¯+}§Øñêý–Áúÿ&^ašŠq]õöA!sÆ€úš“•Zª¨A)¿SÊ!¥ïKÒ~ªö§¿ÆÊê’U…õƒä¶;ä·Îõl,ô{{ü,#$ÔP6BzG0@‚QHóMÎ#&sÄ”aØ H$’y\, ¦úZ;bž^žý›|âûw3´Û³ª:A½TrúœÎÕùmÈ\å¸m"n†x\ñÝã×Ü ;ÂÀ‹ŠUkSXAÛe¿Av#$LQ¬:ûˆ$ ŸdÏ0·&HÏlÂ[×oyr®»P;¢lˆœ,Ž«Q-ÀG—[qòK) 89hKÚÔPu*ï^öŸÃs }pí±;HͽYnGWk'_’:ÏAª$-Eä߇&|0‹ñ›TxCÉÄ¢ÝÔAix]N{JªDÐú(ú-ý8|çïÕäðœPìè5†Ï4L÷qý+¶<îø:^ðzm_­ÙC£ƒ 3JˆèÑ›°Ç‰±Ûì¶NÓ¤Ž[W -ëÂíFs\]ÙSÚx–hÁÕ´xïQº; f“Ø.é‰XŠžO‰‰f@4ÊPè²ËÈŒõKC†»V$^"Ý€×ë†ã({iIßž“¹¤±狚ÌL¢VK#.f¢Bƒ ˜˜¦œ£dTL¨‘P/"r›=¨”ÁÔɽ.¦cv£»Tæj¸å=a[IÚ´,CÆ‚Ã""……#&®“3w"º†E12ìîÚ/ ȺÌöLd²ìo›¾H­†’<è›Îc ±9×Q¶[5ëk=~¬rÃïÅѬ´ÖÆÇƒä<š2aQFa«-qƨ,:È"‚—55¬w«†tïôFÓ“©°»z2:(kèAgDìH±á%|,”}`‚IŠ[`^¯5=5{„Dr¼sfî0íƒH#”¼%ñ¬ç'ºs=œ† ^Œ%Hm”dýÌÐÍqm89”[zýк,|7Çß±v4!õÆ¢Sø³eIm~ìw;Xh$$1TÉŸz;&E%ˆt9·‘ª\š";±_²’µÀmÍQ:#.sh±ÏmpÒ:4ol¬ÌZÅl Ýnõ^QìÁFÞôÓm·ækf”q­9Ú1åo¼9ƒË³´šÈáä%$ v·ÇªŒp*§ñ¦0âáhŠŽPéqÖ“ÙV?;0BÉû¹U¤Do¨ÒÕq"ÊfÒjy#&‘|Û}\0¸‰l“;i Ý-°ÒB —#. ³1b`šBØP_ˆQ}õ`n>]Ë8×#$ÂôAÞPLk†6%-'ÛŽ[zMlã\EL¤žÏ _kê›GðÆᆴaìØàí@놚ƒH´Å#$ÇaAlD¡QÕKtáEìçñEáòû û|œ!H­‹Ò-äˆåäA‘!çzð#.RG‡¶~^ ©‡/åòé=KË—àìüûw>²kn‚Á¸O???cðž^-6d[Ù>Iݲ¢³”ñéé‡$5|«ñèû»i½PË>/ãr@«})šé2ß§W5.àiƒ‡B:Ig®.` MŸÓðÕ³ð÷üƒËâ>_³H¼ŽtÍõ¹0tþ Q#&}š  'ý+-¼ì׆óa†ê©° ¼Jè#$vr;‡rv ž~Ðë‡ÃÞâÿ¿‚ÏÔ¾ÏÈd¢85C#Ø.È­G¶)þب\V>ßîO‰;Ô–Z6¹sü·ÔÍ5”9€Í=m¹›AÁO« ÈÖPÔ5ÈA>£«ôlnfž€=«Ìu‚]‰üY" ú—Cö#²>ŸLDø°eúSõòeqƒ´þ·—ß߉¤1f¬…»ý`ô]àúëMšø¿ºôÝÄÚ'¯‡|OPdªúu0|[+¢ mìŽ6[Â×T}B9Ãô|îô|>’¿}·—¡ƒ[k lÕÊ€”*6*"}™uú%)É|ü§ž‰ì#&eškÎèc¼C-1®Õ«îLCö=ýÀ Ði#.vBÛ1>pà‘ä~³¢ÀFq'Íÿ‘ǘ߼ûÃL0-£,ö#&‚è\hxtüoOÖ\œõÍÚÚ¨´h!f K”ã»ã„­”رQ?3a¨9Ø3JYç ŽÒ†Ûh72Ü’2h¨a6ބêJOäzÍq¥;»ØÙæ ¿µŸ#&*Uq¹h{Ì õ}þaöyÀý=ˆò·¹;Ž 9/ª»ÙÞšâ»VÀi÷z‰Rµ›‡nämQÆ×ê<ŽáÙˆþ^³Ôn¾ÇÂÁhËPn¥²1“› )îDîó‹°¸.׊Ҕ`BÉÙÀ¶l"œ^Óbs\ó$t©V6Gak™•’@´ªáÁÁű‚ЫÔùúÁIúàªÔ¤øWêáíûÖ'öyx,† zÊ„¨·ùŠzÙŽu–`t ØLÀVˆ2 b™+üeï§÷@øöÂͱ¿GìÀl¯ŒmG;ÛøÙ_àïÑGц@’V¨¬C‡’#+0’–?òÜeÛp݇^FžúŘ/Aˆ`B—#‚|¿êŒœ*PÁt4•q¥ÆsI¢Ï/ÆØ ”Vr\IâÀh@ $#&ú¯ÓãêÇïv)X`÷ú½”²U|-ñðÅ5>€5¿¿[vQÇ©~ÏÀþ¶ù!’³„ÙdVñrôE¦fýlT,|£€J(!M&º+(Y"ó¹çú{²¾p5À>z¯mSs»ðÔ}«úµ½í]÷\3¶R>ÓYB0žGnN‡#(4y\­kK–ÃUê “à4žï#‡7[ÌB §áúÂÃkYmÁz}ï*"ÙNXw••L"T²õ\ÕŽ£k–GXC[#&7À©™þ‡¼×Bå“÷ò¯çz`E\ae­ý ÆkØKŒ6õñ›ÿ')·Ò"Ù¬ã9ÅÃ:K%¡]Õ;¤’I w´¼Ö\Cޝ§·‡#$ö¿YÄ,ܼÁ6pÀië3àô¨ýÆx¤ÌÜb‘ç¨sø/–¡zš¢d~ç¬ì5.¸<Í›‡rY¶N3$±v¨¦ÙzáÔÛgËØIg¸üÈøŸÙ ;#&°A.þoíçÛ©æý'‹>gqbRÄŸÈ”0º ¤•Sé7_ãH„â¨gëfëÞ;ƒsÊU+LÌ#F% jXeÿ¥ åö‡ð›þJ؃;Ά#«k%ޝöþ÷×û?Phl죮')ôÖd´#.3† %±·D7ÀV «;“Ã^ÜšxNA7PÐ?)·ôË*äN¯SjÆ(‹Li4ù•@XSQ#.D×[Ÿ×Å>Ë[È5:¶aËÁG˜\²“(xº)né("ÓÜÙGSáì3*´ÈÉÑÐ/Ñ™u2bhXL”C#$:ˆ~¹ü¾_ÀxyÃú0_–*~´aÿ;öéªíZ*ª93ü‰|2P€¥~_v0°À¨S?u«K#.`¨#.³ (A•R¤DQn×N:i‹ªÅ¿ö+˜‡Ë$%mè‹3‚WFFÖhÖiëZ…Ll‡Hú¼zqñxÌ#&á1¦ól³+”ªEÓ®¼’Fæj£Mb“2ëSRZZÛiæM<šrÛ²·¦V`adŒº†:õ„ ,oÕ¯5¤jÄ45­š)¦`÷†f·•ÕŽ£#&…ÔrößËW4½½}£ôÖì@NI"„Š( ÷gçÔ¡pyLß×a£+*Ÿ8üBÂô Ǹqèã¥:}íÒ:ë£C¼SájõfÔväy©µ€Cõœ‚¯„+¸ºUä?E™"‹8²®"žø~b‚Z#.z{>û‘ø~PySLjuˆp-¸‡O B[!»6ÔÄY! #$má6“«nwm_içoÀÈÊÌË|‚&#±´Ô6@™N¢Ù¡UÞ¬³X0IA#$ä)l ¤Ú>PdÉ:×n5ŒäÐìgŽ—¥ª¬à\#.¾[cMT¤ˆ" #R…†‘ŒKDux#&¸w­6ÕI¯NùsÖneø´øž‰Ëaé‚J#.S7õc¶Î¨¾…`û%ÀËÐ÷ä¦Ñ=›ú‚Ö,ØÞu>jfpºrÝJ`íÜ;ؤ’$‹" ú;ó×N3“R›¥RãQ$„H2jl}|yÚÇó‰Ëé¿È°T9š¾oÄúmƒêc¨áø~ƒ,Œk툞žîæ„WwÌ­9úNß¹Œ[A%Àö/¸H]>½¹ö57ç;À¶²îè£ïô=¸âØì÷•Mµ!H¢)¾M¤4!Ljxß|8`zœ@ ÝšÒÊ…â€`Ä¥éK:PŠd|¾£Ìݦm¹ö€7`hö „ßÁ}x˜“ó€v=¯‹!jUO1Ä!„PÕ*bGÈ ªÏñiðÈÄ@:ÐüÿÊCžrS™ Y ¿ÇÒÍp/0ÍÊÈ!·€x]žJÑ .£üñL(ýW[8`‡Î‡"#˜@#$„ òÍðû gÂóû/ó’»›úú{KýŸðú#.ŸâãgAQ_Æÿ3%|[Òõ"%Ÿ:PZ ¨—ØlDò±ÈÁöoÌ(žp|þ×cŒžÔvœÀåò‹`ô€°‡?vƒ³ Š! ýï4ÔÑõäpÙàn¶'=ðÿ4"ÆœÀïëú_¨ìCmP}&”»˜Àþ˜~ {^[·x§å8ˆu'ˆ±ðDî #&HÕÁàç˼&S$£XýÁçdŠ@=;ú»+Ç÷ç¤8»š0­G¬Ó–Ð…j àð<ü§‡›ISú¶ñ_PèÑ×i˺!ä,H²`u¢ˆ|Ï #&¾ ³±_ßt €wŠ¼È¤g–«éÅñü»¹Â9˜rù¡”#$º@t å¡ÙÀãýÿ  'èî/öió.~$þš,þ'(ÏÑùDú§Ô¼¥g›šiàÊÔ·LOM5ü”¯Î%´²p`.¦¡=*f YˆxŒ’I=jÜ<‡Ôú?^‚0©œH#&”d€>~?¥Á"Ÿ>±SŰ­ÍûûâÏDf™Ýú<¾Bî•öÞ•…7Kµ±’´÷øq™À¾()™Í;s4?§bx" VÇ åözîŽÀ͹×Ú{*JÀá5‡wÜtú™é¨`DŒã.Ï™UûØúç„b¯ãCQ„08.ðë²'¸æfp)Ó ˆX°ô-CŸ1ÛàqûÎNnER† „›p5#.³Ëí0k‚!¡Šˆ*>R\³“r$® q6ª¦”N75…]4%2ÿ]*'#&j8„¤ö{>é Ä CÍTF2 "¨•!g^¼ù»m ´f9¼HÆäm£ÍÆöç{ÔWDÜúøzÓgNNlS0ˆ¼÷SdÍ:ÃÑKôÌAwQÌõï€ÚäÄKRý|ÏOEŒ=P<‰xЂÄ½W£ò÷{£ò#.~&)Jô#.&®ä/ô˜=õh Éwp°=ÁÐë;!ûæû£ãù|Ã×ßÝû¦É1G_.Œúã@'½¶a…Êú( èô\ДËȤ›x˜R];]PF’ãó£¦s+P#$m$š bo†à„ ðÊêôIG{vU#$#$èÒ{¨ À¡eÚX| ¹$$$2L1 I{#.H®#$˜ŒT‚#ˆ”U,`‘#$Ú™§€~|€PâAÑ6gqãù+İÜ'åäcòp0&‚ÏwÆœÿìÔΓRõ(%r ±T9€le$€ô¾Ô=;Ö|Jwn5)­ÐG§YbY():gb4`Å"ý¯Öþ×PôÚÑGÁ'Uê‘9C#&¡ëøP¥óµ{誰¦Àý—€†¨:ÑK"²"´hÑ)™DfýyZù5ËW¥pÝ"¶»0³Rb#.Íd¨»‰AÊ)…(EŠ'õ!ÚŸ_¯íÎyãÞ$â= Ë>짘{Ÿ^ãÜ!AA½|½îÕðfF`üÉ#$—‰‘Àfû=5!­Ø‘È4¿¼ Ÿ§pp~¾·pn‡éMï,A1T²Œ‚Â!@± Ð"­zÆÍ Ë<¹Hƒ@g0ÖtRâÛA8)y| 7ׇ'o#$4„”yåÐãyÙ?äêBBÅröD)^cµ#.@Õ£wëÜŸ¦}´RN5 7õ†aˆtˆsаÀå=dóBû6”míˆV¡äÒsæ®·=åì¶éo¶ÛÆÝçHtÞÄ!¢rS k…Mc;»,¬ì•F,/L ó+2“\èIö޶— LPRBBrÕX)þ#$>üÊzç‰(ªw{ZOª?¨T…ùçËÝÚØ¼=5*‰ ›¡$?u+#ÈZii™éšº4Y xLŒ!(½æyd•d’GÍÏÏ|›¡™ÃϳZ AMR*ê ÞË‚^BŠ#&Z*§Øoä}h¤>DmB=tsɆ¢ˆç–NHg×û¤dAælؤ#$Ã*àš`HD{¡¼ô‰Ûéö~„:Š“eê3qÙ»j<‹¤;ô,„F“\ûvÄ¡å‡pIGbO‡é†o_/r§?ê„ a¥<ÈFÐu¿Nàd/Y9tAY‘T–v³Ñ 02¸ ’(¼¼·FÍ#&Æ8î’?A˜ŸÚ~ñößíšÏÝ3Có úq÷äPÆõ d4#ûqÙöý™g0Äüä)ÿO¸õæpÃv3ˆbŠ(‚* ä¥U¢¨õáÂ¥1Lþ{S•uú¶T÷ +òOjv¨&6V%8²f*„Ð0NCìaF(òÀ=S…˜Î%g1ER Ukݰ0B 0:¿".‘±ÆTµêú‚»ûŸ­-—/¨XJMŠ´ŠRY.ƒ„‹ðrX,x›¡#$qètÍ<•tñûBÅûpçÎN[»›ÔæD¸3Ú¯h}Ï0ÈP€@ „;>Ÿ¶ÉpHª˜LôWQèì9d§PdúöùëÅ:ì;ƒÕlå$9ZIPT¢ô*´·dë·SÄ6Ø©<0s¾CÀb*9Î eî°žÀóy¨w|}íAW¨|6F-›'u´ªà<ý|NÏOŸÇ´´çóüPé¬QW«ü‚"Ê*GÜ?§•ãûÚ¬§ØLT\_c{™ÏðAv/Ñ´úHàIæ*ȩ⨨8õ(g¼±($Â'GñCb‰ýû›ë”í÷}9™Þ¦ÌÁåþký0lìë„·þp,æ5Œä7ã 9?evÎÿÛ:ߟÊ÷½5˜ð#&Évê ¶AÍ'sÝC¶P¦#.° Á’¥èZY›¹qaÉ€ÜÔ66,Gäæ»}ƒ5°@Â:0áª$i–@ÖÀÔ84BÂG˜uª¥¦¶`~èû;Í#.½Ð2èÌÙ÷!¹EÝÍãBEŒFDfä¼:À¸#.÷&Ôæ]É—}`X:ÄÈî4üγQÅÝÓfAÝôîÚÜsOBÛ¦Z°rbgxCJáÀæ^ÞT*îDºªdÙƒOÁM#$æ[ÃiZõÛ#$mbï ;òB„³’`ާ,›†Ö8ó ›° âXG3‰Fˆj(¸Z|¼ý!ì}ÖeÛãÅKÇWyébH‘&Þú]×o;u6ë#.ÔPIò_&hÜÄE¼#&Œ¶îˆÿ…Çç³ed#&àVï6ˆ]oAœ¨Î$:rPo¾ºji…»«Ò{ÉLW÷‡ËÞ¦~xŒ?kPk„N~º\f&v1O¹Á ÐÄy mñøºøÖ½óEŒheŠ#&îü:OÅ×HÝÝBôÏ<¼"4 ã»´ÅO_¤}0ó•Õkªô>sOà°ûÂæc惆@\D>Iu€{“ºz¤3àpF‘,McðŒ&¥÷vlö|Œ‘àxœSEm·â6p£ÓFÏ#&¥BBMÌ#.I#.êñ¿kÈv Qèì¯YÈ8pÆ¡}»mï35€¸Y¿_Ôo=}öOÔï<á$"XñÈ„’OÌU/L‰&Ë‘„þݬU¦A†¯í) üšå(4~8h•hB{}nítÄ#&4-슴3gök¶ž¢PhM¢ÞÞ`»“¥ëº“2›|»^¶ËWऊ\Í?AÕýÄÿV,|¸*}]u Mz{«„}Ÿµ“JÔg–a >”åœ'Æ{Í”J¦wQÃrÉR»é @#&fçr:· K”|Ø-å9/òââ}@¨¸1ïü#$Œ4Õè!4¿¼Œù6qðsÏάËm#Ž„Ÿa+×V=ÕpÚý*f ™º¶›Ú„‰¹ðS¹È#EN=úeç¿ïí8>ÒšÍ$4̨gJ¿^=®¯ãׯ½ÐTÙC­öCÚ&®v 4רÉÓÝtÌdà?(D¾ÓEÈF”N4ˆTIäQR<Ê…¬Š  ,‰8¥Y#&YRƒÜ€:ÏÙmŸÁð{››UÖ°±“§Ùå¦fBh_Ü}Ö~¬jz~¿©á V¤D¢ãûªÖ#&}KY}È”«h„ !7=‡µ nj]¡hDü#.¸hÄç#&Ù+¹4 ßÍnD±ølêz“›Ú–£çy:Ãíþp‡å§ýßqr/¤™eËw9Q»B{è<¡„ ~o[#$¶ü¯RP‚Õðjéu–¹©7œë¦e²yHjîI@*,–YV L2©l‡â!–"#.K(J#.‰•´+5]MʽZÈÔumY­V…‚t ðzf’¦b•Ʋ¬OümOðúY£PleûFgõ„ÙRÖÌuâ -F?èÙ±Š%¦ ÿQV'œ€Æµå‚é{µ„|f ¯‘ûjÖ–ùƒ|˜«ñ ]bOcV‹lwrJÜ€+¶j‡žÐÐãz½$ ˆâ¤hT?µ;Çãü>5myo"sÞæ^Qfò¶¯OUÿýzÀ·÷›NãÔ½¬3g`„‚¿ÁÂO„½J™Žd“¡Ó²r©X/ö4Ïéesm·öu<&ã‡ËßoÀ>ìª1Õν‡go<̰‡î?‘PhRZ²[ H³K¸åG„ˆ€¿5Ô.…äèý¤‚WsÄl0Y€—ø}<¨<¼3®›ÛOíÆ§/_)L—tìý+¼ÇÉùðúã¯wëãQê±—#&7d.˜ªí2‰Gqú^Ï¡€ èù0ÒEG¹D+ÆpÔÖE ’puâ;‹éZI 9ý<ÎgÛøQw²¬mº|Ï•?šƒ@yÀ(¡Œ/¡Qdy|ÃŽìXL¦Ì$”‚°SöpLãF7UÏàôó‚µbØ!ñpPxô=ÒZyÔ2…·§qÚBA³|G¯Ü™»çûào=ÏõLÁAè/³!Î7 oSG_Âg‰Óž¬ÆóBáÌÚÅøÒ‡‘/©ó ¾ÇØâkè14jm"Lº;¦0÷ßo2m“|üž ˆdLB7—ÙÏß'˜¸ös6â5€{îo–€Á‡è/$Æn±” ìv÷Î#&òa Ùþ<ç–§@þãlWgWøC¶Õ} ¦¼LMT6@Ü4|äð;àÔØ,˃ŒiÍ º ¦ú™¿^EàÚ6ÌC™ç–NNÔY9=Å-Ê¢l†Ø0L‹ð”<¥yƒ:çûJñˆ©‘z‹"å«£Ï(8fáœGrîK]½DÅVƒù'w#&„/‚':=èRá‚{AåF÷ù©á'ô;Öt°–°ËP~ŒÿFzæ?ÇûíËœrïÓcy×ùiBì#$ôâ—ô¸Ò÷þëvý½Í‘Ïú|ªŠ/Êç\:BU¦]ÖA™ØÖžæÄ'@Eª4yI&áØÌJSh˜ùáä”ɨkš<÷/úfÞ‰Óìxú=¡·3¦x¿hz"Qµ©µì ©uí„zÄŸcÇ¿!º«©˜Av•vÅ ©?N€³up²xQª¬êð˜^üoüæììã:ñþ¬¸2·¬<ð¥pp½QG>é7øAGú4ðãïzo¥ƒ&¹DY¯-×8B¹u¸¢_ŸÊœ¢¡Aʽ7Xˆ9ÿ@Ê9Ÿà?P|¶=;~†µD>ha"Ç›ÞY9I §õ8r3Ù{#ðoäô££…Ê$'r‰;ó”àü?bùø‹`®l®#ƒ©qcÀ‰žýl™ëÛ˦ÞgjíÉz§eßuæ÷eëh|%Iêgäèêþ«Ñ ßb¿éiáYÏMÎCP”XF8†…[9ÝJ0õôõ>ÿ>êåcÍÿÖ³Ú¸©‡à9Ë·²²ƒufÀ8å¥S7[æ¸=ÂÓGQó/AŽ÷JÃvš»E“~ýIÖr8éœLðF»?¿[mÐÁ¥†KÆ.‡V²™¥1Æ›)—«¶{jƒYŒ•î[ug#&…Â\Yl8£Ý³ézbâùyiwc^Òj446ó÷”ËY›^.¦Nºg­ÏGS†*zä·C£ELÑ µñêé¶jHhg†Çé4Θ–ìE‡³Ë¥7õÂyÀX}&Ú,Ž.üJæ¶Ã“=Ž+‹ÀÈë´˜[þx #$ ýãæýž/Ä»ôËÏŽôtŠ‹1רAÇRÇ÷cÑ"f/ÍäÛ3èø‹%v+úóUŒÍàUhûëq¬Óê-U;r9Ýñ¦5šµ$en蟫#.J!÷ß&’ß0²Ž«a.øAÒŠõ3bñ+Üñ&W²ò­ÖÝ’ÿ:ãΠâÀ}¢-wëK¸÷ã„HIµ ,ƒ ‰Ô1k#."kÔ¢D\sô_|Çã{þü%[ÖíkRˆF rù•#$°€—‘‘óG3WVÁMÑMQðÓ8ã‹x”vÛÊDzÔc¥°vø©ïÿ”úþÔêì…nîTwñóÃdEÂ_ié¸Þo }|ÖÚŒFõnóˆÑ¶eÍ0ýíÎà±TD0Tž²™3ÀÜå”Fy,ƒÖÏÕÌ4°ϽÀ¾9ƒE¬õÎÈÕé zÑ—üsè¿|2„9AÝ¢K}_àC¡ünYu÷³×êÜ8Lvò}c¬X†aӡÈÅNÍ¿GöއñãÒ·Á8•†ÝóÎ$øMKs{ýtKÌf©Ô³ð³ Û$P&õ¾_ß#&1¹‰;ÚnÌT2fC — ºȽ¨¹–áØäãLvG÷~÷mn–Íç©—úpÓ" #."A­W;E5È^Y¯¡Q@ý1ðüÿÒÏ|öÏÏú==¿Ïý•§¡B¯_Í6ï¾ÿöWíÂiyü‘¦·TçåôzºÎ´4'"/ô@‘WðyF~ }®3‹ ¦Œ¡ƒâÚ¤?ɲéïSö™ýyâ7@þ£½?…Ô½ ˆ>N˜õM z*ª¬?Ô—9 A…22Kæ0Ú»ŒÃ[°Û@äUÛɘkÿ;Å#&[Fm&µaNÝ÷‚Œ8aú\æ­eˆÙ¤$¥HÅŒlŸÉK¸:µMNgMQÖpɦ¶*çCÁƒ[™´ƒˆ&Úÿ•Õº×u^ÞùBbZž2l]ÀеÜ1æ ˉ87ë–ÚÞU^0ˆ4a8ôÔpnGx½àðèuma´^¬¯Ãè9Ë몔m,ðp÷É]TXT •›À±šš˜[3üs¼ãþñ°¤Dc¯;µn®éj7,ÙðöÐ=#&/dP;Eî:®‡`0¬·¼ºØjÆ#&ýnøUK Ðñ=Ÿ±+ìùÛ¿‰ :aeL$“D ³(ß8zâ¬CÏóŸBô#.©äøöB¿zˆ–LÅ‹Ûð40áÝ>W\¡ßC±åa®O±`šÆl¯DÆÈl´~#>1J¡U8²GZëåÁÜ8I†† Æ#&@ cX`Œ‰¡ŒD€¸RAšZ,/™Àwêð:D ÁÚ4®úlnÂ>°3ˆA˜ŠÀ”fZ‹Jm/´w'Õ.k^ÜÑ Žè8Gßñàwæ]VVi#&S³í´æ/@éf®O°ô¦ñ¸'‚îV,#$$yöù 7ê¿ë$)Õ—g²v.P£ÛÑ{ç´zþMQ†ÿ¦?c»=5hÓgfàu£#$4âT´‹ýy²OLJT'*ª†ŠjŲA±„m¤\)Zcl™jJ¤&M+Pá“u¬û’Iš  ÏÈ@©!T‡ð²ÔFBûË%£¥‡òÿwûð8 Åc@ûÜŠY똅d@£ ûì±$„êDƒ¢Áú•ÞŸ™ü.¯¹¨DõÈBU­mÙégPÈ߃üaš"#.C×¼ ÔɯƒH¼„P“w.}ÝBþ²üøìî.ýÍ/"ª+­ON}™åß<{‹>á…I. D¡ÀÎÂýã<œF؉ÐEaÊÇ=ëáŠÛV =üRô.Ÿ1œßø—ªûÓp½þ§N^X¥Ñîgd2”Xl]kb’TäœÌ•£‰ ɲoøX·$‡}¦Ø9<#$ÜÒÓµ#&C˜ÔÄ]ªR¡X³¸nuÐʤ¹ÄJ.bL˜Ñ1—tRþáÌ6&ïÄòñÓºÛ½ƒãâðÏONòËâ…àk%%c“`;wm»·‰¬5ÑBZÑ»ø°Î¢¼¥w踱p#±¢D íÒÒd]Õ4‡ï;·r:i–ñ`#Vû&äõñǦô°ªµùaSqÆÐSiÖLˆ`Ãì,†£Ë*ñuÈôKnŽ&e%•êöP²p&¾WÛ×{›<V/9á³­â1¨Ñ¥â½«ÚS¡ä‡O…êt÷qñvÉ„²yÉá¹]n# ¦ìvÝ¥i3G©ºÖó­ãyRœqK¢-·—xµ¹¢É!Œëf|^sDR7c‰Ék–õÞfkŒ#.ùÚ…æg#$ÁuäÀù tìšImãuÊwMŽ·¨ö#$t(±(CIÜ·»âfyy1âõ†ÉÕµM ²“¼½#"å6#.j&ª„ê^ÍI#$pVfÀÖã!rGQâ`SµÐÜÂÂj™³¨pÙPú?–©£g¿Ù¨Ï·hq,Êgx6{ ’çÇF¯Ñ’JõMºùvwkì÷oîéÇ©TRéÊÂè¢Tˆ%Qƒâ"ZXoŽx=ºèÛÏb“ìe™s™›z)Eåâ%°ÔÎÓ'Y†|U(ÐîžÏ_#.tñ g&“Ó?czø>͹{38XI>޾ι#¿à°y1CšöaC¨Ì`ì*<Ü yn{2áµ¥µsB哪AèäQim÷‡uéÖ:–ž|ä#$äðÈ1-¿U6¢àS­î7C%(›Ž3&âŽæé°Mét¦ÙTôHaܵw tw5‹jˆI!¾¤6Mðwù–l^åŒJOrǯ€×)³"ÎÝ#$œ¥ç Ù®[3æÖ.sJФ 0E’‡4!§¦¸¯‹áŒ$0d&f¯·k‡#&¼8Ì/R)z‚ѱÐ:Ÿ#zdé!¹#&ŒlwI ²E‚¨*4po&"i9u%¡uQX’¤†òå0áºöGÀ˜ØÓ]š‘[‰ ãuL88+|ï§­õ>–Æå®N¦k)¢±F“›#.µR4-+УÌΜ¡ËEÔèrçÁ]ý9©˜%Цf%L̼ÄÌæe³ʳ,Ì’UŽÙžKéVÖ½—[B8°×\c¨Ë—ɪ„b‚É‚IJã|šjX5‹OÝå¿I«+ÍžàvÀ^‚'ŒÕ–î-Ï_B8ß<»ÒÌW7“®e((Œª*qå@¤©—^zÖsϦ&#ÂèºDæ™I×D÷ˆnA¬wl™#. Qíá«(„Âd³„t\læVÜž•¯£Žy¨7q37îrå ,BBŽà¬ôºÂƒâ4N¿_h¤»ŒX( TP’ê–X´TQ.åœô˜{ºŸ Ñ ëµPã° ÛÁ€cDxÑ&ŽùÑ|*Ž®¥’¹sh»µÛ5 b‚2‰¸&æâ0A ÎÒà{Îòd×$MLVs Ð=ŒWÉÜžòS™M|ý’ZÝ‘œÍ|ô@àG£ØYGµï3£·½È¹¬ƒ‘¬°ZCböv²•ì.MÔŽrv‡#$³‡ˆaâìC‘J®eÊ‹¦›(  ³çìч}|r;,@9ÊZT¹ï[;³ÏºÐ{ý¯²#$)#$ˆníâWËáˆ\ÚDÆóeØÄ_ƒÀ;Å•é剱µX«aC¤Úµ0$Îñ—…ðÃzBhÌynůlb_,$"‘ &ÜÌ9 ™æw¶åü"€¹&âʲI´†.ô>Ù j~nÍNq>f¡F1ÿ'šô€C$¾¢Ÿnäð>1µ¦#IY>æqN5‹®D|¾Šˆ G¤v ”BP@ŠŠ:Žêd#.tãoK '5õVÙ.•s¹#$… £—ô½Cͪ(jö@p*BŠŒ#$ñöû^œS˽ÁXçˆR~¼6/‡þüßàÏù.©kóÿÏ«#/el6i[ý?#­J"¡)?râAÛ®Ú®¿?íí_ÈVÖÝûU=åÔ¦i›$£:ÕýÛlÊ;®÷z¼‡ÀzÉ@Ô7zˆñì³õorþã©$̼¶¾ú>!ø E€$ƒ𔉢•½»¨“I¬4ÌÀ"!)ß´ÂJD59J¤ Éãí>\é0#&+i}pd‚ƒ÷oZ^¹Ú-ŠÏ….Èñv@‰Rö²‚YŠöàÇo9ÒHC¹1<…E¯b¸Î^G€~äùä’%Ú¤TƒPAiT#&”€›ÖòN#&¬…ŽVc» <§³%iý²MpªŒßÐÁ!L)‘- ¬>NÇô”‘feØ)tUKyRÞ+˜jý:I #$„"$¦‡Êz¥Ê#.ꋃdDÑjAT7 ð£ #$öRa#(Üo üC\È•/Lý£SK6$$L-Ã]¸ J+ ŠP¤ÃÙÊ»{m|®Õé¶#%¯=\°º5c80.˜HÜÌø6D[¡¯³¸á)YQã—¾ÇØ‡0Ù†Þ=|ÍÌNdëµøµëQTžA‹­ˆ‚AˆÐ`ê3:‚XE«çELÙQ¸uj%:#$h¾YC@c:‚«å­¯³ew›m}™ÚuÛÌÓ† î?‰UôT]Y† ‡ŒT`#$$iÃœÉæ ÀŠ®èÚ¹¨¶ZK-*Iµ¾û{Ûê#…@í‚‹û"‚2"’(€H#$b‚Ñ#$È€ ‚—âýpOG_•öß;…Æ`¨”z8ÉGÁ…ÛKUϬ•¶¹x¤ bu¨ ÃXS r,!€€7F»b#.‹·«=¾eŠO¨i•þ4שx×¢¦I1oWux½6¯Sz›šìJ7wh%/G]‹snl\Ý1ÝÈr¹¹RVWvž»Ä—ǧZ‚jJ/hƒDù1й›egÏù²’¤Ù!]k~öº»›Ð€'ºd…ÃOóÊt47KPV=¡ëfÌž¼/ËêŸÑ“·í+“TSç}TAîj2ÈQÇÓñÎR±ÓÙè_ƒ;‡xóÏsLXËö%à‰ß3†nÍùCÄv×E°‡.ãéäãË}ûüÜ.z½·D´ [Ç€«u§¸üžQ?³nó`CНº­Vç…í„. ½ásüyÛÂÒ¨³Ï½¡úÃc©Céˆ"ø‡ŽÍ{û˜ìË~ù÷é’oööÛRa…äòñ'zˆ‰`£ú¿±ÈáÒBvn¼jN|VÛà¶•d‚0j þÎL¸ˆºÃ thñ…¶!õ€˜t{’~)ÜôÌ „¼V#&ÕÙ o¬âc*S™ 5'çVãûÁÄ?M"»X„ÚŦÈ"ÿaÞ0",®YwoÍ|ŽäëmØB ù^^1@kžL {5Y©î°Ð>Ëjó†xNS2Œÿ! ßÎF‘Úè%ŠÑŽÀÈ¥J!E0p;UŒ“cÑ;£´êû(ïÎ#Ç@m‚j䘤2èWDï*Ã{3Šß´§ÃYA¢|™B¶*ËßÙ7{=o»x~h‡#Ä!Ü¿èwèÜàûhwÛ|nØ~Xzž`¡§ÉÃâðá[žÏ¿.Bª€wƒÀ˜?t¸‘‡Øï‘qó§O ’±Í¼$»ôã\´yûp½Zƒ3–J·O2ôc.ÇsXCÐþØ€:º#$ÍNq#&®¤=›¯v(.}äѤ%4™22ežÙç­oš~gb¸‰úÉ…D›xî÷‹;ƒZ” Îã­8Ó2+Gxzǡͣ…S­›ÞÍößmtÒ‘I[‰A6cmvr ¨’Æô<$o·$ZÚ½&63t'‡*@³Ü‡‰P„Q€ECÜRT¨(z‡Å—1Ã"BÙƒ¾ }šä )#$½A3€d4äxò²¤ÓéÏ•v^µúHn[1†¨³&ì»Ü‡­Éâèˆr)·eð÷௎R7Ƚœ’à‡û¼;õâÄwváu¥äì—%W‹wÊo[ÜæG¡Ž ¢|«Æz¯,ŽE«|GuqzøDmyбo{kÆ;À·1ß?ƒNòoºžèeç=P$ï}2ëÅèç­¥¦ü_‰»;/lih­¢Ë,xÂr€Â•qj£ÂDÒB¡@—(íFÙñ×=k _OM®Ž¼{ ÐôtGXÁ°ØnÝ1Ô]|9ã×[Se¹ç<ÉÓAÈ›¼¶KÖç[:òm¨4Ú¸r8lü6 õy€O[¶j²(é6ƦÝóî‹Sµ6&hŽÕ»(tدýË-fÔò}µƒMH#ÚF"àrdæfÓ#.gÑ©ºÝG‰°×ÊÙ³,#.}#&‡Ã3«»o>¼p–F!Àë±d‰¨ ådÙß[#&º»ùÜÏ»5wx4:§/#.(D{{8rhõSÇÒ€¤õP™úƒçõ[ÈTù 1#$BV¤%QŸD¬‚‘R”/³Ó€ÿ^ëÕ¦óÆZ¶sÓNò±ÚW–<”»'öúÃàcŸ”è8ñ†Dò<Îν]}Ñ56#.ÐzÊ·„#.YÜŠ¨ºnúó† [fþ¥àyËDå|Ç#eðu:ȃΣ6kå·hÚ.=ÖÛ˾‡¬Å ~‹7z¸†9d$#&g€Á]¨j¹7?gØø'™xn68»un£6C´AîlîlKr#Tš,®àµäÉz³ä–¶~ÒÁ×~µì8b!â›JÕp± :“cÕÃê]ÇÇtì h2Øéàtêz$ƒ«8Q£‰FJªi±!“råÖŸ#&úð†OsökŠÍ—ÆA9(ú5ƒêÌeKóHÏ4`ˆŒˆÇ“M’Ê)º¡=ÕŒO¿^#.Øê²DZd™“ tQ#&𽥚B€´ ‰4ÈbÁˆ±Ä8 xEt;/—wŸcßÙݧ2lmØ]Šj&:Æ#.½:| l’im²tˆGmüm§L!xCz{~†5¶#&èýY1,Š.ÂŽ“ÑÝW.kj¤„÷í7„öÚgë+,_8¼½!šQ‚‡*¯£Ž©'xÕÓSR6ÓÃÊ%÷ø†Ówé§%sUÏØ®1ÖNÍ\tj«# ‹l6Yø}èÛ¸B#Ž¿Ÿú÷/Lª$È:àI×ïɬµmP5' ;ûè‚s£)ÊS¼—…‚ÅUE€T«W1K#C6Kk¥Í”ÕÔÙȹ,î«]Kk·æò建lJ Œ#.ã(áZ¬qÄ#&CC(”DP#.4*(e£¢¢¤’Ì"±PÑäà!‘©$FF Õ%)Bm³(ÕYšüõïÕ¤¼ #$ß´(ª¨É#&(%‹8öØ*VñC¹dÚú•»>VýßÊ•Öá)ùW)P#&¡É#.*‚€zŠ(=HQU^7MO #.#$°…@ R‰èY#$QtüåÈ|¬ûÞäðv+ªÁð‚ˆíÐLÎÑí°m€e=&ðÞŠoàŶ“§¥lã/caÍh¦1ÁP¡SšÍŠ×„»9BˆMe†¹ü«UʑٴÞÌP5ïEh)ºÑÐÌ ¸.ù2­Y•A=0ÜTD·íiާiíÓM\vž[DïH'$'Óƒ¢ºú>•ž^ýÑÓÂñÜnëEÊ£TÉbQ¤·HÆ 4I T%Y¤ ¥$@"ƒO’D „”`úŽ#&'EmÌcÄ'ió¡%ŽÆâ[Z!EÙьâ#&F T‘Ó¾ƒ)A‘BDIíÍ=ÄpG‰'tC¶ФLBü¨ÇËvO]ð.î<¨ûClëçX[I)³Égìnª¹:ž\/T¯ÐÐ^‘ŸbÀÑ剦Á#*ò#ÅúÜmf¼ݰD¯Û$ÚRƱuN¨ªš¯F÷#${„À D‚ÛŽ!\#&m÷Ç·ý5ƒ²Q׬¼±”Úz mÅÖÔa˜B£!R3¹ Ji+“œž¾:ë©ô*ìèn#.Ð Á „’.Ó@ØêfÊÍT‘3 ~#&:×1¦*™!s·ÇèöyÝãäý0 ë·3d6Õ ž5¦pÌ*ò€.ÑBnRÂÄz… õ_>§Z$Q#&ñò2JZM>}]&¢¾«í¾¶carðè~¥ÝùÔ ´êú’ÄËQ­4ªËƒ\"õbÖBµQ¤ÔðŶ\ámú³U¬ÔªB»3Ä{ìø'@Ï]ïkÀá[ºj‚‘;ie.xœ/nõ&Œ‹‚¥ËjÙ¼^5&£eÒŠ}50n'Ñ NR¤+<¤*ôËk«úöit§FE3ý:¸æÌÇ#‰á€…Rª²{Ãáµõ© ïœêbˆÒÔH [†‚iu#&”^BB Ð#&xeYÊÍ €ÉKt„R0—t4‡h,$9ÜÐlS5F* #.áZt$%#$A!`˜ìHÈ›û\Úì¯70àϙ܆ CH%u‰ÐlËH½J‚P#$*#&.¾ef#$¦°y0[F“YºÍåØÄeI(ÕB\#.!ÖF‘]L41‰‘0ÁBÅŸQ¥­ÂyE±‡ƒ0Ô&pŒ°¥°£­‹€ ÍÓ0i‚i”[›rSe­ñ—emw«¯ÔšÀ%Ô¥†-Ü‚/ÕÎpòÁϳÀ)‡É>ÌOÝÐÃ4Ûd^§l”l7¢‘ÈMxs –$ñŒõ§g, S*^Š<3}&¨®–22MÀOc#.a5Eç g1à[úRIYo¡êî¦ø\O‰Aî!s.ó ríIMIÅKØÙ¿^£iñrMÀ6½ì]éÖ¦VÐé'vÎÞgå‹{FÊiʸf×·Ç_Œ·µZ†KßÉÖê5ñKÞ —ð®Ÿ>²0E¹u6%HqܱûúYå©Ög ãB&KI¹EªU‰ÍøúÙ"‚b z€ŠVÃàùüõ»ïµ2wh}…¯6ã«ð~Â7ú7#.?_‰ÏP=°ùxꨤ‘ý#.‰ßGqy„ÉgÀ#&L Š…RðSÁõênþ¢'ÕCL”Âua` @ŠCÁ0¤ØÀR7@)"'èJU…ÓP¼´\ÁT¿ Î-5Ðl£f‰ `(„[RõT“&èLIÏ×aâ¸j¢É"‚#MIyó¼ðÐh-#$Õ#$¦-â„Xïyúœ‚À=PÒ“…ÌlÄ&7eùíNÓmü’ÌZU¤f‰Û/´cé£ "xZcü#ÜCLKy5;ÜkÇÇg$Ny¬øé6 ë¤× pÈ•ûòmŒUÈ’¨\ñ1€Ä9£ƒAª€ð&Rz…ý/U >4Lž5ÇV;b’ÏSu”_ÖÉ2ªàWtCv»Ø¯$ÔȈÆ2i›K”³#M$g!S•`ÓbsIý{4Œz5 el¤T"Šªg}žÇ † >·WLô|³†¼ÛÙ„(àšóÙj Ôe^ÄBŒ*œÄ-5ŒGñ25!=1ÚŒÔm‹Y Ù#.b-*M#74ðË>ÙÁPŒ.Œ«Á‡#&cHÀŠEÕ5šæ\…Ó’Î(£˜†¼jÃr62XæÂºt«“#$Æ­Îùý}³\Öìì¦/´vÎŽ |’V{gCî§[Æi©‚DËÓ‰bËŒ,4iþ¬Ü½yÝM*$¸ðO³§p—xÚc4uÆÙàâ×Xgds°•;É»Â9ÌFÖl_âMÿ%6Bp㉺xi™=´±ZÈ6A-Øq5ˆØi"Hª˜ÈÄ4 ‘1pB!ÔÇ#.Õñ´¹S|³1ÊÈR;UxF&c(Äu24€Y”5”zy”ÁºÜ,§l8$Ô; 4ºhš#.›Aš&"S&kFÃA¤š#.!ºhÛCS 5\´« ^åëb}˜C»¼;˜kSŒir6ÞqHúáx3†º+Á‘ ‘ °†ÒdB˜‘3IT¥¡º8áDÐd¶,ˆ1`(S¡»[™­ÇWXŒËtB7ŸIã.Ü9ï‡ÇLLæš‘Òõ¤ˆÂƒšb±•/Wµ]޵H÷£lô7%9kÔ癑6á 6 7-þHJ‰°²‚œÁÊèKJë'9 ”D«f%1!²`æ‚P&Ô¸Üi WÕvM‡l&Ö_M†N3J)ªã.E$9¸#&‚Ç$Leè†f0Ѫ)r! .ß6xÃXR:¢qõâ#$e-ë¬È Æô3Y­­EÑ5ñ2fïwc¢èàÍðc±ŠD22ó„é5'®ÕÊ«#¢웘èLº˜.Ÿ<ìhí˜,UtN8ÚÂWƒ¾¦J¢³x¤Ð¬©Š,–çMYô5v1÷Ãl.#.gËÉö>q¶c¥CÆ™9­Óäó»mRþS¥µnKS»Œ¡™¦[d`:ûÛ3”‰#&ôt'k%ȳ¼¸¡B;Ó¸ànØŸt:ä¶zÈ'†ãvFYÙïè…6 €)Àî,#&ÎãoÃϬ™¢ùö!סÔ÷¨A6„(–"(€ø†‘bsÁhIŒû=´¤€kà°@PáaÑfbˆ©5BäÆŽ™$Ž`"i£HÀ#$²¬FÆo#&xK2æ`{P]4@Hß=¬â8jLJ7C1 S$( ‘€„#&I¬0ƒ‘ªd®í|›ŽCõ ]Å“`V¬Ëº Hzðjõ·çpµ :Å4pgKñ»²þw"À‚Mó­vg¤ƒ?­¶«ÅHþ(äÙ3‘nÞ­T¦òX[ÇÕL9̾'Š¢1x? ÄËEiÚQ©ÎGßW5 ¥81?1’ÌËŠ†)Ê_“±þ¿` ‡‹ï¤#.€àï¶Pœ™åOr‰ôD’$¥JÚýuÏ܆Z+I"¥  Q@@¿U à-⬀€Q…#& ¸ùkïD¸"_ÅÆ#.)«ª;dî7ÛÅW*¦J Œ30z¸‹Yâå4Ôk­÷=GŲB#.§aÌbF„Jd̦R´Ê™2Ì&˜¤Õ“5¤ÂZÒ¡ƒMJ)F%#.5¥{væ†Mi1d”Jf¬Ð¦LLÓ2E0Rª(d”¡¡ôî¢#i,Љš0’‹(aª ’‚ÑiQ¢ˆ2Œ"ŒØÅ&MiŠk!2‰ ÉHSPe*2 ÓA¦m4Ìi)¢‘½©dHðCe²»;l-Ày§'â€ø¶ù°LƒÜüÙ[U_Þc”>ìÆ¾_Ç4#úGd7.qÎ*7#.ŽO¸åxaœìë#$ÄÃ}yÃ`Ô윰boÖ~Ž3ƒjbÅ#&Þ’Ãù&yBàL„ufsêu§fô.ôh¢HÔgæ ôoÀÛ´&Ü\;­7Q.XÚ âæagJì®äãµ¢Õþ4ºÒ0ŸàMñß®ºÖµÙŽÇ«04 núpµÑKú„{ ¯ÃìüPߤ¡o·Ìdtm†8`deS ð¥HMl#&þ…Vø^Ñ­ùuö¬Lce´f6k&ÑQDl–™eHˆÄdOo{:÷øx™˜˜¢ýµ™` º×JŽ@™°¥ !¿&v|÷f¨©½\ã =zKC­/UË€–p{"G}Ÿ¨Î©¿•Ö g¾IUaÕ¨Á6#. DŠ©"H^äÉUoKüúÿ%ÂGÝ QÛ,o’J³Œyòå»K3Åîq"JÂkJX9£Ç>H’TFc¹ÜÖ#&ïÑÞyˆ•Žv³áÇü@W¼Mœ‡mU?sZ¸ÝRKm7ÓÏ®Üh]‡‚tg&S4í¢W…Jì§ß´÷ëV¾“ã×ln¯–Ï]vßÑKωr5†7àßi™”Þ1ã­Q¾ÙAJ½WÅ­ì¦NC¢ÊÆHé¬Ç—‡ ß1—-úŸtË:pñ¶+qŽ…¹¢wÞh×¢IthcdµØÏ%A\? ˆT§©ïß5É{á̼$¥aí8˜óLÍ'—vìÀÚICô¼{É(ìNIÊ"!UCVe£ÜR{#&à`@1¨ú"gd6EóS¤jÛ‘‘{Ã=Š ±oiÔ=¹àTý¯TÕd÷9kÕ<öïîK³¿¾E%å»âN¶­óÔê×^®ãxuöXúý^CKáöýuÆ÷ý'KIÔ•Rãƒßå&ð#$„^€zx¸ †ÊøèòB¡þödOF…â¼2´ø"6#i%±tî¹é¿)=´q§šj®¿QüÏßá>çÓÈŠ#$ÖE$?FàVu,rª¾*‡Ú›<™Äx†ËnÁWf¥I_NÂÇx‹¹ÕJ££®çC(K&|µ¦dZf¤ê#µ7ÁðÈ𙙣¾¸ ³Ç-Ð’eÃó¾°–†‘$âW#$Öº¿Ú<(Ë ÃÀÙâ7¬÷(TåL;™ÅÃ#.Ëþ’ŠÂ ‚méÔž§ÖÑŽ­0®5¹ãyY–ðkClɪˆkPƒÊáFN„]tàÞæ³Óøq™†ò¹÷FÌXÜÔn&s¨óPÓ2ÖÛ5—œŒÓ9cºKLPÎ&Š*jS Sʸí­ïHâV·EÇК̴ºëdÌ)Ñ]Y¦sµ·Àæq8ÇËÆé‹JN3¾kY.N0ÊÓÓt¼Yà^{Å)£om:Ê/Ù0é´"ÖVN#.pf<½õ‡ês]İ®&¦e´TYš)åÂ̈@Ð’g—ó§™|ܼ»Â7ÚvOÁÕÑ÷¨4··.±­…žÔåÒt‡)î^ Ñ´åd»ÌëYŒ 3^¤²9Ù¡q@Ò9‘L ¬ª´]—ÒAûÙü.ºLcÉõPNÁ¡dè!¸P#ƒ2ª‹Š‚ÅËõZ²1=]´óΘ¡#.áµG߯AÏH|´#&ƒp³ªj¬¹´`œ÷ÂÉšA‡Yã¦vHkÑwJ±Õ†Z7<I¦Ø10jæó;NÛWË5Gk“O‘BOF¾W­bí¨Ç<wÜ ã;amÃëZùY̳Ú~s‡Y"`‘_w&·tµ¾Ø\»ýîÛÄÀÆ" wßÊï¼–™/#&˜v£³·Ù¸T‡S…M´e¦ÚÒn¶O—iœËž}Iç¡{q¨5µ/$1ÁÓwq^4ñ‰4.œQoDJÕÞÙ¬ÎLâôb¸|Tëz mÐâg@:wC¶Û'´õW˜ù­±‚„o™ã|úcXڇ߃3|6ä'àFv(„ý®$é=àJJ1³CåáMj5Sž‘ޏ°Œí !4fqÛˆàÅ#1Ø#&£WnJ¢Å(qW¾_7‡n5É Õê ¸É:}‡¸[Ι¬ï¾õÚã%¬ÇQÃîÒòNÊJÓÚ´ &E¡0ã»LÆ92-oB܆¶“X1ŒÙ¢tÛV¹Á0ú†ÐnÕ¥rÃ"„Ìv”¡ÒƆ×LÙ2­p7dKÓQ"H"“UWƒQmŒ{†m5#.jåVͶã\½dͼ…¨¾×žç‡Lä1ÎÏ…£bóÀ±½¾sº6C2B™iôVCZÅÁæìõå<ôÉRl¹ÚÂúL[ËÂÙ”ÃÅ3OI}\e£Š…ÑÆ½ÒqŽÅ´Ç ÌÙCA´±7†S"“s$—¨ÇB@ªdn¹O\8"ÄáS¤3“™M¯BÝÜB9Fé‡\sÂàéµ9,ZÞ±¶êŽr;6®@ÄŽ$lÌ>îИŒÊt%ÉƒŽ€âFš÷5"qu„Jy#&…ÍÄÄJaÙ#.š©UmeP쇴ëIÀ ¬.TúÛ³]Ý3ŽÐïf›;Vï8‚ógXiwo¬¢TlóÊm¦¹`YlR¢CgËK°°ØÐÚ5ŠÄÇ-]œ­Ÿj†ÔŠ(hMÖŠAsƒ‰cc#.ÛƒS…%)‘C&Õ¦ybꟑíYÍq&­6%(ãf‡V¥0ܰ¡ Y}`¹ÔÒ®‡™‡Ö^ñœ˜fh!(kØ–7Á±F7w¯²w£.E…-èra2"“ý¹.2ß›ºY¨ÛháÛá¤Ô«óq¢YN.cq>àžiÕ#²¥³ÍF\ER!Üoœ[Ož¦45mÖi¾¼­š­¹³«¬3eã)™úÝáõÜ|‡ß#&ë+°§33˜›Ìï¨uÉ©–G_#$ÖfS¦@òœ3‡2L>8¦²‡¸‹tòðÊlQU#Ó¨BY²‘¼fHèkMîãsYkÑMhÆå³Œö5(·q.˜˜˜¼LW—Æø¼Å¨Äs=H¢ÜÚg)åRª¸‹pâñ·†®^n¹dmãÝYxh1¬K»=-™1² W¿¬Ä·îM¼“¦uá•x¶[ã/Wâ:zeb×bmãŠv‹G#&¨¾`»Ę‚˜µ ÑÙ#.ÖfLÆŠ$­BA&nÞ©ur&v¼Žì‚,…ˆF ¡BHN’o9‚åù°6 [8P9DSb‚ML× 1†€f˜yW\a­z¬96®îkfjWÞÔ±¨‘„È‘HS”YÁn$m#&:lÚÎSgkZ¸)¨Ú+vgoè‚ö/vm¹ÙZÛ|ç²áW¢I®°dŠmP)-¼XP  ’7±¥ä²ÃѪ>»:Ã:œ®R#$•hx:È2P3³7‘Ý:ÀƒjßRȶɢVž“ÔJ%vʃ¬‹Gº ˜¦ؼ7 Шñi”žP]x[n™C®™G¡„'(ÚÌŽ6“ï(†çŒõQ9c”„x|h5bƒ#$Dw@5äEßȽwË;‘ª4E±WyÛœÚñ_¼UäØùhå“=µ(Ѷ#m/&‘Ç”™Z a݆ٮ Âej¤Ê@Ù#&Á5dÊjf‚Òe‘24ÄÒ‚[%!µR‘rîš:g´ò[‚¼“Û®o‹Ó>ŽY+Ä`Il'-kG(uII (ªè윒ZE´¦¾odólt-Œ*N,ÃÁœ †¬½h)ц¨$5@æÌ%q®làÈ*qJG¹Ë…©Æ"çstè|Á… Öw±D§Š´ãˆpÑу‚Pg$Ê¢êefÄuY´ˆ&b`›ÀåŽÈB $I”3¥/TEˆ<«y®²ÝÛfÁo¨ts¢e÷]a·…¾›ŒíѸ-—(Ønw«ÖìkËÞt¶C›–€uŠhÆ(†w44I!¼ºØÐûaÛi1*K[m+$À­œF¶‚q¨ £`1xÀá—ÝqŠŽŠ!0ÔÑnH&isŠÂ´Ò™´ì`E¸qE »ÌN—Ì3ÀjsÐ4Êþ0“:9áF•‘A¶`‹.#$žÚ»ã±ëƒ"ÂáÑJž¨@„ìÓSHDÁ›MNBcƒÄBl¼!)/DÑ#.a2‘†[B:ÐcŒŒ@æQªÁŽ˜Z.KDMÓ«šJCƒ 56:¡“z‡8Õbê‘BªM×ÂñùrÇ7]ȱÖ05‘ßùJ‘ƒ;@‰°HŽÃ”é]¼z†/XT®•¼ÓKd‹”u‡S€äÜÇ5ƒ(7Tµ¬M#&®Å4àPÖñ6ÚàåvG"(a·•8Ë & m³2±ÂÂ6$@‘™34¹ƒX†Ã7#ìÌÖa±u„ál•JDC¨s”tÀ11Êèp #$s‹Ää4@;ÓĨØxœhåF+݈á%Ë Gi©¬–@ãš#$šLÍN”g¯¬“`¸˜i óiÆ-–á ÝÆKŒÚƒÄ°È‚IƖꎃ ÃÅ(΢‡TÌ]Ph½}qÒ¶†´ŽH ƒ`eÆJr,ͦ`ÓµÂ`Ðv\Ë¡šìº7nfºadÔ”Žq¤ÑÒkÄœ €ºÒÑs°Ó°Ødà ..·%o¥Í§`ÞÃb[T;ö h[@#ÍmAQH ‹Ä‘„bŒA ‚R‰û»Êò’E!W1x3iš”°SYBå÷%" TF¿—_%ÃÀ>“ ¨;UüÛªàhÕŒ0KRÕ-~ïq¸úÇõ™cœ€Q€ \9ŽÑ´þN¯ÆÜ5)¶ß:®·UØÃ`1†³B€0 ¨`@Rì´ò˜„rnmŒy˜mÊC²@¥×e±$øÓ¶ê©0øgÙAïÛx•¦Ðþã™ÁÙG§‡Ø‡À´¹¸5r”Ö¬šéŸQ™¼¢‹±ÜMªC5ËÞ–âc« G^w{f‰”£ädëKÚ÷Ô—c®c3²tÙ[TD&ÂM<µ”)š†#.H9Â7aÚ~®&ÝçÀÍš·«;˜B*Wn¾ÔÓ•ÀøãÇó#jz—}çãªL‹‡ç?˜ÌP.ƒÉÉû+íÁ³ýçÑñc–Ñí̾B6Ȟ˸Ò9VMèwæ©¥¦i·’âj‹ÎíwÁç˜ÂQ¼]YBð=¾¸-ÿ{µ°bhÐ1D)¦ ‹õªS@£¦ …­%˵¥&t#™q¾PLXïí|r3,;É((†±£ÀûØ™Š#$D5‡SúoËäaG“ÀŒAö5¶±·M¢;Ÿ…÷õúUö­jmL[ÆØÑ¨ÚÑm&-2Mƒ4Ì´j"‹lhÆüZ0Rˆì´Oˆïö¾:g¢§p#.•Ö§#&]ë’d.òèê;` ¬ÕÚž¨,YÔ‘¢$ùZÖ"=]¸áY 1L°’a±¥¨Æ£ÊD‘ˆd\0#@6,©#&³›„ñu§»½ìÔêŒ`ëz¨²údþK° ðv”‰É0Ê?5ÑbT¨«™ñÉ«#&á'©9jÒ b™#$lîxóÉ\exfV˜úM#&1YK-Ñ#éR#&þcŠ"±1Ž´‘dŽÇ»XåéZ¹WJ¯“oRôÛЛ@4À+vLhÙ”¥4ÑP‹HZMS& ¦¿Œ×*&ˆÐ×;•¬œ÷¡Œgê:ÒÑŠ>²6ƒON°î·5|;­ëº·#&”Õy¼÷Á/ó&˜¡&#&pÌâ%µ1¾‘r0«s|œªÚ## ‘qõe]rgÜRšrE0„N¯©ËÝ퇷Lk;ZBùCZyU3(JÒÈB»Õ(oE9a£C2ï¾&ÈŒ4 ¤¾ZÊjh†©ql"¹þŽŠÊhÛ##.¡Q3¾ò„ B—ÂŎϹ˜uNi‰jĶ:›tØÓÜ(¼uCLx-gA“”tX#&nìçÜÛ[†q£.‡E?ë¤áš`éøvÁ:ÒIŽG4d…NÎ<6Ïb †°0Èᬦ*“U)TIB¬c;5Ck¼+£îê°Æ,5A_I8˜ªfSXjÂwƒÔòØš.!±#J ·x¸!ÚDz4µ³Ê(ß,í[ÓÙŠ œ¶ô‰#..Ù`Yc§+7n®ÆbC#$ktA,´¸Œ)Ã"#.`ºPhf`\/½]h© TXI0u@ÄÚ!ÆoÙ,×pJ5340‹@ÿEŠáR<:÷#.—P{çÊÚ¾ó"þÌÉ>´ëÓzm¨²2]“#$¦ˆ(OµYqSí 2ûî ŇÔOÙ¥#&ŠøSØØC5ÃbS±—Ûjÿ°Á¶#.=2#&7º ©»zB¦#$Dt8§˜¼WÍ©L'º–Éå*ñ1€Wð¬[hÕQj‹[I[UúûD!"BCÒ÷©š_¨C;Ï?=yʪ1w©sâ©\¹câ'P‡¯¨þh ¬ä¥×zÞk|t¶zÄŒ¯÷ÀÜmVV¨ê¬ÞÔrØrªÝ$ò(l›ÙÓNÎñ+dÓ/&Uª™Ñ•*\RËYÃNûV›i7fÉ7C%o‹±©kÖ£E]ÆØÍœÔq‡M$iL¥âiÁã[xR@ÆÙG%ºÉ0Êo®¤mê·EfžiåÚž«éx·×$KJÅ4äD™æu#$¤êË–šÂ ÷´œ:qrFÇ-N·Â72D3hÃu^1—Oz4—_ ‡F4q÷²Ó­:c“¨æ¸°í¹u¶»8Še…jSæ)1k¥c#$$ t¹“Sq mVfu”œb¨i½ðFÛy4šÚV./|M¾rQ˜ãë3»&=¾‘\d„ÈV\Cí«­ì4(ÚwM‡ƒc&ïI­U×B ït5™.n#.2C(‰ÜiïiÀ깤,…c$"1a¹9âHa7un”ê¢ØÕ®ÍeRÜÛ³fm¶÷Ñ QÊF«]¾¸Ú¥ÆÒÆER’í<„¢+zh#CªBÐËC$ŠyÑQ®·6Úu»kCM66Þ­agDº©œ †q`D„@?dÄ¥­¢†:/<ù$OX¹²ƒÔƒ…\[k¾ÎJîãUv‡;`‰¢#&&ÀnHÆ8ò•«d©’7E,ƒ(îa[@li(1425tKuk´šÁºWu×w›År×®å-;O7]æU䨘ҷS[†£]¦£hÒX…™Oôèd¤Hã#.QAb6j@ÂÔŠíçÛvÜÊj».ʤ²“"l¶6Æ´ÔŠf¼¥®–ÒÍ-)*–eMZ>Ÿ‹+ðzôMFÕ`±S4m­¡@$U‰R¹og £û9êz„uŠýMS·gR/dÁ1TlÀ`ÞÂ#$ˆQ8[@‚—bQ‘Uت ˜‰Ö,ãôDv‘YàPzò¼õŒ.‡zôý·QKÞ9*lŒÔl¸xf)ñŒÆÆˆ¢‚ù|-˜A`.=¶µƒWø0ök¾–6–¦,[t€DŒÜ<,[(׫“\l:ê4Ûg‘ðƧdn¡¨y7©6¥N ö¿……ò‡*(ª„h‚H P £³øÐ¡lFHY‰‹€Kº”ÉRqâs¡ø\R–Q®õЖ)‘†g?¿EõÚ?†"9yø pÝ€2!Mi­KÙ˜ˆ<«…­L¦Euž0;Ñ \#&E#¢#$°ËÞ\$6韗÷·LøiŽ£#ÈMØ:#&Ø®Æ4¾¯Q¢å£¿oV§a´üÉÐ~¹=Xo}¸^hÈEÈ€‰—n¼N©_íð4SÁéˆÍêXi‰#àÁ Úi#$λDÆ<ˆ$?kd6BTv¼ðgØ¡sDpaÓY+-‘æ6­‰6•¨ÈL D±QžÄVÃsÄâ¼ùÆâñð[«¾È#$ùo§Å ýiïH‹F*‘P‰ð|½KèûŸQ±¼“Àáe¦#.²øÌ£_³÷r.I&߬¢#.¡$?Úôõé´d™Í[¯,ÙqLÉ&‡O¯E 9Løzô_sOc„û*a=¨ÓN‰ÒZe2ňˆÀX]xГI$tª’Zm,ҚѶ¥&ªWÑä½¼6¸L€jEE’0BD«ÝV®jØ×ÁÙ¬Òh¥^6ù÷Zò–®šÛsb¨¶Ôa+} %Š™‡RŠiM€um)#$rÚUo•²Û–ª‹&µ–ÍFÌii™‰m"…(´¤Õ)¶Ö›fÌÚÉm†”ÑJ”Ûâ·(a€‰)j+*‰´”‰EšRBŠlÛ)šDÔ™£a¦c(ÅÄXÍ#&¬¢›¨–IV-©J ÔT¥J”¦J¤­˜ÖKI¥ڊٴв$¤Æ‚“)0”É4É©fªmˆÕm"F´RÄÍ©2e–¶¥šÉ“EJJm²Í²‚‚d •H)‚¤R`ÒK{Ík]6lÖÔ¥­e‘ 1#$*"]ˆ Šº-J[’ª-­"Uh„QDC«¡RXÕÚO@qv´×‡[c”0é»eŠR:„û·¢9W¶~pcZ¯v†ýÆ0zNÍcü“ŽR‰¨Éï†ò¬zHâ#.'{†›QO,é%I‘à#$¤yFäoÊ–¨’úp×ù<V±µU~<ïtó†èé3K·¹¾a€rY_‡V5Ëg\îų@fÚÊ Ëê{Lµ`QŸÛöOEÝi7æv)Fí%ƒ„ªËV–±q7šܶ½Ûô#&Mÿ¯Pgå¥÷ã“ÝÙ”:Уø Yd%æf#&¢DÝÞPØ‘Ü#.s9—LŽ41¦$fƸ(F*ˆ!F ¯hpÄ üÒ­ûŽ`-΄þ¬>*Ÿ¬ß·#mshnŠtéÊ#&•´UR¿wÐP"zviÛñ\g²Œšþ¬ÉùŸ{FE1S ö³¢B^‘-Ó¬,Ï×{kOiyΤ¹5ÑãORà¹`ú;I¸¡»Ì‹Iû÷z‰qhÁ*v#&%°[¿Sõû“@x|þ÷ú°BNf;¤ÎEýuµYr$~ª§D¥¥ú3ÂÀNÎ}ñÕe·Ô²HÖÌ#øÅ–Qº°¤ž¶¢‹«Îá‡V¸]ûUÝÕ‹RšŒ4ÚÓ8NIÂ1d3³˜0ˆÔ>㎣ž"ŒÙÁi”^AäA,ðޯͳjü=/Â÷õ¶öí8ÅH<»€#$bÛª0m¿«ºí¹S‡³µ¾­B€àQª-Á=a E$BA¬‰×9EÑz]¸(Ç)~¬­Œ¸9tµ`«1a}[ŽÞãx " dí­Î†ya–&oD¤ƒ:ïº\½ö‰ñÇwÖ8‘:ûùšŸQ›¾Ñ)úúlÛñ#$@ôèKŽÿ'·Ã®Öq·¡Ø7œýÍŽú®½¾TBÆFW÷¦´Ô=#$ÓP?WmÜï’ ²b›úbØ\¶,çˆP¢‚Â’‘_–*â?¹¼µ) âŠa@Å"BÉvTƒý?Ãvì…³M*ÒØ£hee$H£Ñ£9¥I cXÑ #Û´›#&xˆjI­Ô]´`ª¨#.Ëi0ÌF–iDiÔœlÛG†&y"¯m xHÈ&Œå„Ušcq`Û´µHFsba2ÁdÀ SÂ[ŠÃqÅ,TCFm,*ªETk¥Ò@É‚¥¥ŠL*cò¨Râ‘¿J#&PX5Ð#$ØézwÐQÔâç+ѪQâŠÄ@5jKÌêˆA®Û(¾²zµv 9†èø!«mPj"Ñ 5¸éR©†l«F°jzdûЇ&ÆJ„7ôg‚xןզ¦Ãâž3pgЬî8#&aÚ¨¿Þ¾ ÜiMññDÓ PHDo ¨È+à+}Õ¿@ÿ6µm·S]RÄOˆ¿iõƒ£­Dyع»†Ý­·ý]'Öe_c£_=*LDúóÊwÝq6èUü) &3‡&Ÿ—wÖ‰!L@íKU#&-y¼Ž97&|7ïquÉ[ÑÇËbè±î1}Mo²9ˆŒ˜¡¡#&Ê`“W’Íô’›ÊY.´hø ³@ùê-…níWÕa¹A5„(FÿµRøõÒŠV>PÞùvüUh˜ù9‡˜ÉÈn‘fèV(†‰5@´”É&O•ˆ–‰>žZlY‹ç~ôƒi1T’&¸ÂÚeÅ÷K'$À½¨^õ_Œ­€Á­oRWŽ2ó¼åÔÓM´m^­<»½4@i©IIàÑ娊Ô47 ¨ F2e4{š&pp}lìn?=>ªK|ÅÕ€t``1Yš1#.¡‘jÆwOê9·¼™˜¢ Óm§…_g²§r…ps‰Ór虢ѩ!2pã|hy‰¦€Èœ¤â0Ÿ8$rÞáñtu>øú3 UŒDì=9Î¥’ºc[eJ ¢¤X‚wÒ˜š×–²’ÕÛ3Þ¦|È(`Eb@€ædPñ [[(³v³ZbÊtšQ4Ç­•Pc/žÚëã¿£ù;÷Aó<¿#.¤|’‡,Ýû uÕGTw,y§*ÂZ©C‘OÔ@F² Ô‘æ‹r?g14ÌžßNqi9Û¤#&iA¾žÞ˜ÌˆÑ¨‹Â™4L56ºl„*7d7ظ¨ÂÖlz®ó’ƒˆ0äFsúghí#&#.2w…&Ç#&Ú5j¢ã‰«`f4^q퉦©±Ö;[ß=‰r%Ãqu²j&a r‡tð¢N•XŠ«¼'¨½€x}†ùõ:ÍålÏ%BãNóº7<èÀoƒŸ`¾¹‹£Væe¯n¼¶§–¸ˆ%èºrªö®Ð‰¡ôNÐ6ÐÚNˆ"2P<œ³™¤iÁ`T7áF2Ä#&ÝDŠHÁu¼Í¥#$Î:°TˆY#J©Äbß‚ÅAFÕF4Ö!B7bè2–mÆÀz°­èÓ†^ÝØÎqí]ÉçuÝÞ³{sŠTÍ]Ê=FÛ“C*kTŠ"¢Šª†¬Zô­´‡‚S"¦•Nº“f°’4ÐÛ]!ˆ¦Ÿ23ˆ‰’HAyÝ„e/;\æâ`G ……R$`”ÝÑ[FÚ.£ïeIJF6Ç‘@€H6F¸kX7 ‚ Ÿ—mÚïUFh”Ã@]aÀ`¶˜Ò·*³™YîâdüˆJ…y=‹¬ïÕ®ðç%¨Àf‚ùRŽè‰®øÏr÷÷[l7uçÛÓ¯‡•oʇQyÉÀ¦qf¼±Jxan$Øoi!z•Y¥•6Ý‚XýŸ´ÄPôïMAÂ…pœ!·ÇÑŸR5P$tÆ2^'>ð=™ž ë¯»#$-x€œšwbB;íÂ'n3Œ<ôŠÝB1!`½üŒKß›i…ùõ,¶I´€¨“5J©}¿›ƒ“¹‚YãØû#.F¯ –ð#&IņU0L½·RH¤ £n.@RŠ«e‚6üªì‰™D„ï^kÖÕëWR&ímcµ_ ¶×¤V“Zמ§ˆ'½ïçª^;·„Õ˘¡¶0eLAPÕ£J4­)I¦ÀÙ¯®¶däÕ©QÝ÷˜S´Cf9˜ú¾{xŽˆñU#$åÖxˆk5Ü“Ï­ßÆ=$K5Æ|ìH§k]´#.ŒŽ•LFôRô¼a5ùWYÍ‘ªþtù%&Öeª¿s]v£VMª²3õ³‚#$à)@éTÉ ÍÆàla5@`ÁV%(¤ÚµrŠU4Òiinº.UÓY‹QoÙV®F5¢ÚùR[Òô*7€±aü¿š­ETˆQD1BÐÁFù ¸É@a;_,Pæ¢Ì¾üid™uÚíÍÊIl‚„¸…-8˜Îœó»²$a>ô5ƒ6øÀÃ@/SfÛ€ŸÍ÷¢º×@üŽÆAi£T+#&%-¦¡>Ž>îN}¤å¨Ö—¬#îÞ’Á@Ù<NðÕ®pvå\“ß%ÅûB9mF”¤êÅs†¸B;dŠZR8dTüåR¨Ë\ÕkAP¬Rªç8eÉ‹"‡‘­MnOM±¿•ܺW5r³Muwlm2*D„RA¨%@¨ÈU*#H ¬7tÜçÌ\šžMª€fôk(<*Á¯C…pÂW\-[”zA ¤ Er ¬¨ÛÎßôñ}½ÞM|r-uREé°E‘n4dê,Ŭó ×ïݰS8šgãÉþ8@0¢Ãk¤a»«¯ 35ä8h$êöüMøÀFH#$TAC0k÷ðLÉ2oçpúï›b›Þ š'¿f»R‚@L‘T¢Hûä8 ï™…«èþ×()àŒé#$A#QD°Žx%ᘆé²lç™hº <£¯@é3À0«–ð6£wÅïŸäúÿZ|#.#.•ŽœùþÄÀ˜- ßÓ´Ë‹ÏÑ#&õúuÃ1®ýŠ…•÷}AÓ·Pè ‘Gè ÎÁÐØý#.jŠlAñòg€‡ÇÐúB/ùNåÀ‡g¤Ì€ŠI 2¯mR•#$¢*¬‚,"TT¨ ˆ3Gz6,ÀP¼F ˆH„ArZjÎu¹´ÂÙ¾eÇüH`ýn0fJèÁX¢US‚HQo]ú—V¤ŽGqÑ:][Ø¿äÚ¼¶+ݬc‰µ=)íJ4:‚qx…§²þq7óR@‘RA¢ Ëñ-¶ôîOs%¢‘BÐ)ѥШ´ùö>'Õ’]â•F&+?W™Ë‹ À ÂƒÈ.Hf—ªüÆ¿Eê}§´ÅmîŸ#&Áì<6ø|¢Dø­­Ùi_D¿J!Õ°Ó–]Ùo:¼#.=CùUˆwP;2$„‘¢ë#&¼Æ¾;)k%#÷Ô’ê‹%T¦ÙÍEýi‹õµ’>8äöÐgdqQT*ˆ)Ÿá»p%9`’©L¥€$ÀîÈaYÐÂ…Ô†Uhÿ2^ßÉaWZ*@ZT‘læÄwÈ’ll3×Pª¼rÎíuÈ”Ëb•mqÆøÛ2anèÕ’L`‚Œ™VºIšËk‰c#46*þkš-Ÿ.ת^Ä6뻽dòè+j³yG‘hÔ+Cc•’´”b‘¨MLcjµÕ¨Àv!F€m#n2춨„)&Y#.³p–›2SŒPa D‹²XÂcz‘InP"’›h¶äc^íâ¤Ú¼X«ß×kÀÆFFs•ËÚ°bF#&pÕim‘âÔcKm`Ö™lh’R6V|™ÉcxðÂlа¬1†Ú­&õRzʰ$Ë'A­´u#&n$«lm´¬åÝÕÍr×.ZÆÒ»¢Årܪ+–åðo“Ö•{yײãl¢¤Ù‘«#.DIxÚÃDÚf’ †cV`±˜ÂaF“£:‘vÿN³#&2ÂF›áÀ8kV&4wÈ7ºDíåðÆÄ=Ãz!‘Æ&ðjÂÖ±JÊûTÕp#.AP©ŒÓù£Þ@DÑ¨Ü¼ÖÆ¥K%FÍWjŒi¨“%âÜÊ¨ÂÆ±Nêµu6ƬcUvÒÝm„F&ˆ A…¬‚:è#.À%©RD@Qü 'yóO߀§"ðQ!c!”þŽ/yù¾e€ÕûpEˆxË8Îwüë³ßßV=ƒ­”XÁ¹Ëìò3\ÐòÔ‚€Q#.ªÑE} ZJŒ#.…©Wåq¤nÓ"T`ným‹„A#$ªábaA‰Í}õ²·ößË´pâ.#$&èTÖÄý‘Ê#&ãzªˆH’#.ü& 4¡é·-Ëâ¼¥’Õ%¼íÔ¥K5\×5¼m}ך™E²V°Ù#&Tm¯J껪-zjö»Õ‰¤¦ PCT²EY³T‹10¤›jM­IšVLÖà¶ØšA¦D#&¡"‰°‘ç¥cñåÔi¦ˆÆ&¶`“(­°°évA¨%D«‡â¶‰….I~3@!x!‚â&X‹ˆ€e–!(BJá¢#kÞê˜'I˜9@‚ì©øŸÎ£ØFÄ`É*aöQØ×cFBðܰâ¶)"ØOrû䀹"X|zò<|©Ö3Ž/önö-ÊUT |rõyì3ñÒ¡ Jœê„è4\T-Ø\dÔ5â-x2¨áÿX ~Ê3Kçf³…~ïîhe$Ɔn…ðRìu1‘(á¼™êV;qhæ Ã¥ûQ¦KËÈbÏш£§Úšõθl2Pù|CQ8%xiŽò½}7ªý‹QHQh‘¬Ef -II“M3i­¥©-£$ÆdkÞWA&Ì®We¥Ÿ¼«rÕ3-¦ÌmŠE”ÖI­•¦Ël­²ÔÛK)¶–fVÞ.±¬iDU6¦Ͷš&–ͱj¨ª ‹-Zb9 j?…~TÂö…Sô—^[ð›%²Õ ¨ÂŽ@dSëJ$Q¨©""Q9µª*صԫkšÝ›Á«ÆHŒ’/èR%à&ÖÂA'"v'Qß×ÃDN#$(m£d •TÈ)Qd !#.x #.½p„OÀ‚ìŠþ%ÊuøÕ_Ó©ˆµïwE™ÎUåÝ}ÌR|%>Ì`÷LÝ êI”FŽó•‘ê‚ࣳq¼Áx& B]PðÀ§£Å?©Ñæ0å$¢)P#$öÄñ#&jsC`~¶ d{Cí$ ˜€Q®+h‚¥D¨¿tä/jº#.ö <‚ös¼@"òçdžXÈÛ»eÍx,´#&–us #&ø"Îÿ*–œi߀A„7¸!0Ϥ;J /Ì–´f_ Ùð7Ç?Ú— ƒ@>õ, #&EÔ-p‡SÜO–-ðóAy4†‘žG¨™HIˆ½Iè3ˆs°sÞ!ÞLI¦6ü¶ê#$'¯¬Gy#$ à¶bî5íµ°f©QvA®:J€~¾Ú.#&ÀƒöèTÒ\Ð;aùƒ³G,þ—» åZÆÔEihh’8FÖ •xZ‘3GWaQ`“Karå«Åâ~×qѳç£Ú‡åüK¨}°ËJ3êÕ‘­ C¥j¡~~>¸§ß'×Ц…ã°Ëj6OnâqÆQ:ŒÄv`¼™ñ–U42j*:c×_ÏÈèÅH(ÈI{þÝxqÏ·ó|ª<è¥æÔ!î#$å5›ò6·Ð,ä¯Léû#&ä90'Þû¾î«–ø5vNz»Ë^QMÎ7.ÙŠ…H²RŸ—¦:íZ‡Ô›’êE ,Q–mö5ÈeIeë¯'纩ßÈ0û¥At#$‡Oïÿ«È ²å˜VÚ˜ÓVYd„`ˆ$c<ª ©@Œ7%r8†˜Tì ©#m4#åîöý˜“E#.cX˜"ëQJ`å¢,-ÊéÙ»"$ªñ¡c+ i%€Æ FÂñ êX A5cYi}ùè{fÙMj ™èªÒš"ŽÏCd± "e#.d‚ʆð‹ JO$´ðRÈve’ãvm5QR$Äd@4ÁB‡—צ1QU-T©Ýùñ÷ÕÇKÍ%‚ØÔ¥éœág;ÍãCkƒ.&’j#&‘0ŒÃ’âF#$1äÉun‡A8bTK%æ \i¸¹²Vh± )þ díxžža,H2š~kvÕ/&©’ë¥ÚëyvêhŠYH„,‰L Rñh,ö”õ‚y†eš_W£Æ—Á5ª:´Ë·´udòcIB‡“ç8ó†|dŸ¿§ìM}ÝݨÞôì+{ÝÍ]¼.Î÷º’.ñ‚?ïâ„;Q"½ú‡m"¡&Fš`²VäaõZÞ`tc·tר盬²RÔ‹Ü/ p&ü¯‘ÕÃRa"²l•zk˜Úd)©mõ7ôÌðÀ}D9ª,‡D%$)O;•ù›¦Û˽n¼«•´TV1ìâl B/±øzÝZæy'¤ÚyJcæIÍ€!MëªôSÙE©yÛ¦¹^yÒºóÊÝ㶺ÄÒ¬Õ¤¶¨µ©6ªYxòQm<­¼»]/Z„-H”GÓÔuJ(-ƒ!þªWlÔ‘wÅ0 Ø`+i(¢ª ‚B~Ñ7õÛg÷{V"¶4†4Àµ#gÜJÖ¤0È 3U&éZxo#.,ѽ4]u•Ý L¸f5VÚB}`ˆÕpa*ehÄiR¸‚ \j $¶c@ÆiãÍÚÃRi‚n±5-î’lî^×k´{ÚèåÕÒ^–ó¥8…ŒLL6ñéšÕ1êk7²œÈrÈÓÈKwL:ã´žÛY«!PøMAÈ¥8õ¸ï;#&=ùe÷ú#$!Dëý'¹Sô¤‚óƒL&Z‚ÅJ;£h'Ê#&Þê|uˆU¬¦Ûy¼ ’LKÙ úŸŠÈ¶‚ËÅîî=s<îj6˜I4NJщƯbö"NïNÌýdË=~ÌßbaX âkˆ¢Ð7ª‚ ¡#sÄ äuäœr¤Åí´ºã†88DY!Çfyß³†ô£<ò±Þ|žÊÜCD‰¤0IKXrŠŽÇc|DPtÏ _²g) ÒºO“±µøO‘Kã.öú¶Å¹ò 6Bï}#&¶¦çð#c½ŽzxÕv9‹·‘Ù­¡€׿ñÄsþ.%ñç‰R©È¢`£¿™j¶ ݤճí;üý‡Š†½b/ ñÝ‚!p6m==ßmÃÝŒ#.NÐà#ŸÒ#MÚls44°†+ÌJn,нðX‰¿€RÉæÈL±èeFi5G:$SåÇoN˜èoš>›Ëœ#.yqÌ#&>ï”J“V›ºô‚!ØÁZ‡,Ì‚7: tRyjoÙAî’à$ðÀÕ*‡#.LhP Ó„nâäy -VÄ_­/&ŽF½Ñ}Q"°Õ–’™Ó5ÌÔoïC¸>\MBÇo‡ÇhH¢þ’ÃCGp346u΢yÿš½Áž®DĘƒ‡wJF’#rv”U«vÛ#&qÚ×\Û»sn®³ji«4­ggVšM’UTÓ[n«bÅr™x¹yweW•mõe™‘d""ÀT¶Î³Ó#&@ùÕ=<Ê!ˆo¯T¥i¯aJ#.cPÍÓ•¸fæìÛØÛF4ƒ žœúáYŠÅÓ=·³A¦.®¸?ø5:(7ǾÒ<,Œª{lˆ[݃hCC0Í$o°Û¥.­Ò– ÕmD“sc£ ðô¸Zjƒž‚æõ=a}ûÔ³“v<åëÖò×+ãÒ~#.!/ˆI:ß¿Éày'­·óúù_öë¶Š£+0Ð)StÙ›ÝæÓmdÛ&!Žia“0ÚkÒw­ÝÌ›Î}&Û8†r£5­ßG÷~ïóÿ_àße€ZŸÐ„¡K9•®#.Úˆs`ÊFN‹‹‚kŠPX‰·Ò‚éJËÞ$}xV`§BŠ¢ƒ`ZÛ ÎAR Ö^ñ D…X7Ú6\k#&kóæhsP¸BäLÇ4#$jÒŠ›^»@8Š˜oPÂD ÉóÏ#¼6®ÙNƒ«‰ØüXÏ#KciƒÖ°}àvt;Ũ¶c£Ÿ¡ñÉ£9ïŽeèbcsŒ^ƒÌçÀ椴ê }=í}³€Øò˜òäs”„@ìãÏéûºNø?KI%:—Pd³0çÃZ͆Ýx¶ÈQGåH_2’b6˜rdfêƒM.׊`@_#$ÃØÉ5ä.aH÷8{,«8 pî›ph˜3Ô‚oyMáÂ?‹„F!ãw¯^¶Û}½q”"n‡™t&#ÛÉIF|‚‰œ:I N•¨>¬¥ƒ]T¹Çó‡ÚÜñÒ,Ç#.$ «hg…JrÈÿ¦l¡”ÌñžgOâp{`%%–ëi&bÿ\ 7BðŒ¢ÃOã[5hñÞ¥t,d4qûŸv4†tØ–¹&AŽc}ÜéžÝk;@³³FÒ8EOhÃÝF 0,aÖŸ^ð‡–æìIAµõå3½˜* WÆÛËUeÞCÄy#.X^z¾¨øéÇÔˆ?Q„@: ªW¢Š„!“¼âr±`Ìðâ¦j¨(8Õ×¶ã§8¢I<íµr×OƱxÖ-Y5’¢´UlmhÚ-¶Æ‹dB5I­™£kÛW‹\Ê’ H#Q#$v0ʰS´ÖsÔ“¿çm/„õR¶6`Ð<ˆÂ1d1;7*¯E…Ül-$¯fÓÔÛ}Û÷aœDÜDA¹g'sê,Stî¯q¡v*$Ö¬ôjVáÂû ƒÅ€g·qöîß¾mOÚ¾^œ¬ƒè¦=ìðÓWcÈl†üíFm’ CElk]ë1Œ“)¿hð ü;“Üuj›Nø×èö $‘†£Í_=¹‡ˆm6^ʉev”Lˆw%wùTðñÍN{þ=OkÀÃ÷¬Úêêq]Îë»z·Æ‚˜É°çdÆõå‹Ü¶ÛÚÌ•˜(ûNüÌq˜T;äwŸ™­ª\Z¥„r¢&ËTºfBqT¹rœ™ä€Ù¡¨P­¹G®ìºûÓSwÛ"Å bø¿;±rÁb,ÅUXñwR! çj€éŸèÓ‚ê˜äèê<Ý 9Õ#$c´ y“#»Žf*ˆOYî^XCo®èßÒnët¶G«$•»c†LU#.¨¬ÓJ5@·F™”%2ÒcjH†€Gç|àF¸e'Б²š“žû~í×u5#ÔÝ‘Oò´òTÔTP€ ‚JB!ƒHy–ÆÐÑh×]uzmŸ¿ë^zýà q YEœa nE‹4*V"1‘p”`24K†sCrQMA Ž7©^ªJŒCƒ£Z2¦%F€G$Q6Æ´ÐK Æi#&iœÅ[î1#« ãÍ3õK`ˆ#û/!NÝÙ 6ŠÕ”×J¹¾“tȲ¤«M•s¤ÈÏ7]dÈPAH!\®Æâ88Ô—½´Ó2žÛ­Ù òë©{ö¯+Ïœ©½U‰4ÁƒlkVVMÌh¥•é¯Ù7³»n"{.ñWiKšï'\ç^ˆËؤ(BE‰ ë3œix-eFíb©I‹„%ŠÀÕ´mšµ)AŽ VÚ†j­a#.#$`ØÚ0¡RD#$ª!B¡D‚Ò\˜bb ‰‚M0Ú"Ìt)`Þ°(Œ#.\D#&èÏ âžõÄц) Á¦1ˆ ¢#ÖK*#.4äþÞÿ>w¿œ¢^ºÞ#$p£á¡çIt}§å$û0ím#.§>‡¼BUŠÆ}LÛ¦†Cmߦ{¶›§•Òh­$#Ìm%¨Ý«Ow ÷èÈæ#.ªœˆ¨ €÷¡É"¢#. OB˜#$+$Ÿîó3úω¨}Cù)ÎZ~Ÿîý!ã d^$¡ùÓTÁù£^¸“‰Â‡éý(‹Èç+õL§ЇtǹÞbª÷? zXÓc­Àm Žt3ƒÖrШž΀µ"êt‘HÈ\‘¡e©ûÚµª®h#$i¶ÚëÓ¯\^/è àÈ*½:.»Õ x¶æÝÓ!¸(ƒM!#.‚IheÌÖÍ4`M½aüÛÇDí«#5Ýš6K¾øÚm…@¼F ¨ªÞ&’ƒk8(ˆÅÕª‡‘KDl@d‘@#.B#.áåk ¤ É2L›EƒÏïUKÄÌ‘#$×cý5Ý{/‡Öç·áê2PGìã‰ãíªFǨj¦É7g,‡Q“:õ^õxSŒzŽï<6äÓÔ̓z|Û|¶Ö?a‚©UM@¦«t¢Åªîª[«»«ì×Zí"¸#.—¦áß)$‚¢’H dw&23‡Ý»ãÞ(e/!) *€©)’€Ê`%1“Ül#¼üûÙ1`ØÒ½{Ï<òÎóº¹teÍk†”uÝâòœÜËÍÅfÍ*I#.l´µ'·Y¶J,QLØÁãq4K›r¹W67qÝy̼›Žºí”s¡r»»t‹x·5^9fËy<Ëurîk%ÝæÕ6´dyÕ[›–ºmY-£R–+;”Ù–K'¹§wtnœÕÝ™Ó\‘MFs‡*í—:Ê‹FŽV¢×m6Ü£[¥³E µ²"YTbz „uÁÖ‰Cú¢©Ù·Õ¼(àì=PBÄ< 8#$îSý=Áß ÀTYƒ¨BˆÐ€¬D°¶"€È)â ¦ôêþXh¡åú‰Æ{ÑS¸pAÀúŠA¸¡ã@,T9fúãßå ì#.õ_a³ßkÌ¥hÊ#&ö#$zsðPú=ûSö»Ngà¿%ú+yUWÓ[e0FÖ£)š0Ø7*#$#ׂ5EŸø‹—nÿ¢2¬"¡œéîã`0ABAPH+ ¨04|©1Ä Pøê´µñåÛÉ®[·:Ôã®î¥–ní¯R׊òMù<Þb& iŸÌ7§¹ aĉVz~HA^ªK;ª½á‰“&E0ª"EË ŠI à’LØîíº>WzhÒlR5é½+MÈ-ÈȨ6#.”ƒ5ij\‘(ƒ"9P\5Søw˜™)òLçœ(öp=ÆR÷iP„HF1X¥øŸ³ø&‡²µš€Ü!Ð5¦qw#.ª½‘EH€Ä X(ÄŠV@R#$Å‚Gz‚rú‹¢èõ@1c:ÚFˆP„‰Œ#$UBHÕæx±ŒN´5 i¾«ôþ/}E%&¦ZTÌÈ›è` ý#.„„D’!*Ä0\4¹®ÔΔòšEÖ¯ª(ÈÂœàŽ«”) »#$øá¬#.„$›QG§ÝÊúÖó6ò³ÕÛïÛ»ôÝè_¹‘‚{Ð@¨~¿ºÿŸ.EØ!s¥…á€{–‹QV¡5?…ʆÛLƒdH8s@“œFä„ü-VŸ#$êèÆÄIŠI81øº¬!Ä=£æý#a~PßGÂx¡¤äy€ZJs=ÌÓú‰Œ ôÐ LEJ‡À£ævë‰ÛÖ…M$àÝ/0a ³‚ŽUsW1ñœu‰Ûõ1½§-Ì-Fœ“Àû¸#…U9¼ÀÜ—¸¨wâ®—ü|==~¾³óL[5–m;p•´´¸·¸ç#àhz—ù¦¯À1§.€` ÀF ÜbÆš% ¹¾h-ä,’8XAŒÓ cÆ20òx46£l©Ý͈°Ö³#$ÓOA&“F*´»ŸTîÈdšˆ;3ì—ÑãÖç¿f:t±îA‰câ)!…™'4o¶gm‰€Âµ˜ƒÚzÞΰó†öþ/Zy¶lÞKøŒßÁžñìIhÞØcèìƒÜSšø²pèËlnG~sîÛ`§(8¦k®c.Ò»<ðCíㆼòbwO³¹s\òÎ`‚0Dh4vfT–·^ô‘eödXz#&Ðú6õ õk'ÂõM3‰q³ÒÈOá1Ýÿ’È*]ÔkU¼ŸÀê^GÚDtn¬Å†$a#$Ú%K)êlXˆK#$^˜Žd7RÆõsîèy ¨nˆV÷®¶PUyÞñ=ó´˜*´&¸æ#.#&†…U$ëíëêÛ¦`JŸ¤³Î]h\³‚¤®>¼Ûä_ˆ×Å{Hù16@:´¤ ‰°bW¬BD`_#&æ“#&Ðùi_ðóÔ±tXL…¤W†8†H{ÏJ}p#.f#$Š,Å$»¥Ï19åxóìnb¨86#&[V[•âæˆ… C·ò¨ùU+?5TŽj†>HLׯµUV‰UU3›{ü*îT¹Ÿ P}ÒÑ’ñD¨H’Ø„FIJ`S^,R]«-«KÆoEAQrÓ Ívø¿¤KqŠ…$5…UDö%xÎuœáè­Þ!ʇJ IŽêU&vg ŠÇ‹óÕ44É·c,Z$cggHNíXæ)me‘,(] A†ø&Ég”<†¶]BU´UÖ d¢í!AV#.bB ²æ_ŽŸJÍjùÍÜ´©ëªËL XrDÓÌy"»ÛpKZhÓ¾'º”¢Ô!Ü}Ä:5ž;li•_:)#.p‚î·fœÉ”6I$G ¬ð®Ñ(%Ó÷jz¥˜1%² ë£Ùjtã—Þ*©élb,B,V9Žƒ#&ê}[0­ÇI%»{sŸZ†¿]¤I„›ËûÁíר¥â—Y ÍÝ¡˜/v\ "'ß}#$×Ùƒ f3çûúý²^j§ÌT#&áÁù%Ø1"²/ÈŒˆŠ’H#$÷ƒàúëKü!òþE7|H#$‰ H„‰ H‡vâÕtˆ?#&ü°T‡ewŸmzOJd33u&Q¥8d‘Cu‘æÚC³2äz#&Œ3Ko9ï™m¢ˆ:“§q³ðù\:÷¼k ùÄÇhvÇÅÄA#.V³™à#$¦Ô­ïšYn%±GQC^p8{#$|(„;ö…ET›çýªúsJéÏ©Ôù£¯låÛ ÝgµÜóz»&¿ONvåkJ>¾glèsʳÐúÅ®RÂ&6“álw(©ˆ"0‹ò@A#$ÉÄãØÔ3­ÞØÀOfçDšŽ¡CÄ5úà—~‡° ½I(Z°o)DÜ„€âW8·¯¯§ùßÇÞ̬¤žÚ~Ò˜Ñ{Ñ#$±R‚[-¥@ÉÜOŸ.Èh#$Ò#&Tˆíb€X#&CÄC|i¼û) žïYvÿÌÏæÏ/rU4"y+øEÁELͨi¤µm½ôþ¢—-ÑB{‡jhÅÒùu¡C-F©ª%\m¾)MÃ;y òý˜ÅÞu|WÉ‘v`?ÎTÆ'»Ÿ]¿¢~Nu®yˆHiP9›¢ùJ A+’cý\ï4‘!´fþ·_¼ÉÀ,ä7(ªµåÖ”#&½l€p2pŠÊ –"h$Õ¤D¹Ç¦aˆy™*Ý›D10~Uaýß·—å‚FÆÌ4zä&ü—Þ˜ýüùçÏï26øœ*±TÆã9I#™úòë˜îM·TQ8àÁSÿ,ú¯ƒ‡‚CžßŠúµ Ò|ÈÈ–$v=±dvuP›=Wë~Zb\¹­Øéx óÍͱªÎnŒšQR0',|¤Í)xç‘@0ð_[:`eü;'ý¢áHÏ9Nlµ¶â1ßèe0ã'û¶F±²wZ[JQ´”û>OáÖxbx²qºCg&Ai´<»{9Hð¿Ê6$j'â.½hñ•…ø[X[]4›ÕNB7TRzn§ÔÃDÒ0;õ­9obkÍNËÒ|j—a4ðB j5ØíÒ ‰²ƒ1ÁpÖ>prÜ>çÝì¨9»3–âdp(%úªíš·¥¥ÎÙš^6p ¬Rï… ã5I"þ8@É—ü¿æÎù:“µáó"¨5&›æàÿj HZʬJ¦2UJÛO9sª½¬Ý6ñ“Q­]zó½vô1¡²ŒRA49`‰ÈCJåmÚî—7Wb®‘Þ×äf’M4 ¸h£YåÄC#.Û1¦”‘/#.#&€aKÀP¾)#.€âà¥nh‚ñD&DCè¨Û©#ƒT¦H$F6úêñVl¢ ‰ 0ˆ]¢‘# •p6åÒæÖåm¶í{|í×å–ø¹Ôáp̨2˜‹ïu!F`Ð"Ø2Hèw°à“EpÍÄiR©dOŨspÅ!4\Í1Eûé…~çï/S…àò—i³ˆM`tC1•#.*9Ï)ÉèT—îqµÁa½®(Âhj#.B­\U¹Pè£Æ¦’MƒÍ•c@lÙ¢Š=GˆÄ›I"Ä 7Ü®BÌä» JºjP¢¢¹RR4˜6B‰{y! ”óÚ¸ã‘0ª‰&†ÈA„¡ÃørSmQ ERm68ä#ŠÆYm ehÔ@L#ˆRÀÁ™VåeE¢H”5`VT‡§Š ¡®UZt¥È<˜FÜuÆÚÀ(m„UÅ¡:‰VU"ȨW#.Êåcå‘#&¦ãM”¡²¥¼ÂUF¡jÇ“#.¬Taƒ`À2¦¥ÐGz=Øæ÷šT R‘EÜl=EÚš.î%¥ËPL§iX#&Ž#&M=æ-3,ȶ«ÞŒøAŒ4ë É¢œÏ5Ë6#&¶Ñ˜÷ƒzsaˆNÅÎR•‚­W­YBXKk°È™V0ZpºSmi<‚ì|¢èÀc»®âA¿+´ÌÎn¢M„²‚YM\Mñ•¢(u±ÐuÄ=7¢jšdt³E , ,[QjxÜfÙ²©”ÝÀï&µU°â‘; *2n\Ö0Mâ2‘ôÈ4«Á4¼\qF…p¨ãX `;‡TaÎAÁ‹ZN5F†A1Qn†hҀȬ٠5¡ÖƔԌQ­5♚⊧O/…1tc@F`uw}bƒC– ™0MŒm#ZÕ¤"±‚È084™Å[!IL0Ò¹ÒƒA&‚L«M:³W$azæáX(¯JÆÜ×,ÍXÖ]Ú†B¶ÒU4œˆ Ñ‘n ªû]=±WȀͼX†ß2iÁ¥‚8&ÅSAÁX"&ÄÒËrÍâUŠŽRTœ¨õ!«œì•Têít" cÜ4Q¸º'~L3pCOÛL¸t»0Ö‚ÁPa¦Ä3’£)¢"DZ¸÷2ŠÈôÌúÞ7F¸¤:˜ £æ)åv$jrЉ[éêaµyQ‰²]òuu¦÷d ¦tŦbQO· „o@4î±¨Š©Èh+9Ù¢[UçÛ›Ô1X E«-‚6™4Ï6©8!È.ÎwƒÁD‘KD#blêaMHÒZ[ Q%Ì0#.A@KTPE& ¤lLP´–n@$RG  M‘5$P¤g0íìŠ%R…1IVµJʪ–ïºBðjŒ”Uéçut·ñºâz¹ÙÜ¥c¾WÂß:ü{IAmE¶Å RÍ£e’M[ç¶îZ¼ÚÁù‡±#.SöÇ ¿#.·:gGèöz7Ñ—PE€•ýP\ˆXîÀ*쯹§ü_–­iÛˆ[ëö¿¹80µõmû#mI­RZ+SLÖmŠ4Mé.[^—²û¤‹Ê¥#&D9èXM Í×O¶ø¬ 4¬#.à`T"É÷N‘+Ág<¼ÔV r†™„NTY P÷ÛI#.?è`fHØ›$‰66›ØÖêêé«omuÒ‡9G”Ö†{…ªƒa”ÆÐíjÊ\ihÊHkTl‚…1 > ýb–\!Õ#.#&MÍwL¹hÅ,Ù(CÜQGØ—o]B£û~f›Øfu.ÜLÛScìï%p•ì†åöÙ±^6ðÖû°ÌYÔ$Àmñyê¦jN¬‰+6xš¾NüCXP™ýQÛî ø6°Þõ#»L_HÅp3èm:Ÿn¯#&½@ FÐõ(þ¿Þˆ© NÎB€é9O¼×‰’¡ØeÁx(aª(CÌŒ»“¡%gÀðÞ&¤Ähï>þ½¯~‡„_ذX½PĹ •{1bñ6Íô×aʧ˯_`?¼|Šb]=}w:?a×—p pßGîÔì`‹—®RKݱïmåyÀ4åªàqÌ´Zo£bO¼Ö½xð¶ý:wbK|.ïõ4í’'? ³Ó>ªÒç–Š)TR½’Y#.É0ÇÀ#;‡&P„Ú,ø§uû‡#.˜SGmªÏ#³ÇÇáåUT17ÜÛ'#}ø£riŒl2× ÷(–!ƒoÃŒˆR5N×ãßYwYcïi fÍ1/ªÅ@$Õ)#û˜h=ÃÁ;@;Õ9©#&<ëºæºmüy›É]æË©R™”/Ò»Öµr¢Ûb´€ŠU T¸”¶ =žnÇ@üs¢zp»Ÿî«#$²Ê˜ÅRh÷¸m™H#.ÈŸOé¼½PÉeJ²ùMõCÚbá-„BBßQöÿqG|^3!ÅÆEEÄÕÔw¹z÷Æ"ç³bÎ{ÕU)…(ÏzýOÑ~‡ÒT¸€êÕP˜*±P¡Ê ¼ßß5ilî‚ã‚Q¶ß…ÎÞ|$;²ÖG6qÂ^!áø€˜βëŽÖF73<ïê·6ÅBo%aö(vµºt¡9¡2?;#ŽîÏÆâÜe:T¾58ÄŸS§ûºYÕëÖ@û˜5AÒ„¨Ö®Aå—Áã°Îýµ£=­NZÞª:5««äµÐÑU/ˆ@:§ôGou4¢ØìçÏ~ÖŽ˜¦ãÚµf UK6õ5:8êøÖÆã†IÎÚ£]uŒé®9æ”ìbùEVͺ„ô·©9 á»t£á¤h ˜¹3Íé0]¶~}ÞÆñ=]»ÎíªåøEeâh€Hs™fqW5ØLKS!#Ü«{Ì´Úš™ëé…¦Âá¸j“ïåtH%Ó!Ää$Ag”¶û»97Å; VÊ¿_}ï |µ‚(ì ÁáDïÖ–äàÅ,RÊ$÷]Òr‰L ²›2-œJ„ä/Ø×xr%§&qÌÊ[ÐìíÓ:]‰ÒÍ¢á<¥¨kÚZÇö¸Zþî­I(‘ÃbˆÑu•óiÜC¶Å¸0æ¼h„,)±ù ¬ ”^¸Ü¹BHÚŽry Äqš rYÌ£¬Î»%&©vÂqÄBFzqÕSp7Ed%¬v;ÉRð·œè‘ZUW,É4;#.»Ï³Ø¹Åb\m6zÇJùZï!¢•ÛºÚ#&¤ìÛO9žwÿ'¥nûb_w8Üí É—’I9xÌÔoSI­>%é+횎øÄK!ÒëÝÆu!Ë!mi9žè»Ó™ÓÉâ"2rì=NxmøœÕúá@ê­_‚“#.^¤¢oÒ©1|(j_—wyϦb»ôï[Úœú›âo¬ŒIF2µéL#.‡ªÒ%ÛÞÐ#.ª£6V‚·l+v­M’öºåqÊ#.]Ò0!Ìlç6˜[Zζ=óÅ¡Bë#e天…‰tì!¬›áUk—Q{ÕëÆü3ËGy¼Õ\]ïŒ×W#/Ï6ôk|ÐÀjîùÙÈ~N—>rí®_ï¿û Ä-Ñä´‚L½˜pom­yl÷ø=%‰÷ÏŸEàbY—E•² æìõ¨ÇKeŽÞêîí­GNA³«$ÚZ"ÁªKl/¾q¹G–0„l–1·ZqFXÎ ”ðÌì®d'Ø]ÕႆÚ0½²¹dvÃ1Ô–ös§ÍlÄwà&–vE¹"!¥³íQ›«Ût #$”ô%ýS$éæø×BÙ½2>yEãŒIð$è̺¹i›»0I”¥.ÅnÞ.œ22¸àÃ{]®¬ÒðŽQ7‘×£«m¨Øš´6QIŽãyÇŠ‚戨¬iAà§ÓAA£Á±uéBµÚÂÐŽiBª7X*Æ6p¡ÀÌ8Ýxá –²í…×h&$2Ë@ÀƒË#$ê™±…(ßÛíÖHÊg€‘¢C`çÈì¯F»-]ƒË­'=„/ì‡A±k—f™ð ðÀ&{.‰Ówy]yYÈ8•ÂÛ'%BŒ™¢y²ò4Ÿ¹QSÄ%#$÷p(@@Fn6AaŒBÔŽã”8BoLÀ‡ïÕý8ô»Æ`Ä Ü9ܘjC4ªib#HN(ç¼ÆFÞ§MºÍhèª]‡J@HÇœ¸\Ó9»tL¬¨C@¶—Q.fÝQzMw0æœ#®p…}U¯ [’v²M JIÁ… â/=çÇÑ4#$ÍÜIë´eaPuÊ»XF#.4ŒÔ1Š žP|¨áƒ§úTUG(Ÿ”ÎA•zR£Œ>žðU![eÒt{e³Æc]+™to)ë<(5˜æz¢:§ç™ŽŠ­ô¨özpCõ~'­ª¥6”KÄK‘ž1Úl,À­ënq6\;-–ÚÚ»E Üý‘Ém]^v¡«¾ÄYÃß«I1íḨÈãü¾Û%ám=ÎÑõ9WÓ}¤%CÍaÐÌ‘F%ý¢khÞùfð¾{ñ-yËsS1¼„ë ˜Ãdvëxwu®‡~JkZkNÚ21D‘ª cŸeKÆÌí±+ÆŸnª-T¨áèPÌó¿Ä ^›¶ž³Z‰i‘ÝZÙ.X‡¦h¾#{4Fб¯­Ú‰š"#&ˆ¢;å£?GÞr^{öà¯är÷·_LMÚá08ÕÆÝ8ã¹™‚¿/9 û˜ÆW^ÁyKÁví›Ci^æïuëRÜz›sGcG#.:—ûæ¯ÜGG<¶èÝ 2¹ánw—7·ã³êõ )Ñ·Þ]/^gZ}a»š1J|±ˆžÊ¨ØðK·O%#½Üù»f·—”Žüm‰¼§!Þ»»:Ëã. ïW™Šß·#0a$ÎT0©MO2"áìõÑCm#.X”°BõiF#$3éæÙHKÅÌ ª!¤îôh^h¶u¿™-7ª${qyN§pz$΂ˆ°¹Ã#&,[ó6,Èh¨·ty>OíØÁòsþÚÀ;°W#–Ó¶ºØd‘=l˜G)G:ß­ëüL8†±§Ž³°Sb €‹ÚG=ÛâI FÏ›\¹Üd]Ül#.RæÌ®öºø#¢,ŠÒ…­o¼mP¿jØ,Y„c­Ê#.5C§jÙTUÑ*ðQä[OgHtÄYì¥ZMCsÆ0´R8a­ÅÖ!eô#.Î É3@ØRHÞÂì¦pbBI‘ºI®ìDæGRkCˆFa‚H#$ÔÑà&Hp÷}øªë¯#&F§~ÐÃ¥]ªÙ6¿ÉöcßÀ…¾MZÖ>vj^?䮈»¡ð}ã=ý“ó¶É±?ò”r[Øcñpã\¿€ÂÇ\¥X½³¹¾E¿²¥{ë¯ö´v˜Æ,ß³øo«œšÐÄKâàöˆv–ÁÉ ü¶m#.j•h|ºøw•“^ÿ#.ÆûÛ;(ìëÔõƒÇ±,!艼s»í*ô„‹$XžÙB±±ˆÛ}ù¾òšòÕ·-»¼ïG[ÓÒEM#.ÈäW9KЍRdi!(Ùl#$€Í#¡ 6&@•<Æ…$i±iVQŽ@ŒÙY•P$‚UÒ+F$Ǧ­âªJjm£jÆ­½-¢Û‚@Ú£˜A¶TÏM™H²"‚Ö"ºA·¢ÅFPTcJ*"‰…EJ”‡½ó÷ÞsÝœoäsß©_]Š¥#.ÉÀ¼ì{†wxFZàÂãŽúöБ›êß…ù·5é^6ðUȹ66Ómk_V6¶ab#$l#$Æ£#$†™kø·#Y¿¶ÓDUU¼t3_eÖ7òRÙjt-þ>ñV1ŒlX¡[¨ƒ!”5Ëç…X>›¶¹Æ¸ÑŽÓý£f-+°£W"(žÞ¬ÙδãF$«1õŽ®#.RÍwÌy4ƒ*£!!5S€ÊAå©•A¤WË=HïwMãXÌD°L‰*ÒfèiŒq͉¨Òm6–5$RÂ]ÔV6&šcD0€J墣ܧúï%#.|'X·t‘ÒHU’ôûe°>ãÚm0ŽBñRR ¤“@Ö4àÄ©7.†)š¥q…‰¸•ìcn½Œ½5)¦V޼s°Ë7vŸ9ŒÜÊ4Ñ #&ÅZǶֵªd„Ƴ%±³-&Î1qš¶Vøa€mˆLlbÁq‘ yƒgP¸,EšfU‹‹KÁˆ¬¡«6@­¶&s-~7[âîs¬ë]£L…d39Nñ+¬ëÉ8`ÚCÇ”ÞWjn—MÖNrd×§¿žÏ®v¢¨†1„Ûq„9©ðW1^2æVù†•“%+0’ï[0ƒqLº”L`ΰ4Ý€ÙºC³#&:øgX­s„¤IØ'°Ó–Žzê8õÑñÃ#$Ë"j”5”kcF¨Èà1ü·„Ž™ß.´›œ@/;ew–î¢Ò4šf΃%cF¡ѸUPT¡VĦ‡#&!ƒiƒi‰¨A\\Z6óqmäÉe•ÛÓ†ml&åÓçA €ÊÑ#&‘R¢ux˜Àçqkd«”5¢0Š0Ã(Õ ÝŒ»nè*1ø¾3T1ŒÓAÔÕ-°æh¼8%k/sb<·´,*FÜeN¶ü¤m¶1Ù&R^âWzd¦“@ôÕHBFžL"T[ §Ý¡%Ùk2ãU¡‘¶œ«.ráRÅGB´Úް;Œ:ье¢r3ÁŠy虜ÛTDÍËŒ§zF±V=¼§Nø®8ãÃFîÊVu»m¶Ýb æˆ[ÉÚŸæÞŠ= ­GÒ¼Ì}@e@F"Æ#.=È´a#­Ö¥0QsÞÚ z±ƒXrYLrÓCÆ~h §M¼X[2”Àãˆ]’L5c3ºm¥¢à>¬+W¬„c±¦x;„™9õKZ:tÔ+©•¨há“T Hų<Õs2©œ[›f´E·ZЈŒ X,¢–)œœI@ZX˜pÁEFE,V#&öÖbË:ØÇX3¯g#æ…Y¤~zÕÒ}’K߯ä‹ÀÏÈúýÿ[ŸÅª” ²¨”0X(ŒŽÒåÝC$’Ûã·EóÀ_Ï?ã?ÍÁÒ`@™ ‘ŒŒÒ¡òY´ÚØÄæ”´””T‡ÆN?‰Í(Ÿ§y pì}·LH`ÊC¼TîU:UŒØÖ­À4†*SmÙûÎ%*ª V@÷zŠU÷0§©¦¼'äãueb˧Lf12*ý“È)ÏØHxGð (jüÉ»º‚2$†ÚGlEØû;|Aû7XÎâdv?W—®«=<_<È ”á5·®NÓ›A›…‚ôù~?º£]˜«IB–ªW–cÌú8ÍîzmÀÆð‘¡©;»“‹øÛºÉÉÝ»6nî™n žèÂK…t§‰Åxœ(žˆT¹ê™ç:C<¨‡Ôô§m!EIö B²i§'AÙQW0yé¥%”ÓCX…•"~¬@ž=žÄÁ¨î£Bv^éç@Hµ#$•6½#$PÀM+ÐÜÆ¶ìÕ’’®Õ2›HF!6­ @H¨üÕ€7~ÊÍ<Ätˆùk>þ[ßÙnéãð/ç¿·Ùûr4XˆÕMPÓ-m…Ú@ÆM˜Å Z2…ªeX¦›cl–“V±I¢¨’²hТ›**1ˆfЦš‘¥&É(Ù¥$$¨ i´F¢R2‹!LÑR›"š¦Œ6’a¶lÂ%(I1«X Ú§ø{:y=s5ëÌ÷G[© …=ñh˪Áðì¬ç¸ü³AuW¢æ‚|w‚¿SÉÌ×ø¼‰!#8z8à_ k×1+‘[}”RNéumµ#Š—׊#$Uj}Ç#&Ùp3½Q¦†I¡˜ÚË݄Ķáº1™)!²`Ëî|µ>¦»7?b]ø¢ƒXÉáËpKp7=†Î´,æõ3Œ$%˜žŽ!¶òI!un»[Ìøz|³†®·sÓº1/UvtƒˆNÝzþW2º·é±µŠZìs iÚ˜ÛÏãÕùí[íVTdÚ¤¬DšÂ&H­&Û/ÄÚ×MZ6›”“QQ+b2û.µÌ­•K˵VîÖûÒÂYAd7~A3Mü 22‚Me £ðöê-™°YLÊsì/ƒ>´¡Q…¡¦b0ÜürèÕcD>N/±ãPΘŒ1Šd‘ÊLE"ÛdÝ’ÍdzWzëâµxÛk–¢@ÈÜkJ¶„…¤ÄŒóä’Ø´7ºó£· ;wöó«Ì·wQ”å2¯i^RoMYÎ5¹híúèrÍEÑpxc‹³¼C(E„- E(‰IQ &ç…œ¶×Y“ãA¨Ò}N&{YH-ÔÅÀÙ-R%'LHQc0peªȤå®S#.LÄܵěìí¯…íèÇû°9 L6¿†m#x³y¹¥R'IP¦çBŒ<ò8ÄNs$Œ&Q„ …ÀŠƒ6êiI²#&’@(8€ÉÂP1H R\2G"ÞL’$’I ¨sb#.Íüî#&ù &y¦)\3n)ièXH¢êíl<¢ú_».¬»ò;HVëû Ï~ÞIÕ8ÏßJ‰è”6gUèzˆŒ…ÿ&/ì¥f:áq§.’×ÔØóó@.}gàŒ5FAj¨fþ¢¸f†€X âq`’{JRJZK†`ÄÝ•(Z©Ž¿0Ï?Iø€)ËŒÙ42d‘“˜KÚ‹CÓåçÍüž«Ùr˜•vðÆK¬ü¼‹Šä+NA–«B/³Ö{l¢uy«Šì&ÉêNIÈmrS«Ð{èRŠ9àÊz·=d„ˆ#$‰ÄêØlÏx[Ÿt™›~RnCp#$všïb?‰UÏá!FˆÈúàÆ,DF„Vj{ŽÃfQèANð ^ž$­°G¸‚Ø ”š3È3=]^Åw5·Õ‰ºì ×x3!%U›ØÒj…û»·}F×S8ï?Y~9oÑû—äVV-©$°/FŸ8÷—¾©Gq1/Íúi#&qPhh'‘átxÜ«+¿Ô|æÑ÷ßKç—CÆXÆgÃΤ1§c¯5,…WMpþÏÓ£‰ nʆáÈ@èt‡æC Œ½°zŒGc/æ“ÛSáì1ë ÷ûÖ|~»»`±¿¶´~8s–(æ%t—YÙJVúÄÂÈô IÍTüþS¹®yÓÇËÛz‘›d,™ÝÛÙ¬–#i·:¹Rê‡m¡÷—#.îí"Úî†|Y°‰Jw:ˆªº"`~˜œ«Ï¦xN#.š”G x‘((`¡YvÄfF‡@5~ãuV…ƒPKý¸! h¡ó.y2ÎÛ‘n÷EÙ© #$Œc#,Ä 1©H6Ãs(ŒpÉc#&Ò„ÚQF´‚`MrsÛ"i&•ˆ%7gdfšði·f«¢çבا]$\Ž4ã&‘H—_©¡¶ëB n¶Ó±%#¢iE©dú¸ á!ØxØQ¯ \B(ІÕ*D`#.#&3õ˜ @JdDÓK±Œ¡Ò, p¦!LµŠj1Adᙚy#&°ÆÁ»#^Î-Q„Bi CC7#.ABÁŸ2æËÐbØ-ëÏmúhíÁÅ ®´èãZ`&Çm僂§4í3Té„„†#&DÀ1zƒQ:ÁÚ3w¾ÁxÄ +}æàëk@иUÈ‹| ÊŒ"0Þà¤S!20ɼÔê窚j"âe0œ“/ ºèJ!¶ã3ú -÷éÙ}SÌ®Øà™LÎ'íÔŒ2e‡¼õ·q]˜áA>Ã~ `ä.“‚í]Š#&Ù­’)K¡é ``7¶æ\0mÝšŠ¹çDLìYIŠ1g]ÇiÍ‚’S4d,ŒL…hÀdaÏ]"â.n¼ÇʯƒÖšS-+c=×Xf¥fST}g^0P›(·õ‰S5{ÓX¦¹‡%Ó<âiîÄá®›Á>¸€å^.dÜM"zNÚ‘kŽ“© @ŒeÆ·xB˕ʬÕmvMÀÈ—0´÷N4I ÉEG@/JfkåεlWsÀ}:ï˜+64K✕»;]³½À÷½uÎQÈ«§:I¤ ¤X¬E#.5³6Ý\ ›ÐÐÜÇI䔨XÓ T¸ºNÚNÂÁ¡aÜÁÐXâ€ë{AÓ™Œ°88q§W°îRýšÝˆjQµð°ÆM¦…¬èœ ™€¬vai˜C5ä¦ ·€S”Éã`Yl0”!Q€"Fõ//VVÌÐI¥ipeÐ^Ï!’Ñ&a…@ÞGT‚BN\®cGclêë•cÅp¶f6p6æhçŒaòDg²€Ój‰¡c̪Äïßò\ó¦6vÓòÌlÔ?=n¨Mرû¦Bp%Q2g w𡨕¢í’&×Û%Æ0vJf²[ìäå°çQv3lŽQ'ç‡Ý(‡¶Ë™ptÙ¹#&kÏM'ìô2qÒ¶¯41õ&f¤9MGnýÜÙx‚u%‡™ë§¡¶5;•…竦Ôi‰X£ ²‰—|ê –yË)ÜtÔë.Éz—y|¶Ò +d³„ÛA¼ßˆ°&X]Y)5¨!›%#&aðËqV ­÷ÌpòÔ0+—æð?u~]wFƒ«Q0Qˆ€Û¬v)ÀÍ#.Òy½h6–×Ôš"mÂyyõŸ#ÆÔ£U@…TDX kÓG‡8A›Ã–Ç¿É~D°Ð3ôíÌ7XD„€šÐT*¡ÌŠþmõç®Ö@O#¨åê¬[Ίâp·dÓ*1N /ϸ¬j\>³R”!ôÎ _°X‰ rI ·s'©¼†<.8¶xI‹hæGuЮÚÓmM÷XéØ*FŠpr#ÃWˆ†m ×—iX`Û È©(ú¶ý=½@ lÐÙW%U²ÔEü`›ã!œ»”D¯u"²#.ñ’p©S_¶°ƒMìÐNÆíaÇ®ÖhƒŽÞ y8•\%DÉQ™åÊ‘8r¸AC°ÍXÛnÀý4¢\gª$^š#ð•¤_IQ‰X®{¯Dy˜†Å̰ë»xàŸ“ZWapDç1PC4Ah¾Š\Y}5Uktär6ç^0Ûš>·½Ï+;Y¬å¤Qd\Ýéy#~yCÈ“äÊ¿©'È#$R">Ú2áÁøŸBYo‘2¬ÎwÕ²'rø(6Å~H.Û¡»\ÏŽ™È ñP†×ÅäéÝÖ£ì^W¦™Û¢wg@héí;¥Ýç7q–dÞc§j•£"6–"0v8ɪÆÚBe(·‰©ÞwKy»´6o]Õç¢e‰1Ò #„²Bd˜`’7@Œ@¨65fŒAhpұ̟«±€@|˜¯s$Û$²ƒHuÒÜÅTMqÚ NPÅsW„hÕ‡M g]Ií6”ÒQ „ Fùèë쯿»È!ë†<ýZ÷ H}@H"ra#$³‘ßBÙŸHbÔT)¬•òb“ãiµ±£Iè@Œ&ˆ1:CÌ5™‘УCH5¹Ɇáï ]1Ó6‡5¢‰:pƒqBzy„ا#ŽæDfæšG{-;Þß#.w°:KXÝÅ6áäÛ¶¤Ê,¤žý$_ÓíšÜC\„Nû¼G¡œ?#$€ôqhµI’e D,¬2©ÉyɹÇ5ØŒî͉ۖòÕ¨Õqd,Ä*˜ƒ‰npÌú£NMÌ]S5Öˆº!²")ïõ=S¦[6àò]©!#.'^É#&‡]4Ö>{âôQ#&i‹1¿Ã»mä†Zú~›^›I&Ic$a>0£Øhì¼z÷äp3£#×^Í_¾ºD£ô*êÆÂÚýÖÛvÚŠJ+IF¥¶´šR$¶¦ýN¤Û%m’´j·´µ“fì—E¹-*"^Æ, TRD$¸EpÀ$E‚5ª¼`±'»Ðg2(¤iL÷a#$j#.~? úTu 5Pß–ãb8Ó¤ … Õ¨ÓÙBð”1yÆÌmšdqïýmÓˆîyž’Џ !0€"boÏ–ÐK×RK¾… ÷ÕK>¥E›ècÜÀâ4]‘@QÖ¤¦‚ŒL²B†iÆ‚Ø îÑÉX0/9ÎøâÍa`i`;‰ÁÈLHÊWóþï.™&<¶ò£€¸T,3SäО#.„ä%çŒë‘´G¦ÜŽz6›sqÈG-ñ̰æþÆþÝYƒ‰Lu4)B½ü^WøÖËڵзY5—½YÓDÂ.¨‡Œ4ʃ¨ôèö÷@}‘¨ªŒŠÈ² §Bà i¦ˆ­H÷õl¨BûI{úÈ\’ù6>¬ýiö‘£ßµ#°‚qEFþ7o$bD`©ŠÂ$“e›f1Y&PmVÆ-f&Л4TÖ˜­i-_À±µ\g-ÐÑlÙµÖúmÆè&Éâž©hAÅËWŒÀ×Û­õó;úMÞSS˜è€w@:}]}H«Ÿ˜±;=¼°5€vA$FE\Ùõ,¾¬•ðãÐ!Ù­ÝÅ=¨= û}ÅS"*©È—ƒ;#LÈ$‘˜ÑÍv5)·Xxèt*ÆHÜŽi¨V"[iiz‚#.âo©¶ŠôÔä4Ø-J[hy­œqƒmše;;p´#&D÷M<7"#YJåÜ–'ΔM”¸aWT#.`jÊC-²½,È9Ct×È‘¹$Ðe²Ý¶ÕÆw#&ý§_uð‡$› p)Ý#&5ZÀa3¥]a—F©_÷XVƬJØÒö­r— jº²e@–ˆ¶”…X°&ä1™3.´6?F§{»Óg¬>(1@B"úÝL™—p=‚Y•‘dÝŠh7ùÕ­ ,ŪÖKï,~ækdÆXȾ–Väý9p€ÀèKLgw©êÇy¢ÉgD @w} )qÒˆ²$ALpIF¨;¤éÓÁ͉tÊñ¨œÃ`ž!BF dV1è$DÕAˆÎœé(Ûñ<4O ¥¯.FÿIÉ À„,I#$ä˜$…J‡‚ÅbŠ#&z÷Î=`ß¿(b È#&(ŽC)@©â÷€ßJïðÙBéª#$&âRPR.@›$ Ђ`¶ˆRÞ¯sãa(ùŸ”£wÇáõkðã=ÕZIw£ ¼(…à‰ÛÏìÌïä#$üí߇÷r{ Q¦oSn˳cꚀ‡   }%›5ôáCGSµ€f¾ ·å(¨xƒq¤_ª)°ˆ£˜ëŠÜÜ ÎÁÍ0Üd‹ÕÙ­P`ßJ0`HdÆdxº‹hÒeÝ]&i”ì¿OunUÍËžy¼÷õõùÆÙÂCdÊ)²[TKÔÝÍt#.ó%FѶñ¹®[ˆcnî¼[Ï+´­'+mæJ¿¬µÖ»vŒPÔª~”Šl(&Ó-[>¿ßá(¡¢«äWËÛcÄÃ{0õy%f#ó4ú¯¤ôP9öä¿&,U!TªY[6ÓÔ„õ‡ByB@×ÄR#.dXŠ€¡)E’4³IlÙµzüñ¨Ûð꿃}#&%#.) 1jj6Q´ÓL£U¿VÔíL’ØK`1#$#.d†Ÿ’5 \z†9…Ñð"¶ÙfÆH;‰èQWÂ[Ö›ºæÈ¦Æ¨VÍ´b£21XÊÅL¢Ù`¤’õç•[É­J›R›IÕJ¢r^ÜWøxþäÔóí'µóú30ëÈ’w«ÞMzÀQ“ܾgѲ¦2$ÒÄ?EÔ~{0³ƒæ6‡`y Âí#&xÎH.YÌ„Ê/èÈoì•õßîÆ,,a!HH±Iͧ¹>EW‡ñæÆ{+&¸4™Lq¨Á ‹" dû pÝ5úî#$áŠDo¨6=h;22Рpû®Bf‚u¹Z (H/¸D(Ju#&¦Æ‚‰†Û¤¶©¶¤ÓRmKiÙtJ šlŸMEÁµ¶Ý½:ä®E÷“ªkUѰàD¡XãyRŸ+K,‹>¼\.N'#&8s8øÕèÏw–w)}UçaÃô0Ö^œÒÀ›‰ŽâZ. % gÍ]¢¬ƒ_K#.ÚCi÷#X°vlÄ#$Ÿ{hÓ°5åy,ûh÷+–öñA׆+.vØiúq!»~D<‹™š)â‚égÞWúЃÉCRCËÆÕÌ"H,€¦˜•&ÈÔSos&#&DZ¤¹UqM¢¿aU÷-x–Ph­››IfPBÈ/WCÇ„åc“c%h„šÙ Y‘„=#$Ü« |Ræè @èT*ŒL˜Qð¢š&†Í¥ÂÈ BpÖyAIåú3#&ë·dÄ#&×u*Y(Qþ! *Kj1«iCThMl£,E¬jÔf›Y[ñÛ¯¶ùúúûøý‘çªÒ¤’6‚¤ÏËóaø@¢ †Œî›Ó_kLÔKPF”—xPcQƒaì9T“eY7ú‹¿^#.¬TµdRÐÑ-·èß‚Ylœ32$ؘ°»¢¸˜˜Îûµ §«úתùÄMkáKvŠîÛ¦ñÖù-Û4šš)RÞ³a]´îÍ]ÝJñ¯7•®©´i5½*䨷›ºÌÝ™•]svÔUÎÔ["ͫ˻h•»»nîÖ“eIS"Sc[ÎêÞi¯:"°‘%N#.U„†" ÛQ‹X£›ÔÕ0‹Ù^Þ\Fµ)¥²š4ÄÀÆ€¬åI,bm!єͶ[)k×w-ÚîÝXV!(Q¢ hŒDQè§Flx3¢h ‘VØMÄI«B6ŒË‚üãÂ)‘ªE“`ú½ dTb¨&H,Œp#.¤…>( kgòÞ§&h¥Å&XÆ#.¡rï|™3¦ºæHrŠhT#.U6$7UJíØ35s¯Î ]Ú:ƒ;‹MéEHvzÜG@¸09$†ƒìcÌcv ¶€ëÛ®`@¢:C#xØÇo’²»GibN•j‰P4ÚLÈ4WZd  ÑLJ(ZoWiþ{wòüóžÂzC/ž¬ñý):tÒMr/‚Iñu5¶ f0­ÂÁåæí g:c1úüÜÁktã%îÛ¨¤ÂƒvKv×™³Lì@rðs<Γ[t þj¢XŽ6`¾Žy•)L{æÏ:Ñ`Ø=f³áOçiU[Åa§m“±_:µ·“j#&ÀáR†Oç¥Xì"󒺄qx‘·LÑaWNõA¯ €Ò- ÈѤ#&!ZÊ^#”:DB4DbÎCTöˆ/}é¤[Å%Qhšds$£(q3çJ膅ç§U‡P#.#&§¼ÔoÝÅm¨ü[F1àÔ+•‚BG¼Ô_h¸zgë"„V®C`á×p›çsXÁè)³Mß(=}m-è%ABƒÒà#.)>ìE-Š?lLþŒ–È ¦TõR*9FÍ!¸B˜ó'Ì…²Þ]°ÁC äò=ç²>7Š`ûnýÛ6Gʵ©r­‚åÐ2ÐÈ#$01P™þå€ÐyëÿáüÝþ¿÷Ãöúþç~ôÿ÷þïòoÿ¾=¿ïÿÿ/ø¿þü»vü=Gûs÷|ŸöOü¿¿Ùÿïþ?åÕÓÿ÷}Y¿ü¿_ýßáþÿú¿oýßòîÿ·þ÷ÿ¿“ÿøßùÿÏþ^¿€ÿ’ÿÏÿ»Ïú#÷(ýª?C'ô( KÁüÔ¦q!÷y麉”þ@¤p5`ÿWòH#$ï!Pv‘#&%Ïý"˜Z².ùÈÚhü‰ñU²h`íIxý'ú#ü½Îî¢ILÌúµ¾vÊ·êËÃc\#&…Lˆe€SœRÛr?¦Ï \L¸SJà©Ú?íÛÏrdšÅõÿX¶êv†ûqìþÐhÉð‹ýàÌ ;ÚæGn⺠‚ ê ½øêûÊÒ2ª ¿$¦z·´Ê(Í,ÐHã#&–?D©ƒa³ßƒK%+;*…ÎŽàïf>Aàj'’ ])Ùª÷×fîiºÑha$#.L‹É˜MªGZ#&uŽZtM*²’€fÜkc•AšŠŽ°là¼,ÁÖ<#&íkLM¤Ú ýØ|±.97ºSƒ†–Ý]uÇÆu*Ä#z“gw8Ñ5ˆwHÝBCeaËÝÏy§PöCA—Îÿm3^—-Øìým&X‹™Œƒ¼I"ÑŒZåç¶/!–Œ»4£¡㎤ô™Ÿ–˜xe:úÊj7­}¢ ß]çƒÔG}Ý©NãÀáÍã#&ÌÕ@Ì–U»à«é”á߇Àþõb'w÷Ð4ž¸<ˆT /q·GÖÂÇŸV³U1^),0QAHޏ#‚0ö}K¸RCd0+•ç¸(Ow¾q¹d–Ò#3’Q\o@ú5ò nÒæÈXÜÇ«P±S²fª‚¬žË¨“6óßqLÇÇn³00™”$d^×t¸ls: ¢!a‚=bÈ<Œ Eú¬Ü2¨@ IQšz¨“¡ãÂ[„ áƒh\i|F‡ã—™œ#ýîðÒhfê°aÚcºäEˆÁBLqÕJ†Ö ¡»CYnþu¬Np„£Q ÛZR&*ÄÔJƒlkY¥2ÛE©M²UM$LhQšú]µt€çB'(‚:>“è"™À?ã"ã#$1áTÝo0X" ¥±dè{%—š@ŠÄPú:½è ¿Ï›õòÈÃI”#$ùËù[åÌ~ƽ‰,¬ŸöçWÈ_jzmî™ „Ëíñ?N®Ž¿õÍäÛ­]'š,©DQní’:rÉcooÿ=ý´Û>c3·º ½!Ë‹¸-¦Þ!’¢s #&ya«w#$Á@c˜h¥7Ìo&”îë??³§?18(ÃÍ–hj€DÑß\:i#$Û5„‡Í‹#&D'ËVÔNÚ.! "ÜI^F²×ÓMéò6[FW ôÅðå#&«½#.Ì'TH°¶H-¿Qnb¢‚Ûš¨Æñ­s[Ò^ËVòX­Ib"©#$Ò JBYk­Ü 3v m×#$º.æäQ‰#&™$44‡Þ… þ$/#&Ül©ÆH.#$Üdê#. v:¶.=âÑ‹AS ƒd^ÎýÆó=ã6ú)zÉ’õ´´nJò êÿ´x¦ý±Rð ×$Y#$‘“pÁÌRßçû'rwýŒ£Ñ©èŽäu>ÞBùr(ÀŸÐÀ(x¨zèÖŠåŠÈ#.ÅŠŽ×j;ŽíJQ%#.TX#$°Š³)®šÝ­]šÛêX›5&ý•ììOI E$@oÌo¿ã¡èù¡ÿWØŽ¨¤ ëÕÕ<|ÉG·ã&2œŒ6ÛbÐüg`¼ާSôDùKdOÉ÷‡ãÃïÿØÒ ºà"å?èÝøAÊlÆÓÙ‚€±E“‰ÿ·ÊKkþá¿M:Ø*“ÿ‡þ,‰ =d1@™B|Ÿílê zŸ]üMºEž@—-ÿŸö³ãÿÁÚ} ðð‡Î]†ž“–ΜÜCÓN¨ò½ã»ªù<\žðŸöû$ž%’ÿO¯ÏÔq'„†ž Ê¿úmO70ÙþѸ§ †(ò„ }ÿôÒî‰ú%àp#.?dYÓ¯'_ýZU½ËK7±r…unž±Ÿ8eT™˜ŸòÔkŠ—[ûl£÷Ô£ÿ³Ò»xœULà›çªáq–s¦ò8‡ÇÓiÓ¶ˆÐeecøs–&ðÖÂX߇„hœTK^VÓ˜H0¼ÿ>ƒ“#&i’ßO?Ñu‡íD fÄ8SýßB#.iönrgPÄ'û§ÉAÆ]?üUü#.ÚÞH"Û•#&T#.öoF$’ư;·î¾Ïþf[¼ý'/Ã÷¿€#$ÿâîH§#.ƒ¯@ +#BZh91AY&SY¼$VFzIÿÿ´PÿÿÿÿÿÿÿÿÿÿÿM (B€ð…0u@b)|÷I#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-¾®Ø aª0û¾ïGm_YJëRÚõËNonéµ±eôpצٲÁE÷u=ÛéãY]ðn­°ØW»:´#1õï§ß{p&]RÑÖöû燮äØ1»²†Nõ|íï|=ì=vÞøÞÞõlwyÇ]À]Z±Û}ïW#-#-#-@è]î#-ôXECï}•­Í°W×)Ù´Iá™´SM#-t!sZÝÓè ç ` 3ØèW #-S]T#-¡T ¢ö5BU"D€IG =ª ¥(·wmîôÉŸ{¹îÖsâ{›d¼õÉÜé^lô••ƒJ&kmÛˆ®Ù[-óÝk£šÛj}|ݽÑמÙ{=Øvò¯Gžç›ÀôžÝîç›Û»­¼Û½Ñïžö{4ç׺;pÛJÞ·¾õÞ¹|€h¡ÓPŠ>û¸×ש¾Ç|aè^ë)3Ÿ;x{@+Û•À¾îÜÃ۽ϋnŸwÏ€ã¾Õó·tÀ4(º÷ßžûv|#.Ù·tAÉG-I½uÌöîß}J¥6wr‡»®Õ±óÍ«švvÝ3z>ã¼úw\ãÞïL¥»^ÚÖ÷gZx÷»ÞšcOF®›ÛW{Ï#-ÞO^ç¹ëÜçØ{¡õ¦÷#-õ(¢€TJ ª¨ì…žâÀ;EµÐîË«¥¬uÔúzÇ'!Û³^Û^éÎvdSƒ+½Þ;ÍU7U²õ#1;¬Õ³^æx#-·¶¸Ð#-yêåÅuxóÞÍÞÖ˜æõ¯¯{ßAéÞëŽkÝ‹u¥¹"n.wHØ÷¹×–…=÷sÓèäNŽik °æìDÚå\íÙâ÷{ëÕÚ÷gÀ)^÷{î]»ÓÚKÖG²º½6ÏZàoE}·‘¾¬l;êåÝÎ}W»{|vêLfû°soºî˺¾.{jÍÛ6÷{Ûëî>šîu@¸2СèÖ^ïrQÚ÷§`îÕ 6Áºè±âÝ/T¶Æs›ÙëŸ{¾”ÛîûP+[v§T;”§w½ë×¹:»#1g¶}æ÷Ó„>úoµ¾¾žµ·¬_]æm¼Ú£í¬¹‰»ï½{x|Ý×n”¢ƒÖ”½ÓºèPÛŒjwaîó­;aÞç`^‡»U=0uÃs–½çwÞúêCèö¼àõJ …˜½³=P.@#1±Ý$yíÊßu„ôm #1kÜ’Mîå»`ïY·¼í35×`îqªªÛs¬ÜÜÛv˜1ƒJP·lí#5IÛuÝÚŽ)hŒ˜ÔÆÞé‹ÕRy†‡;í»·Ï{¶ ûéí·fÁºùÏ>°¢i&çÝásÜvq—ºÛm¶í¾õï=냻y>ö=öÎé[#»»ºtñu{Ío½ i|WÅ¡¦ˆ#-€L@h#-™4&&M'¢m#5fDf#-b&M4j¦!2A2F…<$2'¦”ö¡©ä4hÓÒhÐ €#-#-#-#-#-H$D !¦F‚Ó4ò§ú”~SÑMµ2ž¦jmF‡©ú‰£Ô#- ƒ@#-#-ž©IB†“DôI½F4‰¦´Œš#-4Ð €#-#-#-#-#-#-B#-#-š#-™ “DÄô)´É€ˆÚ ‰#1€#-#1#-#-&¢ €€4h#-Bz›Bj=ƒõG”lBD=@#-#-#-#-ÿñ¿ÏÕi¯õ#Æ­]5ßÁÕiæA¡©”SÖ«]Ô,"XÃ=ñESÌ`#‰ø¡Jü8":ÌVöî‘Ïè!ú‰&`Iµ›j[LªmÊíV›U±U³-VMm´š£Uж6¶Ö*-§ÚëQM­µñ»Iµµ^àP“K‘±=ð¥­åmY­«Ùu^t[мmF¶ÖµoǶÖÕ©1˜Y(ÌÔ4’i² &HÂH²Tm¢fjK#-›JQ¥ÛFÂÄÍ’SQšš¦¢ŠÆÁ¡m¤#5M$h–ˆB™b)iM€%cRƆ&˜²¦Ñ¢)6KFÔQ¥–šiZ1²Ò4Q™˜Å¤F µLØÒ›!„P›1¤ i#1l´Œ`E)hÔÓdMiZÙ­¶3XÌÉ™¢$ƒ,›im¦•5%%f“SmŒÚ›lÍL´˜™ˆ Ô I,«14i( ¦FdT¡¨³HÚ)(#1X,RlÀÍ640Äb6!QH’"¤™’FØÐ™ÂÄBClÍ ’2a$iBE3"–VX34H˜ÒYŠY6ɱ±¢Å ,I –R4m)¦ ²II‘£(ƒFM#13&$’"Q‰i!JEˆ¢fR²¦`˜ÑY!›&)¡™°L,FÒZJ)YÐ$“b Ä›IcAIj!$J6¤Y‚0`% †$”Â0˜a2’Q¨³*4[)*H’b iJ E²$‹%±&,³(¤lÉ(›)‘Q*&I£6F`™¶i‰!R&-JH4¦†Æ"5+%’6(”R"šIˆ&¤a²ÌcIC#5EA–De*4™¤V("šh¢kRÁ”Æhɳ‹%$‰¤ ¦CF†¢Æa"VP¤ÆÁ°F22HLRf0"Å‚&É °”PFÉ2Ó2ÖbŠ¥*‰¢&df¤ZbˆÌQ¡B²†*H¢Œ¤L’l‘Œi54‹LÑbÄfÌÊ1HͦÁ”I¬M‘‚"‰,–jdÓ2#5CDR4F›i©RRSE²2bLŠm!E’1J2’ˆ¢ÅQI&‰$Ò›F“Q²Ii„ˆÆˆ™¨ÓL¦ÐÊ63E!4Ì£±¤L†™Jl”¦)DBi•&ÔÙa6É%€È†’ÉŒ†ÑQT‰¬)d(¨ÌH0ˆh5%EšF”M´‰¥ RZ’6S)1D†i6De0H¥š4¦0†Š#1 ²e#HlJ­6ÖÑ‚e ©š2™šF"I¦DEh5)¶,Ñf)4Œ¤,³fȨ´Í¢ ˜¤(¦ÄB”DÒ!BÊmµÐ&ËSaˆÊŒQ¶5QQ²e)¦Â©&$Ói#5Ѱ’j6²!´4Ìe"Ë0„©#Yƒ12•™lÍh£’–£j)#5Æf˜MKLX°±d$ËM¦L†ZJѲ­#F¦Í­“,Š¥RÉRÙ±˜ÌÕ±¢ÈŠÙ4•„©–M¬•e²”¥™h*F€ÉHÚ‹ÖX’Ò)µ¨5¨¦Z2TVM%¨¬lT‰E´M2ERh£DQ­FÃ-XØ#5É“#50mbÀi€”R £ÈZ&IB´ŠÄmLJÆ-’,m¢ [TÖ¶h¨d&S5-dÑ%HÅ"DÓi&ˆÚÔµ‘lb”Úm–RÙM–%jYˆ’ÛMRÈšSTÔÑbÐ’™Mfņ‰R–ÊÊ)#1¬³H(eFÉhÑ,3IC"D6ƒ&12fÚ†hd±C%Q´“MdÉd¢È€l±H¦Ê@ÑŠË %(ÊS…%šf‰#56‹L™´ÚL`ƒcd)„RÊJ (1!£2QCLI5ˆÄ†¤ Ä`’f,PQ%2JQ ²¥T%L‘£I˜Jj$Ù!5Œ„FfÔe4lÊI4d#1#1!‹)¶")†²hÑ©6M„cd,2Œ‘¨¡@X¦f$Ѣ̥L¤¥”¬È2V0‚Í‹!± ZE±F¨Ùm¢*5)I°Ó,m‰†M¨ H”$ÈRM‹%IRJÔ¦$b‚C"e°”¦£X6Ä Y¤²le,YˆšiE%¦FÕ‘³B¦h”°¢FHARŒÐb3%JFÅ"[4›%1 #1%$$I&RLŒ-‹a*5Š1XÅ£2Øi0²I¡CdÌlbš Š6¢(6–lY ÊH6*PÉ“JRTÈŠ±hÖ5F¨“E –jŠ I©5F ÉF©JEX´hÒ–e¢™D‘TÉ6ˆ#TX‹”ª¤TѬmŠL”‘dÐS2†´ˆd €FMMˆfÈ™­¨‹X4TÈØ²j+i‹4£E¤ÉX­´”R[)¶D­&Á´4eIR£BÁccd¢¤Ñ%ˆÄm’$6ŠLÌZ̘,Q¥53c1¶6£f[%RF´ÒME!XÑ(Z’±l¤–6LE(HŒ%jDQie£PÊÑ&£UdÔe(ÚMlˆ!j6Ƥ¢Å"š!&FdÙ‰CS)“-£d±¶I²Í¬–Ñ2Ô‹cQ#jŠeSJبÚ6-±kI˜ÊQ f²±-F5škb6•*)ME²ŒÅB4ÒbaeŰD–‹%¶(Ë*Hڊزm”™Z4[Ó&ÑI66ª¥mb‘2¡Q1%І“e)¤•L1’m£l[bÓ5mÖ²ÒTÐË[)dÛSSl…µ&‰V&Œ¢ˆ£i Š#1†³d¦òíÙ²ÍDP!±!¦E¤#$DR1 _ö¡þ”?àCTþÛþëÆ)’ÿ|#ŒÞj}xô9×L¦¹‚Ú›ÓŸ»ý]L„ÕŸÑFATÎß¹†ÈØÓ¥Cþ•ÔUŽ1ûÿ¾ÅŒó‘ƒV7!Òù5ShêÿËë,¦q7Ñ®eÂýõ¥Ö¢äPR\'ÆÝÁU& ©2„¥ª"ÅÖ¨|^ïáì½_¿ºÖ×ÿ€»ÿGÓÍãýæÛLø¿M3ÍÖg‘,"ôsL?ÍbÛ'õ$Ø kè;vÁq¾Î…dác+`Ô~ªí;³Z‹®'$ï–¿¥†©$QCd†Üœ–#êB¿–¸6;Õ…£#5JL Uy¶ã»%E0•†,¾Ö¹äé4uÙ;Îè&7®]é†L #mWúl²XŸêKö5"#12ue¢¨ŒŠïP¦"w«6LªwãK™C˜.[rÆÛ,Îís_Wž<íë­~.Ko_nUãλa—ɹR3znh°káÒ)4KÛ×·ªô½wißO+Ìö;«ú#1WÍþ¾~Û™A6|Ý5ókÉ£Ç,/·µÊ$ªX±A—ûh·/ux½3FŠî­ˆ¢˜JbÚŠCË–J¼0 Í{5¥ ‹ÆnDå@áùiWŠöŒ;±8»¡j4AŒ1›™=‡Ùþyt9\ßGkK•HuM{¸ÑŠ ÄtuºÆnÔ¶”HªÇ=çuæÇ:]qL®mÒ¤ŠŠ¹·^;6*3íºùÍo‹TôW/È×"™_Fª+›ÿAºµú^ž.nèùýt ððÌ øÚy2ÁFm˜ôÓ²!Œh¶û¼ëx·»Ùâ÷Z®RV3ºŒÇ6¼'; "##1êQ•„kl9cPq5×+¼îÆå+—2bwI#s™K¾›µWµíÕþ½üû¼¾k¶ù¹!‹O{®Ùõ+šß¶2铘ËT|ñ›˜õÔ¡õ$ÇíhÆÕ¿quô½ÙÑ∕þÊ)7a£o]lµPPaÿרQ»ùSTÇí©-r3Ò¾Ü@çš:¾nsÛ£&ctELH P0ïT\&Éw]|lŽºì-rL Ì¦‡âÕŒGV«Ã¶¼E–yŸ<)Äruj³oQœ# º"ÐÛÊ9„>É©3Ï3¥îËÒÊ#5ï¡‹)95§Ã¯×~Žz_¹;wkvúÝûÝ®ký'A=6®Zû"§“vûWέL«HM,¯4Å”§c-¦ÈùPUéO- -™bÊHRuiXRª#ŠÏL_Úý8©Õ\²(§]hÙÕÉhT>¬ÝÁ¤ŒbH¯¹”+ï §ÕúÞ4dˆlržyþ"¿®Þ”cs¦?Ëu†dÅ/½EtP²1ÐøÐZæ¶ÁK«ØˆØÁ=]º/~ãÝ^¼Î¸ws)ìêC;·+½ÝZÝ.›}¾Q Dåu”¦de9JUP¦„m±zïϽz¼ˆFiVMCÚîX´kõKuîì~~¸Å¨¡5õQ¦¸–ƒ¥U E  )‘Nc-A¤·Üè£>àóÞWì~O–Wø¸µQƃ$I±þôG93#{7RšÏÎìüÝmÙCBn”2wÖöR•@uM.…¤•Óžzu–X×:\Ûæñ·\—.˜¤&ͤNê$9ëO#5†‡®¹;n”S(E%´–”ŠíßEÈßÊ­O#1#5#5ÊrrËÓ»ËÊbØMEôºè¾M|Jñ|9aÞ…ÓOÝhîѸbÛV¸¡5QB)ÙEO¥#5á\¶*ŠÄêÃÍ–±û4°ð„^L!ì“Â/ƒ/¾kÎŒgßÁ¶,: ”ØF—)î¹–¨v0ã·Ïø9í=ê!ËÕ]Î`GŒk´ÍÌXq&üàXÅâÕðצ½¾ž^UŠ¤É˜b’4¦üýÓ1øûÚy¦ë«”Ë?Ó^ÿ‹²øz@6ÑôIyØcÔ‘ðÕ4È4A¨1‹ÂvBD¿TAü§ËÊOœ¹‘sÒ:…¶µ99¦Øšd“=r’\€c}äaĆÚ%žÛŽ[ÁA󸻢¯­šÞ»â™«ö^˜ Ftfì5a”×çSdÆÔyÕ:”èøXðö\äáDäÑö%*=éúyß<{ße‚DÆÉ¦ѳõúì-z¾%þXâ¹+šuBÍôÝsžºisϲ¤>Ò¹@dq~ëÙ IíB'›#1^óÞÂÙ-n6a†A>gª ï¨ÍÛ}䨹e ÔéHñáaÎyN¤Ä—É:PâyË‹ás%ú}¼ê÷Œ1X‘«‰Ã.FO#1hô5v~Õc߯c²=NØGçÖãq6/u7HÏ\­àÀ¦Ú•GæúqŽÜËfË]·ñ·{-Fpu³~]’¤‚îÒôfc"7Z¼½:#¾Ç®W^»©õ·vPP2AV3F„L4g×sÇ-4­U©Šÿ­aJ(€"Bü¯ùwúÝm>¿¶ñÕ…/ÚÔWڔϭ)Š<Šï¢­X*‚‹}Lù3Ô%¿×vÉ÷}6«})>¶­Èz˜k÷Ù•Ÿî¾uA}z•’Dßáo”Â>4p[ÉUzµ#1‘Jí©B <SQÁ^naêíˆû ÙL¶”¼é#-” ¢¡©íp†~»[ãFSmÙúÝÇ¿J26Ò €½h¨ÎÚÚäúžÇÌ5›eZ'ÑX•5:yœ¸Ú_ÅB>?Š.aú‰ÄA±œâŸAÀ˜¨#—¥#1w7â´ƒ ƒ„Àâ?$:d±ü¹£#7Í¥ˆ¹ÜÍðÈ,E"‘…§*»ð¢Òçß½àôÖ´0ŸGƒgô7ñ׿ì4ÂEû,ò4USŸ 0hDZu>t‹¡#1ÝÙÓ|-¿Gšñ¢Úí­«‡©óï4ýn$BµÎfd_w”Ÿ5MÑÛdC A—Ùj]©¶ÐLºl÷5$Ç{8»¼:Ì;)ÅDÉ™xѦŸm2§f?9Ñéš!Žê),Å…+×·k†’³Ah¶#5(úÒYÙÃIƒ@DQQe0¨xþYAm½µT‡‹72jÉh¢«M=úUêW•œ’”šQÒölzãxlÇ:´ˆŠ³ -ESUŠ fÏ,Ž;ÅfšP¿]:¼\Cñ¿Mš„'£EJ&ã%ú4nÊTˆŒ^t"æÖúúfsçYÑ9YýÚÑãÆ ) Û¡Ùà—,¥bnÎl¨¼õׯ†ãÛ¾Èé|8»-”“¤$“lýonü¬Ñï’1¦²ß{4þ×S;_Ý·ø 7i¥³Ó}}Mš Š¦9s~YªùÁkæí˜?‡¤á„z¯V:/|GÏR‘žÆV±aÔá­å8s•á¯÷Pi½SÑn¥køé-”À²ð†Èé3šÇÏ­Hº»:Õß•ÍûjfM‘ž²åoÂêóÅRRÍoûK=çÇCž#5EP¹µÙ#5dX8¿Mæ·¥y²Æ®]}ºiªÁjô°ŒøÓÕrˆœ(hŸ\ûš ,‘P·µ[ßÏLLH¼IÒ‹T‹dáIß[}kÐ@“¼¬®[^BjŒªƒÞmì0¢¸µÌÕ-‹œRu»M½¨kÂŒ;º{ænOg#-ÖÇÛGëCÀMX8®ÞTq²ƒ.Ny¾uK0Ågîòšñ;꨷«0í•#1û4á=£4=°:%o.6¿ÜíùòæcÚÊ¡|Ó?›±Æ¾Q)©s»„&å'yaÑ/ü£¤ÕI¿”TˆZOb–érÅKÞrU~<®õÊ í4£˜^E^£¨Üj@Šëm‹ú2æ;?¯3ãÝMÇˈ$KÃì´H!Ý8îRÆ#5#1Ö«à Å×öbS?‡ïÓη¼Ãíx}[ñžÌÁfÇ•{Üðþ<ó™7E†‹µ]݇)ÏH~Â1——N{°¢Ä|›¬^^=Q®$óôj\¢îÖ¥#5#5rç‹#1|† */#1¨9ÝÝþ®œóS+´ƒ[(µïƒáÛ‘Å!‰PI¿ðÀgœöFluÆœˆ•XȘÀ“‰L}¹Ýaì~l Ûrs˜WŸ*ÍD¼Ÿ¿#íÎñó58Qìi¼ËGíú'L^\`<+ôð¼#1n•òDzÏ+¤E‚åìn<; ü{8n©$Þåh÷áð˜ðx7Öª10½ÓÃñ½Zvfˆz™úœ¦Z1½µ‰ÖüÒþM"æóÆ÷%² L «ËNÊK•åŸXl²B!7§ižÉ'êk—ÇmQN.a†ÛòÔ ÇôO<F”x³0Tb¬îªCøuÒÜ¥UDåŒlá$7‹%‰Žô Ò _}`x$¤Õxf,C²#1‘ #„§wÊl˜ÿ5Ìâ¿9ñ¾åk/„Ÿ®ãI­“;¸¿O;TR´ÿ*’®2NP‚â£X¿ÙãÎ̾ý¤Êé¦]¯‡%à?’ œâÆ=*D<Ãó3fùíA•ŸѲ­´ªý–~Î?-7í½t £ñ¢1AÍ0#Š¥ãSšß,âvÔ¢aMZäÑJʳzµOÅRŸ»ZM«®šµŒ˜}ZQ„ÀÓl¦;]—Ž4}©‚,Eg©áêØ/ž•/äÊ-ÏËlŸ¼À¬ZèhÆGƒ§z|p8wøáyöØëYßÐ÷´æ†19¥„Y+–|¤]‚ËASqŒá<§$q ; ü6™Î®)v¸ONμ;±Õ:jVÎÉ}‰íaÙ-›ëG{_vÛf#-Ctà™¶ï#1"P–…#+Æ TÖÚÂ7 ÌÄÛh1½úõ“dº™¶»¸Ë`WÞÑÐO‹§LÜòI£ ,Úª¢n’ÔVÚDÜž4ÌpŒÙ#w¯V÷ƒ-y9;d;Á´mðöÛöÇ1•£a¼s=ÐÒ^NÙ5,?@J:tøV±DÐã1ºô;Œ?}U&° TTM~ÔÁxRÁ¨5š‘mž-f²±ŠØªÎbcZ‰A²AA¸£/N)q@d¨ªP!ÔîÜÞ'k‘KŽ«®NºËϳcDUXºU0˜A¦#‚‹?—}žƒ4ú~é¿ î#-•š–J7ûž‰NkŠ|]òEÈsÖjsþNÍÌ…±§òµ5@N̾^=8/Kûî#b|¹,H.P €å£v·çþ6æå©à£IòéZùä[ËuìgôTœÓ8&a9„—ªjâ€D¢À„tÌÝQôA•º¤Ñ³ÚÅAv÷TGo8Ê”ÆÍs¹ÁZ¬:ή¨Hf³œ:Æ`×-áî’˜Õfî2¦yfæ)¤¥ÒÊÌþŸík[»†’]õˆÂ¥”pîZô·Î£3q6I#1J¾>vJCŠú™#5%‚¥¥âCˆb–X³’·½\­ÕÀSØ2Ýî#5£ç1†&Øo(eÇ):)ä`ÉäÐ ¤`4—Š*€aö|W?^•º­ËF†˜8˜Šò[kÃá¡“+~ÆÝ®“ïu\|šbèùÂ>snÕDÊüý0©Ì‚$#1±{د-Yäj/g®;ŸþBéÑÄ£5?2ò+íTe汘$dÈG‡ÔÔ.qU†Ö£#àwå;Vø’ S¸¶‡>õÚ¹Ä@WÜí©µ=×J´gJ¢ðþIÆÉá™ ´éÁøwÚ‡f‡Ã}oÉ:ºzèßBz%°I–lÂÉ\% ʧ6÷ªöñ*áº+µpþ¾8ì#1&ŒÃ¤JÍ¿‹˜ïËI±jn8†ënC6Îý—V¸iu‡ã¼ÄÆŠÀøEízõè(«‘’vxéÑýÞV~Ù6›®ïÑLÓ†]Ó·Ò55zm­ÃéqÜÑ.½“£lÆžæý±°ÅY(ÔÂ!2q:;áGFò K½D8‡»>>?xpfp9—“îË™ô~Žá-óŠøÕt\¹ïñQù1wÚN]x¤oÖÉÓA”ùýVáÖOY †²áÊPt~ñBÏÜHˆxAÁŒ(Þj”=í‚#5\åÚ¶ÔB¥ÎÏD@JÁÖEé½ÆÌø!¶x— §r[¬\lZ畲"X0s”ê.ÄcR»âȤÕ&X ³«bœOÑÍüt£Ûç®9öt¯oKëõÞ¬õ«.EíÉcgsàï|M¢Iùy†½6Ñ"Ü3n“¿×«Éêøª:=ZÓhD@„?w^ÏBé ‘Ký“NŸ«V·sÚb0ÀlU…!lLá×#-RðɵïWÍE##-,@>Ý~Ùp#-_Z;búïÉÒí½œ¡!´ntC¸@š‹¤ô-bþoWìáØ½KþL½º­£!#-~Úã¦tÿצ!ÙéÇoØçI'r"N.xUEC#5†üÂpd _ë|EQ¦´è_shÁôúD—ª5ˆê~|­%Ö]~næ}:Aí–¢‚µ`÷ +ùs9IïÔ»ûhÚÜwü‰5»†0C0C‘1Š£}c¸%ßۇݱk‚#Bß:#5½p/‘j@mVbcbˆ)ãøñ¦%Â@L Q@†ÓJtóÁÖ˜á»Ê „VÝ^¸{ýšl3ã³f:ÍûÊјŸ6>´€¬@6Â1ŽÞçáûì$ótL?ZƒVt­Cá±µf©‹‡Å=ÍÉ ’?ÁþIúû/™lÁ„ÌWøÖÿï†à1€¹ý%ðB(בj>ðõŸlÒ-Õ?å¥ðe½ß#1:ÇÓîòÃ#1×YëþÍøù°?Êæ?Ý`ôê{ÔO…醗j¢Áòá/îÂ(úÊŸÌîý4ý¤1¦Únä´Ø8t——]»n¨wÀpƒîÇ“•Fpªú:éÝúzvÿ¢I! mkâ‚ÝWd"0Ï æ€³»§¦sùŸG¢Æ,‹Jˆ<šýDÀz\»¯Áá+C<È…púÏÑ¡à7-c&ÉDUIä{Ï)Ç—éq®ˆ/mÒ²‚ŸÄ}þþŒêvdŒ»éŸE‚ÂÔh J€]#-ø#ÇûW}è½EH;´»e´ûeB/Â@­Ž(ì6½ÃŠîVjS ´Ÿ£#Ùz­àÞÉ~±+4[;~±éa*`ëI¸½æ •$àÐtŠØ‰Gü<›3“è^æé΢9n³©ýPÄ‹²›7ûøZÂO .xy Y ³ªÂŽÝ-Æz«Æ6‚9GMkC)àvywÖ’mÓB%Š&”>ó(õ–ñýOtà˜BÛžsŸ³Çi•bvò†¤“o^'ýO3ÎlÜÃ#ôo.ÇÙæƒ‘‹2Š£Ô9ÏXgN*'"W.K{¨¤#-;™,^H1ÀTnbæyzë;Ðêlj&½Å6kpú(/mY%`†nøim{EÛ¿­bÂÙ‡×_¥ÚBííÌFÚÏ7hl°I†g…âb~Lÿ­ªC¨úG¨ìJlÎ:ÆöÀXÈ-%n|ß„YO‡iõÄžôÅÐkSßý¯¿ë^Ùm#”>Àãn æúÀHk #1 ?åGY,-äKÐÛLé­jBƒzìgEŠ©(RKwó¶xà¢VÙÎã|/Gsû{ï\*øþ™%ˆH©Û3öJ8lˆH\Íí·ïa->¼ZÜ!ê}äe|]yÈí`4 9†›D‡1• Y¤ÀüÕ&‰5ïÏÓ;<|ÏÚtþðWõsøZqöt+Oí@OM!È­„–3W8_«øÌ¤eãÿQ7·§#18ÖXËŸ xÆ<7¯Ëô»½ ¿„ä©Ó¼{ôÅ7ؘ6÷¬µj˜Ùwărøæ^Ëʾ®Xs=ÉÆCa½Æ©µ*©¦´+XÔqÜiª¯¤7¿)ð¦ô»óî/p™Ú#yûÚ’KíÁ<ožÒÌ«œ¸Þ|Н:!{gìÞ¿¢õÆzˆÝ#5=pú¼=ÍBṵ́ãE¢:RK¼ŽœíÊø4ÇêÝžÙYbI8½þ[ÆU`„tG~÷ƒX·û„ª¢ \¤©ëž6ëöרkœñþ<^ÄÐÚW€Æ¢ÅèC¹Á.$‚ù¼ja3´BÕæ£Ò;ÅXBÛ†²s%ñÃØ´°Á!x  œD»"“$Ô,«tÂ{û¦áÙï÷ÑDbMœ Ì"#÷ü Ó|Ý@ó5‚cmvØ»g—¿ÜåŽÔ…‚#5IЉR˜ìe …T¦2f]¶6Í‹-¨ˆ²f8–aQWAB,”!x¶›0u¬iwŸÈAjöâ¿^µ‡*9Ñ›ø´ƒGžºôyfl+¼F2¤E€ÄU€}A¶4œ¼ùVPœÚòNÿgãþvŠÙ qØm²HH‘ O”HWno·õ×3îÿ#57ü¨ý×g¨¿Ë¨˜®û¹¦Þ¾ìò’„Ä#-´Ì%m¥jí#5@[¨h ƒá€«ä“n¿A®¼Myx†ª¬‡#mÐФívT|1a{È€õ@#+Ú$8£œª/‘m¸4¹ZcŒì)v¥œÊ‡^£ÄZaib„Ñ(Öë'bŽÛƒ; ÀáÂA ®ŽwªÃLtd ïT™ßxá5!lŸÙ@Þ]·5¬zæ Þ§NuæC“б."©¬-I#5¤B+•ʰ-ךè8§ií°æÈo䨉[,êHhE2O•þž9±’Ñ™Dfhsgd缩,^J®›_TÑ#5”ÝÇœ:=z»¥ÛÝpýrt|==¹æ¢„8võr{v¸‘ü‘±áøG†gT4&Q¥·nÕÆÎØq‘stÅÛ$ÓçG™c9áÍFbç/P…0ç(Ýl+‡ª³2:–-ú#uÜ(» ²£žç«ãÛ c­zÜôcvˆ¥!#ŠV:;¿S{Ê:y±±éb¨Ù!–·(XŸ>žÚóG®²^}né›+ÝÈèÝTjyÅ”ÌöÜ ‰ƒJ…ĤAœa€#1ˆÒèCƒQ­îƒdôú||3™7W¦sÓfØm³n+‘$ #h}ï|j)ò¨À㕤˜#%7l+×®18>²ìÕºÝÙ¡šA0&’#5­Ì;a%å+d1q¶Æ‘¡4p5¸#15)ŒÆ«`0ÃÝhÒ?s" † ÑX°1,q`„Í”.²ŽÎ@˜‘ éÇÕDؼx¸ê™}ù#ŽÚ:O³E¢¾KnhÅI{+¦¯LZ¾ò¼÷í·#¨Ó"ŒXšöî×un[’UîÛÆŠŠöjó ›á†?§ï|ùX;›Gz„“0èN2)eÖZ[/<‹}ð…‚â±^ ã¼·Sƒ3éxo§>1ÃݱH„ØôcÝr#Ç^Š6ýG,áX’ê(»\°Öõggا€Cí=L–°¤W‚›€nŠV¿Ðå˜:ãOƒGË.Èû*,Èã%:¹g•s›„ùCåÊjmøy…X‡ŠÎR^²H¥$Z¦É¼NÏ>mƒƒ[ÅÒ¹õ*TEÿSRÎ!Ou.y<¬wìøÏæÖÎ$f_­>óp|œÆª×›ÿ“ú³÷ùp“&;‚`ÙæI)‚w|S›².ÜIXf$ÿDÃ6ëòýÀ·¶Àñ6j:)x“ÚØ‹FÛkA‚ÍÈË1ôÎK`xU­ñŠF%"©áÆ„cÊ#í¬¥í³ìß¿n!Ç^òìŠÍ¾z#+<ë+ù&¤H-‡vÆ^r_¯›ö¿_løs|üþ®x ¹Ù«fa[`hgû÷Fm¹reF’#PËpÅJoËÊW×…Vþñs³!“Û*Ž(éÁ'ò/ãZ“Cû#57³Ôå|eU±F"~Xø`cü1˜6˜8H^%#5Ym˜¥çëö|ïÞ`÷Ç´PwÀ#-ŒOã÷Û(8à÷nðÛXÛ•Y§m«\ ÛióT’ë¼3oõ–âp1Ñj:†#šú:v¬á-¾4ûûלkFÄ›Q-ËZéRF®[¥´[rªém4´ˆyƒE˜Ž)XiŒŽ`Õ²ì:Zˆ¼’$SJ-~9©:ð.™›Zˆ’PØq·›½›šk ï]?œìgš²žß¤(ôÇÞ{ÏhO=b~B H ÃÓÔ[ñ®f'?£@8r¯ýí¿pwÕËÅû£F1õAÐk<އ‡>ôz!†¡J—f:Ô8k®èÐX6šŒý÷~íÃ÷Ž?¿NŽžÔ´Š‘ÞB|R£ŒÏBwÈZ|VˆïàjäòÃwg7#1?/G³Ê(%ËF„»qtAÄÇÕµ€¢& ?ª|i›UаQ@ÿ´f×ÖÃ88‡m>òý¼+ȧÓf—&¦3ý³áWÏ3¢wË|y­Øù”ê|"¤{œÂÕìW³‹œóÆÍýϲ[£ÃŠþ F}ÐañxÿJþ?œ8õý;“0‘oxãòß»S»¬²BŒÜ-íéåºu?Ezz]ñn²]>/ª@nôÁ3Ѹÿ*°òüzö¿¿î×ÍZìîA¼½PÀDc´÷­€Ç­|Ž¥dX8¹!ì’G”ñìîôò6‰B‰uÃøÒëcöWa“bpN(wøWŠ,À–4ˆ†.©–!f Œ_ Ì£Œ}þÿ§è¾=bxL9n†ðªâ£ŠZG5–…õÒæSNÉõºª"4ën»Kºü¿Ó¿ïp £Ãÿ”ÆgîºÑ”¼?/ÕíÙ/•#-':(D% %.åç²×Òü:7¡øÇÒTxr÷?Oi^™#1¨}Ñó·÷ái¦ô²,QlFzZ¦mIdÑ& 3eK‹õÜf&6*þRôvݬlYnç]ºUÄ#Rþ´¡@Ò_îà57'p¶¯ú¶A€¬$îŠPXëýƒýôÊÀonÙéh†ˆv3Ö(áåæXœJ [‚„% ‡œ‰“·6”¼ß@u•a#-RζQà³Kª£Ñœf9Œ†¬(E /Ñâ–£Q  ë\o¿ì®OÓÊ©#-”Ù*0Q†õ\šoXdQP‰Z•௼=X`hh±ùK=%ÿeo~SDüb5Feùø)3P°è(ôÞ€üŒ‡çm!ÁÙÒŠÂ'öÐò!œŽ?լĢ€ô°R_ÒƒÜ_ß>]^O­~ß›à~k¿Ëìwåox—Ñô}"Ï‹©|œÉ•=‡¢Ðå´r©ôìèå;¿hÛAô–š¦¶ýÂ#ÓŠöÇö.Ï^R wc×9~”ñ²+ÔuW¸êè ˦­öðPðü´x(é:Eºl·Á€ç w¢Œ°ÁÞ¥Óx¾¶RœÖ¬dz œDÊù£¤ˆžÛ¡å%„ûzn6¥ÈXѶü¢_ÃEŸ{H·#—qÒå•]þ:{#áºMfªÊçþyޝᢷopµ,Òá½û» ’¾ÎxŒuäâUHN#5–éïµà‘ô(¢þ†4ÁÓáz—½C+´á«}÷[E¹Þ£MoëpÖƒp ·øªLÉÆ¬ºA£ÎjD†mºÊXí.s¹µQÿ'DÒùA“§Eú<¯Ê6n“ïÇ>Yv×…!Uï`á1˜·"ÀÝ*G‘þgVe?¥ÿŸÆ¶{ÓúϬü§õ6,?Y^0žéŠ¿À#5ôO‰C€e(^œ¬:ÞŸ .o(€î*Š.:Ï0™ý'{Ë!°Æ±M—Ùûûì#1))'k,HZF£FìÌ"m§as¯ò°¹ý=:íqÇ6>šØP*}wÛ{¹¥§‰V}ýÜ8×IÛtü*™ò:mƲC¤Õî2®²GÄ´¾Ö@çß®¦ÏBþ± $'ÓcÃÞ ~A°:Ä$á*ü"^Ud<ˆ8•(NÒŸÓOÞÃM¾öû&.й1("qî! #1{=ʸ’4@€RÈ(¡€?*¨Ÿíþ^׈3ï í°ñ÷!ñÝåýŠ‘TQ_#-¦ÓÕãØÏ)‚ŽSã;ÎÛ,,1#zÿŸÒMa¾Ùb ?ðxIW®¹| Ùa~O2'ót=?Å\¡Å"…>#-7IQhI„µ%E´E2B•.ý§lô_ÝÇžÁþ¤aütma(æ?åéݹRi]­’Ф-…j"YH½3©IpŒ[À Bê[ï¤J1A¦Ž@QÎD‰ˆÈ‡@x*,J.ž«ù´ô|}¿?$ý?…•_G2å=¼½2UAëóó}¾Å»Ó]ƒYÛòÃé¶Ó\xyH¾ÏKg/5Ìë»HÓãš )¢ìí\l×§­þ›Ñiìîû›¿Ò-û±¶Çð·_wÙÛ¸jÝí—6û-y^ˆy¯Æ8Kj²#1¯ýóuÊÂïô72óð“Fè,Ë—[Ã8*›¶gæÍÈv;t—âV“»H¸téý¶߿ٯ³ÑžZ|j¢Àï&[3Óý9üî;£ÁÈÚl¸(¸dÝ~]XW¢zy¼U¶ƒËN{qÀv½‡Fiø'§ùÇâˆø‘È@2àÏgÞûâ½™º}Øë¬é°Ä[ó½7eîûž8)²BXÈr/; G’ëe ÷_ʶ׫’M3µàûnTçžÝ>gɳYFÚô¢_#50ÑÑêÕõ›]Ç£$æÃOó±Œ5U»¾WR7>‡Ž½š#u¯ üuKt.Eî[Žôt<ÉXŽèÖ‡müñ¯D´ŽùÉÑ"ÆÓË™¼m­iªË;"z¬°yJSã¬5÷grVÑ?žæ4lõSLj0#5˜\ç‰ù÷ˆÄ¹‚ÅÌÌ`osºÕÍ/³Òç½G9añ鋸ú9¸aÉ·Ç¿›#1Üžs·Q6î^r ïW¯’ût\}*‘Ûe98¶;µèhxÔ0ç",‡³·BÆC~ŠZtz¬?G¸¤‹—äg§|•ˆ‘¡ññTÓÿ:Øc»ãöjtàcus7[ÝÉöô¥ôÆ:UßsÆþOœ&±—›ÏνŸq§·Ëç³aËѶúN#3#1çh¦±Ÿº#ÝÑ·â€ÜНª»ï³?Õ§Ã]ösjv±mC-›œ}~Aé_>Y>xé g ÛÚô†K©÷{¥öZ@eTc<<>x;IÅ•90¶=ÐÛEõŽ‘#1û<—ßåÙnQÃø(‘Jü•N\W»åäñkêÏþS«1€m§¯±<ŸÏÙîŒôþ×áwíúö»vó³ùhœß³øácÚþtz´÷Ó½¡ÃÈ–ƒFÓOu)12‹/n“Vî!Aðu ûô"¼r•Ä(íÃÍOªñÒÃ^ˆ¯³‹Z>]|ò¾˜;~‰½ÙÇãé›|Æ–ºzú,ùÝXc72s‚ òãÆë<³ÿkÕûþüô#…ú9T<;ó#-¸W›lg¦Ïo³ô­®Ñͳ<à}Gáðûý.ÓÛ£enê‚d,ëùgêôW·ù\.R©Ëû|¾Ó˜Byÿ#-Ô½ÎêÔ5xååm#1,<¡¿ìÖÔþßÞ„î{ŒP‹5åñÁX‡ùÍãã¦+ú>¢¦õ¾ëK9ÔèÖƒp*‰ÒDº¸xÚf£ñ÷ˆMT^#-REæ0˜Ï¯S¯áôa³üÊ‘³íõýû‡5ž~‘—Ï·õü>[º0Ѩú¾9uvgÚ%ûŒÍùlë·cç˜ÔýGw¨ß§Òݹpˆ"¶ÙÕ³e«³oÕ²þ9© »R'/à½è;Ê#1=v8MÃÅñ˳¯÷ê´_öw{G¸}Ãxél9|Cö(\¶Ž†Ù)cÙsÿ•@×û´íª#- ÿþ¤[´x}ÏŸ0ûuÿoËóþ•é»Oê®×#1C¿‹Vïßê¾5Fß:pçŽÿ¯7 ³Wa¸tÃèò{×Íúô'È?|†‘¸(ñåp"!‡¸s¸~¨äà)÷‰êù¸Ž#-|c÷}‡Ö•Náßãâ5c#-˜ª½J?¯¿61óly^}=w¸'ö’‚¢çÔO¬0DUDG"l ŸèŸsyåÙüûº;—YÇÂÈÿ.:£ò–+ø6×b›‡oj¶žüç¶±&_Kß;iäš(úAÏûð²"„ƒÈ<;`(0Ðoîºß«Z®žþþTŽ—ÀiPB|Q¾‚~¿L% ƒ™´°#-£ÅBr²$<ÕŠ’ö0atµK ®c2J2A7º%lÈ #-¢ãUÞŽhÜ1€deùTNZ•vak:ø)ƒêšW›-ú¼Vgí´g°ç¯/ ØôG³§F¬ëw±RÖ}~VÛÎíÛå™G©Ø×Qç‘” '‘ÁEE¦ÓÖ7£Hýgîä¦Ï$ú¼n­ÛæäüG§«CøÈLÃ|½Š?ÊÂÆßNGã€h¡ˆ2ùƒ)ñ¹_0­gÖ´¢+‡9ç2PåèσYkX+7t{éËI­’!Ë*¨s…’ˆ°NrW$¢Ï‘ºbªå®‹)°±¶1/1B+SêËù^fë5Õ÷*/£ë§eƒZ#1¡}›»]ýçàS!èf¼(©”-´¥ƒ:Ò5àæõJ1±m6ß÷oæ&u³€$ýþÇ9nIY£;ÒÕ})Ñv«ü÷yø@õƒ%;RI»kéÓŠUýŒ„NÒ{#te¯ºƒÈFX±8øÿkì·Ó”º¼´…žÊZ=v^¡¤7Œ#5 á%ì{u)$¯?€Ð2ä=ß·…RÑú{Ù-K:t Ï“Ó~AÊá)èx‡¤¼²~@ûyã¶­1´¾:Àë3¼ZtSý?/ÖSpÐÉ÷£”þ…Gñ È칑µ,nÂVX5 ˆàÂ0§ãê¨Æµ ¥m±R8"<RŠ”I»b4CC˜ÜX1UPq¸TEŸÓ¥ÜRf®îSVe™ªŽ„CFØXGb–Ç`SX äXÖ0Ó$¦ Õ‘447B‘KX­‚/mZõÇêéÃTâuÁcm#1¥Õ0Á±I#-ëü[xºjÓ’àä‘·´Z׊ÑÃ&Ó¬Oçü¸oG|ˆý™Îc: AAôÿHüV°¦xMf@zÂkýÂÐÓz•Ô[zõ9 Ô…‡a±…È£I–†31Ï'øúøýù¼|ÿ–þO#pN0£’uæT2f]؉U#5ŽŽ”Íf.ÚF“z ˜Èd‰µ#5Q ¢VD”Z…]ÕÈ5GÑ”‰‘ÌU‘UT¯õU@J±l‡ ®IÝ÷þ˜¾Ý~¬¿-?ÇÑ îØìÄXØï»=šL çvù–4­Ms.»+Ï<<³‰ßÃ\´¼wZ&Q†sîADzD’=ùK®¨ûÇ›³Ûù~Õúõ{4êÙÜQ-$AâWõü#-pÐé(¦ÅSwÇÁÀ]q×O_8®VÆÝüœýYãídIּ߾Ïðz=ïg½`Ë ¹­ . <}7?—ùÝ|¦Å-ÒïwæÞßÙÃ_“gén¾®àM>ôMÙYùÏöiK :üÜkÀž_¬Gë?Ù(ÔgöýÁ‹0•ÀÿS¬{qoŸ;cFmw1[ª-«DŠÚNG%cµÃÆÁQ¬!ëG ð‚¬Ä7ž<·ŒkœÖe,•"‰7SšŒm¦6ÐŽ šÕ*zr(j#1ŒmF)F”,v¸XF†Šä’*ÊV[?³Ò£FÁöRVÞµ æ"ð]|I¯µâ ã(èKk™Á$-$Ò’.NZn£ØKFñ¸4ó¤mñÌÎêð£ñAAIeC…N"G»Ç<.#•2ÿ A*5¥‰6*æEG!4®`¢Q…]i…F8‘–‘Œcdó•ÎÁ*¹àÎÄà…V’#5I%G1¡d3õC‹T^§%ïd½c==ù¯L¦Þ’ÿ´cÿ?'+\,»î25"‚²{yÔÏû—ñ÷úÓ¸ö²ýJŠ[«F…ìÇçýb)ë†;—™×‚FtT¾'yâC­øìÉÃÝ–Õuõû‡¯>mA0«Þ[öšÚè)ÿoeá3§F9–ûçùj~˜îîh¨xÎìÒ±ê ÝrûËëðÑ›""'NÞ6é¦ÊñoV{-3ñ]ðé“àmóö™K´Wöÿ]ò½oB8'ð—8£uîÞ¤>gÊÏŠŽ%Öö" tð}¹ìOÖOŸÃr“ݤüoël¨|>•Sj#1KñyËÒª¥ÈI%™UU®îQ8$whM 9,HÙby·÷ˆ øAø:Ò~¨:_—̼£S;—ùI£×<³í©«O+>ŸÝêÄóUQ…?’‹ ƆfU#5 ¬n#5Ððv¯‡òk­*X@=Y²gF1üÚ_}pn>z;: lœP^˜_S]Îy$Êá1?îF=Ü8C «²küùDG¡¿ÛóùíÓùä£ÊZ%T»…#5Ï׬ª‹Uü´޲FÕ#c$#ˆÜ‚±#1@?¥àÑüšºoÚ%åÑ–(£ ÝŠ:ÃFFºß7®_Ëßòy+Ô:¾þöØnÀa/Xí퇃ÄÔ²`ÔB„‚©#c=?›6z&As(O« vó›®^4Ɔ C„¥³[·FŠFÑM`¸‰ëU‹x#5æ:ØAŠ <ÔºƒÕ®úè« Á­dhmW’0Î5”ppE30ËBƒH¨Á‰”V¢À*÷¹¢k‹KĽv"15õâ¶D=ÓxŒÇ‘À­«G:ó¬Ã·×…aˆ>Ӄ÷Páp9Å#5Æ©Cw‚2¢0Õ‚åS¹•1é´³vçÈ•MàÇ!#"êø\–Xƒ¢9«CãU›z´ëkU­"­U¬Î—¾”&ƒ*IPDh9(hkmƒPvàÚØ´w.Ó7@­èÒÃXŠÈÚÁ¶î®…£šU€¹ ¢ÙºÚPª8õOÛþ=4Øúaƒã{{w§‘‚ç¯s]Ã/:•¥‰=2#1ajÓîÍæÊÙÖ¼!βˆps) Á`¨Ræöû'qoU³Ð,AhB‹k`F¸ñ÷ô½ƒøx#-wùC¿\1gºJH#ßû:/E!¢Œx³l‚eã1Ÿy”ÜÂ$ê#5 Ô#5ò÷ðéçš Eãëß.sªìs¥—†Ä{ójOÄ õŸõøì³NZùvzº@èé¼:ÜvêõšÇ¡²C$¶(Å‹ JªŸ¦•T@Á§/Þ-È®‘©"âã ËJ2!ÔéòŸN–#5¬’éÔˆâ$dH‘²¨q”wãf¢ÆâDéúæ†cý=!£Dž›¼Îö1m@ØzuÄQé28ôÛ(qŽÇŠ÷<^`hIhºyˆ0‰PN"§Cn#1æ9˜TêðrÅUŠ¡©Ëo?Ù哳]®î˜ÖÛæaL²¡(,,×BµQˆd‚üúwÿAü.»ú~/@êüùxÀxüøâï—³H»-^!’|ø‹ôòJ½µ`Q‚*e Ò‡9A4ûfϺM‹–•©‘¸·HÛNÕî3¹Âes,dS#5 E¿mQ±¨8ÈÆÓ1΄#.´Â@ƒÌ}¤u·Ó†´ÉÞÑm<¥ÀFÆÏÌbVÇ•sˆÂµÚpXèÜr¥êôaÛÇ…F0О5®»†g†Í`q¤N#1R绋GÁŒ¢Ë#5‚³XL2ˆ\5¦ñ¥cà#1 íÓF ãÆif¨h±ë@Uƒå»¡¿Å¤aVÉSF`×´Pvª2CaºcMKU“§Ôã±›Ô“Qº:H8m‚èКIhN12ȉÃ{±8°/Gu©#5…q½˜2Ñ–Ùlbq,1}nJXfŠFRÛ:L¨å2!´øÜV¤XR…y™dŒaL•ôʬݮý—w?Ãz;ùôzùõ-ï§‘fO/áæòóéô½R½.§iÿ‰FÕzÖf­Ó}ƒÂÛCYxgÔéÏX?_ïòbÐß«e’H;8áåÌzåœEŽÚ#5Ѽµ!k¨:£ºY•á® lª¤$u>MþÖ}ï¾ñ¹,ÖÁÒä=ÞY /$T‡Tjm¤n¤kž1’œÕOFð2®ðËmÆþÔÑQñŠ.á¼ã¾ú3Q·&䊕t¥¸Æ=ØÚ­að&2 ‘6Ç­W!;úÀÞ—YÊh#hŠ5íæÛÆKß1·M±›bˆ‹‡‰#5—#1MÙ6!L”9†·ˆ°,PDøÔ I>Ùxœ&˜Î×#5îP™«sGöš(ͱH x4U=¦,Ø8çãÅ!— OÁ¾æ* A"f6±ÇI·v/Ì-O&ãŒÞ“BÃ-:lj&›ÜJ*gYÒèÀ¤§!‰Àd¬Óâa¢%aø¦Ë-¹ç¨aÐì’yÜiÝ£²1^Üën#-7Ô#1n{œ<à(´ýDÅ+{Êv¹óGM‘¦4Êý;iw9ó;aGÜ›6ÈIƒñm0ËìëzÖ6hÇXÖ¶}^Bå 5÷Ñá©t#1,lmÃågK¨ôÌ<­Uky2ª ¨iu%.#5Ыå;ùºh ;«ÎÚc…ÁÄ`ÉoÉm† ÃC¹¨äd锚á(·‹|âÙ6í­t®TGÖ$H5¸ÐÜ#5;+ÊhÁvۆ婸òãiÞ‡^Bö£¨ë³º‚²o1,•ÿL¼È\ps¸QÒøÓiÓq$R틎…`'áA¬XpÛZÕCŽ‹÷®±Y±Ú91MÇK£ŒæNLìeN©Æ¦¾Y°!ƒÎcP6¢µYctNf)ÎLkB¦Ã›¬"HÎ6J) bæ²WK»`XVCm7ÛlÙ8iòM­I¡‘O3c’ÈF2E6Jƒ²Áà#1.•+TJ §?Hq‹¢#1¸ª– ÐBPÞMÎ??|Ú^) ćZÛ¥=*!ȸZ}ìÄ¿eƸKÝðé·­0p´sG"yS‡»>8rqB1’ª8מm[t?ÔΛ=ÒÍÜhIÄäGL®#5&Dëé“í¿kÞiú#5b-ß÷ÐTM_]Û¦f•¢#0ïÍ4,¹‹èººˆÂ×mAtUüetDÝO!RYY5 D©œ¶•K¥ßce¬-$m#5pAG›p3kxfœÊ¯iÑöE̾jaC&t¤Qíq=Œº‘#…Q^5ÆÙ-bÀgM2dÐçÏIÑÏ#1|qƒhÁŒPšp¾%ц={fõ×÷%#1ÓUFuέïŽñ}óD¬óLáCXv™Î]áŠè>º¬u¼õZU;„ à æ#1¡è^J°S;tÍd8çi|š¿ð¸û³»á÷˜›U§}CÎù'‚²rÛA³^?ÏÒ¼NÏF×Çž»óM³ç¯fü¹aìá/]Ü2M¹£žµž-.U\6áK«ÉZ¦©‡Ýá Õ•Y›†}óOsMc¹¢¹¶yUõ¾×6¸…¾ß6@ý¢«­Î"4&ÛÉVã²T‡L鮈ªëíTǹñÞ@…ƒ–­ñxάԘ“NÀåÑqH} $ò?hçiå½™7ž"øK³œÕ?©1 Ðtsù‰úÙ‹Ä¥)ÖД|œˆùh‹•“SƒßPkàJ6šê‚,ã4¦ÐPf·­|ýz3#-šjôžeý}’ã5ñ˜x®¾Ü¨nÛ7@£§Œ&TU>S¯y”ü7鯓¤«fY@Œ¹r £i‚U‡ñ%·`6¶L[2;BÑZ–jå&%Á2g§רðü/åýÅ·»—T¬Ø˜‰x:x1s#-?Q78Õ™ŠÎúOèHÕ_eošF}û]yIHB¥§Ù,²tJÄ=:q↞øò©¯#1Ó`Ál5ÞÀIþùF*xumŸ&B•ˆ_iG¨²Ý‘ÙO’9Ñzš3°˜‘$S5_Û#-ºœÉ€¸O5˜©Oõ´ò^Þ®DøL«#5n‘¸É»2yÙ˜~Úþ[å’&”ÖIÑÇM¤"H9¸×”É&¦óÀ²ì¹èŠ$à{e²êòkÔ™)&:iýY±G“,#oTÄuq 3Üš3é?uuæu»ùYfím#t¢ä'£oqb™ñ±–ÓåÖ1Üþ´bqÔy4ÜÓ˰ÍJFÁ÷TWFÈJ÷&Õ­Óš?ǯ.W¥9´äÔÑò´µ’}GTq_t¯fÔE­K¤™læšNÆ~S4¾\÷c%|á{ôlK!¸vŽÅÉÌètãáyÂñ©:·ÅôMìPü²@»Ç0Aç‘ùDàÚxP þ<îd÷É“-í:…bÅpç,š~ËÒÊbG6‚“HŒ$˜ð úgÍ•luazí‡>˜© L:- @~½p^ÁæDqן£àºôÑÑ^„Q&–OÍqÁ¬1üdeLå½ ŸsM·Êò½Ø8#HÐ Ñ\÷¡“1fÔÂÏe¢Ù¡cQ.¿‹Ò¯c/3¡îvðšøw.Þ…’î¢Ê^~~gæ#ÖŽÌÒ~'ïœoº“¢]ÑaÑR‹ëŠÁg¤¹%¹W?m\m„ª?b%~ÏhÍTîj+9 ·ù2;Á‘‰2Ñ|¢³kx“&A"²Î»&\zgäCmfUcã;Ó}Æs¹Õ2Ýo”¯î'|Îðÿa§çºàÏ”aŽk,­eE|—ÖR³1v’ x­s³³“ÆÐ(ÿ×`¾91’­¿¢à{Ae{žy j‹¤4y ÚwÚ6íp?M÷D,åG£ X«¤<_{K|u}uÑJÅ B”éîR›ôŸÆnÉ"yºó¨²_D혗šßŽI#5µÑóCªÕ¼2MöÕfc­‡É3£ óÎÓ˜}¥™û5œÍNîQÝp­NHéËôÓéqCéktCíí];eõº-(QåÎòb¾s<ú×ÑVÛãÎsnïnaŒóÌQzy¢Œ[y½)ž©Ò_]¬£ßŽÞܰÁ¸†`c"cÑš5¦]å½ÈïÞó^QÒZü±v÷¿>T‹\ˆè-üÆÝíSÅdù]ï¤íÊØÙÍŸ H„(äÞœSAͽÈàœa©”ä;MF‹ò‹¶9êçÅôÄvèìëÕxç¼cÉuT(ìå:KZ÷½­û›Ézʧ(XrvèŸ9»œ¥öo“|ú7ôNumÐH⎑ΆÀŸÊàÎaûf‰»Eòð>®:6nïuÁŒ ^ï[ž†Zbµ»EΉŽÝ¾¿$8lAŽõL«MBô‹’Qo5üpA|{­¸ôÄ;ÙRÕ8>ç<)Áê¤sS‰ùæb’÷ͱµ }èÆm‡Éi¹ÁölíÄíJÙ{>L;2a’#².öÛÍ—P{ÂÌ•mêÔÀìç’iîÞŸBI/Dwï:øÇ‘ ©^v›‘N?³n΂ÓÛÂé/n)n¹3 „W}4ô!KDO?Cb¶Îù™K'‡xì‡CíçM!–¶OÞg¾)»ÕlöwhíÓ;ìi!õ—!àî98USHè†deo¹µ¾3^X„›Ö‡]K_âÁ46:9û”AÒ¹æ7æŽ@ÈŸ&ê<ˆ|95×âS¾çã¢|[͇éMÃÏÍúQ%[õøõ£¯Zˆ‹]–kœÐ”À¼Æ¡†Ø¥å£âŠêìîâA+gÉ›­|KƶNñƒ7µL#5¥à ´|G…¾ÝXë¢Àï&Æiz=ˆÄ<žðçñÆ2xáÎ1.$BM#-èò}”µXØ#%L¸%Ò#1­âš›?¬owìAHã[Äüîÿl½ÑÞm,é `Ä(ý{|Œõº­Uû~gFº×Uøì¥‰4Ρ›ž:ÿ1*=ä$­5Ç]gO¶tÀ¹Vç-¤ºú¹ÖW¨ÒPyÖÂé+»TOGì†^§K ôp}Ù­ü—‹q&K4vËȬ¥mÑ…ý73úuÛ}ˆ5ÞÖbÀ­[?ÜåÂÁ%³jIðk׊d8*Ë4NX½®F´yt@3±¤õ¾‹™æ'$Æ¡Mb~×§3å¿;ã\â+ã×–Ù[„`¡”`ÿíóX’ªìÖ|õ‡-ùk¥þG[L™;ä·-¦+È^x¿aa¨ƒW[«UÖ,Tó~:†ˆGÕ­^ýûÆa|VgËÄ︹Õjù¾ñÑU™ªXÁ1y0ƒ¬±¬>6sqöËææ±5 ળ[m8em[]‡—ŒøÙõw=y`¾nÉ‘Îc‹Ãý3•Òý§n»t¬·VQ¶ØGpÙå‹ðS·+3Ë8´³HOû¶«£#1§W‰4‡ˆ—•{¡| Ý+£Kæí#5&PI d9a†‰Ä lc`ÕWcmC4¸97AÓõÑx[}EøÄKßdèÂFF$wªŒáci}Ö »­Â7z鯱£‚ã‹KY‹dhõ.Wò/¹Áû!û[š}zd#ñåk¥&Þå""#|¬ÉÓ{µ@#1„Báȶ_e0Šx(ò`ö}ÓÃwm¿$˜ÛÑ},EÑs­ÔÏÙß.}J.ˆÍšºúnîµRëËvá÷ AåÕh&•I0£5‹¾ûp²uÙØ$ :ÖÆN‘/V©q.ì|øù`0¤ƒ m#52tà -Tgß§vBbÍ!fû±.а~›ƒY#-VJ´2{¶dÌóªÁÝ]¯…Ð 6ì.r—V2m9g›šjYð£‹rºáP?D%¡tÆRr쫪›uê‹å,èKÄިś”NMßœï£H"—XÈMŸÞÑr\¦wP¡nuÙ[¼IRè(‰ˆÕ¬ûˆ×ºK6O;]$ïQñ/Ú¿HÖzõ:çn¥ù#Fãœg|dç<Àd®¸mƒ£‹ð6aðrõƒ«l ÁÎÓãÎ’Žªï¾”ŒÞJß(#1;¬]O‰€Ñ1Ä-dx†ãŸÓÄïK?%Q~r u¿¬EsÎÁ¯Nσ¤µÍÏh#-è/=é¶Yçhé˜Ç¾“v³Ã±„µ./#5ã=‚M‰¾ãFç¤C+«Uƒ’g#1z““#5¸2 ÇE—:Gy˜º#- ι¼ö-‰bäî-làò9XývEâõϲ!÷tZì´ø×[¬µC¡ÍÁ ñ;7^ ù—Jê§Íß {&‡ÔX÷©ç×'l3öÜ}³ ¾œdûi‰RêŒ@°¬®acÞŠ¨í8Ý+$øù  #1 Xm£¹,®Úân_ªx|öæÑa5üj¦W3¿3[*S­üõðÙÑ£^tZ²Ñ囋¥¶—Ú®½Î‹ØH!a-”9“¶×ÈéäFei«D«#5ÝU»tá^˜¡¶¬7ï+æû]…+97O©÷xà¿ë½¥úÖÈ„§drÒý*üTk‹…‡òw‚ È5¹ŒüñÀèºQ{ô}_½òâ³Ï+k8ô¨Š¥Ü0r6žšIc·#®G:%U^7Ž{çDrKq:ñp¡':ÆVFXÍÐ%Yî]‹äê„ø>4`ªHAÐX•ˆÝaɆ¨ìpþ4Êpµ¶öÈÓ:áIÛ‹hÓÍ!¶áG¶ì!j–·^ûãuEá…)ZVMmÆc/1Úd‰§ûœØÚ«ƒ¡­óË&Á¥ê–Í›ÜW¾Û«#1NO’ÉHZÉÌ;÷sZü,ÊÂñVÃÂõ¾6vmwUN\ø4ë¨1#1·š‘K'²¥â4¨†=š,¹û†¶ÆÈºÿMÔ݆ jÌk ±Â°ŽÂGdß81-„¥é‹úqŽ6ìNÙ«"VHq¶©42fd47æ›»·.Å’E¼gו„º‘DŒ7@óÊýtåÖе ;LMÆ<ÅÔ5ã2…DP¡Å™ç3v‰.‡6ì*á#5…¡‚ïpBÂ6S-¯hÛß*Ëðm¿üê#5õµ™Æ6I‰ÒŒ¢ëóÒëã„Xæ½Çìù0sïð×rØù×Âz7*=ó&-bMYsGc]vëˆQEUì*3ª1ýòT¥£ ÂAê¶7kêê–è-1u79l hŽNF€&Ý!•|'w)u#-¹&£0u#-`²p©•«Dñëxïi +J ÞjbK±ŽyègãT‚¨5YÚ“QËÎÂqÖ7ô÷äè=¡<8ãš~¼EOå¶)uüºøÍ–êÕCw ÒöãÕtSõÛ²œCò»qM}?fØHE #5}ûoEpciÂl!N$ogGôY‘Hë³Gp"ë\…XBhk—nÏtuÕ´ªî‡:§3·ëóÕ2mÅŒw Ñ}Ò‚Ü&!ÓEŒ™³j:­ ,G*ÁÕYBÌ–¨‡ÅçsÇÂfu¼|ãØåBèãíöDÓn²öfýßd\½âe8nì•L{ºûUopã¬z¸áÕøÆ5&eaQ#1¿JÀø*‡¢ØÈѪ‚Y¦ÓÀ³< QÔlïZ¿T@h(}C•úSx9À ãq³×Sk[psåψ䈑RxÛªŠki+®öØ£Â2+•Ã3ƒÖ“Ã{Ü÷¦•#¦ ‰†Þ¡—׊>ºß®5£tëÝ:6åŠù¬+ë¼mRù‚Ò÷ ¥*·‡uÂŒa½L<Öç6ÍÔ€ˆÐ±â5jxÂn ÈýnL`2scÓHR0d66!7IÇKÃÁS;®sã¹ÁzU­3¹†HH\I7°Q©1f*-p5`©¶¬õX0Ôn«…Ô$w-¼NQoH­<°Á×ó¸`ßlcBeêþÆþMRglêZ’ˆÙ¶.o£@å4‹1–—¼¾#1´9œ´.{ç,e©©Y2Å–»é¿cÖ#5LwÎ×%ÐWë·–×jìb•0þáó6gQÇ{Q½U†×Ì |½d8Å`OL'–¥Á¥GZir Ó›©q\7¬kꢕã}Ó²~SœáªØäô#5P ×Õm¼¾‘ =´S!NOkÐliÃÆFÃç#e_±SŽ­"ç);` JN â9‘ƒŸÐìÈc¤3º÷såº2N¢ž«•8óZÅʃtèåsø† ʵmÚF‡;)ó]m[°)<Îk$°W7]pl½KÚ<˜g¤#-ª[¶"‘UMËås8-À-uìo´k?^OÑ•Xœ¢"\‚B¬#5.fBð^DÅ…åP÷ c£ÒeI‹ñ³€Óh¥>Ÿ).³{gýþf®A;hÑd×ý5æý·XK£¢ÕJ½ÊÂøŽsÜFí÷8 ‡!Æsj±yó«ªS‚ñ¸t=.šqBÝQ“=ãá9”#’¶nR¦#-éQ:JÈ9£®æ™ÇŽªZú‹q~ÝRF¢>ú½_öyö¯,Py! w}¦êxÁ}ÞQ³Ægžót[ÛÏX3?Çû¯ðçå¬íc?í;×—Bý‹ôZ“‰1QÈ{Z¾é6´"³azüXÆ×ÝtírV#5`¤†;J@Ùò+RL›.O‹„)ù¥ñF²‡£¹˜ ŠísgÖçå‹·±ûñÒšíñå+üóƒ#·oN“áó——Óëv¸Ä0áœÙc´yTVÍÊ 9¢°a#5MÍi„ –YäYîõ¬ öur?ƒÒÂxÝ~9»Tcá§EBYq‚®™ˆ:£y›ÝÈ®~KB[i')KǬ>¿L…#1OljÔËÕ<”ö~ù<¶.#1þO§Æa‰9´c'‰w#1P÷ø¡²Ê4¼«“86Ï[­·û6ºÊd¡¯Vì§YAtß4´tšÒeJûÜ2 ñ²‚Çêï“8¸¡æ‹a@÷ý2kyÞÅ,(Å#-TÚ¹!±µ¤ê'$KŽ˜‹´é„\+éë~eäyÞ§2&0ÞÖô;tjÇ÷ËIXëÖÍ屬.î“NÎ{¼KÑSdІ*„¼¡ùfÚÈ”·lt¯RêHL>d`+¥4gð€´†™"(`–òZA× ·™Â°`œ²á±tB<&4#1Xz¹»oŒæÃ©jXsq‰?˜ûÒæ#Ò¥lô¿ܤ#5/[ñMTÔ+JsïZFlÓ‹¸J:û2‡@é#5R{ãÏk’ÎLöaŠÓF×>BbŠ0Íg%¾*KÈ´øƒÑõPü#5ß^±Äéº@»ݘÃ;l~cåI#Ç(ª£Í{/¥b/1ñÏá·¶#1´>]#ܽºpx˜Ç|ÏToÇxôpµˆÛš5ý•‰Ê_Ï'7D--¦É9_¹e}&á‘×EÖ[…ÉWJÉ¥ZÙÈo2¼w£¤…@øLÒ÷2S¹ø¹=¤.oAö×Lßß’JøKÍ0¶Nm†,4¹Ä6ªE³,ºj _Ï^|8Ù]s™×­ž ½kŒûªtj½ñ”‘Ÿ½ÖçÊ2*#(1sš‹‘µ¼\ᵟß+îSwÎè¼ÛM%#Ðkº®ÎMkÅU»\=¡¾ûãÒ[ÝðÉãÜÜ#9é:‰â%©øK!T§¬ÎþÖÝûpÿY!O íØõçrŒ7GÏ™Ò4£™‡ƒysï]}G3ÚðW¾pÑXA‡Xv!•GJ¦å°pÌm§ û]ù·ù’Y墿î#-¢ÙÛWÂúãz¹ÎÇÏf€Ê^^2‰E[@Å š­„0a×ôߨLƒŒ'w#õ61‰Ù¢ž1éPmæçMj¥ý»NÅ9¾ý%¸W§Ï/+ášà¼t3Ú9ÄÛìÑÁoV°ì< ”ÙÎØ“ýèÓù `#5ée®û3µÝ_ƒ:g¶Ôaá­ÎXDò¿=±íæä*Á1¡ÄRߌM~Þ¹ŸÀçh¼T’|ÑÌz@ou nƒ„H!¾ÇmϤ³g£¶ÿoNx“Ü\¤&H!ur¸hv"ÍÆR)Ń̮~›ß·dZƒ"pV×=Ә殒E7w¾¾ð\>3œŽøž¿ó81Ó^Öâ;¾H±T‰&ýCUBR„ýŸã'b¸†<:m¨Of#Ç jm {ÜØFÂGƒædpзy#d)­×é‚!¥øN(EQqh0 xnxj»HKß2Hm¶˜Àjމʅ<÷¢Ø§·µª³ Y8;e>—^`Ó‡B «/ª[¡,ÇQeÝÑ(ëPý%éT soì÷Á"ļ "¢ãÆaœ‘¬¢yå'eî¸5@¥Ta{3º,QOqy¬Û‘•yíi>‰Å©/{½ïÖUîM‰6†wÖŽçm‹3Qq ãSG4ž‹m?-ëû3¤ÞgºÕs+µ9sîtº@ϲxn¸Ž]9Ë£@½P¤*9-ÎÁHÑ£§,4ÜÕç~—•º‘#5¢R#-^mtÊ%•øó‹·a>Ôÿ¦Ý}7#- {þ¶ ×åÓÂÊY$Ë­PQ#5'i¡ÒFEö»i/®–ÕåòBCo8â&}l•‘¶n#- <_B©à©I%¸ì˜{õü0°jÒ=í{¡ÏÇÖ~/xžÛ´pekeÙ‡!HFû—³‡Ï”îµå u¼äÿâ¨æj“ÜFþúò‰öív±¡Vj;b®Ü¤°Üœ¾9jÈB[ìºUS[X†0XÚ#5-†2eÜ(¬#1^ú°G•eV ŽãÀ»½n¹¢ª]7Ä\_©–).Ö†J<#-©3æ@ÖX¤ejQé˪ÞqÚüƒ¢¤–ó!ž\Ť9/ B­“"ˆ©(Šå†ªcŒ#-:ti[sÆ#1ëoŸùΛO$ã¦o¢1œ5¨@„™×Þ îš“¾ t ÆáÓ‡qÌ“±FE’0‚‘AƒP15N¼= ÕéÔà}›lRÁÝiø{¯¾³Šl7‰Æ #Ñ<äÐþêfm¬cºJåÔ?Õ~>¯Åõ]3äMBÿˆ~ÿá—ÌqÄ£Sˆ¿¯–Y§8å¹MÁÄîÉŸ#-øgíŸó[;jŠ´QjªîU÷|ßÞ ¨²Hb”Ê…³\î|ÿ¯i»Ê~ŒŽ.%ãüŒÃž"ùëý¹oo¤ Ï]=ît©"rNK(÷#5[åóÛ‹§²vÅÀ“¯·ìwŸ(|#5%œC#¢ðä]\ƒ•µèÞºø%öGàþÝhî?Á…ß•“õý7ít-SÕ{p&#V‘º:jF@mI#1= M‹µ¥Íçgv¢]çê§þÌýÍ“ä.:û­ßf/ŘY±ºÖ§\x§œÀçƒ`(6ËöΆ³DöêÔiäy²Õç({õDq¥àSøÆö-À§Ó;7›DRã©j°P#5ªÍƒ`TXƒF°ìuý+öêø¿UÌð\J´ Üö%„mœêš˜%ëõ;=õrEÆQÜ/ò~Öwm¢©µê5™6bW¼Ïœ XÅBùA¸CH~ßö9¼5I“Öûç<Ð6ëØfìv«’Z×î܈¡AB-ÌáG*)*,ˆkŒ6C|xDÒ)øÏŒ>˜ B¢-@ qúÿÂÛ T9™”>|ª ¡Î xªû7P©çÉ€^£È¹¢ ÷°õ¥&Œ<µ¬:æ¥'RC‚ă–V˜`¸¨ì†m0=€0#1³Œ"ØéÌ,á>ö.Ÿù FÚ­gèóê}>ƒ'sۭˉý#5‡tSÕþ[Øêuê÷YºŒGæùYVççE`÷Ô-vJ××Z!‚D:§qǹ¯ëõÚ‡Fh[Ý*ôu¢GK¸…V^ÄŽ?ªÆ—,1{aá¥<ù/ÊöÑàØÅõu‚š»wQÕx52°0‘b²«°Û7f#5€R#5 ƒ2 I,«Ö»¸é/HÓ5(T¾-·I¼ý²#5ð¼~/#5Ô#1Çéï‡dïùÕÀ”žü`$Wì÷'ïwÁÏi”ÜAß-#$Guõ@–w†t‹áÃÄ#¾åнÅW=ñá½øT>†z½Ï%U™¸(ÔIWRÓ8€Á „!#5!¼NÂ߯ù®…”/u*™ß0麧%ÇÛT“Õ*RBÔ¬#5©©~¶çs¼fØ4E•;Ñ|ÛÞ§3ÏFŒLKÎõ¥à;½ÒÙ™³•0%Ïn÷Õb~Hê´Éǯmþ3)šÛlAÃÜ6šAå3Öµ!à~ É2Hê5KI‰ bi‰ó#-Úç#-Zcb(r#Ä@–”Ac‹«Ê?dyŽ¥âx6uà=Ô¡åàÀ#5 ½…û¶:%c²Žì”nä×-Û4W‘3´“Jæj3áõ»tËÿZ…í¾õDåep¢®‡ßÂ7Õ'Æwu¨5¶°¯6 ù@0Š 6Î:îùÓG“’÷«ß1U*‚³&~#5ŠH ¸áí+DÊû* O{$#5@¾ÚC‹ÁE„Ï‘ñpw²’™n§ep[lT"EÓ~éFQ`¹—mßVP¤.Dp—qC³n·Vou[p¬÷c#5蛫ÚbùôÇg8îˆç#-d$S­PÁIöRÄìzqÅ”iPËÁþ=Ú·üáéâ0Ãn)=\áTõ¾ :{ ‰@Š˜m¤N¥:ùå[‰ ‚¨@È”Œr‹Mµß$³ÙG Àh“ôaj "áѬ<%r›s+U¼çާZAóô< éñÎ<™Í°˜ñ÷Ú”ºSyã¼åö¼™ëáäôÈu´#5“âu?ÙüÿqØoB˜sѶͪæµc¤f/›Ò!ÙdôÔ"¹šku<üÁMF­ŽŸF×òí}žQ…Ëýúm¶ì|¥ÿ#1R2!Øbš-‚a7züŠ®\ùŸ§‡Ÿ Êr‚ˆ“—Ý(,@ch¢”Ÿ,ÑÌË:Ló­6ÇP/ªCNN#1•1š{M¤W)T[Ç£¼èI£îÇJ9ú´Û’YË~¥¢#W>¯:P´¯öÆŽí¨Ë#-AE/Ft„ÖðähªÎàA $ší”´_È>#]ufw˜5U+&Ú—¯Ò‡6V£ÀZV0­ÏO{¨ñÀ^ïµ'f–Jh˜†®ëN›o•Ý“1iƒH 0} Zë>^Œå±f#5(’òë#5¹Ã‘A*‡Žt –æÌŽÙ¼z /r¹Cb…†ý¦ékˆÀ„ÂCD;]`‹‹jcmÕòߨǃ_`¾f™•üVçLöÈñ‹öÁ‡†øpräÌœ•J„(ô`›€sØ­Ãl% àÂX0EÈ€ÞûD*âݯNIš€¼·"¢ÑxæãÊ GBæ/»0¹K5Â×ÜcÌ/aoŠ :J½e ÑPþë±âøa§ÆK,MÅ%%¢$%ý¯',/òmîR½\à Ü~°æ¸ïÅàí£)|î'²‚ÔCÓ4j˜dqTýݸÁ7ΓÍ.\0`ÀP#5¡ž ë×6Dp™zk.¿¨=ê6¶±†­œË“ó¹J¬jTa‚¤#1.A`³|\©GWª~¤ìÓ8Öy!}iu9ú“™$Rï6Ôž_QGɇ–¥‡|JçʸM¤:‡—iè¶Þ<{Üœ5Ô· w´6a³„°`ô>`Œ‰<ŠÃœkÐ¥ Z³­ÕßÞ2ž®Y±çodU1¹yôœ;ŒeZ=#5œÀňh¾ð?¾ãðêŸÑÏ õ_™±„Q½„ÏHô§ÞظÚ? ~¦5‰;·²ê}'X#€ÍãÀÊ,Û¡¬…(àÆ|6ââs*€‰ˆ::Br2CÕR0€µz__Ý+ï«ÒÔ¥üÈ‹“ü ä‡ûæþgψpýÃGô}ßm±èßµÉÅ7¤!ë)…6äw9èŸ×™µÏg¿ûñj›[Un?Ï#1H‡«×õ'9'Õ8E<ŒeË5‡g°BÚêd¨Ô@”4 f Xt@äBÌ=ÌL ƒØù.]T¥G¢€h/ #1(p²5A†êÛT.¡f ô‡-®¸½\²iDSqIc"*H%ÁÙr•ÔDæ{ì•.$¨&ƒ»…Þá0„1­(²lJ4Þ/t± ÙŒÄðH#ĉ`rRúk…ºÌ€PaDp{r„ @Î%_â!BÛlñÉô«ýÞ«<ÿŸ¤ý ãÙî¶DÖuIžÐî¥A,@Žþo1+»?óÞ±¿ëѰà|«eÁrŸãBþµ8Óû×E’Ã^PnØä›+qÎ"]ô°wW¤œÞt·ìžª(ÅB÷´ÆFcyM™!mw†÷G舲 úXøÛÏlæöÚ:ÄÒ»Ú)öº§´ÞoùÅ­³zYnñü¸h‚¨M†cn[ø%ï¶é\ú:8+{#5ûìwï"mâ‰n&`<‘$d»¢“4Qsn®w’Œ+}3™Í0ˆ›Ç×­Üš¤’Ý–ÛQÖ§}×o}®í'¯××¾#Œl‹÷IÂö"*>ûˆÿ ­Æ‹Më«(䣉¾&ͪª5Pm…nâ¾Ô¨Zh–½ÚGIvÉu£ü·#5(v'vÐÞ„ƒa…²b( mDN%B”>BK*´I»²­@‘ mo&±RŒìšl£¦Â‚6€;­Ðìôßfq÷*Bi»‰Ôrq¶)¹R¸ÉYIÒÆ¹×"5»aÊÝy9YœÒ'­9HBO@½.æ{âÂD,»„Ö²!Ñß þýïr¾;üJør·btké©B{_×ǵÀZŸ9Xò êdã>^WS¬“¢A`ªDŠH "Æ#-Èr:‹dì‚{ c\åãSnA‡G²YãÇE =äoMPE‹Üš4 z¡Ê¸5[b¨Í &M ™wC:!/7E#1ÉBœ–þ•¬ÔxØVylPDIâPÀŽÌØù^k~]¯\;öuWÓ^ýtÖæ¶êm¶5 hÙÝk{×âjÛ×ó¯Qb Ö¸#1¡’$(“ë½Aõåìò$í÷ÁvÞ¯»díê~¢#5ŒZBªµáÙˆ²”Ua‰6©ä£!Õݚ̔ƒ4o‹(¸" 5”:é×MNµß–;9£‚ÄCÍFP‚¬Ý ° !”œ;~\º¦úøñ¯Z9éa+ÃyÑ$©Ï—EÆ<˜³:Ï,|êé-(Yt¨6Ã@@ ?ÁË–y÷tíá x]‚pØÙƒÊ1‰$ ùŽò#1ö• ÞD'~¸d˜…9«;5ÊðÍObXr¨J€‹I)d–„D `ãJéVmÃ#8^„©"²(MÍPB0ÛÏ–wNé‘—-’ºòÅX,á†UHxÍìHœQ¾])3¤ ééÆY`3?G>j:Ç´¾Ø7å¾P~28DTÕW±+˜Oz#5#Í#5#5¯C,AŒŒ2¢¶¥Q¸DÀ$’‘P]#1Í çEuKßã|Eîqת©G#-«´z5+Ü­¶`ÝφÄíìBéÍÍoÔLLï î|fIÉÀÓ0mºP‡˜ÈQÅ´'ƒæ©¢WÓRJ!› Æygë ‹¼Bu'3vï8í4Ôm9–²5TJ&:1á}Á™Ç‡Þ–N˜³ow@¦nÍògK޶˜*q{š¤’a6yJdýÓÅÑ>#1/h/ò rÙ5ÁӟͺqÞê<6.¯‡ž{ºSS8T­ÖÍÉâo+ôywc'#-bQ- µR*ÍÖÍ·ž#5Nîµ/]…¬iÜùo#1rÖC¼ìÌwîóÖõJßpTY¾[ö0Û}"÷6]–æ}̪·iA&FÉh(¢#5<6òŸ!Åv©¯,ƒà˜Ø^&=Û8‘V^î\±Þ\»Žç;—qwâóòü]øj÷XÝfŠM,§×Ú¸¤¯˜b»«j¥ˆ·‹¦Äb Á„½(<§Ç8Æ“Á<ú(¤Î#ƒ<<#5R?œÞÀÐÂØOÑÅÍ¢ϳá©rÓƒHÇîß>þ14’;ÓP$Ñ”‚Ò6‚—‰Ë°á m‚ð÷Dž4jäÆù­v„õ@{–jÐcÀvFhùé¡c#Í3Ó?ŽsŽϧUÒæu Ps¶x|¼*n¬ª˜æ'Œ=Rk¥„¤H¸h£Aî|#ÂÒ[_¸w¿Rö¾…ÕÕêîÆi±è§[„:Rêèµtã%ÇäZöO ߢ WRÅ£žÊî;(÷ÇïŒßfPk©…sªç]VÐ^Ø9o|âûàmǾm7Í{¡3ò²kÜ{Âf893é#4øM&-¶Ü¡ñ`¶"³v]»ÈwÆoº…÷”@Œ¤E}-š) ªg›@UàÌ…KįÆ<"ÃwØÏ¤”.Dɇ[˜8¡b:»Q@p‡ŽÒÓ΋­ûà Ç$뉼Lѱc¨CDˆÄ4]3‘׃s€šTku¡øvPćžœÑ"Å— êN5ºÌ2ŒšàåR$#1Ä ’ ¾ð½¿sC”À¡¸X€ 'nÌ”… ¡ƒ{›²'ÃiàotsBÉO,ïøQ#5~Lj¬Þ¼æ¤ÛÚóÓSˆ™/¥Ã“ò¾†—l¹c@/…ÆÑÕ¾)›`åÍÌ ¶GWÛcKr(ÁŒ ®ÌCÆO½ÙûW”²?¬‹áÙFÔ„$G‰§H_Üÿ+­7lËHí²#5€v¿zª#ˈxKÞÈ#'ò®79oöý#1ó¢¡of®§2ƒv½£±i”šZÜ$¤ƒòš¥Œ:·%00¼ô Í„†óìw°5DwçÍ$ a—Nïïi™fla¿Š0€ò~gÄãqÜrœ¨HÎ+ÔA„2‹¶&øu<Ñ¥¸ÖNûC:Ù,VËpx‘#1ÄCY vÔ9´^ôîÈ5,í"~UÝv®ZFhš-&ʤÒi2–7u·¸Å½1µó_G^ùâ‡Nàî‘4q0"àñ#1£ò:Ç2nlYº²H“¤)#5€nv¨z0ž<=é•d_·‹¢´¤½&ÜzfžO»¾‹=sµ›OÁ#5#-9 #5ñ¡êZxTðY©*l÷Õ}ã®u7aXª¥:´0!ÀÄ+¾Š§@³’ãê~ŠÏ&üàõö>f‚É¢#-Æów:rã36Ã̹¹Ð¡A’<ƒd³ç9™Izì­¨­û!œ#1ÅÓÚßÞôc¡Püsmä‡Ç„õ½tÉ÷àÑ:§“="#‚¬†¯Q£h|uîêÇ~Ìf®Í„°ÿºüxFpA3íþ_Wñêìú(ð±ý—:‰„>p+"ƒÎ#콑U!Z¥î>—@î!×ùÿ~Á©¥ácùúpáOº ŒÏe—ÎE.ÛÔÎÓ+|ÜÖ¿ùÞ?×ß:£P´ý^6gk˜ÎºŽ¿Añì!t¯õ*Ò@Df;«/Ÿ}Ñ ×. >K#1Þx9³—øû%5pÕ%á㙎Þ&fvÍ~ÜáNz~O´lï"OôH;hË#1hà>ô?Çú>¾{'¿4G8Ч×òß:@NܲþîgS²#5-#1Vd0Ò7?ÉçÃËÀ²ý;œb³›³Ô•Èþ´œÒS!?´@¡‚ ,x!‹ ÿ*XÊ’•ôþkï1†~áŸß1’ÿoôpÉúM“û3pþ&?g¸!}~Fq”ã¥Rv¦M,d P„?:©L•D‹!‚ïÑVäžâgÜÔ0 mž4‡ˆX®‚(ê˜ ‡Ã¤~ÎÁ¬Sn¦šU%Fž«½ùhÕWKL‡Ú¼|WöÞóuâ;6rŠŒ­1ß×mÏۯ˿'ºwÏË<ò9C|Ò lãB싲±ÎãœV!‰#-&¢%ñhàºé@õ”\,Ô•¢C¸ÙIši/ÿ;žšxçƒÈ‘êò°XcÎ¯ÎØ%Êf#5£#1%…ÚÇ¥ñÒ˜zàjÈ#5vÕ1Š(vÊ>>›{ö|.´øÐÏaòµ¤/1·ì Mð"ýˆ8F€„XDˆÈ”¹ Ó—ÃÑÀ/þ¯³ÔÜVJcÎÃQP^¿³pó¾ÅÝd´«´Ï[‚1G@ìz¨|"yÃÕ2.^gv¿y—úáü±át2‰¶?Ç#1/R1ð¦ˆ22.qöèqáÏݧÆÜåUv––0Z"-$ btëØÂ H^%[éÙÒ-ø#1¶ùwÛÝü°d—„aÄ"kÌpDõ_§Ç±#-¯!P#1‰ÐyXuöjãèçèܯ·ãDõ~ÿÂ*¢]A8QÔqùúýÓü¾ÿÇ#-øŒšþØSJ#?±y{ië`Öè2•¿œiótíÔ—NLIòt‘ žG¶½CÓ‡×dû¥9޽à9úù´Ü›5'˜ ð{Î÷àË'²?Š©F>½¯ïÄ?jý% Éü_ûºŽþ;Ä>74W¿-=¶>bIåÅñóMmOˆ5LK@€#-TD04l<8û#-ƒ€·å #-yìÑhrkôÐ%÷õ5¨HbIÅ‚¥²€érK)HóC’)ÒP˜AVeCèEyDGŽ‹Æžˆb~no=]í•À¯>åb°Œ#-Sq±ïÂ*~%Ð×§’?Ó²©ÖÇž8UÄwŒªÿ#•0x¹Š“{z3ur÷™n+¥ÑoÄØ(ìË`ֲݢ ·Ž¥\_agí÷o¹”ÿµá2Ã¥²ø©Hœ>×/˜‘š™ýº¸û,íðö²o¬9¸œG¹7BÝ’°H®îº½ËFU„U¦¢É\Xcq[„׿e ÑEò¸ÙeÑ.’i}‚H#lk¶zœ³ýýôyÑ[Ë:ÙŽu,Æú¨l‘" ²¶ Ë¥ è¥wî»…^~Ó½wÓŠ ×ñÕ¨w[ŸÄ‚Â/­7»ÈwyÛ:3¹>ÉŸh3×;h¬äçÚ(¾ËÚ#5Šáó_j9¢ýTâ\\zÅ]uNJμ®&Mîä¶s>`û•c^<æSMHt)â#Ò}Ç•]îµZþ9bÃ(;õ»•ÒŒ–½Ã½Y/Õ·«S®å*ªèöW]ò7ª‡j*@WÄT³•:40ŸªZ¹öL ƒEo#q`0Ãý”ñ\ ›½ÇuJìV€¿Õ.؇ª ÆEŽ•`p˜¯&¯M(Q²}%颊I?EÓŠ@s8!PÎJO§Ö¢›j‹—IÑH‡L¿IƽØbTýçJkã¨ï—ÍÇ騗noN¿”ÑÅ=yG²ÿ§½wË™´CœD‰C¥ú‹"×zu95ªB÷hß5¤1›Lš"ÀËÊ h,(ÁÍ& ª|í3Öçì¤ UN_D³ôßëê¡Ò_ÎÍ5nÛùñÁ‡tn¢*g“Rfåög;>0ÞxõÜE>’ÊØ£²1‹·µïXq.xâ·ãlyN‹c­:Ã…¿¦«­œ-’ñ­ ËüÅ6ÑñŠœywNè·ìð"ð³v3äD\'Ò9ó‹ÞÑuü·‘þ8kO¬gãIçqê/áês¾[‹3G¾?Þ›ßgPFÍ<óDw [˜ûf~ÏÑåBæ#1¥óNŽ(¨úº3¯„{=érÎøm›>›ëµù|4áq#5‚p‡Ÿ}wwTÐx2JJ`eØ~oT2'uKi7pÙ?£öjwÂ>|þ¶«>CBáõ™óý¼½ tdNy–ß”òjo™s-3³çÏ/„#1±äà#5±´r@ü(ú{úhè[ivý½à.ÜÙÇ5÷mC& ø_àù­?ÉTýP‚Î,Nú0-º˜viRµùûDµ ÄÃý´B'•ÂþŸÀZîë‘U#-b®RÄ]Fg¾î8C÷Ïš/œYáËÀƒãõr[3šMÄ%s¾Ó=T!¿7s«··‰Âe>²4z•¹fqˆT×!ëˆØ@•“dTZµܪÙ íD¡ÈÊña†x¡)'«5´fŒ’ôM¯¹ÇÏ|NW•ôƒÍlF¥Ý’aÁØ‹œUÎ‰Ú †‹;Œû¬Ï¿õÊÓ0~ªNŸzýú¿zÄw_ÔTS+F+iWªt*ë #1E5…€d@ÑÒÀ' ˜‡wŸÚƉ'Ãæ]¿U9(dŒ¨AöÏÃë«çÆÏ˜Èw›Ô¦­˜#59¡²;ÐhñD0jPE<ÑñÖ¦^J4b’{P«(¦`ŠBÚM¾],ýá®"òøòöÃèŽÛk_#5>g}Ô4xy‘a`pqæûv®å"<Ýâlª(\iI-‚Êç+2…"…êLDkã@jDDo‰ÚWS¯¡ Y¬aT•#5 ý7TÄq/#-#5‚.#•«(!”:ª‹ÁìˆÞ…ôvnáå'ªéÝoÙÖAík¯$ m}Ü|4X#1ï”}ä)÷\,Y‘Ö%õ·„ÿž~é“-·¥áçcãS1åWBš‰%Ê36%²€üºþ?q@}:÷±®ë©Ìú2B†ÿ¨þcýfùÑ/ý§ Àxô¨$\üƒöðáãò†M#-¯µ1R04«È)ôŽ@:õ2ŠÿÚoú#-O7™?ÕÊš»D:#ãm¾¥áÃí‚Zô‰¿7ŽÉåÒ6u«ŸwÙëù>ž Ö#5I]"÷1NŽÁ]«PñÅöO«Ÿ8-3”Jrî”ÕýAR$#5”Kýãȃ·Þ‡ù¿ð ŽŽ >ÇïÅQöoPq×– ´l‰ø;ErùÇ(úb5I ©{"#Dþ€H'×gtxkÏŒTa¤¹{¸°,øûLЉâ^ö…ÞÊâ 8j`‹èq_šn%… (¢û]ôûž¬ï‚õ«êÜ$? hÙn¡á]Ý„†z{OÄ`9ÊŒH¶‹“½î{]9q­§Ê0à#-ùI2€sDnù¯ŒéæÀ¸`9àÁ÷]«j‘‰\Œ ŠÔ4€xNWfªHá¿h#Ø%}÷·Rñ~X¸§ƒÈÈ4ª±x(.Ü¡œ¨R3$´«—€Rƒœ™ŒÇX¶¹BD‘ÎD„ÕY[”«@ÑAïý=¶`ÛO=޾óDpA` #-X{rØ•÷s8— #-ª)d³€‰è#gEá*‰¥ü7¸K?Ê3(ñ Ò¡J ºË#-ÊËMVÛ–4Þ«ìS#-‹å?›ŸuüwpÕ‡J¤dƒÆ‰ “£Œ¬FL˜ö“&ÅæIadé5”ØX"èÏÎÎeÕ.Äÿ*rg,dâotñ&¿-#5SmLzP>:Q8÷;¶ã!·õrº­|7yEeÿ ¥|+¥ª#5ÌÌÕJXG‚éã5W5×Ü÷g½,–ÊQ­zMcR*az¢dm-;.%tk€‰I ¢†…®ðݺ5ôý\YA¡61[R¡ßvÝ· 4²·Íy);Ÿ&a”ì8qœuW Wh漨ˆ¥DWSìñ#¦ƒ­‹ž½íf»òZÊMþ(Á5*`ã{äÕ¼ê~É©«“·M­Š‹â¸‘U ADì”õkbA”ÏŽb6Áè¨=EÜ¡AðœûšJ´ÃÔ<6ÿ¥Üœ±ü°ý­£’ÜÑûRw³ï]/Á¹„(rºÝ4»ÒE±˜\e;ˆÀÄUÒEã¶Ö¶Ã¤ÈJB¤/çåÇnÛçMç šy¨Ô#5Âå娫GrŸÅãüÚñòô<‘Ⱦ­ˆü#,¹´³¢²äõ÷3Âù„sÔýPÜ]£@R\ùâãæ! ΨÓÉ£lá ­¹Úñ‘­üyKÞÄ0ÐR‚#h+zf¬x­•UöÜ{-–KsÇ•w´·\'Íq“x³²Ñ¸Û™ [ŽŒVg=ø"7PL€å@bâÏ4D¾#5‹+äÐЄ$ÒÓ~¨Ž?¾£+qõ#1Ã18D_$k‡Lt4õÃ7WP–*%±³–’°ÄtE Ñ2žZ¤Ôz-.“9Wkn-7=KèþžEçn/Gr³W4ûy­ÖØC—Óˆ?™¿F·c©#-p(J5r¢´Tâ¹½Ìî—{`áë‘”¦)X*áíãÓ]3­¹ˆ·HÝæòôRJÓ²m^Wëï”#'§§U'7œ%]:;~'Ù'\ǽq=øƒrIš».¨kåƒ,(¹ØÂI¹qºÙP§IpŨ_ mRCÆðH˜ÁÚ-6¡ïÃÁ˳‹±nY¿fûVD«f绦Ù7híÐ4.½W8y#5ÏÞÄnˆø*:õ#1Úë£Í·ÀºãÔ9ò5·4tä&Hæ"}±1áLR©” swÑÀd&]»Ù³»È±à9Ÿ¢ˆCxx£é™…leXp,ÊÀdC™½ü~²ß—ÄÒ¨öÛì,›R¨2ÊjÖP)#1¥y°`YCq¥‹Œ˜ò~ OÅ¡ò™ðÿkÝrüäÉèœÒ~>™ô„é#䊎w”YÕÆÅãÄ=¿9·Ñ­;`M¥Ë’¦zG’…Óneþººšƒ)ч•#5‘ÎÓtèMöæ>³6yï©ôc“»Ã'PëæBb×I½MEæíž&ýÃ]zO 43΋ #™)3H³í’)¾«pŒý­ÐÝiŸôv=‰m¿©eSp–ÞóÓ7x¹o^\†¦vòéá yaJâµ4‚Åç¥óû¾aIlx®·#1‚ÐGšávcL‚Éwµ=4¸zwSZÂd¶"¾[nD{¾OFž¬¯¥2ÊÃuˆó>ŠU9ëdC‹ÇôLeɧgÄ(ñÝÌ(ôØœy)1èU Jª´›ð€%0¢^ô½'é¯j{ü¶WT’¬/¬ /%±ß 5¾p0ר¤l€agŸÙßá`ðs`5#-ãóþhÃÃcíÔÉ(³.vüšÐH$’y\, ¦úZ;bž>žý›|Âûw3´Û³ª:A½TrúœÎÕùmÈ\å¸m"n†x\ñÝåëîGÐá`EÄ#*†°U5ˆô¶[ò—`źÀaƒ¯¸‚J#-9öLó rdŒöÌ%½v÷—!ëµ°z&ɉÂÈẵÜyu·²àQÃ6€4½­ER®õïiü72Ðw^ÛC´ŒÛÓ–äuv²uø£¬ôZ¢BÔ^MørgÿµG„0üÜ8J)ÝÝD—…Ôç´ª¤M)nS¿¯œþ:¼žSаÙ扞î?­vÇ¥ßÖ¨_[WévPèàè Ò¢:4fì1Äblvûm“´é#–ÕÂBËzð»Qœ×vTö‚ ^%š0um#-cûÔnŽðƒ…¤ö ºbV"£g“ÆbbY#1²”:,²ò#=RÐá®Õ‰ˆ‚7`µúa¸Ê#ÞÚRwç¤îi,F9ä"æ³(•ËÞÞɱ哵x4mÙˆ‰sÛi9”áÕ΢š‰LL›Ðêf7j;µA=ãçÖ«ŽSÖ´«BÄÏCÞŠ·ôi–¨‡mšñ…€íÏ ßÒ¾;s^­)¸‡Ï *–½†KÙ† _£±Úpé] [\’+láYŽÈ‹ãwuCUõ«’L‚Ç®³ùû!¸:Óh‹Üaçì8+í1 Å‘[úWæwN‚{»…èC«;SÀ„©ÆCN øA­u:r…ƒ'Ø1•¶8]h±zçÇë(¢Ý‹a'9úJ«`މ„¹jÖª >P4ê¨á†\GKAÔ!oCíl&ñ$áõ‰o‘££ ËM·~6ÅIPf%¥½ºh9C(ãÑÊJ|5äpJ桊µäJÙt»Ÿ#-óµR&ÃmÖõ#-d Ä5ÐÔû¶-{«(‹#1bŠ{1Û¥íbŽ&Ú"#â9KÁr_Îr{¡w3ÙÈ`@•èÂT†ÙFOÜÍ ×Óƒ™E·¡©ãŽøüv.Æ„>¸ÔJ ÙAÒ[_»ÎÖ ”dÅS&}èì™–!Ðç#5Þ_T¹4D.wb2¾e%k€Ûš¢t<æÐ#1cžÚá¤thÞÙY˜´ŠØ@©#-·z¯8ö`£o‚i¶ÛöÆÙ¥kNvŒyÃ…ÛïN`óìí&²8y€‰I(­ñîBñ#5©üSqp´EG(t¸ëI쫘 !dýÜ­öÁ޵Z Ž$YLÀÃ:MO!²/›b/«†-’gb­!{¥¶HAráDf,LH[#5 ñ#5/¾·8*ˆŸåœë€az ï(&5ˆÃ–“ƒíÇ ­½&Ç‚6ñ®"¦RO_…¯µõM£‚ŒxcpÃZ0vlpv u€À‹CMÁ¤Zb€c° ¶"P¨Œ‚ê‹¥º p¢öòyQx{}eý¾ ΤVÅÈiò Drñ Èó½x…)#Ãd¸k#-½ˆpás¯’Œb¢Ú#-ñÔs‡>—PÏÏÏØü'‡…—‹d#1A™öDO’wl¨¬å5ú?Á¦Øÿ–ÿÒ·/£ø¥ŸØ‡õìE +`ˆ/KÔ3’UÿÔˆd‘S'üMUB§÷?ÈK©eDæÕ\ßM(K v À#HØ”XXÜ5N(,E€ÕPa…ÔÛ¤b6¹ŸÐ[Ë>îÚ4O«‰‡v…†ßì›ûáþÊGë ÜÀìS1Àe¿°X__yÀTÔ3CiÚ—!:$q9s½\Í©ËëñF95Þyj JVLøwÂcòK±+Å,ûd,Ô¤ÁÊ¢6a× ËÐ3°ÝU6—‘]@h÷£¼w§h°)éîÀê>?þ~¥öýæJ#ƒXd{EÛ¨÷E?Õ ŠÇÄ< ]Éò'hš¥–Í…î\þ[¿ç æ›#5ÀfžÆÝ#1Àà§×#-äl(j$ ŸQ×û›[™§¤rô€—b&H‚èx÷Qe6§¯·¯R ~¸÷¿ðU´ƒv3‹òå­f Y«! _P]À/ƒÞSý«í]âô¥²Èøm⧨ $þ[ÄFô‘¸c#-+#-ÖPÕŒï#˜#œ?_ÁÿŸáóûí¸œ¼ìÛYf¨öT¡Q±QêËO¨ƒ}sÑ¢{CaC¦›3ºðËLlµkÄ÷„[§ã;ûA Òì…¶b|à8½#Èü†ó â%O›þŽ<ÆýÇÜam`¿Î*‚ãCÄ {çç#1Ý_Ä\®›&ýTZ4 ³¥ÊrÆîÂVÚFáÐX¨Ÿ‹a¨9Ø5…p=ȧ|äQå{œÂÔV(Í &ëИu’“ü¯Q@íÙS¿”žRÇqOÑÇ^TÕm³h’äžßŸp|{ûûQæzw`s^']x3Á6Ew-€Óðõ’¥l7Žíé.™œÀáñ=§¡8åŸ§ÄøœñÆzì-‘Pn› aŒ]ñ‘$Pak,:yÉÚ\sÉiJ0!díâ[6HNOPu`àG©-x\Ç)Ù®ÅÂm +“ªaƒƒ‹b%¡W¨ñë'éƒ$”ÞýuîÅ`~oÊcêÿ7·×"™CäR5a/‡qx`Ó»'C™”<îVÅ¥Ïi‰*õIñO™Ç£±è!üî³ø‚ÃkYm\W«òyÑÊsÉàTBU0‰PË×sLu›œ²;Ê.àx3?Ðà7šè\²'*þ‡¦XõƬ—i{XH„"»ƒŒØ’Õ{Þÿà`r ZqÒ MšÁž3œ\3¤²ZÝSºI$±—{bVÅÄ9kô÷qâçë9›‘ø&Þ8ôÃ>/U3âg€zLÍÆ)z‡?‚ùj©ª&@ú¹Ï´ÕvAâñöíÚÌ;ÒͲq™%‹µE6ËÙ¶Û~~ÐúK=çâ_ þøIÚn‚ wñÿGNí^Áì¼î,JX“ø% .¨)eTú¦ëüÓRÜ;¢+ÝQ‚ÿ‘ù‘÷g;§Û¼m E­Èrcû|b”'—Ú;Âo÷‘+`: ï:ŽªÔSÃþçüŸàþ!±ÇʯÚz\[B™ÔÔë˜e$µ›>cŒjA䇃º<ñ´wEù·#-ÚAØÐ?4ÛýùcE\çÕêmXÑ¥`‘*™GsYeÎsC˜Ç>¡Qik*¼`ƒ@ ÄæCs`Õúø‚îÞ´S#ˆæñ(ùz4Јh ©®bl}"~‡ûzÝö‡ÝÙàA ÑI%2 oìSq½>þY¡v(ö \Îhvw _…CI-F6 V#5„+ƒÚI%Ñå¹Ð´ $ÉUÃ#1Bäü’Ï‘pÉd6™Îê±I>%mš½uøï%Ù²z%°0ö ^ uƒ3@u3#5ap¶Ñ’8aÐ Yœ~=ÿ®Õ´Ëz.nÁãÖI¥ÂºŸ"¾€ÐÌcÁ Ëî‹|Ê ,)¨…"k­Ïù\WìÛky†®»MÐçâ£Ð.YI”<·|”iïl£«ñí3*é‘“£ _!£2êdÄÐ8#5°™(†#-u(ý3ûþ_¬ðó‡õ`¿vEü,ACü·i„žÂHžS/]`6Wæûq…†B™û­ZXPã *Ì.¡bUJ‘DPY»]8é¦.«bÿ›ò\Ä>Y!+oDX霺26³F³OZÔ*dƒd:@ÇÕãÓ‹Æ`o 7˜ãe™\¥P.uä’73Uk˜©—Zšv¶Úy“O&œ¶ÀÖVôÊÌ #Ì‘—PÇ^°…âzµæ´X††µ³E4Ì{Ã0ÃòY:Œ4cP—/bS9\Òöõ÷¿Oê­Ù€•‘BEœû¿¿ôê¡py“LßⲇYEO~Aazã‚\8ôqÒ>~véuˆNÑ¡ÞTøZ½YµâàÌÒ@ó‚­±+¡j’MÀ‡ÛcC¤F3×—y’’ýþ-Õ¼QRüÞ_üøþ`yøÙçÌ;D9‡àC¯ÊЖÈ`pçRɬ: Šª,P%ûcãË\K¯Ì{¯ÛýÒ˳2ß0‰ˆìD-5#1¤&S¨¶DèUw«,Ö D@9#5[()6œ2NŸùmƱÏ'Aòk¶îëm¶º•½I]r(ÄÁÊk;tL¯|̼J'Ãé¨t5û‘ôÛÔÇSçýÃ,ŒlÎþö„?·îN~…Ó……·îcÐIp=‹òO»nG÷¸~›ðÛ »àÀU¾d=ØäØíøM%)H¢)¾M¤4!Ljxß|8`z€†ø€»6%• Å#-Á‰JÒ–t¡Èú~£vwy¶äÚ#-Ý£Ú‚öÂu3'ä:Õì| Rªy! ¢†©q$=`b–Z·T¤…ÂÃìý…i¹%‘À@œ<}!l×ô ܬ‚¸‡õÜ[ä­ê’)€_º#5âÃÏo#-ñû(r"9„#-@Ÿ0ü~i,øÞy \‹xîÞÕúÑuà‚ÓòXÂ¥A'Âß\Zõ„·­ìDK>šPZ ¨—ÚmDôXè`ûxf ¿˜OLOÜíqôIïGqÐH›¬l€„:|4¸dPˆòz$~¶³#Žßx}Ðy¹ð‡üP‹sóê~³´>†ê ú)w±ýý#-{ž{÷ù'¼ä!ÖžBÇż€4#Wˆß?#-D™L’ƒø¢É€ z¸uö×—ìx¤äïh¶=>ÃN{‚¨gˆ‘éç_›HC03`bÁBA ¤Jz¼Q~LÕöx}½ ~9{Ë!_Pÿ-?$†ØÈ3óæ'Ñ>…à}Užni¤lkƒ(P~Œ®÷å©Ùñ_¾•òû!r[K'&êj'©LÔ 1CшÉ$“Ø­ÁSÍ}o¤ø´½«#5™Ä€Ø@HèòýhþtŠ}‚§d‰Zšõu2=ðç¼åöÊ;}ADè¼ë:tÒApEµˆxL¯ë‰œ#1ï`‚g0o—PÁ@ȯ[Ú{<Ðó3«'´îWo#1ó ÷#-óUï78=ÏéKê8 ŦefRbK "~ÑÂ6Òá)Š#5(£Û½d©ý@~¼ž’'Ø4U³­þæ“êð#5£?0Þùûû›‡ª¥Cô¹¾#1³ú‘6}bЫKLÌoLÕÑ¢È[Âd`ãÀÞ¥¥’U’I7< ~Íòn†gNÍjC@ÃKX¦$ Ê–‡ŒmÓ”’ŸiÙôr¢ù ¹öQÓ&”G<²rC>ÏÚ½Q! !·j#1T„?s’kˆ÷!C‰ë¿×îÕ²¤ÛzŒÞvïÜ3O#Õà0• øwyòa@3ô§ *"íIùáò~¸fösùw¨ôþØHñ–SÄ,„a]C±úw£Ù®ã# èÀ€I0d‰,íg¢H –Ð%U%N½t±L‹P23Ü©ôÄÿAþ#î¿Ü!6¼f‡éAõcòÉ •êÈhG÷c·îûL³˜b~’ÿ_¼öfqÓ¦u„ÍQTAÉJ«EQêÃ…Jb™!ý§*>Ûõl¨2/îWæOjv¨3{SyI˜ª@Á9±…£Ë#-õ§ 1œIÃÔ$IŒD—1PL@Â?¥_Ò‘ÌÚãNºÜõýA^ï^–ËŸÔ,%¦ÕZE),ŠAÂE‰ùÁÞ– &fø@ \zŽ¡ ÓÍ#-ÐÑ÷‹÷`6O²N{ûͼèD¼3ܯp~@ÈP€@ „;~ŸºÉpHª˜LôWSÓÚsÉN°ÉönôlÕ;°;C‹×tç$9ÚIPT¢ô*´»dìyج©<0s¾CÀb*9Î eî°öc©¬%*HšÔ*åC±!™Ã#1æ°(² Ç\ö¢W¤Ä0¯¬šÜ­Eà™ï×þ픚ºvÓ@›lš1黸UÔX ¨r©L#5¨ývlŽ*Ö1ÑD+Œ%6Ú–'­(l Û/§pƒˆ ÛÜ.dżå÷þÁóøÇàÁÝ1çñùbâܸùî*Ïw¹$UÞnçn¤ù¶r'f#5TK©Ѫ9™šõpšÍ©vÌPÛÞ[.#5|9ÇhCÈ8ž•Ñá¶ûyÚhŽÌÂ/P9ªPýDØ¡â#1Æ•††(âA¶ #h"–H‰@Á9œ/]Vä|¡=¡èz(wøs'Zzƒ» â]ÈÈJ¬Êª%¤[9‡‡g#—)Äéݸë6»ÊYBÓ™¯&@ÝËÛêô|»‹NŸoé@h[¦Àú̲ÛïÿY µ¦[©<±Àý<¯âÕ` =>¢b¢àBûäg?ÁÚ4¿FÓè#'âÕÊŸ–¥Cßó¢ó–Ö Æ´_èI®L~|çÝ)Ûð?s3½M™ƒÏþMþ˜6vuÂ[ÿx 9c9ÍøèDÙ]³¿öN·çó½ïMf<ƒr]ºƒ8!Ï-QÏ7SdSXàÉÒ,„AQ#1 “ 6[š†ÆÅ€¨üÃ|Ð<½¢ä…„‡ ×’#1!óÃhŽ#1…°‘芩gq±˜pû)ü#kxÔ½Ð2êeÃíüÞ¢ÇðŠÈfò€¡"Æ#"?·x‹à€\{ÓrtN§ŸzeáC˜Á2;Ä‚#1ÏAØjrwõmÑ ¨;þû›ŽcIé[ue®c LïiU'Zñ:¿E]È—UL’ø`Óó©  xî+`w!Ý`#1Ì^t†HP–rLÕË&Ṏý&Åì¬9ÌÄ'jQ¢”\-#1¾~P{_}ŸÛvøòRñd#-*׃D‰6÷Òî»yÛ¨x¯·Y!þEðfÌD[ÀØËnáòñùlÙYä!(Ì@Ãm–ta8îk#†µ‹Ár”Z­’ZÚ­FIúÃÙé#1Íâ}Œ }ñ¢9¤çåKŒÄÎÆ)ópH41BIsˇ ¦*‹ÐËîûúOÁ×HÝÝBôÏ<¼"2‹~ÚÛÖÜb‡«ÙåêT=ºíaõÞ‡Ñ4ýv€\Ì}1dÄCæ—X½;ç¬úC>'iÁØ?Âj¾þݾߙ’B‘±>Ïk嬃>ôJU´BŒžÃ܇X75]ÁhDüå#1\4`‹sŽü•ÞšoâØ‚ÃÙ] ¡I­îò8 y¿pzU?¯gQp èè]¾ãòâ¥ý„tþnä,Ñ¿éýVÏ÷f–"L°*]e®jMç:é™lžRç›n¨FÞ^]å.kØ^6¿zW³%©@‰šn°š“ÈèKÚOVÕšÐ`UhQX'B¦ai*f)\a.COûRmOíú™£PleúLÏõ‚l©#5kf:ñ£óêØŒÅÓ±QV'œ€Æ±å‚é{µ„|f ¯û*Ö–ùƒ|˜«ñ ]bOcV‹lwrJÜ€#¶j‡žÐÐãzô¹6b#Š=Pü>Äï—ñùêÚòÞDç½Ì¼¢Íåm^ž«þtü5oð6Ç©{XfÎ(Á ‹„Ÿ zT12É'C§dåR°_ÜÓ?s+›m¿·©á7=½öüîÊ£—LJo<̰‡î?‚ Ð¤µd¶‘fœªf»{¶¡õË:øS¥í2ßû ?eÒŒp&B·˜ (³CÀÑŒ§u`ŽñÆn’¿ƒ€Br*‹sòsp[q#5·ã¯wëâ£Ü±—#17d.˜ªí2‰Gqú^ÏæÇèèø°ÒEGƢNJã8jk"ɸ:ñC¤²2“û:º03ñ>¢î?E²¬nº}‡ÎŸÆƒ@z@(¡”/¡QdyüI×·X¬&SfJAX)ú¸&q‰#ªù~Ÿ˜@ö,[>WCÝ!§™C!([zw¡Txõû“7|ÿ©ûzn»3¨¾ü‡8Ü,~<ÙÏN®ŒÏ«¦¸ Ž…á¸?“ôR‡™/«è,Ÿ:_{ìq5ôš56‰‘&]Ó{ï·¡6ɾ~oD2&!#5‡#1›Ïïçñ“Ð\{9›q¿È>7Ë@`Ãôšc7XÊ÷»|'ù0Ðlÿ.sËS y¶+³«ü¡Ûj¾ÐSÞ&&ª› n>ryðjleÁÆ#PiÙ·ÐdÔ)¼‹Á´m˜‡3Ï…,œ¨³ r{™Ìpª&Á˜m€sȱ þ²‡Œ¯1âpg\ÿa^1#’/QdCüutyå ÃÜ3ˆî@Ïék·¨˜ªØP$îá°…âðDçG½#5@<0O`#|¨Þÿ%<$þ‡zŽ– ˜èû£BÿõÿfÇ"Û*£ŽŸJ¢#-ä ÊõD÷¸*#-~ŸCkólF@‘ýûÕB‡bᤌÈ*£ ¡±fv5§ƒ‡9± ÐQj2I¸v3”Ú&>Hx¥0fr@™Z‰NSeÿÏ'0äãÒ‘ÅîÖEM¯eK¯l#Ö üû=ù#1Õ]L ´«¶(]I÷h 0Žœdò>ÓUX'Üð˜_ oýÓv#5öqxÿw.,Œ÷¬<ð•pp½QG>é7õ‚ôéáÇÞôßKMrˆ³^[®p…rëqD#5¿&#•9EBƒ•zn±r9þp1”s>Rà?'P{lzvü­jˆ{ §óÕS±ø#ßcY¨ÚuDå.蟷JJ/üÝs~º'ƒðæóúîç:áéÖ¡Qž³Faêé{Ç[vKüøèGŽuÎík¿X"]Ç¿D„›pC%d"Z†-aDMz”H‹Ž~{ï˜ü±¯ß„«zÝ­x²NÄ)‡ãP9!:§søëÞñÇF¼ÞšÃuñÓ8èE¼J;­çcÛj1ÕlÞJ|?áÕAûïðÿuvB·w*;ôòÃdEÂ_aé¸Þo }\ÖÚŒFönóѶtõêcÎÞË•+2¤_Æ=GÙ€ñ½¨Ï^•¡‡#éæXgÞàÝÁ¢Özó²5zC†€î‡ùžçm"‰ iJ#-°³ŸÇìf?bBr€×ëÚ¢rë­sãlˆ)Q;ƒrPb±DÅ.Þ!íãÇ­o‚q+#1»çœIñš–æ÷ú8è•Ȧ©Ô³ï³ Û$P&õ¾_ß#11¹‰;ÚnÌT2fC — ºȽ¨¹–áÙEœãLv¯ïýîÚÝ-šÓS!/ôá¦D“2A­W;E5È^Y¯’0#{ºec$º:„b^½spN^I CdÖÉJd;­.¦ ª:#‡QÙrú#5"*C—ç±Sà{Ÿ#-žç1àYE‚{O @DP?MªC÷öÑýž#5~ÑôÝ—ò>Ctí<øÝC_Jb›¦üóBÞšª«öŸËœ„ ¦bËbp28fä :ìÛœ H<‡†ç °‘䆻†nFÅNíüAF0ýnG]…ˆÙ¤$¥HÅŒlŸå¥ÞzÍ\βš£¬á“MlU·ƒ·:3i.Mµÿ3«u®ê¼½ò„Ä<µ˜¥P‰ªœY#­uòàî$ÃCc#1 1¬0FÄÐŒF"@\)F Í-‡Öpú¼‘0v+¾›°ƒ¬ âf"°%–Si}ý8޲æÅãÁÁòÚï€!²>ÿ¿2ê²°‹Hjœïm§1xêK5Äþ§¥7Á<§r±`#Í·É¿UÿI!N¬»8µ“±r€ý~΋ß=£Î|eòjˆÔ7üÑúÙ訳F›;7­§¡ˆ‹ü¢y²OLJT'*ª†ŠjÉS9 ØÂ6Ò.­1¶Lµ%R &Š#1¨pÉŒºÖ|ÒIš ×ë*HU!ûÙj#! þBÉhÆ)aþOô·‚ V1!dÉÈ¥ŽÉtlˆa?ºÁ»A±¸@þ„H:$É÷ÿ’t‡Ø… I>p<‡ÉDE«»nÎôCöyÈ2%÷zçì8B$ËcA™ žôp#-r!à 8UWMÛ±cò†ùÄ'N@å™yQXˆhz½\û3Ê?Ë<{‹>°ð¤—5¥£2GùÈopdfÐ#1‰ •¨–ÊÇ=ËߊÛV =ÜRô.Ÿ1œßöJ;/U÷¦á{ü9yb”#GµÊQaµv,qŠIPll’t2VŽF€C&ÉÃåbÎIûM°rx¹¥§j‡1©ˆ»T¥B±gxÜ7l¡•Is”\Ä ™1¢8bÝÑýâ¥ýØlMà1ˆ1åã§u·{ËÅàCžžå—Å À ÖJ#5JÇ&ÀvîÛv ŸXk¢„µ¢wñaEyJïÑpbàGcD‰AÛ¥¤È<ûªiîÛ¹4Ëx°? &ôöòǪô°ªµùáSyÊÐSqÖLˆ`Ãî,†¦¶Uäêl#Ô—;LÊJ#1+Õì¡dàM|¯·®÷6x0¬^r)Ãg[Äb.;aE7Fóo2#1XPtfÁ¯L{ÔC¼ýù·®·…Óv;@îŠR´™£Ünµ¼ëxÞT§Rè‹måÞ-nh²Hc:Ù„ßœÑØâqâÄms3X…›g#/8#1¶I‰»†„.ÍR­:…¼?#1ݲuŒ °AÆòd8‚ÛÙowÈÌòócÉì#1³¯rše'z,FEÊ<¬Ô.1ÖÐcß'vÄ€9©¸6¸ÀÈ\§iØyTït80°š„LÙØ8l¨}ÏTÑ·ÇáíÔÏ¿pr,Êgˆ6{Œ’çËF··$4ê›uòìî×ÙîßÝÓR¨¥Ó•…ÑD©Gq”Aò-,7Ë<Ýtmé‰1Iö2ÌǽÌͽ¢Š‚òñž3°>¹»‡¸9ѯ#-´A¥Ñ÷™ öÕ4,| * Ö-jo¼Ùb µØ=h= XoA#1!rWÖp`p'ö@å)D’+„ÁÅ]=Ö 9 påHk´/:!6[ ãVÚql–m¸F¡Ñ…t8æû(øCB„àì¢^»¢hmwÆ>—§ ,Iª›MMÏT€ÒG´ì‹—¢µŽý;—3c ²sIݨª‚Xjgi“‰¬Ã>*”hwo0çtéâÎM'¦~Æõñ}›röfp°’}½Þι##¿ä°y±CšöaCC¨Ì`ì,Î[‰À·M©™pÜÀÒÛ :!rÉ×#5!ïšZ[}áÀzu€Ž¥§Ÿ9#-9<2„óÙü5Qr.;ó|2R‰¼ãÇ2o(ïn›Dà—JmeA/@uCõ«¸ê_|æi4E©bOaJœÊ w{ÍœK”žå_®SfE;º8#1K;γ\¶gͬ\æÊФ 0E’‡(¿jÄ,G2Ü‘<ÓQçm„å©12^ºÙQÅ$”##6 ëì#zC@9ªsÆ!#1Žð)$H°UCæ’æ«v;¸ÀµS$`Ô#5ƒÈÕ³Q6Ò]™yÕSa˳R+q õn©‘߃‘ÈÌàrÄæò$YI#5FŒ"ÄAÑX¡9}wgG½u÷^Þÿ^8´°t9s#-ƒ`®þ¼Ô̈‡hS3¦s&\ÌÌËf5•fY™$«³<—‡Rþ­™î¼é qa®¸ÇQ—/“T5Å’ ;j”Y/ ±–UÐÌL¾=tí¬ÝB›9cÛ#-!zˆ ž ~¬·qn{¾ã|òõ—kÏ=2uÌ¥•EN<¨•#ÒëÏZÍsÒZYpº.†Ñ9¦gGšuÑ=âkÛ&cʨd;yjÊ¡0™lá9•·ç¥k›ƒv➊$#1üŒÎ{œùÂK£¼+=1"&`ø¯ÓÚ).ã#5U$º¥–-A‹¹g=&î§ÃÂôC:íTé‘«<Ü­HÔ¢Â#Pbâ,Ìé%±/§4ŒkfsgưuZY^¨í#1#-Ä4B2Áuuàôõ†²iÉ!Øf#”ÑJ#5@»WK‚óÒv%ÿž#5ži“¬å²**ÑÇ©Z‰Ïi$8ÓCTH¯œÊÏ—sǽÿé¾#1 mUé°´”ÃqÂ8/¬W™#4Ó`FHô´ˆðª¯b#-E#-(8ßÎ\6ÆJ«UŒo·U¢z.#5Û&àÒÔ°/`Çšxã`Ýg'@ÞeÜH ]W@NÓ׳‰fíZØ›Z–ȱ T%&@ì6bÔª¨žš`D@=óF%O2‘#5=ÇКiEzI#1p&Lɓƪé!’dœa%ÇDV=˜nì0ŽÁ½÷Í a…4ñSŸì‹óhð§§œ–°¦Û’˜Óañà6MÂe i4$Ì(ËD$1X_qDŽÊ¤Êê‡;4Æf:ÀìîFç.¦#Ä-ÐÚDZaò#18Â^ä»Ï$5|zê”G#17ôÉ.dKÃéÑTÒ´#1æ–©ÛÆ…'Ô©ß í%šš&Ré_öô(b/ÊR0c¢µ"¨”nJï¼j“ˆÏd;6WgÐ\óf_L†¤,ÀMiéȳ²KÒ•Øz½7n ºÝºl;"…Yw K†8Þ–)a• ‡€”T½J:!ÑM KçSmȲA@5ØÀkZC8΄Ü5àâ8ç1c Èqà©4™¦ŒLd£“¡á¤ÆæZkZ½C^Ön»Ó6ínS./ÌçÇÜÖÞꔌeêœvªˆ<ÍsÅ'wÆ«Ö8ɱÀ5;`à_~L#5â#5j#-´ #5nmv»f©˜¤Ýo¥/¥ó“P aæYIÔ86IV+7†hÖ+æï ϹЦ¾Ïl–·lgCgM8‘ê{K(÷>twx96r6 Hm^ÞæR½¥É¾‘ÎC¯¼9†ÜO7r#5Us.T]4ÝE#5¾_w(_“ꟗ`q_!O–,y€ÑÑõ‘ª+á ’øûï²#-)#-ˆní௟Ë<¹´‰æË±ˆ¿'€w‹+Ý牱µX«aC¤Úµ0R^e_ ÝY:ÞoÇÃW.´ózCCC@¸ÁMc³ÁìÛ—òŠä›‹*É&Ò<øcÔøHí탖Gà`™ÿ{ršô€C$¾¢Ÿfås=T³(Z0¦-ª½ÎÆx»²ôÐËZZ ýÇÄÖ,‰”6› ÚaÊ^ž\ýfÍiÏ»wm’éW3Ät„ôDःÎ#5¼^>0‡iÑHýÓªÿ2ŸÄ1–u‡f3Öø®+ÔþÄެߟòiht:w}—Ø4ã)À²p[ßw†{HA¥Ö-‚ÊÒRÞx :Þ{nô$õ§‹FŠ($K @¢$‚Q#µ²ùR={GÍ@J#Fïåœ0ú\„D~h#56ÄP„M'#-£uHÕ™pWZä#5G†ç#=?ÒG¯ýHòú¦Y÷»}#1>ŠâqÚº|?ÆB†H ~’”FIMQéõ¨~¸#5-z€™ DHÀ!2„?ž*Ê;î÷úüÇÄ{ @Ô7ž²<»lý„[Ü¿¼ëEÃl¦}a÷úëø³JDš¥´Éµ)E+{wQ&“Xi›)_‹wjߊJxï2IH†Ç)Ay<¼ÏUW#5…Ã!JÚ_f P`¢>íëCŠI6+>)vG‹²BóååÕ¼½WËõôÞðTN¨¡w¸’RíWËÀhôXâ~`?¦I"]ªEH5•@òÛH½4âÚÀyXç`ô¼N×Ïeþ¹ ¦¸UFoÀèƒà¦‡Hˆ–ƒšmR¼§æ* G&Ö QTA-çKx®€:( ôì=d$ˆ"š9ì—()"Gê‹#5”ƒdDÑŠET8€ò&@ í¤7BFQ @€°6$Áå´~ŠfCÕÅ×â.‰Úà͵²ôtšõç~U¸†wV.‰1\´€Ô©™#1ʆH¢ÄH^0Xº5c80.˜HÜÌ÷6D[¡³·¼ã9YQå—ÂÇÞÇ0Û¦î2=#1ìN„ìµµìQTža‹­ˆ‚AˆRñ5<²3ãøÑN±¦)€xn4è  e#1Œë#5¯œR·>ݵànµöäwéÖ#oA§C¼þUWÒQuÌ0á<¢£ N=&OAUwÀdP¨„‚Á! #-„"´räp¨ÐQŽ#") (€- ˆ#-°X)~OÐôòI=—Û|î‚¢Qèã%#m-TP)>@0¬­µËÅ [­@`ÖšEȰ†#-Ü \!ý¨(j]./o¡_Ñ'Ø4Êþ”שx×¢¦I1oWux½6¯Sz›šìJ7wh%/G]‹snl\Ý1ÝÈr¹¹RVWvž»Ä—˧[¨]©(½¢#1XdTêc¨¹›`3éýܤ©6HWZß·¯Çùiœ€'¾d…ÃOæ”6ê48kÂIHÎaãƒxøÞOgµý¸9üŠÝ*ŠŠé— PÿPÓµ;¥#1œ,«ÇÒ‚ÈH$I 0[o½M®V1%¨­\Ûš’¹mÊf•2Ц`´TšØÑd9Ù%oÛöß«~E\ÈÎYËj€ãAV,„PDrT|#-ÕGöq}]cø^ûý§³çM4p–ÈË„Mÿ0Ì‚zÞ„5é€òhŒ#1RšL2ÏdóÖ·Í?b¸‰û‰…D›xÝï2wµ…(!;Ž´ãLÈ­áë‡4RŽN5û³{Ù¾Ûí®š@R)+q(&Ìc®ÂÎTXÞ‡„öä‹[W¤ÆÒn„ðñ¥Hv!ä@dD!`P÷”•*#5±òO*:нø¹æ'Æ”÷ASoo3¥f«~Çû(OZÁãþü^¼¡¥!A«)ª¢¥ˆÁb§×Ìð»”P‘|€ª¨W(š,2Äšü_£ù{¢^Ú­¯Œ”V'–hÑÙå$áÚ¶¸ËŸ<á àgÉyº¿ZCÃÉ#-ˆŽƒã5}-,(`ÑÒª|ú\Ç ˆq føõ41ÌH4R#-z–A3€d4æxñ²¤ÓáŸ*ì,½kó20ܶc#1P#1fMÙwȇ­Éâèˆr)·eñø`¯'ŽR7Ƚœ’à‡ü1Êû ÉRÃx ¹\0&ÔÉ–ôÊE5¢×xA`Ê'μOUàßÏ#‘jßÝ\^¾1^t,[ÞÚñ΂`[˜ïžÇäÕÅp§ºzOdY<_\ºáèæø%¦ü_‰»;/lih­¢Ë,xÂr€Â•qj£ÂDÓ%B2.QÚ8³ã\õ®|q=6º:ñì3CÑÑ Ú""˜]Á¿l×=$–Û+\+Ñ…¨S'$NKÖç[:òm¨4Ú¸r8lüv ÷<À'­Û5Yt›cSnù÷ũڛ4#5#5ÇjÝ”:lGÝŽûNZÌ5©äù‰kš GÄF"àrdæfÓ#5gÙ©ºÝGƒa¯•³fXú›ŽgVvÞ}Üp–&LOH!„Æ#1pþ÷Üã^þòcÛ ?‡8n뢈Bså³t£Às;;è#5‡…Ç´=þФuo!SÚaˆ°µ!*Œú%dŠ”¡}žŒ÷zÿËf½P;ËÍ#̨ðUûð@ä'ùôÚŸ¢#-ñã ‰æzzºûâjl õ•o³¹IMßC>pÁ‹lßÜ¡ãœ9hœ¯ äl¾D4Øh««°9ê=0•·g=Û†ÑsûwÛw?*Ã%ú¡oör sÈH‚:ÛP»ù7?gÕðO3 ðÜlp;vêÝFl†/hƒÞÙÜØ].oxâæ¤.Ý´&½lÍh]É®WCŽÞ5î8›¢!⛊ÖábuM¯_cªï7‘7QÔ°ØÑχ3€[‰ÚQ$-#1MÒw •TÓb B'7 çϱ>\6a#1 ž×ê8×›.Œ‚r$Qôk՘ʧ×TžhÁ&š+%”SuB{«ß?ž8+cª6Éi’fLÑD7Æö–i#5DAƒTP«,2Ȩ̀X­’lycOExtêåÓ-ã¡§"Ö$3!~,I$áÃÔ6Цn2tˆGmü[N˜Bò†õöý klÑüÙ1,0]…SÓß^}ÕI5ñÜp î;ôÏØVU4ã‹ÏÕ¢¥(r¡Êú8ê’wQí55$Üü¾Q/¸ßÀÀ´6˜ã¿M9+š®|nÅpxÉ~+Ë^Z5U‘…Å6›NLüžÍ»„"1ˆëöq˯rôÊ¢A|ƒ®~üšË]Ê©Èþú œèÊs”Á/#AEUE€VëW1K#C6Kk¥Í”ÕÔÙȹ,î«]Kk·ÝåËwA±(X&0+ŒT£…j±Ç5 D¢"€Q¡Q@@öP÷ñvŒFnÂ+#-L9NnÒ¤€ÈÍMRR”&ªe«3_Uü¹Øä]åXSg.ÄÆ•¯>;¯»¡ž5¥»¿…ÞçoáþD®· OȹH²€h*'’$U##-õPz¢ª¼n› ža#5@"¥Ð<²!ß?IrEŸ{Þž.Õu€qDüðQÚ#5DÇtPXR‹;ki’Í<ÒhÈÙÛ[8ËØØsZ)ŒpT(Tæ…3@¢¸ê­[dmõ(§«óN˜F×nçŠz€§à€#5#5GvÄt3. €}ùV¹•A=PÞTD·*îiŽ·qîÓMyn<÷ àNhOž¥ßO·~ÊÏ/~héáxî7u¢åhЦKˆ€¢#5Ý#25ª¨J²+HAJH€EŸ4  1 (ÁôNŠÚ)˜LjNg¾„–;[‰mˆ…‡S=ä@©#§….Rƒ"„ˆ’!Ýš{Èà"$‡ß{Ñbኼ+üº8øöNkÕÀo$„Ç~ðëξºÂÚHéMžK?SuUÉÔòázÒ¿CA{ñ…ü&'\F(BñIs©N'àÒÌg›EÄAD¿I&Ò–4m‹ªuETÕz·¼Þ0$#-óÏ`WS„ÇFyÿmdËäÐÏ9Ùw'Pìe6žƒquµf¨ÈTŒîBSÀÅ£‘­Žþ™ášÂî\âÙ •§Ç´ $ EP9¼xÖ²H,5žÆöTH­ äõT$‡#1|ÝÆÍ¡7§ˆ€([!R,UA<6kLá˜Uå#-]¢„Ü¥…ˆõ#5è¾}N´Iµî¾^FIKI§Ó«¤ÔWÙ}×DÉ“arðè~0CT;ó¨S«ètnK,YF´Ò«-FÈo‘/#1÷}9?°Óu§9ÞN±ÓXff«`; ~Õ¸¼AôG@­¾¨Kcf!nèRAª#5Dí¥”¹âp½¸;Ôš2. |ùœ»—8¥Ÿ~,4+:Ø£dãm“"w¬ÏŸnqr›Hãú:yðŸ‘ÈÀì¸%T’IÒ­-ƔƸÆeèB¤aæì‰kpÐBm.¡²‹ÃˆHAša¯ •n¨#A´Œ,Ó.A ì5‡˜h,$9ÜÐlS5F9, ˆrti[̀ðÀˆ2'7õsk²½Ã>4Dgr#1 •ã;•Ú9–‘z•J‚P#-*#1.Î…f#-¦Ày°[F“a¾ÍåØÄeI!ÒÀd(„oYEu0ÐÆ&DÃ|M-hŽÊ-Œ<†¡3„ìe…-…lXÌ n™ƒL ÙyH·6ä4¦Ë[å.ÊÚïE£ ]JXaá¢ÝÈ òüüç,ðvÏ#-¨žÈz® _}.|™$)Þb³§&¬HC,„æ6åÁ Cz2o‚6mî#5‰¶%ôµQ²+­ŒŒ“xÚ˜M‘zCÌu‡º2Ú£’ÈH²:äxr©|.'Ä ù‰ƒOQ˜`Ä¥b;òCqéÃs‘öM!ÌžI9YÂâZÁ²«’œ{¯Má‘cUÇ6½Þ[< }ÝÈû2_nÇSg$°rëºïùõ‘‚(åË©±*CŽåÝÒÏ(N°;92ZMÊ(R¨”NoÇÐþÔ€ÈAˆƒÖ#-íR¶ŸÑölx_r[s“÷‘/iÔ7Õw/ÎÜx÷™Ø7>=*Ùû¹ù·–_[8¼Âdˆ3à (#DÀ˜¡øQ•/1Ì^¦áßáùb}4ÉL'V#-ÆY0¤ØÀR7@)"'åJU…ÓP¼´\ÁT¿…ÖVq±T…SÐĈh Æ•ŠxRºL@˜“Ÿ®ÃÄpÕE’EFš[cºØÙÐZ¬˜·ŠcÁèu9õï@gˆ­#1)8\ÆÌBa“v_¯jp˜pÆ*û$°™–Èc%QJŒ/ÀG|˜0Dð´Ç÷Ǹ†˜–òj(v¸×ŽÎ H3­4´<0›PvRl„žEÁ°¡zþ‰RA±›99ÒM óðºâ›º’¸í—2Ca×d×Ùp)7 0žêq´àηÛ_dr6¶k(ã׌–`e™%Ù Bã%±2†‰T­‘ßv÷ ¦àï—.`%PP‿ê¡gƉ“¡àÂøêÇlRYên²‹ü&P5C<#5îˆaÎ×{äš™ÆM3YTmdÓcÉÈTåX4ØœÒ-šF=†2¶#1Ò*EU³¾Ï ã…Ã'WLô|³†¼ÛÙ„(àšóÙj Ôe^¤BŒ*œÄ-5ŒGëdjB~Éçšõ)¨Û³A²Äb i¹ w€¾YöÎ#5„late^ 5f ¤]SY®Bä.œ–qE‚ÄqÚP܆ –#-y°®*äÀ1«cs¾Ol×V>-<ãMö•¾u·…~æ)¯Eâb;ºÄM:©‚DË׉bËŒ,4iþm¬Ü½z]M*$¸òO³§p—xÚc4uÆÙàâ×Xgds°•;É»Â9ÌFÖl_ê¦ÿi6Bp㉺yi™Y7Lkm‚Z#´"ò*l°ÒMîä¹i«.Ë|]vµíÕô½:­_K‘•7Ë3¬…#µW„bf2‰)PÙ&†O/*@â•T À:Ø’ÄÚE¢Í‡l°šÑ*”ɚѰÐi&£Bˆnš6ÐÂÂMD&Ì–“ÎÙÎ&1(Qɽ]Ì5©Æ4¹o8¤}p¼Ã] •€àɪ…A6ÂZLˆSÒ&i*”´7CG(š –Å‘,#5t7krŸBÂÃ!™cðˆFóò©ÜG÷²ï¬cã4¥úzDQÖeŒ¼}¶àúcÇŽH’TFc¹ÜÖ#1ïÑÞ|´g/»>ÿÊ#5÷‰³ƒ󭪧íkWªIm¦úyõÃ{NöB°ö3Å®íF·é=‘LÚͼqб: Êg}#SöÑ_}+mHyÓc”4âÆüí3 ‚áò˜sÄxÖ¨ßl ¥^Æ«äÖöS'!Ñe å$tÖcÏË…ð˜Ë–ýOºe8x¶+qŽ…¹¢wÞhתIthcdµØÏ%A\? ˆT§©ïß5É{á̼$¥b²”DìŠÜëé¬nKAhžû_Ò-ž&ÍrÆ0’SMÒ“ÔoGÐé;$…>mkAF»²2/xgµA6£Ñî:Ç»<#5Ÿ«×5²û9ìÖz7pïK³Ã˜Îe%ç¿âNÆ­õêê'²½}ç#-ìí±õzüÆ— Çíú«•ïúΫIÖ•Rã‹á' ³Â¦â§~ß³¯éËi滓óxPtI÷~–ýp„±–­çô!é;úhÏNº8ÓК‚VÏAR|'núöMx”н45#5üÛXuÔ­nLÔc‚ûÑØóôOÌ^aÚøh«³UI_NÒÇ€‹½ÕJ££²çQ”%“>{2ºf¤ë#¹7›ñÈñ™™£Â¸‰ÇÙ§4WN¿²zód,`lÁIº€E  Ô¿âƒ=Ê2Âðð6yFã"5žå#5ƒœ©‡s8¸|Y,ÿy©MÔM½:“Ôù4Gc„kL+nF8ÞVe¼ÐÛ2j¢Â ò¸Q“¡]87¹¬Á´ýüfa¼®|ãf,nj7N“9Ôy¨i™kmšËÎFiœŽ1Ý %¦(gFE5)…©å\vÖ÷¤q+[À¢ãèMfZ]u²f讬Ó9ÚÛàs8œcåãtÅ¥'ß5¬—'eiéº^,Æð/=×)£om:Ê/Õ0é´"ÖVN#5pd©Ž*OØêÞ¸¸š™–ÑQfh§— 0R+[a1–On¦dçy)ÚJÎÝs«Ë¼fóµ–öåÖ •°±³ÚºNå=ËÁš6œ¬—yk1`ÛLC#1cp²HÌS`@¶Ä“’±.µN‡êSÓzñËLJcMj™Ô4,7#5pcUQqP@˜¹~¢+CÖF'«³¶žyÓ!Q¼6¨üxÀÈ9éƒv°nuMU—6Œž$‡y¦ 1ÖzR7fÊôgF]Ò¬ua–ÏRi¶ L¹¼Æ‘Á¹±½´uÍQÚäÓäP“ѯ²õ¬]£uç‚®û‚gl-¸}k@Ÿ+9–{OÎb0ë$L+îäÖâ.–·Û+—ÅÛx˜À6ZIÛÙQêí†Úfî²UÍ‹Gœ]ÙÕwzc©Â¦ŒÚ2Ómi5ç×´æ\ôêO= ÛA­¨ò™‘Øà黸«xÄšN(·¢%k|c}^§F±{®:∄8™ÐÐí¶Éí=Uæ>kl`¡æxß>¸Ö6¡÷àÌß#1¹ øŠ!?k‡I:Ox’ŒlÐùxSZTç¤c®,#;gC‚HMœvâ81HÌvhÕÛ’¨±JGï—ÍáÛ£ÍrC÷=B¡4q’´ûˆ[Ι¬ï¾õÚã%¬ÇQÃîÒòNÊJÓÚ´ i›cD$Y1ŽcÌK[Ãз#á­³&‘E”>¶Õ®pCÌ>¡´µi\…0Æ€0bîÒö”¡ÒƆ×LÙ2­p7dKÓQ"H"“UWƒQmŒ{†m5#5jåVͶã\½dÍ©r!ˆòr»[ç ãOÎoFJÍâ#c{|çtl†d„!2Ó謆µ‹‚#1ÍÙëÎyé’¤Ùxu²›ñ˹’¾©å—IgŽN›£èwÕ~08kÝ'ì[LpÌÍ”4ÛKcxe1B)78S\Í^›Ft곦*S„’qê,eU"[SDµpƒ‰u°%å™d”zcn¶ Ü̾†P®#5N!b8ãNäÝX©£«#1½ö÷åÅÞHÁ˜M¼“ uÌÃtH7*#£ÆÉÕ»ò˜CVãÚ”ìû»Q„Ô_H0a]âLÁL@Ú†hÀì…k3& ãEaQz„‚Š»xN¥ÕÈ™ÚòH˜S¾Êø„aÀÊ$„é öó˜._›`ų…ÔE9($ÔÍp³hi‡•q%ÆZ×­aɵpÇp›[3R¾öFËҴ̽1åf3gsphÚtÙµœ¦ÍµTHÏN&!(yßúœníË›ª×v«¶;°ŠáY4„‡)#-¨_BF] γGk,xhp£#1¦Ê#-¶‰38Ü,4¯YÒœf@Ù¹ÁàYµÉÐǧ¤õ‰]² Ã«"Ñíˆ&#-i‡v/#1È4*o´ÚÞÜÎ ~¸²8}8ü>Ä>µ6bDÑ0AR\!ëYñ37”Qv!Û‰µHf¹{ÒÜLuaˆëÎã…h¡æD>ÐFñƤ»s“¦ÊÚ¢"9Èß{‘€ÌÂàªpØFF·Mù:’Û²Ï<7 „"ÁެÛíêÀ1žW#-ñØo-©üRýÛP=R\ X’ ðC’‡ûlí°#- PÓD©UD殮ݶ¾äÍjɼ”š­K`,fŠ‘xùÅ( *¼Â-¦#¨˜¤/Ù娑2†AµëÔ©MiŬjÆÌr"Q‘K”>^ÿ6d'ŠŸD=1K©»§Ï4îUú" ”E]ÿ»ö[,Ö¯©rþKS—Wwsfc[®ìË›®›¶ \Ù+ùkÍO/äîí u½6àí=po$:/ĈrœºÊ3‘­E¬ÂާÄÀ{Ë~ð”yåãÛîOZð±ÀýÉ‘pý'ôŠÐy¹?ke}Ø6ÿ´ú>LrÜ=Ù—ÈFÙÛwG*ɽ ÞH$bD¨ÈØÐ#1–DN©´%ÅÐ%”.££¬DÒ‰“?ÃI $aD`b¡+(‚,[ÔJ©MŽ˜.´Œ1Nb8‚ÆùA2AcøzrÈÌ`H°ð$ D¢òbf)ü!ØoÎÞ^Arå‰z¸oK¡êŽQ¼’ÆDÕ7ãÛGÓº"„tI1l[cF£kE´D˜´DÉ6 Ó2Ñ© AYFGnˆ$ˆRˆí´Oð÷>Zg¢§xŠ•Ø§½~ ’d/èêw@!ÀßΑ‘ðƒ(`ý×v"=]¸áY 1L°’ai#5ÞîeKØ¥K¤)ÎQAÊÝ0¸O'br;üÝ]B#X;º-'„ÍzÖ#-ë.@\Ñ Ü;¡(¼(‹õÚ‹ ¥E\Ï–MXo =h Ï] Æ r( #1Ç#14m)•á™Zcé44PÆ5e,·D¥H4úl…¤D 6É ª¤‹£´˜CW*éUómê^›zªeW‹WdÆ™JY±M‚n0¬\5D‚Æ!¨çúÇTLÛâ§r‰„knVDûÎ]‰)áT°X8hŠÕ4H,m¤Á¤ª¿U-ÎýôhašŽæ ™Rï¿ çú©¼±=.4šP5ÎI‘“{mMwwyÑ#.y%¢;óãÇ:ɼ6nÃD¡R5 #5…ËÞ©K„lÖ‹Œ;:4.Ã6ȱNÂݽI#1'XhHCr"ÄdN 1äÎ"§ TÇTK¡‘$H(^)ˆ±,™#5בûž€â y‡”#ÏÚTÁFT€(‰i#5²¤BÌB”¸àE³ö†¤…ÄÁp)?!k#-%Ed¼~6tQ Å`Ðå…lH;8B#-È}øùéb$ëÒ!Úõ3+áû,¢pg2î“Jñ°šb„&Â'Ím1¾‘r0«s|œªÚ## ‘qñÎ=uÉŸ2•iÉÂ:¾§/´Ú&ãGNxh¢5ásF°çÂ.Š:]ŠïT¡½©1iÄÔµ©l¡ ‹¶Ž0ÔÔÑ#1RâØEsýþŠÊhÛ##5¡Q3¾ò„ BÉÃãdS±ÍŽ±Ã£9¸µÅ#1®:›tØÓÜ(¼uCLx-gA™Ë<_#-Á­Ý˜tû›`«pÎ4eÂBÐè§þZN¦Ÿ—l­$˜äsFHTìãÃlö ¸k ŽÊaÁò©5Q’•Dd*Æ3e@[pä*1Nî« aÒÃY´}$ã.´“Ípµ„ï1©å±4\CbF”ñpCµd"ikg”Q¾YÚ·¦2$ ³tb$(»eeŒBœ¬Ýº»Xd‰ ±Ñ²ÐJc­é¶¢ÈÉvLš ¡>ÕeÅO´€Èÿ/0TŸ{[f¸`mJv²û­CŸ÷˜7AÄÄ ÓpùÄ7aÏHTÀއóŠúÚ’TB%xÙwUHÕŒ.t$0€ö¤Rmª-Qki-µ_¯µ-5"BCÔø)š_¨C;ÏG£frªŒ^±z<æ&Rä}ïˆxB¾Óú 2³’—]ëy­ñÒÙëj2¿Æ#1ÆÕe`ZŽªÍíG-‡*­ÒO"†É½4á¬ï¶M2òeZ©˜R¥Å,µ™Nµ—¼ÜÝ›$Ý •¾.Æ¥¯ZŒUt¥DÐæ£Œ8"i#Hºe/NÛÂ"6Ê9-ÖI†S}th5#oUº=&jdjxJ¯¥âß\‘-+Ó‘g™Ô“dDÄ„4Aïi8t8âäŽ +:Zk [Ë»¶$¸hÄ¢)]Í·1&˜ÇÞ‹N´éŽN£šàdröåÖÚìàc4Ÿoæ)1k¥c#-$ t¹“Sq mPó;>ìïÆ*†›ßm·“I­¥bâðWÄÛç%Î>³;²cÛéÆHL…aÄ1áŽÚºß„¥åE#1kD­/zÎkl0†Ã#%ÍÁFHe;Í>#18nd–ƒ1’°ÜœŒ_AMÝ[¥(HÈ‚S€j-1c\–QºF£]¾˜Ú¥ÆÒÆER’í:’T-zh#CªBÐËC$ŠX6‰CQT”4¢I$Y¹°,è—B•3`PÎ,ˆè†PZ°lm1ÑyäÇ"zÀõæÊR‘¢Û]ð\ä®î5Whs¶.ÍtI°$Œc)Z±–J™#p„]çt¼žzõxRê&Û¥š\ÕÑ-Õ®Òké]×]ÜRoË^»”L´í<Ýw™W’¢cJÝMnvš£Iiy¿³C%"GRŠ ³R¤UmÜRó)ªì»*’BÊL‰²ØÛÓR)šò–º[K4´²U,Êšµ}ƒ+êõ蚪"Áb¦hÛ[BTl± B*B-íô”M^±‚¿TlmÛ(l¥¶ w0A¤È¸@“   #-*'“hRìJ"R*¡Ý“;‚¢¾˜Žâ!ìÌ °g³5ïaê×},m-LX·é#-‰M9‰0Jš3½ªÎº Êb©Ôúó¼â·Ôy·©6˜¥N)÷?žÂùƒƒUB4A$D¨ÅŠYý(Z0ŠE1e*1Ø£I6$6.ÝN゙2ÚôÇèéf´~ÝL¸ì³o¿7BôýP#1¤Â9¾" >ZÀ(A¼ë[‹LÈ®³Æz$ ¡¤tBˆV›P\6pÓÑû^sÕ7<u‘䑎Fz‡ÛW¨ÑráQữWi¸üìdê®O^ßvš/E:YqÆëÄê•þÏE<˜ŒÞ¨† ©ÝC&(Ù’Uá/HU[ lBTv¼ògØ¡sDpaÓY+-‘Úrùm+Q˜@‰b¢ÿ[#1Ï[pM#1°µ:ð»©w9hPƒ²ïÓÕ ~IùÖ F*‘P‰ó¾~Ï¥u>óìë6·‚2xœl£ôÁ@vß”löþöE‰#§æ(Ð[m>ïkZ¼›Teã²Z¨€ÿº¢¬šñº…³%*‰%$›šÚäYC*%fLV÷Ý«\©¦ÓhÊ[I²‹HŠi–K,ÉJ£LRͨ£?2ë#5IjÍ3#1›DÌi‰*(ÚÔ«SDôâŠÈÖ¦»®›P—Ù´º³W·nÆLEALÛm(ÍiMZJ؃SLƲ«÷–«µ}ND¨Õïݱ6EXÙ5¶Û"I¶j¶åÖ–ŠLÕ$Ô£lmyço)¤–›M”&f55°ªÊͤÞέZ[c¨tk6[ή¼î%-©“C\·Vj’¼f¯ÕS« o"k²m›[ V©/ü9#1:þÇJ¹SN§¿T+$áÛÓ¹°ÚU(åz`ù|»«ÕÙ¦á’g5àxÉ`®–º™’L!Ÿ-Z€hå2ßëè¾lL`¾ õÕÂ{P-¦¤´#5Êe‹!"&{i\•I!eTF d0A„H##-äØžÞ\¯ÊÚ¯…¤"-³JÙJ‹Wº­\Õ±¯‡f³I¢•xÛéÝkÊZºklUÍŠ¢ÛQ­wÞ¤²S0ëQM)°»Š@”…[çl¶åª¢É­e³Q³Zfb[H¡J-)5J[kM³fmd¶ÃJh¥Jmò[˜a€‰)j+*‰´”‰EšRBŠlÛ)šDÔ™£a¦c(ÅÄXÍ#1¬¢›´e†£RU‹jR¨,YJ•)L•IZ)1¬–’+J!µ³i¡d(II&Ra)’i“RÍTÛª"ÚDh¥‰›RdË+mK5“&Š”6”Ûe›mImZ÷µk»VÒ²ÛM¤²Ô†’ÞóV×M›5µ)kY5«F«Ù«ÊžÊ®k^³U¶ÑjRØ,•Qmi«MM¬ ˆ‡_QRX׸žäí lÃÄ­ÑÒ Qåšb\¨ [^Øo™mÍÛ¿3=udž¢ ûÀaê»ü“–R‰©“áXõ#1äOAfö)çzIRdx†§—ˆR<ãr7—mKTI}8k÷üPV±¹U~1=ºz!¾:Ž>öù†Éd]~¯.¬k–ι݋f€Íµ”1àó2Ô‚5F~ï²z.ëI¿3±J7i(y9T¥v˜#ý^!òA¾ëèñä:,ÿ?!ÃÏw×ßwfPëBàl1dP—™šwx=CbGp)Ìæ]28ÐÆ˜Ä×&¸(F*ˆ!F ¯hp¤#1}Ыþ%½€-Ò„þÜ>J£ØpÝ‘ºŽÉ¸7Å:º¹Á² w#-×ïúJOVÝ ›£”>²8EÆ{(Á‰¯éÌŸ‹ïp(Ȧ*b0<QC¸ã¸m§f¸6ry<]¦¥lݺÆå9WÕãg©p]?k±œ#5c9‘i?b?R±.-%NÁ¤ v wêßÏñMåóüŸíÀ9 *iì&p¨*/ê¨}ªË‘#ôU:%-?’U¸ ÙÏ£þz¬¶úIÙ„(²Ê7V†ø(›A'¾¡u·àømVÊ7q&/0Š/„lÉ’f+*˜(¦@U9÷p-c爣)Ái”^AäA,ð`}6¯¿Òûï[onÓ€,T€!˸#-#-V-±ª£Ûê•w]·!êpöv·ÙªÕØ%Ÿ'° ¤¤ˆH5‘;'8º!ÅåS»ç/ו±—.«V#5³'ÂxËö÷À¸I` 'mnt0£Ë ±0ÒàqÙÄá¡_ ÙW»²Æ8[:vaöõËä3ûLÞw OÙÕ·wÈÕ r.<<ÞïÊCaÊÞ—hÞt÷¶<*»7xÑQ _à›Qêšú{näï|]“þöE°(¹lYÏèŠ5Ëþ®ó'ï¼övå§®¹¨#16&#1¥iø¿ž×Ë#5Ö÷+-Š"°KK*¤,Da“Yr0˜aF@‰‘„mZM†‡¼D5$Öê-rõwv¢¦)=ž-é½f¢ò÷ë™Rq³mj˜i䊽´-á# š3–ViÆAƒnÒÕ!|ؘL¨nL0\%¸¬7RÅD4avÒª¤PFº]$ ˜*ZPñH¤Â¦?5B×úPj‚Á®€ÇKÓ¾‚ ‹œïF²$P‘>H®¡|å‚Ce¶Ñ}„õê Ü€æã↻ªƒR-Ä­I¸¤hÕ%c˜àú4~vîÐ4y´¢¯dÞhó?»ï¼»j=Ná\I†î`+Ðõku}ç¹{ÖÍ#©¸v>”M0•€F†ñ*#5Œ‚°.·Ûß\4騭¢÷j×\±ä/Ü}Ad ììQ–.oã»snWTúéÆUöº5öiP‚b'מSÂ뉻B¯ãHb&˜riùw}h’ÄÔ±PÒ×›Èã“rHCá¿s‹®H:ÞŽ>Eˆ‡q‹âÖû#˜‚#&(hCr˜ dÕÆ¤³d=$¦Å2 Hß" †m>(,Ð=õÂÀ·v«ê°Ü‰ „Â#~=%èT#å×J&žÇÊá.ß©U“a˜y‡üÜcIn…bHh“T IL’dùQˆ´IôóÓjÌ_;ø$ IФyD‘6E,ãPðøª.ìÁ¿F¤ŸoÚ§ aÄé•Ã/;ÁÞ]M4ÛFÕêÓË»ÓUÒMJJO-DV¡¡¸M@(Xä#51“(£ÚÑ3€Ûƒëgcqû¹éïR\ƒæ.¬£ˆêÌшU ‹V3ºxQȵ½ä„ÌÅ›m8œ(2ú½U;”+ƒœN›—DÍI “‡ãCÌMª”œFã£çŽ[Ü#^OÆ>ŒÈM4ÆyŸGpÆÎ‡UUñÌÆëQH‰,A;éLMkËaYIjí™îS>²Š#1X„ 9™<‚ÖØÊ,ݬ֘„¦Ę̂Äs1pÈ \¡û0Ï«OOÙߺ™çùyU#ìJ³wì-×TIQܱæœ<ë j¤PXUG"ž¨€Œ dA©#Í*ä~® bi™=žœâÒs·HÒƒ}=1™£Q…2h˜jmtÙDíÓ’ MµÉôÛ­;Gq5áøçhî#1#52x&×#1ê5Ö‹Ž&»C1¢ó§tM5›]ƒ¹½óÚ—"Q°7—[ &¢fÇ)éáDœ9*±WxOQz #-ë;¹ãÂfêÖ‘í’™SW¢8³ÝEÃ[›ùKïLXuº·3-{uåµ<µ‚^‹§*¯bíšDím#1¤è‚#%ÍË9šFœCy.ôã(ü€Û#-ôH¡ÖÀcÐÜP#-=#®#5‘ $#5UNC( ‡ * €20Ϊ&ÓX…݈P ÊY·ê·íiÃ/nìg8ö®äÆóºîïY½Œâ•óW.Qê6ØÔšSZ¤Q|1Ô¿7{Ëůƹ°½ûŸå¬$46×HEb#i§ÌŒâ"d’]/;°Œ¥çkœÜLápòºÛ’Í.yçZÇ^v~†ó2’1¶8$Š#-’A²5ÃZÁº1#?7mÚïUFh”Ã@]aÀR6˜Ò·ª³™YïädÈJ…y½«°ð×eáÒKQ€Íó"¤5;â&Ëã=ëÞ#ßmÐçã¯oN¾U¿(NDuä '™ÅšòÅ(Uá…¸^!Ë©Šjµ…É!‰Ä…Ÿ×öxÃ,êàš‡Âq†ï/N}hÕ@‘ÓÈ=˜<#-öæzò¼Zí#-´^vói߉qí»œØÎ0=麄bB2 Á|9˜–'ÃYs óêYl“iQ&j•R0û?'s³Ç±ö",Œ=^,0%à“‹ y#C4ý,I&Ä!±×• l„•ªI·äWd!”HNõæ½m^µu"nÖÖ;UñvÚôŠÒkZóÔñ÷½üõKÓ§vâš¹s3l`ʘ‚¡«F”iZR“dnÎÊÚe“’CX½jŽø¯À˜ñ×D6èô™¯ìÝÈtG’¨0#°òØl¹'£±áÉê—kŒùØ‘NÖ»h#*˜š¿ƒ/KÆ_‘u™ Ù¯êéIµ†j¯Ú×]¨Õ“j¬ŒÃÿ1œJJ¦Mn7 ° ª&ŠÄ¥›V®QJ¦šM--×EʸrbK#b±j-üjäcZ-¯(Õ¯ Á£x ÑøÕ ª‘2b…¡‚ò.p+’€Âv¾X¡ÍE™}øÒÉ2ëµÛš ”’Ù)q#5Zq19çwdHÂ}Èk²Þ&BJ#-vo–‰_%Ä#1Xau8T[ÓF¨V.J[M!B}|ù4ç#1'=M‰pŠÂ>þ ,#1“Äõp#1vN.쫚|$ Ú¢ÿÆËr4¥'^+¤6BÝBÒ˜@á‘Sñ*•FZæ¨CZ#5…^¤UÎq 4Ë“ E3b›ž>ª=êú›ûåÒ¹«•šk«»ci‘Q¤Ú-Ë\®bîÛZݪ×Ð7õo•æ:F¸ÈÙ£|°böxË–p3ÕIÖºæâ—\ä'z@€;Ñ\ƒ+*6ô[å>žO´8< ³–E®ªH½[DY¸ÐHEC¬°7–³Ð3_Ë~ÑLâiŸ–>ÿù°€aE†çH;_f@flÈ,pÐI×îù1€Œ#-¨‚†¡+úpLÉ2oçpúo›b›Þ¼j&–|ú\´ü|Ϩ›<è±þ Ήš§4ýý>’9B€¨¨9¡m¾¢¥Ž>]˜Å’O—‘“®²÷t…æÝ›?¯â9q LˆB?@N.†ÐxhSTSb—˜ô^—Ôâ;×¾ OA#5Åm·Ýݵʫ]6ÛbÖÈ•*" Ñà‹0/¨"!\…Vš†3ßn0»ÌËõ¡ƒôq¸Á™+£bˆETcY¢B‹{/Öºê‘Èï:“¨úì{Õü¾ýËÏj±#1ñ'ü€t$MÉêOrQ¡Ö“ÄÔZprk/ê‡@ç!$ $Pâì¿ØoAžáD÷2Z)-‘]]#5;OŸcâ}%Þ)Tbb³ñõyòãØY X%ès–RzŸq´¸szYÖ] ­Ýóã¼=§ŽïœHŸ¹µ»m+è—ê¢î[#19eß—¯ÆXþebäT܉!$DêPa·˜×Çe-d¤~ê’]Qdª”Û ¹ªŸÞfR~Fa"„¿×F]Gûh†~;W.awZ/ßóÇ©usݪì•L#5e,&vC#5Ȇ´(~„ 0º¨(ëGý½¿……]h©iRDHAM®¡Ã$UPÆ#5óÀ.[†YÝ®¹Zi¶Ód®vΙΒÆvÁ#†bY„dÈ‚µÒLÖ[\K‘\ÑoEÍÏŸkÕ/buÎÝØ˜éÆ#5Ú¬ÞQäZ5#5ÆÐØÆed­%¤jSÚ­uj0ˆQ ¯w-GÉ®ùv®VŒ[{6º;7 i³%8ÅÄH»%Œ&7©–û*ʶ‹nF5îÞ*M«Å›yŒ`c##9ÊåmX1#¸j´¶Èñ=#1-µƒZe±¢IHÙX!ðBdo%ãÃ#1²*°Æj´X›ÕIë*À“,¶ÐuÔ5¸’¬ƒo'.î®k–¹sk•Ý+–åQ\·/†ñ‰1íiFnŒ¥,²Š“fDb¬)%ãk#1³•¨lé ¸f5iÖ Œ&Di:3©o÷5˜i–4ßÃZ±1£¾A¼}ÐÒ'h—/†6!îÑ Ž17ƒP$µŠVVÚ¦©ãˆ£g!FˆA¸Ë+ïÙAýiŸsêÍ„ë P¨(eù´>`|ém¶OEI š0Øž\ÝŒs?)ìTOeTˆÇñB  bhPIEÕWH¨³NºÇj²Ý盯&ƒC#1 JÐ")ô~Œ¿ž’˜F:æèú{2·#Ÿ?{×ô7‡ŽŽˆ<ÀQìMÞàÜ~úH}#5AP­Œù=¿ZV´îFåæ¶5*Y*6j»TcMD™/æUF5ŠwjÕÔÛ±‹UÊA¡X@"˜J 1²è +#-–¥IIGó–OìOဧ"ôR*BÎ"BJ_'ÀûÞÎ…€×÷`‹ ò–qœðû+·ááV=£±”XÁ½Ëíó3\ÐóÕ1#-¢1U„ ƒó0-n4Â2Ä’üø(…Š&ч?é¶&0ˆ5)ÙÀÕ“CGãׯ\zM~!óõxIÀ|ªlb~Øåñ½UD$E$Ÿ[’åHd¤”›%‚DH$ó·R•,Õs\Öñµõ¶×š™E²V°Ù#1Tm¯J껀R`dÒó`™¨THR@Ý$.M¶5y»¨Å˜˜RMµ&„c"&šÔ¶h¨YЪˆB(E=p‘ç¥cñåÔi¦ˆœšÙ‰&Q^Å,BìňÑ1è[DB—$ˆ?) ¼ ÀÁ™a˜62e€eÄ%I\4DcMz@<ŽÒAÂh"ȳ8¥¬ªB rV`Qý‰´Ç#5ÆŒ…á½aÉlRE°žõøIrD°ùvdyyÓ°g,_í>î Z1”ª¤g·ãîâkìÚ‘zTçT'A¢â¡lb3`1¸·(*Y ItÉüK³§úKø„^¸)Q׿²€ê/æIöô‰ÁÂUÌ“µ*’Î;7 íÒú|ÁÜW“-xç[¶î)(ÒËJTÕcj´ÔØÓUšk)䮣h#4k)"m»5ïú}oË÷ÿ!Ó‰˜þž6 D ƒU¤øÞÜ4~-RŒR®ƒ0õì>ï‹gPû§SƒÕùˆéô°ýj3‹„é‰sfÓ¾´ðèú Á–‡>ZuI‰¤Ëa#5)“~åonÕœCÓÃróÎ*/,`Ä5¬(BA„:cZÎj‹}¡ú;¡nÌÉ&42m•´PËZÇ0àhquDA‰¦#‡¹1#±¨ˆ›ì40¡ÊÉ(“mê’ª†ÀYˆä>@pø(%iê|}#-!â*¬6„ï@ ?¼Ë4šó€ä ÜnÆPèõ:x ¥ ÅB8O*`‰ðD(„†øVšš2E#1Ó -E¦¤!l ÉþÕØ4p’lèè sõ/S<GIɳšbÿ#5&I˜qIÎÌH³{¶¨¦#-ŸÄÔ±,SÁ¨{$ k§y Òdƒ‚Ý •v|ãJèQ¤M e`i"‚ (@Û!).TßKTQ=#-(cî›oÇm|9šÏVƒ„4r#1¤3ÈA0û]<ºµ!×þP#-d8qTá“ú¿§“m*ɆÉ48¶™0xLQÃy³Ô¬vã:>£˜ƒ:“놬}bd}I`õ"­÷&Í@é\v™(|þA©8ˆ%x–i#-/¹¸yˆH’h‘¬Ef -II“M3i­¥©-£$ÆdkÞWA&Ì®We¥Ÿ¶«rÕ3-¦ÌmŠA¥5’kei²ÆÛ+lµ6ÒÊm¥™–ÛÅÖ µ(ЦÔÂY¶ÓDÒÙ¶-UDe ËM7¾«çYºÛ~ïaþ0£ê~’ëφl¶ZÂj#5áG G¶-ø?³×FÖæÚ5­u9µª*صԫkšÝ›Á«Ì*0mûûD¼ÜØH$â„NÔë<;8è‰ÄQ#1Ô}KnZîì[\Ø¡.¥ùí-í¶ž(Šý‚Å’~ÓHw G*(¢6H6&œqI©¢0`SìPañ½ÏC‚}1qDa>\vG® ‚ŽÝçà˜#55êUCÇžŸ$þçG Àœ@ˆ¥@Ý<4}ÉÑ#1¡üL@È÷ÜHA1!#-¢/d#-´AR¢T_Âr#5—µ]ûDža{9Þ yåÇ,dnß¶æÌ Y%‹fMø"Îÿ*‡šd7Îð 5Iê# [¡ÚPI~„µ£2ø0fωÑ6?©HÀòRÀÀÔº…®ëbÛ‘óž>„™@(iæzÉ”„‘y Ö£D(äúZÕV€g橳ân+'ˆ#-œžÎÀM$#-CQ¹6â"‚Yi:mØ+x`8” º6&×ÜÎGÄúÓ#1Óù.‰çZÆÔEihh’8FÔÁBžºÕ0Á¶ZŠ(°ÜX»‹—-^o3‡yÔÙôAh÷!ù¿AuºiF}zäcÑ­£d~YúbŸtŸ.…4/}†[Q°š{p‚w:^mþÌÁy2ã,ªhdÔT0tÆÆ?wDÉ!„ˆ¢Û_ў͸ÛkO¯ÙLßEI¾R‚ž€#1În»­ô 9+ÁS:~£r˜è}~º’jÀ¬œõw–¼¤¦ç—KÆõÚí©O‡ËÀÓv­CèMÉu"†PX‘“½…"±°‰ˆ¸,}úg8~~Vk\WA½#-2OwúÿäºL Ù¼H“€ù8å-嵚J”³7åîѵñÕÊ÷îñÑÄ4§`H5Ii¡Ýñúÿ&%’lÌ÷bÁ‰ñdhzpAË r„zv@®È‰#j¼hXÊÃI@`1‚Q°¼H:–@ÐMEÆ„ÖZ_vEzɶSZ C&z*´†f4$.ß%QG(S „ÆT7„b©ãÐîàOã©‘ÄTÆê5s S"D—ƒíˆ: y ÓŒÈÓ ‘R#»óï÷UÇKÍ%‚ØÔ¥éœág;ÍãCkƒ.&’5"T)P¹¡k©pëb‹Äà' ¬J‰d£<Ô 76J§ A¯é Z/9´ïïâˆ$Q„ ˆÒ€Ê4Ld Ȉ*EA¤T†‚D¦#5„ƒ)y7°]î(ìó#-L6i}~Ÿ*^—بè;?6ždÌ#1!ÛP¢@팉L­îÝéŽÅ}n½þdOª$TšQTbB©RÙžWšªœ©=ÉúSL¼#1vØRM›¶Ü%¡&Fš`²VôõZà`tc»|Ù¨ Ó7ad¥;é¼_HàNúé”Lá"…I“ “d«Ý®ci•ÎÙ¬`}MýYž8 ‡EEꄤ… ÝR¦PÉ,¬J,#5BA@P‹ 倸à½è”ºo&òéL”…Þyå+'sB"†¨ªŠ‰wp²/&55CìãeE{\HˆaôBR4Eí7/K¶X›ä»0â—g3&¨˜0tla²_ÌCÑÅ4PÝ#uƆ£S'Ú-™…{ŽÁ– ?/Ÿ°xN›ÉèÆ}äzmŒŸ˜D}´‹çV8êÓm²¡´®[˜‹dP l6õ¤ýû»™Ä¶@ªô1kgðN/7ˆ©¢(›±ío¹)ø?ÏÎMa¸Kí¶v‡äBIƒŽ#-X!ÙÇv´ÌÖ” hÊ@j¬®£n·¢H4Î~ÍP×w»LÉ®ð{ü3‰´¾×ãìuÙ3É=@>ãÎS@A'F#-…4è ÉLdM¥çnšåyçJëÏ+wŽÚëJ³V’Ú¢Ö¤Ú©eãÉE´ò¶òít¼Ý·%jD °@ú>ž³¬ÜQAlöÒ»¦©„Q¤PG`Nã„$Œ@†?Ø&þVÚþ>µ`€»ÐùlËi ikR&&tdß°µûz›w{o]ecwEÂÓ.cU¶ŸX"5\DÊ™Z1T® ƒ†‚ÁI-˜à"eÂZ]¡š¬¡Ò2®ê‘G$fˆˆ†ö"#1Æ@ƒŒW¥8…ŒLL6ñéšÕ1êk7²œÈrÈÓÈKwL:å¹E®ëY«!PøÍC™Jrë9òàvš|2ËÛÌ#-9#5'_æ!=ªŸ$œa2Ô=;êfÑ:O”½ÔøÖ!V±þå¶óx$˜—²ü £K­ ²ñ{§{\ÏÇ{š¦M’¨0‚ìóÀ˜^ßMñ÷#5ë¯ÌêÝ7Ã}ìÈz &6¢Ð7ª‚ ¡#sÈ ævdœ²¤Åí¸ºãŽ8¸DY!Çnyß·£<ò"[N3"ÁŒ©"U1H #1CLŠ4K&Ó±¾¢(:g†/Õ3”i]'ɈXÖƒ=3Ì¥ò—{}[bÜû#1»ßCm©¹üˆØïFãž¾Dµ]†Nbíävkhd uù¼q$ÿ«Øšû)Tªr(˜(ðǪZ­‚÷i5Û÷i䡳`‹é<‘÷à§H\#1»WÝp÷Å哸3#5õýB4Ðm¦Ç1SCKb¼Ä¦âçCú©b&þ!K'£!26Ǩ!”!¤ÕÈâxܺºœ*Vr&ŽçóH²|¨VcSWn{cÔLN¨Tb¼<è\ò>þRónÇ—Ž c|éß°LŽ]ÉX”;|Mƒ²!š´GT)æÁ… pª’øEiD56?îÙ¦Áó4½Ýû/pÓÉ|"z1ÜÆ0ø9×EHõvˆÀD¶¶„ÚÑ$Z±ZŽ DÖŠ–*’Ñ’D¤$H#5ÛÐRÐEä¦Qã멟1Êé€ä-Æø0`Ü&eþŸ¸¿^ auAb ,Zk€¶°º#ˆ}ƒÒ0LLxÉ©+,E 8@n°lP±Hà¤p˜”M$…Š„³H%ˆ ˜ ¬ Z&™) “(›[ùIØynÎGgu«¬×¡ävšÈpÕ …ÜÈ4Ô3Jk r""ñdUY{ÿ‰ÄdÑA*«;Xµ«z¹%–'!=¢¾þ)òÑ"ƒ¿ 3Íù‹Œ“•ÅùSÄ K°¥AR‘*Y«½ 8Q M¼i6ÆG#5‰ gŒ#5†ËP‹~Ê|—'–©T8RcB€@;N»‹‘ä$µ[~Iy4r5í‹âÐÒP×-%3«5ÌäoðC¼>|D%Žï— Y>’‰G¡568ø¾îþÊí>”ïdLI€ˆ8wt¤i"7'iEZI!®;Zë›wnmÕÖmM-f•¬ìêÓI²KUM5¶ê¶,W)—‹——vUyVßfY±±DE‚¨2"'u‡ª„;ˆ#~_«†FÜsàFÁ³8#1Xô݆nnÖŽ‚°ÃéJ#–½ôZRBÒo âÀËG×\{ŒûÚãÝiFHH½õÄ¿žN@r#1CX,Ç–Õ&üÚ‘Aª#5Ú‰&æÇFáép´xÐl%‰m03šÐÎj£DhÕ†ÃÏîv#1YÔÄŠY )AŒiòã;°½¿Ñæ#éhBZ ¦[^<íÀ›³(ÈÝ ™^“½#1á@ÙÙÅñ7 dÊÈCÃÓïÊÏlåð¯†áô* TRzKœöøpRÎMØñSŸ³`[Ïd¯—U›”B_’u¿“Àó:N[oæ5ôò¿ök¶Š£+0Ð)StÙ›ÝäÓmdÛ&!Žk¬bÐÖú95ÚC“¶sîÍ®x T)4s¯~Oû¿óÓý¿‹}VjB…,æTV¸+j!̓):.. ) ‘ø'˾‚Õ!Hnô‘BA5Ïeaít(ª ˆ6­±0¼ä!ùeï´HUƒ}£eưֿ.f‡5 „.DÌsA°­(©±eà8Û±dˆ©†õ 8$HÒ Ÿ^yáµvÊt\NÇàÆy[Lµƒï³¡Þ(­E³üÏŽMÏ|ð.ÌLnq‹Ðc™‚<øtÔ–D«Þ×Ù9ÂøCÙG55­K²¥ÃñéŽ:Ÿ†Š«á&á’Ìæw#1‹6›¶bÛaD!|ÊJºd°äÈÍÕš\¯À€½á‡¨) 6±›#-ì7³SzÀKÃPƒ¯²âŒ±F4ÀE&ðáÁˆÂ#ñƒ;ׯ[m¾Þ¸Ê&4€¡æ] ˆöÀ²R`QŸ0¢g’C Ãè>¬¥ƒ]t¹×ô‡ÜÜòÒ,Ç$ ¬‚ó¥9dÏ6PŽÊfxÏ3§Ž@ñ8=Ià%%–ëi&bÿ–¡xFQá§õVÍZ%³³FÒ8EOhÑñ† 0,aÖŸ^ð‡–æìIAµõå3½rRT%_¯-U—€u ‡ó°½5ú£å§/Z~Db0Á$¯}ˆšN‡aÛe†g%3PuŠƒ{7\réI%©R実‰bñ¬Z²k%EhªØµ£h´ZÛ76º!¤ÖŠÌѵƒUv[kÅ‹ERD jÖCž#5wš¤ðýûÎa=qªVÆÌ‘F"L‚EßÓ#1Ê«Ñaw›E I+åÛ¸âu¶ávýøg!7‘nYÁ ÞúËÝ;ëÞd(]Љ6+=:€\8ßipy0 ÷o>íü8Bn©ú¦þ—>ŠcàÀ=šüˆ!¿;Q„ÛdƒÑžû÷V¤bº9&<ÉÕï,OÙé™à@Ýäz™_¹î@H!##1OB¾Ù‡n6Þʉe›Ãr„ÁÐþ®Þ~”ëpVžþ\9²JŸAÙ?]!hŒÊwnÊú[¦4=̘ƒõAÇ1x÷lZúR‡¦‡²·*‚ÅÀü¡s‡#1ô5Íí¶ŽQ"C᛾H,ŠZTÕ+f–-þí¬×]0JlêH‰\»#-ö“‘lcȃÿ&ëìv3C/}(•|F°2zÛŠFà|ƒm|FÚøü!“䟫¨K†¡fôɲR¹™d-§X pae…ˆ)•Åö—4¨ƒÕÌ,‰?Ò£h(ÚVÓ`Ðf$n~~sÙnÀÁ&dêIÃ7†’ ¼ª”F]Ê.¯û=¾#1÷¥žÆ¼½\ãtg™êõ¦õ›]]N+¹ÝwoVùPS "ŽpF@|¡á^x½Ën½¬ÉQ©‚¸ð̮ʙC¾GqaÉùšÚ¥ÅªXG*"a,µK¦LpzÈQÃGXfx:ª¢ÐëÚðg¿²~_TpôÙ‘bÐ1|Ÿ²Àí\°X‹1AUG&& j †ƒª¦£N ªc“£¨òv€çTŽÐð<ºMü³1TB{zó½—Fþ£c¥²=x1$®µæÌ2b¨UEfšQªº4Ì¡)Ž0#RD4?+à75Ã)>„”Ôœ#1=¨ÞäP7ƒQ1°u¢‚„U5 ˆ ’ˆ`ÆÖ¦f[CE£]uÔmé¶~î"çà>#5”YÆÚäP³B¥cÓI¼d048*j¥â JihsT6æä–$“•D‘DÛÓA,‚?A©‘†”ÔŽ[ˆ)²óäÃé«Z„¡?gŸ*òíÝ“hÖ¬¦ºUÍõÍÓ"Ê’­6UΓ#<Ýu‘ˆ¨ ¤®WcqjKß66¢†Ó2žÛ­Ù òë©{ö¯ø fšKI‚#5ŒÍÕ¡Áºô×—’½5ç]iRML¤h ÔÚÊVXJcc¤’M#5k,Ri¶fI‘¶Ë+/]vòí͉gw75tF3u×-¹§nÞòæ¼Þw‘4R¢¥¨f7-ñë½žÌØdÒ "Ù…,¥ÒƒIyqE‘’¬<·š(cFë8¦&Ø j°xN#1Z´ `´E@ì¶¼WNÙŒcnCnéÙ0Ö¢­1Ɔ£G Ã_*«XÔ¦-¶°hÛVÑ'ËÏmG6›J#1À›‘‹MpÌ1C7Zé€iªcMÇJ}"òõo7jÅPHV½mt±E#_ÊËIká·IíĨòÇf°ÅE[m7mºŽ¡W4ãVY  [ÂTDrÔ,‰vHípÛHйb ´ÓcÜ"m¢«jU¬¯‘Љ¶Ç‘¸À¶–2D™— -ÆVz d ¦TælcØ2eˆcC˜[S³~¼¹ÇŒŒ1¼1 ­Øe#t…ÂfH0jqŒØPÝØë*•”w9­5¬i¼õÚ›Z“$_[Qï:¶Í>…o£ÃÑÀ#ÞÂ&ë&Þ”°ð0œòÑV욇ûÝêÕÜô@oø¶¦iM(:uyCLòª>Ñvq>V%’<5fæÖуŽδŒ¬iŠá°#1f1m˜ÞÞªƒ©³Ã¥ˆ0%#5O€È‘õ9Þ’#-àݤA~£Üs­%~³¡ç;*Ë#H:Œìhfzáå¸û;Í\‹mÄk!7@#-½®ígyåa}ì\ç7Xc§‡¯+£ÞàÚF5G·“†”ÈVrÒ²ö³F5ï"®4°xÑ„pÕi*›l¬ßKÜá¾7Æ¡4”â—8zÁ&s•Z„[!T tET‘ÚEJÁjhßB¦&¸5ÃFøDݾüöýÉÑ­‡S“žAcn ăÝ”@Tà…¹¦Q‹LMƒÔIÔžŒ Ìª·lc~I77ß)ßO8a‚ÂèH<ºE@Ð hš(ˆ‚ƒBZeTmtÖ›yuå,¯[T­w­FÓ ÁŽ¢(#1 ŠDi$°ØÁpÃd¹$°”Ûö„˜ŽœŽ¾ÃÖ‘ÛîÛ΂Q¼P®ß§dÒéD¹ÆUO§ì£36߆òƒÓÕ¬8GÍâÄ?l´Ñú>»ZX2SY=ÿ)®™É·umà1Gðôî¼}(ÉÁý#1+&ÒVýŸºçÕ(á׋^xìeã»ñÝÎé\´O£Ð””Q œŸD%'­Å<–Fö÷t‹—­ÊótÕà{Wxîõww†•×fDK*ÉŽ½¼’®.Þ] ¥dóë®§4¶ýÏfîÈu˜ˆI!Ës36äê6oZ®¹ãÔÛ„˜F6qÇ8hPhÂ,CíTBY{ŒPÚ‹PÅ„3s²-£G/K£á®F±UÜ·/:ÄÙÛcy|MA·ʃBz5×i„ƒ·Íô#1aGò$2cò7 ƒƒÐyM⻥¯Ã«;:óMÏ#5òá‚à<öͪëšãŠúŒÃå"ž~”kN,¨‰·#-c®”I GSÕíáš'ÓÄÏ]HñÇ´k†Ï~ úþÏŽ„ÖéM¨#5kf©l#5ÃÆò“l*53ŸP'°=èž·î˜õ²ãë‘¿j¯šÑ¨ÛÒ-a5ÙµÖ¾añ‘fˆÅ.£Ip²(¦­‘bý4ª#1ñ2PÒ\f”Ô¹V‘HÚCCµZä\Õ`+n[¦Õ&µÞ;-£kιtÕÛmÍ»·L‡:¼VŠxf™cR6‘´ÔÕÎ;ªî걪•Fšôµ^5¼›ZMÓjìÚUמoÖ4mmâÚ*½óKhIB­#1/É‹hXŽiBšª•Vì»S¶Å_Zr-ÂÊ Cšd»kvUSkÏ-³ö…³/U€ À#e†‚P•YTH…HNj»^Ö¼&le³Z¼Ý¯]·Å徆Xjˆ¬)†TaD2ÂÅFªS-H¿ÆuRE,ªŠÌS,ÓQTmF¦b£cQµŬ͠Ñb3T–K2‰’DªQDmLɲµSWjíø8«¬…kì¼¶×ïz½WêýË$LùêÛ^zƒIµEa*CFÉeùõ¿Î}þ>ŒÜý |Öõ|{.Üþï*,oŃÀ’?‹D4w,QiœQ²A$‚ Í +ªíà -Ud!XÅ(èD‰î1 9Ý\‚Û‘qA¼®¹ˆlÊÈ„¢áÝÝMt,Þ2Ì&“í}’'ÂåOˆì}<šõM¾q$!èÁcd ƒÜaŸ:ð¢™h³WZ$2£IêÍ6´ðfÃß"¥®¼Lیڦf3Ÿ…–61Œ®æI¦¡1 ƘœjÁ›SMÑÁ&˜fHó‡rnNŽS)Ä”v»Æ#1ÂR¥t—ceö;Þ7B˜²^l2æyâGfÎù6´ì§Á¨Kk@üç†kFÚëæRºáèöÊŠ‘fU§qƒ K&b]ÞÏsÁ2dI$ýCÌ¢HÆ ‹Ú<"ô¹Ë‘ŽÝ³©Øn¼¬‡b‰µM™ÆËgèÆÁû|h¢±áŠäÅX½&eꕵRëRõ„àèNO‰²Å„>Ñ í#1CDNP¢ )¶JÖÆÔj’ÛçOÄÖ®•B2$"[û¹^“|EsŠÉ¤o$öîXµ&Õ±¶£jåk^ßæÛVîëÕÊÓòGqÆî”ý8‘T 1µùٿȨ‡(^ÞÉ{â$ŽÝ-’Úk®Ïn,m¬fU]$ d±’ur`Ì¡C B ”ÆT²&ªœÃ®daÐ €‘s­·«jßY-­³$Æ,›[W4ª/—¶MãlîÛˆžË¼UÚDFÒæ»É×?Ï]3×—jêÒH0‘‚H$à g8ÒðZ8ÊÚÅR“K›»3w%X#D•¶‚!š°«XB€66…*H€D(T(V+¶-Àf‘¤#-‹ ‹å¡ Ÿ„)`Þ°(Œ#5\D#1FèÏ äe³»”KÀ*¶SÄQë%•roŸ;ßÎPÑŒD€¥!yÙîtCŸ¨üäŸf­ ÂÁTçÐÜmi1ž3Ó´bí˜;¸ižýÆùçtš+IHÆ3‰j7ë§¿Ž|4@æt„#-dJÕµ¿5x“#-”J·æ­}Ð\Uq"¤#-7þžàòÓ?¬ùÔC뾦­¦‡ëÛþ/åb^†#t?Õ0~H×®$âp¡ú¿¶yæKMèªDªaê¥e!DXcÍ#5ŽÇ+Ìf¡„zÜ#-fиçC8=g-#5‰ú)›9bñ;}Q¶2tll¡ÀØÿ„š!ˆM#1@d’;5Ù²ï ÞK™z<õve‹¨hOÜÂùÅ4Èn#5 ÞЈ†%Xiƒ„;Œìe”@HiââÕq”Nê²3eÙ£d»ð &èT Ä`‘ņÎW ±Z$oÁD@6.¨¥T<ŠV…F ¶Ò#-"W;XX@Í XŽI’dØ2,ÉU/?ˆȸs+Õ¯Iß<½”vwüi$±o×ÇÇÖ©àÕM’\>PæÂ7dm³zµàIN1ê8w¼ðÛ“OSTßÌý¨GÏž#5û¿½'ãÊ »«–ÖéE‹UT‚©‘ˆu%#1ÚD-t.ÿeÃÔÔ RB„EV@4=!#1S³øb{=­÷­«Ý躮ê¸í[ •#5Œ_˜\²¡û¸Éô1H«"Á h>yåçurèËš×#1(ë»Åä6%9¹—›ŠÍ›T’6ÙijOn³l”X¢™±ƒÆâh0—6år®lnã—^xËɸë®ÙG:+»·H·‹sUã–l·“Ì·W.æ²XÐíÞmSkFGU¹¹k¦Õ’Ú5)b³¹M™d²xÛšwwFéÍ]Ù5ÉÔg8r®Ùs¬¨´hå`JR+P$Fˆ+E ¹²"YTjzà„vA؉Cûb©Û»×À(ðí=pBÄ<à8#-ïSúûÃÂ^•¶‹cif·$­Q­Ö«ÌD°¶"€/¼SÉAN ×üðÑCÏø Ê|S¼pAÀúÊA¸¡å@,T9æû#áç í#5õßi·ákÌ¥hÎ#1ö€z³ñPûŸ#1Éú»ã¨Üvðl#-öÛ)‚6µL̾»z¹U­Û‚5EŸù —nÿš2¬"¡«áóí°$†DŠ# H* b£˜D2"{l/´ =h Ò¡KÉ®[·:Ôã®î¥–eT†…¡b3ÆåÅŸ´*—/rÉ­ôûÈ‚´#-Æ' Æ!HÌȦ‚K{4v‚„bF°I, G$J#1ô&46„ؤ,kÓzV·©mêcm[ÊêÝ´³è×™—ªÛµ®–5S-•ÃS²ð3³0²Ÿ1ÒzaD¿‘˜r…ŽÚT!ŒV)D>GíÿÐöÖÃP7‚uÄÎ.õUW¶"²)3V–l¶³MªmŠÚU›,G‚‚²ú‹¢èö@1c;ÖéSjF–™3Tm¶¡B×è<˜Æ'b;ϧ–HH„š™iS3"oÜʵý#1µI$B0Uˆ` ¸¨ ise©TóšEد®(ÈÂœàŽ·(SAvò<`I6†¥¯;êÇ¡»ž¾ï†íÿ®èŸBþ Œà„ ‚Có’þûþœ¹—€Ž˜n[8Øä»…ÌÑY¯Å9X”)R­d$içy!>ûEÕ§Æ@:º1±E"’N ~.«q`ÃöÀþqоä õ¡¤ý‹Å#1'#ÌÒQã‚ì?Š•Tø!vJ’1¡‚’%•–Z)&*,ÿ„H‘6¢HâD#1…D›Š61#-º:`0–ˆÈ:6–#5`PˆÀŒ#1ºÒM‹[€l¬Ñ!* Jè £oŒf Åi¶T1¡¶f:®ñ^kx£[>Ö«ÅÒ®l•QÈb¢Z(­ˆŒ 4¬·lÒX’$–ÑTÉ8ŠÂ‚«àâOoµáPîÊ«úCI¢ ¦!j€R@)£&§Áw‚ša#Ëzý"´á‹FÚеmÈÛ ·R,¶(S#5°aEè`´’ÞJ®«ÔZ¨­[z¶×.ÜÊ)R’a´ÜýºCfØÜã¤5Ð!ÑWaI¿~÷h\ ÆÍB?¤wõ!Ö‡ØÈƒè&oï=pò_¡V«"¢ êĈ0°¨(V4Ý©4©ë ÄÐ’%-R·Fékv§¤ó󻽇Ҽì†ÂF?#5T„ŒI€©S»Œ9Tí»U-•[4«§.꿨 €å¢2,NÒ@ŒÜIKj`Ù‚Õ ‡eŒHÅV‚h¢Üí^3Ýxf©Ôã 22⿦¡!#5£Jn÷'ç’¬êŽ4µQ%7$¤”Šn =©?.”(ÏÑAYÐÚâ{Èk*Q¸Ò{„º©…L›®dÚ®æ–FuØž—VL“IŽHÓ#5ë1àÔxÔy¶ÜeýÙF×÷Øm‘`*ãrxvpw}¼p>E¨’®òªÛl­7»L8ç5—õÏR]44¡ØCÉ")ž'ڌچ¦x}¨:`¨§ìÐO²;Qº¾r¢é g]‡#1ÛÅDÞ#ùâ,€€I쥦(€GÛ*DD‡ˆA’E-°…G° Q/Û5[t0kMƒá#-qo¹KäúÏ9?šqÁ·]ݨ~ÿÌ“#½;Só„ &ˆ4…ƒS4lý½k÷³ ¹³çÅÕù‚©¨~ð_‡9 èŸf2zîR†¿Û2=·ÄP'ò#1( m±Ñ}]UèuýíÉ÷ –—oEÓ,y†lM åÓgî¬hÎXá†D¡¦TTvÒ!›h7·ü­ œ ”ç3Î Q«aØaqïTh¯n-T)ˆåˆüZð:øxLœ)«S|J‡5Ï+cl]#5 M#-ÄÄT¡ba=ÌÎï«Ìã«·­#5šIÁº^`ÃAgªæ®c›£{:‘ޱÀ¨·0´U>rL#îàŽTæówr^â¡ßЏ\Gð}ðôõü½ÓóL[5–m;p•´´¸·»ç±Z’ë8¯{/åÚÃz4È Á]›N8(&®šZw!d‘ÂÂ^Êô’Ir¾ûÖÔÑZò™ææƒDXkY€i§ “I£Z]ÏŒîÈdšˆ;3ì—ÑãÖç»f:t¶¡N1Xð„JdœÑ¼T^Ù´&#5Ôbbiè {:ÃÎÛø½iæÙ° y/äv3|S{DZ%£xa£°ZyNkäÉã-±¹ùÏ¿m‚œ hâ™®¹üûJì@óÁ·ˆÀŒ&í/,¢þø"kžYÌFŽÌÀê’ÖëÄÉ™}YƒA´>½H=ZÉï½Z§ñ\øì®ze»º^¯×²šÉéÓWÏÙ=#xiѸN°#"‘„hx•*GÚ©FÐ Ó̇#5Xà®}ýG˜öž'“+Ž+ºÔE^w¼GÍG{‹ÚLZ\s„¢AU@ÅztnÓ0%OÖYé®Ä.YÁÔW/fmò/ÈkÄä½Ä|Í©˜›`zRPDÚAm²‘J€_#1æ“#1~°2¡òÒ»áÁFöÚ‚Ò+à C¤=ç¥>¸3AE˜¢€¨–µH"f÷ÜuQÙ¿“¾ØÀFÔÔ™á§7¾Ë¶Öê~¦ýÙË¡÷°ñuHæ¨cäÀ÷5Ƕp5$¬d’kzÕ~¿ªb܃òŒK–ý/ôÚ×#Emä²m°\Aöh øbµ¶«•—5vô»Å•û_˜ŸÃúŨPòCXUTObQg‰Î³œ=»Û#1X;Œ£hõ»(™B#1ìT–Ù#1!cµ;IFšÏ()ªÚ*‰lؽ†(‚¡C×0æwñû5κS¾G3w-*zÆj²Ó‘4óh®öÜÖ•è‰ÛŒõéÎæq›Bœ`»íÛ§Be#1²IÂì9㕯õ+£‡ªYƒ["º=–§N9}⪞‘¦Æ"Ä"Ç@Æ6¾„'™ñáJ)Uã=9íë뛂qëð“yÿX˜=ºû¼¥ÖH3whð*žÿFoóuØ;¾<¬gQÔeìÿ'Ãó艶§Ù(p/Í.Á‰‘~dd@¼T’@À‡Ðb_㘯ùT`Ýò"‚$"$ "@#5ûËUÐv üxsÁRµà}Õê=IÌÍô™F”â°E#1ôqbG |GqÜ}…Ìô›Xf–à0r:_2Û…t=GWq·QñùÜ;7Ÿâ6„äc¸;€åäâ …+YÌð Sj#5ØøM,·Ñ›#5d ùÀà!ì#-Puð¢ïÚRoŸÙ_VøW#1üN'¾>Îj{9£ÒÇœÞôz‚e‘N\òlYŠCYÌeŒ€»ÆHD  }€ö6;ÔTÄE„O "!#-b@0.Æ¡nð¶Æ{78ê$ÑGPèû\fã¡ê/BJ„,›ÊQ7! 8ƒ•Î-ëåÓüOé÷3+)'²Ÿ°¦4^@Q¶%™ê…£iR@0t~ížI’¨#1 ÕHŽæ(€Ôyp7Ÿm!‚/£²â(o/𜕱¼T¿k”¨wN!©/Ê.#5*fmU™×«GNõ=ãÈn3CúH¢‚ÿ}s›ê„a µ5šÄ«¢xE)¸_o$8ý·Iʸ:Û±ŠŽ(Ø\DŽ—}ßï?up}Ä6¦Ç3Š1£Ai#5í†ÏÝ„ˆëC;‰øûW_Çh†€ÙèÕ‚ÂZ¼LJ$îÆ@8ä6ã”,E[ â^sqŒe]¼tŒG¼Ñ+Ø1 Mt H#Œ§ÑŒƒöý{ëzö#ní¬?Dݘæ|">œ¯õýÆBBFÁþ·#5¬U1¸ÎRHæD¾Yu‰Lw&‡[ª(œp`©áß–}kÞáìb5Ù~> ~ô3Ú·ü1†ìœù…oQ¿‡oŸrÏ›tÉÉjÓ½wX˜ˆD I#1rËÃφfJ€1Ò…)Ùj+lCH:aúj1¦ÏÓ—JIÖpðÝ–ï—‚Ø…>”²c:W´ïË1ùQ„dµ"”Ùä®7v¹läþEÖ©4k[-›M5K5‰iJÕ5#-„YÛ¿mï:Õ$ˆ oC¢æ±rðí¹¿×ä}ÌXÝEUõÔ«@È z^ µö}Ý&ÛÿÇÊÖâ*ª2 ‚ÅI$kåÝ™ÝÃeŠ ¦Ñ(šˆ¤Äi˦4J1¦š‘’‰¡HI£"2+Q1RŒœø¼³Øs€C½üD,,U'‡ÒÀúLjqøãÆ}ÛeÁƒcµÒð6™ç››c[9º2iEHÂ4™æãå&iKÇ<ˆ¢i€w‚ù3¦_¿²˜0l.®‘R¦†t¨!dÙr‰#5¯UœhUN–Ò”m%>Ï“øužž, BnÆYÇ— [!tRÁÅqãÝ´‡;ý‚’¨ô'Uì°¿+k k¦“z©ÈCæêŠOMÔûXc¨™&Aá±i˃`Þjí½'Ê©v“O"¦zô‚bl Ìp\5œ·¬éöû*nÌå´ÍÁ—閵û‡ìzçKw9xØ6â—|-ªIñBL¿×þ¾wÉÔ¯™A©4ß7û@ÂÖUbU1’ªVÚiÆG#-4& ÒmŠ´Æ´ †Y“z#1^š/%»ºÍ;®¨ªå×®VÝ®ésuv*éíyÞFm£@¥hÉc1Ax¼BŒ©†2*B7…Ð0¥à(_…@qðR·ÄAx¢"!ôTmЩLHŒmõÕâ¬ÛEA@a»E"F-BJ¸résNës[m»^ß7{uù#á¾#-®u8\3* …f"+îx„£(ÐBØ2Hèw°à“EpÍÄiR©r'âÔN\Æ\1HM3LQu0¯ÚýÅêp³¼B2âm6q鬈f2 aBG"å9=ª’ýî6¸,7¢•Èc6Ä8†ÄJÛÉÀèDx¶>Ñm#I°y²¬b[i›4QG¨à‘“`â‰]Êà,ÆK°´«¦¥#5*(ë•%#Iƒd!X—©·’O=«Ž0i#5¨’hl„AZ?y‡%6дU!†ÓcŽB8 ,e–ì-•£Q0Ž!Ke[”Q•D Á«²¤=8ìPerªÓ¥.AäÂ6ã®6Ö#-!Cl"®- ÖJ²©nD…B¸VW+Ç+#1¦ãM”¡²¥¼ÂUF¡jÇ“#5¬Taƒ`À2¦¥ÐGz=ØæîiP%JEp]°õjh»¸–—-A2°b¯±Ñ©§¼Å¶e™Õ{Ñžø1†[J²YµvM“@UaŒ:`\µ“@µ¨O"ç)JÁV«Vƒ,!,%µØd Lƒ«F­ #1pºSmi<‚ì|$ÝÝwˆEü®ÑÌÎqQ&ÂY`ÊFή&ŒxÊÑ:Øè:âˆÑ#15M2:Y‹"†-¨µÀ=’!býðÚU‰÷|qkED6åH岇ãEªÔÀCœžåd€Œ PB$b1RS‚@K–Ù¥ì¾ù"ó¦‰LÓ×^øÂhNnº}—Å`a¥`°W¡O¶t‰^ 9忢°K”4Ìc;À4ÒACÚGm$(ÿ™™#b.îØŒlkuutÕ·¶ºéBšAl@NéTXÁ%Ê1f#1Û(¬YxR,є֨(Ù#5b@|!úE,¸C« ›šî™rÑŠ#5Y²P†3¹*SPRMuï†h¸kÑüþÓ)XLì)l¡3mMŒK³¼–5ÂTw²—Ûeƽ{•nvBEBLßžªf¤á#5È’µ‘ÀŸÊÞj„ÏêŽè7qŵ†÷©úbúF+ŸCiÔs}Úøè•ë#-J7­@áù?„P|¹#5¤å>ã^&J‡a—à¡Lá 'pÆá“¡%gÄñà&©ˆÑà~]×ÓÉõ4¾JÉу0™SI ë§ÝøÆ»Nu>}›;Aÿó)‰t öv]ý(÷Úvç`@ãÂØüŸ[µ‚.]ÚI'†ÃÅÛyÙ`o£Œ¬QFZ -7ÑÆ±?çâUxÇ1ð¡§„34·7 &’cj • WÔÉÏIÝ{MÁå! ”N£§+ß8BôWHÛ”|`Y5/²#-wNÇeš˜ð({` QDÞÁ7Ö»M•–Û#5@'_ÒXJ§fe¶HÅ<Øvr5;¢tÈ/Ð2`#2A5X¥Èñ;­·šøÞë㋺&>¹¹úYVb4Œ^÷:F²-ÕYb€Îƒ#1ýØ5¦#‡©ÃBµÄÚKõ9®æ·580Úq]ÎøÚæíØhH,‚È¢n›_!ýuzwr½º·$¹mt‹»n+/oãù^4T›ÚrÖKhײÖìKrîímûó[†×Š£lhår·JÛtÚÈ[si*ÜÛ.ë›\¶æÖåNïv¹Iµ(Öñ\¯yÝXÐ~¢-¥àî:¢èZÖ#1£Ãì êˆkù- C˜oÚËM…€û˜ãêÇ̶ †oÜAàE¹¥…$B(pˆ 9Ä=«N)CÞÿ2 ]hÁááÃWOË5ôšÐsxUD²ÑUøB¿H+ŸfÿÅTþ"5MÀ}ù©%¤L#°"šN¬QDª˜.ÇŸ*½)”‰¶ìÒ×qk®î;]&Ӣ׿ü÷¨¨ Ô² 0ÁÀê —` bª6µY+mﵡ[]“b@`:¶+yÍÁ‚¥&JݽÞeM¾¶›mjüMµ,£I¶$Ѱ¬ "ÄxŸX(%ƒDÔ:©ùþ„Ùú"ýTvê(Ūƒ#-ú x¦Ò”"¼‘ÇñË\o’ÖÐ8—š@ è/·&eùÎðÿ|A‰ÃÔnJɺ˜+í©¥CÛ¨fÅïˆ!Ò) ìm¯v»~ÅZí­{e+D²I,¡AcJ$Œ´i545¤¢±b¥()&fZ(Sm´ETVتÑjÑ6•¦Z–L¡²RED$@/ÞcÙkeU½!â½zêðº®h¹éµÅã»k²á†Phcm¨Ì¡”ƒrG’ÔG#5Ì¢ƒ.A€P%‹L Œ™R@Š0 9 £@Æ:E& nU‰° â°+´cRT:"…PTh%ˆL¡˜†d*J`Q@0„#)iT‚DE#- ‹Ø`HܾJ¡ ‚Ô#5XÁ‹>úýüîA~nÚñ*xÒè·MJîéݘÜ:ÓöUÛÐ-Õë»6·WSZ›,HAlb­¡Úª†âèác~¨Àc÷Ø~¹#1¼Õ‡? «Ó>ªÒç–Š)TT“’žå’+È-¦ÏàÐÒަGšw_¸qp )…4vÚ¡ú›©Xúõ††€–K‚Ñ9ïÅLca–¹¹D± Ð<ƒ~`B!ªp~Ÿú˺Ì{L†C%\oe€ »P™„ý˜h=ãÅ;€<#-z0„‘%¨ª*%hw3y+¢¼Ùu*S2…ú—zÖ®T[lV-­¨îÕp½X‰Q‚÷|V‘Oäöíæ éíjÿ+mJE™"jø8m™`#5ÈŸ_ïÓjÎH»8C³³>WV˜¸Ka’ßQø¦Qû1Gš%JÊfÊ”·§³ô3ïXÄ\ÌØ³žõ`UDŠaJ3Þ¿Cô_¡ô•. :µvJÇNøqÉq.«®#|Õ¥³º ŽiFÛ~Q×Ósm’'Ó‚a%²Îô2“ž-GÛ¿Ÿ$ë&§ž=˃|Q»ˆG”°û;ZÝ:PœÐ“?FËßÕïÁ{î¼²q^ÑvL_sßùwO'åä~f€5ø%Fµr,¾†q½Q>iŸ)®¡&¨¨êµÐÑU/ˆ@:§õGo}4¢ØìçÓ~ÖŽ˜¦ãÚµf UK6õ5:8êøÖÆã†IÎÚ£]uŒë®9æ”ìbùUVͺ„yDl>B9¸ŠÒÎ)‚å‡Jt/Ê8`N]•¿Ã¼Ûkñ§¥öº×/Â+/DCœË3й¨ÂbZ™ õ[Þe ŽÐœÔÏ_\(Å6#1ÃP„˜Ç;¢A.™~'!" <å·ÝÙÅÑ3¨wŸO|Ê@i÷Ê»Bq$ëCêÖ–äàÅ,RÊ"§¢ÔT¨naQHéKJGt¼c^5áÈ–œ™Ç3)oC²¦ÜYMùð1»¶O0ØUÄÍ‚Òr@.È1Ÿ×zRG#5$pØ¢4]eA¼Úwí±A.Ì9¯! #5l~Bë(%®7.F’6¥ã4¶<ž˜ç»ëI§ûg-Rí„ã‰:„8Œõï«Óp7Ed%¬v;ÉRð·œè‘ZUW,É4;#5»Ï³Ø¹Åb\m6zÇJ<s﮽zgºHE&¢~Uó¶l=GÓŠ-u,õ—ã'@7Ðèpn††PC1[-ü¸Ï…ž%͵´äˆ¦n熱üzðâç^ uCl«wòÕÛFb³/K0ͪ}òµÞCE+·u´IÙ¶ž8s<ïþ·­nûb_w8Üí É—’I9xÌÔoSI­>%é+횎øÄK!ÒëÝÆu!Ë!mi9žè»Ó™ÓÉàDdåØzœðÛñ9«÷a@ê­_’“#5^âQ7ëT˜¾5ë)’ºZ¤ÏÊìŸXKX¬] b²2tY%Ê×¥0*«H”So{@8*ªŒÙXVí…Ív­Lè’öºåqÊÕº›F²&âé1é|ï¾·â«^Q{)ãm?·líÛc†é®j¯wŽêªëÅùg–Ž"6ôyª.¸»ß®®F_žmèÖù¡€ÕÝó³ü.}%Û\¾%ßíf!n5¤eìÃ{mkÏg¿Éé,OÁÚn8ˆ¹Üdh@u°€Öö~mëØ»5¬Æx]@V(”`ÚLR"ö3Åœq¹G–0„l–1·ZqFXÎ#tÉR‰ï03Ø]եႆÚ0½²¹dvÃ1Ô–ös§ÍlÄwà6–vE¹"!¥³íQ›«Ût #-”ô%ýS$éæø×BÙ½r>yEã¿| #-âû’~PÛKèÔ#547·0ªÜ-À]8äeqÁ†ö»]y¥áÓša¶Ëkˆsv5“w#¢†T˜î7¤xPbhººØÅŒ={¸/zt1ŒQ®”!Ðë]¬-æ”*£sD¦»yf„ù<ŒÃ•×– k.è]wbA£,´ <ð³60¥û½ÛÈ…nq°ƒ)S@çÈì¯F»-]ƒË­'^"a’y'y,¼f™ñ (ñÀ&{n‰ÃY~;‡<÷XÜí+e›'%BŒ™¢;öd³ -Ù˜X,ÀÆÈ,"¢ˆB Ú‘Þs‡_-T!Anœk·wnYg¥Œì.ê:³ˆ-æ¤Fœ‘ÏŒÜeN­»#1ˆè#-݇J@HBÇ¢\.iœÆíÚ&VT! [K¨—3n¨À½&˘sN1Ù8Âʪ×Ð-¨&È<â¹”“ƒ#5Ä>^{Ϥ𠿵S·Ï™ÝÉ¡óÎþÐðbÜô<9 Æ»Ýgg Ý<Ò óª9DüL¦r «Ò•Daô÷‚©ÀòÛ.“ ãÛ-ž3é\ˤèËyÏYáA¬Ç3ÕÕ?<ÌtUo¥G³Ó‚«ñ=hEU)´¢^"\ŒñŽÓafo[s‰²áØl¶ÖÕÚ(^÷ìŽHsjêóµ#1]ö"Îýf2’bq>[‹ÜY¢1}Òïk<˜¢¿l5¿.Ýp1Ùšá‚1T›4q“¹Ö(…Ú7¾Y¼¯žüKCÞG2ÜÔÌo!:Âæ0ÙºÞÝCë¡ß’šÖšÓ¶ŒŒQ$jˆf9ñÌ©xÙ¶%x§Ûª‹U*8z3<ïı(®Äm§¬Ö¢ZdwV¶EË–!Äéš/ˆÞÍ4,kîv¢fˆƒCâ(ŽÇ9hÏÑ÷œ—žý¸+ɹ°íí××v¸L5q·N8îfEä¯ÏÊH>æ1•×°^RòG]»fÐÚW¹»à]zÔ·ãnhìhá@çRÿ|ÕûÈèçžÝ¡fCËw<­Îòæöüv}^¡ƒÂtmÅw—KÝÌëO¬7sF)O™¶1ÙUIv鿤w»ŸGlÁÖâó‘ß±7”â=áîç×К¾¬ß‹IÜfºrÐÔ"+«K*%4Êì*ƒ»¿%†Ú±)`…ëiF#-3êçiàd»Ìa­Òwz4/D[:ßЖ›Õ=¸¼ç#5€Ó¸=g ÁDX\ŽáІ„#5­zd4T[º<ß'“vì`ûÿ v°#¼ÁÆ[ÉúÀ`XxÁÎb(Q¾µãm?\M`ÓËaÚ)¶  RyŒ×ŸF#5™1‰®K—ƒ¡‘wy´(aK›2à8{¶GDy¤#1#59»šà˜Ü¡~å°X³Çc”$%3qéÚ¶U#UtJ††¼yÓÙÄÒ1{)V“PÜñ… -TΕ̠å\;€¬â “4#1©×$ì.Úg$$™ä›.ÄNÄæq5Mˆr£#-ä,­ðs#!Aáê'Êc޼õ¶·ÛÊÈéarõ5ž+úÊuö½™‡‘‘KŠ|â!…ˆë.ÝýÀÈ1©I6Žð®%®#-d#-rÄn,uÊU‹Û;›àtPë÷úêW¾ºÿ£è5­Užÿä¾½$؆"^Z_#-÷Šw–ÁÍ úçЗ–MR­—_ò²kßáXß{grzŸ@NÏ(Yàá5Äù•Š"ȬŒ>m*c#!"¾æ>é%” µV«œm•ñ’]4+#‘\æ5.*¡I‘¤…X¥ÜAL¤cT! ÆÀƒDȧ˜Öô¶¯&-\ÛÅäÕÍX¼\RÙ ª¡l—Š)h PPé1´#5’š›hµbÖÞ–Ñm͵£˜A¶TÏM™H²"‚Ö"ºA·¢ÅFPTcJ*"‰…EJ”‡¹ó÷^sÛœoàsß©_]Š¥#5ÉÀcÚ3»Â0ª×gŠH3}›â!~mÇ#1zW¼r.M´«k_¡V6¶kW#-Ø$F#12×ô·#Y¿§:ˆª«x>èf¾Ë¬n䥲Ôè([ü}â¬DELJ-n%°ÎÎÚÊdÀê´i»kœa›­é#1?Î:6bÑ"»#50eq2!â‰íêÀœëN4bJ³¹ßXêà`@¥,×|Ç“A82ª20ƒU8 ¤Z™TE|¸ÓÔŽñ—tÞ5ŒÁ¤KÈ‘`B­&n­1ƒn9²Q5M¦ÒƤŠXKºŠÆÄÓLh† \´T{”þ·˜Î§Âu‹wL)$…Y/O¶[î=¦ÓTtÈ”mƒbÒÁÀá Qá‰6¤Üº¦j•Æ&âTc±ºö2ôÔ¦™Z:ñÎÃ,ÝÚ|æ3s(ÓD$7kÛZÖ©nÒ˜ÕÀÃ$¶6e±äÙÆ.3VÊß 0#1± ŒX.2!08eCb,Ó4#5¬Xä¼XÚ^ Ee ºÐ ­¶&s-~7[âîs¬ë]£L…d3Fõ«m8lT†¨,‚$‚8°saPBPÊ"4Ò1aÒð²›D™¼q± TC m¸ÈBÔ‰x#1+˜¯s­ó#1+&JVa%Þ¶aâ™u(˜Á`i»³t‡fuðαZç H“°N'a§-õÔqë£ã†•&pÈÇÎ[äÂ'!¡àgî¸Ä÷®šzÒljq#-¼í•ÞXKºˆoHÒi›: •Ž5„[FàeUAR…Xd7š4†#1¦#1¦&¡qqhÛÍÅ·“%–WoNvµ°›—O2+D6EJŠ5ÕâcÄU­’®PÖˆÂ(à £T&áÑR”ØCº+š€BIƒbœlµþÙ¢ðà•¬½ÍˆòÞа¨iSq—Ûò‘¶ØÇd™IxTo‰]é“#5šMƒÓU! y0‰Ql.œt„—e¬Ë‰¡¡‘¶œ«.ráRÅGB´Úް;Œ:ье¢r3ÁŠy虜ÛTDÍËŒ§zF±V=¼§Nø®8ãÃFîÊVu»m¶Ýb æˆ[åv§öïEÐV£é^f>Œ 2 @£cäZ0‘ÖëR˜(¹ïm=XÁ¬9,¦9i¡ãH>÷4HS‡¦Þ,-™J`qÄ.ɦ±™Ý6ÒÑpV«ƒVB1ØÓ<ÂLŠœúÒÖŽƒÝ5#5êdHÔ4pɪ$bˆYžj‚¹™Xœ[›f´E·ZЈŒ X,¢OŠg'B‡G…&0QD.ÒÅ`ßmf!¬³ ]Œuƒ:ðaÆr>aJ,Þ…ªœ±‹a𻼑W·<ªªglÈ|N÷/Oºgò5R„U† ‚ÑÚ\»¨l`Äã¯nx¦œùžý¿Æ!L÷¡K#ƒ!êPùŒG°ÜnlbHtJZJ‰RA=k·ë7‡ðÕPÙÊyÚ£¹ˆC¼TîU:UŒØÖ­À4†*SmÙü§ƒ U+ {½E*û˜SÔÓ^#1ß/¬‘«ØµL¯„Ùw’|^Щˆ§[>‘#1ôßßA Cu#ˆ6"í}½Þ@ý»¬gHq2;GŸ«Ë×Užž/ždJpšÛ×'iÍΠÍÂÁú<¿ßQ®ÌU¤¡KU+ƒË1æ}f÷=6àcxHÆÐÔƒÝÉ„EüÝääîÝ›7wL·†O|a%ƒ–Òº©är^G3Î'¦.zÀèyÄÏ*!ô=)ÛHQR}bP¬šiÉÐvTUÌziE e4ÐÖ!eHŸ£'g±0jwÑ¡;G³|ôP-GÄ%MÏP#5­¥z˜ÖÝš²RUÚ¦S`C)Ä&Ð ƒU¤R#-„ŠÅ#1pÿ㡬ÓÐ#¤Ïaùø•o‡m»ç—Ä¿žþßoíHÑb#U5CLµ¶ci4bchlÊ©•bšm²ZMZÅ&Š¢JÉ£BŠlÆ-Ä3hSMHÒ“d”bšRBJˆÐÆ›Dj%#(±’Í)²)ªhÃi&a†Ì"R„“µ‚ €þצӖžeÏdÍ@{3>YØê†BŸ´e×`øöÎætÞ~i º×¦æ‚|¸¿SÍÌÙú^d‘œ}"ˆq³¶l˜•Ì­ÞÚ)'|º¶HZ‘ÅKëÅ#-*µ >Ó†ì¸Þœ¹I&†ck/vÛ†èÆd¤†É©µn|µ>¦»7?R]ø¢ƒXÉlݼ¥È8›ÞÓobszÈ™ÆÌOO Ýy$º‡G]—¿ øú¼ôŽÎǃÕß’î™Ý â·P^ïÎæWVý66±K]Žam;DËý€{ÈÁ(#-±#-Œ"‰5„“ZM¶4_µ®š´m65)&¢(¢4VÄeöÝ[™[*——j­Ôµ,%‘d@#wæ4ÐðÊ$Q “YC$(ûýº‹flS²œû ƒ`Ïxm(TGaE¨i˜Œ7? º5Xу‹ìxÔ3¦#Lb™$r“H½Ë§wd³EqìµÞºù-^6ÚáHiVÀ0´˜‘€^|’[Ž›ÝyÑÛ„»{yÕæ[»¨Êr™W´¯)7‹¦‰¬çQŠ;~T9fŒ‚Ž"è¸<1„ÅÙÞ!”hE`¡GLŒ‰‚lÆnxøYËmu™>4'Ðá‘`rgµä‚ÝL\#1’Õ"PBtÄ…3Z ŒÅ¹k”“17-q&û{kS8Qý#1‚ébOÆ´Û=#5ãJ¤•r©–%B›E™ärˆ&IL£ZP2&%‚P‡2ª–hÂä`@UhÖ‘4† A°I.#‘o6II$ŽT:1o—üÐ<Ó”®9·´ô¬$Q#5õ÷6q}Oá—^^Ä+õþ‡Ãw4ëœgñ%DôÊ3®ô=dHFBÿ~/íFF⳸ÕϪZî­G¡¹õœ\Š1Ö2 UC5ð+a9_# *á S´Û æT‚Ô• &HÄß•(Z©Ž~€Ï?Qù#-9Ï”Û42b±wƒm(´ÿgvù÷MW•šŒ*жš!ëîóôšI #5%§Ñ£ó]ÄÙ‡ÄÅ!•Ž8¡«%•ÅÅÎv`Ä„†º¹¤‰g’Št&† ©ñ‚éÔ¸®hó´ækhEöûuƒX~aëä»I¶zÓšs\”ëé>)E0e={ÞÁE€0ƒ'°ðâq×§`úï»Ñu9}ËÎ06€î5u¶ƒ>¢«©J2„Y⑌Œ!„VjzŽÂm„ò,ÔqÆÀ¤Óygœàð/°2B I£<Ã3×Ô*ñÆ+¼±©¯#5°óÄh8a5hZ£õ}¡ºQSçéÏð2OdLâÛÂ>ÿq×€ÉAB‚ETÁ§Ìò÷Õ(î&%ù¿V׉@öv£²åX¹^³ì˜sá}/œV\])c\“ã5Ïc¯5,!UÓEœ?³óÄhâCƒÖêà‡Díçzv`Ö!™3§Ò“ÀÈ(à¬eñ=Í<éõyñÓé‘õû¬O£@±-ò{_lª¤Â—ù‰E]%ÇžvR•¾±0²=sDU??œîVsJVU¥Nè´9$òíj6ök%ˆÇe}(k#ÑV;;d#1z¢ÁmwÃ>,ØD¥;‚DU]0?LNUçƒW¾éA0llÃUÂ^¤J*)VAn—½D”4 ¯ÀÊÍ¢,‚_ê‚¢s|œ¨»ro&šà53œ°cq€„e5!É9Æ\€!8Èá’Æ¥+´¢h+…ràߥðDÒ#5M+JoÎÈÍ6Qž7Ô°ÍK7 Ú™ƒI›83§Lº˜Ù¶éÏCb†Û­ºÛNĔމ¥¤m•o¥–rNI#1ØxØQ¯ \B(ІÔÒ l(4;ÀÃp BÊâɃa¨t† ()ˆS­bšŒh6g %DòaƒvF½\Z£…o‰MâP°CŒéй¶ô$1‰‰¥BÞ¼öߦŽÜPºë@ŽŽ5¦lvÞX@z‡H³5¨Ð±±KM° ^ Á”k{Ÿ?þ—SÈ8߯UЧ;àØ‹èo,Ï×7Ð`ÀR7{‚‘L„ÈÀg&ðS¨¦ªi¨‹‰”ÂrL¼.ë¡(L†ÛŒÏéþÙ:[ïÒ%²ú§™]±Á2™œOÛ©dË zknâ»1‚}{†üÁÈ]'Úº%³[$R—…b ˜¶ÛÙpÁ»~h^(fç3±e$(ÅxêK¤©Š Aj9E,1R#5É€È#5 žÊEÄ\Ý™Ž®æhÕ×¥2Ò¶3Ûu†jVcÆýº¯7nÖÒ§ ÞcÞ®ÞN++àÓ2„™r8:1•΂¾ý»9Bó0\²7Û0\çÕ½Æ\VVñ„ö¼z6?#1ð>ž§(— ¾ü:fÓ½sDky¬u6§3„í#áBÁHˆ"XN„3B¶TÙ€!Áê’8Šœ;ŒöL4î&ì¶NvÚX*UÐäLP˜®š‰1–”•¾cvF÷2f\±ÖzíƒËgÊsK¡1#1Ã#c·”Üãj#1“R|òãA j¶#ggÊvj%ÍhÕÓK¹I1LæZ)”Ó©ÅId2gjreÓÞ¬ºêø…¶Ó§–²æPè¤aʹ/L.Ó‹}äãe"±ê9ìZõßÆÈ·X8íµ¹¾&†ªwŒˆyk‹&Y#eëC¸¨ÄÌU#-¨‘áåõ˜¢´ñr"oùMQõxÁV¹fç¾ãÌÖûily¯ IÄÒ9݉Ã]7’}qʼ\ɸšDô;µ"×'RËnð…—+•…YªÚ움*fA¤Þœh’’ŠŽ,€^”Ì×Ã5QÑoȵµæ#5Íø§%nÎÄÀWGìïp=ï]s”r*éÎ…Òii+BFìÄ·W'@æô47±Òy%64è•..“¶“°‡0E(Xw0t8 :ÞÐtæc,iÅNÅ5ÞÓ!üœZ0à‡YDz†hål­®Ø8PˆHœ’j¤- %ŒªÁ/¤bk®ÐÎÓV¢¢’„*0Hïʯ#1NZØÅ¹º†¡\ Ëî1­>$%ä€j®² 9ó¹ŒA­³«®U%ÀVé˜ÙÀØ#1LQ½Æ&5ÀÂ>TåÙ@¹Àðwïù®yÓ;iùf6jž·T5ælž¦˜àôQ& džšŒ}#1í64M®)¶KŒ`ì&”Íe:·Ø7ÉËa΢ì:fÙ¢OÙ—á(‡¶Ë™ptÙ¹#1kóÄÓIúýLœt­«Ñ }É™)vvÏ«·~în¼uŸ5‡™Ê‘5æw*צn›hÓ±Fe.ùÕ¦)ƒêùr­GÌMû†pòLœ¯èÃ…l‚öp›h7›ð, –VJMjfÁ‰CX|²ÜU‚k}óf°¦!„!uÀÙŒ ¦j>]wFƒ«Q0Qˆ€Û¬v)ÀÍ#5Òy½h6–×É4DÛ:x‡¸÷ô½µY ’{™nÎÝ«AeáåʘP—4 IˆZF6ã‰#12ÎÙ+UsðÎ †¤"2…‰% %!†;ùi©¯Q—£C©’ªA¢3kY ¦Í›£šE[_P*â w:­‘œS±•0‡ONì›#-¯)Â-ä·Ér:rsI—DW.R9vZ†€4˜LþQÛ§h¼ £;ܱRÎ$‚zãbpÒl.“ò„ÃíÙ4Ó}òXcˆ§ë¨MFœ @îÕ2&)m—jÌ5òθòÌÍh—5Õ1òà øHòT^Tk…bT  ŠäÝK¥À(–L¨:ÑÄÄÌü²G#12H ÛQB${˜fU4"ð £#1$\U(jšÉ©ß'¿vÊFïˆq!ÑÒÝŒ¦Ã1YÚ`¢ÇŽ6‹AŽÙKµÍ8BÌÝ™ƒhà6”¶""!àjK¹°»ŠÕ4&³½È&xÆ&#-F§Q%2v<ÑHŠê%Fl!†)‚êñB“YfMJ€Î&á€Ö€๖*¢P ƒK#5¦æÃA™*B!R" 9L\v…Lªã¢Ù v£(в ØŠ)ÙÎÔ:ƒäfÅÁEPS±h#Mèˆ( :%#1vðU‘’Ë¢ì©suSc!†Äx‚ Á‚êM:”Ò)Ý<-alAÔ*Œ#1v#5ÒL©€ÚT\ 9ňÄÁakV)ÀJÉAaF†ºéÅâ´ê`ŒŽâ n#¬ÛQH›AJd ðÈX\a;a9$‘Hébebd4Ü0¬R)"#5²}„òóë>ƒÆÔ£U@…TDX kÓG‡8A›Ã–Ç¿É~‚Xdøó"C¸5XD„€›T*¡ÐŠþ<+ѲÖ@O3¬çë¬[ÑErÈ[¶iYXÅ8,¿gyXÕpû#1/%¡Z´@ŸI3›†Sržó¶›½ö¥F«á„Ä×p‹muÒü6GTàäG†¯ Ú!Ž;j¢Ü¹$%ÈdGÁ(ú·}=Ý` mÐÛW%U¶ÔEýNÎ]Ê"W¾‘YŠõ¥;^Md0’š#5*Ù Œ:ÚÃ]¬Ñ¿†ö—“€ðiUÂTIݵ ï.T‰Ã•ÀÊ#5 “ÕVŠ­Ð}ÖX—ëD‹ÓD~´‹é"i“'‡'{¶?És,:îÞ8'ækJì#œæ*fˆ,Ÿí„M´Ø.áSeôÕU­ÐW‘ÈÛxÃnhù=îyYÚÍg-1±´›Õ»º¾Éu´Q}€{+üÐ}€‘Q÷Q—/Èúê9åÂDʳ:_]±;ÐøAA°6+ï‚îºðåÐùi ¿%ƒ¤$u”Jª“ªP`Æ¥#55Q¡( oàr““Fnã,ɼÆNÕ+F Dm,D`󜻵¼ÒCi2…¼MNóºbªD6Òk$ „Ëc¤G! d… È;0Á$,@4Њ‚jVŒAhpÒ²ÍI˜#-ô&<’YA¤:énbª&±»Ià%¢j0Åk‘™š:ªÌ™i(B „£„ôövÎçáßæöC'†»7‹H}@T9@€YÙô´Æ^аéu­L} RjÓkcF“ÑMbt†9˜k3"!¡F†kzµ1 º4W‡¸5ˆÖÅ„#…ÂTQ'W(7”'« Mªs9odAØoi¤zFßTóõÔéïnÎ}Ñ£ª¢hʪzÔÅ•]¸Rß>ºÀ~Ð)”ðê+Añ hÏDÑË7ÈÈ‚[²Ã*œ—œ›œqÓ]ˆ¡Ó›GAög)•[b–b ÌGÅÄ·Hf~ ¨fM¹ÆMܰÜ,@9 ‰ÅêDSáìz”S«-»°y®äS³#5ä†Ó•.Žk¾¬,å#<þú&n¡¦ÅÀ5ÕßáøpÓyº±db±'±(ú#1§3Ǧ·SZ43­vžP>ä¡A~hXØ[_Äj®ÛQIEi5–ÚÒiH’Ú›ðõ&Ù+l€’ 8 Œ"·`Œ¸Ò(…ȉiQö1e¢’ Ù ­Â+†#-‘",‰±Uå#-¨ =þ“9‘E#Hºd¯#-{PÐÛå•nð5yt°v°­òtœ„HD`²’BGeïçåò}ÖûTÕU‹òÃŒÐXb"¦Ïž7nùrÐ2´òå]PãG¢úú)Éß¿poT¢ñœ¶€{GŠð;GävgS¨ú#5—1F/–NEJZ!(¡ÂpD #1̳‰´p –$›#5Ér•¢‰"–!޲¡ÅHVꉉÈ1(äVÀ#56Â^»Ï-·0õVÉuÕI^e^+A M#5#Qd’JŠJäaÃ[¨e™ž¶Ì$#5JŽÌd¦a «½K @›r)€æikO¼;ù@ìz¾AÔ–  üš]?Ê}UC@Ög$ûx“[å½(DPZ=”!ÿ)ÙC«“zàflíïÿF–9ñ8Q¹ê(«"Œˆ…ÝyòÐ: zêIwС>ê©gШ³} {˜F¯:¶ÅDøí¹­ÔÑ2öm®›ßwLÞÕ!»UV§tØb#-¸#-qÜNBbFR¿îòáÕ×áÏá0%ißOßàg<1 È$:J%Ï×!FmT´(Ô“JŠ… E¨ÁÖZÁ¨W`®ËÑ#-±Ä¡ˆ€R@^567‚ÈœG³išæÛZ±Ãm0¼#¬CÊeAÖz´{»à>ÔnjµÛyNÕµöÞYZP’¥J%(pætß"cã!0.4–~ûí=CGÃrF+aäŠü®ÞHÄ–ÚJfÙ5$ÙfÙŒVI”U±‹Y‰XM‰*kLV´–¯ã,mWËQ$I‹¡Üï7Á6Ï$õËB.Z¹dæ†ÎíˆØx(‡ ðê›üæ®c¢Þ}#-ènõöu¢®~bv®{¹àk#-í‚HŒŠ¹³ê Y}y+ã˨!Û±ßÉ=È=ÀûÏ…ÅS"#-Í—‹>9ìê„’G_¯‡(–¶ÍžƸgÝ'«ðËÒIK‹®,š¥4¹¿º[Ezêrl¥-´<ÕŒaÙÜI"‘ãÏn€a¨žé§†âƒ$Dq«)\»’ÄùÒ‰²ƒW *㈂45iÖ‘«»2PÝ5Æò$nI4l·mµqÃi×Ý1ÂH#1­ ¤ÐšAΡ¶rbŠ¥|8WÜea[£'&ï32£8o†–Q¹d#-PYl±’ÚVXCœ fLË­#1ÜÕàïõEN/ˆ¡ë#-€Á¿ -‹F•¨>)•‘dÝŠh7ùU­ ,ŪÖKî,5²c,d_RZl}ؼˆ¤2^ŸàÏP·D#5+šÏ¤ @wý )uÖˆ²$ALxIf¬;äêêÅðtb]2¼j'@Ú'D‘ƒŒz’ˆƒš#ÕÒ’ß#ÇDùzJQ#5óæpõмBÄi‚HT¡Úx¨PÖ¦¡‚Ÿ?çíýÙC@iD NäÀâ€!”°×}–Ûü¿K^؆S§`5)7;Œñ±ƒùxßRcê1/("Y3ËDAä¢ÞÞÏ#5nÀõ*È—&l€ð¢‚'o?¯0¼Owó·~àEÉë%F™ù´#jpµNc0@ …%›5ôï¡£­ÜÀ3_X[óT<¸Ð¯ÕÚDQÌvEnoé`èžÛÕ†ßòý6Õ•y¾º0`HdÆdxº‹hÒeÝ]&i”ì¿WunUÍÍÏ<Þ{úûÞq¶pÙ2Šl–Åõ7s]¼ÆÉQ´m¼nk–âÛ»¯óÊí+IÊÛy„ô‰BSI#!#1UOÖ‘M¢EÜe®ß¯âxxÊ(hªù•ó÷Xò0ÞÌ=aàIYˆàžãoÃ!3S¾@;¼ƒÀðŠE#-E@H0*K¨øçíE û ›#5Š”¢ÉY¤¶lÚ½~ˆÔm÷õ_ÆßY¤¡E!†-MFѪ6ši”j·åµ;bJ^Öñ­ãS(¢¦_t%#[õŒr. £âElm}KYõdû'èQWÄ·­7uÍ‘MP­›hÅF6db±•Š™E²ÁI%ëÏ*µˆˆ@"„¢uÒh EÞ£B·D×ýþ_²jôî'¹ô}˜vdI<ð&ÍÔ(ÒzIî=Ÿos »XŸ¿ñãKŒ}3axvˆø !àÙ€â!œæ‚åœÈL¢'¤W!¿¶S+Æß¦÷°Xˆ¥@UŠNm=Éò*¼?›63ØÐ Y5Ày¤ÊcŒ…@ ¾XŒY`7ЇRsïáòÀTŽ)dFú†×±h&FZ.…ÀhLÐN±÷«D ÷ˆ…©N¦ñõn®+RZwn’Ú¦Ú‘"ŠRS#5‰A“M“é ¨ 876Ý»«²Jæ_:æÄGúv’B(”+â\o*SçieY‡º‹Ä²í6e³yÇÆ¨ŸS=ÞYܤõgeÑ«Ú`Î [–³0q1ÜKEÔã¡lìÍKGXÛi~4')¯ÌƇ›Úm.mÎÛW¤@·ÊòXöQïW#{xA×­=÷Ìÿ‚—..–#ä\ÌÑO Ô.–!Aù éÿ¿Þ€”j=/Øý9^dÑlRšbT›#QM¼UÌl˜5j’åUÅ6Šý…WçZñ, ÑZ=76’Ì ŠT¶ümúÿ}ø|9¶2Q–ˆH­µ™CÒ#1ʺÉ.o€±„¢¤iTbdÂÑ46î(Ø@JŽÃÎ#5O?ÜÌ8.í³7ÝÕ[Íuµ¿Á©¶¯Z±¶£°V”5F„ÖÊ2ÄZƤ$bEA£¸Ù~MžØôÖÒ¤’6‚¬ÏÏñÃñ4`eˆúã RâQ¥%ÞÔ`ØzŽñ%ÓáM.ŸqoÕ„™ci6Veuþ.ø%–ÈÙÃ3"M‘& ÒÉx‰9ivÁAÁþ¬˜Š:×Å-Ú+»n›Ç[æ·lÒjh¥KzÍ…vÓ»5wu+ƼÞVº¦Ñ¤Öô«’¢Þnë3vfUuÍÛQW;PIl‹6¯.ìi¢Vîí»»ZM•%LŒ`Òm#5È#5¡¢ÀŠÂD•8)V¯S«ËµÐÜ··­É½M©„^Í{yqÔ¦–ÊjL²¯J×#j#5 ZöU¼#1V ˆÉ#DŠ#1‰‰µbB… Æ(ÄEŠtfǃ:&€Ém„Ü@t‘ÒÀMš Ÿ¸ipŠdj‘`²lQ{ѶEF*‚d‚ÈÇ#-ª@HSᒀƶ~ô…93E(~ 2Æ0’@³kkƒ2Ï#<(nŠhT#5U6¤7ÕJîØL̓O.,È„{@#1Ÿãrþ €˜’."+Û>š/—#-ÇѤ<Æ1qBÀ®šüfDÈm™½Œá¸L©M¸Cª5#1縗|#1È·Çðý¦F$Ç©CUR§Ká4®DŒ5ÝÈR&!{ýJi]hdL~¬ùÁAGÊ)ļ.#1õêLoÕ~«ÈÐ’àñÐÑ#¨Ój÷øs—®¶[½ïÖX‹)%ý‡FcÂýåëXètúIYw]*O]olr.ä#5— PyаÅ!®*F<¾ ã=2jðxôо- ö Ej±†…šW¬ÚßA„8€ý°i±Üã§‘EMø8§&Í wg'“ ² !,Ë™§zÕ™ ¦ÉîG|ëúyàÅÝ£°3¨´Â6ô¢¥Šjv!ä¦e.†GÍ;DÚˆ:Û»dÀDø&‡BYžïl#5ã^däX½õtÂà1dLP"4Ñ]hi$JƒDM‚Qb¸"~ÆÛ´åPy@‡’qð@sÎDÑÀ⬚—?m@ÌaZ …ƒÏÑÚÎtÆcù|ÜÁktã%Šª” 6y@XU ªOH¢9Ô ;’#€ÌÑ€/øÐ%Äq´3ñísÌ©K –”wÙa1t(9Æq„h³÷2\µ†´vNÅ|êÖÞM¨7…J?¢–aº*v$º„qx‘·LÑaWNõAÏm#5Æ4âj–Ábd†t¶6'î8sAŒ˜qÚ"1Nç!ª{¾ôÒ#1‰¥!ÈV€Í2: £Í(ÊŒú_Ôº!¡yêÔ°ê(†qð58oä¶Ôü籄3ñjÎÁ!#àj_p¸zŽÃö Â+‘ðÐï=¸×*½·¸ûãßÙÃô‡ãð¶ži¦ÃdOÂò.BŠOÃKB"Ý?£%²i•½tŠŽQ³P9*!ݨþ"‰Eq¿ošd¢*ÚÁà|d|¯À1õ»÷ì`ÙekRå[’À^K#- T&²Àh?èÙÿÃú|?/þŸþoÿ¿þíÿŸÿý?ëÿåo÷y{?ßÿþŸ÷ÿ¿þú~}›>Ÿ—þ¬þ?gýSÿ³úú¿ëÿ‡ý]?ðÿ·èËþïÿ‡û¿¯ýßü¿»ýßñîÿÓþÿüßÉÿ„|?ð‡üãøz¾aÿŠÿÇÿ÷Ùæûcö*õ¨ûY?™Añøª ‰H~gK¨™Oò#€É«÷ž@#5ƒ¸ˆ¸pi.x¿Ô)ˆ®EÀÿ@ËC ªZ¿PB±·½êü+yëø÷ô³ùÎîÑ Š¨Œcpœ$ˆÈHþ¾2ºœJt!–N‘KnÈþ»©Êî#¯´Æ•ÙfLòºƒ‡ø=ß˧ ëÄ2ƒDûð$ŽN(!-T’H¾V¤!¡~ºp¸¦c僗ì 54EŠC‡)ÍÀqš…Á$ DìD¡æ`J/×fʆ$€"F#5BŽ]ã)O‡†k”%@#†#1¡q¥ôŒ>ÿÃ.#38Fÿûâòåòpê°aÜc•Û$EˆÁBLrÖ•#1ÌC~†ÂÞ+`!F¢B*$$LT‰¨”5ØÖ³Je¶‹R›dª2šH˜Ð¢2+5õݵuª¯Ž9ÁòÑõAÎþÂ!þè~º©j*Þ`°DAKbÉÐöJ.%5#-dŠ(}áèèýGüÒ0Òe#->É;|íu>n[ Øÿ³LçÜ?KY¯Mʪb1F?H•sôêèëÿTÞM±#5ÑuÒy¢Ê”@uîÒî Ê%½¿øïì¦ÙõŒÎÞØ‚ô„g,r.;@}r“µ©½ß#5o”-v€#5¢”ß1¸¬ÊäÚ)öwo·´Âmuu‘–Q‡}pé¤lÔpF[ˆO•®sVpÐÞÑqIâJò5–¾šo_°ÙmJS{Lmõëåßkj4Uòk}MÙc^5¤‚Ûð­ÌTQ±ð¶æª1¼[\­éF¯e«y,V¤±X6ƒi€£B#15[âÔÕ Ývu ÐâáÉnEÙ’CCH}ÈQõ „ßÊÊœ¤‚à#1æN¡D×Wu‹€à»uAS ƒd^®ýÆó=Ã6ú)zɃù´´oJ ëÿ´x‡ªýÑUãA²H²#&ñƒ˜¥¿—íéáö²N¯R;ÑÕ÷sÏ™Fÿ3#-¡ä¡é_£b)ÿš+ +*;Èï;õR‰(R¢Ä„#-c%T)ˆø2b¢vˆ'¨„"’ @€·Šf»áòÐôýˆúûRnÈ"@:ø|Pø¾ÏpÑôý‹Ó2ùYiû^®Ð.!x-]X§¦'ÎXƒ1Iå÷þAú8þ_ô‚¡]@‘rŸö7þx9M¸Ýí‚„€,QdÿÝå%µÿhÛÓNŸî*“ÿþ Š=Áú’ÊÊäÿ¥±ÿÜôþÚïâmÒ,ñ¹h?ýx?³þ¿Ÿÿ‹´ù×áá»#1<4''-9¸‡¦Qå{ÇvT)ñy\žðŸÿ}jO)d¿ÑêópåIá!§‚r¯üÖ§“˜lÿ¦ŒùÈb(_üª'6½Âª´âÈ÷øé8úÚ¯ç,ÓnüÛŠ)ÔMºxz„v|•P=þFbûê5ÅK‰)ó([Ê÷ÿ•³Ò»xœULà›çªáq–s¦ò8‡ÇÓiÓ¶ˆÐeecøs–&ðÖÂX߇„hœTK^VÓ˜H0¼Žÿ>ƒ“#1i’ßO?)½—†ˆ¢E”¯W& Áèí–ÈKPÄ'ÿÉòPq—OþJϹ®e=TN}+õ¥r åöû¥ª±ˆ¿âúCm÷:¿öˆU=ƒä¤>/ŸêDˆ?ÿ‹¹"œ(H^+##- #<== -#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEjH6y+TsMRfVzL+XRG6xXHc13IpUFAmZM7vsACgkQG6xXHc13\nIpXIVxAArMHbyJFk/VDRXYhnTCz0lPBKkdqMcaK3JhQzKHjPjgTCHEBffmaIq2hz\ndMs2fgjDUmMlmdO5sEeRGgfDHo+ILlRDWOZF/7120aMeSxtS6JaNCzm5W97bWYUP\nJT67cJFanzcUC/HodaUPjUni3FZdjla4CAC99f3DA2thMLTN06WtAdUnOGsfl0vg\n7Aho57LYtv1nUsW23T9IyF4MjhwVR07dGmV82LYFhZRUtaGiBBxQLXOlxshIWczt\ne20fVerHJ6towSEW/fXTY+xOIPs3PaB5/GqMf9SvvdS+33uMrHJIEBOeoFPbBap/\n6bZuonnubCW2bOwCri7pI8cMbXvGHEnKq5iwZFefJfBMkS+eVTuzq2Cy7Q9/+e6y\nrVZ5pPX2yxvNi0kdDhq9DbZ0hNm+wrnqYS/44zPOVJFBRA+pjTyM14dL/pwnFwQ0\nTN47DVElkAnMs8qjul3rIbT37LJe5hr5QgF/ph4bfK7IYbGPbaHOFkqgPiMipATk\n3zpZvkQwMGx1iQiqvBUgnQabbaX+49GjIqYFv0DnHabYTzJjjn+f/xKxqx37K0a0\nthLK/9Kt4iRlVNz3Xa1osPZKHjK4saeHQQV6geWSu8FjTn2WQSV6QACxTbjYtRdW\nMiM3qXn1rNZAhspHW7uCPifC2JheR5435V7OpEk3T8zqPcne5k0=\n=2de+\n-----END PGP SIGNATURE-----\n +#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEjH6y+TsMRfVzL+XRG6xXHc13IpUFAmcfxegACgkQG6xXHc13\nIpUgtg//WMykqX23+Uk3D9oTsjsL/MwX9M2vIjVubv7Lawrsa1Zv1npjhcn0cS9G\n6Z40dh8H70PUbB/ldlLY+BmSi+ls7I8rIcsZGUSxPhlqaO2GemLkHxM6DgkDrW4k\nVvxQxCH+ndS5NhC+4UtHksVp8aSFd7zpPDID23iqJixDY5vT+cyV8bDCw3Y9FPvU\n/E4hMoem5L4Xfwu30ETlmQf7SRhnD/Zg0CUBY4Cy4Hw/ZvFhWK3grtxxys32izsv\nUj9LQAksxkSNK0BRr9GI0uoULmJGpJx+UNEyG4EcEc6OCDx6Y0wsbk9HSvGc1dyI\nz7gTOabR/+TQCKmzMnoiqaTPng+qC9BueX/8k0tpsSCejmHuMSphjgm1DCy1lbBq\ni2cLa/Su64wTeQzf6lr4DmIMAyLE7ZtNMgtBli4hhmOqLuI/w570auQJDCkTAU7u\nSQtZf4zpVXdTiDzValelwVGuWwzKzBOTuZxes7nYrE5+O/1SK4U0TRnd1qM0g4sl\ngcRMp/Jyc9vUKKw5B9onUI8kwYFGjvoc4PpJeM97HeNjvLEFZ/9vUuMJfopxW50r\n2KiHxk+oRlf/Xh1fGK2Ltf7zGc94c5HukOpL8P/yKa+Z+f5WBh9KY6orzIBeIWNd\nlvjiN6emAyxaQghaFFnZCfqlQ5D1OjHvuxCnxgVM/W3+75v8Hd0=\n=iyYj\n-----END PGP SIGNATURE-----\n From 2a02d447701e005c75495fad19445e5d9ea65cc7 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 7 Nov 2024 11:29:15 +0900 Subject: [PATCH 058/348] zephyr: Disable pedantic Zephyr RTOS heavily uses GCC extensions, especially in the logging subsystem. Enabling -Wpedantic causes tons of warning right now. I'd like to limit the scope and build libcsp with -Wpedantic but that will take time. Thus, disable it for Zephyr RTOS, for now Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b5156a34..1a90fe8de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,7 @@ target_include_directories(csp if(CSP_POSIX) set(CSP_C_ARGS -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter) elseif(CSP_ZEPHYR) - set(CSP_C_ARGS -Wwrite-strings -Wno-unused-parameter) + set(CSP_C_ARGS -Wwrite-strings -Wno-unused-parameter -Wno-pedantic) endif() target_compile_options(csp PRIVATE ${CSP_C_ARGS}) From f22ec7673ce1dd51de993722a49ab99e9c5ba68a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 5 Sep 2024 20:13:14 +0900 Subject: [PATCH 059/348] drivers: eth_linux: Free memory after failure Improved error handling in the case where the eth_linux init fails, ensuring the system properly frees resources in all failure scenarios. Signed-off-by: Gaetan Perrot --- src/drivers/eth/eth_linux.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 541405f9f..56b4c0146 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -87,6 +87,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int /* Ether header 14 byte, seg header 4 byte, CSP header 6 byte */ if (mtu < 24) { csp_print("csp_if_eth_init: mtu < 24\n"); + free(ctx); return CSP_ERR_INVAL; } @@ -103,6 +104,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int if (count > 0) { csp_print("Use command 'sudo setcap cap_net_raw+ep %s'\n", exe); } + free(ctx); return CSP_ERR_INVAL; } @@ -111,6 +113,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int strncpy(ctx->if_idx.ifr_name, device, IFNAMSIZ-1); if (ioctl(ctx->sockfd, SIOCGIFINDEX, &ctx->if_idx) < 0) { perror("SIOCGIFINDEX"); + free(ctx); return CSP_ERR_INVAL; } @@ -120,6 +123,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int strncpy(if_mac.ifr_name, device, IFNAMSIZ-1); if (ioctl(ctx->sockfd, SIOCGIFHWADDR, &if_mac) < 0) { perror("SIOCGIFHWADDR"); + free(ctx); return CSP_ERR_INVAL; } @@ -139,6 +143,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int if (setsockopt(ctx->sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) { perror("setsockopt"); close(ctx->sockfd); + free(ctx); return CSP_ERR_INVAL; } @@ -146,6 +151,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int if (setsockopt(ctx->sockfd, SOL_SOCKET, SO_BINDTODEVICE, device, IFNAMSIZ-1) == -1) { perror("SO_BINDTODEVICE"); close(ctx->sockfd); + free(ctx); return CSP_ERR_INVAL; } From 1653d5b340cdf97ceb97dbf84b2dcbbfe8623e8c Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 11 Nov 2024 19:57:48 +0900 Subject: [PATCH 060/348] samples: Add basic sample code for first-time users Add an introductory sample for first-time users, demonstrating a simple `send()` operation via the USART interface. For now we only support Linux / POSIX system. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 1 + samples/CMakeLists.txt | 3 + samples/posix/CMakeLists.txt | 1 + .../posix/simple-send-usart/CMakeLists.txt | 3 + samples/posix/simple-send-usart/README.md | 69 +++++++++++++++++++ samples/posix/simple-send-usart/src/main.c | 55 +++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 samples/CMakeLists.txt create mode 100644 samples/posix/CMakeLists.txt create mode 100644 samples/posix/simple-send-usart/CMakeLists.txt create mode 100644 samples/posix/simple-send-usart/README.md create mode 100644 samples/posix/simple-send-usart/src/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a90fe8de..92811d3f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ target_compile_options(csp PRIVATE ${CSP_C_ARGS}) add_subdirectory(src) add_subdirectory(examples) add_subdirectory(tests) +add_subdirectory(samples) if(${CSP_ENABLE_PYTHON3_BINDINGS}) find_package(Python3 COMPONENTS Development.Module) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt new file mode 100644 index 000000000..9a66df16a --- /dev/null +++ b/samples/CMakeLists.txt @@ -0,0 +1,3 @@ +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_subdirectory(posix) +endif() diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt new file mode 100644 index 000000000..d5424a93f --- /dev/null +++ b/samples/posix/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(simple-send-usart) diff --git a/samples/posix/simple-send-usart/CMakeLists.txt b/samples/posix/simple-send-usart/CMakeLists.txt new file mode 100644 index 000000000..86ef50b33 --- /dev/null +++ b/samples/posix/simple-send-usart/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(simple-send-usart EXCLUDE_FROM_ALL src/main.c) +target_include_directories(simple-send-usart PRIVATE ${csp_inc}) +target_link_libraries(simple-send-usart PRIVATE csp) diff --git a/samples/posix/simple-send-usart/README.md b/samples/posix/simple-send-usart/README.md new file mode 100644 index 000000000..56e907eec --- /dev/null +++ b/samples/posix/simple-send-usart/README.md @@ -0,0 +1,69 @@ +# Simple Send via USART + +This is a simple sample code to send `"abc"` to a server via the USART +interface. + +## How to Build + +``` +$ cmake -B builddir +$ ninja -C builddir simple-send-usart csp_server +``` + +## How to Test + +To run without UART/USART, you can setup a pair of PTY on Linux: + +``` +$ socat -dd pty,raw,echo=0,link=/tmp/pty1 pty,raw,echo=0,link=/tmp/pty2 +``` + +This command will create two device files `/tmp/pty1` and `/tmp/pty2` +for a server and client respectively. + +You’ll need the socat command. If you don't have it, install it with: + +``` +$ sudo apt-get install socat +``` + +First, you need to run a CSP server: + +``` +$ ./builddir/examples/csp_server -k /tmp/pty1 -a 1 +Initialising CSP +Connection table +[00 0x7f1332d208e0] S:0, 0 -> 0, 0 -> 0 (17) fl 0 +[01 0x7f1332d209f8] S:0, 0 -> 0, 0 -> 0 (18) fl 0 +[02 0x7f1332d20b10] S:0, 0 -> 0, 0 -> 0 (19) fl 0 +[03 0x7f1332d20c28] S:0, 0 -> 0, 0 -> 0 (20) fl 0 +[04 0x7f1332d20d40] S:0, 0 -> 0, 0 -> 0 (21) fl 0 +[05 0x7f1332d20e58] S:0, 0 -> 0, 0 -> 0 (22) fl 0 +[06 0x7f1332d20f70] S:0, 0 -> 0, 0 -> 0 (23) fl 0 +[07 0x7f1332d21088] S:0, 0 -> 0, 0 -> 0 (24) fl 0 +Interfaces +LOOP addr: 0 netmask: 14 dfl: 0 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +KISS addr: 1 netmask: 0 dfl: 1 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +Server task started +``` + +Then, in another terminal, run `simple-send-usart` + +``` +./builddir/samples/posix/simple-send-usart/simple-send-usart +``` + +If it runs successfully, you’ll see a new message on the server +terminal: + +``` +Packet received on SERVER_PORT: abc +``` diff --git a/samples/posix/simple-send-usart/src/main.c b/samples/posix/simple-send-usart/src/main.c new file mode 100644 index 000000000..745cfec43 --- /dev/null +++ b/samples/posix/simple-send-usart/src/main.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#define SERVER_ADDR 1 +#define SERVER_PORT 10 + +#define CLIENT_ADDR 2 +#define DEVICE_NAME "/tmp/pty2" + +int main(int argc, char * argv[]) +{ + csp_usart_conf_t conf = { + .device = DEVICE_NAME, + .baudrate = 115200, + .databits = 8, + .stopbits = 1, + .paritysetting = 0, + }; + csp_iface_t * iface; + csp_conn_t * conn; + csp_packet_t * packet; + int ret; + + /* init */ + csp_init(); + + /* open */ + ret = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, CLIENT_ADDR, &iface); + if (ret != CSP_ERR_NONE) { + csp_print("failed to open: %d\n", ret); + return 1; + } + iface->is_default = 1; + + /* connect */ + conn = csp_connect(CSP_PRIO_NORM, SERVER_ADDR, SERVER_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + csp_print("Connection failed\n"); + return 1; + } + + /* prepare data */ + packet = csp_buffer_get_always(); + memcpy(packet->data, "abc", 4); + packet->length = 4; + + /* send */ + csp_send(conn, packet); + + /* close */ + csp_close(conn); + + return 0; +} From dd3bb8de4f38569897cd26adece64d36b6269e04 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 11 Nov 2024 22:06:54 +0900 Subject: [PATCH 061/348] samples: Add sample code for sending via CANbus This commit adds a simple sample code `send()`ing a packet via CANbus. Signed-off-by: Yasushi SHOJI --- samples/posix/CMakeLists.txt | 1 + .../posix/simple-send-canbus/CMakeLists.txt | 3 + samples/posix/simple-send-canbus/README.md | 86 +++++++++++++++++++ samples/posix/simple-send-canbus/src/main.c | 48 +++++++++++ 4 files changed, 138 insertions(+) create mode 100644 samples/posix/simple-send-canbus/CMakeLists.txt create mode 100644 samples/posix/simple-send-canbus/README.md create mode 100644 samples/posix/simple-send-canbus/src/main.c diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt index d5424a93f..442d9ebac 100644 --- a/samples/posix/CMakeLists.txt +++ b/samples/posix/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(simple-send-canbus) add_subdirectory(simple-send-usart) diff --git a/samples/posix/simple-send-canbus/CMakeLists.txt b/samples/posix/simple-send-canbus/CMakeLists.txt new file mode 100644 index 000000000..0882db6c1 --- /dev/null +++ b/samples/posix/simple-send-canbus/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(simple-send-canbus EXCLUDE_FROM_ALL src/main.c) +target_include_directories(simple-send-canbus PRIVATE ${csp_inc}) +target_link_libraries(simple-send-canbus PRIVATE csp) diff --git a/samples/posix/simple-send-canbus/README.md b/samples/posix/simple-send-canbus/README.md new file mode 100644 index 000000000..b679c62da --- /dev/null +++ b/samples/posix/simple-send-canbus/README.md @@ -0,0 +1,86 @@ +# Simple Send via CANbus + +This is a simple sample code to send `"abc"` to a server via the +CANbus interface. + +## How to Build + +``` +$ cmake -B builddir +$ ninja -C builddir simple-send-canbus csp_server +``` + +## How to Test + +To run without a real CANbus hardware, you can setup a vcan interface +on Linux: + +``` +$ sudo ip link add dev vcan0 type vcan +$ sudo ip link set up vcan0 +``` + +This command will create a virtual CANbus interface that a server and +a client can share. + +You’ll need the `ip` command. If you don't have it, install it with: + +``` +$ sudo apt-get install iproute2 +``` + +First, you need to run a CSP server: + +``` +$ ./builddir/examples/csp_server -c vcan0 -a 1 +Initialising CSP +INIT CAN: device: [vcan0], bitrate: 1000000, promisc: 1 +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +Connection table +[00 0x7fbfc89208e0] S:0, 0 -> 0, 0 -> 0 (17) fl 0 +[01 0x7fbfc89209f8] S:0, 0 -> 0, 0 -> 0 (18) fl 0 +[02 0x7fbfc8920b10] S:0, 0 -> 0, 0 -> 0 (19) fl 0 +[03 0x7fbfc8920c28] S:0, 0 -> 0, 0 -> 0 (20) fl 0 +[04 0x7fbfc8920d40] S:0, 0 -> 0, 0 -> 0 (21) fl 0 +[05 0x7fbfc8920e58] S:0, 0 -> 0, 0 -> 0 (22) fl 0 +[06 0x7fbfc8920f70] S:0, 0 -> 0, 0 -> 0 (23) fl 0 +[07 0x7fbfc8921088] S:0, 0 -> 0, 0 -> 0 (24) fl 0 +Interfaces +LOOP addr: 0 netmask: 14 dfl: 0 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +CAN addr: 1 netmask: 0 dfl: 1 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +Server task started +``` + +Then, in another terminal, run `simple-send-canbus` + +``` +$ ./builddir/samples/posix/simple-send-canbus/simple-send-canbus +INIT CAN: device: [vcan0], bitrate: 1000000, promisc: 1 +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +RTNETLINK answers: Operation not permitted +``` + +You see warnings like `RTNETLINK answers: Operation not permitted` +because your are not root. It should work without using `sudo`. Also +even you use `sudo`, you still see a few warnings because `vcan0` does +not support a few operations. + +If you successfully run `simple-send-canbus`, you see the following +message on the server terminal. + +``` +Packet received on SERVER_PORT: abc +``` diff --git a/samples/posix/simple-send-canbus/src/main.c b/samples/posix/simple-send-canbus/src/main.c new file mode 100644 index 000000000..43135c88d --- /dev/null +++ b/samples/posix/simple-send-canbus/src/main.c @@ -0,0 +1,48 @@ +#include +#include +#include + +#define SERVER_ADDR 1 +#define SERVER_PORT 10 + +#define CLIENT_ADDR 2 +#define DEVICE_NAME "vcan0" + +int main(int argc, char * argv[]) +{ + csp_iface_t * iface; + csp_conn_t * conn; + csp_packet_t * packet; + int ret; + + /* init */ + csp_init(); + + /* open */ + ret = csp_can_socketcan_open_and_add_interface(DEVICE_NAME, CSP_IF_CAN_DEFAULT_NAME, CLIENT_ADDR, 1000000, true, &iface); + if (ret != CSP_ERR_NONE) { + csp_print("failed to open: %d\n", ret); + return 1; + } + iface->is_default = 1; + + /* connect */ + conn = csp_connect(CSP_PRIO_NORM, SERVER_ADDR, SERVER_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + csp_print("Connection failed\n"); + return 1; + } + + /* prepare data */ + packet = csp_buffer_get_always(); + memcpy(packet->data, "abc", 4); + packet->length = 4; + + /* send */ + csp_send(conn, packet); + + /* close */ + csp_close(conn); + + return 0; +} From 585591472db873bec6e4982051e21d97928fcb54 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 11 Nov 2024 21:33:15 +0900 Subject: [PATCH 062/348] unittest: Rename "tests" directory to "unittests" Renames the "tests" directory to "unittests" for improved clarity, indicating that it specifically contains unit tests. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 2 +- {tests => unittests}/CMakeLists.txt | 0 {tests => unittests}/main.c | 0 {tests => unittests}/queue.c | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {tests => unittests}/CMakeLists.txt (100%) rename {tests => unittests}/main.c (100%) rename {tests => unittests}/queue.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 92811d3f0..44fa18999 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ target_compile_options(csp PRIVATE ${CSP_C_ARGS}) add_subdirectory(src) add_subdirectory(examples) -add_subdirectory(tests) +add_subdirectory(unittests) add_subdirectory(samples) if(${CSP_ENABLE_PYTHON3_BINDINGS}) diff --git a/tests/CMakeLists.txt b/unittests/CMakeLists.txt similarity index 100% rename from tests/CMakeLists.txt rename to unittests/CMakeLists.txt diff --git a/tests/main.c b/unittests/main.c similarity index 100% rename from tests/main.c rename to unittests/main.c diff --git a/tests/queue.c b/unittests/queue.c similarity index 100% rename from tests/queue.c rename to unittests/queue.c From 70e9e6145dc0690d6e0add2f86708b3e84182886 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 15 Nov 2024 09:19:38 +0900 Subject: [PATCH 063/348] csp_buffer: Rename buffer to buf Rename pointers to csp_skbf_t from "buffer" to "buf" in csp_buffer_get_actual() to align with other functions. No functionality has been changed. Signed-off-by: Yasushi SHOJI --- src/csp_buffer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 808d149bc..2cc73591c 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -51,28 +51,28 @@ static csp_packet_t * csp_buffer_get_actual(int reserve, int isr) { } /* Now fetch a buffer */ - csp_skbf_t * buffer = NULL; + csp_skbf_t * buf = NULL; if (isr) { int task_woken = 0; - csp_queue_dequeue_isr(csp_buffers, &buffer, &task_woken); + csp_queue_dequeue_isr(csp_buffers, &buf, &task_woken); } else { - csp_queue_dequeue(csp_buffers, &buffer, 0); + csp_queue_dequeue(csp_buffers, &buf, 0); } /* We might be out of buffers */ - if (buffer == NULL) { + if (buf == NULL) { csp_dbg_buffer_out++; return NULL; } - if (buffer != buffer->skbf_addr) { + if (buf != buf->skbf_addr) { csp_dbg_errno = CSP_DBG_ERR_CORRUPT_BUFFER; return NULL; } - buffer->refcount = 1; - csp_id_clear(&buffer->skbf_data.id); - return &buffer->skbf_data; + buf->refcount = 1; + csp_id_clear(&buf->skbf_data.id); + return &buf->skbf_data; } void csp_buffer_free_isr(void * packet) { From 60bb2c8f447d2221b0dbcaf30312cb15e4a34078 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 15 Nov 2024 09:33:39 +0900 Subject: [PATCH 064/348] csp_buffer: Re-initialize packet buffer in csp_buffer_get() Initialize the packet buffer before passing back to user. Signed-off-by: Yasushi SHOJI --- src/csp_buffer.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 2cc73591c..66a1ac4a6 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -36,6 +36,22 @@ void csp_buffer_init(void) { } } +static csp_packet_t * csp_packet_init(csp_packet_t * packet) +{ + +#if (CSP_BUFFER_ZERO_CLEAR) + memset(packet, 0, sizeof(csp_packet_t)); +#endif + + packet->length = 0; + packet->frame_begin = packet->data; + packet->frame_length = 0; + + csp_id_clear(&packet->id); + + return packet; +} + static csp_packet_t * csp_buffer_get_actual(int reserve, int isr) { /* Get buffers remaining */ @@ -71,8 +87,8 @@ static csp_packet_t * csp_buffer_get_actual(int reserve, int isr) { } buf->refcount = 1; - csp_id_clear(&buf->skbf_data.id); - return &buf->skbf_data; + + return csp_packet_init(&buf->skbf_data); } void csp_buffer_free_isr(void * packet) { From 328c8c26edb6e65e2f1b0ec38285bde679aa9e6e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 21 Nov 2024 15:05:16 +0900 Subject: [PATCH 065/348] cmake: Enable zeroing of csp_buffer by default Use `memset()` to clear newly allocated packet buffers by default. This ensures that the contents of a newly provided packet buffer are erased. If your application does not require this or cannot afford the overhead of `memset()`, you can disable it. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 1 + csp_autoconfig.h.in | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44fa18999..52f48cdb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(CSP_USE_RDP "Reliable Datagram Protocol" ON) option(CSP_USE_HMAC "Hash-based message authentication code" ON) option(CSP_USE_PROMISC "Promiscious mode" ON) option(CSP_USE_RTABLE "Use routing table" OFF) +option(CSP_BUFFER_ZERO_CLEAR "Zero out the packet buffer upon allocation" ON) option(CSP_ENABLE_PYTHON3_BINDINGS "Build Python3 binding" OFF) diff --git a/csp_autoconfig.h.in b/csp_autoconfig.h.in index d5449b51e..41cb59475 100644 --- a/csp_autoconfig.h.in +++ b/csp_autoconfig.h.in @@ -20,6 +20,7 @@ #cmakedefine01 CSP_USE_HMAC #cmakedefine01 CSP_USE_PROMISC #cmakedefine01 CSP_USE_RTABLE +#cmakedefine01 CSP_BUFFER_ZERO_CLEAR #cmakedefine01 CSP_HAVE_LIBSOCKETCAN #cmakedefine01 CSP_HAVE_LIBZMQ From db68e77fdc1c5b4b8facc6daa055f962723ce2de Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 21 Nov 2024 15:12:02 +0900 Subject: [PATCH 066/348] meson: Enable zeroing of csp_buffer by default Use `memset()` to clear newly allocated packet buffers by default. This ensures that the contents of a newly provided packet buffer are erased. If your application does not require this or cannot afford the overhead of `memset()`, you can disable it. Signed-off-by: Yasushi SHOJI --- meson.build | 1 + meson_options.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/meson.build b/meson.build index 29edb7327..d0a0cd1b7 100644 --- a/meson.build +++ b/meson.build @@ -27,6 +27,7 @@ conf.set10('CSP_HAVE_STDIO', get_option('have_stdio')) conf.set10('CSP_ENABLE_CSP_PRINT', get_option('enable_csp_print')) conf.set10('CSP_PRINT_STDIO', get_option('print_stdio')) conf.set10('CSP_USE_RTABLE', get_option('use_rtable')) +conf.set10('CSP_BUFFER_ZERO_CLEAR', get_option('buffer_zero_clear')) csp_deps = [] csp_sources = [] diff --git a/meson_options.txt b/meson_options.txt index dc87cdc30..3f94c807f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -13,6 +13,7 @@ option('enable_csp_print', type: 'boolean', value: true, description: 'Enable cs option('have_stdio', type: 'boolean', value: true, description: 'Use print and scan functions (some features may be missing without)') option('use_rtable', type: 'boolean', value: false, description: 'Allows to setup a list of static routes. End nodes do not need this. But radios and routers might') option('print_stdio', type: 'boolean', value: true, description: 'Use vprintf for csp_print_func') +option('buffer_zero_clear', type: 'boolean', value: true, description: 'Zero out the packet buffer upon allocation') # Memory tuning parameters: # Try to balance these so there is enough memory to handle expected system usage plus some, From 8874dc73714dc67cae04258617707a65e8226576 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 21 Nov 2024 15:18:20 +0900 Subject: [PATCH 067/348] waf: Enable zeroing of csp_buffer by default Use `memset()` to clear newly allocated packet buffers by default. This ensures that the contents of a newly provided packet buffer are erased. If your application does not require this or cannot afford the overhead of `memset()`, you can disable it. Signed-off-by: Yasushi SHOJI --- wscript | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wscript b/wscript index 0ab81fd63..481cf78ad 100644 --- a/wscript +++ b/wscript @@ -23,6 +23,7 @@ def options(ctx): gr.add_option('--disable-output', action='store_true', help='Disable CSP output') gr.add_option('--disable-print-stdio', action='store_true', help='Disable vprintf for csp_print_func') gr.add_option('--disable-stlib', action='store_true', help='Build objects only') + gr.add_option('--disable-buffer-zero-clear', action='store_false', help='Zero out the packet buffer upon allocation') gr.add_option('--enable-shlib', action='store_true', help='Build shared library') gr.add_option('--enable-rdp', action='store_true', help='Enable RDP support') gr.add_option('--enable-promisc', action='store_true', help='Enable promiscuous support') @@ -194,6 +195,7 @@ def configure(ctx): ctx.define('CSP_USE_HMAC', ctx.options.enable_hmac) ctx.define('CSP_USE_PROMISC', ctx.options.enable_promisc) ctx.define('CSP_USE_RTABLE', ctx.options.enable_rtable) + ctx.define('CSP_BUFFER_ZERO_CLEAR', ctx.options.disable_buffer_zero_clear) ctx.write_config_header('include/csp/autoconfig.h') From fb3c99efa9b42e5d80a2d05e6767f59d4ae1015b Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 12 Nov 2024 15:09:48 +0900 Subject: [PATCH 068/348] macro: Add __maybe_unused to silence unused variable warnings Add __maybe_unused to suppress warnings for variables that may be unused depending on build configurations, such as NDEBUG. Signed-off-by: Yasushi SHOJI --- src/csp_macro.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/csp_macro.h b/src/csp_macro.h index 90fa08080..338c5d6d2 100644 --- a/src/csp_macro.h +++ b/src/csp_macro.h @@ -7,6 +7,7 @@ #else #define __noinit __attribute__((section(".noinit"))) #define __packed __attribute__((__packed__)) +#define __maybe_unused __attribute__((__unused__)) #define __unused __attribute__((__unused__)) #define __weak __attribute__((__weak__)) From a582f3f04326c9ddfef8ce9979499f9dcc6e8dad Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 12 Nov 2024 15:12:19 +0900 Subject: [PATCH 069/348] interface: zmqhub: Remove trailing whitespaces Removed all trailing whitespaces in the file for cleaner formatting. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 7976d1803..e6405f8f6 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -39,8 +39,8 @@ int csp_zmqhub_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int csp_id_prepend(packet); - /** - * While a ZMQ context is thread safe, sockets are NOT threadsafe, so by sharing drv->publisher, we + /** + * While a ZMQ context is thread safe, sockets are NOT threadsafe, so by sharing drv->publisher, we * need to have a lock around any calls that uses that */ pthread_mutex_lock(&lock); int result = zmq_send(drv->publisher, packet->frame_begin, packet->frame_length, 0); @@ -219,7 +219,7 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr } int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport) { - + char pub[100]; csp_zmqhub_make_endpoint(host, subport, pub, sizeof(pub)); @@ -287,7 +287,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add /* Generate filters */ uint16_t hostmask = (1 << (csp_id_get_host_bits() - netmask)) - 1; - + /* Connect to server */ ret = zmq_connect(drv->publisher, pub); assert(ret == 0); @@ -317,7 +317,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &filt[i][2], 2); } - } + } /* Start RX thread */ From 4630d2084961a439c771b247538b6afd36eaa550 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 12 Nov 2024 15:13:23 +0900 Subject: [PATCH 070/348] interface: zmqhub: Use __maybe_unused macro for ret variable Apply the __maybe_unused macro to the ret variable, which is only checked in debug builds. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index e6405f8f6..786287e6b 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -12,6 +12,8 @@ #include +#include "../csp_macro.h" + /* ZMQ driver & interface */ typedef struct { pthread_t rx_thread; @@ -62,8 +64,7 @@ void * csp_zmqhub_task(void * param) { const uint32_t HEADER_SIZE = (csp_conf.version == 2) ? 6 : 4; while (1) { - int ret; - (void)ret; /* Silence unused variable warning (promoted to an error if -Werr) issued when building with NDEBUG (release with asserts turned off) */ + int __maybe_unused ret; zmq_msg_t msg; ret = zmq_msg_init_size(&msg, sizeof(packet->data) + HEADER_SIZE); From 1adccad68a864502aae0cd757f00b534abc9c842 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 12 Nov 2024 16:19:43 +0900 Subject: [PATCH 071/348] zephyr: sample: server-client: Use __maybe_unused macro Apply the __maybe_unused macro to the result variable, which is only checked when the logging subsystem is enabled. Signed-off-by: Yasushi SHOJI --- contrib/zephyr/samples/server-client/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index a08e1da6c..67a9d8daa 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -89,9 +89,8 @@ void client(void) { k_sleep(test_mode ? K_USEC(200000) : K_USEC(1000000)); /* Send ping to server, timeout 1000 mS, ping size 100 bytes */ - int result = csp_ping(server_address, 1000, 100, CSP_O_NONE); + int __maybe_unused result = csp_ping(server_address, 1000, 100, CSP_O_NONE); LOG_INF("Ping address: %u, result %d [mS]", server_address, result); - (void) result; /* Send reboot request to server, the server has no actual implementation of csp_sys_reboot() and fails to reboot */ csp_reboot(server_address); From e2b4013cf3eb3fb6beb9907127307395063ef109 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 14:36:56 +0900 Subject: [PATCH 072/348] cmake: Fix description for CSP_PORT_MAX_BIND Updated the description of CSP_PORT_MAX_BIND to better convey its purpose: defining the maximum number of ports the system can use with the csp_bind() function. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52f48cdb3..22b3084f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ option(CSP_ENABLE_CSP_PRINT "Enable csp_print() function" ON) option(CSP_PRINT_STDIO "Use vprintf() for csp_print() function" ON) set(CSP_QFIFO_LEN 15 CACHE STRING "Length of incoming queue for router task") -set(CSP_PORT_MAX_BIND 16 CACHE STRING "Length of incoming queue for router task") +set(CSP_PORT_MAX_BIND 16 CACHE STRING "Maximum number of bindable ports") set(CSP_CONN_RXQUEUE_LEN 16 CACHE STRING "Number of packets in connection queue") set(CSP_CONN_MAX 8 CACHE STRING "Number of new connections on socket queue") set(CSP_BUFFER_SIZE 256 CACHE STRING "Bytes in each packet buffer") From aa2651ab599ce9cc4a5f3c99c90ace6763f6f87c Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 14:39:15 +0900 Subject: [PATCH 073/348] meson: Fix description for CSP_PORT_MAX_BIND Updated the description of CSP_PORT_MAX_BIND to better convey its purpose: defining the maximum number of ports the system can use with the csp_bind() function. Signed-off-by: Yasushi SHOJI --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 3f94c807f..582916481 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,7 +19,7 @@ option('buffer_zero_clear', type: 'boolean', value: true, description: 'Zero out # Try to balance these so there is enough memory to handle expected system usage plus some, # while avoiding over-allocating too much memory, that would be better used elsewhere option('qfifo_len', type: 'integer', value: 15, description: 'Length of incoming queue for router task') -option('port_max_bind', type: 'integer', value: 16, description: 'Length of incoming queue for router task') +option('port_max_bind', type: 'integer', value: 16, description: 'Maximum number of bindable ports') option('conn_rxqueue_len', type: 'integer', value: 15, description: 'Number of packets in connection queue') option('conn_max', type: 'integer', value: 8, description: 'Number of new connections on socket queue') option('buffer_size', type: 'integer', value: 256, description: 'Bytes in each packet buffer') From 59db9f826a496b7965732f56ccbe368dc6b4afdb Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 05:27:54 +0900 Subject: [PATCH 074/348] zephyr: Kconfig: Organize device-related entries under menus Move UART and CAN bus-related entries under their respective menus for improved clarity and structure. Signed-off-by: Yasushi SHOJI --- contrib/zephyr/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index 08780701c..e5f6fb6db 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -40,6 +40,8 @@ config CSP_MUTEX_TIMEOUT help Waiting period to lock the mutex. +menu "UART" + config CSP_UART_RX_INTERVAL int "Interval (nesc) at which the receiving thread runs." default 1000 @@ -70,6 +72,10 @@ config CSP_UART_RX_THREAD_NUM help Number of thread stacks for CSP UART RX. +endmenu + +menu "CANbus" + if CAN config CSP_HAVE_CAN @@ -104,4 +110,6 @@ config CSP_CAN_RX_THREAD_NUM endif # CAN +endmenu + endif # LIBCSP From b3cd97c68e41d49af9c1e2352fccaa5e66284ae5 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 05:45:48 +0900 Subject: [PATCH 075/348] zephyr: Kconfig: Add memory tuning parameters With the Zephyr build system, we use Kconfig to set various parameters. However, memory tuning parameters were not integrated into Kconfig when the Zephyr port was merged. This commit enables memory tuning parameters to be configured through Kconfig. Signed-off-by: Yasushi SHOJI --- contrib/zephyr/CMakeLists.txt | 10 ++++++ contrib/zephyr/Kconfig | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/contrib/zephyr/CMakeLists.txt b/contrib/zephyr/CMakeLists.txt index aec543e75..19be58fd3 100644 --- a/contrib/zephyr/CMakeLists.txt +++ b/contrib/zephyr/CMakeLists.txt @@ -9,6 +9,16 @@ if(CONFIG_LIBCSP) # predefine it set(HAVE_SYS_SOCKET_H OFF) + # Cache entries. see the top level CMakeLists.txt + set(CSP_QFIFO_LEN CONFIG_CSP_QFIFO_LEN CACHE STRING "") + set(CSP_PORT_MAX_BIND CONFIG_CSP_PORT_MAX_BIND CACHE STRING "") + set(CSP_CONN_RXQUEUE_LEN CONFIG_CSP_CONN_RXQUEUE_LEN CACHE STRING "") + set(CSP_CONN_MAX CONFIG_CSP_CONN_MAX CACHE STRING "") + set(CSP_BUFFER_SIZE CONFIG_CSP_BUFFER_SIZE CACHE STRING "") + set(CSP_BUFFER_COUNT CONFIG_CSP_BUFFER_COUNT CACHE STRING "") + set(CSP_RDP_MAX_WINDOW CONFIG_CSP_RDP_MAX_WINDOW CACHE STRING "") + set(CSP_RTABLE_SIZE CONFIG_CSP_RTABLE_SIZE CACHE STRING "") + if(CONFIG_CSP_USE_RTABLE) set(CSP_USE_RTABLE ON) endif() diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index e5f6fb6db..ded4f3eed 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -40,6 +40,63 @@ config CSP_MUTEX_TIMEOUT help Waiting period to lock the mutex. +menu "Memory Tuning Parameters" + +config CSP_QFIFO_LEN + int "Length of incoming queue for router task" + default 15 + help + This value specifies the length of queue for incomming + packets that are used by csp_qfifo_read() and + csp_qfifo_write(). + +config CSP_PORT_MAX_BIND + int "Number of port to bind" + default 16 + help + Number of ports available for an interface. + +config CSP_CONN_RXQUEUE_LEN + int "Number of packets in connection queue" + default 16 + help + This value specifies the length of the queue for packets in + a connection. + +config CSP_CONN_MAX + int "Number of new connections on socket queue" + default 8 + help + This value specifies the number of new connections on socket + queue. + +config CSP_BUFFER_SIZE + int "Bytes in each packet buffer" + default 256 + help + This value specifies the number bytes in each packet buffer. + +config CSP_BUFFER_COUNT + int "Number of total packet buffers" + default 15 + help + This value specifies the number of total packet buffers in + the system. + +config CSP_RDP_MAX_WINDOW + int "Max window size for RDP" + default 5 + help + This value specifies the window size of RDP. + +config CSP_RTABLE_SIZE + int "Number of elements in routing table" + default 10 + help + This value specifies the number of entries in the routing + table. +endmenu + menu "UART" config CSP_UART_RX_INTERVAL From 18c36ac179f11d769e5661c7b5a530e3eba5e230 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 15:18:20 +0900 Subject: [PATCH 076/348] csp_conn: Kill all trailing white spaces Kill all trailing white spaces in csp_conn.c so that the following change will not trigger errors on checking it on GitHub Actions. Signed-off-by: Yasushi SHOJI --- src/csp_conn.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/csp_conn.c b/src/csp_conn.c index 6cc497613..e2706cf78 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -106,7 +106,7 @@ csp_conn_t * csp_conn_find_existing(csp_id_t * id) { /* Outgoing connections are uniquely defined by the source port, * So only the incoming destination port must match. This means * that responses to broadcast addresses, are accepted as long - * as the incoming port matches the unique source port of the + * as the incoming port matches the unique source port of the * connection */ if (conn->type == CONN_CLIENT) { @@ -118,7 +118,7 @@ csp_conn_t * csp_conn_find_existing(csp_id_t * id) { * destination port, as well as the source node. Incoming * connections can never come from a brodcast address */ } else { - + /* Connection must match dport */ if (conn->idin.dport != id->dport) continue; @@ -133,7 +133,7 @@ csp_conn_t * csp_conn_find_existing(csp_id_t * id) { } - + /* All conditions found! */ return conn; @@ -242,7 +242,7 @@ int csp_conn_close(csp_conn_t * conn, uint8_t closed_by) { /* Set to closed */ conn->state = CONN_CLOSED; - + return CSP_ERR_NONE; } @@ -250,17 +250,17 @@ csp_conn_t * csp_connect(uint8_t prio, uint16_t dest, uint8_t dport, uint32_t ti /* Force options on all connections */ opts |= csp_conf.conn_dfl_so; - + /* Generate identifier */ csp_id_t incoming_id, outgoing_id; /* Use 0 as incoming id (this disables the input filter on destination node) * This means that for this outgoing connection, we accept the answer coming to whatever address - * the outgoing interface has. CSP does not support "source address" on outgoing connections - * so the outgoing source address will be automatically applied after outgoing routing + * the outgoing interface has. CSP does not support "source address" on outgoing connections + * so the outgoing source address will be automatically applied after outgoing routing * selects which interface the packet will leavve from */ - incoming_id.dst = 0; - outgoing_id.src = 0; + incoming_id.dst = 0; + outgoing_id.src = 0; incoming_id.pri = prio; outgoing_id.pri = prio; @@ -367,7 +367,7 @@ void csp_conn_print_table(void) { conn->idin.dport, conn->idin.sport, conn->sport_outgoing, conn->idin.flags); #if (CSP_USE_RDP) if (conn->idin.flags & CSP_FRDP) { - csp_print("\tRDP: S:%d (closed by 0x%x), rcv %u, snd %u, win %" PRIu32 "\n", + csp_print("\tRDP: S:%d (closed by 0x%x), rcv %u, snd %u, win %" PRIu32 "\n", conn->rdp.state, conn->rdp.closed_by, conn->rdp.rcv_cur, conn->rdp.snd_una, conn->rdp.window_size); } #endif From 7d8b9d7c01ad563ff96723aab2ef65a31142fa97 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 22 Nov 2024 15:20:04 +0900 Subject: [PATCH 077/348] csp_conn: Fix typo Fix "amd" to "and". Signed-off-by: Yasushi SHOJI --- src/csp_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_conn.c b/src/csp_conn.c index e2706cf78..812ae42a0 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -114,7 +114,7 @@ csp_conn_t * csp_conn_find_existing(csp_id_t * id) { if (conn->idin.dport != id->dport) continue; - /* Incoming connections are uniquely defined by the source amd + /* Incoming connections are uniquely defined by the source and * destination port, as well as the source node. Incoming * connections can never come from a brodcast address */ } else { From 3d98f74e4e80631a3db707c32c2644aa4fe5f6bd Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 27 Nov 2024 14:04:12 +0900 Subject: [PATCH 078/348] unittest: Add test to verify allocated buffer is clean In #734, it was reported that a newly allocated packet buffer retained old contents. This issue was resolved in #735. This commit adds a unit test to ensure that newly allocated buffers are always initialized as clean. Signed-off-by: Yasushi SHOJI --- unittests/CMakeLists.txt | 1 + unittests/buffer.c | 44 ++++++++++++++++++++++++++++++++++++++++ unittests/main.c | 2 ++ 3 files changed, 47 insertions(+) create mode 100644 unittests/buffer.c diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 9720cfd9a..ce100fdb1 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -4,5 +4,6 @@ if(CHECK_FOUND) target_sources(csp_tests PRIVATE main.c queue.c + buffer.c ) endif() diff --git a/unittests/buffer.c b/unittests/buffer.c new file mode 100644 index 000000000..d978020f1 --- /dev/null +++ b/unittests/buffer.c @@ -0,0 +1,44 @@ +#include +#include "../include/csp/csp.h" + +/* https://github.com/libcsp/libcsp/issues/734 */ +START_TEST(test_alloc_clean_734) +{ + uint8_t expected[CSP_BUFFER_SIZE]; + + csp_init(); + + /* use all buffer and free them */ + for (unsigned int i = 0; i < CSP_BUFFER_COUNT; i++) { + csp_packet_t * packet = csp_buffer_get_always(); + memset(packet->data, 0, sizeof(packet->data)); /* clear buffer data */ + memcpy(packet->data, "previous_data!!", i+1); /* put some data inside */ + packet->length = i + 1; + csp_buffer_free(packet); + } + + memset(expected, 0, sizeof(expected)); + + /* access the data of previously used buffers */ + for (unsigned int i = 0; i < CSP_BUFFER_COUNT; i++) { + csp_packet_t * packet = csp_buffer_get_always(); + ck_assert_mem_eq(packet->data, expected, sizeof(expected)); + ck_assert_int_eq(packet->length, 0); + csp_buffer_free(packet); + } +} +END_TEST + +Suite * buffer_suite(void) +{ + Suite *s; + TCase *tc_alloc; + + s = suite_create("Packet Buffer"); + + tc_alloc = tcase_create("allocate"); + tcase_add_test(tc_alloc, test_alloc_clean_734); + suite_add_tcase(s, tc_alloc); + + return s; +} diff --git a/unittests/main.c b/unittests/main.c index 5600675a5..947a35a4e 100644 --- a/unittests/main.c +++ b/unittests/main.c @@ -6,6 +6,7 @@ #define DEFAULT_PRINT_VERBOSITY (CK_NORMAL) Suite * queue_suite(void); +Suite * buffer_suite(void); static struct option long_options[] = { {"verbose", no_argument, 0, 'V'}, @@ -45,6 +46,7 @@ int main(int argc, char *argv[]) sr = srunner_create(NULL); srunner_add_suite(sr, queue_suite()); + srunner_add_suite(sr, buffer_suite()); srunner_run_all(sr, print_verbosity); number_failed = srunner_ntests_failed(sr); From c9f8c43efd3f3664193d709e9a92ec8211a1fb07 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 29 Nov 2024 07:09:10 +0200 Subject: [PATCH 079/348] csp_rdp: Change return of csp_rdp_rx_queue_add Change the return value of `csp_rdp_rx_queue_add` by replacing raw integers with error codes defined in `csp_error.h` for better consistency. --- src/csp_rdp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index eb9c18700..fcce2d449 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -278,11 +278,11 @@ static inline int csp_rdp_rx_queue_add(csp_conn_t * conn, csp_packet_t * packet, if (csp_rdp_seq_in_rx_queue(conn, seq_nr)) { csp_rdp_protocol("RDP %p: Already exists in RX queue %u\n", (void *)conn, seq_nr); - return -1; + return CSP_ERR_USED; } csp_rdp_protocol("RDP %p: Add to RX queue %u\n", (void *) conn, seq_nr); csp_rdp_queue_rx_add(conn, packet); - return 0; + return CSP_ERR_NONE; } @@ -668,7 +668,7 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { /* If message is not in sequence, send EACK and store packet */ if (rx_header->seq_nr != (uint16_t)(conn->rdp.rcv_cur + 1)) { - if (csp_rdp_rx_queue_add(conn, packet, rx_header->seq_nr) != 0) { + if (csp_rdp_rx_queue_add(conn, packet, rx_header->seq_nr) != CSP_ERR_NONE) { csp_rdp_check_ack(conn); goto discard_open; } From e89d3172818dd97c467e0da5e0d349429c3c8702 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 29 Nov 2024 07:10:45 +0200 Subject: [PATCH 080/348] csp_rdp: Modify function return value check Modify the return value check for `csp_conn_enqueue_packet` to compare against `CSP_ERR_NONE`, aligning with its usage of standard error codes. --- src/csp_rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index fcce2d449..afe9fbbee 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -199,7 +199,7 @@ static inline int csp_rdp_receive_data(csp_conn_t * conn, csp_packet_t * packet) csp_rdp_header_remove(packet); /* Enqueue data */ - if (csp_conn_enqueue_packet(conn, packet) < 0) { + if (csp_conn_enqueue_packet(conn, packet) != CSP_ERR_NONE) { csp_dbg_conn_ovf++; csp_rdp_error("RDP %p: Conn RX buffer full\n", (void *)conn); return CSP_ERR_NOBUFS; From cb08cf7ce2ced5ef590da44f110f244ba46c2f28 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 29 Nov 2024 07:11:53 +0200 Subject: [PATCH 081/348] csp_rdp: Fix invalid connection creation Update SYN flag check to ensure only the RDP_SYN flag is set, aligning with the RDP specification in RFC 908 (section "5.1 Connection Establishment"). The previous implementation allowed SYN|ACK packets to be misinterpreted as SYN packets, leading to incorrect handling of packet data as connection parameters. The updated check masks out the upper bits of the flags field and requires an exact match with RDP_SYN. This change prevents unintended connection creation, reducing excessive packet and connection allocation durations. --- src/csp_rdp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index afe9fbbee..0ae667140 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -497,8 +497,12 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { */ case RDP_CLOSED: { + /* Clear ephemeral data added by csp_rdp_send_cmp(). + RDP flags are located in the lower 4 bits. */ + uint8_t rx_header_flags = rx_header->flags & 0x0f; + /* No SYN flag set while in closed. Inform by sending back RST */ - if (!(rx_header->flags & RDP_SYN)) { + if (rx_header_flags != RDP_SYN) { csp_rdp_protocol("RDP %p: Not SYN received in CLOSED state. Discarding packet\n", (void *)conn); csp_rdp_send_cmp(conn, NULL, RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); goto discard_close; From cd545cfe74493099122be2e2673c01197f0b7e53 Mon Sep 17 00:00:00 2001 From: parsonsjonathan Date: Tue, 19 Nov 2024 16:41:41 -0400 Subject: [PATCH 082/348] README: fix typos Fix typos in the README.md. - 'mater'->'master' - 'eachother'->'each other' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb0c3feb7..76be09863 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ header. The small footprint and simple implementation allows a small 8-bit system to be fully connected on the network. This allows all subsystems to provide their services on the same network level, without any master node required. Using a service oriented architecture has -several advantages compared to the traditional mater/slave topology used +several advantages compared to the traditional master/slave topology used on many cubesats. - Standardised network protocol: All subsystems can communicate with - eachother (multi-master) + each other (multi-master) - Service loose coupling: Services maintain a relationship that minimizes dependencies between subsystems - Service abstraction: Beyond descriptions in the service contract, From be5f0dede991e60b05dc90af45d5ab44d2a29f48 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 00:12:15 +0900 Subject: [PATCH 083/348] waf: Remove ctx.env.OS The `ctx.env.OS` variable is only used for POSIX-specific code in the examples directory. Replace `ctx.env.OS` with the hardcoded value "posix" to simplify the code. Signed-off-by: Yasushi SHOJI --- wscript | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/wscript b/wscript index 481cf78ad..7a556e809 100644 --- a/wscript +++ b/wscript @@ -86,9 +86,6 @@ def configure(ctx): # Setup default include path and any extra defined ctx.env.append_unique('INCLUDES_CSP', ['include', 'src'] + ctx.options.includes.split(',')) - # Store OS as env variable - ctx.env.OS = ctx.options.with_os - # Platform/OS specifics if ctx.options.with_os == 'posix': ctx.env.append_unique('LIBS', ['rt', 'pthread', 'util']) @@ -238,19 +235,19 @@ def build(ctx): if ctx.env.ENABLE_EXAMPLES: ctx.program(source=['examples/csp_server_client.c', - 'examples/csp_server_client_{0}.c'.format(ctx.env.OS)], + 'examples/csp_server_client_posix.c'], target='examples/csp_server_client', lib=ctx.env.LIBS, use='csp') ctx.program(source=['examples/csp_server.c', - 'examples/csp_server_{0}.c'.format(ctx.env.OS)], + 'examples/csp_server_posix.c'], target='examples/csp_server', lib=ctx.env.LIBS, use='csp') ctx.program(source=['examples/csp_client.c', - 'examples/csp_client_{0}.c'.format(ctx.env.OS)], + 'examples/csp_client_posix.c'], target='examples/csp_client', lib=ctx.env.LIBS, use='csp') From bdeb6efe0a2babde784b742eb47ca0fb48ec09ba Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 22:24:29 +0900 Subject: [PATCH 084/348] examples: cmake: Consolidate `csp_arch` and `zmqproxy` under Linux Reorganized the CMakeLists.txt to move csp_arch` and zmqproxy into the `if(CMAKE_SYSTEM_NAME STREQUAL "Linux")` condition block. `csp_arch` was outside the Linux condition block. Thus, technically, it supported Zephyr and FreeRTOS. However, Zephyr has its own examples, and we don't have a FreeRTOS target to test against. `zmqproxy` was already in a different Linux-specific block. Merging it here simplifies the structure without altering functionality. This change ensures better maintainability and consistent Linux-specific targeting for executables. Signed-off-by: Yasushi SHOJI --- examples/CMakeLists.txt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3e070795a..a13136134 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,23 +1,21 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_executable(csp_arch EXCLUDE_FROM_ALL csp_arch.c) add_executable(csp_server_client EXCLUDE_FROM_ALL csp_server_client.c csp_server_client_posix.c) add_executable(csp_server EXCLUDE_FROM_ALL csp_server.c csp_server_posix.c) add_executable(csp_client EXCLUDE_FROM_ALL csp_client.c csp_client_posix.c) add_executable(csp_bridge_can2udp EXCLUDE_FROM_ALL csp_bridge_can2udp.c) + add_executable(zmqproxy EXCLUDE_FROM_ALL zmqproxy.c) + + target_include_directories(csp_arch PRIVATE ${csp_inc}) target_include_directories(csp_server_client PRIVATE ${csp_inc}) target_include_directories(csp_server PRIVATE ${csp_inc}) target_include_directories(csp_client PRIVATE ${csp_inc}) + target_include_directories(zmqproxy PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) + + target_link_libraries(csp_arch PRIVATE csp) target_link_libraries(csp_server_client PRIVATE csp Threads::Threads) target_link_libraries(csp_server PRIVATE csp Threads::Threads) target_link_libraries(csp_client PRIVATE csp Threads::Threads) target_link_libraries(csp_bridge_can2udp PRIVATE csp) -endif() - -add_executable(csp_arch EXCLUDE_FROM_ALL csp_arch.c) -target_include_directories(csp_arch PRIVATE ${csp_inc}) -target_link_libraries(csp_arch PRIVATE csp) - -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - add_executable(zmqproxy EXCLUDE_FROM_ALL zmqproxy.c) - target_include_directories(zmqproxy PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) target_link_libraries(zmqproxy PRIVATE csp Threads::Threads ${LIBZMQ_LIBRARIES}) endif() From b1d7457244b1858200c1c5e2a3e425a4666bbe09 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 22:48:35 +0900 Subject: [PATCH 085/348] examples: zmqproxy: Replace assert with perror and exit Replaced `assert()` with `perror()` and `exit()` to handle errors explicitly. When `NDEBUG` is defined, `assert()` ignores its argument, making the variable `ret` unused. While options like `(void)ret;` or `__maybe_unused` could address this, they add unnecessary complexity for examples. Since the purpose of `assert()` is to print an error and terminate the program, this change achieves the same behavior in a simpler, more explicit way. Signed-off-by: Yasushi SHOJI --- examples/zmqproxy.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index 4b1777a6b..af665bf61 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -24,9 +24,16 @@ static void * task_capture(void * ctx) { /* Subscriber (RX) */ void * subscriber = zmq_socket(ctx, ZMQ_SUB); ret = zmq_connect(subscriber, pub_str); - assert(ret == 0); + if (ret < 0) { + perror("Unable to connect"); + exit(1); + } ret = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0); - assert(ret == 0); + if (ret < 0) { + perror("Failed to call setsockopt"); + exit(1); + } + /* Allocated 'raw' CSP packet */ csp_packet_t * packet = malloc(1024); @@ -126,13 +133,19 @@ int main(int argc, char ** argv) { void * frontend = zmq_socket(ctx, ZMQ_XSUB); assert(frontend); ret = zmq_bind(frontend, sub_str); - assert(ret == 0); + if (ret < 0) { + perror("Failed to bind to ZMQ_XSUB"); + return 1; + } csp_print("Subscriber task listening on %s\n", sub_str); void * backend = zmq_socket(ctx, ZMQ_XPUB); - assert(backend); + ret = zmq_bind(backend, pub_str); - assert(ret == 0); + if (ret < 0) { + perror("Failed to bind to ZMQ_XPUB"); + return 1; + } csp_print("Publisher task listening on %s\n", pub_str); pthread_t capworker; From f2be792844b68e920bd19939acea4880585c887e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 23:00:48 +0900 Subject: [PATCH 086/348] examples: csp_arch: Replace assert() with ASSERT() Unlike zmqproxy, csp_arch is a test tool. Thus, instead of properly handling errors, introduce a new macro ASSERT() to conditionally handle the given variable. Signed-off-by: Yasushi SHOJI --- examples/csp_arch.c | 55 ++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/examples/csp_arch.c b/examples/csp_arch.c index 75b54db37..b9766dcba 100644 --- a/examples/csp_arch.c +++ b/examples/csp_arch.c @@ -1,4 +1,3 @@ - #include #include #include @@ -8,6 +7,12 @@ #include #include +#ifdef NDEBUG +#define ASSERT(expr) ((void)(expr)) +#else +#define ASSERT(expr) assert(expr) +#endif + void csp_panic(const char * msg) { csp_print("csp_panic: %s\n", msg); exit(1); @@ -19,7 +24,7 @@ int main(int argc, char * argv[]) { // clock csp_timestamp_t csp_clock = {0}; csp_clock_get_time(&csp_clock); - assert(csp_clock.tv_sec != 0); + ASSERT(csp_clock.tv_sec != 0); csp_print("csp_clock_get_time(..) -> sec:nsec = %"PRIu32":%"PRIu32"\n", csp_clock.tv_sec, csp_clock.tv_nsec); // relative time @@ -28,10 +33,10 @@ int main(int argc, char * argv[]) { const uint32_t sec1 = csp_get_s(); const uint32_t sec2 = csp_get_s_isr(); sleep(2); - assert(csp_get_ms() >= (msec1 + 500)); - assert(csp_get_ms_isr() >= (msec2 + 500)); - assert(csp_get_s() >= (sec1 + 1)); - assert(csp_get_s_isr() >= (sec2 + 1)); + ASSERT(csp_get_ms() >= (msec1 + 500)); + ASSERT(csp_get_ms_isr() >= (msec2 + 500)); + ASSERT(csp_get_s() >= (sec1 + 1)); + ASSERT(csp_get_s_isr() >= (sec2 + 1)); // queue handling uint32_t value; @@ -39,32 +44,32 @@ int main(int argc, char * argv[]) { csp_queue_handle_t q; char buf[3 * sizeof(value)]; q = csp_queue_create_static(3, sizeof(value), buf, &sq); - assert(csp_queue_size(q) == 0); - assert(csp_queue_size_isr(q) == 0); - assert(csp_queue_dequeue(q, &value, 0) == CSP_QUEUE_ERROR); - assert(csp_queue_dequeue(q, &value, 200) == CSP_QUEUE_ERROR); - assert(csp_queue_dequeue_isr(q, &value, NULL) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_size(q) == 0); + ASSERT(csp_queue_size_isr(q) == 0); + ASSERT(csp_queue_dequeue(q, &value, 0) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_dequeue(q, &value, 200) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_dequeue_isr(q, &value, NULL) == CSP_QUEUE_ERROR); value = 1; - assert(csp_queue_enqueue(q, &value, 0) == CSP_QUEUE_OK); + ASSERT(csp_queue_enqueue(q, &value, 0) == CSP_QUEUE_OK); value = 2; - assert(csp_queue_enqueue(q, &value, 200) == CSP_QUEUE_OK); + ASSERT(csp_queue_enqueue(q, &value, 200) == CSP_QUEUE_OK); value = 3; - assert(csp_queue_enqueue_isr(q, &value, NULL) == CSP_QUEUE_OK); - assert(csp_queue_size(q) == 3); - assert(csp_queue_size_isr(q) == 3); + ASSERT(csp_queue_enqueue_isr(q, &value, NULL) == CSP_QUEUE_OK); + ASSERT(csp_queue_size(q) == 3); + ASSERT(csp_queue_size_isr(q) == 3); value = 10; - assert(csp_queue_enqueue(q, &value, 0) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_enqueue(q, &value, 0) == CSP_QUEUE_ERROR); value = 20; - assert(csp_queue_enqueue(q, &value, 200) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_enqueue(q, &value, 200) == CSP_QUEUE_ERROR); value = 30; - assert(csp_queue_enqueue_isr(q, &value, NULL) == CSP_QUEUE_ERROR); + ASSERT(csp_queue_enqueue_isr(q, &value, NULL) == CSP_QUEUE_ERROR); value = 100; - assert(csp_queue_dequeue(q, &value, 0) == CSP_QUEUE_OK); - assert(value == 1); - assert(csp_queue_dequeue(q, &value, 200) == CSP_QUEUE_OK); - assert(value == 2); - assert(csp_queue_dequeue_isr(q, &value, NULL) == CSP_QUEUE_OK); - assert(value == 3); + ASSERT(csp_queue_dequeue(q, &value, 0) == CSP_QUEUE_OK); + ASSERT(value == 1); + ASSERT(csp_queue_dequeue(q, &value, 200) == CSP_QUEUE_OK); + ASSERT(value == 2); + ASSERT(csp_queue_dequeue_isr(q, &value, NULL) == CSP_QUEUE_OK); + ASSERT(value == 3); return 0; } From 08df1419fdff802113d1a6303116283e413636a3 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 23:04:06 +0900 Subject: [PATCH 087/348] examples: csp_arch: Remove unused argc and argv The argc and argv parameters are unused in main(). This change removes them to simplify the code. Signed-off-by: Yasushi SHOJI --- examples/csp_arch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/csp_arch.c b/examples/csp_arch.c index b9766dcba..4842f94cb 100644 --- a/examples/csp_arch.c +++ b/examples/csp_arch.c @@ -18,8 +18,7 @@ void csp_panic(const char * msg) { exit(1); } -int main(int argc, char * argv[]) { - +int main(void) { // clock csp_timestamp_t csp_clock = {0}; From 3cbab506927d874aca1cacb5369bbb594dfcf5d8 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 23:10:53 +0900 Subject: [PATCH 088/348] examples: Remove csp_server_client_windows.c Windows support has been removed for a while. Remove it. Signed-off-by: Yasushi SHOJI --- examples/csp_server_client_windows.c | 48 ---------------------------- 1 file changed, 48 deletions(-) delete mode 100644 examples/csp_server_client_windows.c diff --git a/examples/csp_server_client_windows.c b/examples/csp_server_client_windows.c deleted file mode 100644 index 94ce03d05..000000000 --- a/examples/csp_server_client_windows.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include - -void server(void); -void client(void); - -static int csp_win_thread_create(unsigned int (* routine)(void *)) { - - uintptr_t ret = _beginthreadex(NULL, 0, routine, NULL, 0, NULL); - if (ret == 0) { - return CSP_ERR_NOMEM; - } - - return CSP_ERR_NONE; -} - -static unsigned int task_router(void * param) { - - /* Here there be routing */ - while (1) { - csp_route_work(); - } - - return 0; -} - -static unsigned int task_server(void * param) { - server(); - return 0; -} - -static unsigned int task_client(void * param) { - client(); - return 0; -} - -int router_start(void) { - return csp_win_thread_create(task_router); -} - -int server_start(void) { - return csp_win_thread_create(task_server); -} - -int client_start(void) { - return csp_win_thread_create(task_client); -} From e0336805feacd31de86890c53d8446bf395c9546 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 1 Dec 2024 23:20:57 +0900 Subject: [PATCH 089/348] examples: Consolidate csp_*_posix.c into csp_posix_helper.c The files `csp_server_client_posix.c`, `csp_server_posix.c`, and `csp_client_posix.c` contained identical functions, resulting in redundancy. This update consolidates these functions into a single source file, `csp_posix_helper.c`, which is linked as a common object file. The binaries `csp_server_client`, `csp_server`, and `csp_client` now utilize this shared object, reducing code duplication. To further simplify the examples, `server_start()` and `client_start()` have been removed. The `server()` and `client()` function signatures were updated to comply with `pthread_create()`'s routine signature, enabling direct use of `csp_pthread_create()`. Additionally, a new header file, `csp_posix_helper.h`, has been introduced to replace forward declarations previously scattered across the `.c` files. This refactor simplifies maintenance and reduces duplication in the codebase. Signed-off-by: Yasushi SHOJI --- examples/CMakeLists.txt | 15 +++-- examples/csp_client.c | 4 +- ...{csp_client_posix.c => csp_posix_helper.c} | 4 +- examples/csp_posix_helper.h | 4 ++ examples/csp_server.c | 13 ++-- examples/csp_server_client.c | 26 ++++---- examples/csp_server_client_posix.c | 60 ------------------- examples/csp_server_posix.c | 50 ---------------- examples/meson.build | 14 ++++- wscript | 19 +++--- 10 files changed, 57 insertions(+), 152 deletions(-) rename examples/{csp_client_posix.c => csp_posix_helper.c} (90%) create mode 100644 examples/csp_posix_helper.h delete mode 100644 examples/csp_server_client_posix.c delete mode 100644 examples/csp_server_posix.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a13136134..d7d6fa163 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,11 +1,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + + add_library(csp_posix_helper OBJECT csp_posix_helper.c) add_executable(csp_arch EXCLUDE_FROM_ALL csp_arch.c) - add_executable(csp_server_client EXCLUDE_FROM_ALL csp_server_client.c csp_server_client_posix.c) - add_executable(csp_server EXCLUDE_FROM_ALL csp_server.c csp_server_posix.c) - add_executable(csp_client EXCLUDE_FROM_ALL csp_client.c csp_client_posix.c) + add_executable(csp_server_client EXCLUDE_FROM_ALL csp_server_client.c) + add_executable(csp_server EXCLUDE_FROM_ALL csp_server.c) + add_executable(csp_client EXCLUDE_FROM_ALL csp_client.c) add_executable(csp_bridge_can2udp EXCLUDE_FROM_ALL csp_bridge_can2udp.c) add_executable(zmqproxy EXCLUDE_FROM_ALL zmqproxy.c) + target_include_directories(csp_posix_helper PRIVATE ${csp_inc}) target_include_directories(csp_arch PRIVATE ${csp_inc}) target_include_directories(csp_server_client PRIVATE ${csp_inc}) target_include_directories(csp_server PRIVATE ${csp_inc}) @@ -13,9 +16,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_include_directories(zmqproxy PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) target_link_libraries(csp_arch PRIVATE csp) - target_link_libraries(csp_server_client PRIVATE csp Threads::Threads) - target_link_libraries(csp_server PRIVATE csp Threads::Threads) - target_link_libraries(csp_client PRIVATE csp Threads::Threads) + target_link_libraries(csp_server_client PRIVATE csp csp_posix_helper Threads::Threads) + target_link_libraries(csp_server PRIVATE csp csp_posix_helper Threads::Threads) + target_link_libraries(csp_client PRIVATE csp csp_posix_helper Threads::Threads) target_link_libraries(csp_bridge_can2udp PRIVATE csp) target_link_libraries(zmqproxy PRIVATE csp Threads::Threads ${LIBZMQ_LIBRARIES}) endif() diff --git a/examples/csp_client.c b/examples/csp_client.c index ba1d78ae1..6d17d2a1f 100644 --- a/examples/csp_client.c +++ b/examples/csp_client.c @@ -10,9 +10,7 @@ #include #include - -/* This function must be provided in arch specific way */ -int router_start(void); +#include "csp_posix_helper.h" /* Server port, the port the server listens on for incoming connections from the client. */ #define SERVER_PORT 10 diff --git a/examples/csp_client_posix.c b/examples/csp_posix_helper.c similarity index 90% rename from examples/csp_client_posix.c rename to examples/csp_posix_helper.c index be15f0de8..d1b71c4f2 100644 --- a/examples/csp_client_posix.c +++ b/examples/csp_posix_helper.c @@ -2,7 +2,7 @@ #include #include -static int csp_pthread_create(void * (*routine)(void *)) { +int csp_pthread_create(void * (*routine)(void *)) { pthread_attr_t attributes; pthread_t handle; @@ -26,6 +26,8 @@ static int csp_pthread_create(void * (*routine)(void *)) { static void * task_router(void * param) { + (void)param; + /* Here there be routing */ while (1) { csp_route_work(); diff --git a/examples/csp_posix_helper.h b/examples/csp_posix_helper.h new file mode 100644 index 000000000..9f4c22f59 --- /dev/null +++ b/examples/csp_posix_helper.h @@ -0,0 +1,4 @@ +#pragma once + +int router_start(void); +int csp_pthread_create(void * (*routine)(void *)); diff --git a/examples/csp_server.c b/examples/csp_server.c index eff0d8120..b9a5c48a8 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -9,10 +9,7 @@ #include #include - -/* These three functions must be provided in arch specific way */ -int router_start(void); -int server_start(void); +#include "csp_posix_helper.h" /* Server port, the port the server listens on for incoming connections from the client. */ #define SERVER_PORT 10 @@ -35,7 +32,9 @@ enum DeviceType { #define __maybe_unused __attribute__((__unused__)) /* Server task - handles requests from clients */ -void server(void) { +void * server(void * param) { + + (void)param; csp_print("Server task started\n"); @@ -80,7 +79,7 @@ void server(void) { csp_close(conn); } - return; + return NULL; } /* End of server task */ @@ -271,7 +270,7 @@ int main(int argc, char * argv[]) { } /* Start server thread */ - server_start(); + csp_pthread_create(server); /* Wait for execution to end (ctrl+c) */ while(1) { diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 5c34c456c..5f50790a5 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -8,11 +8,7 @@ #include #include - -/* These three functions must be provided in arch specific way */ -int router_start(void); -int server_start(void); -int client_start(void); +#include "csp_posix_helper.h" /* Server port, the port the server listens on for incoming connections from the client. */ #define MY_SERVER_PORT 10 @@ -26,7 +22,9 @@ static unsigned int server_received = 0; static unsigned int run_duration_in_sec = 3; /* Server task - handles requests from clients */ -void server(void) { +void * server(void * param) { + + (void)param; csp_print("Server task started\n"); @@ -72,13 +70,15 @@ void server(void) { } - return; + return NULL; } /* End of server task */ /* Client task sending requests to server task */ -void client(void) { +void * client(void * param) { + + (void)param; csp_print("Client task started\n"); @@ -104,7 +104,7 @@ void client(void) { if (conn == NULL) { /* Connect failed */ csp_print("Connection failed\n"); - return; + return NULL; } /* 2. Get packet buffer for message/data */ @@ -112,7 +112,7 @@ void client(void) { if (packet == NULL) { /* Could not get buffer element */ csp_print("Failed to get CSP buffer\n"); - return; + return NULL; } /* 3. Copy data to packet */ @@ -131,7 +131,7 @@ void client(void) { csp_close(conn); } - return; + return NULL; } /* End of client task */ @@ -200,10 +200,10 @@ int main(int argc, char * argv[]) { csp_iflist_print(); /* Start server thread */ - server_start(); + csp_pthread_create(server); /* Start client thread */ - client_start(); + csp_pthread_create(client); /* Wait for execution to end (ctrl+c) */ while(1) { diff --git a/examples/csp_server_client_posix.c b/examples/csp_server_client_posix.c deleted file mode 100644 index a96d935e4..000000000 --- a/examples/csp_server_client_posix.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include - -void server(void); -void client(void); - -static int csp_pthread_create(void * (*routine)(void *)) { - - pthread_attr_t attributes; - pthread_t handle; - int ret; - - if (pthread_attr_init(&attributes) != 0) { - return CSP_ERR_NOMEM; - } - /* no need to join with thread to free its resources */ - pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); - - ret = pthread_create(&handle, &attributes, routine, NULL); - pthread_attr_destroy(&attributes); - - if (ret != 0) { - return ret; - } - - return CSP_ERR_NONE; -} - -static void * task_router(void * param) { - - /* Here there be routing */ - while (1) { - csp_route_work(); - } - - return NULL; -} - -static void * task_server(void * param) { - server(); - return NULL; -} - -static void * task_client(void * param) { - client(); - return NULL; -} - -int router_start(void) { - return csp_pthread_create(task_router); -} - -int server_start(void) { - return csp_pthread_create(task_server); -} - -int client_start(void) { - return csp_pthread_create(task_client); -} diff --git a/examples/csp_server_posix.c b/examples/csp_server_posix.c deleted file mode 100644 index 5ec5fbc32..000000000 --- a/examples/csp_server_posix.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -void server(void); - -static int csp_pthread_create(void * (*routine)(void *)) { - - pthread_attr_t attributes; - pthread_t handle; - int ret; - - if (pthread_attr_init(&attributes) != 0) { - return CSP_ERR_NOMEM; - } - /* no need to join with thread to free its resources */ - pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); - - ret = pthread_create(&handle, &attributes, routine, NULL); - pthread_attr_destroy(&attributes); - - if (ret != 0) { - return ret; - } - - return CSP_ERR_NONE; -} - -static void * task_router(void * param) { - - /* Here there be routing */ - while (1) { - csp_route_work(); - } - - return NULL; -} - -static void * task_server(void * param) { - server(); - return NULL; -} - -int router_start(void) { - return csp_pthread_create(task_router); -} - -int server_start(void) { - return csp_pthread_create(task_server); -} diff --git a/examples/meson.build b/examples/meson.build index 0996b0d3d..4885054df 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,22 +1,30 @@ +csp_posix_helper = static_library('csp_posix_helper', + 'csp_posix_helper.c', + include_directories : csp_inc + ).extract_all_objects(recursive : true) + executable('csp_server_client', - ['csp_server_client.c', 'csp_server_client_posix.c'], + 'csp_server_client.c', include_directories : csp_inc, c_args : csp_c_args, dependencies : csp_dep, + objects : csp_posix_helper, build_by_default : false) executable('csp_server', - ['csp_server.c', 'csp_server_posix.c'], + 'csp_server.c', include_directories : csp_inc, c_args : csp_c_args, dependencies : csp_dep, + objects : csp_posix_helper, build_by_default : false) executable('csp_client', - ['csp_client.c', 'csp_client_posix.c'], + 'csp_client.c', include_directories : csp_inc, c_args : csp_c_args, dependencies : csp_dep, + objects : csp_posix_helper, build_by_default : false) executable('csp_bridge_can2udp', diff --git a/wscript b/wscript index 7a556e809..490462440 100644 --- a/wscript +++ b/wscript @@ -234,23 +234,24 @@ def build(ctx): pytest_path=[ctx.path.get_bld()]) if ctx.env.ENABLE_EXAMPLES: - ctx.program(source=['examples/csp_server_client.c', - 'examples/csp_server_client_posix.c'], + ctx.objects(source='examples/csp_posix_helper.c', + target='csp_posix_helper', + use='csp_h') + + ctx.program(source='examples/csp_server_client.c', target='examples/csp_server_client', lib=ctx.env.LIBS, - use='csp') + use=['csp', 'csp_posix_helper']) - ctx.program(source=['examples/csp_server.c', - 'examples/csp_server_posix.c'], + ctx.program(source='examples/csp_server.c', target='examples/csp_server', lib=ctx.env.LIBS, - use='csp') + use=['csp', 'csp_posix_helper']) - ctx.program(source=['examples/csp_client.c', - 'examples/csp_client_posix.c'], + ctx.program(source='examples/csp_client.c', target='examples/csp_client', lib=ctx.env.LIBS, - use='csp') + use=['csp', 'csp_posix_helper']) ctx.program(source=['examples/csp_bridge_can2udp.c'], target='examples/csp_bridge_can2udp', From 0761775a238aaddcb541e12df1d863ab8bdce2be Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 01:13:06 +0900 Subject: [PATCH 090/348] interfaces: csp_if_zmqhub: Mark unused variables csp_if_zmqhub.c has a few variables unused. Mark them __maybe_unused. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 786287e6b..14f536c59 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -35,7 +35,7 @@ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; * @param packet Packet to transmit * @return 1 if packet was successfully transmitted, 0 on error */ -int csp_zmqhub_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { +int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t * packet, int __maybe_unused from_me) { zmq_driver_t * drv = iface->driver_data; @@ -157,10 +157,11 @@ int csp_zmqhub_init_w_endpoints(uint16_t addr, } int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr, - const uint16_t rxfilter[], unsigned int rxfilter_count, + const uint16_t __maybe_unused rxfilter[], + unsigned int __maybe_unused rxfilter_count, const char * publish_endpoint, const char * subscribe_endpoint, - uint32_t flags, + uint32_t __maybe_unused flags, csp_iface_t ** return_interface) { int ret; From 39e55739bb34bcbd3997bce9ec2f0b3b169276ab Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 01:48:18 +0900 Subject: [PATCH 091/348] cmake: Consolidate compile flags Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22b3084f2..93c4a7ebf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,6 @@ endif() add_library(csp) set_target_properties(csp PROPERTIES C_STANDARD 11) set_target_properties(csp PROPERTIES C_EXTENSIONS ON) -target_compile_options(csp PRIVATE -Wall -Wextra -Wpedantic - $<$:-Wno-gnu-zero-variadic-macro-arguments>) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release MinSizeRel) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) @@ -101,12 +99,18 @@ target_include_directories(csp PRIVATE ${csp_inc_src} ) +add_library(csp_common INTERFACE) if(CSP_POSIX) - set(CSP_C_ARGS -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter) + target_compile_options(csp_common INTERFACE + -Wall -Wextra -Wpedantic + -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter + $<$:-Wno-gnu-zero-variadic-macro-arguments>) elseif(CSP_ZEPHYR) - set(CSP_C_ARGS -Wwrite-strings -Wno-unused-parameter -Wno-pedantic) + target_compile_options(csp_common INTERFACE + -Wall -Wextra + -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter) endif() -target_compile_options(csp PRIVATE ${CSP_C_ARGS}) +target_link_libraries(csp PRIVATE csp_common) add_subdirectory(src) add_subdirectory(examples) From 50e7638ad939be4bbf78f44fd6d75b763a4caf9c Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 01:53:36 +0900 Subject: [PATCH 092/348] cmake: Propagate compiler flags to object libraries In the CMake build, object libraries are used to prevent leaking compiler flags, include directories, and linking libraries to the core `libcsp`. However, due to the behavior of `add_library(... OBJECT ...)`, compiler flags were not applied to these object libraries. This commit uses the `csp_common` target introduced in the previous commit to properly set warning flags and other compiler options on object libraries. Signed-off-by: Yasushi SHOJI --- src/CMakeLists.txt | 2 +- src/drivers/CMakeLists.txt | 2 +- src/interfaces/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ced57b6c..3d6da4a1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,7 +49,7 @@ if(LIBYAML_FOUND) target_include_directories(csp_yaml PRIVATE ${csp_inc} ${LIBYAML_INCLUDE_DIRS}) - target_link_libraries(csp_yaml PRIVATE ${LIBYAML_LIBRARIES}) + target_link_libraries(csp_yaml PRIVATE csp_common ${LIBYAML_LIBRARIES}) target_link_libraries(csp PRIVATE csp_yaml) if(BUILD_SHARED_LIBS) set_property(TARGET csp_yaml PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 8d3082971..6d29d4076 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -5,7 +5,7 @@ if(LIBSOCKETCAN_FOUND) target_include_directories(driver_can PRIVATE ${csp_inc} ${LIBSOCKETCAN_INCLUDE_DIRS}) - target_link_libraries(driver_can PRIVATE + target_link_libraries(driver_can PRIVATE csp_common ${LIBSOCKETCAN_LIBRARIES}) target_link_libraries(csp PRIVATE driver_can) if(BUILD_SHARED_LIBS) diff --git a/src/interfaces/CMakeLists.txt b/src/interfaces/CMakeLists.txt index 50ca4c8d7..48b61b8d8 100644 --- a/src/interfaces/CMakeLists.txt +++ b/src/interfaces/CMakeLists.txt @@ -14,7 +14,7 @@ if(LIBZMQ_FOUND) target_include_directories(if_zmq PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) - target_link_libraries(if_zmq PRIVATE ${LIBZMQ_LIBRARIES}) + target_link_libraries(if_zmq PRIVATE csp_common ${LIBZMQ_LIBRARIES}) target_link_libraries(csp PRIVATE if_zmq) if(BUILD_SHARED_LIBS) set_property(TARGET if_zmq PROPERTY POSITION_INDEPENDENT_CODE ON) From f0984c018a5a4aa9a2c9748911c7db6d0f017e4e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 01:54:03 +0900 Subject: [PATCH 093/348] examples: cmake: Propagate compiler flags to examples The examples were not using the compiler flags. This commit uses the `csp_common` target introduced in the previous commit to properly set warning flags and other compiler options on examples. Signed-off-by: Yasushi SHOJI --- examples/CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d7d6fa163..970c433b4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,10 +15,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_include_directories(csp_client PRIVATE ${csp_inc}) target_include_directories(zmqproxy PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) - target_link_libraries(csp_arch PRIVATE csp) - target_link_libraries(csp_server_client PRIVATE csp csp_posix_helper Threads::Threads) - target_link_libraries(csp_server PRIVATE csp csp_posix_helper Threads::Threads) - target_link_libraries(csp_client PRIVATE csp csp_posix_helper Threads::Threads) - target_link_libraries(csp_bridge_can2udp PRIVATE csp) - target_link_libraries(zmqproxy PRIVATE csp Threads::Threads ${LIBZMQ_LIBRARIES}) + target_link_libraries(csp_posix_helper PRIVATE csp_common) + target_link_libraries(csp_arch PRIVATE csp csp_common) + target_link_libraries(csp_server_client PRIVATE csp csp_common csp_posix_helper Threads::Threads) + target_link_libraries(csp_server PRIVATE csp csp_common csp_posix_helper Threads::Threads) + target_link_libraries(csp_client PRIVATE csp csp_common csp_posix_helper Threads::Threads) + target_link_libraries(csp_bridge_can2udp PRIVATE csp csp_common) + target_link_libraries(zmqproxy PRIVATE csp csp_common Threads::Threads ${LIBZMQ_LIBRARIES}) endif() From 8f7625427ff44c8f894efa68cd6b829f6e9acc22 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 27 Nov 2024 14:29:29 +0900 Subject: [PATCH 094/348] crypto: csp_hmac: Update packet->length when include_header is true When `csp_hmac_append()` is called with `include_header == true`, we must update `packet->length` along with `packet->frame_length`. Signed-off-by: Yasushi SHOJI --- src/crypto/csp_hmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/csp_hmac.c b/src/crypto/csp_hmac.c index a4daa8cfc..83d45e310 100644 --- a/src/crypto/csp_hmac.c +++ b/src/crypto/csp_hmac.c @@ -139,6 +139,7 @@ int csp_hmac_append(csp_packet_t * packet, bool include_header) { csp_hmac_memory(csp_hmac_key, sizeof(csp_hmac_key), packet->frame_begin, packet->frame_length, hmac); memcpy(&packet->frame_begin[packet->frame_length], hmac, CSP_HMAC_LENGTH); packet->frame_length += CSP_HMAC_LENGTH; + packet->length += CSP_HMAC_LENGTH; } else { From ce474370a2ce7947e8936988fea4c756c78e6c0c Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 27 Nov 2024 15:03:30 +0900 Subject: [PATCH 095/348] unittest: Add HMAC unit test Add unit test for csp_hmac_append() and csp_hmac_verify(). Signed-off-by: Yasushi SHOJI --- unittests/CMakeLists.txt | 1 + unittests/hmac.c | 63 ++++++++++++++++++++++++++++++++++++++++ unittests/main.c | 2 ++ 3 files changed, 66 insertions(+) create mode 100644 unittests/hmac.c diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ce100fdb1..6a29c23de 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -5,5 +5,6 @@ if(CHECK_FOUND) main.c queue.c buffer.c + hmac.c ) endif() diff --git a/unittests/hmac.c b/unittests/hmac.c new file mode 100644 index 000000000..afeab3faf --- /dev/null +++ b/unittests/hmac.c @@ -0,0 +1,63 @@ +#include +#include "../include/csp/csp.h" +#include "../include/csp/csp_id.h" +#include "../include/csp/crypto/csp_hmac.h" + +START_TEST(test_hmac_append_no_header) +{ + uint8_t test_data[] = {0x61, 0x62, 0x63}; /* abc */ + uint8_t expected[] = {0x61, 0x62, 0x63, 0x9b, 0x4a, 0x91, 0x8f}; + csp_packet_t * packet; + + csp_init(); + + packet = csp_buffer_get_always(); + memcpy(packet->data, test_data, sizeof(test_data)); + packet->length = sizeof(test_data); + + csp_hmac_append(packet, false); + ck_assert_mem_eq(packet->data, expected, sizeof(expected)); + + csp_hmac_verify(packet, false); + ck_assert_mem_eq(packet->data, test_data, sizeof(test_data)); +} +END_TEST + +START_TEST(test_hmac_append_include_header) +{ + uint8_t test_data[] = {0x61, 0x62, 0x63}; /* abc */ + uint8_t expected[] = {0x61, 0x62, 0x63, 0x3c, 0xc7, 0x49, 0x8b}; + csp_packet_t * packet; + + csp_init(); + + packet = csp_buffer_get_always(); + + csp_id_prepend(packet); + + memcpy(packet->data, test_data, sizeof(test_data)); + packet->length += sizeof(test_data); + packet->frame_length += sizeof(test_data); + + csp_hmac_append(packet, true); + ck_assert_mem_eq(packet->data, expected, sizeof(expected)); + + csp_hmac_verify(packet, true); + ck_assert_mem_eq(packet->data, test_data, sizeof(test_data)); +} +END_TEST + +Suite * hmac_suite(void) +{ + Suite *s; + TCase *tc_hmac; + + s = suite_create("HMAC"); + + tc_hmac = tcase_create("append"); + tcase_add_test(tc_hmac, test_hmac_append_no_header); + tcase_add_test(tc_hmac, test_hmac_append_include_header); + suite_add_tcase(s, tc_hmac); + + return s; +} diff --git a/unittests/main.c b/unittests/main.c index 947a35a4e..5a4b6de9b 100644 --- a/unittests/main.c +++ b/unittests/main.c @@ -7,6 +7,7 @@ Suite * queue_suite(void); Suite * buffer_suite(void); +Suite * hmac_suite(void); static struct option long_options[] = { {"verbose", no_argument, 0, 'V'}, @@ -47,6 +48,7 @@ int main(int argc, char *argv[]) sr = srunner_create(NULL); srunner_add_suite(sr, queue_suite()); srunner_add_suite(sr, buffer_suite()); + srunner_add_suite(sr, hmac_suite()); srunner_run_all(sr, print_verbosity); number_failed = srunner_ntests_failed(sr); From f8962501d53d22e1eb0b92924bc1d3c21b3c6dac Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 27 Nov 2024 14:14:28 +0900 Subject: [PATCH 096/348] sample: Add HMAC Add a HMAC sample code. Signed-off-by: Yasushi SHOJI --- samples/posix/CMakeLists.txt | 1 + samples/posix/hmac/CMakeLists.txt | 3 +++ samples/posix/hmac/src/main.c | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 samples/posix/hmac/CMakeLists.txt create mode 100644 samples/posix/hmac/src/main.c diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt index 442d9ebac..fd4469f2b 100644 --- a/samples/posix/CMakeLists.txt +++ b/samples/posix/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(simple-send-canbus) add_subdirectory(simple-send-usart) +add_subdirectory(hmac) diff --git a/samples/posix/hmac/CMakeLists.txt b/samples/posix/hmac/CMakeLists.txt new file mode 100644 index 000000000..f9c7478e3 --- /dev/null +++ b/samples/posix/hmac/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(sample-hmac EXCLUDE_FROM_ALL src/main.c) +target_include_directories(sample-hmac PRIVATE ${csp_inc}) +target_link_libraries(sample-hmac PRIVATE csp) diff --git a/samples/posix/hmac/src/main.c b/samples/posix/hmac/src/main.c new file mode 100644 index 000000000..0edc3f269 --- /dev/null +++ b/samples/posix/hmac/src/main.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +int main(int argc, char * argv[]) +{ + csp_packet_t * packet; + + csp_init(); + + packet = csp_buffer_get_always(); + + csp_id_prepend(packet); + + memcpy(packet->data, "abc", 3); + packet->length += 3; + packet->frame_length += 3; + + csp_hmac_append(packet, true); + csp_hmac_verify(packet, true); + + return 0; +} From f8826f1f7f5521d1081d1c0b4f16bc32a2d440b9 Mon Sep 17 00:00:00 2001 From: Riho Natsushima Date: Fri, 25 Oct 2024 17:01:56 +0900 Subject: [PATCH 097/348] interfaces: udp: Replace literal number 4 with header_size Replace the hardcoded literal `4` with the variable `header_size`. The literal `4` was a leftover from CSPv1. The variable `header_size` should be used for comparison against the return value of `csp_id_setup_rx()`, which is stored in `received_len`. Signed-off-by: Riho Natsushima Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index a3993c579..632938ce1 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -46,7 +46,7 @@ int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { int header_size = csp_id_setup_rx(packet); int received_len = recvfrom(sockfd, (char *)packet->frame_begin, sizeof(packet->data) + header_size, MSG_WAITALL, NULL, NULL); - if (received_len <= 4) { + if (received_len <= header_size) { csp_buffer_free(packet); return CSP_ERR_NOMEM; } From 9c021343a8e74e0ef8087118de327d2bcd982214 Mon Sep 17 00:00:00 2001 From: Riho Natsushima Date: Mon, 11 Nov 2024 18:25:10 +0900 Subject: [PATCH 098/348] interfaces: udp: Accept 0-byte payload packets Using `<=` caused 0-byte payloads to be dropped, which was unintended. This change ensures such packets are correctly processed. Signed-off-by: Riho Natsushima Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 632938ce1..73832d935 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -46,7 +46,7 @@ int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { int header_size = csp_id_setup_rx(packet); int received_len = recvfrom(sockfd, (char *)packet->frame_begin, sizeof(packet->data) + header_size, MSG_WAITALL, NULL, NULL); - if (received_len <= header_size) { + if (received_len < header_size) { csp_buffer_free(packet); return CSP_ERR_NOMEM; } From 8457dbdbe78d20eb4180337ec433ee6d2f8b66a2 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 31 May 2024 16:23:50 +0200 Subject: [PATCH 099/348] Make pointer arguments const where mutation is not required Some pointer arguments were updated to `const` to indicate immutability when no modification is necessary. Signed-off-by: Robin Mueller Signed-off-by: Yasushi SHOJI --- include/csp/csp.h | 20 ++++++++++---------- src/csp_conn.c | 10 +++++----- src/csp_hex_dump.c | 4 ++-- src/csp_io.c | 10 +++++----- src/csp_io.h | 6 +++--- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/csp/csp.h b/include/csp/csp.h index 398654897..e5ad78e55 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -71,7 +71,7 @@ const csp_conf_t * csp_get_conf(void); /** * Copy csp id fields from source to target object */ -void csp_id_copy(csp_id_t * target, csp_id_t * source); +void csp_id_copy(csp_id_t * target, const csp_id_t * source); /** * Clear csp id fields after creating new buffer @@ -135,7 +135,7 @@ void csp_send_prio(uint8_t prio, csp_conn_t *conn, csp_packet_t *packet); * Returns: * int: 1 or reply size on success, 0 on failure (error, incoming length does not match, timeout) */ -int csp_transaction_w_opts(uint8_t prio, uint16_t dst, uint8_t dst_port, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen, uint32_t opts); +int csp_transaction_w_opts(uint8_t prio, uint16_t dst, uint8_t dst_port, uint32_t timeout, const void *outbuf, int outlen, void *inbuf, int inlen, uint32_t opts); /** * Perform an entire request & reply transaction. @@ -151,7 +151,7 @@ int csp_transaction_w_opts(uint8_t prio, uint16_t dst, uint8_t dst_port, uint32_ * @param[in] inlen length of expected reply, -1 for unknown size (inbuf MUST be large enough), 0 for no reply. * @return 1 or reply size on success, 0 on failure (error, incoming length does not match, timeout) */ -static inline int csp_transaction(uint8_t prio, uint16_t dest, uint8_t port, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { +static inline int csp_transaction(uint8_t prio, uint16_t dest, uint8_t port, uint32_t timeout, const void * outbuf, int outlen, void * inbuf, int inlen) { return csp_transaction_w_opts(prio, dest, port, timeout, outbuf, outlen, inbuf, inlen, 0); } @@ -167,7 +167,7 @@ static inline int csp_transaction(uint8_t prio, uint16_t dest, uint8_t port, uin * @param[in] inlen length of expected reply, -1 for unknown size (inbuf MUST be large enough), 0 for no reply. * @return 1 or reply size on success, 0 on failure (error, incoming length does not match, timeout) */ -int csp_transaction_persistent(csp_conn_t *conn, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen); +int csp_transaction_persistent(csp_conn_t *conn, uint32_t timeout, const void *outbuf, int outlen, void *inbuf, int inlen); /** * Read data from a connection-less server socket. @@ -238,7 +238,7 @@ int csp_socket_close(csp_socket_t* sock); * @param[in] conn connection * @return destination port of an incoming connection */ -int csp_conn_dport(csp_conn_t *conn); +int csp_conn_dport(const csp_conn_t *conn); /** * Return source port of connection. @@ -246,7 +246,7 @@ int csp_conn_dport(csp_conn_t *conn); * @param[in] conn connection * @return source port of an incoming connection */ -int csp_conn_sport(csp_conn_t *conn); +int csp_conn_sport(const csp_conn_t *conn); /** * Return destination address of connection. @@ -254,7 +254,7 @@ int csp_conn_sport(csp_conn_t *conn); * @param[in] conn connection * @return destination address of an incoming connection */ -int csp_conn_dst(csp_conn_t *conn); +int csp_conn_dst(const csp_conn_t *conn); /** * Return source address of connection. @@ -262,7 +262,7 @@ int csp_conn_dst(csp_conn_t *conn); * @param[in] conn connection * @return source address of an incoming connection */ -int csp_conn_src(csp_conn_t *conn); +int csp_conn_src(const csp_conn_t *conn); /** * Return flags of connection. @@ -270,7 +270,7 @@ int csp_conn_src(csp_conn_t *conn); * @param[in] conn connection * @return flags of an incoming connection, see @ref CSP_HEADER_FLAGS */ -int csp_conn_flags(csp_conn_t *conn); +int csp_conn_flags(const csp_conn_t *conn); /** * Return if the CSP connection is active @@ -500,7 +500,7 @@ void csp_conn_print_table(void); * @param[in] len number of bytes to dump, starting from \a addr. * */ -void csp_hex_dump(const char *desc, void *addr, int len); +void csp_hex_dump(const char *desc, const void *addr, int len); #else diff --git a/src/csp_conn.c b/src/csp_conn.c index 812ae42a0..50485953f 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -331,27 +331,27 @@ csp_conn_t * csp_connect(uint8_t prio, uint16_t dest, uint8_t dport, uint32_t ti return conn; } -int csp_conn_dport(csp_conn_t * conn) { +int csp_conn_dport(const csp_conn_t * conn) { return conn->idin.dport; } -int csp_conn_sport(csp_conn_t * conn) { +int csp_conn_sport(const csp_conn_t * conn) { return conn->idin.sport; } -int csp_conn_dst(csp_conn_t * conn) { +int csp_conn_dst(const csp_conn_t * conn) { return conn->idin.dst; } -int csp_conn_src(csp_conn_t * conn) { +int csp_conn_src(const csp_conn_t * conn) { return conn->idin.src; } -int csp_conn_flags(csp_conn_t * conn) { +int csp_conn_flags(const csp_conn_t * conn) { return conn->idin.flags; } diff --git a/src/csp_hex_dump.c b/src/csp_hex_dump.c index bbf70dd3e..0d5d5b58e 100644 --- a/src/csp_hex_dump.c +++ b/src/csp_hex_dump.c @@ -4,7 +4,7 @@ #include #include -void csp_hex_dump_format(const char * desc, void * addr, int len, int format) { +void csp_hex_dump_format(const char * desc, const void * addr, int len, int format) { int i; unsigned char buff[17]; unsigned char * pc = (unsigned char *)addr; @@ -54,6 +54,6 @@ void csp_hex_dump_format(const char * desc, void * addr, int len, int format) { csp_print(" %s\n", buff); } -void csp_hex_dump(const char * desc, void * addr, int len) { +void csp_hex_dump(const char * desc, const void * addr, int len) { csp_hex_dump_format(desc, addr, len, 0); } diff --git a/src/csp_io.c b/src/csp_io.c index cafd82881..129d0d762 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -66,7 +66,7 @@ csp_packet_t * csp_read(csp_conn_t * conn, uint32_t timeout) { } /* Provide a safe method to copy type safe, between two csp ids */ -void csp_id_copy(csp_id_t * target, csp_id_t * source) { +void csp_id_copy(csp_id_t * target, const csp_id_t * source) { target->pri = source->pri; target->dst = source->dst; target->src = source->src; @@ -216,13 +216,13 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route } -__weak void csp_output_hook(csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me) { +__weak void csp_output_hook(const csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me) { csp_print_packet("OUT: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %u VIA: %s (%u), Tms %u\n", idout->src, idout->dst, idout->dport, idout->sport, idout->pri, idout->flags, packet->length, iface->name, (via != CSP_NO_VIA_ADDRESS) ? via : idout->dst, csp_get_ms()); return; } -void csp_send_direct_iface(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me) { +void csp_send_direct_iface(const csp_id_t* idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me) { csp_output_hook(idout, packet, iface, via, from_me); @@ -311,7 +311,7 @@ void csp_send_prio(uint8_t prio, csp_conn_t * conn, csp_packet_t * packet) { csp_send(conn, packet); } -int csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { +int csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, const void * outbuf, int outlen, void * inbuf, int inlen) { if(outlen > CSP_BUFFER_SIZE){ return 0; @@ -348,7 +348,7 @@ int csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, void * outbu return length; } -int csp_transaction_w_opts(uint8_t prio, uint16_t dest, uint8_t port, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen, uint32_t opts) { +int csp_transaction_w_opts(uint8_t prio, uint16_t dest, uint8_t port, uint32_t timeout, const void * outbuf, int outlen, void * inbuf, int inlen, uint32_t opts) { csp_conn_t * conn = csp_connect(prio, dest, port, 0, opts); if (conn == NULL) diff --git a/src/csp_io.h b/src/csp_io.h index a4030ed11..0cbb68895 100644 --- a/src/csp_io.h +++ b/src/csp_io.h @@ -5,12 +5,12 @@ #include /** - * + * * @param packet packet to send - this may not be freed if error code is returned * @param from_me 1 if from me, 0 if routed message * @return #CSP_ERR_NONE on success, otherwise an error code. - * + * */ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * routed_from); -void csp_send_direct_iface(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); +void csp_send_direct_iface(const csp_id_t* idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); From df3afdad226e242132747d4d937b5583cbfe9325 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 14:47:08 +0900 Subject: [PATCH 100/348] waf: Update to 2.1.4 Update Waf 2.1.4. NEW IN WAF 2.1.4 ---------------- * Prevent subprocesses from disrupting the main build process through inherited stdin * Fix a regression that prevents displaying help from custom commands Signed-off-by: Yasushi SHOJI --- waf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/waf b/waf index 9eae75eca..e968b6364 100755 --- a/waf +++ b/waf @@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.1.3" -REVISION="9bb4ce978ce08636ef790135d3c297a6" -GIT="d89cab923fcd82a576a8fa44ae4a01f0136854bd" +VERSION="2.1.4" +REVISION="72787ce48f227ac42c4b0da24e780694" +GIT="89cd97a8d823d797297592ad751beb678806f339" INSTALL='' -C1='#5' -C2='#1' -C3='#-' +C1='#.' +C2='#-' +C3='#+' cwd = os.getcwd() join = os.path.join @@ -172,6 +172,6 @@ if __name__ == '__main__': Scripting.waf_entry_point(cwd, VERSION, wafdir) #==> -#BZh91AY&SY¼$VFzIÿÿ´PÿÿÿÿÿÿÿÿÿÿÿM (B€ð…0u@b)|÷I#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-¾®Ø aª0û¾ïGm_YJëRÚõËNonéµ±eôpצٲÁE÷u=ÛéãY]ðn­°ØW»:´#1õï§ß{p&]RÑÖöû燮äØ1»²†Nõ|íï|=ì=vÞøÞÞõlwyÇ]À]Z±Û}ïW#-#-#-@è]î#-ôXECï}•­Í°W×)Ù´Iá™´SM#-t!sZÝÓè ç ` 3ØèW #-S]T#-¡T ¢ö5BU"D€IG =ª ¥(·wmîôÉŸ{¹îÖsâ{›d¼õÉÜé^lô••ƒJ&kmÛˆ®Ù[-óÝk£šÛj}|ݽÑמÙ{=Øvò¯Gžç›ÀôžÝîç›Û»­¼Û½Ñïžö{4ç׺;pÛJÞ·¾õÞ¹|€h¡ÓPŠ>û¸×ש¾Ç|aè^ë)3Ÿ;x{@+Û•À¾îÜÃ۽ϋnŸwÏ€ã¾Õó·tÀ4(º÷ßžûv|#.Ù·tAÉG-I½uÌöîß}J¥6wr‡»®Õ±óÍ«švvÝ3z>ã¼úw\ãÞïL¥»^ÚÖ÷gZx÷»ÞšcOF®›ÛW{Ï#-ÞO^ç¹ëÜçØ{¡õ¦÷#-õ(¢€TJ ª¨ì…žâÀ;EµÐîË«¥¬uÔúzÇ'!Û³^Û^éÎvdSƒ+½Þ;ÍU7U²õ#1;¬Õ³^æx#-·¶¸Ð#-yêåÅuxóÞÍÞÖ˜æõ¯¯{ßAéÞëŽkÝ‹u¥¹"n.wHØ÷¹×–…=÷sÓèäNŽik °æìDÚå\íÙâ÷{ëÕÚ÷gÀ)^÷{î]»ÓÚKÖG²º½6ÏZàoE}·‘¾¬l;êåÝÎ}W»{|vêLfû°soºî˺¾.{jÍÛ6÷{Ûëî>šîu@¸2СèÖ^ïrQÚ÷§`îÕ 6Áºè±âÝ/T¶Æs›ÙëŸ{¾”ÛîûP+[v§T;”§w½ë×¹:»#1g¶}æ÷Ó„>úoµ¾¾žµ·¬_]æm¼Ú£í¬¹‰»ï½{x|Ý×n”¢ƒÖ”½ÓºèPÛŒjwaîó­;aÞç`^‡»U=0uÃs–½çwÞúêCèö¼àõJ …˜½³=P.@#1±Ý$yíÊßu„ôm #1kÜ’Mîå»`ïY·¼í35×`îqªªÛs¬ÜÜÛv˜1ƒJP·lí#5IÛuÝÚŽ)hŒ˜ÔÆÞé‹ÕRy†‡;í»·Ï{¶ ûéí·fÁºùÏ>°¢i&çÝásÜvq—ºÛm¶í¾õï=냻y>ö=öÎé[#»»ºtñu{Ío½ i|WÅ¡¦ˆ#-€L@h#-™4&&M'¢m#5fDf#-b&M4j¦!2A2F…<$2'¦”ö¡©ä4hÓÒhÐ €#-#-#-#-#-H$D !¦F‚Ó4ò§ú”~SÑMµ2ž¦jmF‡©ú‰£Ô#- ƒ@#-#-ž©IB†“DôI½F4‰¦´Œš#-4Ð €#-#-#-#-#-#-B#-#-š#-™ “DÄô)´É€ˆÚ ‰#1€#-#1#-#-&¢ €€4h#-Bz›Bj=ƒõG”lBD=@#-#-#-#-ÿñ¿ÏÕi¯õ#Æ­]5ßÁÕiæA¡©”SÖ«]Ô,"XÃ=ñESÌ`#‰ø¡Jü8":ÌVöî‘Ïè!ú‰&`Iµ›j[LªmÊíV›U±U³-VMm´š£Uж6¶Ö*-§ÚëQM­µñ»Iµµ^àP“K‘±=ð¥­åmY­«Ùu^t[мmF¶ÖµoǶÖÕ©1˜Y(ÌÔ4’i² &HÂH²Tm¢fjK#-›JQ¥ÛFÂÄÍ’SQšš¦¢ŠÆÁ¡m¤#5M$h–ˆB™b)iM€%cRƆ&˜²¦Ñ¢)6KFÔQ¥–šiZ1²Ò4Q™˜Å¤F µLØÒ›!„P›1¤ i#1l´Œ`E)hÔÓdMiZÙ­¶3XÌÉ™¢$ƒ,›im¦•5%%f“SmŒÚ›lÍL´˜™ˆ Ô I,«14i( ¦FdT¡¨³HÚ)(#1X,RlÀÍ640Äb6!QH’"¤™’FØÐ™ÂÄBClÍ ’2a$iBE3"–VX34H˜ÒYŠY6ɱ±¢Å ,I –R4m)¦ ²II‘£(ƒFM#13&$’"Q‰i!JEˆ¢fR²¦`˜ÑY!›&)¡™°L,FÒZJ)YÐ$“b Ä›IcAIj!$J6¤Y‚0`% †$”Â0˜a2’Q¨³*4[)*H’b iJ E²$‹%±&,³(¤lÉ(›)‘Q*&I£6F`™¶i‰!R&-JH4¦†Æ"5+%’6(”R"šIˆ&¤a²ÌcIC#5EA–De*4™¤V("šh¢kRÁ”Æhɳ‹%$‰¤ ¦CF†¢Æa"VP¤ÆÁ°F22HLRf0"Å‚&É °”PFÉ2Ó2ÖbŠ¥*‰¢&df¤ZbˆÌQ¡B²†*H¢Œ¤L’l‘Œi54‹LÑbÄfÌÊ1HͦÁ”I¬M‘‚"‰,–jdÓ2#5CDR4F›i©RRSE²2bLŠm!E’1J2’ˆ¢ÅQI&‰$Ò›F“Q²Ii„ˆÆˆ™¨ÓL¦ÐÊ63E!4Ì£±¤L†™Jl”¦)DBi•&ÔÙa6É%€È†’ÉŒ†ÑQT‰¬)d(¨ÌH0ˆh5%EšF”M´‰¥ RZ’6S)1D†i6De0H¥š4¦0†Š#1 ²e#HlJ­6ÖÑ‚e ©š2™šF"I¦DEh5)¶,Ñf)4Œ¤,³fȨ´Í¢ ˜¤(¦ÄB”DÒ!BÊmµÐ&ËSaˆÊŒQ¶5QQ²e)¦Â©&$Ói#5Ѱ’j6²!´4Ìe"Ë0„©#Yƒ12•™lÍh£’–£j)#5Æf˜MKLX°±d$ËM¦L†ZJѲ­#F¦Í­“,Š¥RÉRÙ±˜ÌÕ±¢ÈŠÙ4•„©–M¬•e²”¥™h*F€ÉHÚ‹ÖX’Ò)µ¨5¨¦Z2TVM%¨¬lT‰E´M2ERh£DQ­FÃ-XØ#5É“#50mbÀi€”R £ÈZ&IB´ŠÄmLJÆ-’,m¢ [TÖ¶h¨d&S5-dÑ%HÅ"DÓi&ˆÚÔµ‘lb”Úm–RÙM–%jYˆ’ÛMRÈšSTÔÑbÐ’™Mfņ‰R–ÊÊ)#1¬³H(eFÉhÑ,3IC"D6ƒ&12fÚ†hd±C%Q´“MdÉd¢È€l±H¦Ê@ÑŠË %(ÊS…%šf‰#56‹L™´ÚL`ƒcd)„RÊJ (1!£2QCLI5ˆÄ†¤ Ä`’f,PQ%2JQ ²¥T%L‘£I˜Jj$Ù!5Œ„FfÔe4lÊI4d#1#1!‹)¶")†²hÑ©6M„cd,2Œ‘¨¡@X¦f$Ѣ̥L¤¥”¬È2V0‚Í‹!± ZE±F¨Ùm¢*5)I°Ó,m‰†M¨ H”$ÈRM‹%IRJÔ¦$b‚C"e°”¦£X6Ä Y¤²le,YˆšiE%¦FÕ‘³B¦h”°¢FHARŒÐb3%JFÅ"[4›%1 #1%$$I&RLŒ-‹a*5Š1XÅ£2Øi0²I¡CdÌlbš Š6¢(6–lY ÊH6*PÉ“JRTÈŠ±hÖ5F¨“E –jŠ I©5F ÉF©JEX´hÒ–e¢™D‘TÉ6ˆ#TX‹”ª¤TѬmŠL”‘dÐS2†´ˆd €FMMˆfÈ™­¨‹X4TÈØ²j+i‹4£E¤ÉX­´”R[)¶D­&Á´4eIR£BÁccd¢¤Ñ%ˆÄm’$6ŠLÌZ̘,Q¥53c1¶6£f[%RF´ÒME!XÑ(Z’±l¤–6LE(HŒ%jDQie£PÊÑ&£UdÔe(ÚMlˆ!j6Ƥ¢Å"š!&FdÙ‰CS)“-£d±¶I²Í¬–Ñ2Ô‹cQ#jŠeSJبÚ6-±kI˜ÊQ f²±-F5škb6•*)ME²ŒÅB4ÒbaeŰD–‹%¶(Ë*Hڊزm”™Z4[Ó&ÑI66ª¥mb‘2¡Q1%І“e)¤•L1’m£l[bÓ5mÖ²ÒTÐË[)dÛSSl…µ&‰V&Œ¢ˆ£i Š#1†³d¦òíÙ²ÍDP!±!¦E¤#$DR1 _ö¡þ”?àCTþÛþëÆ)’ÿ|#ŒÞj}xô9×L¦¹‚Ú›ÓŸ»ý]L„ÕŸÑFATÎß¹†ÈØÓ¥Cþ•ÔUŽ1ûÿ¾ÅŒó‘ƒV7!Òù5ShêÿËë,¦q7Ñ®eÂýõ¥Ö¢äPR\'ÆÝÁU& ©2„¥ª"ÅÖ¨|^ïáì½_¿ºÖ×ÿ€»ÿGÓÍãýæÛLø¿M3ÍÖg‘,"ôsL?ÍbÛ'õ$Ø kè;vÁq¾Î…dác+`Ô~ªí;³Z‹®'$ï–¿¥†©$QCd†Üœ–#êB¿–¸6;Õ…£#5JL Uy¶ã»%E0•†,¾Ö¹äé4uÙ;Îè&7®]é†L #mWúl²XŸêKö5"#12ue¢¨ŒŠïP¦"w«6LªwãK™C˜.[rÆÛ,Îís_Wž<íë­~.Ko_nUãλa—ɹR3znh°káÒ)4KÛ×·ªô½wißO+Ìö;«ú#1WÍþ¾~Û™A6|Ý5ókÉ£Ç,/·µÊ$ªX±A—ûh·/ux½3FŠî­ˆ¢˜JbÚŠCË–J¼0 Í{5¥ ‹ÆnDå@áùiWŠöŒ;±8»¡j4AŒ1›™=‡Ùþyt9\ßGkK•HuM{¸ÑŠ ÄtuºÆnÔ¶”HªÇ=çuæÇ:]qL®mÒ¤ŠŠ¹·^;6*3íºùÍo‹TôW/È×"™_Fª+›ÿAºµú^ž.nèùýt ððÌ øÚy2ÁFm˜ôÓ²!Œh¶û¼ëx·»Ùâ÷Z®RV3ºŒÇ6¼'; "##1êQ•„kl9cPq5×+¼îÆå+—2bwI#s™K¾›µWµíÕþ½üû¼¾k¶ù¹!‹O{®Ùõ+šß¶2铘ËT|ñ›˜õÔ¡õ$ÇíhÆÕ¿quô½ÙÑ∕þÊ)7a£o]lµPPaÿרQ»ùSTÇí©-r3Ò¾Ü@çš:¾nsÛ£&ctELH P0ïT\&Éw]|lŽºì-rL Ì¦‡âÕŒGV«Ã¶¼E–yŸ<)Äruj³oQœ# º"ÐÛÊ9„>É©3Ï3¥îËÒÊ#5ï¡‹)95§Ã¯×~Žz_¹;wkvúÝûÝ®ký'A=6®Zû"§“vûWέL«HM,¯4Å”§c-¦ÈùPUéO- -™bÊHRuiXRª#ŠÏL_Úý8©Õ\²(§]hÙÕÉhT>¬ÝÁ¤ŒbH¯¹”+ï §ÕúÞ4dˆlržyþ"¿®Þ”cs¦?Ëu†dÅ/½EtP²1ÐøÐZæ¶ÁK«ØˆØÁ=]º/~ãÝ^¼Î¸ws)ìêC;·+½ÝZÝ.›}¾Q Dåu”¦de9JUP¦„m±zïϽz¼ˆFiVMCÚîX´kõKuîì~~¸Å¨¡5õQ¦¸–ƒ¥U E  )‘Nc-A¤·Üè£>àóÞWì~O–Wø¸µQƃ$I±þôG93#{7RšÏÎìüÝmÙCBn”2wÖöR•@uM.…¤•Óžzu–X×:\Ûæñ·\—.˜¤&ͤNê$9ëO#5†‡®¹;n”S(E%´–”ŠíßEÈßÊ­O#1#5#5ÊrrËÓ»ËÊbØMEôºè¾M|Jñ|9aÞ…ÓOÝhîѸbÛV¸¡5QB)ÙEO¥#5á\¶*ŠÄêÃÍ–±û4°ð„^L!ì“Â/ƒ/¾kÎŒgßÁ¶,: ”ØF—)î¹–¨v0ã·Ïø9í=ê!ËÕ]Î`GŒk´ÍÌXq&üàXÅâÕðצ½¾ž^UŠ¤É˜b’4¦üýÓ1øûÚy¦ë«”Ë?Ó^ÿ‹²øz@6ÑôIyØcÔ‘ðÕ4È4A¨1‹ÂvBD¿TAü§ËÊOœ¹‘sÒ:…¶µ99¦Øšd“=r’\€c}äaĆÚ%žÛŽ[ÁA󸻢¯­šÞ»â™«ö^˜ Ftfì5a”×çSdÆÔyÕ:”èøXðö\äáDäÑö%*=éúyß<{ße‚DÆÉ¦ѳõúì-z¾%þXâ¹+šuBÍôÝsžºisϲ¤>Ò¹@dq~ëÙ IíB'›#1^óÞÂÙ-n6a†A>gª ï¨ÍÛ}䨹e ÔéHñáaÎyN¤Ä—É:PâyË‹ás%ú}¼ê÷Œ1X‘«‰Ã.FO#1hô5v~Õc߯c²=NØGçÖãq6/u7HÏ\­àÀ¦Ú•GæúqŽÜËfË]·ñ·{-Fpu³~]’¤‚îÒôfc"7Z¼½:#¾Ç®W^»©õ·vPP2AV3F„L4g×sÇ-4­U©Šÿ­aJ(€"Bü¯ùwúÝm>¿¶ñÕ…/ÚÔWڔϭ)Š<Šï¢­X*‚‹}Lù3Ô%¿×vÉ÷}6«})>¶­Èz˜k÷Ù•Ÿî¾uA}z•’Dßáo”Â>4p[ÉUzµ#1‘Jí©B <SQÁ^naêíˆû ÙL¶”¼é#-” ¢¡©íp†~»[ãFSmÙúÝÇ¿J26Ò €½h¨ÎÚÚäúžÇÌ5›eZ'ÑX•5:yœ¸Ú_ÅB>?Š.aú‰ÄA±œâŸAÀ˜¨#—¥#1w7â´ƒ ƒ„Àâ?$:d±ü¹£#7Í¥ˆ¹ÜÍðÈ,E"‘…§*»ð¢Òçß½àôÖ´0ŸGƒgô7ñ׿ì4ÂEû,ò4USŸ 0hDZu>t‹¡#1ÝÙÓ|-¿Gšñ¢Úí­«‡©óï4ýn$BµÎfd_w”Ÿ5MÑÛdC A—Ùj]©¶ÐLºl÷5$Ç{8»¼:Ì;)ÅDÉ™xѦŸm2§f?9Ñéš!Žê),Å…+×·k†’³Ah¶#5(úÒYÙÃIƒ@DQQe0¨xþYAm½µT‡‹72jÉh¢«M=úUêW•œ’”šQÒölzãxlÇ:´ˆŠ³ -ESUŠ fÏ,Ž;ÅfšP¿]:¼\Cñ¿Mš„'£EJ&ã%ú4nÊTˆŒ^t"æÖúúfsçYÑ9YýÚÑãÆ ) Û¡Ùà—,¥bnÎl¨¼õׯ†ãÛ¾Èé|8»-”“¤$“lýonü¬Ñï’1¦²ß{4þ×S;_Ý·ø 7i¥³Ó}}Mš Š¦9s~YªùÁkæí˜?‡¤á„z¯V:/|GÏR‘žÆV±aÔá­å8s•á¯÷Pi½SÑn¥køé-”À²ð†Èé3šÇÏ­Hº»:Õß•ÍûjfM‘ž²åoÂêóÅRRÍoûK=çÇCž#5EP¹µÙ#5dX8¿Mæ·¥y²Æ®]}ºiªÁjô°ŒøÓÕrˆœ(hŸ\ûš ,‘P·µ[ßÏLLH¼IÒ‹T‹dáIß[}kÐ@“¼¬®[^BjŒªƒÞmì0¢¸µÌÕ-‹œRu»M½¨kÂŒ;º{ænOg#-ÖÇÛGëCÀMX8®ÞTq²ƒ.Ny¾uK0Ågîòšñ;꨷«0í•#1û4á=£4=°:%o.6¿ÜíùòæcÚÊ¡|Ó?›±Æ¾Q)©s»„&å'yaÑ/ü£¤ÕI¿”TˆZOb–érÅKÞrU~<®õÊ í4£˜^E^£¨Üj@Šëm‹ú2æ;?¯3ãÝMÇˈ$KÃì´H!Ý8îRÆ#5#1Ö«à Å×öbS?‡ïÓη¼Ãíx}[ñžÌÁfÇ•{Üðþ<ó™7E†‹µ]݇)ÏH~Â1——N{°¢Ä|›¬^^=Q®$óôj\¢îÖ¥#5#5rç‹#1|† */#1¨9ÝÝþ®œóS+´ƒ[(µïƒáÛ‘Å!‰PI¿ðÀgœöFluÆœˆ•XȘÀ“‰L}¹Ýaì~l Ûrs˜WŸ*ÍD¼Ÿ¿#íÎñó58Qìi¼ËGíú'L^\`<+ôð¼#1n•òDzÏ+¤E‚åìn<; ü{8n©$Þåh÷áð˜ðx7Öª10½ÓÃñ½Zvfˆz™úœ¦Z1½µ‰ÖüÒþM"æóÆ÷%² L «ËNÊK•åŸXl²B!7§ižÉ'êk—ÇmQN.a†ÛòÔ ÇôO<F”x³0Tb¬îªCøuÒÜ¥UDåŒlá$7‹%‰Žô Ò _}`x$¤Õxf,C²#1‘ #„§wÊl˜ÿ5Ìâ¿9ñ¾åk/„Ÿ®ãI­“;¸¿O;TR´ÿ*’®2NP‚â£X¿ÙãÎ̾ý¤Êé¦]¯‡%à?’ œâÆ=*D<Ãó3fùíA•ŸѲ­´ªý–~Î?-7í½t £ñ¢1AÍ0#Š¥ãSšß,âvÔ¢aMZäÑJʳzµOÅRŸ»ZM«®šµŒ˜}ZQ„ÀÓl¦;]—Ž4}©‚,Eg©áêØ/ž•/äÊ-ÏËlŸ¼À¬ZèhÆGƒ§z|p8wøáyöØëYßÐ÷´æ†19¥„Y+–|¤]‚ËASqŒá<§$q ; ü6™Î®)v¸ONμ;±Õ:jVÎÉ}‰íaÙ-›ëG{_vÛf#-Ctà™¶ï#1"P–…#+Æ TÖÚÂ7 ÌÄÛh1½úõ“dº™¶»¸Ë`WÞÑÐO‹§LÜòI£ ,Úª¢n’ÔVÚDÜž4ÌpŒÙ#w¯V÷ƒ-y9;d;Á´mðöÛöÇ1•£a¼s=ÐÒ^NÙ5,?@J:tøV±DÐã1ºô;Œ?}U&° TTM~ÔÁxRÁ¨5š‘mž-f²±ŠØªÎbcZ‰A²AA¸£/N)q@d¨ªP!ÔîÜÞ'k‘KŽ«®NºËϳcDUXºU0˜A¦#‚‹?—}žƒ4ú~é¿ î#-•š–J7ûž‰NkŠ|]òEÈsÖjsþNÍÌ…±§òµ5@N̾^=8/Kûî#b|¹,H.P €å£v·çþ6æå©à£IòéZùä[ËuìgôTœÓ8&a9„—ªjâ€D¢À„tÌÝQôA•º¤Ñ³ÚÅAv÷TGo8Ê”ÆÍs¹ÁZ¬:ή¨Hf³œ:Æ`×-áî’˜Õfî2¦yfæ)¤¥ÒÊÌþŸík[»†’]õˆÂ¥”pîZô·Î£3q6I#1J¾>vJCŠú™#5%‚¥¥âCˆb–X³’·½\­ÕÀSØ2Ýî#5£ç1†&Øo(eÇ):)ä`ÉäÐ ¤`4—Š*€aö|W?^•º­ËF†˜8˜Šò[kÃá¡“+~ÆÝ®“ïu\|šbèùÂ>snÕDÊüý0©Ì‚$#1±{د-Yäj/g®;ŸþBéÑÄ£5?2ò+íTe汘$dÈG‡ÔÔ.qU†Ö£#àwå;Vø’ S¸¶‡>õÚ¹Ä@WÜí©µ=×J´gJ¢ðþIÆÉá™ ´éÁøwÚ‡f‡Ã}oÉ:ºzèßBz%°I–lÂÉ\% ʧ6÷ªöñ*áº+µpþ¾8ì#1&ŒÃ¤JÍ¿‹˜ïËI±jn8†ënC6Îý—V¸iu‡ã¼ÄÆŠÀøEízõè(«‘’vxéÑýÞV~Ù6›®ïÑLÓ†]Ó·Ò55zm­ÃéqÜÑ.½“£lÆžæý±°ÅY(ÔÂ!2q:;áGFò K½D8‡»>>?xpfp9—“îË™ô~Žá-óŠøÕt\¹ïñQù1wÚN]x¤oÖÉÓA”ùýVáÖOY †²áÊPt~ñBÏÜHˆxAÁŒ(Þj”=í‚#5\åÚ¶ÔB¥ÎÏD@JÁÖEé½ÆÌø!¶x— §r[¬\lZ畲"X0s”ê.ÄcR»âȤÕ&X ³«bœOÑÍüt£Ûç®9öt¯oKëõÞ¬õ«.EíÉcgsàï|M¢Iùy†½6Ñ"Ü3n“¿×«Éêøª:=ZÓhD@„?w^ÏBé ‘Ký“NŸ«V·sÚb0ÀlU…!lLá×#-RðɵïWÍE##-,@>Ý~Ùp#-_Z;búïÉÒí½œ¡!´ntC¸@š‹¤ô-bþoWìáØ½KþL½º­£!#-~Úã¦tÿצ!ÙéÇoØçI'r"N.xUEC#5†üÂpd _ë|EQ¦´è_shÁôúD—ª5ˆê~|­%Ö]~næ}:Aí–¢‚µ`÷ +ùs9IïÔ»ûhÚÜwü‰5»†0C0C‘1Š£}c¸%ßۇݱk‚#Bß:#5½p/‘j@mVbcbˆ)ãøñ¦%Â@L Q@†ÓJtóÁÖ˜á»Ê „VÝ^¸{ýšl3ã³f:ÍûÊјŸ6>´€¬@6Â1ŽÞçáûì$ótL?ZƒVt­Cá±µf©‹‡Å=ÍÉ ’?ÁþIúû/™lÁ„ÌWøÖÿï†à1€¹ý%ðB(בj>ðõŸlÒ-Õ?å¥ðe½ß#1:ÇÓîòÃ#1×YëþÍøù°?Êæ?Ý`ôê{ÔO…醗j¢Áòá/îÂ(úÊŸÌîý4ý¤1¦Únä´Ø8t——]»n¨wÀpƒîÇ“•Fpªú:éÝúzvÿ¢I! mkâ‚ÝWd"0Ï æ€³»§¦sùŸG¢Æ,‹Jˆ<šýDÀz\»¯Áá+C<È…púÏÑ¡à7-c&ÉDUIä{Ï)Ç—éq®ˆ/mÒ²‚ŸÄ}þþŒêvdŒ»éŸE‚ÂÔh J€]#-ø#ÇûW}è½EH;´»e´ûeB/Â@­Ž(ì6½ÃŠîVjS ´Ÿ£#Ùz­àÞÉ~±+4[;~±éa*`ëI¸½æ •$àÐtŠØ‰Gü<›3“è^æé΢9n³©ýPÄ‹²›7ûøZÂO .xy Y ³ªÂŽÝ-Æz«Æ6‚9GMkC)àvywÖ’mÓB%Š&”>ó(õ–ñýOtà˜BÛžsŸ³Çi•bvò†¤“o^'ýO3ÎlÜÃ#ôo.ÇÙæƒ‘‹2Š£Ô9ÏXgN*'"W.K{¨¤#-;™,^H1ÀTnbæyzë;Ðêlj&½Å6kpú(/mY%`†nøim{EÛ¿­bÂÙ‡×_¥ÚBííÌFÚÏ7hl°I†g…âb~Lÿ­ªC¨úG¨ìJlÎ:ÆöÀXÈ-%n|ß„YO‡iõÄžôÅÐkSßý¯¿ë^Ùm#”>Àãn æúÀHk #1 ?åGY,-äKÐÛLé­jBƒzìgEŠ©(RKwó¶xà¢VÙÎã|/Gsû{ï\*øþ™%ˆH©Û3öJ8lˆH\Íí·ïa->¼ZÜ!ê}äe|]yÈí`4 9†›D‡1• Y¤ÀüÕ&‰5ïÏÓ;<|ÏÚtþðWõsøZqöt+Oí@OM!È­„–3W8_«øÌ¤eãÿQ7·§#18ÖXËŸ xÆ<7¯Ëô»½ ¿„ä©Ó¼{ôÅ7ؘ6÷¬µj˜Ùwărøæ^Ëʾ®Xs=ÉÆCa½Æ©µ*©¦´+XÔqÜiª¯¤7¿)ð¦ô»óî/p™Ú#yûÚ’KíÁ<ožÒÌ«œ¸Þ|Н:!{gìÞ¿¢õÆzˆÝ#5=pú¼=ÍBṵ́ãE¢:RK¼ŽœíÊø4ÇêÝžÙYbI8½þ[ÆU`„tG~÷ƒX·û„ª¢ \¤©ëž6ëöרkœñþ<^ÄÐÚW€Æ¢ÅèC¹Á.$‚ù¼ja3´BÕæ£Ò;ÅXBÛ†²s%ñÃØ´°Á!x  œD»"“$Ô,«tÂ{û¦áÙï÷ÑDbMœ Ì"#÷ü Ó|Ý@ó5‚cmvØ»g—¿ÜåŽÔ…‚#5IЉR˜ìe …T¦2f]¶6Í‹-¨ˆ²f8–aQWAB,”!x¶›0u¬iwŸÈAjöâ¿^µ‡*9Ñ›ø´ƒGžºôyfl+¼F2¤E€ÄU€}A¶4œ¼ùVPœÚòNÿgãþvŠÙ qØm²HH‘ O”HWno·õ×3îÿ#57ü¨ý×g¨¿Ë¨˜®û¹¦Þ¾ìò’„Ä#-´Ì%m¥jí#5@[¨h ƒá€«ä“n¿A®¼Myx†ª¬‡#mÐФívT|1a{È€õ@#+Ú$8£œª/‘m¸4¹ZcŒì)v¥œÊ‡^£ÄZaib„Ñ(Öë'bŽÛƒ; ÀáÂA ®ŽwªÃLtd ïT™ßxá5!lŸÙ@Þ]·5¬zæ Þ§NuæC“б."©¬-I#5¤B+•ʰ-ךè8§ií°æÈo䨉[,êHhE2O•þž9±’Ñ™Dfhsgd缩,^J®›_TÑ#5”ÝÇœ:=z»¥ÛÝpýrt|==¹æ¢„8võr{v¸‘ü‘±áøG†gT4&Q¥·nÕÆÎØq‘stÅÛ$ÓçG™c9áÍFbç/P…0ç(Ýl+‡ª³2:–-ú#uÜ(» ²£žç«ãÛ c­zÜôcvˆ¥!#ŠV:;¿S{Ê:y±±éb¨Ù!–·(XŸ>žÚóG®²^}né›+ÝÈèÝTjyÅ”ÌöÜ ‰ƒJ…ĤAœa€#1ˆÒèCƒQ­îƒdôú||3™7W¦sÓfØm³n+‘$ #h}ï|j)ò¨À㕤˜#%7l+×®18>²ìÕºÝÙ¡šA0&’#5­Ì;a%å+d1q¶Æ‘¡4p5¸#15)ŒÆ«`0ÃÝhÒ?s" † ÑX°1,q`„Í”.²ŽÎ@˜‘ éÇÕDؼx¸ê™}ù#ŽÚ:O³E¢¾KnhÅI{+¦¯LZ¾ò¼÷í·#¨Ó"ŒXšöî×un[’UîÛÆŠŠöjó ›á†?§ï|ùX;›Gz„“0èN2)eÖZ[/<‹}ð…‚â±^ ã¼·Sƒ3éxo§>1ÃݱH„ØôcÝr#Ç^Š6ýG,áX’ê(»\°Öõggا€Cí=L–°¤W‚›€nŠV¿Ðå˜:ãOƒGË.Èû*,Èã%:¹g•s›„ùCåÊjmøy…X‡ŠÎR^²H¥$Z¦É¼NÏ>mƒƒ[ÅÒ¹õ*TEÿSRÎ!Ou.y<¬wìøÏæÖÎ$f_­>óp|œÆª×›ÿ“ú³÷ùp“&;‚`ÙæI)‚w|S›².ÜIXf$ÿDÃ6ëòýÀ·¶Àñ6j:)x“ÚØ‹FÛkA‚ÍÈË1ôÎK`xU­ñŠF%"©áÆ„cÊ#í¬¥í³ìß¿n!Ç^òìŠÍ¾z#+<ë+ù&¤H-‡vÆ^r_¯›ö¿_løs|üþ®x ¹Ù«fa[`hgû÷Fm¹reF’#PËpÅJoËÊW×…Vþñs³!“Û*Ž(éÁ'ò/ãZ“Cû#57³Ôå|eU±F"~Xø`cü1˜6˜8H^%#5Ym˜¥çëö|ïÞ`÷Ç´PwÀ#-ŒOã÷Û(8à÷nðÛXÛ•Y§m«\ ÛióT’ë¼3oõ–âp1Ñj:†#šú:v¬á-¾4ûûלkFÄ›Q-ËZéRF®[¥´[rªém4´ˆyƒE˜Ž)XiŒŽ`Õ²ì:Zˆ¼’$SJ-~9©:ð.™›Zˆ’PØq·›½›šk ï]?œìgš²žß¤(ôÇÞ{ÏhO=b~B H ÃÓÔ[ñ®f'?£@8r¯ýí¿pwÕËÅû£F1õAÐk<އ‡>ôz!†¡J—f:Ô8k®èÐX6šŒý÷~íÃ÷Ž?¿NŽžÔ´Š‘ÞB|R£ŒÏBwÈZ|VˆïàjäòÃwg7#1?/G³Ê(%ËF„»qtAÄÇÕµ€¢& ?ª|i›UаQ@ÿ´f×ÖÃ88‡m>òý¼+ȧÓf—&¦3ý³áWÏ3¢wË|y­Øù”ê|"¤{œÂÕìW³‹œóÆÍýϲ[£ÃŠþ F}ÐañxÿJþ?œ8õý;“0‘oxãòß»S»¬²BŒÜ-íéåºu?Ezz]ñn²]>/ª@nôÁ3Ѹÿ*°òüzö¿¿î×ÍZìîA¼½PÀDc´÷­€Ç­|Ž¥dX8¹!ì’G”ñìîôò6‰B‰uÃøÒëcöWa“bpN(wøWŠ,À–4ˆ†.©–!f Œ_ Ì£Œ}þÿ§è¾=bxL9n†ðªâ£ŠZG5–…õÒæSNÉõºª"4ën»Kºü¿Ó¿ïp £Ãÿ”ÆgîºÑ”¼?/ÕíÙ/•#-':(D% %.åç²×Òü:7¡øÇÒTxr÷?Oi^™#1¨}Ñó·÷ái¦ô²,QlFzZ¦mIdÑ& 3eK‹õÜf&6*þRôvݬlYnç]ºUÄ#Rþ´¡@Ò_îà57'p¶¯ú¶A€¬$îŠPXëýƒýôÊÀonÙéh†ˆv3Ö(áåæXœJ [‚„% ‡œ‰“·6”¼ß@u•a#-RζQà³Kª£Ñœf9Œ†¬(E /Ñâ–£Q  ë\o¿ì®OÓÊ©#-”Ù*0Q†õ\šoXdQP‰Z•௼=X`hh±ùK=%ÿeo~SDüb5Feùø)3P°è(ôÞ€üŒ‡çm!ÁÙÒŠÂ'öÐò!œŽ?լĢ€ô°R_ÒƒÜ_ß>]^O­~ß›à~k¿Ëìwåox—Ñô}"Ï‹©|œÉ•=‡¢Ðå´r©ôìèå;¿hÛAô–š¦¶ýÂ#ÓŠöÇö.Ï^R wc×9~”ñ²+ÔuW¸êè ˦­öðPðü´x(é:Eºl·Á€ç w¢Œ°ÁÞ¥Óx¾¶RœÖ¬dz œDÊù£¤ˆžÛ¡å%„ûzn6¥ÈXѶü¢_ÃEŸ{H·#—qÒå•]þ:{#áºMfªÊçþyޝᢷopµ,Òá½û» ’¾ÎxŒuäâUHN#5–éïµà‘ô(¢þ†4ÁÓáz—½C+´á«}÷[E¹Þ£MoëpÖƒp ·øªLÉÆ¬ºA£ÎjD†mºÊXí.s¹µQÿ'DÒùA“§Eú<¯Ê6n“ïÇ>Yv×…!Uï`á1˜·"ÀÝ*G‘þgVe?¥ÿŸÆ¶{ÓúϬü§õ6,?Y^0žéŠ¿À#5ôO‰C€e(^œ¬:ÞŸ .o(€î*Š.:Ï0™ý'{Ë!°Æ±M—Ùûûì#1))'k,HZF£FìÌ"m§as¯ò°¹ý=:íqÇ6>šØP*}wÛ{¹¥§‰V}ýÜ8×IÛtü*™ò:mƲC¤Õî2®²GÄ´¾Ö@çß®¦ÏBþ± $'ÓcÃÞ ~A°:Ä$á*ü"^Ud<ˆ8•(NÒŸÓOÞÃM¾öû&.й1("qî! #1{=ʸ’4@€RÈ(¡€?*¨Ÿíþ^׈3ï í°ñ÷!ñÝåýŠ‘TQ_#-¦ÓÕãØÏ)‚ŽSã;ÎÛ,,1#zÿŸÒMa¾Ùb ?ðxIW®¹| Ùa~O2'ót=?Å\¡Å"…>#-7IQhI„µ%E´E2B•.ý§lô_ÝÇžÁþ¤aütma(æ?åéݹRi]­’Ф-…j"YH½3©IpŒ[À Bê[ï¤J1A¦Ž@QÎD‰ˆÈ‡@x*,J.ž«ù´ô|}¿?$ý?…•_G2å=¼½2UAëóó}¾Å»Ó]ƒYÛòÃé¶Ó\xyH¾ÏKg/5Ìë»HÓãš )¢ìí\l×§­þ›Ñiìîû›¿Ò-û±¶Çð·_wÙÛ¸jÝí—6û-y^ˆy¯Æ8Kj²#1¯ýóuÊÂïô72óð“Fè,Ë—[Ã8*›¶gæÍÈv;t—âV“»H¸téý¶߿ٯ³ÑžZ|j¢Àï&[3Óý9üî;£ÁÈÚl¸(¸dÝ~]XW¢zy¼U¶ƒËN{qÀv½‡Fiø'§ùÇâˆø‘È@2àÏgÞûâ½™º}Øë¬é°Ä[ó½7eîûž8)²BXÈr/; G’ëe ÷_ʶ׫’M3µàûnTçžÝ>gɳYFÚô¢_#50ÑÑêÕõ›]Ç£$æÃOó±Œ5U»¾WR7>‡Ž½š#u¯ üuKt.Eî[Žôt<ÉXŽèÖ‡müñ¯D´ŽùÉÑ"ÆÓË™¼m­iªË;"z¬°yJSã¬5÷grVÑ?žæ4lõSLj0#5˜\ç‰ù÷ˆÄ¹‚ÅÌÌ`osºÕÍ/³Òç½G9añ鋸ú9¸aÉ·Ç¿›#1Üžs·Q6î^r ïW¯’ût\}*‘Ûe98¶;µèhxÔ0ç",‡³·BÆC~ŠZtz¬?G¸¤‹—äg§|•ˆ‘¡ññTÓÿ:Øc»ãöjtàcus7[ÝÉöô¥ôÆ:UßsÆþOœ&±—›ÏνŸq§·Ëç³aËѶúN#3#1çh¦±Ÿº#ÝÑ·â€ÜНª»ï³?Õ§Ã]ösjv±mC-›œ}~Aé_>Y>xé g ÛÚô†K©÷{¥öZ@eTc<<>x;IÅ•90¶=ÐÛEõŽ‘#1û<—ßåÙnQÃø(‘Jü•N\W»åäñkêÏþS«1€m§¯±<ŸÏÙîŒôþ×áwíúö»vó³ùhœß³øácÚþtz´÷Ó½¡ÃÈ–ƒFÓOu)12‹/n“Vî!Aðu ûô"¼r•Ä(íÃÍOªñÒÃ^ˆ¯³‹Z>]|ò¾˜;~‰½ÙÇãé›|Æ–ºzú,ùÝXc72s‚ òãÆë<³ÿkÕûþüô#…ú9T<;ó#-¸W›lg¦Ïo³ô­®Ñͳ<à}Gáðûý.ÓÛ£enê‚d,ëùgêôW·ù\.R©Ëû|¾Ó˜Byÿ#-Ô½ÎêÔ5xååm#1,<¡¿ìÖÔþßÞ„î{ŒP‹5åñÁX‡ùÍãã¦+ú>¢¦õ¾ëK9ÔèÖƒp*‰ÒDº¸xÚf£ñ÷ˆMT^#-REæ0˜Ï¯S¯áôa³üÊ‘³íõýû‡5ž~‘—Ï·õü>[º0Ѩú¾9uvgÚ%ûŒÍùlë·cç˜ÔýGw¨ß§Òݹpˆ"¶ÙÕ³e«³oÕ²þ9© »R'/à½è;Ê#1=v8MÃÅñ˳¯÷ê´_öw{G¸}Ãxél9|Cö(\¶Ž†Ù)cÙsÿ•@×û´íª#- ÿþ¤[´x}ÏŸ0ûuÿoËóþ•é»Oê®×#1C¿‹Vïßê¾5Fß:pçŽÿ¯7 ³Wa¸tÃèò{×Íúô'È?|†‘¸(ñåp"!‡¸s¸~¨äà)÷‰êù¸Ž#-|c÷}‡Ö•Náßãâ5c#-˜ª½J?¯¿61óly^}=w¸'ö’‚¢çÔO¬0DUDG"l ŸèŸsyåÙüûº;—YÇÂÈÿ.:£ò–+ø6×b›‡oj¶žüç¶±&_Kß;iäš(úAÏûð²"„ƒÈ<;`(0Ðoîºß«Z®žþþTŽ—ÀiPB|Q¾‚~¿L% ƒ™´°#-£ÅBr²$<ÕŠ’ö0atµK ®c2J2A7º%lÈ #-¢ãUÞŽhÜ1€deùTNZ•vak:ø)ƒêšW›-ú¼Vgí´g°ç¯/ ØôG³§F¬ëw±RÖ}~VÛÎíÛå™G©Ø×Qç‘” '‘ÁEE¦ÓÖ7£Hýgîä¦Ï$ú¼n­ÛæäüG§«CøÈLÃ|½Š?ÊÂÆßNGã€h¡ˆ2ùƒ)ñ¹_0­gÖ´¢+‡9ç2PåèσYkX+7t{éËI­’!Ë*¨s…’ˆ°NrW$¢Ï‘ºbªå®‹)°±¶1/1B+SêËù^fë5Õ÷*/£ë§eƒZ#1¡}›»]ýçàS!èf¼(©”-´¥ƒ:Ò5àæõJ1±m6ß÷oæ&u³€$ýþÇ9nIY£;ÒÕ})Ñv«ü÷yø@õƒ%;RI»kéÓŠUýŒ„NÒ{#te¯ºƒÈFX±8øÿkì·Ó”º¼´…žÊZ=v^¡¤7Œ#5 á%ì{u)$¯?€Ð2ä=ß·…RÑú{Ù-K:t Ï“Ó~AÊá)èx‡¤¼²~@ûyã¶­1´¾:Àë3¼ZtSý?/ÖSpÐÉ÷£”þ…Gñ È칑µ,nÂVX5 ˆàÂ0§ãê¨Æµ ¥m±R8"<RŠ”I»b4CC˜ÜX1UPq¸TEŸÓ¥ÜRf®îSVe™ªŽ„CFØXGb–Ç`SX äXÖ0Ó$¦ Õ‘447B‘KX­‚/mZõÇêéÃTâuÁcm#1¥Õ0Á±I#-ëü[xºjÓ’àä‘·´Z׊ÑÃ&Ó¬Oçü¸oG|ˆý™Îc: AAôÿHüV°¦xMf@zÂkýÂÐÓz•Ô[zõ9 Ô…‡a±…È£I–†31Ï'øúøýù¼|ÿ–þO#pN0£’uæT2f]؉U#5ŽŽ”Íf.ÚF“z ˜Èd‰µ#5Q ¢VD”Z…]ÕÈ5GÑ”‰‘ÌU‘UT¯õU@J±l‡ ®IÝ÷þ˜¾Ý~¬¿-?ÇÑ îØìÄXØï»=šL çvù–4­Ms.»+Ï<<³‰ßÃ\´¼wZ&Q†sîADzD’=ùK®¨ûÇ›³Ûù~Õúõ{4êÙÜQ-$AâWõü#-pÐé(¦ÅSwÇÁÀ]q×O_8®VÆÝüœýYãídIּ߾Ïðz=ïg½`Ë ¹­ . <}7?—ùÝ|¦Å-ÒïwæÞßÙÃ_“gén¾®àM>ôMÙYùÏöiK :üÜkÀž_¬Gë?Ù(ÔgöýÁ‹0•ÀÿS¬{qoŸ;cFmw1[ª-«DŠÚNG%cµÃÆÁQ¬!ëG ð‚¬Ä7ž<·ŒkœÖe,•"‰7SšŒm¦6ÐŽ šÕ*zr(j#1ŒmF)F”,v¸XF†Šä’*ÊV[?³Ò£FÁöRVÞµ æ"ð]|I¯µâ ã(èKk™Á$-$Ò’.NZn£ØKFñ¸4ó¤mñÌÎêð£ñAAIeC…N"G»Ç<.#•2ÿ A*5¥‰6*æEG!4®`¢Q…]i…F8‘–‘Œcdó•ÎÁ*¹àÎÄà…V’#5I%G1¡d3õC‹T^§%ïd½c==ù¯L¦Þ’ÿ´cÿ?'+\,»î25"‚²{yÔÏû—ñ÷úÓ¸ö²ýJŠ[«F…ìÇçýb)ë†;—™×‚FtT¾'yâC­øìÉÃÝ–Õuõû‡¯>mA0«Þ[öšÚè)ÿoeá3§F9–ûçùj~˜îîh¨xÎìÒ±ê ÝrûËëðÑ›""'NÞ6é¦ÊñoV{-3ñ]ðé“àmóö™K´Wöÿ]ò½oB8'ð—8£uîÞ¤>gÊÏŠŽ%Öö" tð}¹ìOÖOŸÃr“ݤüoël¨|>•Sj#1KñyËÒª¥ÈI%™UU®îQ8$whM 9,HÙby·÷ˆ øAø:Ò~¨:_—̼£S;—ùI£×<³í©«O+>ŸÝêÄóUQ…?’‹ ƆfU#5 ¬n#5Ððv¯‡òk­*X@=Y²gF1üÚ_}pn>z;: lœP^˜_S]Îy$Êá1?îF=Ü8C «²küùDG¡¿ÛóùíÓùä£ÊZ%T»…#5Ï׬ª‹Uü´޲FÕ#c$#ˆÜ‚±#1@?¥àÑüšºoÚ%åÑ–(£ ÝŠ:ÃFFºß7®_Ëßòy+Ô:¾þöØnÀa/Xí퇃ÄÔ²`ÔB„‚©#c=?›6z&As(O« vó›®^4Ɔ C„¥³[·FŠFÑM`¸‰ëU‹x#5æ:ØAŠ <ÔºƒÕ®úè« Á­dhmW’0Î5”ppE30ËBƒH¨Á‰”V¢À*÷¹¢k‹KĽv"15õâ¶D=ÓxŒÇ‘À­«G:ó¬Ã·×…aˆ>Ӄ÷Páp9Å#5Æ©Cw‚2¢0Õ‚åS¹•1é´³vçÈ•MàÇ!#"êø\–Xƒ¢9«CãU›z´ëkU­"­U¬Î—¾”&ƒ*IPDh9(hkmƒPvàÚØ´w.Ó7@­èÒÃXŠÈÚÁ¶î®…£šU€¹ ¢ÙºÚPª8õOÛþ=4Øúaƒã{{w§‘‚ç¯s]Ã/:•¥‰=2#1ajÓîÍæÊÙÖ¼!βˆps) Á`¨Ræöû'qoU³Ð,AhB‹k`F¸ñ÷ô½ƒøx#-wùC¿\1gºJH#ßû:/E!¢Œx³l‚eã1Ÿy”ÜÂ$ê#5 Ô#5ò÷ðéçš Eãëß.sªìs¥—†Ä{ójOÄ õŸõøì³NZùvzº@èé¼:ÜvêõšÇ¡²C$¶(Å‹ JªŸ¦•T@Á§/Þ-È®‘©"âã ËJ2!ÔéòŸN–#5¬’éÔˆâ$dH‘²¨q”wãf¢ÆâDéúæ†cý=!£Dž›¼Îö1m@ØzuÄQé28ôÛ(qŽÇŠ÷<^`hIhºyˆ0‰PN"§Cn#1æ9˜TêðrÅUŠ¡©Ëo?Ù哳]®î˜ÖÛæaL²¡(,,×BµQˆd‚üúwÿAü.»ú~/@êüùxÀxüøâï—³H»-^!’|ø‹ôòJ½µ`Q‚*e Ò‡9A4ûfϺM‹–•©‘¸·HÛNÕî3¹Âes,dS#5 E¿mQ±¨8ÈÆÓ1΄#.´Â@ƒÌ}¤u·Ó†´ÉÞÑm<¥ÀFÆÏÌbVÇ•sˆÂµÚpXèÜr¥êôaÛÇ…F0О5®»†g†Í`q¤N#1R绋GÁŒ¢Ë#5‚³XL2ˆ\5¦ñ¥cà#1 íÓF ãÆif¨h±ë@Uƒå»¡¿Å¤aVÉSF`×´Pvª2CaºcMKU“§Ôã±›Ô“Qº:H8m‚èКIhN12ȉÃ{±8°/Gu©#5…q½˜2Ñ–Ùlbq,1}nJXfŠFRÛ:L¨å2!´øÜV¤XR…y™dŒaL•ôʬݮý—w?Ãz;ùôzùõ-ï§‘fO/áæòóéô½R½.§iÿ‰FÕzÖf­Ó}ƒÂÛCYxgÔéÏX?_ïòbÐß«e’H;8áåÌzåœEŽÚ#5Ѽµ!k¨:£ºY•á® lª¤$u>MþÖ}ï¾ñ¹,ÖÁÒä=ÞY /$T‡Tjm¤n¤kž1’œÕOFð2®ðËmÆþÔÑQñŠ.á¼ã¾ú3Q·&䊕t¥¸Æ=ØÚ­að&2 ‘6Ç­W!;úÀÞ—YÊh#hŠ5íæÛÆKß1·M±›bˆ‹‡‰#5—#1MÙ6!L”9†·ˆ°,PDøÔ I>Ùxœ&˜Î×#5îP™«sGöš(ͱH x4U=¦,Ø8çãÅ!— OÁ¾æ* A"f6±ÇI·v/Ì-O&ãŒÞ“BÃ-:lj&›ÜJ*gYÒèÀ¤§!‰Àd¬Óâa¢%aø¦Ë-¹ç¨aÐì’yÜiÝ£²1^Üën#-7Ô#1n{œ<à(´ýDÅ+{Êv¹óGM‘¦4Êý;iw9ó;aGÜ›6ÈIƒñm0ËìëzÖ6hÇXÖ¶}^Bå 5÷Ñá©t#1,lmÃågK¨ôÌ<­Uky2ª ¨iu%.#5Ыå;ùºh ;«ÎÚc…ÁÄ`ÉoÉm† ÃC¹¨äd锚á(·‹|âÙ6í­t®TGÖ$H5¸ÐÜ#5;+ÊhÁvۆ婸òãiÞ‡^Bö£¨ë³º‚²o1,•ÿL¼È\ps¸QÒøÓiÓq$R틎…`'áA¬XpÛZÕCŽ‹÷®±Y±Ú91MÇK£ŒæNLìeN©Æ¦¾Y°!ƒÎcP6¢µYctNf)ÎLkB¦Ã›¬"HÎ6J) bæ²WK»`XVCm7ÛlÙ8iòM­I¡‘O3c’ÈF2E6Jƒ²Áà#1.•+TJ §?Hq‹¢#1¸ª– ÐBPÞMÎ??|Ú^) ćZÛ¥=*!ȸZ}ìÄ¿eƸKÝðé·­0p´sG"yS‡»>8rqB1’ª8מm[t?ÔΛ=ÒÍÜhIÄäGL®#5&Dëé“í¿kÞiú#5b-ß÷ÐTM_]Û¦f•¢#0ïÍ4,¹‹èººˆÂ×mAtUüetDÝO!RYY5 D©œ¶•K¥ßce¬-$m#5pAG›p3kxfœÊ¯iÑöE̾jaC&t¤Qíq=Œº‘#…Q^5ÆÙ-bÀgM2dÐçÏIÑÏ#1|qƒhÁŒPšp¾%ц={fõ×÷%#1ÓUFuέïŽñ}óD¬óLáCXv™Î]áŠè>º¬u¼õZU;„ à æ#1¡è^J°S;tÍd8çi|š¿ð¸û³»á÷˜›U§}CÎù'‚²rÛA³^?ÏÒ¼NÏF×Çž»óM³ç¯fü¹aìá/]Ü2M¹£žµž-.U\6áK«ÉZ¦©‡Ýá Õ•Y›†}óOsMc¹¢¹¶yUõ¾×6¸…¾ß6@ý¢«­Î"4&ÛÉVã²T‡L鮈ªëíTǹñÞ@…ƒ–­ñxάԘ“NÀåÑqH} $ò?hçiå½™7ž"øK³œÕ?©1 Ðtsù‰úÙ‹Ä¥)ÖД|œˆùh‹•“SƒßPkàJ6šê‚,ã4¦ÐPf·­|ýz3#-šjôžeý}’ã5ñ˜x®¾Ü¨nÛ7@£§Œ&TU>S¯y”ü7鯓¤«fY@Œ¹r £i‚U‡ñ%·`6¶L[2;BÑZ–jå&%Á2g§רðü/åýÅ·»—T¬Ø˜‰x:x1s#-?Q78Õ™ŠÎúOèHÕ_eošF}û]yIHB¥§Ù,²tJÄ=:q↞øò©¯#1Ó`Ál5ÞÀIþùF*xumŸ&B•ˆ_iG¨²Ý‘ÙO’9Ñzš3°˜‘$S5_Û#-ºœÉ€¸O5˜©Oõ´ò^Þ®DøL«#5n‘¸É»2yÙ˜~Úþ[å’&”ÖIÑÇM¤"H9¸×”É&¦óÀ²ì¹èŠ$à{e²êòkÔ™)&:iýY±G“,#oTÄuq 3Üš3é?uuæu»ùYfím#t¢ä'£oqb™ñ±–ÓåÖ1Üþ´bqÔy4ÜÓ˰ÍJFÁ÷TWFÈJ÷&Õ­Óš?ǯ.W¥9´äÔÑò´µ’}GTq_t¯fÔE­K¤™læšNÆ~S4¾\÷c%|á{ôlK!¸vŽÅÉÌètãáyÂñ©:·ÅôMìPü²@»Ç0Aç‘ùDàÚxP þ<îd÷É“-í:…bÅpç,š~ËÒÊbG6‚“HŒ$˜ð úgÍ•luazí‡>˜© L:- @~½p^ÁæDqן£àºôÑÑ^„Q&–OÍqÁ¬1üdeLå½ ŸsM·Êò½Ø8#HÐ Ñ\÷¡“1fÔÂÏe¢Ù¡cQ.¿‹Ò¯c/3¡îvðšøw.Þ…’î¢Ê^~~gæ#ÖŽÌÒ~'ïœoº“¢]ÑaÑR‹ëŠÁg¤¹%¹W?m\m„ª?b%~ÏhÍTîj+9 ·ù2;Á‘‰2Ñ|¢³kx“&A"²Î»&\zgäCmfUcã;Ó}Æs¹Õ2Ýo”¯î'|Îðÿa§çºàÏ”aŽk,­eE|—ÖR³1v’ x­s³³“ÆÐ(ÿ×`¾91’­¿¢à{Ae{žy j‹¤4y ÚwÚ6íp?M÷D,åG£ X«¤<_{K|u}uÑJÅ B”éîR›ôŸÆnÉ"yºó¨²_D혗šßŽI#5µÑóCªÕ¼2MöÕfc­‡É3£ óÎÓ˜}¥™û5œÍNîQÝp­NHéËôÓéqCéktCíí];eõº-(QåÎòb¾s<ú×ÑVÛãÎsnïnaŒóÌQzy¢Œ[y½)ž©Ò_]¬£ßŽÞܰÁ¸†`c"cÑš5¦]å½ÈïÞó^QÒZü±v÷¿>T‹\ˆè-üÆÝíSÅdù]ï¤íÊØÙÍŸ H„(äÞœSAͽÈàœa©”ä;MF‹ò‹¶9êçÅôÄvèìëÕxç¼cÉuT(ìå:KZ÷½­û›Ézʧ(XrvèŸ9»œ¥öo“|ú7ôNumÐH⎑ΆÀŸÊàÎaûf‰»Eòð>®:6nïuÁŒ ^ï[ž†Zbµ»EΉŽÝ¾¿$8lAŽõL«MBô‹’Qo5üpA|{­¸ôÄ;ÙRÕ8>ç<)Áê¤sS‰ùæb’÷ͱµ }èÆm‡Éi¹ÁölíÄíJÙ{>L;2a’#².öÛÍ—P{ÂÌ•mêÔÀìç’iîÞŸBI/Dwï:øÇ‘ ©^v›‘N?³n΂ÓÛÂé/n)n¹3 „W}4ô!KDO?Cb¶Îù™K'‡xì‡CíçM!–¶OÞg¾)»ÕlöwhíÓ;ìi!õ—!àî98USHè†deo¹µ¾3^X„›Ö‡]K_âÁ46:9û”AÒ¹æ7æŽ@ÈŸ&ê<ˆ|95×âS¾çã¢|[͇éMÃÏÍúQ%[õøõ£¯Zˆ‹]–kœÐ”À¼Æ¡†Ø¥å£âŠêìîâA+gÉ›­|KƶNñƒ7µL#5¥à ´|G…¾ÝXë¢Àï&Æiz=ˆÄ<žðçñÆ2xáÎ1.$BM#-èò}”µXØ#%L¸%Ò#1­âš›?¬owìAHã[Äüîÿl½ÑÞm,é `Ä(ý{|Œõº­Uû~gFº×Uøì¥‰4Ρ›ž:ÿ1*=ä$­5Ç]gO¶tÀ¹Vç-¤ºú¹ÖW¨ÒPyÖÂé+»TOGì†^§K ôp}Ù­ü—‹q&K4vËȬ¥mÑ…ý73úuÛ}ˆ5ÞÖbÀ­[?ÜåÂÁ%³jIðk׊d8*Ë4NX½®F´yt@3±¤õ¾‹™æ'$Æ¡Mb~×§3å¿;ã\â+ã×–Ù[„`¡”`ÿíóX’ªìÖ|õ‡-ùk¥þG[L™;ä·-¦+È^x¿aa¨ƒW[«UÖ,Tó~:†ˆGÕ­^ýûÆa|VgËÄ︹Õjù¾ñÑU™ªXÁ1y0ƒ¬±¬>6sqöËææ±5 ળ[m8em[]‡—ŒøÙõw=y`¾nÉ‘Îc‹Ãý3•Òý§n»t¬·VQ¶ØGpÙå‹ðS·+3Ë8´³HOû¶«£#1§W‰4‡ˆ—•{¡| Ý+£Kæí#5&PI d9a†‰Ä lc`ÕWcmC4¸97AÓõÑx[}EøÄKßdèÂFF$wªŒáci}Ö »­Â7z鯱£‚ã‹KY‹dhõ.Wò/¹Áû!û[š}zd#ñåk¥&Þå""#|¬ÉÓ{µ@#1„Báȶ_e0Šx(ò`ö}ÓÃwm¿$˜ÛÑ},EÑs­ÔÏÙß.}J.ˆÍšºúnîµRëËvá÷ AåÕh&•I0£5‹¾ûp²uÙØ$ :ÖÆN‘/V©q.ì|øù`0¤ƒ m#52tà -Tgß§vBbÍ!fû±.а~›ƒY#-VJ´2{¶dÌóªÁÝ]¯…Ð 6ì.r—V2m9g›šjYð£‹rºáP?D%¡tÆRr쫪›uê‹å,èKÄިś”NMßœï£H"—XÈMŸÞÑr\¦wP¡nuÙ[¼IRè(‰ˆÕ¬ûˆ×ºK6O;]$ïQñ/Ú¿HÖzõ:çn¥ù#Fãœg|dç<Àd®¸mƒ£‹ð6aðrõƒ«l ÁÎÓãÎ’Žªï¾”ŒÞJß(#1;¬]O‰€Ñ1Ä-dx†ãŸÓÄïK?%Q~r u¿¬EsÎÁ¯Nσ¤µÍÏh#-è/=é¶Yçhé˜Ç¾“v³Ã±„µ./#5ã=‚M‰¾ãFç¤C+«Uƒ’g#1z““#5¸2 ÇE—:Gy˜º#- ι¼ö-‰bäî-làò9XývEâõϲ!÷tZì´ø×[¬µC¡ÍÁ ñ;7^ ù—Jê§Íß {&‡ÔX÷©ç×'l3öÜ}³ ¾œdûi‰RêŒ@°¬®acÞŠ¨í8Ý+$øù  #1 Xm£¹,®Úân_ªx|öæÑa5üj¦W3¿3[*S­üõðÙÑ£^tZ²Ñ囋¥¶—Ú®½Î‹ØH!a-”9“¶×ÈéäFei«D«#5ÝU»tá^˜¡¶¬7ï+æû]…+97O©÷xà¿ë½¥úÖÈ„§drÒý*üTk‹…‡òw‚ È5¹ŒüñÀèºQ{ô}_½òâ³Ï+k8ô¨Š¥Ü0r6žšIc·#®G:%U^7Ž{çDrKq:ñp¡':ÆVFXÍÐ%Yî]‹äê„ø>4`ªHAÐX•ˆÝaɆ¨ìpþ4Êpµ¶öÈÓ:áIÛ‹hÓÍ!¶áG¶ì!j–·^ûãuEá…)ZVMmÆc/1Úd‰§ûœØÚ«ƒ¡­óË&Á¥ê–Í›ÜW¾Û«#1NO’ÉHZÉÌ;÷sZü,ÊÂñVÃÂõ¾6vmwUN\ø4ë¨1#1·š‘K'²¥â4¨†=š,¹û†¶ÆÈºÿMÔ݆ jÌk ±Â°ŽÂGdß81-„¥é‹úqŽ6ìNÙ«"VHq¶©42fd47æ›»·.Å’E¼gו„º‘DŒ7@óÊýtåÖе ;LMÆ<ÅÔ5ã2…DP¡Å™ç3v‰.‡6ì*á#5…¡‚ïpBÂ6S-¯hÛß*Ëðm¿üê#5õµ™Æ6I‰ÒŒ¢ëóÒëã„Xæ½Çìù0sïð×rØù×Âz7*=ó&-bMYsGc]vëˆQEUì*3ª1ýòT¥£ ÂAê¶7kêê–è-1u79l hŽNF€&Ý!•|'w)u#-¹&£0u#-`²p©•«Dñëxïi +J ÞjbK±ŽyègãT‚¨5YÚ“QËÎÂqÖ7ô÷äè=¡<8ãš~¼EOå¶)uüºøÍ–êÕCw ÒöãÕtSõÛ²œCò»qM}?fØHE #5}ûoEpciÂl!N$ogGôY‘Hë³Gp"ë\…XBhk—nÏtuÕ´ªî‡:§3·ëóÕ2mÅŒw Ñ}Ò‚Ü&!ÓEŒ™³j:­ ,G*ÁÕYBÌ–¨‡ÅçsÇÂfu¼|ãØåBèãíöDÓn²öfýßd\½âe8nì•L{ºûUopã¬z¸áÕøÆ5&eaQ#1¿JÀø*‡¢ØÈѪ‚Y¦ÓÀ³< QÔlïZ¿T@h(}C•úSx9À ãq³×Sk[psåψ䈑RxÛªŠki+®öØ£Â2+•Ã3ƒÖ“Ã{Ü÷¦•#¦ ‰†Þ¡—׊>ºß®5£tëÝ:6åŠù¬+ë¼mRù‚Ò÷ ¥*·‡uÂŒa½L<Öç6ÍÔ€ˆÐ±â5jxÂn ÈýnL`2scÓHR0d66!7IÇKÃÁS;®sã¹ÁzU­3¹†HH\I7°Q©1f*-p5`©¶¬õX0Ôn«…Ô$w-¼NQoH­<°Á×ó¸`ßlcBeêþÆþMRglêZ’ˆÙ¶.o£@å4‹1–—¼¾#1´9œ´.{ç,e©©Y2Å–»é¿cÖ#5LwÎ×%ÐWë·–×jìb•0þáó6gQÇ{Q½U†×Ì |½d8Å`OL'–¥Á¥GZir Ó›©q\7¬kꢕã}Ó²~SœáªØäô#5P ×Õm¼¾‘ =´S!NOkÐliÃÆFÃç#e_±SŽ­"ç);` JN â9‘ƒŸÐìÈc¤3º÷såº2N¢ž«•8óZÅʃtèåsø† ʵmÚF‡;)ó]m[°)<Îk$°W7]pl½KÚ<˜g¤#-ª[¶"‘UMËås8-À-uìo´k?^OÑ•Xœ¢"\‚B¬#5.fBð^DÅ…åP÷ c£ÒeI‹ñ³€Óh¥>Ÿ).³{gýþf®A;hÑd×ý5æý·XK£¢ÕJ½ÊÂøŽsÜFí÷8 ‡!Æsj±yó«ªS‚ñ¸t=.šqBÝQ“=ãá9”#’¶nR¦#-éQ:JÈ9£®æ™ÇŽªZú‹q~ÝRF¢>ú½_öyö¯,Py! w}¦êxÁ}ÞQ³Ægžót[ÛÏX3?Çû¯ðçå¬íc?í;×—Bý‹ôZ“‰1QÈ{Z¾é6´"³azüXÆ×ÝtírV#5`¤†;J@Ùò+RL›.O‹„)ù¥ñF²‡£¹˜ ŠísgÖçå‹·±ûñÒšíñå+üóƒ#·oN“áó——Óëv¸Ä0áœÙc´yTVÍÊ 9¢°a#5MÍi„ –YäYîõ¬ öur?ƒÒÂxÝ~9»Tcá§EBYq‚®™ˆ:£y›ÝÈ®~KB[i')KǬ>¿L…#1OljÔËÕ<”ö~ù<¶.#1þO§Æa‰9´c'‰w#1P÷ø¡²Ê4¼«“86Ï[­·û6ºÊd¡¯Vì§YAtß4´tšÒeJûÜ2 ñ²‚Çêï“8¸¡æ‹a@÷ý2kyÞÅ,(Å#-TÚ¹!±µ¤ê'$KŽ˜‹´é„\+éë~eäyÞ§2&0ÞÖô;tjÇ÷ËIXëÖÍ屬.î“NÎ{¼KÑSdІ*„¼¡ùfÚÈ”·lt¯RêHL>d`+¥4gð€´†™"(`–òZA× ·™Â°`œ²á±tB<&4#1Xz¹»oŒæÃ©jXsq‰?˜ûÒæ#Ò¥lô¿ܤ#5/[ñMTÔ+JsïZFlÓ‹¸J:û2‡@é#5R{ãÏk’ÎLöaŠÓF×>BbŠ0Íg%¾*KÈ´øƒÑõPü#5ß^±Äéº@»ݘÃ;l~cåI#Ç(ª£Í{/¥b/1ñÏá·¶#1´>]#ܽºpx˜Ç|ÏToÇxôpµˆÛš5ý•‰Ê_Ï'7D--¦É9_¹e}&á‘×EÖ[…ÉWJÉ¥ZÙÈo2¼w£¤…@øLÒ÷2S¹ø¹=¤.oAö×Lßß’JøKÍ0¶Nm†,4¹Ä6ªE³,ºj _Ï^|8Ù]s™×­ž ½kŒûªtj½ñ”‘Ÿ½ÖçÊ2*#(1sš‹‘µ¼\ᵟß+îSwÎè¼ÛM%#Ðkº®ÎMkÅU»\=¡¾ûãÒ[ÝðÉãÜÜ#9é:‰â%©øK!T§¬ÎþÖÝûpÿY!O íØõçrŒ7GÏ™Ò4£™‡ƒysï]}G3ÚðW¾pÑXA‡Xv!•GJ¦å°pÌm§ û]ù·ù’Y墿î#-¢ÙÛWÂúãz¹ÎÇÏf€Ê^^2‰E[@Å š­„0a×ôߨLƒŒ'w#õ61‰Ù¢ž1éPmæçMj¥ý»NÅ9¾ý%¸W§Ï/+ášà¼t3Ú9ÄÛìÑÁoV°ì< ”ÙÎØ“ýèÓù `#5ée®û3µÝ_ƒ:g¶Ôaá­ÎXDò¿=±íæä*Á1¡ÄRߌM~Þ¹ŸÀçh¼T’|ÑÌz@ou nƒ„H!¾ÇmϤ³g£¶ÿoNx“Ü\¤&H!ur¸hv"ÍÆR)Ń̮~›ß·dZƒ"pV×=Ә殒E7w¾¾ð\>3œŽøž¿ó81Ó^Öâ;¾H±T‰&ýCUBR„ýŸã'b¸†<:m¨Of#Ç jm {ÜØFÂGƒædpзy#d)­×é‚!¥øN(EQqh0 xnxj»HKß2Hm¶˜Àjމʅ<÷¢Ø§·µª³ Y8;e>—^`Ó‡B «/ª[¡,ÇQeÝÑ(ëPý%éT soì÷Á"ļ "¢ãÆaœ‘¬¢yå'eî¸5@¥Ta{3º,QOqy¬Û‘•yíi>‰Å©/{½ïÖUîM‰6†wÖŽçm‹3Qq ãSG4ž‹m?-ëû3¤ÞgºÕs+µ9sîtº@ϲxn¸Ž]9Ë£@½P¤*9-ÎÁHÑ£§,4ÜÕç~—•º‘#5¢R#-^mtÊ%•øó‹·a>Ôÿ¦Ý}7#- {þ¶ ×åÓÂÊY$Ë­PQ#5'i¡ÒFEö»i/®–ÕåòBCo8â&}l•‘¶n#- <_B©à©I%¸ì˜{õü0°jÒ=í{¡ÏÇÖ~/xžÛ´pekeÙ‡!HFû—³‡Ï”îµå u¼äÿâ¨æj“ÜFþúò‰öív±¡Vj;b®Ü¤°Üœ¾9jÈB[ìºUS[X†0XÚ#5-†2eÜ(¬#1^ú°G•eV ŽãÀ»½n¹¢ª]7Ä\_©–).Ö†J<#-©3æ@ÖX¤ejQé˪ÞqÚüƒ¢¤–ó!ž\Ť9/ B­“"ˆ©(Šå†ªcŒ#-:ti[sÆ#1ëoŸùΛO$ã¦o¢1œ5¨@„™×Þ îš“¾ t ÆáÓ‡qÌ“±FE’0‚‘AƒP15N¼= ÕéÔà}›lRÁÝiø{¯¾³Šl7‰Æ #Ñ<äÐþêfm¬cºJåÔ?Õ~>¯Åõ]3äMBÿˆ~ÿá—ÌqÄ£Sˆ¿¯–Y§8å¹MÁÄîÉŸ#-øgíŸó[;jŠ´QjªîU÷|ßÞ ¨²Hb”Ê…³\î|ÿ¯i»Ê~ŒŽ.%ãüŒÃž"ùëý¹oo¤ Ï]=ît©"rNK(÷#5[åóÛ‹§²vÅÀ“¯·ìwŸ(|#5%œC#¢ðä]\ƒ•µèÞºø%öGàþÝhî?Á…ß•“õý7ít-SÕ{p&#V‘º:jF@mI#1= M‹µ¥Íçgv¢]çê§þÌýÍ“ä.:û­ßf/ŘY±ºÖ§\x§œÀçƒ`(6ËöΆ³DöêÔiäy²Õç({õDq¥àSøÆö-À§Ó;7›DRã©j°P#5ªÍƒ`TXƒF°ìuý+öêø¿UÌð\J´ Üö%„mœêš˜%ëõ;=õrEÆQÜ/ò~Öwm¢©µê5™6bW¼Ïœ XÅBùA¸CH~ßö9¼5I“Öûç<Ð6ëØfìv«’Z×î܈¡AB-ÌáG*)*,ˆkŒ6C|xDÒ)øÏŒ>˜ B¢-@ qúÿÂÛ T9™”>|ª ¡Î xªû7P©çÉ€^£È¹¢ ÷°õ¥&Œ<µ¬:æ¥'RC‚ă–V˜`¸¨ì†m0=€0#1³Œ"ØéÌ,á>ö.Ÿù FÚ­gèóê}>ƒ'sۭˉý#5‡tSÕþ[Øêuê÷YºŒGæùYVççE`÷Ô-vJ××Z!‚D:§qǹ¯ëõÚ‡Fh[Ý*ôu¢GK¸…V^ÄŽ?ªÆ—,1{aá¥<ù/ÊöÑàØÅõu‚š»wQÕx52°0‘b²«°Û7f#5€R#5 ƒ2 I,«Ö»¸é/HÓ5(T¾-·I¼ý²#5ð¼~/#5Ô#1Çéï‡dïùÕÀ”žü`$Wì÷'ïwÁÏi”ÜAß-#$Guõ@–w†t‹áÃÄ#¾åнÅW=ñá½øT>†z½Ï%U™¸(ÔIWRÓ8€Á „!#5!¼NÂ߯ù®…”/u*™ß0麧%ÇÛT“Õ*RBÔ¬#5©©~¶çs¼fØ4E•;Ñ|ÛÞ§3ÏFŒLKÎõ¥à;½ÒÙ™³•0%Ïn÷Õb~Hê´Éǯmþ3)šÛlAÃÜ6šAå3Öµ!à~ É2Hê5KI‰ bi‰ó#-Úç#-Zcb(r#Ä@–”Ac‹«Ê?dyŽ¥âx6uà=Ô¡åàÀ#5 ½…û¶:%c²Žì”nä×-Û4W‘3´“Jæj3áõ»tËÿZ…í¾õDåep¢®‡ßÂ7Õ'Æwu¨5¶°¯6 ù@0Š 6Î:îùÓG“’÷«ß1U*‚³&~#5ŠH ¸áí+DÊû* O{$#5@¾ÚC‹ÁE„Ï‘ñpw²’™n§ep[lT"EÓ~éFQ`¹—mßVP¤.Dp—qC³n·Vou[p¬÷c#5蛫ÚbùôÇg8îˆç#-d$S­PÁIöRÄìzqÅ”iPËÁþ=Ú·üáéâ0Ãn)=\áTõ¾ :{ ‰@Š˜m¤N¥:ùå[‰ ‚¨@È”Œr‹Mµß$³ÙG Àh“ôaj "áѬ<%r›s+U¼çާZAóô< éñÎ<™Í°˜ñ÷Ú”ºSyã¼åö¼™ëáäôÈu´#5“âu?ÙüÿqØoB˜sѶͪæµc¤f/›Ò!ÙdôÔ"¹šku<üÁMF­ŽŸF×òí}žQ…Ëýúm¶ì|¥ÿ#1R2!Øbš-‚a7züŠ®\ùŸ§‡Ÿ Êr‚ˆ“—Ý(,@ch¢”Ÿ,ÑÌË:Ló­6ÇP/ªCNN#1•1š{M¤W)T[Ç£¼èI£îÇJ9ú´Û’YË~¥¢#W>¯:P´¯öÆŽí¨Ë#-AE/Ft„ÖðähªÎàA $ší”´_È>#]ufw˜5U+&Ú—¯Ò‡6V£ÀZV0­ÏO{¨ñÀ^ïµ'f–Jh˜†®ëN›o•Ý“1iƒH 0} Zë>^Œå±f#5(’òë#5¹Ã‘A*‡Žt –æÌŽÙ¼z /r¹Cb…†ý¦ékˆÀ„ÂCD;]`‹‹jcmÕòߨǃ_`¾f™•üVçLöÈñ‹öÁ‡†øpräÌœ•J„(ô`›€sØ­Ãl% àÂX0EÈ€ÞûD*âݯNIš€¼·"¢ÑxæãÊ GBæ/»0¹K5Â×ÜcÌ/aoŠ :J½e ÑPþë±âøa§ÆK,MÅ%%¢$%ý¯',/òmîR½\à Ü~°æ¸ïÅàí£)|î'²‚ÔCÓ4j˜dqTýݸÁ7ΓÍ.\0`ÀP#5¡ž ë×6Dp™zk.¿¨=ê6¶±†­œË“ó¹J¬jTa‚¤#1.A`³|\©GWª~¤ìÓ8Öy!}iu9ú“™$Rï6Ôž_QGɇ–¥‡|JçʸM¤:‡—iè¶Þ<{Üœ5Ô· w´6a³„°`ô>`Œ‰<ŠÃœkÐ¥ Z³­ÕßÞ2ž®Y±çodU1¹yôœ;ŒeZ=#5œÀňh¾ð?¾ãðêŸÑÏ õ_™±„Q½„ÏHô§ÞظÚ? ~¦5‰;·²ê}'X#€ÍãÀÊ,Û¡¬…(àÆ|6ââs*€‰ˆ::Br2CÕR0€µz__Ý+ï«ÒÔ¥üÈ‹“ü ä‡ûæþgψpýÃGô}ßm±èßµÉÅ7¤!ë)…6äw9èŸ×™µÏg¿ûñj›[Un?Ï#1H‡«×õ'9'Õ8E<ŒeË5‡g°BÚêd¨Ô@”4 f Xt@äBÌ=ÌL ƒØù.]T¥G¢€h/ #1(p²5A†êÛT.¡f ô‡-®¸½\²iDSqIc"*H%ÁÙr•ÔDæ{ì•.$¨&ƒ»…Þá0„1­(²lJ4Þ/t± ÙŒÄðH#ĉ`rRúk…ºÌ€PaDp{r„ @Î%_â!BÛlñÉô«ýÞ«<ÿŸ¤ý ãÙî¶DÖuIžÐî¥A,@Žþo1+»?óÞ±¿ëѰà|«eÁrŸãBþµ8Óû×E’Ã^PnØä›+qÎ"]ô°wW¤œÞt·ìžª(ÅB÷´ÆFcyM™!mw†÷G舲 úXøÛÏlæöÚ:ÄÒ»Ú)öº§´ÞoùÅ­³zYnñü¸h‚¨M†cn[ø%ï¶é\ú:8+{#5ûìwï"mâ‰n&`<‘$d»¢“4Qsn®w’Œ+}3™Í0ˆ›Ç×­Üš¤’Ý–ÛQÖ§}×o}®í'¯××¾#Œl‹÷IÂö"*>ûˆÿ ­Æ‹Më«(䣉¾&ͪª5Pm…nâ¾Ô¨Zh–½ÚGIvÉu£ü·#5(v'vÐÞ„ƒa…²b( mDN%B”>BK*´I»²­@‘ mo&±RŒìšl£¦Â‚6€;­Ðìôßfq÷*Bi»‰Ôrq¶)¹R¸ÉYIÒÆ¹×"5»aÊÝy9YœÒ'­9HBO@½.æ{âÂD,»„Ö²!Ñß þýïr¾;üJør·btké©B{_×ǵÀZŸ9Xò êdã>^WS¬“¢A`ªDŠH "Æ#-Èr:‹dì‚{ c\åãSnA‡G²YãÇE =äoMPE‹Üš4 z¡Ê¸5[b¨Í &M ™wC:!/7E#1ÉBœ–þ•¬ÔxØVylPDIâPÀŽÌØù^k~]¯\;öuWÓ^ýtÖæ¶êm¶5 hÙÝk{×âjÛ×ó¯Qb Ö¸#1¡’$(“ë½Aõåìò$í÷ÁvÞ¯»díê~¢#5ŒZBªµáÙˆ²”Ua‰6©ä£!Õݚ̔ƒ4o‹(¸" 5”:é×MNµß–;9£‚ÄCÍFP‚¬Ý ° !”œ;~\º¦úøñ¯Z9éa+ÃyÑ$©Ï—EÆ<˜³:Ï,|êé-(Yt¨6Ã@@ ?ÁË–y÷tíá x]‚pØÙƒÊ1‰$ ùŽò#1ö• ÞD'~¸d˜…9«;5ÊðÍObXr¨J€‹I)d–„D `ãJéVmÃ#8^„©"²(MÍPB0ÛÏ–wNé‘—-’ºòÅX,á†UHxÍìHœQ¾])3¤ ééÆY`3?G>j:Ç´¾Ø7å¾P~28DTÕW±+˜Oz#5#Í#5#5¯C,AŒŒ2¢¶¥Q¸DÀ$’‘P]#1Í çEuKßã|Eîqת©G#-«´z5+Ü­¶`ÝφÄíìBéÍÍoÔLLï î|fIÉÀÓ0mºP‡˜ÈQÅ´'ƒæ©¢WÓRJ!› Æygë ‹¼Bu'3vï8í4Ôm9–²5TJ&:1á}Á™Ç‡Þ–N˜³ow@¦nÍògK޶˜*q{š¤’a6yJdýÓÅÑ>#1/h/ò rÙ5ÁӟͺqÞê<6.¯‡ž{ºSS8T­ÖÍÉâo+ôywc'#-bQ- µR*ÍÖÍ·ž#5Nîµ/]…¬iÜùo#1rÖC¼ìÌwîóÖõJßpTY¾[ö0Û}"÷6]–æ}̪·iA&FÉh(¢#5<6òŸ!Åv©¯,ƒà˜Ø^&=Û8‘V^î\±Þ\»Žç;—qwâóòü]øj÷XÝfŠM,§×Ú¸¤¯˜b»«j¥ˆ·‹¦Äb Á„½(<§Ç8Æ“Á<ú(¤Î#ƒ<<#5R?œÞÀÐÂØOÑÅÍ¢ϳá©rÓƒHÇîß>þ14’;ÓP$Ñ”‚Ò6‚—‰Ë°á m‚ð÷Dž4jäÆù­v„õ@{–jÐcÀvFhùé¡c#Í3Ó?ŽsŽϧUÒæu Ps¶x|¼*n¬ª˜æ'Œ=Rk¥„¤H¸h£Aî|#ÂÒ[_¸w¿Rö¾…ÕÕêîÆi±è§[„:Rêèµtã%ÇäZöO ߢ WRÅ£žÊî;(÷ÇïŒßfPk©…sªç]VÐ^Ø9o|âûàmǾm7Í{¡3ò²kÜ{Âf893é#4øM&-¶Ü¡ñ`¶"³v]»ÈwÆoº…÷”@Œ¤E}-š) ªg›@UàÌ…KįÆ<"ÃwØÏ¤”.Dɇ[˜8¡b:»Q@p‡ŽÒÓ΋­ûà Ç$뉼Lѱc¨CDˆÄ4]3‘׃s€šTku¡øvPćžœÑ"Å— êN5ºÌ2ŒšàåR$#1Ä ’ ¾ð½¿sC”À¡¸X€ 'nÌ”… ¡ƒ{›²'ÃiàotsBÉO,ïøQ#5~Lj¬Þ¼æ¤ÛÚóÓSˆ™/¥Ã“ò¾†—l¹c@/…ÆÑÕ¾)›`åÍÌ ¶GWÛcKr(ÁŒ ®ÌCÆO½ÙûW”²?¬‹áÙFÔ„$G‰§H_Üÿ+­7lËHí²#5€v¿zª#ˈxKÞÈ#'ò®79oöý#1ó¢¡of®§2ƒv½£±i”šZÜ$¤ƒòš¥Œ:·%00¼ô Í„†óìw°5DwçÍ$ a—Nïïi™fla¿Š0€ò~gÄãqÜrœ¨HÎ+ÔA„2‹¶&øu<Ñ¥¸ÖNûC:Ù,VËpx‘#1ÄCY vÔ9´^ôîÈ5,í"~UÝv®ZFhš-&ʤÒi2–7u·¸Å½1µó_G^ùâ‡Nàî‘4q0"àñ#1£ò:Ç2nlYº²H“¤)#5€nv¨z0ž<=é•d_·‹¢´¤½&ÜzfžO»¾‹=sµ›OÁ#5#-9 #5ñ¡êZxTðY©*l÷Õ}ã®u7aXª¥:´0!ÀÄ+¾Š§@³’ãê~ŠÏ&üàõö>f‚É¢#-Æów:rã36Ã̹¹Ð¡A’<ƒd³ç9™Izì­¨­û!œ#1ÅÓÚßÞôc¡Püsmä‡Ç„õ½tÉ÷àÑ:§“="#‚¬†¯Q£h|uîêÇ~Ìf®Í„°ÿºüxFpA3íþ_Wñêìú(ð±ý—:‰„>p+"ƒÎ#콑U!Z¥î>—@î!×ùÿ~Á©¥ácùúpáOº ŒÏe—ÎE.ÛÔÎÓ+|ÜÖ¿ùÞ?×ß:£P´ý^6gk˜ÎºŽ¿Añì!t¯õ*Ò@Df;«/Ÿ}Ñ ×. >K#1Þx9³—øû%5pÕ%á㙎Þ&fvÍ~ÜáNz~O´lï"OôH;hË#1hà>ô?Çú>¾{'¿4G8Ч×òß:@NܲþîgS²#5-#1Vd0Ò7?ÉçÃËÀ²ý;œb³›³Ô•Èþ´œÒS!?´@¡‚ ,x!‹ ÿ*XÊ’•ôþkï1†~áŸß1’ÿoôpÉúM“û3pþ&?g¸!}~Fq”ã¥Rv¦M,d P„?:©L•D‹!‚ïÑVäžâgÜÔ0 mž4‡ˆX®‚(ê˜ ‡Ã¤~ÎÁ¬Sn¦šU%Fž«½ùhÕWKL‡Ú¼|WöÞóuâ;6rŠŒ­1ß×mÏۯ˿'ºwÏË<ò9C|Ò lãB싲±ÎãœV!‰#-&¢%ñhàºé@õ”\,Ô•¢C¸ÙIši/ÿ;žšxçƒÈ‘êò°XcÎ¯ÎØ%Êf#5£#1%…ÚÇ¥ñÒ˜zàjÈ#5vÕ1Š(vÊ>>›{ö|.´øÐÏaòµ¤/1·ì Mð"ýˆ8F€„XDˆÈ”¹ Ó—ÃÑÀ/þ¯³ÔÜVJcÎÃQP^¿³pó¾ÅÝd´«´Ï[‚1G@ìz¨|"yÃÕ2.^gv¿y—úáü±át2‰¶?Ç#1/R1ð¦ˆ22.qöèqáÏݧÆÜåUv––0Z"-$ btëØÂ H^%[éÙÒ-ø#1¶ùwÛÝü°d—„aÄ"kÌpDõ_§Ç±#-¯!P#1‰ÐyXuöjãèçèܯ·ãDõ~ÿÂ*¢]A8QÔqùúýÓü¾ÿÇ#-øŒšþØSJ#?±y{ië`Öè2•¿œiótíÔ—NLIòt‘ žG¶½CÓ‡×dû¥9޽à9úù´Ü›5'˜ ð{Î÷àË'²?Š©F>½¯ïÄ?jý% Éü_ûºŽþ;Ä>74W¿-=¶>bIåÅñóMmOˆ5LK@€#-TD04l<8û#-ƒ€·å #-yìÑhrkôÐ%÷õ5¨HbIÅ‚¥²€érK)HóC’)ÒP˜AVeCèEyDGŽ‹Æžˆb~no=]í•À¯>åb°Œ#-Sq±ïÂ*~%Ð×§’?Ó²©ÖÇž8UÄwŒªÿ#•0x¹Š“{z3ur÷™n+¥ÑoÄØ(ìË`ֲݢ ·Ž¥\_agí÷o¹”ÿµá2Ã¥²ø©Hœ>×/˜‘š™ýº¸û,íðö²o¬9¸œG¹7BÝ’°H®îº½ËFU„U¦¢É\Xcq[„׿e ÑEò¸ÙeÑ.’i}‚H#lk¶zœ³ýýôyÑ[Ë:ÙŽu,Æú¨l‘" ²¶ Ë¥ è¥wî»…^~Ó½wÓŠ ×ñÕ¨w[ŸÄ‚Â/­7»ÈwyÛ:3¹>ÉŸh3×;h¬äçÚ(¾ËÚ#5Šáó_j9¢ýTâ\\zÅ]uNJμ®&Mîä¶s>`û•c^<æSMHt)â#Ò}Ç•]îµZþ9bÃ(;õ»•ÒŒ–½Ã½Y/Õ·«S®å*ªèöW]ò7ª‡j*@WÄT³•:40ŸªZ¹öL ƒEo#q`0Ãý”ñ\ ›½ÇuJìV€¿Õ.؇ª ÆEŽ•`p˜¯&¯M(Q²}%颊I?EÓŠ@s8!PÎJO§Ö¢›j‹—IÑH‡L¿IƽØbTýçJkã¨ï—ÍÇ騗noN¿”ÑÅ=yG²ÿ§½wË™´CœD‰C¥ú‹"×zu95ªB÷hß5¤1›Lš"ÀËÊ h,(ÁÍ& ª|í3Öçì¤ UN_D³ôßëê¡Ò_ÎÍ5nÛùñÁ‡tn¢*g“Rfåög;>0ÞxõÜE>’ÊØ£²1‹·µïXq.xâ·ãlyN‹c­:Ã…¿¦«­œ-’ñ­ ËüÅ6ÑñŠœywNè·ìð"ð³v3äD\'Ò9ó‹ÞÑuü·‘þ8kO¬gãIçqê/áês¾[‹3G¾?Þ›ßgPFÍ<óDw [˜ûf~ÏÑåBæ#1¥óNŽ(¨úº3¯„{=érÎøm›>›ëµù|4áq#5‚p‡Ÿ}wwTÐx2JJ`eØ~oT2'uKi7pÙ?£öjwÂ>|þ¶«>CBáõ™óý¼½ tdNy–ß”òjo™s-3³çÏ/„#1±äà#5±´r@ü(ú{úhè[ivý½à.ÜÙÇ5÷mC& ø_àù­?ÉTýP‚Î,Nú0-º˜viRµùûDµ ÄÃý´B'•ÂþŸÀZîë‘U#-b®RÄ]Fg¾î8C÷Ïš/œYáËÀƒãõr[3šMÄ%s¾Ó=T!¿7s«··‰Âe>²4z•¹fqˆT×!ëˆØ@•“dTZµܪÙ íD¡ÈÊña†x¡)'«5´fŒ’ôM¯¹ÇÏ|NW•ôƒÍlF¥Ý’aÁØ‹œUÎ‰Ú †‹;Œû¬Ï¿õÊÓ0~ªNŸzýú¿zÄw_ÔTS+F+iWªt*ë #1E5…€d@ÑÒÀ' ˜‡wŸÚƉ'Ãæ]¿U9(dŒ¨AöÏÃë«çÆÏ˜Èw›Ô¦­˜#59¡²;ÐhñD0jPE<ÑñÖ¦^J4b’{P«(¦`ŠBÚM¾],ýá®"òøòöÃèŽÛk_#5>g}Ô4xy‘a`pqæûv®å"<Ýâlª(\iI-‚Êç+2…"…êLDkã@jDDo‰ÚWS¯¡ Y¬aT•#5 ý7TÄq/#-#5‚.#•«(!”:ª‹ÁìˆÞ…ôvnáå'ªéÝoÙÖAík¯$ m}Ü|4X#1ï”}ä)÷\,Y‘Ö%õ·„ÿž~é“-·¥áçcãS1åWBš‰%Ê36%²€üºþ?q@}:÷±®ë©Ìú2B†ÿ¨þcýfùÑ/ý§ Àxô¨$\üƒöðáãò†M#-¯µ1R04«È)ôŽ@:õ2ŠÿÚoú#-O7™?ÕÊš»D:#ãm¾¥áÃí‚Zô‰¿7ŽÉåÒ6u«ŸwÙëù>ž Ö#5I]"÷1NŽÁ]«PñÅöO«Ÿ8-3”Jrî”ÕýAR$#5”Kýãȃ·Þ‡ù¿ð ŽŽ >ÇïÅQöoPq×– ´l‰ø;ErùÇ(úb5I ©{"#Dþ€H'×gtxkÏŒTa¤¹{¸°,øûLЉâ^ö…ÞÊâ 8j`‹èq_šn%… (¢û]ôûž¬ï‚õ«êÜ$? hÙn¡á]Ý„†z{OÄ`9ÊŒH¶‹“½î{]9q­§Ê0à#-ùI2€sDnù¯ŒéæÀ¸`9àÁ÷]«j‘‰\Œ ŠÔ4€xNWfªHá¿h#Ø%}÷·Rñ~X¸§ƒÈÈ4ª±x(.Ü¡œ¨R3$´«—€Rƒœ™ŒÇX¶¹BD‘ÎD„ÕY[”«@ÑAïý=¶`ÛO=޾óDpA` #-X{rØ•÷s8— #-ª)d³€‰è#gEá*‰¥ü7¸K?Ê3(ñ Ò¡J ºË#-ÊËMVÛ–4Þ«ìS#-‹å?›ŸuüwpÕ‡J¤dƒÆ‰ “£Œ¬FL˜ö“&ÅæIadé5”ØX"èÏÎÎeÕ.Äÿ*rg,dâotñ&¿-#5SmLzP>:Q8÷;¶ã!·õrº­|7yEeÿ ¥|+¥ª#5ÌÌÕJXG‚éã5W5×Ü÷g½,–ÊQ­zMcR*az¢dm-;.%tk€‰I ¢†…®ðݺ5ôý\YA¡61[R¡ßvÝ· 4²·Íy);Ÿ&a”ì8qœuW Wh漨ˆ¥DWSìñ#¦ƒ­‹ž½íf»òZÊMþ(Á5*`ã{äÕ¼ê~É©«“·M­Š‹â¸‘U ADì”õkbA”ÏŽb6Áè¨=EÜ¡AðœûšJ´ÃÔ<6ÿ¥Üœ±ü°ý­£’ÜÑûRw³ï]/Á¹„(rºÝ4»ÒE±˜\e;ˆÀÄUÒEã¶Ö¶Ã¤ÈJB¤/çåÇnÛçMç šy¨Ô#5Âå娫GrŸÅãüÚñòô<‘Ⱦ­ˆü#,¹´³¢²äõ÷3Âù„sÔýPÜ]£@R\ùâãæ! ΨÓÉ£lá ­¹Úñ‘­üyKÞÄ0ÐR‚#h+zf¬x­•UöÜ{-–KsÇ•w´·\'Íq“x³²Ñ¸Û™ [ŽŒVg=ø"7PL€å@bâÏ4D¾#5‹+äÐЄ$ÒÓ~¨Ž?¾£+qõ#1Ã18D_$k‡Lt4õÃ7WP–*%±³–’°ÄtE Ñ2žZ¤Ôz-.“9Wkn-7=KèþžEçn/Gr³W4ûy­ÖØC—Óˆ?™¿F·c©#-p(J5r¢´Tâ¹½Ìî—{`áë‘”¦)X*áíãÓ]3­¹ˆ·HÝæòôRJÓ²m^Wëï”#'§§U'7œ%]:;~'Ù'\ǽq=øƒrIš».¨kåƒ,(¹ØÂI¹qºÙP§IpŨ_ mRCÆðH˜ÁÚ-6¡ïÃÁ˳‹±nY¿fûVD«f绦Ù7híÐ4.½W8y#5ÏÞÄnˆø*:õ#1Úë£Í·ÀºãÔ9ò5·4tä&Hæ"}±1áLR©” swÑÀd&]»Ù³»È±à9Ÿ¢ˆCxx£é™…leXp,ÊÀdC™½ü~²ß—ÄÒ¨öÛì,›R¨2ÊjÖP)#1¥y°`YCq¥‹Œ˜ò~ OÅ¡ò™ðÿkÝrüäÉèœÒ~>™ô„é#䊎w”YÕÆÅãÄ=¿9·Ñ­;`M¥Ë’¦zG’…Óneþººšƒ)ч•#5‘ÎÓtèMöæ>³6yï©ôc“»Ã'PëæBb×I½MEæíž&ýÃ]zO 43΋ #™)3H³í’)¾«pŒý­ÐÝiŸôv=‰m¿©eSp–ÞóÓ7x¹o^\†¦vòéá yaJâµ4‚Åç¥óû¾aIlx®·#1‚ÐGšávcL‚Éwµ=4¸zwSZÂd¶"¾[nD{¾OFž¬¯¥2ÊÃuˆó>ŠU9ëdC‹ÇôLeɧgÄ(ñÝÌ(ôØœy)1èU Jª´›ð€%0¢^ô½'é¯j{ü¶WT’¬/¬ /%±ß 5¾p0ר¤l€agŸÙßá`ðs`5#-ãóþhÃÃcíÔÉ(³.vüšÐH$’y\, ¦úZ;bž>žý›|Âûw3´Û³ª:A½TrúœÎÕùmÈ\å¸m"n†x\ñÝåëîGÐá`EÄ#*†°U5ˆô¶[ò—`źÀaƒ¯¸‚J#-9öLó rdŒöÌ%½v÷—!ëµ°z&ɉÂÈẵÜyu·²àQÃ6€4½­ER®õïiü72Ðw^ÛC´ŒÛÓ–äuv²uø£¬ôZ¢BÔ^MørgÿµG„0üÜ8J)ÝÝD—…Ôç´ª¤M)nS¿¯œþ:¼žSаÙ扞î?­vÇ¥ßÖ¨_[WévPèàè Ò¢:4fì1Äblvûm“´é#–ÕÂBËzð»Qœ×vTö‚ ^%š0um#-cûÔnŽðƒ…¤ö ºbV"£g“ÆbbY#1²”:,²ò#=RÐá®Õ‰ˆ‚7`µúa¸Ê#ÞÚRwç¤îi,F9ä"æ³(•ËÞÞɱ哵x4mÙˆ‰sÛi9”áÕ΢š‰LL›Ðêf7j;µA=ãçÖ«ŽSÖ´«BÄÏCÞŠ·ôi–¨‡mšñ…€íÏ ßÒ¾;s^­)¸‡Ï *–½†KÙ† _£±Úpé] [\’+láYŽÈ‹ãwuCUõ«’L‚Ç®³ùû!¸:Óh‹Üaçì8+í1 Å‘[úWæwN‚{»…èC«;SÀ„©ÆCN øA­u:r…ƒ'Ø1•¶8]h±zçÇë(¢Ý‹a'9úJ«`މ„¹jÖª >P4ê¨á†\GKAÔ!oCíl&ñ$áõ‰o‘££ ËM·~6ÅIPf%¥½ºh9C(ãÑÊJ|5äpJ桊µäJÙt»Ÿ#-óµR&ÃmÖõ#-d Ä5ÐÔû¶-{«(‹#1bŠ{1Û¥íbŽ&Ú"#â9KÁr_Îr{¡w3ÙÈ`@•èÂT†ÙFOÜÍ ×Óƒ™E·¡©ãŽøüv.Æ„>¸ÔJ ÙAÒ[_»ÎÖ ”dÅS&}èì™–!Ðç#5Þ_T¹4D.wb2¾e%k€Ûš¢t<æÐ#1cžÚá¤thÞÙY˜´ŠØ@©#-·z¯8ö`£o‚i¶ÛöÆÙ¥kNvŒyÃ…ÛïN`óìí&²8y€‰I(­ñîBñ#5©üSqp´EG(t¸ëI쫘 !dýÜ­öÁ޵Z Ž$YLÀÃ:MO!²/›b/«†-’gb­!{¥¶HAráDf,LH[#5 ñ#5/¾·8*ˆŸåœë€az ï(&5ˆÃ–“ƒíÇ ­½&Ç‚6ñ®"¦RO_…¯µõM£‚ŒxcpÃZ0vlpv u€À‹CMÁ¤Zb€c° ¶"P¨Œ‚ê‹¥º p¢öòyQx{}eý¾ ΤVÅÈiò Drñ Èó½x…)#Ãd¸k#-½ˆpás¯’Œb¢Ú#-ñÔs‡>—PÏÏÏØü'‡…—‹d#1A™öDO’wl¨¬å5ú?Á¦Øÿ–ÿÒ·/£ø¥ŸØ‡õìE +`ˆ/KÔ3’UÿÔˆd‘S'üMUB§÷?ÈK©eDæÕ\ßM(K v À#HØ”XXÜ5N(,E€ÕPa…ÔÛ¤b6¹ŸÐ[Ë>îÚ4O«‰‡v…†ßì›ûáþÊGë ÜÀìS1Àe¿°X__yÀTÔ3CiÚ—!:$q9s½\Í©ËëñF95Þyj JVLøwÂcòK±+Å,ûd,Ô¤ÁÊ¢6a× ËÐ3°ÝU6—‘]@h÷£¼w§h°)éîÀê>?þ~¥öýæJ#ƒXd{EÛ¨÷E?Õ ŠÇÄ< ]Éò'hš¥–Í…î\þ[¿ç æ›#5ÀfžÆÝ#1Àà§×#-äl(j$ ŸQ×û›[™§¤rô€—b&H‚èx÷Qe6§¯·¯R ~¸÷¿ðU´ƒv3‹òå­f Y«! _P]À/ƒÞSý«í]âô¥²Èøm⧨ $þ[ÄFô‘¸c#-+#-ÖPÕŒï#˜#œ?_ÁÿŸáóûí¸œ¼ìÛYf¨öT¡Q±QêËO¨ƒ}sÑ¢{CaC¦›3ºðËLlµkÄ÷„[§ã;ûA Òì…¶b|à8½#Èü†ó â%O›þŽ<ÆýÇÜam`¿Î*‚ãCÄ {çç#1Ý_Ä\®›&ýTZ4 ³¥ÊrÆîÂVÚFáÐX¨Ÿ‹a¨9Ø5…p=ȧ|äQå{œÂÔV(Í &ëИu’“ü¯Q@íÙS¿”žRÇqOÑÇ^TÕm³h’äžßŸp|{ûûQæzw`s^']x3Á6Ew-€Óðõ’¥l7Žíé.™œÀáñ=§¡8åŸ§ÄøœñÆzì-‘Pn› aŒ]ñ‘$Pak,:yÉÚ\sÉiJ0!díâ[6HNOPu`àG©-x\Ç)Ù®ÅÂm +“ªaƒƒ‹b%¡W¨ñë'éƒ$”ÞýuîÅ`~oÊcêÿ7·×"™CäR5a/‡qx`Ó»'C™”<îVÅ¥Ïi‰*õIñO™Ç£±è!üî³ø‚ÃkYm\W«òyÑÊsÉàTBU0‰PË×sLu›œ²;Ê.àx3?Ðà7šè\²'*þ‡¦XõƬ—i{XH„"»ƒŒØ’Õ{Þÿà`r ZqÒ MšÁž3œ\3¤²ZÝSºI$±—{bVÅÄ9kô÷qâçë9›‘ø&Þ8ôÃ>/U3âg€zLÍÆ)z‡?‚ùj©ª&@ú¹Ï´ÕvAâñöíÚÌ;ÒͲq™%‹µE6ËÙ¶Û~~ÐúK=çâ_ þøIÚn‚ wñÿGNí^Áì¼î,JX“ø% .¨)eTú¦ëüÓRÜ;¢+ÝQ‚ÿ‘ù‘÷g;§Û¼m E­Èrcû|b”'—Ú;Âo÷‘+`: ï:ŽªÔSÃþçüŸàþ!±ÇʯÚz\[B™ÔÔë˜e$µ›>cŒjA䇃º<ñ´wEù·#-ÚAØÐ?4ÛýùcE\çÕêmXÑ¥`‘*™GsYeÎsC˜Ç>¡Qik*¼`ƒ@ ÄæCs`Õúø‚îÞ´S#ˆæñ(ùz4Јh ©®bl}"~‡ûzÝö‡ÝÙàA ÑI%2 oìSq½>þY¡v(ö \Îhvw _…CI-F6 V#5„+ƒÚI%Ñå¹Ð´ $ÉUÃ#1Bäü’Ï‘pÉd6™Îê±I>%mš½uøï%Ù²z%°0ö ^ uƒ3@u3#5ap¶Ñ’8aÐ Yœ~=ÿ®Õ´Ëz.nÁãÖI¥ÂºŸ"¾€ÐÌcÁ Ëî‹|Ê ,)¨…"k­Ïù\WìÛky†®»MÐçâ£Ð.YI”<·|”iïl£«ñí3*é‘“£ _!£2êdÄÐ8#5°™(†#-u(ý3ûþ_¬ðó‡õ`¿vEü,ACü·i„žÂHžS/]`6Wæûq…†B™û­ZXPã *Ì.¡bUJ‘DPY»]8é¦.«bÿ›ò\Ä>Y!+oDX霺26³F³OZÔ*dƒd:@ÇÕãÓ‹Æ`o 7˜ãe™\¥P.uä’73Uk˜©—Zšv¶Úy“O&œ¶ÀÖVôÊÌ #Ì‘—PÇ^°…âzµæ´X††µ³E4Ì{Ã0ÃòY:Œ4cP—/bS9\Òöõ÷¿Oê­Ù€•‘BEœû¿¿ôê¡py“LßⲇYEO~Aazã‚\8ôqÒ>~véuˆNÑ¡ÞTøZ½YµâàÌÒ@ó‚­±+¡j’MÀ‡ÛcC¤F3×—y’’ýþ-Õ¼QRüÞ_üøþ`yøÙçÌ;D9‡àC¯ÊЖÈ`pçRɬ: Šª,P%ûcãË\K¯Ì{¯ÛýÒ˳2ß0‰ˆìD-5#1¤&S¨¶DèUw«,Ö D@9#5[()6œ2NŸùmƱÏ'Aòk¶îëm¶º•½I]r(ÄÁÊk;tL¯|̼J'Ãé¨t5û‘ôÛÔÇSçýÃ,ŒlÎþö„?·îN~…Ó……·îcÐIp=‹òO»nG÷¸~›ðÛ »àÀU¾d=ØäØíøM%)H¢)¾M¤4!Ljxß|8`z€†ø€»6%• Å#-Á‰JÒ–t¡Èú~£vwy¶äÚ#-Ý£Ú‚öÂu3'ä:Õì| Rªy! ¢†©q$=`b–Z·T¤…ÂÃìý…i¹%‘À@œ<}!l×ô ܬ‚¸‡õÜ[ä­ê’)€_º#5âÃÏo#-ñû(r"9„#-@Ÿ0ü~i,øÞy \‹xîÞÕúÑuà‚ÓòXÂ¥A'Âß\Zõ„·­ìDK>šPZ ¨—ÚmDôXè`ûxf ¿˜OLOÜíqôIïGqÐH›¬l€„:|4¸dPˆòz$~¶³#Žßx}Ðy¹ð‡üP‹sóê~³´>†ê ú)w±ýý#-{ž{÷ù'¼ä!ÖžBÇż€4#Wˆß?#-D™L’ƒø¢É€ z¸uö×—ìx¤äïh¶=>ÃN{‚¨gˆ‘éç_›HC03`bÁBA ¤Jz¼Q~LÕöx}½ ~9{Ë!_Pÿ-?$†ØÈ3óæ'Ñ>…à}Užni¤lkƒ(P~Œ®÷å©Ùñ_¾•òû!r[K'&êj'©LÔ 1CшÉ$“Ø­ÁSÍ}o¤ø´½«#5™Ä€Ø@HèòýhþtŠ}‚§d‰Zšõu2=ðç¼åöÊ;}ADè¼ë:tÒApEµˆxL¯ë‰œ#1ï`‚g0o—PÁ@ȯ[Ú{<Ðó3«'´îWo#1ó ÷#-óUï78=ÏéKê8 ŦefRbK "~ÑÂ6Òá)Š#5(£Û½d©ý@~¼ž’'Ø4U³­þæ“êð#5£?0Þùûû›‡ª¥Cô¹¾#1³ú‘6}bЫKLÌoLÕÑ¢È[Âd`ãÀÞ¥¥’U’I7< ~Íòn†gNÍjC@ÃKX¦$ Ê–‡ŒmÓ”’ŸiÙôr¢ù ¹öQÓ&”G<²rC>ÏÚ½Q! !·j#1T„?s’kˆ÷!C‰ë¿×îÕ²¤ÛzŒÞvïÜ3O#Õà0• øwyòa@3ô§ *"íIùáò~¸fösùw¨ôþØHñ–SÄ,„a]C±úw£Ù®ã# èÀ€I0d‰,íg¢H –Ð%U%N½t±L‹P23Ü©ôÄÿAþ#î¿Ü!6¼f‡éAõcòÉ •êÈhG÷c·îûL³˜b~’ÿ_¼öfqÓ¦u„ÍQTAÉJ«EQêÃ…Jb™!ý§*>Ûõl¨2/îWæOjv¨3{SyI˜ª@Á9±…£Ë#-õ§ 1œIÃÔ$IŒD—1PL@Â?¥_Ò‘ÌÚãNºÜõýA^ï^–ËŸÔ,%¦ÕZE),ŠAÂE‰ùÁÞ– &fø@ \zŽ¡ ÓÍ#-ÐÑ÷‹÷`6O²N{ûͼèD¼3ܯp~@ÈP€@ „;~ŸºÉpHª˜LôWSÓÚsÉN°ÉönôlÕ;°;C‹×tç$9ÚIPT¢ô*´»dìyج©<0s¾CÀb*9Î eî°öc©¬%*HšÔ*åC±!™Ã#1æ°(² Ç\ö¢W¤Ä0¯¬šÜ­Eà™ï×þ픚ºvÓ@›lš1黸UÔX ¨r©L#5¨ývlŽ*Ö1ÑD+Œ%6Ú–'­(l Û/§pƒˆ ÛÜ.dżå÷þÁóøÇàÁÝ1çñùbâܸùî*Ïw¹$UÞnçn¤ù¶r'f#5TK©Ѫ9™šõpšÍ©vÌPÛÞ[.#5|9ÇhCÈ8ž•Ñá¶ûyÚhŽÌÂ/P9ªPýDØ¡â#1Æ•††(âA¶ #h"–H‰@Á9œ/]Vä|¡=¡èz(wøs'Zzƒ» â]ÈÈJ¬Êª%¤[9‡‡g#—)Äéݸë6»ÊYBÓ™¯&@ÝËÛêô|»‹NŸoé@h[¦Àú̲ÛïÿY µ¦[©<±Àý<¯âÕ` =>¢b¢àBûäg?ÁÚ4¿FÓè#'âÕÊŸ–¥Cßó¢ó–Ö Æ´_èI®L~|çÝ)Ûð?s3½M™ƒÏþMþ˜6vuÂ[ÿx 9c9ÍøèDÙ]³¿öN·çó½ïMf<ƒr]ºƒ8!Ï-QÏ7SdSXàÉÒ,„AQ#1 “ 6[š†ÆÅ€¨üÃ|Ð<½¢ä…„‡ ×’#1!óÃhŽ#1…°‘芩gq±˜pû)ü#kxÔ½Ð2êeÃíüÞ¢ÇðŠÈfò€¡"Æ#"?·x‹à€\{ÓrtN§ŸzeáC˜Á2;Ä‚#1ÏAØjrwõmÑ ¨;þû›ŽcIé[ue®c LïiU'Zñ:¿E]È—UL’ø`Óó©  xî+`w!Ý`#1Ì^t†HP–rLÕË&Ṏý&Åì¬9ÌÄ'jQ¢”\-#1¾~P{_}ŸÛvøòRñd#-*׃D‰6÷Òî»yÛ¨x¯·Y!þEðfÌD[ÀØËnáòñùlÙYä!(Ì@Ãm–ta8îk#†µ‹Ár”Z­’ZÚ­FIúÃÙé#1Íâ}Œ }ñ¢9¤çåKŒÄÎÆ)ópH41BIsˇ ¦*‹ÐËîûúOÁ×HÝÝBôÏ<¼"2‹~ÚÛÖÜb‡«ÙåêT=ºíaõÞ‡Ñ4ýv€\Ì}1dÄCæ—X½;ç¬úC>'iÁØ?Âj¾þݾߙ’B‘±>Ïk嬃>ôJU´BŒžÃ܇X75]ÁhDüå#1\4`‹sŽü•ÞšoâØ‚ÃÙ] ¡I­îò8 y¿pzU?¯gQp èè]¾ãòâ¥ý„tþnä,Ñ¿éýVÏ÷f–"L°*]e®jMç:é™lžRç›n¨FÞ^]å.kØ^6¿zW³%©@‰šn°š“ÈèKÚOVÕšÐ`UhQX'B¦ai*f)\a.COûRmOíú™£PleúLÏõ‚l©#5kf:ñ£óêØŒÅÓ±QV'œ€Æ±å‚é{µ„|f ¯û*Ö–ùƒ|˜«ñ ]bOcV‹lwrJÜ€#¶j‡žÐÐãzô¹6b#Š=Pü>Äï—ñùêÚòÞDç½Ì¼¢Íåm^ž«þtü5oð6Ç©{XfÎ(Á ‹„Ÿ zT12É'C§dåR°_ÜÓ?s+›m¿·©á7=½öüîÊ£—LJo<̰‡î?‚ Ð¤µd¶‘fœªf»{¶¡õË:øS¥í2ßû ?eÒŒp&B·˜ (³CÀÑŒ§u`ŽñÆn’¿ƒ€Br*‹sòsp[q#5·ã¯wëâ£Ü±—#17d.˜ªí2‰Gqú^ÏæÇèèø°ÒEGƢNJã8jk"ɸ:ñC¤²2“û:º03ñ>¢î?E²¬nº}‡ÎŸÆƒ@z@(¡”/¡QdyüI×·X¬&SfJAX)ú¸&q‰#ªù~Ÿ˜@ö,[>WCÝ!§™C!([zw¡Txõû“7|ÿ©ûzn»3¨¾ü‡8Ü,~<ÙÏN®ŒÏ«¦¸ Ž…á¸?“ôR‡™/«è,Ÿ:_{ìq5ôš56‰‘&]Ó{ï·¡6ɾ~oD2&!#5‡#1›Ïïçñ“Ð\{9›q¿È>7Ë@`Ãôšc7XÊ÷»|'ù0Ðlÿ.sËS y¶+³«ü¡Ûj¾ÐSÞ&&ª› n>ryðjleÁÆ#PiÙ·ÐdÔ)¼‹Á´m˜‡3Ï…,œ¨³ r{™Ìpª&Á˜m€sȱ þ²‡Œ¯1âpg\ÿa^1#’/QdCüutyå ÃÜ3ˆî@Ïék·¨˜ªØP$îá°…âðDçG½#5@<0O`#|¨Þÿ%<$þ‡zŽ– ˜èû£BÿõÿfÇ"Û*£ŽŸJ¢#-ä ÊõD÷¸*#-~ŸCkólF@‘ýûÕB‡bᤌÈ*£ ¡±fv5§ƒ‡9± ÐQj2I¸v3”Ú&>Hx¥0fr@™Z‰NSeÿÏ'0äãÒ‘ÅîÖEM¯eK¯l#Ö üû=ù#1Õ]L ´«¶(]I÷h 0Žœdò>ÓUX'Üð˜_ oýÓv#5öqxÿw.,Œ÷¬<ð•pp½QG>é7õ‚ôéáÇÞôßKMrˆ³^[®p…rëqD#5¿&#•9EBƒ•zn±r9þp1”s>Rà?'P{lzvü­jˆ{ §óÕS±ø#ßcY¨ÚuDå.蟷JJ/üÝs~º'ƒðæóúîç:áéÖ¡Qž³Faêé{Ç[vKüøèGŽuÎík¿X"]Ç¿D„›pC%d"Z†-aDMz”H‹Ž~{ï˜ü±¯ß„«zÝ­x²NÄ)‡ãP9!:§søëÞñÇF¼ÞšÃuñÓ8èE¼J;­çcÛj1ÕlÞJ|?áÕAûïðÿuvB·w*;ôòÃdEÂ_aé¸Þo }\ÖÚŒFönóѶtõêcÎÞË•+2¤_Æ=GÙ€ñ½¨Ï^•¡‡#éæXgÞàÝÁ¢Özó²5zC†€î‡ùžçm"‰ iJ#-°³ŸÇìf?bBr€×ëÚ¢rë­sãlˆ)Q;ƒrPb±DÅ.Þ!íãÇ­o‚q+#1»çœIñš–æ÷ú8è•Ȧ©Ô³ï³ Û$P&õ¾_ß#11¹‰;ÚnÌT2fC — ºȽ¨¹–áÙEœãLv¯ïýîÚÝ-šÓS!/ôá¦D“2A­W;E5È^Y¯’0#{ºec$º:„b^½spN^I CdÖÉJd;­.¦ ª:#‡QÙrú#5"*C—ç±Sà{Ÿ#-žç1àYE‚{O @DP?MªC÷öÑýž#5~ÑôÝ—ò>Ctí<øÝC_Jb›¦üóBÞšª«öŸËœ„ ¦bËbp28fä :ìÛœ H<‡†ç °‘䆻†nFÅNíüAF0ýnG]…ˆÙ¤$¥HÅŒlŸå¥ÞzÍ\βš£¬á“MlU·ƒ·:3i.Mµÿ3«u®ê¼½ò„Ä<µ˜¥P‰ªœY#­uòàî$ÃCc#1 1¬0FÄÐŒF"@\)F Í-‡Öpú¼‘0v+¾›°ƒ¬ âf"°%–Si}ý8޲æÅãÁÁòÚï€!²>ÿ¿2ê²°‹Hjœïm§1xêK5Äþ§¥7Á<§r±`#Í·É¿UÿI!N¬»8µ“±r€ý~΋ß=£Î|eòjˆÔ7üÑúÙ訳F›;7­§¡ˆ‹ü¢y²OLJT'*ª†ŠjÉS9 ØÂ6Ò.­1¶Lµ%R &Š#1¨pÉŒºÖ|ÒIš ×ë*HU!ûÙj#! þBÉhÆ)aþOô·‚ V1!dÉÈ¥ŽÉtlˆa?ºÁ»A±¸@þ„H:$É÷ÿ’t‡Ø… I>p<‡ÉDE«»nÎôCöyÈ2%÷zçì8B$ËcA™ žôp#-r!à 8UWMÛ±cò†ùÄ'N@å™yQXˆhz½\û3Ê?Ë<{‹>°ð¤—5¥£2GùÈopdfÐ#1‰ •¨–ÊÇ=ËߊÛV =ÜRô.Ÿ1œßöJ;/U÷¦á{ü9yb”#GµÊQaµv,qŠIPll’t2VŽF€C&ÉÃåbÎIûM°rx¹¥§j‡1©ˆ»T¥B±gxÜ7l¡•Is”\Ä ™1¢8bÝÑýâ¥ýØlMà1ˆ1åã§u·{ËÅàCžžå—Å À ÖJ#5JÇ&ÀvîÛv ŸXk¢„µ¢wñaEyJïÑpbàGcD‰AÛ¥¤È<ûªiîÛ¹4Ëx°? &ôöòǪô°ªµùáSyÊÐSqÖLˆ`Ãî,†¦¶Uäêl#Ô—;LÊJ#1+Õì¡dàM|¯·®÷6x0¬^r)Ãg[Äb.;aE7Fóo2#1XPtfÁ¯L{ÔC¼ýù·®·…Óv;@îŠR´™£Ünµ¼ëxÞT§Rè‹måÞ-nh²Hc:Ù„ßœÑØâqâÄms3X…›g#/8#1¶I‰»†„.ÍR­:…¼?#1ݲuŒ °AÆòd8‚ÛÙowÈÌòócÉì#1³¯rše'z,FEÊ<¬Ô.1ÖÐcß'vÄ€9©¸6¸ÀÈ\§iØyTït80°š„LÙØ8l¨}ÏTÑ·ÇáíÔÏ¿pr,Êgˆ6{Œ’çËF··$4ê›uòìî×ÙîßÝÓR¨¥Ó•…ÑD©Gq”Aò-,7Ë<Ýtmé‰1Iö2ÌǽÌͽ¢Š‚òñž3°>¹»‡¸9ѯ#-´A¥Ñ÷™ öÕ4,| * Ö-jo¼Ùb µØ=h= XoA#1!rWÖp`p'ö@å)D’+„ÁÅ]=Ö 9 påHk´/:!6[ ãVÚql–m¸F¡Ñ…t8æû(øCB„àì¢^»¢hmwÆ>—§ ,Iª›MMÏT€ÒG´ì‹—¢µŽý;—3c ²sIݨª‚Xjgi“‰¬Ã>*”hwo0çtéâÎM'¦~Æõñ}›röfp°’}½Þι##¿ä°y±CšöaCC¨Ì`ì,Î[‰À·M©™pÜÀÒÛ :!rÉ×#5!ïšZ[}áÀzu€Ž¥§Ÿ9#-9<2„óÙü5Qr.;ó|2R‰¼ãÇ2o(ïn›Dà—JmeA/@uCõ«¸ê_|æi4E©bOaJœÊ w{ÍœK”žå_®SfE;º8#1K;γ\¶gͬ\æÊФ 0E’‡(¿jÄ,G2Ü‘<ÓQçm„å©12^ºÙQÅ$”##6 ëì#zC@9ªsÆ!#1Žð)$H°UCæ’æ«v;¸ÀµS$`Ô#5ƒÈÕ³Q6Ò]™yÕSa˳R+q õn©‘߃‘ÈÌàrÄæò$YI#5FŒ"ÄAÑX¡9}wgG½u÷^Þÿ^8´°t9s#-ƒ`®þ¼Ô̈‡hS3¦s&\ÌÌËf5•fY™$«³<—‡Rþ­™î¼é qa®¸ÇQ—/“T5Å’ ;j”Y/ ±–UÐÌL¾=tí¬ÝB›9cÛ#-!zˆ ž ~¬·qn{¾ã|òõ—kÏ=2uÌ¥•EN<¨•#ÒëÏZÍsÒZYpº.†Ñ9¦gGšuÑ=âkÛ&cʨd;yjÊ¡0™lá9•·ç¥k›ƒv➊$#1üŒÎ{œùÂK£¼+=1"&`ø¯ÓÚ).ã#5U$º¥–-A‹¹g=&î§ÃÂôC:íTé‘«<Ü­HÔ¢Â#Pbâ,Ìé%±/§4ŒkfsgưuZY^¨í#1#-Ä4B2Áuuàôõ†²iÉ!Øf#”ÑJ#5@»WK‚óÒv%ÿž#5ži“¬å²**ÑÇ©Z‰Ïi$8ÓCTH¯œÊÏ—sǽÿé¾#1 mUé°´”ÃqÂ8/¬W™#4Ó`FHô´ˆðª¯b#-E#-(8ßÎ\6ÆJ«UŒo·U¢z.#5Û&àÒÔ°/`Çšxã`Ýg'@ÞeÜH ]W@NÓ׳‰fíZØ›Z–ȱ T%&@ì6bÔª¨žš`D@=óF%O2‘#5=ÇКiEzI#1p&Lɓƪé!’dœa%ÇDV=˜nì0ŽÁ½÷Í a…4ñSŸì‹óhð§§œ–°¦Û’˜Óañà6MÂe i4$Ì(ËD$1X_qDŽÊ¤Êê‡;4Æf:ÀìîFç.¦#Ä-ÐÚDZaò#18Â^ä»Ï$5|zê”G#17ôÉ.dKÃéÑTÒ´#1æ–©ÛÆ…'Ô©ß í%šš&Ré_öô(b/ÊR0c¢µ"¨”nJï¼j“ˆÏd;6WgÐ\óf_L†¤,ÀMiéȳ²KÒ•Øz½7n ºÝºl;"…Yw K†8Þ–)a• ‡€”T½J:!ÑM KçSmȲA@5ØÀkZC8΄Ü5àâ8ç1c Èqà©4™¦ŒLd£“¡á¤ÆæZkZ½C^Ön»Ó6ínS./ÌçÇÜÖÞꔌeêœvªˆ<ÍsÅ'wÆ«Ö8ɱÀ5;`à_~L#5â#5j#-´ #5nmv»f©˜¤Ýo¥/¥ó“P aæYIÔ86IV+7†hÖ+æï ϹЦ¾Ïl–·lgCgM8‘ê{K(÷>twx96r6 Hm^ÞæR½¥É¾‘ÎC¯¼9†ÜO7r#5Us.T]4ÝE#5¾_w(_“ꟗ`q_!O–,y€ÑÑõ‘ª+á ’øûï²#-)#-ˆní௟Ë<¹´‰æË±ˆ¿'€w‹+Ý牱µX«aC¤Úµ0R^e_ ÝY:ÞoÇÃW.´ózCCC@¸ÁMc³ÁìÛ—òŠä›‹*É&Ò<øcÔøHí탖Gà`™ÿ{ršô€C$¾¢Ÿfås=T³(Z0¦-ª½ÎÆx»²ôÐËZZ ýÇÄÖ,‰”6› ÚaÊ^ž\ýfÍiÏ»wm’éW3Ät„ôDःÎ#5¼^>0‡iÑHýÓªÿ2ŸÄ1–u‡f3Öø®+ÔþÄެߟòiht:w}—Ø4ã)À²p[ßw†{HA¥Ö-‚ÊÒRÞx :Þ{nô$õ§‹FŠ($K @¢$‚Q#µ²ùR={GÍ@J#Fïåœ0ú\„D~h#56ÄP„M'#-£uHÕ™pWZä#5G†ç#=?ÒG¯ýHòú¦Y÷»}#1>ŠâqÚº|?ÆB†H ~’”FIMQéõ¨~¸#5-z€™ DHÀ!2„?ž*Ê;î÷úüÇÄ{ @Ô7ž²<»lý„[Ü¿¼ëEÃl¦}a÷úëø³JDš¥´Éµ)E+{wQ&“Xi›)_‹wjߊJxï2IH†Ç)Ay<¼ÏUW#5…Ã!JÚ_f P`¢>íëCŠI6+>)vG‹²BóååÕ¼½WËõôÞðTN¨¡w¸’RíWËÀhôXâ~`?¦I"]ªEH5•@òÛH½4âÚÀyXç`ô¼N×Ïeþ¹ ¦¸UFoÀèƒà¦‡Hˆ–ƒšmR¼§æ* G&Ö QTA-çKx®€:( ôì=d$ˆ"š9ì—()"Gê‹#5”ƒdDÑŠET8€ò&@ í¤7BFQ @€°6$Áå´~ŠfCÕÅ×â.‰Úà͵²ôtšõç~U¸†wV.‰1\´€Ô©™#1ʆH¢ÄH^0Xº5c80.˜HÜÌ÷6D[¡³·¼ã9YQå—ÂÇÞÇ0Û¦î2=#1ìN„ìµµìQTža‹­ˆ‚AˆRñ5<²3ãøÑN±¦)€xn4è  e#1Œë#5¯œR·>ݵànµöäwéÖ#oA§C¼þUWÒQuÌ0á<¢£ N=&OAUwÀdP¨„‚Á! #-„"´räp¨ÐQŽ#") (€- ˆ#-°X)~OÐôòI=—Û|î‚¢Qèã%#m-TP)>@0¬­µËÅ [­@`ÖšEȰ†#-Ü \!ý¨(j]./o¡_Ñ'Ø4Êþ”שx×¢¦I1oWux½6¯Sz›šìJ7wh%/G]‹snl\Ý1ÝÈr¹¹RVWvž»Ä—˧[¨]©(½¢#1XdTêc¨¹›`3éýܤ©6HWZß·¯Çùiœ€'¾d…ÃOæ”6ê48kÂIHÎaãƒxøÞOgµý¸9üŠÝ*ŠŠé— PÿPÓµ;¥#1œ,«ÇÒ‚ÈH$I 0[o½M®V1%¨­\Ûš’¹mÊf•2Ц`´TšØÑd9Ù%oÛöß«~E\ÈÎYËj€ãAV,„PDrT|#-ÕGöq}]cø^ûý§³çM4p–ÈË„Mÿ0Ì‚zÞ„5é€òhŒ#1RšL2ÏdóÖ·Í?b¸‰û‰…D›xÝï2wµ…(!;Ž´ãLÈ­áë‡4RŽN5û³{Ù¾Ûí®š@R)+q(&Ìc®ÂÎTXÞ‡„öä‹[W¤ÆÒn„ðñ¥Hv!ä@dD!`P÷”•*#5±òO*:нø¹æ'Æ”÷ASoo3¥f«~Çû(OZÁãþü^¼¡¥!A«)ª¢¥ˆÁb§×Ìð»”P‘|€ª¨W(š,2Äšü_£ù{¢^Ú­¯Œ”V'–hÑÙå$áÚ¶¸ËŸ<á àgÉyº¿ZCÃÉ#-ˆŽƒã5}-,(`ÑÒª|ú\Ç ˆq føõ41ÌH4R#-z–A3€d4æxñ²¤ÓáŸ*ì,½kó20ܶc#1P#1fMÙwȇ­Éâèˆr)·eñø`¯'ŽR7Ƚœ’à‡ü1Êû ÉRÃx ¹\0&ÔÉ–ôÊE5¢×xA`Ê'μOUàßÏ#‘jßÝ\^¾1^t,[ÞÚñ΂`[˜ïžÇäÕÅp§ºzOdY<_\ºáèæø%¦ü_‰»;/lih­¢Ë,xÂr€Â•qj£ÂDÓ%B2.QÚ8³ã\õ®|q=6º:ñì3CÑÑ Ú""˜]Á¿l×=$–Û+\+Ñ…¨S'$NKÖç[:òm¨4Ú¸r8lüv ÷<À'­Û5Yt›cSnù÷ũڛ4#5#5ÇjÝ”:lGÝŽûNZÌ5©äù‰kš GÄF"àrdæfÓ#5gÙ©ºÝGƒa¯•³fXú›ŽgVvÞ}Üp–&LOH!„Æ#1pþ÷Üã^þòcÛ ?‡8n뢈Bså³t£Às;;è#5‡…Ç´=þФuo!SÚaˆ°µ!*Œú%dŠ”¡}žŒ÷zÿËf½P;ËÍ#̨ðUûð@ä'ùôÚŸ¢#-ñã ‰æzzºûâjl õ•o³¹IMßC>pÁ‹lßÜ¡ãœ9hœ¯ äl¾D4Øh««°9ê=0•·g=Û†ÑsûwÛw?*Ã%ú¡oör sÈH‚:ÛP»ù7?gÕðO3 ðÜlp;vêÝFl†/hƒÞÙÜØ].oxâæ¤.Ý´&½lÍh]É®WCŽÞ5î8›¢!⛊ÖábuM¯_cªï7‘7QÔ°ØÑχ3€[‰ÚQ$-#1MÒw •TÓb B'7 çϱ>\6a#1 ž×ê8×›.Œ‚r$Qôk՘ʧ×TžhÁ&š+%”SuB{«ß?ž8+cª6Éi’fLÑD7Æö–i#5DAƒTP«,2Ȩ̀X­’lycOExtêåÓ-ã¡§"Ö$3!~,I$áÃÔ6Цn2tˆGmü[N˜Bò†õöý klÑüÙ1,0]…SÓß^}ÕI5ñÜp î;ôÏØVU4ã‹ÏÕ¢¥(r¡Êú8ê’wQí55$Üü¾Q/¸ßÀÀ´6˜ã¿M9+š®|nÅpxÉ~+Ë^Z5U‘…Å6›NLüžÍ»„"1ˆëöq˯rôÊ¢A|ƒ®~üšË]Ê©Èþú œèÊs”Á/#AEUE€VëW1K#C6Kk¥Í”ÕÔÙȹ,î«]Kk·ÝåËwA±(X&0+ŒT£…j±Ç5 D¢"€Q¡Q@@öP÷ñvŒFnÂ+#-L9NnÒ¤€ÈÍMRR”&ªe«3_Uü¹Øä]åXSg.ÄÆ•¯>;¯»¡ž5¥»¿…ÞçoáþD®· OȹH²€h*'’$U##-õPz¢ª¼n› ža#5@"¥Ð<²!ß?IrEŸ{Þž.Õu€qDüðQÚ#5DÇtPXR‹;ki’Í<ÒhÈÙÛ[8ËØØsZ)ŒpT(Tæ…3@¢¸ê­[dmõ(§«óN˜F×nçŠz€§à€#5#5GvÄt3. €}ùV¹•A=PÞTD·*îiŽ·qîÓMyn<÷ àNhOž¥ßO·~ÊÏ/~héáxî7u¢åhЦKˆ€¢#5Ý#25ª¨J²+HAJH€EŸ4  1 (ÁôNŠÚ)˜LjNg¾„–;[‰mˆ…‡S=ä@©#§….Rƒ"„ˆ’!Ýš{Èà"$‡ß{Ñbኼ+üº8øöNkÕÀo$„Ç~ðëξºÂÚHéMžK?SuUÉÔòázÒ¿CA{ñ…ü&'\F(BñIs©N'àÒÌg›EÄAD¿I&Ò–4m‹ªuETÕz·¼Þ0$#-óÏ`WS„ÇFyÿmdËäÐÏ9Ùw'Pìe6žƒquµf¨ÈTŒîBSÀÅ£‘­Žþ™ášÂî\âÙ •§Ç´ $ EP9¼xÖ²H,5žÆöTH­ äõT$‡#1|ÝÆÍ¡7§ˆ€([!R,UA<6kLá˜Uå#-]¢„Ü¥…ˆõ#5è¾}N´Iµî¾^FIKI§Ó«¤ÔWÙ}×DÉ“arðè~0CT;ó¨S«ètnK,YF´Ò«-FÈo‘/#1÷}9?°Óu§9ÞN±ÓXff«`; ~Õ¸¼AôG@­¾¨Kcf!nèRAª#5Dí¥”¹âp½¸;Ôš2. |ùœ»—8¥Ÿ~,4+:Ø£dãm“"w¬ÏŸnqr›Hãú:yðŸ‘ÈÀì¸%T’IÒ­-ƔƸÆeèB¤aæì‰kpÐBm.¡²‹ÃˆHAša¯ •n¨#A´Œ,Ó.A ì5‡˜h,$9ÜÐlS5F9, ˆrti[̀ðÀˆ2'7õsk²½Ã>4Dgr#1 •ã;•Ú9–‘z•J‚P#-*#1.Î…f#-¦Ày°[F“a¾ÍåØÄeI!ÒÀd(„oYEu0ÐÆ&DÃ|M-hŽÊ-Œ<†¡3„ìe…-…lXÌ n™ƒL ÙyH·6ä4¦Ë[å.ÊÚïE£ ]JXaá¢ÝÈ òüüç,ðvÏ#-¨žÈz® _}.|™$)Þb³§&¬HC,„æ6åÁ Cz2o‚6mî#5‰¶%ôµQ²+­ŒŒ“xÚ˜M‘zCÌu‡º2Ú£’ÈH²:äxr©|.'Ä ù‰ƒOQ˜`Ä¥b;òCqéÃs‘öM!ÌžI9YÂâZÁ²«’œ{¯Má‘cUÇ6½Þ[< }ÝÈû2_nÇSg$°rëºïùõ‘‚(åË©±*CŽåÝÒÏ(N°;92ZMÊ(R¨”NoÇÐþÔ€ÈAˆƒÖ#-íR¶ŸÑölx_r[s“÷‘/iÔ7Õw/ÎÜx÷™Ø7>=*Ùû¹ù·–_[8¼Âdˆ3à (#DÀ˜¡øQ•/1Ì^¦áßáùb}4ÉL'V#-ÆY0¤ØÀR7@)"'åJU…ÓP¼´\ÁT¿…ÖVq±T…SÐĈh Æ•ŠxRºL@˜“Ÿ®ÃÄpÕE’EFš[cºØÙÐZ¬˜·ŠcÁèu9õï@gˆ­#1)8\ÆÌBa“v_¯jp˜pÆ*û$°™–Èc%QJŒ/ÀG|˜0Dð´Ç÷Ǹ†˜–òj(v¸×ŽÎ H3­4´<0›PvRl„žEÁ°¡zþ‰RA±›99ÒM óðºâ›º’¸í—2Ca×d×Ùp)7 0žêq´àηÛ_dr6¶k(ã׌–`e™%Ù Bã%±2†‰T­‘ßv÷ ¦àï—.`%PP‿ê¡gƉ“¡àÂøêÇlRYên²‹ü&P5C<#5îˆaÎ×{äš™ÆM3YTmdÓcÉÈTåX4ØœÒ-šF=†2¶#1Ò*EU³¾Ï ã…Ã'WLô|³†¼ÛÙ„(àšóÙj Ôe^¤BŒ*œÄ-5ŒGëdjB~Éçšõ)¨Û³A²Äb i¹ w€¾YöÎ#5„late^ 5f ¤]SY®Bä.œ–qE‚ÄqÚP܆ –#-y°®*äÀ1«cs¾Ol×V>-<ãMö•¾u·…~æ)¯Eâb;ºÄM:©‚DË׉bËŒ,4iþm¬Ü½z]M*$¸òO³§p—xÚc4uÆÙàâ×Xgds°•;É»Â9ÌFÖl_ê¦ÿi6Bp㉺yi™Y7Lkm‚Z#´"ò*l°ÒMîä¹i«.Ë|]vµíÕô½:­_K‘•7Ë3¬…#µW„bf2‰)PÙ&†O/*@â•T À:Ø’ÄÚE¢Í‡l°šÑ*”ɚѰÐi&£Bˆnš6ÐÂÂMD&Ì–“ÎÙÎ&1(Qɽ]Ì5©Æ4¹o8¤}p¼Ã] •€àɪ…A6ÂZLˆSÒ&i*”´7CG(š –Å‘,#5t7krŸBÂÃ!™cðˆFóò©ÜG÷²ï¬cã4¥úzDQÖeŒ¼}¶àúcÇŽH’TFc¹ÜÖ#1ïÑÞ|´g/»>ÿÊ#5÷‰³ƒ󭪧íkWªIm¦úyõÃ{NöB°ö3Å®íF·é=‘LÚͼqб: Êg}#SöÑ_}+mHyÓc”4âÆüí3 ‚áò˜sÄxÖ¨ßl ¥^Æ«äÖöS'!Ñe å$tÖcÏË…ð˜Ë–ýOºe8x¶+qŽ…¹¢wÞhתIthcdµØÏ%A\? ˆT§©ïß5É{á̼$¥b²”DìŠÜëé¬nKAhžû_Ò-ž&ÍrÆ0’SMÒ“ÔoGÐé;$…>mkAF»²2/xgµA6£Ñî:Ç»<#5Ÿ«×5²û9ìÖz7pïK³Ã˜Îe%ç¿âNÆ­õêê'²½}ç#-ìí±õzüÆ— Çíú«•ïúΫIÖ•Rã‹á' ³Â¦â§~ß³¯éËi滓óxPtI÷~–ýp„±–­çô!é;úhÏNº8ÓК‚VÏAR|'núöMx”н45#5üÛXuÔ­nLÔc‚ûÑØóôOÌ^aÚøh«³UI_NÒÇ€‹½ÕJ££²çQ”%“>{2ºf¤ë#¹7›ñÈñ™™£Â¸‰ÇÙ§4WN¿²zód,`lÁIº€E  Ô¿âƒ=Ê2Âðð6yFã"5žå#5ƒœ©‡s8¸|Y,ÿy©MÔM½:“Ôù4Gc„kL+nF8ÞVe¼ÐÛ2j¢Â ò¸Q“¡]87¹¬Á´ýüfa¼®|ãf,nj7N“9Ôy¨i™kmšËÎFiœŽ1Ý %¦(gFE5)…©å\vÖ÷¤q+[À¢ãèMfZ]u²f讬Ó9ÚÛàs8œcåãtÅ¥'ß5¬—'eiéº^,Æð/=×)£om:Ê/Õ0é´"ÖVN#5pd©Ž*OØêÞ¸¸š™–ÑQfh§— 0R+[a1–On¦dçy)ÚJÎÝs«Ë¼fóµ–öåÖ •°±³ÚºNå=ËÁš6œ¬—yk1`ÛLC#1cp²HÌS`@¶Ä“’±.µN‡êSÓzñËLJcMj™Ô4,7#5pcUQqP@˜¹~¢+CÖF'«³¶žyÓ!Q¼6¨üxÀÈ9éƒv°nuMU—6Œž$‡y¦ 1ÖzR7fÊôgF]Ò¬ua–ÏRi¶ L¹¼Æ‘Á¹±½´uÍQÚäÓäP“ѯ²õ¬]£uç‚®û‚gl-¸}k@Ÿ+9–{OÎb0ë$L+îäÖâ.–·Û+—ÅÛx˜À6ZIÛÙQêí†Úfî²UÍ‹Gœ]ÙÕwzc©Â¦ŒÚ2Ómi5ç×´æ\ôêO= ÛA­¨ò™‘Øà黸«xÄšN(·¢%k|c}^§F±{®:∄8™ÐÐí¶Éí=Uæ>kl`¡æxß>¸Ö6¡÷àÌß#1¹ øŠ!?k‡I:Ox’ŒlÐùxSZTç¤c®,#;gC‚HMœvâ81HÌvhÕÛ’¨±JGï—ÍáÛ£ÍrC÷=B¡4q’´ûˆ[Ι¬ï¾õÚã%¬ÇQÃîÒòNÊJÓÚ´ i›cD$Y1ŽcÌK[Ãз#á­³&‘E”>¶Õ®pCÌ>¡´µi\…0Æ€0bîÒö”¡ÒƆ×LÙ2­p7dKÓQ"H"“UWƒQmŒ{†m5#5jåVͶã\½dÍ©r!ˆòr»[ç ãOÎoFJÍâ#c{|çtl†d„!2Ó謆µ‹‚#1ÍÙëÎyé’¤Ùxu²›ñ˹’¾©å—IgŽN›£èwÕ~08kÝ'ì[LpÌÍ”4ÛKcxe1B)78S\Í^›Ft곦*S„’qê,eU"[SDµpƒ‰u°%å™d”zcn¶ Ü̾†P®#5N!b8ãNäÝX©£«#1½ö÷åÅÞHÁ˜M¼“ uÌÃtH7*#£ÆÉÕ»ò˜CVãÚ”ìû»Q„Ô_H0a]âLÁL@Ú†hÀì…k3& ãEaQz„‚Š»xN¥ÕÈ™ÚòH˜S¾Êø„aÀÊ$„é öó˜._›`ų…ÔE9($ÔÍp³hi‡•q%ÆZ×­aɵpÇp›[3R¾öFËҴ̽1åf3gsphÚtÙµœ¦ÍµTHÏN&!(yßúœníË›ª×v«¶;°ŠáY4„‡)#-¨_BF] γGk,xhp£#1¦Ê#-¶‰38Ü,4¯YÒœf@Ù¹ÁàYµÉÐǧ¤õ‰]² Ã«"Ñíˆ&#-i‡v/#1È4*o´ÚÞÜÎ ~¸²8}8ü>Ä>µ6bDÑ0AR\!ëYñ37”Qv!Û‰µHf¹{ÒÜLuaˆëÎã…h¡æD>ÐFñƤ»s“¦ÊÚ¢"9Èß{‘€ÌÂàªpØFF·Mù:’Û²Ï<7 „"ÁެÛíêÀ1žW#-ñØo-©üRýÛP=R\ X’ ðC’‡ûlí°#- PÓD©UD殮ݶ¾äÍjɼ”š­K`,fŠ‘xùÅ( *¼Â-¦#¨˜¤/Ù娑2†AµëÔ©MiŬjÆÌr"Q‘K”>^ÿ6d'ŠŸD=1K©»§Ï4îUú" ”E]ÿ»ö[,Ö¯©rþKS—Wwsfc[®ìË›®›¶ \Ù+ùkÍO/äîí u½6àí=po$:/ĈrœºÊ3‘­E¬ÂާÄÀ{Ë~ð”yåãÛîOZð±ÀýÉ‘pý'ôŠÐy¹?ke}Ø6ÿ´ú>LrÜ=Ù—ÈFÙÛwG*ɽ ÞH$bD¨ÈØÐ#1–DN©´%ÅÐ%”.££¬DÒ‰“?ÃI $aD`b¡+(‚,[ÔJ©MŽ˜.´Œ1Nb8‚ÆùA2AcøzrÈÌ`H°ð$ D¢òbf)ü!ØoÎÞ^Arå‰z¸oK¡êŽQ¼’ÆDÕ7ãÛGÓº"„tI1l[cF£kE´D˜´DÉ6 Ó2Ñ© AYFGnˆ$ˆRˆí´Oð÷>Zg¢§xŠ•Ø§½~ ’d/èêw@!ÀßΑ‘ðƒ(`ý×v"=]¸áY 1L°’ai#5ÞîeKØ¥K¤)ÎQAÊÝ0¸O'br;üÝ]B#X;º-'„ÍzÖ#-ë.@\Ñ Ü;¡(¼(‹õÚ‹ ¥E\Ï–MXo =h Ï] Æ r( #1Ç#14m)•á™Zcé44PÆ5e,·D¥H4úl…¤D 6É ª¤‹£´˜CW*éUómê^›zªeW‹WdÆ™JY±M‚n0¬\5D‚Æ!¨çúÇTLÛâ§r‰„knVDûÎ]‰)áT°X8hŠÕ4H,m¤Á¤ª¿U-ÎýôhašŽæ ™Rï¿ çú©¼±=.4šP5ÎI‘“{mMwwyÑ#.y%¢;óãÇ:ɼ6nÃD¡R5 #5…ËÞ©K„lÖ‹Œ;:4.Ã6ȱNÂݽI#1'XhHCr"ÄdN 1äÎ"§ TÇTK¡‘$H(^)ˆ±,™#5בûž€â y‡”#ÏÚTÁFT€(‰i#5²¤BÌB”¸àE³ö†¤…ÄÁp)?!k#-%Ed¼~6tQ Å`Ðå…lH;8B#-È}øùéb$ëÒ!Úõ3+áû,¢pg2î“Jñ°šb„&Â'Ím1¾‘r0«s|œªÚ## ‘qñÎ=uÉŸ2•iÉÂ:¾§/´Ú&ãGNxh¢5ásF°çÂ.Š:]ŠïT¡½©1iÄÔµ©l¡ ‹¶Ž0ÔÔÑ#1RâØEsýþŠÊhÛ##5¡Q3¾ò„ BÉÃãdS±ÍŽ±Ã£9¸µÅ#1®:›tØÓÜ(¼uCLx-gA™Ë<_#-Á­Ý˜tû›`«pÎ4eÂBÐè§þZN¦Ÿ—l­$˜äsFHTìãÃlö ¸k ŽÊaÁò©5Q’•Dd*Æ3e@[pä*1Nî« aÒÃY´}$ã.´“Ípµ„ï1©å±4\CbF”ñpCµd"ikg”Q¾YÚ·¦2$ ³tb$(»eeŒBœ¬Ýº»Xd‰ ±Ñ²ÐJc­é¶¢ÈÉvLš ¡>ÕeÅO´€Èÿ/0TŸ{[f¸`mJv²û­CŸ÷˜7AÄÄ ÓpùÄ7aÏHTÀއóŠúÚ’TB%xÙwUHÕŒ.t$0€ö¤Rmª-Qki-µ_¯µ-5"BCÔø)š_¨C;ÏG£frªŒ^±z<æ&Rä}ïˆxB¾Óú 2³’—]ëy­ñÒÙëj2¿Æ#1ÆÕe`ZŽªÍíG-‡*­ÒO"†É½4á¬ï¶M2òeZ©˜R¥Å,µ™Nµ—¼ÜÝ›$Ý •¾.Æ¥¯ZŒUt¥DÐæ£Œ8"i#Hºe/NÛÂ"6Ê9-ÖI†S}th5#oUº=&jdjxJ¯¥âß\‘-+Ó‘g™Ô“dDÄ„4Aïi8t8âäŽ +:Zk [Ë»¶$¸hÄ¢)]Í·1&˜ÇÞ‹N´éŽN£šàdröåÖÚìàc4Ÿoæ)1k¥c#-$ t¹“Sq mPó;>ìïÆ*†›ßm·“I­¥bâðWÄÛç%Î>³;²cÛéÆHL…aÄ1áŽÚºß„¥åE#1kD­/zÎkl0†Ã#%ÍÁFHe;Í>#18nd–ƒ1’°ÜœŒ_AMÝ[¥(HÈ‚S€j-1c\–QºF£]¾˜Ú¥ÆÒÆER’í:’T-zh#CªBÐËC$ŠX6‰CQT”4¢I$Y¹°,è—B•3`PÎ,ˆè†PZ°lm1ÑyäÇ"zÀõæÊR‘¢Û]ð\ä®î5Whs¶.ÍtI°$Œc)Z±–J™#p„]çt¼žzõxRê&Û¥š\ÕÑ-Õ®Òké]×]ÜRoË^»”L´í<Ýw™W’¢cJÝMnvš£Iiy¿³C%"GRŠ ³R¤UmÜRó)ªì»*’BÊL‰²ØÛÓR)šò–º[K4´²U,Êšµ}ƒ+êõ蚪"Áb¦hÛ[BTl± B*B-íô”M^±‚¿TlmÛ(l¥¶ w0A¤È¸@“   #-*'“hRìJ"R*¡Ý“;‚¢¾˜Žâ!ìÌ °g³5ïaê×},m-LX·é#-‰M9‰0Jš3½ªÎº Êb©Ôúó¼â·Ôy·©6˜¥N)÷?žÂùƒƒUB4A$D¨ÅŠYý(Z0ŠE1e*1Ø£I6$6.ÝN゙2ÚôÇèéf´~ÝL¸ì³o¿7BôýP#1¤Â9¾" >ZÀ(A¼ë[‹LÈ®³Æz$ ¡¤tBˆV›P\6pÓÑû^sÕ7<u‘䑎Fz‡ÛW¨ÑráQữWi¸üìdê®O^ßvš/E:YqÆëÄê•þÏE<˜ŒÞ¨† ©ÝC&(Ù’Uá/HU[ lBTv¼ògØ¡sDpaÓY+-‘Úrùm+Q˜@‰b¢ÿ[#1Ï[pM#1°µ:ð»©w9hPƒ²ïÓÕ ~IùÖ F*‘P‰ó¾~Ï¥u>óìë6·‚2xœl£ôÁ@vß”löþöE‰#§æ(Ð[m>ïkZ¼›Teã²Z¨€ÿº¢¬šñº…³%*‰%$›šÚäYC*%fLV÷Ý«\©¦ÓhÊ[I²‹HŠi–K,ÉJ£LRͨ£?2ë#5IjÍ3#1›DÌi‰*(ÚÔ«SDôâŠÈÖ¦»®›P—Ù´º³W·nÆLEALÛm(ÍiMZJ؃SLƲ«÷–«µ}ND¨Õïݱ6EXÙ5¶Û"I¶j¶åÖ–ŠLÕ$Ô£lmyço)¤–›M”&f55°ªÊͤÞέZ[c¨tk6[ή¼î%-©“C\·Vj’¼f¯ÕS« o"k²m›[ V©/ü9#1:þÇJ¹SN§¿T+$áÛÓ¹°ÚU(åz`ù|»«ÕÙ¦á’g5àxÉ`®–º™’L!Ÿ-Z€hå2ßëè¾lL`¾ õÕÂ{P-¦¤´#5Êe‹!"&{i\•I!eTF d0A„H##-äØžÞ\¯ÊÚ¯…¤"-³JÙJ‹Wº­\Õ±¯‡f³I¢•xÛéÝkÊZºklUÍŠ¢ÛQ­wÞ¤²S0ëQM)°»Š@”…[çl¶åª¢É­e³Q³Zfb[H¡J-)5J[kM³fmd¶ÃJh¥Jmò[˜a€‰)j+*‰´”‰EšRBŠlÛ)šDÔ™£a¦c(ÅÄXÍ#1¬¢›´e†£RU‹jR¨,YJ•)L•IZ)1¬–’+J!µ³i¡d(II&Ra)’i“RÍTÛª"ÚDh¥‰›RdË+mK5“&Š”6”Ûe›mImZ÷µk»VÒ²ÛM¤²Ô†’ÞóV×M›5µ)kY5«F«Ù«ÊžÊ®k^³U¶ÑjRØ,•Qmi«MM¬ ˆ‡_QRX׸žäí lÃÄ­ÑÒ Qåšb\¨ [^Øo™mÍÛ¿3=udž¢ ûÀaê»ü“–R‰©“áXõ#1äOAfö)çzIRdx†§—ˆR<ãr7—mKTI}8k÷üPV±¹U~1=ºz!¾:Ž>öù†Éd]~¯.¬k–ι݋f€Íµ”1àó2Ô‚5F~ï²z.ëI¿3±J7i(y9T¥v˜#ý^!òA¾ëèñä:,ÿ?!ÃÏw×ßwfPëBàl1dP—™šwx=CbGp)Ìæ]28ÐÆ˜Ä×&¸(F*ˆ!F ¯hp¤#1}Ыþ%½€-Ò„þÜ>J£ØpÝ‘ºŽÉ¸7Å:º¹Á² w#-×ïúJOVÝ ›£”>²8EÆ{(Á‰¯éÌŸ‹ïp(Ȧ*b0<QC¸ã¸m§f¸6ry<]¦¥lݺÆå9WÕãg©p]?k±œ#5c9‘i?b?R±.-%NÁ¤ v wêßÏñMåóüŸíÀ9 *iì&p¨*/ê¨}ªË‘#ôU:%-?’U¸ ÙÏ£þz¬¶úIÙ„(²Ê7V†ø(›A'¾¡u·àømVÊ7q&/0Š/„lÉ’f+*˜(¦@U9÷p-c爣)Ái”^AäA,ð`}6¯¿Òûï[onÓ€,T€!˸#-#-V-±ª£Ûê•w]·!êpöv·ÙªÕØ%Ÿ'° ¤¤ˆH5‘;'8º!ÅåS»ç/ו±—.«V#5³'ÂxËö÷À¸I` 'mnt0£Ë ±0ÒàqÙÄá¡_ ÙW»²Æ8[:vaöõËä3ûLÞw OÙÕ·wÈÕ r.<<ÞïÊCaÊÞ—hÞt÷¶<*»7xÑQ _à›Qêšú{näï|]“þöE°(¹lYÏèŠ5Ëþ®ó'ï¼övå§®¹¨#16&#1¥iø¿ž×Ë#5Ö÷+-Š"°KK*¤,Da“Yr0˜aF@‰‘„mZM†‡¼D5$Öê-rõwv¢¦)=ž-é½f¢ò÷ë™Rq³mj˜i䊽´-á# š3–ViÆAƒnÒÕ!|ؘL¨nL0\%¸¬7RÅD4avÒª¤PFº]$ ˜*ZPñH¤Â¦?5B×úPj‚Á®€ÇKÓ¾‚ ‹œïF²$P‘>H®¡|å‚Ce¶Ñ}„õê Ü€æã↻ªƒR-Ä­I¸¤hÕ%c˜àú4~vîÐ4y´¢¯dÞhó?»ï¼»j=Ná\I†î`+Ðõku}ç¹{ÖÍ#©¸v>”M0•€F†ñ*#5Œ‚°.·Ûß\4騭¢÷j×\±ä/Ü}Ad ììQ–.oã»snWTúéÆUöº5öiP‚b'מSÂ뉻B¯ãHb&˜riùw}h’ÄÔ±PÒ×›Èã“rHCá¿s‹®H:ÞŽ>Eˆ‡q‹âÖû#˜‚#&(hCr˜ dÕÆ¤³d=$¦Å2 Hß" †m>(,Ð=õÂÀ·v«ê°Ü‰ „Â#~=%èT#å×J&žÇÊá.ß©U“a˜y‡üÜcIn…bHh“T IL’dùQˆ´IôóÓjÌ_;ø$ IФyD‘6E,ãPðøª.ìÁ¿F¤ŸoÚ§ aÄé•Ã/;ÁÞ]M4ÛFÕêÓË»ÓUÒMJJO-DV¡¡¸M@(Xä#51“(£ÚÑ3€Ûƒëgcqû¹éïR\ƒæ.¬£ˆêÌшU ‹V3ºxQȵ½ä„ÌÅ›m8œ(2ú½U;”+ƒœN›—DÍI “‡ãCÌMª”œFã£çŽ[Ü#^OÆ>ŒÈM4ÆyŸGpÆÎ‡UUñÌÆëQH‰,A;éLMkËaYIjí™îS>²Š#1X„ 9™<‚ÖØÊ,ݬ֘„¦Ę̂Äs1pÈ \¡û0Ï«OOÙߺ™çùyU#ìJ³wì-×TIQܱæœ<ë j¤PXUG"ž¨€Œ dA©#Í*ä~® bi™=žœâÒs·HÒƒ}=1™£Q…2h˜jmtÙDíÓ’ MµÉôÛ­;Gq5áøçhî#1#52x&×#1ê5Ö‹Ž&»C1¢ó§tM5›]ƒ¹½óÚ—"Q°7—[ &¢fÇ)éáDœ9*±WxOQz #-ë;¹ãÂfêÖ‘í’™SW¢8³ÝEÃ[›ùKïLXuº·3-{uåµ<µ‚^‹§*¯bíšDím#1¤è‚#%ÍË9šFœCy.ôã(ü€Û#-ôH¡ÖÀcÐÜP#-=#®#5‘ $#5UNC( ‡ * €20Ϊ&ÓX…݈P ÊY·ê·íiÃ/nìg8ö®äÆóºîïY½Œâ•óW.Qê6ØÔšSZ¤Q|1Ô¿7{Ëůƹ°½ûŸå¬$46×HEb#i§ÌŒâ"d’]/;°Œ¥çkœÜLápòºÛ’Í.yçZÇ^v~†ó2’1¶8$Š#-’A²5ÃZÁº1#?7mÚïUFh”Ã@]aÀR6˜Ò·ª³™YïädÈJ…y½«°ð×eáÒKQ€Íó"¤5;â&Ëã=ëÞ#ßmÐçã¯oN¾U¿(NDuä '™ÅšòÅ(Uá…¸^!Ë©Šjµ…É!‰Ä…Ÿ×öxÃ,êàš‡Âq†ï/N}hÕ@‘ÓÈ=˜<#-öæzò¼Zí#-´^vói߉qí»œØÎ0=麄bB2 Á|9˜–'ÃYs óêYl“iQ&j•R0û?'s³Ç±ö",Œ=^,0%à“‹ y#C4ý,I&Ä!±×• l„•ªI·äWd!”HNõæ½m^µu"nÖÖ;UñvÚôŠÒkZóÔñ÷½üõKÓ§vâš¹s3l`ʘ‚¡«F”iZR“dnÎÊÚe“’CX½jŽø¯À˜ñ×D6èô™¯ìÝÈtG’¨0#°òØl¹'£±áÉê—kŒùØ‘NÖ»h#*˜š¿ƒ/KÆ_‘u™ Ù¯êéIµ†j¯Ú×]¨Õ“j¬ŒÃÿ1œJJ¦Mn7 ° ª&ŠÄ¥›V®QJ¦šM--×EʸrbK#b±j-üjäcZ-¯(Õ¯ Á£x ÑøÕ ª‘2b…¡‚ò.p+’€Âv¾X¡ÍE™}øÒÉ2ëµÛš ”’Ù)q#5Zq19çwdHÂ}Èk²Þ&BJ#-vo–‰_%Ä#1Xau8T[ÓF¨V.J[M!B}|ù4ç#1'=M‰pŠÂ>þ ,#1“Äõp#1vN.쫚|$ Ú¢ÿÆËr4¥'^+¤6BÝBÒ˜@á‘Sñ*•FZæ¨CZ#5…^¤UÎq 4Ë“ E3b›ž>ª=êú›ûåÒ¹«•šk«»ci‘Q¤Ú-Ë\®bîÛZݪ×Ð7õo•æ:F¸ÈÙ£|°böxË–p3ÕIÖºæâ—\ä'z@€;Ñ\ƒ+*6ô[å>žO´8< ³–E®ªH½[DY¸ÐHEC¬°7–³Ð3_Ë~ÑLâiŸ–>ÿù°€aE†çH;_f@flÈ,pÐI×îù1€Œ#-¨‚†¡+úpLÉ2oçpúo›b›Þ¼j&–|ú\´ü|Ϩ›<è±þ Ήš§4ýý>’9B€¨¨9¡m¾¢¥Ž>]˜Å’O—‘“®²÷t…æÝ›?¯â9q LˆB?@N.†ÐxhSTSb—˜ô^—Ôâ;×¾ OA#5Åm·Ýݵʫ]6ÛbÖÈ•*" Ñà‹0/¨"!\…Vš†3ßn0»ÌËõ¡ƒôq¸Á™+£bˆETcY¢B‹{/Öºê‘Èï:“¨úì{Õü¾ýËÏj±#1ñ'ü€t$MÉêOrQ¡Ö“ÄÔZprk/ê‡@ç!$ $Pâì¿ØoAžáD÷2Z)-‘]]#5;OŸcâ}%Þ)Tbb³ñõyòãØY X%ès–RzŸq´¸szYÖ] ­Ýóã¼=§ŽïœHŸ¹µ»m+è—ê¢î[#19eß—¯ÆXþebäT܉!$DêPa·˜×Çe-d¤~ê’]Qdª”Û ¹ªŸÞfR~Fa"„¿×F]Gûh†~;W.awZ/ßóÇ©usݪì•L#5e,&vC#5Ȇ´(~„ 0º¨(ëGý½¿……]h©iRDHAM®¡Ã$UPÆ#5óÀ.[†YÝ®¹Zi¶Ód®vΙΒÆvÁ#†bY„dÈ‚µÒLÖ[\K‘\ÑoEÍÏŸkÕ/buÎÝØ˜éÆ#5Ú¬ÞQäZ5#5ÆÐØÆed­%¤jSÚ­uj0ˆQ ¯w-GÉ®ùv®VŒ[{6º;7 i³%8ÅÄH»%Œ&7©–û*ʶ‹nF5îÞ*M«Å›yŒ`c##9ÊåmX1#¸j´¶Èñ=#1-µƒZe±¢IHÙX!ðBdo%ãÃ#1²*°Æj´X›ÕIë*À“,¶ÐuÔ5¸’¬ƒo'.î®k–¹sk•Ý+–åQ\·/†ñ‰1íiFnŒ¥,²Š“fDb¬)%ãk#1³•¨lé ¸f5iÖ Œ&Di:3©o÷5˜i–4ßÃZ±1£¾A¼}ÐÒ'h—/†6!îÑ Ž17ƒP$µŠVVÚ¦©ãˆ£g!FˆA¸Ë+ïÙAýiŸsêÍ„ë P¨(eù´>`|ém¶OEI š0Øž\ÝŒs?)ìTOeTˆÇñB  bhPIEÕWH¨³NºÇj²Ý盯&ƒC#1 JÐ")ô~Œ¿ž’˜F:æèú{2·#Ÿ?{×ô7‡ŽŽˆ<ÀQìMÞàÜ~úH}#5AP­Œù=¿ZV´îFåæ¶5*Y*6j»TcMD™/æUF5ŠwjÕÔÛ±‹UÊA¡X@"˜J 1²è +#-–¥IIGó–OìOဧ"ôR*BÎ"BJ_'ÀûÞÎ…€×÷`‹ ò–qœðû+·ááV=£±”XÁ½Ëíó3\ÐóÕ1#-¢1U„ ƒó0-n4Â2Ä’üø(…Š&ч?é¶&0ˆ5)ÙÀÕ“CGãׯ\zM~!óõxIÀ|ªlb~Øåñ½UD$E$Ÿ[’åHd¤”›%‚DH$ó·R•,Õs\Öñµõ¶×š™E²V°Ù#1Tm¯J껀R`dÒó`™¨THR@Ý$.M¶5y»¨Å˜˜RMµ&„c"&šÔ¶h¨YЪˆB(E=p‘ç¥cñåÔi¦ˆœšÙ‰&Q^Å,BìňÑ1è[DB—$ˆ?) ¼ ÀÁ™a˜62e€eÄ%I\4DcMz@<ŽÒAÂh"ȳ8¥¬ªB rV`Qý‰´Ç#5ÆŒ…á½aÉlRE°žõøIrD°ùvdyyÓ°g,_í>î Z1”ª¤g·ãîâkìÚ‘zTçT'A¢â¡lb3`1¸·(*Y ItÉüK³§úKø„^¸)Q׿²€ê/æIöô‰ÁÂUÌ“µ*’Î;7 íÒú|ÁÜW“-xç[¶î)(ÒËJTÕcj´ÔØÓUšk)䮣h#4k)"m»5ïú}oË÷ÿ!Ó‰˜þž6 D ƒU¤øÞÜ4~-RŒR®ƒ0õì>ï‹gPû§SƒÕùˆéô°ýj3‹„é‰sfÓ¾´ðèú Á–‡>ZuI‰¤Ëa#5)“~åonÕœCÓÃróÎ*/,`Ä5¬(BA„:cZÎj‹}¡ú;¡nÌÉ&42m•´PËZÇ0àhquDA‰¦#‡¹1#±¨ˆ›ì40¡ÊÉ(“mê’ª†ÀYˆä>@pø(%iê|}#-!â*¬6„ï@ ?¼Ë4šó€ä ÜnÆPèõ:x ¥ ÅB8O*`‰ðD(„†øVšš2E#1Ó -E¦¤!l ÉþÕØ4p’lèè sõ/S<GIɳšbÿ#5&I˜qIÎÌH³{¶¨¦#-ŸÄÔ±,SÁ¨{$ k§y Òdƒ‚Ý •v|ãJèQ¤M e`i"‚ (@Û!).TßKTQ=#-(cî›oÇm|9šÏVƒ„4r#1¤3ÈA0û]<ºµ!×þP#-d8qTá“ú¿§“m*ɆÉ48¶™0xLQÃy³Ô¬vã:>£˜ƒ:“놬}bd}I`õ"­÷&Í@é\v™(|þA©8ˆ%x–i#-/¹¸yˆH’h‘¬Ef -II“M3i­¥©-£$ÆdkÞWA&Ì®We¥Ÿ¶«rÕ3-¦ÌmŠA¥5’kei²ÆÛ+lµ6ÒÊm¥™–ÛÅÖ µ(ЦÔÂY¶ÓDÒÙ¶-UDe ËM7¾«çYºÛ~ïaþ0£ê~’ëφl¶ZÂj#5áG G¶-ø?³×FÖæÚ5­u9µª*صԫkšÝ›Á«Ì*0mûûD¼ÜØH$â„NÔë<;8è‰ÄQ#1Ô}KnZîì[\Ø¡.¥ùí-í¶ž(Šý‚Å’~ÓHw G*(¢6H6&œqI©¢0`SìPañ½ÏC‚}1qDa>\vG® ‚ŽÝçà˜#55êUCÇžŸ$þçG Àœ@ˆ¥@Ý<4}ÉÑ#1¡üL@È÷ÜHA1!#-¢/d#-´AR¢T_Âr#5—µ]ûDža{9Þ yåÇ,dnß¶æÌ Y%‹fMø"Îÿ*‡šd7Îð 5Iê# [¡ÚPI~„µ£2ø0fωÑ6?©HÀòRÀÀÔº…®ëbÛ‘óž>„™@(iæzÉ”„‘y Ö£D(äúZÕV€g橳ân+'ˆ#-œžÎÀM$#-CQ¹6â"‚Yi:mØ+x`8” º6&×ÜÎGÄúÓ#1Óù.‰çZÆÔEihh’8FÔÁBžºÕ0Á¶ZŠ(°ÜX»‹—-^o3‡yÔÙôAh÷!ù¿AuºiF}zäcÑ­£d~YúbŸtŸ.…4/}†[Q°š{p‚w:^mþÌÁy2ã,ªhdÔT0tÆÆ?wDÉ!„ˆ¢Û_ў͸ÛkO¯ÙLßEI¾R‚ž€#1În»­ô 9+ÁS:~£r˜è}~º’jÀ¬œõw–¼¤¦ç—KÆõÚí©O‡ËÀÓv­CèMÉu"†PX‘“½…"±°‰ˆ¸,}úg8~~Vk\WA½#-2OwúÿäºL Ù¼H“€ù8å-嵚J”³7åîѵñÕÊ÷îñÑÄ4§`H5Ii¡Ýñúÿ&%’lÌ÷bÁ‰ñdhzpAË r„zv@®È‰#j¼hXÊÃI@`1‚Q°¼H:–@ÐMEÆ„ÖZ_vEzɶSZ C&z*´†f4$.ß%QG(S „ÆT7„b©ãÐîàOã©‘ÄTÆê5s S"D—ƒíˆ: y ÓŒÈÓ ‘R#»óï÷UÇKÍ%‚ØÔ¥éœág;ÍãCkƒ.&’5"T)P¹¡k©pëb‹Äà' ¬J‰d£<Ô 76J§ A¯é Z/9´ïïâˆ$Q„ ˆÒ€Ê4Ld Ȉ*EA¤T†‚D¦#5„ƒ)y7°]î(ìó#-L6i}~Ÿ*^—بè;?6ždÌ#1!ÛP¢@팉L­îÝéŽÅ}n½þdOª$TšQTbB©RÙžWšªœ©=ÉúSL¼#1vØRM›¶Ü%¡&Fš`²VôõZà`tc»|Ù¨ Ó7ad¥;é¼_HàNúé”Lá"…I“ “d«Ý®ci•ÎÙ¬`}MýYž8 ‡EEꄤ… ÝR¦PÉ,¬J,#5BA@P‹ 倸à½è”ºo&òéL”…Þyå+'sB"†¨ªŠ‰wp²/&55CìãeE{\HˆaôBR4Eí7/K¶X›ä»0â—g3&¨˜0tla²_ÌCÑÅ4PÝ#uƆ£S'Ú-™…{ŽÁ– ?/Ÿ°xN›ÉèÆ}äzmŒŸ˜D}´‹çV8êÓm²¡´®[˜‹dP l6õ¤ýû»™Ä¶@ªô1kgðN/7ˆ©¢(›±ío¹)ø?ÏÎMa¸Kí¶v‡äBIƒŽ#-X!ÙÇv´ÌÖ” hÊ@j¬®£n·¢H4Î~ÍP×w»LÉ®ð{ü3‰´¾×ãìuÙ3É=@>ãÎS@A'F#-…4è ÉLdM¥çnšåyçJëÏ+wŽÚëJ³V’Ú¢Ö¤Ú©eãÉE´ò¶òít¼Ý·%jD °@ú>ž³¬ÜQAlöÒ»¦©„Q¤PG`Nã„$Œ@†?Ø&þVÚþ>µ`€»ÐùlËi ikR&&tdß°µûz›w{o]ecwEÂÓ.cU¶ŸX"5\DÊ™Z1T® ƒ†‚ÁI-˜à"eÂZ]¡š¬¡Ò2®ê‘G$fˆˆ†ö"#1Æ@ƒŒW¥8…ŒLL6ñéšÕ1êk7²œÈrÈÓÈKwL:å¹E®ëY«!PøÍC™Jrë9òàvš|2ËÛÌ#-9#5'_æ!=ªŸ$œa2Ô=;êfÑ:O”½ÔøÖ!V±þå¶óx$˜—²ü £K­ ²ñ{§{\ÏÇ{š¦M’¨0‚ìóÀ˜^ßMñ÷#5ë¯ÌêÝ7Ã}ìÈz &6¢Ð7ª‚ ¡#sÈ ævdœ²¤Åí¸ºãŽ8¸DY!Çnyß·£<ò"[N3"ÁŒ©"U1H #1CLŠ4K&Ó±¾¢(:g†/Õ3”i]'ɈXÖƒ=3Ì¥ò—{}[bÜû#1»ßCm©¹üˆØïFãž¾Dµ]†Nbíävkhd uù¼q$ÿ«Øšû)Tªr(˜(ðǪZ­‚÷i5Û÷i䡳`‹é<‘÷à§H\#1»WÝp÷Å哸3#5õýB4Ðm¦Ç1SCKb¼Ä¦âçCú©b&þ!K'£!26Ǩ!”!¤ÕÈâxܺºœ*Vr&ŽçóH²|¨VcSWn{cÔLN¨Tb¼<è\ò>þRónÇ—Ž c|éß°LŽ]ÉX”;|Mƒ²!š´GT)æÁ… pª’øEiD56?îÙ¦Áó4½Ýû/pÓÉ|"z1ÜÆ0ø9×EHõvˆÀD¶¶„ÚÑ$Z±ZŽ DÖŠ–*’Ñ’D¤$H#5ÛÐRÐEä¦Qã멟1Êé€ä-Æø0`Ü&eþŸ¸¿^ auAb ,Zk€¶°º#ˆ}ƒÒ0LLxÉ©+,E 8@n°lP±Hà¤p˜”M$…Š„³H%ˆ ˜ ¬ Z&™) “(›[ùIØynÎGgu«¬×¡ävšÈpÕ …ÜÈ4Ô3Jk r""ñdUY{ÿ‰ÄdÑA*«;Xµ«z¹%–'!=¢¾þ)òÑ"ƒ¿ 3Íù‹Œ“•ÅùSÄ K°¥AR‘*Y«½ 8Q M¼i6ÆG#5‰ gŒ#5†ËP‹~Ê|—'–©T8RcB€@;N»‹‘ä$µ[~Iy4r5í‹âÐÒP×-%3«5ÌäoðC¼>|D%Žï— Y>’‰G¡568ø¾îþÊí>”ïdLI€ˆ8wt¤i"7'iEZI!®;Zë›wnmÕÖmM-f•¬ìêÓI²KUM5¶ê¶,W)—‹——vUyVßfY±±DE‚¨2"'u‡ª„;ˆ#~_«†FÜsàFÁ³8#1Xô݆nnÖŽ‚°ÃéJ#–½ôZRBÒo âÀËG×\{ŒûÚãÝiFHH½õÄ¿žN@r#1CX,Ç–Õ&üÚ‘Aª#5Ú‰&æÇFáép´xÐl%‰m03šÐÎj£DhÕ†ÃÏîv#1YÔÄŠY )AŒiòã;°½¿Ñæ#éhBZ ¦[^<íÀ›³(ÈÝ ™^“½#1á@ÙÙÅñ7 dÊÈCÃÓïÊÏlåð¯†áô* TRzKœöøpRÎMØñSŸ³`[Ïd¯—U›”B_’u¿“Àó:N[oæ5ôò¿ök¶Š£+0Ð)StÙ›ÝäÓmdÛ&!Žk¬bÐÖú95ÚC“¶sîÍ®x T)4s¯~Oû¿óÓý¿‹}VjB…,æTV¸+j!̓):.. ) ‘ø'˾‚Õ!Hnô‘BA5Ïeaít(ª ˆ6­±0¼ä!ùeï´HUƒ}£eưֿ.f‡5 „.DÌsA°­(©±eà8Û±dˆ©†õ 8$HÒ Ÿ^yáµvÊt\NÇàÆy[Lµƒï³¡Þ(­E³üÏŽMÏ|ð.ÌLnq‹Ðc™‚<øtÔ–D«Þ×Ù9ÂøCÙG55­K²¥ÃñéŽ:Ÿ†Š«á&á’Ìæw#1‹6›¶bÛaD!|ÊJºd°äÈÍÕš\¯À€½á‡¨) 6±›#-ì7³SzÀKÃPƒ¯²âŒ±F4ÀE&ðáÁˆÂ#ñƒ;ׯ[m¾Þ¸Ê&4€¡æ] ˆöÀ²R`QŸ0¢g’C Ãè>¬¥ƒ]t¹×ô‡ÜÜòÒ,Ç$ ¬‚ó¥9dÏ6PŽÊfxÏ3§Ž@ñ8=Ià%%–ëi&bÿ–¡xFQá§õVÍZ%³³FÒ8EOhÑñ† 0,aÖŸ^ð‡–æìIAµõå3½rRT%_¯-U—€u ‡ó°½5ú£å§/Z~Db0Á$¯}ˆšN‡aÛe†g%3PuŠƒ{7\réI%©R実‰bñ¬Z²k%EhªØµ£h´ZÛ76º!¤ÖŠÌѵƒUv[kÅ‹ERD jÖCž#5wš¤ðýûÎa=qªVÆÌ‘F"L‚EßÓ#1Ê«Ñaw›E I+åÛ¸âu¶ávýøg!7‘nYÁ ÞúËÝ;ëÞd(]Љ6+=:€\8ßipy0 ÷o>íü8Bn©ú¦þ—>ŠcàÀ=šüˆ!¿;Q„ÛdƒÑžû÷V¤bº9&<ÉÕï,OÙé™à@Ýäz™_¹î@H!##1OB¾Ù‡n6Þʉe›Ãr„ÁÐþ®Þ~”ëpVžþ\9²JŸAÙ?]!hŒÊwnÊú[¦4=̘ƒõAÇ1x÷lZúR‡¦‡²·*‚ÅÀü¡s‡#1ô5Íí¶ŽQ"C᛾H,ŠZTÕ+f–-þí¬×]0JlêH‰\»#-ö“‘lcȃÿ&ëìv3C/}(•|F°2zÛŠFà|ƒm|FÚøü!“䟫¨K†¡fôɲR¹™d-§X pae…ˆ)•Åö—4¨ƒÕÌ,‰?Ò£h(ÚVÓ`Ðf$n~~sÙnÀÁ&dêIÃ7†’ ¼ª”F]Ê.¯û=¾#1÷¥žÆ¼½\ãtg™êõ¦õ›]]N+¹ÝwoVùPS "ŽpF@|¡á^x½Ën½¬ÉQ©‚¸ð̮ʙC¾GqaÉùšÚ¥ÅªXG*"a,µK¦LpzÈQÃGXfx:ª¢ÐëÚðg¿²~_TpôÙ‘bÐ1|Ÿ²Àí\°X‹1AUG&& j †ƒª¦£N ªc“£¨òv€çTŽÐð<ºMü³1TB{zó½—Fþ£c¥²=x1$®µæÌ2b¨UEfšQªº4Ì¡)Ž0#RD4?+à75Ã)>„”Ôœ#1=¨ÞäP7ƒQ1°u¢‚„U5 ˆ ’ˆ`ÆÖ¦f[CE£]uÔmé¶~î"çà>#5”YÆÚäP³B¥cÓI¼d048*j¥â JihsT6æä–$“•D‘DÛÓA,‚?A©‘†”ÔŽ[ˆ)²óäÃé«Z„¡?gŸ*òíÝ“hÖ¬¦ºUÍõÍÓ"Ê’­6UΓ#<Ýu‘ˆ¨ ¤®WcqjKß66¢†Ó2žÛ­Ù òë©{ö¯ø fšKI‚#5ŒÍÕ¡Áºô×—’½5ç]iRML¤h ÔÚÊVXJcc¤’M#5k,Ri¶fI‘¶Ë+/]vòí͉gw75tF3u×-¹§nÞòæ¼Þw‘4R¢¥¨f7-ñë½žÌØdÒ "Ù…,¥ÒƒIyqE‘’¬<·š(cFë8¦&Ø j°xN#1Z´ `´E@ì¶¼WNÙŒcnCnéÙ0Ö¢­1Ɔ£G Ã_*«XÔ¦-¶°hÛVÑ'ËÏmG6›J#1À›‘‹MpÌ1C7Zé€iªcMÇJ}"òõo7jÅPHV½mt±E#_ÊËIká·IíĨòÇf°ÅE[m7mºŽ¡W4ãVY  [ÂTDrÔ,‰vHípÛHйb ´ÓcÜ"m¢«jU¬¯‘Љ¶Ç‘¸À¶–2D™— -ÆVz d ¦TælcØ2eˆcC˜[S³~¼¹ÇŒŒ1¼1 ­Øe#t…ÂfH0jqŒØPÝØë*•”w9­5¬i¼õÚ›Z“$_[Qï:¶Í>…o£ÃÑÀ#ÞÂ&ë&Þ”°ð0œòÑV욇ûÝêÕÜô@oø¶¦iM(:uyCLòª>Ñvq>V%’<5fæÖуŽδŒ¬iŠá°#1f1m˜ÞÞªƒ©³Ã¥ˆ0%#5O€È‘õ9Þ’#-àݤA~£Üs­%~³¡ç;*Ë#H:Œìhfzáå¸û;Í\‹mÄk!7@#-½®ígyåa}ì\ç7Xc§‡¯+£ÞàÚF5G·“†”ÈVrÒ²ö³F5ï"®4°xÑ„pÕi*›l¬ßKÜá¾7Æ¡4”â—8zÁ&s•Z„[!T tET‘ÚEJÁjhßB¦&¸5ÃFøDݾüöýÉÑ­‡S“žAcn ăÝ”@Tà…¹¦Q‹LMƒÔIÔžŒ Ìª·lc~I77ß)ßO8a‚ÂèH<ºE@Ð hš(ˆ‚ƒBZeTmtÖ›yuå,¯[T­w­FÓ ÁŽ¢(#1 ŠDi$°ØÁpÃd¹$°”Ûö„˜ŽœŽ¾ÃÖ‘ÛîÛ΂Q¼P®ß§dÒéD¹ÆUO§ì£36߆òƒÓÕ¬8GÍâÄ?l´Ñú>»ZX2SY=ÿ)®™É·umà1Gðôî¼}(ÉÁý#1+&ÒVýŸºçÕ(á׋^xìeã»ñÝÎé\´O£Ð””Q œŸD%'­Å<–Fö÷t‹—­ÊótÕà{Wxîõww†•×fDK*ÉŽ½¼’®.Þ] ¥dóë®§4¶ýÏfîÈu˜ˆI!Ës36äê6oZ®¹ãÔÛ„˜F6qÇ8hPhÂ,CíTBY{ŒPÚ‹PÅ„3s²-£G/K£á®F±UÜ·/:ÄÙÛcy|MA·ʃBz5×i„ƒ·Íô#1aGò$2cò7 ƒƒÐyM⻥¯Ã«;:óMÏ#5òá‚à<öͪëšãŠúŒÃå"ž~”kN,¨‰·#-c®”I GSÕíáš'ÓÄÏ]HñÇ´k†Ï~ úþÏŽ„ÖéM¨#5kf©l#5ÃÆò“l*53ŸP'°=èž·î˜õ²ãë‘¿j¯šÑ¨ÛÒ-a5ÙµÖ¾añ‘fˆÅ.£Ip²(¦­‘bý4ª#1ñ2PÒ\f”Ô¹V‘HÚCCµZä\Õ`+n[¦Õ&µÞ;-£kιtÕÛmÍ»·L‡:¼VŠxf™cR6‘´ÔÕÎ;ªî걪•Fšôµ^5¼›ZMÓjìÚUמoÖ4mmâÚ*½óKhIB­#1/É‹hXŽiBšª•Vì»S¶Å_Zr-ÂÊ Cšd»kvUSkÏ-³ö…³/U€ À#e†‚P•YTH…HNj»^Ö¼&le³Z¼Ý¯]·Å徆Xjˆ¬)†TaD2ÂÅFªS-H¿ÆuRE,ªŠÌS,ÓQTmF¦b£cQµŬ͠Ñb3T–K2‰’DªQDmLɲµSWjíø8«¬…kì¼¶×ïz½WêýË$LùêÛ^zƒIµEa*CFÉeùõ¿Î}þ>ŒÜý |Öõ|{.Üþï*,oŃÀ’?‹D4w,QiœQ²A$‚ Í +ªíà -Ud!XÅ(èD‰î1 9Ý\‚Û‘qA¼®¹ˆlÊÈ„¢áÝÝMt,Þ2Ì&“í}’'ÂåOˆì}<šõM¾q$!èÁcd ƒÜaŸ:ð¢™h³WZ$2£IêÍ6´ðfÃß"¥®¼Lیڦf3Ÿ…–61Œ®æI¦¡1 ƘœjÁ›SMÑÁ&˜fHó‡rnNŽS)Ä”v»Æ#1ÂR¥t—ceö;Þ7B˜²^l2æyâGfÎù6´ì§Á¨Kk@üç†kFÚëæRºáèöÊŠ‘fU§qƒ K&b]ÞÏsÁ2dI$ýCÌ¢HÆ ‹Ú<"ô¹Ë‘ŽÝ³©Øn¼¬‡b‰µM™ÆËgèÆÁû|h¢±áŠäÅX½&eꕵRëRõ„àèNO‰²Å„>Ñ í#1CDNP¢ )¶JÖÆÔj’ÛçOÄÖ®•B2$"[û¹^“|EsŠÉ¤o$öîXµ&Õ±¶£jåk^ßæÛVîëÕÊÓòGqÆî”ý8‘T 1µùٿȨ‡(^ÞÉ{â$ŽÝ-’Úk®Ïn,m¬fU]$ d±’ur`Ì¡C B ”ÆT²&ªœÃ®daÐ €‘s­·«jßY-­³$Æ,›[W4ª/—¶MãlîÛˆžË¼UÚDFÒæ»É×?Ï]3×—jêÒH0‘‚H$à g8ÒðZ8ÊÚÅR“K›»3w%X#D•¶‚!š°«XB€66…*H€D(T(V+¶-Àf‘¤#-‹ ‹å¡ Ÿ„)`Þ°(Œ#5\D#1FèÏ äe³»”KÀ*¶SÄQë%•roŸ;ßÎPÑŒD€¥!yÙîtCŸ¨üäŸf­ ÂÁTçÐÜmi1ž3Ó´bí˜;¸ižýÆùçtš+IHÆ3‰j7ë§¿Ž|4@æt„#-dJÕµ¿5x“#-”J·æ­}Ð\Uq"¤#-7þžàòÓ?¬ùÔC뾦­¦‡ëÛþ/åb^†#t?Õ0~H×®$âp¡ú¿¶yæKMèªDªaê¥e!DXcÍ#5ŽÇ+Ìf¡„zÜ#-fиçC8=g-#5‰ú)›9bñ;}Q¶2tll¡ÀØÿ„š!ˆM#1@d’;5Ù²ï ÞK™z<õve‹¨hOÜÂùÅ4Èn#5 ÞЈ†%Xiƒ„;Œìe”@HiââÕq”Nê²3eÙ£d»ð &èT Ä`‘ņÎW ±Z$oÁD@6.¨¥T<ŠV…F ¶Ò#-"W;XX@Í XŽI’dØ2,ÉU/?ˆȸs+Õ¯Iß<½”vwüi$±o×ÇÇÖ©àÕM’\>PæÂ7dm³zµàIN1ê8w¼ðÛ“OSTßÌý¨GÏž#5û¿½'ãÊ »«–ÖéE‹UT‚©‘ˆu%#1ÚD-t.ÿeÃÔÔ RB„EV@4=!#1S³øb{=­÷­«Ý躮ê¸í[ •#5Œ_˜\²¡û¸Éô1H«"Á h>yåçurèËš×#1(ë»Åä6%9¹—›ŠÍ›T’6ÙijOn³l”X¢™±ƒÆâh0—6år®lnã—^xËɸë®ÙG:+»·H·‹sUã–l·“Ì·W.æ²XÐíÞmSkFGU¹¹k¦Õ’Ú5)b³¹M™d²xÛšwwFéÍ]Ù5ÉÔg8r®Ùs¬¨´hå`JR+P$Fˆ+E ¹²"YTjzà„vA؉Cûb©Û»×À(ðí=pBÄ<à8#-ïSúûÃÂ^•¶‹cif·$­Q­Ö«ÌD°¶"€/¼SÉAN ×üðÑCÏø Ê|S¼pAÀúÊA¸¡å@,T9æû#áç í#5õßi·ákÌ¥hÎ#1ö€z³ñPûŸ#1Éú»ã¨Üvðl#-öÛ)‚6µL̾»z¹U­Û‚5EŸù —nÿš2¬"¡«áóí°$†DŠ# H* b£˜D2"{l/´ =h Ò¡KÉ®[·:Ôã®î¥–eT†…¡b3ÆåÅŸ´*—/rÉ­ôûÈ‚´#-Æ' Æ!HÌȦ‚K{4v‚„bF°I, G$J#1ô&46„ؤ,kÓzV·©mêcm[ÊêÝ´³è×™—ªÛµ®–5S-•ÃS²ð3³0²Ÿ1ÒzaD¿‘˜r…ŽÚT!ŒV)D>GíÿÐöÖÃP7‚uÄÎ.õUW¶"²)3V–l¶³MªmŠÚU›,G‚‚²ú‹¢èö@1c;ÖéSjF–™3Tm¶¡B×è<˜Æ'b;ϧ–HH„š™iS3"oÜʵý#1µI$B0Uˆ` ¸¨ ise©TóšEد®(ÈÂœàŽ·(SAvò<`I6†¥¯;êÇ¡»ž¾ï†íÿ®èŸBþ Œà„ ‚Có’þûþœ¹—€Ž˜n[8Øä»…ÌÑY¯Å9X”)R­d$içy!>ûEÕ§Æ@:º1±E"’N ~.«q`ÃöÀþqоä õ¡¤ý‹Å#1'#ÌÒQã‚ì?Š•Tø!vJ’1¡‚’%•–Z)&*,ÿ„H‘6¢HâD#1…D›Š61#-º:`0–ˆÈ:6–#5`PˆÀŒ#1ºÒM‹[€l¬Ñ!* Jè £oŒf Åi¶T1¡¶f:®ñ^kx£[>Ö«ÅÒ®l•QÈb¢Z(­ˆŒ 4¬·lÒX’$–ÑTÉ8ŠÂ‚«àâOoµáPîÊ«úCI¢ ¦!j€R@)£&§Áw‚ša#Ëzý"´á‹FÚеmÈÛ ·R,¶(S#5°aEè`´’ÞJ®«ÔZ¨­[z¶×.ÜÊ)R’a´ÜýºCfØÜã¤5Ð!ÑWaI¿~÷h\ ÆÍB?¤wõ!Ö‡ØÈƒè&oï=pò_¡V«"¢ êĈ0°¨(V4Ý©4©ë ÄÐ’%-R·Fékv§¤ó󻽇Ҽì†ÂF?#5T„ŒI€©S»Œ9Tí»U-•[4«§.꿨 €å¢2,NÒ@ŒÜIKj`Ù‚Õ ‡eŒHÅV‚h¢Üí^3Ýxf©Ôã 22⿦¡!#5£Jn÷'ç’¬êŽ4µQ%7$¤”Šn =©?.”(ÏÑAYÐÚâ{Èk*Q¸Ò{„º©…L›®dÚ®æ–FuØž—VL“IŽHÓ#5ë1àÔxÔy¶ÜeýÙF×÷Øm‘`*ãrxvpw}¼p>E¨’®òªÛl­7»L8ç5—õÏR]44¡ØCÉ")ž'ڌچ¦x}¨:`¨§ìÐO²;Qº¾r¢é g]‡#1ÛÅDÞ#ùâ,€€I쥦(€GÛ*DD‡ˆA’E-°…G° Q/Û5[t0kMƒá#-qo¹KäúÏ9?šqÁ·]ݨ~ÿÌ“#½;Só„ &ˆ4…ƒS4lý½k÷³ ¹³çÅÕù‚©¨~ð_‡9 èŸf2zîR†¿Û2=·ÄP'ò#1( m±Ñ}]UèuýíÉ÷ –—oEÓ,y†lM åÓgî¬hÎXá†D¡¦TTvÒ!›h7·ü­ œ ”ç3Î Q«aØaqïTh¯n-T)ˆåˆüZð:øxLœ)«S|J‡5Ï+cl]#5 M#-ÄÄT¡ba=ÌÎï«Ìã«·­#5šIÁº^`ÃAgªæ®c›£{:‘ޱÀ¨·0´U>rL#îàŽTæówr^â¡ßЏ\Gð}ðôõü½ÓóL[5–m;p•´´¸·»ç±Z’ë8¯{/åÚÃz4È Á]›N8(&®šZw!d‘ÂÂ^Êô’Ir¾ûÖÔÑZò™ææƒDXkY€i§ “I£Z]ÏŒîÈdšˆ;3ì—ÑãÖç»f:t¶¡N1Xð„JdœÑ¼T^Ù´&#5Ôbbiè {:ÃÎÛø½iæÙ° y/äv3|S{DZ%£xa£°ZyNkäÉã-±¹ùÏ¿m‚œ hâ™®¹üûJì@óÁ·ˆÀŒ&í/,¢þø"kžYÌFŽÌÀê’ÖëÄÉ™}YƒA´>½H=ZÉï½Z§ñ\øì®ze»º^¯×²šÉéÓWÏÙ=#xiѸN°#"‘„hx•*GÚ©FÐ Ó̇#5Xà®}ýG˜öž'“+Ž+ºÔE^w¼GÍG{‹ÚLZ\s„¢AU@ÅztnÓ0%OÖYé®Ä.YÁÔW/fmò/ÈkÄä½Ä|Í©˜›`zRPDÚAm²‘J€_#1æ“#1~°2¡òÒ»áÁFöÚ‚Ò+à C¤=ç¥>¸3AE˜¢€¨–µH"f÷ÜuQÙ¿“¾ØÀFÔÔ™á§7¾Ë¶Öê~¦ýÙË¡÷°ñuHæ¨cäÀ÷5Ƕp5$¬d’kzÕ~¿ªb܃òŒK–ý/ôÚ×#Emä²m°\Aöh øbµ¶«•—5vô»Å•û_˜ŸÃúŨPòCXUTObQg‰Î³œ=»Û#1X;Œ£hõ»(™B#1ìT–Ù#1!cµ;IFšÏ()ªÚ*‰lؽ†(‚¡C×0æwñû5κS¾G3w-*zÆj²Ó‘4óh®öÜÖ•è‰ÛŒõéÎæq›Bœ`»íÛ§Be#1²IÂì9㕯õ+£‡ªYƒ["º=–§N9}⪞‘¦Æ"Ä"Ç@Æ6¾„'™ñáJ)Uã=9íë뛂qëð“yÿX˜=ºû¼¥ÖH3whð*žÿFoóuØ;¾<¬gQÔeìÿ'Ãó艶§Ù(p/Í.Á‰‘~dd@¼T’@À‡Ðb_㘯ùT`Ýò"‚$"$ "@#5ûËUÐv üxsÁRµà}Õê=IÌÍô™F”â°E#1ôqbG |GqÜ}…Ìô›Xf–à0r:_2Û…t=GWq·QñùÜ;7Ÿâ6„äc¸;€åäâ …+YÌð Sj#5ØøM,·Ñ›#5d ùÀà!ì#-Puð¢ïÚRoŸÙ_VøW#1üN'¾>Îj{9£ÒÇœÞôz‚e‘N\òlYŠCYÌeŒ€»ÆHD  }€ö6;ÔTÄE„O "!#-b@0.Æ¡nð¶Æ{78ê$ÑGPèû\fã¡ê/BJ„,›ÊQ7! 8ƒ•Î-ëåÓüOé÷3+)'²Ÿ°¦4^@Q¶%™ê…£iR@0t~ížI’¨#1 ÕHŽæ(€Ôyp7Ÿm!‚/£²â(o/𜕱¼T¿k”¨wN!©/Ê.#5*fmU™×«GNõ=ãÈn3CúH¢‚ÿ}s›ê„a µ5šÄ«¢xE)¸_o$8ý·Iʸ:Û±ŠŽ(Ø\DŽ—}ßï?up}Ä6¦Ç3Š1£Ai#5í†ÏÝ„ˆëC;‰øûW_Çh†€ÙèÕ‚ÂZ¼LJ$îÆ@8ä6ã”,E[ â^sqŒe]¼tŒG¼Ñ+Ø1 Mt H#Œ§ÑŒƒöý{ëzö#ní¬?Dݘæ|">œ¯õýÆBBFÁþ·#5¬U1¸ÎRHæD¾Yu‰Lw&‡[ª(œp`©áß–}kÞáìb5Ù~> ~ô3Ú·ü1†ìœù…oQ¿‡oŸrÏ›tÉÉjÓ½wX˜ˆD I#1rËÃφfJ€1Ò…)Ùj+lCH:aúj1¦ÏÓ—JIÖpðÝ–ï—‚Ø…>”²c:W´ïË1ùQ„dµ"”Ùä®7v¹läþEÖ©4k[-›M5K5‰iJÕ5#-„YÛ¿mï:Õ$ˆ oC¢æ±rðí¹¿×ä}ÌXÝEUõÔ«@È z^ µö}Ý&ÛÿÇÊÖâ*ª2 ‚ÅI$kåÝ™ÝÃeŠ ¦Ñ(šˆ¤Äi˦4J1¦š‘’‰¡HI£"2+Q1RŒœø¼³Øs€C½üD,,U'‡ÒÀúLjqøãÆ}ÛeÁƒcµÒð6™ç››c[9º2iEHÂ4™æãå&iKÇ<ˆ¢i€w‚ù3¦_¿²˜0l.®‘R¦†t¨!dÙr‰#5¯UœhUN–Ò”m%>Ï“øužž, BnÆYÇ— [!tRÁÅqãÝ´‡;ý‚’¨ô'Uì°¿+k k¦“z©ÈCæêŠOMÔûXc¨™&Aá±i˃`Þjí½'Ê©v“O"¦zô‚bl Ìp\5œ·¬éöû*nÌå´ÍÁ—閵û‡ìzçKw9xØ6â—|-ªIñBL¿×þ¾wÉÔ¯™A©4ß7û@ÂÖUbU1’ªVÚiÆG#-4& ÒmŠ´Æ´ †Y“z#1^š/%»ºÍ;®¨ªå×®VÝ®ésuv*éíyÞFm£@¥hÉc1Ax¼BŒ©†2*B7…Ð0¥à(_…@qðR·ÄAx¢"!ôTmЩLHŒmõÕâ¬ÛEA@a»E"F-BJ¸résNës[m»^ß7{uù#á¾#-®u8\3* …f"+îx„£(ÐBØ2Hèw°à“EpÍÄiR©r'âÔN\Æ\1HM3LQu0¯ÚýÅêp³¼B2âm6q鬈f2 aBG"å9=ª’ýî6¸,7¢•Èc6Ä8†ÄJÛÉÀèDx¶>Ñm#I°y²¬b[i›4QG¨à‘“`â‰]Êà,ÆK°´«¦¥#5*(ë•%#Iƒd!X—©·’O=«Ž0i#5¨’hl„AZ?y‡%6дU!†ÓcŽB8 ,e–ì-•£Q0Ž!Ke[”Q•D Á«²¤=8ìPerªÓ¥.AäÂ6ã®6Ö#-!Cl"®- ÖJ²©nD…B¸VW+Ç+#1¦ãM”¡²¥¼ÂUF¡jÇ“#5¬Taƒ`À2¦¥ÐGz=ØæîiP%JEp]°õjh»¸–—-A2°b¯±Ñ©§¼Å¶e™Õ{Ñžø1†[J²YµvM“@UaŒ:`\µ“@µ¨O"ç)JÁV«Vƒ,!,%µØd Lƒ«F­ #1pºSmi<‚ì|$ÝÝwˆEü®ÑÌÎqQ&ÂY`ÊFή&ŒxÊÑ:Øè:âˆÑ#15M2:Y‹"†-¨µÀ=’!býðÚU‰÷|qkED6åH岇ãEªÔÀCœžåd€Œ PB$b1RS‚@K–Ù¥ì¾ù"ó¦‰LÓ×^øÂhNnº}—Å`a¥`°W¡O¶t‰^ 9忢°K”4Ìc;À4ÒACÚGm$(ÿ™™#b.îØŒlkuutÕ·¶ºéBšAl@NéTXÁ%Ê1f#1Û(¬YxR,є֨(Ù#5b@|!úE,¸C« ›šî™rÑŠ#5Y²P†3¹*SPRMuï†h¸kÑüþÓ)XLì)l¡3mMŒK³¼–5ÂTw²—Ûeƽ{•nvBEBLßžªf¤á#5È’µ‘ÀŸÊÞj„ÏêŽè7qŵ†÷©úbúF+ŸCiÔs}Úøè•ë#-J7­@áù?„P|¹#5¤å>ã^&J‡a—à¡Lá 'pÆá“¡%gÄñà&©ˆÑà~]×ÓÉõ4¾JÉу0™SI ë§ÝøÆ»Nu>}›;Aÿó)‰t öv]ý(÷Úvç`@ãÂØüŸ[µ‚.]ÚI'†ÃÅÛyÙ`o£Œ¬QFZ -7ÑÆ±?çâUxÇ1ð¡§„34·7 &’cj • WÔÉÏIÝ{MÁå! ”N£§+ß8BôWHÛ”|`Y5/²#-wNÇeš˜ð({` QDÞÁ7Ö»M•–Û#5@'_ÒXJ§fe¶HÅ<Øvr5;¢tÈ/Ð2`#2A5X¥Èñ;­·šøÞë㋺&>¹¹úYVb4Œ^÷:F²-ÕYb€Îƒ#1ýØ5¦#‡©ÃBµÄÚKõ9®æ·580Úq]ÎøÚæíØhH,‚È¢n›_!ýuzwr½º·$¹mt‹»n+/oãù^4T›ÚrÖKhײÖìKrîímûó[†×Š£lhår·JÛtÚÈ[si*ÜÛ.ë›\¶æÖåNïv¹Iµ(Öñ\¯yÝXÐ~¢-¥àî:¢èZÖ#1£Ãì êˆkù- C˜oÚËM…€û˜ãêÇ̶ †oÜAàE¹¥…$B(pˆ 9Ä=«N)CÞÿ2 ]hÁááÃWOË5ôšÐsxUD²ÑUøB¿H+ŸfÿÅTþ"5MÀ}ù©%¤L#°"šN¬QDª˜.ÇŸ*½)”‰¶ìÒ×qk®î;]&Ӣ׿ü÷¨¨ Ô² 0ÁÀê —` bª6µY+mﵡ[]“b@`:¶+yÍÁ‚¥&JݽÞeM¾¶›mjüMµ,£I¶$Ѱ¬ "ÄxŸX(%ƒDÔ:©ùþ„Ùú"ýTvê(Ūƒ#-ú x¦Ò”"¼‘ÇñË\o’ÖÐ8—š@ è/·&eùÎðÿ|A‰ÃÔnJɺ˜+í©¥CÛ¨fÅïˆ!Ò) ìm¯v»~ÅZí­{e+D²I,¡AcJ$Œ´i545¤¢±b¥()&fZ(Sm´ETVتÑjÑ6•¦Z–L¡²RED$@/ÞcÙkeU½!â½zêðº®h¹éµÅã»k²á†Phcm¨Ì¡”ƒrG’ÔG#5Ì¢ƒ.A€P%‹L Œ™R@Š0 9 £@Æ:E& nU‰° â°+´cRT:"…PTh%ˆL¡˜†d*J`Q@0„#)iT‚DE#- ‹Ø`HܾJ¡ ‚Ô#5XÁ‹>úýüîA~nÚñ*xÒè·MJîéݘÜ:ÓöUÛÐ-Õë»6·WSZ›,HAlb­¡Úª†âèác~¨Àc÷Ø~¹#1¼Õ‡? «Ó>ªÒç–Š)TT“’žå’+È-¦ÏàÐÒަGšw_¸qp )…4vÚ¡ú›©Xúõ††€–K‚Ñ9ïÅLca–¹¹D± Ð<ƒ~`B!ªp~Ÿú˺Ì{L†C%\oe€ »P™„ý˜h=ãÅ;€<#-z0„‘%¨ª*%hw3y+¢¼Ùu*S2…ú—zÖ®T[lV-­¨îÕp½X‰Q‚÷|V‘Oäöíæ éíjÿ+mJE™"jø8m™`#5ÈŸ_ïÓjÎH»8C³³>WV˜¸Ka’ßQø¦Qû1Gš%JÊfÊ”·§³ô3ïXÄ\ÌØ³žõ`UDŠaJ3Þ¿Cô_¡ô•. :µvJÇNøqÉq.«®#|Õ¥³º ŽiFÛ~Q×Ósm’'Ó‚a%²Îô2“ž-GÛ¿Ÿ$ë&§ž=˃|Q»ˆG”°û;ZÝ:PœÐ“?FËßÕïÁ{î¼²q^ÑvL_sßùwO'åä~f€5ø%Fµr,¾†q½Q>iŸ)®¡&¨¨êµÐÑU/ˆ@:§õGo}4¢ØìçÓ~ÖŽ˜¦ãÚµf UK6õ5:8êøÖÆã†IÎÚ£]uŒë®9æ”ìbùUVͺ„yDl>B9¸ŠÒÎ)‚å‡Jt/Ê8`N]•¿Ã¼Ûkñ§¥öº×/Â+/DCœË3й¨ÂbZ™ õ[Þe ŽÐœÔÏ_\(Å6#1ÃP„˜Ç;¢A.™~'!" <å·ÝÙÅÑ3¨wŸO|Ê@i÷Ê»Bq$ëCêÖ–äàÅ,RÊ"§¢ÔT¨naQHéKJGt¼c^5áÈ–œ™Ç3)oC²¦ÜYMùð1»¶O0ØUÄÍ‚Òr@.È1Ÿ×zRG#5$pØ¢4]eA¼Úwí±A.Ì9¯! #5l~Bë(%®7.F’6¥ã4¶<ž˜ç»ëI§ûg-Rí„ã‰:„8Œõï«Óp7Ed%¬v;ÉRð·œè‘ZUW,É4;#5»Ï³Ø¹Åb\m6zÇJ<s﮽zgºHE&¢~Uó¶l=GÓŠ-u,õ—ã'@7Ðèpn††PC1[-ü¸Ï…ž%͵´äˆ¦n熱üzðâç^ uCl«wòÕÛFb³/K0ͪ}òµÞCE+·u´IÙ¶ž8s<ïþ·­nûb_w8Üí É—’I9xÌÔoSI­>%é+횎øÄK!ÒëÝÆu!Ë!mi9žè»Ó™ÓÉàDdåØzœðÛñ9«÷a@ê­_’“#5^âQ7ëT˜¾5ë)’ºZ¤ÏÊìŸXKX¬] b²2tY%Ê×¥0*«H”So{@8*ªŒÙXVí…Ív­Lè’öºåqÊÕº›F²&âé1é|ï¾·â«^Q{)ãm?·líÛc†é®j¯wŽêªëÅùg–Ž"6ôyª.¸»ß®®F_žmèÖù¡€ÕÝó³ü.}%Û\¾%ßíf!n5¤eìÃ{mkÏg¿Éé,OÁÚn8ˆ¹Üdh@u°€Öö~mëØ»5¬Æx]@V(”`ÚLR"ö3Åœq¹G–0„l–1·ZqFXÎ#tÉR‰ï03Ø]եႆÚ0½²¹dvÃ1Ô–ös§ÍlÄwà6–vE¹"!¥³íQ›«Ût #-”ô%ýS$éæø×BÙ½r>yEã¿| #-âû’~PÛKèÔ#547·0ªÜ-À]8äeqÁ†ö»]y¥áÓša¶Ëkˆsv5“w#¢†T˜î7¤xPbhººØÅŒ={¸/zt1ŒQ®”!Ðë]¬-æ”*£sD¦»yf„ù<ŒÃ•×– k.è]wbA£,´ <ð³60¥û½ÛÈ…nq°ƒ)S@çÈì¯F»-]ƒË­'^"a’y'y,¼f™ñ (ñÀ&{n‰ÃY~;‡<÷XÜí+e›'%BŒ™¢;öd³ -Ù˜X,ÀÆÈ,"¢ˆB Ú‘Þs‡_-T!Anœk·wnYg¥Œì.ê:³ˆ-æ¤Fœ‘ÏŒÜeN­»#1ˆè#-݇J@HBÇ¢\.iœÆíÚ&VT! [K¨—3n¨À½&˘sN1Ù8Âʪ×Ð-¨&È<â¹”“ƒ#5Ä>^{Ϥ𠿵S·Ï™ÝÉ¡óÎþÐðbÜô<9 Æ»Ýgg Ý<Ò óª9DüL¦r «Ò•Daô÷‚©ÀòÛ.“ ãÛ-ž3é\ˤèËyÏYáA¬Ç3ÕÕ?<ÌtUo¥G³Ó‚«ñ=hEU)´¢^"\ŒñŽÓafo[s‰²áØl¶ÖÕÚ(^÷ìŽHsjêóµ#1]ö"Îýf2’bq>[‹ÜY¢1}Òïk<˜¢¿l5¿.Ýp1Ùšá‚1T›4q“¹Ö(…Ú7¾Y¼¯žüKCÞG2ÜÔÌo!:Âæ0ÙºÞÝCë¡ß’šÖšÓ¶ŒŒQ$jˆf9ñÌ©xÙ¶%x§Ûª‹U*8z3<ïı(®Äm§¬Ö¢ZdwV¶EË–!Äéš/ˆÞÍ4,kîv¢fˆƒCâ(ŽÇ9hÏÑ÷œ—žý¸+ɹ°íí××v¸L5q·N8îfEä¯ÏÊH>æ1•×°^RòG]»fÐÚW¹»à]zÔ·ãnhìhá@çRÿ|ÕûÈèçžÝ¡fCËw<­Îòæöüv}^¡ƒÂtmÅw—KÝÌëO¬7sF)O™¶1ÙUIv鿤w»ŸGlÁÖâó‘ß±7”â=áîç×К¾¬ß‹IÜfºrÐÔ"+«K*%4Êì*ƒ»¿%†Ú±)`…ëiF#-3êçiàd»Ìa­Òwz4/D[:ßЖ›Õ=¸¼ç#5€Ó¸=g ÁDX\ŽáІ„#5­zd4T[º<ß'“vì`ûÿ v°#¼ÁÆ[ÉúÀ`XxÁÎb(Q¾µãm?\M`ÓËaÚ)¶  RyŒ×ŸF#5™1‰®K—ƒ¡‘wy´(aK›2à8{¶GDy¤#1#59»šà˜Ü¡~å°X³Çc”$%3qéÚ¶U#UtJ††¼yÓÙÄÒ1{)V“PÜñ… -TΕ̠å\;€¬â “4#1©×$ì.Úg$$™ä›.ÄNÄæq5Mˆr£#-ä,­ðs#!Aáê'Êc޼õ¶·ÛÊÈéarõ5ž+úÊuö½™‡‘‘KŠ|â!…ˆë.ÝýÀÈ1©I6Žð®%®#-d#-rÄn,uÊU‹Û;›àtPë÷úêW¾ºÿ£è5­Užÿä¾½$؆"^Z_#-÷Šw–ÁÍ úçЗ–MR­—_ò²kßáXß{grzŸ@NÏ(Yàá5Äù•Š"ȬŒ>m*c#!"¾æ>é%” µV«œm•ñ’]4+#‘\æ5.*¡I‘¤…X¥ÜAL¤cT! ÆÀƒDȧ˜Öô¶¯&-\ÛÅäÕÍX¼\RÙ ª¡l—Š)h PPé1´#5’š›hµbÖÞ–Ñm͵£˜A¶TÏM™H²"‚Ö"ºA·¢ÅFPTcJ*"‰…EJ”‡¹ó÷^sÛœoàsß©_]Š¥#5ÉÀcÚ3»Â0ª×gŠH3}›â!~mÇ#1zW¼r.M´«k_¡V6¶kW#-Ø$F#12×ô·#Y¿§:ˆª«x>èf¾Ë¬n䥲Ôè([ü}â¬DELJ-n%°ÎÎÚÊdÀê´i»kœa›­é#1?Î:6bÑ"»#50eq2!â‰íêÀœëN4bJ³¹ßXêà`@¥,×|Ç“A82ª20ƒU8 ¤Z™TE|¸ÓÔŽñ—tÞ5ŒÁ¤KÈ‘`B­&n­1ƒn9²Q5M¦ÒƤŠXKºŠÆÄÓLh† \´T{”þ·˜Î§Âu‹wL)$…Y/O¶[î=¦ÓTtÈ”mƒbÒÁÀá Qá‰6¤Üº¦j•Æ&âTc±ºö2ôÔ¦™Z:ñÎÃ,ÝÚ|æ3s(ÓD$7kÛZÖ©nÒ˜ÕÀÃ$¶6e±äÙÆ.3VÊß 0#1± ŒX.2!08eCb,Ó4#5¬Xä¼XÚ^ Ee ºÐ ­¶&s-~7[âîs¬ë]£L…d3Fõ«m8lT†¨,‚$‚8°saPBPÊ"4Ò1aÒð²›D™¼q± TC m¸ÈBÔ‰x#1+˜¯s­ó#1+&JVa%Þ¶aâ™u(˜Á`i»³t‡fuðαZç H“°N'a§-õÔqë£ã†•&pÈÇÎ[äÂ'!¡àgî¸Ä÷®šzÒljq#-¼í•ÞXKºˆoHÒi›: •Ž5„[FàeUAR…Xd7š4†#1¦#1¦&¡qqhÛÍÅ·“%–WoNvµ°›—O2+D6EJŠ5ÕâcÄU­’®PÖˆÂ(à £T&áÑR”ØCº+š€BIƒbœlµþÙ¢ðà•¬½ÍˆòÞа¨iSq—Ûò‘¶ØÇd™IxTo‰]é“#5šMƒÓU! y0‰Ql.œt„—e¬Ë‰¡¡‘¶œ«.ráRÅGB´Úް;Œ:ье¢r3ÁŠy虜ÛTDÍËŒ§zF±V=¼§Nø®8ãÃFîÊVu»m¶Ýb æˆ[åv§öïEÐV£é^f>Œ 2 @£cäZ0‘ÖëR˜(¹ïm=XÁ¬9,¦9i¡ãH>÷4HS‡¦Þ,-™J`qÄ.ɦ±™Ý6ÒÑpV«ƒVB1ØÓ<ÂLŠœúÒÖŽƒÝ5#5êdHÔ4pɪ$bˆYžj‚¹™Xœ[›f´E·ZЈŒ X,¢OŠg'B‡G…&0QD.ÒÅ`ßmf!¬³ ]Œuƒ:ðaÆr>aJ,Þ…ªœ±‹a𻼑W·<ªªglÈ|N÷/Oºgò5R„U† ‚ÑÚ\»¨l`Äã¯nx¦œùžý¿Æ!L÷¡K#ƒ!êPùŒG°ÜnlbHtJZJ‰RA=k·ë7‡ðÕPÙÊyÚ£¹ˆC¼TîU:UŒØÖ­À4†*SmÙü§ƒ U+ {½E*û˜SÔÓ^#1ß/¬‘«ØµL¯„Ùw’|^Щˆ§[>‘#1ôßßA Cu#ˆ6"í}½Þ@ý»¬gHq2;GŸ«Ë×Užž/ždJpšÛ×'iÍΠÍÂÁú<¿ßQ®ÌU¤¡KU+ƒË1æ}f÷=6àcxHÆÐÔƒÝÉ„EüÝääîÝ›7wL·†O|a%ƒ–Òº©är^G3Î'¦.zÀèyÄÏ*!ô=)ÛHQR}bP¬šiÉÐvTUÌziE e4ÐÖ!eHŸ£'g±0jwÑ¡;G³|ôP-GÄ%MÏP#5­¥z˜ÖÝš²RUÚ¦S`C)Ä&Ð ƒU¤R#-„ŠÅ#1pÿ㡬ÓÐ#¤Ïaùø•o‡m»ç—Ä¿žþßoíHÑb#U5CLµ¶ci4bchlÊ©•bšm²ZMZÅ&Š¢JÉ£BŠlÆ-Ä3hSMHÒ“d”bšRBJˆÐÆ›Dj%#(±’Í)²)ªhÃi&a†Ì"R„“µ‚ €þצӖžeÏdÍ@{3>YØê†BŸ´e×`øöÎætÞ~i º×¦æ‚|¸¿SÍÌÙú^d‘œ}"ˆq³¶l˜•Ì­ÞÚ)'|º¶HZ‘ÅKëÅ#-*µ >Ó†ì¸Þœ¹I&†ck/vÛ†èÆd¤†É©µn|µ>¦»7?R]ø¢ƒXÉlݼ¥È8›ÞÓobszÈ™ÆÌOO Ýy$º‡G]—¿ øú¼ôŽÎǃÕß’î™Ý â·P^ïÎæWVý66±K]Žam;DËý€{ÈÁ(#-±#-Œ"‰5„“ZM¶4_µ®š´m65)&¢(¢4VÄeöÝ[™[*——j­Ôµ,%‘d@#wæ4ÐðÊ$Q “YC$(ûýº‹flS²œû ƒ`Ïxm(TGaE¨i˜Œ7? º5Xу‹ìxÔ3¦#Lb™$r“H½Ë§wd³EqìµÞºù-^6ÚáHiVÀ0´˜‘€^|’[Ž›ÝyÑÛ„»{yÕæ[»¨Êr™W´¯)7‹¦‰¬çQŠ;~T9fŒ‚Ž"è¸<1„ÅÙÞ!”hE`¡GLŒ‰‚lÆnxøYËmu™>4'Ðá‘`rgµä‚ÝL\#1’Õ"PBtÄ…3Z ŒÅ¹k”“17-q&û{kS8Qý#1‚ébOÆ´Û=#5ãJ¤•r©–%B›E™ärˆ&IL£ZP2&%‚P‡2ª–hÂä`@UhÖ‘4† A°I.#‘o6II$ŽT:1o—üÐ<Ó”®9·´ô¬$Q#5õ÷6q}Oá—^^Ä+õþ‡Ãw4ëœgñ%DôÊ3®ô=dHFBÿ~/íFF⳸ÕϪZî­G¡¹õœ\Š1Ö2 UC5ð+a9_# *á S´Û æT‚Ô• &HÄß•(Z©Ž~€Ï?Qù#-9Ï”Û42b±wƒm(´ÿgvù÷MW•šŒ*жš!ëîóôšI #5%§Ñ£ó]ÄÙ‡ÄÅ!•Ž8¡«%•ÅÅÎv`Ä„†º¹¤‰g’Št&† ©ñ‚éÔ¸®hó´ækhEöûuƒX~aëä»I¶zÓšs\”ëé>)E0e={ÞÁE€0ƒ'°ðâq×§`úï»Ñu9}ËÎ06€î5u¶ƒ>¢«©J2„Y⑌Œ!„VjzŽÂm„ò,ÔqÆÀ¤Óygœàð/°2B I£<Ã3×Ô*ñÆ+¼±©¯#5°óÄh8a5hZ£õ}¡ºQSçéÏð2OdLâÛÂ>ÿq×€ÉAB‚ETÁ§Ìò÷Õ(î&%ù¿V׉@öv£²åX¹^³ì˜sá}/œV\])c\“ã5Ïc¯5,!UÓEœ?³óÄhâCƒÖêà‡Díçzv`Ö!™3§Ò“ÀÈ(à¬eñ=Í<éõyñÓé‘õû¬O£@±-ò{_lª¤Â—ù‰E]%ÇžvR•¾±0²=sDU??œîVsJVU¥Nè´9$òíj6ök%ˆÇe}(k#ÑV;;d#1z¢ÁmwÃ>,ØD¥;‚DU]0?LNUçƒW¾éA0llÃUÂ^¤J*)VAn—½D”4 ¯ÀÊÍ¢,‚_ê‚¢s|œ¨»ro&šà53œ°cq€„e5!É9Æ\€!8Èá’Æ¥+´¢h+…ràߥðDÒ#5M+JoÎÈÍ6Qž7Ô°ÍK7 Ú™ƒI›83§Lº˜Ù¶éÏCb†Û­ºÛNĔމ¥¤m•o¥–rNI#1ØxØQ¯ \B(ІÔÒ l(4;ÀÃp BÊâɃa¨t† ()ˆS­bšŒh6g %DòaƒvF½\Z£…o‰MâP°CŒéй¶ô$1‰‰¥BÞ¼öߦŽÜPºë@ŽŽ5¦lvÞX@z‡H³5¨Ð±±KM° ^ Á”k{Ÿ?þ—SÈ8߯UЧ;àØ‹èo,Ï×7Ð`ÀR7{‚‘L„ÈÀg&ðS¨¦ªi¨‹‰”ÂrL¼.ë¡(L†ÛŒÏéþÙ:[ïÒ%²ú§™]±Á2™œOÛ©dË zknâ»1‚}{†üÁÈ]'Úº%³[$R—…b ˜¶ÛÙpÁ»~h^(fç3±e$(ÅxêK¤©Š Aj9E,1R#5É€È#5 žÊEÄ\Ý™Ž®æhÕ×¥2Ò¶3Ûu†jVcÆýº¯7nÖÒ§ ÞcÞ®ÞN++àÓ2„™r8:1•΂¾ý»9Bó0\²7Û0\çÕ½Æ\VVñ„ö¼z6?#1ð>ž§(— ¾ü:fÓ½sDky¬u6§3„í#áBÁHˆ"XN„3B¶TÙ€!Áê’8Šœ;ŒöL4î&ì¶NvÚX*UÐäLP˜®š‰1–”•¾cvF÷2f\±ÖzíƒËgÊsK¡1#1Ã#c·”Üãj#1“R|òãA j¶#ggÊvj%ÍhÕÓK¹I1LæZ)”Ó©ÅId2gjreÓÞ¬ºêø…¶Ó§–²æPè¤aʹ/L.Ó‹}äãe"±ê9ìZõßÆÈ·X8íµ¹¾&†ªwŒˆyk‹&Y#eëC¸¨ÄÌU#-¨‘áåõ˜¢´ñr"oùMQõxÁV¹fç¾ãÌÖûily¯ IÄÒ9݉Ã]7’}qʼ\ɸšDô;µ"×'RËnð…—+•…YªÚ움*fA¤Þœh’’ŠŽ,€^”Ì×Ã5QÑoȵµæ#5Íø§%nÎÄÀWGìïp=ï]s”r*éÎ…Òii+BFìÄ·W'@æô47±Òy%64è•..“¶“°‡0E(Xw0t8 :ÞÐtæc,iÅNÅ5ÞÓ!üœZ0à‡YDz†hål­®Ø8PˆHœ’j¤- %ŒªÁ/¤bk®ÐÎÓV¢¢’„*0Hïʯ#1NZØÅ¹º†¡\ Ëî1­>$%ä€j®² 9ó¹ŒA­³«®U%ÀVé˜ÙÀØ#1LQ½Æ&5ÀÂ>TåÙ@¹Àðwïù®yÓ;iùf6jž·T5ælž¦˜àôQ& džšŒ}#1í64M®)¶KŒ`ì&”Íe:·Ø7ÉËa΢ì:fÙ¢OÙ—á(‡¶Ë™ptÙ¹#1kóÄÓIúýLœt­«Ñ }É™)vvÏ«·~în¼uŸ5‡™Ê‘5æw*צn›hÓ±Fe.ùÕ¦)ƒêùr­GÌMû†pòLœ¯èÃ…l‚öp›h7›ð, –VJMjfÁ‰CX|²ÜU‚k}óf°¦!„!uÀÙŒ ¦j>]wFƒ«Q0Qˆ€Û¬v)ÀÍ#5Òy½h6–×É4DÛ:x‡¸÷ô½µY ’{™nÎÝ«AeáåʘP—4 IˆZF6ã‰#12ÎÙ+UsðÎ †¤"2…‰% %!†;ùi©¯Q—£C©’ªA¢3kY ¦Í›£šE[_P*â w:­‘œS±•0‡ONì›#-¯)Â-ä·Ér:rsI—DW.R9vZ†€4˜LþQÛ§h¼ £;ܱRÎ$‚zãbpÒl.“ò„ÃíÙ4Ó}òXcˆ§ë¨MFœ @îÕ2&)m—jÌ5òθòÌÍh—5Õ1òà øHòT^Tk…bT  ŠäÝK¥À(–L¨:ÑÄÄÌü²G#12H ÛQB${˜fU4"ð £#1$\U(jšÉ©ß'¿vÊFïˆq!ÑÒÝŒ¦Ã1YÚ`¢ÇŽ6‹AŽÙKµÍ8BÌÝ™ƒhà6”¶""!àjK¹°»ŠÕ4&³½È&xÆ&#-F§Q%2v<ÑHŠê%Fl!†)‚êñB“YfMJ€Î&á€Ö€๖*¢P ƒK#5¦æÃA™*B!R" 9L\v…Lªã¢Ù v£(в ØŠ)ÙÎÔ:ƒäfÅÁEPS±h#Mèˆ( :%#1vðU‘’Ë¢ì©suSc!†Äx‚ Á‚êM:”Ò)Ý<-alAÔ*Œ#1v#5ÒL©€ÚT\ 9ňÄÁakV)ÀJÉAaF†ºéÅâ´ê`ŒŽâ n#¬ÛQH›AJd ðÈX\a;a9$‘Hébebd4Ü0¬R)"#5²}„òóë>ƒÆÔ£U@…TDX kÓG‡8A›Ã–Ç¿É~‚Xdøó"C¸5XD„€›T*¡ÐŠþ<+ѲÖ@O3¬çë¬[ÑErÈ[¶iYXÅ8,¿gyXÕpû#1/%¡Z´@ŸI3›†Sržó¶›½ö¥F«á„Ä×p‹muÒü6GTàäG†¯ Ú!Ž;j¢Ü¹$%ÈdGÁ(ú·}=Ý` mÐÛW%U¶ÔEýNÎ]Ê"W¾‘YŠõ¥;^Md0’š#5*Ù Œ:ÚÃ]¬Ñ¿†ö—“€ðiUÂTIݵ ï.T‰Ã•ÀÊ#5 “ÕVŠ­Ð}ÖX—ëD‹ÓD~´‹é"i“'‡'{¶?És,:îÞ8'ækJì#œæ*fˆ,Ÿí„M´Ø.áSeôÕU­ÐW‘ÈÛxÃnhù=îyYÚÍg-1±´›Õ»º¾Éu´Q}€{+üÐ}€‘Q÷Q—/Èúê9åÂDʳ:_]±;ÐøAA°6+ï‚îºðåÐùi ¿%ƒ¤$u”Jª“ªP`Æ¥#55Q¡( oàr““Fnã,ɼÆNÕ+F Dm,D`󜻵¼ÒCi2…¼MNóºbªD6Òk$ „Ëc¤G! d… È;0Á$,@4Њ‚jVŒAhpÒ²ÍI˜#-ô&<’YA¤:énbª&±»Ià%¢j0Åk‘™š:ªÌ™i(B „£„ôövÎçáßæöC'†»7‹H}@T9@€YÙô´Æ^аéu­L} RjÓkcF“ÑMbt†9˜k3"!¡F†kzµ1 º4W‡¸5ˆÖÅ„#…ÂTQ'W(7”'« Mªs9odAØoi¤zFßTóõÔéïnÎ}Ñ£ª¢hʪzÔÅ•]¸Rß>ºÀ~Ð)”ðê+Añ hÏDÑË7ÈÈ‚[²Ã*œ—œ›œqÓ]ˆ¡Ó›GAög)•[b–b ÌGÅÄ·Hf~ ¨fM¹ÆMܰÜ,@9 ‰ÅêDSáìz”S«-»°y®äS³#5ä†Ó•.Žk¾¬,å#<þú&n¡¦ÅÀ5ÕßáøpÓyº±db±'±(ú#1§3Ǧ·SZ43­vžP>ä¡A~hXØ[_Äj®ÛQIEi5–ÚÒiH’Ú›ðõ&Ù+l€’ 8 Œ"·`Œ¸Ò(…ȉiQö1e¢’ Ù ­Â+†#-‘",‰±Uå#-¨ =þ“9‘E#Hºd¯#-{PÐÛå•nð5yt°v°­òtœ„HD`²’BGeïçåò}ÖûTÕU‹òÃŒÐXb"¦Ïž7nùrÐ2´òå]PãG¢úú)Éß¿poT¢ñœ¶€{GŠð;GävgS¨ú#5—1F/–NEJZ!(¡ÂpD #1̳‰´p –$›#5Ér•¢‰"–!޲¡ÅHVꉉÈ1(äVÀ#56Â^»Ï-·0õVÉuÕI^e^+A M#5#Qd’JŠJäaÃ[¨e™ž¶Ì$#5JŽÌd¦a «½K @›r)€æikO¼;ù@ìz¾AÔ–  üš]?Ê}UC@Ög$ûx“[å½(DPZ=”!ÿ)ÙC«“zàflíïÿF–9ñ8Q¹ê(«"Œˆ…ÝyòÐ: zêIwС>ê©gШ³} {˜F¯:¶ÅDøí¹­ÔÑ2öm®›ßwLÞÕ!»UV§tØb#-¸#-qÜNBbFR¿îòáÕ×áÏá0%ißOßàg<1 È$:J%Ï×!FmT´(Ô“JŠ… E¨ÁÖZÁ¨W`®ËÑ#-±Ä¡ˆ€R@^567‚ÈœG³išæÛZ±Ãm0¼#¬CÊeAÖz´{»à>ÔnjµÛyNÕµöÞYZP’¥J%(pætß"cã!0.4–~ûí=CGÃrF+aäŠü®ÞHÄ–ÚJfÙ5$ÙfÙŒVI”U±‹Y‰XM‰*kLV´–¯ã,mWËQ$I‹¡Üï7Á6Ï$õËB.Z¹dæ†ÎíˆØx(‡ ðê›üæ®c¢Þ}#-ènõöu¢®~bv®{¹àk#-í‚HŒŠ¹³ê Y}y+ã˨!Û±ßÉ=È=ÀûÏ…ÅS"#-Í—‹>9ìê„’G_¯‡(–¶ÍžƸgÝ'«ðËÒIK‹®,š¥4¹¿º[Ezêrl¥-´<ÕŒaÙÜI"‘ãÏn€a¨žé§†âƒ$Dq«)\»’ÄùÒ‰²ƒW *㈂45iÖ‘«»2PÝ5Æò$nI4l·mµqÃi×Ý1ÂH#1­ ¤ÐšAΡ¶rbŠ¥|8WÜea[£'&ï32£8o†–Q¹d#-PYl±’ÚVXCœ fLË­#1ÜÕàïõEN/ˆ¡ë#-€Á¿ -‹F•¨>)•‘dÝŠh7ùU­ ,ŪÖKî,5²c,d_RZl}ؼˆ¤2^ŸàÏP·D#5+šÏ¤ @wý )uÖˆ²$ALxIf¬;äêêÅðtb]2¼j'@Ú'D‘ƒŒz’ˆƒš#ÕÒ’ß#ÇDùzJQ#5óæpõмBÄi‚HT¡Úx¨PÖ¦¡‚Ÿ?çíýÙC@iD NäÀâ€!”°×}–Ûü¿K^؆S§`5)7;Œñ±ƒùxßRcê1/("Y3ËDAä¢ÞÞÏ#5nÀõ*È—&l€ð¢‚'o?¯0¼Owó·~àEÉë%F™ù´#jpµNc0@ …%›5ôï¡£­ÜÀ3_X[óT<¸Ð¯ÕÚDQÌvEnoé`èžÛÕ†ßòý6Õ•y¾º0`HdÆdxº‹hÒeÝ]&i”ì¿WunUÍÍÏ<Þ{úûÞq¶pÙ2Šl–Åõ7s]¼ÆÉQ´m¼nk–âÛ»¯óÊí+IÊÛy„ô‰BSI#!#1UOÖ‘M¢EÜe®ß¯âxxÊ(hªù•ó÷Xò0ÞÌ=aàIYˆàžãoÃ!3S¾@;¼ƒÀðŠE#-E@H0*K¨øçíE û ›#5Š”¢ÉY¤¶lÚ½~ˆÔm÷õ_ÆßY¤¡E!†-MFѪ6ši”j·åµ;bJ^Öñ­ãS(¢¦_t%#[õŒr. £âElm}KYõdû'èQWÄ·­7uÍ‘MP­›hÅF6db±•Š™E²ÁI%ëÏ*µˆˆ@"„¢uÒh EÞ£B·D×ýþ_²jôî'¹ô}˜vdI<ð&ÍÔ(ÒzIî=Ÿos »XŸ¿ñãKŒ}3axvˆø !àÙ€â!œæ‚åœÈL¢'¤W!¿¶S+Æß¦÷°Xˆ¥@UŠNm=Éò*¼?›63ØÐ Y5Ày¤ÊcŒ…@ ¾XŒY`7ЇRsïáòÀTŽ)dFú†×±h&FZ.…ÀhLÐN±÷«D ÷ˆ…©N¦ñõn®+RZwn’Ú¦Ú‘"ŠRS#5‰A“M“é ¨ 876Ý»«²Jæ_:æÄGúv’B(”+â\o*SçieY‡º‹Ä²í6e³yÇÆ¨ŸS=ÞYܤõgeÑ«Ú`Î [–³0q1ÜKEÔã¡lìÍKGXÛi~4')¯ÌƇ›Úm.mÎÛW¤@·ÊòXöQïW#{xA×­=÷Ìÿ‚—..–#ä\ÌÑO Ô.–!Aù éÿ¿Þ€”j=/Øý9^dÑlRšbT›#QM¼UÌl˜5j’åUÅ6Šý…WçZñ, ÑZ=76’Ì ŠT¶ümúÿ}ø|9¶2Q–ˆH­µ™CÒ#1ʺÉ.o€±„¢¤iTbdÂÑ46î(Ø@JŽÃÎ#5O?ÜÌ8.í³7ÝÕ[Íuµ¿Á©¶¯Z±¶£°V”5F„ÖÊ2ÄZƤ$bEA£¸Ù~MžØôÖÒ¤’6‚¬ÏÏñÃñ4`eˆúã RâQ¥%ÞÔ`ØzŽñ%ÓáM.ŸqoÕ„™ci6Veuþ.ø%–ÈÙÃ3"M‘& ÒÉx‰9ivÁAÁþ¬˜Š:×Å-Ú+»n›Ç[æ·lÒjh¥KzÍ…vÓ»5wu+ƼÞVº¦Ñ¤Öô«’¢Þnë3vfUuÍÛQW;PIl‹6¯.ìi¢Vîí»»ZM•%LŒ`Òm#5È#5¡¢ÀŠÂD•8)V¯S«ËµÐÜ··­É½M©„^Í{yqÔ¦–ÊjL²¯J×#j#5 ZöU¼#1V ˆÉ#DŠ#1‰‰µbB… Æ(ÄEŠtfǃ:&€Ém„Ü@t‘ÒÀMš Ÿ¸ipŠdj‘`²lQ{ѶEF*‚d‚ÈÇ#-ª@HSᒀƶ~ô…93E(~ 2Æ0’@³kkƒ2Ï#<(nŠhT#5U6¤7ÕJîØL̓O.,È„{@#1Ÿãrþ €˜’."+Û>š/—#-ÇѤ<Æ1qBÀ®šüfDÈm™½Œá¸L©M¸Cª5#1縗|#1È·Çðý¦F$Ç©CUR§Ká4®DŒ5ÝÈR&!{ýJi]hdL~¬ùÁAGÊ)ļ.#1õêLoÕ~«ÈÐ’àñÐÑ#¨Ój÷øs—®¶[½ïÖX‹)%ý‡FcÂýåëXètúIYw]*O]olr.ä#5— PyаÅ!®*F<¾ ã=2jðxôо- ö Ej±†…šW¬ÚßA„8€ý°i±Üã§‘EMø8§&Í wg'“ ² !,Ë™§zÕ™ ¦ÉîG|ëúyàÅÝ£°3¨´Â6ô¢¥Šjv!ä¦e.†GÍ;DÚˆ:Û»dÀDø&‡BYžïl#5ã^däX½õtÂà1dLP"4Ñ]hi$JƒDM‚Qb¸"~ÆÛ´åPy@‡’qð@sÎDÑÀ⬚—?m@ÌaZ …ƒÏÑÚÎtÆcù|ÜÁktã%Šª” 6y@XU ªOH¢9Ô ;’#€ÌÑ€/øÐ%Äq´3ñísÌ©K –”wÙa1t(9Æq„h³÷2\µ†´vNÅ|êÖÞM¨7…J?¢–aº*v$º„qx‘·LÑaWNõAÏm#5Æ4âj–Ábd†t¶6'î8sAŒ˜qÚ"1Nç!ª{¾ôÒ#1‰¥!ÈV€Í2: £Í(ÊŒú_Ôº!¡yêÔ°ê(†qð58oä¶Ôü籄3ñjÎÁ!#àj_p¸zŽÃö Â+‘ðÐï=¸×*½·¸ûãßÙÃô‡ãð¶ži¦ÃdOÂò.BŠOÃKB"Ý?£%²i•½tŠŽQ³P9*!ݨþ"‰Eq¿ošd¢*ÚÁà|d|¯À1õ»÷ì`ÙekRå[’À^K#- T&²Àh?èÙÿÃú|?/þŸþoÿ¿þíÿŸÿý?ëÿåo÷y{?ßÿþŸ÷ÿ¿þú~}›>Ÿ—þ¬þ?gýSÿ³úú¿ëÿ‡ý]?ðÿ·èËþïÿ‡û¿¯ýßü¿»ýßñîÿÓþÿüßÉÿ„|?ð‡üãøz¾aÿŠÿÇÿ÷Ùæûcö*õ¨ûY?™Añøª ‰H~gK¨™Oò#€É«÷ž@#5ƒ¸ˆ¸pi.x¿Ô)ˆ®EÀÿ@ËC ªZ¿PB±·½êü+yëø÷ô³ùÎîÑ Š¨Œcpœ$ˆÈHþ¾2ºœJt!–N‘KnÈþ»©Êî#¯´Æ•ÙfLòºƒ‡ø=ß˧ ëÄ2ƒDûð$ŽN(!-T’H¾V¤!¡~ºp¸¦c僗ì 54EŠC‡)ÍÀqš…Á$ DìD¡æ`J/×fʆ$€"F#5BŽ]ã)O‡†k”%@#†#1¡q¥ôŒ>ÿÃ.#38Fÿûâòåòpê°aÜc•Û$EˆÁBLrÖ•#1ÌC~†ÂÞ+`!F¢B*$$LT‰¨”5ØÖ³Je¶‹R›dª2šH˜Ð¢2+5õݵuª¯Ž9ÁòÑõAÎþÂ!þè~º©j*Þ`°DAKbÉÐöJ.%5#-dŠ(}áèèýGüÒ0Òe#->É;|íu>n[ Øÿ³LçÜ?KY¯Mʪb1F?H•sôêèëÿTÞM±#5ÑuÒy¢Ê”@uîÒî Ê%½¿øïì¦ÙõŒÎÞØ‚ô„g,r.;@}r“µ©½ß#5o”-v€#5¢”ß1¸¬ÊäÚ)öwo·´Âmuu‘–Q‡}pé¤lÔpF[ˆO•®sVpÐÞÑqIâJò5–¾šo_°ÙmJS{Lmõëåßkj4Uòk}MÙc^5¤‚Ûð­ÌTQ±ð¶æª1¼[\­éF¯e«y,V¤±X6ƒi€£B#15[âÔÕ Ývu ÐâáÉnEÙ’CCH}ÈQõ „ßÊÊœ¤‚à#1æN¡D×Wu‹€à»uAS ƒd^®ýÆó=Ã6ú)zɃù´´oJ ëÿ´x‡ªýÑUãA²H²#&ñƒ˜¥¿—íéáö²N¯R;ÑÕ÷sÏ™Fÿ3#-¡ä¡é_£b)ÿš+ +*;Èï;õR‰(R¢Ä„#-c%T)ˆø2b¢vˆ'¨„"’ @€·Šf»áòÐôýˆúûRnÈ"@:ø|Pø¾ÏpÑôý‹Ó2ùYiû^®Ð.!x-]X§¦'ÎXƒ1Iå÷þAú8þ_ô‚¡]@‘rŸö7þx9M¸Ýí‚„€,QdÿÝå%µÿhÛÓNŸî*“ÿþ Š=Áú’ÊÊäÿ¥±ÿÜôþÚïâmÒ,ñ¹h?ýx?³þ¿Ÿÿ‹´ù×áá»#1<4''-9¸‡¦Qå{ÇvT)ñy\žðŸÿ}jO)d¿ÑêópåIá!§‚r¯üÖ§“˜lÿ¦ŒùÈb(_üª'6½Âª´âÈ÷øé8úÚ¯ç,ÓnüÛŠ)ÔMºxz„v|•P=þFbûê5ÅK‰)ó([Ê÷ÿ•³Ò»xœULà›çªáq–s¦ò8‡ÇÓiÓ¶ˆÐeecøs–&ðÖÂX߇„hœTK^VÓ˜H0¼Žÿ>ƒ“#1i’ßO?)½—†ˆ¢E”¯W& Áèí–ÈKPÄ'ÿÉòPq—OþJϹ®e=TN}+õ¥r åöû¥ª±ˆ¿âúCm÷:¿öˆU=ƒä¤>/ŸêDˆ?ÿ‹¹"œ(H^+##- +#BZh91AY&SY«fyzqÿÿ´PÿÿÿÿÿÿÿÿÿÿÿM"¨B#.€ð0u@b)\÷_mÈ€#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+µëìÓÓm&ûq.°=Ø¥Ûl´Ôµ=ºîÌßsNÖÑ¥¶¯J×J2&)·M]¯»¸±VE×vµª ƒyÞú·Ë‚o³UѱîÛÙĪ5Fí»=½Þ#v妛=8½ÛlÒì÷¾·}õ5Eèß;ÜÚc{µó]ï£ïž ¶÷pra@;zõç½ï¾ß}ð÷aë¶÷Æö÷«Yöù÷”Àwcwzó½Üç#+#+@#+Ïc@ôMÀ÷eð(Û2挥۶àY·±êí¹ÕÍl#. C®.îí×ÜÏMM#-h  3ØèƒÐE;XvÀP( J”(ïcª*(ŠD‚…+A@ÕAJ퓽ÊåQ@sï»G/D†òû6Ù¼÷;½¼ô­ÝÏ@ÍmJžî彸ŠR»-}=OXÛzÔ6ûÇßtr÷‘îÞ‚ÞzÛ½Þ½{Þ3¶ïyÏof×g·wºúØ–Áåïo+çµÏµ»åÞ·½èïvyîöV÷ݾÇ×AA_]ìõÐ*ª© ìj=Q•Znîëg½º×¦»·7z›ËÐ:é]7°Ã$Ù£"¤½äúT#+TËTºÀ)¡Ô#-ì#-Íîeõ¯{×5è¹´½:÷Q]Ÿ^÷ÞkçÛ—ÙîeIZBHÞî5õêBö7Œ>½#.½ÜOo8ß;¾<#+³«íö÷ì-ï^zh˧Ýõðú¼tÏÝÐw…;ï¼ð¯µÛwÈãp±é¶…¼}·¾·6ÕbÝ\éduÓ“6[[.ÖÓ³s;}yÞ]u»ëu>áºÝo¯{¸¯·¸vÓ°n¸Ý»O±“é÷½ÅÙð;f«ˆ4Qé­³ÝVw›—ßR©Lßw&ɶÛÛ9µíÝÍ›vç½Úör{ÓŒÇÞêvÒûܧ{Zßl‡­\{uÓ,iÉÐÎTÎo/X#+wº­½½zñëïsm;]#-JE#+¨•@TG³U ow\dÕÐÝÙ³—1=Ìݶ®Ù^Ó{§qÙ•Vå9O^=šªoU¶Þ¡Ðº.Æ­•îËÀ#+ jÆ€#+w¯g7%×yì»7x¥½æÞækíÞÇ£FknU|våâÌÈØæk®^¾n¸z3f#-_VXM©ÎD@úÅ{ÜS}žv½ìÜꫳ7€ÉV‚ºÖó+{z=ý½^y®™5C°ÓjÝ4®}ÛÈ÷Ö{nÙ;ªÝko»±|mÖ×»G_.çhëNOeaÎö÷{ÊÐͬ m#+ö#.ë¥Îs(o»ß[·; O»‚éSÏUO]w[—Og®åîSï{ïm@ “èÀ]cŠwe½Úïw6ºs»8k«¼¹]Ýl`‡S®Ý뽬Ó×=K¼ævîë½Î˰³¾¾ç¸¾n¶ÝFàô=ìõÝhV— ­7v<ï:Ó çeàÀíª‚#-ÁÇ.½æï'¾7 Ðè:¡‘öÞÜÝÝ£½À#+O=Jžë»ïºwc³#+4%"öÔG·³»,îÀ÷ßsxÎó´ØÛºös†û>íéí6Û¶²wJ@(¡©ÌPÝšmµÙÙ5±NÜŽë9ž·;•»d–cÛÇîºöÃîνìö»-Ó¸ôÝÎ{ì}ïtúªö›®ö÷´|ZçÂï'½ì_ð¸i¢#+&€#+š44hȘLŒFIèÒ&ДôÔÌ4ÄmF‡¡¢zOÕS@„š “@&M*~4ÐÕ?J~š$Í@ƒSh4=@h#+#+#+#+#+H$D&€e3"'Š£ôÚŒT©LõOõMCÒi™G¨¤ýI£` ¦ #!  =R’!&&™SÉLÚ§´$õPÍ5#-=C 4 ¡#+#+#+#+#+I #+M#+ LÑ=„hh##-)²¦€4#+€0#+€$ÔDɦ@ ž‰‰4õ'“CA¡OÊÒŸªzž§„ž #+ #+#+åý¯çê´×úqâÛSøš­<Ê…¤ ¢ÏZ­wQ6+RØL#.€TT@øÀOPÄDù/—Ó‘kþÊØ+{w ⨵x«Åâqs1ĉ,a)̽Ù{çS–1U¦YþãÌÿ:÷¤ëpº"#.BP@Ÿï€Ch7ÇC¡N¶NxÎz9Iš…o2ôfªiTb¢"ð°<x¼\I%:¬eZöÆ……õp¶ÿ°…q$Š(ÅB™lÛ•Ú­6«b«fZ¬šÛhµQVÆÖÚÅE´ûh"*™´Š(@P“K‘±>0ˆ#`Pb(b¨Y E Qz×kjÔ˜Ì,”fjI4Ù$a$Y*6Ñ35%Œ€M¥(ÒŠm£abf€É)(ÍMSQEc`жŒÒ&’4KD!LŠ1‰´¦‚@±©cCLYShÑ›%£j(ÒËM4­ˆYi#-š ƒ(ÌÌbÒF£PZƒ&liMÂ(M˜ÒP4†‹6ZF0"”´ji²&´­lÖÛF™‰,fdÌ€ÑA–M´¶ÓJÀš’’³I©¶ÆmM¶f¦ZLLÄPj¤–U˜š4”PS# ²*FЈTY¤m”Љ,)6`f›#.˜b1„(¤IÒLˆÉ#lhL„ab!!¶e‰#&F”$S2)eeƒ3D‰%‘ˆÒ ›dØØÑb‰„–F¤†K) 6”ÓÙ$¤ÈÑ”A£&†™“ IF‘(ˆÄ´¥F‰"ÄQ3)YS0Lh¬Íˆ“ÐÌØ&#i-%@‰”¬„h ’HI±PbM¤± ¤‰5’%R,Á0†CJaL0™I(ÔY•-Œ•$I1„´¥"ÙE’Ø€“Y”R6d”MšdTDʉ’E(Í‘˜&mšbHE”‰†ËE€’’#-)¡±ˆJÉdŠ%„Ȧ’b ©l³ XÒP‘Pe‘„ÅF“4ŠÅSMMjBX#˜Í6c Qd¤‘44ÈhÐÔXÌ$JʘØ6ÆFR €ŠLÆA°DÙ!–ŠÙ&ZfZÌQT¥Q4ĎԋLB‘Š4(VPÅIQƒ‰’M’1±&¦‘iš,XŒÒ™™ SŒÚlFMblŒId³S&™RÊ"Ù¢4Ø£MJ’’š,…‘“dSi#.,‘ŠQ”¤E*ŠI4I&”Ú4š’HÃLl$F4DÍFše6†Q±˜2) ¦e"d4È‚Sd¥1J"³L„©6¦Ë ¶Li,D4–Ld6ŠŠ ¨$MaH“!EFbA„CA©,j,Ò4¢hÀ•¤M(b’Ô‘°’™IŠ$3I²#)‚E,Ñ¥1„4PhM“)CbUi¶¶Œ(MLÑ”ÈÄÒ1M2"+A©M°)f‹1I¤e!e›6EE¦mdÅ!E6"¢&‘ÒSkõ6ºÙjb Œ1QŠ1±¶Æª*6L¥4ØB•$Ѥšm!Z6²MFÖD6†™Œ£$YL!*HÖF ÌL¥f[3Z(Ĥ¥¨ÚŠB±™¦RÓ,,Y 2Ói“!–’´l«HÑ©³B+dË"©T²T…6lf35lh²"¶M%a*e“k%Yl¥)fFÚ#.‘ ))QbšËZC%6¢µµÂÑ«FJ‚Ѫɢ¤±ZŠÆÅH”[DÑ$U&ˆÚ4EÔl2ѵ€¬™0£Ö,˜ E š1Œ…¢d”+H¬FØÑ´Ä¬bÙ"ÆÚ µµMkfІBe3RÖMTŒR$M6‘²h­KYÆ)M¦Ùe-”ÙbV¥˜‰-´Õ,‰¥5MM!m )”ÖlXh•)l¬¢ÚË1¤‚†Tl‘!bÃ4”1$Ch1"c&m¨f†K2UI4ÑFL–J,ˆËŠl¤#-¬²RŒ¥1(RXÙ¦h£h´É ‘›M¤Æ1¶6A„RÊJ (1!£2QCLI5ˆÄ†¤ Ä`’f,PQ%„’IF‚Ê”#-P•0&6a)¨“d„ÔZ2E›Q”ѳ)$Ñ4d4†,¦Øˆ¦É£F¤Ù6’4°Ê2F¢…b™˜“F‹2•2’–R³ ÉX 6,†Ä1iÅ£e´fˆ¨hÔ¥&ÃLT±¶ ɵ „™cJI±d©*IC”ÄŒPHdL¶”ÔkØ” 4–MŒ£E‹1M(¤´ÈÚ²6hTÍ2–HÉ*Qš DÉER‘±Fˆ–Í&ÉLBÄăII I”“# E¢ØJF¢ŒV1hÌÛ#-&I4(l™ŒSD±FÀ”EÒÍ‹!™IÅJ1²iJJ™V-ƨÕh¡2ÍQAb‚ 5&¨Ô0#BÂ(Õ)H«RÌÒ)”IL“h‚5Eˆ±JªEMÆØ¤ÉIM3(kH†@ÈdÔØ†l‰šÚˆµƒEL‹&¢±†˜³J4ZL•ŠÛIE%²›dJÒlCFT‘e*4 L66J*MXŒFÙ"Ch¤ÌÁ¬É‚Á5SS63M±µ2Ù5IBÓI5„QcD¡jJŲ’XÙ1h¡"0d•©E¥–C+DšTm“Q”£i5² …¨Û’‹ŠhL„™LSdF%#-L¦L¶Œlm’ÆÚ5&Ë6²[DËR-Dlldª)•M+b£hضŭ&c)D%šÊĵÖ6i­ˆÚT¨¥5Ê3ÓI‰…” FÁZ,–Ø£,©#j+bɶRehÑlCL›E&4XØÚª•µŠDÊ…DÄ”F*HZM”¦’TU0ÆI¶±m‹LÕ´cZËISC-l¥“mMM²Ôš%Xš2Š"4f¤&(6Í’›ÎÝ›,ÔEdQB6Émiˆ›÷ž×þ6øXpÿ¿Ås"[’ÿ\#Ÿ8qŒªÁ}o _þÚÒ(~¶L¥~ÿþ#-…ÑÒ?ÎÃ(¤š×÷jƆ4{ˆÿ:ÄÚÇÇý× bÂvU Ë¥¤ª7¾Æ\XpŸîø!«to©€ÏÂs‰F–Èó°]%…‰i€ŠH*ª”&* æq÷nhœå÷³ÿ/üa¡üöI£ÿ°«ÜôÍe;Lu*è©L ík(UÔÑ+R»¤Pž‡8&ºql-+Y„µSÝmÓ»5¨±ÊârNùkù0Õ"M‡,O´FÜœ–#êB¿Ž¸6;Õ…£#.JL Uy¶ã»%E0•†(ì)±¡vNóº ë—zzmêj,ñUþ«,–'ûRýHƒLYhª#"»Ô)ˆ„ÝêÍ“*øÒæP¦*”’RFÒI‰§"#Gk]‹ :F1,â#.2¼y×l2ø·*FoMÍ#-|"‚0Q3Œà0˜ªƒ\,.9Z þ—Uò®¯—¶æPFŸ'EÍ|šòhñË ìír‰*Œb“øÐêܼYÕâôÍ*wº¶"Ša)‹hR).Y*ðÀ/5ìÖ” 2,¹•#+Gîܹüñ£Í…ÙôaZ4AŒ1–òþGÏüÕ×F[1à¢6Z6d—}Ü—p–::ÝHc7j[J$Ucšº¢äZhJ‹¸¦W6éREE\Û¯‰›ö]|¦·‹E‰*#.Šù»¯â+—âk‘L¯ƒ‘ªŠä¤Wþƒ(!òpÚS)Sèß•„(¬QÏž0Pë¤qXº%&‰‡,nª«$óº%¶÷{<^ëUÊJÆw\£Ó”o£¹ÜxIoRŒ¬#[a›@Ûq±®¹]çv7(é\¹“뺋)¦"ð+J€3Aÿþr¬Ý*MÚDV)BŠ‘¦†w팺dæ2Õ ÊFÏ6«Ž‰ûàK¸ûì¥kM7)!IÕ¥`xQJ¨Ž+=1kõb§Ur‚ŠuÖ\–…C相4”‰Rü’¥_xM>¯ÕãFH†Ç!¹oøXzdXÁ´ntÇóa™1Kéë¯É×FÌ÷>†ù­°Rêö"60L(V"iTÞØeN·$iƒÓ€Æk´Ró¹‡Å©Çáïàúºö/‡Ú*5·Ä¥G½?.wÏEpu00àÌ\¤lý`~» ^‰Ž8®JæP³9âO#-g2||Ý#ã­+”@çï½´‘XpÙî‰Ãõ>¶ŠÒ¬{S0øŒüdAßQ›¶ü ±rʩґãÂÜ8òΘqHßÍÆìÎ\^û™/ÏëçW¼aŠÀ<ì‡JèÂ"I£'†´z» ¿j±ƒoê˜ìS¶àAùõ¸ÀÜM‹ßMÒ3ÇÖVÀðh#® jCô}XÇneÈ3e®Ûø[½–£8ýûg#Ò¥%%îf!÷à‚iû¦‰ÊuátFWœCŽ˜@™j5š¦½ôrfx:[TC:mƒt†#-(>MX#-É `8FW_fU9”Zp ‘NÞ3º&~ Ã/ãµLà1ON®m&DPùz^¯$#m§Ðú-|ùQm÷Ï#-àºOm#C÷´F×,í„ÇÄPcé9kÔ9Ð]Gmj¸7ûalœ25¹61A³P#‹¶‹z²ïFÁE#-*ƒ´x72ž.èaTiûô£ÑF¹‡‹×oM¬o¼Ç]ŸÒÉ$ Š/»¤*zó¥'•Fm ãð͆èü³wÑý'‰Ÿj(TG¯/Po©á¬¯]yï”×iUð(¦//'¦¡ŒNñP:c²c¶œ%xr„@´ÀÓ»œ¡•„GyZÛoúè½lÆÛ\M´Rꈈb¥h?OƧҙ}Úso G ­¦µ;kÝLgÙ‹éÈl†¨Š›±¿¶OŸO–ºÙ¾»•$²¨œ™;SJØÛ 0ТÅô8…rºõÝO¸Ëêêêm ŒÑ¡#-õÜñÅ M+U@ªb¿êØRŠ ¿+ÿ‚ë‹÷¹ÖÓã÷öÞ:°¥ûZŠûW7ì.b|÷jýwˆ¨ú2™ô³Ô%¿×vÉ÷}V«}*<.æ•GrüÉ•ŸS÷ß: ¾z•’Dßáo®cº[º$¼5#-‘Jí©B <SQ\@Ï…¦|©±Hm”ËiKÎ J#.“RðR‚¾u”oFSmÙúÝÇ¿J26Ò €½h¨ÎÚÚäù½Ž˜j'6Ê´Ob\eoØjbk¤ŸÊÖ?“¶Iû‡qaØ,Fe§Ìp&*å鹦—§~wC L#.ëJb±üù£#6Í¥ˆ¹ÜÍñÈ,E"‘…§*»ð¢Òçß½àôÖ´0ŸÏåoá¯~5Øi#.©únº™.P–mÓž J™n;ºYSr©'uJg¬£Iú<׎‹k¶¶®§Ë¼Óõ¸‘#.×9™]}|$ù*^1ueLs:¾™™:õ¦dMžö£ä˜ïgw‡Y‡aå8¨™3/4ÓkM"&µåŸ¨Ö‡¨|ug¤#V²„møùóQîa9p1­ÀAEZK:ðÒ`ÐTYL*žP[oeU!àÍÌš²Z(ªÓOv’£É“ÝÎì‹pñ¼ºé;\tÏ X˜ÆÛYöR¶6<ŽLåå‘Çx¢¬ÓJçN¯þ+ñ³P„âlm?.ƒJüš7e*DF/:‘sk}}39ó¬èœ¬ÿf´xñ¨#.h6ÀèD¶x0¥Ë)AX›¦å3²\ã|´/#-e#.ãm‰ˆ!:CéÇw{GïXóߌšAi^GBdÒås0~Æ?ÊÕ’YŠ2nßßóŒ.3˜ÆyÑ¡œ;²>úræü3T?òƒG詵Ÿ§Ç¦<Ÿ(t}W|½+8#-HŠÄÑ^ :¸¤k†v<ÿõ\/N땺•®7ã™l Æ—„6GIœÖ>_UHº»:Â×Úðv¦l¡7ÐU:u²é1䌟 DL5:1O¬ûvxaB(ª76»!L‹”ù „.D1\ºú;tÓUí-nõÛ&~ÅNü0éD¥~¶»|åȪ¡oj·¿ž˜˜‘x“¥©*É“¾¶þõ|PÛžëÏ%rtŠÈGù?žTóë1ŸÃ-ìîþ׬ŒÍÎ6ö¡¯#.0îéôLܞέ¶Ö†3€š¨y=; ièðËá#kü\júÍ5[öç~º¢Ütz¬Ã¶Tn\4#å§ ðŒuÐöÀ蕼¹5?9Ñ·ßZ;9¡•áPå¿¶ð̼øÐ[/ò“¼°è—þ1Òj¤ßÂ*D-'±L}²øuT»»×‡#…ÝR‚|&”s ˆ©ê:M¦¢WSk>©(ë¦Ç4ëÕyk¿Ï½0oÖu} Há!§Æ:7'ØØ1DE5Æ=˜”Ïßû´ó­ï0û^=øÏf`³cʾ…[ƒš¦‘BLZYxˆ€Ù1µ9çØF2òéÏ\(‚1Ù&ë—DDë‰<¼ÙÒ¤%¤øH8æ`,Mô°$„¶Ë‡cìãÛçßÃvW2 JáEã>~Z`™i ¤åùP‡exRh:ãNDJ¬dL`IĦ>Üî°ö¿6<çÕ•µS#ddâ)ru/ÕšºÊ[öR²û}2¼¬¥ˆ}?q+ ï;;¦sàMñæYôBGäɾ)Ö…Z­±‚Ü{(–™©:¾x§ŽÚaæj8p¢;ØÓy–Ýñ1yq€úÌ¿[ƒÃ‹¥|±ì³Êé`¹{è|û(8¹Vz«G»„ljâo¼í³Îf¬À[ñ§„²šÓäßÉR)9:sIåºÇª#è Uæ îKd`=k#-ÞonÞW6Š%³·c¨‘îfμsd­o(`«×4¯ö«Ï‘¥,Ì«;ªýýt·)UQ9c:ªNý#.WçA¥@¾ÊÀðHKåᘡÈ6D$ŠÈø¬ŠDg¬G÷ çEß ¥•QAiœ–$ û ÓMl™ÝÅúxü­QJÓüj6J¸É8Mý Šbÿ?_nÎgo<9aÝ‘¬È·Þ(+ý˜à'1̲¡üUƒîM#-6〰KOwß²­´ªý–~˜mþ—¯Å‚”Î~®:¹på!Kº[»r”rÎ'mJ&Õ®M¬«7«TüÊ”ýÚÒmX6Þøs0ŒÇòÜ1˜8ëQ§Í¥ÎÐüY„XŠ"ÏSÃÕ°_=*_ÒÊ-ÇÙP»ªü-MZ(”:‘® ú¤0£ùœŠøÀÕëR]sÄ~´µSëNhcšXœµ÷Ý+ztíØqCÜ1ºèŽOÄÒoÔ•à€ì€WïÚg:Òòöimf¥=õPêÓ2é*/¹«AAØvKfúÑÞ׿îÛlÃÑànœ6Ýá¤JРDbÅcäL`ÅMm¬"ÑŒ`\EX]#.=¹©Õ¦b¯5ɤ¶}íø:tÍÏ$š0±‚ͪª&é-…«!ZUs§L‰N:,wJ%4,w»‘¼1¶‰é]aÕ;² qþN~©˜ÅÑøÃ6nˆ„ûÇ1Þ¦çÝP¡&u…‰4Ì~Ù¬ÄFפü*ç{ý#-GbÚRŠ&iÀúòwÈxA´mðöÛ÷Ç1•£a¼s>Ò^nù5,?HJ:tùÖ±DÐã1ºó»Œ?‘U&° TTM2`¼)àÔ‰ÍHŽŽ6ϳYXÅl‡²ëT ¤Ff¤¡J¢P±F^>X¥ÅUív ªR²¦¯»·7‰ÚäRãªë“®²óèØÕ€Ç¿q†” Ó!Hä¯Ùã¸Týßž«çòí3òºqÓRÉB GwsÁñ)Íh1Oƒ¯üñp€`G(˜Ãëδ£v1´þ“ø &¨ Ù—ÑãÓàI‡ÕÂïñô¬žH.P €å£uÿ£Þ%ülÉÊ0SÁFgËšÕç‘o-·1ŸïÛ'Sʧ¨}†•ðÖ¯Î(z ÔWŒ=p4›ªM=¡ÌTåouDvòŒ©Ll›6ø'dЃ¬êꄆk0yìf#-rÞ»c#NMZ¿¡¶c㾪Èã#{¤c_³Nq¸¶úvˆ¤4’ï¬F,£‡r×¾u›CeÝ=c÷²PŠÔȰQ+Õ,(ÇC®µœ•½ªãý-ªð}/`Ëo°*yŒ06@3x\1“¢žF žMжF2ñEP >¯‚×êÍm©¹(ÐÎ&"­ÖXðøhdÆÏ©¶êƒäûS“8º>C¬y³b¢ã =ý¨äAȽìW’¦yKÙë†×ÿx¶tq(ÅGÚf^E_#. ‹(?-lÉVa‘ú†6S(Ó¹ð\ÆÐÕd{ÿ Ú·Àà$bÅ´9ö.ÐíÎ"¾ŽÚ›QÃÝt«Ftª!v³ÖSÝœur¤ÆºGw“®ˆ’>Μ_{<žþˆo¡=Ø $Ë6ad®…ŒåS›{Õ(‰þ; i ,¾ßâÞ{µ¦—H•›˜ïËIšÔ Üq#-Ö܆mû.©°™¶sï¹ÄÆŠÀúb÷=zè(«‘’vï†ý||gÃÓgàÍ“iºîýÍ8a%Ý1[|ãSP0:§q$ÖçÉÇsDºöγ{›÷FÃf3¦VVœ£¾qto!J»ÔCˆ{³áãûƒƒ3ÈtpZÙ™µTN-Ï}¦Å2{=t‰cÖŠ¤äBÊð ˜>žø‰6wñ—*bL²ƒ£÷ŠxžâDC aFóT¡ïlRç.Õ¶¢.vxÒ"V n²/=î6gÀ™#-³Ä¸dï®Ï©Ñpûø÷ç/Ÿ:³ þpñô=;é-±c@ßnO=gÉõEÕÝbòßk]ôܬõ©—öÝ[ekàïl;x¬¶½´”E!ædU–ßqŽtb¼uí½eÃC]¿tæ$¤ç,Vñˆ-,Á¼+ˆ}üv+ù6×Y¡Ö!{mÒ¶#-œúXÅ»êAŸz"Z)¡¯ìñâ>’š&Ue¸@¤uÖâ ßõXéí" ³ûÜ}¾TAþÚµ²¥oŠ:þ†&<Š<ŸQoù-”$#-Rõ®p€ª-ðk{ÉJŸdÉó KÿÆŽÏVvTÝ ?Þ9wQ:¨‹»àÙï¢iíø»ð÷zp÷ü;Vžìÿ´ŒöMöÐqg Ð‚«²¤p å bfšR1ÏtÇtZ"\E#-PW#-w0¤#-œh鬧G,º¬ËŸf5¥žóYJ+¦«ýÑeëD¿•3¾Òù?i$•áÑ:ós=]±p/˜,äïT¼^WJvóïÖ°K½y¾Ü5a;é¥8˜ñß7åQ\ì’‹yZ,Dp©à€¹e7&¡z‹æ÷`Áã‘Ø–­qÖpÅôýkDôÍÂ~Lñ¿…ƒP©Áúi3.­mÔç—f¡¥»Ð/~úoÖî,<ìÑ)¤är¨Äž3Lé“9Z_.yÀÜ0 ÐY¥MW-´–Œ³¥ÀI3’‡WÒ1b}çÇnâçðùÙ(âãimú4Æý=Ý$ÕŽ†<“ß—Ðå±Ô›WžjÀ’בJ§®±ÂåK,È…j¡ˆ÷2¥w¡ÎëáSR¯øhW?¡ÃK¨¯ø±˜Ý·Ì_pˆÕñ[‡êÛˆŒá7¥IãrøÜGùÀŠn#L3Ûþûû}¿GkŒ1{g‰€®9Om]Ÿæº.:ý!ÕF}60(pY vQ>YUí^m­³u×afô#+C_Ý#+‡/–ÌAÈ$:~oçÀ,‚šfš®š Gõ¹mR2ZÜb»ûêÓR?šÂÉ*×$l´¨ä_£’~té㟇(Õ”H· › ïõiòz~#.‡?§Rlˆ‡íâ«×ç\È$Rï\Ó£èÓ©Ü֋ïÕA!NCð‰>ûõ¹âÀ1þŒÆ˜aý\P! @H¡ûÿ»_õð#+ã·aø[ž6Ÿëé}>|kô”$6#-®ˆw&€Ê“\1~8Çïp¨5~môw¶%ÇL#+¼àþŽÕÿÑ÷ܱÛÛ|ÿiU³7t!‹žC‘DRIò>VB!#+ΟujÏx˜RÃF‰ôz‡Ljˆêˆ>ül%ÕÛ>^Ö}:ì–’‚ª˜=Àc“ý:D¬#ñuÌ7+Ǭ8tí„ÐT‚Â&1To£ÂŒw»û°üÛ&u*d&›d.wÐ_S PYIq&BÇŽx‚椙eËb`·fÍžÿnWß2|jw€.[4ú¡íõÊ‚7RÇþ/•¢!r@#+‘@ÈL™6ÞÝû¬$ñtL?Jƒì³ Ìmj#-«5L\>)înHL‘ýß럷Ù|‹f &`ê¿À·ÿA¨n ŸÒY™êΗ†Ž¨å«©O¯ÇMˆûµþÇYÙY(½î÷g¨|þÏ_¶Úñ_Ѿï-çùZÇ÷Ö=^õár_›´ÑFÐùp—ï¾(ú¥OÐíþ9üä0¦ÊmÝa¬pè'&«6[Pw¸pƒíÃw"Œ¡Rùùß…ÉÛü}?®I! lk¢‚Í6â"/kн²7}ÿ~ºý'Ñê±…Ë"Å’¢$&¿i0Žžxé:5±®“r!\>³ø1ñx#-ËXɲQHM#ˆÒ•Wè1¤R¢`×&ð𵺈$«4} Tç‘ 8)²­¨ÿ}þ=XuNn…ïóŸnvÿvG^ÙHXÄ#.@“z Ú-áðIÔ Á|NSr­À¾¹~±*ôY;>‘èa*^ë ´½æ#-8¨ÀÈd7©wP«ËcC“ÉlÛ5#-•ˆ o•‚¢;e5í#-ö𱄠÷Úðò)@²äU…¶[Dd"ˆ—b$#.Jk!°g»ZÈš¥‘1‹.>ó(ô–ñþ‡ºpL!mž5*ÉP7s‹I‰ïäÐépÊ÷8´:˜2<’•?'}Z‡@Tˆ¡z¼TåAõy`äbÌ¢¤z‡9ë ©ÅDäJãºÎ³¦3I#+uõܱxU ÄWRu¹ýÍ‹˜Iåë©'ÅS&ÀÂשMšÜ>j ÛVIX!›¾[^ÑvƯí¬X[0úëðó»H]±þÄhݬòv†Ê 8JW|8ËŸ5?%¬"tg¼ˆÆ¹Î<¯m ,d’·>OÂ,§Ã´úbOrbè5)ïþ¿Ùƒu¯l¶‘Ê`q·Ps}`$5…††𣬖Ws«9gUjBƒrëgÕEj©(RKoédï(0½D¬¯™Æè\Žå"õ†öÜ-´TøÿD´$TvLýRúâ'O+|6}¬%Ÿ«²øz_qÝ\q;'T'AçÉ“ëYäi0¿%I€bM{³óÎÏ#ì8~`qŸ·ÂÓ·¡\#.‚zQŠØIc5sfzèÒlŒÊF^1oûÞÞœ4ãYc/#.~b”!Izü?K»Ð›÷ÎJ;DZ¿LS}IƒorËV¸B©3¸7/Že켫ëå‡3ÕýÅ=NÔééYÌt_34©à1\v¾³(só=4¹1ÉÚæ¹]#+âüÔpqk 5ãœBwŠnŠ#+öÔ+[·‹¤b/]ÚîóÌ»]ÝÖž;#1€Ý#snZ*JŸÓgªqïÓ-3-⾜¯ªj—¤tn>k„Ó#+ÓDhM´ÔD)$MSXY„Š2£hÁ³=·¢`Ò¡q)E¦À¶‚·(Ähe36å4 áîéÌåÞá¼':²ÐZE§h—b‡p’6‡Þ÷Å£S—£‚ÕÓ‹ ¥äÑçýzcƒí—`Þ¨ÖîÍ Ðšé4P¸°ÅnaÛ ,w)[!‹±ãöß&æíKÆZ©×ùš]ªÙoÑÏ4\0p P¦E ß8Uã&aWTðCyp2b2 €€ÿ#+{hwÝc^ΚÍ[qÌ‹©­RŽ®t':žïK¾[å°©¨ú*éêL/ÑåQærßNG#.@½Eï£`­9ºAæjˆJ;É‹±ÛpÑ[l3ó]º_‡\1•&kžüá[pý£ê´Œ`ߣ6h€Nê“%7òºÙæ–6Ƙ¡"[gLE£Áß"µV&rœÝg t³²T7Å.ěܺƑ¡4p5¸#-5)ŒÆ«`0ÃßhÒ?{" † ÑX°1,q`„Í”.²‘@Ë#.4`Ä=ÝþözÀý}w}Xüv/—fçAé ¡²IL(™WM^˜µ}*óß¶ÜŒ£LŠ1bkÛ»]Õ¹nIW»o#+l4ÐYß 1ã±êº©cÉðymx«Å¹¼E^…®¦ÖHÐ$û¶g£ãͺ|pq™Ü$Ï·ÇË‹u^Þ±ETVbÅW#TÁ˜Êã¨ñ”´QLj`±|]Cep…«†ús÷ƒ·ôgætÙÊwC¤ «´1k/Öyj“ì|éü¥d¹óMyÐ_»àÝ:y^/|â8\«Ãx"‰”xöòêO¬}®å˜sÚ(ìZ¥¹Àlú$<îóÙ`ú<–fÂ…|ƒ‘ì×ÓkƒÊwH.áMè™=$¨Å-†áè –dp\r¡uKKŠ6Š@²6À–"É‘ÜkzšÐyE{øÿKµÇ{ÅLƒþÜ;2äµvªS†4 „}òÖ}X_„’Ö¤ü¶g`ùT½`‘Í=Æï¢Ù¿DnAm„$/ßÒ Ðcämáxò–<ŸWÆs‡qŸ|?êDÐñøÅêì`ì'ML¼S†“9á20­¡µcÙâz#. l2H#-ÿ†_(;WWÛ‰þݳ9¿ãÝãÁ^ɱ¹×.þš?šþ¦_·CYžó*«s– 7…Ö‰].C:+šézµ#-sC¿‡óšhóÞj¤¢ ‚P`ad´ nxÎÒ6ñª.Œ±‰aÃù‡oà›ìÙ‹ŒGfOLH÷v˜y¾a'/HÁƒ>í_†ÄŒ­íKFÉl÷ŸÔu8d†N)GvÿÃ/¼¶èKV§¥·•°R’&Èt¦·DaRë(j5ð€ÒmåÊ·ßܾèÜÆŸ"ò¢tEÏñøòm ư2!€qw“¼š²"µF)š˜èdXUK¤@md… †d ::Ña#-IÀu‘´6#FJEéQÄcÞµ !²¬`£X†Yø] €š„Ò+J‡:†c(¢I«HÞŽØ…)ÚŠˆS£jÅš *Š0š7ÌX™–”L5Õ¡·#@ìF5j#™†‰‘‰ CLD—eÃzϯݻÙúó4$«_¯Ûp¢‚ド ¤7†ÎBÜ[®ëu¿¿Š[‡ŽÛöÔSGùbK—(Ù7ivg‡@Ïà›lXç/xêQ;D#ǘg08›#-q£KÎn—MœŒ6¤Bi?'h+”yb9iȦ`b":è|sì|øX;·z´ˆÆã"–]e¥±áæÖúøBÁqXÑ‚:\ÅÔàÌùÞçÏœ pÇ7lR!6<Øõ¹ã×¢¿ˆ#-ð¬Iu ”NÝ®Xkz#³ÎìSÀ!öž¦KXR+ÁMÀ7E+_ì¹f¸ÓàÑñ˲>§Ê‹28ÉN‡îYá\Å&Æ!>Pùrš›~C¾#.6^ùt©òØm²£kÛÛTkß㤎p|§&ÝR¥@ä_ô5,â÷RçƒÊÇ~ÏŒþMlâFeúÓï7½ÉÌj¨Ýy?õÿF~ÏdÇpL<É e0NïŠsvEÛ‰"Ë ÄŸìÌ3aÞ³ì?´&Ûlf£¢—‰=͈´m°&´*Á§S1pJõÓ·iœc8.µ,g•¹_"uÑhž¥@…#.v@3å•ÂÖ‘•“‘ôy©"ã/9/Ï›ö¿^Ùôó|ü½œðs³VÌÁj0‰¡ŸÏtfÛ—&Ti"1E#-:ŒŠ#-7åå+ë«x¹Ù‡‡ÖöÊ£‰ÁÕ'?„#.Ô˜ÂØxQ½ž‡+á*®Š09ñÇ¿#+«ÞŒÁ´ÁÀ"Bñ(RËlÅ//Lÿ¿¯/3ÂÞáAß#+1?£ál ãƒÝ»ÃmcnT&-,³x:èíä››Mh•t9+tPZf×!XþJjgmVM`õ˜ºXAdQE›–µÒ¤\·Kh¶åUÒHhb‘0h³Å+#-1‘̶C½Ñ,!K¸]ÙS¼40(3 žÙÁ׺fmj Î(#-árµ‹Ã§è¯úŽÉ¶Cb59ûÂHüî ç¬@OÔ ”€œ=:‹~•µ¼áÛ xr¯ýM¿hwÑÉÅû#F)ôÁÐjüއ6ôóÂý"î….Èu(pÔU± ¬l4 ûmýÛGä8þYèèìAY¨ŽâÛñª8Ìó§t…‡Å`øþ§ —„6õòðÏ.mãWPKëÁÑ~f>ˆ+4ý©ìL˜’¬e!Ô+þù›_6ùÁÄ8ûið÷Ÿà¼Š{¡é²Sã?×>*¾Y¾[áÍnÇȧS⊑îs P }br®yã^þÇ×%jÑáÅ~pF]aÑp¿·5û¿qêù¶¦@ "Ü-ï´kíýÚ]Û]rfággG%³¨ûêèèw¯mrèñ~©·ÏËFÓüªaáìÕ¬ý߯W-UkíÁ¼:axˆÂaíS‡RùJ¤X8¹!ì’G?›ßtz„ï˜rÚ~D˜ÔqXW5–…õ¥Ì¦“æê¨ˆËjÛiw@¿ŸùwýÒxò€C8Ìý¶°(¤ÓÃõ|:åò çE„ T¥¼œÕØú];†ö~Ó÷Ê>¾^÷å©ãî+ÒCCª||íþÌ-4Þ–BM…B‹b3ÔêÐE3jK&‰1›*X”_sŒÄѦÅ_¾½·k[¹Ò¢‚#+R‰)wRP i/ïÜ4¶îÑeOýZÿ˜¼¬Ž«§ ¨±×ûú{é•#€Þݲ;ÒÐ3Mtô±¿kÏÁæöQ«Æ‹–»¦¡HS#.X©ô«ºµ5ßãÓ–Ùû´þ-'Í«”Y¡´%;§fú?¢5}û¾¯íäzï“ôV èŒ(瑉»"ȤPŸÎ˜x=á-õÔÝ+Ñb%G„h‰&ªTw@OõÕ{4m·©k¢†k{$ØÓbâA6ÀBÄU ‚LU20SÍ~ß= ð¢½Âô;¨n£m6#ŒE,ÂODÕ!oÁ…<ô©m4ó¡ÞÍï¸ÒZýÆ}N˜[ÇùÝÛ½æ<ˆ†_ñ!¹ÿZÝ'º/jÃüÏè§ðjÅ‹çƒæ¹¥ÔuÆ „Ñ‘ sÙêÕWýYÃR²ú”öX7,{Ì-ëg¬QÃÕbp(m#.”‚b$#.NÌšRòüÁÝqv¦ÆÊyRÐÊnÉ‹]–x#-#†ˆ1C ñ‡ò%¨ÄBƨ:×ïû+“õrªGÀ%öJ… aýW&›ÖT"Nœê×ÞÌ044Xü¥È’ÿÚ ­ïÊhŸ:†lL¿/&j›Ð‹!ú[Hpvt¢°PþªA€aF“ô0 f݅ߺeåBè`¤¿¡±ÙV¯¶|š|ŸJý/¸ü¶ý߇ÔïÂ?p—Íó|⿃¥|œ©=gžÀå°r)ôkç¥;~ñ²ƒæþëý&¦ýÂ#Ñ‚öGö.¿V2 w[Ó1>tÑ®+wÐt›´éç Éž¦úø(x~:;Ôt•"Ý;àÀs;‘ ‚Eá×ÞïJçpºªéN[2<åÎ"e|±ÌˆžËawá%¶}›Ô,hÛ>A/ËEkH¶ç.Ó›–U;ü™õÇ¿lš½5J×þ9ŸËD#-Vïp±+ÍÃ{öõÁ%u|ÑjÉÀª1œ,Ϻǒ¾€¨¿›`Çéáz—½C+´á«~J´°ø¨Ó[ù¸aËA¸WüJ“2pàÁˆg Ñz e5 "¿&Û]+vns¹tÑÿ<ÒéA“£EÚ< ”kÛ'݆\’ë!®#.B«ÜÁÂc!f$¶TŽçùLJ<Ô…K#‘Pëè, AêÚçÁ/D+øa:.¨Ì>»?É»…¾ã#è6{Z áó~ç÷N÷–Cab›/³÷÷ØRRNÖQˆ¬FØ7˜DÛNÂ$ç_ÜÂçôôëmŒfk #ðÒ¡$é:Üx?>ûlV-½,dcüî›q¬éûó*ë$|KKîdœœ”ô¶ZööD0ŸEozø¼ê‡ñWßÊò¨³!äAÄ©Bvþyý¬3³ÚßTűW&#-^ßµ"—ÖÍ3ŠÜ©àšðÑrÒŽƒýº£C_ó·õàÉŸm‡½‘âdcÂ/ IНJõÓÒÄ“E„ùoð¢¸2´A¦1BA0U&#+Õ5yZ…ܪ(¯€Ãaéñëg”½G!ñçeu–½flyÎ)Êä C¾ebKôÄ4± Pêü´ÍcCÓðìUÊR(Sàt•„H7P¦KHÑQQÊ“ŸÓÉ÷cêëÛÀÐŒ?¢,#-Éþe8tC¤r®rÃhÐ#."ÔD²‘zN¥%Â1o#+€U ©oÃ?ÐižiÙ²ú Ýs±²_¸À}[+2‹ŸMܹü]>þíÓô{½ÕÔ¾~UÆ{9:$ªƒÕæåúýko¢­cYÙòCç²ÃV<º¿Ce/-¬ë·1ŸŽh+¦‹r±p¯V}Oô\‹O_oØÝÞƒøoÛ…•¿…š»>®Í£N߆\»ë±åyáå»ß+ܦ¸6¯ëתU—3åå^nhÛ´ºš ÁTݯ/.NC­Ûd¿´ØEãOìlaý[ýzºüùcŸTVäÇ^YþœÞg±àälë´(´bÝ^o«žyòøª²ƒÃšÌ/—0çÉ"?jz?Hü9\  ûëû_tW¯'OŸ—#-UNšÌE—q÷¦Ü}ŸcÇ5ÈK AáÌÃä¶ÉC-·r-•tî“aÊíW¾ËU9§³?+äÙ,£e]æ€ÕÂŒ4sútý&ÇqçÅ9oÏô‘Œ4ÔÝ¿#Æ™_CÇV½¶ÇÐ}Úe¶ÇŽç-§z:Tª#¶5P컚5sË1Ý9:$VÙòdnË*ªšk¯®'¦±s³¥>*¡«·+R«üyö°ùcXߦ™Æ¡xT¾×ýŸ¯Ýò[Ï~'ÓñK§¯.Á/Ø+foÃ_Pì·4Æ—é;}&ìý#-Ù‚*²¾zì]{>wsyD.Ý(œŸµ{w::«pþz‹â—_Wå¦ÁwÕÛð`ûñÐ<·òx‡Þ<ã°s¶¹K»_üªWîÏeHƒù¯ð±"݃¼{hžl¹G׫û¿ÇùÕÑnª­ŽFÿoªßËÓtjÅCg™8sGÓ“…î×§¬Ú:!óy=«åýz㔆chQãÆÐDC`æpýQÅÀSíÓòñ#+ø‡îú©*NÑÝãâ4á#+ŽUh…ò¾'° ùÈigëÚÓ#+È"€¨@ù¢'ƒÖ¢*¢H#‘5†Oë¾ÆóK¯ôíçí]¾=õÇùqÓ±_ÚÛ€>^ŠÙðã=•D™|ìOtì§’h£çwþŸÕ}q$ÿ²‚ýîÙ lú5!¶ÙïîäHæø ÔŸn Ÿ«Ñ h äl,#+(ÇiPœŒ‰-Añ­R^¶ -–™s•È`†IBfH&çQä­x Zj]èæ€]£I_Då¥W]ö3®‚˜>¤Íyqß§Å^_#-ƒ,,‡5\œA˜(mjTˆŽ† /«V°÷$Fø%N©/ß'¤ëkH¨y¤eIÜࢂ"ÃaêÆÁ˜þ)¯÷n¦¿$ú{Âml<»¾áèéÐþ n&a¾NµµÂ#+²‡p­·Ósð¼4PÄ|¡”øÜ¯˜V¯ƒêª”G+(æ<ÆJ¼ùpjìjÅsw?¶œ”N4k&CÖu¨•z!d¢,œ–[8Š:Ö“èÝܵÕe6<;ç2ó"¶˜ÿ¾o3u-u}Ê‹èúéÖ®8n·­–MûÏà;$!.Nñ„{ekÇ'm)`ÆN´x9½RŒd$’wÕW°+©_Iû}oã¹%^Œ®HKLðiП¨ŒFBÞ"ËÉʃD°¿Dâñrhó_W{ÇÈ&áóTëèk-µ¥íŠ^"'‰s¸„ް5,P‹¹×#+7ò#XN:éßxŸ_–#-NUí´P…ö|Æ2yü†ÖAÏåÊúÞLƯe‚³(¢zóq: ;›Vkd–A»+¥S…Iw©ãªM(ÈŽ'‹ÔòòŽgò#¾ôRÙ ¯Ed‚–è«.8ô×+jÑWôuÝÚåùþÖ•#‰ÛvT;7i DòdL™ŸØXÉnf¿i耓ߧÍóq? “€Ø#.r p³gu“÷ùu¶¶‹¼?ŽÅÍÔRò¿'Ÿòè÷æœöé»Ío›„P2S±$›v>!ôÝÏIÖÈDì'®1ÁõØûh<„cxJÓï}vz1—O…!_ò–UƒO¤fÆp’õ½„:T’W›¼hî=¿#.’Áü}¬–%}GÉè»Æäp”ô?Œ¼CÐ#-¾~ ÷óGeM1°¾:€ê³¼Yè§ø|ŸIM£C'ý…‘ÿÞpÿºZŒÌ¹‘µ,nÂVX5 ˆàÂ0§ñ{*1­BDiDlTŽÔ¢¥nØ€Ðæ7 UF”Z %ºlþî—qH`Qš»¹MY–jDöàÆ †°°ŽÅ-ŽÀ¦°Aȱ¬a¦ILA«"hhn„)"–1±[^Úµê1ŽÔ'Ë#-S‰×´6—TÃÅ$¯íÒÛÅÓVœ$½¢Ðž¸­2m:ÄþïÛ{†ôwÈÙœæ3 ”OîôÅaû#.g„Öd¬ ñf¿ÂZoRº‹o^§!:°ì60¹ci2t‰”ýWëïß󨟻û±ô\nÜ#¯#±£JÚ1’"GGJf³m#I½Ld2DÚ…(ÂÁM P­„!l©DQôe"dÅ•Urª©¯ùÕPY’$Ùõðý™ý¦R¾ì¼ÞÏãËVªqçøjÕ«ññúö øB­Ÿ¯åóåÚ`:Ç¿GãÓ'tóíáô‹$7#.±Nß·øàû4Wú±ü4Hþ~xpG^¶Ã}¹kÌÂwg•cJ¨²kYuWUÞùßÞ%”Nþ¥ -öÀá2Œ2Ÿj#.#Ò$pçwÈ]mCí^¿‡ðû×éÓëÏN¾Ò‰a ‚¿¬oàà†€¿9E5ª›~.ÛNªz¹…XÙ7îæéÊøojâN¥åü«þÇ£Þö{Ö °›šÀÂÐÃÇÑkù?Kn˜TÖ¥ºìü[áýœ5y5ÿ5pÓoiö¢mƿƳ>Ù‹$²a¾À·yB(éó+ŠG×ΑØJàr)Ö=¸¿‡Ÿ;cFmw1[ª-«DŠÚNG%cµÃÆÁQ¬!ê2Žéêëx½hóÇ–ñsšÌ¥’PQEmpÂð¬!» Vse§§" Ø1´E¥P°dQØZáa+’H«)Ylþ¯JGÆUZ¹ÍÚŒ‚–©F¢0¶T-ЃƒM‰HÆAºt“y»ž À¤c]¹ DÜ„0RŒA¹ƒ#.4lÅÒF9·´ËÒÐÖÉMcêˆm#–5„8ÈVtª-'Ä¡Üe)ms8$…¡Ã|Ä3D¶´ "Ä*Ó?¶6Ì3#+!—Ò¯]«®oYH.%#.#.H“**q=ØÖ9áqð„©Gù#-¨Ö–$Ø«™„Ò¹‚‰Fu¦àFFZF1“Ìh.Y¡³‚w*°ÄRI*9ˆ%{â¨{Bn‘{Ù.DO>pü@Õ€‡¢SoAÖ °ÿNîF´WoØdj"‚©=¼@égŽx}‹÷{}IÚ{É~¥E-Ó£Bõáïýb)ê†^÷/+®Œ¨©tNóćYñWŠ3‡²%Šëªû«.]A0«‹“Æ/H#Ǩ=zY5œŠ)R#c“Þ‰»’!aAÏgŒ­É*H^ÛWÚ_W»D"kˆˆ8røÛ¢šêâÞœµØgâ·ÝÐà6Š4ôks‰½Ÿóî…‡L&¬Ÿº\â×®õ!ò>}i#.ZÃñB<—Æ»!ê«W$‘ än³]AðùÔqMˆ4¯Áæ.5U.BI,ʪ­oj‰Á#´ f’´u§—p€yàp$wéU'ß#.Íøü«È"0Þsë~õH–è­‡G툃Á¶Û 5þw¡ä{m²nA.ÿV{ÿ7+áêk}·PYNÍ“:1äÒûèúãàG›³ Ç'Ö™)maÉ!]É}ÕªísÉ&V¤Äÿx©ü÷èöpá ë^ž¹¬CòäÄûzzeoÄ>%Qˆ!U($¯åáH›“ùàV:ÈETŒŒn#r#.Ä5#+þ—ƒF<`OûzºoÚ%åÑ•QQHPK*)b¢25¶y}Rþ^ßÉWHéû{›XU¶ñ~߬û>Í? š•meÑ#.#.¤XŒŒôþ<Ùè™QÌs(¯^#.½þãA3³†#›p‘´¶kvèÑHÚ)¬1=j ±o\Ç[1A'š—PzµßZ*Ã0d+DUäŒ3ceLÌ2РÒ*0be¨°#.¾hšâÒñ/]ˆŒM|ñ["é¼FcÈàVÕ£sS&ÿ!°}fwØ0Øâš ŒÍ†¬o*Ì©H¥›·86Dªo9 WÀ2ä²ÄÍZ¨¬ÛÕ«¬!­VÜcmÂÈ™)­Þ›ƒ62¨•A4±a‘˜bÃ0j—ަ“Bdä^‘7@­èÒÃXŠÈÚÁ·l°lÔÝX Ê-›­¥#.£TýŸäÎL‡$wÁÖg5†ÅŠ¢°§Ħ)ER±.jÙ(,‘ÊPÌs/$Þl­a1Æfnx0ËiGHƒ…¹rü>±ä Ú[ÓAëó…JÐX¢ØÁ˜f}½`þà>ßÎcùôí¼ãjQ<ÿNó™’Ê2XŽ&0Å(‰™oåö“¼˜A(þE*q8ôªWû_oÓLt‚.Nùs,P¶á•(œœ5£ß“Pz>#+O¨÷ŽúªÑ†ž:½ú~#öˆ|°V÷y =†pá`¥QŠ¢$ŒX°´ª©ú©UD q‚ûnñnEtˆHé^ZQ‰§Oª|t°Ud—N¤G#"D•CŒ¦Lùäȱ¸‘:EA¾¹¡†ÿWHhÑ'¦ï3½Œ[P6qzLŽ=5CˆŒv<„W¹âôk@KEÓÈ´A„J‚q:plo0Ö0\nsjéUA¢¨D*rÛ‰åúü´;6ÞÙ”¡YÑèâCÊV¡afºuªŒC$Ýžïæ?ƒÏáÓw?¯Ì:?N0þ\0w·«1n:{†)îÀx½ÇÇ*ºê`P†Qý°ŸyT§ÜÂ#-Óí›?4š -+S#qnÊTn7rƒâc¹Â¹\ËÙ§FãE.ºãö{¯yº\¹zwÊëƒÅÖ˜Hyƒ#O´Ž¶ã:qôá‹Lá]Ù÷e,¤nx266~&%lyW8Œ+Pm§ŽÇ!#.^¯FM¹RC‚†Rš¶·&y²¤1L>#.‚'EºÑÁ0ltY"ñ0)E“xL2ˆ\ƒ*cB#+¡ê‰J”SM@Qª€¹Ùo"üÙ 6Jš3¸%¢ƒµQ¡J›à…´¼x&A.;½I5£²¨hÑ »#+´Ì“BpቖDNØ í‰Åx*;­HT+ìÀÉ–Œ¶Ëc‰a‹ërRÃ4R2•N#-¢T:î+R,)B¼ŠL²F0Œ¦JúeVA˜·#Aõ´Ëu§Å”±HBw™ <­öa ·’á^wS´ÿtD£j½k3V龡ám¡¬¼3êt礳÷x1hoÇe’H;8ááÌzeœEŽÚ#.Ѿ&Qß½lêÉC¶®ª#-ùY5'(‘üÉòloõ³ï}÷Éf¶Ži˜ö<8¡n\ URQª¶‘º‘®xÆJsU=À©n5]yÓæÍ¶Æ1øÅpÞqß}¨Û“rEJºRÜcìmV‰0‹xH’Bª„î>æ|@ºmŸ(#- ¢(×·“o/|Þ¾=xça¢‘²àEQÁѥȈ҃Ò5¼E€±b‚'¬SUk†ÕxÕ˜½¸ijê¡3Væë4Q›b@ðhª{LY°qÏÊB.>Ÿƒ}ÌU@‚DÌmcŽ“nì_˜ZžMǼ愘¤çN™0"õ ‡‹¬7HrD†uA³Àd¬Óàa¢%aø¦Ë-¹çÔ0èvI<î4ÆîÑÙ¯nuƒ·#+ê·=\< (´ýDÄ ˆC%sñ?F5e1–öñÌämØqÁcyœÎ‚,#-PQFÚ¦7 [³ž¿o«=í&¤/µ·Jz,T™>Ñ;ö½æŸ ¦!âÝÿuDÕõݺfiZ"13üÓBÇûX¾‹«¨Œ-vÔE_½FWDMÔð"%•“PÄAª™ËiTº]ñÖ6ZÂÒFЧpY·8ö·Ó˜BÕí:>¨¹—ÍL(dΜ#-éþŽ[oY3®¥qp)ÉÍ×q¶KEX°xôº(³žmº!%[cÎdË&&HöÈrS.¾#-§ëþEá˧w1Û7yXã=ãÍ>³Í3…#-aÚg9w†+ úê±ÖóÕiTî' '˜6‡¡x*ÁLíÓ5ã¤}òjÿ¼ãîÎï‡ÞbmV•fÊê¤X|…‰FHGöZüÑ1HW£“XÝzuÖ.ÃÙº ^ »¸dšrçzÝ:T’nŠTnq¡0÷X6cÜ"oLF·]ˆªëíTǫ㼠-Z è¥"äRšn‰”*ôgv¹)½(Õ¬µŒX«ÿq\o~·(¡¨Dú΋2@88B/Wlûü*B0€YÊ®hÁ¿bíׯ [A:§_*î2bŽ£ïÔ9:Cšž'àõãÇdvš7ÙÜHŠÃÞìÛô-cxî9ø‡{(ãú] ç@( ¡#”Bþ҆bw6ª³…‘o¦»#-Ì:#-1ÞÎO¢ÉQÓÖ_ŸÈ£³ÕFíØí:¤öG¥›ƒÔþ`ï ¾Ÿ×6~a–®d(Û¶Á÷KsèG´)+¼ìkËôÁ¿wõ±‹!xlK0š@ÿ;¯¨]¾˜ù0ÜëoJ6×Ú~s?2@PßÃüwT—éú˃Ð@Á„nŽ{YÓ÷ú¼E¥rظêò>Šáæ]Íyñçd¼C> ¶ø@Zè4îÖÿ…$¼„™`n#+óÛˆpÐKX€gDÇ+q?<€vJòÎSH³ÀÌi›®iÓW?'{„ùL§Û‡ð^ñL±îÒ€dBè#B–ÉÊÌ AÀ8?îºë»”çüô®±m¶”¾óˆdw½†AÁ<{œc‡ÔbOPúcñÑ­}¯²Ìâ~BÅqqø`ÇÈá´-2Á•¨ 3ëy!ÜÏ×øã0#•âÜ-ês¶qÏlF?„Ë?jÛð%ü°`ÜR8 <Ú9ÚyofM刾ìÇÜg5OèLH4þB~¶bñ)ã­nüáoÏ¡w“¦p|µMbÜ FÓ]P;N&™í‚:J¨{ÏÇF`M^“Ì¿§™ñ\a¾òçùÛ•MÛfètàq‚dÊŠ§Êcuî2Ÿ†ý8Òc"”•lË(—.B„m0J°þ¶ìÖÉ‹aæGhZ+RÍ\¤Ã”!ã@νp¼dµŸG”ÓUŠ^`(ÌåanC0õsY˜¡àLï¤þdUõVðù¤gÍnÈ·¯L4Æ=>›L+å8Ì|Y¨á.…=ñáS^-Ó`Ál5ÞÀIþrŒTðêÛ> …+¾²Ae»#²4Ÿ$s;¥N¶‰í£(ÂFÞ./_ñØþ“#-ØèkÅôºo÷¢ÅÕ¼öNì°È†“7HÜdÝ™<ìÌ?u^ùf#-c[0ñ„k¢$ÜkÊd“Sy` Yv\ôEp=DzÙuy5èL”“4þh,Ø£Á‡·¢b:¸Î„™‚aÆÎMóŸ¥uæu»øYfím#t¢â'£oR2Å3âÿ6[O‡XÇsúQ‰ÇQäPÓsO.Ã5)Ò¢º6BWªmZÝ9Gù+®‚SB¦|Äšš"~V–²O êŽ+î–øïÍx;™ilæšNÆ~3$¾<÷c%{Çá{´lK!¸vŽÅÉÌètãßyÂñÔ?Ÿ·Áöfö(~Y ]Žc˜ „sÈü¢pm<(ÿ÷2{¤É–öB±b¸s–M?Uée1#›aAI¤FLx¨>yÀ³e[X^›aÏž†jBÓ‹BP³\°yuçáÀh÷®½4tW¡A ¥“ó\pk Aÿ%S9o3'ѦÛãy^¸H[BðG´Ï5$(dÈW±/¯×`²hAT%ÕãgŒÊH!¨Šî¨½Ä½GŠþŒ¶‰ƒ\¸Zjm .r+Ásd*SdÝ”…§¡Œ<š7ImP–Úsé“×Ññàbµ®mC Œ’“#ᎼÁnÓò]Á¾Ì'p—Üéåñ²Äk4]mÏ¿~‡îÌtÎusJ,rÃ/D¯ÊÔ¸Nôó5Ír蘘 tÆ y˜3=ÿ–‹Ò¯c/3ÄøEêÖûÈor‹ %ÝE”¼¼¼ÈG¥™¤ûÎ?Ðq¾êN‰tLE‡EJ,·‚Ï9rKr®~º¸Ú  T~h–=Û=£5P`O¹¨¬æ€^ß×‘Þ ŒI–ˆÛã˜sXòÄ™2 –uÙ0rR?M3#-†:?_2íY¨&"¯,Ñú“¾gx¨‡ÓóÝpgÂ0Ç5–V²¢¾+€ŒÊë-¾–';£¿}{¼mø¯U‹£‹*ÙüVóÖâ +Ü󸶘ºCG‘R#-žûÍŽçºØ…œ¨ô`A+s€,­ `MQ¹g×E+#.S¤[¹JoÎ~ù»$ˆIæëÊ¢É}¶b^j~8y$*×GÍ«VðÉ7×SY—Øë€ø¦te>¹Ús´³?fã5uÔw\«S¢5ËôÓéqCéktCíí];eõº-(QáÎòb¾S<úWÍVÛãÊsnïnaŒóÌQzy¢Œ[y=+ߌ©+··fF ùwïD6PM‹mugK·ª;÷ƒ¼×„t–¿ ]½ïÏŠª‘k‘¿‹Ä[~ÊÚ‹|6x–óŸUr$ûlæ›)߯!±ŒoS_>ž2¥òn·€…ÑjåÞ”!æa¸íÀ‚t›Ä`ÎdÕÑZÐJ2b<#-G‚ìk]Ú·„þÎîØÖ|½£¥ZäNwÏe4‡»¼äú·ê?[†{/gq1$7jå65?šò‘ºæâ¶è`ô˜ÛsÑõZï·Z0éŸZÃF÷Q-M¶=#.ðE¶ÉøÑ 6ñgk]½ÅQàß‚,ÚGqàé=W5»z4ð/¬ÜÎmŸc nö©â²|n÷ÒvållæÏ„$B”roN) æÞäpN0ÔÊ r¦£EøEÁ—(܃ô@ÂJ–ª)Òr³ŽâðZõRLçîz[ö+7’õ•NP°äìÿÍ>Sw9Kêß…&ùóoæœêÛ ‘Å#œ9#-?…Áœ(Ãö0Ív‹åà}\tlÜØêƒýø½Þ·?„>Þ¹Ö|ºN°vlõy!ÃZŒ7ªcU4‹’4#.-IDhWÿ#-[šáLGY^"æ‰Çµ¨Ö#-9ÖÒz‹OW=¬ó‹’cH„&±?[Ó•òߕѫ(мz¢ÒÙ+/Œ2‹ßý>ZÒU.°p¹gÍT9.ÇP(÷îu”Å“ºKjØb»‹Ïà¬+51UuštÛZÅO/Ý¥×è°,àg#.±Å¤ÇrÌøxÎñK‹V¯›ïYš¥ŒŠJîxâ8X¯—Œî²_/-i¤@V6…UšÙa¿*mUŒ2–TYºÄBÎ#.,jáä”°Öê]K_$¹ l²Ú5øE÷©Ùyc”ZY$'ûö+£#-‡O‰3/*÷BèAºFo›´^(˜Á$0ä…ú'/q­cMN6Fʃ4¸96ÁÓõQxYuBì"#-n¥Ï®ta##;•FP­³}µŒ{l¾6ú©Æ¨ÑÁpÁ%‰,ű4z—+ÈyZàýpûÛ–}YÈGâÆÇg1ñ—F òÇr³ÇyÕO¥‡–.ÄÊOƒñ×Ç|ã0a»Ôy/{>Ùß·²Ïp’Ll纕¢èµÖigëî—6•ÄdÍV®‹{D,T¶·®Í—åu¢Ày4Ø ¥I&f­wÝ[®Ñâ.·*Ä”XØIÒ%êÏô®ÝoŸ é à Œ]0Âû÷g·1^afûp.а~v†®#+¬•hd÷kÅ™çMc¶­…° 6ë.rWNlñË'4Ô³áGät  ~ˆKBçIÊ7²®šlÕ¦/”²¡/z£ NQ97vSº Š][!5ÿSEÉk#.Œí¡BÜË®ªÍ¦ñF]º[˜X==R+¢=pÝ2údóµÒNõ!ý«ôg¯S®vê_‚4`.9ÆwðÌÍ+;¼ô=×1Á÷šÍŒx9Nú ê¬a9Ùøò¤£¦­÷R‚¨À-Ä¿[—×¼¿¥aY売ðˆãCqÏé‚âw‚¥Ÿ’¨‹?)P:ßÒ"¹ç`ןgÁÓ>‹š‹ ·úuM’Ë+#+Ë9Œ;©7j7›úØKJàð®3Ö$Ø­4njDBYé¾îMË_ž¯×Ô{ÛYƒeRC¯(…åÇIÊø­ ð#+ÄÚë[ÍZÖ•®.âÖN#rŠßª¸¼\¹uÄ>Þ{Ž~5ÔêìPèrðhÙ¿|ßN2}t6—¦~ì`8Oo¼²V÷¢ª;<-•r||†P„¬6QÛ«}ó­€¥‹qVY_c™‰Hn{Üãcª±Ï¡ytêÑ=´th×,´xMÅÒÙK¬W\çEì$#.°–‚ÊÉÙcäsÄFei§Dªåm©mÛ8UÑ6TÃ~ò±«'ØëéTÜ*+7jʱ–rÜúĺ„XŽ[_ žÓ® b¸*±dŠÜ¦~hÞt[(½ú >Š.ÞùqYƒ_ƒj8t(ˆT·…îFÏ„EB“I,vbuHáÇDªUãpæºtG$¶“«#.rª2¯ðÔÚOº®Ñ³þ¿uíõV7Ý ææÞËCqƒlQ†ëôÀG[‡çL§ƒYYlM2ªúÑFžHä‘–Š=¶ß µš·Ýj¥TªM®M%s^皣›Upcô5¾ydØ4½ñÙ³{›þÎzo~’¬¬”…ªNaÝ·–ÇßXÂU—Œ*ZÏ –è×ײAÝ4i9ràÓª-P1#-³–‘J箢ñU óëÑ]¯Ú56Å×z-¦ÛïcS1ªAc|ad޹¾pbZùIŸœaÉ\k¥áÔ–2-y¡j‚â6b°lýéM˱d‘nÇñÒÈbA„ NH{tÇ…bïÏs¼Ÿc«ÐÇ”º† ^2(TE#.ÊYžr6è’èsm¾§T† ½Â5„k¦;Ðx±yM¼cÏõ•¬÷Ý]ºî²y¤ôw9¢îò®.U ʧng,Õ–5Ðm5ÏTbÐј{¡d—˜žð¯[óSô/Mzưûo|«/ÄÛãáb®ÔLìUÔÕåÖ#-&'HF2‹«[C®ó¶’Éc'ê-è›2K¢ØùW¾z7*=Ó&#-bMYÒ1áÎ7õç׊*¯YQ•HÇò’¥,†2U¬·PWU#.Y ´@ÁÔÚ客7nhlÌ2¯|íä. ¤Ôd AÎX±ÀO§Nà’´ ½Æ£]„rËC?³µ»‹r_\¶œÁóúA¬zGù¿½ÞnšÃŽ9¡BÝ[=Ýt‹ÍÝwe(){&!„ηTÚM§Sú*À¾Pý‡4×Ç_Ï|¤"Ä…gíÅÉç¶×^ß$|bÛþ»é’>ñG‹OÔŽœË'‚ö¨†¹vì÷G][J ^°çTævý™–M°¯EøeW9·äÑm2fŨêšXŽEƒªYB»––(‡ÁæMöWµÎtên-ìr¡tqöú¢#+é·Y{3~ŸT\½âe8nì•LzuõªÞáÇXôqëñŒjLÊ¢µí~Žå3ñ#-ÝÄ¢5ì¢)0çܸý½z`"Ü­Éx]|Û³< Ñ´×|j½ÔØÖZùF©ð¢$E´…¾6颚£,Êê¹5¨ïŒ€ŠãhÈÞõ¤ïÞ÷=é™pRƒ9’¦TbO=oóªèÎb¢§S¦(ØGQâbauMGËæ KÔM)U¼;®c#-èaæ°G>1ã;YƒÉñÄiÓ ð%|ÜAsôu9/#-xÅ͇U)¨ehM²qÍá„`©™Ö9ñŠØà½#.Ö?•Ü£$.›˜(ôoƒËs“yð;{·Šw¸=Mäâõ ËoÊ-éïöÓË +†#-öÆ0T&^ìoàÕ&vÎ¥©(›bæú4SH³i{ËàÛÊb_e5ZÏ„´µ*“,Yaɾ›õ½`¤Ç|ìr[~«9,vž¸Õ¨ÃøY~#ÊDØ›ÈqGíFôÕ#-™@ $ùzˆqŠÀŽˆO+{JްÒ<ÄA!žN¥¥oÞ±« •E*ãu³®~)ÃM‘Åè A«¦Ê;y}"{(¦*Bœ^× ÖÓ‡ŒgÌFº(»Z§9‹\¤ì€`,)8/ˆäEî;² 8@9NaÕ·~;c$é)éµS%Œ\¨/6ÎŽsø‡†mŒÕ»n‘›•1ºÚ›y¬)<Žjä°W4UtÁ±ô¯`ò]ŒF`RÝ‘Šªv/€#•ÁmjÓ©¾±‰úo>ljb»àÂq!ÞÓ?ŠÉÔT>Ê_“Úr¤ÑÑÆ:=T˜»#.÷Œì§ÏàK«Áß ÿ«ÊÕ_43²M_ÐÓVOÑ+5¶:,T¨…íVDsê˜Fâ6ïµÀZ7'6¨ì‹Ï™]QN ÆÑÎô¶iÄ mCxгÖ*öƒ;å^i#.e¸Ol«YL=Ì7bÛ]'EôK£psQ:J¸9£ªÖ™Âþ~ÊXú…b8>ô#.m¨‘¤¬³+âÑ}xô±Aà„%Ýö›©ã÷yFÏž{ÍÑoo=`“¼þµÛfùʵ»[ª~ˆkÌÍÂ1g¨Ü{>ÙìhEfÂåø0¶ éØäª#.`¤†;#.@×ñ«RLšíOƒ„ùeðF©CÏÚÌ%v9€¯Œêµò @õÚô„85¯øáÅ„`¾üít€­e%rÍu;TbpÊl¸ðçjBÎÏ2åŸ#.ìÞཱུ1Ê»RùñùžÓótî¥dñ¶ëôrö(ÿ=®Ó[ö³tÆò·³r¹ø¬Y l¤œ¤ Þ=@ó‰õxú$(j<"ªvÔBçƒËõ­‚ÿtµˆ‘Çaß1!¿7÷ž—_åüïÚº4¼‹‹86¿S¬³ú6ª¥2PŽÇ«uK« ¶g–Z>§#-Úkøðct_ÆèD>ó}Û^tcðÖÓ¿æ¼ùT Ìö)YF(zfÕb†¶Ô“¨NH–œâ-Ï8@uB¯GSò/ ˆƒ¹¬és!Bc#-íX/C­—Fœ0î|· +Z™¼+FjË»dÓ¯ ßâ4TkšÅP—”?$ÛQ–Ýn•Ê]I ‡Ì‹ÅY¦Œ½ÐïE ÍÇN‡uCæpª ’\5«´B<&4#-7úypjÒÒ‚ÀÅ,9¸ÄŸÆ}éséR¶z_îT(¢»¿^{E³F•4Š©NmëHÍš`±w G]^0ç:#B”žøsF¤³€“=˜`´ÁѱϘ¢‹òYÉnŠ„’îZ|èú”>ò·UÔ"˜í.Âçd/ÊÊ߬`Hƒ‚µv¼=ï³Èø´$Û¥ÓMq˜Y)#Q×mc'4q“®"ªñlÁJÌâÔ‹Iâ~÷ÅÒ'Ó!dÌÑÕÉÈ/*ý«+©7 HÖ ö´à-®ËíJ*æ”8è['!SœrÅâ×@øLÒ÷2S¹÷¹=¤.oAõ×LßÙ‡Ëg­çý-®skða›œCi¤["ËBüÕs_ƺµNbë 2úV+õ©Ñª÷FP"F~t^÷[ŸÈ©îÌl¹SÅ®ýÒºÕ6ûÝ›)™Hóš  â_z‡>yU[µÃÑŠì¾<å½}ù<}[„g='Qx”Α¥Ì<Ë’çØºúgµà:¯tᢰ4 ƒ°ìC*Ž”MË`à1˜ÛNAõ»óoò$³ÃEÍÜ<E³¶$¯}õÆõs–(Í”¼yy^ü×㡞ÑÎ&ßfŽ xÒµ‡aá¦ÎvĘ¿íÀæŸÁbDþp±õ]jv«bª÷3¡{lF}úœå„O#ò¤uèPÅø& ´8Š[ñƒ ¯ÛÓ3÷íwŠ’O’98 ^“dMиAõS°õÝxp§o³†ý×;ŽÈ]<Ž€¯i”Šq`ó+_ÏÙ®- Á‘+¤‰QŽjé!ÝÞúû‚=0øÌ8œŽøžøò81Ó^Öâ;¾H±T‰&üFª„¥ û?ÂNÅq 0 )J<€‹e–FI‚v( "€‘ùY4-¾H×#.juÙÁƒ‡Rëç"¤\ X '`&´D»HKß2Hm¶˜Àjމʅ<÷¢Ø§¹i…¬œ²Ÿ'^`Ó‡B «/³‰‰E—wD£­Cô—¥P%Í¿³ß‹ð>S¿†ˆ–ÆöNÓ9žÿÜèFá¶îX‰û8rB3Û‘•yíi>‰Å©/{Q½ïÔ«Þ×u©ßZ;¶,ÍEÄ@/…MPÒy­´ü·¤70,Í‚5‘©\ äS~’N@@Žgf6ÐP$rÆ|ñ2Š…@Be%µ×©R4sÆöLíj¹Ÿ›Â̈q#+P•Òþ¼†Â˜‰í¯ÅóÙ«¢ÐoÒÂ:¼3á]+’cÔ¨(…°‹ÀÄPæF*¹Ÿr%VþqhŸOÝúÞOwìú! mu¿ãêNà"x>¯:©à©•I%•˜k˜{õ{¯¬iÌ{ZçC›¨üÑ=–èÞÊÕž»qBÖ¯_~0 {:—-¶nxþçþjŽf xøý„oî«O³cµ#-#.³QÙvÕ ì†ÔäñËHþBøñÓ;ºß˜D+|r&Vü+µ˜y³À¶s­oQVU`Ší< »–Ñcš*¥Ó|E¥úYb’ìh^ôxRgʨ±HÊÄ£Ó“MœÃ±ø‡EI-æ†yp_aÉpZlYEIDW,4Ó ÏFkj +ˆ©ôÉ¥•š4ŠÊn°:6Ê‘ñdb¡ vÂÃ*³`¤”rháP›@3ŸÁæcà4âat,·Ï¾ç <½pù|·U÷v÷'tO¨pçýÓá)f½\x#+0 ù½*#+üSîú#+ñt"¹ì5ŽI®ÑÄzå I…Ts´C&4þ÷ŽÕ¦ÑfŒô…éB#.3ôºŸ |jï-…Ôd¿´çx}¿î*åD)¢©$QIó~_6WðhØ”+I¶MŒ@2#.PkîÔ<˜œŒˆ–ˆ…?V”;eã!Øý¯a‰@PØ@×½£“ÄwÔ‰ñ.2Áïó}wÑmõclÔZ,SM_#. ƒ¤.¦°Ñ6Ü(;% ¤ 3(Vü›'ªJ/H¦NŒDHÌ«¤&SÑh1$ÁÊYz éäô§#Fðf½ {¼ðîû‚‰‹‚'Ç|"ƒ¬‘¦£»&|#+Oߟ¶|;Ílíª*ÑEª6«¹Wç÷OÞ *™C‹!Q¤ nWCþTöÿO—±ýš…#-nVýHFM .ú$š÷?ä#+è»Z÷tƒ¤ 5-òùíŃÓÙ;bàI××õ;Ï„>Òˆj#.‹ÃréÜ9Vë«‚]\}ÁnàÏŽÓü[÷x2~¿™ýŽ…ŠznnÄiÌmŽuˆC>t¦µØÒåó3€µê‰Zø0Œ>8+>òBñ—$i¥š¨‡XX£ßiRÏ4>\3‚ÀgD}o¢¦‡ó¨†)ŒË©²!›3ëP1ŠnH?˜Aê˜ën:þ:ãy×°E-:V¥‚€@UVkÚò¢´5a«ç_¯OÁûIW3Áq*ЃsV•‘²s¨94/(K—èvZ#éÝGh`¿ÉûÝ–#.“cÔj2l„®yŸ38@8±Š…ðÑ Ãö™ÍߦLž§Ý9ä¶ÜÃ'a±\’Ô¿fÔë@4ˆ¢#.ˆá£²Š…2,#-ÓëN ÍlÈ~§ëO½ àC¨‡ PÈ~Ïø[d#.‚‡32‡Ï•T´9Áo_nê<â0 ´wr€PÏ¡‡®%&Œ<µ¬:æ¥'RCRé 啦.*;!›Lh lã¶KQjÍ,ø:ú¨ЂeÙý\¶£'sۭˉþhCº)ëþËØêuê\afê2ÂvØ«sò¢°} ]ÒµõÖˆ`‘ Áœqîmö÷Ú§Žh[Ý*ñëDŽ–q#.:¬½Š©SÆÕH{(߆¹h[ÒÛpoƒã²…ÎiÝGTqâtÔÊÀÂEŠÊ®Àolݘ*H(‚ Ë-$²¯Zî㤽#LÔ5°©}M·L»×óH+Âñún^U¨Óá…ªOÆ®¤÷c"¿g¹?s¸Ž 9íTˆ;ÛHÉ#.;¯µþo#-pà8½ü$,M‰¦'Ìkœiˆ¡È‚ÐQXQGUÈ>á\œ.!ñÒ¼O}We `Cµx0‚oav±en‰X룺åwj–Ýj’+ÈÝõ$Ò¹šŒ÷ýnÝ2À¿Ö¡{o½Q9F™\(«PxðÕ"øŽÞ¥å¢"UYZÊòÞ¿€ñ “káêߕÝpa99.z½ør•R¡H«2dGy@Å$Pèö~Õh™_e¤ ô0!H7ÛHqb‡ -È7‹C½t”È©º]¡l­P8‰[#-ÛeqIê׉{þ­² Á¡±i´Ó‚ëÛA;Îu[p¬÷c#.ø›«ÜbùôÇg8îˆç#+b(j†#.N´j–'cÓŽ,£J†^òwD·¼G}û0Iéæ#.§¤°ªè0èëH@&(d~A¶A:Têæ•V’õP‘)ã›jºI_®Ž#+yìÑÝú>z<6甩ĜTp;ëíÅdÈÄñx¸"ü¨¦%4¹jPB!Øè=Ýs o@OnØÕ™|TÈZ¯­âüü¢Ä@EßPxJ±›r«U¸å†—X@YC¤r`ÂÝÒŽ¢€$J ˜9•9@ƒMÄÚôÑ]%¤$%s6Jᜀºò±tÚ#øvùá3@£2Êö+šÅŽc!tޑǦ‘ÈЃU´ór…5#-:Ý;R~‹áz¸ˆ›žiJ^7¹z„QîC* @Ëd¶ „Ýè[ô•\¹ó?.|')Ê#."N_t ±¢ŠR}9£™–u™çZmŽ _T1§'ʘÍ=¦ÒG£ˆ*H-ãÍÞt&_ÓŽ”sõ;i·$³–ýKDF2®|üéBÐ ¿$4weC4¹Ò[ƒ‘¢«;A0’j²RÑ×ã%`ðJzo¥ñWæí¾cÜÝC¿É#-Ê 9q±5âÁãÔ±‚‚&!ÙÛH!=E,£nwá„¢¨¢)&&ŽÏ–÷.ØkŽÑ•Áâo¨h/+ÀA©Ð#-ŒÈ(ò­…V¼q=ΣÇs¼dUbN¼Ù)¢b{l=µ©5¾BVvLŦ#- €Áó€9k¬øy°6Þ@¢‰/& «”0ר>åÓ%úàì†í“ÊÿàĨôce¯ßUÁB|8‰ª"m7%“E‚“>0lœ°@/ïß#+ŽLY“uIPB@f ´0MjÜ/f¾R$ƒ= g=Qgƹ-»ïáe´·ùôfIÎK‡/E:!u¹Ä’Y­>ÓQs;|UÌ«×7­•ëv"œ^ü4øä²ÄÜRRZ"B_ÌŠðrÂñO&ÐþªW£˜a;Ö×ø¼´e/• ó[|u'’?ËÓ0]5µðeGgŒ°„d(gƒ:õÄ#-‘&M±vn»¤=ê5ƒ²¨ÃN¾UÅùZ¥V5Þ©KPV+øâ]oÄa vCŠ÷C¡N9ˆ^­ççdyj¿Ü]·Ëàü2Rò•?4°×Nàœt„p"§ š|#{«{Rå"® ¢Ò‘âø¤Ÿ`%b¿:¤ùz“³LãYä…ýõ¥ÔçêLzd‘K¼ÛRy|Ê=° jZ–ñ*Ÿ*á6êþ]§ªÛxñïr^‹’Ú,ÞÐ×~»ùÒ±{Ðbù‚1$îVÃV…(JÅÖûTO|{x¼e=\³cÊÞ D‰‘vÆæ3ç8#+v1•hó)þS^!¢ûÀþ뻪|?G<Ñ~FÆFô=#Ο{bãhû¥ú˜Ö$TªšÍÇÐu8 ž;Á|¡ZÀP[¶ˆRŽ gÃf'"¨˜ƒ£˜MÏ&Ì<AÞ-Jm®W!/)¡:ÉÑ‚9wÁç žIP?çÆ)ïϯ,TURÖ‡øÐê†ÿr}(9!þõÿ[çÄ8~ÃGóûþëcÔ?Éjx‡³M<ô0oß×õ6DÝ–p ^¸GO€nû¾^¸oã¼ËüöéÒAÓrxé;41Ù0ãLA戒;QpdÖ5<ö×Bâéµn–~|=®Œv=vš“‡Ç…Ý_„:¦³ãâÙeTÑ*",õ{|s¸ç¸ÖËëAƨ8#.€¤…ê…G¼ª6Ò¦ˆv'or¢›f#[5R°€ˆ¤ÒgÌj›~Ê6_›~Ãü™Üß²Ãyˆ%P#¥,îè}wt ÀõÃêéi:…³,øD¹ƒƒIl¡Qד°¹(9@£ú»á¹¹ïöP¥ÓŠrdx½u~˜¨Ð'+rª*#.ŸS"fPއõ8NδN»a»2‡A`±~à÷—+`û²ïòùÉýmŸÀþŸo†>ÙÅ7¤!ì)…6äw9ì§SkžÏ£ü(x‡5M‹¦Ü¢‘W¯æœäŸ!Â)ä`ì .Y¬;=¢ЧS%F¢#+ä¡ ¹#+5â"aïb+œAÎuËñdÏD°#+H…¹ÎF¨0Ý[j…À,Ä>‚PåµÂ—üQöA[&”E7–2"¤‚\—)]@`P„@€ŽgÂÀYRâJ‚h;¸Ø]îØCÒ&Ä£MâðK͘ÌAß‚1,8H–#+q·%/¦¸[¡ ÈG»$APC8•˜B…¶Ùã¥òWý~«<ÿŸ‘ü ãÙî¸,Ðê²´J§#-–…‰ÝáX¼Q}5>5wLP({èkp˜ƒò˜ÍB;ÀÚX8]( s±»cfl­ÄS8‰wÑ€Cº½ Íñß²ÿKû!ÜT/{Ldf7”Ù˜×eswtýT;ymœÞÛGXšBW{E>×Töƒ»Íÿ(µ¶oQ«-Þ?#-U#+é°ÌmË`>ä½ÖÝ+Ÿ2GGoa_eŽýà¤M¼Q-ÂdÍWà^¯>5Ý™¢‹›us¼”EBp3ša7¯[¹5H)%æ´N[d<¢õ³ÓÖ®î'¯¯^øŽ1²/ß' Úˆ¨òÒ¡üí°Àø4Zo]YG%Mñ6mQã0Ñb12“ºë€’•2ÍèÒù9¶Pa)*«‹‘ïê¡Å½š3Tj²œÓÔ üÎ}]×¢2Y‚9€ìÞÔï0Ø2Øy›Çee×WðÓÌøÃò²‰Óºê´‡L¼™þh;³Ú<èô-i¡g°Ç3ú^àÉë³n·½“ø@à~6 ³h_#+ï6¥OWa! ƒ÷@9“ß•žMů?-å¾ß»m×¾íÉb’i,µ’™¥K5¦a)‹cϪü·ßö†`”UUFTFФ±Ï?,ÜÖç‹¼ç æ»Öhð#-]6+¼7M,ΔÊ!ײ¶í3²¹,Õ·#.B÷Ù#+j#„€Fx²B(”S©dWÝi¥¦Íëóg†Ý¹5=GÙ Ô›±Œ‰#.aØlq¢6¸¦}T:j8 Û#.ÝÅ}©P´&Ñ ™gaÒF]²]hÀÿ£J†ÉÝ´7™psI²"8€`CJm)*@ö(lUeY¤V H…ƒ¶·“aØ©FvM6QÓaFÁ@Öèvz_fq÷*Bi¿‰Ôrq¶)¹R¸ÉYIÒÆ¹×"5»aÊÝy9YœÒ'±9HBOP½.æ|"ÂD,»„Ö²ݹF?~FpWo |:=#.9»³žCã®A)€G”æÉà{¥‘8²w±C›* µ#-¹bÕ½–æ¶M«·ÁÖjÉ[ck\µsj-’­r׊84i€#-1ŒÈÞ았ÀÞh4›JôŒ]›5dU)#.QŠŒßZfy³ÄŽÄ“Ó¼È7EôG떤ƒç‚ûHrœ‡Iž!Š ó5Âʲ$f4xA‘$ÏF];ºBϯ&½{­4޽ôÑ7Àµñíp§ÊV<¨:™8ϧÊàŠu’tH-“h­™V*üšßøíæ¼Ró¾Jf®O_-3iâÜP<Ï^Ò_¬ŠPE‹Üš4 z¡Ê¸5[b”™$‘–óÅRJK_#.#.L“™‘ÆÆ•ã`Ž£ÆÂ³Ãb‚"OLåÜìÍŸŽ5Y@“ð§-›Ëƒ‰@*(ˆÔE ŠÈ»„´lîµ½ëëjÛ×ò¯€¨²€«j%,ªBTˆNûà#-›;ûý®6÷…õIÙ»õË™'@îøÛ´ÀÕ”¢« IµO%®ìÔÉ@x³F÷Lä0„ 6Â*±}oWñ­§é=¹BdÈ,D<Ôe*ÍЛ`’I÷ۻŒ5ϳmxÃ÷÷=öºõx@ØEiß»„—¿lduÂ÷Bá,edV1´lOêG³Û»úpç±CÀêè†ÆÌQŒI gÌlwo´¤z†#+`êÎñ[¥LU‰!Ëä[[6ÞX);ºÔ¼ñJ<må÷ý»§$>}ª³rÖC¼ìÌwóyëz¥o¸H*)t£•WäÃmó‹ÜÙvv[™õeTm»HÒ 0 —¸Q1í¥ÔAC‡†ÞSÞä8®À‘qßA¤x&Ú^&víœ8uY{¹cs— ‘Ç#$lžËïÙ=#+Ûj ¨¨ÔÒÊ|ý«ŠJñ¹†*¨)sT±ñtØŒAx0—¥‘õp8ãÎfÛÉ´ÎÎⳜG<@ëè÷`P‚!Éðøñsl§Sª,ïY#-´F¤! ì__³«;âHt¦ H£)¤m/–/aÌ$¶€ŠÁx{Ž"O#.5rc\Vz#áîY«AŽØ}£ç¦…Œ4ÎVa×±E«ƒö ŒœÂ*C(4^›¶)­`ª„sƹ5ÒÂR$\5FƒÜøG…¤¶80¾Gé8¦N#.PP`ª‹ÈÈ¢ÎPBI#+ƒš[@]«<$¸|kW\ú µ:•­öWq×G¾;ŽøÍõã¶—Ñáöº§:Ú›A{^å¹ó‹î€q³é´MܼN¦(¶ ê#+sš±`–nŽÈ¦Æ#+”4œ8˜: b+7eÛ¼‡yÜfû¨_q™D²¸£äÁÏ{ÂÙ±TC¿¾ô8äwJ— ]„xE†ß©ŸI(0[ʉ“§0p#BÄeuüƒ*‚æ_fÎéi‡gEÖýá…ã’uÄÞ&NèØÈ1Ô l‘†ƒK fr:ðnpB=ÔU#.n´>òîÊóÓš$X²áIÆ·Y†QÝ}ì:U&ÃÑ!´7ÏR¾¿³ü6^íÃÐå#+,¸;2R2† aînÈlžý§¾uÑÍ %GŽà£” åB@q^¡"&ŒœXsNùÛ R`î–š×Êácœ$CqÖB]µm½;²#-K;FÒßî»W-#4M“eRi4™KºÛÜbÞ˜Úù/™Ãn»l#fhï”féëWCóž%lG…ͦÂÍÕ’D!HTsµCÔ9„ñáêÇMÍ™£‹–q—kžÞ:ªlü½aO¡z5Ê÷0|(#+æ€(kƇ¨YiáSÁd¤©³ßU÷޹ÔdÝ…bª”êÐÀ‡®÷:tA¿¹}[k–~ Ô±&k@#-!Ù$—RfC±âhtä¾ÁÎ0Ns‚Ò—ó|°î?;#8%°<ú~ªî:?–%³ü ðá=o]2}ø4N©äÏHˆÄ`«!«Ä”i–½ÝXïÙŒÕÙ¯óû ?¼/ÃÎ$tüܺòú¦”íQ±‡äá/@;ˆ #+ o(™Ê#+€U$,Èiƒ­”#+#+à ]ïöHˆ(=ìšÚQOÈÉ,öY|á”RãÆ‹[—©;1àí‡|Òb¿Wi~4?7]#ö0 M6Ì{žÉ&éÒ”­9™ð_·^ߎTwúŽÑWžußÄÃÂ^#.hßõ?YL&³/Ä:ùÛý£t#댖ªKíñšš$¸Í?iýÇòþžë’øÆ_×fn HCþGø!Hnš°¤$ @ A*ŽT#.Cý«ïúõjœ'IÁ3"¼çÎæ*ÅÎyµü$Uû¿#.¥7šÚk\*õm™Úæ3¯ÛÚ|ûH]+þ*´{Eû;—ìa€a• &«ŒÑ••÷ùA9¨:q/.¹˜íæfgl×óg#.tëùþѳ¼‰?Ó(–µˆZÑÀ~óû9ìŸ Ñâ*Ÿgç÷gÆcë¤ïÓOðâs:$„”J¬)yPYû~ï#-ý§~ zïâ³›³Ø•ÈþúNi)ŸÔ PÁ#+¼ÅЕ,Få IJúÿŽûÌaO`)îxo†ƒÓGI@’#ì{ ùƒ¾#+î€÷=·#=£}ê+LO÷ÈײC—]e'ÏÛÚý%Ÿ±-]tŒV7<G™Eðólçß nßÕöŸêþ&¡MšZiR #.Œúmù1Ѧ§K8¯¹7¶ãGÉ#- lçUÈHÚ>>«{ö|.´øÐÏaòµ¤/1¿í/Vúþ…6ýNšWÒÝRlšj»THNš€ËüžN#AáÉΔ¨._Ù´yNZì²ZÚ'¥Á´o$#+û”=ñ<á뉙/#»_¼ËþPýèÃ’hË?¾›b–3Ò¥ ,š³à'qÖÜ:%ÖØUÒZúÔUDE¤” Nzµ°ˆ EÁÖyõó‹=ÃežìíþWŒqK‚0â5d8"z§ÑãÖˆ€U¸¨´ç<Œ<}zG77>Ñßw—ÎN‚2'sÛNè»é®}²œÇVàý;Dö&­#Ê;½ë¹Àw²è¤àªQÃ܈}ëó”$r§æÿÝÌôwç¸CâsE{xq§ž«#å'øÈö.ˆ»\ÖÀäøTb\‚QC¨;û§Ÿ«ò@O3Ǫ¿vÞ­ÅÞßÇQáÃÜ[rZ¦æ#.š,”‘¾R Ï~âôD@5º-ÏÒ_k 36Drh #-@ÌŽól÷yµkÊCØ.¬'@4êÓv´æÖ´Ø@^e Ãçt#.^Á¢Þ˜³ÒÀ„<‚¨¨H>ÏÊábÕ‘ D™S¬/jÖ[¯ßû?Óžàw#-©_<º4 ØŸ¹‰¤à@‘ÓeÚ£¥XÁœbº‡½I­“_Äý¤9¿o£ÿª((¿)¶ÂHV9}¾Nk®éßK¨:1u«dáÐìk$¦I»¢0‘è*õ™èPã)>.'mWaLD¶8¨n9Ý€b¹¯½¾ùÎ#.OôØPÈ0Î4tà3•nÞÂ.Ç‚õã ´‹‘ A¸"¾(÷aÒ~=Þ8 lê›N°~ÉB#+«ƒ|ô{ƒU‡Vh>WО¡ü}cšO‚“M2õ)Ã÷Òªäü‰Ç.AÏxÎÞÏoÇ·¤ç>Tå’#+uø¨$#+vV ö8'oXêìµE¬î’^ðÂôækåeÿoÞm"ÇE¼uÞM°QËŸ¤µŸ£#+ÅRœL%Àiœå)çÄqAñ¢€’#+(÷JžÕåÃO¯ucG™#+pPŒPË/^H»cîÙãõÏÒ4BA#+À»‘]¡?³×]Þ«óÆ­™æèëÃé à©]ÇíœÙN`(D†Ûn×uï#+_̲'öko8ý4ý!ÃÏÎèСUöÔ>b”>WÖ@>…wÅ@-êp”‘BQÛa*þ¶)J9/jQ^=7@À<Î{eðC˜5ºÅ½ŸÙõ,$ƒóªÐî‚)Ïû;Ä/Šò¾™½iðÕ'2j\I~¡Ìõz\§«»TÚ\c}Eðp\ÜÞ8¼ïå²QÞð.ÒYÉÇpï×´\¼<(ösŽ彯É!Ì@ï\NübfsõÓÏTÞ ?¦N8šJeyê#-jëo^£û#m¾vJKt/J}¥ÑH<‘ÒõyÅÕ'ëèôè¯ ÛÖ³—ëâLË—ÙÚF°˜ÚGvLixh´Õb¢úÔéé¯òØz4£w1¶}È8ß+âª^S=ßܲ!&S"*ÕìB²Õ$¸úÜ7Qâ$Ú熾`R‹² ” §g0!â¦hïUµá _@6½_qâû¾¹óÀD2å⓹¼šH¯±}þUFÓqÆv]f>9~C ‰Ò™¹Õ•“©F!«ˆW•ç6É\˜çP߽ϯŠPù~Ø+ øùÛœ§&ô<28GåûWž?~pGŽ‹†|ðÀüœ¾j…Í ¯6Õb°Œ#+Si­ï¾*wƒhkÓÉÙeS­,p«ˆïUýnTÁãsÿw{r3tòv†™n+›¢ßq¬QÙ$2Á¬e·C0‚ÇRÀ®/°³õúï¹”ÿµá2Ã¥²ø)Hœ>×/˜‘š™ýº¸ú¬íïö²o¬9¸œGªn„:éÝØ$WoUOrѤai¨®V–ZVÑ5ú`H4Qt­5×lK¤$™¾±$²5lž—,ÿ.ê<èªâβc˜¶ù6ú¨l‘" ²¶ Ë¥ è¥wëw#.¼½§z璘á«Pî·?)„_Zo_ÝçlèÌzd÷×&} Ï\í¢³“Ÿh¢û/h*+‡Ì=}õ¨æ‹ôS‰qqéxu×5x\Lš1ÝÈ?læ|]`ú*Ƽ|¦S=Ht)â#Î}O#.»Ýk1ZþbÃ(;õˆgéFK^ᇠ^¬—êÛÕ©×r•Ut{+®ùµF4•F +â*YÊœú#-¯Ó©\úæ¡¢·´°ßþjx­M¾Ã€¶¢ºÕ .ôáɲ!ãb¨1‘cš°7ÌU»O¢”G¨×>‚ôÑE$žÙÅ ¹œ¨g%ŸCÚ›j‹—IÑH‡L¾çIƽpĨ)ûΔ×Á¡#À†¥J  r‚ ¢oSèí!\V86³ÿÆNfÑt‰‡Kñ,‹]éÔxòkT…ë£|îckñÔi-™ígǪv"Þö‚c0Cºýñ¥÷Í{mvUN_D³ôßíƒñ¡Ò_ÊÍ5nÛùqÁ‡tn¢*gRfåöç;>¨oEȦÃ"ä/rœQÃõ²hÊQåzS„×+0î}sXÚ(é›í:8Š‚¿ {Ù›\ù[=¸þw¥ã(~O Ãnž—wßtñ*{¾yʇàÍ‹–%=GÕ4CmGùý|½Ï¾gž‰x°ƒV X'G@ÐÎ2xÜhÏàˆS™/CX[)YåGu¤ÅI»Wé÷áN8í­êú÷pÉ–,MŒ43-¤á|€3ñ mƒ»·fWˆ‘pY–R8‘ òû¢ø4]~ëÈÿœ5§ÖN³ø©=»¾ñþÿCòÜY˜"=Ñ÷¶ôÞë:‚6içš#¸\ÜÇ×3ï~~o,#.0m/šp„qEGÌÝÐ…‡ûÓv|åðÛ6}V=÷j)óøéÂâá>úéÒœ‡ƒ$¤¦]‡äõC"pgT¶“w…îLëÊŸ‰³éŸºm4#ä& gŸ§æW_#-!Óže·Ã¥<§Z›æDÆËLìùóËá¯V6‘îH„£çî飡†Òíû{À]¹³Žcäu… ˜3á‚wæ´þЧãE,âÀôï£8¸·f•+_—´KZ L?×D!Ry\/î|.×w~è÷*#+Ä ¥ˆ¶ŒÏ}¼o…ñí3Ÿ}4]8³Ã—€a¹‹êä¶g4›ˆJç}¦z¨C~NçWooÂe>™[S§èú^ŠõðÚÿ˜êc„ãPÁŸxÚ'æãA¨”9aåŒØež,%$õf¡öŒÑ‚*óM¯£žøœ¯ é’ ØK»$ñø†çDíCEÆ~k3ïýr´Ì²“§Þ¿’¯Õx?.—utzb˜Ø3Íl*õNu]D¤¦ ° ˆ9° YD!Üyý¬h’|_2íøÓ’†HÊ„\ûðI\yAõ ‡qµJ`z™×G$6Gz#-#+è†#-J§zy¾ÔÂKÉFŒRObeÌÀ’„„ T‰rÚÃÎêEÈM|•îĂRŠñQÐ1¨Õ}f`ø|!3Ryù‘oxÙ}²\Ù­Ö”’Ø,®q\ò…"…êLDkã@b©$ÑâD6•ÔÆ$kàÐ,Ö0‘#.§õÝSÄpD¼QLG+ÖPC(uUƒÙ3½#.5çÙ»‡Œž—Nëx–XÛ¡‹o°ãÑä„#-·‹E`ÜùGÚBm¢µ™-#+K;ž#.Åÿý&Mü6Þ—‹ÎǦb:>!Ê®…5J1”xÄžjš oÝ8í$ðzMyéŒÖñÚw_ÉŒœ B‹#+‚ŸÞ ±#+ µ@x  oxáSØWuˆ@Šíª‘\}ã|ñA$T½‘Š"Ú ô×۲㕘fجõÄ#.+έ¦ÛüêI˜®ŸÚÒø˜ =ñ­—ƒÿN¥&$¢‹lwÏìz³½ËÒŸ£hý²Ñ®Í#¿#- =¤#!ç´ý#ÎTbE´\ïsÚéËmŸqËÔñÉ%#+æˆ ÛòÝÓËyp¼sAƒí·NÅ#¸˜¨4€hNWdªHÁ¾êh#Ö%}·7Jñ~88§ò²\ì¤õê/D¿½1Ï\ü´ëÿ§¯ÔTOi¨Èu *Æ$Žb$&¨1ÆÌeUE·øü5¸J¤.â G#+°‡±—-‰PPëÓá…Ä#+ª)\²€‰ç#_=Á*DÍû÷¸K/Â3(ð СJ z‹#+ÊÆñM6YŽ\«ëS#+‹à|¶bÜ‚Æd‰ÂÉŠ“mD÷ $`Æ'}†)±y’XY:@ÄÍe6º3ås³™D5K±?Æœ™Ë8›Öx“_†Š)¶¦<è(œz»¶ã!·ôrº­{÷yEeþì¥{ëé´î'Ð 0WT”¬Ž ÏŒÔI^×WkÝ–ô®ZéF±èU5)S³­IÜóo{åÑoÃÏSFoœ¢ôæÜè¢qM|ÿ, È›­©Pï»nÛÐY[æ¼ÆÏƒ0Êv8Î:«„©øŸ "æ¢+¡!óäu¡Û=øˆEq’!)A"ysð—AÐ]Ká)“CÆŒ+on7š‘†ÁàGhËl¢„©:¨âE”Ñ:ÙFIC;ðW)|@™ïP&ʉJ#+í«R†§Ó.^ãXÎj“Øš#ÅÞLžZŠs&žJ Èz[’½ã£hc´;jH ×Ž_,õ?ÉúêĈß4¬~¨¾DNP ëªR©›y¾ZgŽˆôÕ¦ëF›A œ¿»åP:liïdiŸweJú|ßêéG …¦•@'²‰žµM&\>c»ÏN–öÑ6MR2WÇΡGs|ªY§²6PnÜ!¹f1C­tÙd?ŽcþNaÎJÈßÖÃ#.–ìý¬;˄ƑS¤ñ{üÞˆyû}º…ƒ©·º¯Öiû£JÆDi‚g»óÓ%áš>Mþ(Á5*`ã{ã«yÔþ©«“·M­‹+ïè§?Pˆœ×È¿—¬$,é~Ýæéä§æùÏ«IV([€É*ïxòºH=[à`a}4Ìû—7ÞÈ\Â8‰[ft·ÐE‘˜Úe;H¼ÄT¤‘ÇL‘¥ˆ·B9GõvòägŒà{:qÆ&³] ¸Í2€b9¤YQŠ#.ÃM¾ÇÛvä éÑ`»¾2Ç—6tV[½]¬ð¾Q´¿L6—hÐi.|ðqò„ŽWT3Ý£dá ­™Xñ‰ªíÐyKžÄ0ÐR‚"ŒSÑŸBú¢~»e²ÉnxøWNøK…Êq=_:8‹;-#-Á’¸á!1(ÅfqcÝ‚#uK°°ÕŽÂË$Dº#.‹+¤ÐЄ$ÒÃv˜Ž?•C0ô£!8D]$ë‡Dt4õC'U¤#-+ØÎ‹ÙËIVb "ÐhŒrž:dÔz--“9Wkm-•6½Kèûp•+„Æ!ò«š}¼–ël!ˇ éÄFŸÈߣ\øz0H™&б¢´Tà¹=Ìî…GªF©ê·ëË9[)Ûs3©Q#w›ËÑI+Nȵx_§ºPŒž~}TœÞp•tèí÷ŸTpcåÄ÷â#-É&j캢:ç´=ìþªœ*d=UOß×#-¡Ö#+¨.q‡›¬Þ¯°ÄƒBDMWsˆ2¯Ê-íöõŽúá·NV'3 Q#+ÆC;ÙðƒXêtc#.Æ/¬a++p¶ÁZ#.#.º§ÇÚè¢Ûƒ_'9ù”V½, jÔÕ†©÷¨é¨p¿@«ÐÐu[„Pr¤Jdàè¬âçÌÔàÀѾOMÍ›lÏ1cK]=t:vöèhu å  ;G(Y)ðבÁ+š†6ÛŒ4ß§¾þŸ‡#b¤MfËlé#+ÈAˆj2#¡¥õ£kZ»j”Ef¨¢ž¼=Þu9ò\ìÌÕƒæ¨V¥ºã}k57Ó먖Vƒ=Z©#-’ŒŸµš.#-îe\„>¢#-¦;ªU ˆ]q¨”þ,ÙAÒ[_®;¬4(ÉàíÞ+«OxgIˆt9‚·‡‰ª.M x¸ n€×IXà6d¨œå1°TrÙUùŽ}Ûò#+pZÈ#.b÷èmhÅ6–›m¿3XÛ4£iÎÑ(p»}à©Ì]¤ÖG#+#.¶TÒµóG¾ùs]ù†´<ËÍóJWi=•có³$,Ÿ¯#ë}°c®çœn#äŽ6ñùRjwâù¶êœ/´‰k“;i -Ì„­ô ^B´½3 YAvE×Uk‚¨‰ñíYÀí´"â‚cPŒ0­)a7¾Ì/ÆË’lx#^.¢*2’z»ì}©6#.0á„ÂýHÂ5kÖàí êšc1aІ²‚ȉB¡ÓKlÑEëãðEáðú‹û;Ó˜!H­kˆÌY¸`#‰$‡ëÀ<)IåÃPèC‡ ]p„”^zÕÀŽ¡ÌúZ E@;›››­÷ÎþúîHƒ2,눞éÛ®¡Tå<::!º~ßjÔ¨3)¤!d9©Í«})šé2ß§W!#+¾¥®Ò}SÆ<ÒŠ €I5ÿ?»N¿»Ûð€ø~ŒÅÄs åoËúƒ§óˆh£èШ‘?åYh}Ç^¨s·‘⤮ÅS#Ä‚@£,Añ8uu #.1¥P²":UnÊiœx“ŽÇ¿"ÔÑ5/aÞàzþ.ŽŒ{OÇÉU¢×‹_YÞ8€qU‡áòWjœàuÌnå`átû6//,ÀÐí:,`|æS«vLýÁz»íìãÃðÉ~AàuY^/ÃøâÜ/.?q´}?²6xÖÉèÂ/AP±÷ŒÐ ýçFW¼" 4˜T?‡s;#-9{þ{Ê­Vvþ¤ã>ÿÿ;‚3m(©ÚU „æP ½ï…Iù!ë_ƒû?ô¾Ï„¦¿¨¨þHïB|ˆ¡elåñâ z†rJ¿üQ ’*b¤ÿ+UP©þ·úIu,¨ýz«›éJÜÂG]‚ðÒ6%7#-SŠ `ú* 00º›tŒFÏúÙþbÞY÷vÑ¢}|L8;´,6ÿ ¿¾éÉnáP(ýÁ›˜Šf8 ·ö ïëï8#.š‚&hm;RàD`Ë»·¯W3jrúüQŽMwžZ‚ÃÇ…U“>ð˜ÇªGbÑ“Üó_ŠE8#0ïLiƒ¸/@ÿ½Ð3°ÝU6—‘]@h÷£¼w§h°)éïÀê>_þoÃ"ïØ¾û™(Ž#-a‘îlV£ÝÿŒT.+ðw'È¢j–Z6¹sü—ÚÍ69€Í=­ºÁO³ ÈØPÔ6HA>³¯ömnfž€{× ì»úrD÷®‡uSjxóºW„[Æ=ïüʶnÏãq~\µ¬Á‹5d-ßê¤#.ï#+íXH¯)å8´&\’B6Ó&#.õ{\ƒßÉpˆÞ’6+`#+¥P#-]#-Lgq¡áúýÏø¼ÞÛe¤ãæ`Û÷Ù-ÕN-I#(ï¥ÇÓ³ÒRœ×Õç=Z'¸66s¶%‚½Ã涇×SîÍ,%ãÜálS²Ù‰ò€à>ô#ñ7˜cZ’™¯û5ìÈø†PÀ]Ø—Dþ²Xb Dê|§äº¿œ¸=]6Mû¨´h!f K”åÜ#„­´Ã ±Q?Ka¨9Ø3Jaê‚ó‘DC•ìLs QX¢í$ Ö(L:ÉIýQ@íÙS¿”žRÇqOÎ!޼©ªÛfÐ= ’{¾žàùw!øv£Ì;ôï:Àæ¼Nºðg‚lŠî[§ãì%JØoÛѵGCg°öž„ã–~^'ÀçŽ3×allŠƒu,…‘Œ›ã"H¡ ÂÖXtó“´¸.ç’Ò”`BÉÝĶl"œž êÁ˜ÌÈ×1Êvk±pà›C#.Ç$ê˜aÕåiŒhlÀØoBEZ€uO•z¯WŸç ±õ_·×"™Cé)°—øÂd=osß`º…@\âKc!‚¿Ð[\¿ê‡³šé#+ºv—XÁÀH“W¾6#ëü+«ówë£èÃI!ßv${BÓÙ‚OÏ~Àï:ÿ J Föæì:ò6,#-÷Ö,ć¤zdx°_»ÿ?áºelúÙmmÑkM¦}eøÛ’ŠÎK‰5CO¥"@á‘Du…e $^—=_»¿+çdíÖöÖowþZŸzþýƒWŸ,·¢ÿ à ‚ŒGÚyé6;MQí¹[–=¦$«Ô'Èi>gŽÇ „òuŸÎZËjâ½_©çD[)Ï'Q TÂ%@K/]Í/‡¬Üç™ØئQs¬§ûæy®…Ë'ñr¯åz`E\ajÉv¾šDWpq›Z¯sßüAkN:D ³X3Æs‹†t–KB»ªwI$’2ïlJØ‘ƒ¶¿?^½CÞý‡ ³r?ÛÇþãÚgÅê¦|ƒkÖîIˆ‡=CŸ½|µ ÔÕ |ùÏÈÞN :Î>í»Y‡zY¶N3$±v¨¦Ù{aÖÛoÓÞ~”#.üÏöBNÓtK¿§ýÝ;µz?²?¡b# ûÒ†T‚²ª|æëüsRÝj‰yó¶êÞ;CsJU¦F£Ð5PæúíTVnåM\kð…Z€Ø'*Óá.ág‡ú¿àü½ü±ÇʯÚz\[B™ÔÔë˜e$¶#6ð(†¤Hx;£ËGt_“p#- $‹úMŸôår?§W©µcF”E‚Dd`â\KÍe—9Í`?úE¥¬@ªñb#+$‰Ì†æÁ«öqݼ?€¦GÍâQóõi¡ÐS\ÄØú„(4üùõ!»î¿³À‚A¢’Jd@ߨ¦ãz~³BìQìMNØ>‚c¥²Ýã#+¬”@D|®hd$”G–çBÐ$“%W 7 ë…Ïa€ÒE8š½1ßeAùä«TÈÕìÅÃã•êÙÍr#.¢£¬ƒ½vA™ :™…08 Ž[#.hÉ0è¬Î?.ÿÝjÚdÑst ²M.Ôùô†Í'/º,~‰•@XSQ#.D×[Ÿæâ¿›ß´7ší7CŸŠ@¹e&PòtRÝòPE§½²Ž™ÃzÍ–Ùl„ØÎ`–bÙ0 °ØU„ÉD0¨Gå?¹åúÏ86 òÅOÉ X“öiªíZ*ª93ûÒød¡Jýn0²õ#.æýïéëÖíA½¥Ò“ªTˆŠ"‚ÍÚéÇM1uX£ý?]ãyd„­½c¦pJèÈÚÍÍ=kP©’#-éWN>/¼&4Þc–er•@hºu×’HÜÍTi¬Rb¦]jiØZÛiæM<šrÛY[Ó+002F]CzÂ7‰êךÒ5bÖÍÓ1ï à [ÉdXê0ÐXQ@df›S)\ÒöõôþŸÛ­Ù€œ´m(<à/ßþÏת…ÁæM3žÊfViýÇùÊ…ÈéhãÏÇ4èós7@êª!;‡x'ºÅéɨí¨ñ%#+‡är#.¾®âéW@üöd`PŠ,Xâʸˆ"}üJ h( éìùÃø!>¿È~6yóÑE¡øëò´%²9ÓaÍBtTX Kö<ÇÇ–¸—_ ÷_·øM#-46ÄùLGb!i¨m 2E²'B«½Yf°`’ ‚ÈRÙAI´| É’tÿÇn5ŒäвVû»­¶ÚêTõ%tqÈ£0hNJbF1-QÕà6àiÞ´Ûz‡#-8ËåÓa½—äÓæyF'=§tPR™»ùpýúwdî‹âm+Ý.^†JqÐ3xZśΩ†§\C¿HdôæN‘U‚€ÅdDŽìߢºq›µ)ºP.n$‰@R-³—KXþá9ýWúKõá¯ÚüϪØ>¶:œ/Øe‘ÑÛßÞЂJïûJÖкp°¶ýXÅ´\bü…Óé·#ýÍMùÏ@/‰ÍÙó*þá>¬öK<¾eT¢¤¥)E7É´†„8ñàïï‡ /P s`{³bYP¼P ” }IgJL«ë7gw›nA½ #-Ø=È!7ð_t&ì:Õì| Rªy! ¢†©qñõ#hdRÓ8T€Ù`ùþEi¹%‘À@œ<}Ù®è¹Y7q츷ÉZ4Ôþ¨¦~ø+‹=¼pëù¨š & € ýÁú¾œnzðÿ^?2×sOi«ýØtê(Dfž?ÚågAQ_ÂÿJø·±ìDK>” ´AQ/´Ú‰ê±ÐÁ÷pÌ@ž}>÷k¢O‚;Ž€sú€äØ=ƒ`,!Óã íÃ"„@ˆD?©è‘û>ÜŽ;|Má÷ÁæçÂå„XÓ˜Ÿ[ö¡õ7UÖiK½ŒõCóÞóß¿É>!´ò>(ä ‰¸<@Þùø$Êd”lÄ=VH¤×ï¶¼¿àzÓ“½£#.ØôöšsÜ­C83â¼g›šiàÊÔ³LOM7<~KøR¾_l.KidâÀ]MDõ©šf Hz±$’{U¸*y¯±ôþ}/j¦q 6P’#+ú¼¿r?’E>ÞÁS²È­Mzº™øFóàòû¥¾°¢tjúé­Bãe7Kµ±’´÷÷ñ™À¾H)™Í;s4?£!Ù Ä CÍTF2 "¨•!g^¼ù»m ´f9¼H­ÈÛG¢-Ž %Ϭ®¤ÞûxûSoW76)˜D^#üg–³—<ñ²q{‹$.µO‹wðñ?ï4W"hrOêqµQ•IB¿œí¢¾:ozq”õ8B¾ŸŽ|1pö*4©PTƒU»1øøxú>óÑ{§­ƒôŸ:;TÀçŠiOÛzÃÜzÔ+†­o—mU[TŽP1W0QÿAÏÞïG«ÞD6ü%#.žð(r[#->è Ćí¦ß§ë>[Øð$%mµ}”_lŒÊéCK¥[²×O¥MÞIÌûÐÏðÍ@€n§qÝJvý¤Í;ÅQùÌ!vÑÌõî€ØäÀKJüü¯O5l=;’à#-7#ž›ç΃ƒä>Ë‹#.9…‚¡háôaƒõÊÀkEµ¨Ïâá]~LÈÌ´ x™Lo»Ñè¤6;R9‚xx“âuw‡ììwfè~áhàòBÄK DˆÈ,"#+àl&#.!¨“ÅÒ,#.uË%úQ°êRâÛA8©y„ 8W7w6E£Ý§yÙ‡ËZŸÜ𠢈’hC/AÜ… k£wìÞŸº}ôRNUÆ 8vaˆuA:@@Ú`)GžÂz!}»Š7wD+QæÒtè®Ç=çîÖê·ßm®coªBœ„4Nc@êÂÛ!A`ÃÕwm€xO•Q›$é>EfRbK~Á˜¶—jHNzÖ#.ÆêÁÞ§¶yŠ òãG½¤üü„J5ö‡9íùyË0žúi?`˜9¢§ï„Ùó…ZZfczf®BÞ#J/BBÉ*É$“ž#žù7C3‡Ÿfµ!@ƒ.*ê ÞË‚^BŠ#-tUO¸áÌú9QH}$nB=”tɆ¥Ï,œÏ³ø/TdAèmÚ¤#+ÃU!ÙÉ5ÈHD{¡Èö ßì÷ê‡YRm½Fo;wîG™t‡ià…ˆÒc¯§vè”D;ÂHB;R~Pù„?t3{9üûÔz|$xË#-)âB0®¡á>þpŒ/Y9tAY‘T–v³Ñ $Khªƒ§^ºX¦E¤45Ü©ôÄÿqü²}÷ûÄ&ÃüFh~´^?VM#+¯P–CB?Å¿~2—b~²ÿ§Þ{?ˆö˜;ñ˜/nºëHJ{\]×åŸ*ù»sH-©Ê¶ý[* ‹ûFúÚª ƒÍSyI˜ª@Á9±…£Ë#+õNc8“‡¨H“‰.b ˜ƒ&?­_Ö‘Ò@ƒîq§]ïzþ°¯÷ǯKeÏëÓj­"”„E  á"ÄüÞ– &fø@ \zŽ¡ ÓÍ#+ÐÏï îÀlŸl0œ÷÷›x)Јxg¹^àü^¡#+€Avý_}’à‘U0™è®§§iÏ%:Ã'Û»Õ°{TìÀí/\MÓœô­¸Æ¤pÈI%ú*¡ ¨ :­ç`r²¤ðÁÎù ˆªtç8—ºÀñ#-Ü[P@’D¹†$ #-~ ¿Ç÷4xzêpü>^˜¡îÚªIÎ6ðÁ#+UP”!h漸³|›šÂ|bÂpœôSÊáÅR©"ñ#Í­(¾Â#5ñW#+X/± ç <#¤¤_¹Ú5»ªsáßf>¢¤‰­B®T;‘,0ÞkŒCõÛµ¼æ!…}dÖåj/Ëÿ=p_벓WNÚhm“F<÷w#.£ZëU)U²Í‘ÅZÆ:(…q„¦ÛRÄõ¥#-€´eôîq›{…¾æyUô­¡Ó¼=pžàõ=;ü9“­ =AÝq.ädH%VeUÒ-œÃ‹Ã³‘Ë”âtîÜu›„]å,¡'ï‡ì>½ùoª#-ù-'  îv‰â¥'«Jò䩜?¯‘Ô1}ƒéF#.àDëlVΕ” BBͽwP“Æíß Þgº(ëO»·×êù÷>ïրзMöe·áÿ#AkL·Ryc€[ÿ^øÚ¦#.Óè&*-/­¾6sûÐ[£7èØ|äp$ò•dTð¦“ÓáE±‰iD…ó¢ßŒð_ë£àûo)øs3½M™ƒËý‹ý0lìë„·þØsÆr›ñЈ²»gêoÏå{ÞšÌxä»uÛ ç-QÏ7SdSXàÉÒô-,¨††IŽ­ÍCcbÀT~A¾h·Ø3\ !A¾ERT»2ÐáÜ™6 –BÂG v*¥ÆÆaÃí§ñ­àSR÷@Ë©—»ñCz‹Æ+!›Ê„‹Œˆÿ#-â/€vpïMÉÑ:ž}é—…`X;Èï4=Ga©ÉßÕ·F‚ ïú·în9'¢Û«-psbgxCJ©8Ò׉ÐÅüè2*îDºªdáƒOÉM#+è[Çq[¹ë#+nbð <2B„³’`Ž®Y7#-Ìt,öÕ2ö‡™D.#-1ûÑPt!Y°WÝî÷‡Æ|®{öHa‘@#.¼$QdI·¾—uÛÎÝCÅxݺÛOØßpѹˆ‹xmÜ#þW¢Í•7[¹ˆm£B2ÎŒ'ÍdpÖ±x.Rˆx¶"5ZŒ“÷·Ð77‰ö°JøFˆæ•§¥‹^a´»^ÙB†'WM%dÆ@Æ42Å÷}ý'Õ×H•T#.˜cvZŠ,AIö$˜œŒÑ;þŸg¬}põJ뵇ÙzTÓ÷X~!s1õ Ä!¥.°‚wÏaõaÄâ"X"›åMWáÛ·Ýô™#Äò9&ŠÛwÌlà?g®¾;Š„„›Ø’×å~ç˜í£Ôí¯iØ8GyI޾ö?Öjn=@çÖæ(ü¥åañ"=µwBHD ±ãµ×wu+ðKy»Ï–D“eÈ¿„ýûX«A‡ §ï4©ùà-+” Ñøá¢U¡ íõ»µÓ˜k±ÚÙý:Ám§¨”Óh¶*›l‘Ž ÉAÆCZK«6’E.fŸ°ëÿy?Û‹OÉC³¯²¡) ðOsp»öäÒ£õ…ÉÀöê#.ß~OWì~g¨úQ×&­ÂšõR€#-îôuÞ1.Qò`|þ¹ÒCÙÎò~€iÚZ|ÿ´3ÊgoᨿÀ¨yšägÝ'ŸY–ÚG )>J6ÁϺSð˜×éI˜‚dsëÜmÿl$H~/Šî@A*qãñÐå/9Uÿ›qÅ÷”Öh!!¦eC:Uù1ïuþ½›<M´;t=áRkÒÁhÄšvZð¼+ ù]'ÀÉx¢™H1"xŒ¡ Į̈ZȪ#+’ÈÉÀ²Õd7Ò”ôØ mÿÉìB6nnWcÄ^.̯š¬ÿ‹ù”Âpzÿ±m`ørFÄû<@mÖµgÞ‰J¶ˆ@’ƒsÚ{Ðë櫸-Ÿ‘CW#-"Üã¿%w¦›úmÌ–º´&„¦¤ ·ÈâsËû€#Щý½}%À/œZË’.ðÅKûéü|ijFÿ»ú¬+ŸíÍ,D˜5`P”e®jMç:é™lžRç›n¨¢Ée•`”Ã*–È~b ¤² 4Ýa5'‘З‡48ž­«5 ÀªÐ¢°N„LÂÒTÌR¸Â\†)ŸðIµ?‡Ð̓c(ÿÄf@M•!MlÇ^ ÂÔcþÐcb3KLAÿf¢¬O2JaÆÔpšGX™oû᪚òâX91WâºĞƭØîä•·p?´€„0YR¤)„ÀOG1ãM’ÉDqR¬*wÔÃðüýõ6¬w€‘9ïs/ ¯y[£¦ïz~Ý@Yü#-‡ié^Æ3Š0BA_ÍÂO„½#.™Žd“¡Ñ®r¨¬÷4ÏØÊæÙgßÒð›Mÿužàûq¨aøÞ/íç™–ý§ïT–¬–ÂÒ,Ó•L×ou~ì—H¶s“Ÿ÷’ [ͯ¬ Á\À9wr#.ô<#-JvÕwŽ3t•ü#+š‰T[_‹›‚Ù€U»{¿_UŒ¸i»!päÅWi”J;Òö&?@‰ÇçÛÌ”nC—¿‡Éü5~‚=Q´[wa䞦9'/•!`é?ÕÐè}ñŸ‰õ—qùÛ*Æë§Ú}4þš Yé#+  „P¼#.…D‘çò'^Ýb°™M˜I)`§êà™Æ$Œn«éü3?@ëXµè|<ït„žU „¡méÚvPHMã×ê™»çüP?·žçû“0Pz ìÀHsÂÇãÉœóêàùLñ:ºk€˜àh\:ƒú:Pó%õ}E‚³çKì}Ž&¾cF¦Ñ2$Ë£ºc}öó&Ù7ËÉàȆDÄ!Pàá³y}œýòy‹g3n#Wø¾æùh ~‚òLfë@¾Ço|àß&íŸãÎyjtî6Åvu„;a#-WÚ#.aÄÄÕ@óa#-ÃGÊO¾#-M‚̸8Äj#- » ¶ùŒš…/ÞW‘x6³æy𥓓µdOs9ŽDØ3#-`˜–4Û(xÊòž&öu¯õ•ãQ¢õÄ?ÇS£Í(8dá”GjíC[½DÅKYAü“·†²‹Áô)#+ðÁ=`ò£{|”ï“ùÝé9°•#+„Áû3ý™ì˜ÿ¯ø[ŸHåá¦Öó³ôR…ØëÅ/ì%r¤Ïçm^]hÈ?–õP¡Ø8fFDQÎPÖÈ2:ÚÃÁØքè#+¨±F$Ú:Ù‰JlF±D=#.6BE/´²>r0’(ÏæpÜÏe빿“ÒŽö¨ª$蠟ß÷¯—ˆ²#.æÆÐÕ‹…Å&{õ²g¯o.›yª?«$eê—}×›ß_’¡*OK?GOô^x\úÕÿ+Nú§<ë~RD¢Â1À3ÑF%ÄE^Ö[¨udñÒû s_’[ë{™kÉ*k0(6Õ6ÇÕ2užKCÜ,4u2ôot«0çS´Q…XÜN³‘ÇLâg‚5ÙýúÛn†#-,2^0qpÜ:µ”Í)Ž0ìÙL½]³ÛPüÌd¯rÛ«8l.òà’ËaÀx%íŸKÓËËK»ô~“Q¢/Ù?ëF¹í×S¿¾q§s8Þ¦.#.Äø¡ºS‹w=­Á#µfدÂÛÊóß·`;Ø`NîØ‚— §-âOZs‡j[xP;žSw|w-u§Þò™mM˜›©“†n™ësÑÔኞ¤9-ÐÁ@Aa4TÍ _®›f¤¨—hg†Çé4Θ–ìE‡³Ë¥7õÂyÀîÿk²AJヿù-„0Ý–·ÁàbuXL ,þø #+ ýãÙ÷÷ü>òï²^\7£ ÈÄT0ÕÖAÃJÂÙ%1~o&ÙŸGÄY+©_ÞÍV37U£ï¬qƳO©üµTì}ÈçwƘÖj6Q9DKº'ëÇ‚’‹ÿS®c/×Cäøw7ŸÙw9×Nµ#.Œõš3Pÿ;Þ:Û²_êã¡<ê!œÚ"×~°D»~8D„›pCK È"D´Œ²ˆš´¨‘œ¬ó\³†w}·Ê«–ÝKºf):Ÿêì n¨î¿\x­ç~ rºa'ÇLàœj-âQÝo;ëQŽ«`îòSãÿ^ªä›ú§®[Ú¨ïã䆸‹D¾£Ñi¸.Îzyl±‹ÏìÛå£kéìÔǽ·+:VeH¿¦=GÛ€ñ½¬Ï^•¡‡#êæXgÞàæŽ`Ñk=s²5zC†€í‡÷ž×l" aJ#+°¯›Çëd?bBã«Ù´7Då×ZçÆÙR¢wä Ä-b‰Š]¼CÚ:ÃJßâVwÏ8“á5-Ì!ïôqÑ+‘MS©gßgA¶H Lë|¾¾csw´.ݘ¨d̆A.t#-‘{P?k-!ò‹9Ƙì=>îýÎÚÝ-šÏS!/óá¦D“3\ró>[zäêVú¦"QGÓ÷AÎü¶Ñ,g.ÞH‹v¦‡`9¡¶a$ëèø¼`ts½­øMSè–­zpŠÄhÔ5T''ôsõyÞ, áÕhî"Á±Tú¼îçal’çé‰zõõLTàœ›¤0#-‹Y%)ˆí°º—°V¨sÆþ“®(ä)óDT‡/¾µOqí|{ Çe í>" ¢ÏÓÿËýLú'µý¿»ßçþöVœ· M#.B½¿jná~ð®-Û„Òóû#L?Š#.©ÒÿÛéìì8ë¡¡9‘ÑE_Éçù|­÷¸Î,‚š2†“jþM´«ÁOà>£ýyKä7@þóÁ?¡Ô5ôLAótÀ¶h[ÒªªÃýçù3”S#$¾c#-˼Àv;MÐ$eYßÀ™†Ïêy!®á›‡‘±G`S»@þP£~ç#®ÂÄlÒR¤b„F6Oì¥ÞzÍ\β¥9Õ¶¢ñIUžI„ú:*Â`Ü`£:ÿ[«u®ê¼½ò„Ä<µ@ÙU(%fÇ´,f¦e䦿•t=Nßß9í14?/ [v[‘¹eIj¸wtPæaéMÚ{#-ަÐ.„k.>ÆãÇ„*¥„ê°3ˆA˜ŠÀ”fXWKîéÄu—6/ èG–×| #-Œú>ü˪ÊÂ-!ªvs½¶œÅã¨,×ùŸªoBw¡NÕbÈHòìò@oÓwÎHS§¾-\ë\`?_¯žçÏ`óÊ2‹÷iˆÒ7ü±ú×ç¨W£:úöƒ©žC„¢œO6Ié‰J„à…UBÐÑMY*gBˆR²‚Ëb"©X»’\UꢃAj2c.µŸrI3Dåýc¤…R¹–¢2¿â.ÑŒRÃú?ÓþÌ“"ͳ4•‹ýf÷»bo™ë[ÎíJ0ŸÞ°nÄ@ln?‰1~Ÿûÿä®°ûAÔ¯èCOœañQjîÛ³ÕÐ>0È—ßíŸâ8B$ÏcA™¡¯©¸Ä„P“wNŸP¿¤€óÀßoqwîi.vH•‘áá¿–73÷=  ·Y ¶C5¦Q¨œIýs™45#-h b¤+r†Ó dP÷¯†+mX€÷ñHSк|ÆsÉ(ì½WÞ›…ïõºròÅ(Fs;!”¢Ã²]Sb’T$ •£‘ ɲpùØ´À§}¦Ø9<#+ÜÒÓµ#-C˜ÔÄ]ªR¡Y¹ÌXúÁ9‡˜ˆa¦1MA­&•°þ’¥ýØlMÞ1ˆ1åã§u·{Óâð!¶ý¼¥—Å À ÖJ#.JÇ&ÀvîÛv ŸqÂ5ÑBZÑ»ø°Î¢¼¥w踱p/°Ü¹%Ž\2‘×Òa¸}çG¡n³m²#+êádÞžîXõÞ–V¿<*o9B#.n3ºÉ‘ }åÔöʼM„z’çc£‰™IA¥xyQ8_+íë½Íž +œŠpÙÖñ‹ŽØdÌñ^Õí)ÐòC§ŠBõ:{¸ù;dÂY<äðÜ×–íátÝŽÐ;¢”­&hõ7ZÞu¼o*SŽ)t;ZYw‹[š,’ζa7Åç4E#v8œ–¹µÌÍbBmœŒ¼à6Ù$>î»5ûÌe*Zmµnz:m™9#+p(y°¢OzÞ9åæÇ“Øg^å42ÊOôXŒ‹”yX*R`ŒÞÑð'¢ùð‚ЭÎAÆg$S;NÃȧ{¡Á…„Ô"fÎÁÃeCèþÚ¦¾?v¦}ûƒ‘fSz0½¹ $ ïTÛ¯—gv¾Ïvþîœz•E.œ¬.Š%H‚UDÒÃ|sÁí×FÞx“Ÿc,Ì{œÌÛÑJ(¨//)ã1 [èú7põA´kć7yŠø\¤L:ˆ\ g9­;Í– ‘K]ƒÖƒÒ†ôÒ%qíg#+fxðï`„R”A‰"±@âÕÑsÑ`³·T†»Bó¢e°Î8uÇÉfÐë„jWC.o²ŽDNsz #.´=ÿ(ú==AbMTÚjnz –,r#+rxdˆGžÏ檈‘p@©Ø÷›á’”Mç9“yG{tÚ'ºSlƒ* zªw­]ÇÝNòÚŵ Ä$ˆßÒ#-&áØ;ü‹6/rÆ%'¹cÇ×Àk”Ù‘gNî€NRÎó„ì×-™ók9²¢© d¤!ÍiíX…ˆæP;’!Ç’j<­°œµ&&HÆ v^ ´B,t·ÇÄ„à™¾HoCj„6;À¤Y"ÁT š87“ 2wq3j¦HÁ¨‘«f¢lÖNJð;« ¦64×f¤VâAè8ÝS#ÆN Î,No"E”¤hÂ,D(EF“›#.µR4-+УÌΜ¡ËEÔèrçÁ]ý9©˜%Цf%L̼ÄÎfe³ʳ,Ì’UŽÙžKé#-VÖ½—[‚8°×\c¨Ë—ɪ„b‚É‚IJã|šjX5‹OÝå¿I«+ÍžàvÀ^‚'ŒÕ–î-Ï_B8ß<»ÒÌW:dë™J#.#*ŠœyP)*G¥×žµœó鉈¸]ChœÓ3£É:èžñ#-È5Ží“1áT#.2¼5eИL€¶pŽ‹œÊÛóÒµàÔqÁOEþFg½Î|á%ˆHQÞž—XC0|F‰×êí—q‹*Š]RËŠŠ Åܳž“wSßáz!vª‹}'+R5(°„ÈÔ¸…‹3:IlKéÍ#ÙC¬ã§>8±Øm¾_½žuPlEV¸Ó,W^OXk&œ’†b9M ¢ [t±/='aAb_ú ©æ‘é:Î["¢­z•¨œö’C45DŠùÁ=ˆ`ù÷ ;MmSLQ­GRädL°ÀȉB3§y#.Jd&a<ÄUy£@cN›>#.¨ÕF#-#-ö#-/ßS˜¾¯†FqÌÖ¡¨ƒÈ×»´¨Š¨¢¥íe³íÁŽþÞs¤‡H¡w´’RîWËÀhõXä~#+t’D»TŠj-*ã¶3{iŵ€ñ±ÎÀ7éx¯–#.ËþòŽYÞI!v#+ëpJ‰P80„-Í6­^Sð)‰¦#-mZ#-– Eùı¤¹r}üxŠ# Èl}ÏÁÁAP`ÏÂ,*R#-‘Dv)PâÈ8™#+zÀ!ñ¨‘b „@D#+÷ÛÂÿ#+×2%gKÓ?1èÔÒ͉  B:õç~U¸†wV.‰0ör§np¦¥!x¬„Œ –½Ë £V3ƒé„ÌÀÿ¹²"Ý#-½çyÊÈÊ,¾6>àæ9†Øˆí7q‘ìèobt'e¨8E¯hBФó ]l@$ @èƒa™ÖÂ,}Ÿm3eFBà ש(Ð@>2ÊÖ_LR·>íµàM־܎ý:Ämê4ã¨wŸÒ#+\ÃCÊ*0@€4áÒdô`EW|E#.ˆH,`B(û‡!ÞG#.ÝùkV±­¢¶ª*©ekn•d@‚ÁKò~€¸'§$“Ûnvßd³•G(÷°¡ij¢Iô#+…¥«\¼R±:Ô#-a¬)¤`Òc4 €C#+ŒC对ˆ`Žx(b4Ó+üi¯R"ñ¯EL’bÞ®êñzm^¦õ75Ø”nîÐJ^Ž»æÜعºc»åsr0L$CÉXÆs˜£NZˆ,2*?÷ñÔ#.ÜÍŠ0³úÈ€H4 ²BºÖýZü?޵½_N¥Pù:@Àmþ&‰dóc¡á"xˆª‚±íäÇÆò{}Ïøðsù•ºUÓ-!Ù%%Õ4ïòÆÒ ÙÝÚ_šrñçžæ˜±—ì KÁ>¾g Ü/›ôÃÄv×E°‡.ãçäϦµãåæm±Øòn8dÌ:D¨=çÐx–?³fÏõÓ¹´GH²"2&ôjÌŒ‘Œˆ0$YŸÓ©Ä·+шõ 'V9‰^ƒþBªG{Vkáy꯬±ëÃ|ÍÇTò¡s2S/%’s`(`e±å\ã#.›25ÖÃÖÒL‘XI£DJ… M·Ò§C4°¤¸Ø …Ïõ1@1"v@Ú;‡‡&âøò2^Ê?y¥Ë~àÁNUÝ4}ÝæËíz½¶…D° YÇ€©³AÒ<ÛÊZmàmrUøUªÁÞñ½°…ÄÁ.×™]Üm*‹=<°6º¨}Q_ òÛ³o‚rfÞšxêÆAׯL8CöÌ–•É«n Ñý¿ú½ãlq7ÞRsâ¶ß´«$ƒPgöd˜ªbg5Òc”YÒ˜QÉï˜|OÔÀÃãøÐ‰*¥T¢“·¿ë¾Í¢Š Ô#.mOm&´½ÝSÑÉQì"€Ð5>P Ewƒ Ud@bD@ö#+ôW¡”¯¤¨w²ÌU*ÙhuHœ“Yw[-'ó{x´¶"ØØ›_Â3¶±f3$oR,ÓAx#.îòݯem>Ÿ—l!IK#)‰QB¢ ¾Ü=k@ÔWóp¸¿bšÅZ mŠJ-±Y(𱤖Q%IcYY«X…*’ÒKlkI¶³&Ti³)jÓVÓklÑ‘Ðü¿ìãõó\KIjœíöÊ:꺡Ӏ‰æ'8ƒ ªAAIzýh| ”Ÿê hLƒÅ·õíñLøӹˆD[ª‘¾¶ØŸòWUÌ… ††Ç]6M,”F‹Ô"7/C|(ÜIænQY¢)o8 ˜ƒ›šd:gYÕú±î­øâ‘TÆ¢0Î$žED42VŽ ‚št¯•MÍ#-%€hkI”b/¬X_JÈ™’f«Èý”w—#+)ÅU>Þp@ ´EÞ#.óf`zoI;#.2i­Ú‚E¶¥á¢+«*öD`X-ð°uA~ÏAç΋ÛÈÙœ]’¤`@…4„ç!8óë@¬€Õ(' *ñÆôÆ*² H,¯#+mr±‰-EjæÜÔ•ËnS6©”U3¢¤ÖÆ™d9Ù%oÕí¿o~'z½às–rÁš†à8ÀU‹!$•@5Qç}œ__Xþ7¾ÿqãíŹÓM%¤20âáÒOcЃƽ;Áãá†)"é€m$È>¨FåiôÈ{ÏýîÿÇõ}ÿÍû=?ßþPÒ'¤ãˆCÛÖ!"·:«¾/ƒŠb*€…ÝÀù”0’Ö)ã#+ÁÖìB4P@¢#+!DSÒª¡‘ó7Þ÷àw}},dºÄ×mZuiC1Œÿ„D\¢?´›~­4¸Šd>ÖÕ>øO°#.ÿ#-›<#æÅ…¤Ÿ¡<žìÊKÅaèÝ]„ÇJÎ&2¥;H[üÔyÎþ'øˆŠ‘]Ì ÂnbÓd ø|1`÷rÇ–ü×Öy'[nÂè¾ÖKÁÆ(#-“O[A–ÐÖø¬´…OªÞ‡ýÐÏ ÊfQŸâC7ó‘¤v¼±Z1Ú©D(¢†jkc$øœ½ÝÏbwgyÛ¥åÄ¥ÝÓ˜Bˬ®øw•a½™ÅoÚS㬠Ñ?¼L¡[eïìu{zߟ#+÷D9žAýÔw¯Éã«s“ï¡ãlQôݰþ”Ý1äq:õR½Û>‡éJÝÀZal4ŸÑ.¤aõ»äCÜ|éÓÆd¬so .ý\k‘”ßêÀõêœòP~z£tú.lcÞ툷¯¨ jtŒ€nuCå¾ñh`2L>!äѤ%4™22ežÙç­o˜?b¸‰û …D›xî÷‹;ƒZ” ØÇZq¦dVŽðõCš)G#.§ýùwf³¬âí!’ô!Á²™2œÄ8:b±½ íɶ¯I¤ Ý áãJ7ìSÈ€ÈBÀ"¡ñ)*T=ƒäžTu '{òsÌO)ðˆÞÞgJÍ’_‡ò†„õ¬.?â?ÇëÊR²šª(œNÉ£ ö¿WÓ~*¾Ï£&Çä«»µÜ¢h°Ëkìýoë÷D½µY3@2ŠÁóÍzvùÉ8÷-§v2éÓ8C6r@£F^n¯ÔóÉ#+ˆŽƒëš¾––Dhpñ‘þ/³®†‚‡‡ª`~pކ9ƒëŠ@/RÈ&p "°<Œ‚)4÷eʺË/Zü¬¤ ö­xCLQ“v[ñ²üž.ˆ‡!›v_~#.ðxå ã|‹ÙÈÙ.^¬1º¸1T¬Ü.W ɱñã½1‘MP¨Xà¯peå^3Õx›ùdr-[â;«‹×Â#kÎ…‹{Û^1Üè&¹Žùì~-\QÇ#.{¡—œö@u“Æúå×ñeÐK#-Ø?nUÜØRÁU‚ºëx¾r€¢•8µHð˜[d¨FP%Ê;G¶|uÏZèÇÓk£¯Ã4= H¹¢()}±¸°²½Q‘£ÒIe’±Á‚½X…1–ÁÉzÜëg^M³µ›W@g#-Ÿ†Á~¯0 ëvÍVE&ØÔÛ¾}Ñjv¦ÄÍ‚±Ú·e›ÍvujS»å%«Ð)‚>#+Š0ƒˆ¯36pÛdzSuºa¯•³fXú†gVvÞ}xà ,L˜:žpC à#-ýϹƽÝäǶ#-þ-pÝ×E„çËféG€ævwÐ#.!p|}ÅCáç#.\Ë™¸ íÛkllvèT6ÇdÆï—!u¿­9¼î,C4O"£ÀqWê ½ûџϘ°üw½ðdO#ÌìëÕ×ÝS`­¬«x@¥Ð’Hºnúó† [fþµàyËDå|‡#eäCM†Šº»‡ª£6ìç»pÚ.nûnçãCØb„¿T"ÍþÎAŽa aâ0GCÛjßÀñÜýŸcàžf@9á¸ØàvíÕºŒÙ ^ѹ³¹±,IÈRhD°»‚ד%êÏ’ZÙûb^=kâu901”6äVø æðã<:óWy¸Ì‰ºŽ¥†ÆŽ|9œÜNÒ‰ qh0jnã¸dª¦›!9¹>}‰òá³02%x½Æ¹ÖÓBð|ªˆÕHXîÌÂ\Ÿ:¤óFˆÈŒy4ÑY,¢›ªÜó-òùc‚¶:£l‘™&dÈC|/if 2„‘#LV,‹GŠŒWC·{«Ô~w¿³»NdØÛ°»ÔLuŒzt÷’Ù#¸PÉÒ!·ñ¶0…á#-éìúÖØ7£ø²bì0]…SÓ¾&¼ú-ª’kÿ¿òùñ9„øž[zýäÞ³Pƒø|•©c84CVû¡›’ôÑôåMHÚ?_/s"<Íz’$â„Núé|$©ßâ•Îõ¿å¯-ªÈÂâÀ›M§&@þŸ~ÝÂÄuû¸å×¹zeQ ¾×N¿–Me®åTä—•ÐNte9Êb‘ ¢ª"ÀE¶ºÕÌRÈÐÍ’Úése'uu6r.K;ª×RÚíù<¹ŠH6% ÆqŠ”p­V8⡃(”DP#.4*(Ê.шº#+ØE` ‡" ÍÀ:C#T1šš¤¥,%ªe«16žßã®Ðy0õønaMœ»SVv¼ú7›Ÿjû+kôøUý›“ìòD(”¨ƒä”‚)Pm ~Et°R³Uù®º¿2¸’{¬tC=´ D6È/ˆ{N*„|ý…È}V~ozx»UÖÅ󂜶‰Ž4è °¤vÖ Ó%šy¤Ñ‘³¶¶q—±°æ´Sà¨P©Í#.fÂŽªÕ¶A†ßRŠ{?Lé„mvîx§¨#.|P‚‘ݱ  †àŽU®@ePO\7•#Q-Ê»€šc­Ü|4Ó^[=Âx$šéÉß$áß=é4¡U ´6¥µK*ˆ,Œ´ES%‰D@Q Hn‘Œ—3]ÉÝk¼ÖÛµ-®ÓU6­SÛ`A†!%>'“¢¶Šf¡Ø3áBK­Ä¶ÄB‹‡C©Œò#-F T‘Óƒ)A‘BDIîÍ>p&ì&ftC¶ФLBühÇÕ»'®ø —xLwï¼ëçX[I)³Égënª¹:ž\/Y/Ñ{ñ…ùaƒ¯Å^).u)ÄüÍ)¦¹h¸ˆ"‚Wë$ÚRƱuN¨ªš¯F÷#+z†ƒ€yì#.êp˜èÏ?鬙|šç;.äꌦÓÐcn.¶£ À¢#.‘ÈJ`x ´`’5±ßÓ<3X]Ëœ[!’´ø`–„”ª#`ã7ÖI&oçoÏi g[&4ÅS$.wy}ï3¿À|ߪ#+” ÛQªŽúõ&PÈ*ò#+-ÑBmR´z… ó]?–9›2&Ö,DA Œ#C™ætc«Î®’¹j\ÍÕsÞÆf:'3A‹Q­4ªËQ²âKÀ‡C}>œŸÌÓu§9ÞN±ÓXff«`; ~Õ¸¼`ù£ VÞøÔ%Àá[ºj‚‘;ie.xœ/nõ&Œ‹‚¥ÓµlÞ/“Q²éE>z˜7æ†@Eg[lœm²dNõ™÷vç)´Ž?£§žlÌr9—ª’I"ú¯Kq¥1®1™zGy»"@ZÜ4›K¨l¢ðâf€ØkÃ%[ªÐm##+Ä ¤4Å-C;#-aä w4Í@Q†Jˆ„xV @HX&;²Dæþ×6»+ÍÌ83ãDAfw!¨ÒeŒîWhæXEêU*#.@#+¨4»:˜›æÁmM†û7—c•#!ÒÀd(„oYEu0ÐÆ&DÃ}F–´G åÆ ÃP™Âv2–޶,fƒ7LÁ¦Z­ì¼¤[›rSe­ñ—eHV#.;Fº”°ÃÃE»Aåùsœ<°s¿7¬*'¶»‚WáK†|™$)Þb³§&¬(i’€ÄD2^è%‰³dW[&ð'¹…0›"ô†3˜ë|dµG%‘duÈðóŽõ©žÓ±ð†þF‘†(ÛL}æ‘”ãÓ†ç#ìšC˜ <’r³…ĵƒeW%8ö^²K†EŒ9WÚ÷ùlò÷÷#íPÉ|9»Mœ’×X¸õ×ˬŒG.]M‰Rw,~î–yDêuÙÈ8Љ’ÒnQ@ª•D„¢sžÎóýIƒ5¬Ú¥m>O«íØðuh –HxrÀ©dè8…'¶#.¶ P¡Fë^ÉÛ»¹öï,¾wqy„ÉgÀ#.Ñ0&(}ôeKÁLsש¸wø~xŸd¦« #+c,‡€˜Rl`9<ê‹d¿§]@kÎv¼öuæõw€…çšèK—$%#.’êO#.WIˆsõØx‚.¨²H ‰*RÛÖÆÌ†‚Ð#-`żP‹C©ãÔ÷ú<@=PÒ“…Ìmb ›²üö§ †¨Å_d€v2Ù dª)Q…øï“<-1ýñî!¦%¼šŠ€î5ã㳂êÖšZM¨;)6BO"àØP½‹’A±›99ÒM óðºâ›º’´Û.d†Ã®É¯²àRn2a=ÔãiÁc¾¨äml6ÖQǧ,ÀË2K³@…ÆKbe#.¹U Ã`b;‡Åá†ÈÔZdõ ú^ª|(™<* /ެvÅ%ž¦ë(¿ÒÉ2ªàWtCv»Ø¯$ÔȈÆ1oU•FÖ@6<‘œ…NUƒM‰Í'õìÒ1èÔ1•°n‘PŠ(:¨}öxg.0úÝ]3ÑòÎðkof£‚kÏe¨3PQ•{2tªs´Ö1­‘©iìžy¯Rš±k4!Lcb i¹ w€¾YöÎ#.„lateœÐÕ˜d0"‘uMf¹ ºrYÅSˆã´¡¹ ,#+óa]:UÉ€c.ék–?g‚:Ý”ÅãV»gG¾I+=³¡,wuˆšuS‰—§$Å—XhÓü›Y¹ /zóºšTIqàŸgNá.ñ´Æhë³ÁÅ®°ÎÈça!*w“w„s˜Œ#-¬Ø¿ÓMþTÙ ÃŽ&éÏU•“tƹØ#-¢;B ï"¦Á#-A‚ƒUH”`D¨“RŠ„3AÀÃ…jøÚ\Œ©¾Y˜åd)ª¼#1,Hp1J€ìÉ42yx™Rª Ð§l8¡°‰!¬M¤Z,ØvË ­`‰L™­#-’j4(†é£m !L$ÔBlÉi0 làœâcâ…áÜÃZœcK‘¶óŠG× Áœ5ÐÉX ˆdˆˆg# ebÐÂ4‰šŒ‘KCt4q‰ ÉlYbÀP§CuôSèXXd3,}ÑÞ~5'Œ»pç¾3ºš™3šjGKÔ’##. FiŠÎ`Åêö«±Ö¢)ômž†ä¡êsMB3³eÀÑ ú˜geäŠ@àð~&,~Xwï#.üqí#1#-Ì„ M™q¸$ʯ®ì›Ø\ˆÖž œf”R)……UÆþ\ŠHspŽH˜Í`²¥Dh‹×¹w•#.|Ji°)brõ)Ä#+Ê[ÛY•èfÃcZ%†61âdÍÞîÇE!ÑÁ›þeö.Q Œ<ÃÚtš“×j‰åU‘ŒQ vMÌt&]Lf¶åÁ§GlÁb«¢pÆÐ†²¸ðPî=F)4+*b‹%ºi«>‚f£.Æ>øm…ÁLùy>ÇÃŽ6Ìt¨xÃó'5£š|ža÷íªXwÊt¶­Ñ©jwve Í2Û#×ÞÙœ¤Ho£¡;Y.EžâF޽7F=ާ!ê˜þóJ1°‚rùo7éÖeŸ¤(°dNy`nw›¾^®ÂfžÒš›ÖŠ#+”Qan”X(€ù‘bsÁhIŒû=´¤€kà°@PáaÑfbˆ©5BäÆŽ™$Ž`%b&€eXŒ;;0–eÌÀ÷-@Är€1#+ÝðæƒG^ŠNÂ7D¨ÈPA##+!¦ÀÂFB©’»ösn9Ö1w–LM¡Zæ]ÑZCÛ‚5ôjïA$ÝEãõ¢Ô•qŸ9™#æÍÝ À†Ú²ý«£0ÏëmªñG’?‚96Lä[·«U)¼–dìóqts/‰â¨Œ^¾àq2Ñó½{Š6:HûêæÁ”§'âd³2⡊r—æì¯Ú vÄ@nxNë(N#LõSßµ¯Ùh$¥JÚû—<[rh­hÛ^*-sZµF¶®€µdˆ$(h]§V£Ûê—˜Ä#>KZ!¦H{›÷ïÉ$·"`Dƒõ‘†f_¡k<ƒ<¦š›+…ÏCäÙ!S²LÆ$hI¦LÊe+L©“,ÂiŠMQi3PJL%­*1Q4Ô¡’”bP£ZX×·nhdÖ“IE)š³B™13LÉÁJ4"¨¡’R†‡ÏÝDFÒY)4dIE”0ÕÉAh´¨ÑDFFlb“&´Å5Š™DÐd¤)¨2•i Ó6šf4”г9víI´Ùì©ÀxXÍõ… @÷¶ù°LƒÙù²¶ª¿¸Æ(|-Ù|?~0hGë)ƒ»{–Å’ÉLÁó7Zñ1&heG^XÈ`ìÜI-¦Ã~»¶Ê&‚Ó(¶€þG’àL„uÌéÖìNÞ]êh¢HÔWñpùiêqÀ.*CÚ.) àø6#.f]´Òq$ñÁ8íhµ}.†´Œ'ó¦øï×]kZì¿)Æ7"’Üóqªd¿´N…±îõûÒNu`µºñ\ÆGA±6Øc†FU2#.T„æ€iõ&¦E„ñ‡šE±dIR35“h¨¢6KL²•QˆÈžÞ:öuïðñ311Eûk2ÁA,?Gg LÀØRŒ߃;>#-{3TTÞ¯ë#.Ïíi®WÕL×»n³39S1%šJ’gárßgr¢õ_Ò$F0&ïñï²>ŽðÚzú'Ó½BCÊnÏS™á»TUS`%wÌì š#-%eû~vn]Gzgס…ë¹b­sHï³ò3¨‘ïåuƒï’UXuj0 T€Á(‘U$I#+‹Ü™1ë® ·?#+JtvºÛ×%YÆ;œòå·6g‹œâD•„×m¸>xÆ1ãÉJˆÌw#‚ÛšÕdqÝú;Ã19|xuÙô÷ÿ,ïg!ç[UOÜÖ®7T’ÛMôóë†öœì…aíg‹]ÚoÒ#.{bžqø3ob¬Nƒ2‚]HÔaÊ+î¥VTCÎu¹CN(*¬ßi™”Þ1ã­Q¾ÙA:½WÅ­ì¤ác(Ó¯ç…ÛöúvF]ÃsÀ®tióá×h¬!Æ9-͸žóF½K–†6A›]LòQ„Ãð˜JzŸ ×$=ï‡2𒕇´âNȭήšÆä´‰ðµý£“ÄÙ¢îaAÊi£ÞR{#-à`@1¨ú"gϺ#,Ù­h(×vFEï ö¨&Ôz=ÇX÷gSö{ Ù#+Û…&xWn´C³#+#+¦P(# :ÁͼsMDö׳¼à¶>¿g˜ÁãA8ýß]r½ÿiÕi:ÒªC3·}{f¼JhÞš…UFP+º•¦îF8/½?DüÅæo†‰‰ôJJú¶–<#+Ì]è–ªU—:Œ¡,™óØ™uÓ5'YɼØŽGŒÌÌ…ÄM¾Yo„“.?“ã‹ ` ˆiANEq­€+þDlQ÷¯Àm2#QíP¨9Š˜v³‹‡—úÚ”ÝDÛÓ©=O­¢;#Za\kr1Æò³-àÖ†Ù“UÖ¡•ÂŒºéÁ½Íf#-§ðã3#-ås¹¨Ý:LçQ桦e­¶k/9¦r8Çt,–˜¡œM5TÔ¦§•qÛ[Þ‘Ä­o±Ë­S1VÐï2A¦Š‡¤fÚÖó‡Ä¬©n˜´¤¼>ù­d¹8Ì­=7KŘÞç±úå4mí§YEý‰‡M î²²pSƒ1åïR~n­ë,+‰©™mfŠyp³"5m„S'»S2s¼”í%gn¹ÕÎå‹^3¶uÐt[Û—X‚VÂÆÏjré:C”÷/hÚr²]æu¬Æƒm1 5ÂÉ#0MZ™;¶†}$¹ŸÂë¤Æ9Ü‘UìN‚…81ƒ*¨¸¨ L\¿Q•¡ë#ÕÙÛO<銨ÞT}ü`e¤sÒ_%4#-ƒp³ªj¬¹´`|ñ$;Í0IŽ³Ò‘»0W‚4‹ºUެ2ѹàêM6Á‰ƒW7˜°€‡ÜØÞÚ:æ¨íriò(Iè×ÕzÖ.ѺŒsÁ×}Á#.3¶Ü>±­-jYðŸE aÖH˜$WÝÉ­„ ]-o¶ W.ÿ{¶ñ01€H´“·¶£ÙÛ#-´ÍÝd«›8»³ªîôÈó‡M´e¦ÚÒj#˯i̹çÔžz·ƒ[Qá3#±ÁÓwqV4ñ‰4.œQoDJÖøÆú½Nbö1\>*uÅ7q3 ;¡Ûm“Úz«Ì|–ØÁB7Ìñ¾}1¬mCïÁ™¾rð#;B~×’tžð%%Ù¡ò𦵩ÏHÇ\XFv·š38íÄpb‘˜ìÑ«·%QiKŽ#Ê÷ËæðíÑæ¹!z½B¡4q’´ûˆ[Ι¬ï¾õÚã%¬ÇQÃîÒòNÊJÓÚ´ &FØÑ Lc˜ó RÖðô-Èøki5‚‹(}m«\à‡˜}Ch7jÒ¹#.aŠ#+Á‹»H ÚR‡K\3d`ȵÀÝ‘/MD‰ ŠMT^ aE¶1îd}´Ô)«”y[6Ûrõ“6ò+·8g!Žv{QZ0V/Q Ûç;£d3$! –ŸEd5¬\nnÏ^SÏL•&Ë¡Ü,ߎ]Ì•õO,ºKÝrž6¸pE‰Â&.§HNL¼Ü¢îâÊ7L:àë˜8FM©ÉbÐæôí´oTsÙµr$q Ëfa÷v„ÄfS¡.Lt4×¹©‹¬"S΂Bæâb%0ì…MTª¶²¨vCÚu¤àÖ —!–ÑW#. ÆîqIÂQš¦ ̾Î[p–ÏD¨Ùç”Ú& MrÀ²Ø¥D†ï–—aa±¡´k‰ŽZ»9[>Õ#-©PЛ­‚çÆÆ.·§#.J%S"†M«Lò>ÅÕ?#Ú³šâMZ0lJQÆÍ­Ja¹aB²>úÁs©¤!]3¬½ã90ÌÐB P×±!¾Ø£»ßÙ;Ñx.E…-è†V™tçß’á#!#-ù»¥š¶Ž°n©ÞžïÅÊpás‰÷óN©•-žj9ƒ5¦Yñ9Å´ùê`ÐÂÛ¬Ó}x7[*OfÊL²F!ÈŸÚ£ kuð¸’êaD)ÅÌÌÆ&&ó;êrje‘×À5™”é<§ á̇#-Žú[4MÛ¸æJžlwZÂj:ÆùÙ¦o’:Ó{¸ÜÖ[Tц°”ž!åŽé‹MÓ‹ÄÅy|o‹ÌZŒG3ÔŠ-Í¡æq2žU(*®bO˜˜À¤Ó½$yéἜ‚žRÊØ¨6Ÿ=£:uNÙÓ©‰BI8õ2ª‘-©¢Zˆ¸AŒÄº6¼«,’ƒ¯LmÖÔ™—ÐÊÑáIÃ$,GiÜ›«ŒŒm¾Í{lvSÅZqÄ8hèÁƒANŽ(3‘‘ãQu2ƒ9ØÏ'ÎÛ.eÊfÈpBÇd!’$ÊÒ—ª"ÄU¼‰W#-Ùní³`·Ô:9ŽQ2û®°ÛÂßMÆvèÜË”l7;Õëv#-µÀåï:[!ÍŽ Ë@:Å4cC;šN¤ÞÝlh}°í´˜•%­¶•’`VÎ#[A8ÔÑŽ0¼`pËî¸ÅGE˜jh„7#+¤4¹ÅaZiLÚv0"Ü8‹¢o|ÒÓ“¼2sYà59èe ˜I‰ð‚£JÈ × ¬AvnÜÛÞûðÍ“]Î{ó"ÂáÑJž¨@…qb fa#.Àͦ§!1ŽÁâ!6^”—lÛЛ#-20ËhGZ q€1Ád¦ZÀÀÓ EÉh€ÉºqˆoƒP6Kä.f‡¤Á­&ô3ª½ª¡Pª¤SBpçzý{#Z[x©†Ñ8¥oý2ÊA8ÐTX$GaJt®Þ=#-ÃHÇ¥o3›d‹”u‡SqÉÄÇ5ƒ(7T¹PÎ#œK2jJ3¦"¬ÔÚq†Ä,#.5œpãQÆ ¬#-N8HèPhC“3K˜5ˆl3p²>ÌÍL6.°œ-’©HˆurŽ˜f"2eIY°H.Ý …t!‹†(ËF+݈á2ª7Cƒ„¨`†Ö!d2pæp£vñŠÜbIvò g›N1l·fèÎ2\fÐÜ$#-†D—hÝñ4l£:ŠS1`º eë뎕´5¤r@ZŽÁ¿6âÙSîp#..²æ] ×mÑ»s5ÐÈÚÓ &©HàGM&ÌIĸ ±-;#-;M¦A°)‘#6ÍlÍ9“ƒp» ­ 8Üp¡­€NNM#-„Ψ ‰#Å‚ÿ¨‚R‰ü|#.ê$ŠB(®bñfã5(h¦Â…Ê#ðJD¨Ÿg0CÇÄ>\Ó ¨w+ÞÐ$‘$A‘’2HA A>UNóìç2 Æ 9€£#+AÈt¤R~ä:F¤ˆ@Šº ÐJ¢ÅP ã$&@0-·YyLB976Æ<Ì6å!Ù R‡ë²Ø’|iÛuT˜|3ì ÷í¼JÓhss81ù]—¼é Þu,áÅ¥ÍÁ«”¦°Ýd×LúŒÍå]ˆvâmR®^ô·Xb:ó¸ëÛ(y‚‘´¼q©.Ç\Ægdé²¶¨ˆM„›ïr0˜\`uAλÈÖ㉷yÄ»°2f›‹UÌ! ‚+·gjiÊà;#-åµ?žP5æo.q’åÄHÇzHÏñUÖˆmFˆÐ@¦•;aˆ0ˆˆA@±š(PEáå ,ªò¶˜XŽ¢b¿_ZDÈN׫R¤!5¤C°]¨K1ÈT‰FE.Pøûü#+Ø!ž#.|¡êŠ]MÝ>y§r¯É‚ª#+DUßóÞ,³Z¾òåüv§.®îæÌÆ·]Ù—7]7l¹²WõËÍO/ãîì‹·¦Ñü§®Š£Ë°x²Â Cþ'£þ–"›å16‚.ôGz'#+8Ÿ·#.(Ä™óy!Ñ~$C”åÖPÁœˆµj(€fpE>&àXøÀý0#-ç—nCì^o?-$â\?3ûŒÅè<ÜŸµ²¾üæ}69nìËä#l‰î»#•dÞ…ï$1"Ø…H4e‘‡jm Fñt e ÀöøLfà´kúv¶ M1 È…4ÄbÞ¢UJhtÁpµ¤aŠsĸ0Y¶#$<;‡§,ŒÆ‹JJ!°hñ?K1Oæ†Àë~›yyË–%êá¼.‡½š3#.Ù ïGÔ|hûÐóa 2#."1l[cF£kE´D˜´DÉ6 Ó2ѨŠ%dIº ’!J#¶Ñ>cÃÞùižŠâ*Wbœvõø.Id¼ £©Ý#+M†½ÉðIðƒ(`ý×v"=]¸áY šc¬ÓBKð#.ÄN–£)EÒç#+¨ åHn˜ Ü'“±9þn®à †™#ºX‰:ôˆv½LÊ÷þvQ83„ÈÚÇIÆÿIšF†Ø5Ã3ˆ–ÔpÆúEÈ­Íòr«hŒŒ&EÇÕœ,$zë“>â•iÉÂ:¾§/´Ú&ãGNzí±Ž{nhÖãEÑGK¡]ê”7¢œ¤Ñ¡5-j[(BbÁ­£Œ554CT¸¶\ÿo€¢²‡Cš6ÈãB¨TLA¥ð±c³ö9±Ö8tg7¸¡µÃÇB³n›{…ލi¥°,áÈ39g‹à-È‘ÜÛ[†q£.NBÿŽ“†iƒ§áÛëI´xt9+ÔP•u›n­Âgu³H€ùTš¨ÉJ¢H2c² -¸rgáB°Æ,5AGÒN2ëI<× \9)Ç'–ÄÑp #-‰PNt;ÅÁÖ=‰¥­Ÿ “ŽÒ¦xæ#.d2­ôƒ£ËË…9Y»uv°È3c¢ e xÀ„aLh¦SÐ(ÒƒcS!Ž’N T$€ª 2¸+ƒŒŒ#-®\›ê F¬Í "Ð?Ð@¤"¼T›‚ËŸ¨;kÊÖ³î2!_èÌ“>µ1ÖôÛQdd»&MPŸj²â§Ú@d ù® ŇÔ?ñ¹‚¤üñ{Õf¸`mJv²û­CŸûŒ bbFJ=±MØsÒ0#¡Å<Åâ¾mH¢`™=Ô¶HÇ(ÑWˆ‘Œ#+?ÀÄØ’@Qj‹[Imªû¶¥¦£H¿Y|’8A‰~¡þ¯«¯Hk&²7§¼…üÓ‹"uzúÏæ€ÊÎJ]w­æ·ÇKg¬A¨Êÿ#+7U•j:«7µ¶ª·I<Š&ötÓ†³¼JÙ4ËÉ•j¦`teJ—²Öe:Ö^óm¤ÙCi •¾.Æ¥¯ZŒUt¥DÐÚá®#-HšHÒ.™KÄӃƶ𤲎Ku’a”ß]#-HÛÕngIš™¥o[ñ¼[ë’%¥bšr"Ló:€Rlˆ˜‚¤p1½¤áÐã‹"680¬éju¾¹’0‹bK†ŒJ"•ÑLÛsi Œ}ˆˆ6ƒR¤ê9®G,;n]m®Î3IðöñÎnš6ºV0@ÇHÛ™57ÈÖÕ3³îÎø–†'¾Ûo&“[JÅÅ௉·ÎJ3œ}fwdÇ·Ò+Œ™#. ˆcõu½†…ÊŠÖ‰Z^õœÖ$Ø`N“c¡’æà£$2ˆæŸœ·2K A†*(ņääbøÂ#.nêÝ)@#.H¡!LV#+A¨´ÅŒUrX!Få”{³ÓßT¸ÚXȪRR§RJ…®IlÐê€Ð´2ÐÁ‰"–#-€Ú £J’†”I"D‹"·6èR¦hXH² ÁûD’¬8,,0ÑyäË"zÀõÍ”¤+#@’'#-Üä®î5Whs¶.Ítب$Œc)Z±–J™#p„RÈ2=z¼)umÒÍ.jè–ê×i5ƒt®ë®î)7Šå¯]Ê&Zvžn»Ì«ÉQ1¥n¦·#-F»MFѤ´¼ÎßÍøÞûLGRŠ ³R¤UmÜRÔ`D®Ë²©$,¤È›-±­5"™¯)k¥´³KM%RÌ©«Q§Ïõe}ï^‰¨Õ¢,*fÆÚÚ£mšPŠ…Ë{½#.?ÝÓW¬G`¯×vÊ)m¨q @Ð(#+éɴ)v%)UPîˆɈ‚ÁPÒ#¸ˆ{OƒÛ•ç°„at<«øJXwrÉPÓlf¦Û‡ŽbŸ6 v °Ú“ÄQA|=èÌ °g1‚† }ì=šï¥¥©‹à] #·#-Æœ‹ê•XÏ Ù†£$‡å]±¸&£Í½I´Å*qO½ü¬( 9Ð1@5T#DDJ€]¥ŸÎ… FRÅÛÙXÔ¦H¤‚“Éå—½û³Ðº:F­ÃS.;,ÛðÍÐc}?\f±1#.kMnã#³0…yW Z™LŠëÅ š#ƒ˜pºÉYlŽÓ—ÈÍ–| ¡0ÅEþK[#-Ï[pM#-°µ8×kSk;²( Âi&¼<>Åå‘ ŒU"¡ äùû~¦5Ôü·¬ÚÞÉâu¹ ÷¤€N8Î¥>?¿CB³oÈ¢#.²[OÉíkW“jŒ¼vKQµÌ&¼n‰Y’•D’’MÍmr,¡•³&+{îÕ®TÓi´e-¤ÙFŤE4Ë%–d¥Q¦)fÔ#.QŸjë#.IjÍ3#-M¢f4Ä•mjkjhžœQYÔ×uÓjú6—VjöíØÑ©ˆ È)›m¥­)«I[ji˜ÖUj»WÞr%F¯~퉲(ÚÆÉ­¶ÙHͲ«n]ih¤ÍRMJ6Æ×žvòšIi´ÙBfcS[#.¬¬ÚMìêÕ¥¶1Šˆñ·F³e¼êëÎâRÚ™45Ëuf©+ÁÖjñ]Q¥1*°‘¶ò&»&Ùµ´Ã™ V.?”ʬӗÒùRVc|:K#+©G(«ÓÏçÝ^¾Í7 “9¯#+«ÆKtµÚ½êªåké‚ÊŒ(=ýZ¾Ø0€ùKÃåT í@¶štN’Ð+)–,DF13ÛJ"äªI*¢0H$R ‚ ²@A#+‡&ÄöðÚày2ðZB"Û4­”¨µ{ªÕÍ[ø;5šM«Æß7u¯)jé­±W6*‹mFµßOjµâS0ëQM)°»Š@”€¢­¹j¨²kYlÔlÆ–™˜–Ò(R‹JMR–ÚÓlÙ›Y-°Òš)R›|Væ`"JZŠÊ¢m%¢Qf”¢…6Êf‘5&hØi˜Ê1A±3Ck(¦ÆÆ-a¨Ô•bÚ”ª R¥JS%RVŠLk%¤ŠÒˆmElÚhY#.RcAI”˜JdšdÔ³U6Äjˆ¶‘#Z)bei2e•¶¥šÉ“EJJm²Í¶¤¶­{ڵݫiYm¦ÒYjCIoy«k¦ÍšÚ”µ¬šÕ£UìÕåOeW5¯YªÆÛhµ)lJ¨¶´‰U¦¦Ö¬k_QRX׸žãÍÜÛ“È­ñÖ Qçšb\¨ [žèp™mÍÛÃ3=uÇŽ¢ üm`0õÎíƒýS–R‰©“ãXõ#-äOAfö)çzIRdy§ŸR<ãr7ŸmKTI}Xkù°Ì–E×ãáÕrÙ×;±lж²‚fÞ™|†C#-Ñ#-¸J~έ»¾`½‘qáæ÷xöRVôvçOƒc«³w±‘•þ)±5 €€e˜;¹;ß$d$ÄþÏç‹`Qrسž!B‹F¹rÃõÞdýçžÎÜ´õ×5ÔÀ˜X6•¤Häj×Ë#.Ö÷+-Š1¶†VRDŠ13šTš5È20°+I°Ð÷ˆ†¤šÝE®I1IìñoMë5—¿\Ï6pm£C#-<‘W¶…¼$dFrÂ*Í1¸È0mÚZ¤#o› •#-¥€ÊæKÇ®ôó=q„¯v¼ñÆ»»b¨×K¤“KJ)˜TÇè¨R K¡G³†$Â~€zEø¸9äÕ'„¹©G’(HŸ0@×P¾rˆE 'Ù[h¾Â{5î@s#-ññC]ÕA©ˆbV¤Ê𦲭ÀY©é“îB˜ ;*ßÑžò?¯Ùx%v>´y´Â¸’#-ÜÀT›¹ìÖêûÏzø-šGSpì|<«~Pœ:ˆëÈN3‹5åŠP«Ã pÍ¡ºö’©UšYSh]Ú%áóìLE_Ô8ЮŒ7yzgÖT 1ŒƒÛ‰ÓÀvg°;+ŮРEà 'o6ø‡Ð{°‰ÈÝŒãÕž‘[¨F$#" Ù‰b|slá~]K-’m *$ÍRªFgãƒAÉÜÁ,ñì}ˆ… #W…Ë x¤âÃ*˜&^Û©$RQ· )EU²Á¿»! ¢Bw¯5ëjõ«©v¶±Ú¯…ÛkÒ+I­kÏSÄ÷½üõKÓ§vâš¹sš" q. »)’첊‘ŠÝ•´Ë'$†±zÕñ_‰…1㮈mÑé3gÛ»è%P`]‡†ÃeÉ=]Qê…æs¾ö¯³}KàwàêIžýÌš¿‡/KÆ_‰u™ Ù¯êWMÒ“k 2Õ_µ®»Q«&ÕY‡üìà€8#.P:U2h3q¹#+-€(MP0+”RmZ¹E*ši4´·]*áɉ,ŠÅ¨·çV®F5¢ÚùR[ÒõJà,Xoé«AU"T`†(Z(ß"á—#+‚¹( 'kåŠÔY—èÆ–I—]®ÜÐL¤–È(AKˆP€ 2Ó‰ŒéÏ;»"FîCX3iߘh%êlÛc0ù¾ôWZá‘¢ØÈ–š5B±pÒRÚiØúãÇÝÉÏ£4œõ,„~X('‰ëàìœ]ÙW4øÉAµEÿ°#–äiJN¼WHl„#ºH¥¡L8dTüÅR¨Ë\ÕkAP¬RȹÎ!†™rbÁˆ¡ælAÓcÓÇ×F@mò{—Jæ®Vi®®í¦EF!j P*2JˆÒl#-ý[å|ÅÉ¡1©äÚ¨ÖoF²ˆð¬ùFq®8Jì…«zTB#+:¡+eeFÞ«|çÕÉ÷àMœ²-uREêÚ"ȥƀ "B,‚e€±¼µžšþ­ûE0˜Î>ûÿÞ¦#+¦a™$e#-°såç``ÚÂ#+Î#+‰:ýÿ3†0#+Ñ’#+PÌþ\2L›ùÜ>«æØ¦ÁàwFXn"ŒQ#-¤DWíä  ‚|vì±%( €™"¤¢Hûd8 î™…‹æüÜ ¤{Bu³¤%²9 —b¢¹³že¢Ø0ñŽ­ ÊðÁü³÷—µ¾/|þŸ«òOyAR¡ðÓŸ?Ô˜!Àý;L¸¼ýßOBޏf5ߣP² þϬ:vêá#-F} ÷"Ölq Ób¥QRÄ|¼Ç¡â!óô}aü‡zàC·Ö ê "’H €«ÝT¥@(Š« ‹•*" Ñà‹0/¨"!\…Vš†3ßn0¶DY™qþ´0~Ž73%t`¬Qª‰ªÌ…[Û~µ×TŽGyÔGÙcÚö¯êü7/=ªÄ7ÄŸöÐ`‘7'­=éF‡XNOQiÁɬ¿¸Nœ„$dT‘CˆDh„!Ù~%°6Þƒ=‰îd´R(Z"º4ºvŸ.ÇÄøä—x¥Q‰ŠÏÃÕçË‹ Ô¨aAä¤3KÕ}çc#+ߢõ>ÓÚb[»çËx{Þ?LHŸ#¹µ»m+è—ê¢î[#-9eß—¯Æ`þ…bäT܉!$DêP#a·˜×Ãe-d¤~ê’]Qdª”Úæªm™Iø³ %þº0:ê8/ØèÀƵ’˜¨ªDÏî»p%9`’©L¥€$ÀîÈaYÐÂ…Å .ª#.:Ñý‰{~û#.ºÑXAiŠHˆ„6sb;äI66Øf ®¡UxåÜኒeбJ¶¸ã|m™0€·tHjLK0‚Œ™VºIšËk‰c#4ñó.h¶|»^©{Û®vîõ’l`­ªÍåE£P¬m#-ŒfVJÛrÝÍ×{w¤oænT󵹪64i .Ì+jHAb’e¡k7 i³%8ÅÄH»%Œ&7©–åæÚ-¹×»x©6¯*ÞD#ÈÈÎr¹cÛB HÁ®X¶Èñj1¥¶°kL¶4I)+>Lä±¼xa¶EXVÃmV‹z©=eXeƒ“ ÖÚº†·U¶1·“—wW5Ë\¹µŠÊî‹Ër¨®[—Á¼bL{ZUíçJX+e&̈ÅXR"KÆÖ'+PÙÒApÌjÓ¬3L ˆÒtgR§ìÎ0e.Ц.­«3u‡,P¸yAH¢\¾؇¸oD28ÄÞ#-@ 8ZÖ)YXwjš§Ž"ŒmœD…Änã,¯¿eú>çÖ!› Ö:¡PP2Ëô;h|Àúim¶OUI š0Øž\ÝŒpýÏÜ—Ýݲgî+µLÊž+«n®Ûn‘QfuŽÕe»Ï7^L·XhbPÀ.úO£óËû))„c®n€¯³+r9óø=CxxèèƒÌÄØ-ï#-Çò$‡Û#+ „e#+:шÀM?ÍV´îFåæ¶5*Y*6j»TcMD™/æUF5ŠwjÕÔÛ±‹UÛKp¬ ŒL%ƒ Yt#-ЀKR¤ˆ$€£ùOíO性"ôR(*BÎ"BJ£“à~gBÀküPEˆyK8Îx}µÛñð«áØÊ,`Þå÷y™®Hhyê‚€Q#.ªÑE} ZJŒ#.…©Wé¸Ò7i‘*07ÿ8  €C…i}J$D-×U]V™õÝɰpâpß뛟Â9A¼oUQ A_”ÁmÛW¦Ü·/Šò–KT–ó·R•,Õs\Öñµó¶×š™E²V°Ô†ª6×¥u]Õ½ÁW±"Bô”Á*k,‘VD<ÝÔbÌL)&Ú“kRfˆ™Ò€#-#+Ë e#.ª€°…„Q#Ö2<ã¢Ô¬~<º!ÔÑð[1,,6ÚÙ„t‚;`Ô¢UÐüÖÑ#P¥É"Îh/D00\DÂKq F̈ [2„#*W ƒqì6- È.ÊŸ‰û”{ØŒ%L(ÿJm1±£!xoXr[‘l'Á~2@\‘,>]™^tìËû¿ƒŒe*ª¾9û=\M}›R#Jœê„è4\T-Ø\dÔ1¸·(*Y ItÉü ³§ùËø‡Þ™¼ÌTA5úCP#+ÝEüÃê>î‘88J¹’v %RYÇfô z>¥Ÿ°wäÁ-*PÓ»ŠJ4²Ò•5XÚ†m564ÕfšÊy+¨ÚÄc„AFIQ†Ÿ,O.¾"yM@èwôñ°b \­ wÆöá£òj”#.).ÕF0wèrœãZð#-(÷}¥ßîñÐ?Ó'}#ÓFi§ÔBŠdߪ··jÎ!éá¹yŒçŒ±’ƒ0ØYVŠ%0ßm¶ÎM+…×J†¼dŒˆÁ+Dµ…†.Ù†„CQ€C[Í¡#8=ɉDDßa …VÑûd‰UC`,Är%áñPJ<ÓÖöwÁ#+ØI$‰hT#+øãc'#ÐõE­éÞ™ö)×f ÛeFã&»íÙšßôfLÉ¥$éÚâ²°¼HIR”KD0¿Õóäµ7$›::üW¡ž#+Σ¤ŠäÙÍ1­Ž8M°YË•ÖG[ZU!ûÍn—LUCÙ%#+ÓX;È^“$î@†*ìúcJèQ¤M e`iÂ@!B؉ Ir¦wlU•E¸ ?¡Þzîÿ¥óÈÔ2–°ÜF,ÈÇG>µ]“;›ÏD#+°ö9§Ï„?ôÿ%¤«&$Ñ@mubQ0nzÚÅæ|·i©ï(ÎÆu'Ù#-Y:úÄÈúÒÁëDcïMšÒ¸í2Pú~a©8ˆB½eÊŒè9L´€ Š‚Q±˜0µ%&Ji˜«Mm-Ihµ&3#^ò¸’ 6er»-,ýJ·-S2ÚlÆØ¤SY&¶V›,m²¶ËSm,¦ÚY™m¼]b XÒˆªmL%›k4M-›b¶Ø¶ ‹A$‘‚DŽHŸ“B¿M0ƒÿ`Qõ¿Quçà ¶[-a5p£#ÛåüéDˆ´ÛFµ®§6µEµ‹]J¶¹­Ù¼¼Â£ßÈÚkÒµóï-A'"v§YáÙÇDN"ˆn£l •T–×6(K©~kK{m·âHZþ5#+6Å2å)Ò#+{·PFBA2*‰-4eQëaH Yï”`úñƒÝ2?nƒ˜¸„€Â|ø<ì\»Î Á1kÔª‡Ž=<“û݃#+Nq@J"•#+|@ðÑ÷§D6‡ó±#Þy!Ä„ˆ½ÑJ‰QÈ*^Ô!t4#îy…ìçx€Eæ<Ÿ³¡ËŸ2Xfàl³«˜HhGÁwùT6J•× X΀FÖÞÌ» ¶ŒËàÁ›>DØþ…##+Xdæf ‰d-p‡[ܧùz^dM#+¡¤g™ì&J+'a'„<‰BQØû®ê­D#-X`Þ>Fâ²x¨¡ÉììÑâAà@8-˜»Í¸ˆ –ZN›v#.Æžãå`·%j¦’æ£÷5Üû8whèÿ¶÷a<â!¬a#-DV††‰#„mL"ñþ…"fŽÎŠ,7âåËW›ÌáÞu6}PZ=è~̺‡ß ´£>½r1hÖѲ?<ý)¯uW¦¥Œ#.>Ã-¨ØM=È8A;/6ÿW4ß' N9OYŽ Yæ™çöî8FH$„Qm¯çžÍ¸Ûk?ÓQçE/6¤ p)¬ß‘µ¾g%x*gOÔnC“|__®€¤š°*#N#.²ƒ)¥””%³#.‘d¥=ÿO¦:íZ‡Å|­çlW¢(Ë6ûä2¤ÄÃ"Y) _+ª‘#-ûøƒºT@wôÿý(Ì@Ɔ˜­µ1¦¬²ÉÁbi¯|ˆm#ˆ†ä®GÓ#.T2Ê¥c|½Þ߯b¨¡LcËD]blN9anPNÈÙ$mW XcI( 0J6‰RÀH ¨£КËKïȯCÛ6ÊkT^¯Í³„…ð E¾’Ĉ‰”)Bc*Â1TñhwFð'ôTŠÈâ*cu¹)‘"KÁ÷DL(r@GN3#L$vEHŽïϿ櫎8—šK±©KÓ9Â8Îw›Æ†×\M$(Ô#"aIƒbñ!€xFÖu:#-!pÞ"ÞÜócËyËLhaÍ!e?¸BÙ;^'§§XO%¦ÖJÞôß~šÝµKɪd¨¢”6) hŠYH„,‰L Ròo`»ÜP#Ø æ#+˜lÒû=<©z\b£ ì@ýy“04‡leB‰¶2%2·»w¦;ö:÷ù‘ô~äs¿’tÞaÍq°oL8BÈ9œÑ ?,pý±#+ð0:1ݾlÔé›°²Rô‹Þ3è&Htôßmj‹ R°Šˆ¡X@2‰Q‘HÀ©K÷'„·ÇeÃÜ'EEꄤ… ÝR¦PÉ,¬J,®VÑQXÖü~«Ìõ^ôJ]7Ž“yt¦JBï<ò†ß[¤ŠÆHH›c-¨¢]Áªg(²(|âDC‰T)#DQ³ Æ]ÚÐe‰¾K³#+Ž)vs2jQCB_Ć'¢ Šh¡º2FìZ8Ù¤ç[µ…³›H†Šaùzw‡T¨ó… áyS-±“ñm"û c*ÇZm¶T6•ËslŠ-†Þ´ŸÃwkX€Cù¤(zµ³÷§›ÄTÑMáué¥q0×#ü|ö7Ör_…|1Â똊 jrä#+KB#-ÕÚÓ3ZPd-£)¨r²ººÞ‰ ò5,ª9é#°¬ ÂÇàÏÒþ8@#-$‰‚W=Æ™j]SpDMØÒ‚4Ãt356SxuÌND3¥ÄU ã­#-tF!†tá›äʬmÒPdë40Èôj”ºÊ¥¯³=v…EHÍ=¾¯Jäl½mE:œK­R¶æ¨MÄ.ïMWï«C\ç7Š ËÛLkdï³ 'eQaMáá¬ÎÑ Tæþ´í=¾ÏŸLYâ•65ÐY•Tî`±#+I#+¢Ú¯Ynºš]¼TÞ^5åZÞ–)²DTyŒeãÞPykL‚žò#+Y€:ª»xzçgÞªÏ߉¿…™#+áý{ ·a /v>ïÍäu1·c ޾âáÊ(ŒŠ,#.^9pÒÜ:„ñBG»O³Bø]¨@nYGC•–è#+rG8—¼Cjü ö|¨#-œ:ýº¡®îv™“]àö!øçi}ÏËÚë²g’zÀ}眦oÏKO¯*ÕÛªõÕz)‚lˆ†Ä ±A¢0¶ !h)\H‚lzUš´–Õµ&ÕK/HlHt¤DTJ1–!(0üO´ë7P[Cýô®éªEáƒ`M @®CET é"ýw_Éá1 QE0-HÙ÷Ø´ñˆl+BlM­H˜™Ñ“~Ò×îë#.mÝí½u•Ý L¸f7¦ðmgÍÖ¹¼t®Ë̼jKUT® ƒ†‚ÁI-˜Ð1šxÀsv°Ôš`›¬MKdc£’3DDBèB…¤ ¡ /{&Ø„ÂDˆ)·H„Ö©ˆÀ¯P#X¡¸”l圲´òÝÓÈ”fÙ½b,#.ªYujY«2IüÚYl#.`³G&į=§.BØ4ƒKª y1 Û!f#k.ÃMëהä§iÉLüÍRBce’Äv•J¸èMœý$óôÇÊË®Uoè#-IÄN\þe°¹Êµ÷;¹þŸ8½€ŒV%ÓPÏ )²U•þM+…VY5huFJóœÅðòåBR'd\IÕ{`–b‘$²¢²jÊTl–’­S5¯Ð«ó6Ònš©ùü<¼guk€HUA#.¡Wïï_‚ñüø Oµ‡Tlz®X.nëmSê[•Q#+!ë²”Ô9”§.³Ÿ.i§Ç,½ÜÀ@°ËO ¢½ÊŸ’H/84Âe¨,zwØÎ·:9ËÓzÏbkç¶Þo$“öCutiv¶xÄpÊ8¹ž Ž÷5L$š'%Paç2™F‡ñõíÏÚL±ÇÐâðÛ›îC +œM€rZõPA4$nyÎÌ“–T˜½·\qÇ Æq×\yué#.5×B#-íÕаΕ&Á&tvA-aÊ*;ðAÒûîýÕ} 6Ã4.〿;õ˜‡ê²­rp¨Èl…ÞúmMÏàFÇz7ôð%ªì2so$Kj¦0y}r÷žÃÌÍ}ZqȶAè‡Ë¹j¶ ݤ×oÞxz½Ç’†Í‚/¡äˆ/Ã:BàmÜzûþû‡Â/(Á˜T?1Ê1Žb¦†–Åy‰MÅ·ôSc3Hiò{“g^=àwbuŒçMjóɪËX¦´&ŽçòH²}4+1©«·=±ê½éLd=®3bÏßÙJßfíï¡AÊî¹ØbÎ;¢}âYú̓²!š´GT)æÁ… €ªT'ð1†Œ¸7üݳ°ËÙa‰–çiÏVø;*%Ž^#Hˆ{á’ä;¸Ô!Wv°‹h’-X€­#-Ç"kEKR"´ÛhˆH0·¨¥ ‹ÉL£»ß­ïÖQ‹uoÕ€n/ÐwAÀ?¸h*ø¿"ýV‹ Xˆ šà-¬.ˆâ`æ"DpÉ©+,E 8@n°lP±HàÕJ*àA˜ª4À/$T%šA,HÈ’DBØdû† Àä‰}—“˜Ý\H!RÑ[.tœ¶ßÈkêåí×mF0Va¨ò•7†‘ÖÔfZfº`º_eZLØ¢'Õµ•XÛT$*t´º (€ÌŠ+ÄŸ©ß'ö~_Óú~môV‰üÈJ¯–)|I}ìËçc=™Æ<Ð|XÑñd…!»Ð‚ ®{+kRÃd¡( [bayÈ*Cñ5—¼BÑ!V#-ö—ÃZýšÔ.¹1ÍÀ´¢¦Å—€ãnÅ"¦Ô0à‘#H2|óÈï#-ËÑ,Ünk\O’'S3AbsåAŰåJ+QlÇG?Ç&Œç¾xf,èvMø=,s±*ð4#¨€ûþIÿà6<£f<¹å!‡Á;8óú~½'|¥¤’^LÚ fXøÙ™nwë«Ùbå~QpDLFÓLŒÝPi§2lÎÅÂî Åä@Ù¹…#Þáí°b®dC¯£Ë&ÐÉ®ð{ÊoüŒ"0ñ3½zõ¶Ûí댢cK •ŒR0¿ME’“Œù8t’^/ ý-@Q~NÍa»úÃïnyicVAa¶\²Ö©ûk"Ô<=±®’áÐÁݘ),·[I3üp0Ý Â2ˆG#-?lÕ£Çz•бÑÇêû±¤3¦Ä°È92 sîæ§˜ˆªÏa€³tÆÒ8EOhÑõC€#.4yGïÓ zhÒd3†c¯#-Zr•‡By®%C½ûƒ¨d<‡˜¥…é¯×-9{1ë Â0ˆ\Jô¢¡dð9ìX3!ŸY­ª\Z¥„r¢&ËTºfBqT¹rœ½÷–#-špKLðîÚÎ>1ÃÓfE‹@ Åò~ÛµrÁb,ÅU˜òwÒe×:·Ð_YÿmµÊ»u<½ß#+~ߘ^û@çàÃ<ºMü³1TB{Oƒ;s—ÓˆL{Î~3kÐødʵּنLU#.¨›ß߯‚¯îoenms•r7wi Gè|àF¸e'Å#e5'Oj7¹$àÔLlh ‡aMEEªv®¶Ý×iHÚ™™lm6!´BKIþX‹Ÿ€ø(hRc\ ,ª” +& "# #-#.£Zƒ©B8† †GƒÔƒ¯U%F!ÁÑ­S£‚@#’(›cZh%Gá4†´Îb­÷‘Õ†qæ™úe°Dý7¤R&6Æ=£mYMt«›ç›¦E•%Zl«&Fyºë&T…( …r»ˆàãR^ôfÐPVAŒAÒ‚‰¡¶é ÜABõM사IÄTfn­F#-ÖYbavÀëJ’je#@5¦ÖR³a)ŒV’I4)¬±I¦Ù™&FÛ,¬½uÛË·6%ÜܵÐc7]rÛšvíï.kÍçyE**Z†crß]ìöfÃ&™R<½—üWH*llJPòÞh\¡¬â˜›`1ªÁá85jЂÑM²Úñk-˜Æ6èÔ6î“#-j*Óhj4r 05õÕZƦ1mµƒFÚ¶‰>\x0{ª8É´ÚP nÜŒZk†aF4¶ ¾#+Ë,ˆ´ÐƒÀRÌçjÅPHV½mt±E#_ŒÆ#†”õG–;5†**Õ‹wwšnÚ#ŠË!„ bȘǧDËDŸ5i@€×,A6šbŒ{„M´C‚µmJµ•â²"±Q±6Òò7ÒÆH“1²á…¸ÊÁD¬€a4ʜ͌c#+fL± hc3 jco×.qÂ## o C+vHÝ!p™’ œ@ã67v:ãʇ%eÄÀŽkEÍko=mM­I’/›Qïu[fŸ#+ÂŒèðôq(÷°‰ºÉ·¥,<#L'<«­ÄÖ‰ù¤ÖÞ÷ž,ÔÏ]­%c)ŒY<ª´]†œO•‰d#-Y¹µ´`ã…³­#†«b¸lYŒ[f7·ª ÄªlðÄ)b #.°X¯"§‰ƒL° ™ZÅ£÷¦˜ÂÇÄÑ)Nl†˜p—LÁ°Ì˜>´|#+Ç©úüN‹eí¦¦ÄwbIeÅÙ>óÊÂü¹În°ÇO\t®z7ƒiÕ6ÞNVS!YËJÈcØBÍ×Àˆn¸ÒÁãF& j´•M¶Vo‰¥ïpßãPšJqKƵ!‚L:ç*µ¶B¨芩#´Š”/‚§ ¢ôC!“`¬&/ =ÄzÏŸ·“é5†ÆLä0FàœH0ýùDN[š`u´‘AÍHÜŽ#.),-n鬋ZP|%;éç 0X] —Bˆ¨E„ ¢†ˆ‰²) DH¶!A‰†$LA„m: é#."€Ð˜ TA¦’E4`iT`Û¢Soàb:r:û`vGo¿o: FñB¸d}ŸžË2ÊN©Ý~«”#¥ÍÌ¥P5C˜c襥…æñŸÆ!3H†tô|¾‹ª3Ýóä1Gðôî¼}(ÉÁþ•ˆÈ"ßæSl ZV‹H]µ=ýÕ˺ŽåÓñãY8g¹°máïÝÔÈRÈÞÞrãõr¼Ý5xÕÞ;½]Ýá¥uÙ‘ʲc®Ô»ÃaKo×CÓC¿m«c4¶ý^ÍÝë1’C–æfmÉÔlÞ•]sÇ¡· 0Œlã;Ž4pЩÐá›:ÝkEo™»Æ†ÔXІ, É’>ìD@˜Ê¦ÒÂl‰!b«¹n^t8#‰³¶Æ6ðø"ƒn: •„ôk®Ò3 o“è4Ââ9:"HdÇàn˜ò›Æ»¥¯»«;:òMÏ#.òá‚à=sP’X§Þ_ï$ðŒ>™óô£ZqeDMl²H$(#áœHx]‘WdÈ‚×!IÀ¤ŽN êìÐjpN,læ=V–ø§ ­ÒòÇÙ‘¨ø¨ÚOc÷Ì{qöÈ}€k‘ ð‹FØMvmu¯•~ÅlkmžÓ.#º%À>Ø¢š¶E‹õR¨@<6rðO˜£ÂQÈÙ¤4;Wž;«xé¨Þ*»+n[¦Õ&µÞ;-£kιtÕÛmÍ»·L‡:¼V‹<3L±©HÚjjçÕwuXÕJ£MzZ¯ÞM­&éµvm*ëÏ7ŒB,BKI#+Á4¶„”!êÐÒýí7XQQ°ŽH¤¾ìQûé3¤[Ejae¡Q#R0#+"–°¬öIŒ.p)#+¼6røÚë]Þü´5s Ê®×µ¯ Œ‹F.T1R|[èe†¨ŠÂ’ºnÛMu¯fµQª”ËR/ñÝT‘K*¢³Ë4ÔZ±¨ÔˊѬmfm‹iš¤²Y”L’%RŠ#jfM%ªš¼ëWo«ŠºÈV¾‹ËH~0ÚDEªð$…àU‚2#+¡X,¹>÷|}Ïéõû²yöû¢ßiy¯¤´Ÿåø9+Ô€÷š66B¾SÇ)ˆX o-õÑÉë@„@,±jßpÔÓkI¦kFùÕóïWP³Fª?,Öü‹kp­¯º_ÅêêÍ<ÝÔEYÝÙ×VíÚJVÛ˜¨µXÒ[s\¹¶uu¥5­5¬¯]»5š#+)H´ÑöÙÿ ×v)Á“}ERʧ܈T#-9Qn¬„¸$A’$`o4,®«·#.-¤’"ˆ“62p#.¡aê.–7Ý\‚Û‘qA¼¯)À„Ñ9Œ…?:•Ü\Ãضa4Ÿsí)>7(’|‡céɯ\ÛçBì–pAÉõÝ^ª*6Æ`*ëD†TbÉ=Y¦À!öž lØyÕGñg#-k4‚é)™ŒÆçáeƒŒc+¹’i¨LH1€¦'°fÔÓtpI¦’<áÜ›“£”˜0ïˆQ‰qàu'í.o]û^“^ž´u [ËÓǬà9{žü–wà5¨ë±äA­äŸi]%ØÙ}N÷ãF„Ц6d͇0á›äsŒ"\öäÚÓ²ŸKP–Öì®xÎMð²ÛhítKEH³ *Ó¸Áƒ† ¥“#-1.ïg¹àšE‚¯ñ´¡X°d^Ñáw¥Î\‹óÔ+ ñšÕÛa9 (›TÙœl¶6ÙãCüµ T‡&*Åàᢦp­*¤ÎšâBr|M–(õ!÷\pj"r€ý„VdPR[|©õ­«¥JcI¯ïò¿À‹•è@2ЉœlB^ª ‚Š 6’ÒÁ¡ ßæ®´¾0œþ¤x‡ réOÕ @Ã[_›üʈpìL_ÒãÃ#+Ed”$ˆ’ (¨ç‹mŒÊ«¥«ÆÞJ­¾ûÙUƒ2… %.ST‹ ‚5U2 É‘„?0À\‹˜+qAÝ!LE“kjæ•Eñöɼm×=—x«´ˆ¥Íw“®}k=tÏ^]««IjS[4¶”à g8ÒðZ8ÊÚÅR“K«hÛ5jRƒ­´ Õ…ZÂ#+Á±´ (0¡RD#+ª!B¡Ti/D™bf ‰‚MpÚ$ÏÆ°oXF. "£t g˜{ó^¸š0Â!8´Æ1D`zÉeBFœŸÙßçÎ÷ó”K×[À|4<é!ÏÄü¤Ÿf­ ÂÁTçÌà7ˆJ±XÏ É›tÖ`îá¦{÷çÒh­$#Ìn%¨ß®Ÿ9ðÑ™Ð#.#+y‘Aï B#+”J·ÛZü•z¶Û{iU}¯¸<´Ïì>d uPûð§9hd~­?åûƒÊȼ‚07Có&©ƒñzâN'#.Ÿõ@ ¢/-_Å0^œwBÓçxt9ˆ<>»Üü%écNA¾@ˆò–®ŸYòvoÑLÙˉÛíFØÉѱÁºý »…²åã YÃ~1:>T—AUéÑuÞ¨X(#Å·0€¾è¦™#-Á¤2É’ h!ã;ex‡òê¸Ê'uY²ìѲ]øÆÐt*â0CZ¢«xšJ#-¬à¢ TRªKˆØ€É"€„ÃÎÖ3H#’d™6 ‹«õ*¥âéH€lŠ="’xgw2ŒõõªX7sL*…[¡ï"ƒP©²K‡ÊØFì¶oFŒ Ìø•Nœâ3„“Ö\ÖlÓé?d!ôí¬~Â>RªžÕËkt¢Åªîª[«»«ìJ´ˆZàª]ÿUÃÂRI* D ªÈ‡¤3¡ªv~üOfIÐ4p£R€®«ŽÕ°™YÙZìÇé¯7–·Ó~÷>,R*ȰC#-Ý‘«ª#.J%5®Q×w‹ÈlJss/7›6©$l)²ÒÔž6ÝfÙ(±E3cÄÐa.mÊå\ØÝÇ.¼ñ—“q×]²Žt.Wwn‘oæ«Ç,Ùo'™n®]Íd±¡Û¼Ú¦ÖŒ:«sr×M«%´jRÅgr›2Édñ·4îîÓš»³:k’)¨Îpå]²çYQhÑÊÔZí¦Û”i¢#.ÑAH#+nlˆ–UܞȆáv"DþøªvîöpÊÀ;Od±8#+;ÔÿOxxBð$E Äj€"4 's,-ˆ ËðòPS‚uÿl4Póýäå>(©Þ8 ƒ`}… ÜPò *ó}±ðó„÷{/´Ûñµæ#.Ò´éƒ} ¼üE$ ~ÏŽäý½‡qÔn;€6#+z+2HH¢”ÌËç·«›Z·ãV·-E«wVí.]»þxÈ"°Š†s¯áÎÀ*`ƒ ,"‚’@‚I£˜D2"{Ot/¶ê+úMV–¾<»c5ËvçZœî]ÝK-0ÝÛ^¥¯ä‹ÝUM4Lþ`‘½=ÈK$J´ Óï»kWT–wU{ŒB‘™Í–"ökjûÝn¹m{z¶ÛÕyäîíº>WzhÒlR5ê7€È-ÈȨ6#.”ƒ6D³\‘(ƒ"9P\5;'ôo1;3 )ôˆ¦“ÒGýœŒÃ”.Š¥îÒ¡Œb±J!ó?‡øæ‡º¶¼¨6&qwŠª½°‘H€Ä X(ÄŠV@R#+Å‚G‚‚²úË¢èö@1c;FˆP„ˆ´Éš£mµ#.µ~Ï}Ù™¯ g3ïìÒ‚ Œ$ÆDß³•kûíµZ™m³W°qP@Òû-Lê§œÒ!±_dQ‘…9ÄnP¦‚íæxÀ*’m#-J=wÔCw;=}ßÛÿuÑ>…üY'Å#.‡ä<ü3÷oÐÆ\éax`å¢ÔU¨MOägr‰2#-‘ áÍ#+V$ØJùÄ`^HO¾Ôu`iñ]]mqI'?U„8£¬ùÐ~ÒX-Úˆu$YÖö@ÉÜw#+Z =ØZÜl’?k XâQ´‘ Ô‘,„¬²ÑI1P‰`§÷."D‰µ†µP. Z”¢@GLÂ6žï•ÙµÍ]$«•{¼mŒZÜ#+£ekD„¨ŠWAx4c0f+M²¡#-³1ÕUaP«Бãìj¼Q½*æÉ\Þöm­xÚÒXÂ(&eIòvlUW…ÙyÆ kyÕ‚«ÞâOoµáPîÊ«üBÚÐ"X`\0Äh6Òàê<.ðSL$yc®]š¤É£2LáÑj• ·R,¶6¢Ph…Ù…`Á!Q€@bTZ¨Ú­½[k—nn¸\¹Mhê?<“Hƒ¡©·$Ï NŠ»#.Mû÷»Bà‘”~U~Òsï)‚ œ,=éP=#.ÆŸQë%úUj²,*"¬Hƒë#.‚…a3MÚ“J޲ M #-)X…ˆµ;SÐó󻽇ԼõCa#BET:#+„;䇟T”R#+Ô•#+0b@(•#. :Ðê#+þ¨ ûª6kñÅ2ÊíêÓ¶Vܵ^h6F µC!ÙAgc1U š(·;WŒ÷ÄÞ§ju8ÂÈ Œ‡¼¯î¨HB¨æ,_|²Ü«¢”9²#-Æä”’‘±B#-ÀbÀ„µ'èÒ…û`Mlù¼_S †²¥'¸Kª˜TÀ”Ô)ˆÈsK#:ìOK¯;½wzvÉÝ̺Ìx55cm·Ù~Qµþm†Ù#+â®0‡'‡g×wÑ«ÇäZ‰*ï*ÎRBÑ“+X¹ž—Åí÷›Ž‹ÑãCJ„<’"‰â}Áêͨjh÷‡Üƒ¦#.Š‚}±ÚÕó•wI8"ì8nÞ*&ðÊ"Ȉ!IžÚZbˆ}Î5­/ÍKWÖ6¼[Wá¥Íø©]KöÍVÝ Ó`ÃøÀ[ïEäÁ¹>ÃÎOërqß—”ëçùAÐô‡”?„D`¢¬`É"‰"Ïš'ÑXMÍŸ8ˆæ(y÷’‡ï tíQ÷8¿Íßx²Ã?2¿/…b·ÄP)²4¨1¶Ç‚ú:«Ðëú´S$[ÜbµDR!ÔÉ'Öì˜g(F›?JÆŒåŽdJeAGam!Xt´ÿ4"#- !Ãã†%—K©¤´Å­wª4W·ªºiøÇyfýåQ°½fÖ-Mñ*×<­±t(ñH˜Èª†'<ð³™RڥĤ̀{71T œF†­«-ÊñsDCku?S~ìåÐûØxº¤sT1ò@òf¾5¨Ê’±’I­ëUúýà bQ‹ðu¦ñ¥k‘¢¶òY6Ø.jç~kÊ|µ¶«•—5vô»Å•ûŸ˜ç¬÷Ü…;ñª¡ä†°ª¨žÄ¢Ïγœ=»Ä9Pâ‰D'Iyˆ•94¶3ê­ücb¯†;SDʬQ±ÈµÇ…Ó벉” ÞÅêÎYOzv’4 žPRU´UÖ d²ë°8<²aM—2ütùÖkPwÈænå¥OXÍVZaÃ’&žcÉÞÛ‚ZÓF˜}ñ=Ô¥t+$'a‘O§½9ÜÎ!PHòb^ßÏqZ7I$ÃS6Ç…v‰A.Ÿ»SÕ,Á‰-‘]ËS§¾ñUOHÓcbb°™ÌtoSá”R«ÆzsÛ××7ã׆‚ÏÞ0=:ú¼AÒë$»´xЧ¿Í›ýN»¯áÚÈñúõž´ÈffúL£JqØ "†ú8±#Ð>C¸‡n>Òé=#-¬3Kp9/™mˆ:³«ˆ¸ÛŽ(øý7Íçò›ÇÆr1ÜÀròqB•¬æx)µl|&–[‰hÈM…2{ÐØÈA׈C¼;hTUI¾†¾|Òºsêu>HëÙÍAo4zXó›Þ€¯PQí²lõõt·;ZQöt;§QÓ*Ï@áö j‚–1¸Ÿ+c½ELE"#R¡0(@Ô=½†÷)ãVíŒönqÔI¢Ž¡!Ð/ö¸ÍÆŽààAèZ°o)DÜ„€âW8·¯¯§÷_ËÞ̬¤žÚ~˜ÑzAFØŠŸØÊÕqȃðݳÉ2U¤©ÜÅ#+°!¦óî¤0Eõv@1?·NWó÷¢¨ªëpªÅSŒå$ŽdKëˬJc¹4:Ýši•(€Øß™¸ÝOt£® ¯Ç}‘¹š…ñš~U"Ý#-Öà ¾Àµ€³æMê“>MÓ&?«NõÝba©©GÂ#.<÷òùøðlH5Ì"ëh­±#- lé‡ñÔ$cMŸÇ—JIÖpðÝ—âñ‰Q‘z%“Ò½§~YÎŒ#%©¨²Ä#.‘®×-œŸ»uªMÁUKfÓJ¥šÂÒ•ªhµMŸèûÞ_CεI"Ð蹬\¼;nkòí>˜ÈËQDPõQ]J´`,Š¥àË_gÝÒm°ŸÐùü<ÈÚ3(Ê’H×Ç»3»†ËM¢Q5IˆÓ—Lh”b#M5#%B“fɰ¢,b¤?4œø¼³Øs€C½üÂ,ƒ´‰ïˆ{£·®„Ûì¿côé‰ræÇk¥àm3Ï76ƶstd ÒŠ‘„i3ͼÑa†­žš¢i€w‚úÙÓ/ßÙLãw‚™ËŽÒ Bɱä(!^š8Ъ™š8–£%Úç'ìòÎèÎû4 dG* ‚´‹Ú¾û÷Ë1ÌhHf)Ùtm xÊÂü-¬-®šM¸Åô V™c%aâ{8‘‘á±i˃`Þjí½'Ωv“O"¦zòH]Ò@ø.ÇÊ[‡Ötû}•7frÜLŽ¿”µ®Ü?c×:Ròë`ÛŠ]mh7š¤‘Ó„ ™Çÿ¯òu'kÃæEPjM7ÍÁþÄ@0µ•X•Ldª•¶šq‘À#- ¨9%±#.(ÅÖ+L„ 0RÄ•Ýf×TUrÎëŠW+n×t¹º»tŽö¼ï#6ÙšÕïbd — +lÆšM±c vI/Bø¤*ˆ‚•¾%’í ˆ|j6êHàÕ)’ ŠÿÆ’O´#µ¢²jõºíi›rJ¸r¢U#-DUi1¬¬QØYf¥l3s†T#.ÌDWÞñ FQ Eh9iIÐïaÀ3&Šáš7ˆÒ¥RÕG£*62ópÂB.f˜¢þõ0¯ÜýåΧ ]àÙ¤¤¼EŠf‚òÌðL%ÀB„#.ŽD3Êrz#U%û‡\mpXoE+Š0š$‚«WE nT:(ñ©¤†bƒ oÊ0ÈÁa©Š”)PY#+0„ö6ð)š-#.ÉcŠ#-±±×*JF“ÈB±/co$"$2žsiãŒDª!V‡àaÉM´D-Ha´ØãŽ( e´+So²¥é=k^¯mÞ=‘7c®îÛ 5`VT'§Š ¡®UZt¥È<˜FÜuÆÚÀ(m„UÅ¡:‰VU"ȨW#.Êåcåj46›6R†Ê–ó Uj…«BL*°iQ†#-ƒ#+ÈNš—Aè÷c›Þiè¥"‹¸.Øz‹µ4]ÜKK– ™NÒ!¡H6:54÷˜¶Ì³"Ú¯z3á0Ó¬ƒ&Šs<×,Ø6ÛFcÞ#-éÍ#.ÝBy9JV#.µ\j´d a-®Ã JdXÂ5hHk…Ò›kIäcé‹£w]âA¿+´s3œTI°–AK)gWF™•x&—‹Ž(Юˆâ`1‚@îQ„k9ÆŒ hi8ÕÅEº£J"³d€ÔB„C[SR1F´×ŠfkŠ*<¾ÇX4f^ÿ°PhrÁ“#f ¶6‘!ZÕ¤"±‚È084™Å[!IL0Ò¹ÒƒA&‚L«M:³W$azçÍV#.+Ò±·5Ë3V5—vŬ’Ôdj P†C 5Ò‰zªû]=±WȀͼX†ß2iÁ¦âf)lÄ(`6¡)bÞÅHå›Å(Û˜¢]áòaÃÖ¹dAƒ"ò|Ý‚"˜(Z›ÆúÐÆhQ—ÂÌ^ݘkA`¨0ÓbШÊh¤‘k4÷5Ed{f}o£\RL Qóò»59ED­ôõ0Ú¼¨ÄÙ.ùººÓ{‹2ÀÇÓ‡:bÓ1(§Û†B7 Þ±¨Š©Èh+9Ù¢[UçÛ›ÀebŠ-Ke‚6™4Ï6©8$ª²™P܃%šT¥\Tƒ!Ôš" ‘¤´¶(@¢ K˜ `#.6‚€–$¨ Š2*LHؘÐ@/i,Ü€H¤ŽB.š?Õ"×kWôWàú±.⹋w‡pÝݼOßó•æ#-Q’^žwWK~ï\OW#»;”¬wÊø[毯i(-¨¶Ø¡jY´l²I«|Õ]Ë”Côj§ñ†n8tÎÙîôáFž#+$$BÀ>{ }+Ë÷ÀWiüùV'ßòÅ­!Û•#–Ê'²Š6ýgwÂõZú6üê´šÕ%¢µ4Ì1RS‚@K–Ù¥ì¿ "ó©CQgz1‡|Ï/¶È…åID’\ˆR+'Û:D¯œòóQX%Êf9Pi ¡î#¶’Ú`fHØ›$‰7ØÖêêé«omuÒ‡9GÂwJ¢Æ .P¡‹0°nÙ@bË‘fŒ¤†µAFÈ(SàÉÈRË„:²A©¹®é—- ¥›%MhggNÂC60FÔæ:/ßñ%* …-ó&m©±‰vw’ƸJŽöCrûlدxk}ØH³¨I€ÛâóÕLÔœ!YVlñ5|œð#-‚5BgõÇt¸ƒâÚÉyy#¿L_HÅp3èm:Žo¿_½€ Fàö(>Ï利 >\…ÒrŸq¯%C°Ë‚ðPÃTP‡˜Æá“¡%gÄñà&©ˆÑà~®Íφ‡Œ_+Ø¡LM3 •4šº}ߌSÞzGù{ºûÁþSé)‰t öv]ýh÷Üvç`@ãÂã~o±ÚÁ.í$“Ãaâí¼ì°7ÑÆV(£FÆsW×CbO¼ªñÇ1ï¡§„34·7#.zDýO1‡¢|2a#-Ü4zŒ(uÉ¢ PP€…xýiÑLœôø×´ÜRÉDáz:r½ó„/EtÐm¹GÁV“Rû tìvY©‡¶#+•Mì}k´ÙYm±Ð¤xõýEñĪvf[dŒPƒÀ-ÈC‘©Üm¦A~#&3$UŠ)"*§×TI.ÍZR¨Q‡¶ªgoR\ÌF‘‹àçHÖEº«,@0Ða¿Í€cqÎb¨¨Š6´¦E~éXÜksSƒ#-§%Ün݆„‚È,Š&â)¤“øªƒ‘† (Æ21" l‘(Ø&kõÚñ¢¤ÞÓ–²[F½–·b[—wkoäMn^*±£•ÊÝ+mÓk!ljæÒU¹¶]Ö7+rÛ›[•;½ÚäTU&Ô£[År¼UçucUûóo“¸ê‹¡kX6 o°'¯ê¤6E~€Ó8!УB¶õé·#(æ$Ì=É¿àž³$/eûX<·4 p¤ˆE"Ø€‡ ªÓŠPø¿Í̈Z0xxðãUÓõf¾³Zo#.¨ƒÙ#+èªü€¡_¨ϳéU?yˆ¦à>‰ýt’ÇÔ&ØLž1€‚ˆƒSØóåW¥2‘„•$»‹]wqÚé6¾ßÍz®m­Ï`€ž6U»#+A#+“[U’¶ÞûZµÙ6#.ʡձ[È–n )2Víîó*.èCjúÛjYF“lI£kl•fD?œñ>Å@Kˆ+¨uR/õ 'þÙùÄ;ú©:íÔQ‹T+õñL”=(¤"e/U*z¯kJÎ(Q KÈAAÐ_vLËò;ÀSþI$TOY¹+&èt:`¯º¤”=v¡›¾ ‡H¤‚²*e·ðª×mkû»)Z Í’Ie#. ÚQ$e£I¨©¡­%‹)AI32ÑB›m¢*¢¶ÅVV‰´­2Ô´jeJÆÖ‘d  ú {-lª¢[C2l€F†Èñ¤FÊäH‡ÙpÃ(41¶ÔfPÊA¹#Éj#ƒ…fQA— À(ŦÆL© E#.˜Q c"“7#.*ÄØ#-qXŽÚ1©*B¨*6 £ˆ`aJZˆQ@D!D”´€Á¢#."€Eì0$n_%PAj,`ÅŸ…&w (;éKBñ¥Ñnš•ÝÓ»1¸u§ë+· [«×vZÞ5]Mjm³Imch$mÐ8ÝT7D ó„B3ê°ýry«:}þÿW¦}U¥Ï-R¨¥{$>¡’+È-¦Ïß¡¤!L9U>Ò‰º@S#.híµCõ7R±öõ††€–K‚Ðùã 7 6˜ÆÃ-rr‰b x9ü8À(… CTàþ?ú˺Ì{{HÀ„20Pb_[a#+“YHáãÃAð)æêwDE`ÝE0¡’•«n2åtW›.¥JfP¿mw­jåE¶ÅcZÚŽíW Õ®ÛÉ[ñû¦agðøìÚ‰êãw?άË*cI«àá¶e€+"}»M«9"ìáÌVÌún´a¥¯!%¾£îþÂÏy"T¬¦l©KyÚ{?ÙÕñƒ¤C`Î{ÕU)}(ÏzüÏÑv‡ÒT´€êªT&J¬\r\Kªëˆß5ilî‚ã‚Q¶ß„máoiìµÍœp—ˆx~ &δë~;ddÔóǪàßnâá¬>ÅÖ·N”'4&GçdqÝÚwà½÷ÞYG¸¯h»&/Ì÷þVéäü¼’Å `#-pÁî Q­\ƒË/ƒÇaûkF{Zœ¦º†4š¢£ª×@{ET¼R!#+êŸÑ½ÔÒ‹` ³Ÿ=ûX:b›h:Õ˜1U,ÛÔÔ`èã«ã[ŒN$w;juÖ0O¦¸çšS±‹åý[6êá°ùæâ(K ˜¢G—l“Ù52ÑkgçÝìoÕÛ¼îÚ®_„V^&ˆ‡9–gsP=„ĵ2=Ê·¼ËA¡9©ž¾˜QŠl.†¡ 1ŽþWD‚]2üNC=¸»vŠŤ{ØNÓ=ß,ÆÀßä}2Ðjˆ‘'ZV´·')b–Q$‡ºè¦“”J`M„Ù‘lâT'$ xƼkÃиA½5"C³¶wLê=v'K4z‹„ò– zBŽH×3únJHßDúÔF‹¨¨7Ò²(/%À×-ÃD!YMoÄ[]¢õÂÕÄÀÂFÒr“ÍŒØç»ëI§ý³–©vÂqÄBFzŽ÷Õ鸢²Ö;ä©x[ÎtH­*«–dšÎ…]çÙì\â±.6›=c‚¥#œû«¯^™Æî’I¨ŸF¼­‚QÅôâ‹c]K=%øÉÐ#-ôz¡¡”ÌVË3Áâ³Ä¹¶¶œìóq<Ô¯w#-Z›gœœÁPèÉ áÇ•–‡.Rô¬LÖé8áãÛ°Jåk˜BìÏHyRèvÝ4Á"¤ð$’D'/€OžHÜ™¼Ù:,£3ã¾Tt ƒ9aZLâ#-xêy)˜¦#Cg±¶æô6*Ì%Q9j”JigÞ!„ Ehâq+.]œ³²öpiædñ—«ê·#-]´f+2ô³ Ú§ß+]ä4R»w[A´›iã‡3Îÿäô­ßlCËîç¡92òI'/šãŠi5§Ä½%}³Q߉d:]{¸Î¤9d3­­'8¢÷Ýzs:y&tÞõAçx`¡¶Œ/l†®Y‡0ÆLu%½…„Üéó[1ø#É¥‘nH£HilûTfêöÝ@%= pÂïTÉ:y¾5жoLžQFÀøãy#+ÎÝ«8Ñ”^üPXá‘S9J`Êà]#+ž#ËfðZ4²ˆB˜§·„cÛG¦l¶¸‡7cY7r:(eIŽãyÇŠƒDTU‰0‡k-Ieò³s¤AIÀfönp·k B9¥#.¨ÜÑ)®ÞB¡>|ŽÏË#-µ—t.»1 Ñ–ZxY›Rýþý‚dB·8ØA”© oÜr!×£]®ŽAåÖÇ“¯0É<“¼–^ Gmz„3#-Ô cÈ1ÓJzð61Ä6ãí#.á&Ì4˜àˆ¼ÙyOܨ©â€vf 0ÆÈ,"¢ˆ!04;#-ÉàŽ¨›Ó0!ÈïÕý8ô»Æ`Ä ÜQнœAo0Ý"4€DäŽ| dnã*uh•ØlG@è4:RB(XõK…Í3˜Ý»DÊÊ„4 iuæmÕ¤ÙsiÕœ©G몼lÀ¤‡¬“@BÒ’P PR 9µM9u¦€»I##-V k*©U­•„`£ÌñrwºÎÎ1ºx¥AåTr‰øLäW¥*0ˆÃéïRå¶]'AǶ[¯PÁâq]åÒõæu§Ö¹£§Èۉ쪻tòR;ÝÏ›¶`ëq yHïÆØ›ÊqàõçÓÌš¾¬ß{3w)úqg#+˜ÛáÆÔLŽ5<È+‡³×E#-´)bRÁ Õ¥#+Ï£§’ï1†´U‡²hè/4[:ßÌ–›Õ=¸¼§#.€Ó¸=g ÁDX\ŽáІ„#.­y›d4T[º:ëC›Ë‘™ê£þ‡,€!—+q»Gq°`XxÁÎb(Q¾µãm?tM`ÓËaÚ)µ @)<ÆkÏ£L˜„D×%ËÁƒÐÐÄæq#.RæÌ¸çm‘ÑžEiBŽnæ¸fyHó’ÂˈÆpš"GÁéÚ¶U#UtJ††¼yÓÙÄÒ1{)V“PÜìJ"€”¡½0Öòƒs²áÜjÀŠë#+ãY‹$ãQë"ºÕáv"t'3‰ªlCD ¹d#-oƒ›#.Y>suç­µ¾ÞVGK»”1¶m#-Ÿ‡Üþ¢]`†îEfaädRâžå´ˆAáb:‚ vÞp |#-Óf<¿`ò”t.,1ù8b°.z!Ô'‘ÉšÖ!X"ŠtdÈ[éóÃÞöGjúÝ›¶¹Æ¸ÑŽÓü‡FÌZ$WaF ®&DG{—§¾cì•MU† ‚ÑÚ\»¨l`Äã¯nx¯£ç¯Ññþd®oЮ5)“³!ëPúF#Øn761$:%-%D© žÅÛöáCøê¨lå<íQ‚\Ĩ$Q{lñ”k“‰^Œ2(ÓUÚ9؉U+ {½E*û˜SÔÓ^#-“ô¸ÝV¯bÕ2¾eÜIò{B¦>Blú„5ü{Ó}d$I#-ÔŽ Ø‹µ÷wy÷jO<ºC‰Ú<ý^^º¬ôñ|ó ‚S„ÖÞ¹;Nnvè<Ûïý_Ú‰Ï<”uŠ#-–ªW–cÌøñ›ÜôÛá#CR rHÇ„Eû»ºÉÉÝ»6nî{ýï¾ØÂK-¥uSÈ伎gœOHT¹ì¡ÜÀ8&yQ‹Ò´…'Ö%#.ɦœeE\Áøopƒ)psH“?n ^ï?¥š:r>Dñæû¨’™ëžS¼  x@C#.ÜÆ¶ìÕ’’®Õ2›V—•dA#.#h¨6((Š¥Gä‚o#+Yõ(EŠH„Mô»‚·V–Öwu¹ ü¹A¢ÄFªj†™kl(ÆÒ2hÄÆ)ÐÙ”-S*Å4Ûd´šµŠMD•“F…ÙŒZ1ˆfЦš‘¥&É(Å4¤„•¡6ˆÔJFQc$)š*SdSTцÒL6Ã#-˜D¥ &5kU¿—¾¿¼rÓ̹홨fgÄë;PÈS㌺ì.ÙÜΛÏÑ4Zô¹ Ÿ>#+¯Öós6~·™$$gAD8ƒÙÇÆÛ6LJæVïu“¾][A- ¼Š ú2#+6ØÁ£ñ:ôj ×I#-àªI¡˜ÚË݄Ķáº1™)!²`Ëèú`÷ÞšìÜýIwàvŠ Ac$G‡.`}îAÄÞö›{³›Ö@Î0–bzr#-×’I ¨tuÙ{ú—¯ÏAAùlìx=]ñ‰z¨Õ4q Û¨/_ÊæWVý66±K]Žam;C3¦ãUú-[ð«ETdµI¬DšÂIˆ­&Û/©µ®š´m65)&¢(¢64²0ê(Œ€±HT¹,%‘d@#wé šhxe‘‚Bk(d…·QlÍ‚Ê`¶SŸaxàJØQjf##-ÏÃ.V4Céq}†tÄ`àÉŒS$ŽRb)Û äì–h®=–»×_«ÆÛ\µŒÆ´«`HZLHÀ/>I-‹C4o:;p“·q¯o:¼ËwuNS*ö•å&ñtÑ5œã[–çž}t9fŒ‚Ž"è¸<1„ÅÙÞ!”a BQcD#."RTB(I¹ãág-µÖdøPj4Ÿ ‹“=¬‡$êbàl–©‚¦$(±˜82ÕdRRK”“17-q&û;ká{z'îàr6˜m$ÚG:ñf6ór1J¤NŽ2›E™ärˆ&IL£`d:LK% ¡eU,Ñ…Õ#.*g#+P8J)Àd#.K†Hä[Í’D’I#„ŒACÝÃ¥Á¿4Ï4À@¥+ŽmÅ-=(…{;›8¾·ñ˯/ Žâ€û?C㻯 güéQ=%#-™×z²$#!Ã÷?žF⳸ÕϪZî­W©¹ö\Š1Ö2 UC5ð+a9d™`#´Û æT‚Ô• &‰¿*PµSž Ï?Yù€LçÊmš1X¥€#IC5ój°ùMWs"”!YðÊþm¹ä¢÷ëf¦Jª)R͵ëZƒàP§ˆSÖ|K†ÌQ|¥kÖ™&#-“7 Ò¶Ò]¥©/`m¨“ãi¶–çÒrZ|t~7Cñ6aõ°$2±Ç5d²¸¸¹ÎÍlléÑp‰Î*5%4è&M#-ASå/Ó©q\ÑçiÌ2ÖЋîöžû±:üÃÙÉv“lö'4æ6¹)×ÐøÐ¥tÁ”öo{ "/‘×´ÛŸDñ·Nù37}2oCx@wºÛA«Ÿ¾B“R½dc"#b+6=Gaµ(ô À§0 ^ž$­°G¸‚Ø”š3Ì3=B¯b»Ëšð«}ÙAm’ ÈID•Gé÷4šÂŠ~ûþ³í‚©œ[~UõË~Ÿ×¿#.²°±mI%z½þ@÷—¾©Gq1/Íùé#-qPhh'‘átxà«*¼=‡Û,f/Æú_8¬¸ºRÆ3>^ªÆŽ¼Ô°…WMpþÏÊ#G:Ý• 8C˜!ÔuC•óc…Æ^ø=f#‚±—Èõ4ó§×ä_Ä@OOIg¾Äú4 ß:Ê{/1ˆ’ô óŠºK,ì¥+}aç#Ð'4ESóùNæ¹çO/mêFm°nd[wof²XŒq¤BÜêåKª¶B‡Þ\+»´‹k¸ñfÂ%)džf¤à~˜œ«Ï¦xN#.šfÂ^¤J*(VAn—ÈÐà ?iº«BÁ¨%þ„¨¨Å™p+É–vÜ‹wº.ÍH`URb2ÌB”‰t7±²Ú²ñ–0Ý(A]¥kA\(&×'=±‘€1”3 /HaÀ\âe¨ÏêXfÇ›m…#-bæZ©PC*…a,Rìá¿°Àìã[iØ’‘Ñ4¢Ô²}\pŽÆ“6kÆ Š"¡µJ‘ƒcÌþ3#+‚ˆ Lˆšiv1ÀC|”Áe…1#.`u¬SQŠ ' ÌÐ04h†ê™Ý­Ü¤*„˜d!!ÌIÁ:½ÝÆ8¢‚0HÉaw¶w¿#.7Áˆ­ P'MHHQ”#.œÓ´ÍS±H`ÔAl¨0eÞçËÆ¿ÁÔð7ëÕtéÎø6"úË3õÍô0Þà¤S!20ɼÔê窚j"âe0œ“/ ºèJ!¶ã3ú˜-÷éÙ}oqU/Û0L¦göêF2Â^zÛ¸®Ìp ŸNá¿°rIÁv®‰EìÖÉ¥Ðô†00Ç,¤0nßšŠ¹çDLìYIŠ1^:’ðiÍ‚’S4d,ŒL…i‹“Ž:Ä%¦— ¯ÕÜÍ Zô¦ZBB<⤚xD©KΡMÄ[[4EÌ«¨µ£ÓŠÊø4Ì¡&\ŽŒes† ¯¿nÅDмÌ,öÌ9Æu@oq—„¸pp‡ÍJ¢ÅÐ’ð-s»å‡‰$Eñ…í;×1¤F·êÀGSk9ÎXN|e·£(͆t•{OA‹š5¦Î÷YÄF£=“#-;‰…;-€“†¶–#.•t9&+¦¢LFe¥%o˜Ý‘¤=Ì™—,už»`Á²ÙòœÒèLCpÈØÃƒ­å78ÚƒdÔŸ<¸ÐhZ­‡HÙÙÂ2š‰sZ5tÒîR@ŒS9€–ŠA¥4êqRY ™Úœ™t÷«.º¾!m´Ái嬹”:)r®]7óêH¶Bwkáß@í©Ó¸!ÍÈ…\t‰ë¢¶·7ÄÐÕCîñ‘-qdË$l¢hw™ƒ#. Œ%™:stk¤¶o`׊|¦¨úμ`«\¢ßÖ%LÕïMbšæ—Ló‰¤s»†ºoúâ•x¹“q4‰é:wjE®:N¤0-1—Ýá .W+#.³UµÙ7#+C"\`ÂÓÝ'LI ÉEG@/JfkéçZ¶+¹à>wÍ5Íø§%nÎÄÀWGìïp=ï]s”r*éÎ…Òii+BFìÄ·W'@æô47±Òy%64è•..“¶“°‡0E(Xw0t8 :ÞÐtæc,iÅäÄA,;Ž—á­Ø†¥+_ dÚhZΉÀ™ È-3f¼”Äð#.r™ ¼c,#+«-„À¨¨¤¡#.Œ7©x¹z²¶f‚M+Kƒ(¶‚öyÌ>±&a…@ÞI»„œùÜÆ ŽÖÙÕ×*Ç’à+tÌlàl¦(Þ„càa*-)†E‰¼ª¨ïßò\ó¦6vÓòÌlÔ?=n¨Mرû¦Bp%Q2g w𡨕¢í’&×Û%Æ0vJf²[ìäå°çQv3lŽQ'ç‡Ý(‡·J4 ¨èKÑg)ôg|7>žÓCw lÇdÛ¤9ÙÛ>ŽÝû¹ºñë>K3•"#.kÌîU¯<Ý6Ѧ%bŒ&Ê&]ó¨‚Yä[,§qÓS¬»%è##.TÆÛi² ÙÂm ÞoÄX,.¬”šÔ̓†°øå¸«ÖûæÍaÅÜ-º“kÔzm›öðÒÆTBRBl°UØh§4(LIæõ Ú[_&L;$t M»ãÂ7ªÉ#+L;ÜËvvè­Z //SJæ” 1 HÆÜq!¦YÛc;çLYÆåI” ¤,e JJBSøñx1ÔMœX(†€ç`L9bbmªh ²Ë=1#-mõTV¢ w:­‘›§bôÇnVG)¬ìšàùp.望Â8g:cÑ3ÎŒ£­:N÷,#-Ø2»o³§eé²g{–*YÁÄO\lN2AË«& Ý»&šo¾K qã`kCT‚ÑÒÚ¦DÁá-²íY†¾Y×ff¨u”õ³!eèRf4§ÃÆÄsm HH°dhXºˆt`AÔnC: X˜™Ÿ¢Há¦Ij(D{ ÉßXÇ„WJ¦²jwÉônÙ”ãZÝ#.RtÒ¡²æ¦4Í#.5CE[vÁKAŽÙKµÍ8BÌÝ™ƒhà6”¶"#-@`l3¹°»ŠÕ4&³½ÈC!Ãà €1nñ“I#-'@çš)]D¨Í†Óf\ƒ¦¶”@×c `q#+๖*¢P ƒK#.¦æÃA˜Ò –T •P¥`ç¶‹.T1fÎÈKµF…•(§d9¡Ô#6.Ør*‚‹AšoDA@aÐ1(k·‚¬ŒXZEWFÛ90 ‚h3h@‚0K—€Y‰ƒac§tðµ…±Pª0;#-šI€50J‹„8±˜,,á¦ÄÇ€0SC„–cºÝ'X$dÌÜ€s61¼aE jm)ÊäĸYˆóGt޶&X&CMC#.Å"‘b «'ØO/>¡ñ1ìmAÊ5TUewå6oDŽ©»CÓ¶OpØ2 |¹‘!Ü‚,"B@Mˆ*PèEO#.õlµÌë9û+õQ\ƒ²íšVV1N /ÛÞV5]/¸ç¬?Ò>£#+¦á†܆OSy x\qlð6“ÑÌŽë¡\µ¦Ú›î±ÜAR4Sƒ‘¼D3hf¼»HÒÃØîC r>)G×»êîën†Ú¹*¨=Ö¢/çá åÜ¢%|)W”“Hs›=õ€ºJh(¨Gf‚v0èk=v³Dü~Ú^NÁ¥W Q'vÔ\fƒ¼¹R'W(6£å+mØ•(—ꉦˆü%iÒDÓ&ON÷l~#’­4ytw:³õ¹¹õY¯šR_FõÚW·Z:Îþmv­Ü*l¾šªµº#.ò9s¯mÍ[Þç•­g1Y7z^Hß…^Pò€Iôô×ù‘ö€ŠEGßF\x¿3èS¨ç— *Ìé}vÄïCãÀد ºèo×Cç¦@s‚üö¦­ñFù:wu¨û•çi¦vèÇ"pd&ŽžÓº]Þp3wfMæ0*v©Z0b#i`‚#cŒ‘#.¡ŒI”-âjwÒÞní#-›×p\&X“ Â8ÙK$)AÙ…ÅFèˆÀƬÒˆ-’4l~U#-HºõEœÙ#-ÒžšQ®us\v‚Ó”1\Õá5aÓBÙÁÂD’{R,ÒQ „'§glî~=þal1âxk³p´‡ÖõAóœÂÌúƒ¢§ƒ­jcèb“V›[4žˆÔÂhƒ¤1ÌÃY™#-#.44ƒ[Õ©ˆeÑ¢¼;CLd4àDÍ¡Íh¢N®"Po(O_@›TærÞȃ°ÞÓHðe§ƒßãOTµþhÛ‡“lfÚ’?(²’{ô‘/lÐ~Òä$h1êï+$ úÂÃÈ k¢hå‹ädA-Ùa•NKÎMÎ8é®Ä`wvܶlO§V£UÆ)f!P¼Ä|\Kt†g×L.›ØºÌDÔÀ,@#-ð!#-°)O´iêQN¬¶îÁæ»’BNÌ+’NTº8i¬|®1÷¦z(†´Å˜ÇßáÝŒ6‚òCŒ-|ÿM¯M¤˜²1Xˆ“Ø”|M§3Ǧ·SZ43­vßWí.‘(ý*º±°¶¿aª»mE%¤Ö[kI¥"KjoÁÔ›d­²VVö–²e»eÆ‘D.@„KJˆ—±‹(‘Én\0‰b16*¼ µ'ÃÐÎdQHÒ.™òÂ#+ô46ùe[¼#-^],âV¸8; ˆÁ‚B’(H2‚;/?/›ê·Ü¦ª¬Xß–fƒû€#6}8Ý»çË@ÊÓË•uC«ëê§'~ýÁ¼]R‹ÆrÚî+â{Å«æ{¸ÔüHðÔ5›ÚÙJ p‚ÒD®€d˜2h#-Ä" #-A*êH¡i.ZÑD‘KÇYPâ¤+uDÄä”r+`F…a –‰(Óo#+I‹®ªLjó*ñxµ;u­­ÍZd#.ì âtßššïz‚…B™³)˜H*ïRÂÐ&ÜŠ`9šZÓðONÄ7Lˆ#+0J^,ë2 ’FCG-·” Ô¦ÍAãœΫ#nsM@š±ÛKKÔP|íM´W¦§!¦ÁjRÛCÍhŒãˆ¤lÓ)ؙۅ j'ºiḠÉjÊW.ä±>t¢l ÕÃ#.¸â SVRmÍéfAʦ¸ÞDÉ&ƒ-–í¶®3¸oí:û¯„9$Ý€!NèiªÖ *ë º5Jøp¯¸Ê¶5bVÆ—µk”¸cUÕ“(Ü€²#+(,´lE´¤*Å7¡ŒÉ™u¡±û5x;ýqaÖzÉàƒ"/ ]ÔÉ™wØ%™YAÍØ¨Öƒ¡ZÐÁbÌZ­aÁ´¿1`3ö³[&2ÆEô2°§'å— „± Àv÷û°lxº Q\Öxu!#+#++¿êK¢¨þäE‘ âBd|Hxó0aß'WV/ƒ£é•ãQ:Ñ<‚ „ŒȬbõDAÍAŠŒêéIFï™ã¢|ý#.#.óæpõœÐ¼BÄi‚HT¡Úx¨ÑŒV(¨Ð×·„åØvÞ†" €Òˆä2t#.ž!o~gϯooá£é­ÇÔ|,ÛJN‚h ý/º#ÀH`¶ˆRÞ¯sça(ûOÐQ¿çòúöxòŸ#.­$»Õd'g7« ¸Opïænëÿ°‹SÔJŒçåЦàmÚû& CÐ  %›5ôô¡£­ÜÀ3_`[ôT<¸Ð¯×Ú@ÌvEnoé`è˜n2EëíØ¨0 ;‚0`HdÆdxº‹hÒeÝ]&i”ì¿oº·*æåÏ<Þ{úú|ãlá!²eÙ-Š*%ênæºy’£hÛxÜ×-Ä1·w^-ç•ÚV“•¶ó%_½k­ví—Ëe?rE6‰q–»~Ï‘áã(¡¢«é+é÷Øò0ÞÌ=àIYˆà}FŸ]ô€âž¥§huib©#.¥T‚ÊÙ¶ž¶·å¾Ûé·ãÖÕðüQhZld**R66‹$if’Ù³jõú#Q·ßÕ~æùÍ%#.) 1jj6Q´ÓL£U¿©ÛD06ˆÚ!#+2ûá)Ø'°c‘pH]+aM°Fma$ƒ¼ž$«á-ëMÝsdScT+fÚ1Q™¬eb¦Ql°RIzóÊ­äÖ¥M©M­~ÈŠ]ê4!{qDMéåüsW§q=ï«èÌó"IீA6l!FOzú/£mLdI¥ˆ~˨ý»p³ƒê › ôGÀawÌ ç4,æBeñ=rû¥2¼oöã0¤$X¤æÓÜŸIUáüy±žÆ€JÉ®Í&Sd*ðBÄbȃY?GVÏm qK"7Ô6½‚;A22РpünBf‚uÁZ (H/ÀD(JuM­$!•[¤¶©¶¤ÓRmKiÙwW¾³dú¨*(#-Í·nêì’¹—àN¹±Aþí¤€‡Š%#.Àx—Ê”ùÚYVD!ï¢ñ,»]™lÞm쪌}]¸Ô# “\ãÊÔfý 5†W§4°&âc¸–‹¨ËBÙÙš–ÆÑVA¯…m!´ûQƒ,XT›\#+Ÿ{hÓ°5åy,ú¨÷+‘½¼PuëO}í´Ó÷bo¢pȇ™s3E<ƒPºYýBƒóÓùUÿbPyˆjóòµ@³’#+HbT›#QM¼UÌl˜5j’åUÅ6Šþmù–¼K((­›•’Ì €@I<;ÏgW¶ÎÙf’¶†²\ ¸¬!è#-ʺÍ.o€±„¢¤iTbd•Ñ46î(Ø@JŽÃÎ#.O?Ù˜p]Ûf o»ª¥’…ï¡t6ÔcV#.Ò†¨ÐšÙCBABF$Q€ô£¸Ù~MžèôÖÒ¤’6‚¸ÏÏôáùFˆ‚0?}eˆû# RâS#-U²¹`„jÐÝJëåA–Mþ%߯V*Z,ŠZ%¶üÛç&.älᙑ&ÄÅ…ÝÄÄÆwÝ­#-ƒÃý™0a#-A%ABªJmt³I©¢•-ë6ÛNìÕÝÔ¯óyZê›F“[Ò®J‹y»¬ÍÙ™U×7mE\íA%²,Ú¼»±¦‰[»¶îíi6T•2%65¼î­æšó«·H’§*ÂC‘m¨Å¬Q`ö¦{5íåÄkRš[)©1"…B]Þ.I0‘dXÓ6Ùl¥¯]Ü·k»tYcyÛJhƒ£z)Ñ› èš$UP­*½T%¤#-!€›4A?hÒáÈÕ"ÁdØ>eïFBÙª ’ÈÇ#+ª@HSᒀƶrô…93E(~ 2Æ0U —{äÉ5Ð×2&Cb R©µ!¾ªTŠÂflyp¹`.D#Ú#+lÿ—ï¸ pöÂK°’s}ô¾ÝvIÜ1‹ŠtßçŒ IÂOCmg&£‡ âêccż·âBÝÚñÓ“áù‘º©Ý’ª•8º_ ÑbÞ#-œ-ïxcMü½æf¹#.v}›yÁAGÊ)ļ.#-õêLoÕ~«Øa$Ôè#MÀfU½ÒsQð Є2Ý"I÷‹QGÀBs¢ÞGÉiéœ[ñÒÒšC@òä#.:¸£(Þ–3wÄ3¿;Ö-™Ã\ª(uKFÐGACM4lî¼ÛÞh0‡¶#-6;œtð(©¿–h‹±‘{³“ɄٖeÌÓ½jÌ…ŽÓãîG|ëøì5©‰É‡NÊ®eJX¦§bAºpC ³òG¼Gd 9]#+ôãeC#+⼑gA ®ÿÝýFà/‡h¨m&dFƒ+­#-2‰P‰LJ(ZoWiývðçúçM¤õ†_n¹ãûÒuuiÒ/‚Iñu5¶ f0­ÂÁåæí g:c1ü~N`µºq’ÆÈwm‡ÔRaA»%»kÌÙ¦v,ü\ÏQÕ67@¿é J%ˆãhf ãÚç™R$Ǿ`|ó­–#-ƒÖk1ŽýÍ*«cXiÛA'dìWέmäÚƒp8T¡“ùiµŽÂ/0i+¨G‰qäÁmtïT÷P¨cpkC†Ðli„áFúLûŽÐc¦tñ,ü#.ƒÈŽ.“0XżRUˆ¦GA!!Ôy¥C‘ŸKú×D4/=z–EÀn>&§#-ü–ÚŸ‘ía#+£ÌÆÛ¿vÆ#-‘õVµ.kh0Äù6#+Iãúh#+V?õWþ¿ôìõçþïö¯ýÎûÿêÿõýßìoý?ûö»ýÿêÿû¿ßOáÙÓóz}ß÷eíø?îŸû?»Õÿüz9ÿßÿ—Íûpý¿ÿ?·ý¿õþÿöÿíÿ£þúÿé»þñÂãþ?ãé÷ýWüÿ}~®?Z¡?RÖÉüÊBÄ0m)œH~>gún¢e?°)MX?Ùþé#+*â"áÁ¤¹äÿ¤S+\Œý„m4 þTø*Š„Y40v¤¼~“ý9üžçwh’@“3>o›lÛ_ŽˆÿvÖ¸›J™Ë#+§H¥·dªÏ,\L¸ÓJà©Ü?ñÝÓzjo4 êCþÁmÖîäÛþàhÉñ‹þ#+Ì¢<èGvòºÁuž:DšzLÀ¥dÜÒ]D÷±)YlÞƒí)/@­…ãÈ­ü U´¡½Ñ„K”Æ¢.*ôG,ÖQÛ¬1y•m^Õ#-â((¤×X¥â½«Ê#+#4×ÒáÓÿ³wÌìl*¬;íÇ–ïåž>ÚÝ6ivÏ·b7–Ë[JN‡ý3™Ï÷ø^ }[ñÔ#-÷#.Ò2ª ¿JS=[ÚefìÐHã#-–?D©ƒa³ßƒS%+;*…ÎŽàïf>Aàj'’ \R3³Uï®ÍÜ4Óu¢4ÐÂH6’/&a6ªh4mÖ8uiÑ4ªÊJ›q­ŽUj*:Á³‚ð³Xð7µœ¤Y>8=˜“]4²ËÔCVMœ3¯©ö*Ä&Þ¤ÙÝÎ4A#-bÒ7P‡ÙXgl›ÑTß:ŽÍ•S™Ï†[K|Dü™ç³è"™À?æD?á@1áTÝo0X" ¥±dè{%‰v¥ Eb(}áêèýgýùi2€ªŸŒ~È–cö5ìIedÿ~pU}BûSÓûhwtÈL„„&_h‡‰ùõtuÿ¨o&Ø…hºé<ÑeJ <‡Ú,‘Ó–K{ûoì¦Ùó½ÑéÎXä\v€úå'kSz+¾ß(Zí#+84E)¾cy4§wXÙú=9ù‰ÁFÎl³CT&ŽúáÓHÙ¨àŒ$>lXS°Ë-XhOQ8hoh¸„$‹q%yË_=7§¸àð½— ôÅðå#-«½#.Ì'THµãZH-¿ÜÅE·5QâÚåoJ5{-[ÉbµŠV@4ˆ–ZëwÌÝ‚ÛuÀ.…ƒ‹‡$9¹bCfI#-#-!ôÀ¡?üˆ^ùYS”\¼ÉÔ(Úê÷¦ ä-±$26EìïÜo3Þ3o cVÙUy {ZZ7¥öà?«ÿ܂삋”ÿãßùAÊqÏ/ŠH#.ÅOý¾RP‹_ö¸4éÿTŸøÒðH8é!ŠÆÝþ¦Ãþ·'ôÕwfb¿KVƒý}ïëÿ¿ßÿ[³ó/»¾uùðЛ¹+èÉÄ8éÓG¼vßÓBŸƒ“Úÿ?R„ƒ“À²]çôùxr$ðÏ‚r/üö'“”kÿTóÆe€ˆ~¿úÔNl?s„T#.?aÅ‘ïñÒpÿóµ7J0„²{(WQ6gÃÒ#¯ã ªïò3ÿçH!­*\’oé²ÝRþ4ZÎJíâqTE3‚o–«…ÆY̘wÈâ=§NÚ gA••ßÎX›Â7 ^[ c~¢qQ-y[Na Âð;ü¸fL5¦HK}<ÿ5Öµ$a›áO÷|=)§Ûœ‰iÿÊ{¨8Ë£þŶ7²Õõµ¨öðF2HÆ_¼<8ð¾ßü¦[_ÈýÇ7/ÏùÆŠ§ÿø»’)„€#-[3È #<== -#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEjH6y+TsMRfVzL+XRG6xXHc13IpUFAmcfxegACgkQG6xXHc13\nIpUgtg//WMykqX23+Uk3D9oTsjsL/MwX9M2vIjVubv7Lawrsa1Zv1npjhcn0cS9G\n6Z40dh8H70PUbB/ldlLY+BmSi+ls7I8rIcsZGUSxPhlqaO2GemLkHxM6DgkDrW4k\nVvxQxCH+ndS5NhC+4UtHksVp8aSFd7zpPDID23iqJixDY5vT+cyV8bDCw3Y9FPvU\n/E4hMoem5L4Xfwu30ETlmQf7SRhnD/Zg0CUBY4Cy4Hw/ZvFhWK3grtxxys32izsv\nUj9LQAksxkSNK0BRr9GI0uoULmJGpJx+UNEyG4EcEc6OCDx6Y0wsbk9HSvGc1dyI\nz7gTOabR/+TQCKmzMnoiqaTPng+qC9BueX/8k0tpsSCejmHuMSphjgm1DCy1lbBq\ni2cLa/Su64wTeQzf6lr4DmIMAyLE7ZtNMgtBli4hhmOqLuI/w570auQJDCkTAU7u\nSQtZf4zpVXdTiDzValelwVGuWwzKzBOTuZxes7nYrE5+O/1SK4U0TRnd1qM0g4sl\ngcRMp/Jyc9vUKKw5B9onUI8kwYFGjvoc4PpJeM97HeNjvLEFZ/9vUuMJfopxW50r\n2KiHxk+oRlf/Xh1fGK2Ltf7zGc94c5HukOpL8P/yKa+Z+f5WBh9KY6orzIBeIWNd\nlvjiN6emAyxaQghaFFnZCfqlQ5D1OjHvuxCnxgVM/W3+75v8Hd0=\n=iyYj\n-----END PGP SIGNATURE-----\n +#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEjH6y+TsMRfVzL+XRG6xXHc13IpUFAmczp/cACgkQG6xXHc13\nIpUETQ//V9rcegq5fXQ4wJpTMVvKwtldiObdr7R/ZfMHgHvp6LFQ3XwCWsh+Qqcp\nXWAmQNtPfgxQcfGnGHEupp6d1XsLGHWi/zqFQx43qE6XIBxnNAO9SKLTRlJrWuUX\nq09jkP7qrV96SVKqpFf5bIS9Pb0ZtvFsnawIBgWZY8Da//W9eDCBSE0cD0lAhsCE\nwTBIO9avqRlaxCbGUr0UF+p3T4A5Qv2qVFfX6qGCKvTMcg9/uSUU3LTn4FUXVBeQ\nwJmBzZN1IRH5t54b1PHjCjGkLkD6Wr7cqecjEyPvJUzqad75MBLrj4qR+QtzMb40\noWX5zg2QwVcdWOtbR6R0toYE2d2PgvgSHtyvcNutIKlcOm8Me2xzFy8KS1hhK3go\n3cdlz57onWftgAyU0OKTz8FXlPDnQOtzANLRTSd8oaPceF/FdHTPVZNLMJlmvBC8\n7CHP245o+iYEiM0BfV2CM4w6pmXwK9kU5DHlLN9oM0jd6jTYntP3+zwqTRw9drg6\nMDXijWLPFiNMUegz6isOrLnilHtjss3GSQ5SC39fmy94QoqI0k9wQRSQt2adZj7v\nSlWE4Cm/png5L2bLFg4cykDRKmnD0NFjWkNb5fprUM7Xnm4TfgUSCusPQe8gox2L\nGj/8L+0OIq5+1eN/7CVwf+Wmu7c3+yYtXlEzNVK8wkX8lz15TBM=\n=GL1q\n-----END PGP SIGNATURE-----\n From 9ae9647102cb35371a888fee48fbf48fb0201a63 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 14:28:19 +0900 Subject: [PATCH 101/348] sample: simple-send-canbus: Fix buffer length "abc" without NUL terminator is 3 bytes instead of 4 bytes. We don't want to send NUL terminator on CSP packet. Signed-off-by: Yasushi SHOJI --- samples/posix/simple-send-canbus/src/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/posix/simple-send-canbus/src/main.c b/samples/posix/simple-send-canbus/src/main.c index 43135c88d..2149d99f5 100644 --- a/samples/posix/simple-send-canbus/src/main.c +++ b/samples/posix/simple-send-canbus/src/main.c @@ -35,8 +35,8 @@ int main(int argc, char * argv[]) /* prepare data */ packet = csp_buffer_get_always(); - memcpy(packet->data, "abc", 4); - packet->length = 4; + memcpy(packet->data, "abc", 3); + packet->length = 3; /* send */ csp_send(conn, packet); From 50116491f89b987b0983546844a5841e279fbb88 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 14:29:52 +0900 Subject: [PATCH 102/348] sample: simple-send-usart: Fix buffer length "abc" without NUL terminator is 3 bytes instead of 4 bytes. We don't want to send NUL terminator on CSP packet. Signed-off-by: Yasushi SHOJI --- samples/posix/simple-send-usart/src/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/posix/simple-send-usart/src/main.c b/samples/posix/simple-send-usart/src/main.c index 745cfec43..bb53f8bd1 100644 --- a/samples/posix/simple-send-usart/src/main.c +++ b/samples/posix/simple-send-usart/src/main.c @@ -42,8 +42,8 @@ int main(int argc, char * argv[]) /* prepare data */ packet = csp_buffer_get_always(); - memcpy(packet->data, "abc", 4); - packet->length = 4; + memcpy(packet->data, "abc", 3); + packet->length = 3; /* send */ csp_send(conn, packet); From 93e69dde5c8f67957e5fb18e903afc3e838f9f55 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 21:02:34 +0900 Subject: [PATCH 103/348] interfaces: csp_if_zmqhub: Mark ret __maybe_unsed csp_if_zqmhub.c doesn't properly handle return values but depends on assert(). For now, make compiler silent by marking them by __maybe_unused. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 14f536c59..9e01deeea 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -164,7 +164,7 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr uint32_t __maybe_unused flags, csp_iface_t ** return_interface) { - int ret; + int __maybe_unused ret; pthread_attr_t attributes; zmq_driver_t * drv = calloc(1, sizeof(*drv)); assert(drv != NULL); @@ -228,7 +228,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add char sub[100]; csp_zmqhub_make_endpoint(host, pubport, sub, sizeof(sub)); - int ret; + int __maybe_unused ret; pthread_attr_t attributes; zmq_driver_t * drv = calloc(1, sizeof(*drv)); assert(drv != NULL); From 793e10b84eee0cb26b8b405c62b49fd317f1e29a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 3 Jun 2024 10:31:53 +0200 Subject: [PATCH 104/348] cmake: Add CSP_BUILD_SAMPLES option Building samples and examples with CMake has been tedious, requiring manual specification of each sample to build. This commit introduces a new option, `CSP_BUILD_SAMPLES`, which simplifies the process. With this option, you can now build all samples using the following commands: cmake -B builddir -DCSP_BUILD_SAMPLES=1 ninja -C builddir This enhancement streamlines the workflow for building all samples. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 7 ++++++- doc/INSTALL.md | 7 +++++++ examples/CMakeLists.txt | 14 +++++++------- samples/posix/hmac/CMakeLists.txt | 2 +- samples/posix/simple-send-canbus/CMakeLists.txt | 2 +- samples/posix/simple-send-usart/CMakeLists.txt | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93c4a7ebf..834fb4d2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ option(CSP_USE_RTABLE "Use routing table" OFF) option(CSP_BUFFER_ZERO_CLEAR "Zero out the packet buffer upon allocation" ON) option(CSP_ENABLE_PYTHON3_BINDINGS "Build Python3 binding" OFF) +option(CSP_BUILD_SAMPLES "Build samples and examples by default" OFF) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) @@ -112,9 +113,13 @@ elseif(CSP_ZEPHYR) endif() target_link_libraries(csp PRIVATE csp_common) +if(NOT CSP_BUILD_SAMPLES) + set(CSP_SAMPLES_EXCLUDE "EXCLUDE_FROM_ALL") +endif() + add_subdirectory(src) -add_subdirectory(examples) add_subdirectory(unittests) +add_subdirectory(examples) add_subdirectory(samples) if(${CSP_ENABLE_PYTHON3_BINDINGS}) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 89cbe3950..f780082f7 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -88,6 +88,13 @@ use the following command: cmake --install builddir --component runtime ``` +if you want to build tools and samples, define `CSP_BUILD_SAMPLES=ON` +when you run `cmake`. + +```shell +cmake -B builddir -DCSP_BUILD_SAMPLES=ON +``` + ## Reproducible Builds libcsp supports Reproducible Builds. To enable it, set diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 970c433b4..9f24ab274 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,12 +1,12 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - add_library(csp_posix_helper OBJECT csp_posix_helper.c) - add_executable(csp_arch EXCLUDE_FROM_ALL csp_arch.c) - add_executable(csp_server_client EXCLUDE_FROM_ALL csp_server_client.c) - add_executable(csp_server EXCLUDE_FROM_ALL csp_server.c) - add_executable(csp_client EXCLUDE_FROM_ALL csp_client.c) - add_executable(csp_bridge_can2udp EXCLUDE_FROM_ALL csp_bridge_can2udp.c) - add_executable(zmqproxy EXCLUDE_FROM_ALL zmqproxy.c) + add_library(csp_posix_helper OBJECT ${CSP_SAMPLES_EXCLUDE} csp_posix_helper.c) + add_executable(csp_arch ${CSP_SAMPLES_EXCLUDE} csp_arch.c) + add_executable(csp_server_client ${CSP_SAMPLES_EXCLUDE} csp_server_client.c) + add_executable(csp_server ${CSP_SAMPLES_EXCLUDE} csp_server.c) + add_executable(csp_client ${CSP_SAMPLES_EXCLUDE} csp_client.c) + add_executable(csp_bridge_can2udp ${CSP_SAMPLES_EXCLUDE} csp_bridge_can2udp.c) + add_executable(zmqproxy ${CSP_SAMPLES_EXCLUDE} zmqproxy.c) target_include_directories(csp_posix_helper PRIVATE ${csp_inc}) target_include_directories(csp_arch PRIVATE ${csp_inc}) diff --git a/samples/posix/hmac/CMakeLists.txt b/samples/posix/hmac/CMakeLists.txt index f9c7478e3..1fd085248 100644 --- a/samples/posix/hmac/CMakeLists.txt +++ b/samples/posix/hmac/CMakeLists.txt @@ -1,3 +1,3 @@ -add_executable(sample-hmac EXCLUDE_FROM_ALL src/main.c) +add_executable(sample-hmac ${CSP_SAMPLES_EXCLUDE} src/main.c) target_include_directories(sample-hmac PRIVATE ${csp_inc}) target_link_libraries(sample-hmac PRIVATE csp) diff --git a/samples/posix/simple-send-canbus/CMakeLists.txt b/samples/posix/simple-send-canbus/CMakeLists.txt index 0882db6c1..5881d9838 100644 --- a/samples/posix/simple-send-canbus/CMakeLists.txt +++ b/samples/posix/simple-send-canbus/CMakeLists.txt @@ -1,3 +1,3 @@ -add_executable(simple-send-canbus EXCLUDE_FROM_ALL src/main.c) +add_executable(simple-send-canbus ${CSP_SAMPLES_EXCLUDE} src/main.c) target_include_directories(simple-send-canbus PRIVATE ${csp_inc}) target_link_libraries(simple-send-canbus PRIVATE csp) diff --git a/samples/posix/simple-send-usart/CMakeLists.txt b/samples/posix/simple-send-usart/CMakeLists.txt index 86ef50b33..59e5dc281 100644 --- a/samples/posix/simple-send-usart/CMakeLists.txt +++ b/samples/posix/simple-send-usart/CMakeLists.txt @@ -1,3 +1,3 @@ -add_executable(simple-send-usart EXCLUDE_FROM_ALL src/main.c) +add_executable(simple-send-usart ${CSP_SAMPLES_EXCLUDE} src/main.c) target_include_directories(simple-send-usart PRIVATE ${csp_inc}) target_link_libraries(simple-send-usart PRIVATE csp) From 0dee6a601bc21b6f5dee75172c3e4df0d37fe47e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Dec 2024 21:23:06 +0900 Subject: [PATCH 105/348] buildall.py: cmake: Tell how to build samples & examples Teach buildall.py how to build samples and examples using `CSP_BUILD_SAMPLES`. Signed-off-by: Yasushi SHOJI --- examples/buildall.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/buildall.py b/examples/buildall.py index 0f9b2b8e4..44527cc3b 100755 --- a/examples/buildall.py +++ b/examples/buildall.py @@ -24,18 +24,13 @@ def build_with_meson(): def build_with_cmake(): - targets = ['examples/csp_server_client', - 'examples/csp_server', - 'examples/csp_client', - 'examples/csp_bridge_can2udp', - 'examples/csp_arch', - 'examples/zmqproxy'] + build_samples = '-DCSP_BUILD_SAMPLES=ON' builddir = 'build' - cmake_setup = ['cmake', '-GNinja', '-B' + builddir] + cmake_setup = ['cmake', '-GNinja', '-B' + builddir, build_samples] cmake_compile = ['ninja', '-C', builddir] subprocess.check_call(cmake_setup) - subprocess.check_call(cmake_compile + targets) + subprocess.check_call(cmake_compile) def build_with_waf(): From 8649f145c544a4a69214412a3c7b31d0af84fa59 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 3 Dec 2024 17:38:04 +0900 Subject: [PATCH 106/348] github: workflow: build-test-zephyr: Replace with HWMv2 board name "mps2_an385" was a old board name. With Zephyr Hardware Model v2 (aka. HWMv2), it's been renamed to mps2/an385. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-zephyr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 0c8e969e6..1597c7e3d 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -14,7 +14,7 @@ jobs: board: - scobc_module1 - qemu_cortex_m3 - - mps2_an385 + - mps2/an385 python-version: - '3.10' - '3.11' From e945ab155869447cf272055908ac02cece6c2c0e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 3 Dec 2024 17:55:10 +0900 Subject: [PATCH 107/348] github: workflow: build-test-zephyr: Add Python 3.12 Add Python 3.12 for the Zephyr build test. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-zephyr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 1597c7e3d..7466c0aa4 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -18,6 +18,7 @@ jobs: python-version: - '3.10' - '3.11' + - '3.12' steps: - name: Setup Python ${{ matrix.python-version }} From 6de44fbc7f8842e006820da6dfb1be7a3ca0c2a3 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 3 Dec 2024 17:49:22 +0900 Subject: [PATCH 108/348] cmake: Remove compile options for Zephyr When building libcsp with Zephyr RTOS, the compilation flags are handled by the Zephyr Build System. Adding extra flags here causes more harm than good. This commit removes these flags completely. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 834fb4d2d..c0d834836 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,10 +106,6 @@ if(CSP_POSIX) -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter $<$:-Wno-gnu-zero-variadic-macro-arguments>) -elseif(CSP_ZEPHYR) - target_compile_options(csp_common INTERFACE - -Wall -Wextra - -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter) endif() target_link_libraries(csp PRIVATE csp_common) From 926ee7cf09f9670f8444adde9c12af988024e487 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Mon, 16 Dec 2024 05:56:32 +0200 Subject: [PATCH 109/348] cmake: Add build support for FreeRTOS The CSP_SYSTEM_NAME variable is introduced to specify the kernel name since CMAKE_SYSTEM_NAME does not recognize FreeRTOS. This approach ensures flexibility for adding support for new kernels in the future. --- CMakeLists.txt | 6 ++++++ csp_autoconfig.h.in | 1 + src/arch/CMakeLists.txt | 10 +++++++--- src/arch/freertos/CMakeLists.txt | 14 +++++++++++--- src/arch/freertos/csp_hooks.c | 17 +++++++++++++++++ 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/arch/freertos/csp_hooks.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c0d834836..2a1b3024b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,12 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Zephyr") set(CSP_ZEPHYR 1) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") + if(CSP_SYSTEM_NAME STREQUAL "freertos") + set(CSP_FREERTOS 1) + else() + message(FATAL_ERROR "Not supported ${CSP_SYSTEM_NAME}") + endif() endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND diff --git a/csp_autoconfig.h.in b/csp_autoconfig.h.in index 41cb59475..1edb5aeec 100644 --- a/csp_autoconfig.h.in +++ b/csp_autoconfig.h.in @@ -1,5 +1,6 @@ #cmakedefine01 CSP_POSIX #cmakedefine01 CSP_ZEPHYR +#cmakedefine01 CSP_FREERTOS #cmakedefine01 CSP_HAVE_STDIO #cmakedefine01 CSP_ENABLE_CSP_PRINT diff --git a/src/arch/CMakeLists.txt b/src/arch/CMakeLists.txt index fcf6e7585..bd22728bc 100644 --- a/src/arch/CMakeLists.txt +++ b/src/arch/CMakeLists.txt @@ -1,9 +1,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) add_subdirectory(posix) -elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeRTOS") - set(CSP_FREERTOS 1) - add_subdirectory(freertos) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") + if(CSP_SYSTEM_NAME STREQUAL "freertos") + set(CSP_FREERTOS 1) + add_subdirectory(freertos) + else() + message(FATAL_ERROR "Not supported ${CSP_SYSTEM_NAME}") + endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "Zephyr") set(CSP_ZEPHYR 1) add_subdirectory(zephyr) diff --git a/src/arch/freertos/CMakeLists.txt b/src/arch/freertos/CMakeLists.txt index 21478ec90..fb7e2d407 100644 --- a/src/arch/freertos/CMakeLists.txt +++ b/src/arch/freertos/CMakeLists.txt @@ -1,3 +1,11 @@ -message(FATAL_ERROR - "${CMAKE_SYSTEM_NAME} is not yet supported by CMake. Instead, use Meson. - https://github.com/libcsp/libcsp/issues/524") +target_sources(csp PRIVATE + csp_clock.c + csp_queue.c + csp_semaphore.c + csp_system.c + csp_time.c + csp_hooks.c +) + +target_link_libraries(csp PRIVATE freertos_kernel) + diff --git a/src/arch/freertos/csp_hooks.c b/src/arch/freertos/csp_hooks.c new file mode 100644 index 000000000..9791c49b1 --- /dev/null +++ b/src/arch/freertos/csp_hooks.c @@ -0,0 +1,17 @@ +#include +#include "csp_macro.h" + +__weak uint32_t csp_memfree_hook(void) { + return 0; +} + +__weak unsigned int csp_ps_hook(csp_packet_t * packet) { + return 0; +} + +__weak void csp_reboot_hook(void) { +} + +__weak void csp_shutdown_hook(void) { +} + From 935506a09f1b25a65617090382134b43ac11541b Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 16 Dec 2024 21:46:25 +0900 Subject: [PATCH 110/348] cmake: arch: freertos: Remove excessive newline Removed an unnecessary trailing newline from the CMakeLists.txt file. No functionality has been changed. Signed-off-by: Yasushi SHOJI --- src/arch/freertos/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arch/freertos/CMakeLists.txt b/src/arch/freertos/CMakeLists.txt index fb7e2d407..594faf000 100644 --- a/src/arch/freertos/CMakeLists.txt +++ b/src/arch/freertos/CMakeLists.txt @@ -8,4 +8,3 @@ target_sources(csp PRIVATE ) target_link_libraries(csp PRIVATE freertos_kernel) - From da00bc5d15b5a2f691c8ceebcf6642a3d855fd98 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 16 Dec 2024 22:32:51 +0900 Subject: [PATCH 111/348] cmake: Replace explicit CMAKE_SYSTEM_NAME checks with CSP_* variables Replaced the use of CMAKE_SYSTEM_NAME with CSP_POSIX, CSP_FREERTOS, and CSP_ZEPHYR across all CMakeLists.txt files. This simplifies conditional branching by using these variables instead of relying on explicit checks for CMAKE_SYSTEM_NAME. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 5 ++--- examples/CMakeLists.txt | 2 +- samples/CMakeLists.txt | 2 +- src/arch/CMakeLists.txt | 17 ++++------------- src/drivers/CMakeLists.txt | 4 ++-- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a1b3024b..5eafc7423 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,8 +51,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") endif() endif() -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND - CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") +if(CSP_POSIX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") include(CheckIncludeFiles) check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) @@ -137,7 +136,7 @@ endif() configure_file(csp_autoconfig.h.in include/csp/autoconfig.h) -if(NOT CMAKE_SYSTEM_NAME STREQUAL "Zephyr") +if(NOT CSP_ZEPHYR) install(TARGETS csp LIBRARY COMPONENT runtime) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/;${CMAKE_CURRENT_SOURCE_DIR}/include/; TYPE INCLUDE diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 9f24ab274..41174d5e3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CSP_POSIX) add_library(csp_posix_helper OBJECT ${CSP_SAMPLES_EXCLUDE} csp_posix_helper.c) add_executable(csp_arch ${CSP_SAMPLES_EXCLUDE} csp_arch.c) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 9a66df16a..1880767af 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,3 +1,3 @@ -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CSP_POSIX) add_subdirectory(posix) endif() diff --git a/src/arch/CMakeLists.txt b/src/arch/CMakeLists.txt index bd22728bc..a98e0f0c5 100644 --- a/src/arch/CMakeLists.txt +++ b/src/arch/CMakeLists.txt @@ -1,16 +1,7 @@ -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(CSP_POSIX 1) +if(CSP_POSIX) add_subdirectory(posix) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") - if(CSP_SYSTEM_NAME STREQUAL "freertos") - set(CSP_FREERTOS 1) - add_subdirectory(freertos) - else() - message(FATAL_ERROR "Not supported ${CSP_SYSTEM_NAME}") - endif() -elseif(CMAKE_SYSTEM_NAME STREQUAL "Zephyr") - set(CSP_ZEPHYR 1) +elseif(CSP_FREERTOS) + add_subdirectory(freertos) +elseif(CSP_ZEPHYR) add_subdirectory(zephyr) -else() - message(FATAL_ERROR "invalid system ${CMAKE_SYSTEM_NAME}") endif() diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 6d29d4076..109d9f55a 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -13,10 +13,10 @@ if(LIBSOCKETCAN_FOUND) endif() endif() -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CSP_POSIX) target_sources(csp PRIVATE usart/usart_linux.c) target_sources(csp PRIVATE eth/eth_linux.c) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Zephyr") +elseif(CSP_ZEPHYR) target_sources(csp PRIVATE usart/usart_zephyr.c) endif() From 6b1403536499780609657fbc2417e4e8702deacd Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 16 Dec 2024 22:39:34 +0900 Subject: [PATCH 112/348] cmake: Move Zephyr to CSP_SYSTEM_NAME Previously, Zephyr RTOS used "Zephyr" as the value for CMAKE_SYSTEM_NAME. To standardize behavior, CMAKE_SYSTEM_NAME is now left unchanged, defaulting to "Generic" for systems unknown to CMake. With CSP_SYSTEM_NAME introduced in recent commits, this update moves Zephyr RTOS to use CSP_SYSTEM_NAME instead. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 4 ++-- contrib/zephyr/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eafc7423..075ae754f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,11 +41,11 @@ option(CSP_BUILD_SAMPLES "Build samples and examples by default" OFF) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Zephyr") - set(CSP_ZEPHYR 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") if(CSP_SYSTEM_NAME STREQUAL "freertos") set(CSP_FREERTOS 1) + elseif(CSP_SYSTEM_NAME STREQUAL "Zephyr") + set(CSP_ZEPHYR 1) else() message(FATAL_ERROR "Not supported ${CSP_SYSTEM_NAME}") endif() diff --git a/contrib/zephyr/CMakeLists.txt b/contrib/zephyr/CMakeLists.txt index 19be58fd3..44c01aa9c 100644 --- a/contrib/zephyr/CMakeLists.txt +++ b/contrib/zephyr/CMakeLists.txt @@ -1,5 +1,5 @@ if(CONFIG_LIBCSP) - set(CMAKE_SYSTEM_NAME "Zephyr") + set(CSP_SYSTEM_NAME "Zephyr") set(CMAKE_BUILD_TYPE None) set(CSP_PRINT_STDIO OFF) From 2b0af62dedaae563369ef9882e55cf33dd10bbbc Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 16 Dec 2024 22:51:50 +0900 Subject: [PATCH 113/348] cmake: Make CSP_SYSTEM_NAME case-insensitive `CMAKE_SYSTEM_NAME` is typically uppercase or capitalized, but our FreeRTOS port uses lowercase. To handle this inconsistency, this commit makes `CSP_SYSTEM_NAME` checks case-insensitive by converting the value to lowercase before comparison. This change ensures compatibility with various naming conventions. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 075ae754f..98f1a125d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,9 +42,10 @@ option(CSP_BUILD_SAMPLES "Build samples and examples by default" OFF) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") - if(CSP_SYSTEM_NAME STREQUAL "freertos") + string(TOLOWER "${CSP_SYSTEM_NAME}" csp_system_name) + if(csp_system_name STREQUAL "freertos") set(CSP_FREERTOS 1) - elseif(CSP_SYSTEM_NAME STREQUAL "Zephyr") + elseif(csp_system_name STREQUAL "zephyr") set(CSP_ZEPHYR 1) else() message(FATAL_ERROR "Not supported ${CSP_SYSTEM_NAME}") From a4d21813dd44415ebf6a907a708572c7923250d4 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 22 Dec 2024 12:29:04 +0900 Subject: [PATCH 114/348] doc: INSTALL: Add Python bindings build instructions for CMake Document the steps to build Python bindings using CMake. Include necessary options and additional steps for enabling the routing table and setting `PYTHONPATH` for usage. Also, add an additional headline level for improved section structure. Signed-off-by: Yasushi SHOJI --- doc/INSTALL.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index f780082f7..709cfa606 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -88,6 +88,8 @@ use the following command: cmake --install builddir --component runtime ``` +### Building All Samples with CMake + if you want to build tools and samples, define `CSP_BUILD_SAMPLES=ON` when you run `cmake`. @@ -95,6 +97,25 @@ when you run `cmake`. cmake -B builddir -DCSP_BUILD_SAMPLES=ON ``` +### Python Bindings with CMake + +If you want to build Python bindings, define +`CSP_ENABLE_PYTHON3_BINDINGS=ON` when you run `cmake`. You also need +to enable the routing table (`CSP_USE_RTABLE`) when building the +Python bindings. + +```shell +cmake -B builddir -DCSP_ENABLE_PYTHON3_BINDINGS=ON -DCSP_USE_RTABLE=ON +``` + +To use the bindings, you need to install them to a location where +Python searches by default or specify the path to Python: + +``` +PYTHONPATH=build python3 -c 'import libcsp_py3 as csp' +``` + + ## Reproducible Builds libcsp supports Reproducible Builds. To enable it, set From f2ac619be073ece2f2c550b05311f883990c8282 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 22 Dec 2024 15:46:42 +0900 Subject: [PATCH 115/348] doc: INSTALL: Fix typo in PYTHONPATH path Correct the PYTHONPATH path in the INSTALL document. The path incorrectly used `build` instead of the consistent `builddir` used throughout the document. Signed-off-by: Yasushi SHOJI --- doc/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 709cfa606..b1395b875 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -112,7 +112,7 @@ To use the bindings, you need to install them to a location where Python searches by default or specify the path to Python: ``` -PYTHONPATH=build python3 -c 'import libcsp_py3 as csp' +PYTHONPATH=builddir python3 -c 'import libcsp_py3 as csp' ``` From 5a3ca0d0f746b5839ddc4458e1b13f27b62de87b Mon Sep 17 00:00:00 2001 From: Wookhyun Shin Date: Thu, 26 Dec 2024 11:47:22 +0900 Subject: [PATCH 116/348] waf: interfaces: Add csp_if_tun Include the missing file `src/interfaces/csp_if_tun.c` in `wscript`. This file was introduced in commit `70e43adec06dea2` but was not added to the waf build system. Note that it was already added to CMake in commit `2f9adbfeb79ec6f`. --- wscript | 1 + 1 file changed, 1 insertion(+) diff --git a/wscript b/wscript index 490462440..2715a02b2 100644 --- a/wscript +++ b/wscript @@ -118,6 +118,7 @@ def configure(ctx): 'src/interfaces/csp_if_can_pbuf.c', 'src/interfaces/csp_if_kiss.c', 'src/interfaces/csp_if_i2c.c', + 'src/interfaces/csp_if_tun.c', 'src/arch/{0}/**/*.c'.format(ctx.options.with_os), ]) From c0453335b13f7066352e5307d6ce6d685786d4f4 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 4 Jan 2025 20:05:43 +0900 Subject: [PATCH 117/348] github: workflows: doc: rollback to ubuntu-2204 GitHub Actions recently updated `ubuntu-latest` from `ubuntu-22.04` to `ubuntu-24.04`, causing documentation builds to fail with the following error: Exception occurred: File "/home/runner/.local/lib/python3.12/site-packages/clang/cindex.py", line 4179, in get_cindex_library raise LibclangError(msg) clang.cindex.LibclangError: /usr/lib/llvm-14/lib/libclang-14.so: cannot open shared object file: No such file or directory. To provide a path to libclang use Config.set_library_path() or Config.set_library_file(). The full traceback has been saved in /tmp/sphinx-err-424nf1r4.log, if you want to report the issue to the developers. Please also report this if it was a user error, so that a better error message can be provided next time. A bug report can be filed in the tracker at . Thanks! gmake[2]: *** [CMakeFiles/docs.dir/build.make:73: html/index.html] Error 2 gmake[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/docs.dir/all] Error 2 gmake: *** [Makefile:91: all] Error 2 Error: Process completed with exit code 2. To resolve this, the workflows are rolled back to explicitly use `ubuntu-22.04`. Signed-off-by: Yasushi SHOJI --- .github/workflows/develop-build-sphinx-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index 5cf48da51..0dbec21af 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -8,7 +8,7 @@ on: jobs: build-docs: if: github.repository_owner == 'libcsp' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout the repository uses: actions/checkout@v4 From 0057b257955f7160580d3a660cfe0ee7d8a88c75 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 4 Jan 2025 19:40:58 +0900 Subject: [PATCH 118/348] github: workflows: build-test: Remove MacOS Remove MacOS from GitHub Actions workflows as it is not currently supported. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 165984032..df811a620 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -35,13 +35,6 @@ jobs: run: | sudo apt-get install ninja-build ${{ matrix.buildsystem }} - - name: Setup packages on MacOS - if: ${{ runner.os == 'macOS' && matrix.buildsystem != 'waf' }} - run: | - brew update - brew install ninja ${{ matrix.buildsystem }} - brew install zeromq - - name: Checkout uses: actions/checkout@v4 From fc3de68fef5642a2e195351a306e2e2dfe3f5f17 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 01:35:10 +0900 Subject: [PATCH 119/348] github: workflows: build-test: Test with various GCC versions Building libcsp sometimes fails with specific GCC versions due to changes in the default warning levels. This commit adds a matrix to test builds with GCC versions 10 through 14. The "exclude" strategy is used instead of "include" because, despite GitHub Actions supporting matrix expansion, I could not make it work in this case. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test.yml | 41 ++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index df811a620..747626557 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -14,13 +14,50 @@ jobs: - cmake - waf compiler: - - CC: gcc - CXX: g++ + - CC: gcc-10 + CXX: g++-10 + - CC: gcc-11 + CXX: g++-11 + - CC: gcc-12 + CXX: g++-12 + - CC: gcc-13 + CXX: g++-13 + - CC: gcc-14 + CXX: g++-14 - CC: clang CXX: clang++ csp_version: - 1 - 2 + exclude: + # Ubuntu 20.04 only has GCC 10 + - os: ubuntu-20.04 + compiler: + CC: gcc-11 + - os: ubuntu-20.04 + compiler: + CC: gcc-12 + - os: ubuntu-20.04 + compiler: + CC: gcc-13 + - os: ubuntu-20.04 + compiler: + CC: gcc-14 + # Ubuntu 22.04 only has GCC 10, 11, 12 + - os: ubuntu-22.04 + compiler: + CC: gcc-13 + - os: ubuntu-22.04 + compiler: + CC: gcc-14 + # Ubuntu 24.04 only has GCC 12, 13, 14 + - os: ubuntu-24.04 + compiler: + CC: gcc-10 + - os: ubuntu-24.04 + compiler: + CC: gcc-11 + runs-on: ${{ matrix.os }} steps: - name: Setup packages on Linux From 7371a612a77b010c62d180a98ebdbde6b27d2059 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 16:29:05 +0900 Subject: [PATCH 120/348] examples: zmqproxy: Use `size_t` for datalen Update datalen to use `size_t` since `zmq_msg_size()` returns `size_t` instead of `int`. Ensure strict type correctness. Signed-off-by: Yasushi SHOJI --- examples/zmqproxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index af665bf61..207a75587 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -58,7 +58,7 @@ static void * task_capture(void * ctx) { continue; } - int datalen = zmq_msg_size(&msg); + size_t datalen = zmq_msg_size(&msg); if (datalen < 5) { csp_print("ZMQ: Too short datalen: %u\n", datalen); while (zmq_msg_recv(&msg, subscriber, ZMQ_NOBLOCK) > 0) From bb9d76fe8c2b0dc7d12aa3f399be1cc4c1ecae11 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 16:31:50 +0900 Subject: [PATCH 121/348] examples: zmqproxy: Include csp_id.h instead of declaring functions Replace local declarations of csp_id_strip() and csp_id_setup_rx() with `#include ` to use the existing declarations from the header file. Signed-off-by: Yasushi SHOJI --- examples/zmqproxy.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index 207a75587..d596fa182 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -5,9 +5,7 @@ #include #include - -int csp_id_strip(csp_packet_t * packet); -int csp_id_setup_rx(csp_packet_t * packet); +#include int debug = 0; const char * sub_str = "tcp://0.0.0.0:6000"; From 6c85979a5ac7fc9900ca369421a1b2ac18232b09 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 16:43:46 +0900 Subject: [PATCH 122/348] interfaces: csp_if_zmqhub: Use `size_t` for datalen Update datalen to use `size_t` since `zmq_msg_size()` returns `size_t` instead of `int`. Ensure strict type correctness. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 9e01deeea..b9ffe25a9 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -76,7 +76,7 @@ void * csp_zmqhub_task(void * param) { continue; } - unsigned int datalen = zmq_msg_size(&msg); + size_t datalen = zmq_msg_size(&msg); if (datalen < HEADER_SIZE) { csp_print("ZMQ RX %s: Too short datalen: %u - expected min %u bytes\n", drv->iface.name, datalen, HEADER_SIZE); zmq_msg_close(&msg); From 1c13b4c756a8deb50644d84abbd5992111a88c11 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 16:53:41 +0900 Subject: [PATCH 123/348] interfaces: csp_if_zmqhub: Remove `const` qualifier from `rx_data` The `zmq_msg_data()` function does not return a `const` value. While using `const` is generally good practice for unmodified data, this change is a preparatory step for an upcoming commit that modifies the `rx_data`. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index b9ffe25a9..5f8ae63a9 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -92,7 +92,7 @@ void * csp_zmqhub_task(void * param) { } // Copy the data from zmq to csp - const uint8_t * rx_data = zmq_msg_data(&msg); + uint8_t * rx_data = zmq_msg_data(&msg); csp_id_setup_rx(packet); From bdc7f574e725444fefd710ee84bd10a20d6e4e54 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 16:57:28 +0900 Subject: [PATCH 124/348] csp_id: Add CSPv1 fixup versions of prepend and strip functions Add `csp_id_prepend_fixup_cspv1()` and `csp_id_strip_fixup_cspv1()` to address a CSPv1 protocol issue where the ZMQ interface incorrectly uses host byte order. These functions ensure compatibility with such misbehaving interfaces. Modify `csp_id1_prepend()` and `csp_id1_strip()` to accept an optional `network_byte_order` flag for explicit byte-order control. These remain internal APIs, with the new fixup functions being their only current users. Enhance compatibility by supporting interfaces that incorrectly use host byte order in the CSPv1 protocol. Signed-off-by: Yasushi SHOJI --- include/csp/csp_id.h | 3 +++ src/csp_id.c | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/csp/csp_id.h b/include/csp/csp_id.h index 19c88b664..41ec6687a 100644 --- a/include/csp/csp_id.h +++ b/include/csp/csp_id.h @@ -15,6 +15,9 @@ unsigned int csp_id_get_max_port(void); int csp_id_is_broadcast(uint16_t addr, csp_iface_t * iface); +void csp_id_prepend_fixup_cspv1(csp_packet_t * packet); +int csp_id_strip_fixup_cspv1(csp_packet_t * packet); + #ifdef __cplusplus } #endif diff --git a/src/csp_id.c b/src/csp_id.c index e6957b701..ec1ea7154 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -37,7 +37,7 @@ #define CSP_ID1_HEADER_SIZE 4 -static void csp_id1_prepend(csp_packet_t * packet) { +static void csp_id1_prepend(csp_packet_t * packet, bool network_byte_order) { /* Pack into 32-bit using host endian */ uint32_t id1 = (((uint32_t)(packet->id.pri) << CSP_ID1_PRIO_OFFSET) | @@ -48,7 +48,11 @@ static void csp_id1_prepend(csp_packet_t * packet) { ((uint32_t)(packet->id.flags) << CSP_ID1_FLAGS_OFFSET)); /* Convert to big / network endian */ - id1 = htobe32(id1); + if (network_byte_order) { + id1 = htobe32(id1); + } else { + id1 = htole32(id1); + } packet->frame_begin = packet->data - CSP_ID1_HEADER_SIZE; packet->frame_length = packet->length + CSP_ID1_HEADER_SIZE; @@ -56,7 +60,7 @@ static void csp_id1_prepend(csp_packet_t * packet) { memcpy(packet->frame_begin, &id1, CSP_ID1_HEADER_SIZE); } -static int csp_id1_strip(csp_packet_t * packet) { +static int csp_id1_strip(csp_packet_t * packet, bool network_byte_order) { if (packet->frame_length < CSP_ID1_HEADER_SIZE) { return -1; @@ -68,7 +72,11 @@ static int csp_id1_strip(csp_packet_t * packet) { packet->length = packet->frame_length - CSP_ID1_HEADER_SIZE; /* Convert to host order */ - id1 = be32toh(id1); + if (network_byte_order) { + id1 = be32toh(id1); + } else { + id1 = le32toh(id1); + } /* Parse header: * Now in easy to work with in 32 bit register */ @@ -182,7 +190,7 @@ void csp_id_prepend(csp_packet_t * packet) { if (csp_conf.version == 2) { csp_id2_prepend(packet); } else { - csp_id1_prepend(packet); + csp_id1_prepend(packet, true); } } @@ -190,7 +198,24 @@ int csp_id_strip(csp_packet_t * packet) { if (csp_conf.version == 2) { return csp_id2_strip(packet); } else { - return csp_id1_strip(packet); + return csp_id1_strip(packet, true); + } +} + +void csp_id_prepend_fixup_cspv1(csp_packet_t * packet) { + if (csp_conf.version == 2) { + csp_id2_prepend(packet); + } else { + csp_id1_prepend(packet, false); + + } +} + +int csp_id_strip_fixup_cspv1(csp_packet_t * packet) { + if (csp_conf.version == 2) { + return csp_id2_strip(packet); + } else { + return csp_id1_strip(packet, false); } } From b4cdceb112eb0b2920680646552316e35a024129 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 5 Jan 2025 17:44:17 +0900 Subject: [PATCH 125/348] csp_id: Use second parameter to indicate whether fixup is needed Update `csp_id1_prepend()` and `csp_id1_strip()` to clearly indicate that the second parameter (`cspv1_fixup`) is used solely for handling the fixup byte order quirk. Simplify the endian conversion logic by always converting to big-endian unconditionally: id1 = htobe32(id1_raw); Subsequently, apply the fixup only if needed. This makes the intention of converting to/from big-endian clearer. No functionality should be changed. Signed-off-by: Yasushi SHOJI --- src/csp_id.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/csp_id.c b/src/csp_id.c index ec1ea7154..ae4c5fce9 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -37,21 +37,21 @@ #define CSP_ID1_HEADER_SIZE 4 -static void csp_id1_prepend(csp_packet_t * packet, bool network_byte_order) { +static void csp_id1_prepend(csp_packet_t * packet, bool cspv1_fixup) { /* Pack into 32-bit using host endian */ - uint32_t id1 = (((uint32_t)(packet->id.pri) << CSP_ID1_PRIO_OFFSET) | - ((uint32_t)(packet->id.dst) << CSP_ID1_DST_OFFSET) | - ((uint32_t)(packet->id.src) << CSP_ID1_SRC_OFFSET) | - ((uint32_t)(packet->id.dport) << CSP_ID1_DPORT_OFFSET) | - ((uint32_t)(packet->id.sport) << CSP_ID1_SPORT_OFFSET) | - ((uint32_t)(packet->id.flags) << CSP_ID1_FLAGS_OFFSET)); + uint32_t id1_raw = (((uint32_t)(packet->id.pri) << CSP_ID1_PRIO_OFFSET) | + ((uint32_t)(packet->id.dst) << CSP_ID1_DST_OFFSET) | + ((uint32_t)(packet->id.src) << CSP_ID1_SRC_OFFSET) | + ((uint32_t)(packet->id.dport) << CSP_ID1_DPORT_OFFSET) | + ((uint32_t)(packet->id.sport) << CSP_ID1_SPORT_OFFSET) | + ((uint32_t)(packet->id.flags) << CSP_ID1_FLAGS_OFFSET)); /* Convert to big / network endian */ - if (network_byte_order) { - id1 = htobe32(id1); - } else { - id1 = htole32(id1); + uint32_t id1 = htobe32(id1_raw); + + if (cspv1_fixup) { + id1 = htole32(id1_raw); } packet->frame_begin = packet->data - CSP_ID1_HEADER_SIZE; @@ -60,22 +60,22 @@ static void csp_id1_prepend(csp_packet_t * packet, bool network_byte_order) { memcpy(packet->frame_begin, &id1, CSP_ID1_HEADER_SIZE); } -static int csp_id1_strip(csp_packet_t * packet, bool network_byte_order) { +static int csp_id1_strip(csp_packet_t * packet, bool cspv1_fixup) { if (packet->frame_length < CSP_ID1_HEADER_SIZE) { return -1; } /* Get 32 bit in network byte order */ - uint32_t id1 = 0; - memcpy(&id1, packet->frame_begin, CSP_ID1_HEADER_SIZE); + uint32_t id1_raw = 0; + memcpy(&id1_raw, packet->frame_begin, CSP_ID1_HEADER_SIZE); packet->length = packet->frame_length - CSP_ID1_HEADER_SIZE; /* Convert to host order */ - if (network_byte_order) { - id1 = be32toh(id1); - } else { - id1 = le32toh(id1); + uint32_t id1 = be32toh(id1_raw); + + if (cspv1_fixup) { + id1 = le32toh(id1_raw); } /* Parse header: @@ -190,7 +190,7 @@ void csp_id_prepend(csp_packet_t * packet) { if (csp_conf.version == 2) { csp_id2_prepend(packet); } else { - csp_id1_prepend(packet, true); + csp_id1_prepend(packet, false); } } @@ -198,7 +198,7 @@ int csp_id_strip(csp_packet_t * packet) { if (csp_conf.version == 2) { return csp_id2_strip(packet); } else { - return csp_id1_strip(packet, true); + return csp_id1_strip(packet, false); } } @@ -206,7 +206,7 @@ void csp_id_prepend_fixup_cspv1(csp_packet_t * packet) { if (csp_conf.version == 2) { csp_id2_prepend(packet); } else { - csp_id1_prepend(packet, false); + csp_id1_prepend(packet, true); } } @@ -215,7 +215,7 @@ int csp_id_strip_fixup_cspv1(csp_packet_t * packet) { if (csp_conf.version == 2) { return csp_id2_strip(packet); } else { - return csp_id1_strip(packet, false); + return csp_id1_strip(packet, true); } } From b7f4f01019297f54ab59cac4d38c29ff58dd2dfb Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 19:58:22 +0900 Subject: [PATCH 126/348] csp_id: Make fixup functions a compile-time option csp_id_prepend_fixup_cspv1 and csp_id_strip_fixup_cspv1 add unnecessary code for configurations that do not require them. Enable these functions only when CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN is non-zero. This change reduces code size slightly compared to previous commits: text data bss dec hex filename 80228 2952 15016 98196 17f94 builddir/libcsp.so 79759 2936 15016 97711 17daf builddir/libcsp.so Signed-off-by: Yasushi SHOJI --- include/csp/csp_id.h | 9 +++++++++ src/csp_id.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/include/csp/csp_id.h b/include/csp/csp_id.h index 41ec6687a..fd6a60a7d 100644 --- a/include/csp/csp_id.h +++ b/include/csp/csp_id.h @@ -15,8 +15,17 @@ unsigned int csp_id_get_max_port(void); int csp_id_is_broadcast(uint16_t addr, csp_iface_t * iface); +#if (CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN) void csp_id_prepend_fixup_cspv1(csp_packet_t * packet); int csp_id_strip_fixup_cspv1(csp_packet_t * packet); +#else +static inline void csp_id_prepend_fixup_cspv1(csp_packet_t * packet) { + csp_id_prepend(packet); +} +static inline int csp_id_strip_fixup_cspv1(csp_packet_t * packet) { + return csp_id_strip(packet); +} +#endif #ifdef __cplusplus } diff --git a/src/csp_id.c b/src/csp_id.c index ae4c5fce9..8ff0cdd9b 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -202,6 +202,8 @@ int csp_id_strip(csp_packet_t * packet) { } } +#if (CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN) + void csp_id_prepend_fixup_cspv1(csp_packet_t * packet) { if (csp_conf.version == 2) { csp_id2_prepend(packet); @@ -219,6 +221,8 @@ int csp_id_strip_fixup_cspv1(csp_packet_t * packet) { } } +#endif + int csp_id_setup_rx(csp_packet_t * packet) { if (csp_conf.version == 2) { csp_id2_setup_rx(packet); From eaf40baa0a92e5a77aff0f9cb738e8ab2829fcde Mon Sep 17 00:00:00 2001 From: n-log-gos Date: Fri, 22 Nov 2024 10:47:27 -0300 Subject: [PATCH 127/348] interfaces: csp_if_zmqhub: Restore destination byte for CSPv1 Reintroduce the destination address as the first byte for ZMQ interface with CSPv1. The destination address byte was removed in commit `b3217c0f48d47192f2f` for CSPv2. This commit re-adds it when using CSPv1. Code changes include helper functions to handle destination address addition/removal for CSPv1. Signed-off-by: n-log-gos Signed-off-by: Yasushi SHOJI --- examples/zmqproxy.c | 5 ++- include/csp/interfaces/csp_if_zmqhub.h | 3 ++ src/interfaces/csp_if_zmqhub.c | 45 +++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index d596fa182..831d28811 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -6,6 +6,7 @@ #include #include +#include int debug = 0; const char * sub_str = "tcp://0.0.0.0:6000"; @@ -64,9 +65,11 @@ static void * task_capture(void * ctx) { continue; } + uint8_t * rx_data = csp_zmqhub_fixup_cspv1_del_dest_addr(zmq_msg_data(&msg), &datalen); + /* Copy to packet */ csp_id_setup_rx(packet); - memcpy(packet->frame_begin, zmq_msg_data(&msg), datalen); + memcpy(packet->frame_begin, rx_data, datalen); packet->frame_length = datalen; /* Parse header */ diff --git a/include/csp/interfaces/csp_if_zmqhub.h b/include/csp/interfaces/csp_if_zmqhub.h index 17d972442..829e58e59 100644 --- a/include/csp/interfaces/csp_if_zmqhub.h +++ b/include/csp/interfaces/csp_if_zmqhub.h @@ -123,6 +123,9 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport); +void csp_zmqhub_fixup_cspv1_add_dest_addr(csp_packet_t * packet); +void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen); + #ifdef __cplusplus } #endif diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 5f8ae63a9..d7e0b9eae 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -14,6 +14,11 @@ #include "../csp_macro.h" +/** + * ZMQ destination size (for libcsp1 backwards compatibility) + */ +#define ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1 1 + /* ZMQ driver & interface */ typedef struct { pthread_t rx_thread; @@ -30,6 +35,42 @@ typedef struct { /* Linux is fast, so we keep it simple by having a single lock */ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +/** + * Add one byte of the dest or "via" address to the beginning of the + * ZMQ message for the CSPv1 protocol. + * + * This extra byte is not used with the CSPv2 protocol. + * + * @param packet pointer to packet buffer + */ +void csp_zmqhub_fixup_cspv1_add_dest_addr(csp_packet_t * packet) { + + if (csp_conf.version == 1) { + packet->frame_begin -= ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1; + *(packet->frame_begin) = (uint8_t)packet->id.dst; + packet->frame_length += ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1; + } +} + +/** + * Skip the extra byte of the dest or "via" address at the beginning + * of the ZMQ message for the CSPv1 protocol. + * + * This extra byte is not used with the CSPv2 protocol. + * + * @param rx_data pointer to ZMQ message content + * @param datalen pointer to ZMQ message content size + */ +void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen) { + + if (csp_conf.version == 1) { + rx_data += ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1; + *datalen -= ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1; + } + + return rx_data; +} + /** * Interface transmit function * @param packet Packet to transmit @@ -40,6 +81,7 @@ int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t zmq_driver_t * drv = iface->driver_data; csp_id_prepend(packet); + csp_zmqhub_fixup_cspv1_add_dest_addr(packet); /** * While a ZMQ context is thread safe, sockets are NOT threadsafe, so by sharing drv->publisher, we @@ -61,7 +103,7 @@ void * csp_zmqhub_task(void * param) { zmq_driver_t * drv = param; csp_packet_t * packet; - const uint32_t HEADER_SIZE = (csp_conf.version == 2) ? 6 : 4; + const uint32_t HEADER_SIZE = (csp_conf.version == 2) ? 6 : 4 + ZMQ_DEST_ADDR_SIZE_FIXUP_CSPV1; while (1) { int __maybe_unused ret; @@ -93,6 +135,7 @@ void * csp_zmqhub_task(void * param) { // Copy the data from zmq to csp uint8_t * rx_data = zmq_msg_data(&msg); + rx_data = csp_zmqhub_fixup_cspv1_del_dest_addr(rx_data, &datalen); csp_id_setup_rx(packet); From c8158c4c5ced2a570b36cfa1a5e46188d10f4b73 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 20:19:21 +0900 Subject: [PATCH 128/348] interfaces: csp_if_zmqhub: Fix CSPv1 endianness issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address a CSPv1 protocol compatibility issue in the ZMQ interface. In libcsp v1, the ZMQ interface incorrectly used host byte-order for the CSP ID, leading to interoperability issues. Libcsp v2 corrected this by enforcing big-endian encoding for the CSP ID, regardless of the host system’s endianness. But, this mismatch prevents libcsp v2 ZMQ interfaces from communicating with nodes running libcsp v1 over CSPv1 protocol. These commit adds compatibility between libcsp v2 and nodes running libcsp v1 with CSPv1 protocol over ZMQ, ensuring consistent header handling across systems. Signed-off-by: Yasushi SHOJI --- src/interfaces/csp_if_zmqhub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index d7e0b9eae..5e6a56e44 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -80,7 +80,7 @@ int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t zmq_driver_t * drv = iface->driver_data; - csp_id_prepend(packet); + csp_id_prepend_fixup_cspv1(packet); csp_zmqhub_fixup_cspv1_add_dest_addr(packet); /** @@ -143,7 +143,7 @@ void * csp_zmqhub_task(void * param) { packet->frame_length = datalen; /* Parse the frame and strip the ID field */ - if (csp_id_strip(packet) != 0) { + if (csp_id_strip_fixup_cspv1(packet) != 0) { drv->iface.rx_error++; csp_buffer_free(packet); zmq_msg_close(&msg); From dddf64750d1965df715b288ed53ea39de2eaf585 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 20:37:30 +0900 Subject: [PATCH 129/348] examples: zmqproxy: Fix CSPv1 endianness issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address a CSPv1 protocol compatibility issue in zmqproxy. In libcsp v1, the ZMQ interface incorrectly used host byte-order for the CSP ID, leading to interoperability issues. Libcsp v2 corrected this by enforcing big-endian encoding for the CSP ID, regardless of the host system’s endianness. But, this mismatch prevents libcsp v2 ZMQ interfaces from communicating with nodes running libcsp v1 over CSPv1 protocol. This commit adds compatibility between libcsp v2 and nodes running libcsp v1 with CSPv1 protocol over ZMQ, ensuring consistent header handling across systems. Signed-off-by: Yasushi SHOJI --- examples/zmqproxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index 831d28811..7db966909 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -73,7 +73,7 @@ static void * task_capture(void * ctx) { packet->frame_length = datalen; /* Parse header */ - csp_id_strip(packet); + csp_id_strip_fixup_cspv1(packet); /* Print header data */ csp_print("Packet: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %" PRIu16 "\n", From 146c2904067f3258728d85c7eb9610dbf9273071 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 19:58:44 +0900 Subject: [PATCH 130/348] cmake: Add option to align ZMQ quirk with CSPv1 Introduce a new CMake option `CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN`. When enabled, this option applies a fixup to the ZMQ interface to match the libcsp v1 behavior, which incorrectly used host endianness instead of the expected big-endian format. This fixup ensures compatibility with systems using libcsp v1 while aligning with the behavior of libcsp v2. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 2 ++ csp_autoconfig.h.in | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98f1a125d..fe7e1f508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ option(CSP_BUFFER_ZERO_CLEAR "Zero out the packet buffer upon allocation" ON) option(CSP_ENABLE_PYTHON3_BINDINGS "Build Python3 binding" OFF) option(CSP_BUILD_SAMPLES "Build samples and examples by default" OFF) +option(CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN "Use little-endian CSP ID for ZMQ with CSPv1" ON) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") diff --git a/csp_autoconfig.h.in b/csp_autoconfig.h.in index 1edb5aeec..0c108c793 100644 --- a/csp_autoconfig.h.in +++ b/csp_autoconfig.h.in @@ -25,3 +25,5 @@ #cmakedefine01 CSP_HAVE_LIBSOCKETCAN #cmakedefine01 CSP_HAVE_LIBZMQ + +#cmakedefine01 CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN From e16c77b3ddb26681ac68d519c90623d9ce28e352 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 20:02:42 +0900 Subject: [PATCH 131/348] meson: Add option to align ZMQ quirk with CSPv1 Introduce a new Meson option `fixup_v1_zmq_little_endian`. When enabled, this option applies a fixup to the ZMQ interface to match the libcsp v1 behavior, which incorrectly used host endianness instead of the expected big-endian format. This fixup ensures compatibility with systems using libcsp v1 while aligning with the behavior of libcsp v2. Signed-off-by: Yasushi SHOJI --- meson.build | 2 ++ meson_options.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/meson.build b/meson.build index d0a0cd1b7..5e239be38 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,8 @@ conf.set10('CSP_PRINT_STDIO', get_option('print_stdio')) conf.set10('CSP_USE_RTABLE', get_option('use_rtable')) conf.set10('CSP_BUFFER_ZERO_CLEAR', get_option('buffer_zero_clear')) +conf.set10('CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN', get_option('fixup_v1_zmq_little_endian')) + csp_deps = [] csp_sources = [] diff --git a/meson_options.txt b/meson_options.txt index 582916481..6d328f638 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,3 +26,5 @@ option('buffer_size', type: 'integer', value: 256, description: 'Bytes in each p option('buffer_count', type: 'integer', value: 15, description: 'Number of total packet buffers') option('rdp_max_window', type: 'integer', value: 5, description: 'Max window size for RDP') option('rtable_size', type: 'integer', value: 10, description: 'Number of elements in routing table') + +option('fixup_v1_zmq_little_endian', type: 'boolean', value: false, description: 'Use little-endian CSP ID for ZMQ with CSPv1') From 6cd564ecd42e47afe93ee127fa4563518c72244d Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 14 Jan 2025 20:10:05 +0900 Subject: [PATCH 132/348] waf: Add option to align ZMQ quirk with CSPv1 Introduce a new Waf option `--enable-fixup-v1-zmq-little-endian`. When enabled, this option applies a fixup to the ZMQ interface to match the libcsp v1 behavior, which incorrectly used host endianness instead of the expected big-endian format. This fixup ensures compatibility with systems using libcsp v1 while aligning with the behavior of libcsp v2. Signed-off-by: Yasushi SHOJI --- wscript | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wscript b/wscript index 2715a02b2..3f2f91afe 100644 --- a/wscript +++ b/wscript @@ -51,6 +51,8 @@ def options(ctx): # OS gr.add_option('--with-os', metavar='OS', default='posix', help='Set operating system. Must be one of: ' + str(valid_os)) + # Fixup + gr.add_option('--fixup-v1-zmq-little-endian', action='store_true', help='Use little-endian CSP ID for ZMQ with CSPv1') def configure(ctx): # Validate options @@ -195,6 +197,7 @@ def configure(ctx): ctx.define('CSP_USE_RTABLE', ctx.options.enable_rtable) ctx.define('CSP_BUFFER_ZERO_CLEAR', ctx.options.disable_buffer_zero_clear) + ctx.define('CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN', ctx.options.fixup_v1_zmq_little_endian) ctx.write_config_header('include/csp/autoconfig.h') From 39a31a0a8c523e13ca128452e4f17fb2da1a0467 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 23 Jan 2025 15:42:01 +0900 Subject: [PATCH 133/348] examples: Remove check NULL for packet in csp_server_client Since csp_buffer_get_always() is used to get the csp packet, it should never return NULL but instead panic so we don't need to check if NULL is returned. Signed-off-by: Gaetan Perrot --- examples/csp_server_client.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 5f50790a5..61171fc36 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -109,11 +109,6 @@ void * client(void * param) { /* 2. Get packet buffer for message/data */ csp_packet_t * packet = csp_buffer_get_always(); - if (packet == NULL) { - /* Could not get buffer element */ - csp_print("Failed to get CSP buffer\n"); - return NULL; - } /* 3. Copy data to packet */ memcpy(packet->data, "Hello world ", 12); From e094a27c270e6622dc9f7a7b0748c32282ce3d43 Mon Sep 17 00:00:00 2001 From: Takumi Ando Date: Tue, 15 Oct 2024 15:16:52 +0900 Subject: [PATCH 134/348] include: csp: Remove union in csp_packet_t Both structs in csp_packet_t may be accessed in same layers. Therefore, they shouldn't be shared memory. The size of csp_packet_t has increased by 4 or 8 bytes. - 32bit: 300 bytes -> 304 bytes - 64bit: 320 bytes -> 328 bytes Signed-off-by: Takumi Ando --- include/csp/csp_types.h | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index c2b7e4c6d..9548ce019 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -117,25 +117,15 @@ typedef struct __packed { */ typedef struct csp_packet_s { - union { - - /* Only used on layer 3 (RDP) */ - struct { - uint32_t timestamp_tx; /*< Time the message was sent */ - struct csp_conn_s * conn; /*< Associated connection (this is used in RDP queue) */ - }; - - /* Only used on interface RX/TX (layer 2) */ - struct { - uint16_t rx_count; /*< Received bytes */ - uint16_t remain; /*< Remaining packets */ - uint32_t cfpid; /*< Connection CFP identification number */ - uint32_t last_used; /*< Timestamp in ms for last use of buffer */ - uint8_t * frame_begin; - uint16_t frame_length; - }; - - }; + uint32_t timestamp_tx; /*< Time the message was sent */ + struct csp_conn_s * conn; /*< Associated connection (this is used in RDP queue) */ + + uint16_t rx_count; /*< Received bytes */ + uint16_t remain; /*< Remaining packets */ + uint32_t cfpid; /*< Connection CFP identification number */ + uint32_t last_used; /*< Timestamp in ms for last use of buffer */ + uint8_t * frame_begin; + uint16_t frame_length; uint16_t length; /*< Data length */ csp_id_t id; /*< CSP id (unpacked version CPU readable) */ From a4af8a9af59a103b770ce364c9e74cb3fdcdec10 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 5 Feb 2025 21:11:20 +0900 Subject: [PATCH 135/348] csp_hooks: Fix EOF newlines Remove executive newlines at EOF. Signed-off-by: Gaetan Perrot --- src/arch/freertos/csp_hooks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arch/freertos/csp_hooks.c b/src/arch/freertos/csp_hooks.c index 9791c49b1..f576e62e8 100644 --- a/src/arch/freertos/csp_hooks.c +++ b/src/arch/freertos/csp_hooks.c @@ -14,4 +14,3 @@ __weak void csp_reboot_hook(void) { __weak void csp_shutdown_hook(void) { } - From 63e1b05933cccdad5571bc51031c2e8c43b3987b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 5 Feb 2025 21:11:44 +0900 Subject: [PATCH 136/348] csp_if_tun: Fix EOF newlines Remove executive newlines at EOF. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_tun.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index 6225af10e..838215363 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -118,4 +118,3 @@ void csp_if_tun_init(csp_iface_t * iface, csp_if_tun_conf_t * ifconf) { csp_iflist_add(iface); } - From 176bbc612d31bfcc29f2f46a593523428ab06f32 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 5 Mar 2025 17:49:23 +0900 Subject: [PATCH 137/348] Add .gitlint Add .gitlint to teach how to check git commit messages. This version does two things: - Ignore signed-off lines - Ignore long lines with a URL reference; ex) "[1]: https://..." These are not perfect. But can be a starter. --- .gitlint | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitlint diff --git a/.gitlint b/.gitlint new file mode 100644 index 000000000..87587e6f4 --- /dev/null +++ b/.gitlint @@ -0,0 +1,5 @@ +[general] +regex-style-search=true + +[ignore-body-lines] +regex=^(?:(?:Signed-off|Acked|Co-Authored|Reported|Tested)-by: |\[\d+\]: https:\/\/) From 8cbb70feea88527dc97e69a968f4f541cc3e73f8 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 28 Jan 2025 20:50:42 +0900 Subject: [PATCH 138/348] csp_conn: Fix typo Fix "leavve" to "leave". Signed-off-by: Gaetan Perrot --- src/csp_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_conn.c b/src/csp_conn.c index 50485953f..2d5d1e525 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -258,7 +258,7 @@ csp_conn_t * csp_connect(uint8_t prio, uint16_t dest, uint8_t dport, uint32_t ti * This means that for this outgoing connection, we accept the answer coming to whatever address * the outgoing interface has. CSP does not support "source address" on outgoing connections * so the outgoing source address will be automatically applied after outgoing routing - * selects which interface the packet will leavve from */ + * selects which interface the packet will leave from */ incoming_id.dst = 0; outgoing_id.src = 0; From 4bcb0ec11a7c9f49a9716ecec0f8ce9da6f16096 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 4 Sep 2024 12:09:16 +0900 Subject: [PATCH 139/348] csp_driver_can: Fix commentary typo in csp_driver_can_init Fix commentary typo, "Regsiter" in "Register". Signed-off-by: Gaetan Perrot --- contrib/drivers/can/csp_driver_can.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/drivers/can/csp_driver_can.c b/contrib/drivers/can/csp_driver_can.c index caaeb7fce..78a1770f9 100644 --- a/contrib/drivers/can/csp_driver_can.c +++ b/contrib/drivers/can/csp_driver_can.c @@ -226,7 +226,7 @@ csp_iface_t * csp_driver_can_init(int addr, int netmask, int id, can_mode_e mode mcan[id].interface.addr = addr; mcan[id].interface.netmask = netmask; - /* Regsiter interface */ + /* Register interface */ csp_can_add_interface(&mcan[id].interface); #if CAN_DEFER_TASK From fd48208c594fa5651792f451da6714aae3887182 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 3 Feb 2025 15:43:27 +0900 Subject: [PATCH 140/348] interfaces: csp_if_udp: Fix typo Fix commentary typo, "Regsiter" to "Register". Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 73832d935..b05a9c48a 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -132,7 +132,7 @@ void csp_if_udp_init(csp_iface_t * iface, csp_if_udp_conf_t * ifconf) { csp_print("csp_if_udp_init: pthread_attr_destroy failed: %s: %d\n", strerror(ret), ret); } - /* Regsiter interface */ + /* Register interface */ iface->name = "UDP", iface->nexthop = csp_if_udp_tx, csp_iflist_add(iface); From c5216facea48498c5f2257a4297324ec963c4a96 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 3 Feb 2025 15:43:49 +0900 Subject: [PATCH 141/348] interfaces: csp_if_tun: Fix typo Fix commentary typo, "Regsiter" to "Register". Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index 838215363..8ce4298fa 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -112,7 +112,7 @@ void csp_if_tun_init(csp_iface_t * iface, csp_if_tun_conf_t * ifconf) { iface->driver_data = ifconf; - /* Regsiter interface */ + /* Register interface */ iface->name = "TUN", iface->nexthop = csp_if_tun_tx, csp_iflist_add(iface); From 7d779ef621e8e036a174b6a7053b67fa64b21b7a Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Mon, 17 Feb 2025 11:41:16 +0100 Subject: [PATCH 142/348] Use struct initializer To avoid "unintialized value" Valgrind warning --- src/csp_conn.c | 2 +- src/drivers/can/can_socketcan.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/csp_conn.c b/src/csp_conn.c index 2d5d1e525..5ad935b10 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -252,7 +252,7 @@ csp_conn_t * csp_connect(uint8_t prio, uint16_t dest, uint8_t dport, uint32_t ti opts |= csp_conf.conn_dfl_so; /* Generate identifier */ - csp_id_t incoming_id, outgoing_id; + csp_id_t incoming_id = {0}, outgoing_id = {0}; /* Use 0 as incoming id (this disables the input filter on destination node) * This means that for this outgoing connection, we accept the answer coming to whatever address diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index b90233d92..ca674289f 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -46,8 +46,9 @@ static void * socketcan_rx_thread(void * arg) { fd_set input; FD_ZERO(&input); FD_SET(ctx->socket, &input); - struct timeval timeout; - timeout.tv_sec = 10; + struct timeval timeout = { + .tv_sec = 10, + }; int n = select(ctx->socket + 1, &input, NULL, NULL, &timeout); if (n == -1) { csp_print("CAN read error\n"); From a5cdf8f59a41d2d944a64ebe9f8ddb09a5e9bb6b Mon Sep 17 00:00:00 2001 From: Iliya Iliev Date: Fri, 7 Feb 2025 09:01:47 +0200 Subject: [PATCH 143/348] doc: conf.py: Add find_libclang method The find_libclang method searches for the currently installed libclang version in common system directories. It returns the full path to the .so or raises an exception in case the .so was not found. Signed-off-by: Iliya Iliev --- doc/conf.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 3b05aa2e6..3255d3e6b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,8 +10,33 @@ import pygit2 # -- Path setup -------------------------------------------------------------- -cl.Config.set_library_file('/usr/lib/llvm-14/lib/libclang-14.so') -cl.Config.set_library_path('/usr/lib/llvm-14/lib/libclang-14.so') +def find_libclang() -> str: + """ + Searches for the `libclang-xx.so` file in common system directories. Where + 'xx' is the current libclang version installed on the system. + + This function looks for the shared library file `libclang-xx.so` in typical + installation paths such as `/usr/lib`, `/usr/lib/llvm`, and `/usr/lib/x86_64-linux-gnu`. + + Returns: + str: The full path to the `libclang-xx.so` file if found. + + Raises: + FileNotFoundError: If the `libclang-xx.so` file cannot be located in the + predefined search paths. + """ + search_paths = [Path('/usr/lib'), Path('/usr/lib/llvm'), Path('/usr/lib/x86_64-linux-gnu')] + + for path in search_paths: + if path.exists() and path.is_dir(): + for file in path.rglob('libclang*.so'): # Recursively search for matching files + return str(file) # Return the first match as a string + + raise FileNotFoundError("libclang not found in the predefined search paths.") + +libclang_path = find_libclang() +cl.Config.set_library_file(libclang_path) +cl.Config.set_library_path(libclang_path) # -- Project information ----------------------------------------------------- project = 'Lib CSP' From 6fa9d43d125fbc67e205e13d521dee66624b0b09 Mon Sep 17 00:00:00 2001 From: Iliya Iliev Date: Fri, 7 Feb 2025 09:09:12 +0200 Subject: [PATCH 144/348] doc: Dockerfile: Use ubuntu-latest Change Docker image to ubuntu latest instead of using a fixed version (22.04). This commit also changes the Docker image to use the native libclang version of the latest ubuntu distribution and adopts the `--break-system-packages` pip flag. Signed-off-by: Iliya Iliev --- doc/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Dockerfile b/doc/Dockerfile index f474f4d38..8212ed101 100644 --- a/doc/Dockerfile +++ b/doc/Dockerfile @@ -1,11 +1,11 @@ -FROM ubuntu:22.04 +FROM ubuntu:latest ARG DEBIAN_FRONTEND=noninteractive MAINTAINER "Iliya Iliev" # Install dependencies RUN apt-get update && \ apt-get install --no-install-recommends --no-install-suggests -y sudo git python3-pip cmake \ - libclang-14-dev build-essential locales locales-all + libclang-dev build-essential locales locales-all ENV LC_ALL en_US.UTF-8 ENV LANG en_US.utf8 @@ -16,7 +16,7 @@ RUN mkdir /home/libcsp_sphinx_docs && \ cd /home/libcsp_sphinx_docs && \ git clone https://github.com/libcsp/libcsp.git --branch develop && \ cd libcsp && \ - pip3 install -r doc/requirements.txt && \ + pip3 install -r doc/requirements.txt --break-system-packages && \ cmake -B build-docs -S doc && \ cmake --build build-docs From 0d2b7436d2f7087b2d797f06447afb140935d1d7 Mon Sep 17 00:00:00 2001 From: Iliya Iliev Date: Fri, 7 Feb 2025 09:40:43 +0200 Subject: [PATCH 145/348] workflows: develop-build-sphinx-docs: Change version to ubuntu-latest Change image version to ubuntu latest instead of using a fixed version (22.04). Signed-off-by: Iliya Iliev --- .github/workflows/develop-build-sphinx-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index 0dbec21af..5cf48da51 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -8,7 +8,7 @@ on: jobs: build-docs: if: github.repository_owner == 'libcsp' - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@v4 From 6b6e2dd2870bd1f8ab4516123f1fa255d6c1b632 Mon Sep 17 00:00:00 2001 From: Clemens Horch Date: Wed, 12 Feb 2025 11:21:29 +0100 Subject: [PATCH 146/348] reset rtable_inptr in csp_rtable_free() to avoid segfaults If csp_rtable_free() is called, the rtable_inptr is not reset to 0. If later the rtable is used again, this can lead to a null pointer dereference in other parts of the code. --- src/csp_rtable_cidr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/csp_rtable_cidr.c b/src/csp_rtable_cidr.c index 8dc9ed42b..a51323d42 100644 --- a/src/csp_rtable_cidr.c +++ b/src/csp_rtable_cidr.c @@ -97,6 +97,7 @@ int csp_rtable_set_internal(uint16_t address, uint16_t netmask, csp_iface_t * if void csp_rtable_free(void) { memset(rtable, 0, sizeof(rtable)); + rtable_inptr = 0; } void csp_rtable_clear(void) { From acefc85276cca1e2b43d7e3872d0ee495e786ccf Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Thu, 13 Feb 2025 16:36:10 +0200 Subject: [PATCH 147/348] Zephyr: Use C linkage for C++ builds Zephyr CAN driver fails to build for Zephyr projects using C++. This commit adds C linkage in the Zephyr CAN driver in case C++ is used. Signed-off-by: Manolis Surligas --- include/csp/drivers/can_zephyr.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/csp/drivers/can_zephyr.h b/include/csp/drivers/can_zephyr.h index 8169fd30e..d00f71f05 100644 --- a/include/csp/drivers/can_zephyr.h +++ b/include/csp/drivers/can_zephyr.h @@ -13,6 +13,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Open CAN and add CSP interface. * @@ -47,3 +51,7 @@ int csp_can_set_rx_filter(csp_iface_t * iface, uint16_t filter_addr, uint16_t fi * @return #CSP_ERR_NONE on success, otherwise an error code. */ int csp_can_stop(csp_iface_t * iface); + +#ifdef __cplusplus +} +#endif From e6555c5677c8e03e276eec5fcaadf49ae6abda0a Mon Sep 17 00:00:00 2001 From: david gauchard Date: Thu, 9 Jan 2025 07:09:18 -0800 Subject: [PATCH 148/348] experimental cygwin port These changes need to be applied for libcsp to be compiled on fresh cygwin installation on windows 11. Ethernet packet forge API is not available with cygwin so it has been disabled with a warning at configuration and compilation time. If this API is needed under cygwin, a similar and portable API is supposed to be provided by libpcap (check also https://pcapplusplus.github.io). This port has been proven useful for an internal project and may benefit to others. The only build system which has been updated for cygwin is CMake. Limitation to CMake, and Ethernet removal is the justification for the "experimental" mention in the summary. --- CMakeLists.txt | 9 +++++++-- src/arch/posix/csp_system.c | 16 ++++++++++++++++ src/csp_macro.h | 4 ++++ src/drivers/eth/eth_linux.c | 10 +++++++++- src/drivers/usart/usart_linux.c | 2 ++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe7e1f508..06abab702 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,9 @@ option(CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN "Use little-endian CSP ID for ZMQ with CSP if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) +elseif(CMAKE_SYSTEM_NAME STREQUAL "CYGWIN") + set(CSP_POSIX 1) + message(WARNING "CYGWIN detected: experimental port and ethernet not implemented") elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic") string(TOLOWER "${CSP_SYSTEM_NAME}" csp_system_name) if(csp_system_name STREQUAL "freertos") @@ -123,8 +126,10 @@ endif() add_subdirectory(src) add_subdirectory(unittests) -add_subdirectory(examples) -add_subdirectory(samples) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "CYGWIN") + add_subdirectory(examples) + add_subdirectory(samples) +endif() if(${CSP_ENABLE_PYTHON3_BINDINGS}) find_package(Python3 COMPONENTS Development.Module) diff --git a/src/arch/posix/csp_system.c b/src/arch/posix/csp_system.c index 78c830aac..7ac114481 100644 --- a/src/arch/posix/csp_system.c +++ b/src/arch/posix/csp_system.c @@ -2,8 +2,12 @@ #include #include +#ifdef __CYGWIN__ +#include +#else #include #include +#endif uint32_t csp_memfree_hook(void) { uint32_t total = 0; @@ -18,11 +22,23 @@ unsigned int csp_ps_hook(csp_packet_t * packet) { } void csp_reboot_hook(void) { +#ifdef __CYGWIN__ + csp_print("HALTED - Please reboot\n"); + while (true) + sleep(1); +#else sync(); reboot(LINUX_REBOOT_CMD_RESTART); +#endif } void csp_shutdown_hook(void) { +#ifdef __CYGWIN__ + csp_print("HALTED - Please power off\n"); + while (true) + sleep(1); +#else sync(); reboot(LINUX_REBOOT_CMD_HALT); +#endif } diff --git a/src/csp_macro.h b/src/csp_macro.h index 338c5d6d2..e98ada3da 100644 --- a/src/csp_macro.h +++ b/src/csp_macro.h @@ -9,7 +9,11 @@ #define __packed __attribute__((__packed__)) #define __maybe_unused __attribute__((__unused__)) #define __unused __attribute__((__unused__)) +#ifdef __CYGWIN__ +#define __weak +#else #define __weak __attribute__((__weak__)) +#endif #define CONTAINER_OF(ptr, type, member) \ ((type *)(void *)((char *)(ptr) - offsetof(type, member))) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 56b4c0146..7c1809782 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -1,3 +1,8 @@ + +#ifdef __CYGWIN__ +#warning CYGWIN: ethernet not implemented - libpcap can be used if needed +#else // !__CYGWIN__ + #include #include @@ -181,4 +186,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int *return_iface = &ctx->ifdata.iface; } - return CSP_ERR_NONE;} + return CSP_ERR_NONE; +} + +#endif // !__CYGWIN__ diff --git a/src/drivers/usart/usart_linux.c b/src/drivers/usart/usart_linux.c index 48aa3d2da..3b8aa6442 100644 --- a/src/drivers/usart/usart_linux.c +++ b/src/drivers/usart/usart_linux.c @@ -123,12 +123,14 @@ int csp_usart_open(const csp_usart_conf_t * conf, csp_usart_callback_t rx_callba case 3000000: brate = B3000000; break; +#ifndef __CYGWIN__ case 3500000: brate = B3500000; break; case 4000000: brate = B4000000; break; +#endif default: csp_print("%s: Unsupported baudrate: %u\n", __func__, conf->baudrate); return CSP_ERR_INVAL; From 352dd73f1914b4c6fed38798f23abe1fe779e665 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 18 Apr 2025 17:07:41 +0900 Subject: [PATCH 149/348] interfaces: kiss: Prevent possible NULL dereference Add defensive checks in csp_kiss_rx() to avoid potential crashes due to NULL pointer dereference when rx_packet is unexpectedly NULL. Both KISS_MODE_STARTED and KISS_MODE_ESCAPED now verify that rx_packet is valid before writing to it. In case of invalid state, rx_error or drop counters are incremented accordingly to aid debugging. These changes improve the robustness of the KISS interface when dealing with malformed or corrupted input streams. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_kiss.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index e529a4cc0..376d23eab 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -120,6 +120,13 @@ void csp_kiss_rx(csp_iface_t * iface, const uint8_t * buf, size_t len, void * px break; } + /* Should not append in this mode, but guard against possible NULL dereference */ + if (ifdata->rx_packet == NULL) { + iface->rx_error++; + ifdata->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + /* End Char */ if (inputbyte == FEND) { @@ -164,6 +171,13 @@ void csp_kiss_rx(csp_iface_t * iface, const uint8_t * buf, size_t len, void * px case KISS_MODE_ESCAPED: + /* Should not append in this mode, but guard against possible NULL dereference */ + if (ifdata->rx_packet == NULL) { + iface->rx_error++; + ifdata->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + /* Escaped escape char */ if (inputbyte == TFESC) ifdata->rx_packet->frame_begin[ifdata->rx_length++] = FESC; From a0cfa71adbe12612513490ce42df60baa0b15f7a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 8 Apr 2025 22:27:20 +0900 Subject: [PATCH 150/348] github: workflows: Remove Ubuntu 20.04 from workflows The ubuntu-20.04 runner is scheduled for deprecation starting 2025-02-01 and will be fully unsupported by 2025-04-15. Workflows have been updated to remove 20.04 support to avoid CI breakage in the future and to align with GitHub Actions runner updates. Signed-off-by: Gaetan Perrot --- .github/workflows/build-test-python.yml | 1 - .github/workflows/build-test.yml | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 62ed362c2..fc806d64b 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -13,7 +13,6 @@ jobs: os: - ubuntu-24.04 - ubuntu-22.04 - - ubuntu-20.04 buildsystem: - meson - cmake diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 747626557..a9719410a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -8,7 +8,6 @@ jobs: os: - ubuntu-24.04 - ubuntu-22.04 - - ubuntu-20.04 buildsystem: - meson - cmake @@ -30,19 +29,6 @@ jobs: - 1 - 2 exclude: - # Ubuntu 20.04 only has GCC 10 - - os: ubuntu-20.04 - compiler: - CC: gcc-11 - - os: ubuntu-20.04 - compiler: - CC: gcc-12 - - os: ubuntu-20.04 - compiler: - CC: gcc-13 - - os: ubuntu-20.04 - compiler: - CC: gcc-14 # Ubuntu 22.04 only has GCC 10, 11, 12 - os: ubuntu-22.04 compiler: From be6fd9ed5150ee3a64c9a10d3e74042f9c1a311b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 26 Mar 2025 11:29:26 +0900 Subject: [PATCH 151/348] buffer: Ensure buffer allocation respects reserve limit Fix buffer allocation check to correctly respect reserve limit. csp_buffer_get() states that it reserves 2 buffers, but previously it was actually reserving only one. Fix this by adjusting the condition to properly enforce the requested reserve. Signed-off-by: Gaetan Perrot --- src/csp_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 66a1ac4a6..431f9ea54 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -61,8 +61,8 @@ static csp_packet_t * csp_buffer_get_actual(int reserve, int isr) { } else { remain = csp_queue_size(csp_buffers); } - /* Respect if remaining is lower than the reserve requested */ - if (remain < reserve) { + /* Respect the requested reserve */ + if (remain <= reserve) { return NULL; } From 5883840bef0d26b5ce3d57fd85ef9b1dd2a6fa9b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sat, 24 May 2025 14:56:27 +0900 Subject: [PATCH 152/348] buffer: Replace hardcoded reserve value by a define value Previously, the reserve value of 2 was hardcoded in buffer-related functions. This commit replaces the magic number with a named constant, CSP_BUFFER_RESERVE, making the code clearer and easier to maintain. A documentation comment has been added to explain the rationale behind this reservation: the last two buffers are kept for operations that can tolerate allocation failure, such as clients with error handling or services that may timeout gracefully under memory pressure. Signed-off-by: Gaetan Perrot --- include/csp/csp_buffer.h | 9 +++++++++ src/csp_buffer.c | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index 62b2a18df..4420af326 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -11,6 +11,15 @@ extern "C" { #endif +/** + * Number of buffers reserved by CSP for fault-tolerant operations. + * + * These reserved buffers are used for operations that can tolerate allocation failure, + * such as client requests with proper error handling, or services that may timeout + * safely when memory is low. + */ +#define CSP_BUFFER_RESERVE 2 + /** * Get free buffer from task context. * diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 431f9ea54..323262a27 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -211,9 +211,9 @@ csp_packet_t * csp_buffer_get_always_isr(void) { * sparse. */ csp_packet_t * csp_buffer_get(size_t unused) { - return csp_buffer_get_actual(2, 0); + return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 0); } csp_packet_t * csp_buffer_get_isr(size_t unused) { - return csp_buffer_get_actual(2, 1); + return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 1); } From 56b2e83533b62ca5b4fc0048373df4bb617ed418 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:06:57 +0900 Subject: [PATCH 153/348] drivers: eth_linux: Use C linkage for C++ builds This commit adds C linkage in the linux ETH driver in case C++ is used. Signed-off-by: Gaetan Perrot --- include/csp/drivers/eth_linux.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/csp/drivers/eth_linux.h b/include/csp/drivers/eth_linux.h index 7e08f2ef1..6b71705ae 100644 --- a/include/csp/drivers/eth_linux.h +++ b/include/csp/drivers/eth_linux.h @@ -11,6 +11,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Open RAW socket and add CSP interface. * @@ -41,3 +45,7 @@ int csp_eth_tx_frame(csp_iface_t * iface, csp_eth_header_t * eth_frame); * @return NULL */ void * csp_eth_rx_loop(void * param); + +#ifdef __cplusplus +} +#endif From 7dfe8b01b56479037371934ecb5cb9fd439cc2c0 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:07:36 +0900 Subject: [PATCH 154/348] interfaces: csp_if_eth: Use C linkage for C++ builds This commit adds C linkage in the ETH interface in case C++ is used. Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_eth.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/csp/interfaces/csp_if_eth.h b/include/csp/interfaces/csp_if_eth.h index ad9577213..08eb08a8a 100644 --- a/include/csp/interfaces/csp_if_eth.h +++ b/include/csp/interfaces/csp_if_eth.h @@ -43,6 +43,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* 0x88B5 : IEEE Std 802 - Local Experimental Ethertype (RFC 5342) */ #define CSP_ETH_TYPE_CSP 0x88B5 @@ -146,3 +150,7 @@ int csp_eth_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int fro * @return #CSP_ERR_NONE on success, otherwise an error code. */ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t received_len, int * task_woken); + +#ifdef __cplusplus +} +#endif From e006b05ad69f4a91784f72e391f024da041eca60 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:08:12 +0900 Subject: [PATCH 155/348] interfaces: csp_eth_pbuf: Use C linkage for C++ builds This commit adds C linkage in the ETH interface pbuf in case C++ is used. Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_eth_pbuf.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/csp/interfaces/csp_if_eth_pbuf.h b/include/csp/interfaces/csp_if_eth_pbuf.h index 4290ed314..dc3104fb3 100644 --- a/include/csp/interfaces/csp_if_eth_pbuf.h +++ b/include/csp/interfaces/csp_if_eth_pbuf.h @@ -35,6 +35,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint16_t rx_count; /* Received bytes */ uint16_t remain; /* Remaining packets */ @@ -46,3 +50,7 @@ void csp_eth_pbuf_free(csp_eth_interface_data_t * ifdata, csp_packet_t * buffer, csp_packet_t * csp_eth_pbuf_new(csp_eth_interface_data_t * ifdata, uint32_t id, uint32_t now, int * task_woken); csp_packet_t * csp_eth_pbuf_find(csp_eth_interface_data_t * ifdata, uint32_t id, int * task_woken); void csp_eth_pbuf_cleanup(csp_eth_interface_data_t * ifdata, uint32_t now, int * task_woken); + +#ifdef __cplusplus +} +#endif From 18191d291597a1cb36c2c678ec381218c1c50140 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 3 Apr 2025 17:40:22 +0900 Subject: [PATCH 156/348] github: workflow: build-test-zephyr: Add Python 3.13 Add Python 3.13 for the Zephyr build test. Signed-off-by: Gaetan Perrot --- .github/workflows/build-test-zephyr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 7466c0aa4..8be29ce5b 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -19,6 +19,7 @@ jobs: - '3.10' - '3.11' - '3.12' + - '3.13' steps: - name: Setup Python ${{ matrix.python-version }} From f69821f6e3a9421428034adaf7ed434e5f21b36c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 21 Apr 2025 23:56:40 +0900 Subject: [PATCH 157/348] python: bindings: Add missing CSP_ERR_NOSYS constant The error constant CSP_ERR_NOSYS (-38) was defined in csp_error.h but was not exposed in the Python bindings. This commit adds the missing constant using PyModule_AddIntConstant, making it available to Python users of the module. Signed-off-by: Gaetan Perrot --- src/bindings/python/pycsp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bindings/python/pycsp.c b/src/bindings/python/pycsp.c index efff44eb0..d5b49616f 100644 --- a/src/bindings/python/pycsp.c +++ b/src/bindings/python/pycsp.c @@ -1088,6 +1088,7 @@ PyMODINIT_FUNC PyInit_libcsp_py3(void) { PyModule_AddIntConstant(m, "CSP_ERR_TX", CSP_ERR_TX); PyModule_AddIntConstant(m, "CSP_ERR_DRIVER", CSP_ERR_DRIVER); PyModule_AddIntConstant(m, "CSP_ERR_AGAIN", CSP_ERR_AGAIN); + PyModule_AddIntConstant(m, "CSP_ERR_NOSYS", CSP_ERR_NOSYS); PyModule_AddIntConstant(m, "CSP_ERR_HMAC", CSP_ERR_HMAC); PyModule_AddIntConstant(m, "CSP_ERR_CRC32", CSP_ERR_CRC32); PyModule_AddIntConstant(m, "CSP_ERR_SFP", CSP_ERR_SFP); From b2f81f9b88c2eff392db71e23c86d4073fecd356 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 12:30:30 +0900 Subject: [PATCH 158/348] doc: Improve debug API documentation Grouped error macro definitions under clear sections: * General error codes * CAN-specific error codes * ETH-specific error codes Added section for print macros for debug output control. Add documentation entries for debug variables in `csp_debug.h`. Enhanced clarity and navigability of the debug API documentation. Signed-off-by: Gaetan Perrot --- doc/api/csp_debug_h.rst | 75 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/doc/api/csp_debug_h.rst b/doc/api/csp_debug_h.rst index 39b335972..45b2943af 100644 --- a/doc/api/csp_debug_h.rst +++ b/doc/api/csp_debug_h.rst @@ -2,4 +2,77 @@ CSP Debug ========= .. autocmodule:: csp_debug.h - :members: + +Variables +--------- + +.. autocdata:: csp_debug.h::csp_dbg_buffer_out + +.. autocdata:: csp_debug.h::csp_dbg_conn_out + +.. autocdata:: csp_debug.h::csp_dbg_conn_ovf + +.. autocdata:: csp_debug.h::csp_dbg_conn_noroute + +.. autocdata:: csp_debug.h::csp_dbg_inval_reply + +.. autocdata:: csp_debug.h::csp_dbg_errno + +.. autocdata:: csp_debug.h::csp_dbg_can_errno + +.. autocdata:: csp_debug.h::csp_dbg_eth_errno + +.. autocdata:: csp_debug.h::csp_dbg_rdp_print + +.. autocdata:: csp_debug.h::csp_dbg_packet_print + +Defines +------- + +General error codes +~~~~~~~~~~~~~~~~~~~ + +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_CORRUPT_BUFFER +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_MTU_EXCEEDED +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_ALREADY_FREE +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_REFCOUNT +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_INVALID_RTABLE_ENTRY +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_UNSUPPORTED +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_INVALID_BIND_PORT +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_PORT_ALREADY_IN_USE +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_ALREADY_CLOSED +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_INVALID_POINTER +.. autocmacro:: csp_debug.h::CSP_DBG_ERR_CLOCK_SET_FAIL + +CAN-specific error codes +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_FRAME_LOST +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_RX_OVF +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_RX_OUT +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_SHORT_BEGIN +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_INCOMPLETE +.. autocmacro:: csp_debug.h::CSP_DBG_CAN_ERR_UNKNOWN + +ETH-specific error codes +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_FRAME_LOST +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_RX_OVF +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_RX_OUT +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_SHORT_BEGIN +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_INCOMPLETE +.. autocmacro:: csp_debug.h::CSP_DBG_ETH_ERR_UNKNOWN + +Print macros +~~~~~~~~~~~~ + +.. autocmacro:: csp_debug.h::csp_print +.. autocmacro:: csp_debug.h::csp_rdp_error +.. autocmacro:: csp_debug.h::csp_rdp_protocol +.. autocmacro:: csp_debug.h::csp_print_packet + +Functions +--------- + +.. autocfunction:: csp_debug.h::csp_print_func From 5809f7ca89bb44020c3e06174317f736eff15bcf Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 10 Apr 2025 17:21:36 +0900 Subject: [PATCH 159/348] doc: Add missing functions to documentation for csp_buffer Include documentation for functions: csp_buffer_copy, csp_buffer_get_always, and csp_buffer_get_always_isr. Update the Sphinx documentation to reflect all available functions in the csp_buffer.h header file. Signed-off-by: Gaetan Perrot --- doc/api/csp_buffer_h.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/api/csp_buffer_h.rst b/doc/api/csp_buffer_h.rst index 74cb87c8a..b070bbc9d 100644 --- a/doc/api/csp_buffer_h.rst +++ b/doc/api/csp_buffer_h.rst @@ -11,6 +11,9 @@ Interface Functions .. autocfunction:: csp_buffer.h::csp_buffer_free .. autocfunction:: csp_buffer.h::csp_buffer_free_isr .. autocfunction:: csp_buffer.h::csp_buffer_clone +.. autocfunction:: csp_buffer.h::csp_buffer_copy .. autocfunction:: csp_buffer.h::csp_buffer_remaining .. autocfunction:: csp_buffer.h::csp_buffer_init .. autocfunction:: csp_buffer.h::csp_buffer_refc_inc +.. autocfunction:: csp_buffer.h::csp_buffer_get_always +.. autocfunction:: csp_buffer.h::csp_buffer_get_always_isr From 6ae4c2b049f16d9bc3ae14abe6ddcd4a6047cea6 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 21 Mar 2025 15:53:44 +0900 Subject: [PATCH 160/348] doc: Clarify CMake build command usage The documentation previously omitted the build directory in the `cmake --build` command. Update it to explicitly specify `builddir` for clarity. Signed-off-by: Gaetan Perrot --- doc/build-doc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build-doc.md b/doc/build-doc.md index 0cdebae96..5838d0ea8 100644 --- a/doc/build-doc.md +++ b/doc/build-doc.md @@ -12,5 +12,5 @@ python3 -m venv venv pip install -r doc/requirements.txt mkdir builddir cmake -S doc -B builddir -cmake --build +cmake --build builddir ``` From 9d9cd8df37c62e12a1b26cd6428a54f7f9385642 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 22:35:21 +0900 Subject: [PATCH 161/348] doc: Add ETH driver to directory structure table The documentation for the libcsp folder structure now includes the ETH driver located in src/drivers/eth. This update ensures that all existing driver directories are reflected in the table. Signed-off-by: Gaetan Perrot --- doc/structure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/structure.md b/doc/structure.md index b3b0eacc1..b80d6392e 100644 --- a/doc/structure.md +++ b/doc/structure.md @@ -20,6 +20,7 @@ following table: | `libcsp/src/drivers` | Drivers, mostly platform specific (Linux) | | `libcsp/src/drivers/can` | CAN | | `libcsp/src/drivers/usart` | USART | +| `libcsp/src/drivers/eth` | ETH | | `libcsp/src/interfaces` | Interfaces, CAN, I2C, KISS, LOOPBACK, ZMQHUB and others | | `libcsp/src/rtable` | Routing tables | | `libcsp/src/transport` | Transport layer: UDP, RDP | From c781a43765d25647cddc7bef59554f6f8ed67a29 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 22:40:44 +0900 Subject: [PATCH 162/348] doc: Add Zephyr to architecture section in structure table The structure documentation in the RST file was updated to include the Zephyr-specific architecture folder under `src/arch`. This reflects the actual presence of platform support for Zephyr within the libcsp codebase. Signed-off-by: Gaetan Perrot --- doc/structure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/structure.md b/doc/structure.md index b80d6392e..688bf6f52 100644 --- a/doc/structure.md +++ b/doc/structure.md @@ -14,6 +14,7 @@ following table: | `libcsp/src/arch` | Architecture (platform) specific code | | `libcsp/src/arch/freertos` | FreeRTOS | | `libcsp/src/arch/posix` | Posix (Linux) | +| `libcsp/src/arch/zephyr` | Zephyr | | `libcsp/src/arch/windows` | Windows | | `libcsp/src/bindings/python` | Python3 wrapper for libcsp | | `libcsp/src/crypto` | HMAC, SHA | From b73e2f5fefe05f2a65abe4f13e2986a9ae4cde33 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 22:43:10 +0900 Subject: [PATCH 163/348] doc: Remove Windows from architecture section The Windows-specific code is no longer maintained under `src/arch` and has been moved to the `contrib` directory due to lack of current support. The structure documentation has been updated to reflect this relocation. Signed-off-by: Gaetan Perrot --- doc/structure.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/structure.md b/doc/structure.md index 688bf6f52..e5f242836 100644 --- a/doc/structure.md +++ b/doc/structure.md @@ -15,7 +15,6 @@ following table: | `libcsp/src/arch/freertos` | FreeRTOS | | `libcsp/src/arch/posix` | Posix (Linux) | | `libcsp/src/arch/zephyr` | Zephyr | -| `libcsp/src/arch/windows` | Windows | | `libcsp/src/bindings/python` | Python3 wrapper for libcsp | | `libcsp/src/crypto` | HMAC, SHA | | `libcsp/src/drivers` | Drivers, mostly platform specific (Linux) | From 97e95349c40f5795f930afbf8486ab33704603c0 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 22:48:46 +0900 Subject: [PATCH 164/348] doc: Remove transport and rtable directories from structure The `src/transport` and `src/rtable` directories have been removed and their contents relocated directly under `src/`. The structure documentation was updated accordingly to reflect the current layout of the source tree. Signed-off-by: Gaetan Perrot --- doc/structure.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/structure.md b/doc/structure.md index e5f242836..217235406 100644 --- a/doc/structure.md +++ b/doc/structure.md @@ -22,8 +22,6 @@ following table: | `libcsp/src/drivers/usart` | USART | | `libcsp/src/drivers/eth` | ETH | | `libcsp/src/interfaces` | Interfaces, CAN, I2C, KISS, LOOPBACK, ZMQHUB and others | -| `libcsp/src/rtable` | Routing tables | -| `libcsp/src/transport` | Transport layer: UDP, RDP | | `libcsp/utils` | Utilities, Python scripts for decoding CSP headers. | | `libcsp/examples` | CSP examples, C/Python, zmqproxy | | `libcsp/doc` | RST based documentation (this documentation) | From 3c476ec735ca3d73dfa17afa935482f7c3aa3af7 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 23:06:07 +0900 Subject: [PATCH 165/348] doc: Update structure table to missing directory Update structure table to missing directory include samples, unittests, and contrib folders. Expanded the documentation section describing the libcsp folder structure: - Added `samples/` and `samples/posix/` for simple example programs. - Added `unitests/` to reflect the presence of unit test code. - Added `contrib/` section to list unmaintained or community-contributed ports and samples: - `contrib/macosx` for macOS support (currently unmaintained) - `contrib/windows` for Windows support (currently unmaintained) - `contrib/zephyr` for Zephyr OS sample applications This update helps clarify the current layout of the repository and reflects recent organizational changes made in the current state of libcsp. Signed-off-by: Gaetan Perrot --- doc/structure.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/structure.md b/doc/structure.md index 217235406..9ed68f040 100644 --- a/doc/structure.md +++ b/doc/structure.md @@ -25,3 +25,10 @@ following table: | `libcsp/utils` | Utilities, Python scripts for decoding CSP headers. | | `libcsp/examples` | CSP examples, C/Python, zmqproxy | | `libcsp/doc` | RST based documentation (this documentation) | +| `libcsp/samples` | Simple sample | +| `libcsp/samples/posix` | Posix simple sample | +| `libcsp/unittests` | Unittests | +| `libcsp/contrib` | Community contributions | +| `libcsp/contrib/macosx` | MacOSx | +| `libcsp/contrib/windows` | Windows | +| `libcsp/contrib/zephyr` | Zephyr samples | From 7d284921574e5b2746aa3d27d069c6008e52da18 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 10 Apr 2025 01:50:30 +0900 Subject: [PATCH 166/348] doc: Add sphinx gh-page extension to handle .nojekyll file Add sphinx.ext.githubpages extension for automatic .nojekyll file generation. This change enables Sphinx to automatically generate the .nojekyll file when building the documentation. This eliminates the need to manually manage the .nojekyll file for GitHub Pages. Signed-off-by: Gaetan Perrot --- doc/.nojekyll | 0 doc/CMakeLists.txt | 1 - doc/conf.py | 3 ++- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 doc/.nojekyll diff --git a/doc/.nojekyll b/doc/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 5ddb21dfc..6a8f8f627 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -14,7 +14,6 @@ file(GLOB DOCS_FILES "${SPHINX_SOURCE}/api/drivers/*.rst" "${SPHINX_SOURCE}/api/interfaces/*.rst") -file(COPY ${SPHINX_SOURCE}/.nojekyll DESTINATION ${SPHINX_BUILD}) add_custom_target(docs ALL DEPENDS ${SPHINX_INDEX_FILE}) add_custom_command(OUTPUT ${SPHINX_INDEX_FILE} COMMAND diff --git a/doc/conf.py b/doc/conf.py index 3255d3e6b..a9b3bf407 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -56,7 +56,8 @@ def find_libclang() -> str: 'sphinx_c_autodoc.viewcode', "sphinx_design", "sphinx_git", - "sphinx_copybutton" + "sphinx_copybutton", + "sphinx.ext.githubpages" ] # Add any paths that contain templates here, relative to this directory. From 8fa44964660c9c6fb766058f2405c3ed8f8628b8 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 9 Apr 2025 23:14:30 +0900 Subject: [PATCH 167/348] doc: conf.py: Remove duplicate sticky_navigation The 'sticky_navigation' key was defined twice in the html_theme_options dictionary. This commit removes the redundant entry to clean up the configuration. Signed-off-by: Gaetan Perrot --- doc/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index a9b3bf407..bbd3ba2bf 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -92,7 +92,6 @@ def find_libclang() -> str: 'navigation_depth': 2, 'includehidden': True, 'titles_only': True, - 'sticky_navigation': True } def include_readme_file(app, docname, source): From 188683f156585e8bc647a1acbe4a27b104543100 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 21 Mar 2025 15:48:41 +0900 Subject: [PATCH 168/348] doc: Add CAN Zephyr driver to documentation The CAN Zephyr driver (`drivers/can_zephyr.h`) was missing from the documentation. Signed-off-by: Gaetan Perrot --- doc/api/drivers/can_zephyr_h.rst | 11 +++++++++++ doc/api/drivers/drivers.rst | 1 + 2 files changed, 12 insertions(+) create mode 100644 doc/api/drivers/can_zephyr_h.rst diff --git a/doc/api/drivers/can_zephyr_h.rst b/doc/api/drivers/can_zephyr_h.rst new file mode 100644 index 000000000..4b3bcdc2c --- /dev/null +++ b/doc/api/drivers/can_zephyr_h.rst @@ -0,0 +1,11 @@ +Socket CAN driver (Zephyr) +========================== + +.. autocmodule:: drivers/can_zephyr.h + +Interface Functions +------------------- + +.. autocfunction:: drivers/can_zephyr.h::csp_can_open_and_add_interface +.. autocfunction:: drivers/can_zephyr.h::csp_can_set_rx_filter +.. autocfunction:: drivers/can_zephyr.h::csp_can_stop diff --git a/doc/api/drivers/drivers.rst b/doc/api/drivers/drivers.rst index fa442241d..3beeff18d 100644 --- a/doc/api/drivers/drivers.rst +++ b/doc/api/drivers/drivers.rst @@ -5,5 +5,6 @@ Drivers :maxdepth: 4 can_socketcan_h + can_zephyr_h eth_linux_h usart_h From 7f3fbb762ae71b1bad4756021a06b26455127f35 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 3 Apr 2025 16:38:42 +0900 Subject: [PATCH 169/348] github: workflow: python: Add Python 3.13 This update extends the GitHub Actions workflow to test against Python 3.13, ensuring compatibility with the latest Python versions. Signed-off-by: Gaetan Perrot --- .github/workflows/build-test-python.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index fc806d64b..77ab0c59e 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -10,6 +10,7 @@ jobs: - '3.10' - '3.11' - '3.12' + - '3.13' os: - ubuntu-24.04 - ubuntu-22.04 @@ -45,7 +46,7 @@ jobs: - name: Install the latest Meson using Pip if Python is 3.12 or later # Meson 1.3+ supports Python 3.12 (distutils removed) - if: ${{ matrix.buildsystem == 'meson' && matrix.python-version == '3.12' }} + if: ${{ matrix.buildsystem == 'meson' && matrix.python-version >= '3.12' }} run: | pip install meson meson --version From 12c103928cb0b89ef6a1e6d073001cdbcb2a3dc8 Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sat, 8 Mar 2025 00:59:12 +0100 Subject: [PATCH 170/348] build: warn about unused parameters Turn on `-Wunused-parameter` and use same compiler warnings in all builds to improve code safety. --- CMakeLists.txt | 11 +++++++++-- meson.build | 14 ++++++++------ wscript | 14 +++++++++++--- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06abab702..86dff82c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,8 +114,14 @@ target_include_directories(csp add_library(csp_common INTERFACE) if(CSP_POSIX) target_compile_options(csp_common INTERFACE - -Wall -Wextra -Wpedantic - -Wshadow -Wcast-align -Wpointer-arith -Wwrite-strings -Wno-unused-parameter + -Wall + -Wcast-align + -Werror + -Wextra + -Wpedantic + -Wpointer-arith + -Wshadow + -Wwrite-strings $<$:-Wno-gnu-zero-variadic-macro-arguments>) endif() target_link_libraries(csp PRIVATE csp_common) @@ -135,6 +141,7 @@ if(${CSP_ENABLE_PYTHON3_BINDINGS}) find_package(Python3 COMPONENTS Development.Module) if(Python3_Development.Module_FOUND) Python3_add_library(libcsp_py3 MODULE WITH_SOABI src/bindings/python/pycsp.c) + add_compile_options(-Wno-unused-parameter) target_include_directories(libcsp_py3 PUBLIC ${csp_inc}) target_link_libraries(libcsp_py3 PUBLIC csp) else() diff --git a/meson.build b/meson.build index 5e239be38..ed8764da2 100644 --- a/meson.build +++ b/meson.build @@ -63,11 +63,13 @@ csp_deps += clib csp_inc = include_directories('include', 'src') # Default compile options -csp_c_args = ['-Wshadow', - '-Wcast-align', - '-Wwrite-strings', - '-Wpointer-arith', - '-Wno-unused-parameter'] +csp_c_args = ['-Wall', + '-Wcast-align', + '-Wpointer-arith', + '-Wshadow', + '-Wwrite-strings', + '-Wextra', + '-Wpedantic'] if cc.get_id() == 'clang' csp_c_args += '-Wno-gnu-zero-variadic-macro-arguments' endif @@ -108,7 +110,7 @@ if get_option('enable_python3_bindings') # dependency() instead pydep = dependency('python3', version : '>=3.5', required : true) py.extension_module('libcsp_py3', 'src/bindings/python/pycsp.c', - c_args : csp_c_args, + c_args : [csp_c_args, '-Wno-unused-parameter'], dependencies : [csp_dep, pydep], install : true) endif diff --git a/wscript b/wscript index 3f2f91afe..a530dd992 100644 --- a/wscript +++ b/wscript @@ -79,9 +79,15 @@ def configure(ctx): # Setup CFLAGS if (len(ctx.stack_path) <= 1) and (len(ctx.env.CFLAGS) == 0): - ctx.env.prepend_value('CFLAGS', ["-std=gnu11", "-g", "-Os", "-Wall", "-Wextra", "-Wshadow", "-Wcast-align", - "-Wpointer-arith", "-Wpedantic", - "-Wwrite-strings", "-Wno-unused-parameter", "-Werror"]) + ctx.env.prepend_value('CFLAGS', ["-std=gnu11", "-g", "-Os", + "-Wall", + "-Wcast-align", + "-Werror", + "-Wextra", + "-Wpedantic", + "-Wpointer-arith", + "-Wshadow", + "-Wwrite-strings"]) if ctx.env.CC_NAME == 'clang': ctx.env.append_value('CFLAGS', ["-Wno-gnu-zero-variadic-macro-arguments"]) @@ -237,6 +243,8 @@ def build(ctx): use=['csp_shlib'], pytest_path=[ctx.path.get_bld()]) + ctx.env.append_value('CFLAGS', ["-Wno-unused-parameter"]) + if ctx.env.ENABLE_EXAMPLES: ctx.objects(source='examples/csp_posix_helper.c', target='csp_posix_helper', From bf904f5f85a51d2dbb32f3a832e9ffe58c6566b3 Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sat, 8 Mar 2025 01:28:56 +0100 Subject: [PATCH 171/348] build: fix unused parameters Cast unused function parameters to `void` to avoid compiler warnings about unused parameter. This makes the intention of keeping unused parameters for compatibility reasons more explicit. --- src/arch/freertos/csp_clock.c | 1 + src/arch/freertos/csp_hooks.c | 1 + src/arch/freertos/csp_system.c | 1 + src/arch/posix/csp_queue.c | 4 ++++ src/arch/posix/csp_system.c | 1 + src/arch/zephyr/csp_atomic.c | 5 +++++ src/arch/zephyr/csp_hooks.c | 1 + src/csp_buffer.c | 2 ++ src/csp_conn.c | 1 + src/csp_init.c | 1 + src/csp_io.c | 1 + src/csp_port.c | 1 + src/csp_promisc.c | 1 + src/csp_route.c | 4 +++- src/csp_rtable_cidr.c | 1 + src/csp_sfp.c | 2 ++ src/drivers/usart/usart_linux.c | 2 ++ src/interfaces/csp_if_can.c | 4 ++++ src/interfaces/csp_if_eth.c | 5 ++++- src/interfaces/csp_if_i2c.c | 1 + src/interfaces/csp_if_kiss.c | 3 +++ src/interfaces/csp_if_lo.c | 4 ++++ src/interfaces/csp_if_tun.c | 13 +++++++++++++ src/interfaces/csp_if_udp.c | 4 ++++ 24 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/arch/freertos/csp_clock.c b/src/arch/freertos/csp_clock.c index 92d2c5d7d..11be803b7 100644 --- a/src/arch/freertos/csp_clock.c +++ b/src/arch/freertos/csp_clock.c @@ -9,5 +9,6 @@ __weak void csp_clock_get_time(csp_timestamp_t * time) { } __weak int csp_clock_set_time(const csp_timestamp_t * time) { + (void)time; /* Avoid compiler warnings about unused parameter */ return CSP_ERR_NOTSUP; } diff --git a/src/arch/freertos/csp_hooks.c b/src/arch/freertos/csp_hooks.c index f576e62e8..748467533 100644 --- a/src/arch/freertos/csp_hooks.c +++ b/src/arch/freertos/csp_hooks.c @@ -6,6 +6,7 @@ __weak uint32_t csp_memfree_hook(void) { } __weak unsigned int csp_ps_hook(csp_packet_t * packet) { + (void)packet; /* Avoid compiler warnings about unused parameter */ return 0; } diff --git a/src/arch/freertos/csp_system.c b/src/arch/freertos/csp_system.c index 7b7229ba8..014323c95 100644 --- a/src/arch/freertos/csp_system.c +++ b/src/arch/freertos/csp_system.c @@ -13,5 +13,6 @@ __weak uint32_t csp_memfree_hook(void) { } __weak unsigned int csp_ps_hook(csp_packet_t * packet) { + (void)packet; /* Avoid compiler warnings about unused parameter */ return 0; } diff --git a/src/arch/posix/csp_queue.c b/src/arch/posix/csp_queue.c index 3c213b6e1..21628a143 100644 --- a/src/arch/posix/csp_queue.c +++ b/src/arch/posix/csp_queue.c @@ -4,6 +4,10 @@ #include "pthread_queue.h" csp_queue_handle_t csp_queue_create_static(int length, size_t item_size, char * buffer, csp_static_queue_t * queue) { + /* Avoid compiler warnings about unused parameter */ + (void)buffer; + (void)queue; + /* We ignore static allocation for posix for now */ return pthread_queue_create(length, item_size); } diff --git a/src/arch/posix/csp_system.c b/src/arch/posix/csp_system.c index 7ac114481..05d9250a7 100644 --- a/src/arch/posix/csp_system.c +++ b/src/arch/posix/csp_system.c @@ -18,6 +18,7 @@ uint32_t csp_memfree_hook(void) { } unsigned int csp_ps_hook(csp_packet_t * packet) { + (void)packet; /* Avoid compiler warnings about unused parameter */ return 0; } diff --git a/src/arch/zephyr/csp_atomic.c b/src/arch/zephyr/csp_atomic.c index b6c330cef..872f37dba 100644 --- a/src/arch/zephyr/csp_atomic.c +++ b/src/arch/zephyr/csp_atomic.c @@ -4,6 +4,11 @@ K_MUTEX_DEFINE(atomic_lock); bool __atomic_compare_exchange_4(volatile void * ptr, void * expected, unsigned int desired, bool weak, int success_memorder, int failure_memorder) { + /* Avoid compiler warnings about unused parameter */ + (void)weak; + (void)success_memorder; + (void)failure_memorder; + bool ret; k_mutex_lock(&atomic_lock, K_MSEC(CONFIG_CSP_ATOMIC_MUTEX_TIMEOUT)); diff --git a/src/arch/zephyr/csp_hooks.c b/src/arch/zephyr/csp_hooks.c index 23c30bcc5..5ad874d71 100644 --- a/src/arch/zephyr/csp_hooks.c +++ b/src/arch/zephyr/csp_hooks.c @@ -5,6 +5,7 @@ __weak uint32_t csp_memfree_hook(void) { } __weak unsigned int csp_ps_hook(csp_packet_t * packet) { + (void)packet; /* Avoid compiler warnings about unused parameter */ return 0; } diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 323262a27..319aed60b 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -211,9 +211,11 @@ csp_packet_t * csp_buffer_get_always_isr(void) { * sparse. */ csp_packet_t * csp_buffer_get(size_t unused) { + (void)unused; /* Avoid compiler warnings about unused parameter */ return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 0); } csp_packet_t * csp_buffer_get_isr(size_t unused) { + (void)unused; /* Avoid compiler warnings about unused parameter */ return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 1); } diff --git a/src/csp_conn.c b/src/csp_conn.c index 5ad935b10..b8f7c543c 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -247,6 +247,7 @@ int csp_conn_close(csp_conn_t * conn, uint8_t closed_by) { } csp_conn_t * csp_connect(uint8_t prio, uint16_t dest, uint8_t dport, uint32_t timeout, uint32_t opts) { + (void)timeout; /* Avoid compiler warnings about unused parameter */ /* Force options on all connections */ opts |= csp_conf.conn_dfl_so; diff --git a/src/csp_init.c b/src/csp_init.c index c1389a0b9..0d977ca3d 100644 --- a/src/csp_init.c +++ b/src/csp_init.c @@ -10,6 +10,7 @@ #include "csp_rdp_queue.h" __weak void csp_panic(const char * msg) { + (void)msg; /* Avoid compiler warnings about unused parameter */ return; } diff --git a/src/csp_io.c b/src/csp_io.c index 129d0d762..ec1ea19a2 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -217,6 +217,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route } __weak void csp_output_hook(const csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me) { + (void)from_me; /* Avoid compiler warnings about unused parameter */ csp_print_packet("OUT: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %u VIA: %s (%u), Tms %u\n", idout->src, idout->dst, idout->dport, idout->sport, idout->pri, idout->flags, packet->length, iface->name, (via != CSP_NO_VIA_ADDRESS) ? via : idout->dst, csp_get_ms()); return; diff --git a/src/csp_port.c b/src/csp_port.c index 71cfc577d..cbec0e127 100644 --- a/src/csp_port.c +++ b/src/csp_port.c @@ -75,6 +75,7 @@ csp_socket_t * csp_port_get_socket(unsigned int port) { } int csp_listen(csp_socket_t * socket, size_t backlog) { + (void)backlog; /* Avoid compiler warnings about unused parameter */ socket->rx_queue = csp_queue_create_static(CSP_CONN_RXQUEUE_LEN, sizeof(csp_packet_t *), socket->rx_queue_static_data, &socket->rx_queue_static); return CSP_ERR_NONE; } diff --git a/src/csp_promisc.c b/src/csp_promisc.c index 60e313139..a65696a81 100644 --- a/src/csp_promisc.c +++ b/src/csp_promisc.c @@ -11,6 +11,7 @@ static char csp_promisc_queue_buffer[sizeof(csp_packet_t *) * CSP_CONN_RXQUEUE_L static int csp_promisc_enabled = 0; int csp_promisc_enable(unsigned int queue_size) { + (void)queue_size; /* Avoid compiler warnings about unused parameter */ /* If queue already initialised */ if (csp_promisc_queue != NULL) { diff --git a/src/csp_route.c b/src/csp_route.c index 18c5f7cd2..614a38a6f 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -27,7 +27,9 @@ * @return CSP_ERR_NONE is all options are supported, CSP_ERR_NOTSUP if not */ static int csp_route_check_options(csp_iface_t * iface, csp_packet_t * packet) { - + /* Avoid compiler warnings about unused parameter */ + (void)iface; + (void)packet; #if (CSP_USE_HMAC == 0) /* Drop HMAC packets */ diff --git a/src/csp_rtable_cidr.c b/src/csp_rtable_cidr.c index a51323d42..438fbb269 100644 --- a/src/csp_rtable_cidr.c +++ b/src/csp_rtable_cidr.c @@ -128,6 +128,7 @@ void csp_rtable_iterate(csp_rtable_iterator_t iter, void * ctx) { #if (CSP_ENABLE_CSP_PRINT) static bool csp_rtable_print_route(void * ctx, csp_route_t * route) { + (void)ctx; /* Avoid compiler warnings about unused parameter */ if (route->via == CSP_NO_VIA_ADDRESS) { csp_print("%u/%u %s\r\n", route->address, route->netmask, route->iface->name); } else { diff --git a/src/csp_sfp.c b/src/csp_sfp.c index 89c43e2af..fbc54581d 100644 --- a/src/csp_sfp.c +++ b/src/csp_sfp.c @@ -49,6 +49,8 @@ static inline sfp_header_t * csp_sfp_header_remove(csp_packet_t * packet) { } int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int totalsize, unsigned int mtu, uint32_t timeout, csp_memcpy_fnc_t memcpyfcn) { + (void)timeout; /* Avoid compiler warnings about unused parameter */ + if (mtu == 0 || mtu + sizeof(sfp_header_t) > CSP_BUFFER_SIZE) { return CSP_ERR_INVAL; } diff --git a/src/drivers/usart/usart_linux.c b/src/drivers/usart/usart_linux.c index 3b8aa6442..5587c5cfb 100644 --- a/src/drivers/usart/usart_linux.c +++ b/src/drivers/usart/usart_linux.c @@ -24,10 +24,12 @@ typedef struct { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void csp_usart_lock(void * driver_data) { + (void)driver_data; /* Avoid compiler warnings about unused parameter */ pthread_mutex_lock(&lock); } void csp_usart_unlock(void * driver_data) { + (void)driver_data; /* Avoid compiler warnings about unused parameter */ pthread_mutex_unlock(&lock); } diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index d5b25d0ac..e0b951d61 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -163,6 +163,7 @@ int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t } int csp_can1_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + (void)from_me; /* Avoid compiler warnings about unused parameter */ /* Loopback */ if (packet->id.dst == iface->addr) { @@ -363,6 +364,9 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t } int csp_can2_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)via; + (void)from_me; /* Loopback */ if (packet->id.dst == iface->addr) { diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index 711abe831..cc4f262b0 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -238,8 +238,11 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei } int csp_eth_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)via; + (void)from_me; - csp_eth_interface_data_t * ifdata = iface->interface_data; + csp_eth_interface_data_t * ifdata = iface->interface_data; /* Loopback */ if (packet->id.dst == iface->addr) { diff --git a/src/interfaces/csp_if_i2c.c b/src/interfaces/csp_if_i2c.c index ffb3228b7..68e740e58 100644 --- a/src/interfaces/csp_if_i2c.c +++ b/src/interfaces/csp_if_i2c.c @@ -5,6 +5,7 @@ #include int csp_i2c_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + (void)from_me; /* Avoid compiler warnings about unused parameter */ /* Loopback */ if (packet->id.dst == iface->addr) { diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index 376d23eab..b1dfd0d9e 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -21,6 +21,9 @@ #define TNC_DATA 0x00 int csp_kiss_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)via; + (void)from_me; csp_kiss_interface_data_t * ifdata = iface->interface_data; void * driver = iface->driver_data; diff --git a/src/interfaces/csp_if_lo.c b/src/interfaces/csp_if_lo.c index dba171479..f4f17cfba 100644 --- a/src/interfaces/csp_if_lo.c +++ b/src/interfaces/csp_if_lo.c @@ -9,6 +9,10 @@ * @return 1 if packet was successfully transmitted, 0 on error */ static int csp_lo_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)iface; + (void)via; + (void)from_me; /* Send back into CSP, notice calling from task so last argument must be NULL! */ csp_qfifo_write(packet, &csp_if_lo, NULL); diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index 8ce4298fa..bd825a4c5 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -5,14 +5,27 @@ #include "csp_macro.h" __weak int csp_crypto_decrypt(uint8_t * ciphertext_in, uint8_t ciphertext_len, uint8_t * msg_out) { + /* Avoid compiler warnings about unused parameter */ + (void)ciphertext_in; + (void)ciphertext_len; + (void)msg_out; + return -1; } __weak int csp_crypto_encrypt(uint8_t * msg_begin, uint8_t msg_len, uint8_t * ciphertext_out) { + /* Avoid compiler warnings about unused parameter */ + (void)msg_begin; + (void)msg_len; + (void)ciphertext_out; + return -1; } static int csp_if_tun_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)via; + (void)from_me; csp_if_tun_conf_t * ifconf = iface->driver_data; diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index b05a9c48a..054a1be02 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -17,6 +17,9 @@ #endif static int csp_if_udp_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* Avoid compiler warnings about unused parameter */ + (void)via; + (void)from_me; csp_if_udp_conf_t * ifconf = iface->driver_data; @@ -36,6 +39,7 @@ static int csp_if_udp_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packe } int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { + (void)unused; /* Avoid compiler warnings about unused parameter */ csp_packet_t * packet = csp_buffer_get(0); if (packet == NULL) { From af65f265a824d16948cf8768f31aa25451851364 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 19 Jul 2025 20:02:38 +0900 Subject: [PATCH 172/348] buffer: Add warning about csp_buffer_get_always() Add a warning comment on csp_buffer_get_always(). The function is not a general-purpose convenience function. Signed-off-by: Yasushi SHOJI --- include/csp/csp_buffer.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index 4420af326..6432ca430 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -30,6 +30,14 @@ csp_packet_t * csp_buffer_get(size_t unused); /** * Get a buffer or get killed (from task context) + * + * This function return a buffer or kill the whole program when it + * failed. DO NOT USE THIS FUNCTION if you don't know what you are + * doing. Never use this function from application layer. This + * function should be an internal function and will be sonn. + * + * https://github.com/libcsp/libcsp/issues/864 + * * @return Buffer (pointer to #csp_packet_t) */ csp_packet_t * csp_buffer_get_always(void); From a8649782b05f67701c736dba0c51289dfa33eb7e Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 22 May 2025 15:15:54 +0900 Subject: [PATCH 173/348] drivers: usart_zephyr: Fix potential heap overflow The csp_usart_open function used an unsafe strcpy call to copy conf->device into ctx->name, which has a fixed size of 11 bytes. If conf->device exceeds this length, it would cause a buffer overflow, posing a risk to memory safety. This commit replaces strcpy with strncpy, limiting the copy to sizeof(ctx->name) - 1 bytes to ensure memory safety and prevent overflows. This prevents overflows while ensuring the string is properly null-terminated, assuming the struct is zero-initialized. Signed-off-by: Gaetan Perrot --- src/drivers/usart/usart_zephyr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/usart/usart_zephyr.c b/src/drivers/usart/usart_zephyr.c index 12e108815..395710d21 100644 --- a/src/drivers/usart/usart_zephyr.c +++ b/src/drivers/usart/usart_zephyr.c @@ -110,7 +110,7 @@ int csp_usart_open(const csp_usart_conf_t * conf, csp_usart_callback_t rx_callba return CSP_ERR_NOMEM; } - strcpy(ctx->name, conf->device); + strncpy(ctx->name, conf->device, sizeof(ctx->name) - 1); ctx->rx_callback = rx_callback; ctx->user_data = user_data; ctx->fd = device_get_binding(conf->device); From dd1ad4d2c5c5c1135af68ee846be82c2863f75e5 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 22 May 2025 13:10:18 +0900 Subject: [PATCH 174/348] drivers: eth: Fix potential heap overflow The csp_eth_init function previously used strcpy to copy the interface name into ctx->name, which can lead to a heap-based buffer overflow if the input name exceeds CSP_IFLIST_NAME_MAX bytes. This commit replaces the unsafe strcpy call with strncpy, limiting the copy to sizeof(ctx->name) - 1 bytes to ensure memory safety and prevent overflows. This reverts an earlier change that mistakenly replaced a safe strncpy with strcpy, reintroducing the vulnerability. Signed-off-by: Gaetan Perrot --- src/drivers/eth/eth_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 7c1809782..73901df58 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -79,7 +79,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int return CSP_ERR_NOMEM; } - strcpy(ctx->name, ifname); + strncpy(ctx->name, ifname, sizeof(ctx->name) - 1); ctx->ifdata.iface.name = ctx->name; ctx->ifdata.tx_func = &csp_eth_tx_frame; ctx->ifdata.tx_buf = (csp_eth_header_t*)&csp_eth_tx_buffer; From efdde2a885ede7184b613b8567e4f6618252e046 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:45:00 +0900 Subject: [PATCH 175/348] csp_if_udp: Fix typo Fix "frane" to "frame". Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 054a1be02..7f823e8c6 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -46,7 +46,7 @@ int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { return CSP_ERR_NOMEM; } - /* Setup RX frane to point to ID */ + /* Setup RX frame to point to ID */ int header_size = csp_id_setup_rx(packet); int received_len = recvfrom(sockfd, (char *)packet->frame_begin, sizeof(packet->data) + header_size, MSG_WAITALL, NULL, NULL); From 728e41a6f23e272bedb169cc542383eb5c35727f Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:45:35 +0900 Subject: [PATCH 176/348] csp_rdp: Fix typo Fix "timetamp" to "timestamp". Signed-off-by: Gaetan Perrot --- src/csp_rdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 0ae667140..038d5b339 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -126,7 +126,7 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, conn->rdp.rcv_lsa = ack_nr; } - /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp + /* Every outgoing message contains the last valid ACK number. So we always set last ack timestamp * We do this early to minimize race condition between read() call and router task csp_rdp_new_packet() */ conn->rdp.ack_timestamp = csp_get_ms(); @@ -396,7 +396,7 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { /* Update to latest outgoing ACK */ header->ack_nr = htobe16(conn->rdp.rcv_cur); - /* Every outgoing message contains the last valid ACK number. So we always set last ack timetamp */ + /* Every outgoing message contains the last valid ACK number. So we always set last ack timestamp */ conn->rdp.ack_timestamp = csp_get_ms(); /* Send copy to tx_queue */ packet->timestamp_tx = csp_get_ms(); From 6ee8d63da5d4eea0706c8c04d0f3555fd9a5632a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:46:00 +0900 Subject: [PATCH 177/348] csp_yaml: Fix typo Fix "Unkown" to "Unknown". Signed-off-by: Gaetan Perrot --- src/csp_yaml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_yaml.c b/src/csp_yaml.c index f77c2b6cb..2402546a6 100644 --- a/src/csp_yaml.c +++ b/src/csp_yaml.c @@ -195,7 +195,7 @@ static void csp_yaml_key_value(struct data_s * data, char * key, char * value) { } else if (strcmp(key, "promisc") == 0) { data->promisc = strdup(value); } else { - csp_print("Unkown key %s\n", key); + csp_print("Unknown key %s\n", key); } } From 105cd91be53d25b79d347bbee11d4af845f1fcbb Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:46:22 +0900 Subject: [PATCH 178/348] csp_services: Fix typo Fix "termianted" to "terminated". Signed-off-by: Gaetan Perrot --- src/csp_services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_services.c b/src/csp_services.c index 8495f9959..c1d191c3a 100644 --- a/src/csp_services.c +++ b/src/csp_services.c @@ -128,7 +128,7 @@ void csp_ps(uint16_t node, uint32_t timeout) { break; } - /* We have a reply, ensure data is 0 (zero) termianted */ + /* We have a reply, ensure data is 0 (zero) terminated */ const unsigned int length = (packet->length < sizeof(packet->data)) ? packet->length : (sizeof(packet->data) - 1); packet->data[length] = 0; csp_print("%s", packet->data); From 9edfd1def7fc54db45df5cace40caf171afa8684 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:46:46 +0900 Subject: [PATCH 179/348] csp_route: Fix typo Fix "compatability" to "compatibility". Signed-off-by: Gaetan Perrot --- src/csp_route.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_route.c b/src/csp_route.c index 614a38a6f..c8fe0c13b 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -63,7 +63,7 @@ static int csp_route_security_check(uint32_t security_opts, csp_iface_t * iface, /* CRC32 verified packet */ if (packet->id.flags & CSP_FCRC32) { - /* Verify CRC32 (does not include header for backwards compatability with csp1.x) */ + /* Verify CRC32 (does not include header for backwards compatibility with csp1.x) */ if (csp_crc32_verify(packet) != CSP_ERR_NONE) { iface->rx_error++; return CSP_ERR_CRC32; @@ -76,7 +76,7 @@ static int csp_route_security_check(uint32_t security_opts, csp_iface_t * iface, #if (CSP_USE_HMAC) /* HMAC authenticated packet */ if (packet->id.flags & CSP_FHMAC) { - /* Verify HMAC (does not include header for backwards compatability with csp1.x) */ + /* Verify HMAC (does not include header for backwards compatibility with csp1.x) */ if (csp_hmac_verify(packet, false) != CSP_ERR_NONE) { /* HMAC failed */ iface->autherr++; From 0224fcd0f4f9f7795b6849ef17f89131f6cb49e2 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:47:27 +0900 Subject: [PATCH 180/348] arch: zephyr: csp_clock: Fix typo Fix "retruning" to "returning". Signed-off-by: Gaetan Perrot --- src/arch/zephyr/csp_clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/zephyr/csp_clock.c b/src/arch/zephyr/csp_clock.c index 902de2ff6..d4e8f926b 100644 --- a/src/arch/zephyr/csp_clock.c +++ b/src/arch/zephyr/csp_clock.c @@ -10,7 +10,7 @@ __weak void csp_clock_get_time(csp_timestamp_t * time) { ret = clock_gettime(CLOCK_REALTIME, &ts); if (ret < 0) { - LOG_WRN("clock_gettime() failed, retruning with 0s"); + LOG_WRN("clock_gettime() failed, returning with 0s"); time->tv_sec = 0; time->tv_nsec = 0; } else { From 8a6e79cd178727fe7e1855e2ae38ff5531c08257 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:48:02 +0900 Subject: [PATCH 181/348] csp_conn: Fix typo Fix "brodcast" to "broadcast". Signed-off-by: Gaetan Perrot --- src/csp_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_conn.c b/src/csp_conn.c index b8f7c543c..ef690f0b5 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -116,7 +116,7 @@ csp_conn_t * csp_conn_find_existing(csp_id_t * id) { /* Incoming connections are uniquely defined by the source and * destination port, as well as the source node. Incoming - * connections can never come from a brodcast address */ + * connections can never come from a broadcast address */ } else { /* Connection must match dport */ From d7cd8a139411083045c2107c4f416822ffcb4c7d Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:48:27 +0900 Subject: [PATCH 182/348] csp_buffer: Fix typo Fix "funktions" to "functions". Signed-off-by: Gaetan Perrot --- src/csp_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 319aed60b..5a1880975 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -206,7 +206,7 @@ csp_packet_t * csp_buffer_get_always_isr(void) { } /* CSP will try to reserve the last two buffers for calls which can take it, - * examples are client funktions that are allowed to fail and have adequate + * examples are client functions that are allowed to fail and have adequate * error checking. Or services which are allowed to timeout of memory becomes * sparse. */ From 45253073505cab64f1f4ff1365e263370518fefa Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:48:54 +0900 Subject: [PATCH 183/348] csp_iflist: Fix typo Fix "invalud" to "invalid". Signed-off-by: Gaetan Perrot --- src/csp_iflist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 3ad071f6b..5dabc66d0 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -43,7 +43,7 @@ csp_iface_t * csp_iflist_get_by_subnet(uint16_t addr, csp_iface_t * ifc) { while (ifc) { - /* Reject searches involving subnets, if the netmask is invalud */ + /* Reject searches involving subnets, if the netmask is invalid */ if (ifc->netmask == 0) { ifc = ifc->next; continue; From 2ea1df31bf719cb780b8676a4db7f99f27e9ff7c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:49:25 +0900 Subject: [PATCH 184/348] csp_qfifo: Fix typo Fix "untill" to "until". Signed-off-by: Gaetan Perrot --- src/csp_qfifo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_qfifo.h b/src/csp_qfifo.h index 31670670b..ef3cd9ae2 100644 --- a/src/csp_qfifo.h +++ b/src/csp_qfifo.h @@ -6,7 +6,7 @@ #if (CSP_USE_RDP) #define FIFO_TIMEOUT 100 //! If RDP is enabled, the router needs to awake some times to check timeouts #else -#define FIFO_TIMEOUT CSP_MAX_TIMEOUT //! If no RDP, the router can sleep untill data arrives +#define FIFO_TIMEOUT CSP_MAX_TIMEOUT //! If no RDP, the router can sleep until data arrives #endif /** From a946782d828570c0ecd23b0775362364d8b13441 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:49:59 +0900 Subject: [PATCH 185/348] drivers: can: can_zephyr: Fix typo Fix "addtion" to "addition". Signed-off-by: Gaetan Perrot --- src/drivers/can/can_zephyr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/can/can_zephyr.c b/src/drivers/can/can_zephyr.c index 37d161705..acc464bf9 100644 --- a/src/drivers/can/can_zephyr.c +++ b/src/drivers/can/can_zephyr.c @@ -242,7 +242,7 @@ int csp_can_open_and_add_interface(const struct device * device, const char * if * The following section is for restoring acquired resources when * something fails. Unfortunately, we can't take any action if the * restoration process fails, so we proceed with the remaining - * cleanup. In addtion to this, we've chosen not to restore the + * cleanup. In addition to this, we've chosen not to restore the * CAN bit rate. If this causes any issues, please open an issue * on GitHub. */ From ff0d703c3add1356ed03c4b2525cdb10eb2816ad Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:50:55 +0900 Subject: [PATCH 186/348] drivers: eth: eth_linux: Fix typo Fix "incase" to "in case". Signed-off-by: Gaetan Perrot --- src/drivers/eth/eth_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 73901df58..385e12741 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -143,7 +143,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int ((uint8_t *)if_mac.ifr_hwaddr.sa_data)[4], ((uint8_t *)if_mac.ifr_hwaddr.sa_data)[5]); - /* Allow the socket to be reused - incase connection is closed prematurely */ + /* Allow the socket to be reused - in case connection is closed prematurely */ int sockopt; if (setsockopt(ctx->sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) { perror("setsockopt"); From 5077a1784038e32f0f68e5500b1cd09daa0d1597 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:52:06 +0900 Subject: [PATCH 187/348] csp_io: Fix typo Fix "inteface" to "interface". Fix "brodcast" to "broadcast". Fix "compatability" to "compatibility". Signed-off-by: Gaetan Perrot --- src/csp_io.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index ec1ea19a2..6d3e7616a 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -107,7 +107,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route local_found = 1; - /* Do not send back to same inteface (split horizon) + /* Do not send back to same interface (split horizon) * This check is is similar to that below, but faster */ if (iface == routed_from) { continue; @@ -123,7 +123,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route _idout.src = iface->addr; } - /* Rewrite routed brodcast (L3) to local (L2) when arriving at the interface */ + /* Rewrite routed broadcast (L3) to local (L2) when arriving at the interface */ if (csp_id_is_broadcast(idout->dst, iface)) { _idout.dst = csp_id_get_max_nodeid(); } @@ -152,7 +152,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route do { route_found = 1; - /* Do not send back to same inteface (split horizon) + /* Do not send back to same interface (split horizon) * This check is is similar to that below, but faster */ if (route->iface == routed_from) { continue; @@ -186,7 +186,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route /* Try to send via default interfaces */ while ((iface = csp_iflist_get_by_isdfl(iface)) != NULL) { - /* Do not send back to same inteface (split horizon) + /* Do not send back to same interface (split horizon) * This check is is similar to that below, but faster */ if (iface == routed_from) { continue; @@ -238,7 +238,7 @@ void csp_send_direct_iface(const csp_id_t* idout, csp_packet_t * packet, csp_ifa /* Append HMAC */ if (idout->flags & CSP_FHMAC) { #if (CSP_USE_HMAC) - /* Calculate and add HMAC (does not include header for backwards compatability with csp1.x) */ + /* Calculate and add HMAC (does not include header for backwards compatibility with csp1.x) */ if (csp_hmac_append(packet, false) != CSP_ERR_NONE) { /* HMAC append failed */ goto tx_err; @@ -251,7 +251,7 @@ void csp_send_direct_iface(const csp_id_t* idout, csp_packet_t * packet, csp_ifa /* Append CRC32 */ if (idout->flags & CSP_FCRC32) { - /* Calculate and add CRC32 (does not include header for backwards compatability with csp1.x) */ + /* Calculate and add CRC32 (does not include header for backwards compatibility with csp1.x) */ if (csp_crc32_append(packet) != CSP_ERR_NONE) { /* CRC32 append failed */ goto tx_err; From 0aa6aa32ae195d6c2325811afb0cc13d7d9fc611 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:52:41 +0900 Subject: [PATCH 188/348] utils: cspsplit: Fix typo Fix "Priotity" to "Priority". Signed-off-by: Gaetan Perrot --- utils/cspsplit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/cspsplit.py b/utils/cspsplit.py index f2a0f901a..69d7b6bb0 100755 --- a/utils/cspsplit.py +++ b/utils/cspsplit.py @@ -20,7 +20,7 @@ def main(): print("HEADER must be in hexadecimal format") sys.exit(-1) - print("Priotity: {0}".format((hdrhex >> 30) & 0x03)) + print("Priority: {0}".format((hdrhex >> 30) & 0x03)) print("Source: {0}".format((hdrhex >> 25) & 0x1f)) print("Destination: {0}".format((hdrhex >> 20) & 0x1f)) print("Destination port: {0}".format((hdrhex >> 14) & 0x3f)) From 575b0c263a1ad8479d78c15c8184eaf9e750901b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:53:21 +0900 Subject: [PATCH 189/348] examples: csp_server: Fix typo Fix "timout" to "timeout". Signed-off-by: Gaetan Perrot --- examples/csp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/csp_server.c b/examples/csp_server.c index b9a5c48a8..69c9b7d7f 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -57,7 +57,7 @@ void * server(void * param) { continue; } - /* Read packets on connection, timout is 100 mS */ + /* Read packets on connection, timeout is 100 mS */ csp_packet_t *packet; while ((packet = csp_read(conn, 50)) != NULL) { switch (csp_conn_dport(conn)) { From 22200468faaf08df585f8a677553d2fff1baa8f2 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:53:40 +0900 Subject: [PATCH 190/348] examples: csp_server_client: Fix typo Fix "timout" to "timeout". Signed-off-by: Gaetan Perrot --- examples/csp_server_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 61171fc36..2edcbe2bc 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -47,7 +47,7 @@ void * server(void * param) { continue; } - /* Read packets on connection, timout is 100 mS */ + /* Read packets on connection, timeout is 100 mS */ csp_packet_t *packet; while ((packet = csp_read(conn, 50)) != NULL) { switch (csp_conn_dport(conn)) { From 1c6f109439311b9bf90409b572c8af7cb4ddbdd0 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:54:15 +0900 Subject: [PATCH 191/348] examples: iflist: Fix typo Fix "attirbutes" to "attributes". Signed-off-by: Gaetan Perrot --- examples/iflist.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/iflist.yaml b/examples/iflist.yaml index 09a26f058..7b850c1e8 100644 --- a/examples/iflist.yaml +++ b/examples/iflist.yaml @@ -7,7 +7,7 @@ # List of supported drivers: # can, zmq, uart, tun, TODO: udp # -# The following additional attirbutes are optional: +# The following additional attributes are optional: # device: used for can, and uart typically set to /dev/ttyUSB0 or can0 # server: used for zmq, typically set to an IP address of a zmqproxy # default: true, set to true on one interface only. Sets the default route to this if. From 4dde8b853dd37a10e0d91f16fb1e354eb34d2b1d Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:55:13 +0900 Subject: [PATCH 192/348] include: csp: Fix typo Fix "resorces" to "resources". Fix "fuction" to "function". Fix "acknowleges" to "acknowledges". Fix "reguarly" to "regularly". Fix "occurrs" to "occurs". Fix "tranmits" to "transmits". Signed-off-by: Gaetan Perrot --- include/csp/csp.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/csp/csp.h b/include/csp/csp.h index e5ad78e55..8361f79f9 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -56,7 +56,7 @@ extern csp_conf_t csp_conf; void csp_init(void); /** - * Free allocated resorces in CSP. + * Free allocated resources in CSP. * This is intended for testing of CSP, in order to be able re-initialize CSP by calling csp_init() again. */ void csp_free_resources(void); @@ -89,7 +89,7 @@ csp_conn_t *csp_accept(csp_socket_t *socket, uint32_t timeout); /** * Read packet from a connection. - * This fuction will wait on the connection's RX queue for the specified timeout. + * This function will wait on the connection's RX queue for the specified timeout. * * @param[in] conn connection * @param[in] timeout timeout in mS to wait for a packet, use CSP_MAX_TIMEOUT for infinite timeout. @@ -203,7 +203,7 @@ void csp_sendto_reply(const csp_packet_t * request, csp_packet_t * reply, uint32 /** * Establish outgoing connection. * The call will return immediately, unless it is a RDP connection (#CSP_O_RDP) in which case it will wait until the other - * end acknowleges the connection (timeout is determined by the current connection timeout set by csp_rdp_set_opt()). + * end acknowledges the connection (timeout is determined by the current connection timeout set by csp_rdp_set_opt()). * * @param[in] prio priority, see #csp_prio_t * @param[in] dst Destination address @@ -316,7 +316,7 @@ int csp_bind_callback(csp_callback_t callback, uint8_t port); /** * Route packet from the incoming router queue and check RDP timeouts. - * In order for incoming packets to routed and RDP timeouts to be checked, this function must be called reguarly. + * In order for incoming packets to routed and RDP timeouts to be checked, this function must be called regularly. * @return #CSP_ERR_NONE on success, otherwise an error code. */ int csp_route_work(void); @@ -372,7 +372,7 @@ void csp_ping_noreply(uint16_t node); * .. note:: This is currently only supported on FreeRTOS systems. * * @param[in] node address of subsystem. - * @param[in] timeout timeout in mS to wait for replies. The function will not return until the timeout occurrs. + * @param[in] timeout timeout in mS to wait for replies. The function will not return until the timeout occurs. */ void csp_ps(uint16_t node, uint32_t timeout); @@ -452,7 +452,7 @@ int csp_get_uptime(uint16_t node, uint32_t timeout, uint32_t * uptime); /** * Set RDP options. * The RDP options are used from the connecting/client side. When a RDP connection - * is established, the client tranmits the options to the server. + * is established, the client transmits the options to the server. * * @param[in] window_size window size * @param[in] conn_timeout_ms connection timeout in mS From 9e004d1195f1513dc6a09383b2ea21d622fed862 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:55:43 +0900 Subject: [PATCH 193/348] include: csp_cmp: Fix typo Fix "memeory" to "memory". Signed-off-by: Gaetan Perrot --- include/csp/csp_cmp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/csp_cmp.h b/include/csp/csp_cmp.h index 96f5132ad..dcb45f5d9 100644 --- a/include/csp/csp_cmp.h +++ b/include/csp/csp_cmp.h @@ -83,12 +83,12 @@ extern "C" { #define CSP_CMP_ROUTE_IFACE_LEN 11 /** - * CMP peek/read memeory - max read length. + * CMP peek/read memory - max read length. */ #define CSP_CMP_PEEK_MAX_LEN 200 /** - * CMP poke/write memeory - max write length. + * CMP poke/write memory - max write length. */ #define CSP_CMP_POKE_MAX_LEN 200 From dfb7cda4e5e35cf34f20f0a01ad789f84e202db2 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:56:09 +0900 Subject: [PATCH 194/348] include: csp_debug: Fix typo Fix "Toogle" to "Toggle". Signed-off-by: Gaetan Perrot --- include/csp/csp_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/csp_debug.h b/include/csp/csp_debug.h index 8f4924100..b5d3a8754 100644 --- a/include/csp/csp_debug.h +++ b/include/csp/csp_debug.h @@ -62,7 +62,7 @@ extern uint8_t csp_dbg_eth_errno; #define CSP_DBG_ETH_ERR_INCOMPLETE 5 #define CSP_DBG_ETH_ERR_UNKNOWN 6 -/* Toogle flags for rdp and packet print */ +/* Toggle flags for rdp and packet print */ extern uint8_t csp_dbg_rdp_print; extern uint8_t csp_dbg_packet_print; From 03a398a83939b476341c3da7b02e05561a6e27f9 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:56:34 +0900 Subject: [PATCH 195/348] interfaces: csp_if_udp: Fix typo Fix "attemting" to "attempting". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_udp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/interfaces/csp_if_udp.h b/include/csp/interfaces/csp_if_udp.h index a37cf260c..69ddf5575 100644 --- a/include/csp/interfaces/csp_if_udp.h +++ b/include/csp/interfaces/csp_if_udp.h @@ -34,7 +34,7 @@ typedef struct { * RX task: * A server task will attempt at binding to ip 0.0.0.0 port 9600 * If this fails, it is because another udp server is already running. - * The server task will continue attemting the bind and will not exit before the application is closed. + * The server task will continue attempting the bind and will not exit before the application is closed. * * TX peer: * Outgoing CSP packets will be transferred to the peer specified by the host argument From 80dec9035b6e0e096e19350234f70e2df0e2997f Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:57:12 +0900 Subject: [PATCH 196/348] interfaces: zmq: Fix typo Fix "refered" to "referred". Fix "succcess" to "success". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_zmqhub.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/csp/interfaces/csp_if_zmqhub.h b/include/csp/interfaces/csp_if_zmqhub.h index 829e58e59..dd90ef1d6 100644 --- a/include/csp/interfaces/csp_if_zmqhub.h +++ b/include/csp/interfaces/csp_if_zmqhub.h @@ -3,7 +3,7 @@ * * **Description:** ZMQ (ZeroMQ) interface. * - * The ZMQ interface is designed to connect to a ZMQ hub, also refered to as + * The ZMQ interface is designed to connect to a ZMQ hub, also referred to as * zmqproxy. The zmqproxy can be found under examples, and is based on * zmq_proxy() - provided by the ZMQ API. * @@ -44,7 +44,7 @@ extern "C" { * @param[in] port IP port. * @param[out] buf user allocated buffer for receiving formatted string. * @param[in] buf_size size of buf. - * @return #CSP_ERR_NONE on succcess. #CSP_ERR_NOMEM if supplied buffer too small. + * @return #CSP_ERR_NONE on success. #CSP_ERR_NOMEM if supplied buffer too small. */ int csp_zmqhub_make_endpoint(const char * host, uint16_t port, char * buf, size_t buf_size); @@ -57,7 +57,7 @@ int csp_zmqhub_make_endpoint(const char * host, uint16_t port, char * buf, size_ * created using the host and the default subscribe/publish ports. * @param[in] flags flags for controlling features on the connection. * @param[out] return_interface created CSP interface. - * @return #CSP_ERR_NONE on succcess - else assert. + * @return #CSP_ERR_NONE on success - else assert. */ int csp_zmqhub_init(uint16_t addr, const char * host, @@ -75,7 +75,7 @@ int csp_zmqhub_init(uint16_t addr, * to zmqproxy's publish port #CSP_ZMQPROXY_PUBLISH_PORT. * @param[in] flags flags for controlling features on the connection. * @param[out] return_interface created CSP interface. - * @return #CSP_ERR_NONE on succcess - else assert. + * @return #CSP_ERR_NONE on success - else assert. */ int csp_zmqhub_init_w_endpoints(uint16_t addr, const char * publish_endpoint, @@ -97,7 +97,7 @@ int csp_zmqhub_init_w_endpoints(uint16_t addr, * publish port #CSP_ZMQPROXY_PUBLISH_PORT. * @param[in] flags flags for controlling features on the connection. * @param[out] return_interface created CSP interface. - * @return #CSP_ERR_NONE on succcess - else assert. + * @return #CSP_ERR_NONE on success - else assert. */ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr, const uint16_t rx_filter[], unsigned int rx_filter_count, From 706e7c1f14d43ebed46eaac24896515e90f7f6c7 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:57:39 +0900 Subject: [PATCH 197/348] interfaces: kiss: Fix typo Fix "reveived" to "received". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_kiss.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/interfaces/csp_if_kiss.h b/include/csp/interfaces/csp_if_kiss.h index 97a3ee1a4..a40f6cd7b 100644 --- a/include/csp/interfaces/csp_if_kiss.h +++ b/include/csp/interfaces/csp_if_kiss.h @@ -71,7 +71,7 @@ int csp_kiss_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int fr * frame has been received, the CSP packet will be routed on. * * @param[in] iface incoming interface. - * @param[in] buf reveived data. + * @param[in] buf received data. * @param[in] len length of \a buf. * @param[out] pxTaskWoken Valid reference if called from ISR, otherwise NULL! */ From 3fe3c868cf51f71fdeff81ee9d5c814f9c3993b3 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:58:07 +0900 Subject: [PATCH 198/348] interfaces: csp_if_eth: Fix typo Fix "reqired" to "required". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_eth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/interfaces/csp_if_eth.h b/include/csp/interfaces/csp_if_eth.h index 08eb08a8a..33c452730 100644 --- a/include/csp/interfaces/csp_if_eth.h +++ b/include/csp/interfaces/csp_if_eth.h @@ -63,7 +63,7 @@ extern "C" { #define CSP_ETH_ALEN 6 /* Octets in one ethernet addr */ /** - * Definition of ethernet header, including reqired MAC addresses + * Definition of ethernet header, including required MAC addresses * Frame data is required to proceed in memory space after this struct */ typedef struct csp_eth_header_s From 23c409a04203545636ccf2f876ed4b04409abbbd Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:58:32 +0900 Subject: [PATCH 199/348] interfaces: can: Fix typo Fix "addressses" to "addresses". Fix "priotized" to "prioritized". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_can.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/interfaces/csp_if_can.h b/include/csp/interfaces/csp_if_can.h index 9df662884..5d04b17e7 100644 --- a/include/csp/interfaces/csp_if_can.h +++ b/include/csp/interfaces/csp_if_can.h @@ -14,7 +14,7 @@ * - Remain: 8 bits * - Identifier: 10 bits * - * The Source and Destination fields must match the source and destiantion addressses + * The Source and Destination fields must match the source and destination addresses * in the CSP packet. The Type field is used to distinguish the first and subsequent * frames in a fragmented CSP packet. Type is BEGIN (0) for the first fragment and * MORE (1) for all other fragments. The Remain field indicates number of remaining @@ -33,7 +33,7 @@ * - End flag: 1 bit * * The \b Priority represents the CSP prio field. Placing this as the first bits in - * the transmission ensure that packets with high priority is priotized on the bus + * the transmission ensure that packets with high priority is prioritized on the bus * due to the nature of CAN arbitration. * The \b Destination field represents the CSP node of the receiving node * The \b Sender holds the least significant bits of the transmitting interface, From 093a4e6cbd84a001afa1e1ec4fac266387a96909 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:58:53 +0900 Subject: [PATCH 200/348] interfaces: csp_if_eth_pbuf: Fix typo Fix "dows" to "does". Fix "leas" to "least". Signed-off-by: Gaetan Perrot --- include/csp/interfaces/csp_if_eth_pbuf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/interfaces/csp_if_eth_pbuf.h b/include/csp/interfaces/csp_if_eth_pbuf.h index dc3104fb3..01e1e0e94 100644 --- a/include/csp/interfaces/csp_if_eth_pbuf.h +++ b/include/csp/interfaces/csp_if_eth_pbuf.h @@ -9,7 +9,7 @@ * counter that wraps around at 32768. With multiple nodes connected on ethernet * the packet identifiers on different nodes may collide, which is why the CSP * source address is included in the pbuf identifier. Multiple source addresses - * causes no problems, as the packet_id dows not depend on the packet content. + * causes no problems, as the packet_id does not depend on the packet content. * * A segment identifier is considered not required, as the stream of ethernet * frames follows a single path, and there are no retransmission, so it can be @@ -18,7 +18,7 @@ * CSP_IF_ETH_PBUF_TIMEOUT_MS after latest data was received. * * There is a minimum ethernet frame size, such that the received size is at - * leas 60 bytes. The segment size field of the header provides the length of + * least 60 bytes. The segment size field of the header provides the length of * the actual payload. * * The size of a CSP packet can generally not be determined from a partial CSP From 7f6eac5a97ba2064422e9406f2fab9cf6449817b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:59:17 +0900 Subject: [PATCH 201/348] drivers: usart: Fix typo Fix "convience" to "convenience". Fix "internface" to "interface". Signed-off-by: Gaetan Perrot --- include/csp/drivers/usart.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/drivers/usart.h b/include/csp/drivers/usart.h index 2a68df9f2..fabb51caf 100644 --- a/include/csp/drivers/usart.h +++ b/include/csp/drivers/usart.h @@ -89,12 +89,12 @@ void csp_usart_unlock(void * driver_data); /** * Opens UART device and add KISS interface. * - * This is a convience function for opening an UART device and adding it as an interface with a given name. + * This is a convenience function for opening an UART device and adding it as an interface with a given name. * * .. note:: On read failures, exit() will be called - terminating the process. * * @param[in] conf UART configuration. - * @param[in] ifname internface name (will be copied), or use NULL for default name. + * @param[in] ifname interface name (will be copied), or use NULL for default name. * @param[in] addr CSP address of the interface. * @param[out] return_iface the added interface. * @return #CSP_ERR_NONE on success, otherwise an error code. From 73c3e6a0d25135419d3d95e86bed5e75e9ededd4 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:59:35 +0900 Subject: [PATCH 202/348] include: csp_types: Fix typo Fix "apllies" to "applies". Fix "accomodate" to "accommodate". Signed-off-by: Gaetan Perrot --- include/csp/csp_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index 9548ce019..d9d4522a6 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -85,7 +85,7 @@ typedef struct __packed { #define CSP_SO_CRC32REQ 0x0040 /*< Require CRC32 */ #define CSP_SO_CRC32PROHIB 0x0080 /*< Prohibit CRC32 */ #define CSP_SO_CONN_LESS 0x0100 /*< Enable Connection Less mode */ -#define CSP_SO_SAME 0x8000 /*< Copy opts from incoming packet only apllies to csp_sendto_reply() */ +#define CSP_SO_SAME 0x8000 /*< Copy opts from incoming packet only applies to csp_sendto_reply() */ /**@}*/ @@ -135,7 +135,7 @@ typedef struct csp_packet_s { /** * Additional header bytes, to prepend packed data before transmission - * This must be minimum 6 bytes to accomodate CSP 2.0. But some implementations + * This must be minimum 6 bytes to accommodate CSP 2.0. But some implementations * require much more scratch working area for encryption for example. * * Ultimately after csp_id_pack() this area will be filled with the CSP header From cdf1093871afcfbf0900f27ff46b7ae434e3509a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 19:59:46 +0900 Subject: [PATCH 203/348] include: csp_sfp: Fix typo Fix "chuncks" to "chunks". Fix "usefull" to "useful". Signed-off-by: Gaetan Perrot --- include/csp/csp_sfp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/csp_sfp.h b/include/csp/csp_sfp.h index 68f35c8fb..33a01af4c 100644 --- a/include/csp/csp_sfp.h +++ b/include/csp/csp_sfp.h @@ -4,7 +4,7 @@ * **Description:** Simple Fragmentation Protocol (SFP). * * The SFP API can transfer a blob of data across an established CSP connection, - * by chopping the data into smaller chuncks of data, that can fit into a single CSP message. + * by chopping the data into smaller chunks of data, that can fit into a single CSP message. * * SFP will add a small header to each packet, containing information about the transfer. * SFP is usually sent over a RDP connection (which also adds a header), @@ -28,7 +28,7 @@ extern "C" { * * csp_sfp_recv() or csp_sfp_recv_fp() can be used at the other end to receive data. * - * This is usefull if you wish to send data stored in flash memory or another location, where standard memcpy() doesn't work. + * This is useful if you wish to send data stored in flash memory or another location, where standard memcpy() doesn't work. * * @param[in] conn established connection for sending SFP packets. * @param[in] data data to send From 8943ef28541bab3ab33de03af90ce3f18c67b1bb Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:00:14 +0900 Subject: [PATCH 204/348] contrib: zephyr: Kconfig: Fix typo Fix "incomming" to "incoming". Fix "reveiving" to "receiving". Signed-off-by: Gaetan Perrot --- contrib/zephyr/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index ded4f3eed..24e699db0 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -46,7 +46,7 @@ config CSP_QFIFO_LEN int "Length of incoming queue for router task" default 15 help - This value specifies the length of queue for incomming + This value specifies the length of queue for incoming packets that are used by csp_qfifo_read() and csp_qfifo_write(). @@ -103,7 +103,7 @@ config CSP_UART_RX_INTERVAL int "Interval (nesc) at which the receiving thread runs." default 1000 help - Interval (nsec) at which the reveiving thread runs. + Interval (nsec) at which the receiving thread runs. config CSP_UART_RX_BUFFER_LENGTH int "Size of buffer to be stored when receiving in the CSP UART Driver." From c7319f8d61ea69510bdc66177490a1ae37b33e0c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:00:32 +0900 Subject: [PATCH 205/348] doc: basic: Fix typo Fix "accomodate" to "accommodate". Fix "configued" to "configured". Signed-off-by: Gaetan Perrot --- doc/basic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/basic.md b/doc/basic.md index c2f4b66df..15c3e6fe3 100644 --- a/doc/basic.md +++ b/doc/basic.md @@ -75,7 +75,7 @@ typedef struct { uint16_t frame_length; /* Additional header bytes, to prepend packed data before transmission - * This must be minimum 6 bytes to accomodate CSP 2.0. But some implementations + * This must be minimum 6 bytes to accommodate CSP 2.0. But some implementations * require much more scratch working area for encryption for example. * * Ultimately after csp_id_pack() this area will be filled with the CSP header @@ -224,7 +224,7 @@ time). > `static` routing table has the > fastest lookup, but requires more setup. > - cidr (Classless Inter-Domain Routing): supports a one-to-many -> mapping, meaning routes can be configued for a range of +> mapping, meaning routes can be configured for a range of > destination addresses. The `cidr` is > a bit slower for lookup, but simple to setup. From 5ca9049ba7d257dcc8ad86cb894ea8ac1a997ae9 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:01:19 +0900 Subject: [PATCH 206/348] contrib: macosx: pthread_queue: Fix typo Fix "Nofify" to "Notify". Signed-off-by: Gaetan Perrot --- contrib/macosx/pthread_queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/macosx/pthread_queue.c b/contrib/macosx/pthread_queue.c index eb242bf85..374b82676 100644 --- a/contrib/macosx/pthread_queue.c +++ b/contrib/macosx/pthread_queue.c @@ -93,7 +93,7 @@ int pthread_queue_enqueue(pthread_queue_t * queue, const void * value, uint32_t queue->in = (queue->in + 1) % queue->size; pthread_mutex_unlock(&(queue->mutex)); - /* Nofify blocked threads */ + /* Notify blocked threads */ pthread_cond_broadcast(&(queue->cond_empty)); return PTHREAD_QUEUE_OK; @@ -139,7 +139,7 @@ int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) queue->out = (queue->out + 1) % queue->size; pthread_mutex_unlock(&(queue->mutex)); - /* Nofify blocked threads */ + /* Notify blocked threads */ pthread_cond_broadcast(&(queue->cond_full)); return PTHREAD_QUEUE_OK; From 88bb6deeb2207343e2851bcec970c2ae17cd9f3b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:01:41 +0900 Subject: [PATCH 207/348] doc: git-commit: Fix typo Fix "varaible" to "variable". Fix "underlaying" to "underlying". Signed-off-by: Gaetan Perrot --- doc/git-commit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/git-commit.md b/doc/git-commit.md index 8671bf32e..d0c25b2c9 100644 --- a/doc/git-commit.md +++ b/doc/git-commit.md @@ -70,7 +70,7 @@ These are examples taken from our own commit hisotry. cmake: Fix python binding option, change to py3 The previous option name for the python bindings, - 'enable-python3-bindings' is not a valid varaible name in CMAKE. + 'enable-python3-bindings' is not a valid variable name in CMAKE. Also only Python3 is supported forwards so the CMake package Python3 is now used. @@ -90,7 +90,7 @@ These are examples taken from our own commit hisotry. All RX functions need to check for overflow of the packet data field. All TX functions that is askd to transmit a csp packet larger than their - underlaying layer cannot handle, should drop the packet. + underlying layer cannot handle, should drop the packet. In-the field MTU analysis is done by sending larger and larger ping packets over the network to determine what the end-to-end MTU is. From eb51d9752d98f967f4fd6e29add6b1423d5550b4 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:02:14 +0900 Subject: [PATCH 208/348] doc: memory: Fix typo Fix "initiallization" to "initialization". Signed-off-by: Gaetan Perrot --- doc/memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/memory.md b/doc/memory.md index 6f7381a7a..f18c92092 100644 --- a/doc/memory.md +++ b/doc/memory.md @@ -9,7 +9,7 @@ allocation during initialization, where all structures are allocated: port tables, connection pools, buffer pools, message queues, semaphores, tasks, etc. -Once the initiallization is complete, there are only a few functions +Once the initialization is complete, there are only a few functions that uses dynamic allocation, such as: - `csp_sfp_recv()` - sending larger memory chunks than can fit into a From e935fced5d81c180f8f8c1dea9b77ea4df9ea475 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:02:32 +0900 Subject: [PATCH 209/348] doc: outflow: Fix typo Fix "perfoms" to "performs". Signed-off-by: Gaetan Perrot --- doc/outflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/outflow.md b/doc/outflow.md index 2c6ac8fe8..70295d32e 100644 --- a/doc/outflow.md +++ b/doc/outflow.md @@ -16,7 +16,7 @@ copies id to packet csp_send_direct() ----------------- called by: send, sendto, router, rdp - perfoms outgoing routing, selects interface + performs outgoing routing, selects interface csp_send_direct_iface() ----------------------- From 7fc66847deef71abef6fcdd89944cd12f306c1a5 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:03:18 +0900 Subject: [PATCH 210/348] doc: tunnel: Fix typo Fix "tunned" to "tunnel". Signed-off-by: Gaetan Perrot --- doc/tunnel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tunnel.md b/doc/tunnel.md index fab035bfa..ba34fcf4e 100644 --- a/doc/tunnel.md +++ b/doc/tunnel.md @@ -8,7 +8,7 @@ The usage is best explained using a little example. So lets start with a satelli 0/8 satellite bus (node id 0-63) 64/8 mission control bus (node id 64-127) -Let's say we wish to create an encrypted tunned between these two. We define a new "open" network to sit +Let's say we wish to create an encrypted tunnel between these two. We define a new "open" network to sit between the satellite and the ground station 128/8 open / insecure transport network (128-195) From 83be973ce9a8ee006e0fc887cf87d90c6fe2702c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:03:45 +0900 Subject: [PATCH 211/348] include: csp_yaml: Fix typo Fix "overriden" to "overridden". Signed-off-by: Gaetan Perrot --- include/csp/csp_yaml.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/csp/csp_yaml.h b/include/csp/csp_yaml.h index bc0bcb84c..25d1a169c 100644 --- a/include/csp/csp_yaml.h +++ b/include/csp/csp_yaml.h @@ -13,7 +13,7 @@ extern "C" { * Setup the CSP stack based on a yaml configuration file * * @param[in] filename YAML configuration file. - * @param[in] dfl_addr The default interface address can be overriden + * @param[in] dfl_addr The default interface address can be overridden * by passing a pointer to an integer higher than zero. * The default interface address can be read back using the * same integer, if its set to zero. Passing NULL to From b3d3606c296818babe51cbed9ec18cdde3a56e80 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:04:23 +0900 Subject: [PATCH 212/348] contrib: zephyr: samples: server-client: Fix typo Fix "timout" to "timeout". Signed-off-by: Gaetan Perrot --- contrib/zephyr/samples/server-client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index 67a9d8daa..9dd1fb25a 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -49,7 +49,7 @@ void server(void) { continue; } - /* Read packets on connection, timout is 100 mS */ + /* Read packets on connection, timeout is 100 mS */ csp_packet_t *packet; while ((packet = csp_read(conn, 50)) != NULL) { switch (csp_conn_dport(conn)) { From 7d5df8dc6182675a3501e732f698a8efbeb9bac3 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:17:21 +0900 Subject: [PATCH 213/348] interfaces: csp_if_tun: Fix typo Fix "Incomming" to "Incoming". Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index bd825a4c5..83454d706 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -39,7 +39,7 @@ static int csp_if_tun_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packe if (packet->id.dst == ifconf->tun_src) { /** - * Incomming tunnel packet + * Incoming tunnel packet */ //csp_hex_dump("incoming packet", packet->data, packet->length); From 6bcb903acc6411524f58e4415916d2cd5c91d59c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:17:45 +0900 Subject: [PATCH 214/348] arch: posix: pthread_queue: Fix typo Fix "Nofify" to "Notify". Signed-off-by: Gaetan Perrot --- src/arch/posix/pthread_queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index ce35cf414..213ed01dc 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -137,7 +137,7 @@ int pthread_queue_enqueue(pthread_queue_t * queue, const void * value, uint32_t pthread_mutex_unlock(&(queue->mutex)); if (ret == PTHREAD_QUEUE_OK) { - /* Nofify blocked threads */ + /* Notify blocked threads */ pthread_cond_broadcast(&(queue->cond_empty)); } @@ -199,7 +199,7 @@ int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) pthread_mutex_unlock(&(queue->mutex)); if (ret == PTHREAD_QUEUE_OK) { - /* Nofify blocked threads */ + /* Notify blocked threads */ pthread_cond_broadcast(&(queue->cond_full)); } From 54902b7d0cf72db24942c371c011c9a7109e4628 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:18:03 +0900 Subject: [PATCH 215/348] licence: Fix typo Fix "Contributers" to "Contributors". Signed-off-by: Gaetan Perrot --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 832670406..0e818d622 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2009-2021 CSP Contributers (see AUTHORS file) +Copyright (c) 2009-2021 CSP Contributors (see AUTHORS file) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c3b82aec679e49bdc7a2f211458eaa92c60d49f8 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:18:23 +0900 Subject: [PATCH 216/348] authors: Fix typo Fix "Contributers" to "Contributors". Signed-off-by: Gaetan Perrot --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index d24ee85f2..de660085b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,7 +7,7 @@ Author: Johan was the inventor of CSP in 2008 and is still the primary developer in 2020 -Contributers: +Contributors: Jeppe Ledet-Pedersen company: GomSpace From bebd05207e30799c4ea6894fb443d53fbd00aafe Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 20:18:39 +0900 Subject: [PATCH 217/348] changelog: Fix typo Fix "leightweight" to "lightweight". Fix "instaed" to "instead". Fix "connecton" to "connection". Fix "promiscous" to "promiscuous". Signed-off-by: Gaetan Perrot --- CHANGELOG | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 28763aeed..dac9ac560 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -36,11 +36,11 @@ libcsp 2.0, 19-04-2024 - api: csp_init(): Now a void function, because allocation cannot fail - api: python bindings: Refreshed to new 2.0 api, builds with meson too - api: Interface names are now case sensitive (this is faster and avoids pulling in _ctypes_ and saves 340 bytes of flash) -- api: csp_socket(): is replaced by simply `csp_socket_t my_sock;` static allocation. This is very leightweight +- api: csp_socket(): is replaced by simply `csp_socket_t my_sock;` static allocation. This is very lightweight - api: csp_bind_callback(): instead of using a socket, and binding that socket to a port. - api: new debug API. CSP does not print error messages any longer. Instead a new series of error counters has been added and a new error code system. There are flags to enable the old level 4 and 5 debug to stdout with printf. -- api: csp reboot and shutdown are now implemented using hooks instaed of callback functions (see hooks.md) +- api: csp reboot and shutdown are now implemented using hooks instead of callback functions (see hooks.md) - fix: router counts incoming packets before deduplication - fix: rdp: Fix ack_delay_count off by one error - fix: rtable cidr: continue parsing, even if an unknown interface is found @@ -61,7 +61,7 @@ libcsp 1.6, 16-04-2020 - Added support for timestamps in logs by setting CSP_DEBUG_TIMESTAMP (uses csp_clock_get_time()). - Renamed mac to via (structs, functions, examples and documentation) - RDP: Ensure connection is kept in CLOSE_WAIT for period of time. In some cases, the connection would switch to CLOSED immediately. -- RDP: Fixed connection leak, if a RST segment was received on a closed connecton. +- RDP: Fixed connection leak, if a RST segment was received on a closed connection. - RDP: Ensure connection is closed from both userspace and protocol, before closing completely (preventing undetermined behaviour). - RDP: Added support for fast close of a connection (skipping the CLOSE_WAIT period), but only if both ends agree on close. - RDP: Fixed issue "Possible bug in RDP TX timeout" (#109), see issue on github for further details. @@ -204,7 +204,7 @@ libcsp 1.0, 24-10-2011 ---------------------- - First official release - New: CSP 32-bit header 1.0 -- Features: Network Router with promiscous mode, broadcast and QoS +- Features: Network Router with promiscuous mode, broadcast and QoS - Features: Connection-oriented transport protocol w. flow-control - Features: Connection-less "UDP" like transport - Features: Encryption, Authentication and message check From 0535533302605fd6d03909841024b3b9b55e6886 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 10 Mar 2025 13:37:44 +0900 Subject: [PATCH 218/348] example: python: client: Fix typo Fix "recieving" to "receiving". Signed-off-by: Gaetan Perrot --- examples/python_bindings_example_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_bindings_example_client.py b/examples/python_bindings_example_client.py index ef39861be..ec66496df 100644 --- a/examples/python_bindings_example_client.py +++ b/examples/python_bindings_example_client.py @@ -106,6 +106,6 @@ def get_options(): # 10 - dest port # 1000 - timeout ms # outbuf - outgoing data (request) - # inbuf - buffer provided for recieving data (reply) + # inbuf - buffer provided for receiving data (reply) libcsp.transaction(0, options.server_address, 10, 1000, outbuf, inbuf) print (" got reply from server [%s]" % (''.join('{:02x}'.format(x) for x in inbuf))) From c4777b441929fddac815f5c7acdad3eca7648c8a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 10 Mar 2025 13:38:17 +0900 Subject: [PATCH 219/348] contrib: windows: uart_windows: Fix typo Fix "gettings" to "getting". Signed-off-by: Gaetan Perrot --- contrib/windows/usart_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/windows/usart_windows.c b/contrib/windows/usart_windows.c index ce0f9cd4e..bfc52ab2e 100644 --- a/contrib/windows/usart_windows.c +++ b/contrib/windows/usart_windows.c @@ -69,7 +69,7 @@ static int setPortTimeouts(csp_usart_fd_t fd) { COMMTIMEOUTS timeouts = {0}; if (!GetCommTimeouts(fd, &timeouts)) { - csp_print("Error gettings current timeout settings, error: %lu\n", GetLastError()); + csp_print("Error getting current timeout settings, error: %lu\n", GetLastError()); return CSP_ERR_INVAL; } From 886a93e127ba44fedbe6805ca593ee32e37ce99d Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 10 Mar 2025 13:38:50 +0900 Subject: [PATCH 220/348] contrib: drivers: can: Fix typo Fix "configration" to "configuration". Fix "fuctions" to "functions". Fix "futher" to "further". Signed-off-by: Gaetan Perrot --- contrib/drivers/can/csp_driver_can.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/drivers/can/csp_driver_can.c b/contrib/drivers/can/csp_driver_can.c index 78a1770f9..9e9f4e106 100644 --- a/contrib/drivers/can/csp_driver_can.c +++ b/contrib/drivers/can/csp_driver_can.c @@ -47,7 +47,7 @@ static StackType_t can_task_stack[500]; static TaskHandle_t can_task_handle; #endif -/** Driver configration */ +/** Driver configuration */ static struct mcan_s { can_mode_e mode; uint32_t id; @@ -162,10 +162,10 @@ int csp_can_tx_frame(void *driver_data, uint32_t id, const uint8_t * data, uint8 * We could go for async transmission with software queues, this will allow for larger amounts * of data to be queued. However in this case, simplicity is chosen over performance. * When the TX FIFO is full, we ask the task to sleep a tick. - * Usually data heavy fuctions are running in their separate tasks anyways. + * Usually data heavy functions are running in their separate tasks anyways. * A CAN frame is 96 bits and is transmitted within 96 us. * A tick period can vary from 1 to 10 ms (typically) - * 3 retries have been chosen because a futher dealy than this would almost certainly be + * 3 retries have been chosen because a further dealy than this would almost certainly be * due to an error. */ int attempts = 3; From d1f913b1938b0c434d89a506cdc4bfb3b5a6c926 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Wed, 12 Feb 2025 12:06:24 +0100 Subject: [PATCH 221/348] Fixed compilation in release mode Fails due to warning about unused variable --- src/interfaces/csp_if_zmqhub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 5e6a56e44..b1231baec 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -252,7 +252,7 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr assert(ret == 0); ret = pthread_create(&drv->rx_thread, &attributes, csp_zmqhub_task, drv); assert(ret == 0); - + (void)ret; /* Register interface */ csp_iflist_add(&drv->iface); @@ -338,7 +338,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add assert(ret == 0); ret = zmq_connect(drv->subscriber, sub); assert(ret == 0); - + (void)ret; if (promisc) { From 8252c70991f97515ec49bc7319a6afd9311508db Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sun, 9 Mar 2025 13:24:38 +0100 Subject: [PATCH 222/348] build: make static/shared lib. build optional Add an option to the CMake build process to build either a static or shared library and default to shared library for GNU/Linux. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86dff82c5..8a3ed3b98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,9 @@ cmake_minimum_required(VERSION 3.20) project(CSP VERSION 2.1) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(BUILD_SHARED_LIBS ON) + set(DEFAULT_BUILD_SHARED_LIBS ON) endif() +option(BUILD_SHARED_LIBS "Build using shared libraries" ${DEFAULT_BUILD_SHARED_LIBS}) add_library(csp) set_target_properties(csp PROPERTIES C_STANDARD 11) From 133fe30416c6460cc946e8e744f396dbe29ff23b Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sun, 9 Mar 2025 15:05:15 +0100 Subject: [PATCH 223/348] posix: fix cleanup after partial queue creation Currently, when during queue creation the internal initialization fails, it might not clean up properly. This change set carefully steps through each initialization and cleans up all previous steps if one fails. --- src/arch/posix/pthread_queue.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index 213ed01dc..6c450c266 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -62,7 +62,24 @@ pthread_queue_t * pthread_queue_create(int length, size_t item_size) { q->items = 0; q->in = 0; q->out = 0; - if (pthread_mutex_init(&(q->mutex), NULL) || init_cond_clock_monotonic(&(q->cond_full)) || init_cond_clock_monotonic(&(q->cond_empty))) { + + int ret_val = pthread_mutex_init(&(q->mutex), NULL); + if (ret_val == 0) { /* Proceed? */ + ret_val = init_cond_clock_monotonic(&(q->cond_full)); + if (ret_val != 0) { + (void)pthread_mutex_destroy(&(q->mutex)); /* Cleanup */ + } + } + if (ret_val == 0) { /* Proceed? */ + ret_val = init_cond_clock_monotonic(&(q->cond_empty)); + if (ret_val != 0) { + /* Cleanup */ + (void)pthread_mutex_destroy(&(q->mutex)); + (void)pthread_cond_destroy(&(q->cond_full)); + } + } + if (ret_val != 0) { + /* Cleanup */ free(q->buffer); free(q); q = NULL; From bb2b82f02c17ff3bb4f34d5d19ac1bf5e2145ae5 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 20 Jul 2025 12:22:35 +0900 Subject: [PATCH 224/348] interfaces: csp_if_tun: replace csp_buffer_get_always() Avoid using csp_buffer_get_always() in non-critical path. Use csp_buffer_get(0) instead and handle NULL return safely. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_tun.c b/src/interfaces/csp_if_tun.c index 83454d706..c1660bed3 100644 --- a/src/interfaces/csp_if_tun.c +++ b/src/interfaces/csp_if_tun.c @@ -30,7 +30,7 @@ static int csp_if_tun_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packe csp_if_tun_conf_t * ifconf = iface->driver_data; /* Allocate new frame */ - csp_packet_t * new_packet = csp_buffer_get_always(); + csp_packet_t * new_packet = csp_buffer_get(0); if (new_packet == NULL) { csp_buffer_free(packet); return CSP_ERR_NONE; From 997d3c973dc80b56f37b872671b3ad9362143451 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 31 Jan 2025 15:44:35 +0900 Subject: [PATCH 225/348] contrib: zephyr: avoid csp_buffer_get_always() The sample now uses csp_buffer_get(0) instead of csp_buffer_get_always(), which is not recommended in application-level code. Signed-off-by: Gaetan Perrot --- contrib/zephyr/samples/server-client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index 9dd1fb25a..a310a168a 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -107,7 +107,7 @@ void client(void) { } /* 2. Get packet buffer for message/data */ - csp_packet_t * packet = csp_buffer_get_always(); + csp_packet_t * packet = csp_buffer_get(0); if (packet == NULL) { /* Could not get buffer element */ LOG_ERR("Failed to get CSP buffer"); From e981c78370777cca5a7a51faf3c505f66ba2c7be Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Wed, 5 Mar 2025 22:28:51 +0100 Subject: [PATCH 226/348] build: warn about and fix missing prototypes Turn on `-Wmissing-prototypes` for all builds and fix all occurrences to improve code safety. --- CMakeLists.txt | 3 ++- examples/csp_bridge_can2udp.c | 1 + examples/csp_client.c | 4 ++-- examples/csp_posix_helper.c | 2 ++ examples/csp_server.c | 6 +++--- examples/csp_server_client.c | 4 ++-- include/csp/csp_hooks.h | 2 +- include/csp/drivers/eth_linux.h | 4 ++-- meson.build | 11 +++++++---- src/csp_bridge.c | 1 + src/csp_buffer.c | 2 +- src/csp_debug.c | 1 + src/csp_hex_dump.c | 5 ++--- src/csp_id.c | 1 + src/csp_iflist.c | 2 +- src/csp_io.c | 1 + src/csp_io.h | 3 --- src/csp_rdp.c | 2 ++ src/csp_rdp_queue.c | 2 ++ src/csp_route.c | 1 + src/csp_rtable_cidr.c | 2 +- src/drivers/can/can_socketcan.c | 5 ++--- src/drivers/eth/eth_linux.c | 7 ++++--- src/interfaces/csp_if_can.c | 10 ++++------ src/interfaces/csp_if_eth.c | 17 ++++++++++------- src/interfaces/csp_if_udp.c | 6 +++--- src/interfaces/csp_if_zmqhub.c | 4 ++-- wscript | 4 +++- 28 files changed, 64 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a3ed3b98..74d5c499e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ if(CSP_POSIX) -Wcast-align -Werror -Wextra + -Wmissing-prototypes -Wpedantic -Wpointer-arith -Wshadow @@ -142,7 +143,7 @@ if(${CSP_ENABLE_PYTHON3_BINDINGS}) find_package(Python3 COMPONENTS Development.Module) if(Python3_Development.Module_FOUND) Python3_add_library(libcsp_py3 MODULE WITH_SOABI src/bindings/python/pycsp.c) - add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-missing-prototypes -Wno-unused-parameter) target_include_directories(libcsp_py3 PUBLIC ${csp_inc}) target_link_libraries(libcsp_py3 PUBLIC csp) else() diff --git a/examples/csp_bridge_can2udp.c b/examples/csp_bridge_can2udp.c index a232d1be1..e5982d495 100644 --- a/examples/csp_bridge_can2udp.c +++ b/examples/csp_bridge_can2udp.c @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/examples/csp_client.c b/examples/csp_client.c index 6d17d2a1f..326258d72 100644 --- a/examples/csp_client.c +++ b/examples/csp_client.c @@ -62,7 +62,7 @@ static struct option long_options[] = { {0, 0, 0, 0} }; -void print_help(void) { +static void print_help(void) { csp_print("Usage: csp_client [options]\n"); if (CSP_HAVE_LIBSOCKETCAN) { csp_print(" -c set CAN device\n"); @@ -86,7 +86,7 @@ void print_help(void) { } } -csp_iface_t * add_interface(enum DeviceType device_type, const char * device_name) +static csp_iface_t * add_interface(enum DeviceType device_type, const char * device_name) { csp_iface_t * default_iface = NULL; diff --git a/examples/csp_posix_helper.c b/examples/csp_posix_helper.c index d1b71c4f2..ca70a4947 100644 --- a/examples/csp_posix_helper.c +++ b/examples/csp_posix_helper.c @@ -1,3 +1,5 @@ +#include "csp_posix_helper.h" + #include #include #include diff --git a/examples/csp_server.c b/examples/csp_server.c index 69c9b7d7f..a6b848fe0 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -32,7 +32,7 @@ enum DeviceType { #define __maybe_unused __attribute__((__unused__)) /* Server task - handles requests from clients */ -void * server(void * param) { +static void * server(void * param) { (void)param; @@ -112,7 +112,7 @@ static struct option long_options[] = { {0, 0, 0, 0} }; -void print_help(void) { +static void print_help(void) { csp_print("Usage: csp_server [options]\n"); if (CSP_HAVE_LIBSOCKETCAN) { csp_print(" -c set CAN device\n"); @@ -135,7 +135,7 @@ void print_help(void) { } } -csp_iface_t * add_interface(enum DeviceType device_type, const char * device_name) +static csp_iface_t * add_interface(enum DeviceType device_type, const char * device_name) { csp_iface_t * default_iface = NULL; diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 2edcbe2bc..3de47da7c 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -22,7 +22,7 @@ static unsigned int server_received = 0; static unsigned int run_duration_in_sec = 3; /* Server task - handles requests from clients */ -void * server(void * param) { +static void * server(void * param) { (void)param; @@ -76,7 +76,7 @@ void * server(void * param) { /* End of server task */ /* Client task sending requests to server task */ -void * client(void * param) { +static void * client(void * param) { (void)param; diff --git a/include/csp/csp_hooks.h b/include/csp/csp_hooks.h index 5e90249eb..7a7ab3182 100644 --- a/include/csp/csp_hooks.h +++ b/include/csp/csp_hooks.h @@ -12,7 +12,7 @@ extern "C" { #endif -void csp_output_hook(csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); +void csp_output_hook(const csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); void csp_input_hook(csp_iface_t * iface, csp_packet_t * packet); void csp_reboot_hook(void); diff --git a/include/csp/drivers/eth_linux.h b/include/csp/drivers/eth_linux.h index 6b71705ae..e0233926a 100644 --- a/include/csp/drivers/eth_linux.h +++ b/include/csp/drivers/eth_linux.h @@ -32,11 +32,11 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int /** * Transmit an CSP ethernet frame * - * @param[in] iface Ethernet interface to use. + * @param[in] driver_data Ethernet interface to use. * @param[in] eth_frame The CSP ethernet frame to transmit. * @return #CSP_ERR_NONE on success, otherwise an error code. */ -int csp_eth_tx_frame(csp_iface_t * iface, csp_eth_header_t * eth_frame); +int csp_eth_tx_frame(void * driver_data, csp_eth_header_t * eth_frame); /** * Posix ethernet RX thread diff --git a/meson.build b/meson.build index ed8764da2..6f738ff18 100644 --- a/meson.build +++ b/meson.build @@ -65,11 +65,12 @@ csp_inc = include_directories('include', 'src') # Default compile options csp_c_args = ['-Wall', '-Wcast-align', + '-Wextra', + '-Wmissing-prototypes', + '-Wpedantic', '-Wpointer-arith', '-Wshadow', - '-Wwrite-strings', - '-Wextra', - '-Wpedantic'] + '-Wwrite-strings'] if cc.get_id() == 'clang' csp_c_args += '-Wno-gnu-zero-variadic-macro-arguments' endif @@ -110,7 +111,9 @@ if get_option('enable_python3_bindings') # dependency() instead pydep = dependency('python3', version : '>=3.5', required : true) py.extension_module('libcsp_py3', 'src/bindings/python/pycsp.c', - c_args : [csp_c_args, '-Wno-unused-parameter'], + c_args : [csp_c_args, + '-Wno-missing-prototypes', + '-Wno-unused-parameter'], dependencies : [csp_dep, pydep], install : true) endif diff --git a/src/csp_bridge.c b/src/csp_bridge.c index 72d0be934..644959944 100644 --- a/src/csp_bridge.c +++ b/src/csp_bridge.c @@ -1,5 +1,6 @@ #include "csp_macro.h" +#include "csp/csp_hooks.h" #include "csp_qfifo.h" #include "csp_io.h" #include "csp_promisc.h" diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 5a1880975..78fd60122 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -23,7 +23,7 @@ void csp_buffer_init(void) { * Chunk of memory allocated for CSP buffers: * This is marked as .noinit, because csp buffers can never be assumed zeroed out * Putting this section in a separate non .bss area, saves some boot time */ - static csp_skbf_t csp_buffer_pool[CSP_BUFFER_COUNT] __noinit; + static csp_skbf_t csp_buffer_pool[CSP_BUFFER_COUNT] __noinit; static csp_static_queue_t csp_buffers_queue __noinit; static char csp_buffer_queue_data[CSP_BUFFER_COUNT * sizeof(csp_skbf_t *)] __noinit; diff --git a/src/csp_debug.c b/src/csp_debug.c index d1113fdd6..acb67c02e 100644 --- a/src/csp_debug.c +++ b/src/csp_debug.c @@ -17,6 +17,7 @@ uint8_t csp_dbg_packet_print; #if (CSP_PRINT_STDIO) #include #include +#include "csp/csp_debug.h" __weak void csp_print_func(const char * fmt, ...) { va_list args; va_start(args, fmt); diff --git a/src/csp_hex_dump.c b/src/csp_hex_dump.c index 0d5d5b58e..fbcaeee1f 100644 --- a/src/csp_hex_dump.c +++ b/src/csp_hex_dump.c @@ -1,10 +1,9 @@ - - #include #include +#include #include -void csp_hex_dump_format(const char * desc, const void * addr, int len, int format) { +static void csp_hex_dump_format(const char * desc, const void * addr, int len, int format) { int i; unsigned char buff[17]; unsigned char * pc = (unsigned char *)addr; diff --git a/src/csp_id.c b/src/csp_id.c index 8ff0cdd9b..726462996 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -7,6 +7,7 @@ #include #include +#include /** * CSP 1.x diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 5dabc66d0..880c4ba21 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -86,7 +86,7 @@ csp_iface_t * csp_iflist_get_by_isdfl(csp_iface_t * ifc) { } -csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc) { +static csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc) { /* Head of list */ if (ifc == NULL) { diff --git a/src/csp_io.c b/src/csp_io.c index 6d3e7616a..66b6fa92b 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/csp_io.h b/src/csp_io.h index 0cbb68895..7e829ee4d 100644 --- a/src/csp_io.h +++ b/src/csp_io.h @@ -1,5 +1,3 @@ - - #pragma once #include @@ -11,6 +9,5 @@ * @return #CSP_ERR_NONE on success, otherwise an error code. * */ - void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * routed_from); void csp_send_direct_iface(const csp_id_t* idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 038d5b339..86bf2f615 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -4,6 +4,8 @@ * delayed acknowledgments, to improve performance over half-duplex links. */ +#include "csp_rdp.h" + #include "csp_rdp_queue.h" #include diff --git a/src/csp_rdp_queue.c b/src/csp_rdp_queue.c index 70ce3cef1..08567c1ec 100644 --- a/src/csp_rdp_queue.c +++ b/src/csp_rdp_queue.c @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/src/csp_route.c b/src/csp_route.c index c8fe0c13b..b9f8f4fc1 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -17,6 +17,7 @@ #include "csp_dedup.h" #include "csp_rdp.h" #include +#include #include #include "csp_macro.h" diff --git a/src/csp_rtable_cidr.c b/src/csp_rtable_cidr.c index 438fbb269..297e00d82 100644 --- a/src/csp_rtable_cidr.c +++ b/src/csp_rtable_cidr.c @@ -73,7 +73,7 @@ csp_route_t * csp_rtable_find_route(uint16_t addr) { return NULL; } -int csp_rtable_set_internal(uint16_t address, uint16_t netmask, csp_iface_t * ifc, uint16_t via) { +static int csp_rtable_set_internal(uint16_t address, uint16_t netmask, csp_iface_t * ifc, uint16_t via) { /* First see if the entry exists */ csp_route_t * entry = csp_rtable_find_exact(address, netmask, ifc); diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index ca674289f..a606ee55c 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -121,7 +121,7 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat while (pdata < pend) { int written; - + written = write(ctx->socket, (void *)pdata, length); if (written < 0) { if (errno == ENOBUFS) { @@ -151,8 +151,7 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat return CSP_ERR_NONE; } - -int csp_can_socketcan_set_promisc(const bool promisc, can_context_t * ctx) { +static int csp_can_socketcan_set_promisc(const bool promisc, can_context_t * ctx) { struct can_filter filter = { .can_id = CFP_MAKE_DST(ctx->iface.addr), .can_mask = 0x0000, /* receive anything */ diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 385e12741..1248210cc 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -3,6 +3,8 @@ #warning CYGWIN: ethernet not implemented - libpcap can be used if needed #else // !__CYGWIN__ +#include + #include #include @@ -32,7 +34,7 @@ typedef struct { struct ifreq if_idx; } eth_context_t; -int csp_eth_tx_frame(void * driver_data, csp_eth_header_t *eth_frame) { +int csp_eth_tx_frame(void * driver_data, csp_eth_header_t * eth_frame) { const eth_context_t * ctx = (eth_context_t*)driver_data; @@ -78,7 +80,7 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int if (ctx == NULL) { return CSP_ERR_NOMEM; } - + strncpy(ctx->name, ifname, sizeof(ctx->name) - 1); ctx->ifdata.iface.name = ctx->name; ctx->ifdata.tx_func = &csp_eth_tx_frame; @@ -96,7 +98,6 @@ int csp_eth_init(const char * device, const char * ifname, int mtu, unsigned int return CSP_ERR_INVAL; } - /** * TX SOCKET */ diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index e0b951d61..bcc5602df 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -1,5 +1,3 @@ - - #include #include @@ -43,7 +41,7 @@ enum cfp_frame_t { CFP_MORE = 1 }; -int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t dlc, int * task_woken) { +static int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t dlc, int * task_woken) { /* Test: random packet loss */ // if (0) { @@ -162,7 +160,7 @@ int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t return CSP_ERR_NONE; } -int csp_can1_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { +static int csp_can1_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { (void)from_me; /* Avoid compiler warnings about unused parameter */ /* Loopback */ @@ -259,7 +257,7 @@ int csp_can1_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int fr return CSP_ERR_NONE; } -int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t dlc, int * task_woken) { +static int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t dlc, int * task_woken) { csp_can_interface_data_t * ifdata = iface->interface_data; @@ -363,7 +361,7 @@ int csp_can2_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, uint8_t return CSP_ERR_NONE; } -int csp_can2_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { +static int csp_can2_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { /* Avoid compiler warnings about unused parameter */ (void)via; (void)from_me; diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index cc4f262b0..5821bb64d 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -34,9 +34,9 @@ bool csp_eth_pack_header(csp_eth_header_t * buf, return true; } -bool csp_if_eth_unpack_header(csp_eth_header_t * buf, - uint32_t * packet_id, - uint16_t * seg_size, uint16_t * packet_length) { +static bool csp_if_eth_unpack_header(csp_eth_header_t * buf, + uint32_t * packet_id, + uint16_t * seg_size, uint16_t * packet_length) { if (packet_id == NULL) return false; if (seg_size == NULL) return false; @@ -68,16 +68,18 @@ static size_t arp_used = 0; static arp_list_entry_t * arp_list = 0; -arp_list_entry_t * arp_alloc(void) { - +static arp_list_entry_t * arp_alloc(void) { + if (arp_used >= ARP_MAX_ENTRIES) { return 0; - } + } return &(arp_array[arp_used++]); } -void arp_print(void) +// FIXME: Function unused. Remove it? +#if 0 +static void arp_print(void) { csp_print("ARP CSP MAC\n"); for (arp_list_entry_t * arp = arp_list; arp; arp = arp->next) { @@ -88,6 +90,7 @@ void arp_print(void) } csp_print("\n"); } +#endif void csp_eth_arp_set_addr(uint8_t * mac_addr, uint16_t csp_addr) { diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 7f823e8c6..518edbc6e 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -38,7 +38,7 @@ static int csp_if_udp_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packe return CSP_ERR_NONE; } -int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { +static int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { (void)unused; /* Avoid compiler warnings about unused parameter */ csp_packet_t * packet = csp_buffer_get(0); @@ -49,7 +49,7 @@ int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { /* Setup RX frame to point to ID */ int header_size = csp_id_setup_rx(packet); int received_len = recvfrom(sockfd, (char *)packet->frame_begin, sizeof(packet->data) + header_size, MSG_WAITALL, NULL, NULL); - + if (received_len < header_size) { csp_buffer_free(packet); return CSP_ERR_NOMEM; @@ -68,7 +68,7 @@ int csp_if_udp_rx_work(int sockfd, size_t unused, csp_iface_t * iface) { return CSP_ERR_NONE; } -void * csp_if_udp_rx_loop(void * param) { +static void * csp_if_udp_rx_loop(void * param) { csp_iface_t * iface = param; csp_if_udp_conf_t * ifconf = iface->driver_data; diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index b1231baec..7f9dea68a 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -76,7 +76,7 @@ void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen) * @param packet Packet to transmit * @return 1 if packet was successfully transmitted, 0 on error */ -int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t * packet, int __maybe_unused from_me) { +static int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t * packet, int __maybe_unused from_me) { zmq_driver_t * drv = iface->driver_data; @@ -99,7 +99,7 @@ int csp_zmqhub_tx(csp_iface_t * iface, uint16_t __maybe_unused via, csp_packet_t return CSP_ERR_NONE; } -void * csp_zmqhub_task(void * param) { +static void * csp_zmqhub_task(void * param) { zmq_driver_t * drv = param; csp_packet_t * packet; diff --git a/wscript b/wscript index a530dd992..10cdab6ad 100644 --- a/wscript +++ b/wscript @@ -84,6 +84,7 @@ def configure(ctx): "-Wcast-align", "-Werror", "-Wextra", + "-Wmissing-prototypes", "-Wpedantic", "-Wpointer-arith", "-Wshadow", @@ -243,7 +244,8 @@ def build(ctx): use=['csp_shlib'], pytest_path=[ctx.path.get_bld()]) - ctx.env.append_value('CFLAGS', ["-Wno-unused-parameter"]) + ctx.env.append_value('CFLAGS', ["-Wno-missing-prototypes", + "-Wno-unused-parameter"]) if ctx.env.ENABLE_EXAMPLES: ctx.objects(source='examples/csp_posix_helper.c', From 3e13fcd1bb3cdcf4dbff9a8c6288ad86377ac281 Mon Sep 17 00:00:00 2001 From: 6arms1leg Date: Sun, 20 Jul 2025 11:29:29 +0200 Subject: [PATCH 227/348] Fix compiler warnings ... about left-over unused parameters. --- include/csp/csp_rtable.h | 22 +++++++++++++++++++--- src/csp_conn.c | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/csp/csp_rtable.h b/include/csp/csp_rtable.h index 9270b99c6..57c383fd2 100644 --- a/include/csp/csp_rtable.h +++ b/include/csp/csp_rtable.h @@ -73,9 +73,25 @@ int csp_rtable_load(const char * rtable); int csp_rtable_check(const char * rtable); #else -inline int csp_rtable_save(char * buffer, size_t buffer_size) { return CSP_ERR_NOSYS; } -inline int csp_rtable_load(const char * rtable) { return CSP_ERR_NOSYS; } -inline int csp_rtable_check(const char * rtable) { return CSP_ERR_NOSYS; } +inline int csp_rtable_save(char * buffer, size_t buffer_size) { + /* Avoid compiler warnings about unused parameter */ + (void)buffer; + (void)buffer_size; + + return CSP_ERR_NOSYS; +} + +inline int csp_rtable_load(const char * rtable) { + (void)rtable; /* Avoid compiler warnings about unused parameter */ + + return CSP_ERR_NOSYS; +} + +inline int csp_rtable_check(const char * rtable) { + (void)rtable; /* Avoid compiler warnings about unused parameter */ + + return CSP_ERR_NOSYS; +} #endif /** diff --git a/src/csp_conn.c b/src/csp_conn.c index ef690f0b5..74d6b9d1e 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -211,6 +211,7 @@ int csp_close(csp_conn_t * conn) { } int csp_conn_close(csp_conn_t * conn, uint8_t closed_by) { + (void)closed_by; /* Avoid compiler warnings about unused parameter */ if (conn == NULL) { return CSP_ERR_NONE; @@ -409,6 +410,8 @@ const csp_conn_t * csp_conn_get_array(size_t * size) { } bool csp_conn_is_active(csp_conn_t *conn) { + (void)conn; /* Avoid compiler warnings about unused parameter */ + #if (CSP_USE_RDP) if ((conn->idin.flags & CSP_FRDP) || (conn->idout.flags & CSP_FRDP)) { /* This is for sure an RDP connection */ From b54956d6cba1727f42e4c9b51edeb495a2e15d82 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Mar 2025 18:58:30 +0900 Subject: [PATCH 228/348] workflow: codespell: Add codespell github workflow Add GitHub Actions workflow for spelling check using Codespell. Set up a workflow to automatically check for spelling mistakes in the codebase using Codespell. Configured Codespell to skip waf files. Uses quiet-level=2 to minimize false positives and ensure only critical spelling mistakes are reported. Signed-off-by: Gaetan Perrot Update codespell.yml --- .github/workflows/codespell.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/codespell.yml diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 000000000..64d3114ca --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,20 @@ +name: Codespell Check + +on: + pull_request: + push: + +jobs: + spellcheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install codespell + run: pip install codespell + + - name: Run codespell + run: | + codespell --skip="waf" --quiet-level=2 From c7d598c05dd20421c2a06236dfd6c937bef7fb5f Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 10 Mar 2025 13:50:34 +0900 Subject: [PATCH 229/348] workflow: codespell: Add "re-use" to the ignore list Created a `.codespell-ignore` file and added "re-use" to the ignore list to prevent false positives. The word "re-use" is valid and should not be corrected to "reuse". Added it to the ignore list to prevent false positives. Signed-off-by: Gaetan Perrot --- .codespell-ignore | 1 + .github/workflows/codespell.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .codespell-ignore diff --git a/.codespell-ignore b/.codespell-ignore new file mode 100644 index 000000000..773f808d4 --- /dev/null +++ b/.codespell-ignore @@ -0,0 +1 @@ +re-use diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 64d3114ca..962d94094 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -17,4 +17,4 @@ jobs: - name: Run codespell run: | - codespell --skip="waf" --quiet-level=2 + codespell --skip="waf" --ignore-words=.codespell-ignore --quiet-level=2 From fbb131c6a89d77a0777692e016d91352b0260fa2 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 20 Jul 2025 12:04:57 +0900 Subject: [PATCH 230/348] samples: simple-send-usart: avoid csp_buffer_get_always() The sample now uses csp_buffer_get(0) instead of csp_buffer_get_always(), which is not recommended in application-level code. This change improves robustness by handling the case where no buffer is available, following the guidance from issue #866 and #864. Signed-off-by: Gaetan Perrot --- samples/posix/simple-send-usart/src/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/samples/posix/simple-send-usart/src/main.c b/samples/posix/simple-send-usart/src/main.c index bb53f8bd1..90197e754 100644 --- a/samples/posix/simple-send-usart/src/main.c +++ b/samples/posix/simple-send-usart/src/main.c @@ -23,7 +23,7 @@ int main(int argc, char * argv[]) int ret; /* init */ - csp_init(); + csp_init(); /* open */ ret = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, CLIENT_ADDR, &iface); @@ -41,7 +41,12 @@ int main(int argc, char * argv[]) } /* prepare data */ - packet = csp_buffer_get_always(); + packet = csp_buffer_get(0); + if (packet == NULL) { + csp_print("Failed to get buffer\n"); + csp_close(conn); + return 1; + } memcpy(packet->data, "abc", 3); packet->length = 3; From 564e9d863b4a4e78ed25aa9031bdd349086cc80c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 20 Jul 2025 12:07:49 +0900 Subject: [PATCH 231/348] samples: simple-send-can: avoid csp_buffer_get_always() The sample now uses csp_buffer_get(0) instead of csp_buffer_get_always(), which is not recommended in application-level code. This change improves robustness by handling the case where no buffer is available, following the guidance from issue #866 and #864. Signed-off-by: Gaetan Perrot --- samples/posix/simple-send-canbus/src/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/posix/simple-send-canbus/src/main.c b/samples/posix/simple-send-canbus/src/main.c index 2149d99f5..df6173ff5 100644 --- a/samples/posix/simple-send-canbus/src/main.c +++ b/samples/posix/simple-send-canbus/src/main.c @@ -34,7 +34,12 @@ int main(int argc, char * argv[]) } /* prepare data */ - packet = csp_buffer_get_always(); + packet = csp_buffer_get(0); + if (packet == NULL) { + csp_print("Failed to get buffer\n"); + csp_close(conn); + return 1; + } memcpy(packet->data, "abc", 3); packet->length = 3; From e9deea0e7fb575e105570fb7702598a494e10c03 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 20 Jul 2025 13:12:04 +0900 Subject: [PATCH 232/348] samples: hmac: avoid csp_buffer_get_always() The sample now uses csp_buffer_get(0) instead of csp_buffer_get_always(), which is not recommended in application-level code. This change improves robustness by handling the case where no buffer is available, following the guidance from issue #866 and #864. Signed-off-by: Gaetan Perrot --- samples/posix/hmac/src/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/posix/hmac/src/main.c b/samples/posix/hmac/src/main.c index 0edc3f269..663db2664 100644 --- a/samples/posix/hmac/src/main.c +++ b/samples/posix/hmac/src/main.c @@ -9,7 +9,10 @@ int main(int argc, char * argv[]) csp_init(); - packet = csp_buffer_get_always(); + packet = csp_buffer_get(0); + if (packet == NULL) { + return 1; + } csp_id_prepend(packet); From 678f6f8cadaeebf04af37e40995b700b9b6d111d Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 27 May 2025 09:03:31 +0900 Subject: [PATCH 233/348] csp: Remove remaining references to csp_free_resources The function csp_free_resources was removed in commit 18bd994, but its declaration still remained in the public header `csp.h`, along with its associated documentation comment. This commit completes the removal by deleting the obsolete declaration and its documentation, fully eliminating csp_free_resources from the codebase. Signed-off-by: Gaetan Perrot --- doc/api/csp_h.rst | 1 - include/csp/csp.h | 6 ------ 2 files changed, 7 deletions(-) diff --git a/doc/api/csp_h.rst b/doc/api/csp_h.rst index 36a5dae2d..76708da36 100644 --- a/doc/api/csp_h.rst +++ b/doc/api/csp_h.rst @@ -25,7 +25,6 @@ Interface Functions ------------------- .. autocfunction:: csp.h::csp_init -.. autocfunction:: csp.h::csp_free_resources .. autocfunction:: csp.h::csp_get_conf .. autocfunction:: csp.h::csp_id_copy .. autocfunction:: csp.h::csp_accept diff --git a/include/csp/csp.h b/include/csp/csp.h index 8361f79f9..5961ebbcc 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -55,12 +55,6 @@ extern csp_conf_t csp_conf; */ void csp_init(void); -/** - * Free allocated resources in CSP. - * This is intended for testing of CSP, in order to be able re-initialize CSP by calling csp_init() again. - */ -void csp_free_resources(void); - /** * Get a \a read-only reference to the active CSP configuration. * From 3b1a7ac7387fe63cede9a804945c7b77a057c742 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 13 Apr 2025 21:02:07 +0900 Subject: [PATCH 234/348] csp_buffer: Fix pointers in csp_buffer_clone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frame_begin pointer was introduced in commit 5ee2d36, but csp_buffer_clone() was not updated accordingly. As a result, cloned packets would end up with a frame_begin pointer referencing the original packet’s memory, which is incorrect and unsafe. This commit fixes the issue by recalculating frame_begin after copying the packet, ensuring it points to the correct memory area within the new buffer. Signed-off-by: Gaetan Perrot --- src/csp_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 78fd60122..4f055badb 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -159,7 +159,8 @@ csp_packet_t * csp_buffer_clone(const csp_packet_t * packet) { void csp_buffer_copy(const csp_packet_t * src, csp_packet_t * dst) { if ((NULL != src) && (NULL != dst)) { (void)memcpy(dst, src, sizeof(csp_packet_t)); - } + dst->frame_begin = (dst->header + CSP_PACKET_PADDING_BYTES) - (src->data - src->frame_begin); + } } void csp_buffer_refc_inc(void * buffer) { From 859b7af77b3de7bfd6a410655e2c3fd8a270446b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 13 Apr 2025 21:45:35 +0900 Subject: [PATCH 235/348] unittests: Add test to verify frame_begin cloning behavior Add unit tests to verify that the frame_begin pointer in a cloned packet points to the correct memory region within the clone. The tests also confirm that modifying the original packet's data does not affect the cloned packet, ensuring proper memory separation between the two. Signed-off-by: Gaetan Perrot --- unittests/buffer.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/unittests/buffer.c b/unittests/buffer.c index d978020f1..0f98e954e 100644 --- a/unittests/buffer.c +++ b/unittests/buffer.c @@ -1,5 +1,8 @@ #include #include "../include/csp/csp.h" +#include "../include/csp/csp_id.h" + +#define CSP_ID2_HEADER_SIZE 6 /* https://github.com/libcsp/libcsp/issues/734 */ START_TEST(test_alloc_clean_734) @@ -29,6 +32,41 @@ START_TEST(test_alloc_clean_734) } END_TEST +START_TEST(test_clone_frame_begin_fixed) +{ + csp_init(); + + csp_packet_t *src = csp_buffer_get_always(); + ck_assert_ptr_nonnull(src); + + /* Simulate a packet with no header*/ + memcpy(src->data, "hello", 6); + src->length = 6; + + /* Add header to simulate a prepared to send packet */ + csp_id_prepend(src); + + csp_packet_t *clone = csp_buffer_clone(src); + ck_assert_ptr_nonnull(clone); + + /* Verify that the data content is identical */ + ck_assert_mem_eq(clone->frame_begin + CSP_ID2_HEADER_SIZE, src->frame_begin + CSP_ID2_HEADER_SIZE, 6); + + /* Modify source data to verify that pointer not pointing the same area */ + memcpy(src->data, "world", 6); + + /* Check that clone is unaffected by src modification */ + ck_assert_mem_ne(clone->frame_begin + CSP_ID2_HEADER_SIZE, src->frame_begin + CSP_ID2_HEADER_SIZE, src->length); + ck_assert_mem_eq(clone->frame_begin + CSP_ID2_HEADER_SIZE, "hello", 6); + + /* Ensure that frame_begin does NOT point to the same address as the original */ + ck_assert_ptr_ne(clone->frame_begin, src->frame_begin); + + csp_buffer_free(src); + csp_buffer_free(clone); +} +END_TEST + Suite * buffer_suite(void) { Suite *s; @@ -38,6 +76,7 @@ Suite * buffer_suite(void) tc_alloc = tcase_create("allocate"); tcase_add_test(tc_alloc, test_alloc_clean_734); + tcase_add_test(tc_alloc, test_clone_frame_begin_fixed); suite_add_tcase(s, tc_alloc); return s; From 1e82efde55d84fdbf056c1dff95c2f5110b14692 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 1 Apr 2025 19:03:53 +0900 Subject: [PATCH 236/348] csp_iflist: Remove unnecessary 'continue' statement Remove unnecessary 'continue' statement in csp_iflist_get_by_isdfl(). The 'continue' statement after updating 'ifc' in the while loop was redundant and did not affect the flow of the program. Removed for cleaner and more readable code. Signed-off-by: Gaetan Perrot --- src/csp_iflist.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 880c4ba21..dc2a2b3ba 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -78,7 +78,6 @@ csp_iface_t * csp_iflist_get_by_isdfl(csp_iface_t * ifc) { } ifc = ifc->next; - continue; } From 3747c67a3e96afed9e1108247ddc170c97e75813 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 21 Mar 2025 21:45:45 +0900 Subject: [PATCH 237/348] drivers: can: can_socketcan: Fix memory leak Ensure that ctx is properly freed if csp_can_socketcan_set_promisc() fails in csp_can_socketcan_open_and_add_interface. Previously, if the function returned an error, ctx was not cleaned up, leading to a potential memory leak. Signed-off-by: Gaetan Perrot --- src/drivers/can/can_socketcan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index a606ee55c..918c7a4cf 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -241,6 +241,7 @@ int csp_can_socketcan_open_and_add_interface(const char * device, const char * i /* Set filter mode */ if (csp_can_socketcan_set_promisc(promisc, ctx) != CSP_ERR_NONE) { csp_print("%s[%s]: csp_can_socketcan_set_promisc() failed, error: %s\n", __func__, ctx->name, strerror(errno)); + socketcan_free(ctx); return CSP_ERR_INVAL; } From dcb090719d79ea111281fc273fcdc17be992b24b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 21 Mar 2025 22:17:21 +0900 Subject: [PATCH 238/348] drivers: can: can_socketcan: Clean up interface if pthread_create fails If pthread_create() fails in csp_can_socketcan_open_and_add_interface, the interface was already added to CSP but not properly removed, leading to a potential resource leak. This commit calls csp_can_remove_interface() before freeing ctx to ensure a proper cleanup. Signed-off-by: Gaetan Perrot --- src/drivers/can/can_socketcan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index 918c7a4cf..7f799adb7 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -256,7 +256,8 @@ int csp_can_socketcan_open_and_add_interface(const char * device, const char * i /* Create receive thread */ if (pthread_create(&ctx->rx_thread, NULL, socketcan_rx_thread, ctx) != 0) { csp_print("%s[%s]: pthread_create() failed, error: %s\n", __func__, ctx->name, strerror(errno)); - // socketcan_free(ctx); // we already added it to CSP (no way to remove it) + (void)csp_can_remove_interface(&ctx->iface); + socketcan_free(ctx); return CSP_ERR_NOMEM; } From 82933a2ee10f7ee81447e439b47ff744cd30c92c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 1 Apr 2025 12:28:36 +0900 Subject: [PATCH 239/348] interfaces: zmq: Destroy thread attributes after pthread_create Destroy the thread attributes after creation, as the attributes are no longer needed once the thread is created. Kept the assert(ret == 0) pattern for consistency with the existing ZMQ interface code. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_zmqhub.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 7f9dea68a..ff437ed75 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -252,6 +252,8 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr assert(ret == 0); ret = pthread_create(&drv->rx_thread, &attributes, csp_zmqhub_task, drv); assert(ret == 0); + ret = pthread_attr_destroy(&attributes); + assert(ret == 0); (void)ret; /* Register interface */ csp_iflist_add(&drv->iface); @@ -372,6 +374,8 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add assert(ret == 0); ret = pthread_create(&drv->rx_thread, &attributes, csp_zmqhub_task, drv); assert(ret == 0); + ret = pthread_attr_destroy(&attributes); + assert(ret == 0); /* Register interface */ csp_iflist_add(&drv->iface); From 7d7ced87202aaac1ad736ad4ba95916e17331cb1 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 21 Jul 2025 17:42:55 +0900 Subject: [PATCH 240/348] arch: posix: refactor pthread_queue_create error handling Error paths in `pthread_queue_create()` were deeply nested conditions with duplicated cleanup code, making it hard to follow and easy to introduce leaks. Refactor the code to use a linear initialization sequence with goto-based cleanups. Now each failure jumps to a single well-defined cleanup point, ensuring proper resource release, reducing duplication, and improving readability. Signed-off-by: Yasushi SHOJI --- src/arch/posix/pthread_queue.c | 85 ++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/arch/posix/pthread_queue.c b/src/arch/posix/pthread_queue.c index 6c450c266..66b426bf5 100644 --- a/src/arch/posix/pthread_queue.c +++ b/src/arch/posix/pthread_queue.c @@ -52,44 +52,57 @@ static inline int init_cond_clock_monotonic(pthread_cond_t * cond) { pthread_queue_t * pthread_queue_create(int length, size_t item_size) { - pthread_queue_t * q = malloc(sizeof(pthread_queue_t)); - - if (q != NULL) { - q->buffer = malloc(length * item_size); - if (q->buffer != NULL) { - q->size = length; - q->item_size = item_size; - q->items = 0; - q->in = 0; - q->out = 0; - - int ret_val = pthread_mutex_init(&(q->mutex), NULL); - if (ret_val == 0) { /* Proceed? */ - ret_val = init_cond_clock_monotonic(&(q->cond_full)); - if (ret_val != 0) { - (void)pthread_mutex_destroy(&(q->mutex)); /* Cleanup */ - } - } - if (ret_val == 0) { /* Proceed? */ - ret_val = init_cond_clock_monotonic(&(q->cond_empty)); - if (ret_val != 0) { - /* Cleanup */ - (void)pthread_mutex_destroy(&(q->mutex)); - (void)pthread_cond_destroy(&(q->cond_full)); - } - } - if (ret_val != 0) { - /* Cleanup */ - free(q->buffer); - free(q); - q = NULL; - } - } else { - free(q); - q = NULL; - } + int ret; + pthread_queue_t * q; + + q = malloc(sizeof(pthread_queue_t)); + if (q == NULL) { + goto out; + } + + q->buffer = malloc(length * item_size); + if (q->buffer == NULL) { + goto free_q; + } + + q->size = length; + q->item_size = item_size; + q->items = 0; + q->in = 0; + q->out = 0; + + ret = pthread_mutex_init(&(q->mutex), NULL); + if (ret != 0) { + goto free_q_buffer; + } + + ret = init_cond_clock_monotonic(&(q->cond_full)); + if (ret != 0) { + goto destroy_mutex; } + ret = init_cond_clock_monotonic(&(q->cond_empty)); + if (ret != 0) { + goto destroy_cond; + } + + return q; + +destroy_cond: + (void)pthread_cond_destroy(&(q->cond_full)); + +destroy_mutex: + (void)pthread_mutex_destroy(&(q->mutex)); + +free_q_buffer: + free(q->buffer); + q->buffer = NULL; + +free_q: + free(q); + q = NULL; + +out: return q; } From d1c414d1a954baafbb626fc621cbc0f08b243c89 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 4 Apr 2025 14:08:38 +0900 Subject: [PATCH 241/348] github: workflow: limit permissions to least required Update GitHub Actions workflows to follow principle of least privilege Default to permissions: read-all in all workflows and then add additional permissions as needed at the job level. Elevated permissions (`pages: write`, `id-token: write`) are now granted only at the job level in the Sphinx docs workflow. This change improves security and aligns with the recommended model since GitHub changed the default token permissions to read-only in February 2023. Signed-off-by: Gaetan Perrot --- .github/workflows/abi-checker.yml | 2 ++ .github/workflows/build-test-freertos.yml | 2 ++ .github/workflows/build-test-python.yml | 2 ++ .github/workflows/build-test-zephyr.yml | 2 ++ .github/workflows/build-test.yml | 2 ++ .github/workflows/codespell.yml | 2 ++ .github/workflows/develop-build-sphinx-docs.yml | 7 +++++++ .github/workflows/gitlint.yml | 2 ++ .github/workflows/linelint.yaml | 2 ++ 9 files changed, 23 insertions(+) diff --git a/.github/workflows/abi-checker.yml b/.github/workflows/abi-checker.yml index 71a5b8c9f..351fd1dbd 100644 --- a/.github/workflows/abi-checker.yml +++ b/.github/workflows/abi-checker.yml @@ -1,5 +1,7 @@ name: ABI Compliance Check on: [pull_request] +permissions: + contents: read jobs: abi-check: strategy: diff --git a/.github/workflows/build-test-freertos.yml b/.github/workflows/build-test-freertos.yml index 56b3b38fb..58db05a3c 100644 --- a/.github/workflows/build-test-freertos.yml +++ b/.github/workflows/build-test-freertos.yml @@ -1,5 +1,7 @@ name: FreeRTOS Build and Test on: [push, pull_request] +permissions: + contents: read jobs: build-freertos: runs-on: ubuntu-latest diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 77ab0c59e..5d069ea87 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -1,5 +1,7 @@ name: Python Bindings on: [push, pull_request] +permissions: + contents: read jobs: build-test-python: strategy: diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 8be29ce5b..efbdd5bfc 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -5,6 +5,8 @@ on: schedule: - cron: '0 10 * * 0' # Run it every Sunday 10am UTC +permissions: + contents: read jobs: build-zephyr: runs-on: ubuntu-24.04 diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index a9719410a..af33bb67d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,5 +1,7 @@ name: Build and Test on: [push, pull_request] +permissions: + contents: read jobs: run-tests: strategy: diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 962d94094..4b21becc7 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -3,6 +3,8 @@ name: Codespell Check on: pull_request: push: +permissions: + contents: read jobs: spellcheck: diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index 5cf48da51..b50c43b49 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -5,10 +5,17 @@ on: push: branches: - develop +permissions: + contents: read jobs: build-docs: if: github.repository_owner == 'libcsp' runs-on: ubuntu-latest + + permissions: + pages: write + id-token: write + steps: - name: Checkout the repository uses: actions/checkout@v4 diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml index c4c47cf7a..eca3b4079 100644 --- a/.github/workflows/gitlint.yml +++ b/.github/workflows/gitlint.yml @@ -2,6 +2,8 @@ name: GitLint on: pull_request +permissions: + contents: read jobs: gitlint: diff --git a/.github/workflows/linelint.yaml b/.github/workflows/linelint.yaml index c337d48ec..536c4ea09 100644 --- a/.github/workflows/linelint.yaml +++ b/.github/workflows/linelint.yaml @@ -2,6 +2,8 @@ name: EOF newline on: pull_request +permissions: + contents: read jobs: linelint: From 2f98eeb5b1624cefb217acf3c7e62ec44481dd80 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 23 Jul 2025 20:44:21 +0900 Subject: [PATCH 242/348] hotfix: restore write access for documentation generation Previous commit removed the write permissions needed for building docs, causing the documentation generation step to fail. This restores write access to the output directory during the doc build. Signed-off-by: Yasushi SHOJI --- .github/workflows/develop-build-sphinx-docs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index b50c43b49..1d7f4e733 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -13,8 +13,7 @@ jobs: runs-on: ubuntu-latest permissions: - pages: write - id-token: write + contents: write steps: - name: Checkout the repository From 61235402c839253c8c3a8ed15602ee9cf51c9b52 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 17 Apr 2025 17:34:03 +0900 Subject: [PATCH 243/348] drivers: can_zephyr: Replace CAN_MAX_DLC by CAN_MAX_DLEN The CAN frame transmit function was using CAN_MAX_DLC to validate the data length, which is incorrect. CAN_MAX_DLC represents the maximum Data Length Code value, not the actual number of bytes allowed in a frame. This could lead to memory corruption if dlc > CAN_MAX_DLEN. Replaced it with CAN_MAX_DLEN, which correctly represents the maximum number of bytes that can be stored in a CAN frame. Signed-off-by: Takuya Sasaki Signed-off-by: Gaetan Perrot --- src/drivers/can/can_zephyr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/can/can_zephyr.c b/src/drivers/can/can_zephyr.c index acc464bf9..9d0b71f56 100644 --- a/src/drivers/can/can_zephyr.c +++ b/src/drivers/can/can_zephyr.c @@ -83,7 +83,7 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat struct can_frame frame = {0}; can_context_t * ctx = driver_data; - if (dlc > CAN_MAX_DLC) { + if (dlc > CAN_MAX_DLEN) { ret = CSP_ERR_INVAL; goto end; } From 6cb73e29f015e39313b1adad51d1529a71fa1bf8 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 17 Apr 2025 17:38:51 +0900 Subject: [PATCH 244/348] drivers: can_zephyr: Drop CAN frames with invalid size This commit addresses an issue where CAN frames with an invalid size field (dlc greater than CAN_MAX_DLEN, 8 bytes for CAN 2.0 frame) are encountered. While invalid values in dlc are not expected when using zephyr's CAN driver, this check is introduced to ensure data integrity and prevent potential security vulnerabilities. The commit drops frames with an invalid size within our user code, providing an additional layer of safety against unexpected or non-conformant behavior. Signed-off-by: Gaetan Perrot --- src/drivers/can/can_zephyr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/drivers/can/can_zephyr.c b/src/drivers/can/can_zephyr.c index 9d0b71f56..db5de1605 100644 --- a/src/drivers/can/can_zephyr.c +++ b/src/drivers/can/can_zephyr.c @@ -60,6 +60,12 @@ static void csp_can_rx_thread(void * arg1, void * arg2, void * arg3) { break; } + /* Drop frames with invalid size field */ + if(frame.dlc > CAN_MAX_DLEN){ + LOG_WRN("[%s] discarding invalid size frame", iface->name); + continue; + } + /* CSP requires extended frame format, drop it. */ if (!(frame.flags & CAN_FRAME_IDE)) { LOG_WRN("[%s] discarding Standard ID frame", iface->name); From b9582fd36184d1d4e3209311941eb16d38aab2b0 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 3 Apr 2025 13:11:09 +0900 Subject: [PATCH 245/348] csp_types: Ensure csp_id_t structure is properly packed The `csp_id_t` structure was not actually packed since the `__packed` macro was not defined in `csp_types.h`. `__packed` is defined in `csp_macro.h`, which is located in the `src/` directory and is not included (nor accessible) from `include/csp/csp_types.h`. As a result, the compiler ignored the packing directive, potentially leading to unexpected padding. Replaced `__packed` with `__attribute__((__packed__))` to explicitly enforce packing. Signed-off-by: Gaetan Perrot --- include/csp/csp_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index d9d4522a6..4f3800987 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -51,14 +51,14 @@ typedef enum { /** CSP identifier/header. */ -typedef struct __packed { +typedef struct { uint8_t pri; uint8_t flags; uint16_t src; uint16_t dst; uint8_t dport; uint8_t sport; -} csp_id_t ; +} __attribute__ ((__packed__)) csp_id_t ; /** @defgroup CSP_HEADER_FLAGS CSP header flags. From 9e70aaebf795b2ff595e90946f9ea73420d2c44c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 6 Feb 2025 23:31:55 +0900 Subject: [PATCH 246/348] sample: udp: Add simple send udp This commit adds a simple sample code `send()`ing a packet via UDP. Signed-off-by: Gaetan Perrot --- samples/posix/CMakeLists.txt | 1 + samples/posix/simple-send-udp/CMakeLists.txt | 3 + samples/posix/simple-send-udp/README.md | 56 ++++++++++++++++++ samples/posix/simple-send-udp/src/main.c | 60 ++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 samples/posix/simple-send-udp/CMakeLists.txt create mode 100644 samples/posix/simple-send-udp/README.md create mode 100644 samples/posix/simple-send-udp/src/main.c diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt index fd4469f2b..d5034bc45 100644 --- a/samples/posix/CMakeLists.txt +++ b/samples/posix/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(simple-send-canbus) add_subdirectory(simple-send-usart) add_subdirectory(hmac) +add_subdirectory(simple-send-udp) diff --git a/samples/posix/simple-send-udp/CMakeLists.txt b/samples/posix/simple-send-udp/CMakeLists.txt new file mode 100644 index 000000000..0bc80ea12 --- /dev/null +++ b/samples/posix/simple-send-udp/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(simple-send-udp ${CSP_SAMPLES_EXCLUDE} src/main.c) +target_include_directories(simple-send-udp PRIVATE ${csp_inc}) +target_link_libraries(simple-send-udp PRIVATE csp) diff --git a/samples/posix/simple-send-udp/README.md b/samples/posix/simple-send-udp/README.md new file mode 100644 index 000000000..0ff664297 --- /dev/null +++ b/samples/posix/simple-send-udp/README.md @@ -0,0 +1,56 @@ +# Simple Send via UDP + +This is a simple sample code to send `"abc"` to a server via the +UDP interface. + +## How to Build + +``` +$ cmake -B builddir +$ ninja -C builddir simple-send-udp csp_server +``` + +## How to Test + +First, you need to run a CSP server: + +``` +$ ./builddir/examples/csp_server csp_server -u "127.0.0.1" -a 1 +Initialising CSP +UDP peer address: 127.0.0.1:1500 (listening on port 1501) +Connection table +[00 0x745a7ae4e920] S:0, 0 -> 0, 0 -> 0 (17) fl 0 +[01 0x745a7ae4ea38] S:0, 0 -> 0, 0 -> 0 (18) fl 0 +[02 0x745a7ae4eb50] S:0, 0 -> 0, 0 -> 0 (19) fl 0 +[03 0x745a7ae4ec68] S:0, 0 -> 0, 0 -> 0 (20) fl 0 +[04 0x745a7ae4ed80] S:0, 0 -> 0, 0 -> 0 (21) fl 0 +[05 0x745a7ae4ee98] S:0, 0 -> 0, 0 -> 0 (22) fl 0 +[06 0x745a7ae4efb0] S:0, 0 -> 0, 0 -> 0 (23) fl 0 +[07 0x745a7ae4f0c8] S:0, 0 -> 0, 0 -> 0 (24) fl 0 +Interfaces +LOOP addr: 0 netmask: 14 dfl: 0 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +UDP addr: 5 netmask: 0 dfl: 1 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +Server task started +``` + +Then, in another terminal, run `simple-send-udp` + +``` +$ ./builddir/samples/posix/simple-send-udp/simple-send-udp +UDP peer address: 127.0.0.1:1501 (listening on port 1500) +``` + +If you successfully run `simple-send-udp`, you see the following +message on the server terminal. + +``` +Packet received on SERVER_PORT: abc +``` diff --git a/samples/posix/simple-send-udp/src/main.c b/samples/posix/simple-send-udp/src/main.c new file mode 100644 index 000000000..821cbdf0e --- /dev/null +++ b/samples/posix/simple-send-udp/src/main.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include +#include + +#define CLIENT_ADDR 2 + +#define SERVER_ADDR 1 +#define SERVER_PORT 10 + +#define DEFAULT_UDP_ADDRESS "127.0.0.1" +#define DEFAULT_UDP_REMOTE_PORT 1501 +#define DEFAULT_UDP_LOCAL_PORT 1500 + +int main(int argc, char * argv[]) { + csp_conn_t * conn; + csp_packet_t * packet; + int ret; + + /* init */ + csp_init(); + + /* Interface config */ + csp_iface_t iface; + csp_if_udp_conf_t conf = { + .host = DEFAULT_UDP_ADDRESS, + .lport = DEFAULT_UDP_LOCAL_PORT, + .rport = DEFAULT_UDP_REMOTE_PORT}; + + csp_if_udp_init(&iface, &conf); + iface.is_default = 1; + iface.addr = CLIENT_ADDR; + + /* connect */ + conn = csp_connect(CSP_PRIO_NORM, SERVER_ADDR, SERVER_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + csp_print("Connection failed\n"); + return 1; + } + + /* prepare data */ + packet = csp_buffer_get(0); + if (packet == NULL) { + csp_print("Failed to get buffer\n"); + csp_close(conn); + return 1; + } + memcpy(packet->data, "abc", 3); + packet->length = 3; + + /* send */ + csp_send(conn, packet); + + /* close */ + csp_close(conn); + + return 0; +} From 8e13b9614881775d8600583b4c17b04a76d389fe Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 4 Mar 2025 16:38:33 +0900 Subject: [PATCH 247/348] example: csp_server: Add UDP interface support to CSP example Integrated support for UDP interface in the CSP example. Added a new command-line option -u to specify the UDP host address. Updated usage help text to include the new -u option. Signed-off-by: Gaetan Perrot --- examples/csp_server.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/examples/csp_server.c b/examples/csp_server.c index a6b848fe0..f96c65ba7 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -8,11 +8,14 @@ #include #include #include +#include #include "csp_posix_helper.h" /* Server port, the port the server listens on for incoming connections from the client. */ -#define SERVER_PORT 10 +#define SERVER_PORT 10 +#define DEFAULT_UDP_REMOTE_PORT (1500) +#define DEFAULT_UDP_LOCAL_PORT (1501) /* Commandline options */ static uint8_t server_address = 0; @@ -27,6 +30,7 @@ enum DeviceType { DEVICE_CAN, DEVICE_KISS, DEVICE_ZMQ, + DEVICE_UDP, }; #define __maybe_unused __attribute__((__unused__)) @@ -103,6 +107,7 @@ static struct option long_options[] = { #else #define OPTION_R #endif + {"udp-address", required_argument, 0, 'u'}, {"interface-address", required_argument, 0, 'a'}, {"connect-to", required_argument, 0, 'C'}, {"protocol-version", required_argument, 0, 'v'}, @@ -126,6 +131,7 @@ static void print_help(void) { if (CSP_USE_RTABLE) { csp_print(" -R set routing table\n"); } + csp_print(" -u set UDP address\n"); if (1) { csp_print(" -a
set interface address\n" " -v set protocol version\n" @@ -173,6 +179,19 @@ static csp_iface_t * add_interface(enum DeviceType device_type, const char * dev default_iface->is_default = 1; } + if (device_type == DEVICE_UDP) { + default_iface = malloc(sizeof(csp_iface_t)); + static csp_if_udp_conf_t udp_conf; + + udp_conf.host = strdup(device_name); + udp_conf.lport = DEFAULT_UDP_LOCAL_PORT; + udp_conf.rport = DEFAULT_UDP_REMOTE_PORT; + + csp_if_udp_init(default_iface, &udp_conf); + default_iface->addr = server_address; + default_iface->is_default = 1; + } + return default_iface; } @@ -185,7 +204,7 @@ int main(int argc, char * argv[]) { csp_iface_t * default_iface; int opt; - while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:v:tT:h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:u:a:v:tT:h", long_options, NULL)) != -1) { switch (opt) { case 'c': device_name = optarg; @@ -198,7 +217,11 @@ int main(int argc, char * argv[]) { case 'z': device_name = optarg; device_type = DEVICE_ZMQ; - break; + break; + case 'u': + device_name = optarg; + device_type = DEVICE_UDP; + break; #if (CSP_USE_RTABLE) case 'R': rtable = optarg; From 7747cd6c29740df4ebedd078aa9c38caf35c65d7 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 4 Mar 2025 17:17:11 +0900 Subject: [PATCH 248/348] example: csp_client: Add UDP interface support to CSP example Integrated support for UDP interface in the CSP example. Added a new command-line option -u to specify the UDP host address. Updated usage help text to include the new -u option. Signed-off-by: Gaetan Perrot --- examples/csp_client.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/examples/csp_client.c b/examples/csp_client.c index 326258d72..6715f0e42 100644 --- a/examples/csp_client.c +++ b/examples/csp_client.c @@ -9,11 +9,14 @@ #include #include #include +#include #include "csp_posix_helper.h" /* Server port, the port the server listens on for incoming connections from the client. */ -#define SERVER_PORT 10 +#define SERVER_PORT 10 +#define DEFAULT_UDP_REMOTE_PORT (1501) +#define DEFAULT_UDP_LOCAL_PORT (1500) /* Commandline options */ static uint8_t server_address = 0; @@ -29,6 +32,7 @@ enum DeviceType { DEVICE_CAN, DEVICE_KISS, DEVICE_ZMQ, + DEVICE_UDP, }; #define __maybe_unused __attribute__((__unused__)) @@ -53,6 +57,7 @@ static struct option long_options[] = { #else #define OPTION_R #endif + {"udp-address", required_argument, 0, 'u'}, {"interface-address", required_argument, 0, 'a'}, {"connect-to", required_argument, 0, 'C'}, {"protocol-version", required_argument, 0, 'v'}, @@ -76,6 +81,7 @@ static void print_help(void) { if (CSP_USE_RTABLE) { csp_print(" -R set routing table\n"); } + csp_print(" -u set UDP address\n"); if (1) { csp_print(" -a
set interface address\n" " -C
connect to server at address\n" @@ -124,6 +130,19 @@ static csp_iface_t * add_interface(enum DeviceType device_type, const char * dev default_iface->is_default = 1; } + if (device_type == DEVICE_UDP) { + default_iface = malloc(sizeof(csp_iface_t)); + static csp_if_udp_conf_t udp_conf; + + udp_conf.host = strdup(device_name); + udp_conf.lport = DEFAULT_UDP_LOCAL_PORT; + udp_conf.rport = DEFAULT_UDP_REMOTE_PORT; + + csp_if_udp_init(default_iface, &udp_conf); + default_iface->addr = client_address; + default_iface->is_default = 1; + } + return default_iface; } @@ -139,7 +158,7 @@ int main(int argc, char * argv[]) { int ret = EXIT_SUCCESS; int opt; - while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:a:C:v:tT:h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, OPTION_c OPTION_z OPTION_R "k:u:a:C:v:tT:h", long_options, NULL)) != -1) { switch (opt) { case 'c': device_name = optarg; @@ -152,7 +171,11 @@ int main(int argc, char * argv[]) { case 'z': device_name = optarg; device_type = DEVICE_ZMQ; - break; + break; + case 'u': + device_name = optarg; + device_type = DEVICE_UDP; + break; #if (CSP_USE_RTABLE) case 'R': rtable = optarg; From 1b52d3f99a74a27d3dd0f9d3919af2320a28dbb5 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 4 Mar 2025 18:41:41 +0900 Subject: [PATCH 249/348] github: workflow: Add UDP interface testing Included tests for the UDP interface using the `csp_server` and `csp_client` examples. Implemented both server and client tests to validate UDP communication. This change enables automated testing of the UDP interface within the CI pipeline, improving test coverage for UDP-related functionality. Signed-off-by: Gaetan Perrot --- .github/workflows/build-test.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index af33bb67d..4948eb0c0 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -124,3 +124,14 @@ jobs: run: | ./build/examples/csp_client -c vcan0 -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & ./build/examples/csp_server -c vcan0 -a 1 -t -v ${{ matrix.csp_version }} + + - name: Run UDP Server Test + run: | + ./build/examples/csp_server -u "127.0.0.1" -a 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_client -u "127.0.0.1" -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} + wait "$!" + + - name: Run UDP Client Test + run: | + ./build/examples/csp_client -u "127.0.0.1" -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & + ./build/examples/csp_server -u "127.0.0.1" -a 1 -T 10 -v ${{ matrix.csp_version }} From 9e88fc914e45db2a64ae71400b9c26a31b454b19 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Feb 2025 13:11:54 +0900 Subject: [PATCH 250/348] interfaces: udp: Fix socket protocol for UDP socket creation AF_PACKET was mistakenly used as the protocol argument in socket(AF_INET, SOCK_DGRAM, AF_PACKET). Since AF_PACKET and IPPROTO_UDP share the same numerical value (17 on Linux), the issue was not detected immediately. However, AF_PACKET is intended for raw Ethernet packet processing, while IPPROTO_UDP is the correct protocol specifier for UDP sockets. This fix ensures proper semantic correctness by explicitly using IPPROTO_UDP when creating a UDP socket. Before: ifconf->sockfd = socket(AF_INET, SOCK_DGRAM, AF_PACKET); After: ifconf->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 518edbc6e..4dcc00920 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -75,7 +75,7 @@ static void * csp_if_udp_rx_loop(void * param) { while (ifconf->sockfd == 0) { - ifconf->sockfd = socket(AF_INET, SOCK_DGRAM, PF_PACKET); + ifconf->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); struct sockaddr_in server_addr = {0}; server_addr.sin_family = AF_INET; From 6d7bba43554bac52569e60ec9fb05e9a39b1bdd9 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 23 May 2025 16:41:02 +0900 Subject: [PATCH 251/348] csp_id: Add helper function to get CSP header size This commit introduces the function `csp_id_get_header_size()` which returns the correct CSP header size depending on the configured protocol version. This improves readability and avoids hardcoding header sizes in multiple places, making the codebase more maintainable when supporting both CSP v1 and v2. Signed-off-by: Gaetan Perrot --- include/csp/csp_id.h | 2 ++ src/csp_id.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/csp/csp_id.h b/include/csp/csp_id.h index fd6a60a7d..3f5c21ca9 100644 --- a/include/csp/csp_id.h +++ b/include/csp/csp_id.h @@ -15,6 +15,8 @@ unsigned int csp_id_get_max_port(void); int csp_id_is_broadcast(uint16_t addr, csp_iface_t * iface); +int csp_id_get_header_size(void); + #if (CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN) void csp_id_prepend_fixup_cspv1(csp_packet_t * packet); int csp_id_strip_fixup_cspv1(csp_packet_t * packet); diff --git a/src/csp_id.c b/src/csp_id.c index 726462996..ad10f2e86 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -270,3 +270,11 @@ int csp_id_is_broadcast(uint16_t addr, csp_iface_t * iface) { } return 0; } + +int csp_id_get_header_size(void) { + if (csp_conf.version == 2) { + return CSP_ID2_HEADER_SIZE; + } else { + return CSP_ID1_HEADER_SIZE; + } +} From ec07f7975949b3a3775729d2558298d1299dc9fa Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 23 May 2025 16:42:32 +0900 Subject: [PATCH 252/348] interfaces: csp_if_eth: Fix incorrect frame length check Previously, the Ethernet RX frame length was validated against `CSP_BUFFER_SIZE`, which only accounts for the payload and not the CSP header. This caused valid packets with `packet->length == CSP_BUFFER_SIZE` to be incorrectly rejected. This commit fixes the check by including the CSP header size using the new `csp_id_get_header_size()` helper function. This ensures proper validation for packets that use the full payload size and avoids false positives on valid frame lengths. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index 5821bb64d..e228a09c6 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -174,7 +174,7 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei return CSP_ERR_INVAL; } - if (frame_length == 0 || frame_length > CSP_BUFFER_SIZE) { + if (frame_length == 0 || frame_length > (CSP_BUFFER_SIZE + csp_id_get_header_size())) { iface->frame++; csp_print("eth rx frame_length of %u is invalid\n", frame_length); return CSP_ERR_INVAL; From f0f3c1704b3589779eb99c0e6f6deaf57f48ea9a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 23 Jan 2025 15:28:42 +0900 Subject: [PATCH 253/348] examples: python: Fix typo There was a typo in the message of a printf in server_task(). "Recieved" instead of "Received". Signed-off-by: Gaetan Perrot --- examples/csp_server_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/csp_server_client.py b/examples/csp_server_client.py index 6e42768ac..bf1a77e19 100644 --- a/examples/csp_server_client.py +++ b/examples/csp_server_client.py @@ -32,7 +32,7 @@ def server_task(addr: int, port: int) -> None: while (packet := csp.read(conn, 50)) is not None: if csp.conn_dport(conn) == port: - _print('Recieved on {port}: {data}'.format( + _print('Received on {port}: {data}'.format( port=port, data=csp.packet_get_data(packet).decode('utf-8')) ) From 912c66672ab5427416a9913335a587d3a1361f9c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 24 Jan 2025 13:07:38 +0900 Subject: [PATCH 254/348] examples: python: Remove unused import Removed unused import in python example files. Signed-off-by: Gaetan Perrot --- examples/csp_server_client.py | 2 +- examples/python_bindings_example_client.py | 1 - examples/python_bindings_example_server.py | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/csp_server_client.py b/examples/csp_server_client.py index bf1a77e19..ad56a85b3 100644 --- a/examples/csp_server_client.py +++ b/examples/csp_server_client.py @@ -4,7 +4,7 @@ import time import threading import libcsp_py3 as csp -from typing import Any, Callable +from typing import Callable def printer(node: str, color: str) -> Callable: diff --git a/examples/python_bindings_example_client.py b/examples/python_bindings_example_client.py index ec66496df..be952a95d 100644 --- a/examples/python_bindings_example_client.py +++ b/examples/python_bindings_example_client.py @@ -10,7 +10,6 @@ # $ LD_LIBRARY_PATH=build PYTHONPATH=build python3 examples/python_bindings_example_client.py -z localhost # -import os import time import sys import argparse diff --git a/examples/python_bindings_example_server.py b/examples/python_bindings_example_server.py index 49e89c69f..c13b2b52a 100644 --- a/examples/python_bindings_example_server.py +++ b/examples/python_bindings_example_server.py @@ -10,9 +10,6 @@ # $ LD_LIBRARY_PATH=build PYTHONPATH=build python3 examples/python_bindings_example_server.py # -import os -import time -import sys import threading import argparse From c0d1dd7c1adf24d907fd60707f77f0ea2154450b Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 26 Jan 2025 23:11:47 +0900 Subject: [PATCH 255/348] pycsp: Fix route_start_task() arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route_start_task() function was declared with METH_VARARGS, but it does not actually accept any arguments. This has been the case already — the function ignores any arguments passed to it. This change replaces METH_VARARGS with METH_NOARGS to better reflect the current function signature and avoid confusion when calling it from Python. Signed-off-by: Gaetan Perrot --- src/bindings/python/pycsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/python/pycsp.c b/src/bindings/python/pycsp.c index d5b49616f..258b382d7 100644 --- a/src/bindings/python/pycsp.c +++ b/src/bindings/python/pycsp.c @@ -963,7 +963,7 @@ static PyMethodDef methods[] = { {"conn_src", pycsp_conn_src, METH_O, ""}, {"listen", pycsp_listen, METH_VARARGS, ""}, {"bind", pycsp_bind, METH_VARARGS, ""}, - {"route_start_task", pycsp_route_start_task, METH_VARARGS, ""}, + {"route_start_task", pycsp_route_start_task, METH_NOARGS, ""}, {"ping", pycsp_ping, METH_VARARGS, ""}, {"reboot", pycsp_reboot, METH_VARARGS, ""}, {"shutdown", pycsp_shutdown, METH_VARARGS, ""}, From cbeeb4113b7354ff6117f845c297cb429e609306 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 23 Jul 2025 12:08:04 +0900 Subject: [PATCH 256/348] examples: python: Remove outdated comment The comment suggesting that route_start_task() takes a 'priority' parameter is misleading, as the function does not accept any arguments and has not done so for some time. This change removes the outdated comment to avoid confusion and reflect the actual behavior of the function. Signed-off-by: Gaetan Perrot --- examples/python_bindings_example_client.py | 1 - examples/python_bindings_example_server.py | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/python_bindings_example_client.py b/examples/python_bindings_example_client.py index be952a95d..ee920ba47 100644 --- a/examples/python_bindings_example_client.py +++ b/examples/python_bindings_example_client.py @@ -62,7 +62,6 @@ def get_options(): # same format/use as line above libcsp.rtable_load(options.routing_table) - # Parameters: {priority} - 0 (critical), 1 (high), 2 (norm), 3 (low) ---- default=2 # Start the router task - creates routing thread libcsp.route_start_task() time.sleep(0.2) # allow router task startup diff --git a/examples/python_bindings_example_server.py b/examples/python_bindings_example_server.py index c13b2b52a..af35cc4cb 100644 --- a/examples/python_bindings_example_server.py +++ b/examples/python_bindings_example_server.py @@ -121,7 +121,6 @@ def csp_server(): libcsp.kiss_init(options.kiss, options.address) libcsp.rtable_load("0/0 KISS") - # Parameters: {priority} - 0 (critical), 1 (high), 2 (norm), 3 (low) ---- default=2 # Start the router task - creates routing thread libcsp.route_start_task() From 9ce8eb8354cf705cc2e67114e796ce618c33fc52 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 27 Nov 2024 11:46:42 +0900 Subject: [PATCH 257/348] samples: Add sample code for sending via zmqhub This commit adds a simple sample code `send()`ing a packet via zmqhub. Signed-off-by: Gaetan Perrot --- samples/posix/CMakeLists.txt | 1 + samples/posix/simple-send-zmq/CMakeLists.txt | 3 + samples/posix/simple-send-zmq/README.md | 94 ++++++++++++++++++++ samples/posix/simple-send-zmq/src/main.c | 56 ++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 samples/posix/simple-send-zmq/CMakeLists.txt create mode 100644 samples/posix/simple-send-zmq/README.md create mode 100644 samples/posix/simple-send-zmq/src/main.c diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt index d5034bc45..e9ae7adc7 100644 --- a/samples/posix/CMakeLists.txt +++ b/samples/posix/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(simple-send-canbus) add_subdirectory(simple-send-usart) add_subdirectory(hmac) add_subdirectory(simple-send-udp) +add_subdirectory(simple-send-zmq) diff --git a/samples/posix/simple-send-zmq/CMakeLists.txt b/samples/posix/simple-send-zmq/CMakeLists.txt new file mode 100644 index 000000000..31d1a2ecb --- /dev/null +++ b/samples/posix/simple-send-zmq/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(simple-send-zmq EXCLUDE_FROM_ALL src/main.c) +target_include_directories(simple-send-zmq PRIVATE ${csp_inc}) +target_link_libraries(simple-send-zmq PRIVATE csp) diff --git a/samples/posix/simple-send-zmq/README.md b/samples/posix/simple-send-zmq/README.md new file mode 100644 index 000000000..ead7201d6 --- /dev/null +++ b/samples/posix/simple-send-zmq/README.md @@ -0,0 +1,94 @@ +# Simple Send via zmqhub + +This is a simple sample code to send `"abc"` to a server via zmqhub +interface. + +## How to Build + +``` +$ cmake -B builddir +$ ninja -C builddir simple-send-zmq csp_server zmqproxy +``` + +## How to Test + +You’ll need to install libzmq3. If you don't have it, install it with: + +``` +$ sudo apt-get install libzmq3-dev +``` + +First, you need to run a CSP server: + +``` +$ ./builddir/examples/csp_server -z localhost -a 1 +Initialising CSP +Connection table +[00 0x756e9c2f3900] S:0, 0 -> 0, 0 -> 0 (17) fl 0 +[01 0x756e9c2f3a18] S:0, 0 -> 0, 0 -> 0 (18) fl 0 +[02 0x756e9c2f3b30] S:0, 0 -> 0, 0 -> 0 (19) fl 0 +[03 0x756e9c2f3c48] S:0, 0 -> 0, 0 -> 0 (20) fl 0 +[04 0x756e9c2f3d60] S:0, 0 -> 0, 0 -> 0 (21) fl 0 +[05 0x756e9c2f3e78] S:0, 0 -> 0, 0 -> 0 (22) fl 0 +[06 0x756e9c2f3f90] S:0, 0 -> 0, 0 -> 0 (23) fl 0 +[07 0x756e9c2f40a8] S:0, 0 -> 0, 0 -> 0 (24) fl 0 +Interfaces +LOOP addr: 0 netmask: 14 dfl: 0 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +ZMQHUB addr: 1 netmask: 0 dfl: 1 + tx: 00000 rx: 00000 txe: 00000 rxe: 00000 + drop: 00000 autherr: 00000 frame: 00000 + txb: 0 (0B) rxb: 0 (0B) + +Server task started +``` + +Then, in another terminal, run `zmqproxy` + +``` +$ ./builddir/examples/zmqproxy +Subscriber task listening on tcp://0.0.0.0:6000 +Publisher task listening on tcp://0.0.0.0:7000 +Capture/logging task listening on tcp://0.0.0.0:6000 +Packet: Src 2, Dst 1, Dport 10, Sport 18, Pri 2, Flags 0x00, Size 4 +``` + +Then, in another terminal, run `simple-send-zmq` + +``` +./builddir/samples/posix/simple-send-zmq/simple-send-zmq +``` + +If it runs successfully, you’ll see a new message on the server +terminal: + +``` +Packet received on SERVER_PORT: abc +``` + +### Note on `usleep(1000)` in the code + +In the `simple-send-zmq` example, a short sleep (`usleep(1000)`) is used before +sending messages. +This delay allows the ZeroMQ sockets enough time to establish their connections +properly. + +This is important because, as explained in the official ZeroMQ guide ([ZMQ +Guide, Chapter 1](https://zguide.zeromq.org/docs/chapter1/)): + +> *"There is one more important thing to know about PUB-SUB sockets: you do not +know precisely when a subscriber starts to get messages. Even if you start a +subscriber, wait a while, and then start the publisher, the subscriber will +always miss the first messages that the publisher sends. This is because as the +subscriber connects to the publisher (something that takes a small but non-zero +time), the publisher may already be sending messages out."* + +In other words, since the connection takes a non-zero amount of time to +establish, sending immediately after connecting may cause the message to be +lost. + +Therefore, the small `usleep(1000)` delay helps ensure the message is sent after +the connection is ready. diff --git a/samples/posix/simple-send-zmq/src/main.c b/samples/posix/simple-send-zmq/src/main.c new file mode 100644 index 000000000..c3aa9d9a9 --- /dev/null +++ b/samples/posix/simple-send-zmq/src/main.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#define SERVER_ADDR 1 +#define SERVER_PORT 10 + +#define CLIENT_ADDR 2 +#define DEVICE_NAME "localhost" + +int main(int argc, char * argv[]) { + csp_iface_t * iface; + csp_conn_t * conn; + csp_packet_t * packet; + int ret; + + /* init */ + csp_init(); + + /* Init zmq interface */ + int error = csp_zmqhub_init(CLIENT_ADDR, DEVICE_NAME, 0, &iface); + if (error != CSP_ERR_NONE) { + csp_print("failed to Init ZMQ interface [%s], error: %d\n", DEVICE_NAME, error); + return 1; + } + iface->is_default = 1; + + /* connect */ + conn = csp_connect(CSP_PRIO_NORM, SERVER_ADDR, SERVER_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + csp_print("Connection failed\n"); + return 1; + } + + /* sleep to have the time to be connected */ + usleep(1000); + + /* prepare data */ + packet = csp_buffer_get(0); + if (packet == NULL) { + csp_print("Failed to get buffer\n"); + csp_close(conn); + return 1; + } + memcpy(packet->data, "abc", 4); + packet->length = 4; + + /* send */ + csp_send(conn, packet); + + /* close */ + csp_close(conn); + + return 0; +} From 7929c887e4f7cbe9af255118b9f80d818455eb8e Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:55:42 +0900 Subject: [PATCH 258/348] examples: csp_server_client: replace csp_buffer_get_always Avoid using csp_buffer_get_always() in non-critical path. Use csp_buffer_get(0) instead and handle NULL return safely. Signed-off-by: Gaetan Perrot --- examples/csp_server_client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/csp_server_client.c b/examples/csp_server_client.c index 3de47da7c..da1bd466c 100644 --- a/examples/csp_server_client.c +++ b/examples/csp_server_client.c @@ -108,7 +108,11 @@ static void * client(void * param) { } /* 2. Get packet buffer for message/data */ - csp_packet_t * packet = csp_buffer_get_always(); + csp_packet_t * packet = csp_buffer_get(0); + if (packet == NULL) { + csp_print("Failed to get buffer\n"); + csp_close(conn); + } /* 3. Copy data to packet */ memcpy(packet->data, "Hello world ", 12); From 57adec58cf8a6b031d3d4c1abf0d49d1de3c1359 Mon Sep 17 00:00:00 2001 From: Iliya Iliev Date: Thu, 31 Jul 2025 12:01:11 +0300 Subject: [PATCH 259/348] doc: Dockerfile: Remove false comment Removes misleading comment. Signed-off-by: Iliya Iliev --- doc/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/Dockerfile b/doc/Dockerfile index 8212ed101..70d0cf963 100644 --- a/doc/Dockerfile +++ b/doc/Dockerfile @@ -11,7 +11,6 @@ ENV LC_ALL en_US.UTF-8 ENV LANG en_US.utf8 ENV LANGUAGE en_US.UTF-8 -# Install libxdp & libbpf RUN mkdir /home/libcsp_sphinx_docs && \ cd /home/libcsp_sphinx_docs && \ git clone https://github.com/libcsp/libcsp.git --branch develop && \ From c1aa74e75fc77d0c6fa4e8dff5a59daee402f6ad Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 14 Apr 2025 23:34:24 +0900 Subject: [PATCH 260/348] doc: Improve documentation for csp_crc32 Added documentation for all functions in csp_crc32. This commit adds documentation entries for: - csp_crc32_init - csp_crc32_update - csp_crc32_final These functions form the low-level CRC calculation API and are useful for incremental checksum computations. This change ensures that all available CRC32-related functionality is visible in the generated documentation. Signed-off-by: Gaetan Perrot --- doc/api/csp_crc32_h.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/api/csp_crc32_h.rst b/doc/api/csp_crc32_h.rst index b67087aa4..928bcfce1 100644 --- a/doc/api/csp_crc32_h.rst +++ b/doc/api/csp_crc32_h.rst @@ -9,3 +9,6 @@ Interface Functions .. autocfunction:: csp_crc32.h::csp_crc32_append .. autocfunction:: csp_crc32.h::csp_crc32_verify .. autocfunction:: csp_crc32.h::csp_crc32_memory +.. autocfunction:: csp_crc32.h::csp_crc32_init +.. autocfunction:: csp_crc32.h::csp_crc32_update +.. autocfunction:: csp_crc32.h::csp_crc32_final From b9140c0f6e7c4d3c0061ae77dc9964c51235c907 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:03:28 +0900 Subject: [PATCH 261/348] csp_id: remove unnecessary parentheses Cleaned up redundant parentheses in bit shift expressions used to calculate max port values. No functional change. Signed-off-by: Gaetan Perrot --- src/csp_id.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_id.c b/src/csp_id.c index ad10f2e86..082bc2eae 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -252,9 +252,9 @@ unsigned int csp_id_get_max_nodeid(void) { unsigned int csp_id_get_max_port(void) { if (csp_conf.version == 2) { - return ((1 << (CSP_ID2_PORT_SIZE)) - 1); + return ((1 << CSP_ID2_PORT_SIZE) - 1); } else { - return ((1 << (CSP_ID1_PORT_SIZE)) - 1); + return ((1 << CSP_ID1_PORT_SIZE) - 1); } } From f31a8c2e6ef5afaefbaa6e6db9a9941cf080fc97 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:04:02 +0900 Subject: [PATCH 262/348] csp_route: remove unnecessary parentheses Removed redundant parentheses around the `is_to_me` expression in `csp_route_work()`. This improves code readability and has no impact on logic. Signed-off-by: Gaetan Perrot --- src/csp_route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_route.c b/src/csp_route.c index b9f8f4fc1..2102e4360 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -143,7 +143,7 @@ int csp_route_work(void) { /* Deduplication */ if ((csp_conf.dedup == CSP_DEDUP_ALL) || - ((is_to_me) && (csp_conf.dedup == CSP_DEDUP_INCOMING)) || + (is_to_me && (csp_conf.dedup == CSP_DEDUP_INCOMING)) || ((!is_to_me) && (csp_conf.dedup == CSP_DEDUP_FWD))) { if (csp_dedup_is_duplicate(packet)) { /* Discard packet */ From 5eb61a073470cf93300fbb509f54ed7478bedd92 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:04:53 +0900 Subject: [PATCH 263/348] csp_io: remove unnecessary parentheses This improves code readability and has no impact on logic. Signed-off-by: Gaetan Perrot --- src/csp_io.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index 66b6fa92b..b4b089ca4 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -120,7 +120,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route } /* Apply outgoing interface address to packet */ - if ((from_me) && (idout->src == 0)) { + if (from_me && (idout->src == 0)) { _idout.src = iface->addr; } @@ -165,7 +165,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route } /* Apply outgoing interface address to packet */ - if ((from_me) && (idout->src == 0)) { + if (from_me && (idout->src == 0)) { idout->src = route->iface->addr; } @@ -199,7 +199,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route } /* Apply outgoing interface address to packet */ - if ((from_me) && (idout->src == 0)) { + if (from_me && (idout->src == 0)) { idout->src = iface->addr; } From f5959315f4aa306ffd653557970280412cca7ad9 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:05:14 +0900 Subject: [PATCH 264/348] csp_rdp: remove unnecessary parentheses This improves code readability and has no impact on logic. Signed-off-by: Gaetan Perrot --- src/csp_rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 86bf2f615..c32d3a24f 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -663,7 +663,7 @@ bool csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { conn->rdp.snd_una = rx_header->ack_nr + 1; /* We have an EACK */ - if ((rx_header->flags & RDP_EAK)) { + if (rx_header->flags & RDP_EAK) { csp_rdp_protocol("RDP %p: Got EACK\n", (void *)conn); goto discard_open; } From 4daf9f446f41cbb879e17065e9e6d17396964cb7 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:05:38 +0900 Subject: [PATCH 265/348] csp_rtable_stdio: remove unnecessary parentheses This improves code readability and has no impact on logic. Signed-off-by: Gaetan Perrot --- src/csp_rtable_stdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rtable_stdio.c b/src/csp_rtable_stdio.c index 6401007d1..bfd4f7634 100644 --- a/src/csp_rtable_stdio.c +++ b/src/csp_rtable_stdio.c @@ -21,7 +21,7 @@ static int csp_rtable_parse(const char * rtable, int dry_run) { /* Get first token */ char * saveptr; char * str = strtok_r(rtable_copy, ",", &saveptr); - while ((str) && (strlen(str) > 1)) { + while (str && (strlen(str) > 1)) { unsigned int address, via; int netmask; char name[15]; From 0531750f576b47e7dc0bfc947381f3ecb4c41ba9 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 14:05:55 +0900 Subject: [PATCH 266/348] interfaces: csp_if_eth: remove unnecessary parentheses This improves code readability and has no impact on logic. Signed-off-by: Gaetan Perrot --- src/interfaces/csp_if_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/csp_if_eth.c b/src/interfaces/csp_if_eth.c index e228a09c6..0d81d6246 100644 --- a/src/interfaces/csp_if_eth.c +++ b/src/interfaces/csp_if_eth.c @@ -139,7 +139,7 @@ int csp_eth_rx(csp_iface_t * iface, csp_eth_header_t * eth_frame, uint32_t recei if (eth_debug) csp_hex_dump("rx", (void*)eth_frame, received_len); /* Filter on CSP protocol id */ - if ((be16toh(eth_frame->ether_type) != CSP_ETH_TYPE_CSP)) { + if (be16toh(eth_frame->ether_type) != CSP_ETH_TYPE_CSP) { iface->frame++; return CSP_ERR_INVAL; } From 32be594e8cf945fb22469fd0541d64a4aee805e4 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 14 Apr 2025 22:46:40 +0900 Subject: [PATCH 267/348] doc: Add documentation to csp_id Add documentation for all public functions of csp_id. This header previously lacked any documentation, making it harder to understand the role of each function, especially for newer contributors or external users. This improves both inline readability and Sphinx-generated documentation. Signed-off-by: Gaetan Perrot --- doc/api/csp_id_h.rst | 18 +++++++++- include/csp/csp_id.h | 85 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/doc/api/csp_id_h.rst b/doc/api/csp_id_h.rst index 0bf0933d9..259e9f14d 100644 --- a/doc/api/csp_id_h.rst +++ b/doc/api/csp_id_h.rst @@ -2,4 +2,20 @@ CSP ID ====== .. autocmodule:: csp_id.h - :members: + +Functions +--------- + +.. autocfunction:: csp_id.h::csp_id_prepend +.. autocfunction:: csp_id.h::csp_id_strip +.. autocfunction:: csp_id.h::csp_id_setup_rx +.. autocfunction:: csp_id.h::csp_id_get_host_bits +.. autocfunction:: csp_id.h::csp_id_get_max_nodeid +.. autocfunction:: csp_id.h::csp_id_get_max_port +.. autocfunction:: csp_id.h::csp_id_is_broadcast + +Fixup Functions for ZMQ CSP v1 little-endian +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autocfunction:: csp_id.h::csp_id_prepend_fixup_cspv1 +.. autocfunction:: csp_id.h::csp_id_strip_fixup_cspv1 diff --git a/include/csp/csp_id.h b/include/csp/csp_id.h index 3f5c21ca9..5bdbab9cf 100644 --- a/include/csp/csp_id.h +++ b/include/csp/csp_id.h @@ -6,27 +6,112 @@ extern "C" { #include +/** + * Prepend CSP header fields into the packet's data buffer. + * + * This function encodes the CSP ID header into the packet and adjusts the data offset. + * + * @param packet Pointer to the packet to modify. + */ void csp_id_prepend(csp_packet_t * packet); + +/** + * Strip CSP header fields from the packet's data buffer. + * + * This function decodes the CSP ID header from the packet and adjusts the data offset. + * + * @param packet Pointer to the packet to modify. + * @return 0 on success, -1 on failure. + */ int csp_id_strip(csp_packet_t * packet); + +/** + * Setup reception information from CSP ID header. + * + * Typically called after stripping the header to configure addressing fields. + * + * @param packet Pointer to the received packet. + * @return 0 on success, -1 on failure. + */ int csp_id_setup_rx(csp_packet_t * packet); + +/** + * Get number of bits allocated for the host part of the address. + * + * @return Number of bits used for the host ID. + */ unsigned int csp_id_get_host_bits(void); + +/** + * Get maximum allowed node ID. + * + * @return Maximum node ID based on current configuration. + */ unsigned int csp_id_get_max_nodeid(void); + +/** + * Get maximum allowed port number. + * + * @return Maximum port number. + */ unsigned int csp_id_get_max_port(void); +/** + * Check whether a given address is a broadcast address. + * + * @param addr The address to test. + * @param iface Interface to use for context (netmask, etc.). + * @return 1 if broadcast, 0 otherwise. + */ int csp_id_is_broadcast(uint16_t addr, csp_iface_t * iface); +/** + * Get the size of the CSP header based on the configured version. + * + * This function returns the size in bytes of the CSP packet header, + * depending on whether CSP version 1 or 2 is configured. + * + * @return The header size in bytes. + */ int csp_id_get_header_size(void); #if (CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN) + +/** + * Prepend CSPv1-compatible ID header (ZMQ fixup). + * + * Used when sending CSPv1 packets on little-endian ZMQ transport. + * + * @param packet Pointer to the packet to modify. + */ void csp_id_prepend_fixup_cspv1(csp_packet_t * packet); + +/** + * Strip CSPv1-compatible ID header (ZMQ fixup). + * + * Used when receiving CSPv1 packets on little-endian ZMQ transport. + * + * @param packet Pointer to the packet to modify. + * @return 0 on success, -1 on failure. + */ int csp_id_strip_fixup_cspv1(csp_packet_t * packet); + #else + +/** + * Wrapper for csp_id_prepend when no fixup is required. + */ static inline void csp_id_prepend_fixup_cspv1(csp_packet_t * packet) { csp_id_prepend(packet); } + +/** + * Wrapper for csp_id_strip when no fixup is required. + */ static inline int csp_id_strip_fixup_cspv1(csp_packet_t * packet) { return csp_id_strip(packet); } + #endif #ifdef __cplusplus From c96137d29c184856e4b6a732a5f13d6b8cfea913 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 11:15:50 +0900 Subject: [PATCH 268/348] doc: update CSP hooks RST to include undocumented functions Updated the documentation file to explicitly list all hook functions from `csp_hooks.h` using `.. autocfunction::` directives. This ensures that previously undocumented functions are properly included in the generated documentation, addressing issues where automatic inclusion via `.. autocmodule::` did not work as expected. Improves visibility and completeness of the CSP hooks API documentation. Signed-off-by: Gaetan Perrot --- doc/api/csp_hooks_h.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/api/csp_hooks_h.rst b/doc/api/csp_hooks_h.rst index f60164d7f..b3439c404 100644 --- a/doc/api/csp_hooks_h.rst +++ b/doc/api/csp_hooks_h.rst @@ -2,4 +2,18 @@ CSP Hooks ========= .. autocmodule:: csp_hooks.h - :members: + +Hook Functions +-------------- + +.. autocfunction:: csp_hooks.h::csp_output_hook +.. autocfunction:: csp_hooks.h::csp_input_hook +.. autocfunction:: csp_hooks.h::csp_reboot_hook +.. autocfunction:: csp_hooks.h::csp_shutdown_hook +.. autocfunction:: csp_hooks.h::csp_memfree_hook +.. autocfunction:: csp_hooks.h::csp_ps_hook +.. autocfunction:: csp_hooks.h::csp_panic +.. autocfunction:: csp_hooks.h::csp_crypto_decrypt +.. autocfunction:: csp_hooks.h::csp_crypto_encrypt +.. autocfunction:: csp_hooks.h::csp_clock_get_time +.. autocfunction:: csp_hooks.h::csp_clock_set_time From 200c918e103af97861125724953109c1c7a44b82 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 11 Apr 2025 11:20:46 +0900 Subject: [PATCH 269/348] csp_hook: add documentation for CSP hook functions This commit adds detailed Doxygen-style comments to `csp_hooks.h` for all hook functions, including: - csp_input_hook - csp_output_hook - csp_reboot_hook - csp_shutdown_hook - csp_memfree_hook - csp_ps_hook - csp_panic - csp_crypto_encrypt / decrypt - csp_clock_get_time / set_time Each hook is now documented with parameter descriptions and return values, in order to improve developer understanding and assist with auto-generated documentation (e.g., Sphinx output). This improves maintainability and provides guidance to users who want to implement or override CSP hooks for their specific use cases. Signed-off-by: Gaetan Perrot --- include/csp/csp_hooks.h | 77 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/include/csp/csp_hooks.h b/include/csp/csp_hooks.h index 7a7ab3182..12af6afab 100644 --- a/include/csp/csp_hooks.h +++ b/include/csp/csp_hooks.h @@ -1,7 +1,7 @@ /**************************************************************************** * **File:** csp/csp_hooks.h * - * **Description:** See Hooks in CSP + * **Description:** Hooks that can be implemented in CSP, see Hooks in csp for more information ****************************************************************************/ #pragma once @@ -12,22 +12,91 @@ extern "C" { #endif +/** + * Hook called when a packet is sent + * + * @param idout ID of the recipient + * @param packet CSP packet to be sent + * @param iface Outgoing interface + * @param via Next hop address (if applicable) + * @param from_me Whether the packet originates from this node + */ void csp_output_hook(const csp_id_t * idout, csp_packet_t * packet, csp_iface_t * iface, uint16_t via, int from_me); + +/** + * Hook called when a packet is received + * + * @param iface Interface that received the packet + * @param packet Received packet + */ void csp_input_hook(csp_iface_t * iface, csp_packet_t * packet); +/** + * Hook called for system reboot + */ void csp_reboot_hook(void); + +/** + * Hook called for system shutdown + */ void csp_shutdown_hook(void); +/** + * Returns the available free memory + * @return Free memory in bytes + */ uint32_t csp_memfree_hook(void); + +/** + * Collects process information into a packet + * + * @param packet Packet to be filled with process info + * @return Number of entries written + */ unsigned int csp_ps_hook(csp_packet_t * packet); +/** + * Called in case of a fatal error + * This function must not return, and should reboot the system + * or the program running CSP to recover responsiveness of the system. + * + * @param msg Error message + */ void csp_panic(const char * msg); -/** Implement these, if you use csp_if_tun */ -int csp_crypto_decrypt(uint8_t * ciphertext_in, uint8_t ciphertext_len, uint8_t * msg_out); // Returns -1 for failure, length if ok -int csp_crypto_encrypt(uint8_t * msg_begin, uint8_t msg_len, uint8_t * ciphertext_out); // Returns length of encrypted data +/** + * Decrypt a message (Implement these if you used csp_if_tun) + * + * @param ciphertext_in Encrypted input data + * @param ciphertext_len Length of encrypted data + * @param msg_out Output buffer for the decrypted message + * @return Length of the decrypted data or an error code on failure + */ +int csp_crypto_decrypt(uint8_t * ciphertext_in, uint8_t ciphertext_len, uint8_t * msg_out); +/** + * Encrypt a message (Implement these if you used csp_if_tun) + * + * @param msg_begin Plaintext message to encrypt + * @param msg_len Length of the message + * @param ciphertext_out Output buffer for encrypted data + * @return Length of the encrypted data or an error code on failure + */ +int csp_crypto_encrypt(uint8_t * msg_begin, uint8_t msg_len, uint8_t * ciphertext_out); + +/** + * Get the current system time + * + * @param time Structure to be filled with the current time + */ void csp_clock_get_time(csp_timestamp_t * time); + +/** + * Set the system time + * + * @param time Structure containing the new time to set + * @return 0 on success, -1 on failure + */ int csp_clock_set_time(const csp_timestamp_t * time); #ifdef __cplusplus From 334b1ee4ea89e6d861990d80bc04a7f4348bc59a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:57:11 +0000 Subject: [PATCH 270/348] build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-checker.yml | 4 ++-- .github/workflows/build-test-freertos.yml | 6 +++--- .github/workflows/build-test-python.yml | 2 +- .github/workflows/build-test-zephyr.yml | 2 +- .github/workflows/build-test.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/develop-build-sphinx-docs.yml | 2 +- .github/workflows/gitlint.yml | 2 +- .github/workflows/linelint.yaml | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/abi-checker.yml b/.github/workflows/abi-checker.yml index 351fd1dbd..6bca11e6a 100644 --- a/.github/workflows/abi-checker.yml +++ b/.github/workflows/abi-checker.yml @@ -22,7 +22,7 @@ jobs: sudo apt-get install abi-compliance-checker abi-dumper - name: Checkout merged-base - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.base.ref }} @@ -32,7 +32,7 @@ jobs: abi-dumper build/libcsp.so -lver "merged-base" -o ../tmp/libcsp-merged-base.dump - name: Checkout Current PR - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 ref: ${{github.event.pull_request.head.ref}} diff --git a/.github/workflows/build-test-freertos.yml b/.github/workflows/build-test-freertos.yml index 58db05a3c..ed36f65cf 100644 --- a/.github/workflows/build-test-freertos.yml +++ b/.github/workflows/build-test-freertos.yml @@ -14,18 +14,18 @@ jobs: sudo apt-get install ninja-build meson tree - name: Checkout Test App - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: libcsp/libcsp-freertos - name: Checkout FreeRTOS Kernel - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: FreeRTOS/FreeRTOS-Kernel path: freertos - name: Checkout libcsp under subprojects - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: path: subprojects/libcsp diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 5d069ea87..e48a6ea0c 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -36,7 +36,7 @@ jobs: sudo apt-get install ninja-build ${{ matrix.buildsystem }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Build libcsp examples run: python3 examples/buildall.py --build-system=${{ matrix.buildsystem }} diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index efbdd5bfc..af8c64bc6 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -34,7 +34,7 @@ jobs: python3 --version - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: path: libcsp-zephyr repository: yashi/libcsp-zephyr diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4948eb0c0..ffdaa2e1b 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -61,7 +61,7 @@ jobs: sudo apt-get install ninja-build ${{ matrix.buildsystem }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Build env: diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 4b21becc7..6cde179c2 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install codespell run: pip install codespell diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index 1d7f4e733..e5e8e91e6 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout the repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup packages on Linux if: ${{ runner.os == 'Linux' }} diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml index eca3b4079..8973621f8 100644 --- a/.github/workflows/gitlint.yml +++ b/.github/workflows/gitlint.yml @@ -16,7 +16,7 @@ jobs: pip3 install gitlint - name: Checkout the code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 diff --git a/.github/workflows/linelint.yaml b/.github/workflows/linelint.yaml index 536c4ea09..2a1fd8dc2 100644 --- a/.github/workflows/linelint.yaml +++ b/.github/workflows/linelint.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Linelint uses: fernandrone/linelint@master From 948334a15a4eeb42fe6821a13189244c273e0765 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:21:21 +0000 Subject: [PATCH 271/348] build(deps): bump actions/setup-python from 5 to 6 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-test-python.yml | 2 +- .github/workflows/build-test-zephyr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index e48a6ea0c..96c127fe7 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -22,7 +22,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index af8c64bc6..b6ecc7d48 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} From 23d2a991f1a1eee690eb68f55b4a8ac59e3913f8 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 24 Jul 2025 19:45:17 +0900 Subject: [PATCH 272/348] Make csp_buffer_get_always() internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit csp_buffer_get_always() is used in critical execution paths where a buffer must be allocated or the mission fails. It’s not intended as a general-purpose convenience function. This change hides it within libcsp, removing it from the public API. Signed-off-by: Yasushi SHOJI --- doc/api/csp_buffer_h.rst | 2 -- include/csp/csp_buffer.h | 29 ----------------------------- src/csp_buffer.c | 11 +++++++++++ src/csp_buffer_private.h | 24 ++++++++++++++++++++++++ src/interfaces/csp_if_can_pbuf.c | 2 ++ src/interfaces/csp_if_kiss.c | 2 ++ unittests/buffer.c | 4 +++- unittests/hmac.c | 1 + 8 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 src/csp_buffer_private.h diff --git a/doc/api/csp_buffer_h.rst b/doc/api/csp_buffer_h.rst index b070bbc9d..c7289b399 100644 --- a/doc/api/csp_buffer_h.rst +++ b/doc/api/csp_buffer_h.rst @@ -15,5 +15,3 @@ Interface Functions .. autocfunction:: csp_buffer.h::csp_buffer_remaining .. autocfunction:: csp_buffer.h::csp_buffer_init .. autocfunction:: csp_buffer.h::csp_buffer_refc_inc -.. autocfunction:: csp_buffer.h::csp_buffer_get_always -.. autocfunction:: csp_buffer.h::csp_buffer_get_always_isr diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index 6432ca430..698be1aa3 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -11,15 +11,6 @@ extern "C" { #endif -/** - * Number of buffers reserved by CSP for fault-tolerant operations. - * - * These reserved buffers are used for operations that can tolerate allocation failure, - * such as client requests with proper error handling, or services that may timeout - * safely when memory is low. - */ -#define CSP_BUFFER_RESERVE 2 - /** * Get free buffer from task context. * @@ -28,20 +19,6 @@ extern "C" { */ csp_packet_t * csp_buffer_get(size_t unused); -/** - * Get a buffer or get killed (from task context) - * - * This function return a buffer or kill the whole program when it - * failed. DO NOT USE THIS FUNCTION if you don't know what you are - * doing. Never use this function from application layer. This - * function should be an internal function and will be sonn. - * - * https://github.com/libcsp/libcsp/issues/864 - * - * @return Buffer (pointer to #csp_packet_t) - */ -csp_packet_t * csp_buffer_get_always(void); - /** * Get free buffer (from ISR context). * @@ -50,12 +27,6 @@ csp_packet_t * csp_buffer_get_always(void); */ csp_packet_t * csp_buffer_get_isr(size_t unused); -/** - * Get a buffer or get killed (from ISR context) - * @return Buffer (pointer to #csp_packet_t) - */ -csp_packet_t * csp_buffer_get_always_isr(void); - /** * Free buffer (from task context). * diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 4f055badb..09f86b598 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -8,6 +8,17 @@ #include #include +#include "csp_buffer_private.h" + +/** + * Number of buffers reserved by CSP for fault-tolerant operations. + * + * These reserved buffers are used for operations that can tolerate allocation failure, + * such as client requests with proper error handling, or services that may timeout + * safely when memory is low. + */ +#define CSP_BUFFER_RESERVE 2 + /** Internal buffer header */ typedef struct csp_skbf_s { unsigned int refcount; diff --git a/src/csp_buffer_private.h b/src/csp_buffer_private.h new file mode 100644 index 000000000..8ecaab526 --- /dev/null +++ b/src/csp_buffer_private.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +/** + * Get a buffer or get killed (from task context) + * + * This function return a buffer or kill the whole program when it + * failed. DO NOT USE THIS FUNCTION if you don't know what you are + * doing. Never use this function from application layer. It is + * intended for internal use only and must not be used from + * application-level code. + * + * https://github.com/libcsp/libcsp/issues/864 + * + * @return Buffer (pointer to #csp_packet_t) + */ +csp_packet_t * csp_buffer_get_always(void); + +/** + * Get a buffer or get killed (from ISR context) + * @return Buffer (pointer to #csp_packet_t) + */ +csp_packet_t * csp_buffer_get_always_isr(void); diff --git a/src/interfaces/csp_if_can_pbuf.c b/src/interfaces/csp_if_can_pbuf.c index cf7641693..faca94e1e 100644 --- a/src/interfaces/csp_if_can_pbuf.c +++ b/src/interfaces/csp_if_can_pbuf.c @@ -7,6 +7,8 @@ #include #include +#include "../csp_buffer_private.h" + /* Buffer element timeout in ms */ #define PBUF_TIMEOUT_MS 1000 diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index b1dfd0d9e..300496eec 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -14,6 +14,8 @@ #include #include +#include "../csp_buffer_private.h" + #define FEND 0xC0 #define FESC 0xDB #define TFEND 0xDC diff --git a/unittests/buffer.c b/unittests/buffer.c index 0f98e954e..4a61f0f0a 100644 --- a/unittests/buffer.c +++ b/unittests/buffer.c @@ -2,6 +2,8 @@ #include "../include/csp/csp.h" #include "../include/csp/csp_id.h" +#include "../src/csp_buffer_private.h" + #define CSP_ID2_HEADER_SIZE 6 /* https://github.com/libcsp/libcsp/issues/734 */ @@ -42,7 +44,7 @@ START_TEST(test_clone_frame_begin_fixed) /* Simulate a packet with no header*/ memcpy(src->data, "hello", 6); src->length = 6; - + /* Add header to simulate a prepared to send packet */ csp_id_prepend(src); diff --git a/unittests/hmac.c b/unittests/hmac.c index afeab3faf..bdcb15a83 100644 --- a/unittests/hmac.c +++ b/unittests/hmac.c @@ -2,6 +2,7 @@ #include "../include/csp/csp.h" #include "../include/csp/csp_id.h" #include "../include/csp/crypto/csp_hmac.h" +#include "../src/csp_buffer_private.h" START_TEST(test_hmac_append_no_header) { From 516ee7e9da324d4efaf85479bcd7954e52f01920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Moreno?= Date: Wed, 24 Sep 2025 11:46:44 +0200 Subject: [PATCH 273/348] wrong text in LOG_ERR message in usart_zephyr drivers Just a minor typo in a LOG_ERR message. It says CONFIG_CSP_CAN_RX_THREAD_NUM when it should say CONFIG_CSP_UART_RX_THREAD_NUM. --- src/drivers/usart/usart_zephyr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/usart/usart_zephyr.c b/src/drivers/usart/usart_zephyr.c index 395710d21..f4df75445 100644 --- a/src/drivers/usart/usart_zephyr.c +++ b/src/drivers/usart/usart_zephyr.c @@ -99,7 +99,7 @@ int csp_usart_open(const csp_usart_conf_t * conf, csp_usart_callback_t rx_callba } if (uart_rx_thread_idx >= CONFIG_CSP_UART_RX_THREAD_NUM) { - LOG_ERR("%s: [%s] No more RX thread can be created. (MAX: %d) Please check CONFIG_CSP_CAN_RX_THREAD_NUM.", __func__, conf->device, CONFIG_CSP_UART_RX_THREAD_NUM); + LOG_ERR("%s: [%s] No more RX thread can be created. (MAX: %d) Please check CONFIG_CSP_UART_RX_THREAD_NUM.", __func__, conf->device, CONFIG_CSP_UART_RX_THREAD_NUM); return CSP_ERR_DRIVER; } From e9a37c66115bf0196586f916266e85cc224eee52 Mon Sep 17 00:00:00 2001 From: "Daniel M." Date: Wed, 1 Oct 2025 13:18:27 +0200 Subject: [PATCH 274/348] Fix typo Fix typo in README.md file: "it's" -> "its". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76be09863..242f2ce47 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ on many cubesats. single master node to several well defined services on the network The implementation of `libcsp` is written -with simplicity in mind, but it's compile time configuration allows it +with simplicity in mind, but its compile time configuration allows it to have some rather advanced features as well. ## Features From f8c366e5dfd9879990747d815ff5b2c2dda4dfe3 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 3 Oct 2025 15:42:32 +0900 Subject: [PATCH 275/348] zephyr: Conditionally include for POSIX time functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zephyr's POSIX time support has been updated (PR #95226) to provide a new header . This change ensures that can be included safely, even when a libc previously provided its own conflicting version. To maintain compatibility before and after the PR, this patch uses __has_include to prefer when is available, and falls back to otherwise. This prevents build errors caused by incompatible or incomplete implementations in some environments. References: – https://github.com/zephyrproject-rtos/zephyr/pull/95226 – https://github.com/zephyrproject-rtos/zephyr/discussions/96911 – https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html Signed-off-by: Yasushi SHOJI --- src/arch/zephyr/csp_clock.c | 7 ++++++- src/arch/zephyr/csp_zephyr_init.c | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/arch/zephyr/csp_clock.c b/src/arch/zephyr/csp_clock.c index d4e8f926b..060f8c201 100644 --- a/src/arch/zephyr/csp_clock.c +++ b/src/arch/zephyr/csp_clock.c @@ -1,6 +1,11 @@ #include #include -#include +/* https://github.com/zephyrproject-rtos/zephyr/discussions/96911*/ +#if __has_include() + #include +#else + #include +#endif #include LOG_MODULE_DECLARE(libcsp); diff --git a/src/arch/zephyr/csp_zephyr_init.c b/src/arch/zephyr/csp_zephyr_init.c index 32f1c6cbc..7d9d4f2bc 100644 --- a/src/arch/zephyr/csp_zephyr_init.c +++ b/src/arch/zephyr/csp_zephyr_init.c @@ -1,8 +1,11 @@ - - #include #include -#include +/* https://github.com/zephyrproject-rtos/zephyr/discussions/96911*/ +#if __has_include() + #include +#else + #include +#endif #include #include From 48f7fb0b57f610bf65bab1aa2d1357c3b9722782 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 3 Oct 2025 19:46:48 +0900 Subject: [PATCH 276/348] CHANGELOG: Update entries for v2.1 release Capture major changes in v2.1, including new features, improvements, and breaking changes. Avoid delaying the release for changelog updates. We can revise or expand entries later if needed. Signed-off-by: Yasushi SHOJI --- CHANGELOG | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index dac9ac560..ca9599bc8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,30 @@ libcsp 2.1, xx-yy-zzzz ---------------------- +- new: Add reproducible builds +- new: Move to reStructuredText and automated documentation deployment +- new: Add convenient macros like `__noinit`, `__weak`, `__packed`, etc. +- new: Add simple sample code under `samples/` +- new: Add separate `csp_server` and `csp_client` in addition to `csp_server_client` +- new: Add raw Ethernet driver for Linux +- new: Add CAN drivers for Zephyr +- new: Begin adding unit tests +- new: Enable `-Wpointer-arith` +- new: Enable `-Wpedantic` +- break: Rename `csp_autoconfig.h` to `build/include/csp/autoconfig.h` +- removed: Remove additional Windows and macOS support +- improvement: Update documentation +- improvement: Improve C++ support by adding `extern "C"` +- improvement: Improve Python support +- improvement: Update examples with various features +- improvement: Update support for FreeRTOS, POSIX, and Zephyr +- improvement: Update build systems: Waf, Meson, and CMake +- improvement: Update various libcsp core modules: + - bridge, conn, crc32, dedup, id, iflist, io, port, packet + - promisc, queue, rdp, route, service, sfp, yaml, and more +- improvement: Update various libcsp device drivers: + - can, socketcan, eth, usart +- improvement: Update various libcsp interface drivers: + - can, eth, kiss, tun, udp, zmq, loopback libcsp 2.0, 19-04-2024 ---------------------- From afc166106009b9d3b9f57d9c78b2681173079f0d Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 11 Oct 2025 18:38:39 +0900 Subject: [PATCH 277/348] CHANGELOG: Update release date Forgot to update the release date. We need a better release process. Signed-off-by: Yasushi SHOJI --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ca9599bc8..f27ae670a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -libcsp 2.1, xx-yy-zzzz +libcsp 2.1, 11-10-2025 ---------------------- - new: Add reproducible builds - new: Move to reStructuredText and automated documentation deployment From 0813f5c6a01dd0b7fd9a2c19c31ed26d4cdf3a2f Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 11 Oct 2025 18:47:43 +0900 Subject: [PATCH 278/348] Bump version to v2.2 Now that libcsp v2.1 is tagged, we can safely call this branch v2.2. Signed-off-by: Yasushi SHOJI --- CMakeLists.txt | 2 +- meson.build | 2 +- wscript | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d5c499e..11c23ea00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.20) -project(CSP VERSION 2.1) +project(CSP VERSION 2.2) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(DEFAULT_BUILD_SHARED_LIBS ON) diff --git a/meson.build b/meson.build index 6f738ff18..bc7f7e536 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('csp', 'c', version: '2.1', license: 'LGPL', meson_version : '>=0.53.2', default_options : [ +project('csp', 'c', version: '2.2', license: 'LGPL', meson_version : '>=0.53.2', default_options : [ 'c_std=gnu11', 'optimization=s', 'warning_level=3', diff --git a/wscript b/wscript index 10cdab6ad..f76a8fc2b 100644 --- a/wscript +++ b/wscript @@ -4,7 +4,7 @@ import os APPNAME = 'libcsp' -VERSION = '2.1' +VERSION = '2.2' valid_os = ['posix', 'freertos'] From 4ca6a1d27b4556743eaad3e45827878aae7405f5 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 16 Oct 2025 13:35:21 +0900 Subject: [PATCH 279/348] github: workflow: Add Python 3.14 This update extends the GitHub Actions workflow to test against Python 3.14, ensuring compatibility with the latest Python versions. Signed-off-by: Gaetan Perrot --- .github/workflows/build-test-python.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 96c127fe7..fa374e263 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -13,6 +13,7 @@ jobs: - '3.11' - '3.12' - '3.13' + - '3.14' os: - ubuntu-24.04 - ubuntu-22.04 From 6d167a696a891ae0b7719415b517cd41db41852a Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 16 Oct 2025 14:22:20 +0900 Subject: [PATCH 280/348] contrib: zephyr: Fix Kconfig warnings related to POSIX_TIMERS The build failed due to Kconfig dependency conflicts caused by LIBCSP selecting POSIX_TIMERS and POSIX_MONOTONIC_CLOCK without satisfying their direct dependency on POSIX_SYSTEM_INTERFACES. This change ensures that POSIX_SYSTEM_INTERFACES is enabled when LIBCSP is selected, preventing the following warnings and build abort: - POSIX_TIMERS has direct dependencies POSIX_SYSTEM_INTERFACES with value n - POSIX_MONOTONIC_CLOCK has direct dependencies POSIX_TIMERS && POSIX_SYSTEM_INTERFACES with value n Remove POSIX_MONOTONIC_CLOCK and enable POSIX_SYSTEM_INTERFACES when selecting LIBCSP. Signed-off-by: Gaetan Perrot --- contrib/zephyr/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index 24e699db0..3ba5585f8 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -6,7 +6,7 @@ config LIBCSP bool "Enable Cubesat Space Protocol Support" select POSIX_TIMERS - select POSIX_MONOTONIC_CLOCK + select POSIX_SYSTEM_INTERFACES help This option enables the Cubesat Space Protocol (CSP) library. From 9adf571b95ffc10f6a2ef27d075e16aacfe5d160 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 24 Oct 2025 17:54:57 +0300 Subject: [PATCH 281/348] csp_sfp: Remove malloc usage Removes the malloc usage by introducing a more generic interface for reading/writing from/to storage. The protocol functionality is not modified, so it remains fully backward compatible. --- doc/api/csp_error_h.rst | 1 + doc/api/csp_sfp_h.rst | 3 +- include/csp/csp_error.h | 1 + include/csp/csp_sfp.h | 114 ++++++++++++++++++++++++++---------- src/csp_sfp.c | 124 ++++++++++++++++++++++++++++++++-------- 5 files changed, 189 insertions(+), 54 deletions(-) diff --git a/doc/api/csp_error_h.rst b/doc/api/csp_error_h.rst index deb5322ad..7d6803d04 100644 --- a/doc/api/csp_error_h.rst +++ b/doc/api/csp_error_h.rst @@ -20,3 +20,4 @@ Error Codes .. autocmacro:: csp_error.h::CSP_ERR_HMAC .. autocmacro:: csp_error.h::CSP_ERR_CRC32 .. autocmacro:: csp_error.h::CSP_ERR_SFP +.. autocmacro:: csp_error.h::CSP_ERR_MTU diff --git a/doc/api/csp_sfp_h.rst b/doc/api/csp_sfp_h.rst index d9a917561..88dbb0304 100644 --- a/doc/api/csp_sfp_h.rst +++ b/doc/api/csp_sfp_h.rst @@ -6,7 +6,8 @@ Small Fragmentation Protocol (SFP) Interface Functions ------------------- -.. autocfunction:: csp_sfp.h::csp_sfp_send_own_memcpy +.. autocfunction:: csp_sfp.h::csp_sfp_opts_max_mtu +.. autocfunction:: csp_sfp.h::csp_sfp_conn_max_mtu .. autocfunction:: csp_sfp.h::csp_sfp_send .. autocfunction:: csp_sfp.h::csp_sfp_recv_fp .. autocfunction:: csp_sfp.h::csp_sfp_recv diff --git a/include/csp/csp_error.h b/include/csp/csp_error.h index 26a8da621..f72a813e1 100644 --- a/include/csp/csp_error.h +++ b/include/csp/csp_error.h @@ -26,4 +26,5 @@ #define CSP_ERR_HMAC -100 /**< HMAC failed */ #define CSP_ERR_CRC32 -102 /**< CRC32 failed */ #define CSP_ERR_SFP -103 /**< SFP protocol error or inconsistency */ +#define CSP_ERR_MTU -104 /**< Invalid MTU */ /**@}*/ diff --git a/include/csp/csp_sfp.h b/include/csp/csp_sfp.h index 33a01af4c..cadc9db2b 100644 --- a/include/csp/csp_sfp.h +++ b/include/csp/csp_sfp.h @@ -21,40 +21,98 @@ extern "C" { /** - * Send data over a CSP connection. - * - * Data will be send in chunks of \a mtu bytes. The MTU must be small enough to fit - * into a CSP packat + SFP header + other transport headers. - * - * csp_sfp_recv() or csp_sfp_recv_fp() can be used at the other end to receive data. + * Structure to be passed as an input parameter to csp_sfp_send(...). + * This structure encapsulates user-defined data and a function for reading data from storage. + */ +typedef struct { + /** + * User-defined data. SFP does not interpret or manipulate this data. + * It can be any user-defined data such as a file handle, a raw buffer pointer, or any other + * relevant information required for the implementation. NULL is accepted. + */ + void * data; + + /** + * User-defined function for reading data from a storage medium. + * + * This callback allows users to define a custom mechanism for retrieving data, such as reading + * from a file, memory buffer, or another type of storage. The SFP layer invokes this function + * during data transmission. + * + * @param[out] buffer Pointer to the buffer where the read data should be stored. This pointer is always valid. + * @param[in] size The number of bytes to read. Guaranteed to be greater than 0. + * @param[in] offset The offset in the source storage from where the data should be read. This allows + * the user to retrieve data from specific positions, such as file offsets or + * buffer indices. + * @param[in] data Pointer to user-specific data provided by the caller (e.g., a file handle, buffer state, + * or context). This pointer is optional and may be NULL, depending on the user implementation. + * @return #CSP_ERR_NONE on success, otherwise an error. + */ + int (* read)(uint8_t * buffer, uint32_t size, uint32_t offset, void * data); +} csp_sfp_read_t; + +/** + * Structure to be passed as an input parameter to csp_sfp_recv(...). + * This structure encapsulates user-defined data and a function for writing data to storage. + */ +typedef struct { + /** + * User-defined data. SFP does not interpret or manipulate this data. + * It can be any user-defined data such as a file handle, a raw buffer pointer, or any other + * relevant information required for the implementation. NULL is accepted. + */ + void * data; + + /** + * User-defined function for writing data to a storage medium. + * + * This callback allows users to define a custom mechanism for handling incoming data, + * such as writing it to a file, buffer, or another type of storage. The SFP layer + * invokes this function during data reception. + * + * @param[in] buffer Pointer to the buffer containing the data to be written. This pointer is always valid. + * @param[in] size The number of bytes to write. Guaranteed to be greater than 0. + * @param[in] offset The offset in the destination storage where the data should be written. This + * allows the user to place data at specific locations, such as file positions + * or buffer indices. + * @param[in] totalsz The total expected size of the incoming data. This value is consistent + * across all calls to this function during a single transfer and can be used + * for validation or progress tracking. + * @param[in] data Pointer to user-specific data provided by the caller (e.g., a file handle, + * buffer state, or context). This pointer is optional and may be NULL, depending + * on the user implementation. + * @return #CSP_ERR_NONE on success, otherwise an error. + */ + int (* write)(const uint8_t * buffer, uint32_t size, uint32_t offset, uint32_t totalsz, void * data); +} csp_sfp_recv_t; + +/** + * Get the maximum MTU (Maximum Transmission Unit) for options. * - * This is useful if you wish to send data stored in flash memory or another location, where standard memcpy() doesn't work. + * @param opts Options indicating required protocol features (RDP, CRC, etc.). + * @return The maximum MTU in bytes. + */ +uint32_t csp_sfp_opts_max_mtu(uint32_t opts); + +/** + * Get the maximum MTU (Maximum Transmission Unit) for a connection. * - * @param[in] conn established connection for sending SFP packets. - * @param[in] data data to send - * @param[in] datasize tsize of \a data - * @param[in] mtu maximum transfer unit (bytes), max data chunk to send. - * @param[in] timeout unused as of CSP version 1.6 - * @param[in] memcpyfcn memory copy function. - * @return #CSP_ERR_NONE on success, otherwise an error. + * @param conn Connection indicating required protocol features (RDP, CRC, etc.). + * @return The maximum MTU in bytes. */ -int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int datasize, unsigned int mtu, uint32_t timeout, csp_memcpy_fnc_t memcpyfcn); +uint32_t csp_sfp_conn_max_mtu(const csp_conn_t * conn); /** * Send data over a CSP connection. * - * Uses csp_sfp_send_own_memcpy() with standard memcpy(). - * * @param[in] conn established connection for sending SFP packets. - * @param[in] data data to send - * @param[in] datasize size of \a data + * @param[in] user User-defined read function and data pointer. + * @param[in] datasize Total size to send. * @param[in] mtu maximum transfer unit (bytes), max data chunk to send. * @param[in] timeout unused as of CSP version 1.6 * @return #CSP_ERR_NONE on success, otherwise an error. */ -static inline int csp_sfp_send(csp_conn_t * conn, const void * data, unsigned int datasize, unsigned int mtu, uint32_t timeout) { - return csp_sfp_send_own_memcpy(conn, data, datasize, mtu, timeout, (csp_memcpy_fnc_t) &memcpy); -} +int csp_sfp_send(csp_conn_t * conn, const csp_sfp_read_t * user, uint32_t datasize, uint32_t mtu, uint32_t timeout); /** * Receive data over a CSP connection. @@ -70,22 +128,20 @@ static inline int csp_sfp_send(csp_conn_t * conn, const void * data, unsigned in * Use NULL to receive first packet on the connection. * @return #CSP_ERR_NONE on success, otherwise an error. */ -int csp_sfp_recv_fp(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout, csp_packet_t * first_packet); +int csp_sfp_recv_fp(csp_conn_t * conn, const csp_sfp_recv_t * user, uint32_t timeout, csp_packet_t * first_packet); /** * Receive data over a CSP connection. * - * This is the counterpart to the csp_sfp_send() and csp_sfp_send_own_memcpy(). + * This is the counterpart to the csp_sfp_send() * * @param[in] conn established connection for receiving SFP packets. - * @param[out] dataout received data on success. Allocated with malloc(), so - * should be freed with free(). The pointer will be NULL on failure. - * @param[out] datasize size of received data. + * @param[in] user User-defined data with write function and data pointer. * @param[in] timeout timeout in ms to wait for csp_read() * @return #CSP_ERR_NONE on success, otherwise an error. */ -static inline int csp_sfp_recv(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout) { - return csp_sfp_recv_fp(conn, dataout, datasize, timeout, NULL); +static inline int csp_sfp_recv(csp_conn_t * conn, const csp_sfp_recv_t * user, uint32_t timeout) { + return csp_sfp_recv_fp(conn, user, timeout, NULL); } #ifdef __cplusplus diff --git a/src/csp_sfp.c b/src/csp_sfp.c index fbc54581d..27d75508e 100644 --- a/src/csp_sfp.c +++ b/src/csp_sfp.c @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include "csp_macro.h" #include @@ -48,14 +50,55 @@ static inline sfp_header_t * csp_sfp_header_remove(csp_packet_t * packet) { return header; } -int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int totalsize, unsigned int mtu, uint32_t timeout, csp_memcpy_fnc_t memcpyfcn) { - (void)timeout; /* Avoid compiler warnings about unused parameter */ +uint32_t csp_sfp_opts_max_mtu(uint32_t opts) { + uint32_t overhead = 0; + + /* If RDP is set, we must take RDP header into account. */ + if (opts & CSP_O_RDP) { + overhead += CSP_RDP_HEADER_SIZE; + } + + /* If CRC is set, we must take CRC size into account. */ + if (opts & CSP_O_CRC32) { + overhead += sizeof(csp_crc32_t); + } + + /* If HMAC is set, we must take HMAC header into account. */ + if (opts & CSP_O_HMAC) { + overhead += CSP_HMAC_LENGTH; + } + + /* Add SFP header size always */ + overhead += sizeof(sfp_header_t); + + return CSP_BUFFER_SIZE - overhead; +} + +uint32_t csp_sfp_conn_max_mtu(const csp_conn_t * conn) { + uint32_t max_mtu = 0; - if (mtu == 0 || mtu + sizeof(sfp_header_t) > CSP_BUFFER_SIZE) { + if (NULL != conn) { + max_mtu = csp_sfp_opts_max_mtu(conn->opts); + } + + return max_mtu; +} + +int csp_sfp_send(csp_conn_t * conn, const csp_sfp_read_t * user, uint32_t totalsize, uint32_t mtu, uint32_t timeout) { + (void)timeout; + + if ((NULL == conn) || (NULL == user) || (NULL == user->read)) { return CSP_ERR_INVAL; + } else { + uint32_t max_mtu = csp_sfp_conn_max_mtu(conn); + + if ((mtu > max_mtu) || (0 == mtu)) { + return CSP_ERR_MTU; + } } - unsigned int count = 0; + int error = CSP_ERR_NONE; + uint32_t count = 0; while ((count < totalsize) && csp_conn_is_active(conn)) { sfp_header_t * sfp_header; @@ -67,7 +110,7 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t } /* Calculate sending size */ - unsigned int size = totalsize - count; + uint32_t size = totalsize - count; if (size > mtu) { size = mtu; } @@ -76,7 +119,12 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t //csp_print("%s: %d:%d, sending at %p size %u\n", __func__, csp_conn_src(conn), csp_conn_sport(conn), (void *)((uint8_t *)data + count), size); /* Copy data */ - (memcpyfcn)((csp_memptr_t)(uintptr_t)packet->data, (csp_memptr_t)(uintptr_t)(((uint8_t *)data) + count), size); + error = user->read(packet->data, size, count, user->data); + if (CSP_ERR_NONE != error) { + csp_buffer_free(packet); + return error; + } + packet->length = size; /* Set fragment flag */ @@ -97,10 +145,11 @@ int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, unsigned int t return CSP_ERR_NONE; } -int csp_sfp_recv_fp(csp_conn_t * conn, void ** return_data, int * return_datasize, uint32_t timeout, csp_packet_t * first_packet) { +int csp_sfp_recv_fp(csp_conn_t * conn, const csp_sfp_recv_t * user, uint32_t timeout, csp_packet_t * first_packet) { - *return_data = NULL; /* Allow caller to assume csp_free() can always be called when dataout is non-NULL */ - *return_datasize = 0; + if ((NULL == conn) || (NULL == user) || (NULL == user->write)) { + return CSP_ERR_INVAL; + } /* Get first packet from user, or from connection */ csp_packet_t * packet; @@ -113,7 +162,7 @@ int csp_sfp_recv_fp(csp_conn_t * conn, void ** return_data, int * return_datasiz packet = first_packet; } - uint8_t * data = NULL; + const uint32_t max_mtu = csp_sfp_conn_max_mtu(conn); uint32_t datasize = 0; uint32_t data_offset = 0; int error = CSP_ERR_TIMEDOUT; @@ -139,19 +188,44 @@ int csp_sfp_recv_fp(csp_conn_t * conn, void ** return_data, int * return_datasiz goto error; } - /* Allocate memory */ - if (data == NULL) { + /* Ensure the packet length does not exceed maximum MTU. We can't receive length > MAX MTU. + * Ensure packet length is > 0. We can't receive length <= 0 */ + if ((max_mtu < packet->length) || (0 >= packet->length)) { + csp_buffer_free(packet); + + error = CSP_ERR_SFP; + goto error; + } + + /* Check if the total size in the SFP header is zero, which is invalid for data transfer. */ + if (0 == sfp_header->totalsize) { + csp_buffer_free(packet); + + error = CSP_ERR_SFP; + goto error; + } + + /* Set total expected size. This is done only on the 1st iteration. */ + if (datasize == 0) { datasize = sfp_header->totalsize; - data = malloc(datasize); - if (data == NULL) { - //csp_print("%s: %u:%u, malloc(%" PRIu32 ") failed\n", __func__, packet->id.src, packet->id.sport, datasize); - csp_buffer_free(packet); - - error = CSP_ERR_NOMEM; - goto error; - } } + /* Mismatch in total size */ + if (datasize != sfp_header->totalsize) { + csp_buffer_free(packet); + + error = CSP_ERR_SFP; + goto error; + } + + /* Ensure the offset does not exceed the expected total size. */ + if (sfp_header->offset > (datasize - packet->length)) { + csp_buffer_free(packet); + + error = CSP_ERR_SFP; + goto error; + } + /* Consistency check */ if (((data_offset + packet->length) > datasize) || (datasize != sfp_header->totalsize)) { //csp_print("%s: %u:%u, invalid size, sfp.offset: %" PRIu32 ", length: %u, total: %" PRIu32 " / %" PRIu32 "\n", __func__, packet->id.src, packet->id.sport, sfp_header->offset, packet->length, datasize, sfp_header->totalsize); @@ -162,15 +236,18 @@ int csp_sfp_recv_fp(csp_conn_t * conn, void ** return_data, int * return_datasiz } /* Copy data to output */ - memcpy(data + data_offset, packet->data, packet->length); + error = user->write(packet->data, packet->length, data_offset, datasize, user->data); + if (CSP_ERR_NONE != error) { + csp_buffer_free(packet); + goto error; + } + data_offset += packet->length; if (data_offset >= datasize) { // transfer complete csp_buffer_free(packet); - *return_data = data; // must be freed by csp_free() - *return_datasize = datasize; return CSP_ERR_NONE; } @@ -188,6 +265,5 @@ int csp_sfp_recv_fp(csp_conn_t * conn, void ** return_data, int * return_datasiz } while ((packet = csp_read(conn, timeout)) != NULL); error: - free(data); return error; } From 6d5f9a119f84296465b3cc0a40326316369c1d03 Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 24 Oct 2025 17:57:38 +0300 Subject: [PATCH 282/348] examples: Add SFP protocol test Introduce a test for the SFP protocol using CSP in loopback mode. The test sends random data to a client, verifying functionality by comparing the received size with the sent size and ensuring CRCs match on both ends. --- .github/workflows/build-test.yml | 11 + examples/CMakeLists.txt | 3 + examples/buildall.py | 3 +- examples/csp_sfp_server_client.c | 334 +++++++++++++++++++++++++++++++ examples/meson.build | 7 + wscript | 4 + 6 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 examples/csp_sfp_server_client.c diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ffdaa2e1b..164502194 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -135,3 +135,14 @@ jobs: run: | ./build/examples/csp_client -u "127.0.0.1" -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & ./build/examples/csp_server -u "127.0.0.1" -a 1 -T 10 -v ${{ matrix.csp_version }} + + - name: Run SFP Test + run: | + ./build/examples/csp_sfp_server_client --mtu 1 --size 1 + ./build/examples/csp_sfp_server_client --mtu 1 --size 128 + ./build/examples/csp_sfp_server_client --mtu 128 --size 1 + ./build/examples/csp_sfp_server_client --mtu 128 --size 10 + ./build/examples/csp_sfp_server_client --mtu 128 --size 100 + ./build/examples/csp_sfp_server_client --mtu 128 --size 1000 + ./build/examples/csp_sfp_server_client --mtu 128 --size 10000 + ./build/examples/csp_sfp_server_client --mtu 128 --size 100000 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 41174d5e3..ba76fbcda 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,7 @@ if(CSP_POSIX) add_executable(csp_client ${CSP_SAMPLES_EXCLUDE} csp_client.c) add_executable(csp_bridge_can2udp ${CSP_SAMPLES_EXCLUDE} csp_bridge_can2udp.c) add_executable(zmqproxy ${CSP_SAMPLES_EXCLUDE} zmqproxy.c) + add_executable(csp_sfp_server_client ${CSP_SAMPLES_EXCLUDE} csp_sfp_server_client.c) target_include_directories(csp_posix_helper PRIVATE ${csp_inc}) target_include_directories(csp_arch PRIVATE ${csp_inc}) @@ -14,6 +15,7 @@ if(CSP_POSIX) target_include_directories(csp_server PRIVATE ${csp_inc}) target_include_directories(csp_client PRIVATE ${csp_inc}) target_include_directories(zmqproxy PRIVATE ${csp_inc} ${LIBZMQ_INCLUDE_DIRS}) + target_include_directories(csp_sfp_server_client PRIVATE ${csp_inc}) target_link_libraries(csp_posix_helper PRIVATE csp_common) target_link_libraries(csp_arch PRIVATE csp csp_common) @@ -22,4 +24,5 @@ if(CSP_POSIX) target_link_libraries(csp_client PRIVATE csp csp_common csp_posix_helper Threads::Threads) target_link_libraries(csp_bridge_can2udp PRIVATE csp csp_common) target_link_libraries(zmqproxy PRIVATE csp csp_common Threads::Threads ${LIBZMQ_LIBRARIES}) + target_link_libraries(csp_sfp_server_client PRIVATE csp Threads::Threads) endif() diff --git a/examples/buildall.py b/examples/buildall.py index 44527cc3b..e25f0916a 100755 --- a/examples/buildall.py +++ b/examples/buildall.py @@ -14,7 +14,8 @@ def build_with_meson(): 'examples/csp_client', 'examples/csp_bridge_can2udp', 'examples/csp_arch', - 'examples/zmqproxy'] + 'examples/zmqproxy', + 'examples/csp_sfp_server_client'] builddir = 'build' meson_setup = ['meson', 'setup', builddir] diff --git a/examples/csp_sfp_server_client.c b/examples/csp_sfp_server_client.c new file mode 100644 index 000000000..6ad3bfd3a --- /dev/null +++ b/examples/csp_sfp_server_client.c @@ -0,0 +1,334 @@ +/* needed for pthread_timedjoin_np */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RECEIVER_ADDRESS 0 +#define RECEIVER_PORT 10 +#define TIMEOUT 10000 /* in ms */ +#define perr(fmt, ...) do { fprintf(stderr, "line %d: " fmt "\n", __LINE__, ##__VA_ARGS__); fflush(stderr); } while (0) +#define print(fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); fflush(stderr); } while (0) + +typedef struct { + uint32_t size; + uint32_t crc; +} sfp_data_t; + +typedef struct { + int size; + int timeout; + int rdp; + int mtu; +} test_options_t; + +static test_options_t test_options = { .size = 1000000, .rdp = 0, .timeout = 30, .mtu = 128 }; +static uint32_t sender_crc = 0; +static uint32_t receiver_crc = 0xffffffff; +static uint32_t received_sz = 0; + +static const char * csp_error_to_str(int err) { + switch (err) { + case CSP_ERR_NONE: return "No error"; + case CSP_ERR_NOMEM: return "Not enough memory"; + case CSP_ERR_INVAL: return "Invalid argument"; + case CSP_ERR_TIMEDOUT: return "Operation timed out"; + case CSP_ERR_USED: return "Resource already in use"; + case CSP_ERR_NOTSUP: return "Operation not supported"; + case CSP_ERR_BUSY: return "Device or resource busy"; + case CSP_ERR_ALREADY: return "Connection already in progress"; + case CSP_ERR_RESET: return "Connection reset"; + case CSP_ERR_NOBUFS: return "No more buffer space available"; + case CSP_ERR_TX: return "Transmission failed"; + case CSP_ERR_DRIVER: return "Error in driver layer"; + case CSP_ERR_AGAIN: return "Resource temporarily unavailable"; + case CSP_ERR_NOSYS: return "Function not implemented"; + case CSP_ERR_HMAC: return "HMAC failed"; + case CSP_ERR_CRC32: return "CRC32 failed"; + case CSP_ERR_SFP: return "SFP protocol error or inconsistency"; + case CSP_ERR_MTU: return "Invalid MTU"; + default: return "Unknown error"; + } +} + +static void print_help(const char * program_name) { + uint32_t max_mtu_nordp = csp_sfp_opts_max_mtu(CSP_O_CRC32); + uint32_t max_mtu_rdp = csp_sfp_opts_max_mtu(CSP_O_CRC32 | CSP_O_RDP); + + print("Usage: %s [OPTIONS]", program_name); + print("Options:"); + print(" -r,--rdp=VALUE Enable/Disable (1 or 0) RDP mode (optional)."); + print(" Default is %s", test_options.rdp ? "Enabled" : "Disabled"); + print(" -t,--timeout=SECONDS Set the timeout in seconds (optional)."); + print(" Values above INT_MAX might crash the program."); + print(" Default is %d seconds", test_options.timeout); + print(" -s,--size=BYTES Set the total transfer size in bytes (optional)."); + print(" Values above INT_MAX might crash the program"); + print(" Default is %d bytes", test_options.size); + print(" -m,--mtu=BYTES Set the maximum transfer unit in bytes (optional)."); + print(" Values above INT_MAX might crash the program"); + print(" Default is %d bytes", test_options.mtu); + print(" Max mtu with RDP disabled is %d bytes", max_mtu_nordp); + print(" Max mtu with RDP enabled is %d bytes", max_mtu_rdp); + print(" -h,--help Show this help message."); +} + +static void process_args(int argc, char * argv[], test_options_t * test_opts) { + static struct option long_options[] = { + { "rdp", required_argument, NULL, 'r' }, + { "timeout", required_argument, NULL, 't' }, + { "size", required_argument, NULL, 's' }, + { "mtu", required_argument, NULL, 'm' }, + { "help", no_argument, NULL, 'h' }, + { NULL, no_argument, NULL, 0 } /* Terminator */ + }; + + int opt; + + while ((opt = getopt_long(argc, argv, "r:t:s:m:h", long_options, NULL)) != -1) { + switch (opt) { + case 'r': + test_opts->rdp = atoi(optarg); + if (test_opts->rdp < 0) { + perr("'rdp' must be non-negative."); + exit(EXIT_FAILURE); + } + break; + + case 't': + test_opts->timeout = atoi(optarg); + if (test_opts->timeout < 0) { + perr("'timeout' must be non-negative."); + exit(EXIT_FAILURE); + } + break; + + case 's': + test_opts->size = atoi(optarg); + if (test_opts->size < 0) { + perr("'size' must be non-negative."); + exit(EXIT_FAILURE); + } + break; + + case 'm': + test_opts->mtu = atoi(optarg); + if (test_opts->mtu < 0) { + perr("'mtu' must be non-negative."); + exit(EXIT_FAILURE); + } + break; + + case 'h': + print_help(argv[0]); + exit(EXIT_SUCCESS); + + default: /* Invalid option */ + print_help(argv[0]); + exit(EXIT_FAILURE); + } + } +} + +static int read_from_buffer(uint8_t * buffer, uint32_t size, uint32_t offset, void * data) { + (void)offset; + + sfp_data_t * d = (sfp_data_t *)data; + + /* Seed the random number generator */ + srand(time(NULL)); + + /* Fill the array with random values */ + for (uint32_t i = 0; i < size; i++) { + buffer[i] = (uint8_t)(rand() % 256); /* Assign a random number to each element */ + } + + /* update crc */ + csp_crc32_update(&d->crc, buffer, size); + + return CSP_ERR_NONE; +} + +static int write_to_buffer(const uint8_t * buffer, uint32_t size, uint32_t offset, uint32_t totalsz, void * data) { + (void)offset; + (void)totalsz; + + sfp_data_t * d = (sfp_data_t *)data; + + /* update crc */ + csp_crc32_update(&d->crc, buffer, size); + + /* update counter */ + received_sz += size; + + return CSP_ERR_NONE; +} + +static void * router(void * params) { + (void)params; + + while (1) { + (void)csp_route_work(); + } + + return NULL; +} + +static void * sender(void * params) { + const test_options_t * test_opts = (test_options_t *)params; + sfp_data_t data; + csp_crc32_init(&data.crc); + csp_conn_t * conn = NULL; + uint32_t opts = CSP_O_CRC32; + opts |= test_opts->rdp ? CSP_O_RDP : 0; + + /* Connect to receiver */ + conn = csp_connect(CSP_PRIO_NORM, RECEIVER_ADDRESS, RECEIVER_PORT, TIMEOUT, opts); + if (NULL == conn) { + perr("Failed csp_connect"); + goto exit; + } + + csp_sfp_read_t user; + user.data = &data; + user.read = read_from_buffer; + + /* Send data */ + int ret = csp_sfp_send(conn, &user, test_opts->size, test_opts->mtu, 0); + if (CSP_ERR_NONE != ret) { + perr("Failed csp_sfp_send dew to: %s", csp_error_to_str(ret)); + goto exit; + } + + sender_crc = csp_crc32_final(&data.crc); +exit: + csp_close(conn); + + return NULL; +} + +static void * receiver(void * params) { + const test_options_t * test_opts = (test_options_t *)params; + sfp_data_t data; + csp_crc32_init(&data.crc); + csp_conn_t * conn = NULL; + + csp_socket_t sock = {0}; + sock.opts |= CSP_SO_CRC32REQ; + sock.opts |= test_opts->rdp ? CSP_SO_RDPREQ : 0; + csp_listen(&sock, 0); + csp_bind(&sock, CSP_ANY); + + conn = csp_accept(&sock, TIMEOUT); + if (!conn) { + perr("Failed csp_accept"); + goto exit; + } + + csp_sfp_recv_t user; + user.data = &data; + user.write = write_to_buffer; + + /* Send data */ + int ret = csp_sfp_recv(conn, &user, 1000); + if (CSP_ERR_NONE != ret) { + perr("Failed csp_sfp_recv dew to: %s", csp_error_to_str(ret)); + goto exit; + } + + receiver_crc = csp_crc32_final(&data.crc); +exit: + csp_close(conn); + + return NULL; +} + +static int loopback_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + (void)iface; + (void)via; + (void)from_me; + + /* add some sleep to avoid starving the system when RDP is not used */ + if (!test_options.rdp) + usleep(1000); + csp_qfifo_write(packet, &csp_if_lo, NULL); + return CSP_ERR_NONE; +} + +int main(int argc, char * argv[]) { + process_args(argc, argv, &test_options); + csp_init(); + + csp_if_lo.is_default = 1; + csp_if_lo.nexthop = loopback_tx; /* replace default tx function */ + + pthread_t router_thread; + if (0 != pthread_create(&router_thread, NULL, router, NULL)) { + perr("Failed to create thread"); + exit(EXIT_FAILURE); + } + + pthread_t receiver_thread; + if (0 != pthread_create(&receiver_thread, NULL, receiver, &test_options)) { + perr("Failed to create thread"); + exit(EXIT_FAILURE); + } + + pthread_t sender_thread; + if (0 != pthread_create(&sender_thread, NULL, sender, &test_options)) { + perr("Failed to create thread"); + exit(EXIT_FAILURE); + } + + /* wait for sender to finish */ + struct timespec timeout; + if (0 != clock_gettime(CLOCK_REALTIME, &timeout)) { + perr("Failed to get time"); + exit(EXIT_FAILURE); + } + timeout.tv_sec += test_options.timeout; + timeout.tv_nsec = 0; + if (0 != pthread_timedjoin_np(sender_thread, NULL, &timeout)) { + perr("Sender timeout"); + exit(EXIT_FAILURE); + } + + /* wait for receiver to finish */ + if (0 != clock_gettime(CLOCK_REALTIME, &timeout)) { + perr("Failed to get time"); + exit(EXIT_FAILURE); + } + timeout.tv_sec += 2; + timeout.tv_nsec = 0; + if (0 != pthread_timedjoin_np(receiver_thread, NULL, &timeout)) { + perr("Receiver timeout"); + exit(EXIT_FAILURE); + } + + /* compare calculated CRCs */ + if (sender_crc != receiver_crc) { + perr("CRC mismatch"); + exit(EXIT_FAILURE); + } + + if ((uint32_t)test_options.size != received_sz) { + perr("SIZE mismatch"); + exit(EXIT_FAILURE); + } + + printf("Test completed!\n"); + + exit(EXIT_SUCCESS); +} diff --git a/examples/meson.build b/examples/meson.build index 4885054df..3f673cc55 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -47,3 +47,10 @@ executable('zmqproxy', c_args : csp_c_args, dependencies : csp_dep, build_by_default : false) + +executable('csp_sfp_server_client', + 'csp_sfp_server_client.c', + include_directories : csp_inc, + c_args : csp_c_args, + dependencies : csp_dep, + build_by_default : false) diff --git a/wscript b/wscript index f76a8fc2b..beb96fbc3 100644 --- a/wscript +++ b/wscript @@ -283,6 +283,10 @@ def build(ctx): lib=ctx.env.LIBS, use='csp') + ctx.program(source=['examples/csp_sfp_server_client.c'], + target='examples/csp_sfp_server_client', + lib=ctx.env.LIBS, + use='csp') def dist(ctx): ctx.excl = 'build/* **/.* **/*.pyc **/*.o **/*~ *.tar.gz' From 7521878101a0d8444aede218ea8c737ada92851a Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 24 Oct 2025 18:00:06 +0300 Subject: [PATCH 283/348] samples: posix: Add SFP protocol sample Introduce a sample demonstrating the use of SFP protocol for file transfer. --- samples/posix/CMakeLists.txt | 1 + .../posix/simple-sfp-send-recv/CMakeLists.txt | 3 + samples/posix/simple-sfp-send-recv/README.md | 20 ++ samples/posix/simple-sfp-send-recv/src/main.c | 207 ++++++++++++++++++ 4 files changed, 231 insertions(+) create mode 100644 samples/posix/simple-sfp-send-recv/CMakeLists.txt create mode 100644 samples/posix/simple-sfp-send-recv/README.md create mode 100644 samples/posix/simple-sfp-send-recv/src/main.c diff --git a/samples/posix/CMakeLists.txt b/samples/posix/CMakeLists.txt index e9ae7adc7..d8d90678c 100644 --- a/samples/posix/CMakeLists.txt +++ b/samples/posix/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(simple-send-usart) add_subdirectory(hmac) add_subdirectory(simple-send-udp) add_subdirectory(simple-send-zmq) +add_subdirectory(simple-sfp-send-recv) diff --git a/samples/posix/simple-sfp-send-recv/CMakeLists.txt b/samples/posix/simple-sfp-send-recv/CMakeLists.txt new file mode 100644 index 000000000..f26c710fb --- /dev/null +++ b/samples/posix/simple-sfp-send-recv/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(simple-sfp-send-recv ${CSP_SAMPLES_EXCLUDE} src/main.c) +target_include_directories(simple-sfp-send-recv PRIVATE ${csp_inc}) +target_link_libraries(simple-sfp-send-recv PRIVATE csp Threads::Threads) diff --git a/samples/posix/simple-sfp-send-recv/README.md b/samples/posix/simple-sfp-send-recv/README.md new file mode 100644 index 000000000..8cd1696e3 --- /dev/null +++ b/samples/posix/simple-sfp-send-recv/README.md @@ -0,0 +1,20 @@ +# Simple Send via USART + +This is a simple sample code to send and receive a file using SFP over LOOPBACK interface. + +## How to Build + +``` +$ cmake -B builddir +$ ninja -C builddir simple-sfp-send-recv +``` + +## How to Test + +In a terminal, run `simple-sfp-send-recv` + +``` +./builddir/samples/posix/simple-sfp-send-recv/simple-sfp-send-recv existing_file_on_system.txt file_to_create_on_system.txt +``` + +If it runs successfully, the program exits with 0. diff --git a/samples/posix/simple-sfp-send-recv/src/main.c b/samples/posix/simple-sfp-send-recv/src/main.c new file mode 100644 index 000000000..a7389fee0 --- /dev/null +++ b/samples/posix/simple-sfp-send-recv/src/main.c @@ -0,0 +1,207 @@ +/* needed for pthread_timedjoin_np */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RECEIVER_ADDRESS 0 +#define RECEIVER_PORT 10 +#define CONN_OPTS CSP_O_CRC32 +#define TIMEOUT 10000 /* in ms */ +#define perr(fmt, ...) do { fprintf(stderr, "line %d: " fmt "\n", __LINE__, ##__VA_ARGS__); fflush(stderr); } while (0) + +typedef struct { + FILE * handle; + char * name; + off_t size; +} sfp_file_t; + +static sfp_file_t send_file; +static sfp_file_t recv_file; + +static void * router(void * params) { + (void)params; + + while (1) { + (void)csp_route_work(); + } + + return NULL; +} + +static int read_from_file(uint8_t * buffer, uint32_t size, uint32_t offset, void * data) { + (void)offset; + sfp_file_t * file = (sfp_file_t *)data; + if (size == fread(buffer, 1, size, file->handle)) + return CSP_ERR_NONE; + return CSP_ERR_SFP; +} + +static int write_to_file(const uint8_t * buffer, uint32_t size, uint32_t offset, uint32_t totalsz, void * data) { + (void)offset; + (void)totalsz; + sfp_file_t * file = (sfp_file_t *)data; + if (size == fwrite(buffer, 1, size, file->handle)) + return CSP_ERR_NONE; + return CSP_ERR_SFP; +} + +static void * sender(void * params) { + csp_conn_t * conn = NULL; + sfp_file_t * file = (sfp_file_t *)params; + + /* Connect to receiver */ + conn = csp_connect(CSP_PRIO_NORM, RECEIVER_ADDRESS, RECEIVER_PORT, TIMEOUT, CONN_OPTS); + if (conn) { + csp_sfp_read_t user; + user.data = file; + user.read = read_from_file; + + /* Send data */ + if (CSP_ERR_NONE != csp_sfp_send(conn, &user, file->size, csp_sfp_conn_max_mtu(conn), 0)) { + perr("Failed csp_sfp_send"); + } + } else { + perr("Failed csp_connect"); + } + + csp_close(conn); + + return NULL; +} + +static void * receiver(void * params) { + csp_conn_t * conn = NULL; + sfp_file_t * file = (sfp_file_t *)params; + + csp_socket_t sock = {0}; + sock.opts |= CONN_OPTS; + csp_bind(&sock, RECEIVER_PORT); + csp_listen(&sock, 0); + + conn = csp_accept(&sock, TIMEOUT); + if (conn) { + csp_sfp_recv_t user; + user.data = file; + user.write = write_to_file; + + /* Receive data */ + if (CSP_ERR_NONE != csp_sfp_recv(conn, &user, 1000)) { + perr("Failed csp_sfp_recv"); + } + } else { + perr("Failed csp_accept"); + } + + csp_close(conn); + + return NULL; +} + +static int loopback_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me) { + /* add some sleep to avoid starving the system when RDP is not used */ + if ((CONN_OPTS & CSP_O_RDP) == 0) + usleep(1000); + + csp_qfifo_write(packet, &csp_if_lo, NULL); + return CSP_ERR_NONE; +} + +int main(int argc, char * argv[]) { + /* open existing file */ + send_file.handle = fopen(argv[1], "rb"); + if (!send_file.handle) { + csp_print("Failed to open file %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + /* get file size */ + if (fseek(send_file.handle, 0L, SEEK_END)) { + csp_print("Failed to get file size %s\n", argv[1]); + fclose(send_file.handle); + exit(EXIT_FAILURE); + } + + send_file.size = ftell(send_file.handle); + rewind(send_file.handle); + + /* create file */ + recv_file.handle = fopen(argv[2], "wb"); + if (!send_file.handle) { + csp_print("Failed to open file %s\n", argv[2]); + fclose(send_file.handle); + exit(EXIT_FAILURE); + } + + /* init csp */ + csp_init(); + + csp_if_lo.is_default = 1; + csp_if_lo.nexthop = loopback_tx; /* replace default tx function + + /* start router */ + pthread_t router_thread; + if (0 != pthread_create(&router_thread, NULL, router, NULL)) { + perr("Failed to create thread"); + goto error; + } + + /* start receiver */ + pthread_t receiver_thread; + if (0 != pthread_create(&receiver_thread, NULL, receiver, &recv_file)) { + perr("Failed to create thread"); + goto error; + } + + /* start sender */ + pthread_t sender_thread; + if (0 != pthread_create(&sender_thread, NULL, sender, &send_file)) { + perr("Failed to create thread"); + goto error; + } + + /* wait for sender to finish */ + struct timespec timeout; + if (0 != clock_gettime(CLOCK_REALTIME, &timeout)) { + perr("Failed to get time"); + goto error; + } + timeout.tv_sec += 120; + timeout.tv_nsec = 0; + if (0 != pthread_timedjoin_np(sender_thread, NULL, &timeout)) { + perr("Sender timeout"); + goto error; + } + + /* wait for receiver to finish */ + if (0 != clock_gettime(CLOCK_REALTIME, &timeout)) { + perr("Failed to get time"); + goto error; + } + timeout.tv_sec += 3; + timeout.tv_nsec = 0; + if (0 != pthread_timedjoin_np(receiver_thread, NULL, &timeout)) { + perr("Receiver timeout"); + goto error; + } + + printf("Test completed!\n"); + fclose(send_file.handle); + fclose(recv_file.handle); + exit(EXIT_SUCCESS); + +error: + fclose(send_file.handle); + fclose(recv_file.handle); + exit(EXIT_FAILURE); +} From 9e17e0adecb089196484295fb0b54a7ea36b6aac Mon Sep 17 00:00:00 2001 From: dimitrovand Date: Fri, 24 Oct 2025 18:27:06 +0300 Subject: [PATCH 284/348] csp_sfp: Update csp_sfp_recv_fp docs Update csp_sfp_recv_fp function input/output parameters docs. --- include/csp/csp_sfp.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/csp/csp_sfp.h b/include/csp/csp_sfp.h index cadc9db2b..b90bb8c7c 100644 --- a/include/csp/csp_sfp.h +++ b/include/csp/csp_sfp.h @@ -120,9 +120,7 @@ int csp_sfp_send(csp_conn_t * conn, const csp_sfp_read_t * user, uint32_t datasi * This is the counterpart to the csp_sfp_send() and csp_sfp_send_own_memcpy(). * * @param[in] conn established connection for receiving SFP packets. - * @param[out] dataout received data on success. Allocated with malloc(), so - * should be freed with free(). The pointer will be NULL on failure. - * @param[out] datasize size of received data. + * @param[in] user User-defined data with write function and data pointer. * @param[in] timeout timeout in ms to wait for csp_read() * @param[in] first_packet First packet of a SFP transfer. * Use NULL to receive first packet on the connection. From 192ff0ca0f2bbf73e41d9c8ddf4046cfaf2473d2 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 26 Oct 2025 04:04:54 +0900 Subject: [PATCH 285/348] CHANGELOG: Update to add results of sfp rework The SFP rework breaks, improves, and add new functions. Document them in the changelog. Signed-off-by: Yasushi SHOJI --- CHANGELOG | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f27ae670a..9dd108823 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +libcsp 2.2, xx-xx-202x +---------------------- +- new: csp_sfp_send(), csp_sfp_conn_max_mtu(), csp_sfp_opts_max_mtu() +- improvement: Small Fragmentation Protocol has been reworked to remove malloc() (#742) +- removed: csp_sfp_send_own_memcpy() +- break; csp_sfp_recv_fp() + libcsp 2.1, 11-10-2025 ---------------------- - new: Add reproducible builds From 0c37a17df45050beaa9312bb136db743c35e537a Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 26 Oct 2025 16:24:52 +0900 Subject: [PATCH 286/348] github: workflows: build-test: Remove trailing white spaces Remove the trailing white spaces. GitHub Actions doesn't seem to like it and complain about it saying: Check failure on line 135 in .github/workflows/build-test.yml GitHub Actions / .github/workflows/build-test.yml Invalid workflow file You have an error in your yaml syntax on line 135 Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 164502194..ad1a551f3 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -135,7 +135,7 @@ jobs: run: | ./build/examples/csp_client -u "127.0.0.1" -a 2 -C 1 -T 10 -v ${{ matrix.csp_version }} & ./build/examples/csp_server -u "127.0.0.1" -a 1 -T 10 -v ${{ matrix.csp_version }} - + - name: Run SFP Test run: | ./build/examples/csp_sfp_server_client --mtu 1 --size 1 From 65bc556f7bd658d17833d689b3023e4c03c9165e Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 21 Oct 2025 23:19:07 +0900 Subject: [PATCH 287/348] contrib: zephyr: Re-enable POSIX_MONOTONIC_CLOCK Commit 6d167a6 mistakenly removed `POSIX_MONOTONIC_CLOCK` while resolving Kconfig warnings related to `POSIX_TIMERS`. However, `POSIX_MONOTONIC_CLOCK` is still required by libcsp for correct POSIX timer behavior. The issue stemmed from a change in Zephyr, where `POSIX_*` symbols now depend on `POSIX_SYSTEM_INTERFACES`. Without selecting it, enabling `POSIX_MONOTONIC_CLOCK` had no effect. Re-enable `POSIX_MONOTONIC_CLOCK` and select `POSIX_SYSTEM_INTERFACES` to align with current Zephyr Kconfig requirements and allow a clean build. Signed-off-by: Gaetan Perrot --- contrib/zephyr/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/zephyr/Kconfig b/contrib/zephyr/Kconfig index 3ba5585f8..736b060af 100644 --- a/contrib/zephyr/Kconfig +++ b/contrib/zephyr/Kconfig @@ -6,6 +6,7 @@ config LIBCSP bool "Enable Cubesat Space Protocol Support" select POSIX_TIMERS + select POSIX_MONOTONIC_CLOCK select POSIX_SYSTEM_INTERFACES help This option enables the Cubesat Space Protocol (CSP) library. From e7cb78f319a766f1e65f1411db62aad91aac56c2 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 27 Oct 2025 17:40:06 +0900 Subject: [PATCH 288/348] examples: csp_sfp_server_client: Remove trailing white spaces This commit removes all trailing white spaces from examples/csp_sfp_server_client.c, which is added by the commit 6d5f9a11. Signed-off-by: Yasushi SHOJI --- examples/csp_sfp_server_client.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/csp_sfp_server_client.c b/examples/csp_sfp_server_client.c index 6ad3bfd3a..7f474f0c3 100644 --- a/examples/csp_sfp_server_client.c +++ b/examples/csp_sfp_server_client.c @@ -1,5 +1,5 @@ /* needed for pthread_timedjoin_np */ -#define _GNU_SOURCE +#define _GNU_SOURCE #include #include @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,7 +122,7 @@ static void process_args(int argc, char * argv[], test_options_t * test_opts) { exit(EXIT_FAILURE); } break; - + case 'm': test_opts->mtu = atoi(optarg); if (test_opts->mtu < 0) { @@ -144,9 +144,9 @@ static void process_args(int argc, char * argv[], test_options_t * test_opts) { static int read_from_buffer(uint8_t * buffer, uint32_t size, uint32_t offset, void * data) { (void)offset; - + sfp_data_t * d = (sfp_data_t *)data; - + /* Seed the random number generator */ srand(time(NULL)); @@ -166,10 +166,10 @@ static int write_to_buffer(const uint8_t * buffer, uint32_t size, uint32_t offse (void)totalsz; sfp_data_t * d = (sfp_data_t *)data; - + /* update crc */ csp_crc32_update(&d->crc, buffer, size); - + /* update counter */ received_sz += size; @@ -178,7 +178,7 @@ static int write_to_buffer(const uint8_t * buffer, uint32_t size, uint32_t offse static void * router(void * params) { (void)params; - + while (1) { (void)csp_route_work(); } @@ -273,7 +273,7 @@ int main(int argc, char * argv[]) { csp_if_lo.is_default = 1; csp_if_lo.nexthop = loopback_tx; /* replace default tx function */ - + pthread_t router_thread; if (0 != pthread_create(&router_thread, NULL, router, NULL)) { perr("Failed to create thread"); @@ -325,7 +325,7 @@ int main(int argc, char * argv[]) { if ((uint32_t)test_options.size != received_sz) { perr("SIZE mismatch"); - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } printf("Test completed!\n"); From 469c6097a8bd52449b6e3dc87abde88d14bbd572 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 27 Oct 2025 17:41:33 +0900 Subject: [PATCH 289/348] examples: csp_sfp_server_client: Fix typo Fix typos introdced in sfp. s/dew/due/g Signed-off-by: Yasushi SHOJI --- examples/csp_sfp_server_client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/csp_sfp_server_client.c b/examples/csp_sfp_server_client.c index 7f474f0c3..7c43de635 100644 --- a/examples/csp_sfp_server_client.c +++ b/examples/csp_sfp_server_client.c @@ -208,7 +208,7 @@ static void * sender(void * params) { /* Send data */ int ret = csp_sfp_send(conn, &user, test_opts->size, test_opts->mtu, 0); if (CSP_ERR_NONE != ret) { - perr("Failed csp_sfp_send dew to: %s", csp_error_to_str(ret)); + perr("Failed csp_sfp_send due to: %s", csp_error_to_str(ret)); goto exit; } @@ -244,7 +244,7 @@ static void * receiver(void * params) { /* Send data */ int ret = csp_sfp_recv(conn, &user, 1000); if (CSP_ERR_NONE != ret) { - perr("Failed csp_sfp_recv dew to: %s", csp_error_to_str(ret)); + perr("Failed csp_sfp_recv due to: %s", csp_error_to_str(ret)); goto exit; } From f02d28e8a0a2c7042b5fa59280990134d640095e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:06:14 +0000 Subject: [PATCH 290/348] build(deps): bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-checker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/abi-checker.yml b/.github/workflows/abi-checker.yml index 6bca11e6a..b1d6f20e6 100644 --- a/.github/workflows/abi-checker.yml +++ b/.github/workflows/abi-checker.yml @@ -53,7 +53,7 @@ jobs: - name: Upload ABI Report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: abi-compliance-report-${{ github.run_id }} path: compat_reports From c425172cb56d74fc2bfdb453844e15c26198e543 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 15 Oct 2025 13:41:35 +0900 Subject: [PATCH 291/348] contrib: zephyr: samples: Fix build errors in Zephyr CSP example This fixes a misnamed function argument in main.c of the Zephyr sample (server-client example). - Changed argument name from `addr` to `address` in the call to csp_usart_open_and_add_kiss_interface(). The incorrect variable caused a build error so fix it. This issue was unintentionally introduced in PR #564. Signed-off-by: Gaetan Perrot --- contrib/zephyr/samples/server-client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index a310a168a..89bc582e5 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -161,7 +161,7 @@ int main(void) { .stopbits = 1, .paritysetting = 0, }; - int error = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, addr, &default_iface); + int error = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, address, &default_iface); if (error != CSP_ERR_NONE) { LOG_ERR("failed to add KISS interface [%s], error: %d", kiss_device, error); exit(1); From e0544c9ca84afd93e7d9e3fb9367e20c28bcc875 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 23 Oct 2025 16:29:52 +0900 Subject: [PATCH 292/348] contrib: zephyr: samples: Separate UART and CAN addresses In the Zephyr server-client sample, previously a single `address` variable was used for both the UART (KISS) interface and the CAN interface. This could lead to confusion or misconfiguration when both interfaces are enabled. Changes: - Introduced `uart_address` for the KISS UART interface. - Introduced `can_address` for the CAN interface. - Updated interface initialization to use the corresponding address variable instead of the shared `address`. - Loopback fallback remains using `server_address = 0` for test mode. This clarifies the sample and makes it easier to extend to multiple interfaces without unintended address collisions. Signed-off-by: Gaetan Perrot --- contrib/zephyr/samples/server-client/main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/zephyr/samples/server-client/main.c b/contrib/zephyr/samples/server-client/main.c index 89bc582e5..727c86ba2 100644 --- a/contrib/zephyr/samples/server-client/main.c +++ b/contrib/zephyr/samples/server-client/main.c @@ -138,7 +138,8 @@ void client(void) { int main(void) { int ret; - uint8_t address = 0; + uint8_t uart_address = 1; + uint8_t can_address = 10; const char * kiss_device = NULL; const char * rtable = NULL; csp_iface_t * can_iface = NULL; @@ -161,7 +162,7 @@ int main(void) { .stopbits = 1, .paritysetting = 0, }; - int error = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, address, &default_iface); + int error = csp_usart_open_and_add_kiss_interface(&conf, CSP_IF_KISS_DEFAULT_NAME, uart_address, &default_iface); if (error != CSP_ERR_NONE) { LOG_ERR("failed to add KISS interface [%s], error: %d", kiss_device, error); exit(1); @@ -177,7 +178,6 @@ int main(void) { * server address to any address not 255. */ const char * ifname = "CAN0"; - address = 10; server_address = 255; const struct device * device = DEVICE_DT_GET(DT_NODELABEL(can0)); uint32_t bitrate = 1000000; @@ -188,11 +188,11 @@ int main(void) { * by me. If you want to receive all packets, please change the filter address * and mask. (For example, filter_addr: 0x3FFF, filter_mask: 0x0000) */ - uint16_t filter_addr = address; + uint16_t filter_addr = can_address; uint16_t filter_mask = 0x3FFF; - int error = csp_can_open_and_add_interface(device, ifname, address, bitrate, - filter_addr, filter_mask, &can_iface); + int error = csp_can_open_and_add_interface(device, ifname, can_address, bitrate, + filter_addr, filter_mask, &can_iface); if (error != CSP_ERR_NONE) { LOG_ERR("failed to add CAN interface [%s], error: %d\n", ifname, error); exit(1); @@ -215,7 +215,7 @@ int main(void) { if (!default_iface) { /* no interfaces configured - run server and client in process, using loopback interface */ - server_address = address; + server_address = 0; /* run as test mode only use loopback interface */ test_mode = true; } From 9f40f26cc9df16fe9a7c77f8fa76e25e2a060a9f Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 25 Jul 2025 15:48:39 +0900 Subject: [PATCH 293/348] doc: Include samples/*/README.md This commit include the samples/*/readme.md in the documentation, and ensures they are properly rendered as part of the generated HTML. Signed-off-by: Gaetan Perrot Signed-off-by: Yasushi SHOJI --- doc/CMakeLists.txt | 1 + doc/index.md | 11 +++++++++++ doc/samples/posix/simple-send-canbus.md | 1 + doc/samples/posix/simple-send-udp.md | 1 + doc/samples/posix/simple-send-usart.md | 1 + doc/samples/posix/simple-send-zmq.md | 1 + doc/samples/posix/simple-sfp-send-recv.md | 1 + 7 files changed, 17 insertions(+) create mode 100644 doc/samples/posix/simple-send-canbus.md create mode 100644 doc/samples/posix/simple-send-udp.md create mode 100644 doc/samples/posix/simple-send-usart.md create mode 100644 doc/samples/posix/simple-send-zmq.md create mode 100644 doc/samples/posix/simple-sfp-send-recv.md diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 6a8f8f627..eef3239ee 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -8,6 +8,7 @@ set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html) file(GLOB DOCS_FILES "${SPHINX_SOURCE}/*.md" + "${SPHINX_SOURCE}/samples/posix/*.md" "${SPHINX_SOURCE}/api/*.rst" "${SPHINX_SOURCE}/api/arch/*.rst" "${SPHINX_SOURCE}/api/crypto/*.rst" diff --git a/doc/index.md b/doc/index.md index 0c59079af..847c67088 100644 --- a/doc/index.md +++ b/doc/index.md @@ -15,6 +15,17 @@ INSTALL build-doc ``` +```{toctree} +:caption: how to run samples +:hidden: + +samples/posix/simple-send-canbus +samples/posix/simple-send-udp +samples/posix/simple-send-usart +samples/posix/simple-send-zmq +samples/posix/simple-sfp-send-recv +``` + ```{toctree} :caption: CSP API :hidden: diff --git a/doc/samples/posix/simple-send-canbus.md b/doc/samples/posix/simple-send-canbus.md new file mode 100644 index 000000000..bf06fe4ee --- /dev/null +++ b/doc/samples/posix/simple-send-canbus.md @@ -0,0 +1 @@ +```{include} ../../../samples/posix/simple-send-canbus/README.md diff --git a/doc/samples/posix/simple-send-udp.md b/doc/samples/posix/simple-send-udp.md new file mode 100644 index 000000000..71dab4fbc --- /dev/null +++ b/doc/samples/posix/simple-send-udp.md @@ -0,0 +1 @@ +```{include} ../../../samples/posix/simple-send-udp/README.md diff --git a/doc/samples/posix/simple-send-usart.md b/doc/samples/posix/simple-send-usart.md new file mode 100644 index 000000000..7e50b460f --- /dev/null +++ b/doc/samples/posix/simple-send-usart.md @@ -0,0 +1 @@ +```{include} ../../../samples/posix/simple-send-usart/README.md diff --git a/doc/samples/posix/simple-send-zmq.md b/doc/samples/posix/simple-send-zmq.md new file mode 100644 index 000000000..5ac91578a --- /dev/null +++ b/doc/samples/posix/simple-send-zmq.md @@ -0,0 +1 @@ +```{include} ../../../samples/posix/simple-send-zmq/README.md diff --git a/doc/samples/posix/simple-sfp-send-recv.md b/doc/samples/posix/simple-sfp-send-recv.md new file mode 100644 index 000000000..dcef6a37c --- /dev/null +++ b/doc/samples/posix/simple-sfp-send-recv.md @@ -0,0 +1 @@ +```{include} ../../../samples/posix/simple-sfp-send-recv/README.md From a0c899042c10bf6910d2bc326f6b40da9c727f85 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Mon, 10 Nov 2025 16:23:59 +0900 Subject: [PATCH 294/348] samples: posix: Clarify README title for simple-sfp-send-recv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sample was previously titled “Simple Send via USARTâ€, which could be confused with a basic serial transmission example. Updated to “Simple Send using SFP via USART†to clearly indicate that it demonstrates file transfer over SFP. Signed-off-by: Gaetan Perrot --- samples/posix/simple-sfp-send-recv/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/posix/simple-sfp-send-recv/README.md b/samples/posix/simple-sfp-send-recv/README.md index 8cd1696e3..962318110 100644 --- a/samples/posix/simple-sfp-send-recv/README.md +++ b/samples/posix/simple-sfp-send-recv/README.md @@ -1,4 +1,4 @@ -# Simple Send via USART +# Simple Send using SFP via USART This is a simple sample code to send and receive a file using SFP over LOOPBACK interface. From f24e19d669b36317a94936dc504172630dd84700 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Thu, 24 Jul 2025 15:48:40 +0900 Subject: [PATCH 295/348] csp_rdp: Remove redundant cast The explicit cast to (csp_packet_t *) in the call to csp_rdp_header_ref() and csp_rdp_check_timeouts() are unnecessary since 'packet' is already of type csp_packet_t *. This became redundant after commit 5ee2d36, which changed packet type from rdp_packet_t to csp_packet_t. Removing this redundant cast improves code clarity without changing behavior. Signed-off-by: Gaetan Perrot --- src/csp_rdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index c32d3a24f..b621bd9ea 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -267,7 +267,7 @@ static inline bool csp_rdp_seq_in_rx_queue(csp_conn_t * conn, uint16_t seq_nr) { csp_rdp_queue_rx_add(conn, packet); - rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *)packet); + rdp_header_t * header = csp_rdp_header_ref(packet); if (header->seq_nr == seq_nr) { return true; } @@ -378,7 +378,7 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { } /* Get header */ - rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *)packet); + rdp_header_t * header = csp_rdp_header_ref(packet); /* If acked, do not retransmit */ if (csp_rdp_seq_before(be16toh(header->seq_nr), conn->rdp.snd_una)) { From cad87ef94f075f7fb29808302110a0c969d8134a Mon Sep 17 00:00:00 2001 From: stadlero <47506268+stadlero@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:35:17 +0100 Subject: [PATCH 296/348] zephyr: Replace symbolic link to zephyr module Fixes Windows builds on machines with symbolic links disabled. This commit removes the symbolic link from `zephyr`-> `contrib/zephyr` and moves `module.yml`, `CMakeLists.txt`, and `Kconfig` into a new `zephyr` directory. `CMakeLists.txt` required a minor change to the add_subdirectory path. --- zephyr | 1 - {contrib/zephyr => zephyr}/CMakeLists.txt | 2 +- {contrib/zephyr => zephyr}/Kconfig | 0 {contrib/zephyr => zephyr}/module.yml | 0 4 files changed, 1 insertion(+), 2 deletions(-) delete mode 120000 zephyr rename {contrib/zephyr => zephyr}/CMakeLists.txt (95%) rename {contrib/zephyr => zephyr}/Kconfig (100%) rename {contrib/zephyr => zephyr}/module.yml (100%) diff --git a/zephyr b/zephyr deleted file mode 120000 index 0f0b58ede..000000000 --- a/zephyr +++ /dev/null @@ -1 +0,0 @@ -contrib/zephyr \ No newline at end of file diff --git a/contrib/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt similarity index 95% rename from contrib/zephyr/CMakeLists.txt rename to zephyr/CMakeLists.txt index 44c01aa9c..9c7f9e11d 100644 --- a/contrib/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -27,7 +27,7 @@ if(CONFIG_LIBCSP) set(HAVE_ZEPHYR_CAN ON) endif() - add_subdirectory(../.. build) + add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR} build) zephyr_interface_library_named(libcsp) target_include_directories(libcsp INTERFACE ${ZEPHYR_CURRENT_MODULE_DIR}/include) diff --git a/contrib/zephyr/Kconfig b/zephyr/Kconfig similarity index 100% rename from contrib/zephyr/Kconfig rename to zephyr/Kconfig diff --git a/contrib/zephyr/module.yml b/zephyr/module.yml similarity index 100% rename from contrib/zephyr/module.yml rename to zephyr/module.yml From a02cb1c7a02cfcde64101d84c80b63c59935387e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:10:12 +0000 Subject: [PATCH 297/348] build(deps): bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-checker.yml | 4 ++-- .github/workflows/build-test-freertos.yml | 6 +++--- .github/workflows/build-test-python.yml | 2 +- .github/workflows/build-test-zephyr.yml | 2 +- .github/workflows/build-test.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/develop-build-sphinx-docs.yml | 2 +- .github/workflows/gitlint.yml | 2 +- .github/workflows/linelint.yaml | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/abi-checker.yml b/.github/workflows/abi-checker.yml index b1d6f20e6..d354e729e 100644 --- a/.github/workflows/abi-checker.yml +++ b/.github/workflows/abi-checker.yml @@ -22,7 +22,7 @@ jobs: sudo apt-get install abi-compliance-checker abi-dumper - name: Checkout merged-base - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.base.ref }} @@ -32,7 +32,7 @@ jobs: abi-dumper build/libcsp.so -lver "merged-base" -o ../tmp/libcsp-merged-base.dump - name: Checkout Current PR - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{github.event.pull_request.head.ref}} diff --git a/.github/workflows/build-test-freertos.yml b/.github/workflows/build-test-freertos.yml index ed36f65cf..2765b156a 100644 --- a/.github/workflows/build-test-freertos.yml +++ b/.github/workflows/build-test-freertos.yml @@ -14,18 +14,18 @@ jobs: sudo apt-get install ninja-build meson tree - name: Checkout Test App - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: libcsp/libcsp-freertos - name: Checkout FreeRTOS Kernel - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: FreeRTOS/FreeRTOS-Kernel path: freertos - name: Checkout libcsp under subprojects - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: path: subprojects/libcsp diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index fa374e263..086f305f5 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -37,7 +37,7 @@ jobs: sudo apt-get install ninja-build ${{ matrix.buildsystem }} - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Build libcsp examples run: python3 examples/buildall.py --build-system=${{ matrix.buildsystem }} diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index b6ecc7d48..8d45873ff 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -34,7 +34,7 @@ jobs: python3 --version - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: path: libcsp-zephyr repository: yashi/libcsp-zephyr diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ad1a551f3..03c172c10 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -61,7 +61,7 @@ jobs: sudo apt-get install ninja-build ${{ matrix.buildsystem }} - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Build env: diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 6cde179c2..219f55141 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install codespell run: pip install codespell diff --git a/.github/workflows/develop-build-sphinx-docs.yml b/.github/workflows/develop-build-sphinx-docs.yml index e5e8e91e6..8e5af2159 100644 --- a/.github/workflows/develop-build-sphinx-docs.yml +++ b/.github/workflows/develop-build-sphinx-docs.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout the repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup packages on Linux if: ${{ runner.os == 'Linux' }} diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml index 8973621f8..318b3cb9f 100644 --- a/.github/workflows/gitlint.yml +++ b/.github/workflows/gitlint.yml @@ -16,7 +16,7 @@ jobs: pip3 install gitlint - name: Checkout the code - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 diff --git a/.github/workflows/linelint.yaml b/.github/workflows/linelint.yaml index 2a1fd8dc2..ee6190415 100644 --- a/.github/workflows/linelint.yaml +++ b/.github/workflows/linelint.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Linelint uses: fernandrone/linelint@master From b4d2d1c38c75f0c35005373c93b8814bf7615825 Mon Sep 17 00:00:00 2001 From: Wookhyun Shin Date: Tue, 2 Dec 2025 15:44:51 +0900 Subject: [PATCH 298/348] examples: csp_server: Fix `csp_read` timeout value in comment Timeout for `csp_read` is set to 50 ms in the code, but the comment still indicated 100 ms. Update the comment to reflect the correct value. --- examples/csp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/csp_server.c b/examples/csp_server.c index f96c65ba7..f3f163622 100644 --- a/examples/csp_server.c +++ b/examples/csp_server.c @@ -61,7 +61,7 @@ static void * server(void * param) { continue; } - /* Read packets on connection, timeout is 100 mS */ + /* Read packets on connection, timeout is 50 mS */ csp_packet_t *packet; while ((packet = csp_read(conn, 50)) != NULL) { switch (csp_conn_dport(conn)) { From e034293b4cd70fddcb0f3df64ea5229c3427d626 Mon Sep 17 00:00:00 2001 From: olmanqj Date: Fri, 7 Nov 2025 12:53:27 +0100 Subject: [PATCH 299/348] pycsp: Add conditional compile flag for SocketCAN support The SocketCAN-related functions are now only compiled when CSP_HAVE_LIBSOCKETCAN is defined. This ensures that the Python bindings can be built even on systems without libsocketcan installed. Signed-off-by: Olman Quiros Jimenez Signed-off-by: Yasushi SHOJI --- CHANGELOG | 1 + src/bindings/python/pycsp.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 9dd108823..d7e383e75 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ libcsp 2.2, xx-xx-202x ---------------------- - new: csp_sfp_send(), csp_sfp_conn_max_mtu(), csp_sfp_opts_max_mtu() - improvement: Small Fragmentation Protocol has been reworked to remove malloc() (#742) +- improvement: PyCSP can now be built without SocketCAN (#638) - removed: csp_sfp_send_own_memcpy() - break; csp_sfp_recv_fp() diff --git a/src/bindings/python/pycsp.c b/src/bindings/python/pycsp.c index 258b382d7..e4e93310e 100644 --- a/src/bindings/python/pycsp.c +++ b/src/bindings/python/pycsp.c @@ -854,6 +854,7 @@ static PyObject * pycsp_zmqhub_init(PyObject * self, PyObject * args) { } #endif /* CSP_HAVE_LIBZMQ */ +#if CSP_HAVE_LIBSOCKETCAN static PyObject * pycsp_can_socketcan_init(PyObject * self, PyObject * args) { char * ifc; int bitrate = 1000000; @@ -870,6 +871,8 @@ static PyObject * pycsp_can_socketcan_init(PyObject * self, PyObject * args) { Py_RETURN_NONE; } +#endif /* CSP_HAVE_LIBSOCKETCAN */ + static PyObject * pycsp_kiss_init(PyObject * self, PyObject * args) { char * device; @@ -998,8 +1001,10 @@ static PyMethodDef methods[] = { #endif /* CSP_HAVE_LIBZMQ */ {"kiss_init", pycsp_kiss_init, METH_VARARGS, ""}, +#if CSP_HAVE_LIBSOCKETCAN /* csp/drivers/can_socketcan.h */ {"can_socketcan_init", pycsp_can_socketcan_init, METH_VARARGS, ""}, +#endif /* helpers */ {"packet_get_length", pycsp_packet_get_length, METH_O, ""}, From 8e1a424ef47194133da175b72ba34ca2b456b2f2 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Tue, 11 Nov 2025 12:46:52 +0900 Subject: [PATCH 300/348] interfaces: csp_if_kiss: Make KISS CRC optional Introduce a new compile-time option `CSP_ENABLE_KISS_CRC` to allow enabling or disabling the CRC in the KISS interface. Default behavior preserves legacy operation (CRC enabled) Add support for this option in: * CMake (`CMakeLists.txt`) * Waf (`wscript`) * Meson (`meson.build`) Updated autoconfig headers and build system defines to propagate the option This makes the CRC in KISS optional while keeping legacy builds fully compatible. Future builds can enable or disable CRC as needed. Signed-off-by: Gaetan Perrot --- CMakeLists.txt | 2 ++ csp_autoconfig.h.in | 2 ++ meson.build | 2 ++ meson_options.txt | 2 ++ src/interfaces/csp_if_kiss.c | 4 ++++ wscript | 4 ++++ 6 files changed, 16 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11c23ea00..2752465aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,8 @@ option(CSP_BUILD_SAMPLES "Build samples and examples by default" OFF) option(CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN "Use little-endian CSP ID for ZMQ with CSPv1" ON) +option(CSP_ENABLE_KISS_CRC "Enables the extra CRC in the KISS interface (legacy)" ON) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CSP_POSIX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "CYGWIN") diff --git a/csp_autoconfig.h.in b/csp_autoconfig.h.in index 0c108c793..1ee30f001 100644 --- a/csp_autoconfig.h.in +++ b/csp_autoconfig.h.in @@ -27,3 +27,5 @@ #cmakedefine01 CSP_HAVE_LIBZMQ #cmakedefine01 CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN + +#cmakedefine01 CSP_ENABLE_KISS_CRC diff --git a/meson.build b/meson.build index bc7f7e536..60233338a 100644 --- a/meson.build +++ b/meson.build @@ -31,6 +31,8 @@ conf.set10('CSP_BUFFER_ZERO_CLEAR', get_option('buffer_zero_clear')) conf.set10('CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN', get_option('fixup_v1_zmq_little_endian')) +conf.set10('CSP_ENABLE_KISS_CRC', get_option('enable_kiss_crc')) + csp_deps = [] csp_sources = [] diff --git a/meson_options.txt b/meson_options.txt index 6d328f638..682b3f7ee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -28,3 +28,5 @@ option('rdp_max_window', type: 'integer', value: 5, description: 'Max window siz option('rtable_size', type: 'integer', value: 10, description: 'Number of elements in routing table') option('fixup_v1_zmq_little_endian', type: 'boolean', value: false, description: 'Use little-endian CSP ID for ZMQ with CSPv1') + +option('enable_kiss_crc', type: 'boolean', value: true, description: 'Enables the extra CRC in the KISS interface (legacy)') diff --git a/src/interfaces/csp_if_kiss.c b/src/interfaces/csp_if_kiss.c index 300496eec..5b9dc33b7 100644 --- a/src/interfaces/csp_if_kiss.c +++ b/src/interfaces/csp_if_kiss.c @@ -33,8 +33,10 @@ int csp_kiss_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int fr /* Lock (before modifying packet) */ csp_usart_lock(driver); +#if CSP_ENABLE_KISS_CRC /* Add CRC32 checksum */ csp_crc32_append(packet); +#endif /* Save the outgoing id in the buffer */ csp_id_prepend(packet); @@ -145,12 +147,14 @@ void csp_kiss_rx(csp_iface_t * iface, const uint8_t * buf, size_t len, void * px break; } +#if CSP_ENABLE_KISS_CRC /* Validate CRC */ if (csp_crc32_verify(ifdata->rx_packet) != CSP_ERR_NONE) { iface->frame++; ifdata->rx_mode = KISS_MODE_NOT_STARTED; break; } +#endif /* Send back into CSP, notice calling from task so last argument must be NULL! */ csp_qfifo_write(ifdata->rx_packet, iface, pxTaskWoken); diff --git a/wscript b/wscript index beb96fbc3..b7e6bea20 100644 --- a/wscript +++ b/wscript @@ -54,6 +54,8 @@ def options(ctx): # Fixup gr.add_option('--fixup-v1-zmq-little-endian', action='store_true', help='Use little-endian CSP ID for ZMQ with CSPv1') + gr.add_option('--disable_kiss_crc', action='store_true', help='Disable the extra CRC in the KISS interface (legacy)') + def configure(ctx): # Validate options if ctx.options.with_os not in valid_os: @@ -206,6 +208,8 @@ def configure(ctx): ctx.define('CSP_FIXUP_V1_ZMQ_LITTLE_ENDIAN', ctx.options.fixup_v1_zmq_little_endian) + ctx.define('CSP_ENABLE_KISS_CRC', not ctx.options.disable_kiss_crc) + ctx.write_config_header('include/csp/autoconfig.h') def build(ctx): From 306bdae4f3f0efdd631f2d0be2515d7faba21c62 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 14 Dec 2025 15:52:40 +0900 Subject: [PATCH 301/348] CHANGELOG: Document optional CRC support for KISS interface The KISS interface now supports enabling or disabling CRC at build time using CSP_ENABLE_KISS_CRC. Document this new configuration option in the changelog. Signed-off-by: Gaetan Perrot --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index d7e383e75..ab17ad904 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ libcsp 2.2, xx-xx-202x ---------------------- - new: csp_sfp_send(), csp_sfp_conn_max_mtu(), csp_sfp_opts_max_mtu() +- new: Make KISS interface CRC optional via CSP_ENABLE_KISS_CRC (#892) - improvement: Small Fragmentation Protocol has been reworked to remove malloc() (#742) - improvement: PyCSP can now be built without SocketCAN (#638) - removed: csp_sfp_send_own_memcpy() From 2626f3831f83e18d827f05a2bd409598ebda99cf Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 7 Feb 2025 12:33:06 +0900 Subject: [PATCH 302/348] csp_io: csp_accept: Reject connection-less sockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit csp_accept() is designed for connection-oriented sockets, as it dequeues incoming connections from the socket’s receive queue. However, connection-less sockets (CSP_SO_CONN_LESS) do not establish connections, making csp_accept() inapplicable in this context. This commit adds an explicit check for CSP_SO_CONN_LESS in csp_accept(). If a connection-less socket is passed, the function now sets csp_dbg_errno to CSP_DBG_ERR_UNSUPPORTED and returns NULL immediately. This prevents unintended behavior and improves API safety by ensuring csp_accept() is only used with connection-oriented sockets. Signed-off-by: Gaetan Perrot --- src/csp_io.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/csp_io.c b/src/csp_io.c index b4b089ca4..70c160d43 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -28,6 +28,11 @@ csp_conn_t * csp_accept(csp_socket_t * sock, uint32_t timeout) { csp_dbg_errno = CSP_DBG_ERR_INVALID_POINTER; return NULL; } + + if (sock->opts & CSP_SO_CONN_LESS) { + csp_dbg_errno = CSP_DBG_ERR_UNSUPPORTED; + return NULL; + } csp_conn_t * conn; if (csp_queue_dequeue(sock->rx_queue, &conn, timeout) == CSP_QUEUE_OK) { From b4eac6c10e88b45edb915948a6040baa9985537c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sun, 14 Dec 2025 14:06:43 +0900 Subject: [PATCH 303/348] CHANGELOG: Document csp_accept() rejection of connection-less sockets Document that csp_accept() explicitly rejects connection-less sockets. Signed-off-by: Gaetan Perrot --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index ab17ad904..d085fcb98 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ libcsp 2.2, xx-xx-202x - improvement: PyCSP can now be built without SocketCAN (#638) - removed: csp_sfp_send_own_memcpy() - break; csp_sfp_recv_fp() +- fix: csp_accept() now explicitly rejects connection-less sockets (#766) libcsp 2.1, 11-10-2025 ---------------------- From 1edf764127e648c128511b8eab20d1839e5438b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:11:20 +0000 Subject: [PATCH 304/348] build(deps): bump actions/upload-artifact from 5 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-checker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/abi-checker.yml b/.github/workflows/abi-checker.yml index d354e729e..1ace9ea01 100644 --- a/.github/workflows/abi-checker.yml +++ b/.github/workflows/abi-checker.yml @@ -53,7 +53,7 @@ jobs: - name: Upload ABI Report if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: abi-compliance-report-${{ github.run_id }} path: compat_reports From 5db8d6516a1729150d847e61d03cfd38321e119e Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sun, 21 Dec 2025 02:06:37 +0900 Subject: [PATCH 305/348] github: workflow: Check PRs for merge commits Add a GitHub Actions workflow that fails pull requests containing merge commits. This reduces the manual effort of pointing them out and encourages a rebase-based history. Signed-off-by: Yasushi SHOJI --- .github/workflows/check-merge-commits.yml | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/check-merge-commits.yml diff --git a/.github/workflows/check-merge-commits.yml b/.github/workflows/check-merge-commits.yml new file mode 100644 index 000000000..310c9fbd8 --- /dev/null +++ b/.github/workflows/check-merge-commits.yml @@ -0,0 +1,32 @@ +name: Merge Commits Check + +on: + pull_request: + +permissions: + contents: read + +jobs: + check_merge_commits: + runs-on: ubuntu-latest + steps: + + - name: Checkout PR head + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Print commits for debugging + run: git log --graph --oneline -n 50 + + - name: Check for merge commits + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + merges=$(git rev-list --merges --count "${BASE_SHA}..${HEAD_SHA}") + if [ "$merges" != "0" ]; then + echo "::error ::Merge commits not allowed; please rebase." + exit 1 + fi From e7ff4075a5ab16287085cddae0da78ad4f673a07 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lab Date: Tue, 16 Dec 2025 11:16:33 +0100 Subject: [PATCH 306/348] Allow setting mask and is_default itf attributes from Python bindings This change extends the "zmqhub_init", "socketcan_init" & "kiss_init" bindings to accept new (optional) "is_default" and "mask" positional parameters that are then applied to the corresponding added interface, allowing more flexibility in interface management from Python --- src/bindings/python/pycsp.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/bindings/python/pycsp.c b/src/bindings/python/pycsp.c index e4e93310e..45b4558a3 100644 --- a/src/bindings/python/pycsp.c +++ b/src/bindings/python/pycsp.c @@ -840,15 +840,21 @@ static PyObject * pycsp_cmp_clock_get(PyObject * self, PyObject * args) { #if CSP_HAVE_LIBZMQ static PyObject * pycsp_zmqhub_init(PyObject * self, PyObject * args) { uint16_t addr; - char * host; - if (!PyArg_ParseTuple(args, "Hs", &addr, &host)) { + const char * host = "localhost"; + int is_default = 0; + uint16_t mask = 8; + if (!PyArg_ParseTuple(args, "H|spH", &addr, &host, &is_default, &mask)) { return NULL; // TypeError is thrown } - int res = csp_zmqhub_init(addr, host, 0, NULL); + csp_iface_t *iface; + int res = csp_zmqhub_init(addr, host, 0, &iface); if (res != CSP_ERR_NONE) { return PyErr_Error("csp_zmqhub_init()", res); } + iface->is_default = is_default; + iface->addr = addr; + iface->netmask = mask; Py_RETURN_NONE; } @@ -860,14 +866,21 @@ static PyObject * pycsp_can_socketcan_init(PyObject * self, PyObject * args) { int bitrate = 1000000; int promisc = 0; uint16_t addr = 0; - if (!PyArg_ParseTuple(args, "s|Hii", &ifc, &addr, &bitrate, &promisc)) { + int is_default = 0; + uint16_t mask = 8; + + if (!PyArg_ParseTuple(args, "s|HiipH", &ifc, &addr, &bitrate, &promisc, &is_default, &mask)) { return NULL; } - int res = csp_can_socketcan_open_and_add_interface(ifc, CSP_IF_CAN_DEFAULT_NAME, addr, bitrate, promisc, NULL); + csp_iface_t *iface; + int res = csp_can_socketcan_open_and_add_interface(ifc, CSP_IF_CAN_DEFAULT_NAME, addr, bitrate, promisc, &iface); if (res != CSP_ERR_NONE) { return PyErr_Error("csp_can_socketcan_open_and_add_interface()", res); } + iface->is_default = is_default; + iface->addr = addr; + iface->netmask = mask; Py_RETURN_NONE; } @@ -880,15 +893,22 @@ static PyObject * pycsp_kiss_init(PyObject * self, PyObject * args) { uint32_t mtu = 512; uint16_t addr; const char * if_name = CSP_IF_KISS_DEFAULT_NAME; - if (!PyArg_ParseTuple(args, "sH|IIs", &device, &addr, &baudrate, &mtu, &if_name)) { + int is_default = 0; + uint16_t mask = 8; + + if (!PyArg_ParseTuple(args, "sH|IIspH", &device, &addr, &baudrate, &mtu, &if_name, &is_default, &mask)) { return NULL; // TypeError is thrown } csp_usart_conf_t conf = {.device = device, .baudrate = baudrate}; - int res = csp_usart_open_and_add_kiss_interface(&conf, if_name, addr, NULL); + csp_iface_t *iface; + int res = csp_usart_open_and_add_kiss_interface(&conf, if_name, addr, &iface); if (res != CSP_ERR_NONE) { return PyErr_Error("csp_usart_open_and_add_kiss_interface()", res); } + iface->is_default = is_default; + iface->addr = addr; + iface->netmask = mask; Py_RETURN_NONE; } From 7e8efa5941c3028e02d27ae43008b6157040514a Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 22 Dec 2025 21:06:42 +0900 Subject: [PATCH 307/348] meson: Bump minimum required version to 0.61.2 Require Meson 0.61.2 to prepare for an upcoming change that uses the `install_tag` feature, introduced in 0.60.0. This version is available in Ubuntu 22.04 LTS, so the update remains compatible with the target platform. - https://packages.ubuntu.com/search?keywords=meson - https://mesonbuild.com/Installing.html#installation-tags Signed-off-by: Yasushi SHOJI --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 60233338a..e3b1d818d 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('csp', 'c', version: '2.2', license: 'LGPL', meson_version : '>=0.53.2', default_options : [ +project('csp', 'c', version: '2.2', license: 'LGPL', meson_version : '>=0.61.2', default_options : [ 'c_std=gnu11', 'optimization=s', 'warning_level=3', From 90711fb563836ac1da65b162397d6d20818eb06d Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 22 Dec 2025 21:31:40 +0900 Subject: [PATCH 308/348] github: workflows: build: Enable cancel-in-progress Add workflow-level concurrency to the build-and-test workflows. Key the concurrency group by workflow name, event, and branch (or PR head branch), and enable cancel-in-progress so newer runs cancel older ones. This reduces wasted runner time and keeps CI results focused on the latest commit. - ${{ github.event_name }} keeps runs triggered by different event types in separate groups, so (for example) push and pull_request runs do not cancel each other. - ${{ github.head_ref || github.ref }} picks the right ref across events. github.head_ref is only set for pull_request and pull_request_target, and refers to the PR source branch. github.ref is the fallback for other events, and is the fully-formed branch or tag ref (for example, refs/heads/main). Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-freertos.yml | 3 +++ .github/workflows/build-test-python.yml | 3 +++ .github/workflows/build-test-zephyr.yml | 3 +++ .github/workflows/build-test.yml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/.github/workflows/build-test-freertos.yml b/.github/workflows/build-test-freertos.yml index 2765b156a..b9060a9ef 100644 --- a/.github/workflows/build-test-freertos.yml +++ b/.github/workflows/build-test-freertos.yml @@ -2,6 +2,9 @@ name: FreeRTOS Build and Test on: [push, pull_request] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true jobs: build-freertos: runs-on: ubuntu-latest diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 086f305f5..1d814f1cf 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -2,6 +2,9 @@ name: Python Bindings on: [push, pull_request] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true jobs: build-test-python: strategy: diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 8d45873ff..8477a350c 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -7,6 +7,9 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true jobs: build-zephyr: runs-on: ubuntu-24.04 diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 03c172c10..5784ac126 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -2,6 +2,9 @@ name: Build and Test on: [push, pull_request] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true jobs: run-tests: strategy: From a0ee6ac74a92356ad5248eec23294f8f03ccf79f Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 22 Dec 2025 21:43:34 +0900 Subject: [PATCH 309/348] github: workflows: build-test-python: Remove Python 3.9 Remove Python 3.9 from the build and test workflow, as it reached end-of-life on October 31, 2025. It is also not supported by Ubuntu 22.04 LTS. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-python.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml index 1d814f1cf..8bbd830af 100644 --- a/.github/workflows/build-test-python.yml +++ b/.github/workflows/build-test-python.yml @@ -11,7 +11,6 @@ jobs: fail-fast: false matrix: python-version: - - '3.9' - '3.10' - '3.11' - '3.12' From 08371df7cf5d428feb2520bf5441accfcceb3abd Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 22 Dec 2025 22:28:32 +0900 Subject: [PATCH 310/348] meson: remove redundant install_headers() for autoconfig.h autoconfig.h, referenced by `scp_config_h`, is already configured to be installed under /include/csp/ via `include/csp/meson.build`, so calling `install_headers()` is redundant. Note: The only reason we have `include/csp/meson.build` is to generate autoconfig.h under /include/csp. As of Meson 1.10.0, `configure_file()` does not support path components in the output. Signed-off-by: Yasushi SHOJI --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index e3b1d818d..020ea90cc 100644 --- a/meson.build +++ b/meson.build @@ -82,7 +82,6 @@ subdir('include/csp') if not meson.is_subproject() install_subdir('include', install_dir : '.') - install_headers(csp_config_h, install_dir : 'include/csp') endif From f1cbb00335fbe79aca6bb16e326de96497025785 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 19 Dec 2025 16:16:19 +0900 Subject: [PATCH 311/348] csp_buffer: Export CSP_BUFFER_RESERVED_COUNT Expose the number of reserved buffers as CSP_BUFFER_RESERVED_COUNT in the public header. These buffers are used for fault-tolerant operations and should not be used by regular users. This allows applications to be aware of the reservation and adjust their buffer usage accordingly. Signed-off-by: Yasushi SHOJI --- include/csp/csp_buffer.h | 9 +++++++++ src/csp_buffer.c | 13 ++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/csp/csp_buffer.h b/include/csp/csp_buffer.h index 698be1aa3..05c15aba0 100644 --- a/include/csp/csp_buffer.h +++ b/include/csp/csp_buffer.h @@ -11,6 +11,15 @@ extern "C" { #endif +/** + * Number of buffers reserved by CSP for fault-tolerant operations. + * + * These reserved buffers are used for operations that can tolerate allocation failure, + * such as client requests with proper error handling, or services that may timeout + * safely when memory is low. + */ +#define CSP_BUFFER_RESERVED_COUNT 2 + /** * Get free buffer from task context. * diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 09f86b598..9171022ba 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -10,15 +10,6 @@ #include "csp_buffer_private.h" -/** - * Number of buffers reserved by CSP for fault-tolerant operations. - * - * These reserved buffers are used for operations that can tolerate allocation failure, - * such as client requests with proper error handling, or services that may timeout - * safely when memory is low. - */ -#define CSP_BUFFER_RESERVE 2 - /** Internal buffer header */ typedef struct csp_skbf_s { unsigned int refcount; @@ -224,10 +215,10 @@ csp_packet_t * csp_buffer_get_always_isr(void) { csp_packet_t * csp_buffer_get(size_t unused) { (void)unused; /* Avoid compiler warnings about unused parameter */ - return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 0); + return csp_buffer_get_actual(CSP_BUFFER_RESERVED_COUNT, 0); } csp_packet_t * csp_buffer_get_isr(size_t unused) { (void)unused; /* Avoid compiler warnings about unused parameter */ - return csp_buffer_get_actual(CSP_BUFFER_RESERVE, 1); + return csp_buffer_get_actual(CSP_BUFFER_RESERVED_COUNT, 1); } From bfec45a7740701b4a14586ad211edb2335535e72 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 23 Dec 2025 04:45:03 +0900 Subject: [PATCH 312/348] CHANGELOG: Update with recent changes - Add Meson v0.61.2 as new minimum version - Add CSP_BUFFER_RESERVED_COUNT build-time constant - Document optional parameters for PyCSP Signed-off-by: Yasushi SHOJI --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d085fcb98..a3b056734 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,11 @@ libcsp 2.2, xx-xx-202x ---------------------- - new: csp_sfp_send(), csp_sfp_conn_max_mtu(), csp_sfp_opts_max_mtu() - new: Make KISS interface CRC optional via CSP_ENABLE_KISS_CRC (#892) +- new: Meson v0.61.2 is the minimal version now (#927) +- new: A new build time constat CSP_BUFFER_RESERVED_COUNT is added (#925) - improvement: Small Fragmentation Protocol has been reworked to remove malloc() (#742) - improvement: PyCSP can now be built without SocketCAN (#638) +- improvement: PyCSP can now take mask and is_default optional parameter (#924) - removed: csp_sfp_send_own_memcpy() - break; csp_sfp_recv_fp() - fix: csp_accept() now explicitly rejects connection-less sockets (#766) From ac8d96568efa546140c428475d6209c70ecb8169 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 23 Dec 2025 03:41:13 +0900 Subject: [PATCH 313/348] meson: Enable installation of libcsp.so Set the install flag to true for libcsp in meson.build to allow it to be installed with the rest of the build artifacts. Signed-off-by: Yasushi SHOJI --- CHANGELOG | 1 + meson.build | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index a3b056734..ab95f359b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ libcsp 2.2, xx-xx-202x - removed: csp_sfp_send_own_memcpy() - break; csp_sfp_recv_fp() - fix: csp_accept() now explicitly rejects connection-less sockets (#766) +- fix: Meson can now install libcsp.so when told to do so (#931) libcsp 2.1, 11-10-2025 ---------------------- diff --git a/meson.build b/meson.build index 020ea90cc..adf9b2d3f 100644 --- a/meson.build +++ b/meson.build @@ -90,7 +90,7 @@ csp_lib = library('csp', include_directories : csp_inc, dependencies : csp_deps, c_args : csp_c_args, - install : false, + install : true, pic:true, ) From fb900f7f309773501db9585e8d0b35a8da724601 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 23 Dec 2025 04:05:00 +0900 Subject: [PATCH 314/348] meson: Avoid using '.' as install_dir in install_subdir() Use an empty string instead of '.' for install_dir to prevent installing into a literal '.' directory. Signed-off-by: Yasushi SHOJI --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index adf9b2d3f..1a0159f6c 100644 --- a/meson.build +++ b/meson.build @@ -81,7 +81,7 @@ subdir('src') subdir('include/csp') if not meson.is_subproject() - install_subdir('include', install_dir : '.') + install_subdir('include', install_dir : '') endif From 7f3da29714654225835298a755390d4f41b18f2c Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Tue, 23 Dec 2025 04:08:24 +0900 Subject: [PATCH 315/348] meson: Exclude include/csp/meson.build from installation The csp/meson.build file under include is only used during the build process and does not need to be installed. Signed-off-by: Yasushi SHOJI --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1a0159f6c..145abb472 100644 --- a/meson.build +++ b/meson.build @@ -81,7 +81,7 @@ subdir('src') subdir('include/csp') if not meson.is_subproject() - install_subdir('include', install_dir : '') + install_subdir('include', install_dir : '', exclude_files : ['csp/meson.build']) endif From 7dc01ca5dbbd57491fbd7f8f2a230da0cb8bfc26 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 29 Jan 2026 19:23:53 +0900 Subject: [PATCH 316/348] github: workflows: build-test-zephyr: Rename board to match upstream Update board name from `scobc_module1` to `scobc_a1` to reflect changes in the upstream. Signed-off-by: Yasushi SHOJI --- .github/workflows/build-test-zephyr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test-zephyr.yml b/.github/workflows/build-test-zephyr.yml index 8477a350c..db56242b2 100644 --- a/.github/workflows/build-test-zephyr.yml +++ b/.github/workflows/build-test-zephyr.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: board: - - scobc_module1 + - scobc_a1 - qemu_cortex_m3 - mps2/an385 python-version: From 8e1508bf8b5a3fb58d716003e8696efff2949aea Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 29 Jan 2026 19:15:45 +0900 Subject: [PATCH 317/348] drivers: eth: eth_linux: Include endian.h for byte order functions Explicitly include when using be16toh and related functions. While GNU Libc may pull it in indirectly, the be16toh(3) man page specifies that should be included directly. Signed-off-by: Yasushi SHOJI --- src/drivers/eth/eth_linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/eth/eth_linux.c b/src/drivers/eth/eth_linux.c index 1248210cc..449d116cd 100644 --- a/src/drivers/eth/eth_linux.c +++ b/src/drivers/eth/eth_linux.c @@ -6,6 +6,7 @@ #include #include +#include #include #include From 1e3c424a002329aa7ba56c30682c89f6a12edfc7 Mon Sep 17 00:00:00 2001 From: Thomas Lykkeberg <62430938+Lykkeberg@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:47:10 +0200 Subject: [PATCH 318/348] service: peek/poke v2 with 64 bit memory support --- include/csp/csp.h | 4 ++- include/csp/csp_cmp.h | 52 +++++++++++++++++++++++++++++++++++++++ include/csp/csp_types.h | 4 +++ src/csp_service_handler.c | 50 +++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/include/csp/csp.h b/include/csp/csp.h index 5961ebbcc..41f968dfe 100644 --- a/include/csp/csp.h +++ b/include/csp/csp.h @@ -475,9 +475,11 @@ void csp_rdp_get_opt(unsigned int *window_size, unsigned int *conn_timeout_ms, unsigned int *ack_timeout, unsigned int *ack_delay_count); /** - * Set platform specific memory copy function. + * Set platform specific memory copy functions. */ void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc); +void csp_cmp_set_memread64(csp_memread64_fnc_t fnc); +void csp_cmp_set_memwrite64(csp_memwrite64_fnc_t fnc); #if (CSP_ENABLE_CSP_PRINT) diff --git a/include/csp/csp_cmp.h b/include/csp/csp_cmp.h index dcb45f5d9..91d4b0188 100644 --- a/include/csp/csp_cmp.h +++ b/include/csp/csp_cmp.h @@ -62,6 +62,14 @@ extern "C" { * Set/configure routing. */ #define CSP_CMP_ROUTE_SET_V2 7 +/** + * Peek/read data from memory - 64-bit version. + */ +#define CSP_CMP_PEEK_V2 8 +/** + * Poke/write data from memory - 64-bit version. + */ +#define CSP_CMP_POKE_V2 9 /**@}*/ /** @@ -92,6 +100,16 @@ extern "C" { */ #define CSP_CMP_POKE_MAX_LEN 200 +/** + * CMP peek/read memory - max read length - 64-bit. + */ +#define CSP_CMP_PEEK_V2_MAX_LEN 196 + +/** + * CMP poke/write memory - max write length - 64-bit. + */ +#define CSP_CMP_POKE_V2_MAX_LEN 196 + /** * CSP management protocol description. */ @@ -142,6 +160,16 @@ struct csp_cmp_message { uint8_t len; char data[CSP_CMP_POKE_MAX_LEN]; } poke; + struct { + uint64_t vaddr; /* Virtual 64-bit address on the target system */ + uint8_t len; + char data[CSP_CMP_PEEK_V2_MAX_LEN]; + } peek_v2; + struct { + uint64_t vaddr; /* Virtual 64-bit address on the target system */ + uint8_t len; + char data[CSP_CMP_POKE_V2_MAX_LEN]; + } poke_v2; csp_timestamp_t clock; }; } __attribute__((__packed__)); @@ -201,6 +229,30 @@ static inline int csp_cmp_poke(uint16_t node, uint32_t timeout, struct csp_cmp_m return csp_cmp(node, timeout, CSP_CMP_POKE, CMP_SIZE(poke) - sizeof(msg->poke.data) + msg->poke.len, msg); } +/** + * Peek (read) memory on remote node - 64-bit version. + * + * @param[in] node address of subsystem. + * @param[in] timeout timeout in mS to wait for reply.. + * @param[in/out] msg memory address and number of bytes to peek. (msg peeked/read memory) + * @return #CSP_ERR_NONE on success, otherwise an error code. + */ +static inline int csp_cmp_peek_v2(uint16_t node, uint32_t timeout, struct csp_cmp_message *msg) { + return csp_cmp(node, timeout, CSP_CMP_PEEK_V2, CMP_SIZE(peek_v2) - sizeof(msg->peek_v2.data) + msg->peek_v2.len, msg); +} + +/** + * Poke (write) memory on remote node - 64-bit version. + * + * @param[in] node address of subsystem. + * @param[in] timeout timeout in mS to wait for reply.. + * @param[in] msg memory address, number of bytes and the actual bytes to poke/write. + * @return #CSP_ERR_NONE on success, otherwise an error code. + */ +static inline int csp_cmp_poke_v2(uint16_t node, uint32_t timeout, struct csp_cmp_message *msg) { + return csp_cmp(node, timeout, CSP_CMP_POKE_V2, CMP_SIZE(poke_v2) - sizeof(msg->poke_v2.data) + msg->poke_v2.len, msg); +} + #ifdef __cplusplus } #endif diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index 4f3800987..46f2b291d 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -193,12 +193,16 @@ typedef const uint32_t csp_const_memptr_t; typedef void * csp_memptr_t; /** Const memory pointer */ typedef const void * csp_const_memptr_t; +/** Memory pointer 64-bit */ +typedef uint64_t csp_memptr64_t; #endif /** * Platform specific memory copy function. */ typedef csp_memptr_t (*csp_memcpy_fnc_t)(csp_memptr_t, csp_const_memptr_t, size_t); +typedef csp_memptr64_t (*csp_memread64_fnc_t)(csp_const_memptr_t, csp_memptr64_t, size_t); +typedef csp_memptr64_t (*csp_memwrite64_fnc_t)(csp_memptr64_t, csp_memptr_t, size_t); /** * Compile check/asserts. diff --git a/src/csp_service_handler.c b/src/csp_service_handler.c index a9eb05c20..7600e567c 100644 --- a/src/csp_service_handler.c +++ b/src/csp_service_handler.c @@ -22,12 +22,22 @@ static uint32_t wrap_32bit_memcpy(uint32_t to, const uint32_t from, size_t size) static csp_memcpy_fnc_t csp_cmp_memcpy_fnc = wrap_32bit_memcpy; #else static csp_memcpy_fnc_t csp_cmp_memcpy_fnc = (csp_memcpy_fnc_t)memcpy; +static csp_memread64_fnc_t csp_cmp_memread64_fnc = (csp_memread64_fnc_t)NULL; +static csp_memwrite64_fnc_t csp_cmp_memwrite64_fnc = (csp_memwrite64_fnc_t)NULL; #endif void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc) { csp_cmp_memcpy_fnc = fnc; } +void csp_cmp_set_memread64(csp_memread64_fnc_t fnc) { + csp_cmp_memread64_fnc = fnc; +} + +void csp_cmp_set_memwrite64(csp_memwrite64_fnc_t fnc) { + csp_cmp_memwrite64_fnc = fnc; +} + static int do_cmp_ident(struct csp_cmp_message * cmp) { /* Copy revision */ @@ -130,6 +140,38 @@ static int do_cmp_poke(struct csp_cmp_message * cmp) { return CSP_ERR_NONE; } +static int do_cmp_peek_v2(struct csp_cmp_message * cmp) { + + cmp->peek_v2.vaddr = htobe64(cmp->peek_v2.vaddr); + if (cmp->peek_v2.len > CSP_CMP_PEEK_MAX_LEN) + return CSP_ERR_INVAL; + + if (!csp_cmp_memread64_fnc) { + return CSP_ERR_DRIVER; + } + + /* Dangerous, you better know what you are doing */ + csp_cmp_memread64_fnc(cmp->peek_v2.data, cmp->peek_v2.vaddr, cmp->peek_v2.len); + + return CSP_ERR_NONE; +} + +static int do_cmp_poke_v2(struct csp_cmp_message * cmp) { + + cmp->poke_v2.vaddr = htobe64(cmp->poke_v2.vaddr); + if (cmp->poke_v2.len > CSP_CMP_POKE_MAX_LEN) + return CSP_ERR_INVAL; + + if (!csp_cmp_memwrite64_fnc) { + return CSP_ERR_DRIVER; + } + + /* Extremely dangerous, you better know what you are doing */ + csp_cmp_memwrite64_fnc(cmp->poke_v2.vaddr, cmp->poke_v2.data, cmp->poke_v2.len); + + return CSP_ERR_NONE; +} + static int do_cmp_clock(struct csp_cmp_message * cmp) { csp_timestamp_t clock; @@ -192,6 +234,14 @@ static int csp_cmp_handler(csp_packet_t * packet) { ret = do_cmp_poke(cmp); break; + case CSP_CMP_PEEK_V2: + ret = do_cmp_peek_v2(cmp); + break; + + case CSP_CMP_POKE_V2: + ret = do_cmp_poke_v2(cmp); + break; + case CSP_CMP_CLOCK: ret = do_cmp_clock(cmp); break; From 5858a84749ea59b48fb35098163f6d3f88950855 Mon Sep 17 00:00:00 2001 From: edvard Date: Tue, 8 Oct 2024 17:56:52 +0200 Subject: [PATCH 319/348] cmp: correct poke/peek length check for v2 --- src/csp_service_handler.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_service_handler.c b/src/csp_service_handler.c index 7600e567c..1947d5631 100644 --- a/src/csp_service_handler.c +++ b/src/csp_service_handler.c @@ -143,7 +143,7 @@ static int do_cmp_poke(struct csp_cmp_message * cmp) { static int do_cmp_peek_v2(struct csp_cmp_message * cmp) { cmp->peek_v2.vaddr = htobe64(cmp->peek_v2.vaddr); - if (cmp->peek_v2.len > CSP_CMP_PEEK_MAX_LEN) + if (cmp->peek_v2.len > CSP_CMP_PEEK_V2_MAX_LEN) return CSP_ERR_INVAL; if (!csp_cmp_memread64_fnc) { @@ -159,7 +159,7 @@ static int do_cmp_peek_v2(struct csp_cmp_message * cmp) { static int do_cmp_poke_v2(struct csp_cmp_message * cmp) { cmp->poke_v2.vaddr = htobe64(cmp->poke_v2.vaddr); - if (cmp->poke_v2.len > CSP_CMP_POKE_MAX_LEN) + if (cmp->poke_v2.len > CSP_CMP_POKE_V2_MAX_LEN) return CSP_ERR_INVAL; if (!csp_cmp_memwrite64_fnc) { From ddc6bbee687eaacb85c8d8e541cd23256e8976e1 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Tue, 8 Apr 2025 14:04:44 +0200 Subject: [PATCH 320/348] Allow CAN broadcast reception when not configuring interface to promisc Previously, posix instances using CAN did not accept L2 and L3 broadcast due to HW filtering --- src/drivers/can/can_socketcan.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index 7f799adb7..63cbb9cfd 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -17,6 +17,7 @@ #include #include +#include // CAN interface data, state, etc. typedef struct { @@ -151,27 +152,36 @@ static int csp_can_tx_frame(void * driver_data, uint32_t id, const uint8_t * dat return CSP_ERR_NONE; } + static int csp_can_socketcan_set_promisc(const bool promisc, can_context_t * ctx) { - struct can_filter filter = { + + struct can_filter filter[3] = { { .can_id = CFP_MAKE_DST(ctx->iface.addr), .can_mask = 0x0000, /* receive anything */ - }; + } }; if (ctx->socket == 0) { return CSP_ERR_INVAL; } + int num_filters = 1; if (!promisc) { if (csp_conf.version == 1) { - filter.can_id = CFP_MAKE_DST(ctx->iface.addr); - filter.can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); + num_filters = 1; + filter[0].can_id = CFP_MAKE_DST(ctx->iface.addr); + filter[0].can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); } else { - filter.can_id = ctx->iface.addr << CFP2_DST_OFFSET; - filter.can_mask = CFP2_DST_MASK << CFP2_DST_OFFSET; + num_filters = 3; + filter[0].can_id = ctx->iface.addr << CFP2_DST_OFFSET; + filter[0].can_mask = CFP2_DST_MASK << CFP2_DST_OFFSET; + filter[1].can_id = ((1 << (csp_id_get_host_bits() - ctx->iface.netmask)) - 1) << CFP2_DST_OFFSET; + filter[1].can_mask = CFP2_DST_MASK << CFP2_DST_OFFSET; + filter[2].can_id = 0x3FFF << CFP2_DST_OFFSET; + filter[2].can_mask = CFP2_DST_MASK << CFP2_DST_OFFSET; } } - if (setsockopt(ctx->socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) { + if (setsockopt(ctx->socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, num_filters * sizeof(struct can_filter)) < 0) { csp_print("%s: setsockopt() failed, error: %s\n", __func__, strerror(errno)); return CSP_ERR_INVAL; } From 92dc0583e077a45b2b51507828ed579293162bf1 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Sat, 14 Feb 2026 10:36:00 +0100 Subject: [PATCH 321/348] Allow CAN broadcast reception when not configuring interface to promisc (#824) Previously, posix instances using CAN did not accept L2 and L3 broadcast due to HW filtering Co-authored-by: Johan De Claville Christiansen From 6c3b18c5b78e02a874793295d3f553af2b7839cc Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Tue, 13 May 2025 15:01:15 +0200 Subject: [PATCH 322/348] Fix CSP v1 over CAN The issue originates in the fact that L3 data is overriding L2 data in the csp_packet header. This is resolved on the libcsp/libcsp/develop branch already, by removing the union. --- src/interfaces/csp_if_can.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index bcc5602df..df7e73fac 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -85,8 +85,6 @@ static int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, u memcpy(packet->frame_begin, data, sizeof(uint32_t)); packet->frame_length += sizeof(uint32_t); - csp_id_strip(packet); - /* Copy CSP length (of data) */ memcpy(&(packet->length), data + sizeof(uint32_t), sizeof(packet->length)); packet->length = be16toh(packet->length); @@ -138,6 +136,11 @@ static int csp_can1_rx(csp_iface_t * iface, uint32_t id, const uint8_t * data, u if (packet->rx_count != packet->length) break; + /* Length information is packed differently for CAN */ + uint16_t length = packet->length; + csp_id_strip(packet); + packet->length = length; + /* Rewrite incoming L2 broadcast to local node */ if (packet->id.dst == 0x1F) { packet->id.dst = iface->addr; From 192568acc0e6ef29be6ad3b3a0163af4e3580935 Mon Sep 17 00:00:00 2001 From: edvard Date: Mon, 2 Jun 2025 12:04:18 +0200 Subject: [PATCH 323/348] check timeout even if userspace --- src/csp_rdp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index b621bd9ea..9380b795f 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -416,6 +416,12 @@ void csp_rdp_check_timeouts(csp_conn_t * conn) { if (conn->rdp.state == RDP_OPEN) { + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + csp_conn_close(conn, CSP_RDP_CLOSED_BY_PROTOCOL | CSP_RDP_CLOSED_BY_TIMEOUT); + csp_bin_sem_post(&conn->rdp.tx_wait); + return; + } + /* Check if we have unacknowledged segments */ if (conn->rdp.delayed_acks) { csp_rdp_check_ack(conn); From 5f8f0d07b7cafabde56c26cf086d7d98343555f2 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 31 Jul 2025 10:22:33 +0200 Subject: [PATCH 324/348] Fix off-by-one in `csp_rtable_set()` We should consider actually returning an error. Instead we still just override the last available route, which seems to have been the previous intention. Co-authored-by: edvard --- src/csp_rtable_cidr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/csp_rtable_cidr.c b/src/csp_rtable_cidr.c index 297e00d82..551b58ab7 100644 --- a/src/csp_rtable_cidr.c +++ b/src/csp_rtable_cidr.c @@ -81,8 +81,8 @@ static int csp_rtable_set_internal(uint16_t address, uint16_t netmask, csp_iface /* If not, create a new one */ if (!entry) { entry = &rtable[rtable_inptr++]; - if (rtable_inptr > CSP_RTABLE_SIZE) { - rtable_inptr = CSP_RTABLE_SIZE; + if (rtable_inptr >= CSP_RTABLE_SIZE) { + rtable_inptr = CSP_RTABLE_SIZE-1; } } From 0ca43dec59a6cc0d99be543497bf6153c62e52b1 Mon Sep 17 00:00:00 2001 From: kivkiv12345 Date: Sat, 9 Aug 2025 20:16:12 +0200 Subject: [PATCH 325/348] Fix `sec_key` valgrind warning in `csp_zmqhub_init_filter2()` When it isn't exactly 40 characters long --- src/interfaces/csp_if_zmqhub.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index ff437ed75..8215fbb39 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -266,6 +266,15 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr } int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport) { + + /* ZMQ will cause valgrind errors if `sec_key` isn't exactly 40 characters long. + For now we deliberately parse an empty string as if no sec_key was specified. */ + const ssize_t sec_key_len = sec_key ? strnlen(sec_key, CURVE_KEYLEN-1) : 0; + if (sec_key_len && sec_key_len != CURVE_KEYLEN-1) { + /* Is it bad to expose the detected length of the ZMQ key here? */ + fprintf(stderr, "ZMQ secret key must be exactly 40 characters long (got %ld)\n", sec_key_len); + return CSP_ERR_INVAL; + } char pub[100]; csp_zmqhub_make_endpoint(host, subport, pub, sizeof(pub)); @@ -301,8 +310,8 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add assert(drv->subscriber != NULL); /* If shared secret key provided */ - if (sec_key) { - char pub_key[41]; + if (sec_key_len) { + char pub_key[CURVE_KEYLEN]; zmq_curve_public(pub_key, sec_key); /* Publisher (TX) */ From f3e6a077293ba4dc252d3efb2fbd1e308a67dd62 Mon Sep 17 00:00:00 2001 From: kivkiv12345 Date: Sun, 10 Aug 2025 11:07:12 +0200 Subject: [PATCH 326/348] Use `CSP_IFLIST_NAME_MAX` for iface name len in `csp_rtable_parse()` --- src/csp_rtable_stdio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/csp_rtable_stdio.c b/src/csp_rtable_stdio.c index bfd4f7634..5965c4533 100644 --- a/src/csp_rtable_stdio.c +++ b/src/csp_rtable_stdio.c @@ -24,13 +24,13 @@ static int csp_rtable_parse(const char * rtable, int dry_run) { while (str && (strlen(str) > 1)) { unsigned int address, via; int netmask; - char name[15]; - if (sscanf(str, "%u/%d %14s %u", &address, &netmask, name, &via) == 4) { - } else if (sscanf(str, "%u/%d %14s", &address, &netmask, name) == 3) { + char name[CSP_IFLIST_NAME_MAX] = {0}; + if (sscanf(str, "%u/%d %9s %u", &address, &netmask, name, &via) == 4) { + } else if (sscanf(str, "%u/%d %9s", &address, &netmask, name) == 3) { via = CSP_NO_VIA_ADDRESS; - } else if (sscanf(str, "%u %14s %u", &address, name, &via) == 3) { + } else if (sscanf(str, "%u %9s %u", &address, name, &via) == 3) { netmask = csp_id_get_host_bits(); - } else if (sscanf(str, "%u %14s", &address, name) == 2) { + } else if (sscanf(str, "%u %9s", &address, name) == 2) { netmask = csp_id_get_host_bits(); via = CSP_NO_VIA_ADDRESS; } else { From 0d6f034044eb474e27118208a8d025f10e2ffc3c Mon Sep 17 00:00:00 2001 From: kivkiv12345 <57446369+kivkiv12345@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:36:35 +0000 Subject: [PATCH 327/348] Post init promisc config (#30) Added `csp_zmqhub__filters()` to change promisc settings after init --- include/csp/interfaces/csp_if_zmqhub.h | 3 + src/interfaces/csp_if_zmqhub.c | 80 +++++++++++++++++++------- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/include/csp/interfaces/csp_if_zmqhub.h b/include/csp/interfaces/csp_if_zmqhub.h index dd90ef1d6..7091c9cc4 100644 --- a/include/csp/interfaces/csp_if_zmqhub.h +++ b/include/csp/interfaces/csp_if_zmqhub.h @@ -125,6 +125,9 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add void csp_zmqhub_fixup_cspv1_add_dest_addr(csp_packet_t * packet); void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen); +void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface); +void csp_zmqhub_add_filters(csp_iface_t * zmq_iface); + #ifdef __cplusplus } diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 8215fbb39..1218ca61b 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -25,6 +25,9 @@ typedef struct { void * context; void * publisher; void * subscriber; + /* We must allocate filters per interface, as ZMQ does not copy the filter value to the + outgoing packet for each setsockopt call. */ + uint16_t filt[4][3]; char name[CSP_IFLIST_NAME_MAX + 1]; csp_iface_t iface; } zmq_driver_t; @@ -265,6 +268,55 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr return CSP_ERR_NONE; } +void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { + + int ret = 0; + zmq_driver_t * drv = zmq_iface->driver_data; + const uint16_t addr = zmq_iface->addr; + const uint16_t hostmask = (1 << (csp_id_get_host_bits() - zmq_iface->netmask)) - 1; + + /* Unsubscribe from any current filters */ + for (int i = 0; i < 4; i++) { + //int i = CSP_PRIO_NORM; + drv->filt[i][0] = __builtin_bswap16((i << 14) | addr); + drv->filt[i][1] = __builtin_bswap16((i << 14) | addr | hostmask); + drv->filt[i][2] = __builtin_bswap16((i << 14) | 16383); + ret = zmq_setsockopt(drv->subscriber, ZMQ_UNSUBSCRIBE, &drv->filt[i][0], 2); + ret = zmq_setsockopt(drv->subscriber, ZMQ_UNSUBSCRIBE, &drv->filt[i][1], 2); + ret = zmq_setsockopt(drv->subscriber, ZMQ_UNSUBSCRIBE, &drv->filt[i][2], 2); + } + + /* subscribe to all packets - no filter */ + ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, NULL, 0); + assert(ret == 0); + (void)ret; +} + +void csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { + + int ret = 0; + zmq_driver_t * drv = zmq_iface->driver_data; + const uint16_t addr = zmq_iface->addr; + const uint16_t hostmask = (1 << (csp_id_get_host_bits() - zmq_iface->netmask)) - 1; + + /* Subscribe to all packets - no filter */ + ret = zmq_setsockopt(drv->subscriber, ZMQ_UNSUBSCRIBE, NULL, 0); + assert(ret == 0); + + /* Subscribe to unpromiscuous filters */ + for (int i = 0; i < 4; i++) { + //int i = CSP_PRIO_NORM; + drv->filt[i][0] = __builtin_bswap16((i << 14) | addr); + drv->filt[i][1] = __builtin_bswap16((i << 14) | addr | hostmask); + drv->filt[i][2] = __builtin_bswap16((i << 14) | 16383); + ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &drv->filt[i][0], 2); + ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &drv->filt[i][1], 2); + ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &drv->filt[i][2], 2); + } + assert(ret == 0); + (void)ret; +} + int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport) { /* ZMQ will cause valgrind errors if `sec_key` isn't exactly 40 characters long. @@ -296,6 +348,9 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add drv->iface.driver_data = drv; drv->iface.nexthop = csp_zmqhub_tx; + drv->iface.addr = addr; + drv->iface.netmask = netmask; + drv->context = zmq_ctx_new(); assert(drv->context != NULL); @@ -340,10 +395,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_IDLE, &idle, sizeof(idle)); zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_CNT, &cnt, sizeof(cnt)); zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_INTVL, &intvl, sizeof(intvl)); - - /* Generate filters */ - uint16_t hostmask = (1 << (csp_id_get_host_bits() - netmask)) - 1; - + /* Connect to server */ ret = zmq_connect(drv->publisher, pub); assert(ret == 0); @@ -352,27 +404,11 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add (void)ret; if (promisc) { - // subscribe to all packets - no filter - ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, NULL, 0); - assert(ret == 0); + csp_zmqhub_remove_filters(&drv->iface); } else { - - /* This needs to be static, because ZMQ does not copy the filter value to the - * outgoing packet for each setsockopt call */ - static uint16_t filt[4][3]; - - for (int i = 0; i < 4; i++) { - //int i = CSP_PRIO_NORM; - filt[i][0] = __builtin_bswap16((i << 14) | addr); - filt[i][1] = __builtin_bswap16((i << 14) | addr | hostmask); - filt[i][2] = __builtin_bswap16((i << 14) | 16383); - ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &filt[i][0], 2); - ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &filt[i][1], 2); - ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &filt[i][2], 2); - } - + csp_zmqhub_add_filters(&drv->iface); } From 97d806ed962a6587b0aea37865b0c6071c6fa072 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 8 Sep 2025 12:48:06 +0200 Subject: [PATCH 328/348] Improve some comments for `csp_zmqhub__filters()` --- include/csp/interfaces/csp_if_zmqhub.h | 11 +++++++++++ src/interfaces/csp_if_zmqhub.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/csp/interfaces/csp_if_zmqhub.h b/include/csp/interfaces/csp_if_zmqhub.h index 7091c9cc4..a26ac29d4 100644 --- a/include/csp/interfaces/csp_if_zmqhub.h +++ b/include/csp/interfaces/csp_if_zmqhub.h @@ -125,7 +125,18 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add void csp_zmqhub_fixup_cspv1_add_dest_addr(csp_packet_t * packet); void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen); +/** + * Make `zmq_iface` promiscuous (parse all packets) + * + * Safe to call after `csp_zmqhub_init_filter2()` to change promiscuity. + */ void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface); + +/** + * Make `zmq_iface` unpromiscuous, only parse matching unicast and broadcast addresses. + * + * Safe to call after `csp_zmqhub_init_filter2()` to change promiscuity. + */ void csp_zmqhub_add_filters(csp_iface_t * zmq_iface); diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 1218ca61b..e0d1b8b31 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -299,7 +299,7 @@ void csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { const uint16_t addr = zmq_iface->addr; const uint16_t hostmask = (1 << (csp_id_get_host_bits() - zmq_iface->netmask)) - 1; - /* Subscribe to all packets - no filter */ + /* Unsubscribe to all packets */ ret = zmq_setsockopt(drv->subscriber, ZMQ_UNSUBSCRIBE, NULL, 0); assert(ret == 0); From 7efb57b09245c050ca3456d52311c2942e935894 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Sat, 18 Oct 2025 13:47:31 +0200 Subject: [PATCH 329/348] Add function to get iface from broadcast node --- include/csp/csp_iflist.h | 1 + src/csp_iflist.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/csp/csp_iflist.h b/include/csp/csp_iflist.h index 4e23b7ec0..62f4f3d9f 100644 --- a/include/csp/csp_iflist.h +++ b/include/csp/csp_iflist.h @@ -27,6 +27,7 @@ void csp_iflist_remove(csp_iface_t * ifc); csp_iface_t * csp_iflist_get_by_name(const char * name); csp_iface_t * csp_iflist_get_by_addr(uint16_t addr); +csp_iface_t * csp_iflist_get_by_broadcast(uint16_t addr); csp_iface_t * csp_iflist_get_by_subnet(uint16_t addr, csp_iface_t * from); csp_iface_t * csp_iflist_get_by_isdfl(csp_iface_t * ifc); csp_iface_t * csp_iflist_get_by_index(int idx); diff --git a/src/csp_iflist.c b/src/csp_iflist.c index dc2a2b3ba..976c26d8d 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -132,6 +132,18 @@ csp_iface_t * csp_iflist_get_by_addr(uint16_t addr) { } +csp_iface_t * csp_iflist_get_by_broadcast(uint16_t addr) { + + csp_iface_t * ifc = interfaces; + while (ifc) { + if (csp_id_is_broadcast(addr, ifc)) { + return ifc; + } + ifc = ifc->next; + } + return NULL; +} + csp_iface_t * csp_iflist_get_by_name(const char * name) { csp_iface_t * ifc = interfaces; while (ifc) { From 26daa13b31df9284bc64ee0ed672ddbf4052d925 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Thu, 23 Oct 2025 19:29:48 +0200 Subject: [PATCH 330/348] Support for CSP alias This is used for multicast-like operations, similar to the original concept of CAN --- include/csp/csp_iflist.h | 3 +++ include/csp/csp_interface.h | 14 ++++++++++ src/csp_iflist.c | 46 ++++++++++++++++++++++++++++++++- src/csp_route.c | 4 ++- src/drivers/can/can_socketcan.c | 34 ++++++++++++++++++++++++ src/interfaces/csp_if_can.c | 2 +- 6 files changed, 100 insertions(+), 3 deletions(-) diff --git a/include/csp/csp_iflist.h b/include/csp/csp_iflist.h index 62f4f3d9f..07194a07d 100644 --- a/include/csp/csp_iflist.h +++ b/include/csp/csp_iflist.h @@ -35,6 +35,9 @@ int csp_iflist_is_within_subnet(uint16_t addr, csp_iface_t * ifc); csp_iface_t * csp_iflist_get(void); +int csp_addr_is_alias(uint16_t addr); +int csp_alias_add(csp_alias_t * addr); + /** * Convert bytes to readable string */ diff --git a/include/csp/csp_interface.h b/include/csp/csp_interface.h index 33a9f4748..b8daac3b6 100644 --- a/include/csp/csp_interface.h +++ b/include/csp/csp_interface.h @@ -19,6 +19,7 @@ extern "C" { * @return #CSP_ERR_NONE on success, otherwise an error code. */ typedef int (*nexthop_t)(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, int from_me); +typedef int (*csp_alias_add_t)(void * driver_data, uint16_t addr); /** * This struct is referenced in documentation. @@ -33,6 +34,7 @@ struct csp_iface_s { void * interface_data; /**< Interface data, only known/used by the interface layer, e.g. state information. */ void * driver_data; /**< Driver data, only known/used by the driver layer, e.g. device/channel references. */ nexthop_t nexthop; /**< Next hop (Tx) function */ + csp_alias_add_t add_alias; /**< Add receive address to interface (could be multicast receptions) */ uint8_t is_default; /**< Set default IF flag (CSP supports multiple defaults) */ /* Stats */ @@ -52,6 +54,18 @@ struct csp_iface_s { }; +/** + * Used to represent an alias reception address, bound to a particular interface + */ +typedef struct csp_alias_s { + uint16_t addr; + csp_iface_t * iface; + + /* For linked lists*/ + struct csp_alias_s * next; + +} csp_alias_t; + /** * Inputs a new packet into the system. * diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 976c26d8d..0e6ebecbe 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -9,8 +9,9 @@ #include #include -/* Interfaces are stored in a linked list */ +/* Interfaces and alias receive addresses are stored in linked lists */ static csp_iface_t * interfaces = NULL; +static csp_alias_t * aliass = NULL; int csp_iflist_is_within_subnet(uint16_t addr, csp_iface_t * ifc) { @@ -100,6 +101,49 @@ static csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc) { } +int csp_alias_add(csp_alias_t * addr) { + + if (addr == NULL || addr->iface == NULL) { + return -1; + } + + /* Register interface for L2 filtering, if interface supports */ + if (addr->iface->add_alias) { + int result = addr->iface->add_alias(addr->iface->driver_data, addr->addr); + if (result < 0) { + return result; + } + } + + /* Add to list */ + addr->next = aliass; + aliass = addr; + + return 0; +} + +csp_alias_t * csp_alias_iterate(csp_alias_t * addr) { + + if (addr == NULL) { + addr = aliass; + } else { + addr = addr->next; + } + + return addr; +} + +int csp_addr_is_alias(uint16_t addr) { + + csp_alias_t * alias = NULL; + while ((alias = csp_alias_iterate(alias)) != NULL) { + if (addr == alias->addr) { + return 1; + } + } + return 0; +} + void csp_iflist_check_dfl(void) { csp_iface_t * iface = csp_iflist_get_by_isdfl(NULL); diff --git a/src/csp_route.c b/src/csp_route.c index 2102e4360..79cd23d24 100644 --- a/src/csp_route.c +++ b/src/csp_route.c @@ -139,7 +139,9 @@ int csp_route_work(void) { /* The packet is to me, if the address matches that of any interface, * or the address matches the broadcast address of the incoming interface */ - int is_to_me = (csp_iflist_get_by_addr(packet->id.dst) != NULL || (csp_id_is_broadcast(packet->id.dst, input.iface))); + int is_to_me = ((csp_iflist_get_by_addr(packet->id.dst) != NULL || + (csp_id_is_broadcast(packet->id.dst, input.iface))) || + (csp_addr_is_alias(packet->id.dst))); /* Deduplication */ if ((csp_conf.dedup == CSP_DEDUP_ALL) || diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index 63cbb9cfd..7ebe26465 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -189,6 +189,39 @@ static int csp_can_socketcan_set_promisc(const bool promisc, can_context_t * ctx return CSP_ERR_NONE; } +int csp_can_socketcan_add_alias(void * driver_data, uint16_t addr) { + + if (csp_conf.version == 1) { + return -1; + } + + can_context_t * ctx = driver_data; + + struct can_filter filter[10]; + socklen_t len = sizeof(filter); + + getsockopt(ctx->socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, &len); + + /* Current implementation has a defined maximum of filters available */ + if (len == sizeof(filter)) { + return -2; + } + + /* If only 1 filter exist for CSP v2, interface is promisc */ + if (len == sizeof(struct can_filter)) { + return 0; + } + + /* Add filter for specific additional receive address */ + filter[len/sizeof(struct can_filter)].can_id = addr << CFP2_DST_OFFSET; + filter[len/sizeof(struct can_filter)].can_mask = CFP2_DST_MASK << CFP2_DST_OFFSET;; + + if (setsockopt(ctx->socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, len + sizeof(struct can_filter)) < 0) { + return -2; + } + + return 0; +} int csp_can_socketcan_open_and_add_interface(const char * device, const char * ifname, unsigned int node_id, int bitrate, bool promisc, csp_iface_t ** return_iface) { if (ifname == NULL) { @@ -217,6 +250,7 @@ int csp_can_socketcan_open_and_add_interface(const char * device, const char * i ctx->iface.interface_data = &ctx->ifdata; ctx->iface.driver_data = ctx; ctx->ifdata.tx_func = csp_can_tx_frame; + ctx->iface.add_alias = csp_can_socketcan_add_alias; ctx->ifdata.pbufs = NULL; /* Create socket */ diff --git a/src/interfaces/csp_if_can.c b/src/interfaces/csp_if_can.c index df7e73fac..a4a83e9ee 100644 --- a/src/interfaces/csp_if_can.c +++ b/src/interfaces/csp_if_can.c @@ -370,7 +370,7 @@ static int csp_can2_tx(csp_iface_t * iface, uint16_t via, csp_packet_t * packet, (void)from_me; /* Loopback */ - if (packet->id.dst == iface->addr) { + if (packet->id.dst == iface->addr || csp_addr_is_alias(packet->id.dst)) { csp_qfifo_write(packet, iface, NULL); return CSP_ERR_NONE; } From 3ec28e48446ce81134a06608a5c71618ed68e654 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Mon, 3 Nov 2025 18:11:18 +0100 Subject: [PATCH 331/348] Add alias support to ZMQ --- src/interfaces/csp_if_zmqhub.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index e0d1b8b31..d7ff32895 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -292,6 +292,20 @@ void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { (void)ret; } +int csp_zmqhub_add_filter(void * driver_data, uint16_t addr) { + + int ret = 0; + zmq_driver_t * drv = (zmq_driver_t*)driver_data; + + /* Subscribe to an extra address, typically registered by alias address */ + for (int i = 0; i < 4; i++) { + uint16_t filter = __builtin_bswap16((i << 14) | addr); + ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &filter, 2); + assert(ret == 0); + } + return ret; +} + void csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { int ret = 0; @@ -347,6 +361,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add drv->iface.name = drv->name; drv->iface.driver_data = drv; drv->iface.nexthop = csp_zmqhub_tx; + drv->iface.add_alias = csp_zmqhub_add_filter; drv->iface.addr = addr; drv->iface.netmask = netmask; From 9163081269c49aa22d8727ea12b826085f623c67 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Wed, 21 Jan 2026 19:41:08 +0100 Subject: [PATCH 332/348] rdp: Reduce conn RX reserved queue size to 1 times the window. From old times when the RX buffer could fluctuate more, we used to reserve two times window in the rx_queue, but after optimization we find that only 1 time is required. --- src/csp_rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 9380b795f..87e8d5c93 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -310,7 +310,7 @@ static inline bool csp_rdp_should_ack(csp_conn_t * conn) { int csp_rdp_check_ack(csp_conn_t * conn) { /* Check RX queue for spare capacity */ - if (CSP_CONN_RXQUEUE_LEN - csp_queue_size(conn->rx_queue) <= 2 * (int32_t)conn->rdp.window_size) { + if (abs(CSP_CONN_RXQUEUE_LEN - csp_queue_size(conn->rx_queue)) < conn->rdp.window_size) { return CSP_ERR_NONE; } From 263af5a0f214a72aa0f5983e2966e00644c48588 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Thu, 29 Jan 2026 11:26:03 +0100 Subject: [PATCH 333/348] Avoid function scope statics These are given funky names by gcc, like csp_buffer_pool.2, which make them difficult for static analysis tools to find and put into context. Moving to file scope is only a slight scope increase which can be tolerated for the benefits of static analysis. --- src/csp_buffer.c | 16 ++++++++-------- src/csp_conn.c | 5 +++-- src/csp_rdp.c | 3 ++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index 9171022ba..ba46fe76c 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -20,15 +20,15 @@ typedef struct csp_skbf_s { // Queue of free CSP buffers static csp_queue_handle_t csp_buffers; -void csp_buffer_init(void) { - /** - * Chunk of memory allocated for CSP buffers: - * This is marked as .noinit, because csp buffers can never be assumed zeroed out - * Putting this section in a separate non .bss area, saves some boot time */ - static csp_skbf_t csp_buffer_pool[CSP_BUFFER_COUNT] __noinit; - static csp_static_queue_t csp_buffers_queue __noinit; - static char csp_buffer_queue_data[CSP_BUFFER_COUNT * sizeof(csp_skbf_t *)] __noinit; +/** + * Chunk of memory allocated for CSP buffers: + * This is marked as .noinit, because csp buffers can never be assumed zeroed out + * Putting this section in a separate non .bss area, saves some boot time */ +static csp_skbf_t csp_buffer_pool[CSP_BUFFER_COUNT] __noinit; +static csp_static_queue_t csp_buffers_queue __noinit; +static char csp_buffer_queue_data[CSP_BUFFER_COUNT * sizeof(csp_skbf_t *)] __noinit; +void csp_buffer_init(void) { csp_buffers = csp_queue_create_static(CSP_BUFFER_COUNT, sizeof(csp_skbf_t *), csp_buffer_queue_data, &csp_buffers_queue); for (unsigned int i = 0; i < CSP_BUFFER_COUNT; i++) { diff --git a/src/csp_conn.c b/src/csp_conn.c index 74d6b9d1e..3575a3eb6 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -21,6 +21,9 @@ /* Connection pool */ static csp_conn_t arr_conn[CSP_CONN_MAX] __noinit; +/* Used by csp_conn_allocate */ +static uint8_t csp_conn_last_given = 0; + void csp_conn_check_timeouts(void) { #if (CSP_USE_RDP) for (int i = 0; i < CSP_CONN_MAX; i++) { @@ -158,8 +161,6 @@ static int csp_conn_flush_rx_queue(csp_conn_t * conn) { csp_conn_t * csp_conn_allocate(csp_conn_type_t type) { - static uint8_t csp_conn_last_given = 0; - /* Search for free connection */ csp_conn_t * conn = NULL; int i = csp_conn_last_given; diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 87e8d5c93..724e8c26f 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -41,6 +41,7 @@ static uint32_t csp_rdp_packet_timeout = 1000; static uint32_t csp_rdp_delayed_acks = 1; static uint32_t csp_rdp_ack_timeout = 1000 / 4; static uint32_t csp_rdp_ack_delay_count = 4 / 2; +static uint8_t csp_rdp_incr = 0; typedef struct __packed { uint8_t flags; @@ -48,6 +49,7 @@ typedef struct __packed { uint16_t ack_nr; } rdp_header_t; + static int csp_rdp_close_internal(csp_conn_t * conn, uint8_t closed_by, bool send_rst); /** @@ -143,7 +145,6 @@ static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, header->ack_nr = htobe16(ack_nr); /* Add a bit of ephemeral data to avoid CMP's to be deduplicated */ - static uint8_t csp_rdp_incr = 0; //header->flags = flags; header->flags |= csp_rdp_incr++ << 4 | flags; From 3c5b3bdfa06afc7df1c2b65d309703fc38163a75 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 19:12:25 +0100 Subject: [PATCH 334/348] do not build csp_posix_helper as default --- examples/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/meson.build b/examples/meson.build index 3f673cc55..833b92690 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,7 @@ csp_posix_helper = static_library('csp_posix_helper', 'csp_posix_helper.c', - include_directories : csp_inc + include_directories : csp_inc, + build_by_default : false ).extract_all_objects(recursive : true) executable('csp_server_client', From 89b6fb4c8f5435c57bb36490334efbdae4b59749 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 19:13:46 +0100 Subject: [PATCH 335/348] use proper stdint or inttypes header where needed --- include/csp/arch/csp_queue.h | 2 +- include/csp/csp_debug.h | 2 +- src/csp_iflist.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/csp/arch/csp_queue.h b/include/csp/arch/csp_queue.h index bb56cc0ed..c6f5ff43a 100644 --- a/include/csp/arch/csp_queue.h +++ b/include/csp/arch/csp_queue.h @@ -5,7 +5,7 @@ ****************************************************************************/ #pragma once -#include +#include #include #include "csp/autoconfig.h" diff --git a/include/csp/csp_debug.h b/include/csp/csp_debug.h index b5d3a8754..acaeaaf0e 100644 --- a/include/csp/csp_debug.h +++ b/include/csp/csp_debug.h @@ -16,7 +16,7 @@ #pragma once #include "csp/autoconfig.h" -#include +#include #ifdef __cplusplus extern "C" { diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 0e6ebecbe..68883daea 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -4,6 +4,7 @@ #include #include +#include #include "csp/autoconfig.h" #include From 2e25917eb07fae3a66bf903dc281e9fb63632de6 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 19:14:04 +0100 Subject: [PATCH 336/348] alias: make csp_alias_iterate static --- src/csp_iflist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 68883daea..7febfffab 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -123,7 +123,7 @@ int csp_alias_add(csp_alias_t * addr) { return 0; } -csp_alias_t * csp_alias_iterate(csp_alias_t * addr) { +static csp_alias_t * csp_alias_iterate(csp_alias_t * addr) { if (addr == NULL) { addr = aliass; From e9bfa6c220f0c9e7675f207c231dd20800e69d1e Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 19:45:50 +0100 Subject: [PATCH 337/348] fix static scope new functions --- src/drivers/can/can_socketcan.c | 2 +- src/interfaces/csp_if_zmqhub.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index 7ebe26465..cfec9974b 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -189,7 +189,7 @@ static int csp_can_socketcan_set_promisc(const bool promisc, can_context_t * ctx return CSP_ERR_NONE; } -int csp_can_socketcan_add_alias(void * driver_data, uint16_t addr) { +static int csp_can_socketcan_add_alias(void * driver_data, uint16_t addr) { if (csp_conf.version == 1) { return -1; diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index d7ff32895..521b6f99f 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -292,7 +292,7 @@ void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { (void)ret; } -int csp_zmqhub_add_filter(void * driver_data, uint16_t addr) { +static int csp_zmqhub_add_filter(void * driver_data, uint16_t addr) { int ret = 0; zmq_driver_t * drv = (zmq_driver_t*)driver_data; From 3215e5cc826bff5d2740581a1a05243685f7c671 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 19:47:34 +0100 Subject: [PATCH 338/348] re-add packet->timestamp_rx This might have to be 64-bit to get full nanosecond network time later. But for now, just to ensure it builds again, we add as uint32 --- doc/basic.md | 1 + include/csp/csp_types.h | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/basic.md b/doc/basic.md index 15c3e6fe3..541212510 100644 --- a/doc/basic.md +++ b/doc/basic.md @@ -67,6 +67,7 @@ Definition of a buffer element `csp_packet_t`: */ typedef struct { uint32_t timestamp_tx; // Time the message was sent + uint32_t timestamp_rx; // Time the message was received uint16_t length; // Data length csp_id_t id; // CSP id (unpacked version CPU readable) diff --git a/include/csp/csp_types.h b/include/csp/csp_types.h index 46f2b291d..578a4f01c 100644 --- a/include/csp/csp_types.h +++ b/include/csp/csp_types.h @@ -118,6 +118,7 @@ typedef struct { typedef struct csp_packet_s { uint32_t timestamp_tx; /*< Time the message was sent */ + uint32_t timestamp_rx; /*< Time the message was received */ struct csp_conn_s * conn; /*< Associated connection (this is used in RDP queue) */ uint16_t rx_count; /*< Received bytes */ From dfbcd0f238cfc1ef3e9f9ef845d42922d4b8cc28 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 21:12:29 +0100 Subject: [PATCH 339/348] rdp: Fixed signed compare --- src/csp_rdp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/csp_rdp.c b/src/csp_rdp.c index 724e8c26f..d1315a37a 100644 --- a/src/csp_rdp.c +++ b/src/csp_rdp.c @@ -19,7 +19,6 @@ #include #include -#include "csp_port.h" #include "csp_conn.h" #include "csp_io.h" #include "csp_semaphore.h" @@ -311,7 +310,7 @@ static inline bool csp_rdp_should_ack(csp_conn_t * conn) { int csp_rdp_check_ack(csp_conn_t * conn) { /* Check RX queue for spare capacity */ - if (abs(CSP_CONN_RXQUEUE_LEN - csp_queue_size(conn->rx_queue)) < conn->rdp.window_size) { + if ((unsigned int) abs(CSP_CONN_RXQUEUE_LEN - csp_queue_size(conn->rx_queue)) < conn->rdp.window_size) { return CSP_ERR_NONE; } From a5f3324d3980b5f15e283737977618453a36c1ff Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Fri, 13 Feb 2026 21:17:21 +0100 Subject: [PATCH 340/348] inttypes.h fix --- examples/zmqproxy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index 7db966909..e45fbe153 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include From 583c9ebc23b9193c2c6800fdff6a182e13df03e7 Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 16 Feb 2026 14:21:37 +0100 Subject: [PATCH 341/348] proper inclusion of string.h --- examples/zmqproxy.c | 1 + include/csp/csp_sfp.h | 2 -- src/csp_conn.c | 1 + src/csp_crc32.c | 5 ++++- src/csp_id.c | 2 ++ src/csp_rtable_stdio.c | 1 + src/drivers/can/can_socketcan.c | 1 + src/drivers/usart/usart_kiss.c | 1 + src/interfaces/csp_if_udp.c | 1 + src/interfaces/csp_if_zmqhub.c | 1 + 10 files changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/zmqproxy.c b/examples/zmqproxy.c index e45fbe153..492a45aff 100644 --- a/examples/zmqproxy.c +++ b/examples/zmqproxy.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/include/csp/csp_sfp.h b/include/csp/csp_sfp.h index b90bb8c7c..cb0a0c437 100644 --- a/include/csp/csp_sfp.h +++ b/include/csp/csp_sfp.h @@ -11,8 +11,6 @@ ****************************************************************************/ #pragma once -#include // memcpy() - #include #ifdef __cplusplus diff --git a/src/csp_conn.c b/src/csp_conn.c index 3575a3eb6..4be164274 100644 --- a/src/csp_conn.c +++ b/src/csp_conn.c @@ -3,6 +3,7 @@ #include "csp_conn.h" #include +#include #include #include diff --git a/src/csp_crc32.c b/src/csp_crc32.c index 480f7ca64..934778567 100644 --- a/src/csp_crc32.c +++ b/src/csp_crc32.c @@ -1,9 +1,12 @@ #include -#include #include +#include + +#include + #ifdef __AVR__ #include diff --git a/src/csp_id.c b/src/csp_id.c index 082bc2eae..5e7dc7d78 100644 --- a/src/csp_id.c +++ b/src/csp_id.c @@ -6,6 +6,8 @@ */ #include +#include + #include #include diff --git a/src/csp_rtable_stdio.c b/src/csp_rtable_stdio.c index 5965c4533..ea5a03f2a 100644 --- a/src/csp_rtable_stdio.c +++ b/src/csp_rtable_stdio.c @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/src/drivers/can/can_socketcan.c b/src/drivers/can/can_socketcan.c index cfec9974b..56b228e7a 100644 --- a/src/drivers/can/can_socketcan.c +++ b/src/drivers/can/can_socketcan.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/src/drivers/usart/usart_kiss.c b/src/drivers/usart/usart_kiss.c index bd9238862..e67ba07ef 100644 --- a/src/drivers/usart/usart_kiss.c +++ b/src/drivers/usart/usart_kiss.c @@ -4,6 +4,7 @@ #include #include +#include #include #include diff --git a/src/interfaces/csp_if_udp.c b/src/interfaces/csp_if_udp.c index 4dcc00920..ec88b167f 100644 --- a/src/interfaces/csp_if_udp.c +++ b/src/interfaces/csp_if_udp.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 521b6f99f..20ff18c70 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include From bf5eae0dc519f106c6bb8d11d16b72002f2c0dba Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 16 Feb 2026 15:50:02 +0100 Subject: [PATCH 342/348] updated string.h in sampels dir --- samples/posix/hmac/src/main.c | 2 ++ samples/posix/simple-send-canbus/src/main.c | 2 ++ samples/posix/simple-send-usart/src/main.c | 3 +++ samples/posix/simple-send-zmq/src/main.c | 2 ++ samples/posix/simple-sfp-send-recv/src/main.c | 2 ++ 5 files changed, 11 insertions(+) diff --git a/samples/posix/hmac/src/main.c b/samples/posix/hmac/src/main.c index 663db2664..3bb7b841c 100644 --- a/samples/posix/hmac/src/main.c +++ b/samples/posix/hmac/src/main.c @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/samples/posix/simple-send-canbus/src/main.c b/samples/posix/simple-send-canbus/src/main.c index df6173ff5..8e353c09b 100644 --- a/samples/posix/simple-send-canbus/src/main.c +++ b/samples/posix/simple-send-canbus/src/main.c @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/samples/posix/simple-send-usart/src/main.c b/samples/posix/simple-send-usart/src/main.c index 90197e754..104c96d69 100644 --- a/samples/posix/simple-send-usart/src/main.c +++ b/samples/posix/simple-send-usart/src/main.c @@ -1,3 +1,6 @@ +#include + + #include #include #include diff --git a/samples/posix/simple-send-zmq/src/main.c b/samples/posix/simple-send-zmq/src/main.c index c3aa9d9a9..f729a0dbd 100644 --- a/samples/posix/simple-send-zmq/src/main.c +++ b/samples/posix/simple-send-zmq/src/main.c @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/samples/posix/simple-sfp-send-recv/src/main.c b/samples/posix/simple-sfp-send-recv/src/main.c index a7389fee0..676c740d1 100644 --- a/samples/posix/simple-sfp-send-recv/src/main.c +++ b/samples/posix/simple-sfp-send-recv/src/main.c @@ -1,6 +1,8 @@ /* needed for pthread_timedjoin_np */ #define _GNU_SOURCE +#include + #include #include #include From cd6daa25773e7d7db8168fda6d9c59fe107fb567 Mon Sep 17 00:00:00 2001 From: Johan De Claville Christiansen Date: Tue, 17 Feb 2026 18:02:40 +0100 Subject: [PATCH 343/348] Remove packet copy when transmitting CSP packets (#942) * Remove packet copy when transmitting CSP packets Credits to @MErdbruegger for suggesting this improvement. * inlined helper function and fix a few spellings --------- Co-authored-by: Troels Jessen --- src/csp_io.c | 148 +++++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 71 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index 70c160d43..38c2b6911 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -90,63 +90,84 @@ void csp_id_clear(csp_id_t * target) { target->flags = 0; } +static inline int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { + + /* This check is is similar to that below, but faster */ + if (iface == routed_from) { + return 1; + } + + /* Do not send to interface with similar subnet (split horizon) */ + if (csp_iflist_is_within_subnet(iface->addr, routed_from)) { + return 1; + } + + return 0; +} + +static inline void convert_broadcast(csp_id_t * idout, csp_id_t * idout_copy, csp_iface_t * snd_iface) { + + /* Rewrite routed broadcast (L3) to local (L2) when arriving at the interface */ + if (csp_id_is_broadcast(idout->dst, snd_iface)) { + idout_copy->dst = csp_id_get_max_nodeid(); + } +} + +static inline void send_packet(csp_id_t * idout_copy, csp_packet_t * snd_pkt, csp_iface_t * snd_iface, uint16_t via, int from_me) { + + /* Apply outgoing interface address to packet */ + if ((from_me) && (idout_copy->src == 0)) { + idout_copy->src = snd_iface->addr; + } + + if (snd_pkt != NULL) { + csp_send_direct_iface(idout_copy, snd_pkt, snd_iface, via, from_me); + } +} + void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * routed_from) { int from_me = (routed_from == NULL ? 1 : 0); - - /* Try to find the destination on any local subnets */ int via = CSP_NO_VIA_ADDRESS; - csp_iface_t * iface = NULL; - csp_packet_t * copy = NULL; - int local_found = 0; /* Quickly send on loopback */ - if(idout->dst == csp_if_lo.addr){ + if (idout->dst == csp_if_lo.addr) { csp_send_direct_iface(idout, packet, &csp_if_lo, via, from_me); return; } - /* Make copy as broadcast modifies destination making iflist_get_by_subnet the skip next redundant ifaces */ - csp_id_t _idout = *idout; + csp_id_t idout_copy = *idout; /* Broadcast function procedure modifies destination */ + csp_iface_t * iface = NULL; /* Interface iterator */ + csp_iface_t * next_iface = NULL; /* Reference to keep track of packet copying */ + /* Try to find the destination on any local subnets */ + int local_found = 0; while ((iface = csp_iflist_get_by_subnet(idout->dst, iface)) != NULL) { local_found = 1; - /* Do not send back to same interface (split horizon) - * This check is is similar to that below, but faster */ - if (iface == routed_from) { - continue; - } - - /* Do not send to interface with similar subnet (split horizon) */ - if (csp_iflist_is_within_subnet(iface->addr, routed_from)) { + /* Do not send back to same interface (split horizon) */ + if (is_same_subnet(iface, routed_from)) { continue; } - /* Apply outgoing interface address to packet */ - if (from_me && (idout->src == 0)) { - _idout.src = iface->addr; - } - - /* Rewrite routed broadcast (L3) to local (L2) when arriving at the interface */ - if (csp_id_is_broadcast(idout->dst, iface)) { - _idout.dst = csp_id_get_max_nodeid(); - } + if (next_iface != NULL) { + csp_packet_t * copy = csp_buffer_clone(packet); - /* Todo: Find an elegant way to avoid making a copy when only a single destination interface - * is found. But without looping the list twice. And without using stack memory. - * Is this even possible? */ - copy = csp_buffer_clone(packet); - if (copy != NULL) { - csp_send_direct_iface(&_idout, copy, iface, via, from_me); + convert_broadcast(idout, &idout_copy, next_iface); + send_packet(&idout_copy, copy, next_iface, via, from_me); } - + next_iface = iface; } - /* If the above worked, we don't want to look at the routing table */ - if (local_found == 1) { - csp_buffer_free(packet); + if (local_found) { + if (next_iface != NULL) { + convert_broadcast(idout, &idout_copy, next_iface); + send_packet(&idout_copy, packet, next_iface, via, from_me); + } else { + csp_buffer_free(packet); + } + /* If a match was found, we don't want to look at the routing table */ return; } @@ -158,32 +179,27 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route do { route_found = 1; - /* Do not send back to same interface (split horizon) - * This check is is similar to that below, but faster */ - if (route->iface == routed_from) { - continue; - } - - /* Do not send to interface with similar subnet (split horizon) */ - if (csp_iflist_is_within_subnet(route->iface->addr, routed_from)) { + /* Do not send back to same interface (split horizon) */ + if (is_same_subnet(route->iface, routed_from)) { continue; } - /* Apply outgoing interface address to packet */ - if (from_me && (idout->src == 0)) { - idout->src = route->iface->addr; - } - - copy = csp_buffer_clone(packet); - if (copy != NULL) { - csp_send_direct_iface(idout, copy, route->iface, route->via, from_me); + if (next_iface != NULL) { + csp_packet_t * copy = csp_buffer_clone(packet); + send_packet(&idout_copy, copy, next_iface, via, from_me); } + next_iface = route->iface; + via = route->via; } while ((route = csp_rtable_search_backward(route)) != NULL); } /* If the above worked, we don't want to look at default interfaces */ if (route_found == 1) { - csp_buffer_free(packet); + if (next_iface != NULL) { + send_packet(&idout_copy, packet, next_iface, via, from_me); + } else { + csp_buffer_free(packet); + } return; } @@ -192,30 +208,20 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route /* Try to send via default interfaces */ while ((iface = csp_iflist_get_by_isdfl(iface)) != NULL) { - /* Do not send back to same interface (split horizon) - * This check is is similar to that below, but faster */ - if (iface == routed_from) { - continue; - } - - /* Do not send to interface with similar subnet (split horizon) */ - if (csp_iflist_is_within_subnet(iface->addr, routed_from)) { + if (is_same_subnet(iface, routed_from)) { continue; } - /* Apply outgoing interface address to packet */ - if (from_me && (idout->src == 0)) { - idout->src = iface->addr; - } - - /* Todo: Find an elegant way to avoid making a copy when only a single destination interface - * is found. But without looping the list twice. And without using stack memory. - * Is this even possible? */ - copy = csp_buffer_clone(packet); - if (copy != NULL) { - csp_send_direct_iface(idout, copy, iface, via, from_me); + if (next_iface != NULL) { + csp_packet_t * copy = csp_buffer_clone(packet); + send_packet(&idout_copy, copy, next_iface, via, from_me); } + next_iface = iface; + } + if (next_iface != NULL) { + send_packet(&idout_copy, packet, next_iface, via, from_me); + return; } csp_buffer_free(packet); From b74a81a05a9b62749e4cc51acae66c37722e88e8 Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Thu, 15 Jan 2026 08:16:58 +0100 Subject: [PATCH 344/348] Remove packet copy when transmitting CSP packets Credits to @MErdbruegger for suggesting this improvement. --- src/csp_io.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index 38c2b6911..2c7bd0571 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -90,7 +90,7 @@ void csp_id_clear(csp_id_t * target) { target->flags = 0; } -static inline int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { +static int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { /* This check is is similar to that below, but faster */ if (iface == routed_from) { @@ -105,15 +105,15 @@ static inline int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) return 0; } -static inline void convert_broadcast(csp_id_t * idout, csp_id_t * idout_copy, csp_iface_t * snd_iface) { +static void convert_broadcast(csp_id_t * idout, csp_id_t * idout_copy, csp_iface_t * snd_iface) { - /* Rewrite routed broadcast (L3) to local (L2) when arriving at the interface */ + /* Rewrite routed brodcast (L3) to local (L2) when arriving at the interface */ if (csp_id_is_broadcast(idout->dst, snd_iface)) { idout_copy->dst = csp_id_get_max_nodeid(); } } -static inline void send_packet(csp_id_t * idout_copy, csp_packet_t * snd_pkt, csp_iface_t * snd_iface, uint16_t via, int from_me) { +static void send_packet(csp_id_t * idout_copy, csp_packet_t * snd_pkt, csp_iface_t * snd_iface, uint16_t via, int from_me) { /* Apply outgoing interface address to packet */ if ((from_me) && (idout_copy->src == 0)) { @@ -146,7 +146,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route local_found = 1; - /* Do not send back to same interface (split horizon) */ + /* Do not send back to same inteface (split horizon) */ if (is_same_subnet(iface, routed_from)) { continue; } @@ -179,7 +179,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route do { route_found = 1; - /* Do not send back to same interface (split horizon) */ + /* Do not send back to same inteface (split horizon) */ if (is_same_subnet(route->iface, routed_from)) { continue; } From a03ac2831fe5d74aa30270264f46a7d1590f4e6e Mon Sep 17 00:00:00 2001 From: Johan de Claville Christiansen Date: Mon, 16 Feb 2026 18:12:01 +0100 Subject: [PATCH 345/348] inlined helper function and fix a few spellings --- src/csp_io.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/csp_io.c b/src/csp_io.c index 2c7bd0571..38c2b6911 100644 --- a/src/csp_io.c +++ b/src/csp_io.c @@ -90,7 +90,7 @@ void csp_id_clear(csp_id_t * target) { target->flags = 0; } -static int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { +static inline int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { /* This check is is similar to that below, but faster */ if (iface == routed_from) { @@ -105,15 +105,15 @@ static int is_same_subnet(csp_iface_t * iface, csp_iface_t * routed_from) { return 0; } -static void convert_broadcast(csp_id_t * idout, csp_id_t * idout_copy, csp_iface_t * snd_iface) { +static inline void convert_broadcast(csp_id_t * idout, csp_id_t * idout_copy, csp_iface_t * snd_iface) { - /* Rewrite routed brodcast (L3) to local (L2) when arriving at the interface */ + /* Rewrite routed broadcast (L3) to local (L2) when arriving at the interface */ if (csp_id_is_broadcast(idout->dst, snd_iface)) { idout_copy->dst = csp_id_get_max_nodeid(); } } -static void send_packet(csp_id_t * idout_copy, csp_packet_t * snd_pkt, csp_iface_t * snd_iface, uint16_t via, int from_me) { +static inline void send_packet(csp_id_t * idout_copy, csp_packet_t * snd_pkt, csp_iface_t * snd_iface, uint16_t via, int from_me) { /* Apply outgoing interface address to packet */ if ((from_me) && (idout_copy->src == 0)) { @@ -146,7 +146,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route local_found = 1; - /* Do not send back to same inteface (split horizon) */ + /* Do not send back to same interface (split horizon) */ if (is_same_subnet(iface, routed_from)) { continue; } @@ -179,7 +179,7 @@ void csp_send_direct(csp_id_t* idout, csp_packet_t * packet, csp_iface_t * route do { route_found = 1; - /* Do not send back to same inteface (split horizon) */ + /* Do not send back to same interface (split horizon) */ if (is_same_subnet(route->iface, routed_from)) { continue; } From 8f9302e5862280af579e989d119a993cb5da182e Mon Sep 17 00:00:00 2001 From: edvard Date: Tue, 17 Feb 2026 10:53:49 +0100 Subject: [PATCH 346/348] zmqhub: make iflist iterator public, return err in zmq funcs Let users have access to iface iterator --- include/csp/csp_iflist.h | 8 ++++++++ include/csp/interfaces/csp_if_zmqhub.h | 4 ++-- src/csp_iflist.c | 4 ++-- src/interfaces/csp_if_zmqhub.c | 19 +++++++++++++------ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/include/csp/csp_iflist.h b/include/csp/csp_iflist.h index 07194a07d..478805545 100644 --- a/include/csp/csp_iflist.h +++ b/include/csp/csp_iflist.h @@ -25,6 +25,14 @@ void csp_iflist_add(csp_iface_t * iface); */ void csp_iflist_remove(csp_iface_t * ifc); +/** + * Iterate over the list of interfaces. + * + * @param[in] ifc Previous interface to continue iteration, or NULL to start at the head of the list. + * @return Pointer to the next interface, or NULL if the end of the list is reached. + */ +csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc); + csp_iface_t * csp_iflist_get_by_name(const char * name); csp_iface_t * csp_iflist_get_by_addr(uint16_t addr); csp_iface_t * csp_iflist_get_by_broadcast(uint16_t addr); diff --git a/include/csp/interfaces/csp_if_zmqhub.h b/include/csp/interfaces/csp_if_zmqhub.h index a26ac29d4..ae6cec903 100644 --- a/include/csp/interfaces/csp_if_zmqhub.h +++ b/include/csp/interfaces/csp_if_zmqhub.h @@ -130,14 +130,14 @@ void * csp_zmqhub_fixup_cspv1_del_dest_addr(uint8_t * rx_data, size_t * datalen) * * Safe to call after `csp_zmqhub_init_filter2()` to change promiscuity. */ -void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface); +int csp_zmqhub_remove_filters(csp_iface_t * zmq_iface); /** * Make `zmq_iface` unpromiscuous, only parse matching unicast and broadcast addresses. * * Safe to call after `csp_zmqhub_init_filter2()` to change promiscuity. */ -void csp_zmqhub_add_filters(csp_iface_t * zmq_iface); +int csp_zmqhub_add_filters(csp_iface_t * zmq_iface); #ifdef __cplusplus diff --git a/src/csp_iflist.c b/src/csp_iflist.c index 7febfffab..2d121b367 100644 --- a/src/csp_iflist.c +++ b/src/csp_iflist.c @@ -19,7 +19,7 @@ int csp_iflist_is_within_subnet(uint16_t addr, csp_iface_t * ifc) { if (ifc == NULL) { return 0; } - + uint16_t netmask = ((1 << ifc->netmask) - 1) << (csp_id_get_host_bits() - ifc->netmask); uint16_t network_a = ifc->addr & netmask; uint16_t network_b = addr & netmask; @@ -87,7 +87,7 @@ csp_iface_t * csp_iflist_get_by_isdfl(csp_iface_t * ifc) { } -static csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc) { +csp_iface_t * csp_iflist_iterate(csp_iface_t * ifc) { /* Head of list */ if (ifc == NULL) { diff --git a/src/interfaces/csp_if_zmqhub.c b/src/interfaces/csp_if_zmqhub.c index 20ff18c70..b726decae 100644 --- a/src/interfaces/csp_if_zmqhub.c +++ b/src/interfaces/csp_if_zmqhub.c @@ -269,7 +269,11 @@ int csp_zmqhub_init_w_name_endpoints_rxfilter(const char * ifname, uint16_t addr return CSP_ERR_NONE; } -void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { +int csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { + + if(zmq_iface == NULL || zmq_iface->driver_data == NULL || zmq_iface->nexthop != csp_zmqhub_tx) { + return -1; + } int ret = 0; zmq_driver_t * drv = zmq_iface->driver_data; @@ -290,7 +294,7 @@ void csp_zmqhub_remove_filters(csp_iface_t * zmq_iface) { /* subscribe to all packets - no filter */ ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, NULL, 0); assert(ret == 0); - (void)ret; + return ret; } static int csp_zmqhub_add_filter(void * driver_data, uint16_t addr) { @@ -307,8 +311,11 @@ static int csp_zmqhub_add_filter(void * driver_data, uint16_t addr) { return ret; } -void csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { +int csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { + if(zmq_iface == NULL || zmq_iface->driver_data == NULL || zmq_iface->nexthop != csp_zmqhub_tx) { + return -1; + } int ret = 0; zmq_driver_t * drv = zmq_iface->driver_data; const uint16_t addr = zmq_iface->addr; @@ -329,11 +336,11 @@ void csp_zmqhub_add_filters(csp_iface_t * zmq_iface) { ret = zmq_setsockopt(drv->subscriber, ZMQ_SUBSCRIBE, &drv->filt[i][2], 2); } assert(ret == 0); - (void)ret; + return ret; } int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t addr, uint16_t netmask, int promisc, csp_iface_t ** return_interface, char * sec_key, uint16_t subport, uint16_t pubport) { - + /* ZMQ will cause valgrind errors if `sec_key` isn't exactly 40 characters long. For now we deliberately parse an empty string as if no sec_key was specified. */ const ssize_t sec_key_len = sec_key ? strnlen(sec_key, CURVE_KEYLEN-1) : 0; @@ -411,7 +418,7 @@ int csp_zmqhub_init_filter2(const char * ifname, const char * host, uint16_t add zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_IDLE, &idle, sizeof(idle)); zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_CNT, &cnt, sizeof(cnt)); zmq_setsockopt(drv->subscriber, ZMQ_TCP_KEEPALIVE_INTVL, &intvl, sizeof(intvl)); - + /* Connect to server */ ret = zmq_connect(drv->publisher, pub); assert(ret == 0); From 0f79fdc213c9282de4e6ea46c2d0354d726d3bb1 Mon Sep 17 00:00:00 2001 From: edvard Date: Tue, 17 Feb 2026 11:54:57 +0100 Subject: [PATCH 347/348] meson option: change default to not zero csp packets This should not really be of any concern of libcsp --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 682b3f7ee..72a1a3c07 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -13,7 +13,7 @@ option('enable_csp_print', type: 'boolean', value: true, description: 'Enable cs option('have_stdio', type: 'boolean', value: true, description: 'Use print and scan functions (some features may be missing without)') option('use_rtable', type: 'boolean', value: false, description: 'Allows to setup a list of static routes. End nodes do not need this. But radios and routers might') option('print_stdio', type: 'boolean', value: true, description: 'Use vprintf for csp_print_func') -option('buffer_zero_clear', type: 'boolean', value: true, description: 'Zero out the packet buffer upon allocation') +option('buffer_zero_clear', type: 'boolean', value: false, description: 'Zero out the packet buffer upon allocation') # Memory tuning parameters: # Try to balance these so there is enough memory to handle expected system usage plus some, From aceef30a7e032ffed58d1700637751960b3f612b Mon Sep 17 00:00:00 2001 From: edvard Date: Tue, 17 Feb 2026 14:10:55 +0100 Subject: [PATCH 348/348] csp_buffer: memcpy only what is used in the packet This is faster in most cases size is protected with if as the user could have set invalid packet length. --- src/csp_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/csp_buffer.c b/src/csp_buffer.c index ba46fe76c..884a4ae4d 100644 --- a/src/csp_buffer.c +++ b/src/csp_buffer.c @@ -160,7 +160,8 @@ csp_packet_t * csp_buffer_clone(const csp_packet_t * packet) { void csp_buffer_copy(const csp_packet_t * src, csp_packet_t * dst) { if ((NULL != src) && (NULL != dst)) { - (void)memcpy(dst, src, sizeof(csp_packet_t)); + size_t size = sizeof(csp_packet_t) - CSP_BUFFER_SIZE + src->length; + (void)memcpy(dst, src, size > sizeof(csp_packet_t) ? sizeof(csp_packet_t) : size); dst->frame_begin = (dst->header + CSP_PACKET_PADDING_BYTES) - (src->data - src->frame_begin); } }